Long HH:MM:SS day-time implemented as an addition to HH:MM.
[timeplan.git] / timeplan.c
index 6c0979d..3847022 100644 (file)
@@ -43,6 +43,7 @@ This command summarizes the timelog information:\n\
 \n\
 Usage: timeplan [-u|--unsort] [-s|--stores] [-T|--timetot]  [-t|--tree]\n\
                 [-c|--condition <cond>] [-f|--formtotal <fmtstring>]\n\
+                [-r|--rule <ruleL>:<ruleR>]\n\
                 [-v|--verbose] [-h|--help] [-V|--version]\n\
 \n\
   -u, --unsort\t\tDon't sort the result in any way\n\
@@ -51,6 +52,7 @@ Usage: timeplan [-u|--unsort] [-s|--stores] [-T|--timetot]  [-t|--tree]\n\
   -t, --tree\t\tOrganize data as hierarchy tree\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\
@@ -65,6 +67,7 @@ static const struct option longopts[]={
 {"tree"     ,0,0,'t'},
 {"condition",1,0,'c'},
 {"formtotal",1,0,'f'},
+{"rule"     ,1,0,'r'},
 {"verbose"  ,0,0,'v'},
 {"help"     ,0,0,'h'},
 {"version"  ,0,0,'V'}};
@@ -180,7 +183,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;
 }
 
@@ -237,7 +240,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 +315,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 +327,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);
@@ -340,40 +344,64 @@ regmatch_t matches[10];
 int i,m;
 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) {
+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) 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);
-                               }
-                       else if (*patt>='0' && *patt<='9') {
+               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;
+                                               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)) {
                                fprintf(ERRH1"Maximum buffer size exceeded during substition for \"%s\""WHERE1,
@@ -418,20 +446,20 @@ char *ce;
        storeone(what,length);
 }
 
-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 (t<last) {
                        fprintf(ERRH1"Time goes backward"WHERE1,ERRH2,WHERE2);
                        t=last;
                        }
-               acts[0]=bufbackup+6;
+               acts[0]=bufbackup;
                acti=1;
                while ((s=strchr(acts[acti-1],'+'))) {
                        *s='\0';
@@ -445,7 +473,7 @@ int timetot,acti,i;
                for (i=0;i<acti;i++)
                        store(acts[i],calctime(timetot,i,acti));
                }
-       strcpy(bufbackup,buf);
+       strcpy(bufbackup,bufaction);
        last=t;
 }
 
@@ -453,10 +481,10 @@ int main(int argc,char **argv)
 {
 int optc;
 time_t basetime=-1,currtime;
-int hour,min;
+int hour,min,sec;
 
        pname=argv[0];
-       while ((optc=getopt_long(argc,argv,"b:pw:qusTtc:f:vhV",longopts,NULL))!=EOF) switch (optc) {
+       while ((optc=getopt_long(argc,argv,"b:pw:qusTtc:f:r:vhV",longopts,NULL))!=EOF) switch (optc) {
                
                case 'u':
                        sortby=SORT_NO;
@@ -478,6 +506,21 @@ int hour,min;
                case 'f':
                        formtotal=optarg;
                        break;
+               case 'r': {
+char *s,*dup;
+
+                       if (!(s=strchr(optarg,':'))) {
+                               fprintf(ERRH1"No delimiting ':'-character found in -r option \"%s\"!\n",ERRH2,optarg);
+                               exit(EXIT_FAILURE);
+                               }
+                       if (!(dup=strdup(optarg))) {
+                               fprintf(ERRH1"malloc() of \"%s\" string"ERRNO1,ERRH2,optarg,ERRNO2);
+                               exit(EXIT_FAILURE);
+                               }
+                       dup[s-optarg]='\t';
+                       addlist(&modifiescmdltail,dup,0);
+                       modifies_tot++;
+                       } break;
                case 'v':
                        verbose=1;
                        break;
@@ -556,10 +599,13 @@ const char *days[]={"Ne","Po","Ut","St","Ct","Pa","So"};
                        continue;
                        }
 
-               if (!isdigit(buf[0]) || !isdigit(buf[1]) || buf[2]!=':' || !isdigit(buf[3]) || !isdigit(buf[4]) || buf[5]!='-'
-                || sscanf(buf,"%d:%d-",&hour,&min)!=2
+               if (!(isdigit(buf[0]) && isdigit(buf[1]) && buf[2]==':' && isdigit(buf[3]) && isdigit(buf[4])
+                && (buf[5]=='-' || (buf[5]==':' && isdigit(buf[6]) && isdigit(buf[7]) && buf[8]=='-'))
+                     )
+                || (sec=0,sscanf(buf,(buf[5]=='-'?"%d:%d-":"%d:%d:%d-"),&hour,&min,&sec)!=2+(buf[5]==':'))
                 || hour<0 || hour>23
                 || min <0 || min >59
+                || sec <0 || sec >59
                    ) {
                        fprintf(ERRH1"Incorrect day-time \"%s\""WHERE1,ERRH2,buf,WHERE2);
                        exit(EXIT_FAILURE);
@@ -568,8 +614,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))  {