rohrpost

A commandline mail client to change the world as we see it.
git clone git://r-36.net/rohrpost
Log | Files | Refs | README | 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