/* quote (c) copyright 1985 Mark E. Mallett Quote program Permission is granted to distribute this program indiscriminately. Please leave all credits in place. If you change it, please try to live with the coding style. -- Edit history -- 850329 MEM Original version 850423 MEM Add the -s option 850427 MEM Add the -r option */ /* Configuration: */ #define ISCPM /* Define this if CP/M system */ #define Z80 /* Define this if Z80 CPU */ #include "stdio.h" #ifdef ISCPM #include "cpm.h" #endif /* Local definitions */ #define _VMAJ 1 /* Major version number */ #define _VMIN 1 /* Minor version number */ #define isalpha(c) (isupper(c) || islower(c)) #define IND static /* For variables with no allocation dependancies (don't have to be on the stack). "static" works best on Z-80, which is not stack-oriented. For stack machines, remove the word "static". */ #define TRUE 1 #define FALSE 0 #define NUL '\000' #ifdef ISCPM typedef /* date/time structure */ struct { int DAT_DAT; /* Days since Jan 1 1978 */ char DAT_HRS; /* BCD hours */ char DAT_MIN; /* BCD minutes */ } DAT; /* Date */ #endif /* External variables */ /* External routines */ extern long ftell(); /* Tell file position */ /* Internal variables (public) */ /* Internal routines */ char *newstr(); /* Local variables */ static char *Alist = {NULL}; /* Acceptance/rejection list */ static int Cnsflg = {FALSE}; /* Copyright suppression flag */ static int Creflg = {FALSE}; /* Create-index mode */ static char *Exline = {NULL}; /* Address of command line on exit */ static FILE *Ifp = {NULL}; /* File var for index file */ static char *Inxfnm = {NULL}; /* Points to index filename */ static int Qcnt = {0}; /* Number of quotes */ static FILE *Qfp = {NULL}; /* File var for quotefile */ static char *Quofnm = {NULL}; /* Points to quote filename */ static int Rejflg = TRUE; /* TRUE if Alist is rejection list */ static int Rptfac = {1}; /* Repeat factor */ static int Rseed; /* Random number seed */ /* Local (forward) routine defs */ /* *//* main The main routine */ main (argc, argv) int argc; char **argv; { IND int c; /* Character var */ IND int i; /* Scratch */ IND int n; /* NEXT index after arg */ IND char *aptr; /* ptr to arg, if any */ char cmdlin[100]; /* to keep command line in */ for (i = 1; i < argc; i++) /* Look at command line arguments */ { if (argv[i][0] != '-') /* Be tough */ { printf ("Illegal argument %s\n", argv[i]); exiter(0); } c = argv[i][1]; /* Look at option char */ c = tolower(c); aptr = NULL; /* Init pot. arg pointer */ if (argv[i][2] != NUL) /* If anything after option char */ { aptr = &argv[i][2]; /* then that is it */ n = i; /* Value of next i */ } else if (i < argc) /* Only if there is one */ { n = i+1; /* Arg is next one */ aptr = argv[n]; /* Point to it */ } switch (c) /* Branch on char */ { case 'c': /* Create-index mode */ Creflg = TRUE; /* Set mode flag */ break; #ifdef ISCPM case 'e': /* Command to use on exit */ strcpy (cmdlin, aptr); /* Init command... */ for (i = n+1; i < argc; i++) { strcat (cmdlin, " "); /* Add separator */ strcat (cmdlin, argv[i]); } Exline = newstr (cmdlin); /* Remember exit line */ break; #endif case 'i': /* Index file name */ if (Inxfnm != NULL) /* Already got? */ { printf ("Duplicate -i seen\n"); exiter(); } Inxfnm = aptr; /* Remember index filename */ i = n; /* Remember next arg position */ break; case 'n': /* "not" */ Rejflg = TRUE; /* Set "not" flag */ Alist = aptr; /* remember rejection list */ i = n; break; case 'o': /* "only" */ Rejflg = FALSE; /* Alist is acceptance */ Alist = aptr; /* remember acceptance list */ i = n; break; case 'q': /* Quotefile name */ if (Quofnm != NULL) /* Already got? */ { printf ("Duplicate -q seen\n"); exiter(); } Quofnm = aptr; /* Remember quote filename */ i = n; /* Point to next arg */ break; case 'r': /* Repeat factor... */ Rptfac = atoi(aptr); /* Get the number */ if (Rptfac < 1) /* Let's be reasonable */ { printf ("Invalid repeat factor %d... dummy!!\n", Rptfac); exiter(); } i = n; /* Switch to right arg */ break; case 's': /* Suppress copyright? */ Cnsflg = TRUE; break; default: /* Something else */ printf ("Ignoring illegal option %s\n", argv[i]); } } /* Supposed to do this for protection, so... */ if (!Cnsflg) /* If not suppressing it */ printf ("QUOTE vsn %d.%d; (C) 1985 Mark E. Mallett.\n",_VMAJ,_VMIN); if (Creflg) /* Making index? */ { makinx(); /* Make index */ exiter(); } pquote(); /* Print quote */ exiter(); } /* *//* pquote() Print quote from scratch Accepts : Returns : */ pquote () { IND int c; /* Character */ IND int i; /* Scratch */ IND int qnum; /* Quote number selected */ IND long qibase; /* fpos of first index */ IND long qiloc; /* fpos of desired index */ IND long qfloc; /* Location of quote */ char fname[50]; /* Holds file name from inx file */ char qfline[150]; /* Quotefile line */ if (Inxfnm == NULL) /* Checkout index filename */ Inxfnm = "QUOTE.INX"; /* use default */ if ((Ifp = fopen(Inxfnm, "r")) == NULL) /* Try to open index file */ { printf ("Can not open index file %s\n", Inxfnm); return; } fgets (fname, 50, Ifp); /* Read name of quote file */ for (i = 0; i < 50; i++) if ((fname[i] == '\n') || (fname[i] == NUL)) break; fname[i] = NUL; if (Quofnm == NULL) /* Now make association by default */ Quofnm = newstr(fname); if ((Qfp = fopen(Quofnm, "r")) == NULL) /* Try to open quote filename */ { printf ("Can not open quote file %s\n", Quofnm); fclose (Ifp); return; } fread (&Qcnt, sizeof(int), 1, Ifp); /* Get # of quotes */ if (Qcnt <= 0) /* Trap bad value */ { printf ("No quotes indexed.\n"); return; } qibase = ftell (Ifp); /* Start of quote index */ while (Rptfac--) /* Print correct number of quotes */ { qnum = irand(Qcnt); /* Generate random quote number */ qiloc = qibase + (qnum*sizeof(long)); /* Where index is for this quote */ fseek (Ifp, qiloc, 0); /* Find this index */ fread (&qfloc, sizeof(long), 1, Ifp); /* Get quote-file location */ fseek (Qfp, qfloc, 0); /* Position there */ printf ("\n"); /* Give separation */ while (fgets (qfline, 150, Qfp) == qfline) { if (qfline[0] == '>') /* If end of quote */ break; for (i = 0; i < 150; i++) /* Make line pretty */ { c = qfline[i]; if ((c == '\n') || (c == NUL)) break; } qfline[i] = NUL; printf ("%s\n", qfline); } printf ("\n"); /* Separate */ } fclose (Ifp); fclose (Qfp); } /* *//* makinx() Make index file using information gathered elsewhere (i.e. from the command line). Accepts : Inxfnm: Addr of name of index file name Quofnm: Addr of name of quote filename Returns : */ makinx() { IND long qcmark; /* Where to write quote-count */ IND long qfpos; /* Quote-file positional marker */ IND int c; /* Character */ IND int i; /* Scratch */ IND int blank; /* Blank-line flag */ IND int dotcnt; /* dot count. . . */ IND int rejcnt; /* Reject count */ char qfline[150]; /* Quotefile line */ if (Quofnm == NULL) /* See if quote filename given */ Quofnm = "QUOTE.TXT"; /* use default */ if (Inxfnm == NULL) /* Ditto for index filename */ Inxfnm = "QUOTE.INX"; if ((Qfp = fopen(Quofnm, "r")) == NULL) /* Open up date file */ { printf ("Can not open file %s for input.\n", Quofnm); exiter(); } if ((Ifp = fopen(Inxfnm, "w")) == NULL) /* Open up index file */ { printf ("Can not open file %s for output.\n", Inxfnm); fclose (Qfp); exiter(); } fprintf (Ifp, "%s\n", Quofnm); /* Make quote file association in index file. */ Qcnt = 0; /* Init quote counter */ rejcnt = 0; /* Init reject count */ qcmark = ftell(Ifp); /* This is where the count is */ fwrite (&Qcnt, sizeof(int), 1, Ifp); /* Write the quote count */ printf ("Constructing quote index."); dotcnt = 12; while (fgets (qfline, 150, Qfp) == qfline) { /* Scan the quotes file */ if (qfline[0] == '!') /* Allow comments inbetween quotes */ continue; for (i = 0; i < 150; i++) /* make it nul-terminated */ { c = qfline[i]; /* Get char */ if ((c == '\n') || (c == NUL)) break; } qfline[i] = NUL; blank = TRUE; /* Presume blank line */ for (i = 0; i < 150; i++) { c = qfline[i]; if (c == NUL) break; if ((c != ' ') && (c != ' ')) { /* Space or tab */ blank = FALSE; break; } } if (blank) /* If blank line */ continue; /* ignore it */ /* Found start of quote */ if (++dotcnt <= 39) printf (" ."); else { printf ("\n."); dotcnt = 0; } if (qacc(qfline)) /* If it is acceptable */ { qfpos = ftell(Qfp); /* Find queuefile posn */ fwrite (&qfpos, sizeof(long), 1, Ifp); Qcnt++; /* Write marker and bump count */ } else /* Not acceptable */ rejcnt++; /* bump reject count */ while (fgets(qfline, 150, Qfp) == qfline) { /* Skip to end of quote */ if (qfline[0] == '>') /* End marker.. */ break; } } /* Done with quote file */ fseek (Ifp, qcmark, 0); /* Go to the count marker */ fwrite (&Qcnt, sizeof(int), 1, Ifp); /* Re-write the counter */ printf ("\nIndex constructed; %d quote", Qcnt); if (Qcnt != 1) printf ("s"); printf (" included"); if (rejcnt != 0) { printf ("; %d quote", rejcnt); if (rejcnt != 1) printf ("s"); printf (" rejected"); } printf (".\n"); fclose (Ifp); /* Index file is done. . . */ fclose (Qfp); } /* *//* qacc (idline) Determine acceptance of quote based on its identification line Accepts : idline identification line of the quote, consisting of keywords describing the quote. Returns : TRUE if acceptable FALSE if not */ qacc (idline) char *idline; /* Identification line */ { IND char *idptr, *alptr; /* Pointers */ char idname[50], alname[50]; /* Holds names */ if (Alist == NULL) /* If no accept/reject list */ return (TRUE); /* then we always succeed */ idptr = idline; /* Setup pointer to list */ while (TRUE) /* Do this for length of id line */ { idptr = xname(idptr, &idname); /* Extract name */ if (idname[0] == NUL) /* If no more names */ break; /* then be done. */ alptr = Alist; /* Check each name in acc/rej list */ while (TRUE) { alptr = xname (alptr, &alname); /* Extract name */ if (alname[0] == NUL) /* If no more names */ break; /* be done. */ if (nccmp(idname, alname) == 0) /* If names match */ return (!Rejflg); /* then return negative of the sense of rejection */ } } /* Here no names matched; return whatever sense of rejection we're in. */ return (Rejflg); } /* *//* xname (ptr, name) Extracts a name from a string. Accepts : ptr Address of the string name Address of the name buffer Returns : Address of pointer to rest of string name Filled with name. Note: A "name" is considered to be a collection of alphanumeric characters. */ char *xname (ptr, name) char *ptr; char *name; { IND int c; /* Character */ while ((c = *ptr++) != NUL) /* Skip to first alphanumeric */ if (isalpha(c) || isdigit(c)) break; while (isalpha(c) || isdigit(c)) /* Save string of alphanumerics */ { *name++ = c; /* Store char */ c = *ptr++; /* Fetch next one. */ } *name = NUL; /* Tie off the name */ if (c == NUL) /* If end of string */ ptr--; /* don't skip past it. */ return (ptr); /* Done. */ } /* *//* exiter() Exits to the system, with proper command line. */ exiter() { #ifdef ISCPM if (Exline != NULL) { strcpy (0x80, Exline); CPM (0x2F, 0xFF); } #endif exit(0); } /* *//* nccmp(s1,s2) Compare strings without case sensitivity Accepts : s1 Addr of first string s2 Addr of second string Returns : -1 if s1 < s2 0 if s1 = s2 1 if s1 > s2 */ nccmp (s1,s2) char *s1; /* String 1 */ char *s2; /* String 2 */ { IND int c1,c2; /* Chars */ while (TRUE) { c1 = *s1++; c1 = toupper(c1); c2 = *s2++; c2 = toupper(c2); if (c1 < c2) return (-1); if (c1 > c2) return (1); if (c1 == NUL) return(0); } } /* *//* newstr (str) Makes a copy of a string Accepts : str Addr of string to copy Returns : Address of copy */ char *newstr (str) char *str; /* String to copy */ { IND char *sptr; /* Points to new string */ sptr = calloc (1, strlen(str)+1); /* Make space for new string */ strcpy (sptr, str); /* Copy it */ return (sptr); /* Return it */ } /* *//* irand (max) Return random number from 0 to max-1 Accepts : max Maximum value Returns : random number from 0 to max-1 inclusive Notes: Conditional code for specific operating systems must be written here. */ irand (max) int max; /* Top end */ { int rslt; /* Resultant value */ #ifdef Z80 /* If z80 */ rslt += (getrr() >> 2); /* Add r-register to whatever garbage rslt is inited to */ #endif #ifdef ISCPM { DAT date; /* Date structure */ int it, id, x; /* time, date, x */ CPM (_MRGDT, &date); /* Call system to get date */ id = date.DAT_DAT; /* Get days since Jan 1 1978 */ x = (date.DAT_HRS>>4)&0x0F; x = x*10 + (date.DAT_HRS&0x0F); x = x*6 + ((date.DAT_MIN>>4)&0x0F); it = x*10 + (date.DAT_MIN&0x0F); rslt += (it+id); /* Get some wierd result */ } #endif Rseed += Rseed*13+rslt; if (Rseed < 0) Rseed = -Rseed; return (Rseed%max); /* What the heck */ } #ifdef Z80 /* *//* getrr() Returns the value of the Z80's refresh register */ #asm public getrr_ getrr_: DB 0EDH,05FH ;LD A,R MVI H,0 ;Put into HL MOV L,A RET #endasm