home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / TEXT / UTILITY / QUOTE24.ZIP / QUOTE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-04  |  18.6 KB  |  418 lines

  1. /*-------------------------------------------------------------------------*/
  2. /* Program:    Quote   .C                                                  */
  3. /* Purpose:    Displays quotes randomly selected from a quote database.    */
  4. /* Notes:      Compiles under TURBO C++, v1.0. Should work on any machine  */
  5. /*                running MS-DOS, v2.xx or higher.                         */
  6. /*             Must be compiled as a .COM file for config() to work.       */
  7. /* Status:     Source released into the public domain. If you find this    */
  8. /*                program useful, please send me a postcard.               */
  9. /* Updates:    21-Mar-87, GAT                                              */
  10. /*                - initial version (in assembler).                        */
  11. /*             24-Apr-87, GAT                                              */
  12. /*                - fixed bug in FindQuote when 1st $ at end of file.      */
  13. /*             27-Jan-88, GAT                                              */
  14. /*                - increased size of quote buffer from 256 bytes.         */
  15. /*             29-Jan-88, GAT                                              */
  16. /*                - prevented problem when pointing to very beginning of   */
  17. /*                   quote database.                                       */
  18. /*             11-Jun-89, GAT                                              */
  19. /*                - ported program to C.                                   */
  20. /*                - provided brief, on-line help message.                  */
  21. /*                - added repeat, clear-screen, and reconfigure options.   */
  22. /*             25-Jun-89, GAT                                              */
  23. /*                - changed long ints in Quote() to unsigned long ints.    */
  24. /*                   This avoids attempts to index negatively into file.   */
  25. /*             08-Jan-90, GAT                                              */
  26. /*                - Replaced #defines with enumerations.                   */
  27. /*                - Used AT&T's getopt() for program args.                 */
  28. /*                - Separated usage message into its own function.         */
  29. /*                - Tweaked variable storage.                              */
  30. /*                - Fixed bug in quote display if quote database began     */
  31. /*                   with a '$'.                                           */
  32. /*                - Fixed bug in quote display if SpotInFile overflowed.   */
  33. /*                - Changed unsigned long ints in display_Quote() back to  */
  34. /*                   long ints but avoided negative indices with labs().   */
  35. /*             02-Aug-90, GAT                                              */
  36. /*                - Rewrote display_Quote() to avoid bug when using small  */
  37. /*                   quote datafiles.                                      */
  38. /*                - Added lrand() to return a long int.                    */
  39. /*                - Quote database is now opened in binary mode to         */
  40. /*                   minimize hassle of working with CRs.                  */
  41. /*                - Quotes are displayed a character at a time so that     */
  42. /*                   CRs occuring in buffer with LFs can be dropped.       */
  43. /*                - Terminate main() by return rather than exit().         */
  44. /*             04-Apr-91, GAT                                              */
  45. /*                - Added copyright message.                               */
  46. /*                - Made use of new TifaWARE library functions.            */
  47. /*                - Removed explicit support for DEC Rainbows.             */
  48. /*                - Added calls to assert() for debugging.                 */
  49. /*                - Made casts in update_config() less convoluted.         */
  50. /*-------------------------------------------------------------------------*/
  51.  
  52. /*-------------------------------------------------------------------------*/
  53. /* Author:     George A. Theall                                            */
  54. /* Phone:      +1 215 662 0558                                             */
  55. /* SnailMail:  TifaWARE                                                    */
  56. /*             506 South 41st St., #3M                                     */
  57. /*             Philadelphia, PA.  19104   USA                              */
  58. /* E-Mail:     GTHEALL@PENNDRLS.UPENN.EDU (Internet)                       */
  59. /*-------------------------------------------------------------------------*/
  60.  
  61.  
  62. #ifndef __TINY__
  63.    #error *** Turbo C's TINY model required for compilation ***
  64. #endif
  65.  
  66. /* Useful type definitions. */
  67. typedef int BOOLEAN;
  68.  
  69. typedef enum {                               /* error classes             */
  70.    err_nrep,                                 /*    bad repetition count   */
  71.    err_cfg,                                  /*    program reconfigured   */
  72.    err_quote,                                /*    bad quote found        */
  73.    err_open,                                 /*    can't open a file      */
  74.    err_read,                                 /*    can't read from file   */
  75.    err_write                                 /*    can't write to file    */
  76. } ERR_TYPE;
  77.  
  78. typedef enum {                               /* return codes */
  79.    rc_ok    = 0,
  80.    rc_help  = 1,
  81.    rc_cfg   = 2,
  82.    rc_open  = 10,
  83.    rc_read  = 15,
  84.    rc_write = 20
  85. } RC_TYPE;
  86.  
  87. #define FALSE        0
  88. #define TRUE         1
  89. #ifndef VERS
  90. #  define VERS       "???"                   /* someone goofed in compiling */
  91. #endif
  92. #define MAX_QUOTE    512                     /* maximum size of a quote */
  93.  
  94. #include <assert.h>                          /* for assert() */
  95. #include <conio.h>                           /* clrscr() - via BIOS call */
  96. #include <dir.h>                             /* for fnsplit(), MAXFILE, etc */
  97. #include <limits.h>                          /* for CHAR_BIT */
  98. #include <stdarg.h>                          /* for va_arg, etc.. */
  99. #include <stdio.h>
  100. #include <stdlib.h>                          /* for exit(), getenv(), etc */
  101. #include <string.h>                          /* for strlwr() */
  102. #include <time.h>                            /* for randomize() */
  103. #include "tifa.h"                            /* TifaWARE library routines */
  104.  
  105. char ProgName[MAXFILE];                      /* space for filename */
  106. static char FullProgName[MAXPATH];           /* path, name, extension */
  107. static char QDat[MAXPATH] = "QUOTE.DAT";     /* includes space for path */
  108. static BOOLEAN                               /* option flags            */
  109.    cFlag = FALSE,                            /*    reconfigure program? */
  110.    hFlag = FALSE,                            /*    needs help?          */
  111.    sFlag = FALSE,                            /*    clear screen first?  */
  112.    vFlag = FALSE;                            /*    verbose reports?     */
  113. static int nReps = 1;                        /* number of repetitions */
  114.  
  115. /* Define the program's error messages. */
  116. /*
  117.  * NB: getopt() itself is responsible for generating the following
  118.  * error messages, which do not appear in the structure below:
  119.  *    ": illegal option -- %c"
  120.  *    ": option requires an argument -- %c"
  121.  */
  122. const static struct {
  123.    ERR_TYPE Type;
  124.    char *Msg;
  125. } Error[] = {
  126.    err_nrep,  ": invalid rep count; %d used instead",
  127.    err_cfg,   ": new configuration of %s accepted",
  128.    err_quote, ": invalid quote found around ==>%s<==",
  129.    err_open,  ": can't open %s",
  130.    err_read,  ": error while reading from %s",
  131.    err_write, ": unable to write new configuration to %s"
  132. };
  133.  
  134. void _setenvp(void) {};                      /* drop some start-up code */
  135.  
  136.  
  137. /*---  main  --------------------------------------------------------------+
  138. |  Purpose:    Main body of program.                                       |
  139. |  Notes:      none                                                        |
  140. |  Entry:      argc = argument count,                                      |
  141. |              argv = array of argument variables.                         |
  142. |  Exit:       Return code as enumerated by RC_TYPE.                       |
  143. +-------------------------------------------------------------------------*/
  144. int main(int argc, char *argv[])
  145. {
  146.    char ch;
  147.    void write_usage(void);
  148.    void update_config(void);
  149.    void display_quote(void);
  150.  
  151.    /* Store complete program name for later in case we're reconfiguring. */
  152.    if (_osmajor < 3)                            /* TURBO C extension! */
  153.       strcpy(FullProgName, "QUOTE.COM");        /* since argv[0] is NULL */
  154.    else
  155.       strcpy(FullProgName, *argv);
  156.  
  157.    /* Isolate program name to keep error messages neat. */
  158.    fnsplit(FullProgName, NULL, NULL, ProgName, NULL); /* TURBO C extension! */
  159.    *argv = strlwr(ProgName);                          /* TURBO C extension! */
  160.  
  161.    /* All options must appear before any filenames. */
  162.    while ((ch = getopt(argc, argv, "cr:s:v?")) != EOF)
  163.       switch (ch)
  164.       {
  165.          case 'c': cFlag = TRUE; break;      /* reconfigure program */
  166.          case 'r':                           /* multiple quotes */
  167.             if (strspn(optarg, "0123456789") != strlen(optarg))
  168.                write_errmsg(Error[err_nrep].Msg, nReps);    /* bad nreps */
  169.             else
  170.                nReps = atoi(optarg);
  171.             break;
  172.          case 's':                           /* clear screen */
  173.             sFlag = (*optarg == '+');
  174.             break;
  175.          case 'v': vFlag = TRUE; break;      /* verbose reporting */
  176.          case '?': hFlag = TRUE; break;      /* help needed or requested */
  177.          default:  ; /* EMPTY */             /* Unreached */
  178.       }
  179.    do
  180.    {
  181.       --argc;
  182.       ++argv;
  183.    } while (--optind);                       /* nb: optind >= 1 in getopt() */
  184.  
  185.    if (hFlag == TRUE || argc > 1)            /* no more than 1 file name */
  186.    {
  187.       write_usage();
  188.       return rc_help;
  189.    }
  190.  
  191.    /*
  192.     * At this point, argc should be 0 or 1, depending on whether
  193.     * default quote database is being used.
  194.     */
  195.    assert(argc == 0 || argc == 1);
  196.    if (argc == 1)
  197.       strcpy(QDat, *argv);
  198.    if (cFlag == TRUE)                        /* update configuration */
  199.    {
  200.       update_config();
  201.       return rc_cfg;
  202.    }
  203.    else                                      /* or display quote */
  204.    {
  205.       display_quote();
  206.       return rc_ok;
  207.    }
  208. }
  209.  
  210.  
  211. /*---  write_usage  -------------------------------------------------------+
  212. |  Purpose:    Provides a brief message about program usage.               |
  213. |  Notes:      none                                                        |
  214. |  Entry:      n/a                                                         |
  215. |  Exit:       n/a                                                         |
  216. +-------------------------------------------------------------------------*/
  217. void write_usage(void)
  218. {
  219.    fprintf(stderr,
  220.       "TifaWARE QUOTE, v" VERS ", displays randomly selected quotes.\n"
  221.       "Copyright (c) 1991 by TifaWARE/George A. Theall.\n"
  222.       "\n"
  223.       "Usage:  %s [options] [qdat]\n"
  224.       "\n"
  225.       "Options:\n"
  226.       "  -c      = configure program using current arguments\n"
  227.       "  -rn     = set the repetition count to n quotes\n"
  228.       "  -s[+,-] = clear/don't clear screen first\n"
  229.       "  -v      = verbose reporting\n"
  230.       "  -?      = provide this help message\n"
  231.       "\n"
  232.       "Qdat refers to the quote database. If it's not specified, then file\n"
  233.       "%s will be used.\n", ProgName, QDat);
  234. }
  235.  
  236.  
  237. /*---  update_config ------------------------------------------------------+
  238. |  Purpose:    Updates program configuration based on option settings.     |
  239. |  Notes:      Requires Turbo C's TINY model and a .COM file.              |
  240. |              This works only with MS-DOS!                                |
  241. |              Data structures being configured must be global.            |
  242. |              If DOS v2.xx or earlier is being used, then the program     |
  243. |                 must be named .\QUOTE.COM.                               |
  244. |              Program aborts if an error occurs updating configuration.   |
  245. |  Entry:      n/a                                                         |
  246. |  Exit:       n/a                                                         |
  247. +-------------------------------------------------------------------------*/
  248. void update_config(void)
  249. {
  250.    FILE *fp;
  251.  
  252.    if ((fp = fopen(FullProgName, "rb+")) == NULL)
  253.    {
  254.       write_errmsg(Error[err_open].Msg, FullProgName);   /* couldn't open */
  255.       exit(rc_open);
  256.    }
  257.  
  258.    /*
  259.     * Advance file pointer to each of options and write out their
  260.     * current values. NB: it must be recognized that .COM files
  261.     * are loaded into memory at offset 0x100 in DS.
  262.     */
  263.    fseek(fp, (char *) &QDat - (char *) 0x100, SEEK_SET);
  264.    fwrite(&QDat, 1, sizeof(QDat), fp);       /* Revise name of database */
  265.    fseek(fp, (char *) &sFlag - (char *) 0x100, SEEK_SET);
  266.    fwrite(&sFlag, 1, sizeof(sFlag), fp);     /* ... then clear-screen flag */
  267.    fseek(fp, (char *) &nReps - (char *) 0x100, SEEK_SET);
  268.    fwrite(&nReps, 1, sizeof(nReps), fp);     /* ... and repetition count */
  269.  
  270.    fclose(fp);
  271.    if (ferror(fp) != FALSE)
  272.    {
  273.       write_errmsg(Error[err_write].Msg, FullProgName);  /* couldn't write */
  274.       exit(rc_write);
  275.    }
  276.    write_errmsg(Error[err_cfg].Msg, FullProgName);    /* new configuration */
  277. }
  278.  
  279.  
  280. /*---  display_quote ------------------------------------------------------+
  281. |  Purpose:    Displays one or more quotes chosen at random.               |
  282. |              A buffer big enough for 2 quotes is used since this way     |
  283. |                 we're guarenteed to find at least one full quote.        |
  284. |  Notes:      Program aborts if an error occurs reading from quote file.  |
  285. |  Entry:      n/a                                                         |
  286. |  Exit:       n/a                                                         |
  287. +-------------------------------------------------------------------------*/
  288. void display_quote(void)
  289. {
  290.    char Buffer[MAX_QUOTE + 2],               /* buffer for 1 quote + 2 "$"s */
  291.       *qp;                                   /* pointer to a quote */
  292.    int ncbuf,                                /* num of chars in buffer */
  293.       nc2adjust;                             /* num of chars to adjust */
  294.    long fpos,                                /* some spot in file */
  295.       fsize;                                 /* file size */
  296.    FILE *fp;
  297.    long lrand(void);
  298.  
  299.    /*
  300.     * Open file and figure out its size. NB: fp is opened in binary
  301.     * mode so input buffer will contain CRs. Later they must be
  302.     * avoided when it comes time to print out the quote.
  303.     */
  304.    if ((fp = fopen(QDat, "rb")) == NULL)
  305.    {
  306.       write_errmsg(Error[err_open].Msg, QDat);  /* couldn't open */
  307.       exit(err_open);
  308.    }
  309.    fseek(fp, 0L, SEEK_END);
  310.    fsize = ftell(fp);
  311.  
  312.    randomize();   /* based on clock ticks */ /* TURBO C extension! */
  313.    if (sFlag != FALSE)
  314.       clrscr();                              /* TURBO C extension! */
  315.  
  316.    while (nReps-- > 0)
  317.    {
  318.  
  319.       /*
  320.        * Generate a random number between 0 and file size. Then
  321.        * move the file pointer there and fill up the buffer.
  322.        */
  323.       fpos = lrand() % fsize;
  324.       if (vFlag == TRUE)
  325.          write_errmsg(": reading from %s at offset %li..", QDat, fpos);
  326.       fseek(fp, fpos, SEEK_SET);
  327.       if ((ncbuf = fread(Buffer, sizeof(char), sizeof(Buffer), fp)) == 0)
  328.       {
  329.          write_errmsg(Error[err_read].Msg, QDat);  /* couldn't read */
  330.          exit(rc_read);
  331.       }
  332.  
  333.       /*
  334.        * Find first "$"; this will mark the end of the quote. NB: memchr()
  335.        * is used instead of strchr() because Buffer is *not* guaranteed to
  336.        * be null-terminated. Also, there must be at least 1 "$" in buffer.
  337.        */
  338.       if ((qp = memchr(Buffer, '$', ncbuf)) == NULL)
  339.       {
  340.          *(Buffer + ncbuf) = '\0';           /* omit junk in buffer */
  341.          write_errmsg(Error[err_quote].Msg, Buffer);  /* bad quote */
  342.          break;
  343.       }
  344.  
  345.       /*
  346.        * Calculate how far over characters must be shifted so
  347.        * the terminal "$" found above lies at end of buffer.
  348.        * Adjust file pointer accordingly. NB: If this adjustment
  349.        * would cause an error then don't fill up buffer; instead,
  350.        * read from bof the number of chars between Buffer and qp.
  351.        */
  352.       nc2adjust = Buffer + sizeof(Buffer) - qp - 1;
  353.       if (fpos < (long) nc2adjust)           /* can we read in this far? */
  354.       {
  355.          ncbuf = ((int) fpos) + qp - Buffer + 1;
  356.          fpos = 0L;
  357.          fseek(fp, 0L, SEEK_SET);
  358.       }
  359.       else
  360.       {
  361.          fpos -= nc2adjust;
  362.          fseek(fp, (-1 * (ncbuf + nc2adjust)), SEEK_CUR);
  363.          ncbuf = sizeof(Buffer);
  364.       }
  365.  
  366.       /*
  367.        * Read again from file into buffer. Nullify the final "$"
  368.        * so quote can be treated like a string. Then scan back-
  369.        * wards to find the start of the quote.
  370.        */
  371.       ncbuf = fread(Buffer, sizeof(char), ncbuf, fp);
  372.       *(Buffer + ncbuf - 1) = '\0';
  373.       qp = strrchr(Buffer, '$');
  374.  
  375.       /*
  376.        * If starting "$" was found, then advance pointer to start of
  377.        * quote; otherwise, bomb with an error message unless we're
  378.        * at the start of the file (no "$" at start of first quote).
  379.        */
  380.       if (qp != NULL)
  381.          ++qp;
  382.       else
  383.          if (fpos == 0)
  384.             qp = Buffer;
  385.          else
  386.          {
  387.             write_errmsg(Error[err_quote].Msg, Buffer);  /* bad quote */
  388.             break;
  389.          }
  390.  
  391.       /*
  392.        * If stdout were opened as a binary stream CRs would be
  393.        * duplicated should output be redirected to, say, LIST.
  394.        * Character-at-a-time output may be slow, but it works.
  395.        */
  396.       for (; *qp != '\0'; ++qp)
  397.          if (!(*qp == '\r' && *(qp+1) == '\n'))
  398.             fputc(*qp, stdout);
  399.       fputc('\n', stdout);
  400.    }                                         /* end, nReps */
  401.    fclose(fp);
  402. }
  403.  
  404.  
  405. /*---  lrand --------------------------------------------------------------+
  406. |  Purpose:    Returns a long int random deviate.                          |
  407. |  Notes:      This is inherently non-portable. It *may* work if negative  |
  408. |                 numbers are represented using two's complement notation  |
  409. |                 and sizeof(long) == 2 * sizeof(int).                     |
  410. |  Entry:      n/a                                                         |
  411. |  Exit:       random long int in range [0, LONG_MAX].                     |
  412. +-------------------------------------------------------------------------*/
  413. long lrand(void)
  414. {
  415.    return (((long) rand() << (sizeof(int) * CHAR_BIT))
  416.       + (rand() + (rand() % 2 << (sizeof(int) * CHAR_BIT - 1))));
  417. }
  418.