dag

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

commit d8d5df2fdebc1a5554b0625ac81d6594f6382f0e
parent 9495d3cbab984d8ba392ebf6a18faa3ea49afd6e
Author: Daniel Moch <daniel@danielmoch.com>
Date:   Sat, 23 Jan 2021 15:24:28 -0500

Refactor to use Dagfile

Diffstat:
M.gitignore | 3+++
MMakefile | 25++++++++++++++++++++-----
Mconfig.mk | 15+++++++++------
Mdag.c | 88++++++++++++++++++++++++++++++-------------------------------------------------
Mdagfile.c | 336+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mdagfile.h | 40+++++++++++++++++++++++++++++++++++++++-
Aparse.l | 45+++++++++++++++++++++++++++++++++++++++++++++
Aparse.y | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mstring.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mstring.h | 4++++
Mstring_test.c | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 759 insertions(+), 219 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,2 +1,5 @@ dag *_test +dagindex +y.tab.* +lex.yy.* diff --git a/Makefile b/Makefile @@ -3,13 +3,28 @@ include config.mk -all: dag +all: dag dagindex -.c.o: +y.tab.o: y.tab.c y.tab.h + ${CC} -Wno-implicit-function-declaration -c -o y.tab.o y.tab.c + +lex.yy.o: lex.yy.c y.tab.h + ${CC} -c -o lex.yy.o lex.yy.c + +.c.o: ${HDRS} ${CC} ${CFLAGS} ${CPPFLAGS} -o $@ $< -dag: ${OBJ} - ${CC} -o $@ ${OBJ} ${LDFLAGS} +y.tab.h y.tab.c: parse.y + yacc -d parse.y + +lex.yy.c: parse.l + lex parse.l + +dag: ${DOBJ} + ${CC} -o $@ ${DOBJ} ${LDFLAGS} + +dagindex: ${IOBJ} + ${CC} -o $@ ${IOBJ} ${LDFLAGS} string_test: string_test.o string.o ${CC} -o $@ string_test.o string.o ${LDFLAGS} @@ -21,7 +36,7 @@ uninstall: rm -f ${DESTDIR}${PREFIX}/bin/dag clean: - rm -f *.o dag *_test + rm -f *.o dag *_test y.tab.* lex.yy.c test: string_test ./string_test diff --git a/config.mk b/config.mk @@ -3,18 +3,21 @@ LOWDOWN := /usr/local/bin/lowdown SASSC := /usr/local/bin/sassc M4 := /usr/bin/m4 +TBL := /usr/local/bin/tbl PREFIX := /usr/local MANPATH := ${PREFIX}/share/man X11BASE := /usr/X11R6 -SRC = dag.c dagfile.c string.c +HDRS = dagfile.h string.h y.tab.h +DSRC = dag.c dagfile.c string.c y.tab.c lex.yy.c +ISRC = dagindex.c string.c DIST_SRC = ${SRC} Makefile README config.mk -OBJ = ${SRC:.c=.o} -INCS = -LIBS = +DOBJ = ${DSRC:.c=.o} +IOBJ = ${ISRC:.c=.o} +LIBS = -ll -ly VERSION = 0.1.0dev0 -CPPFLAGS := -DVERSION=\"${VERSION}\" -DLOWDOWN=\"${LOWDOWN}\" -DSASSC=\"${SASSC}\" -DM4=\"${M4}\" -CFLAGS := -std=c99 -pedantic-errors -Wall -Wextra -Werror -O1 -c ${INCS} -pipe +CPPFLAGS := -DVERSION=\"${VERSION}\" +CFLAGS := -std=c99 -pedantic-errors -Wall -Wextra -Werror -O0 -g -c -pipe LDFLAGS := ${LIBS} diff --git a/dag.c b/dag.c @@ -1,26 +1,29 @@ /* 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/stat.h> -#include <fts.h> #include <unistd.h> #include "dagfile.h" #include "string.h" +int parse_dagfile(struct dagfile *dagfile, FILE *path); +void debug_dagfile(struct dagfile *df); + static char *argv0; enum { ERR_NONE, + ERR_DAGFILE, ERR_UNKNOWN_OPTION, ERR_ARGS, - ERR_FTS + ERR_FILE, + ERR_PARSE }; -void +static void usage(int rv) { FILE *fp; @@ -32,44 +35,31 @@ usage(int rv) fp = stdout; } - fprintf(fp, "usage: %s [-hv] in ... out\n", argv0); + fprintf(fp, "usage: %s [-Vhv] [-f file]\n", argv0); exit(rv); } -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, *out; + char ch, *path = "Dagfile"; + int verbose = 0; + FILE *df; argv0 = basename(argv[0]); - FTSENT *entry = NULL; + struct dagfile dagfile = { NULL }; - while ((ch = getopt(argc, argv, "vh")) != -1) { + while ((ch = getopt(argc, argv, "f:hvV")) != -1) { switch (ch) { + case 'f': + path = optarg; + break; case 'h': usage(ERR_NONE); break; case 'v': + verbose = 1; + break; + case 'V': printf("%s %s\n", argv0, VERSION); exit(ERR_NONE); break; @@ -80,36 +70,24 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (argc < 2) { - fprintf(stderr, "%s: missing input and/or output directory\n", argv0); + if (argc > 0) { + fprintf(stderr, "%s: expected no arguments\n", argv0); usage(ERR_ARGS); } - - char *path_argv[argc]; - for (int i=0; i<argc - 1; i++) { - path_argv[i] = argv[i]; + if ((df = fopen(path, "r")) == NULL) { + fprintf(stderr, "%s: error opening %s\n", argv0, path); + exit(ERR_FILE); } - path_argv[argc - 1] = NULL; - dedup(path_argv); - 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); + if (parse_dagfile(&dagfile, df) != 0) { + fprintf(stderr, "%s: error parsing %s\n", argv0, path); + exit(ERR_PARSE); } - - 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); - process_dagfile(entry->fts_accpath, outpath); - free(outpath); - } + fclose(df); + if (verbose) { + debug_dagfile(&dagfile); } - if (errno != 0) { - fprintf(stderr, "%s: error occurred walking file tree -- %d\n", - argv0, errno); - exit(ERR_FTS); - } + /* TODO - pledge and unveil */ + + return process_dagfile(&dagfile); } diff --git a/dagfile.c b/dagfile.c @@ -1,137 +1,171 @@ /* See LICENSE file for copyright and license details */ #include <libgen.h> #include <stdarg.h> +#include <err.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> +#include <fts.h> +#include "dagfile.h" #include "string.h" -#define HEADER "templates/header.html" -#define FOOTER "templates/footer.html" - -enum { - MD = 0, - SCSS, - HTML, - CSS, - PDF, - OTHER -}; - -char *ext[] = { ".md", ".scss", "/index.html", ".css", ".pdf" }; - -char *xfrm[] = { - NULL, - NULL, - "%s -Thtml %s >>%s", - "%s -t compressed %s %s", - "%s -Tms %s | /usr/local/bin/groff -ms -t -Tpdf >%s", - "cp %s %s" -}; +static void process_source(char *file); +static void process_extension(char *file); +static void process_suffix(char *file); +static void copy_file(char *file); +static char *make_outpath(const char *file); +static int outdated(char *dest, int nsrc, ...); +static void dag_mkdir(const char *out); +static char *fmt_filter(char *f_cmd, char *file, char *target); +static char *expand(char *cmd, char pat, char *subst); + +static struct target *tgt; +static struct source *src; +static struct extension *ext; +static struct suffix *sfx; -static int -getft(const char *infile) +int +process_dagfile(struct dagfile *df) { - int ft = OTHER; - - for (int i=0; i<OTHER; i++) { - if (strend(infile, ext[i])) { - ft = i; - break; + FTSENT *entry = NULL; + FTS *src_tree; + tgt = df->target; + src = df ->target->sources; + char *path[] = { src->path, NULL }; + ext = NULL; + sfx = NULL; + + while (src != NULL && path[0] != NULL) { + src_tree = fts_open(path, FTS_LOGICAL, NULL); + if (src_tree == NULL) { + err(1, "call to fts_open() failed -- %d\n", errno); } - } - return ft; -} + while ((entry = fts_read(src_tree)) != NULL) { + if ((entry->fts_info & FTS_F) == FTS_F) { + process_source(entry->fts_accpath); + } + } -static char* -getout(const char *orig, const int from, const int to) -{ - int plen = strlen(orig) + strlen(ext[to]) - strlen(ext[from]); - char *out = malloc(plen * sizeof(char)); - strcpy(out, orig); - strnswp(out, ext[from], ext[to], plen); + if (errno != 0) { + err(1, "error occurred walking file tree -- %d\n", errno); + } - return out; + fts_close(src_tree); + src = src->next; + if (src != NULL) { + path[0] = src->path; + } + } + return 0; } -static char* -getcmd(int ft, const char *in, const char *out) +static void +process_source(char *file) { - char *ret, *cmd = xfrm[ft]; - int clen; - - switch (ft) { - case CSS: - clen = strlen(cmd) + strlen(in) + strlen(out) + strlen(SASSC) - 6; - break; - default: - clen = strlen(cmd) + strlen(in) + strlen(out) + strlen(LOWDOWN) - 6; - } + int found = 0; + ext = src->extensions; - ret = malloc(clen * sizeof(char)); - - switch (ft) { - case CSS: - sprintf(ret, cmd, SASSC, in, out); - break; - case HTML: - case PDF: - sprintf(ret, cmd, LOWDOWN, in, out); - break; - default: - sprintf(ret, cmd, in, out); + while (ext != NULL) { + if (strend(file, ext->value)) { + process_extension(file); + found = 1; + } + ext = ext->next; } - return ret; + if (!found) { + /* no extension matches, so just copy */ + copy_file(file); + } } -static char* -gethdrcmd(const char * in, const char *out) +static void +process_extension(char *file) { - char *ret, hdrpath[] = HEADER; - const char *slug = out + strlen("target"); /* TODO - don't assume target */ - int clen; - char cmd[] = "%s -DDESCRIPTION=\"$(%s -Xdescription -Tterm %s)\" -DPAGE_TITLE=\"$(%s -Xtitle -Tterm %s)\" -DSLUG=%s %s >%s"; - clen = strlen(cmd) + strlen(M4) + (2 * strlen(LOWDOWN)) + (2 * strlen(in)) + - (2 * strlen(out)) + strlen(hdrpath) - 16; - ret = malloc(clen); - sprintf(ret, cmd, M4, LOWDOWN, in, LOWDOWN, in, slug, hdrpath, out); - return ret; + sfx = ext->suffixes; + + while (sfx != NULL) { + process_suffix(file); + sfx = sfx->next; + } } -static char* -getftrcmd(const char *out) +static void +process_suffix(char *file) { - char *ret, ftrpath[] = FOOTER; - int clen; - char cmd[] = "cat <%s >>%s"; - clen = strlen(cmd) + strlen(ftrpath) + strlen(out) - 6; - ret = malloc(clen); - sprintf(ret, cmd, ftrpath, out); - return ret; + char *target = make_outpath(file); + struct requirement *r = sfx->requirements; + + int old = outdated(target, 1, file); + while (r != NULL) { + old |= outdated(target, 1, r->path); + r = r->next; + } + + if (old) { + struct filter *f = sfx->filters; + while (f != NULL) { + char *cmd = fmt_filter(f->cmd, file, target); + printf("%s\n", cmd); + system(cmd); + free(cmd); + f = f->next; + } + } + free(target); } -void -dag_mkdir(const char *out) +static void +copy_file(char *file) { - struct stat sb; - char fmt[] = "mkdir -p %s"; - int clen = strlen(fmt) + strlen(out) - 1; - char cmd[clen]; - /* TODO: handle case where out exists, but isn't a directory */ - if (stat(out, &sb) != 0 || !S_ISDIR(sb.st_mode)) { - sprintf(cmd, fmt, out); + char *target = make_outpath(file); + + if (outdated(target, 1, file)) { + char fmt[] = "cp %s %s"; + char *cmd = malloc((strlen(file) + strlen(target) + strlen(fmt) + 1) * sizeof(char)); + sprintf(cmd, fmt, file, target); printf("%s\n", cmd); system(cmd); + free(cmd); } + + free(target); } -int -outdated(char *dest, int nsrc, ...) { +static char * +make_outpath(const char *file) +{ + int len = strlen(file) + strlen(tgt->path) + 1; + char *target = malloc(len * sizeof(char)); + + strcpy(target, file); + strnswp(target, src->path, tgt->path, len); + + /* + * TODO: find a better way to determine if the suffix needs + * to be changed + */ + if (sfx) { + len = strlen(target) + strlen(sfx->value) + 1; + char *tmp = malloc(len * sizeof(char)); + strcpy(tmp, target); + strnswp(tmp, ext->value, sfx->value, len); + free(target); + target = tmp; + } + + dag_mkdir(dirname(target)); + return target; +} + +static int +outdated(char *dest, int nsrc, ...) +{ va_list ap; struct stat d_sb, s_sb; int rv = 1; @@ -158,64 +192,62 @@ end: return rv; } -int -process_dagfile(const char *infile, const char *outfile) +static void +dag_mkdir(const char *out) { - char *out = NULL; - char *cmd = NULL; - - switch (getft(infile)) { - case MD: - out = getout(outfile, MD, HTML); - if (outdated(out, 3, HEADER, FOOTER, infile)) { - dag_mkdir(dirname(out)); - cmd = gethdrcmd(infile, out); - printf("%s\n", cmd); - system(cmd); - free(cmd); - cmd = getcmd(HTML, infile, out); - printf("%s\n", cmd); - system(cmd); - free(cmd); - cmd = getftrcmd(out); - printf("%s\n", cmd); - system(cmd); - free(out); - free(cmd); - } - - out = getout(outfile, MD, PDF); - if (outdated(out, 3, HEADER, FOOTER, infile)) { - dag_mkdir(dirname(out)); - cmd = getcmd(PDF, infile, out); - printf("%s\n", cmd); - system(cmd); - } - break; - case SCSS: - out = getout(outfile, SCSS, CSS); - if (outdated(out, 1, infile)) { - dag_mkdir(dirname(out)); - cmd = getcmd(CSS, infile, out); - printf("%s\n", cmd); - system(cmd); - } - break; - default: - out = malloc(strlen(outfile) * sizeof(char)); - strcpy(out, outfile); - if (outdated(out, 1, infile)) { - dag_mkdir(dirname(out)); - cmd = getcmd(OTHER, infile, outfile); - printf("%s\n", cmd); - system(cmd); - } + struct stat sb; + char fmt[] = "mkdir -p %s"; + int clen = strlen(fmt) + strlen(out) + 1; + char cmd[clen]; + /* TODO: handle case where out exists, but isn't a directory */ + if (stat(out, &sb) != 0 || !S_ISDIR(sb.st_mode)) { + sprintf(cmd, fmt, out); + printf("%s\n", cmd); + system(cmd); } +} - free(out); - if (cmd != NULL) { - free(cmd); +static char * +fmt_filter(char *f_cmd, char *file, char *target) +{ + struct requirement *r = sfx->requirements; + char i = '1'; + char *cmd = strdup(f_cmd); + + while (r != NULL) { + if (i > '9') + err(1, "too many requirements: %s\n", f_cmd); + + cmd = expand(cmd, i, r->path); + r = r->next; + i += 1; } + cmd = expand(cmd, '<', file); + cmd = expand(cmd, '>', target); + return cmd; +} - return 0; +static char * +expand(char *cmd, char pat, char *subst) +{ + int ct = 0; + int len = strlen(cmd) + strcnt(cmd, '%') + 1; + cmd = realloc(cmd, len); + strnesc(cmd, len); + for (int j=0;;j++) { + if (cmd[j] == '\0') + break; + if (ct >= 10) + err(1, "too many substitutions\n"); + + if (cmd[j] == '$' && cmd[j+1] == pat) { + ct += 1; + cmd[j] = '%'; + cmd[j+1] = 's'; + } + } + char *tmp = malloc((strlen(cmd) + (ct * strlen(subst)) + 1) * sizeof(char)); + sprintf_ct(tmp, cmd, subst, ct); + free(cmd); + return tmp; } diff --git a/dagfile.h b/dagfile.h @@ -3,6 +3,44 @@ #ifndef __DAGFILE_H #define __DAGFILE_H -int process_dagfile(char *infile, char *outfile); +struct requirement { + char *path; + struct requirement *next; +}; + +struct filter { + char *cmd; + struct filter *next; +}; + +struct suffix { + char *value; + struct requirement *requirements; + struct filter *filters; + struct suffix *next; +}; + +struct extension { + char *value; + struct suffix *suffixes; + struct extension *next; +}; + +struct source { + char *path; + struct extension *extensions; + struct source *next; +}; + +struct target { + char *path; + struct source *sources; +}; + +struct dagfile { + struct target *target; +}; + +int process_dagfile(struct dagfile *df); #endif diff --git a/parse.l b/parse.l @@ -0,0 +1,45 @@ +%{ +/* See LICENSE file for copyright and license details */ +#include "y.tab.h" +%} + +%% + +target { + return TARGET; +} + +source { + return SOURCE; +} + +extension { + return EXTENSION; +} + +suffix { + return SUFFIX; +} + +require { + return REQUIRE; +} + +filter { + return FILTER; +} + +none { + return NONE; +} + +[{}] return yytext[0]; + +\"[^"\n]*["\n] | +'[^'\n]*['\n] | +[^ \t\n]+ { + yylval.str = yytext; + return STRING; +} + +[ \t\n]+ diff --git a/parse.y b/parse.y @@ -0,0 +1,242 @@ +%{ +/* See LICENSE file for copyright and license details */ +#include <stdio.h> +#include <string.h> +#include "dagfile.h" +#include "string.h" + +extern FILE *yyin; + +static struct dagfile *dagfile; +static struct target *t; +static struct source *so; +static struct extension *e; +static struct suffix *s; +%} + +%union { + char *str; +} + +%token <str> TARGET SOURCE EXTENSION REQUIRE FILTER SUFFIX NONE STRING + +%% + +targets: target targets + | target + ; + +target: target_name "{" sources "}" + ; + +target_name: TARGET STRING { push_target($2); } + ; + +sources: source sources + | source + ; + +source: source_name "{" extensions "}" + | source_name + ; + +source_name: SOURCE STRING { push_source($2); } + ; + +extensions: extension extensions + | extension + ; + +extension: extension_name "{" suffixes "}" + ; + +extension_name: EXTENSION STRING { push_extension($2); } + ; + +suffixes: suffix suffixes + | suffix + ; + +suffix: suffix_name "{" requirements filters "}" + | suffix_name "{" filters "}" + ; + +suffix_name: SUFFIX STRING { push_suffix($2); } + ; + +requirements: requirement requirements + | requirement + ; + +requirement: REQUIRE STRING { push_requirement($2); } + ; + +filters: filter filters + | filter + ; + +filter: FILTER STRING { push_filter($2); } + ; + +%% + +int +parse_dagfile(struct dagfile *df, FILE *file) +{ + dagfile = df; + dagfile->target = NULL; + t = NULL; + e = NULL; + s = NULL; + yyin = file; + return yyparse(); +} + +void +push_target(char *string) +{ + if (t != NULL) { + printf("error: only one target block allowed\n"); + exit(1); + } + + t = malloc(sizeof(struct target)); + t->path = strdup(unquote(string)); + t->sources = NULL; + dagfile->target = t; +} + +void +push_source(char *string) +{ + so = malloc(sizeof(struct source)); + so->path = strdup(unquote(string)); + so->extensions = NULL; + so->next = NULL; + + if (t->sources == NULL) { + t->sources = so; + return; + } + + struct source *ts = t->sources; + + while (ts->next != NULL) { + ts = ts->next; + } + ts->next = so; +} + +void +push_extension(char *string) +{ + e = malloc(sizeof(struct extension)); + e->value = strdup(unquote(string)); + e->suffixes = NULL; + e->next = NULL; + + if (so->extensions == NULL) { + so->extensions = e; + return; + } + + struct extension *se = so->extensions; + + while (se->next != NULL) { + se = se->next; + } + se->next = e; +} + +void +push_suffix(char *string) +{ + s = malloc(sizeof(struct suffix)); + s->value = strdup(unquote(string)); + s->requirements = NULL; + s->filters = NULL; + s->next = NULL; + + if (e->suffixes == NULL) { + e->suffixes = s; + return; + } + + struct suffix *es = e->suffixes; + + while (es->next != NULL) { + es = es->next; + } + es->next = s; +} + +void +push_requirement(char *string) +{ + struct requirement *r = malloc(sizeof(struct requirement)); + r->path = strdup(unquote(string)); + r->next = NULL; + + if (s->requirements == NULL) { + s->requirements = r; + return; + } + + struct requirement *sr = s->requirements; + + while (sr->next != NULL) { + sr = sr->next; + } + sr->next = r; +} + +void +push_filter(char *string) +{ + struct filter *f = malloc(sizeof(struct filter)); + f->cmd = strdup(unquote(string)); + f->next = NULL; + + if (s->filters == NULL) { + s->filters = f; + return; + } + + struct filter *sf = s->filters; + + while (sf->next != NULL) { + sf = sf->next; + } + sf->next = f; +} + +void +debug_dagfile(struct dagfile *df) +{ + printf("target %s\n", df->target->path); + struct source *s = df->target->sources; + while (s != NULL) { + printf("source %s\n", s->path); + struct extension *e = s->extensions; + while (e != NULL) { + printf("extension %s\n", e->value); + struct suffix *s = e->suffixes; + while (s != NULL) { + printf("suffix %s\n", s->value); + struct requirement *r = s->requirements; + while (r != NULL) { + printf("require %s\n", r->path); + r = r->next; + } + struct filter *f = s->filters; + while (f != NULL) { + printf("filter %s\n", f->cmd); + f = f->next; + } + s = s->next; + } + e = e->next; + } + s = s->next; + } +} +\ No newline at end of file diff --git a/string.c b/string.c @@ -57,3 +57,79 @@ dedup(char **strarray) i++; } } + +char * +unquote(char *string) +{ + if ((string[0] == '"' && string[strlen(string)-1] == '"') || + (string[0] == '\'' && string[strlen(string)-1] == '\'')) { + string[strlen(string)-1] = '\0'; + return string + 1; + } + return string; +} + +char * +strnesc(char *str, int maxlen) +{ + int len = strlen(str); + for (int i=0;;i++) { + if (str[i] == '\0') + break; + + if (str[i] == '%') { + if (len >= maxlen) + return NULL; + + for (int j=len;j>=i;j--) + str[j+1] = str[j]; + len += 1; + i += 1; + } + } + + return str; +} + +int +strcnt(char *str, char c) +{ + int n = 0; + + for (int i=0;;i++) { + if (str[i] == '\0') + break; + if (str[i] == c) + n += 1; + } + return n; +} + +int +sprintf_ct(char *str, char *fmt, char *subst, int ct) +{ + switch (ct) { + case 0: + return sprintf(str, fmt, NULL); + case 1: + return sprintf(str, fmt, subst); + case 2: + return sprintf(str, fmt, subst, subst); + case 3: + return sprintf(str, fmt, subst, subst, subst); + case 4: + return sprintf(str, fmt, subst, subst, subst, subst); + case 5: + return sprintf(str, fmt, subst, subst, subst, subst, subst); + case 6: + return sprintf(str, fmt, subst, subst, subst, subst, subst, subst); + case 7: + return sprintf(str, fmt, subst, subst, subst, subst, subst, subst, subst); + case 8: + return sprintf(str, fmt, subst, subst, subst, subst, subst, subst, subst, subst); + case 9: + return sprintf(str, fmt, subst, subst, subst, subst, subst, subst, subst, subst, subst); + default: + return 0; + } +} diff --git a/string.h b/string.h @@ -10,6 +10,10 @@ int strbegin(const char *big, const char *little); int strend(const char *big, const char *little); char *strnswp(char *big, const char *old, const char *new, size_t max); +char *unquote(char *string); +char *strnesc(char *str, int maxlen); +int strcnt(char *str, char c); +int sprintf_ct(char *str, char *fmt, char *subst, int ct); /* assumes the last element of strarray is NULL */ void dedup(char **strarray); diff --git a/string_test.c b/string_test.c @@ -141,6 +141,102 @@ test_strend3() return 0; } +int +test_unquote1() +{ + char string[] = "\"test\""; + char *s = unquote(string), exp[] = "test"; + if (strcmp(s, exp) != 0) { + printf("test_unquote1 failed:\n\texp: %s\n\tact: %s\n", exp, s); + return 1; + } + + return 0; +} + +int +test_unquote2() +{ + char string[] = "\"test"; + char *s = unquote(string), exp[] = "\"test"; + if (strcmp(s, exp) != 0) { + printf("test_unquote1 failed:\n\texp: %s\n\tact: %s\n", exp, s); + return 1; + } + + return 0; +} + +int +test_unquote3() +{ + char string[] = "'test'"; + char *s = unquote(string), exp[] = "test"; + if (strcmp(s, exp) != 0) { + printf("test_unquote1 failed:\n\texp: %s\n\tact: %s\n", exp, s); + return 1; + } + + return 0; +} + +int +test_unquote4() +{ + char string[] = "'test"; + char *s = unquote(string), exp[] = "'test"; + if (strcmp(s, exp) != 0) { + printf("test_unquote1 failed:\n\texp: %s\n\tact: %s\n", exp, s); + return 1; + } + + return 0; +} + +int +test_strnesc1() +{ + char string[] = "test", exp[] = "test"; + char *s = strnesc(string, strlen(string)); + if (s == NULL) { + printf("test_strnesc1 failed:\n\tcall to strnesc returned NULL\n"); + return 1; + } + if (strcmp(s, exp) != 0) { + printf("test_strnesc1 failed:\n\texp: %s\n\tact: %s\n", exp, s); + return 1; + } + return 0; +} + +int +test_strnesc2() +{ + char string[] = "test%\0\0", exp[] = "test%%"; + char *s = strnesc(string, 6); + if (s == NULL) { + printf("test_strnesc2 failed:\n\tcall to strnesc returned NULL\n"); + return 1; + } + if (strcmp(s, exp) != 0) { + printf("test_strnesc2 failed:\n\texp: %s\n\tact: %s\n", exp, s); + return 1; + } + return 0; +} + +int +test_strnesc3() +{ + char string[] = "test%"; + char *s = strnesc(string, strlen(string)); + if (s != NULL) { + printf("test_strnesc1 failed:\n\tshould return NULL\n"); + return 1; + } + return 0; +} + int main() { int retval = test_strnswp1(); @@ -151,5 +247,12 @@ int main() retval |= test_strnswp6(); retval |= test_strend1(); retval |= test_strend2(); + retval |= test_unquote1(); + retval |= test_unquote2(); + retval |= test_unquote3(); + retval |= test_unquote4(); + retval |= test_strnesc1(); + retval |= test_strnesc2(); + retval |= test_strnesc3(); return retval; }