Merge pull request #8 from dylan-lom/#4

(#4) Implement header functions
This commit is contained in:
Dylan Lom 2021-02-06 17:49:52 +11:00 committed by GitHub
commit 87d5c9087a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 228 additions and 94 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
a.out
shmd
*.swp
tags

View File

@ -1,9 +1,15 @@
/*
* title "My Markdown Article"
* author "Dylan Lom"
* charset "utf8"
* title 'My Markdown Article'
* author 'Dylan Lom'
* charset utf8
* link stylesheet /main.css
* sh_node() 'echo "console.log($1)" | node'
*/
# $(echo "$title")
## Body Here
Date according to node: `$(sh_node "new Date().toISOString()")`
Built: $(date)

12
examples/index.shmd Normal file
View File

@ -0,0 +1,12 @@
# Index
Files in examples/
$(
for f in examples/*; do
echo "* [$(basename $f)]($f)"
done;
)
Pretty cool :)

12
examples/tutorial.md Normal file
View File

@ -0,0 +1,12 @@
/*
* title "SHMD Tutorial"
* author "Dylan Lom"
* func1() 'echo "hello!"'
*/
# $(echo "$title")
Embed shell commands with `\$(echo "hi!")`
You can write functions: $(func1 "$(whoami)")

226
shmd.c
View File

@ -2,109 +2,188 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "shmd.h"
const char* argv0;
const char *argv0;
char *sh_prefix;
struct str_list header_split(char* s) {
struct str_list l = STR_LIST_INIT();
size_t word_size = 100;
char* word = STR_EALLOC(word_size);
char *header_process_extract_field(FILE *fp)
{
char c;
size_t dest_size = 100;
char *dest = STR_EALLOC(dest_size);
/* Keep track of whether we're inside of a quoted or escaped string */
int in_dquote = 0, in_squote = 0, in_escape = 0;
for ( ; *s != '\0'; s++) {
if (*s == ' ' && !in_squote && !in_dquote) {
str_list_add(&l, word);
word = STR_EALLOC(word_size); /* Allocate memory for next word */
continue;
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;
}
}
else if (*s == '\\' && !in_escape) { in_escape = 1; continue; }
else if (*s == '"' && !in_escape) { in_dquote ^= 1; continue; }
else if (*s == '\'' && !in_escape) { in_squote ^= 1; continue; }
word_size = str_pushc(word, *s, word_size, 100);
/* It's just simpler to reset this after every iteration */
in_escape = 0;
dest_size = str_pushc(dest, c, dest_size, 50);
}
if (strlen(word) > 0) str_list_add(&l, word);
return l;
return dest;
}
char* header_list_to_html(struct str_list l) {
if (l.size < 1) return "";
char** vals = l.values;
/* work out the total length of all strings */
size_t vals_size = 0;
for (int i = 0; i < l.size; i++)
vals_size += strlen(vals[i]);
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* html;
/*
* TODO: This is gross... I did it like this to make the if ... else
* statement readable... need a better abstraction though
*/
#define HTML_SPRINTF(l_min, fmt, ...) if (l.size < l_min) return ""; \
html = STR_EALLOC(sizeof(fmt) + vals_size + 1); \
sprintf(html, fmt, __VA_ARGS__)
/* Known HEAD tags */
if (strcmp(vals[0], "charset") == 0) {
HTML_SPRINTF(2, "<meta charset=\"%s\">", vals[1]);
} else if (strcmp(vals[0], "title") == 0) {
HTML_SPRINTF(2, "<title>%s</title>", vals[1]);
} else if (strcmp(vals[0], "link") == 0) {
HTML_SPRINTF(3, "<link rel=\"%s\" href=\"%s\">", vals[1], vals[2]);
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 {
HTML_SPRINTF(2, "<meta name=\"%s\" content=\"%s\">", vals[0], vals[1]);
result = str_concat(4, name, "='", value, "'; ");
}
#undef HTML_SPRINTF
return html;
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 == '/')
char* header_substitute(FILE* fp) {
#define HEADER_ISEND(s) (strlen(s) >= 2 && s[0] == '*' && s[1] == '/')
char* result = str_concat(1, "<head>");
size_t line_size = 250;
char* line = STR_EALLOC(line_size);
char* lp;
char* tmp;
while (fgets(line, line_size, fp) != NULL) {
str_trimr(line, '\n', 1);
lp = line;
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(*lp) && *lp != '\0' && !HEADER_ISEND(lp)) { lp++; }
if (HEADER_ISEND(lp)) break;
if (*lp == '\0') continue;
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;
struct str_list list = header_split(lp);
char* line_html = header_list_to_html(list);
str_list_free(&list);
/* concat html to result */
tmp = result;
result = str_concat(3, result, "\n\t", line_html);
free(tmp);
ungetc(c, fp);
header_process(fp);
}
tmp = result;
result = str_concat(3, result, "\n", "</head>");
free(tmp);
return result;
#undef HEADER_ISEND
}
#undef HEADER_ISEND
char* command_execute(const char* command) {
FILE *pp;
pp = popen(command, "r");
char* c = str_concat(2, sh_prefix, command);
pp = popen(c, "r");
if (pp == NULL) edie("popen: ");
char* result = STR_EALLOC(1);
@ -189,5 +268,6 @@ int process_input(FILE* fp) {
int main(int argc, char* argv[]) {
SET_ARGV0();
sh_prefix = STR_EALLOC(1);
return process_input(stdin);
}

39
shmd.h
View File

@ -4,11 +4,34 @@
* #argv0
* value of argv[0] at the start of execution (ie. the program's name).
*/
const char* argv0;
const char *argv0;
char *sh_prefix;
enum header_field_type
{
E_HEADER_FIELD_CHARSET,
E_HEADER_FIELD_TITLE,
E_HEADER_FIELD_LINK,
E_HEADER_FIELD_META
};
/* Read the next field from @fp */
char *header_process_extract_field(FILE *fp);
/* Get the number of properties for header type @type */
size_t header_field_count(enum header_field_type type);
/* Read space-seperated fields from @fp */
char **header_process_fields(FILE *fp, enum header_field_type type);
/* Get the html-ized version of a header */
char *header_to_html(enum header_field_type type, char *name, char **values);
/*
* Get the shell-executable assignment of a header, using the last value as
* the value.
* In the special case @type is E_HEADER_FIELD_META and @name ends in '()',
* will return a posix-style function declaration.
*/
char *header_to_sh(enum header_field_type type, char *name, char **values);
void header_process(FILE *fp);
char *header_substitute(FILE *fp);
struct str_list header_split (char* s);
char* header_list_to_html (struct str_list l);
char* header_substitute (FILE* fp);
/**
* #command_execute
@ -17,7 +40,7 @@ char* header_substitute (FILE* fp);
* @return: stdout returned when running @command, with at most one trailing
* newline stripped.
*/
char* command_execute (const char* command);
char *command_execute(const char *command);
/**
* #command_substitute
* Read @fp until the next unmatched `)` character and execute. This process is
@ -26,7 +49,7 @@ char* command_execute (const char* command);
* @fp: file pointer to read from.
* @return: the output of running #execute_command on the input read.
*/
char* command_substitute (FILE* fp);
char *command_substitute (FILE *fp);
int process_input (FILE* fp);
int main (int argc, char* argv[]);
int process_input(FILE *fp);
int main(int argc, char *argv[]);

20
util.h
View File

@ -3,15 +3,15 @@
#define SHIFT_ARGS() argv++; argc--
#define SET_ARGV0() argv0 = argv[0]; SHIFT_ARGS()
void die(const char* fmt, ...);
void edie(const char* fmt, ...);
void usage();
void die (const char* fmt, ...);
void edie (const char* fmt, ...);
void usage (void);
void* ecalloc(size_t nmemb, size_t size);
void* ecalloc (size_t nmemb, size_t size);
size_t str_pushc(char* s, char c, size_t s_size, size_t realloc_amount);
int str_trimr(char* s, char c, int max_num);
char* str_concat(int count, ...);
size_t str_pushc (char* s, char c, size_t s_size, size_t realloc_amount);
int str_trimr (char* s, char c, int max_num);
char* str_concat (int count, ...);
#define STR_EALLOC(s) ecalloc(s, sizeof(char))
#define STR_MALLOC(s) calloc(s, sizeof(char))
@ -20,8 +20,8 @@ struct str_list {
char** values;
};
struct str_list* str_list_add(struct str_list* l, char* s);
struct str_list str_list_new(int count, ...);
void str_list_free(struct str_list* l);
struct str_list* str_list_add (struct str_list* l, char* s);
struct str_list str_list_new (int count, ...);
void str_list_free (struct str_list* l);
#define STR_LIST_INIT() str_list_new(0)