Fixed a lot.

master
Neil Edelman 6 years ago
parent 2b2daae97a
commit 7613e60e86

@ -1,37 +0,0 @@
# Windows port cross-compiling with MacOSX MinGW 4.3.0
# (good luck)
PROJ := MakeIndex
VA := 0
VB := 8
FILES := Recursor Parser Widget Files
BDIR := win
BACK := backup
EXE := $(PROJ).exe
INST := $(PROJ)-$(VA)_$(VB)
OBJS := $(patsubst %,$(BDIR)/%.o,$(FILES))
SRCS := $(patsubst %,%.c,$(FILES))
H := $(patsubst %,%.h,$(FILES))
CC := /usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-gcc
CF := -Wall -O3 -fasm -fomit-frame-pointer -ffast-math -funroll-loops -pedantic -ansi
default: $(BDIR)/$(EXE)
$(BDIR)/$(EXE): $(OBJS)
$(CC) $(CF) -o $@ $^
$(BDIR)/%.o: %.c
@mkdir -p $(BDIR)
$(CC) $(CF) -c $? -o $@
.PHONY: clean backup
clean:
-rm $(OBJS)
setup: $(BDIR)/$(EXE)
@mkdir -p $(INST)
cp $(BDIR)/$(EXE) readme.txt gpl.txt copying.txt $(INST)
cp -a bin/example $(INST)
zip $(BDIR)/$(INST)-Win32-`date +%Y-%m-%dT%H%M%S` $(INST)/$(EXE) -r $(INST)
rm -R $(INST)

@ -41,22 +41,22 @@
<p>
Copyright 2008, 2012 Neil Edelman, distributed under the terms of the
GNU General Public License, see copying.txt, or
<a href = "https://opensource.org/licenses/GPL-3.0">https://opensource.org/licenses/GPL-3.0</a>.
GNU General Public License, see copying.txt, or
<a href = "https://opensource.org/licenses/GPL-3.0">https://opensource.org/licenses/GPL-3.0</a>.
</p>
<p>
Files is a list of File (private class defiend below,) the Files can have
a relation to other Files by 'parent' and 'favourite' (@(pwd) uses this.)
Files is a list of File (private class defiend below,) the Files can have
a relation to other Files by 'parent' and 'favourite' (@(pwd) uses this.)
</p>
<dl>
<dt>author:</dt>
<dt>minimum standard</dt>
<dd>C89/90</dd>
<dt>author</dt>
<dd>Neil</dd>
<dt>version:</dt>
<dd>0.9; 2017-03 fixed pedantic warnings</dd>
<dt>since:</dt>
<dd>0.8; 2013-07 case-insensitive sort
0.7; 2012 sth.dsth.d handled properly
0.6; 2008-03-24</dd>
<dt>version</dt>
<dd>1.1; 2017-03 fixed pedantic warnings; took out arg</dd>
<dt>since</dt>
<dd>0.6; 2008-03-24</dd>
</dl>
@ -200,12 +200,8 @@ After FilesSetFarourite, this enumerates them.
<div><a name = "FilesName"><!-- --></a>
<h3>FilesName</h3>
<pre>char * <b>FilesName</b> (const struct Files *files)</pre>
<p>
</p>
<p>
</p>
<dl>
<dt>return:</dt>
<dt>return</dt>
<dd>The file name of the selected file.</dd>
</dl>
</div>
@ -213,12 +209,8 @@ After FilesSetFarourite, this enumerates them.
<div><a name = "FilesSize"><!-- --></a>
<h3>FilesSize</h3>
<pre>int <b>FilesSize</b> (const struct Files *files)</pre>
<p>
</p>
<p>
</p>
<dl>
<dt>return:</dt>
<dt>return</dt>
<dd>File size of the selected file.</dd>
</dl>
</div>
@ -226,12 +218,8 @@ After FilesSetFarourite, this enumerates them.
<div><a name = "FilesIsDir"><!-- --></a>
<h3>FilesIsDir</h3>
<pre>int <b>FilesIsDir</b> (const struct Files *files)</pre>
<p>
</p>
<p>
</p>
<dl>
<dt>return:</dt>
<dt>return</dt>
<dd>Whether the file is a directory.</dd>
</dl>
</div>

