9511_project03

Command line program that parses a bank .csv file and generates and output file in format .csv or .xml containing the debits and credits of every user.
Index Commits Files Refs README LICENSE
commit bd65d08bf77f1d39df6baf364ba2ef9526bc6bcb
parent 634904e117e0638ac7edb053773b5327ac4416f2
Author: klewer-martin <martin.cachari@gmail.com>
Date:   Thu, 22 Jul 2021 17:52:03 -0300

Update after long time

Diffstat:
MMakefile | 45++++++++++++++++++++++++++++++++++++---------
Mexamples/input_gen.py | 8++++----
Minclude/cla.h | 9++++++---
Minclude/io.h | 48++++++++++++++++++++++++++++++++++++++----------
Minclude/sort.h | 11+++++++----
Ainclude/status.h | 37+++++++++++++++++++++++++++++++++++++
Minclude/types.h | 1+
Msource/cla.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msource/io.c | 323++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msource/main.c | 61+++++++++++++++++++++++++++++++++++++++++++------------------
Msource/sort.c | 53++++++++++++++++++++++++++++++++++++++---------------
Asource/status.c | 44++++++++++++++++++++++++++++++++++++++++++++
12 files changed, 588 insertions(+), 123 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,28 +1,55 @@
 CC=gcc
-CFLAGS=-std=c99 -pedantic -Wall
+CFLAGS=-g -pedantic -Wall
 SRCFOLDER=source
 HFOLDER=include
 PROGNAME=main
 
 all: main clean
 
-main: main.o cla.o errors.o io.o sort.o
-    $(CC) $(CFLAGS) main.o cla.o errors.o io.o sort.o -o $(PROGNAME)
+main: main.o cla.o status.o io.o sort.o
+    $(CC) $(CFLAGS) main.o cla.o status.o io.o sort.o -o $(PROGNAME)
 
-main.o: $(HFOLDER)/cla.h $(HFOLDER)/errors.h
+main.o: $(HFOLDER)/cla.h $(HFOLDER)/status.h
     $(CC) $(CFLAGS) -c $(SRCFOLDER)/main.c
 
-cla.o: $(HFOLDER)/cla.h $(HFOLDER)/types.h
+cla.o: $(HFOLDER)/cla.h $(HFOLDER)/status.h
     $(CC) $(CFLAGS) -c $(SRCFOLDER)/cla.c
 
-errors.o: $(HFOLDER)/types.h
-    $(CC) $(CFLAGS) -c $(SRCFOLDER)/errors.c
+status.o: $(HFOLDER)/status.h
+    $(CC) $(CFLAGS) -c $(SRCFOLDER)/status.c
 
-io.o: $(HFOLDER)/types.h
+io.o: $(HFOLDER)/status.h
     $(CC) $(CFLAGS) -c $(SRCFOLDER)/io.c
 
-sort.o: $(HFOLDER)/types.h
+sort.o: $(HFOLDER)/status.h
     $(CC) $(CFLAGS) -c $(SRCFOLDER)/sort.c
 
 clean:
     rm *.o
+
+run20:
+    ./main -fmt csv -out output.csv -in examples/test_file_20.csv -ti 1320498000 -tf 1320498046
+
+run2k:
+    ./main -fmt csv -out output.csv -in examples/test_file_2k.csv -ti 1320498000 -tf 1325499000
+
+run50:
+    ./main -fmt csv -out output.csv -in examples/test_file_50.csv -ti 1320498000 -tf 1320498049
+
+run500:
+    ./main -fmt csv -out output.csv -in examples/test_file_500.csv -ti 1320498000 -tf 1320529000
+
+run5k:
+    ./main -fmt csv -out output.csv -in examples/test_file_5k.csv -ti 1320498000 -tf 1320529000
+
+run50k:
+    ./main -fmt csv -out output.csv -in examples/test_file_50k.csv -ti 1320498000 -tf 1420529000
+
+run500k:
+    ./main -fmt csv -out output.csv -in examples/test_file_500k.csv -ti 1320498000 -tf 1420529000
+
+run50xml:
+    ./main -fmt xml -out output.xml -in examples/test_file_50.csv -ti 1320498000 -tf 1320498049
+
+run5kxml:
+    ./main -fmt xml -out output.xml -in examples/test_file_5k.csv -ti 1320498000 -tf 1360498049
diff --git a/examples/input_gen.py b/examples/input_gen.py
@@ -2,7 +2,7 @@ from string import digits
 from time import strftime, gmtime
 from random import randint, choice
 
