Lindenii Project Forge
Reformat C files
/* * Copyright (c) 2024-2025 Runxi Yu <https://runxiyu.org> * Copyright (c) 2022-2024 Omar Polo <op@openbsd.org> * * Permission to use, copy, modify, and 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. */ #include <err.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int parse(FILE *, const char *); int nodebug;
void usage(char *progname)
void usage(char *progname)
{ fprintf(stderr, "usage: %s [-G] [-o out] [file ...]\n", progname); exit(1); }
int main(int argc, char **argv)
int main(int argc, char **argv)
{ FILE *fp = stdout; const char *out = NULL; int ch, i; while ((ch = getopt(argc, argv, "Go:")) != -1) { switch (ch) { case 'G': nodebug = 1; break; case 'o': out = optarg; break; default: if (argc == 0) usage("htmplgen"); else usage(argv[0]); } } argc -= optind; argv += optind; if (out && (fp = fopen(out, "w")) == NULL) err(1, "can't open %s", out); if (argc == 0) { if (parse(fp, "/dev/stdin") == -1) goto err; } else { fputs("use fmt;\n", fp); fputs("use io;\n", fp); fputs("use htmpl;\n", fp); for (i = 0; i < argc; ++i) if (parse(fp, argv[i]) == -1) goto err; } if (ferror(fp)) goto err; if (fclose(fp) == -1) { fp = NULL; goto err; } return 0;
err:
err:
if (fp) fclose(fp); if (out && unlink(out) == -1) err(1, "unlink %s", out); return 1; }
/* * Copyright (c) 2025 Runxi Yu <me@runxiyu.org> * Copyright (c) 2022 Omar Polo <op@openbsd.org> * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org> * Copyright (c) 2004-2005 Esben Norby <norby@openbsd.org> * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> * Copyright (c) 2002-2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. * Copyright (c) 2001 Theo de Raadt. All rights reserved. * * Permission to use, copy, modify, and 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. */ %{ #include <sys/queue.h> #include <ctype.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <stdint.h> #include <string.h> #include <unistd.h> #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
static struct file {
TAILQ_ENTRY(file) entry; FILE *stream; char *name; size_t ungetpos; size_t ungetsize; unsigned char *ungetbuf; int eof_reached; int lineno; int errors;
TAILQ_ENTRY(file) entry; FILE *stream; char *name; size_t ungetpos; size_t ungetsize; unsigned char *ungetbuf; int eof_reached; int lineno; int errors;
} *file, *topfile;
int parse(FILE *, const char *); struct file *pushfile(const char *, int); int popfile(void); int yyparse(void); int yylex(void); int yyerror(const char *, ...) __attribute__((__format__ (printf, 1, 2))) __attribute__((__nonnull__ (1))); int kw_cmp(const void *, const void *); int lookup(char *); int igetc(void); int lgetc(int); void lungetc(int); int findeol(void);
int parse(FILE *, const char *); struct file *pushfile(const char *, int); int popfile(void); int yyparse(void); int yylex(void); int yyerror(const char *, ...) __attribute__((__format__ (printf, 1, 2))) __attribute__((__nonnull__ (1))); int kw_cmp(const void *, const void *); int lookup(char *); int igetc(void); int lgetc(int); void lungetc(int); int findeol(void);
void dbg(void); void printq(const char *);
void dbg(void); void printq(const char *);
extern int nodebug;
extern int nodebug;
static FILE *fp;
static FILE *fp;
static int block; static int in_define; static int errors; static int lastline = -1;
static int block; static int in_define; static int errors; static int lastline = -1;
typedef struct { union {
char *string;
char *string;
} v; int lineno; } YYSTYPE; %}
%token DEFINE ELSE END ERROR FINALLY FOR IF INCLUDE PRINTF %token RENDER TQFOREACH UNSAFE URLESCAPE WHILE %token <v.string> STRING %type <v.string> string %type <v.string> stringy
%token DEFINE ELSE END ERROR FINALLY FOR IF INCLUDE PRINTF %token RENDER TQFOREACH UNSAFE URLESCAPE WHILE %token <v.string> STRING %type <v.string> string %type <v.string> stringy
%% grammar : %empty | grammar include | grammar verbatim | grammar block | grammar error { file->errors++; } ; include : INCLUDE STRING {
struct file *nfile;
struct file *nfile;
if ((nfile = pushfile($2, 0)) == NULL) { yyerror("failed to include file %s", $2); free($2); YYERROR; } free($2); file = nfile; lungetc('\n'); } ; verbatim : '!' verbatim1 '!' { if (in_define) { /* TODO: check template status and exit in case */ } } ; verbatim1 : %empty | verbatim1 STRING { if (*$2 != '\0') { dbg(); fprintf(fp, "%s\n", $2); } free($2); } ; raw : STRING { dbg(); fprintf(fp, "htmpl::write(handle, "); printq($1); fprintf(fp, ")?;\n"); free($1); } ; block : define body end { fputs("};\n", fp); in_define = 0; } ; define : '{' DEFINE string '}' { /* TODO: Hare return */ in_define = 1; dbg(); fprintf(fp, "fn %s = {\n", $3); free($3); } ; body : %empty | body verbatim | body raw | body special ; special : '{' RENDER string '}' { dbg(); fprintf(fp, "%s?;\n", $3); free($3); } | printf | if body endif { fputs("};\n", fp); } | loop | '{' string '}' { dbg(); fprintf(fp, "htmpl::write_escape_html(handle, %s)?;\n", $2); /* TODO: quoting issues */ free($2); } | '{' string '|' UNSAFE '}' { dbg(); fprintf(fp, "htmpl::write(handle, %s)?;\n", $2); free($2); } | '{' string '|' URLESCAPE '}' { dbg(); fprintf(fp, "htmpl::write_escape_url(handle, %s)?;\n", $2); free($2); } ; printf : '{' PRINTF { dbg(); fputs("let _htmpl_tmp: str = fmt::asprintf(", fp); } printfargs '}' { fputs(");\n", fp); fputs("defer free(_htmpl_tmp);", fp); fputs("htmpl::write_escape_html(handle, _htmpl_tmp)?;\n", fp); } ; printfargs : %empty | printfargs STRING { fprintf(fp, " %s", $2); free($2); } ; if : '{' IF stringy '}' { dbg(); fprintf(fp, "if (%s) {\n", $3); free($3); } ; endif : end | else body end | elsif body endif ; elsif : '{' ELSE IF stringy '}' { dbg(); fprintf(fp, "} else if (%s) {\n", $4); free($4); } ; else : '{' ELSE '}' { dbg(); fputs("} else {\n", fp); } ; loop : '{' FOR stringy '}' { fprintf(fp, "for (%s) {\n", $3); free($3); } body end { fputs("};\n", fp); } | '{' TQFOREACH STRING STRING STRING '}' { fprintf(fp, "TAILQ_FOREACH(%s, %s, %s) {\n",
$3, $4, $5);
$3, $4, $5);
free($3); free($4); free($5); } body end { fputs("};\n", fp); } | '{' WHILE stringy '}' { fprintf(fp, "while (%s) {\n", $3); free($3); } body end { fputs("};\n", fp); } ; end : '{' END '}' ; string : STRING string { if (asprintf(&$$, "%s %s", $1, $2) == -1) err(1, "asprintf"); free($1); free($2); } | STRING ; stringy : STRING | STRING stringy { if (asprintf(&$$, "%s %s", $1, $2) == -1) err(1, "asprintf"); free($1); free($2); } ; %% struct keywords {
const char *k_name; int k_val;
const char *k_name; int k_val;
};
int yyerror(const char *fmt, ...)
int yyerror(const char *fmt, ...)
{
va_list ap; char *msg;
va_list ap; char *msg;
file->errors++; va_start(ap, fmt); if (vasprintf(&msg, fmt, ap) == -1) err(1, "yyerror vasprintf"); va_end(ap); fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg); free(msg); return (0); }
int kw_cmp(const void *k, const void *e)
int kw_cmp(const void *k, const void *e)
{ return (strcmp(k, ((const struct keywords *)e)->k_name)); }
int lookup(char *s)
int lookup(char *s)
{ /* this has to be sorted always */ static const struct keywords keywords[] = {
{ "define", DEFINE }, { "else", ELSE }, { "end", END }, { "for", FOR }, { "if", IF }, { "include", INCLUDE }, { "printf", PRINTF }, { "render", RENDER }, { "tailq-foreach", TQFOREACH }, { "unsafe", UNSAFE }, { "urlescape", URLESCAPE }, { "while", WHILE },
{ "define", DEFINE }, { "else", ELSE }, { "end", END }, { "for", FOR }, { "if", IF }, { "include", INCLUDE }, { "printf", PRINTF }, { "render", RENDER }, { "tailq-foreach", TQFOREACH }, { "unsafe", UNSAFE }, { "urlescape", URLESCAPE }, { "while", WHILE },
};
const struct keywords *p;
const struct keywords *p;
p = bsearch(s, keywords, nitems(keywords), sizeof(keywords[0]), kw_cmp);
p = bsearch(s, keywords, nitems(keywords), sizeof(keywords[0]), kw_cmp);
if (p) return (p->k_val); else return (STRING); } #define START_EXPAND 1 #define DONE_EXPAND 2
static int expanding;
static int expanding;
int igetc(void)
int igetc(void)
{
int c;
int c;
while (1) { if (file->ungetpos > 0) c = file->ungetbuf[--file->ungetpos]; else c = getc(file->stream); if (c == START_EXPAND) expanding = 1; else if (c == DONE_EXPAND) expanding = 0; else break; } return (c); }
int lgetc(int quotec)
int lgetc(int quotec)
{
int c;
int c;
if (quotec) { if ((c = igetc()) == EOF) { yyerror("reached end of filewhile parsing "
"quoted string");
"quoted string");
if (file == topfile || popfile() == EOF) return (EOF); return (quotec); } return (c); } c = igetc(); if (c == '\t' || c == ' ') { /* Compress blanks to a sigle space. */ do { c = getc(file->stream);
} while (c == '\t' || c == ' ');
} while (c == '\t' || c == ' ');
ungetc(c, file->stream); c = ' '; } if (c == EOF) { /* * Fake EOL when hit EOF for the first time. This gets line * count right if last line in included file is syntactically * invalid and has no newline. */ if (file->eof_reached == 0) { file->eof_reached = 1; return ('\n'); } while (c == EOF) { if (file == topfile || popfile() == EOF) return (EOF); c = igetc(); } } return (c); }
void lungetc(int c)
void lungetc(int c)
{ if (c == EOF) return; if (file->ungetpos >= file->ungetsize) { void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); if (p == NULL) err(1, "reallocarray"); file->ungetbuf = p; file->ungetsize *= 2; } file->ungetbuf[file->ungetpos++] = c; }
int findeol(void)
int findeol(void)
{
int c;
int c;
/* skip to either EOF or the first real EOL */ while (1) { c = lgetc(0); if (c == '\n') { file->lineno++; break; } if (c == EOF) break; } return (ERROR); }
int yylex(void)
int yylex(void)
{
char buf[8096]; char *p = buf; int c; int token; int starting = 0; int ending = 0; int quote = 0;
char buf[8096]; char *p = buf; int c; int token; int starting = 0; int ending = 0; int quote = 0;
if (!in_define && block == 0) { while ((c = lgetc(0)) != '{' && c != EOF) { if (c == '\n') file->lineno++; } if (c == EOF) return (0); newblock: c = lgetc(0); if (c == '{' || c == '!') { if (c == '{') block = '}'; else block = c; return (c); } if (c == '\n') file->lineno++; } while ((c = lgetc(0)) == ' ' || c == '\t' || c == '\n') { if (c == '\n') file->lineno++; } if (c == EOF) { yyerror("unterminated block"); return (0); } yylval.lineno = file->lineno; if (block != 0 && c == block) { if ((c = lgetc(0)) == '}') { if (block == '!') { block = 0; return ('!'); } block = 0; return ('}'); } lungetc(c); c = block; } if (in_define && block == 0) { if (c == '{') goto newblock; do { if (starting) { if (c == '!' || c == '{') { lungetc(c); lungetc('{'); break; } starting = 0; lungetc(c); c = '{'; } else if (c == '{') { starting = 1; continue; } else if (c == '\n') break; *p++ = c; if ((size_t)(p - buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } } while ((c = lgetc(0)) != EOF); *p = '\0'; if (c == EOF) { yyerror("unterminated block"); return (0); } if (c == '\n') file->lineno++; if ((yylval.v.string = strdup(buf)) == NULL) err(1, "strdup"); return (STRING); } if (block == '!') { do { if (ending) { if (c == '}') { lungetc(c); lungetc(block); break; } ending = 0; lungetc(c); c = block; } else if (c == '!') { ending = 1; continue; } else if (c == '\n') break; *p++ = c; if ((size_t)(p - buf) >= sizeof(buf)) { yyerror("line too long"); return (findeol()); } } while ((c = lgetc(0)) != EOF); *p = '\0'; if (c == EOF) { yyerror("unterminated block"); return (0); } if (c == '\n') file->lineno++; if ((yylval.v.string = strdup(buf)) == NULL) err(1, "strdup"); return (STRING); } do { if (!quote && isspace((unsigned char)c)) break; if (c == '"') quote = !quote; if (ending) { if (c == '}') { lungetc(c); lungetc('}'); break; } ending = 0; lungetc(c); c = block; } else if (!quote && c == '}') { ending = 1; continue; } *p++ = c; if ((size_t)(p - buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } } while ((c = lgetc(0)) != EOF); *p = '\0'; if (c == EOF) { yyerror(quote ? "unterminated quote" : "unterminated block"); return (0); } if (c == '\n') file->lineno++; if ((token = lookup(buf)) == STRING) if ((yylval.v.string = strdup(buf)) == NULL) err(1, "strdup"); return (token); }
struct file * pushfile(const char *name, int secret)
struct file *pushfile(const char *name, int secret)
{
struct file *nfile;
struct file *nfile;
if ((nfile = calloc(1, sizeof(*nfile))) == NULL) err(1, "calloc"); if ((nfile->name = strdup(name)) == NULL) err(1, "strdup"); if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { warn("can't open %s", nfile->name); free(nfile->name); free(nfile); return (NULL); } nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; nfile->ungetsize = 16; nfile->ungetbuf = malloc(nfile->ungetsize); if (nfile->ungetbuf == NULL) err(1, "malloc"); TAILQ_INSERT_TAIL(&files, nfile, entry); return (nfile); }
int popfile(void)
int popfile(void)
{
struct file *prev;
struct file *prev;
if ((prev = TAILQ_PREV(file, files, entry)) != NULL) prev->errors += file->errors; TAILQ_REMOVE(&files, file, entry); fclose(file->stream); free(file->name); free(file->ungetbuf); free(file); file = prev; return (file ? 0 : EOF); }
int parse(FILE *outfile, const char *filename) {
int parse(FILE *outfile, const char *filename) {
fp = outfile; if ((file = pushfile(filename, 0)) == 0) return (-1); topfile = file; yyparse(); errors = file->errors; popfile(); return (errors ? -1 : 0); }
void dbg(void) {
void dbg(void) {
if (nodebug) return; if (yylval.lineno == lastline + 1) { lastline = yylval.lineno; return; } lastline = yylval.lineno; // fprintf(fp, "#line %d ", yylval.lineno); // printq(file->name); // putc('\n', fp); // TODO: Removed for now because #line doesn't exist in Hare } /* Print a string in a form appropriate for raw inclusion into a Hare program. */
void printq(const char *str) {
void printq(const char *str) {
putc('"', fp); for (; *str; ++str) { if (*str == '"') putc('\\', fp); putc(*str, fp); } putc('"', fp); }