/*  cal.c -- UTOOL. Calendar.  Print lines with date match.
     author: David H. Wolen
     last change: 6/2/83

     usage: cal file  mm/dd/yy
            prog |cal -r mm/dd/yy mm/dd/yy
            cal file -ge mm/dd/yy

     options: -g (>)
              -l (<)
              -e (=)
              -r (>= date1 and <= date2)

     input:    file or STDIN
     output:   STDOUT

     notes:    -ge, -le, -gl, are ok (effect is "or" relation)
               only first valid date on line is examined for match
               if -r, no other options are allowed
               if no options, default is -e

     linkage: a:clink cal -f dio -ca (uses deff3.crl)
*/

#include "a:bdscio.h"
#include "a:dio.h"
#define STDIN 0
#define STDOUT 1
#define BASEYR 45   /* yy < BASEYR converted to 20yy */
                    /* yy >= BASEYR converted to 19yy */

struct date
     {int  day;
      int  month;
      int  year;
     };

main(argc,argv)
int  argc;
char *argv[];
{
     struct date argdate1, argdate2, linedate;
     int  gt, lt, eq, range, isstdin;
     char *s, line[MAXLINE], cdate[9], ibuf[BUFSIZ];

     dioinit(&argc,argv);
     eq=gt=lt=range=FALSE;

     if(*(argv+1)[0] != '-' && !isdigit(*(argv+1)[0]))
          {isstdin=FALSE;          /* file input */
          if(fopen(*++argv,ibuf)==ERROR)
               error("cal: can't open file");
          --argc;
          }
     else
          isstdin=TRUE;

     while(--argc > 0 && (*++argv)[0] == '-')
          for(s=argv[0]+1; *s != '\0'; s++)
               switch (*s)
                    {case 'E':
                         eq=TRUE;  break;
                    case 'G':
                         gt=TRUE;  break;
                    case 'L':
                         lt=TRUE;  break;
                    case 'R':
                         range=TRUE;  break;
                    default:
                         error("cal: invalid option");
                    }

     if(convdate(*argv++,&argdate1)==ERROR)
          error("cal: invalid date1");

     if(range)
          {if(convdate(*argv,&argdate2)==ERROR)
               error("cal: invalid date2");
          if(eq || gt || lt)
               error("cal: no other options allowed with -r");
          }

     if(!eq && !gt && !lt && !range)    /* default -e if no options */
          eq=TRUE;

     while(isstdin ? fgets(line,STDIN) : fgets(line,ibuf))
          {if(!finddate(line,cdate))  continue;
          else
               convdate(cdate,&linedate);

          if(eq && datecmp(&linedate,&argdate1)==0)
               fputs(line,STDOUT);

          if(gt && datecmp(&linedate,&argdate1) > 0)
               fputs(line,STDOUT);

          if(lt && datecmp(&linedate,&argdate1) < 0)
               fputs(line,STDOUT);

          if(range && datecmp(&linedate,&argdate1) >= 0
               && datecmp(&linedate,&argdate2) <= 0)
               fputs(line,STDOUT);
          }

     dioflush();
}



/* convdate -- convert mm/dd/yy to structure date */
convdate(mmddyy,pd)
char *mmddyy;
struct date *pd;
{
     int  tmonth, tday, tyear;
     char mm[3], dd[3], yy[3];

     mm[0]=mmddyy[0];
     mm[1]=mmddyy[1];
     dd[0]=mmddyy[3];
     dd[1]=mmddyy[4];
     yy[0]=mmddyy[6];
     yy[1]=mmddyy[7];
     mm[2]=dd[2]=yy[2]='\0';

     tmonth=atoi(mm);
     tday=atoi(dd);
     tyear=atoi(yy);

     if(tyear < BASEYR)
          tyear += 2000;
     else
          tyear += 1900;

     if(tmonth < 1 || tmonth > 12)  return(ERROR);
     if(tday < 1 || tday > 31)  return(ERROR);

     switch(tmonth)
          {case 1:  case 3:  case 5:  case 7:  case 8:  case 10:  case 12:
               if(tday > 31)  return(ERROR);
               break;
          case 4:  case 6:  case 9:  case 11:
               if(tday > 30)  return(ERROR);
               break;
          case 2:
               if(tyear % 4 == 0 && tyear % 100 != 0 || tyear % 400 == 0)
                    {if(tday > 29)           /* leap year */
                         return(ERROR);
                    break;
                    }
               else
                    {if(tday > 28)           /* not leap year */
                         return(ERROR);
                    break;
                    }
          default:
               error("in convdate -- can't happen");
          }

     pd->day=tday;
     pd->month=tmonth;
     pd->year=tyear;
     return(OK);
}



/* finddate -- find mm/dd/yy in line */
finddate(line,mmddyy)
char *line, *mmddyy;
{
     struct date dtest;
     char ctemp[9];
     int  i, len, start;

     if((start=index(line,"/")) < 0)  return(FALSE);
     start=max(start-2,0);
     len=strlen(line);

     for(i=start; i<len-8; i++)
          if(dpat(line,i))
               {movmem(&line[i],ctemp,8);
               ctemp[8]='\0';
               if(convdate(ctemp,&dtest) != ERROR)
                    {strcpy(mmddyy,ctemp);
                    return(TRUE);
                    }
               }

     return(FALSE);
}



/* dpat -- see if the pattern nn/nn/nn starts at line[i] */
dpat(line,i)
char *line;
int  i;
{
     if(!isdigit(line[i++]))  return(FALSE);
     if(!isdigit(line[i++]))  return(FALSE);
     if(line[i++] != '/')     return(FALSE);
     if(!isdigit(line[i++]))  return(FALSE);
     if(!isdigit(line[i++]))  return(FALSE);
     if(line[i++] != '/')     return(FALSE);
     if(!isdigit(line[i++]))  return(FALSE);
     if(!isdigit(line[i++]))  return(FALSE);

     return(TRUE);
}



/* datecmp -- compare two date structures.  Return
     <0, 0, >0 if first is <,==,> second.
*/
datecmp(pd1,pd2)
struct date *pd1, *pd2;
{
     if(pd1->year > pd2->year)  return(1);
     if(pd1->year < pd2->year)  return(-1);

     if(pd1->month > pd2->month) return(1);
     if(pd1->month < pd2->month) return(-1);

     if(pd1->day > pd2->day) return(1);
     if(pd1->day < pd2->day) return(-1);

     return(0);
}
     