Загрузка данных


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

#define STATS_FILE "/tmp/chown_stats.txt"

// Функция загрузки статистики из временного файла сессии
void load_statistics(int *success, int *failed) {
    *success = 0;
    *failed = 0;
    FILE *f = fopen(STATS_FILE, "r");
    if (f) {
        if (fscanf(f, "%d %d", success, failed) != 2) {
            *success = 0;
            *failed = 0;
        }
        fclose(f);
    }
}

// Функция сохранения статистики во временный файл сессии
void save_statistics(int success, int failed) {
    FILE *f = fopen(STATS_FILE, "w");
    if (f) {
        fprintf(f, "%d %d", success, failed);
        fclose(f);
    }
}

// Функция сброса статистики
void reset_statistics(void) {
    FILE *f = fopen(STATS_FILE, "w");
    if (f) {
        fprintf(f, "0 0");
        fclose(f);
    }
}

// Проверка имени пользователя или группы по POSIX и правилам ТЗ
bool is_valid_name(const char *name) {
    if (!name || strlen(name) == 0) return false;
    // Имя не должно начинаться с цифры или дефиса
    if (isdigit((unsigned char)name[0]) || name[0] == '-') return false;
    
    for (int i = 0; name[i] != '\0'; i++) {
        char c = name[i];
        if (!isalnum((unsigned char)c) && c != '_' && c != '-') {
            return false;
        }
    }
    return true;
}

// Валидация токена OWNER[:GROUP] или OWNER[.GROUP]
bool validate_owner_group(const char *token) {
    if (!token || strlen(token) == 0) return false;
    
    int colons = 0, dots = 0;
    for (int i = 0; token[i] != '\0'; i++) {
        if (token[i] == ':') colons++;
        if (token[i] == '.') dots++;
    }
    
    // Конфликт разделителей или избыточное количество
    if ((colons > 0 && dots > 0) || colons > 1 || dots > 1) return false;
    
    if (colons == 1) {
        if (strcmp(token, ":") == 0) return false;
        if (token[strlen(token) - 1] == ':') return false; // Запрет висящего двоеточия (root:)
        
        char *dup = strdup(token);
        char *colon_ptr = strchr(dup, ':');
        *colon_ptr = '\0';
        char *owner = dup;
        char *group = colon_ptr + 1;
        
        bool ok = true;
        if (strlen(owner) > 0) ok = ok && is_valid_name(owner);
        if (strlen(group) > 0) ok = ok && is_valid_name(group);
        free(dup);
        return ok;
    }
    
    if (dots == 1) {
        if (token[0] == '.' || token[strlen(token) - 1] == '.') return false;
        char *dup = strdup(token);
        char *dot_ptr = strchr(dup, '.');
        *dot_ptr = '\0';
        char *owner = dup;
        char *group = dot_ptr + 1;
        
        bool ok = is_valid_name(owner) && is_valid_name(group);
        free(dup);
        return ok;
    }
    
    return is_valid_name(token);
}

// Главная функция синтаксического анализа строки вызова
bool validate_chown(const char *cmd_line) {
    char *line_dup = strdup(cmd_line);
    char *tokens[256];
    int token_count = 0;
    
    char *tok = strtok(line_dup, " \t");
    while (tok && token_count < 256) {
        tokens[token_count++] = tok;
        tok = strtok(NULL, " \t");
    }
    
    if (token_count == 0 || strcmp(tokens[0], "chown") != 0) {
        free(line_dup);
        return false;
    }
    
    bool parsing_flags = true;
    char *owner_group = NULL;
    int file_count = 0;
    
    for (int i = 1; i < token_count; i++) {
        char *t = tokens[i];
        if (parsing_flags && t[0] == '-') {
            if (strcmp(t, "-") == 0 || strncmp(t, "--", 2) == 0) {
                free(line_dup);
                return false; // Длинные флаги не поддерживаются в этой версии модулей
            }
            for (int j = 1; t[j] != '\0'; j++) {
                if (t[j] != 'R' && t[j] != 'v' && t[j] != 'c' && t[j] != 'f') {
                    free(line_dup);
                    return false; // Неизвестный короткий флаг
                }
            }
        } else {
            if (owner_group == NULL) {
                owner_group = t;
                parsing_flags = false;
            } else {
                file_count++;
            }
        }
    }
    
    if (!owner_group || file_count == 0) {
        free(line_dup);
        return false;
    }
    
    bool res = validate_owner_group(owner_group);
    free(line_dup);
    return res;
}

int main(void) {
    char line[1024];
    int success_count = 0, failed_count = 0;
    
    while (fgets(line, sizeof(line), stdin)) {
        // Удаление символа перевода строки
        line[strcspn(line, "\r\n")] = '\0';
        
        // Пропуск пустых строк ввода
        if (strlen(line) == 0) continue;
        
        load_statistics(&success_count, &failed_count);
        
        if (strcmp(line, "getsuccess") == 0) {
            printf("%d\n", success_count);
        } else if (strcmp(line, "getfailed") == 0) {
            printf("%d\n", failed_count);
        } else if (strcmp(line, "getstats") == 0) {
            printf("%d %d %d\n", success_count, failed_count, success_count + failed_count);
        } else if (strcmp(line, "resetstats") == 0) {
            reset_statistics();
            printf("\n");
        } else {
            // Обычная строка вызова утилиты для проверки синтаксиса
            if (validate_chown(line)) {
                success_count++;
                save_statistics(success_count, failed_count);
                printf("True\n");
            } else {
                failed_count++;
                save_statistics(success_count, failed_count);
                printf("False\n");
            }
        }
        fflush(stdout);
    }
    return 0;
}