Added variable precedence
This commit is contained in:
27
README.md
27
README.md
@@ -92,12 +92,29 @@ Result:
|
|||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A variable expansion can go through multiple variables until one is found.
|
||||||
|
|
||||||
|
file.html
|
||||||
|
|
||||||
|
```
|
||||||
|
@!var1,var2,var3@
|
||||||
|
@=var2 value@
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```
|
||||||
|
value
|
||||||
|
```
|
||||||
|
|
||||||
|
var1 doesn't exist, so ncdg moves on to var2, which does exist, and skips var3.
|
||||||
|
|
||||||
### Automatic escaping
|
### Automatic escaping
|
||||||
|
|
||||||
```
|
```
|
||||||
<pre><code>%\
|
<pre><code>@\
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
%</code></pre>
|
@</code></pre>
|
||||||
```
|
```
|
||||||
|
|
||||||
Result:
|
Result:
|
||||||
@@ -112,15 +129,17 @@ Note that text inside of escaped sections are not minified.
|
|||||||
### Excluding minification
|
### Excluding minification
|
||||||
|
|
||||||
```
|
```
|
||||||
%&
|
@&
|
||||||
&
|
&
|
||||||
%
|
this text isn't minified
|
||||||
|
@
|
||||||
```
|
```
|
||||||
|
|
||||||
Result:
|
Result:
|
||||||
|
|
||||||
```
|
```
|
||||||
&
|
&
|
||||||
|
this text isn't minified
|
||||||
```
|
```
|
||||||
|
|
||||||
Used for legacy web pages on my site that I don't want to update
|
Used for legacy web pages on my site that I don't want to update
|
||||||
|
|||||||
297
src/'
Normal file
297
src/'
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <parse.h>
|
||||||
|
#include <vector.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
struct var {
|
||||||
|
struct string *var;
|
||||||
|
struct string *value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct expandfile {
|
||||||
|
struct string *data;
|
||||||
|
struct vector *vars;
|
||||||
|
/* This is a vector of struct var */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct minstate {
|
||||||
|
int ignore;
|
||||||
|
/* If this is set, then ignore the current whitespace group */
|
||||||
|
int isspace;
|
||||||
|
/* If this is set, then we've seen whitespace on the previous char. */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int expandfile(struct expandfile *ret, char *filename, int level);
|
||||||
|
static int writefile(struct expandfile *file, FILE *out);
|
||||||
|
static struct string *getstring(FILE *file, char end);
|
||||||
|
static void initminstate(struct minstate *state);
|
||||||
|
static void mputs(struct minstate *state, char *s, FILE *file);
|
||||||
|
static void mputc(struct minstate *state, char c, FILE *file);
|
||||||
|
static int putvar(int i, const struct string *file, const struct vector *vars);
|
||||||
|
|
||||||
|
int parsefile(char *template, FILE *out) {
|
||||||
|
struct expandfile expanded;
|
||||||
|
int ret;
|
||||||
|
expanded.data = newstring();
|
||||||
|
if (expanded.data == NULL) {
|
||||||
|
ret = 1;
|
||||||
|
goto error1;
|
||||||
|
}
|
||||||
|
expanded.vars = newvector(struct var);
|
||||||
|
if (expanded.vars == NULL) {
|
||||||
|
ret = 1;
|
||||||
|
goto error2;
|
||||||
|
}
|
||||||
|
if (expandfile(&expanded, template, 0)) {
|
||||||
|
ret = 1;
|
||||||
|
goto error3;
|
||||||
|
}
|
||||||
|
ret = writefile(&expanded, out);
|
||||||
|
error3:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < expanded.vars->len; ++i) {
|
||||||
|
struct var var;
|
||||||
|
var = getvector(expanded.vars, struct var, i);
|
||||||
|
freestring(var.var);
|
||||||
|
freestring(var.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freevector(expanded.vars);
|
||||||
|
error2:
|
||||||
|
freestring(expanded.data);
|
||||||
|
error1:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int writefile(struct expandfile *file, FILE *out) {
|
||||||
|
long i;
|
||||||
|
struct minstate s;
|
||||||
|
initminstate(&s);
|
||||||
|
for (i = 0; i < file->data->len; ++i) {
|
||||||
|
if (file->data->data[i] == ESCAPE_CHAR) {
|
||||||
|
const struct string *data = file->data;
|
||||||
|
const struct vector *vars = file->vars;
|
||||||
|
switch (data->data[++i]) {
|
||||||
|
case ESCAPE_CHAR:
|
||||||
|
mputc(&s, ESCAPE_CHAR, out);
|
||||||
|
break;
|
||||||
|
case VAR_CHAR: {
|
||||||
|
i = putvar(i, data, vars);
|
||||||
|
if (i < 0)
|
||||||
|
goto error;
|
||||||
|
long start;
|
||||||
|
int j;
|
||||||
|
char *varname;
|
||||||
|
start = ++i;
|
||||||
|
while (data->data[i] != ESCAPE_CHAR &&
|
||||||
|
i < data->len)
|
||||||
|
++i;
|
||||||
|
data->data[i] = '\0';
|
||||||
|
varname = data->data + start;
|
||||||
|
vars = file->vars;
|
||||||
|
for (j = 0; j < vars->len; ++j) {
|
||||||
|
struct var var;
|
||||||
|
var = getvector(vars,
|
||||||
|
struct var, j);
|
||||||
|
if (strcmp(var.var->data,
|
||||||
|
varname) == 0) {
|
||||||
|
mputs(&s, var.value->data,
|
||||||
|
out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AUTOESCAPE_CHAR:
|
||||||
|
for (++i; i < data->len; ++i) {
|
||||||
|
switch (data->data[i]) {
|
||||||
|
case '&':
|
||||||
|
fputs("&", out);
|
||||||
|
break;
|
||||||
|
case ';':
|
||||||
|
fputs(";", out);
|
||||||
|
case '<':
|
||||||
|
fputs("<", out);
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
fputs(">", out);
|
||||||
|
break;
|
||||||
|
case ESCAPE_CHAR:
|
||||||
|
if (data->data[i + 1] != ESCAPE_CHAR)
|
||||||
|
goto autoescapeend;
|
||||||
|
++i;
|
||||||
|
/* fallthrough */
|
||||||
|
default:
|
||||||
|
fputc(data->data[i], out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
autoescapeend:
|
||||||
|
break;
|
||||||
|
case NOMINIFY_CHAR:
|
||||||
|
for (++i; data->data[i] != ESCAPE_CHAR &&
|
||||||
|
i < data->len; ++i) {
|
||||||
|
if (data->data[i] == ESCAPE_CHAR) {
|
||||||
|
if (data->data[i + 1] != ESCAPE_CHAR)
|
||||||
|
break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
fputc(data->data[i], out);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mputc(&s, file->data->data[i], out);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int expandfile(struct expandfile *ret, char *filename, int level) {
|
||||||
|
FILE *file;
|
||||||
|
int c, linenum;
|
||||||
|
file = fopen(filename, "r");
|
||||||
|
if (file == NULL) {
|
||||||
|
fprintf(stderr, "Failed to open file %s\n", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
linenum = 1;
|
||||||
|
if (level >= MAX_INCLUDE_DEPTH) {
|
||||||
|
|
||||||
|
fprintf(stderr, "The include depth has reached %d, quitting\n",
|
||||||
|
level);
|
||||||
|
fclose(file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
c = fgetc(file);
|
||||||
|
switch (c) {
|
||||||
|
case ESCAPE_CHAR:
|
||||||
|
c = fgetc(file);
|
||||||
|
switch (c) {
|
||||||
|
case ESCAPE_CHAR:
|
||||||
|
if (appendchar(ret->data, ESCAPE_CHAR))
|
||||||
|
goto error;
|
||||||
|
if (appendchar(ret->data, ESCAPE_CHAR))
|
||||||
|
goto error;
|
||||||
|
break;
|
||||||
|
case VAR_CHAR: case AUTOESCAPE_CHAR: case NOMINIFY_CHAR:
|
||||||
|
if (appendchar(ret->data, ESCAPE_CHAR))
|
||||||
|
goto error;
|
||||||
|
for (;;) {
|
||||||
|
if (c == EOF)
|
||||||
|
goto error;
|
||||||
|
if (appendchar(ret->data, c))
|
||||||
|
goto error;
|
||||||
|
if (c == '\n')
|
||||||
|
++linenum;
|
||||||
|
if (c == ESCAPE_CHAR) {
|
||||||
|
c = fgetc(file);
|
||||||
|
if (c == ESCAPE_CHAR) {
|
||||||
|
if (appendchar(
|
||||||
|
ret->data, c))
|
||||||
|
goto error;
|
||||||
|
if (appendchar(
|
||||||
|
ret->data, c))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ungetc(c, file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c = fgetc(file);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SET_CHAR: {
|
||||||
|
struct var var;
|
||||||
|
var.var = getstring(file, ' ');
|
||||||
|
var.value = getstring(file, ESCAPE_CHAR);
|
||||||
|
addvector(ret->vars, &var);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INCLUDE_CHAR: {
|
||||||
|
struct string *inclname;
|
||||||
|
inclname = getstring(file, ESCAPE_CHAR);
|
||||||
|
if (inclname == NULL)
|
||||||
|
goto error;
|
||||||
|
if (expandfile(ret, inclname->data,
|
||||||
|
level + 1))
|
||||||
|
return 1;
|
||||||
|
freestring(inclname);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Line %d: Invalid escape %c\n",
|
||||||
|
linenum, c);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
++linenum;
|
||||||
|
goto casedefault;
|
||||||
|
case EOF:
|
||||||
|
goto end;
|
||||||
|
default: casedefault:
|
||||||
|
if (appendchar(ret->data, c))
|
||||||
|
goto error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
fclose(file);
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
fclose(file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct string *getstring(FILE *file, char end) {
|
||||||
|
struct string *ret;
|
||||||
|
int c;
|
||||||
|
ret = newstring();
|
||||||
|
if (ret == NULL)
|
||||||
|
return NULL;
|
||||||
|
for (;;) {
|
||||||
|
c = fgetc(file);
|
||||||
|
if (c == EOF)
|
||||||
|
goto error;
|
||||||
|
if (c == end) {
|
||||||
|
if (appendchar(ret, '\0'))
|
||||||
|
goto error;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (appendchar(ret, c))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
error:
|
||||||
|
freestring(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initminstate(struct minstate *state) {
|
||||||
|
memset(state, 0, sizeof *state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mputs(struct minstate *state, char *s, FILE *file) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; s[i] != '\0'; ++i)
|
||||||
|
mputc(state, s[i], file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mputc(struct minstate *state, char c, FILE *file) {
|
||||||
|
if (isspace(c))
|
||||||
|
state->isspace = 1;
|
||||||
|
else {
|
||||||
|
if (!state->ignore && state->isspace && c != '>')
|
||||||
|
fputc(' ', file);
|
||||||
|
fputc(c, file);
|
||||||
|
state->ignore = c == '<';
|
||||||
|
state->isspace = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,5 +4,6 @@
|
|||||||
#define SET_CHAR '='
|
#define SET_CHAR '='
|
||||||
#define AUTOESCAPE_CHAR '\\'
|
#define AUTOESCAPE_CHAR '\\'
|
||||||
#define NOMINIFY_CHAR '&'
|
#define NOMINIFY_CHAR '&'
|
||||||
|
#define SEPARATOR_CHAR ','
|
||||||
|
|
||||||
#define MAX_INCLUDE_DEPTH 10
|
#define MAX_INCLUDE_DEPTH 10
|
||||||
|
|||||||
63
src/parse.c
63
src/parse.c
@@ -32,6 +32,8 @@ 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, FILE *file);
|
||||||
static void mputc(struct minstate *state, char c, FILE *file);
|
static void mputc(struct minstate *state, char c, FILE *file);
|
||||||
|
static long putvar(long i, struct minstate *s, FILE *out,
|
||||||
|
const struct string *file, const struct vector *vars);
|
||||||
|
|
||||||
int parsefile(char *template, FILE *out) {
|
int parsefile(char *template, FILE *out) {
|
||||||
struct expandfile expanded;
|
struct expandfile expanded;
|
||||||
@@ -80,29 +82,11 @@ static int writefile(struct expandfile *file, FILE *out) {
|
|||||||
case ESCAPE_CHAR:
|
case ESCAPE_CHAR:
|
||||||
mputc(&s, ESCAPE_CHAR, out);
|
mputc(&s, ESCAPE_CHAR, out);
|
||||||
break;
|
break;
|
||||||
case VAR_CHAR: {
|
case VAR_CHAR:
|
||||||
long start;
|
i = putvar(i, &s, out, data, vars);
|
||||||
int j;
|
if (i < 0)
|
||||||
char *varname;
|
return 1;
|
||||||
start = ++i;
|
|
||||||
while (data->data[i] != ESCAPE_CHAR &&
|
|
||||||
i < data->len)
|
|
||||||
++i;
|
|
||||||
data->data[i] = '\0';
|
|
||||||
varname = data->data + start;
|
|
||||||
vars = file->vars;
|
|
||||||
for (j = 0; j < vars->len; ++j) {
|
|
||||||
struct var var;
|
|
||||||
var = getvector(vars,
|
|
||||||
struct var, j);
|
|
||||||
if (strcmp(var.var->data,
|
|
||||||
varname) == 0) {
|
|
||||||
mputs(&s, var.value->data,
|
|
||||||
out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case AUTOESCAPE_CHAR:
|
case AUTOESCAPE_CHAR:
|
||||||
for (++i; i < data->len; ++i) {
|
for (++i; i < data->len; ++i) {
|
||||||
switch (data->data[i]) {
|
switch (data->data[i]) {
|
||||||
@@ -130,7 +114,8 @@ static int writefile(struct expandfile *file, FILE *out) {
|
|||||||
autoescapeend:
|
autoescapeend:
|
||||||
break;
|
break;
|
||||||
case NOMINIFY_CHAR:
|
case NOMINIFY_CHAR:
|
||||||
for (++i; data->data[i] != ESCAPE_CHAR && i < data->len; ++i) {
|
for (++i; data->data[i] != ESCAPE_CHAR &&
|
||||||
|
i < data->len; ++i) {
|
||||||
if (data->data[i] == ESCAPE_CHAR) {
|
if (data->data[i] == ESCAPE_CHAR) {
|
||||||
if (data->data[i + 1] != ESCAPE_CHAR)
|
if (data->data[i + 1] != ESCAPE_CHAR)
|
||||||
break;
|
break;
|
||||||
@@ -290,3 +275,35 @@ static void mputc(struct minstate *state, char c, FILE *file) {
|
|||||||
state->isspace = 0;
|
state->isspace = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long putvar(long i, struct minstate *s, FILE *out,
|
||||||
|
const struct string *file, const struct vector *vars) {
|
||||||
|
long start;
|
||||||
|
for (;;) {
|
||||||
|
int j;
|
||||||
|
start = ++i;
|
||||||
|
while (file->data[i] != SEPARATOR_CHAR &&
|
||||||
|
file->data[i] != ESCAPE_CHAR &&
|
||||||
|
i < file->len)
|
||||||
|
++i;
|
||||||
|
if (i >= file->len)
|
||||||
|
return -1;
|
||||||
|
for (j = 0; j < vars->len; ++j) {
|
||||||
|
const struct var var = getvector(vars, struct var, j);
|
||||||
|
if (var.var->len - 1 != i - start)
|
||||||
|
continue;
|
||||||
|
/* -1 because var.var->len includes the \0. */
|
||||||
|
if (memcmp(var.var->data, file->data + start,
|
||||||
|
i - start) == 0) {
|
||||||
|
mputs(s, var.value->data, out);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
while (file->data[i] != ESCAPE_CHAR && i < file->len)
|
||||||
|
++i;
|
||||||
|
if (i == file->len)
|
||||||
|
return -1;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user