home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume20 / index-db / part02 / screen.c
Encoding:
C/C++ Source or Header  |  1989-10-23  |  14.4 KB  |  845 lines

  1. #ifndef lint
  2. static char *RCSid = "$Header: /u5/davy/progs/index/RCS/screen.c,v 1.1 89/08/09 11:06:53 davy Exp $";
  3. #endif
  4. /*
  5.  * screen.c - screen management routines.
  6.  *
  7.  * David A. Curry
  8.  * Research Institute for Advanced Computer Science
  9.  * Mail Stop 230-5
  10.  * NASA Ames Research Center
  11.  * Moffett Field, CA 94035
  12.  * davy@riacs.edu
  13.  *
  14.  * $Log:    screen.c,v $
  15.  * Revision 1.1  89/08/09  11:06:53  davy
  16.  * Initial revision
  17.  * 
  18.  */
  19. #include <sys/param.h>
  20. #include <signal.h>
  21. #include <curses.h>
  22. #include <stdio.h>
  23. #include "defs.h"
  24.  
  25. static int    screen_inited = 0;    /* for use with set/reset modes    */
  26.  
  27. /*
  28.  * Menu used by disp_entries routine.
  29.  */
  30. static char    *disp_menu[] = {
  31.     "<RET> = next entry                   \"d\" = delete this entry\n",
  32.     "\"-\" = previous entry                 \"e\" = edit this entry\n",
  33.     "\"q\" = return to main menu",
  34.     0
  35. };
  36.  
  37. /*
  38.  * prompt_str - prompt the user for a string.
  39.  */
  40. prompt_str(row, col, promptstr, answer)
  41. char *promptstr, *answer;
  42. int row, col;
  43. {
  44.     char *line;
  45.     int len, col0;
  46.     register int c, i, j;
  47.  
  48.     /*
  49.      * Print the prompt at (row,col).
  50.      */
  51.     mvaddstr(row, col, promptstr);
  52.     refresh();
  53.  
  54.     /*
  55.      * Calculate "column zero", which is at the right
  56.      * end of the prompt.
  57.      */
  58.     col += strlen(promptstr);
  59.     line = answer;
  60.     col0 = col;
  61.     len = 0;
  62.  
  63.     /*
  64.      * Read characters till we get what we want.  The user
  65.      * is allowed basic EMACS-style line editing.
  66.      */
  67.     while ((c = getchar()) != EOF) {
  68.         switch (c) {
  69.         case CTRL(a):            /* beginning of line    */
  70.             col = col0;
  71.             break;
  72.         case CTRL(b):            /* back character    */
  73.             if (col > col0)
  74.                 col--;
  75.             break;
  76.         case CTRL(d):            /* delete character    */
  77.             /*
  78.              * If there's stuff in the string,
  79.              * delete this character.
  80.              */
  81.             if (len) {
  82.                 /*
  83.                  * Calculate string position of
  84.                  * character to delete.
  85.                  */
  86.                 i = col - col0;
  87.  
  88.                 /*
  89.                  * Shuffle the string "left" one
  90.                  * place.
  91.                  */
  92.                 while (i < len) {
  93.                     line[i] = line[i+1];
  94.                     i++;
  95.                 }
  96.  
  97.                 /*
  98.                  * Delete the character on the screen.
  99.                  */
  100.                 len -= 1;
  101.                 delch();
  102.             }
  103.  
  104.             break;
  105.         case CTRL(e):            /* end of line        */
  106.             col = col0 + len;
  107.             break;
  108.         case CTRL(f):            /* forward character    */
  109.             if ((col - col0) < len)
  110.                 col++;
  111.             break;
  112.         case CTRL(h):            /* backspace delete    */
  113.         case '\177':
  114.             /*
  115.              * If there's stuff in the string,
  116.              * delete the character.
  117.              */
  118.             if (len) {
  119.                 /*
  120.                  * Calculate position in string of
  121.                  * character to be deleted.
  122.                  */
  123.                 i = col - col0 - 1;
  124.  
  125.                 /*
  126.                  * Shuffle the string "left" one place.
  127.                  */
  128.                 while (i < len) {
  129.                     line[i] = line[i+1];
  130.                     i++;
  131.                 }
  132.  
  133.                 len -= 1;
  134.  
  135.                 /*
  136.                  * Delete the character on the screen.
  137.                  */
  138.                 move(row, --col);
  139.                 delch();
  140.             }
  141.             break;
  142.         case CTRL(k):            /* kill line        */
  143.             /*
  144.              * Clear the string.
  145.              */
  146.             if (len) {
  147.                 i = col - col0;
  148.  
  149.                 line[i] = '\0';
  150.                 len = i;
  151.  
  152.                 clrtoeol();
  153.             }
  154.             break;
  155.         case CTRL(l):            /* redraw screen    */
  156.             wrefresh(curscr);
  157.             break;
  158.         case '\n':            /* return the string    */
  159.             line[len] = '\0';
  160.             return;
  161.         default:            /* regular character    */
  162.             /*
  163.              * If it's the user's line-kill character,
  164.              * we'll accept that to kill the line too.
  165.              */
  166.             if (c == _tty.sg_kill) {
  167.                 move(row, col0);
  168.                 clrtoeol();
  169.                 col = col0;
  170.  
  171.                 *line = '\0';
  172.                 len = 0;
  173.             }
  174.             else {
  175.                 /*
  176.                  * If it's a printable character,
  177.                  * insert it into the string.
  178.                  */
  179.                 if ((c >= ' ') && (c < '\177')) {
  180.                     /*
  181.                      * Calculate position of character
  182.                      * in string.
  183.                      */
  184.                     i = col - col0;
  185.  
  186.                     /*
  187.                      * If we have to, move the
  188.                      * string "right" one place
  189.                      * to insert the character.
  190.                      */
  191.                     if (i < len) {
  192.                         for (j = len; j >= i; j--)
  193.                             line[j+1] = line[j];
  194.                     }
  195.  
  196.                     line[i] = c;
  197.                     len += 1;
  198.                     col++;
  199.  
  200.                     /*
  201.                      * Insert the character on the
  202.                      * screen.
  203.                      */
  204.                     insch(c);
  205.  
  206.                 }
  207.             }
  208.             break;
  209.         }
  210.  
  211.         /*
  212.          * Move the cursor.
  213.          */
  214.         move(row, col);
  215.         refresh();
  216.     }
  217. }
  218.  
  219. /*
  220.  * prompt_char - prompt the user for a single character, which must
  221.  *         be one of the ones in the "valid" string.
  222.  */
  223. prompt_char(row, col, promptstr, valid)
  224. char *promptstr, *valid;
  225. int row, col;
  226. {
  227.     char *index();
  228.     register int c;
  229.  
  230.     /*
  231.      * Print the prompt.
  232.      */
  233.     mvaddstr(row, col, promptstr);
  234.     clrtoeol();
  235.     refresh();
  236.  
  237.     /*
  238.      * Read characters...
  239.      */
  240.     while ((c = getchar()) != EOF) {
  241.         /*
  242.          * If it's not a valid one, beep
  243.          * and get another one.
  244.          */
  245.         if (index(valid, c) == NULL) {
  246.             putc('\007', stdout);
  247.             fflush(stdout);
  248.             continue;
  249.         }
  250.  
  251.         /*
  252.          * Add the character to the
  253.          * screen, and return it.
  254.          */
  255.         addch(c);
  256.         refresh();
  257.  
  258.         return(c);
  259.     }
  260. }
  261.  
  262. /*
  263.  * main_menu - we dispatch to database functions from here.
  264.  */
  265. main_menu(dbname)
  266. char *dbname;
  267. {
  268.     register char c;
  269.  
  270.     /*
  271.      * Set tty modes.
  272.      */
  273.     set_modes();
  274.  
  275.     /*
  276.      * Forever...
  277.      */
  278.     for (;;) {
  279.         /*
  280.          * Clear the screen.
  281.          */
  282.         clear();
  283.  
  284.         /*
  285.          * Print the name of the database and number of
  286.          * entries.
  287.          */
  288.         mvprintw(0, 0, "Database: %s  (%d entries)", dbname,
  289.              dbentries);
  290.  
  291.         /*
  292.          * Mention whether or not it's modified.
  293.          */
  294.         if (dbmodified)
  295.             addstr("  [modified]");
  296.  
  297.         /*
  298.          * Print the choices.
  299.          */
  300.         mvaddstr(3, 8, "a - Add new entry to database");
  301.         mvaddstr(4, 8, "f - Find entry in database");
  302.         mvaddstr(5, 8, "r - Read database entry by entry");
  303.         mvaddstr(6, 8, "s - Save modifications, do not exit");
  304.         mvaddstr(7, 8, "q - Save modifications, exit");
  305.         mvaddstr(8, 8, "x - Exit");
  306.  
  307.         /*
  308.          * Get one of the choices.
  309.          */
  310.         c = prompt_char(10, 0, "Command: ", "afqrsx\014");
  311.  
  312.         /*
  313.          * Dispatch the choice to the proper
  314.          * database function.
  315.          */
  316.         switch (c) {
  317.         case 'a':
  318.             add_entry();
  319.             break;
  320.         case 'f':
  321.             find_entry();
  322.             break;
  323.         case 'q':
  324.             save_bye(dbname);
  325.             break;
  326.         case 'r':
  327.             read_db();
  328.             break;
  329.         case 's':
  330.             save_db(dbname);
  331.             break;
  332.         case 'x':
  333.             byebye();
  334.             break;
  335.         case CTRL(l):            /* redraw screen    */
  336.             wrefresh(curscr);
  337.             break;
  338.         }
  339.     }
  340. }
  341.  
  342. /*
  343.  * disp_entries - display all database entries with DB_PRINT set, and
  344.  *          let the user choose what to do with the entry.
  345.  */
  346. disp_entries()
  347. {
  348.     int first, save = -1;
  349.     register int c, i, j;
  350.  
  351.     /*
  352.      * Clear the screen.
  353.      */
  354. top:    clear();
  355.  
  356.     /*
  357.      * Print the names of the fields.
  358.      */
  359.     for (i = 0; i < idx.idx_nlines; i++) {
  360.         mvprintw(i, 0, "%s%s", idx.idx_lines[i],
  361.              idx.idx_lines[i][0] ? ":" : "");
  362.     }
  363.  
  364.     /*
  365.      * Print the menu.
  366.      */
  367.     for (i=0; disp_menu[i] != NULL; i++)
  368.         mvaddstr(idx.idx_nlines+i+2, 10, disp_menu[i]);
  369.  
  370.     /*
  371.      * Find the first printable entry, and save its
  372.      * index in first.
  373.      */
  374.     for (first=0; first < dbentries; first++) {
  375.         if (db[first].db_flag & DB_PRINT)
  376.             break;
  377.     }
  378.  
  379.     /*
  380.      * Set current entry to either the first one
  381.      * or the saved one.
  382.      */
  383.     if (save < 0)
  384.         i = first;
  385.     else
  386.         i = save;
  387.  
  388.     /*
  389.      * Until we run out of entries...
  390.      */
  391.     while (i < dbentries) {
  392.         /*
  393.          * Print the entry.
  394.          */
  395.         for (j=0; j < idx.idx_nlines; j++) {
  396.             move(j, idx.idx_maxlen + 2);
  397.             clrtoeol();
  398.             addstr(db[i].db_lines[j]);
  399.         }
  400.  
  401.         /*
  402.          * Print current/total entry numbers.
  403.          */
  404.         mvprintw(idx.idx_nlines+7, COLS-15, "%5d/%-5d", i+1,
  405.              dbentries);
  406.  
  407.         /*
  408.          * See what they want to do with this entry.
  409.          */
  410.         c = prompt_char(idx.idx_nlines+6, 0, "Command: ",
  411.                 "-deq\n\014");
  412.  
  413.         /*
  414.          * Dispatch the command...
  415.          */
  416.         switch (c) {
  417.         case '\n':            /* go to next entry    */
  418.             /*
  419.              * Save this one.
  420.              */
  421.             save = i;
  422.  
  423.             /*
  424.              * Advance to next printable one.
  425.              */
  426.             for (i++; i < dbentries; i++) {
  427.                 if (db[i].db_flag & DB_PRINT)
  428.                     break;
  429.             }
  430.  
  431.             break;
  432.         case '-':            /* go to previous entry    */
  433.             /*
  434.              * Save this one.
  435.              */
  436.             save = i;
  437.  
  438.             /*
  439.              * Hunt for the previous printable one.
  440.              */
  441.             for (i--; i >= 0; i--) {
  442.                 if (db[i].db_flag & DB_PRINT)
  443.                     break;
  444.             }
  445.  
  446.             if (i < 0)
  447.                 i = first;
  448.  
  449.             break;
  450.         case 'd':            /* delete entry        */
  451.             /*
  452.              * See if they really want to delete it.
  453.              * If so, mark it as not valid, and
  454.              * re-sort the database.
  455.              *
  456.              * Because of the save variable, next
  457.              * time through we'll print the last
  458.              * one they looked at.
  459.              */
  460.             if (del_entry(&db[i])) {
  461.                 db[i].db_flag &= ~(DB_VALID | DB_PRINT);
  462.                 qsort(db, dbentries, sizeof(struct dbfile),
  463.                       dbsort);
  464.                 dbmodified = 1;
  465.                 dbentries--;
  466.                 goto top;
  467.             }
  468.  
  469.             save = i;
  470.             goto top;
  471.         case 'e':            /* edit entry        */
  472.             /*
  473.              * Let them edit the entry.
  474.              */
  475.             if (edit_entry(&db[i], "modified"))
  476.                 dbmodified = 1;
  477.  
  478.             save = i;
  479.             goto top;
  480.         case 'q':            /* return to main menu    */
  481.             return;
  482.         case '\014':            /* redraw screen    */
  483.             break;
  484.         }
  485.     }
  486. }
  487.  
  488. /*
  489.  * edit_entry - allow the user to edit a database entry.
  490.  */
  491. edit_entry(entry, word)
  492. struct dbfile *entry;
  493. char *word;
  494. {
  495.     int *len;
  496.     int col0;
  497.     char *line;
  498.     char tbuf[64];
  499.     char *malloc();
  500.     struct dbfile tmp;
  501.     register int c, i, j, row, col;
  502.  
  503.     /*
  504.      * Figure out where "column zero" is, to the
  505.      * right of the longest field name.
  506.      */
  507.     col0 = idx.idx_maxlen + 2;
  508.  
  509.     /*
  510.      * Clear the screen.
  511.      */
  512.     clear();
  513.  
  514.     /*
  515.      * Print the field names.
  516.      */
  517.     for (row = 0; row < idx.idx_nlines; row++) {
  518.         mvprintw(row, 0, "%s%s", idx.idx_lines[row],
  519.              idx.idx_lines[row][0] ? ":" : "");
  520.     }
  521.  
  522.     /*
  523.      * Allocate some space in a temporary entry, and copy
  524.      * the entry to be edited into it.  This way they can
  525.      * abort the edit.
  526.      */
  527.     for (i=0; i < idx.idx_nlines; i++) {
  528.         /*
  529.          * Allocate memory for this line.
  530.          */
  531.         if ((tmp.db_lines[i] = malloc(BUFSIZ)) == NULL) {
  532.             error("%s: out of memory.\n", pname, 0, 0, 0);
  533.             exit(1);
  534.         }
  535.  
  536.         /*
  537.          * Save the length of the entry, zero the
  538.          * memory.
  539.          */
  540.         tmp.db_lens[i] = entry->db_lens[i];
  541.         bzero(tmp.db_lines[i], BUFSIZ);
  542.  
  543.         /*
  544.          * Copy and print the line from the entry.
  545.          */
  546.         if (entry->db_lines[i]) {
  547.             strcpy(tmp.db_lines[i], entry->db_lines[i]);
  548.             mvaddstr(i, col0, entry->db_lines[i]);
  549.         }
  550.     }
  551.  
  552.     col = col0;
  553.     row = 0;
  554.  
  555.     move(row, col);
  556.     refresh();
  557.  
  558.     /*
  559.      * Now let them edit the entry.  We provide the basic EMACS-style
  560.      * control characters.
  561.      */
  562.     while ((c = getchar()) != EOF) {
  563.         /*
  564.          * Get the current line and line length.
  565.          */
  566.         line = tmp.db_lines[row];
  567.         len = &tmp.db_lens[row];
  568.  
  569.         switch (c) {
  570.         case CTRL(a):            /* beginning of line    */
  571.             col = col0;
  572.             break;
  573.         case CTRL(b):            /* back character    */
  574.             if (col > col0)
  575.                 col--;
  576.             break;
  577.         case CTRL(d):            /* delete character    */
  578.             if (*len) {
  579.                 /*
  580.                  * Calculate position of character
  581.                  * in string.
  582.                  */
  583.                 i = col - col0;
  584.  
  585.                 /*
  586.                  * Shuffle the string to the "left".
  587.                  */
  588.                 while (i < *len) {
  589.                     line[i] = line[i+1];
  590.                     i++;
  591.                 }
  592.  
  593.                 *len -= 1;
  594.  
  595.                 /*
  596.                  * Delete the character on the screen.
  597.                  */
  598.                 delch();
  599.             }
  600.  
  601.             break;
  602.         case CTRL(e):            /* end of line        */
  603.             col = col0 + *len;
  604.             break;
  605.         case CTRL(f):            /* forward character    */
  606.             if ((col - col0) < *len)
  607.                 col++;
  608.             break;
  609.         case CTRL(h):            /* backspace delete    */
  610.         case '\177':
  611.             if (*len) {
  612.                 /*
  613.                  * Calculate position of character to
  614.                  * delete.
  615.                  */
  616.                 i = col - col0 - 1;
  617.  
  618.                 /*
  619.                  * Shuffle string "left".
  620.                  */
  621.                 while (i < *len) {
  622.                     line[i] = line[i+1];
  623.                     i++;
  624.                 }
  625.  
  626.                 *len -= 1;
  627.  
  628.                 /*
  629.                  * Delete the character from the screen.
  630.                  */
  631.                 move(row, --col);
  632.                 delch();
  633.             }
  634.             break;
  635.         case CTRL(k):            /* kill line        */
  636.             /*
  637.              * Kill the line.
  638.              */
  639.             if (*len) {
  640.                 i = col - col0;
  641.  
  642.                 line[i] = '\0';
  643.                 *len = i;
  644.  
  645.                 clrtoeol();
  646.             }
  647.             break;
  648.         case CTRL(l):            /* redraw screen    */
  649.             wrefresh(curscr);
  650.             break;
  651.         case CTRL(n):            /* next line        */
  652.             /*
  653.              * Wrap around to the top if necessary.
  654.              */
  655.             if (++row >= idx.idx_nlines)
  656.                 row = 0;
  657.  
  658.             /*
  659.              * If nothing in this column, move to
  660.              * nearest non-empty column.
  661.              */
  662.             if ((col - col0) > tmp.db_lens[row])
  663.                 col = col0 + tmp.db_lens[row];
  664.             break;
  665.         case CTRL(p):            /* previous line    */
  666.             /*
  667.              * Wrap around if necessary.
  668.              */
  669.             if (--row < 0)
  670.                 row = idx.idx_nlines - 1;
  671.  
  672.             /*
  673.              * If nothing in this column, move to
  674.              * nearest non-empty column.
  675.              */
  676.             if ((col - col0) > tmp.db_lens[row])
  677.                 col = col0 + tmp.db_lens[row];
  678.             break;
  679.         case CTRL([):            /* save entry        */
  680.             /*
  681.              * Prompt for whether to save the entry.
  682.              */
  683.             sprintf(tbuf, "Save %s entry in database? ", word);
  684.             c = prompt_char(idx.idx_nlines+2, 0, tbuf, "YyNn\n");
  685.  
  686.             /*
  687.              * See what they said.
  688.              */
  689.             switch (c) {
  690.             case '\n':        /* never mind        */
  691.                 move(idx.idx_nlines+2, 0);
  692.                 clrtoeol();
  693.                 break;
  694.             case 'Y':        /* save entry        */
  695.             case 'y':
  696.                 /*
  697.                  * Copy the temporary entry into the real
  698.                  * entry.
  699.                  */
  700.                 for (i=0; i < idx.idx_nlines; i++) {
  701.                     if (entry->db_lines[i])
  702.                         free(entry->db_lines[i]);
  703.  
  704.                     entry->db_lines[i] = savestr(tmp.db_lines[i]);
  705.                     entry->db_lens[i] = tmp.db_lens[i];
  706.                     free(tmp.db_lines[i]);
  707.                 }
  708.  
  709.                 return(1);
  710.             case 'N':        /* don't save entry    */
  711.             case 'n':
  712.                 /*
  713.                  * Free temporary memory.
  714.                  */
  715.                 for (i=0; i < idx.idx_nlines; i++)
  716.                     free(tmp.db_lines[i]);
  717.                 return(0);
  718.             }
  719.             break;
  720.         case '\n':            /* go to next line    */
  721.             /*
  722.              * Wrap around if necessary.
  723.              */
  724.             if (++row >= idx.idx_nlines)
  725.                 row = 0;
  726.             col = col0;
  727.             break;
  728.         default:            /* something else    */
  729.             /*
  730.              * If it's the user's kill character, we'll
  731.              * accept that to delete the line too.
  732.              */
  733.             if (c == _tty.sg_kill) {
  734.                 move(row, col0);
  735.                 clrtoeol();
  736.                 col = col0;
  737.  
  738.                 *line = '\0';
  739.                 *len = 0;
  740.             }
  741.             else {
  742.                 /*
  743.                  * If it's a printable character, insert
  744.                  * it into the string.
  745.                  */
  746.                 if ((c >= ' ') && (c < '\177')) {
  747.                     /*
  748.                      * Calculate character position.
  749.                      */
  750.                     i = col - col0;
  751.  
  752.                     /*
  753.                      * If necessary, move the string
  754.                      * to the "right" to insert the
  755.                      * character.
  756.                      */
  757.                     if (i < *len) {
  758.                         for (j = *len; j >= i; j--)
  759.                             line[j+1] = line[j];
  760.                     }
  761.  
  762.                     line[i] = c;
  763.                     *len += 1;
  764.                     col++;
  765.  
  766.                     /*
  767.                      * Insert the character on the
  768.                      * screen.
  769.                      */
  770.                     insch(c);
  771.                 }
  772.             }
  773.             break;
  774.         }
  775.  
  776.         /*
  777.          * Move to the current row/column.
  778.          */
  779.         move(row, col);
  780.         refresh();
  781.     }
  782. }
  783.  
  784. /*
  785.  * reset_modes - restore tty modes to their original state.
  786.  */
  787. reset_modes()
  788. {
  789.     /*
  790.      * No need.
  791.      */
  792.     if (!screen_inited)
  793.         return;
  794.  
  795.     screen_inited = 0;
  796.  
  797.     /*
  798.      * Move to bottom of screen.
  799.      */
  800.     move(LINES-1, 0);
  801.     refresh();
  802.  
  803.     /*
  804.      * Restore modes.
  805.      */
  806.     nocbreak();
  807.     echo();
  808.  
  809.     /*
  810.      * Turn curses "off".
  811.      */
  812.     endwin();
  813.  
  814.     signal(SIGQUIT, SIG_DFL);
  815.     signal(SIGINT, SIG_DFL);
  816. }
  817.  
  818. /*
  819.  * set_modes - set tty modes to what we want.
  820.  */
  821. set_modes()
  822. {
  823.     /*
  824.      * Already done.
  825.      */
  826.     if (screen_inited)
  827.         return;
  828.  
  829.     screen_inited = 1;
  830.  
  831.     /*
  832.      * Ignore signals.
  833.      */
  834.     signal(SIGQUIT, SIG_IGN);
  835.     signal(SIGINT, SIG_IGN);
  836.  
  837.     /*
  838.      * Turn "curses" on, turn echo off, turn cbreak on.
  839.      */
  840.     initscr();
  841.     noecho();
  842.     cbreak();
  843. }
  844.  
  845.