bake/bake.l

227 lines
8.9 KiB
Plaintext
Raw Normal View History

2024-09-26 23:13:43 -04:00
/* cbake.l @BAKE flex @FILE && cc -Wall -Wextra -std=c99 -D_GNU_SOURCE -o @SHORT lex.yy.c @ARGS -lfl @STOP */
2024-09-28 17:35:50 -04:00
/* expunge @BAKE flex @FILE && cc -Wall -Wextra -std=c99 -D_GNU_SOURCE -o @{@SHORT} lex.yy.c @ARGS -lfl @STOP */
/* expunge @BAKE @STOP */
2024-09-22 17:21:10 -04:00
%{
2024-09-26 18:16:01 -04:00
#include <ctype.h>
2024-09-28 17:35:50 -04:00
#define CHAR(c) do { if (stdout) { fputc(c, stdout); } if (g_pipe) { fputc(c, g_pipe); } } while (0)
#define STRING(s) do { if (stdout) { fputs(s, stdout); } if (g_pipe) { fputs(s, g_pipe); } } while (0)
#define FORMAT(...) do { if (stdout) { fprintf(stdout, __VA_ARGS__); } if (g_pipe) { fprintf(g_pipe, __VA_ARGS__); } } while (0)
#define FWRITE(str, len) do { if (stdout) { fwrite(str, 1, len, stdout); } if (g_pipe) { fwrite(str, 1, len, g_pipe); } } while (0)
2024-09-26 18:16:01 -04:00
#undef ECHO
2024-09-28 17:35:50 -04:00
#define ECHO STRING(yytext)
2024-09-26 22:35:33 -04:00
/* input from main to lexer */
2024-09-28 17:35:50 -04:00
FILE * g_pipe = NULL, * g_restore, * g_expunge;
int g_ac, g_select = 1, g_color = 1, g_rm = 0;
char ** g_av, * g_filename, * av0;
2024-09-26 22:35:33 -04:00
/* for the lexers eyes only */
2024-09-28 17:35:50 -04:00
int line = 1, expunge_depth, first_nl, tmpline;
2024-09-22 17:21:10 -04:00
2024-09-26 18:16:01 -04:00
extern void root(char * filename);
2024-09-28 18:13:54 -04:00
extern void args(int n, int rest);
2024-09-22 17:21:10 -04:00
extern void shorten(char * filename, int n);
2024-09-28 17:35:50 -04:00
extern void pipeopen(char * filename, char * mode);
2024-09-22 17:21:10 -04:00
%}
2024-09-26 18:16:01 -04:00
SPACE [ \t\r\v\f]
2024-09-26 23:13:43 -04:00
MACROS (@BAKE|@FILENAME|@FILE|@NAME|@SHORT|@ARGS|@LINE|@STOP|$@|$*|$+)
2024-09-26 18:16:01 -04:00
2024-09-28 18:13:54 -04:00
%x FOUND PADDING STOP
2024-09-26 18:16:01 -04:00
%option nodefault noinput nounput noyywrap
2024-09-22 17:21:10 -04:00
%%
2024-09-26 22:35:33 -04:00
2024-09-28 17:35:50 -04:00
\n { ++line; }
. { ; }
2024-09-26 18:16:01 -04:00
@BAKE[[:space:]] { bake:
2024-09-28 17:35:50 -04:00
static int nth = 0;
2024-09-26 18:16:01 -04:00
first_nl = 1;
2024-09-28 18:13:54 -04:00
if (yytext[yyleng-1] == '\n') { ++line; }
if (!g_select) { ; }
2024-09-28 17:41:31 -04:00
else if (g_select < 0) { BEGIN FOUND; printf("%s:%d:s%d: ", g_filename, line, ++nth); }
2024-09-28 18:13:54 -04:00
else if (!--g_select) { BEGIN FOUND; }
2024-09-26 18:16:01 -04:00
}
2024-09-26 22:35:33 -04:00
2024-09-22 17:21:10 -04:00
<FOUND>{
2024-09-28 17:35:50 -04:00
@\{ {
expunge_depth = 1;
if (g_select < 0) { ECHO; }
else if (g_rm && !g_pipe) {
stdout = g_restore;
g_pipe = g_expunge; STRING("rm '");
}
}
\} {
if (g_select < 0 || !expunge_depth) { ECHO; break; }
if (g_rm) { STRING("'"); return 0; }
expunge_depth = 0;
}
' { if (g_rm) { STRING("\\'"); } else { ECHO; } }
@BAKE[[:space:]]|@STOP { BEGIN INITIAL; yyless(0); if (first_nl) { CHAR('\n'); } if (!g_select) { return 0; } }
\\\n { BEGIN PADDING; ++line; CHAR(' '); }
2024-09-28 18:13:54 -04:00
@FILENAME|@FILE|@NAME|$@ { STRING(g_filename); }
@SHORT:[[:digit:]]+ { shorten(g_filename, atoi(strrchr(yytext, ':')+1)); }
@SHORT|$\* { shorten(g_filename, 1); }
@ARGS:[[:digit:]]+ { args(atoi(strrchr(yytext, ':')+1), 0); }
@ARGS:[[:digit:]]+\+ { args(atoi(strrchr(yytext, ':')+1), 1); }
@ARGS|$\+ { args(0, 1); }
@LINE { FORMAT("%d", line); }
@RECURS { char * prog = realpath(av0, NULL); STRING(prog); free(prog); }
2024-09-28 17:35:50 -04:00
\\{MACROS} { STRING(yytext + 1); }
{SPACE} { BEGIN PADDING; CHAR(' '); }
\n { CHAR('\n'); ++line; if (first_nl) { BEGIN STOP; first_nl = 0; tmpline = 0; } }
.|\\' { ECHO; }
}
2024-09-26 18:14:37 -04:00
<PADDING>{
2024-09-26 22:35:33 -04:00
{SPACE} { ; }
2024-09-26 18:16:01 -04:00
.|\n { yyless(0); BEGIN FOUND; }
}
2024-09-26 22:35:33 -04:00
2024-09-26 18:16:01 -04:00
<STOP>{
@BAKE[[:space:]] { line += tmpline; goto bake; }
2024-09-26 22:35:33 -04:00
@STOP { BEGIN FOUND; yyless(0); }
\n { ++tmpline; yymore(); }
.|\\@ { yymore(); }
2024-09-26 18:14:37 -04:00
}
2024-09-26 22:35:33 -04:00
2024-09-26 18:14:56 -04:00
%%
2024-09-26 18:14:37 -04:00
2024-09-28 17:35:50 -04:00
# define RED "\033[91m"
# define GREEN "\033[92m"
# define YELLOW "\033[93m"
# define DIM "\033[2m"
# define BOLD "\033[1m"
# define RESET "\033[0m"
2024-09-22 17:21:10 -04:00
void root(char * filename) {
char * path, * terminator;
if (!(path = realpath(filename, NULL))) { return; }
2024-09-26 18:20:29 -04:00
if ((terminator = strrchr(path, '/'))) {
2024-09-22 17:21:10 -04:00
*terminator = '\0';
chroot(path);
}
free(path);
}
2024-09-28 18:13:54 -04:00
void args(int n, int rest) {
if (!rest && n < g_ac) { STRING(g_av[n]); }
else for (int i = n; i < g_ac; ++i) { STRING(g_av[i]); if (i + 1 < g_ac) { CHAR(' '); } }
2024-09-22 17:21:10 -04:00
}
void shorten(char * filename, int n) {
char * end = filename + strlen(filename);
while (n && (end = memrchr(filename, '.', end - filename))) { --n; }
2024-09-26 22:35:33 -04:00
if (!end) {
fprintf(stderr, "<SHORTEN> context error: Argument out of range.\n");
/* Ensures consistency. @SHORT will always return *something* that isn't filename */
STRING("idiot");
return;
}
2024-09-26 18:16:01 -04:00
FWRITE(filename, end - filename);
2024-09-22 17:21:10 -04:00
}
2024-09-28 17:35:50 -04:00
void help(void) { fprintf(stderr, g_color ? BOLD "%s" RESET : "%s", "see bake(1) - \"Buy high. Sell low.\"\n"); }
void pipeopen(char * filename, char * mode) {
g_pipe = popen(filename, mode);
if (!g_pipe) { fprintf(stderr, "%s: <g_pipe> %s\n", av0, strerror(errno)); exit(1); }
}
2024-09-26 18:16:01 -04:00
2024-09-22 17:21:10 -04:00
int main (int ac, char ** av) {
2024-09-26 18:16:01 -04:00
int run = 1;
2024-09-28 17:35:50 -04:00
av0 = av[0];
2024-09-26 18:14:56 -04:00
FILE * fp;
2024-09-26 22:35:33 -04:00
/* supports long/short, -allinone, (-X ... -X=... -X<NUM>) */
2024-09-22 17:21:10 -04:00
while (++av, --ac) {
2024-09-26 18:16:01 -04:00
size_t i;
2024-09-22 17:21:10 -04:00
if (av[0][0] != '-') { goto start; }
if (av[0][1] == '-') {
2024-09-26 22:35:33 -04:00
if (av[0][2] == '\0') { ++av, --ac; goto start; }
if (!strcmp(av[0]+2, "dry-run")) { i = strlen(av[0]); goto opt_dry_run; }
if (!strcmp(av[0]+2, "color")) { i = strlen(av[0]); goto opt_color; }
2024-09-28 17:35:50 -04:00
if (!strcmp(av[0]+2, "expunge")) { i = strlen(av[0]); goto opt_expunge; }
2024-09-28 18:25:06 -04:00
if (!strcmp(av[0]+2, "select" )) { if (!(ac-1) || !isdigit(av[1][0])) { goto opt_arg; }
2024-09-26 22:35:33 -04:00
++av, --ac; i = strlen(av[0]); goto opt_select; }
if (!strcmp(av[0]+2, "list" )) { i = strlen(av[0]); goto opt_list; }
if (!strcmp(av[0]+2, "help" )) { goto opt_help; }
2024-09-22 17:21:10 -04:00
goto opt_default;
}
for (i = 1; i < strlen(av[0]); ++i) {
switch (av[0][i]) {
opt_dry_run: case 'n': run = 0; break;
2024-09-26 22:35:33 -04:00
case 's':
/* Covers cases -<LAST>s<NUM> -<LAST>s <NUM> */
2024-09-28 17:35:50 -04:00
if (g_select != -1) {
if (isdigit(av[0][i+1])) { g_select = atoi(av[0]+i+1); }
else if (ac > 1 && isdigit(av[1][0])) { ++av, --ac; opt_select: g_select = atoi(av[0]); }
else { g_select = 0; }
if (!g_select) { fprintf(stderr, "%s: Invalid argument for -s\n", av0); return 1; }
}
2024-09-26 22:35:33 -04:00
i = strlen(av[0]);
break;
opt_list: case 'l': run = 0; g_select = -1; break;
2024-09-22 17:21:10 -04:00
opt_help: case 'h': help(); return 0;
2024-09-28 17:35:50 -04:00
opt_color: case 'c': g_color = 0; break;
opt_expunge: case 'x': if (g_select > 0) { g_rm = 1; } break;
2024-09-26 22:35:33 -04:00
opt_default: default: fprintf(stderr, "%s: Unknown option '%s'\n", av0, av[0]); return 1;
opt_arg: fprintf(stderr, "%s: Argument missing for '%s'\n", av0, av[0]); return 1;
2024-09-22 17:21:10 -04:00
}
}
}
2024-09-26 18:16:01 -04:00
start:
2024-09-22 17:21:10 -04:00
if (!ac) { fprintf(stderr, "%s: Missing filename\n", av0); return 1; }
2024-09-26 22:35:33 -04:00
if (!g_select) { goto out_of_range; }
g_filename = av[0];
root(g_filename);
{ /* ensures the filename doesn't have a relative path that would misdirect the command within the new root */
char * tmp = strrchr(g_filename, '/');
if (tmp) { g_filename = tmp+1; }
2024-09-22 17:21:10 -04:00
}
2024-09-26 18:16:01 -04:00
2024-09-22 17:21:10 -04:00
/* open and prepare ac, av */
2024-09-26 22:35:33 -04:00
if (!(yyin = fp = fopen(g_filename, "rb")))
2024-09-28 17:35:50 -04:00
{ fprintf(stderr, g_color ? RED "%s" RESET ": '" BOLD "%s" RESET "' %s\n" : "%s: '%s' %s\n", av0, g_filename, strerror(errno)); return 1; }
2024-09-26 22:35:33 -04:00
g_ac = --ac, g_av = ++av;
2024-09-26 18:16:01 -04:00
2024-09-28 17:35:50 -04:00
/* setup pipe and output */
2024-09-26 18:16:01 -04:00
if (run) {
2024-09-28 17:35:50 -04:00
pipeopen("/bin/sh -e /dev/stdin", "w");
2024-09-22 17:21:10 -04:00
}
2024-09-28 17:35:50 -04:00
if (g_rm) {
g_restore = stdout;
g_expunge = g_pipe;
stdout = NULL;
g_pipe = NULL;
}
if (g_select > 0) { fprintf(stderr, g_color ? GREEN "%s" RESET ": " : "%s: ", av0); fflush(stderr); }
2024-09-26 18:14:37 -04:00
2024-09-26 18:16:01 -04:00
yylex(); fflush(stdout);
2024-09-28 17:35:50 -04:00
if (g_rm) {
fputs("\n", g_restore);
stdout = g_restore;
g_pipe = g_expunge;
}
2024-09-26 18:16:01 -04:00
fclose(fp);
2024-09-28 17:35:50 -04:00
2024-09-26 22:35:33 -04:00
if (g_select > 0) { pclose(g_pipe); goto out_of_range; }
2024-09-26 18:16:01 -04:00
if (!run) { return 0; }
2024-09-28 17:35:50 -04:00
if (!g_rm) { fprintf(stderr, g_color ? GREEN "output" RESET ": " : "output: "); fflush(stderr); }
2024-09-26 22:35:33 -04:00
run = pclose(g_pipe); /* repurposed run */
2024-09-28 17:35:50 -04:00
if (!g_rm) { putchar('\n'); }
if (run) { fprintf(stderr, "%s: Exit code %d\n", av0, run); }
2024-09-26 18:16:01 -04:00
return run;
2024-09-26 22:35:33 -04:00
out_of_range: fprintf(stderr, "%s: <%d> Out of range\n", av0, g_select); return 1;
2024-09-22 17:21:10 -04:00
}