-LINES = 2000
+LINES = 5000
 
 # OUTPUT:
 #     ID_TRANSACCION, ID_USUARIO, FECHA, MONTO, NUMERO DE TRAJETA, DESCRIPCION
@@ -16,14 +16,14 @@ def card_number_generator():
 def generate_file(max_lines):
     id_transaction_base = 123400
     id_user_base = 1
-    id_user_max = 2000
-    amount_base = 10
+    id_user_max = 200
+    amount_base = 1
     amount_max = 10000
     for i in range(max_lines):
         id_transaction = (i + id_transaction_base)
         id_user = randint(id_user_base, id_user_max)
         card_nr = card_number_generator() 
-        date = strftime("%d/%m/%Y %H:%M:%S", gmtime(1320487200 + i))
+        date = strftime("%d.%m.%Y %H:%M:%S", gmtime(1320487200 + i))
         
         j = randint(1, len(descs) - 1)
         desc = descs[j] 
diff --git a/include/cla.h b/include/cla.h
@@ -5,7 +5,7 @@
 #include <string.h>
 #include <stdbool.h>
 
-#include "types.h"
+#include "status.h"
 
 #define NO_ARGS_ENTERED 1
 #define NORMAL_AMOUNT_ARGS 11
@@ -22,14 +22,17 @@ typedef enum {
 
 typedef struct {
     char *fmt, *fi, *fo;
-    unsigned long ti, tf;
+    unsigned long ti, tf, parsed_lines;
 } ADT_cla_t, *cla_t;
 
 status_t validate_arguments(int argc, char **argv);
 status_t check_flags_position(int argc, char **argv);
 status_t check_flags_repeated(int argc, char **argv);
 
-status_t setup(int argc, char **argv, cla_t *cla);
+status_t cla_create(cla_t *cla);
+status_t cla_setup(int argc, char **argv, cla_t *cla);
+status_t cla_destroy(cla_t *cla);
+
 void clean(cla_t cla);
 
 extern const char *available_flags[FLAGS_MAX];
diff --git a/include/io.h b/include/io.h
@@ -6,32 +6,60 @@
 #include <string.h>
 
 #include "cla.h"
-#include "types.h"
+#include "status.h"
 #include "sort.h"
 
+#define _XOPEN_SOURCE
+#define __USE_XOPEN
+#include <time.h>
+
 #define INPUT_FILE_FIELDS 6
 #define BUFFER_SIZE        1000
 
+#define INPUT_FILE_DELIM ","
+#define CSV_OUTPUT_DELIM    ","
+
+#define XML_HEADER        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+#define XML_ROOT_OPEN    "<root>"
+#define XML_ROOT_CLOSE    "</root>"
+
+#define XML_ROW_OPEN    "<row>"
+#define XML_ROW_CLOSE    "</row>"
+
+#define XML_ID_OPEN        "<user>"
+#define XML_ID_CLOSE    "</user>"
+
+#define XML_CREDIT_OPEN        "<credits>"
+#define XML_CREDIT_CLOSE    "</credits>"
+
+#define XML_DEBT_OPEN    "<debits>"
+#define XML_DEBT_CLOSE    "</debits>"
+
 typedef enum {
-    POS_ID_TRANSACTION,
+    POS_ID_TXN,
     POS_USER_ID,
-    POS_TRANSACTION_DATE,
+    POS_TXN_DATE,
     POS_AMOUNT,
     POS_CARD_NUMBER,
     POS_DESC
 } csv_pos_t;
 
+status_t process_file(cla_t cla, user_t **users, size_t *i);
+
 status_t set_data(user_t *user, char **data);
 
-status_t tmp_file_write(FILE *bfp, const user_t user);
-status_t tmp_file_read(FILE *bfp, user_t user);
+status_t string_split(char *s, char **data, char *delim);
+
+status_t load_values(FILE *, cla_t *data);
+
+status_t export_data(cla_t cla, const user_t *users, size_t size);
 
-status_t split(char *s, char **data);
+status_t export_data_as_csv(FILE *fo, const user_t *users, size_t size);
+status_t export_data_as_xml(FILE *fo, const user_t *users, size_t size);
 
-status_t load_values(FILE *);
-status_t export_data(cla_t cla, FILE *bfp);
+status_t destroy_users(user_t *users, size_t size);
 
-void print_user(const user_t user);
+status_t user_create(user_t *usr);
+status_t user_set_data(user_t usr, int id, long credit, long debt);
 
-status_t tmp_file_gen(cla_t cla, FILE **bfp);
 #endif
diff --git a/include/sort.h b/include/sort.h
@@ -2,14 +2,17 @@
 #define SORT__H
 
 #include "cla.h"
-#include "types.h"
+#include "status.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 
-int minmax(const void *, const void *);
-int maxmix(const void *, const void *);
+int credit_minmax(const void *, const void *);
+int credit_maxmix(const void *, const void *);
 
-status_t tmp_file_sort(FILE *tmp, size_t len, char order);
+int debt_minmax(const void *, const void *);
+int debt_maxmin(const void *, const void *);
+
+status_t sort_users(user_t *users, size_t size, char *order);
 
 #endif
diff --git a/include/status.h b/include/status.h
@@ -0,0 +1,37 @@
+#ifndef STATUS__H
+#define STATUS__H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define ERROR_RETRY_MSG "Verifique y vuelva a intentar"
+
+#define STATUS_T_MAX 12
+
+typedef enum {
+    OK,
+    ERROR_MEMORY,
+    ERROR_WRONG_FLAGS,
+    ERROR_WRONG_TIME,
+    ERROR_MISSING_ARGS,
+    ERROR_OPENING_FILE,
+    ERROR_CORRUPT_DATA,
+    ERROR_FLAG_REPEATED,
+    ERROR_FLAG_NOT_FOUND,
+    ERROR_FORMAT_NOT_FOUND,
+    ERROR_USER_NOT_FOUND,
+    ERROR_NULL_POINTER
+} status_t;
+
+typedef struct {
+    size_t id;
+    long credit, debt;
+} ADT_user_t, *user_t;
+
+void show_status(status_t st);
+void free_arrays(size_t num,...);
+
+extern const char *status_strings[STATUS_T_MAX];
+
+#endif
diff --git a/include/types.h b/include/types.h
@@ -12,6 +12,7 @@ typedef enum {
     ERROR_FLAG_NOT_FOUND,
     ERROR_FLAG_REPEATED,
     ERROR_OPENING_FILE,
+    ERROR_MEMORY,
     ERROR_NULL_POINTER
 } status_t;
 
diff --git a/source/cla.c b/source/cla.c
@@ -31,13 +31,12 @@ status_t check_flags_position(int argc, char **argv)
     return OK;
 }
 
-/* Need to add a record for already founded flags */
 status_t check_flags_repeated(int argc, char **argv)
 {
     size_t i, j, fflags_index;
     int founded_flags[FLAGS_MAX];
 
-    /* Inicializa a -1 para evitar confuciones con 0 */
+    /* Inicializa a -1 para evitar confusiones con 0 */
     for(i = 0; i < FLAGS_MAX; i++) founded_flags[i] = -1;
 
     for(i = 1, fflags_index = 0; i <= (argc - 2); i += 2) {
@@ -55,14 +54,9 @@ status_t check_flags_repeated(int argc, char **argv)
     return OK;
 }
 
-status_t setup(int argc, char **argv, cla_t *cla)
+status_t cla_setup(int argc, char **argv, cla_t *cla)
 {
-    /* Falta validar memoria */
-    *cla = (cla_t)malloc(sizeof(ADT_cla_t));
-
-    (*cla)->fmt = calloc(sizeof(char), 100);
-    (*cla)->fo = calloc(sizeof(char), 100);
-    (*cla)->fi = calloc(sizeof(char), 100);
+    char *endptr;
 
     for(size_t i = 1; i < argc; i += 2) {
         for(flags_t f = FLAG_FMT; f < FLAGS_MAX; f++) {
@@ -71,20 +65,65 @@ status_t setup(int argc, char **argv, cla_t *cla)
                     case FLAG_FMT: strcpy((*cla)->fmt, argv[i + 1]); break;
                     case FLAG_OUT: strcpy((*cla)->fo, argv[i + 1]); break;
                     case FLAG_IN: strcpy((*cla)->fi, argv[i + 1]); break;
-                    case FLAG_TI: (*cla)->ti = strtoul(argv[i + 1], NULL, 10); break;
-                    case FLAG_TF: (*cla)->tf = strtoul(argv[i + 1], NULL, 10); break;
+                    case FLAG_TI: 
+                                  (*cla)->ti = strtoul(argv[i + 1], &endptr, 10); 
+                                  if(*endptr != '\0') return ERROR_WRONG_TIME;
+                                  break;
+                    case FLAG_TF: 
+                                  (*cla)->tf = strtoul(argv[i + 1], &endptr, 10); 
+                                  if(*endptr != '\0') return ERROR_WRONG_TIME;
+                                  break;
                     default: return ERROR_FLAG_NOT_FOUND;
                 }
             }
         }
     }
