diff --git a/http/pp/LICENSE b/http/pp/LICENSE new file mode 100644 index 0000000..648cf93 --- /dev/null +++ b/http/pp/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2021, Adrian Emil Grigore + +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/http/pp/Makefile b/http/pp/Makefile new file mode 100644 index 0000000..afc122f --- /dev/null +++ b/http/pp/Makefile @@ -0,0 +1,29 @@ +CC = cc +LD = ${CC} + +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +CFLAGS = -Wall -Wextra -pedantic + +pp: pp.o +pp.o: pp.c + +.c.o: + $(CC) $(CPPFLAGS) $< $(CFLAGS) -c -o $@ +.o: + $(LD) $< $(LDFLAGS) -o $@ + +clean: + rm -rf pp *.o + +install: pp pp.1 + mkdir -p $(DESTDIR)$(PREFIX)/bin + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + cp pp $(DESTDIR)$(PREFIX)/bin/pp + cp pp.1 $(DESTDIR)$(MANPREFIX)/man1/ + chmod 755 $(DESTDIR)$(PREFIX)/bin/pp + +uninstall: + rm $(DESTDIR)$(PREFIX)/bin/pp + rm $(DESTDIR)$(MANPREFIX)/man1/pp.1 diff --git a/http/pp/README.md b/http/pp/README.md new file mode 100644 index 0000000..f69faf1 --- /dev/null +++ b/http/pp/README.md @@ -0,0 +1,188 @@ +# `pp(1)` + +a preprocessor + +`pp(1)` allows embedding +[`sh(1)`](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html) +code in files of any type by nesting it inside the `#!\n` token, where +`\n` is a new line. That means that if you'd like a simple loop or an +`if` inside an HTML file for instance, +you could use `pp(1)`. + +## Basic usage + +For the following code: + + + pp(1) example + + +`pp(1)` outputs: + + + pp(1) example + + +## Arguments + +You can also pass arguments to it by appending them to the command call: + + + $1 +

+ #! + echo $2 + #! +

+ +Calling the above code with +`pp tmp.upphtml 'pp(1) example' 'hello, world'` +will result in: + + + pp(1) example +

+ hello world +

+ +## Pipes + +`pp(1)`'s `stdin` is sent to the child +[`sh(1)`](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html) + + + pp(1) example +

+ #! + cat + #! +

+ +`echo 'hello, world' | pp tmp.upphtml` will output: + + + pp(1) example +

+ hello, world +

+ +## Debugging + +`pp(1)` also takes an optional `-d` flag. If passed, `pp(1)` will dump +the generated +[`sh(1)`](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html) +code instead of executing it: + + + pp(1) example + + +`pp -d tmp.upphtml` will output: + + echo " + pp(1) example + " + +## Example + + + + pp(1) + + + + + Home + #! + addcls() { + sed "s///g" + } + smu README | addcls + #! +

Files

+
    + #! + for f in * + do + case $f in + *.upp*|*.html|*.tgz) + continue + ;; + esac + #! +
  • $f
  • + #! + done + #! +
+

Download

