274 lines
6.7 KiB
C
274 lines
6.7 KiB
C
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "shmd.h"
|
|
|
|
const char *argv0;
|
|
char *sh_prefix;
|
|
|
|
char *header_process_extract_field(FILE *fp)
|
|
{
|
|
char c;
|
|
size_t dest_size = 100;
|
|
char *dest = STR_EALLOC(dest_size);
|
|
|
|
bool is_escaped = false,
|
|
is_dquoted = false,
|
|
is_squoted = false;
|
|
while (
|
|
(c = fgetc(fp)) != '\0' && !(
|
|
!is_dquoted
|
|
&& !is_squoted
|
|
&& !is_escaped
|
|
&& (c == ' ' || c == '\n')
|
|
)
|
|
) {
|
|
if (!is_escaped) {
|
|
if (c == '"' && !is_squoted) {
|
|
is_dquoted = !is_dquoted;
|
|
continue;
|
|
} else if (c == '\'' && !is_dquoted) {
|
|
is_squoted = !is_squoted;
|
|
continue;
|
|
} else if (c == '\\') {
|
|
is_escaped = true;
|
|
continue;
|
|
}
|
|
}
|
|
dest_size = str_pushc(dest, c, dest_size, 50);
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
size_t header_field_count(enum header_field_type type)
|
|
{
|
|
size_t count = 0;
|
|
switch (type) {
|
|
case E_HEADER_FIELD_CHARSET:
|
|
count = 1;
|
|
break;
|
|
case E_HEADER_FIELD_TITLE:
|
|
count = 1;
|
|
break;
|
|
case E_HEADER_FIELD_LINK:
|
|
count = 2;
|
|
break;
|
|
case E_HEADER_FIELD_META:
|
|
count = 1;
|
|
break;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
char **header_process_fields(FILE *fp, enum header_field_type type)
|
|
{
|
|
size_t values_count = header_field_count(type);
|
|
char **values = ecalloc(values_count, sizeof(char*));
|
|
|
|
for (int i = 0; i < values_count; i++) {
|
|
values[i] = header_process_extract_field(fp);
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
char *header_to_html(enum header_field_type type, char *name, char **values)
|
|
{
|
|
char *result;
|
|
switch (type) {
|
|
case E_HEADER_FIELD_CHARSET:
|
|
result = str_concat(3, "<meta charset=\"", values[0], "\">");
|
|
break;
|
|
case E_HEADER_FIELD_TITLE:
|
|
result = str_concat(3, "<title>", values[0], "</title>");
|
|
break;
|
|
case E_HEADER_FIELD_LINK:
|
|
result = str_concat(
|
|
5, "<link rel=\"", values[0], "\" href=\"", values[1], "\">"
|
|
);
|
|
break;
|
|
case E_HEADER_FIELD_META:
|
|
result = str_concat(
|
|
5, "<meta name=\"", name, "\" content=\"", values[0], "\">"
|
|
);
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
char *header_to_sh(enum header_field_type type, char *name, char **values)
|
|
{
|
|
char *result;
|
|
size_t name_size = strlen(name);
|
|
// name=(last value)
|
|
char *value = values[header_field_count(type) - 1];
|
|
bool is_function = (
|
|
type == E_HEADER_FIELD_META
|
|
&& name[name_size-2] == '('
|
|
&& name[name_size-1] == ')'
|
|
);
|
|
|
|
if (is_function) {
|
|
result = str_concat(4, name, " { ", value, "; }; ");
|
|
} else {
|
|
result = str_concat(4, name, "='", value, "'; ");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void header_process(FILE *fp)
|
|
{
|
|
size_t name_size = 100;
|
|
char* name = STR_EALLOC(name_size);
|
|
char c;
|
|
while ((c = fgetc(fp)) != '\0' && c != ' ') {
|
|
name_size = str_pushc(name, c, name_size, 100);
|
|
}
|
|
|
|
enum header_field_type type;
|
|
if (strcmp(name, "charset") == 0)
|
|
type = E_HEADER_FIELD_CHARSET;
|
|
else if (strcmp(name, "title") == 0)
|
|
type = E_HEADER_FIELD_TITLE;
|
|
else if (strcmp(name, "link") == 0)
|
|
type = E_HEADER_FIELD_LINK;
|
|
else
|
|
type = E_HEADER_FIELD_META;
|
|
|
|
char **values = header_process_fields(fp, type);
|
|
|
|
char *tmp = sh_prefix;
|
|
sh_prefix = str_concat(2, sh_prefix, header_to_sh(type, name, values));
|
|
free(tmp);
|
|
|
|
puts(header_to_html(type, name, values));
|
|
}
|
|
|
|
#define HEADER_ISEND(b, c) (b == '*' && c == '/')
|
|
void header_substitute(FILE* fp) {
|
|
puts("<head>");
|
|
|
|
char b = '\0';
|
|
char c;
|
|
while ((c = fgetc(fp)) != '\0') {
|
|
/*
|
|
* Non-alphabetical characters (e.g. whitespace, '*') in the header are
|
|
* ignored, advance lp to the start of input, while also making sure we
|
|
* don't reach the end of the line ('\0') or the end of the header
|
|
* section ('* /' without the space).
|
|
*/
|
|
while (
|
|
!isalpha(c)
|
|
&& c != '"'
|
|
&& c != '\''
|
|
&& c != '\0'
|
|
&& !HEADER_ISEND(b, c)
|
|
) {
|
|
b = c;
|
|
c = fgetc(fp);
|
|
}
|
|
if (c == '\0') continue;
|
|
if (HEADER_ISEND(b, c)) break;
|
|
|
|
ungetc(c, fp);
|
|
header_process(fp);
|
|
}
|
|
puts("</head>");
|
|
}
|
|
#undef HEADER_ISEND
|
|
|
|
char* command_execute(const char* command) {
|
|
FILE *pp;
|
|
char* c = str_concat(2, sh_prefix, command);
|
|
pp = popen(c, "r");
|
|
if (pp == NULL) edie("popen: ");
|
|
|
|
char* result = STR_EALLOC(1);
|
|
result[0] = '\0';
|
|
|
|
size_t buf_size = 81;
|
|
char* buf = STR_EALLOC(buf_size);
|
|
|
|
/*
|
|
* str_concat allocates a new string on the heap, so free the old memory
|
|
* when we concat the next line of output.
|
|
*/
|
|
while (fgets(buf, buf_size, pp) != NULL) {
|
|
char* tmp = result;
|
|
result = str_concat(2, result, buf);
|
|
free(tmp);
|
|
}
|
|
free(buf);
|
|
pclose(pp);
|
|
|
|
str_trimr(result, '\n', 1);
|
|
return result;
|
|
}
|
|
|
|
char* command_substitute(FILE* fp) {
|
|
size_t command_size = 100;
|
|
size_t command_i = 0;
|
|
char* command = STR_EALLOC(command_size);
|
|
memset(command, '\0', command_size);
|
|
|
|
int sub_parens = 0; /* track nested parans inside of command */
|
|
char c;
|
|
while ((c = fgetc(fp)) != EOF) {
|
|
if (c == ')' && sub_parens == 0) break;
|
|
else if (c == ')') sub_parens--;
|
|
else if (c == '(') sub_parens++;
|
|
|
|
if (command_i >= command_size) {
|
|
command_size += 100;
|
|
command = realloc(command, command_size);
|
|
if (command == NULL) edie("realloc: ");
|
|
memset(command + command_i, '\0', command_size - command_i);
|
|
}
|
|
|
|
command[command_i] = c;
|
|
command_i++;
|
|
}
|
|
|
|
return command_execute(command);
|
|
}
|
|
|
|
int process_input(FILE* fp) {
|
|
char b, c;
|
|
|
|
b = fgetc(fp);
|
|
c = fgetc(fp);
|
|
|
|
if (b == '/' && c == '*') {
|
|
header_substitute(fp);
|
|
b = fgetc(fp);
|
|
c = fgetc(fp);
|
|
}
|
|
|
|
while (b != EOF) {
|
|
if (b == '$' && c == '(') {
|
|
printf("%s", command_substitute(fp));
|
|
/*
|
|
* command_substitute consumes from fp to after the closing ')' of
|
|
* the substitution, we then need to get the next character from fp
|
|
* for the next iteration of the loop.
|
|
*/
|
|
b = fgetc(fp);
|
|
} else {
|
|
putchar(b);
|
|
b = c;
|
|
}
|
|
c = fgetc(fp);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
SET_ARGV0();
|
|
sh_prefix = STR_EALLOC(1);
|
|
return process_input(stdin);
|
|
}
|