@ -45,31 +45,90 @@
<a name = "_declarations"><!-- --></a><h2>Declarations</h2>
<div><a name = "struct Parser"><!-- --></a>
<h3>struct Parser</h3>
<pre><b>struct Parser</b></pre>
<p>
See <a href = "#Parser">Parser</a>.
</p>
<dl>
</dl>
</div>
<div><a name = "struct Files"><!-- --></a>
<h3>struct Files</h3>
<pre><b>struct Files</b></pre>
<p>
Dependancy on <em>Files</em>
</p>
<dl>
</dl>
</div>
<div><a name = "typedef int (*ParserWidget)(const struct Files *, FILE *fp)"><!-- --></a>
<h3>typedef int (*ParserWidget)(const struct Files *, FILE *fp)</h3>
<pre><b>typedef int (*ParserWidget)(const struct Files *, FILE *fp)</b></pre>
<p>
All <em>ParserWidget</em>s are in <em>Widget.c</em>
</p>
<dl>
</dl>
</div>
<a name = "_summary"><!-- --></a><h2>Function Summary</h2>
<table>
<tr><th>Return Type</th><th>Function Name</th><th>Argument List</th></tr>
<tr>
<td>const struct Symbol *</td>
<td><a href = "#match">match</a></td>
<td>const char *str, const char *end</td>
<td>struct Parser *</td>
<td><a href = "#Parser">Parser</a></td>
<td>const char *str</td>
</tr>
<tr>
<td>void</td>
<td><a href = "#Parser_">Parser_</a></td>
<td>struct Parser **const p_ptr</td>
</tr>
<tr>
<td>void</td>
<td><a href = "#ParserRewind">ParserRewind</a></td>
<td>struct Parser *p</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#ParserParse">ParserParse</a></td>
<td>struct Parser *p, const struct Files *f, int invisible, FILE *fp</td>
<td>struct Parser *p, const struct Files *f, int invisible,
FILE *fp</td>
</tr>
</table>
<a name = "_detail"><!-- --></a><h2>Function Detail</h2>
<div><a name = "match"><!-- --></a>
<h3>match</h3>
<pre>const struct Symbol * <b>match</b> (const char *str, const char *end)</pre>
<div><a name = "Parser"><!-- --></a>
<h3>Parser</h3>
<pre>struct Parser * <b>Parser</b> (const char *str)</pre>
<dl>
<dt>return</dt>
<dd>Creates a parser for the string, <em>str</em>.</dd>
</dl>
</div>
<div><a name = "Parser_"><!-- --></a>
<h3>Parser_</h3>
<pre>void <b>Parser_</b> (struct Parser **const p_ptr)</pre>
<dl>
<dt>parameter: p_ptr</dt>
<dd>A pointer to the <em>Parser</em> that's to be destucted.</dd>
</dl>
</div>
<div><a name = "ParserRewind"><!-- --></a>
<h3>ParserRewind</h3>
<pre>void <b>ParserRewind</b> (struct Parser *p)</pre>
<p>
binary search
Resets the parser, <em>p</em>.
</p>
<dl>
</dl>
@ -77,11 +136,22 @@ binary search
<div><a name = "ParserParse"><!-- --></a>
<h3>ParserParse</h3>
<pre>int <b>ParserParse</b> (struct Parser *p, const struct Files *f, int invisible, FILE *fp)</pre>
<pre>int <b>ParserParse</b> (struct Parser *p, const struct Files *f, int invisible,
FILE *fp)</pre>
<p>
parse, called recusively (invisible, hack) fixme: this fn needs rewriting, messy
Parse, called recursively.
</p>
<dl>
<dt>parameter: f</dt>
<dd>Called in the handler to <em>ParserWidget</em>s.</dd>
<dt>parameter: invisible</dt>
<dd>No output.</dd>
<dt>parameter: fp</dt>
<dd>Output.</dd>
<dt>fixme</dt>
<dd>This fn needs rewriting; messy.</dd>
<dt>fixme</dt>
<dd>Invisible, hack.</dd>
</dl>
</div>

@ -25,13 +25,13 @@
padding: 4px;
}
</style>
<title></title>
<title>Parser</title>
</head>
<body>
<h1></h1>
<h1>Parser</h1>
<ul>
<li><a href = "#_declarations">Declarations</a></li>
@ -41,18 +41,85 @@
<p>
Copyright 2008, 2012 Neil Edelman, distributed under the terms of the
GNU General Public License, see copying.txt
GNU General Public License, see copying.txt
</p>
<p>
This is the main program. I didn't know what to call it.
<em>MakeIndex</em> is a simple content management system that generates static
content, (mostly index.html,) on all the directories rooted at the directory
specified by the argument. It is based on a template file, ".index.html" and
".newsfeed.rss". Also included are files to summarise the directory structure
for a <em>xml</em> site map, compatible with Google, and any <em>.news</em> for an <em>rss</em>
feed. It takes one argument, &lt;directory&gt;, which is the root of the recursion.
</p>
<p>
There should be an &lt;example&gt; directory that has a bunch of files in it. Run
<em>bin/MakeIndex example/</em>; it should make a webpage out of the directory
structure and <em>.index.html</em>, open <em>example/index.html</em> after running to see.
</p>
<ul>
<li>
If the <em>.index.html</em> file exists in the &lt;directory&gt;, prints &lt;index.html&gt;
recursively; overwrites any <em>index.html</em> on all the directories rooted at
&lt;directory&gt;;
</li>
<li>
if the <em>.sitemap.xml</em> file exists in &lt;directory&gt;, prints (and overwrites) an
index called <em>sitemap.xml</em>;
</li>
<li>
if the <em>.newsfeed.rss</em> file exists in &lt;directory&gt;, prints (and overwrites)
to <em>newsfeed.rss</em> all the <em>.news</em> files (if there are any.)
</li>
</ul>
<ul>
<li>
Treats <em>.d</em> as a description of the file without the <em>.d</em>;
if this is an empty text-file or a zero-byte file, it skips over this file.
</li>
<li>
treats <em>index.d</em> as a description of the directory;
</li>
<li>
treats <em>content.d</em> as an in-depth description of the directory,
replacing &lt;index.d&gt; when in the directory;
</li>
<li>
treats <em>.d.jpg</em> as a image that will go with the description;
</li>
<li>
treats <em>.news</em> as a newsworthy item; the format of this file is ISO 8601
date (YYYY-MM-DD,) next line title;
</li>
<li>
treats <em>.link</em> as a link with the href in the file.
</li>
</ul>
<p>
<em>.index.html</em>, <em>.sitemap.xml</em>, <em>.newsfeed.rss</em>, see <em>Parser</em> for recognised
symbols. Assumes '..' is the parent directory, '.' is the current directory,
and '/' is the directory separator; works for UNIX, MacOS, Windows.
If this is not the case, the constants are in <em>Files.c</em>.
</p>
<dl>
<dt>author:</dt>
<dt>minimum standard</dt>
<dd>C89/90</dd>
<dt>author</dt>
<dd>Neil</dd>
<dt>version:</dt>
<dd>1.0; 2016-09-19 Added umask</dd>
<dt>since:</dt>
<dd>1.0; 2008-03-27</dd>
<dt>version</dt>
<dd>1.1; 2017-03 fixed pedantic warnings; took out arg</dd>
<dt>since</dt>
<dd>1.0; 2016-09-19 Added umask
0.8; 2013-07 case-insensitive sort
0.7; 2012 sth.dsth.d handled properly
0.6; 2008-03-27</dd>
<dt>fixme</dt>
<dd>Don't have &lt;directory&gt; be an argument; just do it in the current.</dd>
<dt>fixme</dt>
<dd>Have a subset of LaTeX converted into html for the .d files?</dd>
<dt>fixme</dt>
<dd>Encoding is an issue; especially the newsfeed, 7bit.</dd>
<dt>fixme</dt>
<dd>It's not robust; eg @(files)<em>@(files){Don't do this.}</em>.</dd>
</dl>

