adds pp sources

This commit is contained in:
2026-06-17 21:46:49 +01:00
parent 506d1adfcc
commit 6dd4caf947
6 changed files with 647 additions and 0 deletions
+15
View File
@@ -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.
+29
View File
@@ -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
+188
View File
@@ -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>
+46
View File
@@ -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
+369
View File
@@ -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;
}
BIN
View File
Binary file not shown.