/
VirusVK
/
ServerIT
Обзор
Документация
Войти
/
VirusVK
/
ServerIT
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
armdb/src/import/csv.c
577 строк
19 KB
VirusVK
upload files
13 дек 2025, 14:28
13 дек 2025, 14:28
a626524
Код
Авторство
О чём код?
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <errno.h> #include "csv.h" #include "models.h" #include "storage.h" /* Вспомогательные функции */ static char *trim_string(char *str) { if (!str) return NULL; // Удаляем начальные пробелы while (*str && isspace((unsigned char)*str)) { str++; } if (*str == 0) return str; // Удаляем конечные пробелы char *end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) { end--; } *(end + 1) = '\0'; return str; } static char *unquote_string(char *str) { if (!str) return NULL; size_t len = strlen(str); if (len >= 2 && str[0] == '"' && str[len - 1] == '"') { str[len - 1] = '\0'; return str + 1; } return str; } /* Открытие CSV файла */ CSVReader *csv_open(const char *filename, char delimiter, bool has_header) { CSVReader *reader = calloc(1, sizeof(CSVReader)); if (!reader) return NULL; reader->file = fopen(filename, "r"); if (!reader->file) { free(reader); fprintf(stderr, "Ошибка открытия файла %s: %s\n", filename, strerror(errno)); return NULL; } reader->delimiter = delimiter; reader->has_header = has_header; reader->line_number = 0; // Пропускаем заголовок, если нужно if (has_header) { if (!fgets(reader->buffer, sizeof(reader->buffer), reader->file)) { fclose(reader->file); free(reader); return NULL; } reader->line_number++; } return reader; } /* Закрытие CSV файла */ void csv_close(CSVReader *reader) { if (!reader) return; if (reader->file) fclose(reader->file); free(reader); } /* Чтение строки из CSV */ char **csv_read_line(CSVReader *reader, int *field_count) { if (!reader || !reader->file) return NULL; // Читаем строку if (!fgets(reader->buffer, sizeof(reader->buffer), reader->file)) { return NULL; // Конец файла или ошибка } reader->line_number++; // Удаляем символ новой строки size_t len = strlen(reader->buffer); if (len > 0 && reader->buffer[len - 1] == '\n') { reader->buffer[len - 1] = '\0'; len--; } // Если строка пустая, пропускаем if (len == 0) { return csv_read_line(reader, field_count); } // Подсчитываем количество полей int count = 1; bool in_quotes = false; for (size_t i = 0; i < len; i++) { if (reader->buffer[i] == '"') { in_quotes = !in_quotes; } else if (reader->buffer[i] == reader->delimiter && !in_quotes) { count++; } } // Выделяем память под массив указателей char **fields = calloc(count, sizeof(char *)); if (!fields) return NULL; // Парсим поля int field_idx = 0; char *start = reader->buffer; in_quotes = false; for (size_t i = 0; i <= len; i++) { if (i == len || (!in_quotes && reader->buffer[i] == reader->delimiter)) { int field_len = &reader->buffer[i] - start; // Выделяем память под поле fields[field_idx] = malloc(field_len + 1); if (!fields[field_idx]) { // Очищаем память в случае ошибки for (int j = 0; j < field_idx; j++) free(fields[j]); free(fields); return NULL; } // Копируем поле strncpy(fields[field_idx], start, field_len); fields[field_idx][field_len] = '\0'; // Обрабатываем кавычки и пробелы char *processed = trim_string(fields[field_idx]); processed = unquote_string(processed); // Если поле начиналось с кавычек, сдвигаем указатель if (processed != fields[field_idx]) { memmove(fields[field_idx], processed, strlen(processed) + 1); } field_idx++; start = &reader->buffer[i + 1]; } else if (reader->buffer[i] == '"') { in_quotes = !in_quotes; } } *field_count = count; return fields; } /* Освобождение полей */ void csv_free_fields(char **fields, int count) { if (!fields) return; for (int i = 0; i < count; i++) { if (fields[i]) free(fields[i]); } free(fields); } /* Импорт АРМ из CSV */ int csv_import_arms(Database *db, const char *filename, char delimiter) { if (!db || !filename) return -1; CSVReader *reader = csv_open(filename, delimiter, true); if (!reader) { fprintf(stderr, "Не удалось открыть файл %s\n", filename); return -1; } printf("Начало импорта АРМ из файла: %s\n", filename); int imported_count = 0; int line_number = 0; while (1) { int field_count; char **fields = csv_read_line(reader, &field_count); if (!fields) break; line_number++; // Минимально необходимое количество полей if (field_count < 6) { fprintf(stderr, "Ошибка в строке %d: недостаточно полей (нужно минимум 6, получено %d)\n", line_number, field_count); csv_free_fields(fields, field_count); continue; } // Создаем запись АРМ ArmRecord arm; memset(&arm, 0, sizeof(ArmRecord)); // Заполняем поля (предполагаемый формат CSV) // 0: inventory_number, 1: name, 2: type, 3: network_name, // 4: ip_address, 5: location, 6: disk1, 7: disk2, 8: notes if (field_count > 0 && fields[0]) { strncpy(arm.inventory_number, fields[0], sizeof(arm.inventory_number) - 1); } if (field_count > 1 && fields[1]) { strncpy(arm.name, fields[1], sizeof(arm.name) - 1); } if (field_count > 2 && fields[2]) { strncpy(arm.type, fields[2], sizeof(arm.type) - 1); } if (field_count > 3 && fields[3]) { strncpy(arm.network_name, fields[3], sizeof(arm.network_name) - 1); } if (field_count > 4 && fields[4]) { strncpy(arm.ip_address, fields[4], sizeof(arm.ip_address) - 1); } if (field_count > 5 && fields[5]) { strncpy(arm.location, fields[5], sizeof(arm.location) - 1); } if (field_count > 6 && fields[6] && strlen(fields[6]) > 0) { strncpy(arm.disks[0].model, fields[6], sizeof(arm.disks[0].model) - 1); } if (field_count > 7 && fields[7] && strlen(fields[7]) > 0) { strncpy(arm.disks[1].model, fields[7], sizeof(arm.disks[1].model) - 1); } if (field_count > 8 && fields[8]) { strncpy(arm.notes, fields[8], sizeof(arm.notes) - 1); } // Устанавливаем временные метки arm.created_at = now(); arm.updated_at = arm.created_at; // Создаем ID arm.id = create_arm_id(db->header.last_object_id[0] + 1); // Сохраняем в базу данных if (arm_insert(db, &arm, NULL) == 0) { imported_count++; if (imported_count % 100 == 0) { printf("Импортировано %d записей...\n", imported_count); } } else { fprintf(stderr, "Ошибка при сохранении АРМ из строки %d\n", line_number); } csv_free_fields(fields, field_count); } csv_close(reader); printf("Импорт завершен. Успешно импортировано %d записей АРМ\n", imported_count); // Создаем запись в журнале аудита AuditRecord audit; memset(&audit, 0, sizeof(AuditRecord)); audit.object_type = OBJECT_ARM; audit.operation = OP_IMPORT; audit.timestamp = now(); snprintf(audit.comment, sizeof(audit.comment), "Импорт из CSV файла: %s, записей: %d", filename, imported_count); // TODO: сохранить запись аудита return imported_count; } /* Импорт пользователей из CSV */ int csv_import_users(Database *db, const char *filename, char delimiter) { if (!db || !filename) return -1; CSVReader *reader = csv_open(filename, delimiter, true); if (!reader) { fprintf(stderr, "Не удалось открыть файл %s\n", filename); return -1; } printf("Начало импорта пользователей из файла: %s\n", filename); int imported_count = 0; int line_number = 0; while (1) { int field_count; char **fields = csv_read_line(reader, &field_count); if (!fields) break; line_number++; if (field_count < 3) { fprintf(stderr, "Ошибка в строке %d: недостаточно полей (нужно минимум 3, получено %d)\n", line_number, field_count); csv_free_fields(fields, field_count); continue; } // Создаем запись пользователя UserRecord user; memset(&user, 0, sizeof(UserRecord)); // Предполагаемый формат CSV: // 0: username, 1: full_name, 2: phone, 3: notes if (field_count > 0 && fields[0]) { strncpy(user.username, fields[0], sizeof(user.username) - 1); } if (field_count > 1 && fields[1]) { strncpy(user.full_name, fields[1], sizeof(user.full_name) - 1); } if (field_count > 2 && fields[2]) { strncpy(user.phone, fields[2], sizeof(user.phone) - 1); } if (field_count > 3 && fields[3]) { strncpy(user.notes, fields[3], sizeof(user.notes) - 1); } // Устанавливаем временные метки user.created_at = now(); user.updated_at = user.created_at; // Создаем ID user.id = create_user_id(db->header.last_object_id[1] + 1); // Сохраняем в базу данных if (user_insert(db, &user, NULL) == 0) { imported_count++; if (imported_count % 100 == 0) { printf("Импортировано %d записей...\n", imported_count); } } else { fprintf(stderr, "Ошибка при сохранении пользователя из строки %d\n", line_number); } csv_free_fields(fields, field_count); } csv_close(reader); printf("Импорт завершен. Успешно импортировано %d пользователей\n", imported_count); return imported_count; } /* Импорт подразделений из CSV */ int csv_import_departments(Database *db, const char *filename, char delimiter) { if (!db || !filename) return -1; CSVReader *reader = csv_open(filename, delimiter, true); if (!reader) { fprintf(stderr, "Не удалось открыть файл %s\n", filename); return -1; } printf("Начало импорта подразделений из файла: %s\n", filename); int imported_count = 0; int line_number = 0; while (1) { int field_count; char **fields = csv_read_line(reader, &field_count); if (!fields) break; line_number++; if (field_count < 1) { fprintf(stderr, "Ошибка в строке %d: недостаточно полей\n", line_number); csv_free_fields(fields, field_count); continue; } // Создаем запись подразделения DepartmentRecord department; memset(&department, 0, sizeof(DepartmentRecord)); // Формат: 0: name, 1: notes if (field_count > 0 && fields[0]) { strncpy(department.name, fields[0], sizeof(department.name) - 1); } if (field_count > 1 && fields[1]) { strncpy(department.notes, fields[1], sizeof(department.notes) - 1); } // Устанавливаем временные метки department.created_at = now(); // Создаем ID department.id = create_department_id(db->header.last_object_id[2] + 1); // Сохраняем в базу данных if (department_insert(db, &department, NULL) == 0) { imported_count++; } else { fprintf(stderr, "Ошибка при сохранении подразделения из строки %d\n", line_number); } csv_free_fields(fields, field_count); } csv_close(reader); printf("Импорт завершен. Успешно импортировано %d подразделений\n", imported_count); return imported_count; } /* Экспорт АРМ в CSV */ int csv_export_arms(Database *db, const char *filename, char delimiter) { if (!db || !filename) return -1; FILE *file = fopen(filename, "w"); if (!file) { fprintf(stderr, "Не удалось создать файл %s: %s\n", filename, strerror(errno)); return -1; } // Заголовок CSV fprintf(file, "inventory_number%cname%ctype%cnetwork_name%cip_address%clocation%cdisk1%cdisk2%cnotes\n", delimiter, delimiter, delimiter, delimiter, delimiter, delimiter, delimiter, delimiter); // Получаем все записи АРМ ArmRecord *arms = NULL; int count = 0; if (arm_list_all(db, &arms, &count) != 0) { fclose(file); return -1; } // Экспортируем каждую запись for (int i = 0; i < count; i++) { ArmRecord *arm = &arms[i]; // Пропускаем удаленные записи if (arm->is_deleted) continue; // Экранируем поля при необходимости char *inv_num_escaped = csv_escape_string(arm->inventory_number); char *name_escaped = csv_escape_string(arm->name); char *type_escaped = csv_escape_string(arm->type); char *net_name_escaped = csv_escape_string(arm->network_name); char *ip_escaped = csv_escape_string(arm->ip_address); char *loc_escaped = csv_escape_string(arm->location); char *disk1_escaped = csv_escape_string(arm->disks[0].model); char *disk2_escaped = csv_escape_string(arm->disks[1].model); char *notes_escaped = csv_escape_string(arm->notes); fprintf(file, "%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s\n", inv_num_escaped, delimiter, name_escaped, delimiter, type_escaped, delimiter, net_name_escaped, delimiter, ip_escaped, delimiter, loc_escaped, delimiter, disk1_escaped, delimiter, disk2_escaped, delimiter, notes_escaped); free(inv_num_escaped); free(name_escaped); free(type_escaped); free(net_name_escaped); free(ip_escaped); free(loc_escaped); free(disk1_escaped); free(disk2_escaped); free(notes_escaped); } free(arms); fclose(file); printf("Экспорт завершен. Экспортировано %d записей АРМ в файл %s\n", count, filename); return count; } /* Экспорт пользователей в CSV */ int csv_export_users(Database *db, const char *filename, char delimiter) { if (!db || !filename) return -1; FILE *file = fopen(filename, "w"); if (!file) { fprintf(stderr, "Не удалось создать файл %s: %s\n", filename, strerror(errno)); return -1; } // Заголовок fprintf(file, "username%cfull_name%cphone%cnotes\n", delimiter, delimiter, delimiter); // TODO: получить пользователей из БД и экспортировать fclose(file); return 0; } /* Экранирование строк для CSV */ char *csv_escape_string(const char *str) { if (!str) { char *result = malloc(1); if (result) result[0] = '\0'; return result; } // Проверяем, нужно ли экранирование bool needs_quoting = false; const char *p = str; while (*p) { if (*p == '"' || *p == delimiter || *p == '\n' || *p == '\r') { needs_quoting = true; break; } p++; } if (!needs_quoting) { return strdup(str); } // Экранируем строку size_t len = strlen(str); size_t escaped_len = len + 2; // для кавычек в начале и конце // Считаем количество кавычек, которые нужно удвоить for (size_t i = 0; i < len; i++) { if (str[i] == '"') escaped_len++; } char *escaped = malloc(escaped_len + 1); if (!escaped) return NULL; char *dest = escaped; *dest++ = '"'; for (size_t i = 0; i < len; i++) { if (str[i] == '"') { *dest++ = '"'; *dest++ = '"'; } else { *dest++ = str[i]; } } *dest++ = '"'; *dest = '\0'; return escaped; }