diff options
| author | Joe Carstairs <me@joeac.net> | 2026-06-17 21:46:49 +0100 |
|---|---|---|
| committer | Joe Carstairs <me@joeac.net> | 2026-06-17 21:48:24 +0100 |
| commit | 6dd4caf947f2997698ebf7354172a6476ad0bf25 (patch) | |
| tree | bcdd557656119dc028234d67396930c124ee0735 /http | |
| parent | 506d1adfccbb494be598e08f8d45aaae641eb6d2 (diff) | |
adds pp sources
Diffstat (limited to 'http')
| -rw-r--r-- | http/pp/LICENSE | 15 | ||||
| -rw-r--r-- | http/pp/Makefile | 29 | ||||
| -rw-r--r-- | http/pp/README.md | 188 | ||||
| -rw-r--r-- | http/pp/pp.1 | 46 | ||||
| -rw-r--r-- | http/pp/pp.c | 369 | ||||
| -rw-r--r-- | http/pp/pp.o | bin | 0 -> 9168 bytes |
6 files changed, 647 insertions, 0 deletions
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 <adrian.emil.grigore@gmail.com> + +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: + + <!doctype html> + <title>pp(1) example</title> + <ul> + #! + i=1 + while test $i -le 10 + do + if test $((i % 2)) -eq 0 + then + #! + <li class=even>$i</li> + #! + else + #! + <li class=odd>$i</li> + #! + fi + i=$((i + 1)) + done + #! + </ul> + +`pp(1)` outputs: + + <!doctype html> + <title>pp(1) example</title> + <ul> + <li class=odd>1</li> + <li class=even>2</li> + <li class=odd>3</li> + <li class=even>4</li> + <li class=odd>5</li> + <li class=even>6</li> + <li class=odd>7</li> + <li class=even>8</li> + <li class=odd>9</li> + <li class=even>10</li> + </ul> + +## Arguments + +You can also pass arguments to it by appending them to the command call: + + <!doctype html> + <title>$1</title> + <p> + #! + echo $2 + #! + </p> + +Calling the above code with +`pp tmp.upphtml 'pp(1) example' 'hello, world'` +will result in: + + <!doctype html> + <title>pp(1) example</title> + <p> + hello world + </p> + +## Pipes + +`pp(1)`'s `stdin` is sent to the child +[`sh(1)`](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html) + + <!doctype html> + <title>pp(1) example</title> + <p> + #! + cat + #! + </p> + +`echo 'hello, world' | pp tmp.upphtml` will output: + + <!doctype html> + <title>pp(1) example</title> + <p> + hello, world + </p> + +## 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: + + <!doctype html> + <title>pp(1) example</title> + <ul> + #! + i=1 + while test $i -le 10 + do + if test $((i % 2)) -eq 0 + then + #! + <li class=even>$i</li> + #! + else + #! + <li class=odd>$i</li> + #! + fi + i=$((i + 1)) + done + #! + </ul> + +`pp -d tmp.upphtml` will output: + + echo "<!doctype html> + <title>pp(1) example</title> + <ul>" + i=1 + while test $i -le 10 + do + if test $((i % 2)) -eq 0 + then + echo " <li class=even>$i</li>" + else + echo " <li class=odd>$i</li>" + fi + i=$((i + 1)) + done + echo "</ul>" + +## Example + + <!doctype html> + <html lang=en> + <title>pp(1)</title> + <meta charset=UTF-8> + <meta name=viewport content='width=device-width'> + <link rel=stylesheet href=../theme.css> + <body> + <a href=../#myprojects>Home</a> + #! + addcls() { + sed "s/<code>/<code class=language-bash>/g" + } + smu README | addcls + #! + <h2>Files</h2> + <ul> + #! + for f in * + do + case $f in + *.upp*|*.html|*.tgz) + continue + ;; + esac + #! + <li><a href=$f>$f</a></li> + #! + done + #! + </ul> + <h2>Download</h2> + <a href=pp.tgz>pp.tgz</a> + <script src=../prism.js data-manual></script> + <script src=../enhance.js></script> + </body> + </html> + +## See also + +* <https://github.com/radare/spp> +* <https://werc.cat-v.org/docs/rc-template-lang> +* <https://code.9front.org/hg/werc/file/92f7463dac1a/bin/template.awk> 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#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 Binary files differnew file mode 100644 index 0000000..7c3f385 --- /dev/null +++ b/http/pp/pp.o |