+
     return OK;
 }
 
-void clean(cla_t cla)
+
+status_t cla_create(cla_t *cla)
 {
-    free(cla->fmt);
-    free(cla->fi);
-    free(cla->fo);
-    free(cla);
+    if(cla == NULL) return ERROR_NULL_POINTER;
+
+    if((*cla = (cla_t)malloc(sizeof(ADT_cla_t))) == NULL)
+        return ERROR_MEMORY;
+
+    if(((*cla)->fmt = calloc(sizeof(char), 100)) == NULL) {
+        free(cla);
+        cla = NULL;
+        return ERROR_MEMORY;
+    }
+
+    if(((*cla)->fo = calloc(sizeof(char), 100)) == NULL) {
+        free((*cla)->fmt);
+        free(cla);
+        cla = NULL;
+        return ERROR_MEMORY;
+    }
+
+    if(((*cla)->fi = calloc(sizeof(char), 100)) == NULL) {
+        free((*cla)->fo);
+        free((*cla)->fmt);
+        free(cla);
+        cla = NULL;
+        return ERROR_MEMORY;
+    }
+
+    return OK;
+}
+
+status_t cla_destroy(cla_t *cla)
+{
+    if(cla == NULL) return ERROR_NULL_POINTER;
+
+    free((*cla)->fmt);
+    free((*cla)->fo);
+    free((*cla)->fi);
+    free(*cla);
+
+    cla = NULL;
+
+    return OK;
 }
diff --git a/source/io.c b/source/io.c
@@ -1,95 +1,330 @@
 #include "../include/io.h"
 
