commit 7d61b0086b50d196ea3f2dda7e2c38ec4ece78e8
Author: Ed van Bruggen <edvb54@gmail.com>
Date: Sat, 11 Mar 2017 11:50:38 -0800
Initial commit
Diffstat:
.gitignore | | | 21 | +++++++++++++++++++++ |
LICENSE | | | 18 | ++++++++++++++++++ |
Makefile | | | 66 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
README.md | | | 41 | +++++++++++++++++++++++++++++++++++++++++ |
arg.h | | | 63 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
config.def.h | | | 1 | + |
config.mk | | | 21 | +++++++++++++++++++++ |
nt.1 | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
nt.c | | | 247 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
util.c | | | 135 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
util.h | | | 16 | ++++++++++++++++ |
11 files changed, 667 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,21 @@
+# swap files
+*~
+*.swp
+*.swo
+\#*\#
+
+# compiled files
+nt
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.o
+*.obj
+*.so
+*.so.*
+
+# misc
+tags
+config.h
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,18 @@
+zlib License
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
diff --git a/Makefile b/Makefile
@@ -0,0 +1,66 @@
+# nt - create notes
+# See LICENSE file for copyright and license details.
+
+include config.mk
+
+EXE = nt
+SRC = $(wildcard *.c)
+OBJ = $(SRC:.c=.o)
+
+all: options $(EXE)
+
+options:
+ @echo $(EXE) build options:
+ @echo "CFLAGS = $(CFLAGS)"
+ @echo "LDFLAGS = $(LDFLAGS)"
+
+.o:
+ @echo $(LD) $@
+ @$(LD) -o $@ $< $(LDFLAGS)
+
+.c.o:
+ @echo $(CC) $<
+ @$(CC) -c -o $@ $< $(CFLAGS)
+
+${OBJ}: config.h config.mk
+
+config.h:
+ @echo creating $@ from config.def.h
+ @cp config.def.h $@
+
+$(EXE): $(OBJ)
+ @echo $(CC) -o $@
+ @$(CC) -o $@ $(OBJ) $(LDFLAGS)
+
+clean:
+ @echo -n cleaning ...
+ @rm -f $(OBJ) $(EXE)
+ @echo \ done
+
+install: all
+ @echo -n installing executable file to $(DESTDIR)$(PREFIX)/bin ...
+ @mkdir -p $(DESTDIR)$(PREFIX)/bin
+ @cp -f $(EXE) $(DESTDIR)$(PREFIX)/bin
+ @chmod 755 $(DESTDIR)$(PREFIX)/bin/$(EXE)
+ @echo \ done
+ @echo -n installing manual page to $(DESTDIR)$(MANPREFIX)/man1 ...
+ @mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ @sed "s/VERSION/$(VERSION)/g" < $(EXE).1 > $(DESTDIR)$(MANPREFIX)/man1/$(EXE).1
+ @chmod 644 $(DESTDIR)$(MANPREFIX)/man1/$(EXE).1
+ @echo \ done
+
+uninstall:
+ @echo -n removing executable file from $(DESTDIR)$(PREFIX)/bin ...
+ @rm -f $(DESTDIR)$(PREFIX)/bin/$(EXE)
+ @echo \ done
+ @echo -n removing manual page from $(DESTDIR)$(MANPREFIX)/man1 ...
+ @rm -f $(DESTDIR)$(MANPREFIX)/man1/$(EXE).1
+ @echo \ done
+
+update-man:
+ @echo -n updating man page $(EXE).1 ...
+ @cat README.md | sed "s/# $(EXE)/# $(EXE) 1\n\n##NAME\n\n$(EXE) /" | \
+ md2man-roff | sed "s/\\[la\]/\</" | sed "s/\\[ra\]/\>/" > $(EXE).1
+ @echo \ done
+
+.PHONY: all options clean install uninstall update-man
diff --git a/README.md b/README.md
@@ -0,0 +1,41 @@
+# nt - simple note taker
+
+## SYNOPSIS
+
+`nt` [-lv] [-f *FILE*] [-d *NOTE*] [-s *SEARCH*] [-n *NUM* | -*NUM*] [*NOTE* ...]
+
+## DESCRIPTION
+
+Simple note taker for the command line.
+
+## OPTIONS
+
+-v
+ Print version info and exit
+
+-f *FILE*
+ Load *FILE* instead of default one
+
+-l
+ List notes
+
+-n *NUM*
+ List last *NUM* notes
+
+-s *SEARCH*
+ Search for *SEARCH* in notes
+
+-d *NOTE*
+ Delete *NOTE* from notes
+
+## AUTHOR
+
+Ed van Bruggen <edvb54@gmail.com>
+
+## SEE ALSO
+
+View source code at: <https://gitlab.com/edvb/nt>
+
+## LICENSE
+
+zlib License
diff --git a/arg.h b/arg.h
@@ -0,0 +1,63 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][0] == '-'\
+ && argv[0][1];\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (brk_ = 0, argv[0]++, argv_ = argv;\
+ argv[0][0] && !brk_;\
+ argv[0]++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][0];\
+ switch (argc_)
+
+/* Handles -NUM syntax */
+#define ARGNUM case '0':\
+ case '1':\
+ case '2':\
+ case '3':\
+ case '4':\
+ case '5':\
+ case '6':\
+ case '7':\
+ case '8':\
+ case '9'
+
+#define ARGEND }\
+ }
+
+#define ARGC() argc_
+
+#define ARGNUMF() (brk_ = 1, atoi(argv[0]))
+
+#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ ((x), abort(), (char *)0) :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ (char *)0 :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#endif
diff --git a/config.def.h b/config.def.h
@@ -0,0 +1 @@
+char *fname = "todo";
diff --git a/config.mk b/config.mk
@@ -0,0 +1,21 @@
+# nt version number
+VERSION = 0.0.0
+
+### Change the variables below for your system
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/share/man
+
+# includes and libraries
+INCS = -Iinclude
+LIBS =
+
+# flags
+CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=600
+CFLAGS = -g -std=c99 -pedantic -Wall ${INCS} ${CPPFLAGS}
+LDFLAGS = -g ${LIBS}
+
+# compiler and linker
+CC = gcc
+
diff --git a/nt.1 b/nt.1
@@ -0,0 +1,38 @@
+.TH nt 1
+.SH NAME
+.PP
+nt \- simple note taker
+.SH SYNOPSIS
+.PP
+\fB\fCnt\fR [\-lv] [\-f \fIFILE\fP] [\-d \fINOTE\fP] [\-s \fISEARCH\fP] [\-n \fINUM\fP | \-\fINUM\fP] [\fINOTE\fP ...]
+.SH DESCRIPTION
+.PP
+Simple note taker for the command line.
+.SH OPTIONS
+.PP
+\-v
+ Print version info and exit
+.PP
+\-f \fIFILE\fP
+ Load \fIFILE\fP instead of default one
+.PP
+\-l
+ List notes
+.PP
+\-n \fINUM\fP
+ List last \fINUM\fP notes
+.PP
+\-s \fISEARCH\fP
+ Search for \fISEARCH\fP in notes
+.PP
+\-d \fINOTE\fP
+ Delete \fINOTE\fP from notes
+.SH AUTHOR
+.PP
+Ed van Bruggen \<edvb54@gmail.com\>
+.SH SEE ALSO
+.PP
+View source code at: \<https://gitlab.com/edvb/nt\>
+.SH LICENSE
+.PP
+zlib License
diff --git a/nt.c b/nt.c
@@ -0,0 +1,247 @@
+/* See LICENSE file for copyright and license details. */
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "arg.h"
+#include "util.h"
+
+/* macros */
+#define MAX_SUB 2048
+
+/* typedefs */
+
+/* functions */
+char *trimwhitespace(char *str);
+
+void nt_new(void);
+void nt_del(void);
+void nt_search(void);
+void nt_list_all(void);
+void nt_list_n(int n);
+
+void run(void);
+void cleanup(void);
+void usage(void);
+
+/* varibles */
+char *argv0;
+
+FILE *fp;
+char *sub;
+char **notes;
+int notec;
+
+char mode = 0;
+int lsnum;
+
+#include "config.h"
+
+/* remove tailing or leading whitespace from str */
+char *
+trimwhitespace(char *str)
+{
+ char *end;
+
+ /* trim leading space */
+ while(isspace((unsigned char)*str)) str++;
+
+ if(*str == 0) /* all spaces? */
+ return str;
+
+ /* trim trailing space */
+ end = str + strlen(str) - 1;
+ while(end > str && isspace((unsigned char)*end)) end--;
+
+ /* write new null terminator */
+ *(end+1) = 0;
+
+ return str;
+}
+
+/* create a new note */
+void
+nt_new(void)
+{
+ if (sub == NULL) usage();
+
+ fprintf(fp, "%s\n", sub);
+}
+
+/* delete oldest matching note from notes */
+void
+nt_del(void)
+{
+ if (sub == NULL) usage();
+
+ int i, found = 0;
+ FILE *tmpfp;
+ char *tmpfpext = "part";
+ char *tmpfpname = ecalloc(strlen(fname)+strlen(tmpfpext)+1, sizeof(char));
+
+ strcpy(tmpfpname, fname);
+ strcat(tmpfpname, ".");
+ strcat(tmpfpname, tmpfpext);
+ tmpfp = fopen(tmpfpname, "w");
+
+ for (i = 0; i < notec; i++)
+ if (strcmp(notes[i], sub) != 0 || found)
+ fprintf(tmpfp, "%s\n", notes[i]);
+ else
+ found = 1;
+ if (!found)
+ die("%s: delete: '%s' not found", argv0, sub);
+
+ fclose(tmpfp);
+ remove(fname);
+ rename(tmpfpname, fname);
+}
+
+/* search notes for given note */
+void
+nt_search()
+{
+ if (sub == NULL) usage();
+
+ int i, found = 0;
+ for (i = 0; i < notec; i++)
+ if (strstr(notes[i], sub)) {
+ printf("%s\n", notes[i]);
+ found = 1;
+ }
+ if (!found)
+ die("%s: search: '%s' not found", argv0, sub);
+}
+
+/* print out entire file */
+void
+nt_list_all(void)
+{
+ int i;
+ for (i = 0; i < notec; i++)
+ printf("%s\n", notes[i]);
+}
+
+/* diplay n most recent subjects in file */
+void
+nt_list_n(int n)
+{
+ int i = notec;
+ n = n > i ? -1 : i-n-1;
+ for (i--; i > n; i--)
+ printf("%s\n", notes[i]);
+}
+
+/* handle options and create notes */
+void
+run(void)
+{
+ switch (mode) {
+ case 'd':
+ nt_del();
+ break;
+ case 'l':
+ nt_list_all();
+ exit(0);
+ case 'n':
+ nt_list_n(lsnum);
+ exit(0);
+ case 's':
+ nt_search();
+ break;
+ default:
+ nt_new();
+ }
+}
+
+/* load fname, set notec, populate notes, allocate sub */
+void
+setup(void)
+{
+ int c, i = 0, last = 0;
+
+ /* open file */
+ fp = fopen(fname, "ab+");
+
+ /* get number of notec */
+ while ((c = fgetc(fp)) != EOF) {
+ if (c == '\n' && last != '\n')
+ ++notec;
+ last = c;
+ }
+ rewind(fp);
+
+ /* copy file fp to notes */
+ notes = ecalloc(notec+1, sizeof(char*));
+ do
+ notes[i] = ecalloc(MAX_SUB, sizeof(char));
+ while (fscanf(fp, "%2048[^\n]\n", notes[i++]) != EOF);
+ rewind(fp);
+
+ sub = ecalloc(MAX_SUB, sizeof(char));
+}
+
+/* close fname, free memory */
+void
+cleanup(void)
+{
+ fclose(fp);
+ for (int j = 0; j < notec+1; j++)
+ free(notes[j]);
+ free(notes);
+ free(sub);
+}
+
+void
+usage(void)
+{
+ die("usage: %s [-lv] [-f FILE] [-d NOTE] [-s SEARCH] [-n NUM | -NUM] [NOTE ...]", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ ARGBEGIN {
+ case 'f':
+ fname = EARGF(usage());
+ break;
+ case 'd':
+ mode = 'd';
+ break;
+ case 'l':
+ mode = 'l';
+ break;
+ case 'n':
+ mode = 'n';
+ lsnum = atoi(EARGF(usage()));
+ break;
+ ARGNUM:
+ mode = 'n';
+ lsnum = ARGNUMF();
+ break;
+ case 's':
+ mode = 's';
+ break;
+ case 'v':
+ printf("%s v%s\n", argv0, VERSION);
+ return 0;
+ default:
+ usage();
+ } ARGEND;
+
+ setup();
+
+ if (argc <= 0 && (!mode || mode == 'd' || mode == 's'))
+ fgets(sub, MAX_SUB, stdin);
+ else if (argc > 0)
+ sub = strconcat(argv, argc);
+ trimwhitespace(sub);
+
+ run();
+
+ cleanup();
+
+ return 0;
+
+}
diff --git a/util.c b/util.c
@@ -0,0 +1,135 @@
+/* See LICENSE file for copyright and license details. */
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!(p = calloc(nmemb, size)))
+ die("calloc: out of memory");
+
+ return p;
+}
+
+void *
+emalloc(size_t size)
+{
+ void *p;
+
+ if (!(p = malloc(size)))
+ die("malloc: out of memory");
+
+ return p;
+}
+
+void *
+erealloc(void *p, size_t size)
+{
+ if (!(p = realloc(p, size)))
+ die("realloc: out of memory");
+
+ return p;
+}
+
+char *
+estrdup(char *s)
+{
+ if ((s = strdup(s)) == NULL)
+ die("strdup: out of memory");
+
+ return s;
+}
+
+char *
+strconcat(char **s, int c)
+{
+ if (s == NULL) die("strconcat: given null pointer");
+
+ int len, i;
+ char *ret;
+
+ for (i = 0; i < c; i++)
+ len += strlen(s[i]) + 1;
+ ret = ecalloc(len, sizeof(char));
+
+ strcpy(ret, s[0]);
+ strcat(ret, " ");
+ for (i = 1; i < c; i++) {
+ strcat(ret, s[i]);
+ strcat(ret, " ");
+ }
+
+ return ret;
+}
+
+/* split string s into muiltple strings by a_delim */
+char **
+strsplit(const char *s, const char a_delim, int *size)
+{
+ if (s == NULL) die("strsplit: given null pointer");
+
+ char **ret = 0;
+ size_t count = 0;
+ char *last_delim = 0;
+ char delim[2] = { a_delim, 0 }; /* converet a_delim into string for strtok */
+ char *a_str = ecalloc(strlen(s)+1, sizeof(char));
+ strcpy(a_str, s);
+
+ /* count number of elements that will be extracted. */
+ for (char *tmp = a_str; *tmp; tmp++)
+ if (a_delim == *tmp) {
+ count++;
+ last_delim = tmp;
+ }
+
+ /* add space for trailing token. */
+ count += last_delim < (a_str + strlen(a_str) - 1);
+
+ /* add space for terminating null string so caller
+ * knows where the list of returned strings ends. */
+ count++;
+
+ ret = ecalloc(count, sizeof(char*));
+
+ if (ret) {
+ size_t idx = 0;
+ char *token = strtok(a_str, delim);
+
+ while (token) {
+ assert(idx < count);
+ *(ret + idx++) = estrdup(token);
+ token = strtok(0, delim);
+ }
+ assert(idx == count - 1);
+ *(ret + idx) = 0;
+ }
+
+ free(a_str);
+ *size = count - 1;
+ return ret;
+}
+
+void
+die(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,16 @@
+/* See LICENSE file for copyright and license details. */
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
+#define LEN(X) (sizeof(X) / sizeof((X)[0]))
+
+void *ecalloc(size_t nmemb, size_t size);
+void *emalloc(size_t size);
+void *erealloc(void *p, size_t size);
+char *estrdup(char *s);
+
+char *strconcat(char **s, int c);
+char **strsplit(const char *s, const char a_delim, int *size);
+
+void die(const char *errstr, ...);