rohrpost

A commandline mail client to change the world as we see it.
git clone git://r-36.net/rohrpost
Log | Files | Refs | LICENSE

mark.c (10694B)


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