Category : C Source Code
Archive   : QUOTE25B.ZIP
Filename : QUOTE.C

 
Output of file : QUOTE.C contained in archive : QUOTE25B.ZIP
/*-------------------------------------------------------------------------*/
/* Program: Quote .C */
/* Purpose: Displays quotes randomly selected from a quote datafile. */
/* Notes: Compiles under Borland C++, v3.1. Should work on any */
/* machine running DOS, v2.xx or higher. */
/* Must be compiled as a .COM file for config() to work. */
/* Status: Source released into the public domain. If you find this */
/* program useful, I'd appreciate a postcard from you. */
/* Updates: 21-Mar-87, GAT */
/* - initial version (in assembler). */
/* 24-Apr-87, GAT */
/* - fixed bug in FindQuote when 1st $ at end of file. */
/* 27-Jan-88, GAT */
/* - increased size of quote buffer from 256 bytes. */
/* 29-Jan-88, GAT */
/* - prevented problem when pointing to very beginning of */
/* quote datafile. */
/* 11-Jun-89, GAT */
/* - ported program to C. */
/* - provided brief, on-line help message. */
/* - added repeat, clear-screen, and reconfigure options. */
/* 25-Jun-89, GAT */
/* - changed long ints in Quote() to unsigned long ints. */
/* This avoids attempts to index negatively into file. */
/* 08-Jan-90, GAT */
/* - Replaced #defines with enumerations. */
/* - Used AT&T's getopt() for program args. */
/* - Separated usage message into its own function. */
/* - Tweaked variable storage. */
/* - Fixed bug in quote display if quote datafile began */
/* with a '$'. */
/* - Fixed bug in quote display if SpotInFile overflowed. */
/* - Changed unsigned long ints in display_Quote() back to */
/* long ints but avoided negative indices with labs(). */
/* 02-Aug-90, GAT */
/* - Rewrote display_Quote() to avoid bug when using small */
/* quote datafiles. */
/* - Added lrand() to return a long int. */
/* - Quote datafile is now opened in binary mode to */
/* minimize hassle of working with CRs. */
/* - Quotes are displayed a character at a time so that */
/* CRs occuring in buffer with LFs can be dropped. */
/* - Terminate main() by return rather than exit(). */
/* 04-Apr-91, GAT */
/* - Added copyright message. */
/* - Made use of new TifaWARE library functions. */
/* - Removed explicit support for DEC Rainbows. */
/* - Added calls to assert() for debugging. */
/* - Made casts in update_config() less convoluted. */
/* 27-Feb-93, GAT, v2.5a */
/* - Cleaned up code and comments. */
/* - Switched stdout to binary mode so I could print */
/* quotes as strings rather than series of characters. *
/* 05-Jul-93, GAT, v2.5b */
/* - compiled with BCC 3.1. */
/*-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/
/* Author: George A. Theall */
/* SnailMail: TifaWARE */
/* 610 South 48th St */
/* Philadelphia, PA. 19143 */
/* U.S.A. */
/* E-Mail: [email protected] */
/* [email protected] */
/* [email protected] */
/* [email protected] */
/*-------------------------------------------------------------------------*/


#ifndef __TINY__
#error *** Turbo C's TINY model required for compilation ***
#endif

/* Useful type definitions. */
typedef int BOOLEAN;

typedef enum { /* error classes */
err_nrep, /* bad repetition count */
err_quote, /* bad quote found */
err_open, /* can't open a file */
err_read, /* can't read from file */
err_write /* can't write to file */
} ERR_TYPE;

typedef enum { /* return codes */
rc_ok = 0,
rc_help = 1,
rc_open = 10,
rc_read = 15,
rc_write = 20
} RC_TYPE;

#define FALSE 0
#define TRUE 1
#define MAX_QUOTE 512 /* maximum size of a quote */

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "tifa.h" /* TifaWARE library routines */

char ProgName[MAXFILE]; /* space for filename */
static char FullProgName[MAXPATH]; /* path, name, extension */
static char QDat[MAXPATH] = "quote.dat"; /* includes space for path */
static BOOLEAN /* option flags */
cFlag = FALSE, /* reconfigure program? */
hFlag = FALSE, /* needs help? */
sFlag = FALSE, /* clear screen first? */
vFlag = FALSE; /* verbose reports? */
static int nReps = 1; /* number of repetitions */

