чување 4ed6157298ad191253c3cf001c2af8028f873858
родитељ 40f313838cc4401e13538737b735e846fedf9ecd
Аутор: Страхиња Радић <contact@strahinja.org>
Датум: Tue, 29 Dec 2020 12:46:17 +0100
Determine real pathname in links
Signed-off-by: Страхиња Радић <contact@strahinja.org>
Diffstat:
измењених датотека: 14, додавања: 215(+), брисања: 71(-)
diff --git a/TODO b/TODO
@@ -11,7 +11,7 @@ Migration to C checklist
[x] Add {.class} processing
[x] Add {#id} processing
[x] Convert strncat token processing to individual token-based
- [ ] -d support (determine real pathname in links)
+ [x] -d support (determine real pathname in links)
[ ] Add {lsdir}
[x] Add title: processing
[ ] Update manpage
diff --git a/default.html.do b/default.html.do
@@ -1,3 +1,3 @@
-redo-ifchange $2.slw
+redo-ifchange $2.slw slweb
./slweb $2.slw >$3
diff --git a/default.o.do b/default.o.do
@@ -1,3 +1,3 @@
redo-ifchange $2.c
-cc -o $3 -c $2.c
+cc -g -Wall -o $3 -c $2.c
diff --git a/examples/includes/footer.slw b/examples/includes/footer.slw
@@ -1,12 +0,0 @@
-{footer}
-{.left}
-slweb v0.2.2-beta
-{/.left}
-{.center}
-23 Nov 2020
-{/.center}
-{.right}
-SLWEB(1)
-{/.right}
-{/footer}
-
diff --git a/examples/includes/inc/copy.slw b/examples/includes/inc/copy.slw
@@ -0,0 +1,2 @@
+{span}([Link to index](../index.html), from include) Copyright © _someguy_{/span}
+
diff --git a/examples/includes/inc/footer.slw b/examples/includes/inc/footer.slw
@@ -0,0 +1,13 @@
+{footer}
+{.left}
+slweb v0.2.2-beta
+{/.left}
+{.center}
+{include "copy"}
+23 Nov 2020
+{/.center}
+{.right}
+SLWEB(1)
+{/.right}
+{/footer}
+
diff --git a/examples/includes/header.slw b/examples/includes/inc/header.slw
diff --git a/examples/includes/index.slw b/examples/includes/index.slw
@@ -3,7 +3,7 @@ site-name: Includes
site-description: Demonstration of the includes
stylesheet: index.css
---
-{include "header"}
+{include "inc/header"}
{main}
{include "description"}
@@ -19,5 +19,5 @@ stylesheet: index.css
{include "bugs"}
{/main}
-{include "footer"}
+{include "inc/footer"}
diff --git a/examples/links/index.slw b/examples/links/index.slw
@@ -14,6 +14,9 @@ Another [link, inline](https://suckless.org).
You can also create and style a [=icon (link with icon)][2]
+You can also make a [local link][3].
+
[1]: https://strahinja.org
[2]: https://materialdesignicons.com
+[3]: ../../index.html
diff --git a/examples/links/style.css b/examples/links/style.css
@@ -6,7 +6,8 @@ html, body
a
{
- line-height: 1em;
+ line-height: 24px;
+ height: 24px;
padding: .5em 1em;
border-radius: 10pt;
background: #00f;
@@ -21,6 +22,33 @@ a:hover
a>svg
{
- vertical-align: middle;
+ vertical-align: text-bottom;
+ margin-bottom: -3px;
+ margin-right: .5ex;
+}
+
+a>span
+{
+ display: inline-block;
+ font-family: serif;
+ transform: skewX(15deg);
+ font-style: oblique;
+}
+
+a>span::after
+{
+ content: ' ';
+ display: block;
+ width: 0;
+ height: 2px;
+ background-color: #fff;
+ margin: 0 auto;
+ margin-top: -4px;
+ transition: all .25s ease;
+}
+
+a:hover>span::after
+{
+ width: 100%;
}
diff --git a/index.html b/index.html
@@ -17,8 +17,9 @@ transforms custom Markdown-like syntax into HTML.</p>
<h2>Prerequisites</h2>
-<p>Aside from the obvious (<strong>GCC</strong> and <strong>GNU make</strong>), slweb requires <strong>groff</strong>(1)
-and <strong>gzip</strong>(1) to create and compress documentation. <strong>git</strong>(1) is, aside from
+<p>Aside from the obvious (<strong>GCC</strong> and <strong>GNU make</strong>), slweb requires
+<strong>realpath</strong>(1) to determine paths in local links and <strong>groff</strong>(1) and
+<strong>gzip</strong>(1) to create and compress documentation. <strong>git</strong>(1) is, aside from
cloning the repository, required to use the directive <code>{git-log}</code>.</p>
<h2>Install</h2>
@@ -114,7 +115,7 @@ this program. If not, see <<a href="https://www.gnu.org/licenses">https://www
<div id="git-log">
Previous commit:
-index.slw b16dc97 2020-12-28 19:12:52 +0100 (Страхиња Радић) (HEAD -> redo-instead-of-make, origin/redo-instead-of-make)
+index.slw 49a47b4 2020-12-29 12:46:17 +0100 (Страхиња Радић) (HEAD -> redo-instead-of-make)
</div><!--git-log-->
diff --git a/index.slw b/index.slw
@@ -11,8 +11,9 @@ transforms custom Markdown-like syntax into HTML.
## Prerequisites
-Aside from the obvious (**GCC** and **GNU make**), slweb requires **groff**(1)
-and **gzip**(1) to create and compress documentation. **git**(1) is, aside from
+Aside from the obvious (**GCC** and **GNU make**), slweb requires
+**realpath**(1) to determine paths in local links and **groff**(1) and
+**gzip**(1) to create and compress documentation. **git**(1) is, aside from
cloning the repository, required to use the directive `{git-log}`.
## Install
diff --git a/slweb.c b/slweb.c
@@ -18,10 +18,13 @@
*/
#include "defs.h"
+#include <string.h>
static size_t lineno = 0;
static size_t colno = 1;
-static uint8_t* filename = NULL;
+static char* input_filename = NULL;
+static char* input_dirname = NULL;
+static char* basedir = NULL;
static char* arg_zero = NULL;
#define CHECKEXITNOMEM(ptr) if (!ptr) exit(error(ENOMEM, \
@@ -75,7 +78,7 @@ warning(int code, uint8_t* fmt, ...)
}
char*
-substr(char* src, int start, int finish)
+substr(const char* src, int start, int finish)
{
int len = strlen(src);
if (finish > len)
@@ -94,6 +97,12 @@ substr(char* src, int start, int finish)
return result;
}
+BOOL
+startswith(const char* s, const char* what)
+{
+ return s && what && !strcmp(substr(s, 0, strlen(what)), what);
+}
+
uint8_t*
get_value(KeyValue* list, size_t list_count, uint8_t* key, BOOL* seen)
{
@@ -117,6 +126,7 @@ get_value(KeyValue* list, size_t list_count, uint8_t* key, BOOL* seen)
int
set_basedir(char* arg, char** basedir)
{
+ size_t basedir_len = 0;
if (*basedir)
free(*basedir);
@@ -125,6 +135,9 @@ set_basedir(char* arg, char** basedir)
CALLOC(*basedir, char, strlen(arg)+1)
strcpy(*basedir, arg);
+ basedir_len = strlen(*basedir);
+ if (*(*basedir + basedir_len - 1) == '/')
+ *(*basedir + basedir_len - 1) = '\0';
return 0;
}
@@ -153,16 +166,16 @@ process_tag(uint8_t* token, FILE* output, KeyValue** macros,
if (!strcmp((char*)token, "git-log")
&& !read_yaml_macros_and_links) /* {git-log} */
{
- if (!filename)
+ if (!input_filename)
return warning(1, (uint8_t*)"Cannot use 'git-log' in stdin\n");
- uint8_t* basename = NULL;
- CALLOC(basename, uint8_t, u8_strlen(filename))
- uint8_t* slash = u8_strrchr(filename, (ucs4_t)'/');
+ char* basename = NULL;
+ CALLOC(basename, char, strlen(input_filename))
+ char* slash = strrchr(input_filename, '/');
if (slash)
- u8_strncpy(basename, slash+1, u8_strlen(slash+1));
+ strncpy(basename, slash+1, strlen(slash+1));
else
- u8_strncpy(basename, filename, u8_strlen(filename));
+ strncpy(basename, input_filename, strlen(input_filename));
fprintf(output, "<div id=\"git-log\">\nPrevious commit:\n");
uint8_t* command = NULL;
@@ -217,46 +230,35 @@ process_tag(uint8_t* token, FILE* output, KeyValue** macros,
if (!arg_zero)
exit(error(EINVAL, (uint8_t*)"Invalid argument zero\n"));
- if (!filename)
+ if (!input_filename)
return warning(1, (uint8_t*)"Cannot use 'include' in stdin\n");
uint8_t* command = NULL;
- uint8_t* slash = u8_strrchr(filename, (ucs4_t)'/');
uint8_t* ptoken = u8_strchr(token, (ucs4_t)' ');
- uint8_t* include_filename = NULL;
- uint8_t* pinclude_filename = NULL;
- uint8_t* include_dirname = NULL;
- uint8_t* pinclude_dirname = NULL;
- size_t include_dirname_len = 0;
- uint8_t* pfilename = filename;
+ char* include_filename = NULL;
+ char* pinclude_filename = NULL;
+ CALLOC(include_filename, char, BUFSIZE)
+ pinclude_filename = include_filename;
+
+ *skip_eol = TRUE;
if (!ptoken)
- {
- *skip_eol = TRUE;
return warning(1, (uint8_t*)"Directive 'include' requires"
" an argument\n");
- }
+
ptoken++;
- CALLOC(include_filename, uint8_t, BUFSIZE)
- CALLOC(include_dirname, uint8_t, BUFSIZE)
- pinclude_dirname = include_dirname;
- if (slash)
- while (pfilename && *pfilename && pfilename != slash)
- *pinclude_dirname++ = *pfilename++;
- else
- *pinclude_dirname++ = '.';
- *pinclude_dirname = '\0';
- include_dirname_len = u8_strlen(include_dirname);
- u8_strncpy(include_filename, include_dirname, include_dirname_len);
- *(include_filename + include_dirname_len) = '/';
- pinclude_filename = include_filename + include_dirname_len + 1;
while (ptoken && *ptoken)
if (*ptoken != '"')
*pinclude_filename++ = *ptoken++;
else
ptoken++;
+
CALLOC(command, uint8_t, BUFSIZE)
- u8_snprintf(command, BUFSIZE, "%s -b %s.slw", arg_zero, include_filename);
+ u8_snprintf(command, BUFSIZE, "%s -b -d %s %s/%s.slw",
+ arg_zero,
+ !strcmp(basedir, ".") ? input_dirname : basedir,
+ input_dirname,
+ include_filename);
FILE* cmd_output = popen((char*)command, "r");
if (!cmd_output)
@@ -281,9 +283,8 @@ process_tag(uint8_t* token, FILE* output, KeyValue** macros,
pclose(cmd_output);
free(cmd_output_line);
- free(include_dirname);
- free(include_filename);
free(command);
+ free(include_filename);
}
*skip_eol = TRUE;
}
@@ -417,15 +418,83 @@ process_blockquote(FILE* output, BOOL end_tag)
return 0;
}
+BOOL
+url_is_local(char* url)
+{
+ return !(startswith(url, "http://")
+ || startswith(url, "https://")
+ || startswith(url, "ftp://")
+ || startswith(url, "ftps://")
+ || startswith(url, "mailto://"));
+}
+
+int
+get_realpath(char** realpath, char* relativeto, char* path)
+{
+ uint8_t* command = NULL;
+ FILE* cmd_output = NULL;
+
+ CALLOC(command, uint8_t, BUFSIZE)
+ strcpy(*realpath, ".");
+ u8_snprintf(command, BUFSIZE, "realpath --relative-to=%s %s",
+ relativeto, path);
+
+ cmd_output = popen((char*)command, "r");
+ if (cmd_output)
+ {
+ uint8_t* cmd_output_line = NULL;
+ CALLOC(cmd_output_line, uint8_t, BUFSIZE)
+ while (!feof(cmd_output))
+ {
+ if (!fgets((char*)cmd_output_line, BUFSIZE, cmd_output))
+ continue;
+
+ char* eol = strchr((char*)cmd_output_line, '\n');
+ if (eol)
+ *eol = '\0';
+
+ strcpy(*realpath, (char*)cmd_output_line);
+ }
+ pclose(cmd_output);
+ free(cmd_output_line);
+ }
+ else
+ warning(1, (uint8_t*)"get_realpath: Cannot popen\n");
+
+ free(command);
+
+ return 0;
+}
+
int
process_link(uint8_t* link_text, uint8_t* link_macro_body, uint8_t* link_id,
KeyValue* links, size_t links_count, FILE* output)
{
- uint8_t* url = get_value(links, links_count, link_id, NULL);
- fprintf(output, "<a href=\"%s\">%s%s</a>",
+ char* url = (char*)get_value(links, links_count, link_id, NULL);
+ char* link_dir = NULL;
+ BOOL local = FALSE;
+ BOOL dir_is_dot = FALSE;
+
+ if (url)
+ local = url_is_local(url);
+
+ if (local)
+ {
+ CALLOC(link_dir, char, BUFSIZE)
+ get_realpath(&link_dir, basedir, input_dirname ? input_dirname : ".");
+ dir_is_dot = !strcmp(link_dir, ".");
+ }
+
+ fprintf(output, "<a href=\"%s%s%s\">%s%s</a>",
+ url && local && !dir_is_dot ? link_dir : "",
+ url && local && !dir_is_dot && url ? "/" : "",
url ? (char*)url : "",
link_macro_body ? (char*)link_macro_body : "",
link_text);
+
+ if (local)
+ free(link_dir);
+
return 0;
}
@@ -433,9 +502,30 @@ int
process_inline_link(uint8_t* link_text, uint8_t* link_macro_body,
uint8_t* link_url, FILE* output)
{
- fprintf(output, "<a href=\"%s\">%s%s</a>",
- link_url, link_macro_body ? (char*)link_macro_body : "",
+ char* link_dir = NULL;
+ BOOL local = FALSE;
+ BOOL dir_is_dot = FALSE;
+
+ if (link_url)
+ local = url_is_local((char*)link_url);
+
+ if (local)
+ {
+ CALLOC(link_dir, char, BUFSIZE)
+ get_realpath(&link_dir, basedir, input_dirname ? input_dirname : ".");
+ dir_is_dot = !strcmp(link_dir, ".");
+ }
+
+ fprintf(output, "<a href=\"%s%s%s\">%s%s</a>",
+ link_url && local && !dir_is_dot ? link_dir : "",
+ link_url && local && !dir_is_dot && link_url ? "/" : "",
+ link_url ? (char*)link_url : "",
+ link_macro_body ? (char*)link_macro_body : "",
link_text);
+
+ if (local)
+ free(link_dir);
+
return 0;
}
@@ -504,7 +594,6 @@ process_text_token(ULONG* state,
int
begin_html_and_head(FILE* output, KeyValue* vars, size_t vars_count)
{
- KeyValue* pvars = vars;
uint8_t* site_name = get_value(vars, vars_count, (uint8_t*)"site-name", NULL);
uint8_t* site_desc = get_value(vars, vars_count, (uint8_t*)"site-desc", NULL);
@@ -612,7 +701,7 @@ slweb_parse(uint8_t* buffer, FILE* output,
{
uint8_t* eol = u8_strchr(pbuffer, (ucs4_t)'\n');
if (!eol)
- continue;
+ break;
pline = line;
while (pbuffer != eol)
@@ -1542,7 +1631,6 @@ main(int argc, char** argv)
char* arg;
Command cmd = CMD_NONE;
BOOL body_only = FALSE;
- char* basedir = NULL;
CALLOC(basedir, char, 2)
*basedir = '.';
@@ -1615,7 +1703,7 @@ main(int argc, char** argv)
return result;
}
else
- filename = (uint8_t*)arg;
+ input_filename = arg;
cmd = CMD_NONE;
}
}
@@ -1629,18 +1717,38 @@ main(int argc, char** argv)
FILE* input = NULL;
FILE* output = stdout;
uint8_t* buffer = NULL;
+ char* slash = NULL;
- if (filename)
+ if (input_filename)
{
struct stat fs;
- input = fopen((char*)filename, "r");
+ input = fopen((char*)input_filename, "r");
if (!input)
- return error(ENOENT, (uint8_t*)"No such file: %s\n", filename);
+ return error(ENOENT, (uint8_t*)"No such file: %s\n", input_filename);
fstat(fileno(input), &fs);
CALLOC(buffer, uint8_t, fs.st_size)
fread((void*)buffer, sizeof(char), fs.st_size, input);
+
+ slash = strrchr(input_filename, '/');
+ if (slash)
+ {
+ CALLOC(input_dirname, char, BUFSIZE)
+ char* pinput_dirname = input_dirname;
+ char* pinput_filename = input_filename;
+ while (pinput_filename && *pinput_filename
+ && pinput_filename != slash)
+ *pinput_dirname++ = *pinput_filename++;
+ }
+ else
+ {
+ CALLOC(input_dirname, char, 2)
+ *input_dirname = '.';
+ }
+
+ if (!strcmp(basedir, "."))
+ strcpy(basedir, input_dirname);
}
else
{
diff --git a/slweb.do b/slweb.do
@@ -1,3 +1,3 @@
redo-ifchange slweb.o slweb.c defs.h
-cc -o $3 slweb.o -Wall -lunistring
+cc -g -Wall -o $3 slweb.o -lunistring