nt

simple note taker
git clone git://edryd.org/nt
Log | Files | Refs | LICENSE

nt.c (5681B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <libgen.h>
      3 #include <stdarg.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <unistd.h>
      8 
      9 #include "arg.h"
     10 #include "str.h"
     11 #include "util.h"
     12 
     13 /* macros */
     14 #define MAX_SUB 2048
     15 
     16 /* typedefs */
     17 typedef struct Note {
     18 	char *str;
     19 	struct Note *next;
     20 	struct Note *prev;
     21 } Note;
     22 
     23 /* functions */
     24 void linkadd(char *str);
     25 int confirm(const char *msg, ...);
     26 char *get_cat(char *str);
     27 
     28 void nt_del(void);
     29 void nt_del_all(void);
     30 void nt_edit(void);
     31 void nt_list_all(void);
     32 void nt_list_n(void);
     33 void nt_new(void);
     34 
     35 void setup(void);
     36 void cleanup(void);
     37 void usage(void);
     38 
     39 /* variables */
     40 char *argv0;
     41 
     42 char *sub;
     43 Note *head = NULL;
     44 Note *tail = NULL;
     45 
     46 void (*mode)(void) = nt_new;
     47 int neednt = 1;
     48 int lsnum;
     49 
     50 #include "config.h"
     51 
     52 /* add str to Note linked list */
     53 void
     54 linkadd(char *str)
     55 {
     56 	if (!head->str && !head->next && !head->prev) {
     57 		head = ecalloc(1, sizeof(Note));
     58 		head->str  = estrdup(str);
     59 		head->next = NULL;
     60 		head->prev = NULL;
     61 		tail = head;
     62 		return;
     63 	}
     64 
     65 	/* go through notes until we reach the last one */
     66 	Note *cur = head;
     67 	while (cur->next)
     68 		cur = cur->next;
     69 
     70 	/* allocate note */
     71 	cur->next = ecalloc(1, sizeof(Note));
     72 	cur->next->prev = cur;
     73 	cur = cur->next;
     74 	cur->str = estrdup(str);
     75 	cur->next = NULL;
     76 	tail = cur;
     77 }
     78 
     79 /* prompt user if no -y option to confirm action */
     80 int
     81 confirm(const char *msg, ...)
     82 {
     83 	char input = 'n';
     84 	va_list ap;
     85 
     86 	if (yes) return 1;
     87 
     88 	va_start(ap, msg);
     89 	printf("%s: ", argv0);
     90 	vprintf(msg, ap);
     91 	printf("? [y/N] ");
     92 	va_end(ap);
     93 	scanf("%c", &input);
     94 
     95 	return (input == 'y' || input == 'Y') ? 1 : 0;
     96 }
     97 
     98 /* return just category from str */
     99 char *
    100 get_cat(char *str)
    101 {
    102 	char *cat = estrdup(str);
    103 	char delim[2] = { catdelim, 0 };
    104 
    105 	if (strchr(str, catdelim))
    106 		strtok(cat, delim);
    107 	else
    108 		cat = NULL;
    109 
    110 	return cat;
    111 }
    112 
    113 /* delete oldest matching note from notes */
    114 void
    115 nt_del(void)
    116 {
    117 	if (!sub) usage();
    118 	Note *cur = head;
    119 
    120 	for (; cur; cur = cur->next) {
    121 		if (!strcmp(cur->str, sub)) {
    122 			if (confirm("delete note '%s'", cur->str)) {
    123 				if (!cur->prev) { /* beginning */
    124 					head = cur->next;
    125 				} else if (!cur->next) { /* end */
    126 					cur->prev->next = NULL;
    127 				} else { /* middle */
    128 					cur->prev->next = cur->next;
    129 					cur->next->prev = cur->prev;
    130 				}
    131 				free(cur);
    132 			}
    133 			return;
    134 		}
    135 	}
    136 
    137 	die("%s: delete: '%s' not found", argv0, sub);
    138 }
    139 
    140 /* delete all notes from fname */
    141 void
    142 nt_del_all(void)
    143 {
    144 	Note *cur = head;
    145 	if (confirm("delete all notes in '%s'", fname)) {
    146 		for (; cur; cur = cur->next)
    147 			cur->str = NULL;
    148 		remove(fname);
    149 	}
    150 }
    151 
    152 /* edit given note sub from stdin */
    153 void
    154 nt_edit(void)
    155 {
    156 	if (!sub) usage();
    157 	Note *cur = head;
    158 
    159 	for (; cur; cur = cur->next)
    160 		if (!strcmp(cur->str, sub)) {
    161 			fgets(cur->str, MAX_SUB, stdin);
    162 			strtrim(cur->str);
    163 			return;
    164 		}
    165 
    166 	die("%s: edit: '%s' not found", argv0, sub);
    167 }
    168 
    169 /* print out entire file */
    170 void
    171 nt_list_all(void)
    172 {
    173 	Note *cur = head;
    174 	for (; cur; cur = cur->next)
    175 		if (cur->str)
    176 			printf("%s\n", cur->str);
    177 }
    178 
    179 /* display n most recent subjects in file */
    180 void
    181 nt_list_n(void)
    182 {
    183 	int i;
    184 	Note *cur = tail;
    185 	for (i = 0; i < lsnum && cur; cur = cur->prev, i++)
    186 		if (cur->str)
    187 			printf("%s\n", cur->str);
    188 }
    189 
    190 /* list all categories or all notes with a given category */
    191 void
    192 nt_cat(void)
    193 {
    194 	Note *cur = head;
    195 	char **cat = ecalloc(1, sizeof(char*));
    196 	int catc = 0, i;
    197 
    198 	for (; cur; cur = cur->next, catc++) {
    199 		cat = erealloc(cat, (catc+2) * sizeof(char*));
    200 		if (!(cat[catc] = get_cat(cur->str)))
    201 			continue;
    202 		if (!strcmp(sub, "") && !strinlist(cat[catc], cat, catc))
    203 			printf("%s\n", cat[catc]);
    204 		else if (!strcmp(sub, cat[catc]))
    205 			printf("%s\n", cur->str);
    206 	}
    207 
    208 	for (i = 0; i < catc; i++)
    209 		free(cat[i]);
    210 	free(cat);
    211 }
    212 
    213 /* create a new note */
    214 void
    215 nt_new(void)
    216 {
    217 	if (!sub) usage();
    218 	linkadd(sub);
    219 }
    220 
    221 /* populate notes list, allocate sub */
    222 void
    223 setup(void)
    224 {
    225 	char buf[MAX_SUB];
    226 	FILE *fp;
    227 
    228 	head = ecalloc(1, sizeof(Note));
    229 	tail = head;
    230 
    231 	/* load file if it exists */
    232 	if (access(fname, F_OK) != -1) {
    233 		if (!(fp = fopen(fname, "r")))
    234 			die("%s: %s:", argv0, fname);
    235 		while (fscanf(fp, "%2048[^\n]\n", buf) != EOF)
    236 			linkadd(buf);
    237 		fclose(fp);
    238 	}
    239 
    240 	sub = ecalloc(MAX_SUB, sizeof(char));
    241 
    242 }
    243 
    244 /* write list to file, free memory */
    245 void
    246 cleanup(void)
    247 {
    248 	FILE *fp;
    249 	Note *cur = head;
    250 
    251 	/* write note list to file */
    252 	if (!(fp = fopen(fname, "w")))
    253 		die("%s: %s:", argv0, fname);
    254 	for (; cur; cur = cur->next)
    255 		if (cur->str)
    256 			fprintf(fp, "%s\n", cur->str);
    257 	fclose(fp);
    258 
    259 	for (cur = head; cur; cur = cur->next)
    260 		free(cur->str);
    261 	free(head);
    262 	free(sub);
    263 }
    264 
    265 void
    266 usage(void)
    267 {
    268 	die("usage: %s [-Dilyv] [-f FILE] [-e NOTE] [-d NOTE]\n"
    269 	    "          [-c [CATEGORY]] [-n NUM | -NUM] [NOTE ...]", argv0);
    270 }
    271 
    272 int
    273 main(int argc, char *argv[])
    274 {
    275 	ARGBEGIN {
    276 	case 'd':
    277 		mode = nt_del;
    278 		break;
    279 	case 'D':
    280 		mode = nt_del_all;
    281 		neednt = 0;
    282 		break;
    283 	case 'e':
    284 		mode = nt_edit;
    285 		break;
    286 	case 'f':
    287 		fname = EARGF(usage());
    288 		break;
    289 	case 'i':
    290 		yes = 0;
    291 		break;
    292 	case 'l':
    293 		mode = nt_list_all;
    294 		neednt = 0;
    295 		break;
    296 	case 'n':
    297 		mode = nt_list_n;
    298 		neednt = 0;
    299 		lsnum = atoi(EARGF(usage()));
    300 		break;
    301 	ARGNUM:
    302 		mode = nt_list_n;
    303 		neednt = 0;
    304 		lsnum = ARGNUMF();
    305 		break;
    306 	case 'c':
    307 		mode = nt_cat;
    308 		neednt = 0;
    309 		break;
    310 	case 'y':
    311 		yes = 1;
    312 		break;
    313 	case 'v':
    314 		printf("%s v%s (c) 2017 Ed van Bruggen\n", argv0, VERSION);
    315 		return 0;
    316 	default:
    317 		usage();
    318 	} ARGEND;
    319 
    320 	setup();
    321 
    322 	if (argc <= 0 && neednt)
    323 		fgets(sub, MAX_SUB, stdin);
    324 	else if (argc > 0) {
    325 		while (*argv) {
    326 			strcat(sub, *argv);
    327 			if (*(++argv))
    328 				strcat(sub, " ");
    329 		}
    330 	}
    331 	strtrim(sub); /* TODO replace by stopping newline from stdin */
    332 
    333 	mode();
    334 
    335 	cleanup();
    336 
    337 	return 0;
    338 }