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


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

#define MAX_TOKENS 128
#define MAX_TOKEN_LEN 512
#define STATS_FILE "chown_stats.txt"

typedef struct {
    char tokens[MAX_TOKENS][MAX_TOKEN_LEN];
    int token_count;
} ParsedCommand;

typedef struct {
    int success_count;
    int failed_count;
} CheckStats;

static void trim_and_clean(const char *src, char *dst) {
    int i = 0, j = 0;
    int in_space = 1;
    while (src[i] != '\0') {
        if (isspace((unsigned char)src[i])) {
            if (!in_space) {
                dst[j++] = ' ';
                in_space = 1;
            }
        } else {
            dst[j++] = src[i];
            in_space = 0;
        }
        i++;
    }
    if (j > 0 && dst[j - 1] == ' ') {
        j--;
    }
    dst[j] = '\0';
}

ParsedCommand parse_chown_command(const char *cmd_line) {
    ParsedCommand cmd;
    cmd.token_count = 0;
    char cleaned[4096];
    trim_and_clean(cmd_line, cleaned);
    
    char *token = strtok(cleaned, " ");
    while (token != NULL && cmd.token_count < MAX_TOKENS) {
        strncpy(cmd.tokens[cmd.token_count], token, MAX_TOKEN_LEN - 1);
        cmd.tokens[cmd.token_count][MAX_TOKEN_LEN - 1] = '\0';
        cmd.token_count++;
        token = strtok(NULL, " ");
    }
    return cmd;
}

int check_command(const ParsedCommand *cmd) {
    if (cmd->token_count == 0) return 0;
    return (strcmp(cmd->tokens[0], "chown") == 0);
}

static int is_valid_name(const char *s) {
    int len = strlen(s);
    if (len == 0) return 0;
    int all_digits = 1;
    for (int i = 0; i < len; i++) {
        if (!isdigit((unsigned char)s[i])) {
            all_digits = 0;
        }
        if (!isalnum((unsigned char)s[i]) && s[i] != '-') {
            return 0;
        }
    }
    if (all_digits) return 1;
    if (isdigit((unsigned char)s[0])) return 0;
    return 1;
}

static int validate_spec(const char *spec) {
    char buf[MAX_TOKEN_LEN];
    strncpy(buf, spec, MAX_TOKEN_LEN - 1);
    buf[MAX_TOKEN_LEN - 1] = '\0';
    
    char *colon = strchr(buf, ':');
    if (colon == NULL) {
        return is_valid_name(buf);
    } else {
        *colon = '\0';
        char *owner_part = buf;
        char *group_part = colon + 1;
        
        if (strchr(group_part, ':') != NULL) return 0;
        if (strlen(owner_part) == 0 && strlen(group_part) == 0) return 0;
        
        int valid = 1;
        if (strlen(owner_part) > 0) valid = valid && is_valid_name(owner_part);
        if (strlen(group_part) > 0) valid = valid && is_valid_name(group_part);
        return valid;
    }
}

int check_keys(const ParsedCommand *cmd, int *has_help_version, int *has_reference_key, int *has_no_dereference, int *key_end_index) {
    *has_help_version = 0;
    *has_reference_key = 0;
    *has_no_dereference = 0;
    *key_end_index = 1;
    
    while (*key_end_index < cmd->token_count) {
        const char *tok = cmd->tokens[*key_end_index];
        if (tok[0] == '-') {
            if (strcmp(tok, "-") == 0 || strcmp(tok, "--") == 0) {
                return 0;
            }
            if (strncmp(tok, "--", 2) == 0) {
                if (strcmp(tok, "--help") == 0 || strcmp(tok, "--version") == 0) {
                    *has_help_version = 1;
                } else if (strcmp(tok, "--no-dereference") == 0) {
                    *has_no_dereference = 1;
                } else if (strcmp(tok, "--changes") == 0 || strcmp(tok, "--silent") == 0 ||
                           strcmp(tok, "--quiet") == 0 || strcmp(tok, "--verbose") == 0 ||
                           strcmp(tok, "--dereference") == 0 ||
                           strcmp(tok, "--no-preserve-root") == 0 || strcmp(tok, "--preserve-root") == 0) {
                    // Валидные длинные ключи
                } else if (strncmp(tok, "--from=", 7) == 0) {
                    if (!validate_spec(tok + 7)) return 0;
                } else if (strncmp(tok, "--reference=", 12) == 0) {
                    if (strlen(tok) <= 12) return 0;
                    *has_reference_key = 1;
                } else {
                    return 0;
                }
            } else {
                int len = strlen(tok);
                for (int j = 1; j < len; j++) {
                    char c = tok[j];
                    if (c == 'h') {
                        *has_no_dereference = 1;
                    }
                    if (c != 'c' && c != 'f' && c != 'v' && c != 'h' && 
                        c != 'R' && c != 'H' && c != 'L' && c != 'P') {
                        return 0;
                    }
                }
            }
            (*key_end_index)++;
        } else {
            break;
        }
    }
    return 1;
}

