home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c500 / 1.ddi / SRC386.WPK / MEMOS.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-28  |  18.3 KB  |  765 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <ctype.h>
  5. #include <time.h>
  6.  
  7. #include "memos.h"
  8.  
  9. /* This program implements a simple memo facility.
  10.  * Memos may be added to a memo file, displayed
  11.  * on the screen, and deleted.
  12.  *
  13.  * Modified     by              reason
  14.  * ========     ==              ======
  15.  * 87/10/02     Steve McDowell  Initial implementation.
  16.  * 88/09/20     Steve McDowell  Fixed up some style issues,
  17.  *                              introduced use of TRUE and
  18.  *                              FALSE.
  19.  */
  20.  
  21. /* Define some constants to make the code more readable.
  22.  */
  23. #define TRUE        1
  24. #define FALSE       0
  25. #define NULLCHAR    '\0'
  26.  
  27. static const char FileName[] = { "memos.db" };
  28. static const char TempName[] = { "tempmemo.db" };
  29.  
  30. static MEMO_EL *  MemoHead      = NULL;
  31. static int        MemosModified = FALSE;
  32. static int        QuitFlag      = TRUE;
  33.  
  34. typedef enum {
  35.     INVALID,
  36.     HELP,
  37.     ADD,
  38.     DELETE,
  39.     REPLACE,
  40.     SHOW,
  41.     UP,
  42.     DOWN,
  43.     TOP,
  44.     TODAY,
  45.     SAVE,
  46.     QUIT
  47. } ACTION;
  48.  
  49. /* This table maps action keywords onto the "actions" defined
  50.  * above. The table also defines short forms for the keywords.
  51.  */
  52. typedef struct {
  53.     ACTION act;
  54.     char * keyword;
  55. } ACTION_MAP;
  56.  
  57. static ACTION_MAP KeywordMap[] = {
  58.     HELP,    "help",
  59.     HELP,    "h",
  60.     ADD,     "add",
  61.     ADD,     "a",
  62.     DELETE,  "delete",
  63.     DELETE,  "del",
  64.     REPLACE, "replace",
  65.     REPLACE, "rep",
  66.     SHOW,    "show",
  67.     SHOW,    "sh",
  68.     UP,      "up",
  69.     UP,      "u",
  70.     DOWN,    "down",
  71.     DOWN,    "d",
  72.     DOWN,    "",
  73.     TOP,     "top",
  74.     TODAY,   "today",
  75.     TODAY,   "tod",
  76.     SAVE,    "save",
  77.     SAVE,    "sa",
  78.     QUIT,    "quit",
  79.     QUIT,    "q",
  80.  
  81.     INVALID, "" };
  82.  
  83. /* Maximum buffer length (maximum length of line of memo).
  84.  */
  85. #define MAXLEN 80
  86.  
  87. /* Function prototypes.
  88.  */
  89. static TEXT_LINE *  AddLine();
  90. static MEMO_EL *    AddMemo();
  91. static MEMO_EL *    DeleteMemo();
  92. static MEMO_EL *    DoActions();
  93. static MEMO_EL *    DoDownAction();
  94. static MEMO_EL *    DoUpAction();
  95. static MEMO_EL *    EnterAMemo();
  96. static ACTION       GetAction();
  97. static void *       MemoMAlloc();
  98. static ACTION       PromptAction();
  99. static ACTION       ReadAction();
  100. static MEMO_EL *    ReadAMemo();
  101. static MEMO_EL *    ShowTodaysMemos();
  102.  
  103. extern int main( int argc, char * argv[] )
  104. /****************************************/
  105. {
  106.     int       index;
  107.     MEMO_EL * el;
  108.  
  109.     printf( "Memo facility\n" );
  110.  
  111. /* Check for a single argument that is a question mark,
  112.  * If found, then display the usage notes.
  113.  */
  114.     if( argc == 2  &&  strcmp( argv[1], "?" ) == 0 ) {
  115.         Usage();
  116.         exit( 0 );
  117.     }
  118.     ReadMemos();
  119.     MemosModified = FALSE;
  120.     QuitFlag      = FALSE;
  121.  
  122. /* Use the command line parameters, if any, as the first
  123.  * actions to be performed on the memos.
  124.  */
  125.     el = NULL;
  126.     for( index = 1; index < argc; ++index ) {
  127.         el = DoActions( el, GetAction( argv[index] ) );
  128.         if( QuitFlag ) {
  129.             return( FALSE );
  130.         }
  131.     }
  132.     HandleMemoActions( el );
  133.     return( FALSE );
  134. }
  135.  
  136. static void ReadMemos( void )
  137. /***************************/
  138.  
  139. /* Read the memos file, building the structure to contain it.
  140.  */
  141. {
  142.     FILE *    fid;
  143.     MEMO_EL * new_el;
  144.     MEMO_EL * prev_el;
  145.     int       mcount;
  146.  
  147.     fid = fopen( FileName, "r" );
  148.     if( fid == NULL ) {
  149.         printf( "Memos file not found."
  150.                 " Starting with no memos.\n" );
  151.         return;
  152.     }
  153.  
  154. /* Loop reading entire memos.
  155.  */
  156.     prev_el = NULL;
  157.     for( mcount = 0;; mcount++ ) {
  158.         new_el = ReadAMemo( fid );
  159.         if( new_el == NULL ) {
  160.             printf( "%d memo(s) found.\n", mcount );
  161.             fclose( fid );
  162.             return;
  163.         }
  164.         if( prev_el == NULL ) {
  165.             MemoHead = new_el;
  166.             new_el->prev = NULL;
  167.         } else {
  168.             prev_el->next = new_el;
  169.             new_el->prev = prev_el;
  170.         }
  171.         new_el->next = NULL;
  172.         prev_el = new_el;
  173.     }
  174. }
  175.  
  176. static int ReadLine( char buffer[], int len, FILE * fid )
  177. /*******************************************************/
  178.  
  179. /* Read a line from the memos file. Handle any I/O errors and
  180.  * EOF. Return the length read, not counting the newline on
  181.  * the end.
  182.  */
  183. {
  184.     if( fgets( buffer, len, fid ) == NULL ) {
  185.         if( feof( fid ) ) {
  186.             return( EOF );
  187.         }
  188.         perror( "Error reading memos file" );
  189.         abort();
  190.     }
  191.     return( strlen( buffer ) - 1 );
  192. }
  193.  
  194. static MEMO_EL * ReadAMemo( FILE * fid )
  195. /**************************************/
  196.  
  197. /* Read one memo, creating the memo structure and filling it
  198.  * in. Return a pointer to the memo (NULL if none read).
  199.  */
  200. {
  201.     MEMO_EL *   el;
  202.     int         len;
  203.     TEXT_LINE * line;
  204.     char        buffer[MAXLEN];
  205.  
  206.     len = ReadLine( buffer, MAXLEN, fid );
  207.     if( len == EOF ) {
  208.         return( NULL );
  209.     }
  210.  
  211. /* First line must be of the form "Date:" or "Date:YY/MM/DD":
  212.  */
  213.     if( (len != 5  &&  len != 13)
  214.             ||  strncmp( buffer, "Date:", 5 ) != 0 ) {
  215.         BadFormat();
  216.     }
  217.     buffer[len] = NULLCHAR;
  218.     el = MemoMAlloc( sizeof( MEMO_EL ) );
  219.     el->text = NULL;
  220.     strcpy( el->date, buffer + 5 );
  221.     line = NULL;
  222.     for( ;; ) {
  223.         len = ReadLine( buffer, MAXLEN, fid );
  224.         if( len == EOF ) {
  225.             BadFormat();
  226.         }
  227.         buffer[len] = NULLCHAR;
  228.         if( strcmp( buffer, "====" ) == 0 ) {
  229.             return( el );
  230.         }
  231.         line = AddLine( buffer, el, line );
  232.     }
  233. }
  234.  
  235. static TEXT_LINE * AddLine( char        buffer[],
  236.                             MEMO_EL *   el,
  237.                             TEXT_LINE * prevline )
  238. /************************************************/
  239.  
  240. /* Add a line of text to the memo, taking care of all the
  241.  * details of modifying the structure.
  242.  */
  243. {
  244.     TEXT_LINE * line;
  245.  
  246.     line = MemoMAlloc( sizeof( TEXT_LINE ) + strlen( buffer ) );
  247.     strcpy( line->text, buffer );
  248.     line->next = NULL;
  249.     if( prevline == NULL ) {
  250.         el->text = line;
  251.     } else {
  252.         prevline->next = line;
  253.     }
  254.     return( line );
  255. }
  256.  
  257. static ACTION PromptAction( void )
  258. /********************************/
  259.  
  260. /* The user didn't specify an action on the command line,
  261.  * so prompt for it.
  262.  */
  263. {
  264.     ACTION act;
  265.  
  266.     for( ;; ) {
  267.         printf( "\nEnter an action:\n" );
  268.         act = ReadAction();
  269.         if( act != INVALID ) {
  270.             return( act );
  271.         }
  272.         printf( "\nThat selection was not valid.\n" );
  273.         Help();
  274.     }
  275. }
  276.  
  277. static ACTION ReadAction( void )
  278. /******************************/
  279.  
  280. /* Read an action from the terminal.
  281.  * Return the action code.
  282.  */
  283. {
  284.     char buffer[80];
  285.  
  286.     if( gets( buffer ) == NULL ) {
  287.         perror( "Error reading action" );
  288.         abort();
  289.     }
  290.     return( GetAction( buffer ) );
  291. }
  292.  
  293. static ACTION GetAction( char buffer[] )
  294. /**************************************/
  295.  
  296. /* Given the string in the buffer, return the action that
  297.  * corresponds to it.
  298.  * The string in the buffer is first zapped into lower case
  299.  * so that mixed-case entries are recognized.
  300.  */
  301. {
  302.     ACTION_MAP * actmap;
  303.     char *       bufptr;
  304.  
  305.     for( bufptr = buffer; *bufptr != NULLCHAR; ++bufptr ) {
  306.         *bufptr = tolower( *bufptr );
  307.     }
  308.     for( actmap = KeywordMap; actmap->act != INVALID; ++actmap ) {
  309.         if( strcmp( buffer, actmap->keyword ) == 0 ) break;
  310.     }
  311.     return( actmap->act );
  312. }
  313.  
  314. static void HandleMemoActions( MEMO_EL * el )
  315. /*******************************************/
  316.  
  317. /* Handle all the actions entered from the keyboard.
  318.  */
  319. {
  320.     for( ;; ) {
  321.         el = DoActions( el, PromptAction() );
  322.         if( QuitFlag ) break;
  323.     }
  324. }
  325.  
  326. static MEMO_EL * DoActions( MEMO_EL * el, ACTION act )
  327. /****************************************************/
  328.  
  329. /* Perform one action on the memos.
  330.  */
  331. {
  332.     MEMO_EL * new_el;
  333.     MEMO_EL * prev_el;
  334.  
  335.     switch( act ) {
  336.       case HELP:
  337.         Help();
  338.         break;
  339.       case ADD:
  340.         new_el = AddMemo( el );
  341.         if( new_el != NULL ) {
  342.             el = new_el;
  343.             MemosModified = TRUE;
  344.         }
  345.         break;
  346.       case DELETE:
  347.         el = DeleteMemo( el );
  348.         MemosModified = TRUE;
  349.         break;
  350.       case REPLACE:
  351.         prev_el = el;
  352.         new_el = AddMemo( el );
  353.         if( new_el != NULL ) {
  354.             DeleteMemo( prev_el );
  355.             MemosModified = TRUE;
  356.         }
  357.         break;
  358.       case SHOW:
  359.         DisplayMemo( el );
  360.         break;
  361.       case UP:
  362.         el = DoUpAction( el );
  363.         break;
  364.       case DOWN:
  365.         el = DoDownAction( el );
  366.         break;
  367.       case TOP:
  368.         el = NULL;
  369.         break;
  370.       case TODAY:
  371.         el = ShowTodaysMemos();
  372.         break;
  373.       case SAVE:
  374.         if( SaveMemos() ) {
  375.             MemosModified = FALSE;
  376.         }
  377.         break;
  378.       case QUIT:
  379.         if( WantToQuit() ) {
  380.             QuitFlag = TRUE;
  381.             el = NULL;
  382.         }
  383.     }
  384.     return( el );
  385. }
  386.  
  387. static MEMO_EL * AddMemo( MEMO_EL * el )
  388. /**************************************/
  389.  
  390. /* Add a memo following the current one.
  391.  */
  392. {
  393.     MEMO_EL * new_el;
  394.     MEMO_EL * next;
  395.  
  396.     new_el = EnterAMemo();
  397.     if( new_el == NULL ) {
  398.         return( NULL );
  399.     }
  400.     if( el == NULL ) {
  401.         next = MemoHead;
  402.         MemoHead = new_el;
  403.     } else {
  404.         next = el->next;
  405.         el->next = new_el;
  406.     }
  407.     new_el->prev = el;
  408.     new_el->next = next;
  409.     if( next != NULL ) {
  410.         next->prev = new_el;
  411.     }
  412.     return( new_el );
  413. }
  414.  
  415. static MEMO_EL * EnterAMemo( void )
  416. /*********************************/
  417.  
  418. /* Read a memo from the keyboard, creating the memo structure
  419.  * and filling it in. Return a pointer to the memo (NULL if
  420.  * none read).
  421.  */
  422. {
  423.     MEMO_EL *   el;
  424.     int         len;
  425.     TEXT_LINE * line;
  426.     char        buffer[MAXLEN];
  427.  
  428.     printf( "What date do you want the memo displayed"
  429.             " (YY/MM/DD)?\n" );
  430.     if( gets( buffer ) == NULL ) {
  431.         printf( "Error reading from terminal.\n" );
  432.         return( NULL );
  433.     }
  434.     len = strlen( buffer );
  435.     if( len != 0
  436.             &&  (len != 8
  437.                 ||  buffer[2] != '/'
  438.                 || buffer[5] != '/') ) {
  439.         printf( "Date is not valid.\n" );
  440.         return( NULL );
  441.     }
  442.     el = MemoMAlloc( sizeof( MEMO_EL ) );
  443.     el->text = NULL;
  444.     strcpy( el->date, buffer );
  445.     line = NULL;
  446.     printf( "\nEnter the text of the memo.\n" );
  447.     printf( "To terminate the memo,"
  448.             " enter a line starting with =\n" );
  449.     for( ;; ) {
  450.         if( gets( buffer ) == NULL ) {
  451.             printf( "Error reading from terminal.\n" );
  452.             return( NULL );
  453.         }
  454.         if( buffer[0] == '=' ) {
  455.             return( el );
  456.         }
  457.         line = AddLine( buffer, el, line );
  458.     }
  459. }
  460.  
  461. static MEMO_EL * DeleteMemo( MEMO_EL * el )
  462. /*****************************************/
  463.  
  464. /* Delete the current memo.
  465.  * Return a pointer to another memo, usually the following one.
  466.  */
  467. {
  468.     MEMO_EL * prev;
  469.     MEMO_EL * next;
  470.     MEMO_EL * ret_el;
  471.  
  472.     if( el == NULL ) {
  473.         return( MemoHead );
  474.     }
  475.     prev = el->prev;
  476.     next = el->next;
  477.     ret_el = next;
  478.     if( ret_el == NULL ) {
  479.         ret_el = prev;
  480.     }
  481.  
  482. /* If it's the first memo, set a new MemoHead value.
  483.  */
  484.     if( prev == NULL ) {
  485.         MemoHead = next;
  486.         if( next != NULL ) {
  487.             next->prev = NULL;
  488.         }
  489.     } else {
  490.         prev->next = next;
  491.         if( next != NULL ) {
  492.             next->prev = prev;
  493.         }
  494.     }
  495.     DisposeMemo( el );
  496.     return( ret_el );
  497. }
  498.  
  499. static MEMO_EL * DoUpAction( MEMO_EL * el )
  500. /*****************************************/
  501.  
  502. /* Perform the UP action, including displaying the memo.
  503.  */
  504. {
  505.     if( el == NULL ) {
  506.         DisplayTop();
  507.     } else {
  508.         el = el->prev;
  509.         DisplayMemo( el );
  510.     }
  511.     return( el );
  512. }
  513.  
  514. static MEMO_EL * DoDownAction( MEMO_EL * el )
  515. /*******************************************/
  516.  
  517. /* Perform the DOWN action, including displaying the memo.
  518.  */
  519. {
  520.     MEMO_EL * next_el;
  521.  
  522.     next_el = (el == NULL) ? MemoHead : el->next;
  523.     if( next_el == NULL ) {
  524.         printf( "No more memos.\n" );
  525.     } else {
  526.         el = next_el;
  527.         DisplayMemo( el );
  528.     }
  529.     return( el );
  530. }
  531.  
  532. static MEMO_EL * ShowTodaysMemos( void )
  533. /**************************************/
  534.  
  535. /* Show all memos that either:
  536.  * (1) match today's date
  537.  * (2) don't have a date stored.
  538.  * Return a pointer to the last displayed memo.
  539.  */
  540. {
  541.     MEMO_EL * el;
  542.     MEMO_EL * last_el;
  543.     time_t    timer;
  544.     struct tm ltime;
  545.     char      date[9];
  546.  
  547. /* Get today's time in YY/MM/DD format.
  548.  */
  549.     time( &timer );
  550.     ltime = *localtime( &timer );
  551.     strftime( date, 9, "%y/%m/%d", <ime );
  552.     last_el = NULL;
  553.     for( el = MemoHead; el != NULL; el = el->next ) {
  554.         if( el->date[0] == NULLCHAR
  555.                 ||  strcmp( date, el->date ) == 0 ) {
  556.             DisplayMemo( el );
  557.             last_el = el;
  558.         }
  559.     }
  560.     return( last_el );
  561. }
  562.  
  563. static void DisplayMemo( MEMO_EL * el )
  564. /*************************************/
  565.  
  566. /* Display a memo on the screen.
  567.  */
  568. {
  569.     TEXT_LINE * tline;
  570.  
  571.     if( el == NULL ) {
  572.         DisplayTop();
  573.         return;
  574.     }
  575.     if( el->date[0] == NULLCHAR ) {
  576.         printf( "\nUndated memo\n" );
  577.     } else {
  578.         printf( "\nDated: %s\n", el->date );
  579.     }
  580.     for( tline = el->text; tline != NULL; tline = tline->next ) {
  581.         printf( "    %s\n", tline->text );
  582.     }
  583. }
  584.  
  585. static int SaveMemos( void )
  586. /**************************/
  587.  
  588. /* Save the memos to the memos file.
  589.  */
  590. {
  591.     FILE *      fid;
  592.     MEMO_EL *   el;
  593.     TEXT_LINE * tline;
  594.     char        buffer[20];
  595.  
  596.     if( MemoHead == NULL ) {
  597.         printf( "No memos to save.\n" );
  598.         return( FALSE );
  599.     }
  600.  
  601. /* Open a temporary filename in case something goes wrong
  602.  * during the save.
  603.  */
  604.     fid = fopen( TempName, "w" );
  605.     if( fid == NULL ) {
  606.         printf( "Unable to open \"%s\" for writing.\n", TempName );
  607.         printf( "Save not performed.\n" );
  608.         return( FALSE );
  609.     }
  610.     for( el = MemoHead; el != NULL; el = el->next ) {
  611.         sprintf( buffer, "Date:%s", el->date );
  612.         if( !WriteLine( buffer, fid ) ) {
  613.             return( FALSE );
  614.         }
  615.         tline = el->text;
  616.         for( ; tline != NULL; tline = tline->next ) {
  617.             if( !WriteLine( tline->text, fid ) ) {
  618.                 return( FALSE );
  619.             }
  620.         }
  621.         if( !WriteLine( "====", fid ) ) {
  622.             return( FALSE );
  623.         }
  624.     }
  625.  
  626. /* Now get rid of the old file, if it's there, then rename
  627.  * the new one.
  628.  */
  629.     fclose( fid );
  630.     fid = fopen( FileName, "r" );
  631.     if( fid != NULL ) {
  632.         fclose( fid );
  633.         if( remove( FileName ) != 0 ) {
  634.             perror( "Can't remove old memos file" );
  635.             return( FALSE );
  636.         }
  637.     }
  638.     if( rename( TempName, FileName ) != 0 ) {
  639.         perror( "Can't rename new memos file" );
  640.         return( FALSE );
  641.     }
  642.     return( TRUE );
  643. }
  644.  
  645. static int WriteLine( char * text, FILE * fid )
  646. /*********************************************/
  647. {
  648.     if( fprintf( fid, "%s\n", text ) < 0 ) {
  649.         perror( "Error writing memos file" );
  650.         return( FALSE );
  651.     }
  652.     return( TRUE );
  653. }
  654.  
  655. /* Routines for displaying HELP and other simple text.
  656.  */
  657.  
  658. static void Usage( void )
  659. /***********************/
  660. {
  661.     printf( "Usage:\n" );
  662.     printf( "    memos ?\n" );
  663.     printf( "         displays this text\n" );
  664.     printf( "  or\n" );
  665.     printf( "    memos\n" );
  666.     printf( "         prompts for all actions.\n" );
  667.     printf( "  or\n" );
  668.     printf( "    memos action\n" );
  669.     printf( "         performs the action.\n" );
  670.     printf( "         More than one action may be specified.\n" );
  671.     printf( "         action is one of:\n" );
  672.     ShowActions();
  673. }
  674.  
  675. static void ShowActions( void )
  676. /*****************************/
  677. {
  678.     printf( "            Help    (display this text)\n" );
  679.     printf( "            Add     (add new memo here)\n" );
  680.     printf( "            DELete  (delete current memo)\n" );
  681.     printf( "            REPlace (replace current memo)\n" );
  682.     printf( "            SHow    (show the current memo again)\n" );
  683.     printf( "            Up      (move up one memo)\n" );
  684.     printf( "            Down    (move down one memo)\n" );
  685.     printf( "            TOP     (move to the top of the list\n" );
  686.     printf( "            TODay   (display today's memos)\n" );
  687.     printf( "            SAve    (write the memos to disk)\n" );
  688. }
  689.  
  690. static void Help( void )
  691. /**********************/
  692. {
  693.     printf( "Choose one of:\n" );
  694.     ShowActions();
  695.     printf( "            Quit\n" );
  696. }
  697.  
  698. static void DisplayTop( void )
  699. /****************************/
  700. {
  701.     printf( "Top of memos.\n" );
  702. }
  703.  
  704. static int WantToQuit( void )
  705. /***************************/
  706.  
  707. /* Check to see if the memos have been modified, but not saved.
  708.  * If so, query the user to make sure that he/she wants to quit
  709.  * without saving the memos.
  710.  */
  711. {
  712.     char buffer[MAXLEN];
  713.  
  714.     if( !MemosModified  ||  MemoHead == NULL ) {
  715.         return( TRUE );
  716.     }
  717.     printf( "\nThe memos have been modified but not saved.\n" );
  718.     printf( "Do you want to leave without saving them?\n" );
  719.     gets( buffer );
  720.     return( tolower( buffer[0] ) == 'y' );
  721. }
  722.  
  723. static void BadFormat( void )
  724. /***************************/
  725. {
  726.     printf( "Invalid format for memos file\n" );
  727.     abort();
  728. }
  729.  
  730. static void * MemoMAlloc( int size )
  731. /**********************************/
  732.  
  733. /* Allocate the specified size of memory, dealing with the
  734.  * case of a failure by displaying a message and quitting.
  735.  */
  736. {
  737.     register char * mem;
  738.  
  739.     mem = malloc( size );
  740.     if( mem == NULL ) {
  741.         printf( "Unable to allocate %d characters of memory\n",
  742.                 size );
  743.         abort();
  744.     }
  745.     return( mem );
  746. }
  747.  
  748. static void DisposeMemo( MEMO_EL * el )
  749. /*************************************/
  750.  
  751. /* Dispose of a memo, including its lines.
  752.  */
  753. {
  754.     TEXT_LINE * tline;
  755.     TEXT_LINE * next;
  756.  
  757.     tline = el->text;
  758.     while( tline != NULL ) {
  759.         next = tline->next;
  760.         free( tline );
  761.         tline = next;
  762.     }
  763.     free( el );
  764. }
  765.