home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / BEEHIVE / UTILITYS / CHX8012B.ARC / CHECKS.C < prev    next >
Text File  |  1990-07-21  |  31KB  |  812 lines

  1. /*  checks.c -- main source file for check register program                  */
  2.  
  3. /*  copyright (c) 1986 by Jim Woolley and WoolleyWare, San Jose, CA          */
  4.  
  5. /*  vers. 1.0, 12/85 thru 5/86
  6.  *
  7.  *  vers. 1.1, 07/86
  8.  *  corrected startup() when line = 10
  9.  *  updated *copyright
  10.  *
  11.  *  vers. 1.2, 07/86
  12.  *  updated *copyright
  13.  *  main(): initialize _Leadin, _Savech, _Funkey, _Newkey, and _Keymap
  14.  *  startup(): read _Leadin, _Funkey, _Newkey, and _Keymap
  15.  *  moved createrr(), writerr(), and baddisk() to checkc.c
  16.  *  integrated CLEARS using #ifdef
  17.  */
  18.  
  19. /*  this file contains:
  20.  *      main()
  21.  *      startup()
  22.  *      getval( q)
  23.  *      getentry()
  24.  *      getinfo()
  25.  *      getyesno( def)
  26.  *      struct nlist *install( name, def)
  27.  *      struct nlist *lookup( s)
  28.  *      hash( s)
  29.  *      char *strsave( s)
  30.  *      compdate( e1, e2)
  31.  *      datecomp( d1, d2)
  32.  *      comppayee( e1, e2)
  33.  *      compcateg( e1, e2)
  34.  *      categcomp( e1, e2)
  35.  *      compamount( e1, e2)
  36.  *      compbbf( e1, e2)
  37.  *      compabrev( p1, p2)
  38.  *      isebbf( e)
  39.  *      isibbf( i)
  40.  *      char *index( s, c)
  41.  *      char *skipspace( s)
  42.  *      typcat( f, t)
  43.  *      openerr()
  44.  *      readerr()
  45.  */
  46.  
  47. #include    "a:checks.h"
  48.  
  49. main( argc, argv)                       /*  check register program           */
  50. int argc;
  51. char *argv[];
  52. {
  53.     char *p, *copyright, *allrights;
  54.     int i, j, length;
  55.  
  56. #ifdef  CLEARS
  57.     copyright = "Check Register CLEARS Program, v.1.2 (c) 1986 by WoolleyWare";
  58. #else
  59.     copyright = "Check Register Program, v.1.2 (c) 1986 by WoolleyWare";
  60. #endif          /*  copyright must be < (COLS - FNAMSIZE - 3) chars          */
  61.     allrights = "All Rights Reserved";
  62.  
  63.     _Outdev = CONOUT;                   /*  direct putchar() to screen       */
  64.     _Leadin = _Savech[ 0] = _Savech[ 1] = _Lastch = 0;
  65.     for ( i = 0; i < 10; ++i)
  66.         for ( j = 0; j < 2; ++j)
  67.             _Funkey[ i][ j] = _Newkey[ i][ j] = 0;
  68.     for ( i = 0; i < 32; ++i)
  69.         _Keymap[ i] = i;                /*  no CTRL key translation          */
  70.     Speed = 5;                          /*  initial Speed for ^QW and ^QZ    */
  71.     Oldfield = -1;                      /*  initialize other globals         */
  72.     Modified = Printing = Ctrlyundo = FALSE;
  73.     strcpy( Title, copyright);
  74. #ifdef  CLEARS
  75.     Usechex = USECHEX;
  76.     Ftoc[ CATFIELD] = 52;               /*  locate cursor for Field f        */
  77.     Ftoc[ CLRFIELD] = 67;               /*  at column c                      */
  78. #else
  79.     Savrecno = -1;
  80.     Today.yy = 0;
  81.     for ( i = 0; i < HASHSIZE; ++i)
  82.         Hashtab[ i] = 0;
  83.     Ftoc[ MMFIELD] = 1;                 /*  Ftoc[ f] = c                     */
  84.     Ftoc[ DDFIELD] = 4;                 /*  locates cursor for Field f       */
  85.     Ftoc[ YYFIELD] = 7;                 /*  at column c, where c = 0         */
  86.     Ftoc[ PAYFIELD] = 9;                /*  corresponds to left edge         */
  87.     Ftoc[ CATFIELD] = 52;               /*  of screen                        */
  88.     Ftoc[ AMTFIELD] = 61;
  89.     Ftoc[ DEPFIELD] = 63;
  90.     Ftoc[ CLRFIELD] = 67;
  91. #endif
  92.  
  93.     if ( argc > 1)                      /*  get Filename root                */
  94.         p = argv[ 1];
  95.     else p = DEFNAM;
  96.     length = strlen( p);
  97.     if ( !index( p, ':'))               /*  if no drive designated           */
  98.     {
  99.         Filename[ 0] = 'A' + defdsk();
  100.         Filename[ 1] = ':';
  101.         i = 2;
  102.     }
  103.     else                                /*  drive was designated             */
  104.     {
  105.         if ( length == 2)               /*  if just d: without filename      */
  106.         {
  107.             strcpy( Filename, p);
  108.             p = DEFNAM;                 /*  use default                      */
  109.             i = 2;
  110.         }
  111.         else i = 0;
  112.     }
  113.     if ( length > ( FNAMSIZE - 5 - i))  /*  should be FALSE if p = DEFNAM    */
  114.         *( p + FNAMSIZE - 5 - i) = '\0';
  115.     strcpy(( Filename + i), p);
  116.     if ( !( p = index( Filename, '.'))) /*  add dot if none                  */
  117.         *( p = Filename + strlen( Filename)) = '.';
  118.     *( p + 1) = '\0';                   /*  truncate after dot               */
  119.  
  120.     startup();
  121.     getentry();                         /*  read entry data                  */
  122.     disheading();                       /*  initialize display               */
  123.     Recno = Maxentry + ( 1 - PAGE);     /*  initialize gobottom()            */
  124.     First = Recno - PAGE;
  125.     Last = First - 1;
  126.     Field = 0;
  127.     gobottom();
  128.     getinfo();                          /*  read title, abrev, auto entries  */
  129.     control();                          /*  never returns                    */
  130. }
  131.  
  132. startup()                               /*  startup check register program   */
  133. {
  134.     char line, *p, *q, keymap;
  135.     char s[ MAXLINE], f[ FNAMSIZE], buf[ BUFSIZ], *fgets();
  136.     int i;
  137.  
  138.     strcpy( f, DEFNAM);
  139.     typcat( f, SCRTYP);
  140.     if ( fopen( f, buf) == ERROR)
  141.         abort( "Cannot open ", f);      /*  never returns                    */
  142.     for ( line = 6; line; --line)       /*  skip 6 lines                     */
  143.         if ( !fgets( s, buf))
  144.             readerr( f);                /*  never returns                    */
  145.     for ( line = 0; line < 11; ++line)  /*  get cursor/screen controls       */
  146.     {
  147.         if ( !fgets(( q = s), buf))
  148.             readerr( f);                /*  never returns                    */
  149.         switch ( line)
  150.         {
  151.         case 0:
  152.             p = Clead1;
  153.             break;
  154.         case 1:
  155.             p = Clead2;
  156.             break;
  157.         case 2:
  158.             p = Ctrail;
  159.             break;
  160.         case 3:
  161.             Cb4flg = getval( &q);
  162.             Linoff = getval( &q);
  163.             Coloff = getval( &q);
  164.             if ( Ascur = getval( &q))   /*  Ascur must be 0, 2, or 3         */
  165.                 Ascur = min( 3, max( 2, Ascur));
  166.             continue;                   /*  next line                        */
  167.         case 4:
  168.             p = Eraeol;
  169.             break;
  170.         case 5:
  171.             p = Lindel;
  172.             break;
  173.         case 6:
  174.             p = Linins;
  175.             break;
  176.         case 7:
  177.             p = Ivon;
  178.             break;
  179.         case 8:
  180.             p = Ivoff;
  181.             break;
  182.         case 9:
  183.             p = Trmini;
  184.             break;
  185.         case 10:
  186.             Dloop = (( DLOOP/10)*max( 1, min( 1000, getval( &q))))/10;
  187.             Inserton = getval( &q);
  188.             _Leadin = getval( &q);
  189.             keymap = getval( &q);
  190.             continue;                   /*  next line                        */
  191.         default:
  192.             continue;                   /*  next line                        */
  193.         }                               /*  note that 1st i may be negative  */
  194.         for ( *p++ = i = getval( &q); i > 0; --i)
  195.             *p++ = getval( &q);         /*  -1 < line < 3 or 3 < line < 10   */
  196.     }
  197.     if ( _Leadin)                       /*  if _Funkey and _Newmap present   */
  198.     {
  199.         for (( line = 2, p = _Funkey[ 0]); line; ( --line, p = _Newkey[ 0]))
  200.         {                               /*  read _Funkey first, then _Newkey */
  201.             if ( !fgets(( q = s), buf))
  202.                 readerr( f);
  203.             for ( i = 20; i; --i)
  204.                 *p++ = getval( &q);
  205.         }
  206.     }
  207.     if ( keymap)                        /*  if CTRL key map present          */
  208.     {
  209.         p = _Keymap;
  210.         for ( line = 2; line; --line)
  211.         {
  212.             if ( !fgets(( q = s), buf))
  213.                 readerr( f);
  214.             for ( i = 16; i; --i)       /*  read 16 per line                 */
  215.                 *p++ = getval( &q);
  216.         }
  217.         for ( i = 1; i < 32; ++i)       /*  except for map from NULL         */
  218.             if ( !( _Keymap[ i]))       /*  do not allow map to NULL         */
  219.                 _Keymap[ i] = i;        /*  instead, map to self             */
  220.     }
  221.     if ( fgets( s, buf))                /*  if more lines                    */
  222.     {
  223.         clrscr();
  224.         while (( i = getc( buf)) != CPMEOF && i != ERROR)
  225.             putchar( i - 1);
  226.         if ( getchar() == CTRLC)
  227.             exit();                     /*  never returns                    */
  228.     }
  229.     fclose( buf);
  230.     clrscr();
  231. }
  232.  
  233. /*  getval( q) returns next int value from string of decimal numbers separated
  234.  *  by white space; string must be pointed to by *q; each decimal number in
  235.  *  string may be headed by white space with optional minus sign followed by
  236.  *  consecutive decimal digits; first non-digit terminates the scan; zero is
  237.  *  returned if no legal value is found; *q will be updated to point to first
  238.  *  white space char following the current decimal number; *q will not point
  239.  *  beyond end of string
  240.  *
  241.  *  sample calling program segment:
  242.  *
  243.  *      char *p;                        string pointer
  244.  *      p = " 1   -32 123   9";         point to typical space separated string
  245.  *      printf( "%d", getval( &p));     pass pointer to string pointer
  246.  */
  247.  
  248. getval( q)                              /*  return int value and update *q   */
  249. char **q;                               /*  pointer to string pointer        */
  250. {
  251.     int i;
  252.  
  253.     i = atoi( *q = skipspace( *q));
  254.     while ( !isspace( **q) && **q)
  255.         ++( *q);
  256.     return ( i);
  257. }
  258.  
  259. getentry()                              /*  get check register entries       */
  260. {
  261.     char *p, buf[ BUFSIZ];
  262.     int g, imax;
  263.  
  264. #ifdef  CLEARS
  265.     typcat( Filename, CLRTYP);
  266.     if ( fopen( Filename, buf) == ERROR)
  267.         abort( "Cannot open ", Filename);
  268. #else
  269.     typcat( Filename, DATTYP);
  270.     if ( fopen( Filename, buf) == ERROR)
  271.     {
  272.         openerr( Filename);
  273.         Maxentry = -1;                  /*  initialize                       */
  274.         Memory.dollar = Memory.cent = 0;
  275.         return;
  276.     }
  277. #endif
  278.     if (( g = getw( buf)) == ERROR)
  279.         readerr( Filename);             /*  never returns                    */
  280.     imax = RECSIZ*g;
  281.     Maxentry = g - 1;
  282.     Memory.dollar = getw( buf);         /*  assume no read error             */
  283.     Memory.cent = getw( buf);
  284.     p = Entry;
  285.     while ( imax--)
  286.     {
  287.         if (( g = getc( buf)) == ERROR)
  288.             readerr( Filename);         /*  never returns                    */
  289.         *p++ = g;
  290.     }
  291.     fclose( buf);
  292.     for ( g = 0; g <= Maxentry; ++g)
  293.         newbalance( g);
  294. }
  295.  
  296. getinfo()                               /*  get title, abrev, and auto trans */
  297. {
  298. #ifdef  CLEARS
  299.     char *p, new, next;
  300.     char *key[ 3], buf[ BUFSIZ], s[ MAXLINE], *fgets();
  301.     int i;
  302.     struct record *e;
  303. #else
  304.     char *p, *q, *amsg, c, new, next, def, savmodified;
  305.     char *key[ 3], buf[ BUFSIZ], s[ MAXLINE], *fgets();
  306.     char adate, adeposit, acategory;
  307.     int i, delta, count, adollar, acent, compdate();
  308.     struct record *e;
  309.     struct calendar maxdate;
  310.  
  311.     amsg = "Installing abreviations ... ";
  312. #endif
  313.  
  314.     key[ 0] = "TITLE";                  /*  initialize                       */
  315.     key[ 1] = "ABREV";
  316.     key[ 2] = "AUTOM";
  317.     typcat( Filename, INFTYP);
  318.     if ( fopen( Filename, buf) == ERROR)
  319.     {
  320.         openerr( Filename);
  321.         return;
  322.     }
  323.     next = 0;
  324.     while ( fgets( s, buf))             /*  read one line at a time          */
  325.     {
  326.         s[ strlen( s) - 1] = '\0';      /*  truncate Abc\n\0 at \n           */
  327.         if ( p = index( s, '|'))        /*  skip comment starting with |     */
  328.             *p = '\0';
  329.         if ( s[ 0] == '\0')
  330.             continue;
  331.         for ( new = 3; new; --new)      /*  look for keyword                 */
  332.         {
  333.             p = key[ new - 1];
  334.             for ( i = 0; i < 5;  ++i)
  335.                 if ( toupper( s[ i]) != *p++)
  336.                     break;
  337.             if ( i == 5)
  338.             {
  339. #ifdef  CLEARS
  340.                 next = new;
  341. #else
  342.                 if (( next = new) == 2)
  343.                     prompt( amsg);
  344. #endif
  345.                 break;                  /*  break for loop on new            */
  346.             }
  347.         }
  348.         if ( new)                       /*  if keyword found                 */
  349.             continue;                   /*      get next line from file      */
  350.         switch ( next)
  351.         {
  352. #ifndef CLEARS  /*  CLEARS only recognizes TITLE  */
  353.         case 2:                         /*  keyword was ABREV                */
  354.             if ( strlen( p = s) < 3)
  355.             {
  356.                 prompt( "Invalid abreviation ");
  357.                 goto error;
  358.             }
  359.             while ( p < ( s + 3))       /*  make abreviation upper case      */
  360.             {
  361.                 *p = toupper( *p);
  362.                 ++p;
  363.             }
  364.             *p++ = '\0';                /*  end abreviation                  */
  365.             p = skipspace( p);          /*  locate full text                 */
  366.             if ( strlen( p) > ( PAYEESIZE - 1))
  367.                 *( p + PAYEESIZE - 1) = '\0';
  368.             if ( !install( s, p))
  369.             {
  370.                 prompt( "Could not install abreviation ");
  371.                 goto error;
  372.             }
  373.             break;                      /*  break switch on next             */
  374. error:      puts( s);                   /*  complete error message           */
  375.             waitesc();
  376.             prompt( amsg);
  377.             break;                      /*  break switch on next             */
  378.         case 3:                         /*  keyword was AUTOM                */
  379.             if ( Maxentry == ( ENTRYSIZE - 1))
  380.             {
  381.                 prompt( "Number of entries is maximum allowed");
  382.                 waitesc();
  383.                 next = 0;
  384.                 break;                  /*  break switch on next             */
  385.             }
  386.             if ( !Today.yy)             /*  initialize maxdate once          */
  387.                 maxdate.mm = maxdate.dd = maxdate.yy = 0;
  388.             ++Maxentry;                 /*  create temporary entry           */
  389.             ++Last;
  390.             for ( i = 0; i < Maxentry; ++i)
  391.                 datemax( &maxdate, &( Entry[ i].date));
  392.             cursorput( Recno, 0);       /*  Recno and Maxentry are same      */
  393.             putdate( &maxdate);
  394.             e = &Entry[ Recno];         /*  setup to use eddate              */
  395.             e->date.mm = maxdate.mm;
  396.             e->date.dd = maxdate.dd;
  397.             e->date.yy = maxdate.yy;
  398.             count = Ftoc[ YYFIELD] + 4;
  399.             savmodified = Modified;
  400.             FOREVER
  401.             {
  402.                 if ( Today.yy)
  403.                     goto query;
  404.                 cursorput( Recno, count);
  405.                 puts( p = "<<<  Enter today's date");
  406.                 Field = MMFIELD;
  407.                 while ( Field < ( YYFIELD + 1))
  408.                 {
  409.                     putquery();
  410.                     putcursor( Recno, Field);
  411.                     c = eddate( getchar());
  412.                     if ( c == ESC)
  413.                         goto skipit;
  414.                     switch ( c)
  415.                     {
  416.                         case 0:
  417.                             break;
  418.                         case CTRLD:  case '\r':  case CTRLF:  case '\t':
  419.                             goright( c);
  420.                             break;
  421.                         case CTRLS:  case '\b':  case CTRLA:
  422.                             if ( Field > MMFIELD)
  423.                             {
  424.                                 goleft( c);
  425.                                 break;
  426.                             }           /*  else fall thru                   */
  427.                         default:
  428.                             putchar( BEL);
  429.                             break;
  430.                     }                   /*  end of switch on c               */
  431.                 }                       /*  end of while loop on Field       */
  432. query:          cursorput( Recno, count);
  433.                 clreol( count);
  434.                 prompt( "Do you wish to revise today's date (Y/N)? ");
  435.                 if ( getyesno( NO))
  436.                     Today.yy = 0;
  437.                 else break;             /*  break FOREVER loop               */
  438.             }                           /*  end of FOREVER loop              */
  439. skipit:     Today.mm = e->date.mm;
  440.             Today.dd = e->date.dd;
  441.             Today.yy = e->date.yy;
  442.             if ( Recno > 0)             /*  delta = last date to cur month   */
  443.             {                           /*  limit 1 year; use 32 days/month  */
  444.                 delta = ( min( 1, ( Today.yy - maxdate.yy))*12
  445.                         + ( Today.mm - maxdate.mm - 1))*32 + ( 32 - maxdate.dd);
  446.             }
  447.             else delta = 0;
  448.  
  449. /*  graphical representation of various automatic entry parameters:
  450.  *
  451.  *  when month of most recent entry (last) < current month (today):
  452.  *
  453.  *      | delta |        Today.dd        |
  454.  *      |       |  adate |               |
  455.  *  ----|-------|--------|---------------|-----------|---
  456.  *     last   32|0      auto           today       32|
  457.  *  last month  |           current month            |
  458.  *
  459.  *  when month of most recent entry (last) = current month (today):
  460.  *
  461.  *              |        Today.dd        | 
  462.  *              | -delta | delta+Today.dd|
  463.  *  ------------|--------|-----|---------|-----------|---
  464.  *            32|0      last  auto     today       32|
  465.  *  last month  |           current month            |
  466.  */
  467.             cursorput( Recno, 0);       /*  clear today's date               */
  468.             clreol( 0);
  469.             --Maxentry;                 /*  delete temporary entry           */
  470.             --Last;
  471.             Field = 0;
  472.             Modified = savmodified;
  473.             if (( delta + Today.dd) <= 0)
  474.             {
  475.                 next = 0;
  476.                 break;                  /*  break switch on next             */
  477.             }
  478.             next = 4;                   /*  prepare for next line from file  */
  479.         case 4:                         /*  interpret automatic transaction  */
  480.             adate = atoi( p = skipspace( s));
  481.             acategory = DEFCAT;
  482.             if ( q = index( p, ' '))
  483.                 acategory = *( p = skipspace( q));
  484.             if ( acategory == '-')
  485.                 acategory = DEFCAT;
  486.             else acategory = toupper( acategory);
  487.             adollar = acent = 0;
  488.             if ( q = index( p, ' '))
  489.             {
  490.                 q = skipspace( q);      /*  start of amount                  */
  491.                 while ( *q == '-')
  492.                     ++q;                /*  ignore minus signs               */
  493.                 if ( p = index( q, '.'))
  494.                     acent = atoi( p + 1);
  495.                 else p = index( q, ' ');
  496.                 if (( p - q) > 2)
  497.                 {
  498.                     p -= 2;
  499.                     acent += 100*atoi( p);
  500.                     *p = '|';           /*  mark end of adollar              */
  501.                     adollar = atoi( q);
  502.                 }
  503.                 else acent += 100*atoi( q);
  504.                 p = q;
  505.             }
  506.             adeposit = FALSE;
  507.             if ( q = index( p, ' '))
  508.                 adeposit = ( toupper( *( p = skipspace( q))) == 'D');
  509.             if ( q = index( p, ' '))
  510.                 p = skipspace( q);      /*  start of payee                   */
  511.             if ( strlen( p) > ( PAYEESIZE - 1))
  512.                 *( p + PAYEESIZE - 1) = '\0';
  513.             count = max( 0, ( delta + adate - 1)/32);
  514.             if ( Today.dd >= adate && -delta < adate)
  515.                 ++count;                /*  count is number of entries       */
  516.             while ( count--)            /*  skip if count is zero            */
  517.             {
  518.                 savmodified = Modified;
  519.                 if ( !insert())         /*  if cannot insert Recno           */
  520.                 {
  521.                     next = 0;
  522.                     break;              /*  break while loop on count        */
  523.                 }
  524.                 e = &Entry[ Recno];
  525.                 i = Today.mm - count;
  526.                 if ( Today.dd < adate)
  527.                     --i;
  528.                 e->date.yy = Today.yy;
  529.                 while ( i <= 0)
  530.                 {
  531.                     i += 12;
  532.                     --( e->date.yy);
  533.                 }
  534.                 e->date.mm = i;
  535.                 e->date.dd = adate;
  536.                 strcpy( e->payee, p);
  537.                 e->category = acategory;
  538.                 e->amount.dollar = adollar;
  539.                 e->amount.cent = acent;
  540.                 e->deposit = adeposit;
  541.                 newbalance( Recno);
  542.                 putrecord( Recno);
  543.                 def = NO;               /*  default                          */
  544.                 c = ESC;
  545.                 while ( c == ESC)
  546.                 {
  547.                     prompt( "Do you accept this automatic entry (Y/N)? ");
  548.                     def = !def;         /*  reverse                          */
  549.                     putchar( def ? 'Y' : 'N');
  550.                     putcursor( Recno, ( Field = 0));
  551.                     c = getchar();
  552.                 }
  553.                 c = toupper( c);
  554.                 if ( c == 'N' || ( !def && c != 'Y'))
  555.                 {
  556.                     delete();           /*  delete Recno                     */
  557.                     Modified = savmodified;
  558.                     Ctrlyundo = FALSE;
  559.                 }
  560.                 else godown( CTRLX);
  561.             }                           /*  end of while loop on count       */
  562.             break;                      /*  break switch on next             */
  563. #endif  /*  not CLEARS  */
  564.         case 1:                         /*  keyword was TITLE                */
  565.             s[ COLS - FNAMSIZE - 3] = '\0';
  566.             strcpy( Title, s);          /*  s truncated at max Title length  */
  567.             cursorto( 0, 0);
  568.             putscr( Ivon);
  569.             puttitle();
  570.             putscr( Ivoff);
  571.             break;                      /*  break switch on next             */
  572.         default:
  573.             break;                      /*  break switch on next             */
  574.         }                               /*  end of switch on next            */
  575.     }                                   /*  end of while loop on fgets       */
  576.     fclose( buf);
  577. }
  578.  
  579. getyesno( def)                          /*  return YES or NO response        */
  580. char def;                               /*  default response                 */
  581. {
  582.     char c;
  583.  
  584.     def = !def;                         /*  reverse default                  */
  585.     c = ESC;
  586.     while ( c == ESC)
  587.     {
  588.         def = !def;                     /*  reverse default                  */
  589.         putchar( def ? 'Y' : 'N');      /*  display default                  */
  590.         putchar( '\b');
  591.         c = getchar();
  592.     }
  593.     c = toupper( c);
  594.     if (( def && c != 'N') || ( !def && c != 'Y'))
  595.         return ( def);
  596.     def = !def;                         /*  reverse default                  */
  597.     putchar( def ? 'Y' : 'N');          /*  display response                 */
  598.     return ( def);
  599. }
  600.  
  601. #ifndef CLEARS  /*  CLEARS does not use abreviations  */
  602.  
  603. struct nlist *install( name, def)       /*  install in Hashtab               */
  604. char *name;                             /*  abrev                            */
  605. char *def;                              /*  fullname                         */
  606. {                                       /*  ref. K & R, p. 136               */
  607.     int hashval;
  608.     struct nlist *np;
  609.  
  610.     if ( !( np = lookup( name)))        /*  if not found                     */
  611.     {
  612.         if ( !( np = alloc( sizeof( *np))))
  613.             return ( 0);
  614.         if ( !( np->abrev = strsave( name)))
  615.             return ( 0);
  616.         hashval = hash( np->abrev);
  617.         np->next = Hashtab[ hashval];   /*  initialized to zero              */
  618.         Hashtab[ hashval] = np;
  619.     }
  620.     else if ( np->fullname)
  621.         free( np->fullname);
  622.     if ( !( np->fullname = strsave( def)))
  623.         return ( 0);
  624.     return ( np);
  625. }
  626.  
  627. struct nlist *lookup( s)                /*  look for s in Hashtab            */
  628. char *s;
  629. {                                       /*  ref. K & R, p. 135               */
  630.     struct nlist *np;
  631.  
  632.     for ( np = Hashtab[ hash( s)]; np; np = np->next)
  633.         if ( !strcmp( s, np->abrev))
  634.             return ( np);               /*  found                            */
  635.     return ( 0);                        /*  not found                        */
  636. }
  637.  
  638. hash( s)                                /*  determine hash value for s       */
  639. char *s;
  640. {                                       /*  ref. K & R, p. 135               */
  641.     int hashval;
  642.  
  643.     hashval = 0;
  644.     while ( *s)
  645.         hashval += *s++;
  646.     return ( hashval%HASHSIZE);
  647. }
  648.  
  649. char *strsave( s)                       /*  save s somewhere                 */
  650. char *s;
  651. {                                       /*  ref. K & R, p. 103               */
  652.     char *p;
  653.  
  654.     if ( p = alloc( strlen( s) + 1))
  655.         strcpy( p, s);
  656.     return ( p);
  657. }
  658.  
  659. compabrev( p1, p2)                      /*  return  1 if abrev at p1 > p2    */
  660. struct nlist **p1, **p2;                /*  return -1 if abrev at p1 < p2    */
  661. {                                       /*  else, return 0 (not possible)    */
  662.     return ( strcmp(( *p1)->abrev, ( *p2)->abrev));
  663. }
  664.  
  665. #endif  /*  not CLEARS  */
  666.  
  667. compdate( e1, e2)                       /*  return  1 if date of e1 > e2     */
  668. struct record *e1, *e2;                 /*  return -1 if date of e1 < e2     */
  669. {                                       /*  else, return strcmp on payee     */
  670.     int test;
  671.  
  672.     if ( test = compbbf( e1, e2))       /*  sort BBF entries to top          */
  673.         return ( test);
  674.     if ( test = datecomp( &( e1->date), &( e2->date)))
  675.         return ( test);
  676.     return ( strcmp( e1->payee, e2->payee));
  677. }
  678.  
  679. datecomp( d1, d2)                       /*  return  1 if calendar d1 > d2    */
  680. struct calendar *d1, *d2;               /*  return -1 if calendar d1 < d2    */
  681. {                                       /*  else, return 0                   */
  682.     if ( d1->yy > d2->yy)
  683.         return ( 1);
  684.     if ( d1->yy < d2->yy)
  685.         return ( -1);
  686.     if ( d1->mm > d2->mm)
  687.         return ( 1);
  688.     if ( d1->mm < d2->mm)
  689.         return ( -1);
  690.     if ( d1->dd > d2->dd)
  691.         return ( 1);
  692.     if ( d1->dd < d2->dd)
  693.         return ( -1);
  694.     return ( 0);
  695. }
  696.  
  697. comppayee( e1, e2)                      /*  return  1 if payee of e1 > e2    */
  698. struct record *e1, *e2;                 /*  return -1 if payee of e1 < e2    */
  699. {                                       /*  else, return compdate( e1, e2)   */
  700.     int test;
  701.  
  702.     if ( test = compbbf( e1, e2))       /*  sort BBF entries to top          */
  703.         return ( test);
  704.     if ( test = strcmp( e1->payee, e2->payee))
  705.         return ( test);
  706.     return ( compdate( e1, e2));
  707. }
  708.  
  709. compcateg( e1, e2)                      /*  return  1 if category of e1 > e2 */
  710. struct record *e1, *e2;                 /*  return -1 if category of e1 < e2 */
  711. {                                       /*  else, return compdate( e1, e2)   */
  712.     int test;
  713.  
  714.     if ( test = compbbf( e1, e2))       /*  sort BBF entries to top          */
  715.         return ( test);
  716.     return ( categcomp( e1, e2));
  717. }
  718.  
  719. categcomp( e1, e2)                      /*  return  1 if category of e1 > e2 */
  720. struct record *e1, *e2;                 /*  return -1 if category of e1 < e2 */
  721. {                                       /*  else, return compdate( e1, e2)   */
  722.     if ( e1->category > e2->category)
  723.         return ( 1);
  724.     if ( e1->category < e2->category)
  725.         return ( -1);
  726.     return ( compdate( e1, e2));
  727. }
  728.  
  729. compamount( e1, e2)                     /*  return  1 if amount of e1 > e2   */
  730. struct record *e1, *e2;                 /*  return -1 if amount of e1 < e2   */
  731. {                                       /*  else, return compdate( e1, e2)   */
  732.     int test;
  733.     struct money *m1, *m2;
  734.  
  735.     if ( test = compbbf( e1, e2))       /*  sort BBF entries to top          */
  736.         return ( test);
  737.     m1 = &( e1->amount);
  738.     m2 = &( e2->amount);
  739.     if ( m1->dollar > m2->dollar)
  740.         return ( 1);
  741.     if ( m1->dollar < m2->dollar)
  742.         return ( -1);
  743.     if ( m1->cent > m2->cent)
  744.         return ( 1);
  745.     if ( m1->cent < m2->cent)
  746.         return ( -1);
  747.     return ( compdate( e1, e2));
  748. }
  749.  
  750. compbbf( e1, e2)                        /*  return  1 if e1 not BBF, e2 is   */
  751. struct record *e1, *e2;                 /*  return -1 if e1 is BBF, e2 not   */
  752. {                                       /*  return  1 if both BBF, e1 > e2   */
  753.                                         /*  return -1 if both BBF, e1 < e2   */
  754.                                         /*  else, return 0                   */
  755.     if ( isebbf( e1))                    
  756.     {
  757.         if ( isebbf( e2))               /*  note two BBF entries cannot      */
  758.             return ( categcomp( e1, e2));   /*  have the same category       */
  759.         return ( -1);
  760.     }
  761.     if ( isebbf( e2))
  762.         return ( 1);
  763.     return ( 0);
  764. }
  765.  
  766. isebbf( e)                              /*  return TRUE if e is a BBF entry  */
  767. struct record *e;                       /*  else, return FALSE               */
  768. {
  769.     return ( e->category & 0x80);
  770. }
  771.  
  772. isibbf( i)                              /*  return TRUE if Entry[ i] is BBF  */
  773. {                                       /*  else, return FALSE               */
  774.     return ( isebbf( &Entry[ i]));
  775. }
  776.  
  777. char *index( s, c)                      /*  point to char c in string s      */
  778. char *s, c;
  779. {
  780.     while ( *s && ( *s != c))
  781.         ++s;
  782.     return ( *s == c ? s : NULL);
  783. }
  784.  
  785. char *skipspace( s)                     /*  point to next non-space in s     */
  786. char *s;
  787. {
  788.     while ( *s && isspace( *s))
  789.         ++s;
  790.     return ( s);
  791. }
  792.  
  793. typcat( f, t)                           /*  add filetype t to filename f     */
  794. char *f, *t;
  795. {
  796.     strcpy(( index( f, '.') + 1), t);   /*  filename f MUST have a dot       */
  797. }
  798.  
  799. openerr( f)                             /*  display file open error          */
  800. char *f;
  801. {
  802.     prompt( "Could not open ");
  803.     puts( f);
  804.     waitkey();                          /*  may never return                 */
  805. }
  806.  
  807. readerr( f)                             /*  display file read error          */
  808. char *f;
  809. {
  810.     abort( "Error reading ", f);        /*  never returns                    */
  811. }
  812.