Last active
June 3, 2018 03:06
-
-
Save brendonhc/605f59d814cd1a7952a86b91ddac84d5 to your computer and use it in GitHub Desktop.
Trabalho 2 da disciplina Algoritmos e estrutura de dados 2 - ICMC - USP - 2018
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Implementação de um modelo simples de banco de dados com leitura e escrita | |
* de arquivos em disco para um trabalho da disciplina Algoritmos e Estrutura | |
* De dados 2. | |
* ICMC - USP - 2018 | |
* Professor: João Batista | |
* | |
* Autor: Brendon Hudson | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#define TAMREG 84 | |
#define NDX_LINE_SIZE 10 // XYZ|NNNNN'\n' | |
#define PKEY 0 // Indice da chave primária na tabela da estrutura indextable_t | |
#define RRN 1 // Indice do RRN na tabela da estrutura indextable_t | |
#define NAME_SIZE 40 | |
#define CAR_NAME_SIZE 20 | |
#define LINE_MAX_SIZE 100 | |
typedef unsigned bool_t; | |
#define TRUE 1 | |
#define FALSE 0 | |
#define SUCCESS_OP 1 | |
#define FAILED_OP -1 | |
#define OUT_OF_DATE_STR "-OutOfDate\0" | |
/* Enumeração das opções utilizadas no menu "database_driver" */ | |
enum OPERATION { | |
INSERT = 1, REMOVE, CHANGE, SEARCH, COMPACT, EXIT | |
}; | |
/** | |
* Estrutura para subir o arquivo de indices primários para serem manipulados | |
* na memória principal. | |
*/ | |
struct indextable_s { | |
char *path; | |
char *_flag; | |
int **index; | |
size_t size; | |
}; | |
typedef struct indextable_s * indextable_t; | |
struct database_s { | |
char *path; | |
char *_flag; // indica se seu respectivo arquivo em disco está compactado | |
int next_offset; | |
size_t size; | |
indextable_t ndx; | |
}; | |
typedef struct database_s * database_t; | |
/** cmp | |
* Função auxiliar para ordenar uma tabela int[][] (int **) em ordem crescente | |
* de sua primeira posição int[0][x] usando a função 'qsort' da stdlib.h. | |
* Parâmetros: | |
* const void *a: endereço do primeiro número | |
* const void *b: endereço do segundo número | |
* Retorno: 1 se a > b; -1 se a < b; 0 se a == b | |
*/ | |
int cmp(const void *a, const void *b) | |
{ | |
int m = *(*((int**)a)); | |
int n = *(*((int**)b)); | |
if (m > n) return 1; | |
else if (n > m) return -1; | |
return 0; | |
} | |
/** trim ** | |
* Funcao auxiliar que percorre uma dada string de trás para frente eliminando | |
* os espacos em branco. | |
* Parâmetros: | |
* char *str - String que será manipulada | |
* | |
* Retorno: | |
* char * - String dada sem espaços em branco | |
*/ | |
char *trim(char *str) | |
{ | |
int i; | |
for (i = strlen(str)-1; str[i] == ' '; i--); | |
str[i+1] = '\0'; | |
return str; | |
} | |
/** flelinha ** | |
* Função implementada pelo professor "João Batista" da disciplina SCC 0503 | |
* para ler as linhas da tabela de informações inicial no formato que foi | |
* definido por ele mesmo e passando as informações pertinentes em cada uma | |
* devolta pelos ponteiros. | |
* Parêmetros: | |
* FILE *fp - | |
* char *number - | |
* char *name - | |
* char *car - | |
*/ | |
void flelinha(FILE *fp, char *number, char *name, char *car) | |
{ | |
char c; | |
int i; | |
// pula "|" inicial | |
c = fgetc(fp); | |
// enquanto houver digitos vai lendo e armazena no number | |
i = 0; | |
while ((c = fgetc(fp)) != ' ') | |
number[i++] = c; | |
number[i] = '\0'; | |
// vai pulando até chegar no name... | |
while ((c = fgetc(fp)) != '|'); | |
// agora le o name | |
for (i=0; i<NAME_SIZE;i++) | |
name[i] = fgetc(fp); | |
name[i] = '\0'; | |
// elimina os brancos do lado direito... | |
name = trim(name); | |
// pula "|" | |
c = fgetc(fp); | |
// agora le o car | |
for (i=0; i<CAR_NAME_SIZE;i++) | |
car[i] = fgetc(fp); | |
car[i] = '\0'; | |
// elimina os brancos do lado direito... | |
car = trim(car); | |
// pula "|" | |
c = fgetc(fp); | |
// pula "\n" | |
c = fgetc(fp); | |
} | |
/** exception | |
* Função que simula uma exceção genérica imprimindo na saída de erro padrão | |
* a mensagem passada por parâmetro na tela e encerrando o programa. | |
* Parâmetros: | |
* char *msg: String com mensagem a ser escrita na saída de erro padrão | |
*/ | |
void exception(char *msg) | |
{ | |
fprintf(stderr, "%s\n", msg); | |
exit(EXIT_FAILURE); | |
} | |
/** fduplicate | |
* Função auxiliar que copia exatamente o conteudo de um arquivo em disco | |
*para outro. | |
*/ | |
void fduplicate(const char *src_path, const char *dest_path) | |
{ | |
FILE *src_fp, *dest_fp; | |
src_fp = fopen(src_path, "r"); | |
dest_fp = fopen(dest_path, "w"); | |
while (!feof(src_fp)) { | |
fputc(fgetc(src_fp), dest_fp); | |
} | |
fclose(src_fp); | |
fclose(dest_fp); | |
} | |
/** indexes_out_of_date | |
* Função que cria arquivo indicando que a tabela de indices em disco difere | |
* dos dados contidos na base de dados em disco, e, dentro do arquivo, um | |
* inteiro correspondente ao número de registros válidos na base de dados, caso | |
* seja necessário para uma posterior recuperação. */ | |
void indexes_out_of_date(database_t db) | |
{ | |
FILE *fp = fopen(db->ndx->_flag, "w"); | |
fprintf(fp, "%lu", db->size); | |
fclose(fp); | |
} | |
/** database_not_compacted | |
* Função que cria um arquivo indicando que o arquivo em disco da base de dados | |
* não está compactado, ou seja, possui espaços vazios em seu corpo não | |
* utilizados. | |
*/ | |
void database_not_compacted(database_t db) | |
{ | |
fclose(fopen(db->_flag, "w")); | |
} | |
/** create_database_with_file_table ** | |
* Função que cria um arquivo de banco de dados formatado no padrão do tipo | |
* "database_t" e sua respectiva tabela de indices primários a partir de uma | |
* tabela de informações com um formato pré-definido. | |
* Parâmetros: | |
* char *table_name - caminho para a tabela de informações com formato | |
* pré-definido. | |
*/ | |
void create_database_with_file_table(char *table_path, char *database_path, | |
char *index_path) | |
{ | |
FILE *input_fp, *database_fp, *indexes_fp; | |
char str[LINE_MAX_SIZE], number[4], name[NAME_SIZE+1], car[CAR_NAME_SIZE+1]; | |
int i, n_registers, offset = 0; // numero de registros.... | |
int **sort_ndx; | |
// PRIMEIRO | |
// Abertura e criação dos arquivos no disco | |
input_fp = fopen(table_path, "r"); | |
if (!input_fp) { | |
exception("Falha ao abrir tabela inicial"); | |
} | |
database_fp = fopen(database_path, "w"); | |
if (!database_fp) { | |
exception("Falha ao criar arquivo da base de dados"); | |
} | |
indexes_fp = fopen(index_path, "w"); | |
if (!indexes_fp) { | |
exception("Falha ao criar arquivo de indices primarios"); | |
} | |
// SEGUNDO | |
// Leitura da tabela inicial e escrita nos arquivos de dados | |
// Lê o número de registros | |
fscanf(input_fp, "nro de registros = %d\n", &n_registers); | |
// Pula as outras duas linhas iniciais que sao comentarios... | |
fgets(str,LINE_MAX_SIZE,input_fp); | |
fgets(str,LINE_MAX_SIZE,input_fp); | |
// Cria base de dados com as linhas no seguinte formato: | |
// indice|name|car'\n' | |
//sort_ndx == tabela de registros em ram para ordena-la | |
sort_ndx = (int **) malloc(n_registers*sizeof(int *)); | |
for (i = 0; i < n_registers; i++) { | |
flelinha(input_fp, number, name, car); | |
sort_ndx[i] = (int *) malloc(2*sizeof(int)); | |
// offset recebe a quandidade de caracteres escritos pelo fprintf | |
// para guardar a posição do próximo registro livre para os índices | |
sort_ndx[i][PKEY] = atoi(number); | |
sort_ndx[i][RRN] = offset; | |
offset += fprintf(database_fp, "%s|%s|%s\n", number, name, car); | |
} | |
// Ordena a tabela de indices primária antes de escreve-la no disco | |
qsort(sort_ndx, (size_t) n_registers, sizeof(int *), cmp); | |
for (int i = 0; i < n_registers; i++) { | |
fprintf(indexes_fp,"%03d|%05d\n", sort_ndx[i][PKEY], sort_ndx[i][RRN]); | |
} | |
// push_indexes(tmp_ndx); | |
fclose(input_fp); | |
fclose(database_fp); | |
fclose(indexes_fp); | |
} | |
/** exists_file ** | |
* Função que retorna TRUE se existir o arquivo procurado ou retorna FALSE | |
* se o arquivo não existir. | |
* Parâmetros: | |
* char *file_path - String com o caminho do arquivo procurado | |
* Retorno: | |
* bool_t - Inteiro 1 ou 0 que representam respectivamente os booleanos | |
* TRUE (verdadeiro) ou FALSE (falso). | |
*/ | |
bool_t exists_file(char *file_path) | |
{ | |
FILE *fp = NULL; | |
fp = fopen(file_path, "r"); | |
if (fp) { | |
fclose(fp); | |
return TRUE; | |
} | |
return FALSE; | |
} | |
/* database_update_ram_indexes | |
* (Só pode ser chamada se a base de dados em disco estiver compactada) | |
* Função que atualiza a estrutura de indices em ram a partir do arquivo | |
* da base de dados em disco, se, verificar que ambos em disco diferem | |
*/ | |
void database_update_ram_indexes(database_t db) | |
{ | |
FILE *database_fp = NULL, *flag_fp = NULL; | |
int key = 0; | |
int i = 0, offset = 0; | |
// Se não existir a flag em disco, já está atualizado, return. | |
if (!exists_file(db->ndx->_flag)) {return;} | |
// Se a base de dados não está compactada, não faço o update | |
if (exists_file(db->_flag)) { | |
exception("Não foi possível atualizar os indices em ram.\n Base de dados em disco não compactada"); | |
} | |
// Abertura da base de dados em disco e flag com o número de registros válidos | |
database_fp = fopen(db->path, "r"); | |
if (!database_fp) { | |
exception("Falha ao abrir arquivo da base de dados"); | |
} | |
flag_fp = fopen(db->ndx->_flag, "r"); | |
if (!flag_fp) { | |
exception("Falha ao abrir arquivo sinalizador c/ numero de registros"); | |
} | |
// Quantidade de registros válidos na base de dados | |
fscanf(flag_fp, "%lu", &db->size); | |
fclose(flag_fp); | |
db->ndx->size = db->size; | |
// Leitura do arquivo da base de dados em disco e upload para ram | |
db->ndx->index = realloc(db->ndx->index, db->size*sizeof(int *)); | |
for (i = 0; i < db->size; i++) { | |
db->ndx->index[i] = (int *) malloc(2*sizeof(int)); | |
fscanf(database_fp, "%d", &key); | |
db->ndx->index[i][RRN] = offset; | |
db->ndx->index[i][PKEY] = key; | |
// while consome os caracteres até o fim de cada linha | |
while (fgetc(database_fp) != '\n'); | |
offset += ftell(database_fp); | |
} | |
fclose(database_fp); | |
db->next_offset = offset; | |
// Ordena a tabela de indices primária antes de escreve-la no disco | |
qsort(db->ndx->index, db->size, sizeof(int *), cmp); | |
} | |
/** database_update_file_indexes | |
* Função que verifica se o arquivo de indices contido em disco está | |
* desatualizado pela existência ou não de um arquivo de verificação com nome | |
* ".arquivo.ndx-outOfDate", caso exista, é feita atualização do "arquivo.ndx" | |
* e exclusão do arquivo de verificação. | |
* Parâmetros: | |
* ndx - Estrutura da tabela de indices na ram, qual será usada para | |
* atualizar sua correspondente em disco. | |
*/ | |
void database_update_file_indexes(database_t db) | |
{ | |
FILE *fp = NULL; | |
size_t i; | |
/* Se exists_file retornar "FALSE", o arquivo de flag não existe, assim, | |
não é necessário atualizar os indices*/ | |
if (!exists_file(db->ndx->_flag)) { return; } | |
fp = fopen(db->ndx->path, "w"); | |
if (!fp) exception("Problemas em abrir ndx para atualização dos indices"); | |
// Sobrescrevo, linha por linha, as alterações feitas na tabela de indices | |
for (i = 0; i < db->ndx->size; i++) { | |
fprintf(fp, "%03d|%05d\n", db->ndx->index[i][PKEY], db->ndx->index[i][RRN]); | |
} | |
if (remove(db->ndx->_flag) == -1) { | |
exception("Problemas ao apagar o arquivo sinalizador de atualização"); | |
} | |
fclose(fp); | |
} | |
/** database_init_indexes | |
* Função que inicializa e retorna uma estrutura de indices primários na ram | |
* a partir de seu caminho no disco passado por parâmetro. | |
* Parâmetros: | |
* char *path: caminho para o arquivo no disco | |
*/ | |
void database_init_indexes(database_t db, char *path) | |
{ | |
FILE *fp; | |
size_t size = 0, i, j; | |
char line_counter[NDX_LINE_SIZE]; | |
db->ndx = (indextable_t) malloc(sizeof(struct indextable_s)); | |
if (!db->ndx) { exception("Problemas na alocação de memória para ndx");} | |
db->ndx->path = path; | |
// Gerando o nome do arquivo que sinalizará quando as chaves em disco | |
// estiverem desatualizadas | |
size = strlen(db->ndx->path) + strlen(OUT_OF_DATE_STR) + 1; | |
db->ndx->_flag = (char *) malloc(sizeof(char)*size); | |
db->ndx->_flag[0] = '.'; // '.' oculta arquivo nas distribuições linux | |
// ndx->_flag = "." + "nomeDoArquivo.ndx" + OUT_OF_DATE_STR | |
for (i = 1; i <= strlen(db->ndx->path); i++) { | |
db->ndx->_flag[i] = db->ndx->path[i-1]; | |
} | |
for (i = i, j = 0; i < size; i++, j++) { | |
db->ndx->_flag[i] = OUT_OF_DATE_STR[j]; | |
} | |
size = 0; | |
// Verifico se o arquivo de indices em disco condiz com a base de dados | |
if (exists_file(db->ndx->_flag)) { // Se existir, inicio a recuperação | |
fprintf(stderr, "Tabela de indices em disco incompatível com base de dados\n"); | |
fprintf(stderr, "Recuperando tabela de indices...\n"); | |
database_update_ram_indexes(db); | |
//database_update_file_indexes(db); | |
return; | |
} | |
// Obtendo o tamanho do arquivo de indicesold_path | |
fp = fopen(db->ndx->path, "r"); | |
while (fscanf(fp, "%s", line_counter) > 0) { | |
size++; | |
} | |
db->size = size; | |
db->ndx->size = size; | |
fseek(fp, 0, SEEK_SET); // Cabeça de leitura no início | |
db->ndx->index = (int **) malloc(db->ndx->size*sizeof(int *)); | |
if (!db->ndx->index) { | |
exception("Problemas na alocação de memória para ndx->index"); | |
} | |
// Subindo a tabela de indices para memória principal | |
for (i = 0; i < db->ndx->size; i++) { | |
db->ndx->index[i] = (int *) malloc(2*sizeof(int)); // 2 colunas | |
if (!db->ndx->index[i]) { | |
exception("Erro na alocação de memória para ndx->index[i]"); | |
} | |
fscanf(fp, "%d|%d", &db->ndx->index[i][PKEY], &db->ndx->index[i][RRN]); | |
} | |
fclose(fp); | |
} | |
/** init_database ** | |
* Função que inicializa um banco de dados a partir de seu arquivo base e | |
* sobe sua tabela de índices primários na memória principal. | |
* Parâmetros: | |
* char *path_database_file - Caminho para o arquivo do banco de dados | |
* char *path_index_file - Caminho para a tabela de indices do banco | |
* Retorno: | |
* database_t - uma entidade contendo a base de dados inicializada | |
*/ | |
database_t init_database(char *path_database_file, char *path_index_file) | |
{ | |
int i; | |
FILE *database_fp = NULL; | |
database_t db = (database_t) malloc(sizeof(struct database_s)); | |
if (!db) exception("Problema na alocação de memória da base de dados"); | |
db->path = path_database_file; | |
database_fp = fopen(db->path, "r"); | |
if (!database_fp) exception("Problema na inicialização da base de dados"); | |
fseek(database_fp, 0, SEEK_END); | |
db->next_offset = ftell(database_fp); | |
fclose(database_fp); | |
// nome para arquivo sinalizador de base de dados não compactada | |
i = strlen(db->path) + 1; | |
db->_flag = (char *) malloc(sizeof(char)*(i + 5)); | |
db->_flag[0] = '.'; // arquivo oculto no linux | |
strcpy(&(db->_flag[1]), db->path); | |
db->_flag[i++] = '.'; | |
db->_flag[i++] = 't'; | |
db->_flag[i++] = 'm'; | |
db->_flag[i++] = 'p'; | |
db->_flag[i] = '\0'; | |
database_init_indexes(db, path_index_file); | |
return db; | |
} | |
/** database_compact | |
* Função que compacta o arquivo da base de dados em disco removendo relmente | |
* os registros marcados como apagados com o caracter '#' e enfim, recalculando | |
* os valores de offset para cada chave na estrutura de indices. | |
*/ | |
void database_compact(database_t db) | |
{ | |
FILE *old_fp, *new_fp; | |
char c; | |
// Se não existe o arquivo que indica a não compactação, finaliza. | |
if (!exists_file(db->_flag)) {return;} | |
/*Passo tudo para o arquivo antigo temporario*/ | |
fduplicate(db->path, db->_flag); | |
/*Depois de duplicado, copio do temporário só os dados não apagados */ | |
new_fp = fopen(db->path, "w"); | |
old_fp = fopen(db->_flag, "r"); | |
while ((c = fgetc(old_fp)) != EOF ) { | |
if (c == '#') { // Se for um registro apagado, pula ele | |
while (fgetc(old_fp) != '\n'); | |
} | |
else { // Escreve o resto no arquivo novo | |
fputc(c, new_fp); | |
} | |
} | |
fclose(new_fp); | |
fclose(old_fp); | |
// Apago a flag, indicando que agora está compactado | |
remove(db->_flag); | |
/* Com a base de dados em disco atualizada, atualizo os "RRN's" (offsets)*/ | |
database_update_ram_indexes(db); | |
} | |
/** database_search | |
* Função que realiza a busca de uma determinada informação dentro da base | |
* de dados do tipo "database_t" por sua respectiva chave utilizando algoritmo | |
* de busca binária. | |
* Parâmetros: | |
* - db: Estrutura que representa o banco de dados inicializado | |
* - target: Chave da informação buscada | |
* Retorno: | |
* O índice do registro se encontrado, se não, índice próximo negativo | |
*/ | |
int database_search(database_t db, int target) | |
{ | |
int left = 0, right = db->size-1, mid; | |
// Busca binária iterativa, mais rápida que a recursiva | |
while (left <= right) { | |
mid = (left + right) / 2; | |
if (db->ndx->index[mid][PKEY] > target) { | |
right = mid - 1; | |
} | |
else if (db->ndx->index[mid][PKEY] < target) { | |
left = mid + 1; | |
} | |
else { | |
return mid; | |
} | |
} | |
return mid == 0 ? -1 : -1*mid; | |
} | |
/** database_insert | |
* Função que insere um registro na base de dados. | |
* Parâmetros: | |
* campos | |
*/ | |
int database_insert(database_t db, int n, char *name, char *car) | |
{ | |
FILE *fp; | |
int index = database_search(db, n), i; | |
int *new; | |
if (index >= 0) { | |
return FAILED_OP; | |
} | |
db->ndx->size++; | |
db->size = db->ndx->size; | |
db->ndx->index = realloc(db->ndx->index, db->size*sizeof(int *)); | |
new = (int *) malloc(sizeof(int)*2); | |
new[PKEY] = n; | |
new[RRN] = db->next_offset; | |
/* quando a busca não encontra, ela retorna o vizinho negativo, onde | |
* poderia estar a chave caso a encontrasse, assim sendo, podemos usar | |
* esse retorno a nosso favor: */ | |
index = abs(index); // modulo, ou, valor absoluto | |
// Insere em cima (desce todos a partir de "index" um para baixo) | |
if (db->ndx->index[index][PKEY] > n) { | |
for (i = db->size-1; i > index; i--) { | |
db->ndx->index[i] = db->ndx->index[i-1]; | |
} | |
db->ndx->index[i] = new; | |
} | |
// Insere em baixo (depois) - avança uma posição todos depois de "index" | |
else if (db->ndx->index[index][PKEY] < n) { | |
for (i = db->size-1; i > index + 1; i--) { | |
db->ndx->index[i] = db->ndx->index[i-1]; | |
} | |
db->ndx->index[i] = new; | |
} | |
// Atualizo a base de dados em disco inserindo no final | |
fp = fopen(db->path, "a"); | |
db->next_offset += fprintf(fp, "%03d|%s|%s\n", n, name, car); | |
fclose(fp); | |
// Agora a base de dados e a tabela de indices em disco diferem -> flag | |
indexes_out_of_date(db); | |
return SUCCESS_OP; | |
} | |
/** database_remove | |
* Função que remove um registro na base de dados. | |
* Parâmetros: | |
* campos | |
*/ | |
int database_remove(database_t db, int n) | |
{ | |
FILE *fp; | |
int index = database_search(db, n), i; | |
if (index < 0) { | |
return FAILED_OP; | |
} | |
// Index agora tem a posição do registro na tabela de indices | |
// Com o offset dele (RRN), podemos remove-lo da base de dados em disco | |
fp = fopen(db->path, "r+"); | |
rewind(fp); | |
fseek(fp, db->ndx->index[index][RRN], SEEK_SET); | |
fputc('#', fp); // caracter que indica que a informação foi removida | |
fclose(fp); | |
// Agora é necessário re-organizar os indices na ram | |
// Subo todos os elementos uma casa para mais próximo da primeira posição | |
db->size--; | |
db->ndx->size--; | |
for (i = index; i < db->size; i++) { | |
db->ndx->index[i] = db->ndx->index[i+1]; | |
} | |
// A base de dados e a tabela de indices em disco diferem, crio uma flag | |
indexes_out_of_date(db); | |
database_not_compacted(db); | |
return SUCCESS_OP; | |
} | |
/** database_change | |
* Função que manipula as alterações dos registros na base de dados. | |
* Para melhor eficiencia, a alteração segue o seguinte algoritmo: | |
* Primeiro verifica se o indice existe, se sim, qual campo será alterado, | |
* a partir daí, guarda-se os campos que mantém-se o valor, remove o antigo | |
* e adiciona um novo registro com os novos valores, assim, há menos | |
* manipulação em disco para calcular novos offsets, e mais e ram para | |
* organizar a estrutura que contém a tabela de indices. | |
*/ | |
void database_change(database_t db, int key) | |
{ | |
FILE *fp; | |
int index = database_search(db, key), field, num, offset_bkp, i; | |
char str1[NAME_SIZE], str2[CAR_NAME_SIZE]; | |
char c; | |
printf("key = %d index = %d\n", key, index); | |
if (index < 0) { | |
printf("Erro! Número de registro inexistente na base de dados."); | |
return; | |
} | |
// Se existir, capturar qual campo será alterado | |
printf("Escolha o campo que deseja alterar:\n"); | |
printf("(1) - Número\t(2) - Nome\t(3) - Carro\n"); | |
scanf("%d", &field); | |
printf("Digite o novo valor: "); | |
offset_bkp = db->ndx->index[index][RRN]; | |
fp = fopen(db->path, "r+"); | |
// Primeiro guardo as informações antigas do registro | |
fseek(fp, offset_bkp, SEEK_SET); | |
fscanf(fp, "%d", &index); // Leitura da chave | |
fgetc(fp); // Para consumir o primeiro delimitador '|' | |
for (i = 0; i < NAME_SIZE; i++) { // Leitura do nome | |
c = fgetc(fp); | |
if (c == '|') { | |
str1[i] = '\0'; | |
break; | |
} | |
str1[i] = c; | |
} | |
for (i = 0; i < CAR_NAME_SIZE; i++) { // Leitura do carro | |
c = fgetc(fp); | |
if (c == '\n') { | |
str2[i] = '\0'; | |
break; | |
} | |
str2[i] = c; | |
} | |
fclose(fp); | |
switch (field) { | |
case 1: // Alteração da "key" | |
// Busca pelo numero, se não existir, sobreescreve o desejado | |
scanf("%d", &num); | |
if (database_search(db, num) > 0) { | |
fprintf(stderr, "Não foi possível realizar alteração. Número"); | |
fprintf(stderr, " duplicado.\n"); | |
} else { | |
database_remove(db, index); | |
database_insert(db, num, str1, str2); | |
} | |
break; | |
case 2: // Alteração do Nome | |
scanf("%s", str1); | |
database_remove(db, index); | |
database_insert(db, index, str1, str2); | |
break; | |
case 3: // Alteração do Carro | |
scanf("%s", str2); | |
database_remove(db, index); | |
database_insert(db, index, str1, str2); | |
break; | |
} | |
} | |
/** database_driver | |
* Função que oferece ao usuário do programa no console, comandos para | |
* gerenciar o banco de dados, como: inserção, busca, remoção, etc. | |
*/ | |
bool_t database_driver(database_t db) | |
{ | |
bool_t _exit = FALSE; | |
int operation; | |
// Variaveis para argumento das funções do Menu | |
int arg1; | |
char arg2[NAME_SIZE], arg3[CAR_NAME_SIZE]; | |
printf("Menu de gerenciamento\nBase de dados: %s\n", db->path); | |
while (!_exit) { | |
printf("\nOperações:\n"); | |
printf("\t1) Inserir\n"); | |
printf("\t2) Remover\n"); | |
printf("\t3) Alterar\n"); | |
printf("\t4) Procurar\n"); | |
printf("\t5) Compactar\n"); | |
printf("\t6) Sair\n"); | |
printf("Digite o número da operação desejada: "); | |
scanf("%d", &operation); | |
switch (operation) { | |
case INSERT: | |
printf("Digite o Numero, Nome e o Veiculo que deseja inserir:\n"); | |
scanf("%d%s%s", &arg1, arg2, arg3); | |
// Inserção e Feedback | |
if (database_insert(db, arg1, arg2, arg3) == SUCCESS_OP) { | |
printf("Registro inserido com sucesso!\n"); | |
} | |
else { | |
printf("Erro! Número de chave já existente.\n"); | |
} | |
break; | |
case REMOVE: | |
printf("Digite o número do registro que deseja remover: "); | |
scanf("%d", &arg1); | |
// Remoção e Feedback | |
if (database_remove(db, arg1) == SUCCESS_OP) { | |
printf("Remoção efetuada com sucesso!\n"); | |
} | |
else { | |
fprintf(stderr, "Erro! Número de registro inexistente na base de dados.\n"); | |
} | |
break; | |
case CHANGE: | |
printf("Digite o número do registro em que fará alteração: "); | |
scanf("%d", &arg1); | |
database_change(db, arg1); | |
break; | |
case SEARCH: | |
printf("Digite o número do registro que deseja buscar: "); | |
scanf("%d", &arg1); | |
// Busca e Feedback | |
if (database_search(db, arg1) >= 0) { | |
printf("Chave encontrada!\n"); | |
} else { | |
printf("Chave não encontrada!\n"); | |
} | |
break; | |
case COMPACT: | |
database_compact(db); | |
printf("Compactação realizada com sucesso!\n"); | |
break; | |
case EXIT: | |
_exit = TRUE; | |
break; | |
} | |
} | |
return FALSE; | |
} | |
void database_close_save(database_t db) { | |
int i; | |
database_update_file_indexes(db); | |
// Liberação da memória alocada para a tabela de indices na ram | |
for (i = 0; i < db->ndx->size; i++) { | |
free(db->ndx->index[i]); | |
} | |
free(db->ndx->index); | |
free(db->ndx); | |
// Liberação da memória alocada para a estrutura database_t | |
free(db); | |
} | |
/** main | |
* Função main que gerencia a chamada das funções que inicializa e manipula | |
* a base de dados. | |
*/ | |
int main(int argc, char **argv) | |
{ | |
database_t database; | |
// Se o banco de dados não existir, cria um novo banco de dados | |
if (!exists_file("dados.txt")) { | |
create_database_with_file_table("TabelaInicial.txt", "dados.txt", | |
"primario.ndx"); | |
} | |
// Inicio a base de dados já criada com seu respectivo arquivo de chaves | |
database = init_database("dados.txt", "primario.ndx"); | |
// Menu principal para manipular a base de dados pelo terminal | |
database_driver(database); | |
database_close_save(database); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment