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 a.out
shmd shmd
*.swp *.swp
tags

View File

@ -1,9 +1,15 @@
/* /*
* title "My Markdown Article" * title 'My Markdown Article'
* author "Dylan Lom" * author 'Dylan Lom'
* charset "utf8" * charset utf8
* link stylesheet /main.css
* sh_node() 'echo "console.log($1)" | node'
*/ */
# $(echo "$title")
## Body Here ## Body Here
Date according to node: `$(sh_node "new Date().toISOString()")`
Built: $(date) 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
#include "shmd.h" #include "shmd.h"
const char* argv0; const char *argv0;
char *sh_prefix;
struct str_list header_split(char* s) { char *header_process_extract_field(FILE *fp)
struct str_list l = STR_LIST_INIT(); {
size_t word_size = 100; char c;
char* word = STR_EALLOC(word_size); size_t dest_size = 100;
char *dest = STR_EALLOC(dest_size);
/* Keep track of whether we're inside of a quoted or escaped string */ bool is_escaped = false,
int in_dquote = 0, in_squote = 0, in_escape = 0; is_dquoted = false,
is_squoted = false;
for ( ; *s != '\0'; s++) { while (
if (*s == ' ' && !in_squote && !in_dquote) { (c = fgetc(fp)) != '\0' && !(
str_list_add(&l, word); !is_dquoted
word = STR_EALLOC(word_size); /* Allocate memory for next word */ && !is_squoted
continue; && !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; } dest_size = str_pushc(dest, c, dest_size, 50);
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;
} }
return dest;
if (strlen(word) > 0) str_list_add(&l, word);
return l;
} }
char* header_list_to_html(struct str_list l) { size_t header_field_count(enum header_field_type type)
if (l.size < 1) return ""; {
char** vals = l.values; size_t count = 0;
/* work out the total length of all strings */ switch (type) {
size_t vals_size = 0; case E_HEADER_FIELD_CHARSET:
for (int i = 0; i < l.size; i++) count = 1;
vals_size += strlen(vals[i]); 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; char **header_process_fields(FILE *fp, enum header_field_type type)
/* {
* TODO: This is gross... I did it like this to make the if ... else size_t values_count = header_field_count(type);
* statement readable... need a better abstraction though char **values = ecalloc(values_count, sizeof(char*));
*/
#define HTML_SPRINTF(l_min, fmt, ...) if (l.size < l_min) return ""; \ for (int i = 0; i < values_count; i++) {
html = STR_EALLOC(sizeof(fmt) + vals_size + 1); \ values[i] = header_process_extract_field(fp);
sprintf(html, fmt, __VA_ARGS__) }
/* Known HEAD tags */
if (strcmp(vals[0], "charset") == 0) { return values;
HTML_SPRINTF(2, "<meta charset=\"%s\">", vals[1]); }
} else if (strcmp(vals[0], "title") == 0) {
HTML_SPRINTF(2, "<title>%s</title>", vals[1]); char *header_to_html(enum header_field_type type, char *name, char **values)
} else if (strcmp(vals[0], "link") == 0) { {
HTML_SPRINTF(3, "<link rel=\"%s\" href=\"%s\">", vals[1], vals[2]); 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 { } else {
HTML_SPRINTF(2, "<meta name=\"%s\" content=\"%s\">", vals[0], vals[1]); result = str_concat(4, name, "='", value, "'; ");
} }
#undef HTML_SPRINTF return result;
return html;
} }
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) { char* header_substitute(FILE* fp) {
#define HEADER_ISEND(s) (strlen(s) >= 2 && s[0] == '*' && s[1] == '/')
char* result = str_concat(1, "<head>"); char* result = str_concat(1, "<head>");
size_t line_size = 250; char b = '\0';
char* line = STR_EALLOC(line_size); char c;
char* lp; while ((c = fgetc(fp)) != '\0') {
char* tmp;
while (fgets(line, line_size, fp) != NULL) {
str_trimr(line, '\n', 1);
lp = line;
/* /*
* Non-alphabetical characters (e.g. whitespace, '*') in the header are * Non-alphabetical characters (e.g. whitespace, '*') in the header are
* ignored, advance lp to the start of input, while also making sure we * 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 * don't reach the end of the line ('\0') or the end of the header
* section ('* /' without the space). * section ('* /' without the space).
*/ */
while (!isalpha(*lp) && *lp != '\0' && !HEADER_ISEND(lp)) { lp++; } while (
if (HEADER_ISEND(lp)) break; !isalpha(c)
if (*lp == '\0') continue; && 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); ungetc(c, fp);
char* line_html = header_list_to_html(list); header_process(fp);
str_list_free(&list);
/* concat html to result */
tmp = result;
result = str_concat(3, result, "\n\t", line_html);
free(tmp);
} }
tmp = result;
result = str_concat(3, result, "\n", "</head>");
free(tmp);
return result; return result;
#undef HEADER_ISEND
} }
#undef HEADER_ISEND
char* command_execute(const char* command) { char* command_execute(const char* command) {
FILE *pp; FILE *pp;
pp = popen(command, "r"); char* c = str_concat(2, sh_prefix, command);
pp = popen(c, "r");
if (pp == NULL) edie("popen: "); if (pp == NULL) edie("popen: ");
char* result = STR_EALLOC(1); char* result = STR_EALLOC(1);
@ -189,5 +268,6 @@ int process_input(FILE* fp) {
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
SET_ARGV0(); SET_ARGV0();
sh_prefix = STR_EALLOC(1);
return process_input(stdin); return process_input(stdin);
} }

39
shmd.h
View File

@ -4,11 +4,34 @@
* #argv0 * #argv0
* value of argv[0] at the start of execution (ie. the program's name). * 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 * #command_execute
@ -17,7 +40,7 @@ char* header_substitute (FILE* fp);
* @return: stdout returned when running @command, with at most one trailing * @return: stdout returned when running @command, with at most one trailing
* newline stripped. * newline stripped.
*/ */
char* command_execute (const char* command); char *command_execute(const char *command);
/** /**
* #command_substitute * #command_substitute
* Read @fp until the next unmatched `)` character and execute. This process is * 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. * @fp: file pointer to read from.
* @return: the output of running #execute_command on the input read. * @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 process_input(FILE *fp);
int main (int argc, char* argv[]); int main(int argc, char *argv[]);

20
util.h
View File

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