+ pp.tgz + + + + + +## See also + +* +* +* diff --git a/http/pp/pp.1 b/http/pp/pp.1 new file mode 100644 index 0000000..a552c97 --- /dev/null +++ b/http/pp/pp.1 @@ -0,0 +1,46 @@ +.Dd Apr 25, 2019 +.Dt pp 1 +.Os +.Sh NAME +.Nm pp +.Nd preprocessor +.Sh SYNOPSIS +.Nm pp +.Op Fl d +.Ar file +.Op args ... +.Sh DESCRIPTION +The +.Nm +utility processes +.Ar file +to produce output that is used as input to another +program. +.Pp +It executes code enclosed in +.Dq #!\en +delimiters using +.Xr sh 1 . +.Op args ... +are passed to the embbeded script and are available +via the usual +.Em $1, $2, ... +positional parameters. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +.Nm +will dump the generated +.Xr sh 1 +code instead of executing it. This is useful for debugging. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Preprocess file with arguments: +.Pp +.Dl $ pp file.upphtml a b +.Pp +.Sh SEE ALSO +.Xr sh 1 diff --git a/http/pp/pp.c b/http/pp/pp.c new file mode 100644 index 0000000..340f430 --- /dev/null +++ b/http/pp/pp.c @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include +#include + +#if !__APPLE__ +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} +#endif /* !__APPLE__ */ + +#define MAXSIZE 12000 +#define MAXINDENTSIZE 80 +#define MAXARGSIZE 20 + +char *argv0; + +int +efgetc(FILE *fp) +{ + int c; + + c = fgetc(fp); + + if (c == EOF && + ferror(fp) != 0) { + perror(argv0); + exit(1); + } + + return c; +} + +int +efseek(FILE *fp, long offset, int whence) +{ + int s; + + s = fseek(fp, offset, whence); + + if (s == -1) { + perror(argv0); + exit(1); + } + + return s; +} + +int +eungetc(int c, FILE *fp) +{ + int uc; + + uc = ungetc(c, fp); + + if (uc == EOF) { + perror(argv0); + exit(1); + } + + return uc; +} + +size_t +estrlcat(char *dst, const char *src, size_t dstsize) +{ + size_t n; + + n = strlcat(dst, src, dstsize); + + if (n >= dstsize) { + fprintf(stderr, "%s: Buffer overflow\n", argv0); + exit(1); + } + + return n; +} + +int +eclose(int d) +{ + int s; + + s = close(d); + + if (s == -1) { + perror(argv0); + exit(1); + } + + return s; +} + +char * +strappend(char *s, char c) +{ + size_t len; + + len = strlen(s); + + s[len] = c; + s[len + 1] = '\0'; + + return s ; +} + +int +shebang(FILE *fp) +{ + int c; + + c = efgetc(fp); + + if (c != '#') { + eungetc(c, fp); + return 0; + } + + c = efgetc(fp); + + if (c != '!') { + eungetc(c, fp); + eungetc('#', fp); + return 0; + } + + c = efgetc(fp); + + if (c == '\n') { + eungetc(c, fp); + eungetc('!', fp); + eungetc('#', fp); + return 0; + } + + for (;;) { + c = efgetc(fp); + if (c == '\n') + break; + } + + return 1; +} + +int +token(FILE *fp, int consume) +{ + int c; + + c = efgetc(fp); + + if (c != '#') { + eungetc(c, fp); + return 0; + } + + c = efgetc(fp); + if (c != '!') { + eungetc(c, fp); + eungetc('#', fp); + return 0; + }; + + c = efgetc(fp); + + if (c != '\n') { + eungetc(c, fp); + eungetc('!', fp); + eungetc('#', fp); + return 0; + } + + if (consume == 0) { + eungetc(c, fp); + eungetc('!', fp); + eungetc('#', fp); + } + + return 1; +} + +size_t +output(char *s) +{ + return estrlcat(s, "echo \"", MAXSIZE); +} + +size_t +outputend(char *s) +{ + return estrlcat(s, "\"\n", MAXSIZE); +} + +int main(int argc, char **argv) +{ + char s[MAXSIZE] = "", indent[MAXINDENTSIZE] = "", + *eargv[MAXARGSIZE] = {"sh", "-c", s, "sh", NULL}; + FILE *fp; + int size, c, i, fd[2], o = 0, status = 0, d = 0; + + + argv0 = argv[0]; + if (argc == 1) { + fprintf(stderr, "usage: %s file [args ...]\n", argv[0]); + return 1; + + } + + if (strcmp(argv[1], "-d") == 0) { + if (argc == 2) { + fprintf(stderr, "usage: %s file [args ...]\n", argv[0]); + return 1; + + } + d = 1; + fp = fopen(argv[2], "r"); + } else { + fp = fopen(argv[1], "r"); + } + + + if (!fp) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], d ? argv[2] : argv[1], strerror(errno)); + return 1; + } + + efseek (fp, 0, SEEK_END); + size = ftell(fp); + + if(size == 0) return 0; + + efseek(fp, -1, SEEK_END); + c = efgetc(fp); + + if (c != '\n') { + fprintf(stderr, "%s: %s: No newline at end of file\n", + argv[0], d ? argv[2] : argv[1]); + exit(1); + } + + efseek(fp, 0, SEEK_SET); + + shebang(fp); + + if (!token(fp, 1)) { + output(s); + o = 1; + } + + for(;;) { + if (token(fp, 1)) { + if (!o) { + c = efgetc(fp); + if (c == EOF) + goto done; + eungetc(c, fp); + output(s); + } else + outputend(s); + o = !o; + } + + c = efgetc(fp); + if (c == EOF) + break; + + if (c == '\n') { + indent[0] = 0; + c = efgetc(fp); + if (c == EOF) + break; + + for (;;) { + if (c != ' ' && c != '\t') + break; + strappend(indent, c); + c = efgetc(fp); + if (c == EOF) + break; + } + + eungetc(c, fp); + + if (token(fp, 0) && o) + continue; + + strappend(s, '\n'); + estrlcat(s, indent, MAXSIZE); + continue; + } + + strappend(s, c); + } + if (o) { + outputend(s); + o = 0; + } + done: + + if(d) { + puts(s); + return 0; + } + + if (pipe(fd) == -1) { + perror(argv0); + return 1; + } + + switch (fork()) { + case -1: + perror(argv0); + return 1; + case 0: + eclose(fd[1]); + eclose(fd[0]); + for (i = 2; i < argc; i++) + eargv[i+2] = argv[i]; + + if (execv("/bin/sh", eargv) == -1) { + perror(argv0); + return 1; + } + break; + default: + eclose(fd[1]); + eclose(fd[0]); + if (wait(&status) == -1) { + perror(argv0); + return 1; + } + } + + return status; +} diff --git a/http/pp/pp.o b/http/pp/pp.o new file mode 100644 index 0000000..7c3f385 Binary files /dev/null and b/http/pp/pp.o differ