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