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