@ -50,11 +50,221 @@
<table>
<tr><th>Return Type</th><th>Function Name</th><th>Argument List</th></tr>
<tr>
<td>int</td>
<td><a href = "#WidgetContent">WidgetContent</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetDate">WidgetDate</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetFilealt">WidgetFilealt</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetFiledesc">WidgetFiledesc</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetFilehref">WidgetFilehref</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetFileicon">WidgetFileicon</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetFilename">WidgetFilename</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetFiles">WidgetFiles</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetFilesize">WidgetFilesize</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetNews">WidgetNews</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetNewsname">WidgetNewsname</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetNow">WidgetNow</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetPwd">WidgetPwd</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetRoot">WidgetRoot</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
<tr>
<td>int</td>
<td><a href = "#WidgetTitle">WidgetTitle</a></td>
<td>const struct Files *f, FILE *fp</td>
</tr>
</table>
<a name = "_detail"><!-- --></a><h2>Function Detail</h2>
<div><a name = "WidgetContent"><!-- --></a>
<h3>WidgetContent</h3>
<pre>int <b>WidgetContent</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetDate"><!-- --></a>
<h3>WidgetDate</h3>
<pre>int <b>WidgetDate</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetFilealt"><!-- --></a>
<h3>WidgetFilealt</h3>
<pre>int <b>WidgetFilealt</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetFiledesc"><!-- --></a>
<h3>WidgetFiledesc</h3>
<pre>int <b>WidgetFiledesc</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetFilehref"><!-- --></a>
<h3>WidgetFilehref</h3>
<pre>int <b>WidgetFilehref</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetFileicon"><!-- --></a>
<h3>WidgetFileicon</h3>
<pre>int <b>WidgetFileicon</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetFilename"><!-- --></a>
<h3>WidgetFilename</h3>
<pre>int <b>WidgetFilename</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetFiles"><!-- --></a>
<h3>WidgetFiles</h3>
<pre>int <b>WidgetFiles</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetFilesize"><!-- --></a>
<h3>WidgetFilesize</h3>
<pre>int <b>WidgetFilesize</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetNews"><!-- --></a>
<h3>WidgetNews</h3>
<pre>int <b>WidgetNews</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetNewsname"><!-- --></a>
<h3>WidgetNewsname</h3>
<pre>int <b>WidgetNewsname</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetNow"><!-- --></a>
<h3>WidgetNow</h3>
<pre>int <b>WidgetNow</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetPwd"><!-- --></a>
<h3>WidgetPwd</h3>
<pre>int <b>WidgetPwd</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetRoot"><!-- --></a>
<h3>WidgetRoot</h3>
<pre>int <b>WidgetRoot</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
<div><a name = "WidgetTitle"><!-- --></a>
<h3>WidgetTitle</h3>
<pre>int <b>WidgetTitle</b> (const struct Files *f, FILE *fp)</pre>
<dl>
<dt>implements</dt>
<dd>ParserWidget</dd>
</dl>
</div>
</body>
</html>

