X-Git-Url: http://git.jankratochvil.net/?p=timeplan.git;a=blobdiff_plain;f=timeplan.c;h=61d71f91fc3e64d3897675fb6b41e2071946350d;hp=6c0979d7fa01048c6fec5669696030554f7bd6c4;hb=0aa855d604bdacb2a88b4617e2f331f0f3387be1;hpb=6c24f75178d70ec0619444aad85c409af4273f58 diff --git a/timeplan.c b/timeplan.c index 6c0979d..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) { @@ -43,14 +45,17 @@ This command summarizes the timelog information:\n\ \n\ Usage: timeplan [-u|--unsort] [-s|--stores] [-T|--timetot] [-t|--tree]\n\ [-c|--condition ] [-f|--formtotal ]\n\ + [-r|--rule :]\n\ [-v|--verbose] [-h|--help] [-V|--version]\n\ \n\ -u, --unsort\t\tDon't sort the result in any way\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\ -v, --verbose\t\tInform about phases of transfer\n\ -h, --help\t\tPrint a summary of the options\n\ -V, --version\t\tPrint the version number\n\ @@ -63,8 +68,10 @@ 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'}, {"verbose" ,0,0,'v'}, {"help" ,0,0,'h'}, {"version" ,0,0,'V'}}; @@ -78,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 '/': { @@ -128,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) @@ -154,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); @@ -180,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; } @@ -188,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; @@ -203,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++; } @@ -219,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) @@ -237,7 +256,7 @@ struct textlist *item; *tail=&item->next; } -static struct textlist *modifies,**modifiestail=&modifies; +static struct textlist *modifies,**modifiestail=&modifies,*modifiescmdl,**modifiescmdltail=&modifiescmdl; static int modifies_tot; static struct modistruct { regex_t regex; @@ -312,6 +331,7 @@ int isplus; } if (fclose(fi)) fprintf(ERRH1"fclose(3) \"%s\""ERRNO1,ERRH2,finame,ERRNO2); + *modifiestail=modifiescmdl; /* add all command-line arguments to the end */ if (!(modistructs=malloc(sizeof(*modistructs)*modifies_tot))) { fprintf(ERRH1"malloc() of %d modistructs's"ERRNO1,ERRH2,modifies_tot,ERRNO2); exit(EXIT_FAILURE); @@ -323,7 +343,7 @@ int isplus; exit(EXIT_FAILURE); } *s++='\0'; modistructs[m].dst=s; - if (verbose) fprintf(stderr,"regcomp: %s -> %s\n",item->text,s); + if (verbose) printf("regcomp: %s -> %s\n",item->text,s); if (regcomp(&modistructs[m].regex,item->text,REG_EXTENDED|REG_ICASE)) { fprintf(ERRH1"regcomp() failed for \"%s\" (%s)"WHERE1,ERRH2,item->text,ERRNO2,WHERE2); exit(EXIT_FAILURE); @@ -338,66 +358,112 @@ 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,*end,*patt; +const char *start FAKEUSE,*end FAKEUSE,*patt; const struct textlist *item; if (verbose) printf("modify: %s\n",what); modify_load(); - for (m=0,item=modifies;item;m++,item=item->next) { - 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) fprintf(stderr,"matched: %s -> %s\n",item->text,modistructs[m].dst); - for (patt=modistructs[m].dst;*patt;patt++) { - if (*patt=='@') { - start=src; end=src+strlen(src); + for (m=0,item=modifies;;m++,item=item->next) { +enum { PATT_START,PATT_MID,PATT_END,PATT_TERM } pattpos; + + for (doprep=1;doprep>=0;doprep--) { + if (doprep) { + *dst++='-'; + for (patt=src;;patt++) { + if (*patt=='-' || !*patt) { + if (dst[-1]!='-') + *dst++='-'; + if (!*patt) + break; + } + else + *dst++=*patt; + } } - 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); + 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 (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); + 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); + } + 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); + } + memcpy(dst,start,end-start); + dst+=end-start; } - start=src+match->rm_so; end=src+match->rm_eo; } - else { - start=patt; end=patt+1; +/* SWAP buffers: */ + if (dst==dstbase || (dst==dstbase+1 && *dstbase=='-')) + return NULL; + *dst='\0'; + if (src==what) { + assert(dstbase==modbuf1); + src=dstbase; + dst=dstbase=modbuf2; } - 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); + else { + char *swap; + assert((src==modbuf1 && dstbase==modbuf2) + ||(src==modbuf2 && dstbase==modbuf1)); + swap=src; src=dstbase; dst=dstbase=swap; } - 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; - } + if (!item) + return src; + } /* for (doprep) */ } - return src; + /* NOTREACHED */ } static void store(char *what,int length) @@ -416,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); @@ -568,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)) { @@ -580,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; }