// A simple database to demonstrate all the parts you have learned // and may be of some real world use for you depending on how you write it // database.c // K. J. Rock // November 11, 2018 // for Bacona Design LLC #include "database.h" #define COUNT 1051 // huge employment bump in November 2018 int id = 42; // guard id number for unique values // working functions void addRecord(int); // add record manually int save(void); // needs work to use any filename instead of myList.dat int read(void); // reads myList.dat and no others int print(void); // prints all fields of all records void sort(int); // added sorting field choice void swap(struct node *, struct node *); // helper function for sort routine int deleteID(int); // wrapper function void deleteNode(struct node *here); // functions needing work int report(void); // initial attempt at filtering on a field value int find(int); // helper function // random data generators void getName(void); // get name from women.txt or men.txt struct data *newData(void); // create random data for node void addNode(int); // add new node with randomly generated data int main(void) { int choice; char filename[128]; int loop = 1; // loop until this becomes a zero int num = 1; // for random record creation counter int idx = 0; // id choice for delete function char *firstName; // holder for name from man[] or woman[] arrays char *sName, *tName; // extras char *p, *r, *t; // working pointers instead of those lazy kind char *word; // instantiate a list here head = NULL; // empty list state tail = NULL; // these guard values are necessary. at least for head srand((unsigned) time(NULL)); // randomize seed from time getName(); // get lists of 100 men's and 100 women's first names while (loop) { // now a bit of old fashioned command line UI printf("\n"); printf("\t Menu\n"); printf("1) Add a record\n"); // records?? in sub menu printf("2) Edit a record\n"); printf("3) Delete a record\n"); printf("\n"); printf("4) Print all fields of all records\n"); printf("5) Print a report\n"); printf("6) Sort on key\n"); printf("\n"); printf("7) Read file\n"); // ask for file name printf("8) Save records\n"); // ask for file name printf("9) Exit\n"); printf("\n"); printf("11) Find a record\n"); // keep these as hidden choices // make sure they work so you can use them for testing purposes // but don't expose them to the public, it will be our little secret. /* printf("19) Fiddle with strings\n"); printf("23) Display file statistics\n"); printf("27) Ask for a filename\n"); printf("31) Add random record(s)\n"); */ printf("\n"); printf("Please make your choice => "); // need to change to sscanf() so I can trap for non-numeric characters if (scanf(" %d", &choice)) printf("Your choice was #%d\n\n", choice); switch (choice) { case 1: // add record // make enter default to one record at a time printf("How many records will you add => "); if (scanf(" %d", &num)) printf("Your choice was #%d\n\n", num); addRecord(num); break; case 2: // edit record printf("edit is not implemented yet\n"); // add code to find a certain record // which will work for case 6 too // once you have found a record you can edit or delete it. // to edit you need to add menu structure after you have displayed your choice // pick a field and go to the data entry routine for that field. // Our search will be based on id // find a group of records matching certain criteria // but reference each individual by their unique id break; case 3: // delete record // run find() to get a list with the proper attributes // once you have the unique id // scan list for that record // then you can delete *here find(1); // 1 signifies id field printf("Delete which id => "); if (scanf(" %d", &idx)) ; deleteID(idx); // then call delete( *here of id number ) // translates id number into its memory location then deletes that node break; case 4: // print all print(); // print all fields of all records // need to be able to pass ep to print so you can // print a single record // needs to be a function because it // could factor out of many places break; case 5: // print some // needs work to choose report criteria(on) // related to find report(); break; case 6: // sort printf("\n"); printf("\t Menu\n"); printf("1) Use id as key\n"); printf("2) Use age as key\n"); printf("3) Use salary as key\n"); printf("4) Use seniority as key\n"); printf("\t Reverse\n"); printf("5) Use id as key, largest to smallest\n"); printf("6) Use age as key, reverse\n"); printf("7) Use salary as key, backwards\n"); printf("8) Use seniority as key, old to young\n"); printf("Please make your choice => "); if (scanf(" %d", &choice)) ; sort(choice); break; case 7: // read file read(); break; case 8: // write file save(); break; case 9: // exit app loop = 0; // flag to drop out of menu loop break; case 11: // find record(s) helper function printf("\n"); printf("\t Menu\n"); printf("1) Use id as key\n"); printf("2) Use age as key\n"); printf("3) Use salary as key\n"); printf("4) Use seniority as key\n"); /* printf(" Reverse\n"); printf("5) Use id as key, largest to smallest\n"); printf("6) Use age as key, reverse\n"); printf("7) Use salary as key, backwards\n"); printf("8) Use seniority as key, old to young\n"); */ printf("Please make your choice => "); if (scanf(" %d", &num)) ; //if (num > 1) { printf("only id works for now\n"); num = 1; } find(num); break; case 19: // fiddle with strings // char *sName, *tName, *p, *t; // non-lazy pointers firstName = (char *) malloc(strlen(woman[6])); // this segment works OK for (int i=0; i <= (int) strlen(woman[6]); i++) *(firstName+i) = *(woman[6]+i); printf("%s\n", firstName); // this routine does not change the address of fName sName = (char *) malloc(strlen(woman[21])); // allocate space tName = (char *) woman[21]; // point tName at first char of woman[21] printf("tName => %p %c %s\n",tName, *tName, tName); // print address, initial, name t = sName; // point at the start while ( (*(sName++) = *(tName++)) ) ; // this routine modifies both pointers sName = t; // reset the address printf("$2 %s\n", sName); p = (char *) malloc(50); printf("a %p\n", p); t = (char *) malloc(strlen(woman[93])); t = woman[93]; r = p; // points at the beginning of your new memory space while ((*(p++) = *(t++))) ; printf("b %p\n", p); // Indy, they’re looking in the wrong place!!! p = r; // reset pointer back to beginning of memory space printf("|4| %s | %s |\n", p, woman[93]); word = (char *) malloc(sizeof(char) * 35); // space for largish word memset(word, '\0', sizeof(char) * 35); printf("What's the word? => "); if (scanf("%s", word)) ; //puts(word); printf("|#%s#|\n", word); r = p; while ((*(p++) = *(word++))) ; p = r; printf("*/%s/*\n", p); printf("***************\n"); // now you need to scan your saved list of data // and add random names from either the man[] or woman[] array // you also need to add a print all with the new field // then save with new field // and read with new field // then fill in the other blanks // after that you need a function to accept new names as you add a record // lines 195-196 look simple enough :) // need to malloc the space // fill the space with 'word' or from 'word' or some such // and hook ep->data->name to it //ep = head; // make sure you have opened the list /* if ((ep=head)) ; else printf("no list in memory\n"); while (ep) { idx = rand() % 200; if (idx > 100) t = man[idx-100]; else t = woman[idx]; ep->data->name = (char *) malloc(strlen(t)); p = ep->data->name; while ((*(p++) = *(t++))) ; ep = ep->next; } print(); */ break; case 23: // display list/report statistics printf("display statistics is not implemented yet\n"); break; case 27: // helper function // ask for a file name printf("Please give me the name of your list => "); if (scanf(" %s", filename)) ; puts(filename); break; case 31: // generate random records printf("How many new records should I add?\n"); printf("Please make your choice => "); // need to change to sscanf() so I can trap for non-numeric characters if (scanf(" %d", &num)) printf("Your choice was #%d\n\n", num); addNode(num); // was for adding random records break; } } } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // get name from women.txt or men.txt void getName(void) { FILE *fp = NULL; // guard value fp = fopen("men.txt", "r"); // open file for read if (fp != NULL) { for (int i = 0; i < 100; i++) { if (fscanf(fp, "%s", str)) ; // read string into str[] array if (fscanf(fp, "%d", &dummy)) ; // eats rank which is not used here s = (char *) malloc(strlen(str)); // allocate memory for new string man[i] = s; // point array at memory space s[0] = str[0]; // copy first letter unchanged for (int j = 1; j < (int) strlen(str); j++) // skip first letter to maintain case s[j] = str[j] + 32; // offset to lower case s[strlen(str)] = '\0'; // terminate string } fclose(fp); } fp = fopen("women.txt", "r"); if (fp != NULL) { for (int i = 0; i < 100; i++) { if (fscanf(fp, "%s", str)) ; // read string into str[] array if (fscanf(fp, "%d", &dummy)) ; // eats rank which is unused s = (char *) malloc(strlen(str)); // allocate space for string woman[i] = s; // point woman array at new string space s[0] = str[0]; // set pointer s to first char of name string for (int j = 1; j < (int) strlen(str); j++) s[j] = str[j] + 32; // change to lower case s[strlen(str)] = '\0'; // terminate string } fclose(fp); } } //////////////////////////////////////////////////////////////////////// // Remo Williams: Do you always talk like a Chinese fortune cookie? // // [Outraged, Chiun strikes Remo in his solar plexus. // // Remo stumbles to the window, // // partially paralyzed, and in pain] // // Chiun: Chinese! *Korean* is the most perfect creature ever // // to sanctify the earth with the imprint of its foot. // //////////////////////////////////////////////////////////////////////// // Parker, on the prom at Harvard University: "If all the ladies // were laid end to end, I wouldn't be surprised." struct data * newData(void) { struct data *myData; int i = 42; // our founder holds id #42 to honor Douglas Adams myData = (struct data *) malloc(sizeof(struct data)); myData->id = ++i; myData->age = rand() % 50 + 20; myData->salary = (float) (rand() % 95000 + 30000); myData->seniority = rand() % 45; return (myData); } void addNode(int num) { struct node *myNode; for (int i = 0; i < num; i++) { myNode = (struct node *) malloc(sizeof(struct node)); // create new node myNode->data = newData(); // add data to our new node if (head == NULL) // empty list case { head = myNode; myNode->next = NULL; } else // non-empty list { tail->next = myNode; myNode->prev = tail; } tail = myNode; } } void addRecord(int num) { int age, seniority; float salary; struct node *myNode; struct data *myData; // id = 42 if new list otherwise // id = whatever you read from myList for (int i = 0; i < num; i++) { myNode = (struct node *) malloc(sizeof(struct node)); // create new node myData = (struct data *) malloc(sizeof(struct data)); myNode->data = myData; // add data to our new node myNode->next = NULL; // guard values myNode->prev = NULL; // this module needs some trapping // I entered 140,000 for the salary and blew it up! // did not like that comma one bit // I need to catch it before it bites me printf("\n\n"); // ask user for manual input printf("Add a new record to the list\n"); printf("\n"); /* t = man[idx-100]; // word is a pointer ep->data->name = (char *) malloc(strlen(t)); p = ep->data->name; while ((*(p++) = *(t++))) ; printf("Who are you? => "); if (scanf("%s", word)) ; word = (char *) malloc(sizeof(char) * 35); // space for largish word memset(word, '\0', sizeof(char) * 35); //puts(word); printf("|#%s#|\n", word); r = p; while ((*(p++) = *(word++))) ; p = r; // printf("*^/%s/^*\n", p); print(); */ printf("Age => "); if (scanf(" %d", &age)) printf("age = %d\n", age); printf("Salary => "); if (scanf(" %f", &salary)) printf("salary = %10.2f\n", salary); printf("Seniority => "); if (scanf(" %d", &seniority)) printf("seniority = %d\n", seniority); printf("\n\n"); myData->id = ++id; // increment either from 42 or from retrieved value myData->age = age; myData->salary = salary; myData->seniority = seniority; if (head == NULL) // empty list case { head = myNode; } else // non-empty list { tail->next = myNode; // add at end of list myNode->prev = tail; } tail = myNode; } } int deleteID(int idx) { ep = head; while (ep) { if (ep->data->id == idx) break; ep = ep->next; } printf("node to delete %p sanity check %4d\n",ep, ep->data->id); deleteNode(ep); return 0; } void deleteNode(struct node *here) { if ((here->next == NULL) && (here->prev == NULL)) { // only one left in list case free(here->data); // free data memory space free(here); // free list node memory space printf("List is now empty.\n"); return; } if (here->prev != NULL) // you want to delete the first node here->prev->next = here->next; else { head = here->next; head->prev = NULL; } if (here->next != NULL) // you want to delete the last node here->next->prev = here->prev; else { tail = here->prev; // you're deleting node from the middle of the list tail->next = NULL; } free(here->data); // free data memory space free(here); // free list node memory space } // sort helper function void swap(struct node *start, struct node *ep) { // notice how only the pointers to the data change struct data *temp; // while the data stays in the very same place it started temp = ep->data; ep->data = start->data; start->data = temp; } // a simple selection sort void sort(int choice) { struct node *start, *ptr; // working pointers start = head; // start at the head of the list while (start != NULL) { // find the smallest and move it into the first position ptr = start; while (ptr != NULL) { // change next line to reflect sort index of choice switch (choice) { case 1: // use id as the key if (ptr->data->id < start->data->id) swap(start, ptr); // swap the pointers, the data stays in place break; case 2: // use age as key if (ptr->data->age < start->data->age) swap(start, ptr); // swap the pointers, the data stays in place /* else if (ptr->data->age >= start->data->age) swap(start, ptr); */ break; case 3: // use salary as key if (ptr->data->salary < start->data->salary) swap(start, ptr); break; case 4: // use seniority as key if (ptr->data->seniority < start->data->seniority) swap(start, ptr); break; // case 5: // use id as the key if (ptr->data->id >= start->data->id) swap(start, ptr); // swap the pointers, the data stays in place break; case 6: // use age as key if (ptr->data->age >= start->data->age) swap(start, ptr); // swap the pointers, the data stays in place /* else if (ptr->data->age >= start->data->age) swap(start, ptr); */ break; case 7: // use salary as key if (ptr->data->salary >= start->data->salary) swap(start, ptr); break; case 8: // use seniority as key if (ptr->data->seniority >= start->data->seniority) swap(start, ptr); break; } // if (ptr->data->salary >= start->data->salary) // if (ptr->data->seniority >= start->data->seniority) // if (ptr->data->age < start->data->age) // change < to >= for fun //if (ptr->data->age < start->data->age) /* { swap(start, ptr); // swap the pointers, the data stays in place } */ ptr = ptr->next; // step inner loop } start = start->next; // step outer loop, find next smallest list member } } int save(void) { FILE *fp; fp = fopen("myList.dat", "w"); fprintf(fp, "%4d\n", id); // need to save MAX id number // walk the list and print out the data it holds ep = head; while (ep != NULL) { fprintf(fp, "%4d", ep->data->id); fprintf(fp, " %s", ep->data->name); fprintf(fp, " %3d", ep->data->age); fprintf(fp, " %10.2f", ep->data->salary); fprintf(fp, " %3d", ep->data->seniority); fprintf(fp, "\n"); ep = ep->next; } fclose(fp); return 0; } int read(void) // string pointer to the filename) { int age, seniority; float salary; char name[25]; struct node *myNode; struct data *myData; FILE *fp; // ask for a file name // printf("Please give me the name of your list => "); // if(scanf(" %s", filename); // puts(filename); // convert selection 27 into a routine // so you can standardize the request for a filename head = NULL; tail = NULL; fp = fopen("myList.dat", "r"); //fp = fopen(str ptr my file, "r"); // need to read id number if (fscanf(fp, "%d", &id)) ; // there is only one id now // one ring to rule them all, one ring to bind them. while (1) { if (fscanf(fp, "%d", &id) == EOF) break; if (fscanf(fp, "%s", name) == EOF) break; if (fscanf(fp, "%d", &age) == EOF) break; if (fscanf(fp, "%f", &salary) == EOF) break; if (fscanf(fp, "%d", &seniority) == EOF) break; myNode = (struct node *) malloc(sizeof(struct node)); // create new node myData = (struct data *) malloc(sizeof(struct data)); myNode->data = myData; // add data to our new node myData->id = id; myData->name = (char *) malloc(strlen(name)); strcpy(myData->name, name); myData->age = age; myData->salary = salary; myData->seniority = seniority; if (head == NULL) // empty list case { head = myNode; } else // non-empty list { tail->next = myNode; // add at end of list myNode->prev = tail; } tail = myNode; } fclose(fp); return 0; } // walk the list and print out the data it holds int print(void) { ep = head; while (ep != NULL) { printf("id %3d\t\t", ep->data->id); printf("name %-15s\t", ep->data->name); printf("age %3d\t\t", ep->data->age); printf("salary $%10.2f\t", ep->data->salary); printf("seniority %3d\n", ep->data->seniority); ep = ep->next; } printf("\n"); return 0; } // need to expand this into a more thorough reporting tool // choice of fields to print // as well as choice of range of values in those fields // Maybe get to ANDing and ORing choices together. // That would require I write my own db language // I think not, I'll just mirror the SQL forms I need // remember: nothing fancy, light, fast int report(void) { ep = head; while (ep) { if ((ep->data->age > 60) && (ep->data->salary < 35000)) //if (ep->data->salary > 200000) { printf("id %3d\t\t", ep->data->id); printf("name %-15s\t", ep->data->name); printf("age %3d\t\t", ep->data->age); printf("salary $%10.2f\t", ep->data->salary); printf("seniority %3d\n", ep->data->seniority); } ep = ep->next; } printf("\n"); return 0; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Tool kit of parts from lessons 5 sortLink, 6 tree, 3 structure // parts of 7 search, 8 string, and 13 queue too //////////////////////////////////////////////////////////////////////// // need these functions to really feel like a database // one that can be useful for day to day stuff // input/output are randomly generated now // edit is related to input, don't create new but read and overwrite // Reports yes but they lead to read() and write() // Sort by your choice of field by tree or function // search within an indexed array // delete a node of the linked list // lists: arrays, linked lists, trees, queue? int find(int num) { int idx; int high = 0, low = 0; switch (num) { case 1: // use id as the key printf("Which id should I find for you? => "); if (scanf(" %d", &idx)) /*dummy*/ ; ep = head; while (ep) { if (ep->data->id == idx) { printf("id %3d\t\t", ep->data->id); printf("name %-15s\t", ep->data->name); printf("age %3d\t\t", ep->data->age); printf("salary $%10.2f\t", ep->data->salary); printf("seniority %3d\n", ep->data->seniority); } ep = ep->next; } break; case 2: // use age as the key printf("What age should I find for you? => "); if (scanf(" %d", &idx)) /*dummy*/ ; ep = head; while (ep) { if (ep->data->age == idx) { printf("id %3d\t\t", ep->data->id); printf("name %-15s\t", ep->data->name); printf("age %3d\t\t", ep->data->age); printf("salary $%10.2f\t", ep->data->salary); printf("seniority %3d\n", ep->data->seniority); } ep = ep->next; } break; case 3: // use salary as the key printf("What is the high salary of your range => "); if (scanf(" %d", &high)) ; printf("What is the low salary => "); if (scanf(" %d", &low)) ; ep = head; while (ep) { if ((ep->data->salary >= low) && (ep->data->salary <= high)) { printf("id %3d\t\t", ep->data->id); printf("name %-15s\t", ep->data->name); printf("age %3d\t\t", ep->data->age); printf("salary $%10.2f\t", ep->data->salary); printf("seniority %3d\n", ep->data->seniority); } ep = ep->next; } break; case 4: // use seniority as the key printf("What seniority should I find for you? => "); if (scanf(" %d", &idx)) ; ep = head; while (ep) { if (ep->data->seniority == idx) { printf("id %3d\t\t", ep->data->id); printf("name %-15s\t", ep->data->name); printf("age %3d\t\t", ep->data->age); printf("salary $%10.2f\t", ep->data->salary); printf("seniority %3d\n", ep->data->seniority); } ep = ep->next; } break; } return 0; }