%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module /** @file ex-sto.c Test program for storage containers. The ex-sto program is a test and demonstration program for storage containers. The program stores information about persons: - surname, - family name and - age. The program reads input line by line, each input line is a command. The following commands can be used: - add _surname_ _family-name_ _age_ - del f _family-name_ - del s _surname_ - del a _age_ - read _filename_ - print - tree - find f _family-name_ - find s _surname_ - find a _age_ - quit The add command can be used to add persons. The del command deletes one or multiple records matching the specified family name, surname or age. The read command reads an input file typically containing add commands. The find command searches for the record for a given family name, surname or age. The print command prints all records beginning at the current record (the first record in the container or the element found in the last find command) until the end of the container. The tree command command prints the tree structures of a container. The quit command can be used to exit the application. Input to the program might look like @code add Erwin Unsinn 30 add David Zeppelin 40 add Johann Unfug 50 add Alter Sack 99 add Bla Blubb 33 print find a 40 find a 45 del s Johann print tree quit @endcode @see ex-sto.c @example ex-sto.c */ #include "dk3all.h" $(trace-include) /** Description of a person. */ typedef struct { char *famname; /**< Family name */ char *surname; /**< Surname */ unsigned long age; /**< Age in years */ } Person; /** Storage, persons sorted by family name. */ static dk3_sto_t *s1; /** Storage, persons sorted by surname. */ static dk3_sto_t *s2; /** Storage, persons sorted by age. */ static dk3_sto_t *s3; /** Storage, persons unsorted. */ static dk3_sto_t *s4; /** Iterator for storage sorted by family name. */ static dk3_sto_it_t *i1; /** Iterator for storage sorted by surname. */ static dk3_sto_it_t *i2; /** Iterator for storage sorted by age. */ static dk3_sto_it_t *i3; /** Iterator for unsorted storage. */ static dk3_sto_it_t *i4; /** Flag, input processing can be continued. */ static int can_continue; /** Flag, stdin is connected to terminal. */ static int stdin_is_tty = 1; static void read_file(FILE *); /** Retrieve age from pointer. The function retrieves an age evaluation of a pointer. The pointer target is either a struct Person or an unsigned long. @param p Pointer to person or unsigned long. @param cr Pointer object type: - 1: \a p points to a struct Person, - anything else: \a p points to an unsigned long. @return The age. */ unsigned long age_fct(void const *p, int cr) { unsigned long back; switch(cr) { case 1: { back = *((unsigned long const *)p); } break; default: { back = ((Person const *)p)->age; } break; } return back; } /** Compare object against other object or text. @param p1 Left pointer (from storage). @param p2 Right pointer (from dk3sto_it_find_like()). @param cr Comparison criteria: - 0 indicates: p1 and p2 are pointers to struct Person, compare by family name. - 1 indicates: p1 is a pointer to a struct Person, p2 is a pointer to a string. Compare the family name of p1 against the string. - 2 indicates: p1 and p2 are pointers to struct Person, compare by surname. - 3 indicates: p1 is a pointer to a struct person, p2 is a pointer to a string. Compare the surname of p1 against the string. @return Comparison result: - Positive value, if left pointer is larger (or behind), - Negative value, if left pointer is smaller (or before), - 0 if both pointers evaluate equal. */ int compare_fct(void const *p1, void const *p2, int cr) { int back = 0; switch(cr) { case 0: { back = strcmp(((Person *)p1)->famname, ((Person *)p2)->famname); } break; case 1: { back = strcmp(((Person *)p1)->famname, (char *)p2); } break; case 2: { back = strcmp(((Person *)p1)->surname, ((Person *)p2)->surname); } break; case 3: { back = strcmp(((Person *)p1)->surname, (char *)p2); } break; } return back; } /** Print a prompt. */ static void print_prompt(void) { if(stdin_is_tty) { printf("\nstotest: "); } } /** Print one persons record. @param pers Person structure. @param fout Output file. */ static void print_person(Person *pers, FILE *fout) { fprintf( fout, "%s %s %lu\n", ((pers->surname) ? pers->surname : "-"), ((pers->famname) ? pers->famname : "-"), pers->age ); } /** Print persons record from an iterators current position until the end of the container. @param i Storage iterator. @param fout Output file. */ static void print_it(dk3_sto_it_t *i, FILE *fout) { void *p; dk3sto_it_reset(i); while((p = dk3sto_it_next(i)) != NULL) { print_person((Person *)p, fout); fprintf(fout, "\n"); } } /** Print storage tree node including subtree. This function is used to show the tree structure. In your applications you should not access the internal members of a dk3_sto_t or dk3_sto_node_t directly. @param n Storage node. @param num Indent depth. @param f Output file. @param c Key character for node. */ void print_node(dk3_sto_node_t *n, int num, FILE *f, char c) { int i; Person *pers; if(n) { for(i = 0; i < num; i++) { fprintf(f, " "); } fprintf(f, "%c %5d (%d)", c, num, n->b); pers = (Person *)(n->o); if(pers) { if(pers->surname) fprintf(f, " %s ", pers->surname); else fprintf(f, " - "); if(pers->famname) fprintf(f, " %s ", pers->famname); else fprintf(f, " - "); fprintf(f, " %lu", pers->age); } fprintf(f, " \tparent:"); if(n->p) { if((n->p)->o) { pers = (Person *)((n->p)->o); if(pers->surname) fprintf(f, " %s ", pers->surname); else fprintf(f, " - "); if(pers->famname) fprintf(f, "%s ", pers->famname); else fprintf(f, "- "); fprintf(f, " %lu", pers->age); } } fprintf(f, "\n"); print_node(n->l, (num+1), f, 'l'); print_node(n->r, (num+1), f, 'r'); } } /** Print the tree structure. In your applications you should not access the internal members of a dk3_sto_t or dk3_sto_node_t directly. @param s Storage to print. @param f Output file. */ void print_tree(dk3_sto_t *s, FILE *f) { if((s->h) && (s->t)) { print_node(s->r, 0, f, '+'); } } /** Free a persons record (release memory.) @param pers Pointer to record to release. */ static void free_person(Person *pers) { char *cptr; if(pers) { cptr = pers->famname; dk3_delete(cptr); cptr = pers->surname; dk3_delete(cptr); pers->famname = pers->surname = NULL; dk3_delete(pers); } } /** Handle command without arguments. @param str Command (first text in input line). */ static void handle_cmd_1(char *str) { if(strcasecmp(str, "quit") == 0) { can_continue = 0; } if(strcasecmp(str, "print") == 0) { fprintf(stdout, "Sorted by family name:\n"); print_it(i1, stdout); fprintf(stdout, "Sorted by surname:\n"); print_it(i2, stdout); fprintf(stdout, "Sorted by age:\n"); print_it(i3, stdout); fprintf(stdout, "Unsorted:\n"); print_it(i4, stdout); } if(strcasecmp(str, "tree") == 0) { fprintf(stdout, "Sorted by family name:\n"); print_tree(s1, stdout); fprintf(stdout, "Sorted by surname:\n"); print_tree(s2, stdout); fprintf(stdout, "Sorted by age:\n"); print_tree(s3, stdout); fprintf(stdout, "Unsorted:\n"); print_tree(s4, stdout); } } /** Handle command with one argument. @param cmd Command to handle. @param arg1 Command argument. */ static void handle_cmd_2(char *cmd, char *arg1) { if(strcasecmp(cmd, "read") == 0) { FILE *in; in = fopen(arg1, "r"); if(in) { read_file(in); fclose(in); } } } /** Handle command with 2 arguments. @param cmd Command to handle. @param arg1 Command argument 1. @param arg2 Command argument 2. */ static void handle_cmd_3(char *cmd, char *arg1, char *arg2) { void *p; if(strcasecmp(cmd, "find") == 0) { if(strcasecmp(arg1, "f") == 0) { p = dk3sto_it_find_like(i1, arg2, 1); if(p) { print_person(p,stdout); printf("\n"); } printf("Followers:\n"); while((p = (Person *)dk3sto_it_next(i1)) != NULL) { print_person(p,stdout); printf("\n"); } } if(strcasecmp(arg1, "s") == 0) { p = dk3sto_it_find_like(i2, arg2, 3); if(p) { print_person(p,stdout); printf("\n"); } printf("Followers:\n"); while((p = (Person *)dk3sto_it_next(i2)) != NULL) { print_person(p,stdout); printf("\n"); } } if(strcasecmp(arg1, "a") == 0) { unsigned long age; if(sscanf(arg2, "%lu", &age) == 1) { p = dk3sto_it_find_like(i3, (void *)(&age), 1); if(p) { print_person(p,stdout); printf("\n"); } printf("Followers:\n"); while((p = (Person *)dk3sto_it_next(i3)) != NULL) { print_person(p,stdout); printf("\n"); } } } } if(strcasecmp(cmd, "del") == 0) { if(strcasecmp(arg1, "f") == 0) { while((p = dk3sto_it_find_like(i1, arg2, 1)) != NULL) { dk3sto_remove(s1,p); dk3sto_remove(s2,p); dk3sto_remove(s3,p); dk3sto_remove(s4,p); free_person(p); } } if(strcasecmp(arg1, "s") == 0) { while((p = dk3sto_it_find_like(i2, arg2, 3)) != NULL) { dk3sto_remove(s1,p); dk3sto_remove(s2,p); dk3sto_remove(s3,p); dk3sto_remove(s4,p); free_person(p); } } if(strcasecmp(arg1, "a") == 0) { unsigned long age; if(sscanf(arg2, "%lu", &age) == 1) { while((p = dk3sto_it_find_like(i3, (void *)(&age), 1)) != NULL) { dk3sto_remove(s1,p); dk3sto_remove(s2,p); dk3sto_remove(s3,p); dk3sto_remove(s4,p); free_person(p); } } } } } /** Handle command with 3 arguments. @param cmd Command to handle. @param arg1 Command argument 1. @param arg2 Command argument 2. @param arg3 Command argument 3. */ static void handle_cmd_4(char *cmd, char *arg1, char *arg2, char *arg3) { if(strcasecmp(cmd, "add") == 0) { unsigned long age; Person *pers; if(sscanf(arg3, "%lu", &age) == 1) { pers = dk3_new(Person,1); if(pers) { pers->famname = dk3str_dup(arg2); pers->surname = dk3str_dup(arg1); pers->age = age; if(!((pers->famname) && (pers->surname))) { free_person(pers); } else { if(!dk3sto_add(s4, (void *)pers)) { free_person(pers); } else { dk3sto_add(s1, (void *)pers); dk3sto_add(s2, (void *)pers); dk3sto_add(s3, (void *)pers); } } } } } } /** Read input file. @param in File to read from. */ void read_file(FILE *in) { char line[1024]; char *lbegin; int i; char str1[sizeof(line)]; char str2[sizeof(line)]; char str3[sizeof(line)]; char str4[sizeof(line)]; int old_can_continue; old_can_continue = can_continue; can_continue = 1; while(can_continue) { if(in == stdin) print_prompt(); if(fgets(line, sizeof(line), in)) { lbegin = dk3str_start(line, NULL); if(lbegin) { dk3str_chomp(lbegin, NULL); printf("# Processing \"%s\"\n", lbegin); i = sscanf(lbegin, "%s %s %s %s", str1, str2, str3, str4); switch(i) { case 1: { handle_cmd_1(str1); } break; case 2: { handle_cmd_2(str1, str2); } break; case 3: { handle_cmd_3(str1, str2, str3); } break; case 4: { handle_cmd_4(str1, str2, str3, str4); } break; } } } else { can_continue = 0; } } can_continue = old_can_continue; } /** Main program. @param argc Number of command line arguments. @param argv Command line arguments array. @return Exit status code. */ DK3_MAIN { void *p; Person *pers; char *cptr; /* Check whether or not we are connected to a terminal */ /* stdin_is_tty = dksf_echo_test_tty(); */ stdin_is_tty = 1; /* Create storage containers */ s1 = dk3sto_open_app(NULL); s2 = dk3sto_open_app(NULL); s3 = dk3sto_open_app(NULL); s4 = dk3sto_open_app(NULL); /* Check whether all containers were created successfully */ if(s1 && s2 && s3 && s4) { /* Set comparison and evaluation functions */ dk3sto_set_comp(s1, compare_fct, 0); dk3sto_set_comp(s2, compare_fct, 2); dk3sto_set_eval_ul(s3, age_fct, 0); /* Create storage container iterators */ i1 = dk3sto_it_open(s1); i2 = dk3sto_it_open(s2); i3 = dk3sto_it_open(s3); i4 = dk3sto_it_open(s4); /* Check whether all iterators were created successfully */ if(i1 && i2 && i3 && i4) { /* Process input */ read_file(stdin); /* Release all the records */ dk3sto_it_reset(i4); while((p = dk3sto_it_next(i4)) != NULL) { free_person((Person *)p); } } } /* Release the iterators */ if(i1) dk3sto_it_close(i1); if(i2) dk3sto_it_close(i2); if(i3) dk3sto_it_close(i3); if(i4) dk3sto_it_close(i4); /* Release the storage containers */ if(s1) dk3sto_close(s1); if(s2) dk3sto_close(s2); if(s3) dk3sto_close(s3); if(s4) dk3sto_close(s4); exit(0); return 0; }