|
|
|
@ -37,8 +37,8 @@
|
|
|
|
|
|
|
|
|
|
@title Parser
|
|
|
|
|
@author Neil
|
|
|
|
|
@std C89/90
|
|
|
|
|
@version 1.1; 2017-03 fixed pedantic warnings; took out arg
|
|
|
|
|
@std POSIX
|
|
|
|
|
@version 1.1; 2017-03 fixed pedantic warnings; command-line improvements
|
|
|
|
|
@since 1.0; 2016-09-19 Added umask
|
|
|
|
|
0.8; 2013-07 case-insensitive sort
|
|
|
|
|
0.7; 2012 sth.dsth.d handled properly
|
|
|
|
@ -48,16 +48,37 @@
|
|
|
|
|
@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 */
|
|
|
|
|
#include <string.h> /* strcmp */
|
|
|
|
|
#include <unistd.h> /* chdir (POSIX, not ANSI) */
|
|
|
|
|
#include <sys/stat.h> /* umask */
|
|
|
|
|
#include <stdlib.h> /* malloc free fgets */
|
|
|
|
|
#include <stdio.h> /* fprintf FILE */
|
|
|
|
|
#include <string.h> /* strcmp */
|
|
|
|
|
#include <unistd.h> /* chdir (POSIX, not ANSI) */
|
|
|
|
|
#include <sys/types.h> /* mode_t (umask) */
|
|
|
|
|
#include <sys/stat.h> /* umask */
|
|
|
|
|
#include "Files.h"
|
|
|
|
|
#include "Widget.h"
|
|
|
|
|
#include "Parser.h"
|
|
|
|
|
#include "Recursor.h"
|
|
|
|
|
|
|
|
|
|
/* constants */
|
|
|
|
|
static const char *const programme = "MakeIndex";
|
|
|
|
|
static const int version_major = 1;
|
|
|
|
|
static const int version_minor = 1;
|
|
|
|
|
|
|
|
|
|
static const size_t granularity = 1024;
|
|
|
|
|
static const int max_read = 0x1000;
|
|
|
|
|
const char *html_index = "index.html"; /* in multiple files */
|
|
|
|
|
static const char *xml_sitemap = "sitemap.xml";
|
|
|
|
|
static const char *rss_newsfeed = "newsfeed.rss";
|
|
|
|
|
static const char *template_index = ".index.html";
|
|
|
|
|
static const char *template_sitemap = ".sitemap.xml";
|
|
|
|
|
static const char *template_newsfeed = ".newsfeed.rss";
|
|
|
|
|
/* in Files.c */
|
|
|
|
|
extern const char *dir_current;
|
|
|
|
|
extern const char *dir_parent;
|
|
|
|
|
/* in Widget.c */
|
|
|
|
|
extern const char *dot_desc;
|
|
|
|
|
extern const char *dot_news;
|
|
|
|
|
|
|
|
|
|
/* public */
|
|
|
|
|
struct Recursor {
|
|
|
|
|
char *indexString;
|
|
|
|
@ -71,36 +92,24 @@ struct Recursor {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* private */
|
|
|
|
|
int filter(const struct Files *, const char *fn);
|
|
|
|
|
int recurse(const struct Files *parent);
|
|
|
|
|
char *readFile(const char *filename);
|
|
|
|
|
void usage(const char *programme);
|
|
|
|
|
|
|
|
|
|
/* constants */
|
|
|
|
|
static const int versionMajor = 0;
|
|
|
|
|
static const int versionMinor = 8;
|
|
|
|
|
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";
|
|
|
|
|
static const char *rssNewsfeed = "newsfeed.rss";
|
|
|
|
|
static const char *tmplIndex = ".index.html";
|
|
|
|
|
static const char *tmplSitemap = ".sitemap.xml";
|
|
|
|
|
static const char *tmplNewsfeed= ".newsfeed.rss";
|
|
|
|
|
/* in Files.c */
|
|
|
|
|
extern const char *dirCurrent;
|
|
|
|
|
extern const char *dirParent;
|
|
|
|
|
/* in Widget.c */
|
|
|
|
|
extern const char *dot_desc;
|
|
|
|
|
extern const char *dot_news;
|
|
|
|
|
static int filter(struct Files *const files, const char *fn);
|
|
|
|
|
static int recurse(struct Files *const parent);
|
|
|
|
|
static char *readFile(const char *filename);
|
|
|
|
|
static void usage(void);
|
|
|
|
|
|
|
|
|
|
/* there can only be one recursor at a time, sorry */
|
|
|
|
|
static struct Recursor *r = 0;
|
|
|
|
|
|
|
|
|
|
/* public */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@param idx: file name of the prototype index file, eg, ".index.html".
|
|
|
|
|
@param map: file name of the prototype map file, eg, ".sitmap.xml".
|
|
|
|
|
@param news: file name of the prototype news file, eg, ".newsfeed.rss". */
|
|
|
|
|
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; }
|
|
|
|
|
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; }
|
|
|
|
|
r->indexString = 0;
|
|
|
|
@ -112,52 +121,58 @@ struct Recursor *Recursor(const char *idx, const char *map, const char *news) {
|
|
|
|
|
r->newsfeedString = 0;
|
|
|
|
|
r->newsfeedParser = 0;
|
|
|
|
|
/* open the files for writing (index is opened multiple times in the directories) */
|
|
|
|
|
if(!(r->sitemap = fopen(xmlSitemap, "w"))) perror(xmlSitemap);
|
|
|
|
|
if(!(r->newsfeed = fopen(rssNewsfeed, "w"))) perror(rssNewsfeed);
|
|
|
|
|
if(!(r->sitemap = fopen(xml_sitemap, "w"))) perror(xml_sitemap);
|
|
|
|
|
if(!(r->newsfeed = fopen(rss_newsfeed, "w"))) perror(rss_newsfeed);
|
|
|
|
|
/* read from the input files */
|
|
|
|
|
if( !(r->indexString = readFile(idx))) {
|
|
|
|
|
fprintf(stderr, "Recursor: to make an index, create the file <%s>.\n", 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);
|
|
|
|
|
fprintf(stderr, "Recursor: to make an sitemap, create the file <%s>.\n",
|
|
|
|
|
map);
|
|
|
|
|
}
|
|
|
|
|
if(r->newsfeed && !(r->newsfeedString = readFile(news))) {
|
|
|
|
|
fprintf(stderr, "Recursor: to make a newsfeed, create the file <%s>.\n", news);
|
|
|
|
|
fprintf(stderr, "Recursor: to make a newsfeed, create the file <%s>.\n",
|
|
|
|
|
news);
|
|
|
|
|
}
|
|
|
|
|
/* create Parsers attached to them */
|
|
|
|
|
if(r->indexString && !(r->indexParser = Parser(r->indexString))) {
|
|
|
|
|
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);
|
|
|
|
|
fprintf(stderr, "Recursor: error generating Parser from <%s>.\n", map);
|
|
|
|
|
}
|
|
|
|
|
if(r->newsfeedString && !(r->newsfeedParser = Parser(r->newsfeedString))) {
|
|
|
|
|
fprintf(stderr, "Recursor: error generating Parser from <%s>.\n", news);
|
|
|
|
|
}
|
|
|
|
|
/* if theirs no content, we have nothing to do */
|
|
|
|
|
if(!r->indexParser && !r->sitemapParser && !r->newsfeedParser) {
|
|
|
|
|
fprintf(stderr, "Recursor: no Parsers defined, it would be useless to continue.\n");
|
|
|
|
|
fprintf(stderr, "Recursor: no Parsers defined, it would be useless to "
|
|
|
|
|
"continue.\n");
|
|
|
|
|
Recursor_();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/* parse the "header," ie, everything up to ~, the second arg is null
|
|
|
|
|
because we haven't set up the Files, so @files{}, @pwd{}, etc are undefined */
|
|
|
|
|
because we haven't set up the Files, so @files{}, @pwd{}, etc are
|
|
|
|
|
undefined */
|
|
|
|
|
ParserParse(r->sitemapParser, 0, 0, r->sitemap);
|
|
|
|
|
ParserParse(r->newsfeedParser, 0, 0, r->newsfeed);
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Recursor_(void) {
|
|
|
|
|
if(!r) return;
|
|
|
|
|
if(r->sitemapParser && r->sitemap) {
|
|
|
|
|
ParserParse(r->sitemapParser, 0, -1, r->sitemap);
|
|
|
|
|
ParserParse(r->sitemapParser, 0, 0, r->sitemap);
|
|
|
|
|
}
|
|
|
|
|
if(r->sitemap && fclose(r->sitemap)) perror(xmlSitemap);
|
|
|
|
|
if(r->sitemap && fclose(r->sitemap)) perror(xml_sitemap);
|
|
|
|
|
if(r->newsfeedParser && r->newsfeed) {
|
|
|
|
|
ParserParse(r->newsfeedParser, 0, -1, r->newsfeed);
|
|
|
|
|
ParserParse(r->newsfeedParser, 0, 0, r->newsfeed);
|
|
|
|
|
}
|
|
|
|
|
if(r->newsfeed && fclose(r->newsfeed)) perror(rssNewsfeed);
|
|
|
|
|
if(r->newsfeed && fclose(r->newsfeed)) perror(rss_newsfeed);
|
|
|
|
|
Parser_(&r->indexParser);
|
|
|
|
|
free(r->indexString);
|
|
|
|
|
Parser_(&r->sitemapParser);
|
|
|
|
@ -168,33 +183,23 @@ void Recursor_(void) {
|
|
|
|
|
r = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* entry-point (shouldn't have a prototype) */
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* set up; fixme: dangerous! use stdarg, have a -delete, -write, and -help */
|
|
|
|
|
if(argc <= 1) { usage(argv[0]); return EXIT_SUCCESS; }
|
|
|
|
|
fprintf(stderr, "Changing directory to <%s>.\n", argv[1]);
|
|
|
|
|
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((mode_t)(S_IWGRP | S_IWOTH));
|
|
|
|
|
|
|
|
|
|
/* recursing; fixme: this should be configurable */
|
|
|
|
|
if(!Recursor(tmplIndex, tmplSitemap, tmplNewsfeed)) return EXIT_FAILURE;
|
|
|
|
|
ret = recurse(0);
|
|
|
|
|
Recursor_();
|
|
|
|
|
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
|
/** Actually does the recursion. */
|
|
|
|
|
int RecursorGo(void) {
|
|
|
|
|
if(!r) return 0;
|
|
|
|
|
return recurse(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* private */
|
|
|
|
|
|
|
|
|
|
int filter(const struct Files *files, const char *fn) {
|
|
|
|
|
char *str, filed[64];
|
|
|
|
|
/** @implements FilesFilter */
|
|
|
|
|
static int filter(struct Files *const files, const char *fn) {
|
|
|
|
|
const char *str;
|
|
|
|
|
char filed[64];
|
|
|
|
|
FILE *fd;
|
|
|
|
|
if(!r) { fprintf(stderr, "Recusor::filter: recursor not initialised.\n"); return 0; }
|
|
|
|
|
if(!r) { fprintf(stderr, "Recusor::filter: recursor not initialised.\n");
|
|
|
|
|
return 0; }
|
|
|
|
|
/* *.d[.0]* */
|
|
|
|
|
for(str = (char *)fn; (str = strstr(str, dot_desc)); ) {
|
|
|
|
|
for(str = fn; (str = strstr(str, dot_desc)); ) {
|
|
|
|
|
str += strlen(dot_desc);
|
|
|
|
|
if(*str == '\0' || *str == '.') return 0;
|
|
|
|
|
}
|
|
|
|
@ -211,11 +216,11 @@ int filter(const struct Files *files, const char *fn) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* . */
|
|
|
|
|
if(!strcmp(fn, dirCurrent)) return 0;
|
|
|
|
|
if(!strcmp(fn, dir_current)) return 0;
|
|
|
|
|
/* .. */
|
|
|
|
|
if(!strcmp(fn, dirParent) && FilesIsRoot(files)) return 0;
|
|
|
|
|
if(!strcmp(fn, dir_parent) && FilesIsRoot(files)) return 0;
|
|
|
|
|
/* index.html */
|
|
|
|
|
if(!strcmp(fn, htmlIndex)) return 0;
|
|
|
|
|
if(!strcmp(fn, html_index)) return 0;
|
|
|
|
|
/* add .d, check 1 line for \n (hmm, this must be a real time waster) */
|
|
|
|
|
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));
|
|
|
|
@ -233,17 +238,18 @@ int filter(const struct Files *files, const char *fn) {
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
int recurse(const struct Files *parent) {
|
|
|
|
|
|
|
|
|
|
static int recurse(struct Files *const parent) {
|
|
|
|
|
struct Files *f;
|
|
|
|
|
char *name;
|
|
|
|
|
const char *name;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
f = Files(parent, &filter);
|
|
|
|
|
/* write the index */
|
|
|
|
|
if((fp = fopen(htmlIndex, "w"))) {
|
|
|
|
|
if((fp = fopen(html_index, "w"))) {
|
|
|
|
|
ParserParse(r->indexParser, f, 0, fp);
|
|
|
|
|
ParserRewind(r->indexParser);
|
|
|
|
|
fclose(fp);
|
|
|
|
|
} else perror(htmlIndex);
|
|
|
|
|
} else perror(html_index);
|
|
|
|
|
/* sitemap */
|
|
|
|
|
ParserParse(r->sitemapParser, f, 0, r->sitemap);
|
|
|
|
|
ParserRewind(r->sitemapParser);
|
|
|
|
@ -251,18 +257,19 @@ int recurse(const struct Files *parent) {
|
|
|
|
|
while(FilesAdvance(f)) {
|
|
|
|
|
if(!FilesIsDir(f) ||
|
|
|
|
|
!(name = FilesName(f)) ||
|
|
|
|
|
!strcmp(dirCurrent, name) ||
|
|
|
|
|
!strcmp(dirParent, name) ||
|
|
|
|
|
!strcmp(dir_current, name) ||
|
|
|
|
|
!strcmp(dir_parent, name) ||
|
|
|
|
|
!(name = FilesName(f))) continue;
|
|
|
|
|
if(chdir(name)) { perror(name); continue; }
|
|
|
|
|
recurse(f);
|
|
|
|
|
/* this happens on Windows; I don't know what to do */
|
|
|
|
|
if(chdir(dirParent)) perror(dirParent);
|
|
|
|
|
if(chdir(dir_parent)) perror(dir_parent);
|
|
|
|
|
}
|
|
|
|
|
Files_(f);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
char *readFile(const char *filename) {
|
|
|
|
|
|
|
|
|
|
static char *readFile(const char *filename) {
|
|
|
|
|
char *buf = 0, *newBuf;
|
|
|
|
|
size_t bufPos = 0, bufSize = 0, rd;
|
|
|
|
|
FILE *fp;
|
|
|
|
@ -281,16 +288,61 @@ char *readFile(const char *filename) {
|
|
|
|
|
if(fclose(fp)) perror(filename);
|
|
|
|
|
return buf; /** you must free() the memory! */
|
|
|
|
|
}
|
|
|
|
|
void usage(const char *programme) {
|
|
|
|
|
fprintf(stderr, "Usage: %s <directory>\n\n", programme);
|
|
|
|
|
fprintf(stderr, "Version %d.%d.\n\n", versionMajor, versionMinor);
|
|
|
|
|
fprintf(stderr, "MakeIndex is a content generator that places a changing\n");
|
|
|
|
|
fprintf(stderr, "index.html on all the directories under <directory>\n");
|
|
|
|
|
fprintf(stderr, "based on a template file in <directory> called <%s>.\n", tmplIndex);
|
|
|
|
|
fprintf(stderr, "It also does some other stuff.\n\n");
|
|
|
|
|
fprintf(stderr, "See readme.txt or http://neil.chaosnet.org/ for further info.\n\n");
|
|
|
|
|
fprintf(stderr, "MakeIndex Copyright 2008, 2012 Neil Edelman\n");
|
|
|
|
|
fprintf(stderr, "This program comes with ABSOLUTELY NO WARRANTY.\n");
|
|
|
|
|
fprintf(stderr, "This is free software, and you are welcome to redistribute it\n");
|
|
|
|
|
fprintf(stderr, "under certain conditions; see gpl.txt.\n\n");
|
|
|
|
|
|
|
|
|
|
static void usage(void) {
|
|
|
|
|
fprintf(stderr, "Usage: %s [--directory|-d] <directory> | [--help|-h]\n\n"
|
|
|
|
|
"Version %d.%d.\n\n"
|
|
|
|
|
"MakeIndex is a content management system that generates static\n"
|
|
|
|
|
"content, (mostly index.html,) on all the directories rooted at the\n"
|
|
|
|
|
"<directory> based on <%s>.\n\n"
|
|
|
|
|
"It also does some other stuff. See readme.txt or\n"
|
|
|
|
|
"http://neil.chaosnet.org/ for further info.\n\n", programme,
|
|
|
|
|
version_major, version_minor, template_index);
|
|
|
|
|
fprintf(stderr, "MakeIndex Copyright 2008, 2012 Neil Edelman\n"
|
|
|
|
|
"This program comes with ABSOLUTELY NO WARRANTY.\n"
|
|
|
|
|
"This is free software, and you are welcome to redistribute it\n"
|
|
|
|
|
"under certain conditions; see gpl.txt.\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* entry-point (shouldn't have a prototype) */
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
int ac, ret;
|
|
|
|
|
int is_help = 0, is_invalid = 0;
|
|
|
|
|
const char *directory = 0;
|
|
|
|
|
|
|
|
|
|
/* gperf is wonderful, but what about other build systems? */
|
|
|
|
|
for(ac = 1; ac < argc; ac++) {
|
|
|
|
|
const char *const av = argv[ac];
|
|
|
|
|
if(!strcmp("--help", av) || !strcmp("-h", av)) {
|
|
|
|
|
is_help = -1;
|
|
|
|
|
} else if(!strcmp("--directory", av) || !strcmp("-d", av)) {
|
|
|
|
|
if(directory) {
|
|
|
|
|
break;
|
|
|
|
|
} else if(++ac < argc) {
|
|
|
|
|
directory = argv[ac];
|
|
|
|
|
} else {
|
|
|
|
|
is_invalid = -1; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(is_help || !directory) {
|
|
|
|
|
usage();
|
|
|
|
|
return is_help ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "Changing directory to <%s>.\n", directory);
|
|
|
|
|
if(chdir(directory)) { perror(directory); return EXIT_FAILURE; }
|
|
|
|
|
|
|
|
|
|
/* make sure that umask is set so that others can read what we create
|
|
|
|
|
"warning: passing argument 1 of 'umask' with different width due to
|
|
|
|
|
prototype" <- umask's fault; can't change? */
|
|
|
|
|
umask(S_IWGRP | S_IWOTH);
|
|
|
|
|
|
|
|
|
|
/* recursing; fixme: this should be configurable */
|
|
|
|
|
if(!Recursor(template_index, template_sitemap, template_newsfeed))
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
ret = RecursorGo();
|
|
|
|
|
Recursor_();
|
|
|
|
|
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|