X-Git-Url: http://git.jankratochvil.net/?p=timeplan.git;a=blobdiff_plain;f=timeplan.c;h=61d71f91fc3e64d3897675fb6b41e2071946350d;hp=f8fd739a7e7630cea3d49a7aa455ee622dac5440;hb=0aa855d604bdacb2a88b4617e2f331f0f3387be1;hpb=e1a7415edb3f81591dce1045b0112d45fc36c05e diff --git a/timeplan.c b/timeplan.c index f8fd739..61d71f9 100644 --- a/timeplan.c +++ b/timeplan.c @@ -29,11 +29,13 @@ static const char version[]="This is TimePlan, version 1.0\n"; static const char *pname; static char *finame; static FILE *fi; -static int verbose=0,tree=0; +static int verbose=0,tree=0,doaverage=0; static char *formtotal=DEF_FORMTOTAL; static char buf[LINE_MAX]; static int line=0; static enum { SORT_NO,SORT_TIMETOT,SORT_STORES } sortby=SORT_TIMETOT; +static unsigned lifetime_days=0; +static unsigned long lifetime_seq=1; static void usage(void) { @@ -50,6 +52,7 @@ Usage: timeplan [-u|--unsort] [-s|--stores] [-T|--timetot] [-t|--tree]\n\ -s, --stores\t\tSort the result by stores count\n\ -T, --timetot\t\tSort the result by total time (default)\n\ -t, --tree\t\tOrganize data as hierarchy tree\n\ + -a, --average\t\tDisplay all times as average-per-day value\n\ -c, --condition\tDefine condition variable\n\ -f, --formtotal\tFormat \"Total\" column (\"text *val/val:width text\")\n\ -r, --rule\t\tAdd to the end of .rc file this rule (':'->'\\t')\n\ @@ -65,6 +68,7 @@ static const struct option longopts[]={ {"stores" ,0,0,'s'}, {"timetot" ,0,0,'T'}, {"tree" ,0,0,'t'}, +{"average" ,0,0,'a'}, {"condition",1,0,'c'}, {"formtotal",1,0,'f'}, {"rule" ,1,0,'r'}, @@ -81,22 +85,25 @@ static int calctime(int timetot,int at,int of) static struct action { struct action *next; int timetot,stores; + unsigned long last_seq; char what[1]; } *hashtable[HASH_SIZE]; static int hashtable_tot=0; static int dumpaction(const struct action *action) { -int tot FAKEUSE,mins FAKEUSE,hours FAKEUSE,days FAKEUSE,origtot; +int tot,mins FAKEUSE,hours FAKEUSE,days FAKEUSE,origtot; char *s,fmt[]="%?d"; if (action) { tot=(action->timetot+30)/60; + if (doaverage && lifetime_days) + tot/=lifetime_days; mins=tot; hours=mins/60; days=hours/24; mins%=60; hours%=24; - origtot=tot; } - else origtot=0; + else tot=0; + origtot=tot; for (s=formtotal;*s;s++) switch (*s) { case '*': case '/': { @@ -131,9 +138,9 @@ dump: else origtot++; } if (action) - printf("=%02d/%02d:%02d\t%4d\t%s\n",days,hours,mins, + printf("=%03d/%02d:%02d %4d\t%s\n",days,hours,mins, action->stores,action->what); - return(origtot); + return origtot; } #define A (*Ap) @@ -157,7 +164,7 @@ struct action **sorta FAKEUSE,**sorti FAKEUSE; int totalwidth=dumpaction(NULL); while (totalwidth-->5) putchar(' '); - puts("Total Da Hr Mi\tStor\tDescription"); + puts("Total Day Hr Mi Stor\tDescription"); if (!(sorta=malloc(sizeof(*sorta)*hashtable_tot))) { fprintf(ERRH1"malloc() of %d pointers"ERRNO1,ERRH2,hashtable_tot,ERRNO2); exit(EXIT_FAILURE); @@ -183,7 +190,7 @@ static unsigned calchash(const char *s) { unsigned r=57; - while (*s) r=r*7+11*(*s++); + while (*s) r=r*7+11*toupper(*s++); return r; } @@ -191,6 +198,8 @@ static void storeone(char *what,int length) { struct action **actionp,*action; + if (!*what) + return; if (verbose) printf("storeone: %d: %s\n",length,what); for (actionp=hashtable+(calchash(what)%HASH_SIZE);(action=*actionp);actionp=&action->next) if (!strcasecmp(action->what,what)) break; @@ -206,6 +215,13 @@ struct action **actionp,*action; *actionp=action; hashtable_tot++; } + else { + if (action->last_seq==lifetime_seq) { + if (verbose) puts("storeone: preventing duplicate"); + return; /* prevent duplicates like: TV-Music --> TV-Fun-Music-Fun */ + } + } + action->last_seq=lifetime_seq; action->timetot+=length; action->stores++; } @@ -222,8 +238,8 @@ static int iscondition(const char *text) struct textlist *cond; for (cond=conditions;cond;cond=cond->next) - if (!strcasecmp(text,cond->text)) return(1); - return(0); + if (!strcasecmp(text,cond->text)) return 1; + return 0; } static void addlist(struct textlist ***tail,const char *text,int line) @@ -342,6 +358,7 @@ static char *modify(char *what) { regmatch_t matches[10]; int i,m; +int doprep; static char modbuf1[sizeof(buf)],modbuf2[sizeof(modbuf1)]; char *src=what,*dstbase=modbuf1,*dst=dstbase; const char *start FAKEUSE,*end FAKEUSE,*patt; @@ -349,83 +366,104 @@ const struct textlist *item; if (verbose) printf("modify: %s\n",what); modify_load(); - for (m=0,item=modifies;item;m++,item=item->next) { + for (m=0,item=modifies;;m++,item=item->next) { enum { PATT_START,PATT_MID,PATT_END,PATT_TERM } pattpos; - i=regexec(&modistructs[m].regex,src,LENGTH(matches),matches,0); - if (i==REG_NOMATCH) continue; - if (i) { - fprintf(ERRH1"regexec() failed for \"%s\""ERRNO1,ERRH2,item->text,ERRNO2); - exit(EXIT_FAILURE); - } - if (verbose) printf("matched: %s -> %s\n",item->text,modistructs[m].dst); - pattpos=PATT_START; patt=NULL; - while (pattpos!=PATT_TERM) { - switch (pattpos) { - case PATT_START: - start=src; - end=src+matches->rm_so; - pattpos=PATT_MID; patt=modistructs[m].dst; - break; - case PATT_MID: - if (!*patt) { - pattpos=PATT_END; patt=NULL; - continue; + for (doprep=1;doprep>=0;doprep--) { + if (doprep) { + *dst++='-'; + for (patt=src;;patt++) { + if (*patt=='-' || !*patt) { + if (dst[-1]!='-') + *dst++='-'; + if (!*patt) + break; } - else if (*patt=='@') { - start=src; end=src+strlen(src); - } - else if (*patt>='0' && *patt<='9') { -regmatch_t *match=matches+(*patt-'0'); - if (match->rm_so==-1 - || match->rm_eo==-1) { - fprintf(ERRH1"Trying to substitute '%c' but no \"matches\" entry not set for \"%s\""WHERE1, - ERRH2,*patt,item->text,WHERE2); - exit(EXIT_FAILURE); - } - if (match->rm_so>match->rm_eo) { - fprintf(ERRH1"Trying to substitute '%c' start>end (%d>%d) for \"%s\""WHERE1, - ERRH2,*patt,match->rm_so,match->rm_eo,item->text,WHERE2); - exit(EXIT_FAILURE); - } - start=src+match->rm_so; end=src+match->rm_eo; + else + *dst++=*patt; + } + } + else { + i=regexec(&modistructs[m].regex,src,LENGTH(matches),matches,0); + if (i==REG_NOMATCH) continue; + if (i) { + fprintf(ERRH1"regexec() failed for \"%s\""ERRNO1,ERRH2,item->text,ERRNO2); + exit(EXIT_FAILURE); + } + if (verbose) printf("matched: %s -> %s\n",item->text,modistructs[m].dst); + pattpos=PATT_START; patt=NULL; + while (pattpos!=PATT_TERM) { + switch (pattpos) { + case PATT_START: + start=src; + end=src+matches->rm_so; + pattpos=PATT_MID; patt=modistructs[m].dst; + break; + case PATT_MID: + if (!*patt) { + pattpos=PATT_END; patt=NULL; + continue; + } + else if (*patt=='@') { + start=src; end=src+strlen(src); + } + else if (*patt>='0' && *patt<='9') { + regmatch_t *match=matches+(*patt-'0'); + if (match->rm_so==-1 + || match->rm_eo==-1) { + fprintf(ERRH1"Trying to substitute '%c' but no \"matches\" entry not set for \"%s\""WHERE1, + ERRH2,*patt,item->text,WHERE2); + exit(EXIT_FAILURE); + } + if (match->rm_so>match->rm_eo) { + fprintf(ERRH1"Trying to substitute '%c' start>end (%d>%d) for \"%s\""WHERE1, + ERRH2,*patt,match->rm_so,match->rm_eo,item->text,WHERE2); + exit(EXIT_FAILURE); + } + start=src+match->rm_so; end=src+match->rm_eo; + } + else { + start=patt; end=patt+1; + } + patt++; + break; + case PATT_END: + start=src+matches->rm_eo; + end=src+strlen(src); + pattpos=PATT_TERM; /* assumed: patt=NULL; */ + break; + default: + assert(0); } - else { - start=patt; end=patt+1; + if ((dst-dstbase+(end-start))>=sizeof(modbuf1)-1/* -1 for '-' during prepping */) { + fprintf(ERRH1"Maximum buffer size exceeded during substition for \"%s\""WHERE1, + ERRH2,item->text,WHERE2); + exit(EXIT_FAILURE); } - patt++; - break; - case PATT_END: - start=src+matches->rm_eo; - end=src+strlen(src); - pattpos=PATT_TERM; /* assumed: patt=NULL; */ - break; - default: - assert(0); + memcpy(dst,start,end-start); + dst+=end-start; + } } - if ((dst-dstbase+(end-start))>=sizeof(modbuf1)) { - fprintf(ERRH1"Maximum buffer size exceeded during substition for \"%s\""WHERE1, - ERRH2,item->text,WHERE2); - exit(EXIT_FAILURE); +/* SWAP buffers: */ + if (dst==dstbase || (dst==dstbase+1 && *dstbase=='-')) + return NULL; + *dst='\0'; + if (src==what) { + assert(dstbase==modbuf1); + src=dstbase; + dst=dstbase=modbuf2; } - memcpy(dst,start,end-start); - dst+=end-start; - } - if (dst==dstbase) return(NULL); - *dst='\0'; - if (src==what) { - assert(dstbase==modbuf1); - src=dstbase; - dst=dstbase=modbuf2; - } - else { -char *swap; - assert((src==modbuf1 && dstbase==modbuf2) - ||(src==modbuf2 && dstbase==modbuf1)); - swap=src; src=dstbase; dst=dstbase=swap; - } + else { + char *swap; + assert((src==modbuf1 && dstbase==modbuf2) + ||(src==modbuf2 && dstbase==modbuf1)); + swap=src; src=dstbase; dst=dstbase=swap; + } + if (!item) + return src; + } /* for (doprep) */ } - return src; + /* NOTREACHED */ } static void store(char *what,int length) @@ -444,22 +482,23 @@ char *ce; else *ce='\0'; } storeone(what,length); + lifetime_seq++; } -static void hit(time_t t) +static void hit(time_t t,char *bufaction) { static time_t last=-1; static char bufbackup[sizeof(buf)]; char *acts[ACTS_MAX],*s; int timetot,acti,i; - if (verbose) printf("hit: %ld: %s\n",t,buf+6); + if (verbose) printf("hit: %ld: %s\n",t,bufaction); if (last!=-1) { if (t23 || min <0 || min >59 + || sec <0 || sec >59 ) { fprintf(ERRH1"Incorrect day-time \"%s\""WHERE1,ERRH2,buf,WHERE2); exit(EXIT_FAILURE); @@ -611,8 +657,8 @@ const char *days[]={"Ne","Po","Ut","St","Ct","Pa","So"}; fprintf(ERRH1"Day-time found but no basetime timestamp set"WHERE1,ERRH2,WHERE2); exit(EXIT_FAILURE); } - currtime=basetime+(hour*60+min)*60; - hit(currtime); + currtime=basetime+(hour*60+min)*60+sec; + hit(currtime,buf+(buf[5]=='-'?6:9)); } if (!feof(fi) || ferror(fi)) { @@ -623,5 +669,5 @@ const char *days[]={"Ne","Po","Ut","St","Ct","Pa","So"}; fprintf(ERRH1"fclose(3) \"%s\""ERRNO1,ERRH2,finame,ERRNO2); dumphashtable(); - return(EXIT_SUCCESS); + return EXIT_SUCCESS; }