@ -5,12 +5,11 @@
Files is a list of File (private class defiend below,) the Files can have
a relation to other Files by 'parent' and 'favourite' (@(pwd) uses this.)
@file Files
@title Files
@author Neil
@version 0.9; 2017-03 fixed pedantic warnings
@since 0.8; 2013-07 case-insensitive sort
0.7; 2012 sth.dsth.d handled properly
0.6; 2008-03-24 */
@std C89/90
@version 1.1; 2017-03 fixed pedantic warnings; took out arg
@since 0.6; 2008-03-24 */
#include <stdlib.h> /* malloc free */
#include <stdio.h> /* fprintf */
@ -20,9 +19,9 @@
#include "Files.h"
/* constants */
const char *dirCurrent = "."; /* used in multiple files */
const char *dirParent = "..";
static const int maxFilename = 128;
const char *dirCurrent = "."; /* used in multiple files */
const char *dirParent = "..";
static const size_t maxFilename = 128;
/* public */
struct Files {
@ -161,10 +160,10 @@ int FilesIsDir(const struct Files *files) {
/* this is just a list of filenames, (not public) "class File" */
static struct File *File(const char *name, const int size, const int isDir) {
int len;
size_t len;
struct File *file;
if(!name || !*name) { fprintf(stderr, "File: file has no name.\n"); return 0; }
if((len = strlen(name)) > maxFilename) { fprintf(stderr, "File: file name \"%s\" is too long (%d.)\n", name, maxFilename); return 0; }
if((len = strlen(name)) > maxFilename) { fprintf(stderr, "File: file name \"%s\" is too long (%lu.)\n", name, maxFilename); return 0; }
file = malloc(sizeof(struct File) + (len + 1));
if(!file) { File_(file); return 0; }
file->next = 0;

@ -1,6 +1,5 @@
/** See \see{Files}. */
struct Files;
struct Recursor;
/** Returns a boolean value. */
typedef int (*FilesFilter)(const struct Files *, const char *);

@ -1,8 +1,43 @@
/* Copyright 2008, 2012 Neil Edelman, distributed under the terms of the
GNU General Public License, see copying.txt */
GNU General Public License, see copying.txt
/* Parsing of strings.
* Created by Neil Edelman on 2008-03-21. */
Parsing of strings. '~' on a line by itself is recognised in ".newsfile.rss"
and ".sitemap.xml" as a <head>~<body>~<tail>; there should be two of them.
Parsed in ".index.html",
* @(content) prints {content.d}, or, if it is missing, {index.d};
* @(files){} repeats for all the files in the directory the contents of the
argument;
* @(htmlcontent);
* @(pwd){} prints the directory, the argument is the separator; eg, @(pwd){/};
* @(root){}, like @(pwd), except up instead of down;
* @(now) prints the date and the time in UTC.
Further parsed in @(files){} in ".index.html",
* @(filealt) prints Dir or File;
* @(filedesc);
* @(filehref) prints the filename or the {.link};
* @(fileicon) prints it's {.d.jpeg}, or if it is not there, {dir.jpeg} or
{file.jpeg};
* @(filename) prints the file name;
* @(filesize) prints the file size, if it exits;
* @(now) prints the date and the time in UTC.
Parsed in ".newsfeed.rss",
* @(date) prints the date on the news file;
* @(news) prints the contents of the file (as a text file);
* @(newsname) prints the filename of the news story;
* @(now) prints the date and the time in UTC;
* @(title) prints the second line in the {.news}.
@title Parser
@author Neil
@std C89/90
@version 1.1; 2017-03 fixed pedantic warnings; took out arg
@since 0.6; 2008-03-21 */
#include <stdio.h> /* [f]printf FILE */
#include <stdlib.h> /* malloc */
@ -22,11 +57,11 @@ struct Parser {
};
/* private - this is the list of 'widgets', see Widget.c - add widgets to here
to make them recognised - ASCIIbetical */
struct Symbol {
static const struct Symbol {
char *symbol;
int (*handler)(const struct Files *, FILE *fp);
const ParserWidget handler;
int onlyInRoot; /* fixme: not used, just trust the users? haha */
} static const sym[] = {
} sym[] = {
{ "content", &WidgetContent, 0 }, /* index */
{ "date", &WidgetDate, 0 }, /* news */
{ "filealt", &WidgetFilealt, 0 }, /* files */
@ -41,54 +76,52 @@ struct Symbol {
{ "news", &WidgetNews, 0 }, /* news */
{ "newsname", &WidgetNewsname, 0 }, /* news */
{ "now", &WidgetNow, 0 }, /* any */
{ "pwd", &WidgetPwd, -1 }, /* don't put it where it doesn't make sense */
{ "root", &WidgetRoot, -1 }, /* like pwd exept up instead of dn */
{ "pwd", &WidgetPwd, -1 }, /* index */
{ "root", &WidgetRoot, -1 }, /* like pwd except up instead of dn */
{ "title", &WidgetTitle, 0 } /* news */
};
/* private */
int parse(struct Parser *p, int invisible, FILE *fp);
static const struct Symbol *match(const char *str, const char *end);
/** @return Creates a parser for the string, {str}. */
struct Parser *Parser(const char *str) {
struct Parser *p;
p = malloc(sizeof(struct Parser));
if(!(p = malloc(sizeof *p))) return 0;
p->str = (char *)str;
p->pos = p->str;
p->rew = p->str;
p->recursion = 0;
return p;
}
void Parser_(struct Parser *p) {
if(!p) return;
/** @param p_ptr: A pointer to the {Parser} that's to be destucted. */
void Parser_(struct Parser **const p_ptr) {
struct Parser *p;
if(!p_ptr || !(p = *p_ptr)) return;
if(p->recursion) fprintf(stderr, "Parser~: a file was closed with recursion level of %d; syntax error?\n", p->recursion);
free(p);
*p_ptr = 0;
}
/** binary search */
const struct Symbol *match(const char *str, const char *end) {
const int N = sizeof(sym) / sizeof(struct Symbol);
int a, lenMatch, lenComp, lo = 0, mid, hi = N - 1;
lenMatch = end - str;
while(lo <= hi) {
mid = (lo + hi) >> 1;
/* this is highly inefficient */
lenComp = strlen(sym[mid].symbol);
a = strncmp(str, sym[mid].symbol, lenMatch > lenComp ? lenMatch : lenComp);
if (a < 0) hi = mid - 1;
else if(a > 0) lo = mid + 1;
else return &sym[mid];
}
return 0;
}
/** Resets the parser, {p}. */
void ParserRewind(struct Parser *p) {
if(!p) return;
p->pos = p->rew;
}
/** parse, called recusively (invisible, hack) fixme: this fn needs rewriting, messy */
int ParserParse(struct Parser *p, const struct Files *f, int invisible, FILE *fp) {
/** Parse, called recursively.
@param f: Called in the handler to {ParserWidget}s.
@param invisible: No output.
@param fp: Output.
@fixme This fn needs rewriting; messy.
@fixme Invisible, hack. */
int ParserParse(struct Parser *p, const struct Files *f, int invisible,
FILE *fp) {
char *mark;
if(!p || !fp || !p->pos) return 0;
if(++p->recursion > maxRecursion) fprintf(stderr, "Parser::parse: %d recursion levels reached (Ctrl-C to stop!)\n", p->recursion);
if(++p->recursion > maxRecursion) fprintf(stderr, "Parser::parse: %d "
"recursion levels reached (Ctrl-C to stop!)\n", p->recursion);
mark = p->pos;
if(p->recursion == 1) p->rew = mark;
for( ; ; ) {
@ -117,7 +150,8 @@ int ParserParse(struct Parser *p, const struct Files *f, int invisible, FILE *fp
char *start = p->pos + 2, *end;
if(!invisible) fprintf(fp, "%.*s", (int)(p->pos - mark), mark);
if(!(end = strpbrk(start, ")"))) break; /* syntax error */
if(!(m = match(start, end))) fprintf(stderr, "Parser::parse: symbol not reconised, '%.*s.'\n", (int)(end - start), start);
if(!(m = match(start, end))) fprintf(stderr, "Parser::parse: "
"symbol not reconised, '%.*s.'\n", (int)(end - start), start);
/* if(p->recursion == 1 && m && m->onlyInRoot) return -1; ? */
open = *(end + 1) == '{' ? -1 : 0;
do {
@ -135,3 +169,22 @@ int ParserParse(struct Parser *p, const struct Files *f, int invisible, FILE *fp
p->recursion--;
return 0;
}
/** binary search */
static const struct Symbol *match(const char *str, const char *end) {
const int N = sizeof(sym) / sizeof(struct Symbol);
int a, lo = 0, mid, hi = N - 1;
size_t lenMatch, lenComp;
lenMatch = end - str;
while(lo <= hi) {
mid = (lo + hi) >> 1;
/* this is highly inefficient */
lenComp = strlen(sym[mid].symbol);
a = strncmp(str, sym[mid].symbol,
lenMatch > lenComp ? lenMatch : lenComp);
if (a < 0) hi = mid - 1;
else if(a > 0) lo = mid + 1;
else return &sym[mid];
}
return 0;
}

@ -1,7 +1,14 @@
/** See \see{Parser}. */
struct Parser;
/** Dependancy on {Files} */
struct Files;
/** All {ParserWidget}s are in {Widget.c} */
typedef int (*ParserWidget)(const struct Files *, FILE *fp);
struct Parser *Parser(const char *str);
void Parser_(struct Parser *p);
void Parser_(struct Parser **const p_ptr);
void ParserRewind(struct Parser *p);
int ParserParse(struct Parser *p, const struct Files *f, int invisible, FILE *fp);
int ParserParse(struct Parser *p, const struct Files *f, int invisible,
FILE *fp);

@ -1,11 +1,52 @@
/** Copyright 2008, 2012 Neil Edelman, distributed under the terms of the
GNU General Public License, see copying.txt
This is the main program. I didn't know what to call it.
{MakeIndex} is a simple content management system that generates static
content, (mostly index.html,) on all the directories rooted at the directory
specified by the argument. It is based on a template file, ".index.html" and
".newsfeed.rss". Also included are files to summarise the directory structure
for a {xml} site map, compatible with Google, and any {.news} for an {rss}
feed. It takes one argument, <directory>, which is the root of the recursion.
There should be an <example> directory that has a bunch of files in it. Run
{bin/MakeIndex example/}; it should make a webpage out of the directory
structure and {.index.html}, open {example/index.html} after running to see.
* If the {.index.html} file exists in the <directory>, prints <index.html>
recursively; overwrites any {index.html} on all the directories rooted at
<directory>;
* if the {.sitemap.xml} file exists in <directory>, prints (and overwrites) an
index called {sitemap.xml};
* if the {.newsfeed.rss} file exists in <directory>, prints (and overwrites)
to {newsfeed.rss} all the {.news} files (if there are any.)
* Treats {.d} as a description of the file without the {.d};
if this is an empty text-file or a zero-byte file, it skips over this file.
* treats {index.d} as a description of the directory;
* treats {content.d} as an in-depth description of the directory,
replacing <index.d> when in the directory;
* treats {.d.jpg} as a image that will go with the description;
* treats {.news} as a newsworthy item; the format of this file is ISO 8601
date (YYYY-MM-DD,) next line title;
* treats {.link} as a link with the href in the file.
{.index.html}, {.sitemap.xml}, {.newsfeed.rss}, see {Parser} for recognised
symbols. Assumes '..' is the parent directory, '.' is the current directory,
and '/' is the directory separator; works for UNIX, MacOS, Windows.
If this is not the case, the constants are in {Files.c}.
@title Parser
@author Neil
@version 1.0; 2016-09-19 Added umask
@since 1.0; 2008-03-27 */
@std C89/90
@version 1.1; 2017-03 fixed pedantic warnings; took out arg
@since 1.0; 2016-09-19 Added umask
0.8; 2013-07 case-insensitive sort
0.7; 2012 sth.dsth.d handled properly
0.6; 2008-03-27
@fixme Don't have <directory> be an argument; just do it in the current.
@fixme Have a subset of LaTeX converted into html for the .d files?
@fixme Encoding is an issue; especially the newsfeed, 7bit.
@fixme It's not robust; eg @(files){@(files){Don't do this.}}. */
#include <stdlib.h> /* malloc free fgets */
#include <stdio.h> /* fprintf FILE */
@ -38,7 +79,7 @@ void usage(const char *programme);
/* constants */
static const int versionMajor = 0;
static const int versionMinor = 8;
static const int granularity = 1024;
static const size_t granularity= 1024;
static const int maxRead = 0x1000;
const char *htmlIndex = "index.html"; /* in multiple files */
static const char *xmlSitemap = "sitemap.xml";
@ -50,15 +91,15 @@ static const char *tmplNewsfeed= ".newsfeed.rss";
extern const char *dirCurrent;
extern const char *dirParent;
/* in Widget.c */
extern const char *desc;
extern const char *news;
extern const char *dot_desc;
extern const char *dot_news;
/* there can only be one recursor at a time, sorry */
static struct Recursor *r = 0;
/* public */
struct Recursor *Recursor(const char *index, const char *map, const char *news) {
if(!index || !index || !map || !news) return 0;
struct Recursor *Recursor(const char *idx, const char *map, const char *news) {
if(!idx || !idx || !map || !news) return 0;
if(r) { fprintf(stderr, "Recursor: there is already a Recursor.\n"); return 0; }
r = malloc(sizeof(struct Recursor));
if(!r) { perror("recursor"); Recursor_(); return 0; }
@ -74,8 +115,8 @@ struct Recursor *Recursor(const char *index, const char *map, const char *news)
if(!(r->sitemap = fopen(xmlSitemap, "w"))) perror(xmlSitemap);
if(!(r->newsfeed = fopen(rssNewsfeed, "w"))) perror(rssNewsfeed);
/* read from the input files */
if( !(r->indexString = readFile(index))) {
fprintf(stderr, "Recursor: to make an index, create the file <%s>.\n", index);
if( !(r->indexString = readFile(idx))) {
fprintf(stderr, "Recursor: to make an index, create the file <%s>.\n", idx);
}
if(r->sitemap && !(r->sitemapString = readFile(map))) {
fprintf(stderr, "Recursor: to make an sitemap, create the file <%s>.\n", map);
@ -85,7 +126,7 @@ struct Recursor *Recursor(const char *index, const char *map, const char *news)
}
/* create Parsers attached to them */
if(r->indexString && !(r->indexParser = Parser(r->indexString))) {
fprintf(stderr, "Recursor: error generating Parser from <%s>.\n", index);
fprintf(stderr, "Recursor: error generating Parser from <%s>.\n", idx);
}
if(r->sitemapString && !(r->sitemapParser = Parser(r->sitemapString))) {
fprintf(stderr, "Recursor: error generating Parser from <%s>.\n", map);
@ -117,11 +158,11 @@ void Recursor_(void) {
ParserParse(r->newsfeedParser, 0, 0, r->newsfeed);
}
if(r->newsfeed && fclose(r->newsfeed)) perror(rssNewsfeed);
Parser_(r->indexParser);
Parser_(&r->indexParser);
free(r->indexString);
Parser_(r->sitemapParser);
Parser_(&r->sitemapParser);
free(r->sitemapString);
Parser_(r->newsfeedParser);
Parser_(&r->newsfeedParser);
free(r->newsfeedString);
free(r);
r = 0;
@ -137,7 +178,7 @@ int main(int argc, char **argv) {
if(chdir(argv[1])) { perror(argv[1]); return EXIT_FAILURE; }
/* make sure that umask is set so that others can read what we create */
umask(S_IWGRP | S_IWOTH);
umask((mode_t)(S_IWGRP | S_IWOTH));
/* recursing; fixme: this should be configurable */
if(!Recursor(tmplIndex, tmplSitemap, tmplNewsfeed)) return EXIT_FAILURE;
@ -153,13 +194,13 @@ int filter(const struct Files *files, const char *fn) {
FILE *fd;
if(!r) { fprintf(stderr, "Recusor::filter: recursor not initialised.\n"); return 0; }
/* *.d[.0]* */
for(str = (char *)fn; (str = strstr(str, desc)); ) {
str += strlen(desc);
for(str = (char *)fn; (str = strstr(str, dot_desc)); ) {
str += strlen(dot_desc);
if(*str == '\0' || *str == '.') return 0;
}
/* *.news$ */
if((str = strstr(fn, news))) {
str += strlen(news);
if((str = strstr(fn, dot_news))) {
str += strlen(dot_news);
if(*str == '\0') {
if(WidgetSetNews(fn) && ParserParse(r->newsfeedParser, files, 0, r->newsfeed)) {
ParserRewind(r->newsfeedParser);
@ -176,12 +217,12 @@ int filter(const struct Files *files, const char *fn) {
/* index.html */
if(!strcmp(fn, htmlIndex)) return 0;
/* add .d, check 1 line for \n (hmm, this must be a real time waster) */
if(strlen(fn) > sizeof(filed) - strlen(desc) - 1) {
if(strlen(fn) > sizeof(filed) - strlen(dot_desc) - 1) {
fprintf(stderr, "Recusor::filter: regected '%s' because it was too long (%d.)\n", fn, (int)sizeof(filed));
return 0;
}
strcpy(filed, fn);
strcat(filed, desc);
strcat(filed, dot_desc);
if((fd = fopen(filed, "r"))) {
int ch = fgetc(fd);
if(ch == '\n' || ch == '\r' || ch == EOF) {
@ -223,19 +264,20 @@ int recurse(const struct Files *parent) {
}
char *readFile(const char *filename) {
char *buf = 0, *newBuf;
int bufPos = 0, bufSize = 0, read;
size_t bufPos = 0, bufSize = 0, rd;
FILE *fp;
if(!filename) return 0;
if(!(fp = fopen(filename, "r"))) { perror(filename); return 0; }
for( ; ; ) {
newBuf = realloc(buf, (bufSize += granularity) * sizeof(char));
if(!newBuf) { perror(filename); free(buf); return 0; }
buf = newBuf;
read = fread(buf + bufPos, sizeof(char), granularity, fp);
bufPos += read;
if(read < granularity) { buf[bufPos] = '\0'; break; }
buf = newBuf;
rd = fread(buf + bufPos, sizeof(char), granularity, fp);
bufPos += rd;
if(rd < granularity) { buf[bufPos] = '\0'; break; }
}
fprintf(stderr, "Opened '%s' and alloted %d bytes to read %d characters.\n", filename, bufSize, bufPos);
fprintf(stderr, "Opened '%s' and alloted %lu bytes to read %lu "
"characters.\n", filename, bufSize, bufPos);
if(fclose(fp)) perror(filename);
return buf; /** you must free() the memory! */
}

@ -1,13 +1,18 @@
/* Copyright 2008, 2012 Neil Edelman, distributed under the terms of the
GNU General Public License, see copying.txt */
GNU General Public License, see copying.txt
/* Widgets like @files @pwd. How to create a widget?
1. stick the code below, it's prototype must be the same, int(FILE *), where
the FILE* is called with the output file
2. create a prototype in Widget.h
3. in Parser.c, add to the symbol table, sym[] with the symbol you want, in
ASCIIbetical order
* Created by Neil Edelman on 2008-03-25. */
Widgets like @files @pwd. To create a widget:
* stick the code below implementing {ParserWidget};
* create a prototype in Widget.h;
* in {Parser.c}, add to the symbol table, sym[] with the symbol you want, in
ASCIIbetical order.
@title Widget
@author Neil
@std C89/90
@version 1.1; 2017-03 fixed pedantic warnings; took out arg
@since 0.6; 2008-03-25 */
#include <string.h> /* strncat strncpy */
#include <stdio.h> /* fprintf FILE */
@ -17,15 +22,28 @@
#include "Recursor.h"
#include "Widget.h"
/* <-- ugly */
#ifndef _MSC_VER /* <-- not msvc */
#define UNUSED(a) while(0 && (a))
#else /* not msvc --><-- msvc: not a C89/90 compiler; needs a little help */
#pragma warning(push)
/* "Assignment within conditional expression." No. */
#pragma warning(disable: 4706)
/* "<ANSI/ISO name>: The POSIX name for this item is deprecated." No. */
#pragma warning(disable: 4996)
#define UNUSED(a) (void)(sizeof((a), 0))
#endif /* msvc --> */
/* ugly --> */
/* constants */
static const char *htmlDesc = "index.d";
static const char *htmlContent = "content.d";
static const char *separator = "/";
static const char *picture = ".jpeg"; /* yeah, I hard coded this */
static const size_t maxRead = 512;
static const char *link = ".link";
const char *desc = ".d"; /* used in multiple files */
const char *news = ".news";
static const char *dot_link = ".link";
const char *dot_desc = ".d"; /* used in multiple files */
const char *dot_news = ".news";
extern const char *dirCurrent;
extern const char *dirParent;
extern const char *htmlIndex;
@ -38,15 +56,17 @@ static char title[64] = "(no title)";
static char filenews[64] = "(no file name)";
/* private */
int correctNo(int no, const int low, const int high);
int clip(int no, const int low, const int high);
int WidgetSetNews(const char *fn) {
char *dot;
int read, tLen;
int read;
size_t tLen;
FILE *fp;
if(!fn || !(dot = strstr(fn, news))) return 0;
if(!fn || !(dot = strstr(fn, dot_news))) return 0;
if(strlen(fn) > sizeof(filenews) / sizeof(char) - sizeof(char)) {
fprintf(stderr, "Widget::SetNews: news file name, \"%s,\" doesn't fit in buffer (%d.)\n", fn, (int)(sizeof(filenews) / sizeof(char)));
fprintf(stderr, "Widget::SetNews: news file name, \"%s,\" doesn't fit "
"in buffer (%d.)\n", fn, (int)(sizeof(filenews) / sizeof(char)));
return 0;
}
/* save the fn, safe because we checked it, and strip off .news */
@ -55,44 +75,56 @@ int WidgetSetNews(const char *fn) {
/* open .news */
if(!(fp = fopen(fn, "r"))) { perror(fn); return 0; }
read = fscanf(fp, "%d-%d-%d\n", &year, &month, &day);
if(read < 3) fprintf(stderr, "Widget::setNews: error parsing ISO 8601 date, <YYYY-MM-DD>.\n");
month = correctNo(month, 1, 12);
day = correctNo(day, 1, 31);
if(read < 3) fprintf(stderr, "Widget::setNews: error parsing ISO 8601 "
"date, <YYYY-MM-DD>.\n");
month = clip(month, 1, 12);
day = clip(day, 1, 31);
/* fgets reads a newline at the end (annoying) so we strip that off */
if(!fgets(title, sizeof(title), fp)) { perror(fn); *title = '\0'; }
else if((tLen = strlen(title)) > 0 && title[tLen - 1] == '\n') title[tLen - 1] = '\0';
if(!fgets(title, (int)sizeof(title), fp)) { perror(fn); *title = '\0'; }
else if((tLen = strlen(title)) > 0 && title[tLen - 1] == '\n')
title[tLen - 1] = '\0';
if(fclose(fp)) perror(fn);
fprintf(stderr, "News <%s>, '%s' %d-%d-%d.\n", filenews, title, year, month, day);
fprintf(stderr, "News <%s>, '%s' %d-%d-%d.\n", filenews, title, year,
month, day);
return -1;
}
/* the widget handlers */
/** @implements ParserWidget */
int WidgetContent(const struct Files *f, FILE *fp) {
char buf[81], *bufpos;
int i;
size_t i;
FILE *in;
UNUSED(f);
/* it's a nightmare to test if this is text (which most is,) in which case
we should insert <p>...</p> after every paragraph, <>& -> &lt;&gt;&amp;,
but we have to not translate already encoded html; the only solution that
a could see is have a new langauge (like-LaTeX) that gracefully handles
plain-text */
if((in = fopen(htmlContent, "r")) || (in = fopen(htmlDesc, "r"))) {
for(i = 0; (i < maxRead) && (bufpos = fgets(buf, sizeof(buf), in)); i++) {
for(i = 0; (i < maxRead)
&& (bufpos = fgets(buf, (int)sizeof(buf), in)); i++) {
fprintf(fp, "%s", bufpos);
}
if(fclose(in)) perror(htmlDesc);
}
return 0;
}
/** @implements ParserWidget */
int WidgetDate(const struct Files *f, FILE *fp) {
UNUSED(f);
/* ISO 8601 - YYYY-MM-DD */
fprintf(fp, "%4.4d-%2.2d-%2.2d", year, month, day);
return 0;
}
/** @implements ParserWidget */
int WidgetFilealt(const struct Files *f, FILE *fp) {
fprintf(fp, "%s", FilesIsDir(f) ? "Dir" : "File");
return 0;
}
/** @implements ParserWidget */
int WidgetFiledesc(const struct Files *f, FILE *fp) {
char buf[256], *name;
FILE *in;
@ -100,29 +132,32 @@ int WidgetFiledesc(const struct Files *f, FILE *fp) {
if(FilesIsDir(f)) {
/* <file>/index.d */
strncpy(buf, name, sizeof(buf) - 9);
strncat(buf, separator, 1);
strncat(buf, htmlDesc, 7);
strncat(buf, separator, 1lu);
strncat(buf, htmlDesc, 7lu);
} else {
/* <file>.d */
strncpy(buf, name, sizeof(buf) - 6);
strncat(buf, desc, 5);
strncat(buf, dot_desc, 5lu);
}
if((in = fopen(buf, "r"))) {
char *bufpos;
int i;
for(i = 0; (i < maxRead) && (bufpos = fgets(buf, sizeof(buf), in)); i++) {
size_t i;
for(i = 0; (i < maxRead)
&& (bufpos = fgets(buf, (int)sizeof(buf), in)); i++) {
fprintf(fp, "%s", bufpos);
}
if(fclose(in)) perror(buf);
}
return 0;
}
/** @implements ParserWidget */
int WidgetFilehref(const struct Files *f, FILE *fp) {
char *str, *name;
int ch;
FILE *fhref;
if(!(name = FilesName(f))) return 0;
if((str = strstr(name, link)) && *(str += strlen(link)) == '\0' && (fhref = fopen(name, "r"))) {
if((str = strstr(name, dot_link)) && *(str += strlen(dot_link)) == '\0'
&& (fhref = fopen(name, "r"))) {
/* fixme: not too good, with the reading one char at a time */
for( ; ; ) {
ch = fgetc(fhref);
@ -135,14 +170,15 @@ int WidgetFilehref(const struct Files *f, FILE *fp) {
}
return 0;
}
/** @implements ParserWidget */
int WidgetFileicon(const struct Files *f, FILE *fp) {
char buf[256], *name;
FILE *in;
if(!(name = FilesName(f))) return 0;
/* insert <file>.d.jpeg if available */
strncpy(buf, name, sizeof(buf) - 12);
strncat(buf, desc, 5);
strncat(buf, picture, 6);
strncat(buf, dot_desc,5lu);
strncat(buf, picture, 6lu);
if((in = fopen(buf, "r"))) {
fprintf(fp, "%s", buf);
if(fclose(in)) perror(buf);
@ -158,44 +194,57 @@ int WidgetFileicon(const struct Files *f, FILE *fp) {
}
return 0;
}
/** @implements ParserWidget */
int WidgetFilename(const struct Files *f, FILE *fp) {
fprintf(fp, "%s", FilesName(f));
return 0;
}
/** @implements ParserWidget */
int WidgetFiles(const struct Files *f, FILE *fp) {
while(0 && fp);
return FilesAdvance((struct Files *)f) ? -1 : 0;
}
/** @implements ParserWidget */
int WidgetFilesize(const struct Files *f, FILE *fp) { /* eww */
if(!FilesIsDir(f)) fprintf(fp, " (%d KB)", FilesSize(f));
return 0;
}
/** @implements ParserWidget */
int WidgetNews(const struct Files *f, FILE *fp) {
char buf[256], *bufpos;
int i;
size_t i;
FILE *in;
UNUSED(f);
if(!filenews[0]) return 0;
if(!(in = fopen(filenews, "r"))) { perror(filenews); return 0; }
for(i = 0; (i < maxRead) && (bufpos = fgets(buf, sizeof(buf), in)); i++) {
for(i = 0; (i < maxRead)&&(bufpos = fgets(buf, (int)sizeof(buf), in)); i++){
fprintf(fp, "%s", bufpos);
}
if(fclose(in)) perror(filenews);
return 0;
}
/** @implements ParserWidget */
int WidgetNewsname(const struct Files *f, FILE *fp) {
UNUSED(f);
fprintf(fp, "%s", filenews);
return 0;
}
/** @implements ParserWidget */
int WidgetNow(const struct Files *f, FILE *fp) {
char t[22];
time_t currentTime;
struct tm *formatedTime;
UNUSED(f);
if((currentTime = time(0)) == (time_t)(-1)) { perror("@date"); return 0; }
formatedTime = gmtime(&currentTime);
/* ISO 8601 - YYYY-MM-DDThh:mm:ssTZD */
strftime(t, 21, "%Y-%m-%dT%H:%M:%SZ", formatedTime);
strftime(t, 21lu, "%Y-%m-%dT%H:%M:%SZ", formatedTime);
fprintf(fp, "%s", t);
return 0;
}
/** @implements ParserWidget */
int WidgetPwd(const struct Files *f, FILE *fp) {