mark.c - rohrpost - A commandline mail client to change the world as we see it.
 (HTM) git clone git://r-36.net/rohrpost
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       mark.c (10722B)
       ---
            1 /*
            2  * Copy me if you can.
            3  * by 20h
            4  */
            5 
            6 #include <unistd.h>
            7 #include <stdio.h>
            8 #include <stdlib.h>
            9 #include <stdarg.h>
           10 #include <string.h>
           11 #include <strings.h>
           12 #include <errno.h>
           13 
           14 #include "ind.h"
           15 #include "arg.h"
           16 #include "cfg.h"
           17 #include "mark.h"
           18 #include "llist.h"
           19 #include "txtdb.h"
           20 #include "imap.h"
           21 #include "path.h"
           22 
           23 mark_t *
           24 mark_init(char *cfgn, char *mailbox)
           25 {
           26         char *path;
           27         mark_t *marks;
           28 
           29         if (cfgn == NULL)
           30                 cfgn = "default";
           31 
           32         path = path_mkmarkfile(cfgn, mailbox);
           33         marks = mark_read(path);
           34         if (marks == NULL)
           35                 marks = mark_new();
           36         marks->path = memdups(path);
           37         free(path);
           38 
           39         marks->data = memdups(mailbox);
           40         marks->name = memdups(cfgn);
           41 
           42         return marks;
           43 }
           44 
           45 void
           46 mark_free(mark_t *marks)
           47 {
           48         if (marks->data != NULL)
           49                 free(marks->data);
           50         txtdb_free(marks);
           51 }
           52 
           53 mark_t *
           54 mark_cfg(config_t *cfg)
           55 {
           56         char *selected;
           57         mark_t *marks;
           58 
           59         selected = config_getstr(cfg, "selected");
           60         if (selected == NULL)
           61                 return NULL;
           62         marks = mark_init(cfg->name, selected);
           63         free(selected);
           64 
           65         return marks;
           66 }
           67 
           68 void
           69 mark_stop(mark_t *marks)
           70 {
           71         char *path;
           72 
           73         if (marks->changed) {
           74                 path = path_mkmarkfile(marks->name, (char *)marks->data);
           75                 if (mark_write(marks, path) == NULL)
           76                         edie("mark_write");
           77                 free(path);
           78         }
           79 
           80         mark_free(marks);
           81 }
           82 
           83 llistelem_t *
           84 mark_set(mark_t *marks, char *seq, char *value)
           85 {
           86         if (strcspn(seq, "[]") != strlen(seq))
           87                 die("'[]' not allowed in sequence name.");
           88 
           89         return txtdb_set(marks, seq, value);
           90 }
           91 
           92 void *
           93 mark_internget(mark_t *marks, char *seq, int llist)
           94 {
           95         llistelem_t *elem;
           96         llist_t *elist, *rlist;
           97         int lseq, begin, end, step, rdir, sdir, nargs, i;
           98         char *cseq, *pbegin, *pend, *pstep, *ppend;
           99 
          100         lseq = strlen(seq);
          101         if (strcspn(seq, "[]") != lseq) {
          102                 elist = NULL;
          103                 nargs = 0;
          104                 //printf("Found a slicing sequence.\n");
          105                 cseq = memdup(seq, lseq+1);
          106                 pbegin = strchr(cseq, '[');
          107                 if (pbegin == NULL)
          108                         die("Sequence slicing should begin with '['.\n");
          109                 pbegin[0] = '\0';
          110                 pbegin++;
          111 
          112                 ppend = strchr(pbegin, ']');
          113                 if (ppend == NULL)
          114                         die("Sequence slicing has to end in ']'.\n");
          115                 if (ppend[1] != '\0') {
          116                         die("No characters allowed after ']' in"
          117                                         " sequence slicing.\n");
          118                 }
          119                 ppend[0] = '\0';
          120                 //printf("pbegin = %s\n", pbegin);
          121 
          122                 pend = strchr(pbegin, ':');
          123                 if (pend != NULL) {
          124                         pend[0] = '\0';
          125                         pend++;
          126                         //printf("pend = %s\n", pend);
          127 
          128                         pstep = strchr(pend, ':');
          129                         if (pstep != NULL) {
          130                                 pstep[0] = '\0';
          131                                 pstep++;
          132                                 //printf("pstep = %s\n", pstep);
          133                         }
          134                 } else {
          135                         pstep = NULL;
          136                 }
          137 
          138                 //printf("Getting elist for %s\n", cseq);
          139                 elist = (llist_t *)mark_internget(marks, cseq, 1);
          140                 if (elist == NULL) {
          141                         free(cseq);
          142                         return NULL;
          143                 }
          144 
          145                 if (elist->len < 1) {
          146                         rlist = elist;
          147                         elist = NULL;
          148                         goto slicingreturn;
          149                 }
          150 
          151                 //printf("Checking nargs = 3\n");
          152                 step = 1;
          153                 if (pstep != NULL) {
          154                         nargs++;
          155                         if (pstep[0] != '\0')
          156                                 step = atoi(pstep);
          157                         //printf("pstep = %s\n", pstep);
          158                 }
          159                 //printf("step = %d\n", step);
          160                 if (step == 0) {
          161                         die("Step size cannot be zero in sequence "
          162                                         "slicing.\n");
          163                 }
          164                 sdir = (step > 0)? 1 : -1;
          165                 //printf("sdir = %d\n", sdir);
          166 
          167                 //printf("Checking nargs = 1\n");
          168                 nargs = 1;
          169                 if (pbegin[0] == '\0' && sdir < 0) {
          170                         begin = elist->len - 1;
          171                 } else {
          172                         begin = atoi(pbegin);
          173                 }
          174                 //printf("begin = %d\n", begin);
          175 
          176                 //printf("Checking nargs = 2\n");
          177                 if (pend != NULL) {
          178                         nargs++;
          179                         if (pend[0] == '\0') {
          180                                 if (sdir < 0) {
          181                                         end = 0;
          182                                 } else {
          183                                         end = elist->len - 1;
          184                                 }
          185                         } else {
          186                                 end = atoi(pend);
          187                                 if (pbegin[0] == '\0')
          188                                         end++;
          189                         }
          190                         //printf("end = %d\n", end);
          191                 }
          192 
          193                 if (nargs >= 2) {
          194                         if (end < 0)
          195                                 end = elist->len + end;
          196                         if (end < 0 || end > elist->len)
          197                                 die("End is out of range.\n");
          198                 }
          199                 if (begin < 0)
          200                         begin = elist->len + begin;
          201                 if (begin < 0 || begin > elist->len)
          202                         die("Begin is out of range.\n");
          203 
          204                 //printf("len = %d\n", elist->len);
          205                 //printf("begin = %d\n", begin);
          206                 //printf("end = %d\n", end);
          207 
          208                 rlist = llist_new();
          209                 /*
          210                  * [0]
          211                  */
          212                 //printf("nargs = %d\n", nargs);
          213                 if (nargs == 1) {
          214                         if (pbegin[0] == '\0') {
          215                                 die("Syntax error in begin in "
          216                                         "sequence slicing.\n");
          217                         }
          218 
          219                         //printf("getn\n");
          220                         elem = llist_getn(elist, begin);
          221                         //printf("add\n");
          222                         llist_add(rlist, elem->key, elem->data,
          223                                         elem->datalen);
          224                         goto slicingreturn;
          225                 }
          226 
          227                 /*
          228                  * [0:1:1]
          229                  */
          230                 rdir = ((end - begin) > 0)? 1 : -1;
          231                 //printf("rdir = %d; sdir = %d;\n", rdir, sdir);
          232                 if (rdir != sdir)
          233                         goto slicingreturn;
          234 
          235                 i = 0;
          236                 elem = llist_getn(elist, begin);
          237                 llist_add(rlist, elem->key, elem->data, elem->datalen);
          238                 for (;;) {
          239                         //printf("begin = %d; step = %d; sdir = %d;"
          240                         //        " end = %d\n", begin, step, sdir, end);
          241                         begin += step;
          242                         if (begin * sdir > end * sdir)
          243                                 break;
          244 
          245                         for (i = abs(step); i > 0; i--) {
          246                                 if (sdir > 0) {
          247                                         elem = elem->next;
          248                                 } else {
          249                                         elem = elem->prev;
          250                                 }
          251                         }
          252 
          253                         llist_add(rlist, elem->key, elem->data,
          254                                         elem->datalen);
          255                 }
          256 slicingreturn:
          257                 free(cseq);
          258                 //printf("slicing return\n");
          259                 if (elist != NULL)
          260                         llist_free(elist);
          261                 //printf("elist freed\n");
          262                 if (!llist) {
          263                         //printf("llist\n");
          264                         pbegin = llist_joinstr(rlist, " ");
          265                         llist_free(rlist);
          266                         //printf("%s = %s\n", seq, pbegin);
          267                         elem = llistelem_rawnew(seq, pbegin,
          268                                 (pbegin != NULL)? strlen(pbegin) : 0);
          269                         return elem;
          270                 }
          271 
          272                 return rlist;
          273         } else {
          274                 //printf("Non-slicing sequence.\n");
          275                 elem = txtdb_get(marks, seq);
          276                 if (elem == NULL || elem->data == NULL)
          277                         return NULL;
          278         }
          279 
          280         if (llist)
          281                 return llist_splitstr((char *)elem->data, " ");
          282         return elem;
          283 }
          284 
          285 llistelem_t *
          286 mark_get(mark_t *marks, char *seq)
          287 {
          288         return (llistelem_t *)mark_internget(marks, seq, 0);
          289 }
          290 
          291 llist_t *
          292 mark_getlist(mark_t *marks, char *seq)
          293 {
          294         return (llist_t *)mark_internget(marks, seq, 1);
          295 }
          296 
          297 char *
          298 mark_getstr(mark_t *marks, char *seq)
          299 {
          300         llistelem_t *elem;
          301 
          302         elem = mark_get(marks, seq);
          303         if (elem == NULL || elem->data == NULL)
          304                 return NULL;
          305 
          306         return elem->data;;
          307 }
          308 
          309 void
          310 mark_printelem(llistelem_t *elem, int onlyname, int onlyvalue)
          311 {
          312         if (onlyname) {
          313                 llistelem_printkey(elem);
          314         } else if (onlyvalue) {
          315                 llistelem_printdata(elem);
          316         } else {
          317                 llistelem_print(elem);
          318         }
          319 }
          320 
          321 void
          322 markusage(char *argv0)
          323 {
          324         die("usage: %s [-hnqv] [-c cfg] [-m folder] [-l|sequence [value ...]]"
          325                         "|-d sequence|"
          326                         "-s regex|-a sequence value ...|-r sequence"
          327                         " value ...]\n", argv0);
          328 }
          329 
          330 int
          331 markmain(int argc, char *argv[])
          332 {
          333         int status;
          334         char *seqname, *str, *selected, *cfgn, *argv0;
          335         config_t *cfg;
          336         mark_t *marks;
          337         llist_t *results, *values, *sequence;
          338         llistelem_t *result, *elem;
          339         int olen;
          340 
          341         enum {
          342                 BEQUIET = 0x01,
          343                 ONLYNAMES = 0x02,
          344                 DOLIST = 0x04,
          345                 DODELETE = 0x08,
          346                 DOSEARCH = 0x10,
          347                 DOADD = 0x20,
          348                 DOREMOVE = 0x40,
          349                 ONLYVALUE = 0x80,
          350 
          351                 NOARGS = 0x100
          352         };
          353 
          354         status = 0;
          355         seqname = NULL;
          356         values = NULL;
          357         selected = NULL;
          358         cfgn = NULL;
          359 
          360         ARGBEGIN(argv0) {
          361         case 'a':
          362                 status |= DOADD;
          363                 break;
          364         case 'c':
          365                 cfgn = EARGF(markusage(argv0));
          366                 break;
          367         case 'd':
          368                 status |= DODELETE;
          369                 break;
          370         case 'l':
          371                 status |= DOLIST;
          372                 break;
          373         case 'm':
          374                 selected = EARGF(markusage(argv0));
          375                 break;
          376         case 'n':
          377                 status |= ONLYNAMES;
          378                 break;
          379         case 'q':
          380                 status |= BEQUIET;
          381                 break;
          382         case 'r':
          383                 status |= DOREMOVE;
          384                 break;
          385         case 's':
          386                 status |= DOSEARCH;
          387                 break;
          388         case 'v':
          389                 status |= ONLYVALUE;
          390                 break;
          391         case 'h':
          392         default:
          393                 markusage(argv0);
          394         } ARGEND;
          395 
          396         if (selected == NULL) {
          397                 cfg = config_init(cfgn);
          398                 selected = config_getstr(cfg, "selected");
          399                 if (selected == NULL)
          400                         die("Cannot proceed without any selected mailbox.\n");
          401                 config_stop(cfg);
          402         } else {
          403                 selected = memdups(selected);
          404         }
          405         marks = mark_init(cfgn, selected);
          406 
          407         if (status & DOLIST) {
          408                 if (marks->values->len > 0) {
          409                         llist_sort(marks->values);
          410                         forllist(marks->values, elem) {
          411                                 mark_printelem(elem, status & ONLYNAMES,
          412                                                 status & ONLYVALUE);
          413                         }
          414                 } else {
          415                         if (!(status & BEQUIET))
          416                                 printf("No marks found.\n");
          417                 }
          418                 free(selected);
          419                 return 0;
          420         }
          421 
          422         if ((status & DOSEARCH) && !(status & DODELETE)) {
          423                 if (argc < 1)
          424                         die("Need at least one argument for search.\n");
          425 
          426                 results = mark_find(marks, argv[0]);
          427                 if (results == NULL) {
          428                         if (!(status & BEQUIET))
          429                                 printf("No matching sequence found.\n");
          430                         goto badmarkending;
          431                 }
          432                 forllist(results, elem) {
          433                         mark_printelem(elem, status & ONLYNAMES,
          434                                         status & ONLYVALUE);
          435                 }
          436 
          437                 free(selected);
          438                 mark_stop(marks);
          439                 llist_free(results);
          440                 return 0;
          441         }
          442         if (status & DODELETE) {
          443                 if (argc < 1)
          444                         die("Need at least one argument for delete.\n");
          445 
          446                 if (status & DOSEARCH) {
          447                         results = mark_find(marks, argv[0]);
          448                         if (results == NULL) {
          449                                 if (!(status & BEQUIET))
          450                                         printf("No such sequence in marks.\n");
          451                                 goto badmarkending;
          452                         }
          453 
          454                         llist_listdel(marks->values, results);
          455                         llist_free(results);
          456                         if (!(status & BEQUIET)) {
          457                                 printf("%d sequences removed from marks.\n",
          458                                                 results->len);
          459                         }
          460                         mark_stop(marks);
          461                 } else {
          462                         result = mark_del(marks, argv[0]);
          463                         if (result == NULL) {
          464                                 if (!(status & BEQUIET))
          465                                         printf("Nothing deleted.\n");
          466                                 return 1;
          467                         } else {
          468                                 if (!(status & BEQUIET)) {
          469                                         printf("One sequence removed from "
          470                                                         "mark.\n");
          471                                 }
          472                                 mark_stop(marks);
          473                         }
          474                 }
          475                 free(selected);
          476                 return 0;
          477         }
          478 
          479         if (argc == 1) {
          480                 result = mark_get(marks, argv[0]);
          481                 if (result == NULL)
          482                         die("No such sequence found.\n");
          483                 mark_printelem(result, status & ONLYNAMES,
          484                                 status & ONLYVALUE);
          485                 free(selected);
          486                 return 0;
          487         }
          488 
          489         if (argc > 1) {
          490                 seqname = argv[0];
          491                 result = mark_get(marks, seqname);
          492                 if (result == NULL) {
          493                         sequence = llist_new();
          494                 } else {
          495                         sequence = llist_splitstr((char *)result->data, " ");
          496                 }
          497         } else {
          498                 markusage(argv0);
          499         }
          500 
          501         values = imap_argv2ids(cfgn, selected, argc, argv);
          502         free(selected);
          503 
          504         if (status & DOREMOVE) {
          505                 olen = sequence->len;
          506                 if (olen < 1) {
          507                         die("%s sequence has no elements to work on.\n",
          508                                         seqname);
          509                 }
          510 
          511                 llist_listdel(sequence, values);
          512                 if (!(status & BEQUIET)) {
          513                         printf("%d elements removed from sequence %s.\n",
          514                                         (olen - sequence->len), seqname);
          515                 }
          516         } else {
          517                 if (status & DOADD) {
          518                         olen = sequence->len;
          519                         llist_listadd(sequence, values);
          520                         if (!(status & BEQUIET)) {
          521                                 printf("%d elements added to sequence %s.\n",
          522                                                 (sequence->len - olen),
          523                                                 seqname);
          524                         }
          525                 } else {
          526                         status |= NOARGS;
          527                         llist_free(sequence);
          528                         sequence = values;
          529                 }
          530         }
          531 
          532         if (sequence->len > 0) {
          533                 str = llist_joinstr(sequence, " ");
          534                 mark_set(marks, seqname, str);
          535                 free(str);
          536         } else {
          537                 mark_set(marks, seqname, "");
          538         }
          539         if (status & NOARGS && !(status & BEQUIET)) {
          540                 result = mark_get(marks, seqname);
          541                 mark_printelem(result, status & ONLYNAMES, status & ONLYVALUE);
          542         }
          543 
          544         mark_stop(marks);
          545         return 0;
          546 badmarkending:
          547         free(selected);
          548         mark_stop(marks);
          549         return 1;
          550 }
          551