int check_arguments(const ParsedCommand *cmd, int key_end_index, int has_help_version, int has_reference_key, int has_no_dereference) {
    if (has_help_version) return 1;
    int rem = cmd->token_count - key_end_index;
    if (has_reference_key) {
        return (rem >= 1);
    } else if (has_no_dereference && rem == 1) {
        return validate_spec(cmd->tokens[key_end_index]);
    } else {
        if (rem < 2) return 0;
        return validate_spec(cmd->tokens[key_end_index]);
    }
}

void load_stats_from_file(CheckStats *stats) {
    FILE *f = fopen(STATS_FILE, "r");
    int total_dummy;
    if (!f) {
        stats->success_count = 0;
        stats->failed_count = 0;
        return;
    }
    if (fscanf(f, "%d %d %d", &stats->success_count, &stats->failed_count, &total_dummy) != 3) {
        stats->success_count = 0;
        stats->failed_count = 0;
    }
    fclose(f);
}

void save_stats_to_file(const CheckStats *stats) {
    FILE *f = fopen(STATS_FILE, "w");
    if (f) {
        fprintf(f, "%d %d %d\n", stats->success_count, stats->failed_count, stats->success_count + stats->failed_count);
        fclose(f);
    }
}

int main() {
    char line[4096];
    CheckStats stats;
    
    while (fgets(line, sizeof(line), stdin)) {
        line[strcspn(line, "\r\n")] = '\0';
        
        if (strlen(line) == 0) continue;
        
        if (strcmp(line, "getstats") == 0) {
            load_stats_from_file(&stats);
            printf("%d %d %d\n", stats.success_count, stats.failed_count, stats.success_count + stats.failed_count);
            fflush(stdout);
            continue;
        }
        if (strcmp(line, "getsuccess") == 0) {
            load_stats_from_file(&stats);
            printf("%d\n", stats.success_count);
            fflush(stdout);
            continue;
        }
        if (strcmp(line, "getfailed") == 0) {
            load_stats_from_file(&stats);
            printf("%d\n", stats.failed_count);
            fflush(stdout);
            continue;
        }
        if (strcmp(line, "resetstats") == 0) {
            stats.success_count = 0;
            stats.failed_count = 0;
            save_stats_to_file(&stats);
            printf("\n");
            fflush(stdout);
            continue;
        }
        
        ParsedCommand cmd = parse_chown_command(line);
        int valid = check_command(&cmd);
        int has_help_version = 0, has_reference_key = 0, has_no_dereference = 0, key_end_index = 1;
        
        if (valid) {
            valid = check_keys(&cmd, &has_help_version, &has_reference_key, &has_no_dereference, &key_end_index);
        }
        if (valid) {
            valid = check_arguments(&cmd, key_end_index, has_help_version, has_reference_key, has_no_dereference);
        }
        
        load_stats_from_file(&stats);
        if (valid) {
            stats.success_count++;
            printf("True\n");
        } else {
            stats.failed_count++;
            printf("False\n");
        }
        save_stats_to_file(&stats);
        fflush(stdout);
    }
    return 0;
}