#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define TAG_MAX_LEN 32  // Максимальная длина имени тега

// Виды лексем
typedef enum {
    T_TAG_BEG,      // Открывающий тег
    T_TAG_END,      // Закрывающий тег
    T_TAG_SINGLE,   // Одиночный тег
    T_DATA,         // "Данные" (то есть, не тег)
    T_END           // Конец потока лексем
} token_type;

// Структура для токена
struct {
    token_type type;                // Вид лексемы

    // Дополнительные данные лексемы
    // Выбирается одно из двух
    union {
        char tag_name[TAG_MAX_LEN]; // Имя тега
        size_t data_length;         // Длина данных
    } value;
} Token;

// Здесь хранится текущий символ
struct {
    int val;        // Сам символ
    size_t pos;     // Позиция в строке
    size_t line;    // Номер строки
} Character;

size_t tabulation;
const size_t TAB_STEP = 4;

static void init();
static void token_next();

//----------------------------------------------------------------------------

int main() {
    init();
    for (token_next(); Token.type != T_END; token_next()) {
        switch (Token.type) {
        case T_TAG_BEG:
            printf("%*c%s\n", tabulation, '{', Token.value.tag_name);
            tabulation += TAB_STEP;
            break;
        case T_TAG_END:
            tabulation -= TAB_STEP;
            printf("%*c\n", tabulation, '}');
            break;
        case T_TAG_SINGLE:
            printf("%*c%s}\n", tabulation, '{', Token.value.tag_name);
            break;
        case T_DATA:
            printf("%*cdata length: %u]\n", tabulation, '[', Token.value.data_length);
            break;
        default:
            fprintf(stderr, "%s\n", "Runtime error");
            exit(EXIT_FAILURE);
        }
    }
    if (tabulation) {
        fprintf(stderr, "Warning! %s\n", "Unmatched tag");
    }
}

//----------------------------------------------------------------------------

static void error(const char *message) {
    fprintf(
        stderr,
        "Error[%u:%u] %s\n",
        Character.line + 1,
        Character.pos,
        message);
    exit(EXIT_FAILURE);
}

//----------------------------------------------------------------------------

static void char_next() {
    Character.val = getchar();
    if (Character.val == '\n') {
        Character.pos = 0;
        Character.line += 1;
    } else {
        Character.pos += 1;
    }
}

//----------------------------------------------------------------------------

static void init() {
    char_next();
}

//----------------------------------------------------------------------------

static void scan_tag();
static void scan_data();

static void token_next() {
    while (isspace(Character.val))
        char_next();

    switch (Character.val) {
    case EOF:
        Token.type = T_END;
        break;
    case '<':
        scan_tag();
        break;
    default:
        scan_data();
    }
}

//----------------------------------------------------------------------------

static void skip_comment() {
    char_next();
    if (Character.val != '-') error("Unexpected symbol");
    char_next();
    if (Character.val != '-') error("Unexpected symbol");

    _Bool comment_flag = true;
    while ((Character.val != EOF) && comment_flag) {
        char_next();
        if (Character.val == '-') {
            char_next();
            if (Character.val == '-') {
                char_next();
                if (Character.val == '>') comment_flag = false;
            }
        }
    }
    char_next();
}

//----------------------------------------------------------------------------

static void tag_get_name() {
    int i = 0;
    while ((i < (TAG_MAX_LEN - 1)) && isalnum(Character.val)) {
        Token.value.tag_name[i] = Character.val;
        char_next();
        i += 1;
    }

    // Завершающий ноль для строки
    Token.value.tag_name[i] = 0;
}

//----------------------------------------------------------------------------

static void scan_tag() {
    char_next();

    if (Character.val == '!') {
        skip_comment();
        token_next();
        return;
    }
    else if (Character.val == '/') {
        Token.type = T_TAG_END;
        char_next();
    }
    else Token.type = T_TAG_BEG;

    // Запоминаем имя тега
    tag_get_name();
    // Детектим пустые теги
    if (!strlen(Token.value.tag_name)) error("Empty tag");

    // Ищем конец тега
    while (Character.val != '>') {
        if (Character.val == EOF) error("`>' expected");
        if (Character.val == '/') {
            char_next();
            if (Character.val == '>') {
                if (Token.type == T_TAG_BEG) {
                    Token.type = T_TAG_SINGLE;
                    break;
                } else error("Unexpected symbol");
            } else error("`/>' expected");
        }
        char_next();
    }

    char_next();
}

//----------------------------------------------------------------------------

static void scan_data() {

    Token.type = T_DATA;
    Token.value.data_length = 0;
    do {
        Token.value.data_length += 1;
        char_next();
    } while ((Character.val != EOF) && (Character.val != '<'));
}
