#include <stdio.h>
#include <string.h>
/* #include <strings.h> */
#include <ctype.h>
#define HRULE_TEXT "----------------------------------------------------------------------------"
#define HTML_PAGE_HEAD "<html><head><title>%s</title></head>\n<body><div align=\"center\"><h1>%s</h1>\n<h3>by %s</h3></div>\n<ul><li><a href=\"#AlphaList\">Alphabetical List of Topics</a></li>\n<li><a href=\"#SectList\">List of Topics by Category</a></li></ul>\n"
#define HTML_PAGE_FOOT "</body></html>\n"
#define HTML_SECTION "<p><hr size=6>\n<h3><a name=\"%s\">%s</a></h3>\n"
#define HTML_SECTLIST_HEAD "<p><hr size=\"6\"><h3><a name=\"SectList\">List of Topics by Category</a></h3>\n<h4>You can get more help on the following topics:</h4>\n<ul>"
#define HTML_SECTLIST_ENTRY " <li><a href=\"#%s\">%s</a></li>\n"
#define HTML_SECTLIST_FOOT "</ul>\n\n"
#define HTML_SECTIDX_BEGIN "<blockquote><table border=0>\n <tr>\n"
#define HTML_SECTIDX_ENTRY " <td width=\"%d%%\"> <a href=\"#%s\">%s</a> </td>\n"
#define HTML_SECTIDX_NEWROW " </tr>\n <tr>\n"
#define HTML_SECTIDX_END " </tr>\n</table></blockquote>\n\n"
#define HTML_INDEX_BEGIN "<p><hr size=\"6\"><h3><a name=\"AlphaList\">Alphabetical List of Topics</a></h3>\n"
#define HTML_IDXGROUP_BEGIN "<h4>%s</h4><blockquote><table border=0>\n <tr>\n"
#define HTML_IDXGROUP_ENTRY " <td nowrap> <a href=\"#%s\">%s</a> </td>\n"
#define HTML_IDXGROUP_NEWROW " </tr>\n <tr>\n"
#define HTML_IDXGROUP_END " </tr>\n</table></blockquote>\n\n"
#define HTML_INDEX_END ""
#define HTML_TOPICHEAD "<hr><h4><a name=\"%s\">"
#define HTML_TOPICHEAD_BREAK "<br>\n"
#define HTML_TOPICBODY "</a></h4>\n"
#define HTML_TOPICEND "<p>\n"
#define HTML_PARAGRAPH "<p>\n"
#define HTML_CODEBEGIN "<pre>\n"
#define HTML_CODEEND "</pre>\n"
#define HTML_ALSOSEE_BEGIN "<p><h5>Also see:\n"
#define HTML_ALSOSEE_ENTRY " <a href=\"#%s\">%s</a>"
#define HTML_ALSOSEE_END "\n</h5>\n"
const char* title = "";
const char* author = "";
const char* doccmd = "";
char *
string_dup(const char *s)
{
char *p;
p = (char *) malloc(strlen(s) + 1);
if (p)
strcpy(p, s);
return p;
}
char sect[256] = "";
struct topiclist {
struct topiclist *next;
char *topic;
char *section;
int printed;
};
struct topiclist *topichead;
struct topiclist *secthead;
void
add_section(const char *str)
{
struct topiclist *ptr, *top;
if (!str || !*str)
return;
top = (struct topiclist *) malloc(sizeof(struct topiclist));
top->topic = NULL;
top->section = (char *) string_dup(sect);
top->printed = 0;
top->next = NULL;
if (!secthead) {
secthead = top;
return;
}
for (ptr = secthead; ptr->next; ptr = ptr->next) ;
ptr->next = top;
}
void
add_topic(const char *str)
{
struct topiclist *ptr, *top;
char buf[256];
const char *p;
char *s;
if (!str || !*str)
return;
p = str;
s = buf;
do {
*s++ = tolower(*p);
} while (*p++);
top = (struct topiclist *) malloc(sizeof(struct topiclist));
top->topic = (char *) string_dup(buf);
top->section = (char *) string_dup(sect);
top->printed = 0;
if (!topichead) {
topichead = top;
top->next = NULL;
return;
}
if (strcasecmp(str, topichead->topic) < 0) {
top->next = topichead;
topichead = top;
return;
}
ptr = topichead;
while (ptr->next && strcasecmp(str, ptr->next->topic) > 0) {
ptr = ptr->next;
}
top->next = ptr->next;
ptr->next = top;
}
char*
escape_html(const char* in, char* buf)
{
char* out = buf;
while (*in) {
if (*in == '<') {
strcpy(out, "<");
out += strlen(out);
} else if (*in == '>') {
strcpy(out, ">");
out += strlen(out);
} else if (*in == '&') {
strcpy(out, "&");
out += strlen(out);
} else if (*in == '"') {
strcpy(out, """);
out += strlen(out);
} else {
*out++ = *in;
}
in++;
}
*out++ = '\0';
return buf;
}
void
print_section_topics(FILE * f, FILE * hf, const char *whichsect)
{
struct topiclist *ptr;
struct topiclist *sptr;
char sectname[256];
char *sectptr;
char *osectptr;
char *divpos;
char buf[256];
char buf2[256];
char buf3[256];
int cnt;
int width;
int hcol;
int cols;
int longest;
char *currsect;
longest = 0;
for (sptr = secthead; sptr; sptr = sptr->next) {
if (!strncasecmp(whichsect, sptr->section, strlen(whichsect))) {
currsect = sptr->section;
for (ptr = topichead; ptr; ptr = ptr->next) {
if (!strcasecmp(currsect, ptr->section)) {
divpos = index(ptr->topic, '|');
if (!divpos) {
cnt = strlen(ptr->topic);
} else {
cnt = divpos - ptr->topic;
}
if (cnt > longest) {
longest = cnt;
}
}
}
}
}
cols = 78 / (longest + 2);
if (cols < 1) {
cols = 1;
}
width = 78 / cols;
for (sptr = secthead; sptr; sptr = sptr->next) {
if (!strncasecmp(whichsect, sptr->section, strlen(whichsect))) {
currsect = sptr->section;
cnt = 0;
hcol = 0;
buf[0] = '\0';
strcpy(sectname, currsect);
sectptr = index(sectname, '|');
if (sectptr) {
*sectptr++ = '\0';
osectptr = sectptr;
sectptr = rindex(sectptr, '|');
if (sectptr) {
sectptr++;
}
if (!sectptr) {
sectptr = osectptr;
}
}
if (!sectptr) {
sectptr = "";
}
fprintf(hf, HTML_SECTION, escape_html(sectptr, buf2), escape_html(sectname, buf3));
fprintf(f, "~\n~\n%s\n%s\n\n", currsect, sectname);
fprintf(hf, HTML_SECTIDX_BEGIN);
for (ptr = topichead; ptr; ptr = ptr->next) {
if (!strcasecmp(currsect, ptr->section)) {
ptr->printed++;
cnt++;
hcol++;
if (hcol > cols) {
fprintf(hf, HTML_SECTIDX_NEWROW);
hcol = 1;
}
escape_html(ptr->topic, buf3);
fprintf(hf, HTML_SECTIDX_ENTRY, (100 / cols), buf3, buf3);
if (cnt == cols) {
snprintf(buf2, sizeof(buf2), "%-0.*s", width - 1, ptr->topic);
} else {
snprintf(buf2, sizeof(buf2), "%-*.*s", width, width - 1, ptr->topic);
}
strcat(buf, buf2);
if (cnt >= cols) {
fprintf(f, "%s\n", buf);
buf[0] = '\0';
cnt = 0;
}
}
}
fprintf(hf, HTML_SECTIDX_END);
if (cnt)
fprintf(f, "%s\n", buf);
fprintf(f, "\n");
}
}
}
void
print_all_section_topics(FILE * f, FILE * hf)
{
struct topiclist *sptr;
for (sptr = secthead; sptr; sptr = sptr->next) {
print_section_topics(f, hf, sptr->section);
}
}
void
print_sections(FILE * f, FILE * hf, int cols)
{
struct topiclist *ptr;
struct topiclist *sptr;
char sectname[256];
char *osectptr;
char *sectptr;
char buf[256];
char buf2[256];
char buf3[256];
char buf4[256];
int cnt;
int width;
int hcol;
char *currsect;
fprintf(f, "~\n");
fprintf(f, "~%s\n", HRULE_TEXT);
fprintf(f, "~\n");
fprintf(f, "CATEGORY|CATEGORIES|TOPICS|SECTIONS\n");
fprintf(f, " List of Topics by Category:\n \n");
fprintf(f, "You can get more help on the following topics:\n \n");
fprintf(hf, HTML_SECTLIST_HEAD);
if (cols < 1) {
cols = 1;
}
width = 78 / cols;
for (sptr = secthead; sptr; sptr = sptr->next) {
currsect = sptr->section;
cnt = 0;
hcol = 0;
buf[0] = '\0';
strcpy(sectname, currsect);
sectptr = index(sectname, '|');
if (sectptr) {
*sectptr++ = '\0';
osectptr = sectptr;
sectptr = rindex(sectptr, '|');
if (sectptr) {
sectptr++;
}
if (!sectptr) {
sectptr = osectptr;
}
}
if (!sectptr) {
sectptr = "";
}
fprintf(hf, HTML_SECTLIST_ENTRY, escape_html(sectptr, buf3), escape_html(sectname, buf4));
fprintf(f, " %-40s (%s)\n", sectname, sectptr);
}
fprintf(hf, HTML_SECTLIST_FOOT);
fprintf(f, " \nUse '%s <topicname>' to get more information on a topic.\n", doccmd);
}
void
print_topics(FILE * f, FILE * hf)
{
struct topiclist *ptr;
char buf[256];
char buf2[256];
char buf3[256];
char alph;
char firstletter;
char *divpos;
int cnt = 0;
int width;
int hcol = 0;
int cols;
int len;
int longest;
fprintf(hf, HTML_INDEX_BEGIN);
fprintf(f, "~\n");
fprintf(f, "~%s\n", HRULE_TEXT);
fprintf(f, "~\n");
fprintf(f, "ALPHA|ALPHABETICAL|COMMANDS\n");
fprintf(f, " Alphabetical List of Topics:\n");
fprintf(f, " \n");
fprintf(f, "You can get more help on the following topics:\n");
for (alph = 'A' - 1; alph <= 'Z'; alph++) {
cnt = 0;
longest = 0;
for (ptr = topichead; ptr; ptr = ptr->next) {
firstletter = toupper(ptr->topic[0]);
if (firstletter == alph || (!isalpha(alph) && !isalpha(firstletter))) {
cnt++;
divpos = index(ptr->topic, '|');
if (!divpos) {
len = strlen(ptr->topic);
} else {
len = divpos - ptr->topic;
}
if (len > longest) {
longest = len;
}
}
}
cols = 78 / (longest + 2);
if (cols < 1) {
cols = 1;
}
width = 78 / cols;
if (cnt > 0) {
if (!isalpha(alph)) {
strcpy(buf, "Symbols");
} else {
buf[0] = alph;
buf[1] = '\'';
buf[2] = 's';
buf[3] = '\0';
}
fprintf(f, "\n%s\n", buf);
fprintf(hf, HTML_IDXGROUP_BEGIN, buf);
buf[0] = '\0';
cnt = 0;
hcol = 0;
for (ptr = topichead; ptr; ptr = ptr->next) {
firstletter = toupper(ptr->topic[0]);
if (firstletter == alph || (!isalpha(alph) && !isalpha(firstletter))) {
cnt++;
hcol++;
if (hcol > cols) {
fprintf(hf, HTML_IDXGROUP_NEWROW);
hcol = 1;
}
escape_html(ptr->topic, buf3);
fprintf(hf, HTML_IDXGROUP_ENTRY, /*(100 / cols),*/ buf3, buf3);
if (cnt == cols) {
snprintf(buf2, sizeof(buf2), "%-0.*s", width - 1, ptr->topic);
} else {
snprintf(buf2, sizeof(buf2), "%-*.*s", width, width - 1, ptr->topic);
}
strcat(buf, buf2);
if (cnt >= cols) {
fprintf(f, " %s\n", buf);
buf[0] = '\0';
cnt = 0;
}
}
}
if (cnt) {
fprintf(f, " %s\n", buf);
}
fprintf(hf, HTML_IDXGROUP_END);
}
}
fprintf(hf, HTML_INDEX_END);
fprintf(f, " \nUse '%s <topicname>' to get more information on a topic.\n", doccmd);
}
int
find_topics(FILE * infile)
{
char buf[4096];
char *s, *p;
int longest, lng;
longest = 0;
while (!feof(infile)) {
do {
if (!fgets(buf, sizeof(buf), infile)) {
*buf = '\0';
break;
} else {
if (!strncmp(buf, "~~section ", 10)) {
buf[strlen(buf) - 1] = '\0';
strcpy(sect, (buf + 10));
add_section(sect);
} else if (!strncmp(buf, "~~title ", 8)) {
buf[strlen(buf) - 1] = '\0';
title = string_dup(buf+8);
} else if (!strncmp(buf, "~~author ", 9)) {
buf[strlen(buf) - 1] = '\0';
author = string_dup(buf+9);
} else if (!strncmp(buf, "~~doccmd ", 9)) {
buf[strlen(buf) - 1] = '\0';
doccmd = string_dup(buf+9);
}
}
} while (!feof(infile) &&
(*buf != '~' || buf[1] == '@' || buf[1] == '~' || buf[1] == '<' ||
buf[1] == '!'));
do {
if (!fgets(buf, sizeof(buf), infile)) {
*buf = '\0';
break;
} else {
if (!strncmp(buf, "~~section ", 10)) {
buf[strlen(buf) - 1] = '\0';
strcpy(sect, (buf + 10));
add_section(sect);
} else if (!strncmp(buf, "~~title ", 8)) {
buf[strlen(buf) - 1] = '\0';
title = string_dup(buf+8);
} else if (!strncmp(buf, "~~author ", 9)) {
buf[strlen(buf) - 1] = '\0';
author = string_dup(buf+9);
} else if (!strncmp(buf, "~~doccmd ", 9)) {
buf[strlen(buf) - 1] = '\0';
doccmd = string_dup(buf+9);
}
}
} while (*buf == '~' && !feof(infile));
for (s = p = buf; *s; s++) {
if (*s == '|' || *s == '\n') {
*s++ = '\0';
add_topic(p);
lng = strlen(p);
if (lng > longest)
longest = lng;
p = s;
break;
}
}
}
return (longest);
}
void
process_lines(FILE * infile, FILE * outfile, FILE * htmlfile, int cols)
{
FILE *docsfile;
char *sectptr;
char buf[4096];
char buf2[4096];
char buf3[4096];
char buf4[4096];
int nukenext = 0;
int topichead = 0;
int codeblock = 0;
char *ptr;
char *ptr2;
char *ptr3;
docsfile = stdout;
escape_html(title, buf);
escape_html(author, buf2);
fprintf(htmlfile, HTML_PAGE_HEAD, buf, buf, buf2);
fprintf(outfile, "%*s%s\n", (36-(strlen(title)/2)), "", title);
fprintf(outfile, "%*sby %s\n\n", (36-((strlen(author)+3)/2)), "", author);
fprintf(outfile, "You may get a listing of topics that you can get help on, either sorted\n");
fprintf(outfile, "Alphabetically or sorted by Category. To get these lists, type:\n");
fprintf(outfile, " %s alpha or\n", doccmd);
fprintf(outfile, " %s category\n\n", doccmd);
while (!feof(infile)) {
if (!fgets(buf, sizeof(buf), infile)) {
break;
}
if (buf[0] == '~') {
if (buf[1] == '~') {
if (!strncmp(buf, "~~file ", 7)) {
fclose(docsfile);
buf[strlen(buf) - 1] = '\0';
if (!(docsfile = fopen(buf + 7, "w"))) {
fprintf(stderr, "Error: can't write to %s", buf + 7);
exit(1);
}
fprintf(docsfile, "%*s%s\n", (36-(strlen(title)/2)), "", title);
fprintf(docsfile, "%*sby %s\n\n", (36-((strlen(author)+3)/2)), "", author);
} else if (!strncmp(buf, "~~section ", 10)) {
buf[strlen(buf) - 1] = '\0';
sectptr = index(buf + 10, '|');
if (sectptr) {
*sectptr = '\0';
}
fprintf(outfile, "~\n~\n~%s\n", HRULE_TEXT);
fprintf(docsfile, "\n\n%s\n", HRULE_TEXT);
fprintf(docsfile, "%*s\n", (38 + strlen(buf + 10) / 2), (buf + 10));
print_section_topics(outfile, htmlfile, (buf + 10));
fprintf(outfile, "~%s\n~\n~\n", HRULE_TEXT);
fprintf(docsfile, "%s\n\n\n", HRULE_TEXT);
} else if (!strncmp(buf, "~~alsosee ", 10)) {
buf[strlen(buf) - 1] = '\0';
fprintf(htmlfile, HTML_ALSOSEE_BEGIN);
fprintf(outfile, "Also see: ");
ptr = buf + 10;
while (*ptr && isspace(*ptr))
ptr++;
while (ptr && *ptr) {
ptr2 = ptr;
ptr = index(ptr, ',');
if (ptr) {
*ptr++ = '\0';
while (*ptr && isspace(*ptr))
ptr++;
}
if (ptr2 > buf + 10) {
if (!ptr || !*ptr) {
fprintf(htmlfile, " and\n");
fprintf(outfile, " and ");
} else {
fprintf(htmlfile, ",\n");
fprintf(outfile, ", ");
}
}
escape_html(ptr2, buf3);
strcpy(buf4, buf3);
for (ptr3 = buf4; *ptr3; ptr3++) {
*ptr3 = tolower(*ptr3);
}
fprintf(htmlfile, HTML_ALSOSEE_ENTRY, buf4, buf3);
fprintf(outfile, "%s", ptr2);
}
fprintf(htmlfile, HTML_ALSOSEE_END);
fprintf(outfile, "\n");
} else if (!strcmp(buf, "~~code\n")) {
fprintf(htmlfile, HTML_CODEBEGIN);
codeblock = 1;
} else if (!strcmp(buf, "~~endcode\n")) {
fprintf(htmlfile, HTML_CODEEND);
codeblock = 0;
} else if (!strcmp(buf, "~~sectlist\n")) {
print_sections(outfile, htmlfile, cols);
} else if (!strcmp(buf, "~~secttopics\n")) {
/* print_all_section_topics(outfile, htmlfile); */
} else if (!strcmp(buf, "~~index\n")) {
print_topics(outfile, htmlfile);
}
} else if (buf[1] == '!') {
fprintf(outfile, "%s", buf + 2);
} else if (buf[1] == '@') {
escape_html(buf + 2, buf3);
fprintf(htmlfile, "%s", buf3);
} else if (buf[1] == '<') {
fprintf(outfile, "%s", buf + 2);
fprintf(docsfile, "%s", buf + 2);
} else if (buf[1] == '#') {
fprintf(outfile, "~%s", buf + 2);
fprintf(docsfile, "%s", buf + 2);
} else {
if (!nukenext) {
fprintf(htmlfile, HTML_TOPICEND);
}
nukenext = 1;
fprintf(outfile, "%s", buf);
fprintf(docsfile, "%s", buf + 1);
escape_html(buf + 1, buf3);
fprintf(htmlfile, "%s", buf3);
}
} else if (nukenext) {
nukenext = 0;
topichead = 1;
fprintf(outfile, "%s", buf);
for (ptr = buf; *ptr && *ptr != '|' && *ptr != '\n'; ptr++) {
*ptr = tolower(*ptr);
}
*ptr = '\0';
escape_html(buf, buf3);
fprintf(htmlfile, HTML_TOPICHEAD, buf3);
} else if (buf[0] == ' ') {
nukenext = 0;
if (topichead) {
topichead = 0;
fprintf(htmlfile, HTML_TOPICBODY);
} else if (!codeblock) {
fprintf(htmlfile, HTML_PARAGRAPH);
}
fprintf(outfile, "%s", buf);
fprintf(docsfile, "%s", buf);
escape_html(buf, buf3);
fprintf(htmlfile, "%s", buf3);
} else {
fprintf(outfile, "%s", buf);
fprintf(docsfile, "%s", buf);
escape_html(buf, buf3);
fprintf(htmlfile, "%s", buf3);
if (topichead) {
fprintf(htmlfile, HTML_TOPICHEAD_BREAK);
}
}
}
fprintf(htmlfile, HTML_PAGE_FOOT);
fclose(docsfile);
}
int
main(int argc, char **argv)
{
FILE *infile, *outfile, *htmlfile;
int cols;
if (argc != 4) {
fprintf(stderr, "Usage: %s inputrawfile outputhelpfile outputhtmlfile\n", argv[0]);
return 1;
}
if (!strcmp(argv[1], argv[2])) {
fprintf(stderr, "%s: cannot use same file for input rawfile and output helpfile\n");
return 1;
}
if (!strcmp(argv[1], argv[3])) {
fprintf(stderr, "%s: cannot use same file for input rawfile and output htmlfile\n");
return 1;
}
if (!strcmp(argv[3], argv[2])) {
fprintf(stderr, "%s: cannot use same file for htmlfile and helpfile\n");
return 1;
}
if (!strcmp(argv[1], "-")) {
infile = stdin;
} else {
if (!(infile = fopen(argv[1], "r"))) {
fprintf(stderr, "%s: cannot read %s\n", argv[0], argv[1]);
return 1;
}
}
if (!(outfile = fopen(argv[2], "w"))) {
fprintf(stderr, "%s: cannot write to %s\n", argv[0], argv[2]);
return 1;
}
if (!(htmlfile = fopen(argv[3], "w"))) {
fprintf(stderr, "%s: cannot write to %s\n", argv[0], argv[3]);
return 1;
}
cols = 78 / (find_topics(infile) + 1);
fseek(infile, 0L, 0);
process_lines(infile, outfile, htmlfile, cols);
fclose(infile);
fclose(outfile);
fclose(htmlfile);
return 0;
}