adds pp sources
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user