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 }