dag

Djmoch's Auto Generator
git clone git://git.danielmoch.com/dag.git
Log | Files | Refs | README | LICENSE

commit 9475ac0f6b795ba8a0d118c80467b20b3535395c
Author: Daniel Moch <daniel@danielmoch.com>
Date:   Tue,  1 Sep 2020 17:01:32 -0400

Initial commit

Traversing a single source directory and enumerating the output file
paths works.

Diffstat:
A.gitignore | 2++
ALICENSE | 17+++++++++++++++++
AMakefile | 41+++++++++++++++++++++++++++++++++++++++++
AREADME | 16++++++++++++++++
Aconfig.mk | 26++++++++++++++++++++++++++
Adag.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astring.c | 37+++++++++++++++++++++++++++++++++++++
Astring.h | 4++++
Astring_test.c | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 396 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +dag +*_test diff --git a/LICENSE b/LICENSE @@ -0,0 +1,17 @@ +ISC License (ISC) + +Copyright 2020 Daniel Moch + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,41 @@ +# See LICENSE file for copyright and license details +.POSIX: + +include config.mk + +all: dag + +.c.o: + ${CC} ${CFLAGS} ${CPPFLAGS} -o $@ $< + +dag: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +string_test: string_test.o string.o + ${CC} -o $@ string_test.o string.o ${LDFLAGS} + +install: dag + install -Dm755 dag ${DESTDIR}${PREFIX}/bin/dag + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dag + +clean: + rm -f *.o dag *_test + +test: string_test + ./string_test + +dist: + rm -rf dag-${VERSION} + mkdir dag-${VERSION} + cp ${DIST_SRC} dag-${VERSION} + tar -cf dag-${VERSION} | gzip > dag-${VERSION}.tar.gz + +distclean: + rm -rf dag-${VERSION} + rm -f dag-${VERSION}.tar.gz + +clean-all: clean distclean + +.PHONY: all clean install uninstall dist distclean clean-all diff --git a/README b/README @@ -0,0 +1,16 @@ +DAG - Djmoch's Automatic Generator +================================== + +The goal of dag(1) is to create a static site generator that's as +Unixy as possible without feeling hacked together. After compilation, +configuration happens entirely via the command line, and the intent +is for the building to be orchestrated with a tool like make(1). +The utility also outsources hard things to other tools, focusing +on tools available in any POSIX operating system. + +- lowdown(1) is used to convert Markdown into HTML and roff_ms + +- groff(1) is used to convert roff_ms into PDF + +- m4(1) is used to process page headers, footers, and any other + data built from a template diff --git a/config.mk b/config.mk @@ -0,0 +1,26 @@ +# See LICENSE file for copyright and license details + +# Paths to helper tools +GROFF = \"/usr/local/bin/groff\" +LOWDOWN = \"/usr/local/bin/lowdown\" +M4 = \"/usr/bin/m4\" + +# Number of DEFINES to allow from the command line +MAX_DEFINES = 100 + +PREFIX := /usr/local +MANPATH := ${PREFIX}/share/man +X11BASE := /usr/X11R6 + +SRC = dag.c string.c +DIST_SRC = ${SRC} Makefile README config.mk +OBJ = ${SRC:.c=.o} +INCS = +LIBS = +VERSION = 0.1.0dev0 + +CPPFLAGS := -DVERSION=\"${VERSION}\" -DGROFF=${GROFF} \ + -DLOWDOWN=${LOWDOWN} -DM4=${M4} -DMAX_DEFINES=${MAX_DEFINES} +CFLAGS := -std=c99 -pedantic-errors -Wall -Wextra -Werror -O1 -c ${INCS} -pipe +#CFLAGS := -std=c99 -pedantic-errors -Wall -Wextra -Werror -O0 -g -c ${INCS} -pipe +LDFLAGS := ${LIBS} diff --git a/dag.c b/dag.c @@ -0,0 +1,149 @@ +/* See LICENSE file for copyright and license details */ +#include <errno.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fts.h> +#include <unistd.h> + +#include "string.h" + +static char *argv0; + +enum { + ERR_NONE, + ERR_UNKNOWN_OPTION, + ERR_ARGS, + ERR_FTS +}; + +void +usage(int e_val) { + FILE *fp; + + if (e_val) { + fp = stderr; + } + else { + fp = stdout; + } + + fprintf(fp, "usage: %s [-hv] [-Dname=value ...] [-H header] [-f footer] in ... out\n", argv0); + exit(e_val); +} + +char * +make_m4_cmd(const char **defines, const int num_defines, const char *infile, const char *outfile) { + char *retval = malloc(strlen(M4) + 1); + sprintf(retval, M4); + + for (int i=0; i<num_defines; i++) { + /* "-D" + defines[i] + " " + '\0' */ + int len = strlen(defines[i]) + 4; + + retval = realloc(retval, strlen(retval) + len); + sprintf(retval, "%s -D%s", retval, defines[i]); + } + + retval = realloc(retval, strlen(retval) + strlen(infile) + strlen(" > ") + + strlen(outfile)); + sprintf(retval, "%s %s > %s", retval, infile, outfile); + + return retval; +} + +char * +make_outpath(const char *out, const char *srcpath, char *src_argv[]) { + for (int i=0; src_argv[i]!=NULL; i++) { + if (strbegin(srcpath, src_argv[i])) { + char *outpath = NULL; + size_t plen = 0; + if (strlen(src_argv[i]) >= strlen(out)) + plen = strlen(srcpath); + else + plen = strlen(srcpath) + strlen(out) - strlen(src_argv[i]); + outpath = malloc(plen * sizeof(char)); + strcpy(outpath, srcpath); + strnswp(outpath, src_argv[i], out, plen); + return outpath; + } + } + + return NULL; +} + +int +main(int argc, char **argv) { + char ch, *header = NULL, *footer = NULL, *defines[MAX_DEFINES]; + char *out; + int num_defines = 0; + argv0 = basename(argv[0]); + FTSENT *entry = NULL; + + while ((ch = getopt(argc, argv, "D:H:f:vh")) != -1) { + switch (ch) { + case 'D': + defines[num_defines] = optarg; + num_defines += 1; + break; + case 'f': + footer = optarg; + break; + case 'H': + header = optarg; + break; + case 'h': + usage(ERR_NONE); + break; + case 'v': + printf("%s %s\n", argv0, VERSION); + exit(ERR_NONE); + break; + default: + usage(ERR_UNKNOWN_OPTION); + } + } + argc -= optind; + argv += optind; + + if (argc < 2) { + fprintf(stderr, "%s: missing input and/or output directory\n", argv0); + usage(ERR_ARGS); + } + + char *path_argv[argc]; + for (int i=0; i<argc - 1; i++) { + path_argv[i] = argv[i]; + } + path_argv[argc - 1] = NULL; + out = argv[argc - 1]; + + FTS *src_tree = fts_open(path_argv, FTS_LOGICAL, NULL); + if (errno != 0) { + fprintf(stderr, "%s: call to fts_open() failed -- %d\n", + argv0, errno); + } + + while ((entry = fts_read(src_tree)) != NULL) { + if ((entry->fts_info & FTS_F) == FTS_F) { + char *outpath = make_outpath(out, entry->fts_accpath, path_argv); + printf("%s: found file -- %s\n", + argv0, entry->fts_accpath); + printf("%s: creating file -- %s/%s\n", argv0, out, entry->fts_accpath); + free(outpath); + } + } + + if (errno != 0) { + fprintf(stderr, "%s: error occurred walking file tree -- %d\n", + argv0, errno); + exit(ERR_FTS); + } + + /* find .md files */ + /* process if newer than corresponding html file in output */ + /* also need to process if any template file is newer */ +} diff --git a/string.c b/string.c @@ -0,0 +1,37 @@ +/* See LICENSE file for copyright and license details + * + * NOTE: All functions in this file assume strings are NUL-terminated + * unless otherwise specified + */ +#include <string.h> + +int +strbegin(const char *big, const char *little) { + return (strstr(big, little) == big); +} + +void +strnswp(char *big, const char *old, const char *new, size_t max) { + size_t biglen = strlen(big); + size_t oldlen = strlen(old); + size_t newlen = strlen(new); + char *begin = strstr(big, old); + + if ((biglen + newlen - oldlen > max) || + (begin == NULL)) { + return; + } + + if (newlen > oldlen) { + for(char* i=&(big[biglen + 1]); i>begin; i--) { + *(i + (newlen - oldlen)) = *i; + } + } + else if (oldlen > newlen) { + for(char* i=begin; i<&(big[biglen + 1]); i++) { + *i = *(i + (oldlen - newlen)); + } + } + + memcpy(begin, new, newlen); +} diff --git a/string.h b/string.h @@ -0,0 +1,4 @@ +/* See LICENSE file for copyright and license details */ + +int strbegin(const char *big, const char *little); +char *strnswp(char *big, const char *old, const char *new, size_t max); diff --git a/string_test.c b/string_test.c @@ -0,0 +1,104 @@ +/* See LICENSE file for copyright and license details */ +#include <stdio.h> +#include <string.h> +#include "string.h" + +int +test_strnswp1() { + char big[] = "thisandthat"; + char old[] = "and"; + char new[] = "foo"; + char exp[] = "thisfoothat"; + strnswp(big, old, new, strlen(big)); + if (strcmp(exp, big) != 0) { + printf("test_strnswp1 failed:\n\texp: %s\n\tact: %s\n", exp, big); + return 1; + } + + return 0; +} + +int +test_strnswp2() { + char big[12] = "thisandthat"; + char old[] = "and"; + char new[] = "foot"; + char exp[] = "thisfootthat"; + strnswp(big, old, new, 12); + if (strcmp(exp, big) != 0) { + printf("test_strnswp2 failed:\n\texp: %s\n\tact: %s\n", exp, big); + return 1; + } + + return 0; +} + +int +test_strnswp3() { + char big[] = "thisandthat"; + char old[] = "and"; + char new[] = "fo"; + char exp[] = "thisfothat"; + strnswp(big, old, new, strlen(big)); + if (strcmp(exp, big) != 0) { + printf("test_strnswp3 failed:\n\texp: %s\n\tact: %s\n", exp, big); + return 1; + } + + return 0; +} + +int +test_strnswp4() { + char big[] = "thisandthat"; + char old[] = "and"; + char new[] = ""; + char exp[] = "thisthat"; + strnswp(big, old, new, strlen(big)); + if (strcmp(exp, big) != 0) { + printf("test_strnswp4 failed:\n\texp: %s\n\tact: %s\n", exp, big); + return 1; + } + + return 0; +} + +int +test_strnswp5() { + char big[] = "thisandthat"; + char old[] = "and"; + char new[] = "foot"; + char exp[] = "thisandthat"; + strnswp(big, old, new, strlen(big)); + if (strcmp(exp, big) != 0) { + printf("test_strnswp5 failed:\n\texp: %s\n\tact: %s\n", exp, big); + return 1; + } + + return 0; +} + +int +test_strnswp6() { + char big[] = "thisandthat"; + char old[] = "foot"; + char new[] = "bat"; + char exp[] = "thisandthat"; + strnswp(big, old, new, strlen(big)); + if (strcmp(exp, big) != 0) { + printf("test_strnswp6 failed:\n\texp: %s\n\tact: %s\n", exp, big); + return 1; + } + + return 0; +} + +int main() { + int retval = test_strnswp1(); + retval |= test_strnswp2(); + retval |= test_strnswp3(); + retval |= test_strnswp4(); + retval |= test_strnswp5(); + retval |= test_strnswp6(); + return retval; +}