/*  dfilter.c    v1.0     */
/*  30.10.1990  Panu Hassi  */

#include <stdio.h>
#include <stdlib.h>

#define CUT 0
#define PRINT 1
#define LF 10
#define OK 1
#define FAIL 0

#define DAYS   31
#define MONTHS 12
#define MAXCOL  100
#define MAXNAME 40
#define DATE_START 35
#define DATE_END   55

#define OPT_SUB_DIRS    1
#define OPT_WRITE_DATE  2
#define OPT_SNOOP_DATE  4

#define SAMESTRING(a,b) (!(stricmp(a,b)))
#define STR2LONG(a,b,c) (' '<<24 | a<<16 | b<<8 | c)
#define ERROR(a) {printf("%s\n",a); exit(10);}
#define IS_PROTECTION_BIT(a) (a=='d'|a=='r'|a=='w'|a=='x'|a=='-')
#define UCHAR unsigned char

#define JAN STR2LONG('J','a','n')
#define FEB STR2LONG('F','e','b')
#define MAR STR2LONG('M','a','r')
#define APR STR2LONG('A','p','r')
#define MAY STR2LONG('M','a','y')
#define JUN STR2LONG('J','u','n')
#define JUL STR2LONG('J','u','l')
#define AUG STR2LONG('A','u','g')
#define SEP STR2LONG('S','e','p')
#define OCT STR2LONG('O','c','t')
#define NOV STR2LONG('N','o','v')
#define DEC STR2LONG('D','e','c')

#define HEADER      0
#define HEADER_EOF  1
#define PROGRAM     2
#define RUBBISH     3  

UCHAR monthstr[MONTHS+1][4] = {"  ","Jan","Feb","Mar","Apr","May","Jun",
                                    "Jul","Aug","Sep","Oct","Nov","Dec" };
UCHAR column[MAXCOL+1];
UCHAR buffer[MAXCOL+1];
UCHAR next_header[MAXNAME+1];
UCHAR current_header[MAXNAME+1] = "Current directory:\n";
UCHAR line_date[DATE_END - DATE_START + 1];
UCHAR tmpdate[4];
int month[MONTHS+1];
int first_year = 99999;
int last_year  = 0;
int date_start_col = DATE_START;
int date_end_col   = DATE_END;
int options = OPT_SNOOP_DATE;

struct line   
{
    struct line *nextline;
    int year;
    char *txt;
};
struct line *daylist[MONTHS+1][DAYS+1] = {0};   /* pointers from days  */

FILE *rdatefile,*wdatefile;


