slweb

Једноставни генератор статичких веб страна
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 853bbd5ef6265224b39ee5cc54d6bbef961c9b45
родитељ 5dba73998348ce28d408005b39e572f77db7a2a3
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Thu,  4 Feb 2021 14:04:45 +0100

Added tables and partial tables; added iter parameter to csv directive; updated
manpage

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
MTODO | 2++
Mdefs.h | 3+++
Aexamples/tables/all.do | 2++
Aexamples/tables/csv.slw | 28++++++++++++++++++++++++++++
Aexamples/tables/index.slw | 16++++++++++++++++
Aexamples/tables/standard.slw | 18++++++++++++++++++
Aexamples/tables/tables.css | 11+++++++++++
Mindex.html | 2+-
Mslweb.1.in | 69++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mslweb.c | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
измењених датотека: 10, додавања: 363(+), брисања: 10(-)

diff --git a/TODO b/TODO @@ -1,6 +1,8 @@ TODO ==== + [x] Add tables + [ ] keep-macros variable: when present in an included file, macros from child process get added to the parent diff --git a/defs.h b/defs.h @@ -119,6 +119,9 @@ typedef struct #define ST_INLINE_FOOTNOTE (1 << 25) #define ST_FORMULA (1 << 26) #define ST_DISPLAY_FORMULA (1 << 27) +#define ST_TABLE_HEADER (1 << 28) +#define ST_TABLE_LINE (1 << 29) +#define ST_TABLE (1 << 30) #define ST_CS_NONE 0 #define ST_CS_HEADER 1 diff --git a/examples/tables/all.do b/examples/tables/all.do @@ -0,0 +1,2 @@ +redo-ifchange index.html + diff --git a/examples/tables/csv.slw b/examples/tables/csv.slw @@ -0,0 +1,28 @@ +## Tables and CSV + +CSV can easily be formatted into a table, like this: + +``` +|@\\------|-----|-----|-----|-----| +{csv "../csv/sales" 1} +|@# $#1 | $#2 | $#3 | $#4 | $#5 | +{/csv} +|@-------|-----|-----|-----|-----| +{csv "../csv/sales"} +|@ $1 | $2 | $3 | $4 | $5 | +{/csv} +|@/------|-----|-----|-----|-----| +``` + +which gives: + +|@\------|-----|-----|-----|-----| +{csv "../csv/sales" 1} +|@# $#1 | $#2 | $#3 | $#4 | $#5 | +{/csv} +|@-------|-----|-----|-----|-----| +{csv "../csv/sales"} +|@ $1 | $2 | $3 | $4 | $5 | +{/csv} +|@/------|-----|-----|-----|-----| + diff --git a/examples/tables/index.slw b/examples/tables/index.slw @@ -0,0 +1,16 @@ +--- +site-name: Tables demo +stylesheet: tables.css +--- + +# Tables + +Tables generally follow Markdown syntax, with a few modifications. + +Tables aren't formatted by default in HTML, so to look "good" they need some +CSS. + +{include "standard"} + +{include "csv"} + diff --git a/examples/tables/standard.slw b/examples/tables/standard.slw @@ -0,0 +1,18 @@ +## Standard tables + +Code: + +``` +| ID | Name | Age | +|----|--------------|-----| +| 1 | John Smith | 23 | +| 2 | Peter Robson | 34 | +``` + +gives: + +| ID | Name | Age | +|----|--------------|-----| +| 1 | John Smith | 23 | +| 2 | Peter Robson | 34 | + diff --git a/examples/tables/tables.css b/examples/tables/tables.css @@ -0,0 +1,11 @@ +table +{ + border-spacing: 0; + border-collapse: collapse; +} + +td, th +{ + border: 1px solid #000; +} + diff --git a/index.html b/index.html @@ -136,7 +136,7 @@ this program. If not, see &lt;<a href="https://www.gnu.org/licenses">https://www <div id="git-log"> Previous commit: -index.slw fc89c76 2021-02-02 20:13:15 +0100 (Страхиња Радић) (HEAD -> master, origin/master, origin/HEAD) +index.slw 5dba739 2021-02-02 22:40:12 +0100 (Страхиња Радић) (HEAD -> master, origin/master, origin/HEAD) </div><!--git-log--> diff --git a/slweb.1.in b/slweb.1.in @@ -405,6 +405,63 @@ will produce: Text surrounded by newlines will be put inside \fC<p></p>\fP tags. See .SM "KNOWN LIMITATIONS." . +.IP \[bu] 4 +.BR Tables . +Lines starting with vertical bars (\fC|\fP) will be transformed into +.SM HTML +tables. The first such line is treated as a header row, second as the alignment +specifier (currently ignored), and the rest regular rows. Table cells and header +cells are specified by additional bar characters. For example, input: +. +.CDS 8 +| Month | Jan | Feb | Mar | Apr | +|-------|-----|-----|-----|-----| +| Qty | 2.35| 1.2 | 8 | 15 | +.CDE +. +.IP "" 4 +will be transformed into: +. +.CDS 8 +<table> +<thead> +<tr><th> Month </th><th> Jan </th><th> Feb </th><th> Mar </th> +<th> Apr </th></tr> +</thead> +<tbody> +<tr><td> Qty </td><td> 2.35</td><td> 1.2 </td><td> 8 </td> +<td> 15 </td></tr> +</tbody> +</table> +.CDE +. +.IP "" 4 +.B Partial tables +are a modification of Markdown tables, allowing them to be combined with the +.SM CSV +directive. In order for tables to work with templating, they need two additional +lines at the top and the bottom, to mark the start and the end of the table. All +the lines in partial tables need to start with \[lq]\fC|@\fP\[rq], with only the +character following at-mark being different: +. +.CDS 8 +|@\\------|-----|-----|-----|-----| +{csv "../csv/sales" 1} +|@# $#1 | $#2 | $#3 | $#4 | $#5 | +{/csv} +|@-------|-----|-----|-----|-----| +{csv "../csv/sales"} +|@ $1 | $2 | $3 | $4 | $5 | +{/csv} +|@/------|-----|-----|-----|-----| +.CDE +. +.IP "" 4 +The rest of the line following the third character in top, bottom and lines +separating header from body is ignored by the parser and is included in this +example only for aesthetic purposes. See +.BR "CSV templating" . +. .SS Math mode . .LP @@ -435,13 +492,15 @@ home page. . .IP \[bu] .BR "CSV templating" . -Directive \fC{csv "csvfile"}{/csv}\fP marks a template. Whatever is between -\fC{csv}\fP and \fC{/csv}\fP will be processed by +Directive \fC{csv "\f[CI]csvfile\fC" \f[CI]iter\fC}{/csv}\fR marks a template. +Whatever is between \fC{csv}\fP and \fC{/csv}\fP will be processed by .B slweb and collected as a template. Afterwards, file \fIcsvfile.csv\fP will be read and -for each of its lines the collected template will be output, substituting each -occurence of a register mark \fC$\f[CI]n\fR (where \fIn\fP is between 1 and 9, -inclusive) with the corresponding +for each of its lines (up to +.IR iter , +if present) the collected template will be output, substituting each occurence +of a register mark \fC$\f[CI]n\fR (where \fIn\fP is between 1 and 9, inclusive) +with the corresponding .SM CSV field of the read line. .SM CSV diff --git a/slweb.c b/slweb.c @@ -45,6 +45,7 @@ static size_t current_inline_footnote = 0; static uint8_t* csv_template = NULL; static size_t csv_template_size = 0; static char* csv_filename = NULL; +static long csv_iter = 0; static ULONG state = ST_NONE; #define CHECKEXITNOMEM(ptr) { if (!ptr) exit(error(ENOMEM, \ @@ -627,7 +628,7 @@ process_csv(uint8_t* arg_token, FILE* output, BOOL read_yaml_macros_and_links, for (UBYTE i = 0; i < MAX_CSV_REGISTERS; i++) CALLOC(csv_register[i], uint8_t, BUFSIZE) - while (!feof(csv)) + while (!feof(csv) && (!csv_iter || csv_lineno <= csv_iter)) { uint8_t* eol = NULL; if (!fgets((char*)bufline, BUFSIZE, csv)) @@ -744,6 +745,7 @@ process_csv(uint8_t* arg_token, FILE* output, BOOL read_yaml_macros_and_links, if (read_yaml_macros_and_links) return 0; + csv_iter = 0; uint8_t* saveptr = NULL; uint8_t* args = u8_strtok(arg_token, (uint8_t*)" ", &saveptr); args = u8_strtok(NULL, (uint8_t*)" ", &saveptr); @@ -758,6 +760,14 @@ process_csv(uint8_t* arg_token, FILE* output, BOOL read_yaml_macros_and_links, *(args_base + u8_strlen(args_base) - 1) = 0; snprintf(csv_filename, BUFSIZE, "%s/%s.csv", input_dirname, (char*)args_base); free(args_base); + args = u8_strtok(NULL, (uint8_t*)" ", &saveptr); + if (args) + { + errno = 0; + csv_iter = strtol((char*)args, NULL, 10); + if (errno) + exit(error(errno, (uint8_t*)"csv: Invalid argument '%s'", args)); + } } return 0; @@ -1402,6 +1412,71 @@ process_kbd(FILE* output, BOOL end_tag) return 0; } +int +process_table_start(FILE* output) +{ + print_output(output, "<table>\n"); + return 0; +} + +int +process_table_header_start(FILE* output) +{ + print_output(output, "<thead>\n<tr><th>"); + return 0; +} + +int +process_table_header_cell(FILE* output) +{ + print_output(output, "</th><th>"); + return 0; +} + +int +process_table_header_end(FILE* output) +{ + print_output(output, "</th></tr>\n</thead>\n"); + return 0; +} + +int +process_table_body_start(FILE* output, BOOL start_row) +{ + print_output(output, "<tbody>\n"); + if (start_row) + print_output(output, "<tr><td>"); + return 0; +} + +int +process_table_body_row_start(FILE* output) +{ + print_output(output, "<tr><td>"); + return 0; +} + +int +process_table_body_cell(FILE* output) +{ + print_output(output, "</td><td>"); + return 0; +} + +int +process_table_body_row_end(FILE* output) +{ + print_output(output, "</td></tr>\n"); + return 0; +} + +int +process_table_end(FILE* output) +{ + print_output(output, "</tbody>\n</table>\n"); + return 0; +} + BOOL url_is_local(char* url) { @@ -1543,9 +1618,12 @@ process_line_start(uint8_t* line, BOOL first_line_in_doc, state &= ~(ST_FOOTNOTE_TEXT | ST_PARA_OPEN); } } - if (!read_yaml_macros_and_links) - print_output(output, "<p>"); - state |= ST_PARA_OPEN; + if (!ANY(state, ST_TABLE | ST_TABLE_HEADER | ST_TABLE_LINE)) + { + if (!read_yaml_macros_and_links) + print_output(output, "<p>"); + state |= ST_PARA_OPEN; + } } return 0; } @@ -2580,6 +2658,124 @@ slweb_parse(uint8_t* buffer, FILE* output, BOOL body_only, pline += 2; colno += 2; } + /* Partial tables (for templating) */ + else if (!(state & ST_PRE) && colno == 1 && u8_strlen(pline) > 1 + && *(pline+1) == '@') + { + skip_eol = TRUE; + + pline += 2; + colno += 2; + + switch (*pline) + { + case '\\': + if (!read_yaml_macros_and_links) + { + process_table_start(output); + process_table_header_start(output); + } + pline = NULL; + break; + + case '#': + state |= ST_TABLE_HEADER; + pline++; + colno++; + break; + + case '-': + state &= ~ST_TABLE_HEADER; + if (!read_yaml_macros_and_links) + process_table_body_start(output, FALSE); + pline = NULL; + break; + + case ' ': + state |= ST_TABLE; + if (!read_yaml_macros_and_links) + process_table_body_row_start(output); + break; + + case '/': + state &= ~ST_TABLE; + process_table_end(output); + pline = NULL; + break; + + default: + warning(1, (uint8_t*)"Invalid partial table line"); + CHECKCOPY(token, ptoken, token_size, pline) + colno++; + } + } + else if (!(state & ST_PRE) && colno == 1) + { + skip_eol = TRUE; + + if (state & ST_TABLE_HEADER) + { + state &= ~ST_TABLE_HEADER; + state |= ST_TABLE_LINE; + pline = NULL; + break; + } + + if (state & ST_TABLE_LINE) + { + state &= ~ST_TABLE_LINE; + state |= ST_TABLE; + if (!read_yaml_macros_and_links) + process_table_body_start(output, TRUE); + } + else if (state & ST_TABLE) + { + if (!read_yaml_macros_and_links) + process_table_body_row_start(output); + } + else + { + state |= ST_TABLE_HEADER; + if (!read_yaml_macros_and_links) + { + process_table_start(output); + process_table_header_start(output); + } + } + + pline++; + colno++; + } + else if (state & ST_TABLE_HEADER) + { + *ptoken = 0; + if (!read_yaml_macros_and_links) + { + print_output(output, "%s", token); + if (u8_strlen(pline) > 1) + process_table_header_cell(output); + else + process_table_header_end(output); + } + RESET_TOKEN(token, ptoken, token_size) + pline++; + colno++; + } + else if (state & ST_TABLE) + { + *ptoken = 0; + if (!read_yaml_macros_and_links) + { + print_output(output, "%s", token); + if (u8_strlen(pline) > 1) + process_table_body_cell(output); + else + process_table_body_row_end(output); + } + RESET_TOKEN(token, ptoken, token_size) + pline++; + colno++; + } else { CHECKCOPY(token, ptoken, token_size, pline) @@ -3364,6 +3560,24 @@ slweb_parse(uint8_t* buffer, FILE* output, BOOL body_only, process_blockquote(output, TRUE); } + if (ANY(state, ST_TABLE_HEADER | ST_TABLE_LINE) + && (!line_len || !*pbuffer)) + { + if (!read_yaml_macros_and_links) + { + warning(1, (uint8_t*)"Malformed table"); + process_table_end(output); + } + state &= ~(ST_TABLE_HEADER | ST_TABLE_LINE); + } + + if ((state & ST_TABLE) && (!line_len || !*pbuffer)) + { + if (!read_yaml_macros_and_links) + process_table_end(output); + state &= ~ST_TABLE; + } + previous_line_blank = FALSE; }