-/* Lee los datos de el archivo de entrada linea por linea 
- * y los imprime en un archivo binario temporal */
-status_t tmp_file_gen(cla_t cla, FILE **bfp)
+#define INIT_SIZE        1000
+#define GROWTH_FACTOR     2
+
+user_t find_user(const user_t *users, int id, size_t size);
+status_t destroy_data(char **data);
+
+status_t get_date(time_t *e, char **data);
+
+void user_clean(user_t usr);
+void clean_buffer(char *buf);
+void clean_data(char **data);
+
+user_t user_dup(user_t src);
+
+/* Lee los datos del archivo de entrada linea por linea mientras los procesa y asigna a un arreglo de usuarios */
+status_t process_file(cla_t cla, user_t **users, size_t *size)
 {
-    FILE *fpi, *fpo;
+    FILE *fpi;
     char *buffer, **data;
-    size_t i;
-    user_t user;
+    time_t epoch;
+    size_t alloc_size;
+    user_t user, user_found, *tmp;
     status_t st;
 
-    if((fpi = fopen(cla->fi, "rt")) == NULL) return ERROR_OPENING_FILE;
+    *size = cla->parsed_lines = 0;
+    alloc_size = INIT_SIZE;
+    user = user_found = NULL;
 
-    if((fpo = fopen(cla->fo, "wt")) == NULL) return ERROR_OPENING_FILE;
-
-    if(((*bfp) = fopen(TMP_FILE_NAME, "wb")) == NULL) return ERROR_OPENING_FILE;
+    if((fpi = fopen(cla->fi, "rt")) == NULL) {
+        *users = NULL;
+        return ERROR_OPENING_FILE;
+    }
 
-    data = (char **)malloc(sizeof(char *) * INPUT_FILE_FIELDS);
+    if((data = (char **)malloc(sizeof(char *) * INPUT_FILE_FIELDS)) == NULL) {
+        fclose(fpi);
+        return ERROR_MEMORY;
+    }
 
-    for(i = 0; i < INPUT_FILE_FIELDS; i++)
-        data[i] = calloc(sizeof(char), BUFFER_SIZE);
+    if((buffer = calloc(sizeof(char), BUFFER_SIZE)) == NULL) {
+        destroy_data(data);
+        fclose(fpi);
+        return ERROR_MEMORY;
+    }
 
-    buffer = calloc(sizeof(char), BUFFER_SIZE);
+    if(user_create(&user) != OK) {
+        free(buffer);
+        destroy_data(data);
+        fclose(fpi);
+    }
 
-    /* Lee los datos de el archivo de entrada linea por linea 
-     * y los imprime en un archivo binario temporal */
-    for(i = 0; (fgets(buffer, BUFFER_SIZE, fpi) != NULL); i++) {
-        if((st = split(buffer, data))) return st;
-        if((st = set_data(&user, data))) return st;
-        tmp_file_write(*bfp, user);
+    /* En caso de haber algun error users no es liberado, se libera luego en main */
+    if(((*users) = (user_t *)malloc(sizeof(user_t) * INIT_SIZE)) == NULL) {
+        free(buffer);
+        free(user);
+        destroy_data(data);
+        fclose(fpi);
     }
 
-    /* tmp_file_sort(*bfp, i, 'd'); */
+    /* Lee los datos de el archivo de entrada linea por linea */ 
+    while(fgets(buffer, BUFFER_SIZE, fpi) != NULL) {
+        /* Extiende el arreglo de usuarios en caso de que haya poca memoria */
+        if(*size == alloc_size) {
+            alloc_size *= GROWTH_FACTOR;
+            if((tmp = realloc((*users), alloc_size * sizeof(user_t))) == NULL) {
+                fclose(fpi);
+                free(buffer);
+                destroy_data(data);
+                return ERROR_MEMORY;
+            }
+            (*users) = tmp;
+        }
 
-    /* tmp_file_read(*bfp, user); */
+        /* Divide el buffer en subarreglos segun un caracter delimitador */
+        if((st = string_split(buffer, data, INPUT_FILE_DELIM)) != OK) {
+            fclose(fpi);
+            free(buffer);
+            destroy_data(data);
+            return st;
+        }
 
-    /* print_user(user); */
+        /* Asigna a 'user' id, creditos y debitos cargados en data */
+        if((st = set_data(&user, data)) != OK) {
+            destroy_data(data);
+            free(buffer);
+            fclose(fpi);
+            return st;
+        }
 
-    /* rewind(*bfp); */
-    /* for(i = 0; fread(user, 1, sizeof(ADT_user_t), *bfp); i++) */
-    /*     print_user(user); */
+        /* Transforma la fecha leida a tiempo UNIX en segundos */
+        if((st = get_date(&epoch, data)) != OK) {
+            destroy_data(data);
+            free(buffer);
+            fclose(fpi);
+            return st;
+        }
 
+        /* Comprueba que la fecha leida este dentro del rango de fechas ingresadas en CLA */
+        if(epoch < cla->ti) continue;
+        else if(epoch > cla->tf) {
+            free(user);
+            destroy_data(data);
+            free(buffer);
+            fclose(fpi);
+            return OK;
+        }
+
+        /* Busca el numero de id en los usuarios ya ingresados, si no lo encuentra agrega un usuario nuevo al arreglo de usuarios */
+        if((user_found = find_user(*users, user->id, *size)) != NULL) {
+            user_found->credit += user->credit;
+            user_found->debt += user->debt;
+        } else {
+            (*users)[(*size)++] = user_dup(user);
+        }
+
+        cla->parsed_lines++;
+
+        user_clean(user);
+        clean_data(data);
+        clean_buffer(buffer);
+    } /* End while */
+
+    free(user);
+    destroy_data(data);
+    free(buffer);
     fclose(fpi);
-    fclose(fpo);
     return OK;
 }
 
 status_t set_data(user_t *user, char **data) 
 {
-    int amount;
+    if(data == NULL || user == NULL) return ERROR_NULL_POINTER;
 
-    (*user) = (user_t)malloc(sizeof(user_t));
+    char *endptr;
+    long amount;
 
-    (*user)->id = strtol(data[POS_USER_ID], NULL, 10);
+    (*user)->id = strtol(data[POS_USER_ID], &endptr, 10);
+    if(*endptr != '\0') return ERROR_CORRUPT_DATA;
 
-    amount = strtol(data[POS_AMOUNT], NULL, 10);
+    amount = strtol(data[POS_AMOUNT], &endptr, 10);
+    if(*endptr != '\0') return ERROR_CORRUPT_DATA;
 
-    if(amount > 0) (*user)->credit += amount;
-    else if(amount < 0) (*user)->debt += amount;
+    if(amount > 0) (*user)->credit = amount;
+    else if(amount < 0) (*user)->debt = -amount; /* '-=' Para eliminar el menos    */
 
     return OK;
 }
 
-status_t split(char *s, char **data)
+status_t string_split(char *s, char **data, char *delim)
 {
-    char *p;
+    char *p, *tmp;
     size_t fields = 0;
-    for(p = s; (data[fields++] = strtok(p, ",")); p = NULL);
+
+    for(p = s; (tmp = strtok(p, delim)); p = NULL)
+        data[fields++] = strdup(tmp);
+
+    if(fields != INPUT_FILE_FIELDS)
+        return ERROR_CORRUPT_DATA;
 
     return OK;
 }
 
-void print_user(const user_t user)
+
+status_t destroy_users(user_t *users, size_t size)
 {
-    printf("ID: %5d CREDITS: %5d DEBITS: %5d\n", user->id, user->credit, user->debt);
+    if(users == NULL) return ERROR_NULL_POINTER;
+
+    for(size_t i = 0; i < size; i++)
+        free(users[i]);
+
+    free(users);
+    return OK;
 }
 
-status_t tmp_file_write(FILE *bfp, const user_t user)
+status_t destroy_data(char **data)
 {
-    if(bfp == NULL) return ERROR_NULL_POINTER;
+    if(data == NULL) return ERROR_NULL_POINTER;
 
-    fwrite(user, 1, sizeof(ADT_user_t), bfp);
+    for(size_t i = 0; i < INPUT_FILE_FIELDS; i++) {
+        free(data[i]);
+        data[i] = NULL;
+    }
+
+    free(data);
+    return OK;
+}
+
+status_t export_data(cla_t cla, const user_t *users, size_t size)
+{
+    if(users == NULL) return ERROR_NULL_POINTER;
+
+    FILE *fo;
+
+    if((fo = fopen(cla->fo, "wt")) == NULL)
+        return ERROR_OPENING_FILE;
 
+    if(!strcmp(cla->fmt, "csv")) {
+        export_data_as_csv(fo, users, size);
+    } else if(!strcmp(cla->fmt, "xml")) {
+        export_data_as_xml(fo, users, size);
+    } else return ERROR_FORMAT_NOT_FOUND;
+
+    fclose(fo);
     return OK;
 }
 
-status_t tmp_file_read(FILE *bfp, user_t user)
+status_t export_data_as_csv(FILE *fo, const user_t *users, size_t size)
 {
-    if(bfp == NULL || user == NULL) return ERROR_NULL_POINTER;
+    if(fo == NULL || users == NULL)
+        return ERROR_NULL_POINTER;
 
-    fread(user, 1, sizeof(ADT_user_t), bfp);
+    for(size_t i = 0; i < size; i++)
+        fprintf(fo, "%ld%s%ld%s%ld\n", users[i]->id, CSV_OUTPUT_DELIM,\
+                users[i]->credit, CSV_OUTPUT_DELIM, users[i]->debt);
 
     return OK;
 }
+
+status_t export_data_as_xml(FILE *fo, const user_t *users, size_t size)
+{
+    if(fo == NULL || users == NULL)
+        return ERROR_NULL_POINTER;
+
+    fprintf(fo, "%s\n%s\n", XML_HEADER, XML_ROOT_OPEN);
+    for(size_t i = 0; i < size; i++) {
+        fprintf(fo, "\t%s\n", XML_ROW_OPEN);
+        fprintf(fo, "\t\t%s%ld%s\n", XML_ID_OPEN, users[i]->id, XML_ID_CLOSE);
+        fprintf(fo, "\t\t%s%ld%s\n", XML_CREDIT_OPEN, users[i]->credit, XML_CREDIT_CLOSE);
+        fprintf(fo, "\t\t%s%ld%s\n", XML_DEBT_OPEN, users[i]->debt, XML_DEBT_CLOSE);
+        fprintf(fo, "\t%s\n", XML_ROW_CLOSE);
+    }
+
+    fprintf(fo, "%s\n", XML_ROOT_CLOSE);
+    return OK;
+}
+
+void clean_buffer(char *buf)
+{
+    for(size_t i = 0; i < BUFFER_SIZE; i++)
+        buf[i] = '\0';
+}
+
+void clean_data(char **data)
+{
+    for(size_t i = 0; i < INPUT_FILE_FIELDS; i++) {
+        free(data[i]);
+        data[i] = NULL;
+    }
+}
+
+status_t get_date(time_t *e, char **data)
+{
+    if(e == NULL || data == NULL) return ERROR_NULL_POINTER;
+
+    struct tm tm;
+
+    /* Desactiva el horario de verano */
+    tm.tm_isdst = 0;
+
+    switch(data[POS_TXN_DATE][2]) {
+        case '/': 
+            strptime(data[2], "%d/%m/%Y %H:%M:%S", &tm);
+            *e = mktime(&tm);
+            break;
+        case '-':
+            strptime(data[2], "%d-%m-%Y %H:%M:%S", &tm);
+            *e = mktime(&tm);
+            break;
+        case '.':
+            strptime(data[2], "%d.%m.%Y %H:%M:%S", &tm);
+            *e = mktime(&tm);
+            break;
+
+        default: return ERROR_CORRUPT_DATA; break;
+    }
+
+    return OK;
+}
+
+void user_clean(user_t usr)
+{
+    usr->id = 0;
+    usr->credit = 0;
+    usr->debt = 0;
+}
+
+status_t user_set_data(user_t usr, int id, long credit, long debt)
+{
+    usr->id = id;
+    usr->credit = credit;
+    usr->debt = debt;
+
+    return OK;
+}
+
+user_t user_dup(user_t src)
+{
+    user_t dst = NULL;
+
+    user_create(&dst);
+
+    dst->id = src->id;
+    dst->credit = src->credit;
+    dst->debt = src->debt;
+
+    return dst;
+}
+
+status_t user_create(user_t *usr)
+{
+    if(((*usr) = (user_t)malloc(sizeof(ADT_user_t))) == NULL)
+        return ERROR_MEMORY;
+
+    (*usr)->id = 0;
+    (*usr)->credit = 0;
+    (*usr)->debt = 0;
+
+    return OK;
+}
+
+user_t find_user(const user_t *users, int id, size_t size)
+{
+    for(size_t i = 0; i < size; i++) {
+        if(users[i]->id == id) {
+            return users[i];
+        }
+    }
+    return NULL;
+}
diff --git a/source/main.c b/source/main.c
@@ -1,45 +1,70 @@
 #include "../include/cla.h"
-#include "../include/errors.h"
+#include "../include/status.h"
 #include "../include/io.h" /* output_gen() */
 #include "../include/sort.h"
 
-#include <stdlib.h>
+/* "ca" - creditos ascendentes | "cd" - creditos descendentes
+ * "da" - debitos ascendentes  | "dd" - debitos descendentes */
+#define SORTING_ORDER    "cd"
 
-#define SORTING_ORDER    "a"
-
-status_t create_array(int *arr, size_t len);
-status_t load_values(int **arr, size_t len);
-status_t destroy_array(int *arr, size_t len);
+#define EXIT_SUCCESS_MSG "Ejecución terminada exitosamente"
+#define USERS_REGISTERED_MSG    "Usuarios registrados: "
+#define PROCESED_LINES_MSG        "Lineas procesadas: "
 
 int main (int argc, char *argv[])
 {
     status_t st;
-    FILE *tmp_file;
     cla_t cla;
+    user_t *users;
+    size_t size;
 
+    /* Valida que los argumentos sean correctos */
     if((st = validate_arguments(argc, argv)) != OK) {
         show_status(st);
         return st;
     }
 
-    if((st = setup(argc, argv, &cla)) != OK) {
+    /* Asigna memoria a cla */
+    if((st = cla_create(&cla)) != OK) {
+        show_status(st);
+        return st;
+    }
+
+    /* Asigna a la estructura 'cla' los argumentos ingresados */
+    if((st = cla_setup(argc, argv, &cla)) != OK) {
+        show_status(st);
+        return st;
+    }
+
+    /* Carga en users los usuarios desde el archivo de entrada */
+    if((st = process_file(cla, &users, &size)) != OK) {
         show_status(st);
+        cla_destroy(&cla);
+        destroy_users(users, size);
         return st;
     }
-    /* En este punto ya tengo todos los datos que necesito, el nombre de los archivos de entrada, el tiempo inicial y final, y el formato de el archivo de salida */
 
-    /* Genera un archivo binario temporal con los datos parseados  */
-    if((st = tmp_file_gen(cla, &tmp_file)) != OK) {
+    /* Ordena los usuarios con orden SORTING_ORDER */
+    if((st = sort_users(users, size, SORTING_ORDER)) != OK) {
         show_status(st);
+        cla_destroy(&cla);
+        destroy_users(users, size);
         return st;
     }
 
-    /* Exporta el archivo temporal a un archivo de texto con formato de acuerdo a la flag recibida como argumento */
-    /* if((st = export_data(cla, tmp_file)) != OK) { */
-    /*     show_status(st); */
-    /*     return st; */
-    /* } */
+    /* Imprime los datos cargados en users a un archivo de salida */
+    if((st = export_data(cla, users, size)) != OK) {
+        show_status(st);
+        cla_destroy(&cla);
+        destroy_users(users, size);
+        return st;
+    }
+
+    printf("\n%s\n%s%ld\n%s%ld\n", EXIT_SUCCESS_MSG, USERS_REGISTERED_MSG,\
+            size, PROCESED_LINES_MSG, cla->parsed_lines);
+
+    cla_destroy(&cla);
+    destroy_users(users, size);
 
-    clean(cla);
     return OK;
 }
diff --git a/source/sort.c b/source/sort.c
@@ -1,30 +1,53 @@
 #include "../include/sort.h"
 
-int minmax(const void *a, const void *b)
+int credit_minmax(const void *a, const void *b)
 {
-    int *A = (int *)a;
-    int *B = (int *)b;
+    user_t *A = (user_t *)a;
+    user_t *B = (user_t *)b;
 
-    return (*A > *B) ? *A : (*A - *B);
+    return ((*A)->credit > (*B)->credit) ? 1 : 0;
 }
 
-int maxmin(const void *a, const void *b)
+int credit_maxmin(const void *a, const void *b)
 {
-    int *A = (int *)a;
-    int *B = (int *)b;
+    user_t *A = (user_t *)a;
+    user_t *B = (user_t *)b;
 
-    return (*A > *B) ? *A : (*A - *B);
+    return ((*A)->credit > (*B)->credit) ? 0 : 1;
 }
 
-status_t tmp_file_sort(FILE *tmp, size_t len, char order)
+int debt_minmax(const void *a, const void *b)
 {
-    if(tmp == NULL) return ERROR_NULL_POINTER;
+    user_t *A = (user_t *)a;
+    user_t *B = (user_t *)b;
 
-    switch (order)
-    {
-        case 'a': qsort(tmp, len, sizeof(ADT_cla_t), minmax); break;
-        case 'd': qsort(tmp, len, sizeof(ADT_cla_t), maxmin); break;
-    }
+    return ((*A)->debt > (*B)->debt) ? 1 : 0;
+}
+
+int debt_maxmin(const void *a, const void *b)
+{
+    user_t *A = (user_t *)a;
+    user_t *B = (user_t *)b;
+
+    return ((*A)->debt > (*B)->debt) ? 0 : 1;
+}
+
+status_t sort_users(user_t *users, size_t l, char *order)
+{
+    if(users == NULL || order == NULL)
+        return ERROR_NULL_POINTER;
+
+    if(!strcmp(order, "ca"))
+        qsort(users, l, sizeof(user_t), credit_minmax);
+
+    if(!strcmp(order, "cd"))
+        qsort(users, l, sizeof(user_t), credit_maxmin);
+
+    if(!strcmp(order, "da"))
+        qsort(users, l, sizeof(user_t), debt_minmax);
+
+    if(!strcmp(order, "dd"))
+        qsort(users, l, sizeof(user_t), debt_maxmin);
 
     return OK;
 }
diff --git a/source/status.c b/source/status.c
@@ -0,0 +1,44 @@
+#include "../include/status.h"
+
+const char *status_strings[] = {
+    "Everything executed succesfully. Have a nice day.",
+    "There is a problem with the memory",
+    "There is a flags misspeled",
+    "There is a problem with entered time",
+    "There are arguments missing",
+    "Error el archivo de se pudo abrir, puede que no existe o que el nombre sea incorrecto",
+    "Error al leer un dato, puede que el archivo de entrada este corrupto",
+    "Error hay una flag repetida",
+    "Error flag no encontrada",
+    "Error el formato de salida no se reconoce",
+    "Error usuario no encontrado, puede que el archivo de entrada este corrupto",
+    "ERROR_NULL_POINTER"
+};
+
+void show_status(status_t st)
+{
+    fprintf(stderr, "\n%s\n", status_strings[st]);
+    fprintf(stderr, "%s\n", ERROR_RETRY_MSG);
+}
+
+void free_arrays(size_t num,...)
+{
+    va_list valist;
+    size_t i;
+
+    va_start(valist, num);
+
+    for(i = 0; i < num; i++)
+        free(va_arg(valist, char *));
+}
+
+void close_streams(size_t num,...)
+{
+    va_list valist;
+    size_t i;
+
+    va_start(valist, num);
+
+    for(i = 0; i < num; i++)
+        free(va_arg(valist, FILE *));
+}