main(argc,argv)
int argc;
char *argv[];
{
    int a,b,c,i,length,mode,arg,arg_w,day_tmp,month_tmp,year_tmp;
    int start_year,start_month,start_day, end_year,end_month,end_day;
    int this_year,this_month, latest_day,latest_month,latest_year;
    long today;

    
    arg = 1;
    
    for(i=1; i<= MAXCOL; i++)  /* default: all columns are printed */
        column[i] = PRINT;
    
    time(&today);
    this_month = IsItMonth(ctime(&today)+4);
    this_year  = strtol(ctime(&today)+20,NULL,10);
    if (!this_year)
        ERROR("ctime() not working")
                            
    start_day  = start_month  = start_year  = 0;
    end_day    = end_month    = end_year    = 0;
    latest_day = latest_month = latest_year = 0;
    
    for(i=1; i<= MONTHS; i++)  
        month[i] = CUT ;

    if ( SAMESTRING(argv[arg],"?") || SAMESTRING(argv[arg],"-h") )
    {
        Help();
        exit(0);
    }
    while (arg < argc)
    {
        if (SAMESTRING(argv[arg],"c"))
        {
            if ((argc - arg) < 2)
                ERROR("Column-numbers missing after c-option")
            if (!(a = strtol(argv[arg+1],NULL,10)))
                ERROR("c begin_cut end_cut")
            if (!(b = strtol(argv[arg+2],NULL,10)))
                ERROR("c begin_cut end_cut")
            if (a > b)
                ERROR("startcolumn <= endcolumn")
            if (a < 1)
                a = 1;
            if (b < 1)
                b = 1;
            if (a > MAXCOL)
                a = MAXCOL;
            if (b > MAXCOL)
                b = MAXCOL;
            for(i=a;i <= b;i++)
                column[i] = CUT;
            arg+=3;
        }
        else
        if (SAMESTRING(argv[arg],"a"))
        {
            options = options | OPT_SUB_DIRS;
            arg++;
        }
        else
        if (SAMESTRING(argv[arg],"r"))
        {
            if (arg == argc)
                ERROR("r filename")
            rdatefile = fopen(argv[arg+1],"r");
            if (!rdatefile)
                ERROR("Couldn't open datefile for input")
            for(i=0; i<3; i++)
                tmpdate[i] = (UCHAR) fgetc(rdatefile);
            start_month = IsItMonth(&tmpdate[0]);
            if (!start_month)
                ERROR("Couldn't read month from datefile")
            if (!(fscanf(rdatefile,"%d",&start_day)))
                ERROR("Couldn't read day from datefile")
            if (start_day < 1 || start_day > DAYS)
                ERROR("1 <= day <= 31")
            if (!(fscanf(rdatefile,"%d",&start_year)))
                ERROR("Couldn't read year from datefile")
            end_day   = DAYS;
            end_month = MONTHS;
            end_year  = this_year;
            fclose(rdatefile);
            arg+=2;
        }
        else
        if (SAMESTRING(argv[arg],"w"))
        {
            options = options | OPT_WRITE_DATE;
            if (arg == argc)
                ERROR("w filename")
            arg_w = arg+1;
            
            arg+=2;
        }
        else
        if (SAMESTRING(argv[arg],"-"))
        {
            if (!(argc - arg))
                ERROR("[start_date] - [end_date]")
            if (!(end_month = IsItMonth(argv[arg+1])))
            {
                if (!start_year)
                    ERROR("[start_date] - [end_date]")
                else
                {
                    end_day   = DAYS;
                    end_month = MONTHS;
                    end_year  = this_year;
                }
                arg++;
            }
            else
            if (3 <= (argc - arg))
            {
                if (!end_month)
                    ERROR("- MMM DD YYYY")
                if (!(end_day = strtol(argv[arg+2],NULL,10)))
                    ERROR("- MMM DD YYYY")
                if (end_day < 1 || end_day > DAYS)
                    ERROR("1 <= day <= 31")
                if (!(end_year = strtol(argv[arg+3],NULL,10)))
                    ERROR("- MMM DD YYYY")

                arg+=4;
            }
            else 
                ERROR("- MMM DD YYYY")
        }
        else
        if (month_tmp = IsItMonth(argv[arg]))
        {
            if (2 <= (argc - arg))
            {
                start_month = month_tmp;
                if (!(start_day = strtol(argv[arg+1],NULL,10)))
                    ERROR("MMM DD YYYY")
                if (start_day < 1 || start_day > DAYS)
                    ERROR("1 <= day <= 31")
                if (!(start_year = strtol(argv[arg+2],NULL,10)))
                    ERROR("MMM DD YYYY")
                arg+=3;
            }
            else
                ERROR("MMM DD YYYY")
        }
        else
        {
            Help();
            exit(0);
        }
    }
    /* default: from the beginning of current month to the end of year */
    if (!start_year && !end_year)
    {
        start_day   = 1;        
        start_month = this_month;
        start_year  = this_year;
        end_day     = DAYS;
        end_month   = MONTHS;
        end_year    = this_year;
    }
    if (!end_year)  /* user wants to print only one day */
    {
        end_day   = start_day;
        end_month = start_month;
        end_year  = start_year;
    }
    if (!start_year) /* there can't be files older than this :) */
    {
        start_day   = 1;
        start_month = 1;
        start_year  = 1960;
    }
    if (start_year > end_year)
        ERROR("start_date < end_date")
    if (start_year == end_year)
    {
        if (start_month > end_month)
            ERROR("start_date < end_date")
        if (start_month == end_month)
        {
            if (start_day > end_day)
                ERROR("start_date < end_date")
        }
        for(i=start_month; i <= end_month; i++)
            month[i] = PRINT;
    }
    else
        for(i=1; i <= MONTHS; i++)
            month[i] = PRINT;
        
    /* command line Ok, let's get going...   */
    do
    {
        mode   = ReadLine();
        length = mode & 255;
        mode = mode >> 8;
        switch(mode) 
        {
            case HEADER_EOF:
            case HEADER:
                i=0;
                
                do                  /* new header name from buffer */
                {
                    c = buffer[i+1];
                    next_header[i++] = (UCHAR) c;
                } while (i < length);
                next_header[length] = 0;

                OutputDir(mode,last_year);     /* dir contents to stdout */
                if (!(options & OPT_SUB_DIRS) && 
                    !(options & OPT_SNOOP_DATE))  
                    goto main99;
                    
                i=0;
                do                      /* swap header names */
                {
                    c = next_header[i];
                    current_header[i++] = (UCHAR) c;
                } while (i < length);
                current_header[length] = 0;
                break;
                
            case PROGRAM:
                if (line_date[9] == ':')
                    year_tmp = this_year;
                else
                    if (!(year_tmp = strtol(&line_date[8],NULL,10)))
                        break;    /* RUBBISH */
                month_tmp = IsItMonth(&line_date[0]);
                if (!(day_tmp = strtol(&line_date[4],NULL,10)))
                    break;        /* RUBBISH */
                
                    /* check that  date >= start_date */
                
                a=0;    
                c = (2*(year_tmp > start_year)) + (year_tmp == start_year);
                switch(c)
                {
                    case 0: 
                        break;
                    case 2: 
                        a=1; break;
                    case 1: 
                        c= (2*(month_tmp> start_month)) + 
                              (month_tmp==start_month);
                        switch(c)
                        {
                            case 0:
                                break;
                            case 2:
                                a=1; break;
                            case 1:
                                c=(2*(day_tmp> start_day)) +
                                     (day_tmp==start_day);
                                switch(c)
                                {
                                    case 0:
                                        break;
                                    case 2:
                                    case 1:
                                        a=1; break;
                                }
                        }
                }
                if (a)
                {
                    /* check that the date is <= end_date */
                    a = 0;
                    c = (2*(year_tmp<end_year)) + (year_tmp==end_year);
                    switch(c)
                    {
                        case 0: 
                            break;
                        case 2: 
                            a=1; break;
                        case 1: 
                            c= (2*(month_tmp< end_month)) + 
                                  (month_tmp==end_month);
                            switch(c)
                            {
                                case 0:
                                    break;
                                case 2:
                                    a=1; break;
                                case 1:
                                    c=(2*(day_tmp< end_day)) +
                                         (day_tmp==end_day);
                                    switch(c)
                                    {
                                        case 0:
                                            break;
                                        case 2:
                                        case 1:
                                            a=1; break;
                                    }
                            }
                    }
                    if (a)  /* date is OK */
                    {
                        if (options & OPT_WRITE_DATE) 
                        {  
                            if (year_tmp > latest_year)
                            {
                                latest_year  = year_tmp;
                                latest_month = month_tmp;
                                latest_day   = day_tmp;
                            }
                            else
                            if (year_tmp == latest_year)
                            {
                                if (month_tmp >  latest_month)
                                {
                                    latest_month = month_tmp;
                                    latest_day   = day_tmp;
                                }
                                else
                                if (month_tmp == latest_month)
                                {
                                    if (day_tmp > latest_day)
                                        latest_day = day_tmp;
                                }
                            }        
                        } 
                        if (year_tmp < first_year)
                            first_year = year_tmp;
                        if (year_tmp > last_year)
                            last_year = year_tmp;
                        if (FAIL == AddProgram(day_tmp,month_tmp,year_tmp,length))
                            ERROR("Out of memory")
                    }
                }
                break;
                
            case RUBBISH:  
                break; /* forget this line and get a new one */
        }
    } while(!feof(stdin));
main99:
   if (options & OPT_WRITE_DATE)
   {
        wdatefile = fopen(argv[arg_w],"w");
        if (wdatefile)
            fprintf(wdatefile,"%s %d %d\n",
                monthstr[latest_month],latest_day,latest_year);
        else
            ERROR("Couldn't open datefile for output")
   }
   return(0);
}


