drankinatty

C - RxRefill - Simple Text Menu Implementation

Jan 4th, 2022 (edited)
323
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.26 KB | None | 0 0
  1. /* See declaration of auth users[] in main() for user/pass for program
  2.  * RxRefill is a simple text menu app with separate functions responsible
  3.  * for displaying current drug quantities, refill and dispensing the
  4.  * medication. The code demonstrates how to gracefully handle the user
  5.  * cancelling input by generating a manual EOF (ctrl + d, or ctrl + z windows)
  6.  * at any input.
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <ctype.h>
  13.  
  14. /* if you need a constant, #define one (or more) */
  15. #define MAXNM  64   /* max characters in user, pass & drug name */
  16. #define MAXC  256   /* max characters in temp buffer for user input */
  17.  
  18. struct drug {       /* struct to hold drug name and quantity */
  19.     char name[MAXNM];
  20.     size_t qty;
  21. };
  22. typedef struct drug drug;   /* create typedef to refer to type "struct drug"
  23.                              * as simply "drug" where needed. can be same,
  24.                              * typedef name space and struct tag name space
  25.                              * are separate and do not conflict.
  26.                              */
  27.  
  28. typedef struct auth {       /* typedef included as part of struct declaration */
  29.     char user[MAXNM];
  30.     char pw[MAXNM];
  31. } auth;
  32.  
  33. /* chkauth takes array of users, no. of users and user that was input,
  34.  * and loops over array of users comparing user.name & user.pass to each
  35.  * element in the users array. If user.name and user.pass match an element
  36.  * in the users array, return index to valid user on success, -1 if no
  37.  * match of was found in the users array.
  38.  */
  39. int chkauth (auth *users, size_t n, auth *user)
  40. {
  41.     for (size_t i = 0; i < n; i++) {                /* loop over all users */
  42.         if (strcmp (users[i].user, user->user) == 0 &&  /* username match? */
  43.             strcmp (users[i].pw, user->pw) == 0) {      /* password match? */
  44.             return i;               /* return index to user in users array */
  45.         }
  46.     }
  47.  
  48.     return -1;  /* user/password not found */
  49. }
  50.  
  51. /* login takes the array of users and then number of users, reads and
  52.  * validates both the username and password into a temporary auth stuct
  53.  * 'user'. any error during input results in the return of -2 (user
  54.  *  canceled input by generating a manual EOF end-of-file, with Ctrl+d on
  55.  * Linux or Ctrl+z on windows), or -1 for general input error. on successful
  56.  * input of both username and password, login() passes the users array, the
  57.  * number of users and the temp stuct user to chkauth(). the funciton returns
  58.  * the direct return from chkauth to indicate success/failure of login.
  59.  */
  60. int login (auth *users, size_t n)
  61. {
  62.     auth user = { "", "" }; /* struct to hold user-input of name/pass */
  63.     char buf[MAXC] = "";    /* temp buffer, initialized all zero */
  64.     size_t len = 0;         /* length of input, initialized zero */
  65.  
  66.     fputs ("Enter Username: ", stdout);     /* prompt or username */
  67.     if (fgets (buf, MAXC, stdin) == NULL) { /* read up to MAXC char into buf */
  68.         fputs ("(user canceled input)\n", stderr);
  69.         return -2;
  70.     }
  71.  
  72.     /* validate username input */
  73.     len = strlen (buf);             /* get length of input */
  74.     if (len && buf[len-1] == '\n')  /* last char is '\n'?, overwrite with 0 */
  75.         buf[--len] = 0;
  76.     else if (len == MAXC - 1) {
  77.         fputs ("error: input exceeds MAXC chars.\n", stderr);
  78.         while (fgets (buf, MAXC, stdin)) {} /* read discard extra chars */
  79.         return -1;
  80.     }
  81.     else if (len > MAXNM - 1) { /* > MAXNM-1 chars? (saves room for nul-char) */
  82.         fputs ("error: username exceeds MAXNM chars.\n", stderr);
  83.         return -1;
  84.     }
  85.     memcpy (&user.user, buf, len + 1);  /* copy buf to user.name, +1 for
  86.                                          * nul-terminating character.
  87.                                          */
  88.  
  89.     fputs ("Enter Password: ", stdout);     /* prompt or password */
  90.     if (fgets (buf, MAXC, stdin) == NULL) { /* read up to MAXC char into buf */
  91.         fputs ("(user canceled input)\n", stderr);
  92.         return -2;
  93.     }
  94.  
  95.     /* validate password input (same thing as with username) */
  96.     len = strlen (buf);             /* get length of input */
  97.     if (len && buf[len-1] == '\n')  /* last char is '\n'?, overwrite with 0 */
  98.         buf[--len] = 0;
  99.     else if (len == MAXC - 1) {
  100.         fputs ("error: input exceeds MAXC chars.\n", stderr);
  101.         while (fgets (buf, MAXC, stdin)) {} /* read discard extra chars */
  102.         return -1;
  103.     }
  104.     else if (len > MAXNM - 1) { /* > MAXNM-1 chars? (saves room for nul-char) */
  105.         fputs ("error: password exceeds MAXNM chars.\n", stderr);
  106.         return -1;
  107.     }
  108.     memcpy (&user.pw, buf, len + 1);    /* copy buf to user.name, +1 for
  109.                                          * nul-terminating character.
  110.                                          */
  111.  
  112.     return chkauth (users, n, &user);   /* &user passes pointer to struct */
  113. }
  114.  
  115. /* check drug quantity menu */
  116. void drugqtymenu (drug *drugs, size_t ndrugs)
  117. {
  118.     /* display heading */
  119.     fputs ("\n\tCurrent Quantities\n\n", stdout);
  120.  
  121.     /* display each drug (12-char lt. justified) and quantity */
  122.     for (size_t i = 0; i < ndrugs; i++)
  123.         printf ("%-12s %zu\n", drugs[i].name, drugs[i].qty);
  124.    
  125.     putchar('\n');
  126. }
  127.  
  128. /* refill menu */
  129. int refillmenu (drug *drugs, size_t ndrugs)
  130. {
  131.     size_t  index,       /* drugs[index] of selected drug */
  132.             qty;        /* quantity of selected drug to refill */
  133.  
  134.     /* display heading */
  135.     fputs ("\nRefill which drug\n\n", stdout);
  136.  
  137.     /* display each drug in menu (I would add space between) */
  138.     for (size_t i = 0; i < ndrugs; i++)
  139.         printf ("%zu. %s\n", i + 1, drugs[i].name);
  140.  
  141.     fputs ("\nSelect drug number  : ", stdout);  /* prompt for drug */
  142.     for (;;) {  /* loop continually until valid drug selected */
  143.         char buf[MAXC];     /* temporary buffer for user input */
  144.  
  145.         if (!fgets (buf, MAXC, stdin)) {    /* if user cancels input */
  146.             fputs ("(user canceled input)\n", stderr);
  147.             return -2;
  148.         }
  149.  
  150.         index = *buf - '0' - 1;     /* convert from ASCII value to decimal */
  151.         if (isdigit (*buf) && index < ndrugs)   /* valid index? */
  152.             break;
  153.  
  154.         fputs ("\nerror: invalid selection\n", stderr);
  155.     }
  156.  
  157.     fputs ("Enter refill amount : ", stdout);    /* prompt for quantity */
  158.     for (;;) {  /* loop continually until valid quantity entered */
  159.         char buf[MAXC];     /* temporary buffer for user input */
  160.         int result;         /* variable to capture return of sscanf */
  161.  
  162.         if (!fgets (buf, MAXC, stdin)) {    /* if user cancels input */
  163.             fputs ("(user canceled input)\n", stderr);
  164.             return -2;
  165.         }
  166.  
  167.         result = sscanf (buf, "%zu", &qty);
  168.         if (result == 1)
  169.             break;          /* valid quantity entered */
  170.     }
  171.  
  172.     drugs[index].qty += qty;        /* add refill quantity to drug */
  173.  
  174.     return 1;   /* return success */
  175. }
  176.  
  177. /* dispense menu */
  178. int dispensemenu (drug *drugs, size_t ndrugs)
  179. {
  180.     size_t  index,       /* drugs[index] of selected drug */
  181.             qty;        /* quantity of selected drug to refill */
  182.  
  183.     /* display heading */
  184.     fputs ("\nDispense which drug\n\n", stdout);
  185.  
  186.     /* display each drug in menu (I would add space between) */
  187.     for (size_t i = 0; i < ndrugs; i++)
  188.         printf ("%zu. %s\n", i + 1, drugs[i].name);
  189.  
  190.     fputs ("\nSelect drug number    : ", stdout);  /* prompt for drug */
  191.     for (;;) {  /* loop continually until valid drug selected */
  192.         char buf[MAXC];     /* temporary buffer for user input */
  193.  
  194.         if (!fgets (buf, MAXC, stdin)) {    /* if user cancels input */
  195.             fputs ("(user canceled input)\n", stderr);
  196.             return -2;
  197.         }
  198.  
  199.         index = *buf - '0' - 1;     /* convert from ASCII value to decimal */
  200.         if (isdigit (*buf) && index < ndrugs)   /* valid index? */
  201.             break;
  202.  
  203.         fputs ("\nerror: invalid selection\n", stderr);
  204.     }
  205.  
  206.     for (;;) {  /* loop continually until quantity < drug quantity entered */
  207.         char buf[MAXC];         /* temporary buffer for user input */
  208.         fputs ("Enter dispense amount : ", stdout);    /* prompt for quantity */
  209.         for (;;) {  /* loop continually until valid quantity entered */
  210.             int result;         /* variable to capture return of sscanf */
  211.  
  212.             if (!fgets (buf, MAXC, stdin)) {    /* if user cancels input */
  213.                 fputs ("(user canceled input)\n", stderr);
  214.                 return -2;
  215.             }
  216.  
  217.             result = sscanf (buf, "%zu", &qty);
  218.             if (result == 1)
  219.                 break;          /* valid number entered */
  220.         }
  221.         if (qty > drugs[index].qty) {   /* is that more than we have? */
  222.             fputs ("Not enough quantity\nRedispense? y or n\n", stdout);
  223.             if (!fgets (buf, MAXC, stdin)) {    /* if user cancels input */
  224.                 fputs ("(user canceled input)\n", stderr);
  225.                 return -2;
  226.             }
  227.             if (*buf == 'y')    /* if 1st char in buf is 'y', try again */
  228.                 continue;       /* iterate outer loop again */
  229.             return 0;           /* otherwise, does not want to dispense */
  230.         }
  231.         break;          /* if we get here - valid quantity entered */
  232.     }
  233.  
  234.     drugs[index].qty -= qty;        /* subtract dispense qty from drug */
  235.  
  236.     return 1;   /* return success */
  237. }
  238.  
  239. int mainmenu (drug *drugs, size_t ndrugs)
  240. {
  241.     for (;;) {  /* loop continually displaying menu until return */
  242.         char buf[MAXC];     /* temporary buffer for user input */
  243.  
  244.         fputs ( "\n\tDrug Inventory\n\n"    /* -- display menu -- */
  245.                 "1. Check Quantity\n"       /* all individual strings "..." */
  246.                 "2. Refill\n"               /* are concantenated */
  247.                 "3. Dispense\n"             /* so only 1 call to an output */
  248.                 "4. Quit\n\n"               /* function is required */
  249.                 "Select Option: ", stdout);
  250.  
  251.         /* read input into buf */
  252.         if (!fgets (buf, MAXC, stdin)) {    /* test if user canceled (EOF) */
  253.             fputs ("(user canceled input)\n", stderr);
  254.             return -2;
  255.         }
  256.  
  257.         /* use 1st character in buf to control next action. you can use buf[0]
  258.          * to access the 1st char (using array index notation) or simply
  259.          * dereference the pointer (e.g. *buf) using pointer notation. they are
  260.          * equivalent, e.g. buf[0] = *(buf + 0) which is just *buf.
  261.          */
  262.         switch (*buf) {
  263.             case '1':   drugqtymenu (drugs, ndrugs);
  264.                         break;          /* otherwise just break from switch */
  265.             case '2':   if (refillmenu (drugs, ndrugs) == -2)
  266.                             return -2;  /* if user canceled, return -2 */
  267.                         break;
  268.             case '3':   if (dispensemenu (drugs, ndrugs) == -2)
  269.                             return -2;  /* if user canceled, return -2 */
  270.                         break;
  271.             case '4':   return 1;
  272.             default:    fputs ("error: invalid selection.\n", stderr); break;
  273.         }
  274.     }
  275. }
  276.  
  277. int main (void) {
  278.  
  279.     drug drugs[] = {{ "Amoxil", 100 },  /* initialize array of struct drug */
  280.                     { "Vicodin", 20 },
  281.                     { "Tylenol", 90 },
  282.                     { "Zestril", 50 }};
  283.     auth users[] = {{ "Admin", "LetMeIn" }};
  284.     size_t ndrugs = sizeof drugs / sizeof *drugs,   /* no. of drugs in array */
  285.            nusers = sizeof users / sizeof *users;   /* no. of users in array */
  286.     int result;     /* varible to capture result of login */
  287.  
  288.     for (;;) {  /* loop continually until valid login (or user cancels) */
  289.         result = login (users, nusers); /* diplay login, get result */
  290.         if (result == -2)           /* result -2, user canceled input */
  291.             exit (EXIT_FAILURE);
  292.         else if (result >= 0)       /* result >= 0 (valid login) */
  293.             break;                  /* break loop and continue with program */
  294.         /* otherwise, login attemp failed, output error, try again */
  295.         fputs ("\nIncorrect username or password\n\n", stderr);
  296.     }
  297.  
  298.     printf ("\nWelcome: %s\n", users[result].user);
  299.  
  300.     mainmenu (drugs, ndrugs);   /* run main menu */
  301. }
  302.  
Add Comment
Please, Sign In to add comment