Compare commits
10 Commits
f680fe177d
...
4b23432cf4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b23432cf4 | ||
|
|
0db57614cd | ||
|
|
561ad55a60 | ||
|
|
ba55ebd057 | ||
|
|
4f512c6503 | ||
|
|
817271a1bb | ||
|
|
fd07dfd962 | ||
|
|
254a644f41 | ||
|
|
bd8ae9c180 | ||
|
|
72c117b388 |
28
.github/workflows/docker.yml
vendored
28
.github/workflows/docker.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
name: Docker Hub
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set login
|
|
||||||
uses: azure/docker-login@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Build image
|
|
||||||
working-directory: ${{github.workspace}}
|
|
||||||
run: ./build.sh
|
|
||||||
|
|
||||||
- name: Push image
|
|
||||||
run: docker push natechoe/ncdg
|
|
||||||
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
|||||||
SRC = $(wildcard src/*.c)
|
SRC = $(wildcard src/*.c)
|
||||||
OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
|
OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
|
||||||
LDFLAGS =
|
LDFLAGS =
|
||||||
CFLAGS := -O2 -pipe -Wall -Wpedantic -Wshadow -ansi
|
CFLAGS := -pipe -Wall -Wpedantic -Wshadow -ansi -ggdb
|
||||||
CFLAGS += -Isrc/include/
|
CFLAGS += -Isrc/include/
|
||||||
INSTALLDIR := /usr/bin/
|
INSTALLDIR := /usr/bin/
|
||||||
OUT = ncdg
|
OUT = ncdg
|
||||||
|
|||||||
@@ -178,9 +178,8 @@ Turns into
|
|||||||
!dlrow olleH
|
!dlrow olleH
|
||||||
```
|
```
|
||||||
|
|
||||||
Things between an `@n` and `@m` tag get processed twice. A double escape char `@@`
|
Things between an `@n` and `@m` tag get processed twice. Defining variables in a
|
||||||
gets escaped into a single escape char `@`. Defining variables in a nest is
|
nest is illegal, so
|
||||||
illegal, so
|
|
||||||
|
|
||||||
```
|
```
|
||||||
@n @@= dynamic_variable @$./generate_variable_value.sh@ @@ @m
|
@n @@= dynamic_variable @$./generate_variable_value.sh@ @@ @m
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#define NOMINIFY_CHAR '&'
|
#define NOMINIFY_CHAR '&'
|
||||||
#define SEPARATOR_CHAR ','
|
#define SEPARATOR_CHAR ','
|
||||||
#define SHELL_CHAR '$'
|
#define SHELL_CHAR '$'
|
||||||
|
#define NEST_START 'n'
|
||||||
|
#define NEST_END 'm'
|
||||||
|
|
||||||
#define ALLOW_SHELL
|
#define ALLOW_SHELL
|
||||||
|
|
||||||
|
|||||||
16
src/include/ncdgfile.h
Normal file
16
src/include/ncdgfile.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef HAVE_NCDGFILE
|
||||||
|
#define HAVE_NCDGFILE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct ncdgfile {
|
||||||
|
int (*putc)(struct ncdgfile *file, int c);
|
||||||
|
int (*puts)(struct ncdgfile *file, char *s);
|
||||||
|
void (*free)(struct ncdgfile *file);
|
||||||
|
void *handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ncdgfile *file2ncdg(FILE *file);
|
||||||
|
struct ncdgfile *stringfile(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
74
src/ncdgfile.c
Normal file
74
src/ncdgfile.c
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <strings.h>
|
||||||
|
#include <ncdgfile.h>
|
||||||
|
|
||||||
|
static int fileputc(struct ncdgfile *file, int c);
|
||||||
|
static int fileputs(struct ncdgfile *file, char *s);
|
||||||
|
static void filefree(struct ncdgfile *file);
|
||||||
|
|
||||||
|
static int stringputc(struct ncdgfile *file, int c);
|
||||||
|
static int stringputs(struct ncdgfile *file, char *s);
|
||||||
|
static void stringfree(struct ncdgfile *file);
|
||||||
|
|
||||||
|
struct ncdgfile *file2ncdg(FILE *file) {
|
||||||
|
struct ncdgfile *ret;
|
||||||
|
if ((ret = malloc(sizeof *ret)) == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ret->putc = fileputc;
|
||||||
|
ret->puts = fileputs;
|
||||||
|
ret->free = filefree;
|
||||||
|
ret->handle = (void *) file;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ncdgfile *stringfile(void) {
|
||||||
|
struct ncdgfile *ret;
|
||||||
|
if ((ret = malloc(sizeof *ret)) == NULL) {
|
||||||
|
goto error1;
|
||||||
|
}
|
||||||
|
if ((ret->handle = (void *) newstring()) == NULL) {
|
||||||
|
goto error2;
|
||||||
|
}
|
||||||
|
ret->putc = stringputc;
|
||||||
|
ret->puts = stringputs;
|
||||||
|
ret->free = stringfree;
|
||||||
|
return ret;
|
||||||
|
error2:
|
||||||
|
free(ret);
|
||||||
|
error1:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fileputc(struct ncdgfile *file, int c) {
|
||||||
|
return fputc(c, (FILE *) file->handle) == EOF ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fileputs(struct ncdgfile *file, char *s) {
|
||||||
|
return fputs(s, (FILE *) file->handle) == EOF ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void filefree(struct ncdgfile *file) {
|
||||||
|
free(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stringputc(struct ncdgfile *file, int c) {
|
||||||
|
return appendchar((struct string *) file->handle, c) ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stringputs(struct ncdgfile *file, char *s) {
|
||||||
|
long i;
|
||||||
|
for (i = 0; s[i] != '\0'; ++i) {
|
||||||
|
if (appendchar((struct string *) file->handle, s[i])) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stringfree(struct ncdgfile *file) {
|
||||||
|
freestring((struct string *) file->handle);
|
||||||
|
free(file);
|
||||||
|
}
|
||||||
178
src/parse.c
178
src/parse.c
@@ -11,6 +11,7 @@
|
|||||||
#include <parse.h>
|
#include <parse.h>
|
||||||
#include <vector.h>
|
#include <vector.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
#include <ncdgfile.h>
|
||||||
|
|
||||||
struct var {
|
struct var {
|
||||||
struct string *var;
|
struct string *var;
|
||||||
@@ -31,38 +32,44 @@ struct minstate {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int expandfile(struct expandfile *ret, char *filename, int level);
|
static int expandfile(struct expandfile *ret, char *filename, int level);
|
||||||
static int writefile(struct expandfile *file, FILE *out);
|
static int writefile(struct expandfile *file, struct ncdgfile *out);
|
||||||
static struct string *getstring(FILE *file, char end);
|
static struct string *getstring(FILE *file, char end);
|
||||||
static void initminstate(struct minstate *state);
|
static void initminstate(struct minstate *state);
|
||||||
static void mputs(struct minstate *state, char *s, FILE *file);
|
static void mputs(struct minstate *state, char *s, struct ncdgfile *file);
|
||||||
static void mputc(struct minstate *state, char c, FILE *file);
|
static void mputc(struct minstate *state, char c, struct ncdgfile *file);
|
||||||
static long putvar(long i, struct minstate *s, FILE *out,
|
static long putvar(long i, struct minstate *s, struct ncdgfile *out,
|
||||||
const struct string *file, const struct vector *vars);
|
const struct string *file, const struct vector *vars);
|
||||||
static int defvars(struct expandfile *expanded, char *filename);
|
static int defvars(struct expandfile *expanded, char *filename);
|
||||||
|
static long count_escapes(struct string *string);
|
||||||
|
|
||||||
int parsefile(char *template, FILE *out) {
|
int parsefile(char *template, FILE *out) {
|
||||||
struct expandfile expanded;
|
struct expandfile expanded;
|
||||||
int ret;
|
int ret;
|
||||||
|
struct ncdgfile *ncdgout;
|
||||||
|
if ((ncdgout = file2ncdg(out)) == NULL) {
|
||||||
|
ret = 1;
|
||||||
|
goto error1;
|
||||||
|
}
|
||||||
expanded.data = newstring();
|
expanded.data = newstring();
|
||||||
if (expanded.data == NULL) {
|
if (expanded.data == NULL) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto error1;
|
goto error2;
|
||||||
}
|
}
|
||||||
expanded.vars = newvector(struct var);
|
expanded.vars = newvector(struct var);
|
||||||
if (expanded.vars == NULL) {
|
if (expanded.vars == NULL) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto error2;
|
goto error3;
|
||||||
}
|
}
|
||||||
if (defvars(&expanded, template)) {
|
if (defvars(&expanded, template)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto error3;
|
goto error4;
|
||||||
}
|
}
|
||||||
if (expandfile(&expanded, template, 0)) {
|
if (expandfile(&expanded, template, 0)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto error3;
|
goto error4;
|
||||||
}
|
}
|
||||||
ret = writefile(&expanded, out);
|
ret = writefile(&expanded, ncdgout);
|
||||||
error3:
|
error4:
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < expanded.vars->len; ++i) {
|
for (i = 0; i < expanded.vars->len; ++i) {
|
||||||
@@ -73,15 +80,21 @@ error3:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
freevector(expanded.vars);
|
freevector(expanded.vars);
|
||||||
error2:
|
error3:
|
||||||
freestring(expanded.data);
|
freestring(expanded.data);
|
||||||
|
error2:
|
||||||
|
ncdgout->free(ncdgout);
|
||||||
error1:
|
error1:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int writefile(struct expandfile *file, FILE *out) {
|
static int writefile(struct expandfile *file, struct ncdgfile *out) {
|
||||||
long i;
|
long i;
|
||||||
struct minstate s;
|
struct minstate s;
|
||||||
|
if (count_escapes(file->data) % 2 != 0) {
|
||||||
|
fputs("File or nest has an odd number of escape chars\n", stderr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
initminstate(&s);
|
initminstate(&s);
|
||||||
for (i = 0; i < file->data->len; ++i) {
|
for (i = 0; i < file->data->len; ++i) {
|
||||||
if (file->data->data[i] == ESCAPE_CHAR) {
|
if (file->data->data[i] == ESCAPE_CHAR) {
|
||||||
@@ -102,16 +115,16 @@ static int writefile(struct expandfile *file, FILE *out) {
|
|||||||
for (++i; i < data->len; ++i) {
|
for (++i; i < data->len; ++i) {
|
||||||
switch (data->data[i]) {
|
switch (data->data[i]) {
|
||||||
case '&':
|
case '&':
|
||||||
fputs("&", out);
|
out->puts(out, "&");
|
||||||
break;
|
break;
|
||||||
case ';':
|
case ';':
|
||||||
fputs(";", out);
|
out->puts(out, ";");
|
||||||
break;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
fputs("<", out);
|
out->puts(out, "<");
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
fputs(">", out);
|
out->puts(out, ">");
|
||||||
break;
|
break;
|
||||||
case ESCAPE_CHAR:
|
case ESCAPE_CHAR:
|
||||||
if (data->data[i + 1] != ESCAPE_CHAR)
|
if (data->data[i + 1] != ESCAPE_CHAR)
|
||||||
@@ -119,7 +132,7 @@ static int writefile(struct expandfile *file, FILE *out) {
|
|||||||
++i;
|
++i;
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
default:
|
default:
|
||||||
fputc(data->data[i], out);
|
out->putc(out, data->data[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +147,7 @@ autoescapeend:
|
|||||||
break;
|
break;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
fputc(data->data[i], out);
|
out->putc(out, data->data[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
#ifdef ALLOW_SHELL
|
#ifdef ALLOW_SHELL
|
||||||
@@ -157,8 +170,81 @@ autoescapeend:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
case NEST_START: {
|
||||||
|
struct ncdgfile *tmp;
|
||||||
|
struct string *buff;
|
||||||
|
struct expandfile nest;
|
||||||
|
int in_escape;
|
||||||
|
if ((buff = newstring()) == NULL) {
|
||||||
|
goto bufferror;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_escape = 0;
|
||||||
|
|
||||||
|
++i;
|
||||||
|
|
||||||
|
/* read nest data into a string */
|
||||||
|
while (i < data->len) {
|
||||||
|
int c;
|
||||||
|
c = data->data[i++];
|
||||||
|
if (c != ESCAPE_CHAR) {
|
||||||
|
appendchar(buff, c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (in_escape) {
|
||||||
|
appendchar(buff, c);
|
||||||
|
in_escape = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i >= data->len) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c = data->data[i++];
|
||||||
|
switch (c) {
|
||||||
|
case NEST_END:
|
||||||
|
goto got_nest;
|
||||||
|
default:
|
||||||
|
in_escape = 1;
|
||||||
|
case ESCAPE_CHAR:
|
||||||
|
appendchar(buff, ESCAPE_CHAR);
|
||||||
|
appendchar(buff, c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fputs("Unexpected EOF in nest\n", stderr);
|
||||||
|
return 1;
|
||||||
|
got_nest:
|
||||||
|
--i;
|
||||||
|
if ((tmp = stringfile()) == NULL) {
|
||||||
|
goto bufferror;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* first pass */
|
||||||
|
nest.data = buff;
|
||||||
|
nest.vars = file->vars;
|
||||||
|
if (writefile(&nest, tmp)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
freestring(buff);
|
||||||
|
|
||||||
|
/* second pass */
|
||||||
|
nest.data = (struct string *) tmp->handle;
|
||||||
|
if (writefile(&nest, out)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
tmp->free(tmp);
|
||||||
|
|
||||||
|
break;
|
||||||
|
bufferror:
|
||||||
|
fputs("Failed to create temporary buffer while nesting\n", stderr);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case NEST_END:
|
||||||
|
fputs("Error in expansion phase: Unmatched nest end char\n", stderr);
|
||||||
|
return 1;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Error in expansion phase: Unknown escape %c%c\n", ESCAPE_CHAR, data->data[i]);
|
fprintf(stderr, "Error in expansion phase: Unknown escape '%c' (0x%x)\n",
|
||||||
|
data->data[i], data->data[i]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,6 +326,39 @@ static int expandfile(struct expandfile *ret, char *filename, int level) {
|
|||||||
freestring(inclname);
|
freestring(inclname);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NEST_START: {
|
||||||
|
int origline = linenum;
|
||||||
|
appendchar(ret->data, ESCAPE_CHAR);
|
||||||
|
appendchar(ret->data, NEST_START);
|
||||||
|
for (;;) {
|
||||||
|
c = fgetc(file);
|
||||||
|
if (c == EOF) {
|
||||||
|
goto no_nest_end;
|
||||||
|
}
|
||||||
|
if (c != ESCAPE_CHAR) {
|
||||||
|
appendchar(ret->data, c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
appendchar(ret->data, c);
|
||||||
|
c = fgetc(file);
|
||||||
|
if (c == NEST_END) {
|
||||||
|
appendchar(ret->data, c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ungetc(c, file);
|
||||||
|
do {
|
||||||
|
c = fgetc(file);
|
||||||
|
appendchar(ret->data, c);
|
||||||
|
} while (c != ESCAPE_CHAR);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
no_nest_end:
|
||||||
|
fprintf(stderr, "Line %d: Unmatched nest start char\n", origline);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
case NEST_END:
|
||||||
|
fprintf(stderr, "Line %d: Unmatched nest end\n", linenum);
|
||||||
|
goto error;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Line %d: Invalid escape %c\n",
|
fprintf(stderr, "Line %d: Invalid escape %c\n",
|
||||||
linenum, c);
|
linenum, c);
|
||||||
@@ -292,25 +411,25 @@ static void initminstate(struct minstate *state) {
|
|||||||
memset(state, 0, sizeof *state);
|
memset(state, 0, sizeof *state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mputs(struct minstate *state, char *s, FILE *file) {
|
static void mputs(struct minstate *state, char *s, struct ncdgfile *file) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; s[i] != '\0'; ++i)
|
for (i = 0; s[i] != '\0'; ++i)
|
||||||
mputc(state, s[i], file);
|
mputc(state, s[i], file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mputc(struct minstate *state, char c, FILE *file) {
|
static void mputc(struct minstate *state, char c, struct ncdgfile *file) {
|
||||||
if (isspace(c))
|
if (isspace(c))
|
||||||
state->isspace = 1;
|
state->isspace = 1;
|
||||||
else {
|
else {
|
||||||
if (!state->ignore && state->isspace && c != '>')
|
if (!state->ignore && state->isspace && c != '>')
|
||||||
fputc(' ', file);
|
file->putc(file, ' ');
|
||||||
fputc(c, file);
|
file->putc(file, c);
|
||||||
state->ignore = c == '<';
|
state->ignore = c == '<';
|
||||||
state->isspace = 0;
|
state->isspace = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static long putvar(long i, struct minstate *s, FILE *out,
|
static long putvar(long i, struct minstate *s, struct ncdgfile *out,
|
||||||
const struct string *file, const struct vector *vars) {
|
const struct string *file, const struct vector *vars) {
|
||||||
long start;
|
long start;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -363,3 +482,14 @@ error2:
|
|||||||
error1:
|
error1:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long count_escapes(struct string *string) {
|
||||||
|
long i, ret;
|
||||||
|
ret = 0;
|
||||||
|
for (i = 0; i < string->len; ++i) {
|
||||||
|
if (string->data[i] == ESCAPE_CHAR) {
|
||||||
|
++ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user