IsItMonth(string)
UCHAR *string;
{
    int i;
    long month_tmp = ' ';
        
    for(i=0; i<3; i++)
        month_tmp = (month_tmp << 8 ) | *string++;
    
    switch(month_tmp)
    {
        case JAN:   return(1);
        case FEB:   return(2);
        case MAR:   return(3);
        case APR:   return(4); 
        case MAY:   return(5); 
        case JUN:   return(6); 
        case JUL:   return(7); 
        case AUG:   return(8); 
        case SEP:   return(9); 
        case OCT:   return(10); 
        case NOV:   return(11); 
        case DEC:   return(12); 
        default:    return(0);
    }
}


ReadLine()
{
    int c,i,j,mode,col=0;
    j=0;
    
    do
    {
        c=getchar();
        col++;
        if ( col<MAXCOL )
            buffer[col] = (UCHAR) c;
        if (col >= date_start_col && col <= date_end_col)
            line_date[ col - date_start_col ] = (UCHAR) c;
    } while( c != EOF && c != LF );    /* read line to buffer */
    
    if (col>MAXCOL)             /* What kind of line is this? */
        col=MAXCOL;
    else
    if (!col)
        mode=RUBBISH;
    if (c==EOF)
        mode=HEADER_EOF;
    else
    if (col<11) 
    {
        for(i=1;i<=col;i++)
        {
            if(buffer[i] == ':' && buffer[i+1] == LF)  
            {
                mode = HEADER;
                break;
            }
            else
                mode = RUBBISH;
        }
    }
    else
    {
        for(i=1; i<11; i++)
        {
            if ( !IS_PROTECTION_BIT(buffer[i]) )
            {
                for(j=1; j<=col; j++)
                {
                    if( buffer[j] == ':' && buffer[i+1] == LF ) 
                    {
                        mode = HEADER;
                        break;
                    }
                    else 
                        mode = RUBBISH;
                }
                break;
            }
            else 
                mode = PROGRAM;
        }
    }
    if (mode == PROGRAM)
    {
        if (options & OPT_SNOOP_DATE)   /* where does date start? */
        {
            date_start_col = FindDate();
            if (date_start_col)
            {
                date_end_col = date_start_col + 12;
                options = options & 0xFFFFFFFB; /* snoop_col off */
                for(i=0; i<=12; i++)
                {
                    line_date[i] = line_date[i+ date_start_col- DATE_START];
                }
            }
            else 
                date_start_col = DATE_START;
        }
        line_date[3]  = 0;
        line_date[6]  = 0;
        line_date[12] = 0;
        if (line_date[4] == ' ')    /* otherwise strtol() fails */
            line_date[4]  = '0';
        
        i=j=0;
        while(++j<=col)               /* do column cutting */
        {
            if (column[j])
                buffer[++i] = buffer[j];
        }
        col = i;
    }
                    /* line-length in 8 LSB, then the 'real' return code */
    return((mode << 8) | col); 
}