/* Define the program's error messages. */
/*
* NB: getopt() itself is responsible for generating the following
* error messages, which do not appear in the structure below:
* ": illegal option -- %c"
* ": option requires an argument -- %c"
*/
const static struct {
ERR_TYPE Type;
char *Msg;
} Error[] = {
{err_nrep, ": invalid rep count; %d used instead"},
{err_quote, ": invalid quote found around ==>%s<=="},
{err_open, ": can't open %s"},
{err_read, ": error reading from %s"},
{err_write, ": can't write to %s"}
};

void _setenvp(void) {}; /* drop some start-up code */


/*--- main --------------------------------------------------------------+
| Purpose: Main body of program. |
| Notes: none |
| Entry: argc = argument count, |
| argv = array of argument variables. |
| Exit: Return code as enumerated by RC_TYPE. |
+-------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
char ch;
void write_usage(void);
void update_config(void);
void display_quote(void);


/*
* Store complete program name in case we're reconfiguring.
* Also, isolate program name for reporting errors.
*/
strcpy(FullProgName, (_osmajor < 3) ? "quote.com" : strlwr(*argv));
fnsplit(FullProgName, NULL, NULL, ProgName, NULL);

/*
* Process commandline arguments. NB: all options must appear before
* any filename. Also, specifying more than one filename is invalid.
*/
while (EOF != (ch = getopt(argc, argv, "cr:s:v?")))
switch (ch) {
case 'c': cFlag = TRUE; break; /* change config */
case 'r': /* multiple quotes */
if (strspn(optarg, "0123456789") != strlen(optarg))
write_errmsg(Error[err_nrep].Msg, nReps);
else
nReps = atoi(optarg);
break;
case 's': sFlag = ('+' == *optarg); break; /* clear screen */
case 'v': vFlag = TRUE; break; /* verbose reporting */
case '?': hFlag = TRUE; break; /* help needed */
default: break; /*** UNREACHED ***/
}
argc -= optind;
if (TRUE == hFlag || argc > 1) {
write_usage();
return rc_help;
}
if (argc > 0) { /* alternate quote datafile? */
argv += optind;
strcpy(QDat, *argv);
}

/* All that's left is do whatever the user requested! */
if (TRUE == cFlag)
update_config();
else
display_quote();
return rc_ok;
}


/*--- write_usage -------------------------------------------------------+
| Purpose: Provides a brief message about program usage. |
| Notes: none |
| Entry: n/a |
| Exit: n/a |
+-------------------------------------------------------------------------*/
void write_usage(void)
{
fprintf(stderr,
"TifaWARE QUOTE, v" VERS_STR ", displays randomly selected quotes.\n"
"Copyright (c) 1991-1993 by TifaWARE/George A. Theall.\n"
"\n"
"Usage: %s [options] [qdat]\n"
"\n"
"Options:\n"
" -c = update program configuration\n"
" -rn = set repetition count to n\n"
" -s[+,-] = clear/don't clear screen first\n"
" -v = verbose reporting\n"
" -? = provide this help message\n"
"\n"
"Qdat refers to the quote datafile; it defaults to %s.\n",
ProgName, QDat);
}


/*--- update_config ------------------------------------------------------+
| Purpose: Updates program configuration based on option settings. |
| Notes: This is highly DOS-specific and hence non-portable! |
| Requires Turbo C's TINY model and COM file format. |
| Data structures being configured must be global. |
| Program aborts if an error occurs updating configuration. |
| Entry: n/a |
| Exit: n/a |
+-------------------------------------------------------------------------*/
void update_config(void)
{
FILE *fp;


if (NULL == (fp = fopen(FullProgName, "rb+"))) {
write_errmsg(Error[err_open].Msg, FullProgName);
exit(rc_open);
}

/*
* Advance file pointer to each of options and write out their
* current values. NB: recognize that COM files are loaded
* into memory at offset 0x100 in DS.
*/
fseek(fp, ((unsigned int) &QDat-0x100), SEEK_SET);
fwrite(&QDat, 1, sizeof(QDat), fp); /* Revise name of datafile */
fseek(fp, ((unsigned int) &sFlag-0x100), SEEK_SET);
fwrite(&sFlag, 1, sizeof(sFlag), fp); /* ... then clear-screen flag */
fseek(fp, ((unsigned int) &nReps-0x100), SEEK_SET);
fwrite(&nReps, 1, sizeof(nReps), fp); /* ... and repetition count */

fclose(fp);
if (ferror(fp)) {
write_errmsg(Error[err_write].Msg, FullProgName);
exit(rc_write);
}
printf("new configuration of %s accepted.\n", FullProgName);
}


