commit 7de7356a1b085cf76c9818d475f1145e44a62a16
parent 2d69a1ac6e1ea828c60759db91c4ce26308d45b1
Author: klewer-martin <martin.cachari@gmail.com>
Date: Wed, 28 Jul 2021 21:56:47 -0300
Update:
Diffstat:
10 files changed, 103 insertions(+), 374 deletions(-)
diff --git a/Makefile b/Makefile
@@ -6,8 +6,8 @@ PROGNAME=main
all: main clean
-main: cla.o status.o sort.o io.o main.o user.o vector.o
- $(CC) $(CFLAGS) main.o cla.o status.o io.o sort.o user.o vector.o -o $(PROGNAME)
+main: cla.o status.o io.o main.o user.o vector.o
+ $(CC) $(CFLAGS) main.o cla.o status.o io.o user.o vector.o -o $(PROGNAME)
main.o: $(HFOLDER)/cla.h $(HFOLDER)/status.h $(HFOLDER)/user.h
$(CC) $(CFLAGS) -c $(SRCFOLDER)/main.c
@@ -24,9 +24,6 @@ status.o: $(HFOLDER)/status.h
io.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
$(CC) $(CFLAGS) -c $(SRCFOLDER)/io.c
-sort.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
- $(CC) $(CFLAGS) -c $(SRCFOLDER)/sort.c
-
user.o: $(HFOLDER)/status.h $(HFOLDER)/user.h
$(CC) $(CFLAGS) -c $(SRCFOLDER)/user.c
diff --git a/include/cla.h b/include/cla.h
@@ -21,17 +21,18 @@ typedef enum {
} flags_t;
typedef struct {
- char *fmt, *fi, *fo;
+ char *fmt;
+ FILE *fi, *fo;
unsigned long ti, tf;
} 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 validate_arguments(int, char **, cla_t);
+status_t check_flags_position(int, char **);
+status_t check_flags_repeated(int, char **);
-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);
+status_t cla_create(cla_t *);
+status_t cla_setup(int, char **, cla_t);
+status_t cla_destroy(cla_t);
extern const char *available_flags[FLAGS_MAX];
extern const char *available_formats[FORMATS_MAX];
diff --git a/include/io.h b/include/io.h
@@ -47,19 +47,18 @@
/* POS_DESC */
/* } csv_pos_t; */
-status_t process_file(cla_t cla, user_t **users, size_t *i);
-
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);
void clean_data(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 destroy_data(char **data);
+status_t destroy_data(char **, size_t);
status_t get_date(time_t *e, char **data);
void clean_buffer(char *buf);
diff --git a/include/user.h b/include/user.h
@@ -3,31 +3,25 @@
#include "status.h"
+#define OUT_FILE_DELIM ","
+
typedef unsigned long ulong;
typedef struct {
/* id: user id / c: user credits / d: user debits */
ulong id, c, d;
-} ADT_user_t, *user_t;
-
-user_t user_find(const user_t *users, int id, size_t size);
-user_t user_dup(user_t src);
+} ADT_user_t;
status_t user_create(ADT_user_t **);
status_t user_set_data(ADT_user_t *, char **data);
status_t user_add_amount(ADT_user_t *, long amount);
-int user_id_equal(const void *, const void *);
-
-void user_clean(user_t usr);
+int user_equals(const void *, const void *);
-status_t sort_users(user_t *users, size_t size, char *order);
-status_t destroy_users(user_t *users, size_t size);
-
-status_t user_printer(const void *, FILE *);
+status_t user_print_as_csv(const void *u, FILE *fp);
+status_t user_print_as_xml(const void *u, FILE *fp);
int user_comparator(const void *, FILE *);
-
int user_comparator_credits_minmax(const void *, const void *);
int user_comparator_credits_maxmin(const void *, const void *);
diff --git a/include/vector.h b/include/vector.h
@@ -30,7 +30,7 @@ status_t ADT_Vector_destroy(ADT_Vector_t **);
status_t ADT_Vector_load(ADT_Vector_t *, FILE *);
status_t ADT_Vector_set(ADT_Vector_t **, void *, size_t);
-status_t ADT_Vector_print(const ADT_Vector_t *, FILE *);
+status_t ADT_Vector_print(const ADT_Vector_t *, FILE *, printer_t);
status_t ADT_Vector_sort(ADT_Vector_t *, comparator_t);
void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e);
diff --git a/source/cla.c b/source/cla.c
@@ -3,17 +3,20 @@
const char *available_flags[] = { "-fmt", "-out", "-in", "-ti", "-tf" };
const char *available_formats[] = { "csv", "xml" };
-status_t validate_arguments(int argc, char **argv)
+status_t validate_arguments(int argc, char **argv, cla_t cla)
{
status_t st;
- if(argv == NULL) return ERROR_NULL_POINTER;
+ if(argv == NULL || cla == NULL) return ERROR_NULL_POINTER;
if((argc == NO_ARGS_ENTERED) || (argc != NORMAL_AMOUNT_ARGS))
return ERROR_MISSING_ARGS;
if((st = check_flags_position(argc, argv))) return st;
if((st = check_flags_repeated(argc, argv))) return st;
+ /* Asigna a la estructura 'cla' los argumentos ingresados */
+ if((st = cla_setup(argc, argv, cla))) return st;
+
return OK;
}
@@ -60,23 +63,32 @@ status_t check_flags_repeated(int argc, char **argv)
return OK;
}
-status_t cla_setup(int argc, char **argv, cla_t *cla)
+status_t cla_setup(int argc, char **argv, cla_t cla)
{
char *endptr;
size_t i;
+ FILE *fp;
flags_t f;
for(i = 1; i < argc; i += 2) {
for(f = FLAG_FMT; f < FLAGS_MAX; f++) {
if(!strcmp(available_flags[f], argv[i])) {
switch (f) {
- 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], &endptr, 10);
+ case FLAG_FMT: strcpy(cla->fmt, argv[i + 1]); break;
+ case FLAG_OUT: if((fp = fopen(argv[i + 1], "wt")) == NULL)
+ return ERROR_OPENING_FILE;
+
+ cla->fo = fp;
+ break;
+ case FLAG_IN: if((fp = fopen(argv[i + 1], "rt")) == NULL)
+ return ERROR_OPENING_FILE;
+
+ cla->fi = fp;
+ 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);
+ 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;
@@ -101,34 +113,18 @@ status_t cla_create(cla_t *cla)
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)
+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);
+ fclose(cla->fo);
+ fclose(cla->fi);
- *cla = NULL;
+ free(cla->fmt);
+ free(cla);
return OK;
}
diff --git a/source/io.c b/source/io.c
@@ -1,120 +1,5 @@
#include "../include/io.h"
-
-/*
-//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;
- char *buffer, **data;
- time_t epoch;
- size_t alloc_size;
- user_t user, user_found, *tmp;
- status_t st;
-
- *size = 0;
- alloc_size = INIT_SIZE;
- user = user_found = NULL;
-
- if((fpi = fopen(cla->fi, "rt")) == NULL) {
- *users = NULL;
- return ERROR_OPENING_FILE;
- }
-
- if((data = (char **)malloc(sizeof(char *) * INPUT_FILE_FIELDS)) == NULL) {
- fclose(fpi);
- return ERROR_MEMORY;
- }
-
- if((buffer = calloc(sizeof(char), BUFFER_SIZE)) == NULL) {
- destroy_data(data);
- fclose(fpi);
- return ERROR_MEMORY;
- }
-
- if(user_create(&user) != OK) {
- free(buffer);
- destroy_data(data);
- fclose(fpi);
- }
-
- //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);
- }
-
- //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;
- }
-
- // 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;
- }
-
- // Asigna a 'user' id, creditos y debitos cargados en data
- if((st = user_set_data(&user, data)) != OK) {
- destroy_data(data);
- free(buffer);
- fclose(fpi);
- return st;
- }
-
- // 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 = user_find(*users, user->id, *size)) != NULL) {
- user_found->c += user->c;
- user_found->d += user->d;
- } else {
- (*users)[(*size)++] = user_dup(user);
- }
-
- user_clean(user);
- clean_data(data);
- clean_buffer(buffer);
- } // End while
-
- free(user);
- destroy_data(data);
- free(buffer);
- fclose(fpi);
- return OK;
-}
-*/
-
status_t string_split(char *s, char **data, char *delim)
{
char *p, *tmp;
@@ -129,13 +14,13 @@ status_t string_split(char *s, char **data, char *delim)
return OK;
}
-status_t destroy_data(char **data)
+status_t destroy_data(char **data, size_t fields)
{
size_t i;
if(data == NULL) return ERROR_NULL_POINTER;
- for(i = 0; i < INPUT_FILE_FIELDS; i++) {
+ for(i = 0; i < fields; i++) {
free(data[i]);
data[i] = NULL;
}
@@ -143,26 +28,7 @@ status_t destroy_data(char **data)
free(data);
return OK;
}
-
-status_t export_data(cla_t cla, const user_t *users, size_t size)
-{
- FILE *fo;
-
- if(users == NULL) return ERROR_NULL_POINTER;
-
- 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 export_data_as_csv(FILE *fo, const user_t *users, size_t size)
{
size_t i;
@@ -197,7 +63,7 @@ status_t export_data_as_xml(FILE *fo, const user_t *users, size_t size)
fprintf(fo, "%s\n", XML_ROOT_CLOSE);
return OK;
}
-
+*/
void clean_buffer(char *buf)
{
size_t i;
@@ -244,4 +110,3 @@ status_t get_date(time_t *e, char **data)
return OK;
}
-
diff --git a/source/main.c b/source/main.c
@@ -1,13 +1,14 @@
#include "../include/io.h"
#include "../include/cla.h"
#include "../include/user.h"
-#include "../include/sort.h"
#include "../include/status.h"
#include "../include/vector.h"
-#define BUFFER_INIT_SIZE 100
-#define INPUT_FILE_DELIM ","
-#define INPUT_FILE_FIELDS 6
+#define IN_FILE_MAX_LEN 100
+#define IN_FILE_DELIM ","
+#define OUT_FILE_DELIM ","
+#define IN_FILE_FIELDS 6
+#define IN_FILE_FIELDS_MAX_LEN 50
int main (int argc, char *argv[])
{
@@ -15,22 +16,19 @@ int main (int argc, char *argv[])
cla_t cla;
ADT_Vector_t *v;
ADT_user_t *user, *user_tmp;
- char *b, *endptr;
+ char buffer[IN_FILE_MAX_LEN];
char **data;
- size_t i, j;
- FILE *fi;
+ char *endptr;
long amount;
- user = NULL;
-
- /* Valida que los argumentos sean correctos */
- if((st = validate_arguments(argc, argv)) != OK) {
+ /* Asigna memoria a cla */
+ if((st = cla_create(&cla)) != OK) {
show_status(st);
return st;
}
- /* Asigna memoria a cla */
- if((st = cla_create(&cla)) != OK) {
+ /* Valida que los argumentos sean correctos */
+ if((st = validate_arguments(argc, argv, cla)) != OK) {
show_status(st);
return st;
}
@@ -38,70 +36,27 @@ int main (int argc, char *argv[])
/* Crea un vector */
if((st = ADT_Vector_create(&v)) != OK) {
show_status(st);
- cla_destroy(&cla);
+ cla_destroy(cla);
return st;
}
- if((st = ADT_Vector_set_printer(v, user_printer)) != OK) {
+ /* Setea el comparador a ADT_Vector */
+ if((st = ADT_Vector_set_comparator(v, user_equals)) != OK) {
show_status(st);
- cla_destroy(&cla);
+ cla_destroy(cla);
ADT_Vector_destroy(&v);
return st;
}
- /* Asigna a la estructura 'cla' los argumentos ingresados */
- if((st = cla_setup(argc, argv, &cla)) != OK) {
+ if((st = user_create(&user_tmp)) != OK) {
show_status(st);
- cla_destroy(&cla);
+ cla_destroy(cla);
ADT_Vector_destroy(&v);
return st;
}
-
- b = calloc(sizeof(char), BUFFER_INIT_SIZE);
- data = malloc(sizeof(char *) * INPUT_FILE_FIELDS);
-
- if(b == NULL || data == NULL) {
- free(b);
- show_status(ERROR_MEMORY);
- cla_destroy(&cla);
- ADT_Vector_destroy(&v);
- return ERROR_MEMORY;
- }
-
- /* Asigna memoria cada puntero dentro de data */
- for(i = 0; i < INPUT_FILE_FIELDS; i++) {
- if((data[i] = calloc(sizeof(char), BUFFER_INIT_SIZE)) == NULL) {
- show_status(ERROR_MEMORY);
- for(j = 0; j < i; j++)
- free(data[j]);
-
- free(b);
- cla_destroy(&cla);
- ADT_Vector_destroy(&v);
- return ERROR_MEMORY;
- }
- }
-
- /* Abre el archivo de entrada */
- if((fi = fopen(cla->fi, "rt")) == NULL) {
- show_status(ERROR_OPENING_FILE);
- for(j = 0; j < INPUT_FILE_FIELDS; j++)
- free(data[j]);
-
- free(b);
- cla_destroy(&cla);
- ADT_Vector_destroy(&v);
- return ERROR_OPENING_FILE;
- }
-
- ADT_Vector_set_comparator(v, user_id_equal);
-
- user_tmp = NULL;
- user_create(&user_tmp);
-
- while(fgets(b, BUFFER_INIT_SIZE, fi)) {
- string_split(b, data, INPUT_FILE_DELIM);
+ while(fgets(buffer, IN_FILE_MAX_LEN, cla->fi)) {
+ string_split(buffer, data, IN_FILE_DELIM);
user_set_data(user_tmp, data);
amount = strtol(data[POS_AMOUNT], &endptr, 10);
@@ -119,67 +74,24 @@ int main (int argc, char *argv[])
}
}
- ADT_Vector_sort(v, user_comparator_credits_maxmin);
-
- ADT_Vector_print(v, stdout);
+ if((st = ADT_Vector_sort(v, user_comparator_credits_maxmin)) != OK) {
+ show_status(st);
+ free(user_tmp);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ return st;
+ }
- for(j = 0; j < INPUT_FILE_FIELDS; j++)
- free(data[j]);
+ if((st = ADT_Vector_print(v, stdout, user_print_as_csv)) != OK) {
+ show_status(st);
+ free(user_tmp);
+ cla_destroy(cla);
+ ADT_Vector_destroy(&v);
+ return st;
+ }
- free(data);
- free(b);
free(user_tmp);
- fclose(fi);
- cla_destroy(&cla);
+ cla_destroy(cla);
ADT_Vector_destroy(&v);
return OK;
}
-
-
-
-
-
-
-/*
-
-// "ca" - creditos ascendentes | "cd" - creditos descendentes
-// "da" - debitos ascendentes | "dd" - debitos descendentes
-#define SORTING_ORDER "cd"
-#define PRINT_EXIT_SUCCESS_MSG
-
-
-
-#ifdef PRINT_EXIT_SUCCESS_MSG
-
- // Imprime un mensaje para darle a conocer al usuario
- // que todo se ejecuto correctamente
- printf("\n%s\n%s%ld\n%s%ld\n", EXIT_SUCCESS_MSG, USERS_REGISTERED_MSG,\
- size, PROCESED_LINES_MSG, cla->parsed_lines);
-
-#endif
-*/
-
-
- /* 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; */
- /* } */
-
- /* 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; */
- /* } */
-
- /* 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; */
- /* } */
diff --git a/source/user.c b/source/user.c
@@ -12,13 +12,6 @@ status_t user_create(ADT_user_t **user)
return OK;
}
-void user_clean(user_t usr)
-{
- usr->id = 0;
- usr->c = 0;
- usr->d = 0;
-}
-
status_t user_set_data(ADT_user_t *user, char **data)
{
char *endptr;
@@ -48,44 +41,32 @@ status_t user_add_amount(ADT_user_t *user, long amount)
return OK;
}
-ADT_user_t *user_dup(user_t src)
+int user_equals(const void *a, const void *b)
{
- ADT_user_t *dst = NULL;
+ ADT_user_t *A, *B;
- user_create(&dst);
+ if(a == NULL || b == NULL) return ERROR_NULL_POINTER;
- dst->id = src->id;
- dst->c = src->c;
- dst->d = src->d;
+ A = (ADT_user_t *)a;
+ B = (ADT_user_t *)b;
- return dst;
-}
+ if(A->id == B->id) return 1;
-user_t user_find(const user_t *users, int id, size_t size)
-{
- size_t i;
- for(i = 0; i < size; i++) {
- if(users[i]->id == id) {
- return users[i];
- }
- }
- return NULL;
+ return 0;
}
-status_t destroy_users(user_t *users, size_t size)
+status_t user_print_as_csv(const void *u, FILE *fp)
{
- size_t i;
+ if(u == NULL || fp == NULL) return ERROR_NULL_POINTER;
- if(users == NULL) return ERROR_NULL_POINTER;
+ ADT_user_t *user = (ADT_user_t *)u;
- for(i = 0; i < size; i++)
- free(users[i]);
+ fprintf(fp, "%ld%s%ld%s%ld\n", user->id, OUT_FILE_DELIM, user->c, OUT_FILE_DELIM, user->d);
- free(users);
return OK;
}
-status_t user_printer(const void *u, FILE *fp)
+status_t user_print_as_xml(const void *u, FILE *fp)
{
if(u == NULL || fp == NULL) return ERROR_NULL_POINTER;
@@ -96,28 +77,14 @@ status_t user_printer(const void *u, FILE *fp)
return OK;
}
-int user_id_equal(const void *a, const void *b)
-{
- ADT_user_t *A, *B;
-
- if(a == NULL || b == NULL) return ERROR_NULL_POINTER;
-
- A = (ADT_user_t *)a;
- B = (ADT_user_t *)b;
-
- if(A->id == B->id) return 1;
-
- return 0;
-}
-
int user_comparator_credits_minmax(const void *a, const void *b)
{
- user_t A, B;
+ ADT_user_t *A, *B;
if(a == NULL || b == NULL) return ERROR_NULL_POINTER;
- A = *(user_t *)a;
- B = *(user_t *)b;
+ A = *(ADT_user_t **)a;
+ B = *(ADT_user_t **)b;
if(A->c > B->c) return 1;
else if(A->c == B->c) return 0;
@@ -126,12 +93,12 @@ int user_comparator_credits_minmax(const void *a, const void *b)
int user_comparator_credits_maxmin(const void *a, const void *b)
{
- user_t A, B;
+ ADT_user_t *A, *B;
if(a == NULL || b == NULL) return ERROR_NULL_POINTER;
- A = *(user_t *)a;
- B = *(user_t *)b;
+ A = *(ADT_user_t **)a;
+ B = *(ADT_user_t **)b;
if(A->c < B->c) return 1;
else if(A->c == B->c) return 0;
diff --git a/source/vector.c b/source/vector.c
@@ -74,8 +74,6 @@ status_t ADT_Vector_load(ADT_Vector_t *v, FILE *fi)
if(*endptr != '\n') return ERROR_CORRUPT_DATA;
ADT_Vector_add(&v, tmp);
-
- ADT_Vector_print(v, stdout);
}
return OK;
@@ -134,14 +132,14 @@ void *ADT_Vector_get_elem(const ADT_Vector_t *v, void *e)
return NULL;
}
-status_t ADT_Vector_print(const ADT_Vector_t *v, FILE *fp)
+status_t ADT_Vector_print(const ADT_Vector_t *v, FILE *fp, printer_t pf)
{
size_t i;
if(v == NULL) return ERROR_NULL_POINTER;
for(i = 0; i < v->size; i++)
- v->printer(v->a[i], fp);
+ (*pf)(v->a[i], fp);
putchar('\n');
return OK;