чување 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:
измењених датотека: 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 <<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;
}