/*--- display_quote ------------------------------------------------------+
| Purpose: Displays one or more quotes chosen at random. |
| A buffer big enough for 2 quotes is used since this way |
| we're guarenteed to find at least one full quote. |
| Quote datafile is opened in binary mode so input buffer |
| will contain CRs, which must then be skipped when the |
| quote is printed. |
| Notes: Program aborts if an error occurs reading from quote file. |
| Entry: n/a |
| Exit: n/a |
+-------------------------------------------------------------------------*/
void display_quote(void)
{
char Buffer[MAX_QUOTE+2], /* buffer for 1 quote + 2 "$"s */
*qp;
int ncbuf,
nc2adjust;
long fpos,
fsize;
FILE *fp;

long lrand(void);


/* Open quote file and determine its size. */
if (NULL == (fp = fopen(QDat, "rb"))) {
write_errmsg(Error[err_open].Msg, QDat);
exit(err_open);
}
fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);

/*
* Since quotes are read in binary mode, it's necessary either
* to strip out CRs or open stdout in binary mode. This prevents
* CRs from being duplicated when output is redirected.
*/
setmode(fileno(stdout), O_BINARY);

if (TRUE == sFlag)
clrscr();

/* See random number generator based on current time. */
randomize();

while (nReps--) {
fpos = lrand() % fsize; /* pick random spot in file */
if (TRUE == vFlag)
write_errmsg(": reading from %s at offset %li..", QDat, fpos);
fseek(fp, fpos, SEEK_SET);
if (0 == (ncbuf = fread(Buffer, sizeof(char), sizeof(Buffer), fp))) {
write_errmsg(Error[err_read].Msg, QDat);
exit(rc_read);
}

/*
* Find first "$"; this will mark the end of the quote. NB: memchr()
* is used instead of strchr() because Buffer is *not* guaranteed to
* be null-terminated. Also, there must be at least 1 "$" in buffer.
*/
if (NULL == (qp = memchr(Buffer, '$', ncbuf))) {
*(Buffer+ncbuf) = '\0'; /* omit junk in buffer */
write_errmsg(Error[err_quote].Msg, Buffer);
break;
}

/*
* Calculate how far over characters must be shifted so
* the terminal "$" found above lies at end of buffer.
* Adjust file pointer accordingly. NB: If this adjustment
* would cause an error then don't fill up buffer; instead,
* read from bof the number of chars between Buffer and qp.
*/
nc2adjust = Buffer + sizeof(Buffer) - qp - 1;
if (fpos < (long) nc2adjust) { /* can we read in this far? */
ncbuf = ((int) fpos) + qp - Buffer + 1;
fpos = 0L;
fseek(fp, 0L, SEEK_SET);
}
else {
fpos -= nc2adjust;
fseek(fp, (-1 * (ncbuf + nc2adjust)), SEEK_CUR);
ncbuf = sizeof(Buffer);
}

/*
* Read again from file into buffer. Nullify the final "$"
* so quote can be treated like a string. Then scan back-
* wards to find the start of the quote.
*/
ncbuf = fread(Buffer, sizeof(char), ncbuf, fp);
*(Buffer + ncbuf - 1) = '\0';
qp = strrchr(Buffer, '$');

/*
* If starting "$" was found, then advance pointer to start of
* quote; otherwise, bomb with an error message unless we're
* at the start of the file (no "$" at start of first quote).
*/
if (NULL != qp)
++qp;
else
if (0 == fpos)
qp = Buffer;
else {
write_errmsg(Error[err_quote].Msg, Buffer);
break;
}
puts(qp);
} /* end, nReps */
fclose(fp);
}


/*--- lrand --------------------------------------------------------------+
| Purpose: Returns a long int random deviate. |
| Notes: This is inherently non-portable. It *may* work if negative |
| numbers are represented using two's complement notation |
| and sizeof(long) == 2 * sizeof(int). |
| Entry: n/a |
| Exit: random long int in range [0, LONG_MAX]. |
+-------------------------------------------------------------------------*/
long lrand(void)
{
return (((long) rand() << (sizeof(int) * CHAR_BIT))
+ (rand() + (rand() % 2 << (sizeof(int) * CHAR_BIT - 1))));
}


  3 Responses to “Category : C Source Code
Archive   : QUOTE25B.ZIP
Filename : QUOTE.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/