AddProgram(day_tmp,month_tmp,year_tmp,length)
int day_tmp,month_tmp,year_tmp,length;
{
    struct line *tmp1,*tmp2;
    
    tmp1 = (struct line *)malloc(sizeof(struct line));
    if (!tmp1)
        return(FAIL);
    tmp2 = daylist[month_tmp][day_tmp];   /* go to end of list */
    if (!tmp2)
        tmp2 = (struct maarittely *)&daylist[month_tmp][day_tmp];
    else
        while(tmp2->nextline)
            tmp2 = tmp2->nextline;
            
    tmp2->nextline = tmp1;          /* add line contents to list */
    tmp1->nextline = NULL;
    tmp1->year = year_tmp;
    tmp1->txt = (char *)malloc(length+1);
    if (!(tmp1->txt))
        return(FAIL);
    memcpy(tmp1->txt, &buffer[1], length); 
    *(tmp1->txt + length) = 0;   
    month[month_tmp]++; 
                /* There are prgs in this month that should be printed */
    return(OK);
}


OutputDir(mode,last_year)
int mode,last_year;
{
    int y,i,j,p=0;
    struct line *tmp1;
    
    for(i=1; i<=MONTHS; i++)
        p+=(month[i] > PRINT);
    if (!p)         /* no programs to print in this directory */
        return(OK);
    if (options & OPT_SNOOP_DATE)
        printf("%s",&next_header[0]);
    else
        printf("%s",&current_header[0]);
   
    for(y=first_year; y <= last_year; y++)
    {
        for(i=1; i<= MONTHS; i++) 
        {      /* There are prgs in this month that should be printed */
            if ( month[i] > PRINT )
            {
                for(j=1;j <=DAYS; j++)
                {
                    tmp1 = daylist[i][j];
                    if (tmp1)
                    {
                        if (y == last_year )  
                            daylist[i][j] = 0; /* must be 0 for next dir */
                        do
                        {
                            if (tmp1->year == y)
                            {
                                printf("%s",tmp1->txt);
                                month[i]--;
                            }
                            tmp1 = tmp1->nextline;
                        } while(tmp1);
                    }
                }  
            }
        } 
    } 
    putchar(LF);
    
    return(0);
}


FindDate()
{
    int i,col = NULL;

    for(i = DATE_START; i <= DATE_END; i++)
    {
        if (buffer[i] == ' ')
        {
            if (IsItMonth(&buffer[i+1]))
            {
                col = i+1;
                break;
            }
        }
    }
    return(col);
}

Help()
{
    printf("dfilter v1.0 by P.Hassi\n");
    printf("USAGE: dfilter ");
    printf("[?] [a] [r datefile] [w datefile] [c startcol endcol]\n");
    printf("                 [startdate] [-] [enddate] <Dir-listing\n");
    return(OK);
}
