With a complete reorganisation of the directory structure and most of the content converted to pandoc-flavoured markdown. Some TODO's left before this can go live: - Main page - Atom feeds - Bug tracker
330 lines
8.7 KiB
C
330 lines
8.7 KiB
C
/*
|
|
* BBCode 2 HTML converter by //YorHel
|
|
* Copyright 2006 Y.Heling,
|
|
* License: MIT
|
|
*
|
|
* Created just to learn C, probably very ugly piece of code
|
|
* and probably with a _LOT_ of bugs... But the only way to
|
|
* learn a programming language is to code something yourself :)
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#define MAX_ARGH_SIZE 500
|
|
#define MAX_TAG_SIZE 10
|
|
#define MAX_NESTED_TAGS 100
|
|
|
|
#define TAGCHARS 28
|
|
#define NUMBER_OF_TAGS 16
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
|
|
/* typedefs */
|
|
typedef enum {
|
|
B,
|
|
I,
|
|
U,
|
|
SIZE,
|
|
COLOR,
|
|
URL,
|
|
QUOTE,
|
|
QUOTE2,
|
|
IMG,
|
|
IMG2,
|
|
EMAIL,
|
|
LIST,
|
|
LIST2,
|
|
CODE,
|
|
HTML,
|
|
UNDEF, /* hack */
|
|
} TAGNAME;
|
|
typedef struct {
|
|
TAGNAME intags[MAX_NESTED_TAGS];
|
|
int curtag;
|
|
char tag[MAX_TAG_SIZE];
|
|
char argh[MAX_ARGH_SIZE];
|
|
FILE *dest;
|
|
int inlist;
|
|
int liststart;
|
|
} PARSEINFO; /* just a hack to prevent the use of global variables */
|
|
typedef struct {
|
|
char *bb;
|
|
char *html_s;
|
|
char *html_e;
|
|
char arg;
|
|
} TAGINFO; /* store global information about the tags */
|
|
|
|
|
|
/* functions */
|
|
void parsebbcode(FILE *, FILE *);
|
|
void converttag(PARSEINFO *);
|
|
void formattag(PARSEINFO *);
|
|
char endtags(PARSEINFO *, TAGNAME);
|
|
void convertchars(PARSEINFO *, const char *);
|
|
void convertchar(PARSEINFO *, const int);
|
|
void convertchararg(char *, int);
|
|
char istagchar(const int);
|
|
void err(const char *);
|
|
|
|
/* global vars (only consts!) */
|
|
const char tagchars[TAGCHARS] = "abcdefghijklmnopqrstuvwxyz/*";
|
|
const TAGINFO taglist[NUMBER_OF_TAGS] = { /* same order as TAGNAME! */
|
|
{ "b", "<b>", "</b>", FALSE },
|
|
{ "i", "<i>", "</i>", FALSE },
|
|
{ "u", "<span style=\"text-decoration: underline\">", "</span>", FALSE },
|
|
{ "size", "<span style=\"font-size: %spx\">", "</span>", TRUE },
|
|
{ "color", "<span style=\"color: %s\">", "</span>", TRUE },
|
|
{ "url", "<a href=\"%s\">", "</a>", TRUE },
|
|
{ "quote", "<span class=\"bbcode_quote_header\">Quote: <span class=\"bbcode_quote_body\">", "</span></span>", FALSE },
|
|
{ "quote", "<span class=\"bbcode_quote_header\">%s wrote: <span class=\"bbcode_quote_body\">", "</span></span>", TRUE },
|
|
{ "img", "<img src=\"", "\" alt=\"\" />", FALSE },
|
|
{ "img", "<img src=\"%s\" alt=\"", "\" />", TRUE },
|
|
{ "email", "<a href=\"mailto:%s\">", "</a>", TRUE },
|
|
{ "list", "<ul>", "</li></ul>", FALSE },
|
|
{ "list", "<ul style=\"list-style-type: %s\">", "</li></ul>", TRUE },
|
|
{ "code", "<span class=\"bbcode_code_header\">Code: <span class=\"bbcode_code_body\">", "</span></span>", FALSE },
|
|
{ "html", "", "", FALSE },
|
|
{ "", "", "", FALSE },
|
|
}; /* NOTE: not all characteristics of the BBCodes are defined above, there is also a lot of hard-coded stuff below for a few tags */
|
|
|
|
|
|
|
|
int main() {
|
|
/* printf("BBCode to HTML converter by //YorHel\n");
|
|
printf("Copyright 2006 Y. Heling\n\n");
|
|
|
|
printf("* Reading & parsing bbcode...\n");
|
|
while(1) {
|
|
FILE *file;
|
|
if((file = fopen(BBFILE, "r")) == NULL) err("Couldn't open BBFILE");
|
|
parsebbcode(file, stdout);
|
|
fclose(file);
|
|
}
|
|
printf("\n");*/
|
|
|
|
parsebbcode(stdin, stdout);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void parsebbcode(FILE *file, FILE *dest) {
|
|
PARSEINFO pitemp; /* make sure we allocate the memory */
|
|
PARSEINFO *pi = &pitemp; /* but we are still going to use a pointer */
|
|
char intag = pi->inlist = pi->liststart = 0;
|
|
pi->curtag = 1;
|
|
pi->intags[0] = UNDEF;
|
|
sprintf(pi->tag, "");
|
|
sprintf(pi->argh, "");
|
|
pi->dest = dest;
|
|
int c;
|
|
char tmp[MAX_TAG_SIZE+MAX_ARGH_SIZE+4]; /* temp string, should be able to hold anything necessary */
|
|
while((c = fgetc(file)) && c != EOF) {
|
|
if(intag == 0 && c == '[') {
|
|
intag = 1;
|
|
}
|
|
else if(intag == 1 && c == '=')
|
|
intag = -1;
|
|
else if(intag != 0 && c == ']') {
|
|
intag = 0;
|
|
converttag(pi);
|
|
sprintf(pi->tag, "");
|
|
sprintf(pi->argh, "");
|
|
}
|
|
else if(intag == 1 && strlen(pi->tag) < sizeof(pi->tag)) {
|
|
if(istagchar(c)) {
|
|
sprintf(tmp, "%c", tolower(c));
|
|
strcat(pi->tag, tmp);
|
|
} else {
|
|
sprintf(tmp, "[%s", pi->tag);
|
|
convertchars(pi, tmp);
|
|
sprintf(pi->tag, "");
|
|
}
|
|
}
|
|
else if(intag == 1) {
|
|
intag = 0;
|
|
sprintf(tmp, "[%s", pi->tag);
|
|
convertchars(pi, tmp);
|
|
sprintf(pi->tag, "");
|
|
}
|
|
else if(intag == -1 && (strlen(pi->argh)+10) < sizeof(pi->argh)) {
|
|
convertchararg(tmp, c);
|
|
strcat(pi->argh, tmp);
|
|
}
|
|
else if(intag == -1) {
|
|
sprintf(tmp, "[%s=%s", pi->tag, pi->argh);
|
|
convertchars(pi, tmp);
|
|
sprintf(pi->tag, "");
|
|
sprintf(pi->argh, "");
|
|
intag = 0;
|
|
}
|
|
else
|
|
convertchar(pi, c);
|
|
}
|
|
/* some stuff left in the buffer */
|
|
if(strlen(pi->tag) > 0 && strlen(pi->argh) == 0)
|
|
fprintf(dest, "[%s", pi->tag);
|
|
else if(strlen(pi->tag) > 0 && strlen(pi->argh) > 0)
|
|
fprintf(dest, "[%s=%s", pi->tag, pi->argh);
|
|
/* automatically close opened tags */
|
|
endtags(pi, UNDEF);
|
|
}
|
|
|
|
void converttag(PARSEINFO *pi) {
|
|
/* ignore tag if we're not allowed to nest */
|
|
if((pi->intags[pi->curtag] == CODE && strcmp(pi->tag, "/code") != 0)
|
|
|| (pi->intags[pi->curtag] == HTML && strcmp(pi->tag, "/html") != 0)
|
|
|| ((pi->intags[pi->curtag] == IMG || pi->intags[pi->curtag] == IMG2) && strcmp(pi->tag, "/img"))) {
|
|
formattag(pi);
|
|
return;
|
|
}
|
|
|
|
/* parse list items */
|
|
if(!strcmp(pi->tag, "*") && pi->inlist) {
|
|
endtags(pi, LIST);
|
|
if(pi->inlist == pi->liststart)
|
|
fprintf(pi->dest, "</li>");
|
|
else
|
|
pi->liststart = pi->inlist;
|
|
fprintf(pi->dest, "<li>");
|
|
|
|
return; /* no need to parse more */
|
|
}
|
|
|
|
/* begin a tag */
|
|
if(*pi->tag != '/') {
|
|
int i; int got = -1;
|
|
for(i=0 ; i<NUMBER_OF_TAGS && got == -1 ; i++)
|
|
if(!strcmp(pi->tag, taglist[i].bb)) {
|
|
got = i;
|
|
if(strlen(pi->argh) == 0 && !taglist[i].arg)
|
|
fprintf(pi->dest, "%s", taglist[i].html_s);
|
|
else if(strlen(pi->argh) > 0 && taglist[i].arg) {
|
|
if(i != LIST2)
|
|
fprintf(pi->dest, taglist[i].html_s, pi->argh);
|
|
else
|
|
fprintf(pi->dest, taglist[i].html_s, strcmp(pi->argh, "1") ? "lower-roman" : "decimal");
|
|
}
|
|
else
|
|
got = -1;
|
|
};
|
|
if(got != -1) {
|
|
pi->intags[++pi->curtag] = got;
|
|
if(got == LIST || got == LIST2)
|
|
pi->inlist++;
|
|
}
|
|
else
|
|
formattag(pi);
|
|
return;
|
|
}
|
|
|
|
/* end a tag */
|
|
else {
|
|
char *tag = pi->tag+1; /* strip the '/' */
|
|
int i;
|
|
char got = FALSE;
|
|
for(i=0 ; i<NUMBER_OF_TAGS && got == FALSE ; i++)
|
|
if(!strcmp(tag, taglist[i].bb) && endtags(pi, i)) {
|
|
fprintf(pi->dest, "%s", taglist[i].html_e);
|
|
if(pi->intags[pi->curtag] == LIST || pi->intags[pi->curtag] == LIST2)
|
|
pi->liststart = --pi->inlist;
|
|
pi->curtag--;
|
|
got = TRUE;
|
|
};
|
|
if(!got)
|
|
formattag(pi);
|
|
}
|
|
}
|
|
|
|
char endtags(PARSEINFO *pi, TAGNAME to) {
|
|
char s = FALSE;
|
|
if(to == QUOTE || to == LIST || to == IMG)
|
|
s = TRUE;
|
|
else if(to == QUOTE2 || to == LIST2 || to == IMG2) {
|
|
s = TRUE;
|
|
to--;
|
|
}
|
|
int i = pi->curtag;
|
|
if(to != UNDEF)
|
|
while(pi->intags[i] != to && (!s || pi->intags[i] != to+1) && i > 0)
|
|
i--;
|
|
if(i) {
|
|
while(pi->intags[pi->curtag] != to && (!s || pi->intags[pi->curtag] != (to+1)) && pi->curtag > 0) {
|
|
if(pi->intags[pi->curtag] == LIST || pi->intags[pi->curtag] == LIST2)
|
|
pi->liststart = --pi->inlist;
|
|
fprintf(pi->dest, "%s", taglist[pi->intags[pi->curtag--]].html_e);
|
|
}
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
void formattag(PARSEINFO *pi) {
|
|
char tmp[MAX_TAG_SIZE+MAX_ARGH_SIZE+4];
|
|
if(strlen(pi->argh) == 0)
|
|
sprintf(tmp, "[%s]", pi->tag);
|
|
else
|
|
sprintf(tmp, "[%s=%s]", pi->tag, pi->argh);
|
|
convertchars(pi, tmp);
|
|
}
|
|
|
|
void convertchars(PARSEINFO *pi, const char *chars) {
|
|
int i;
|
|
for(i=0;i<strlen(chars);i++)
|
|
convertchar(pi, *(chars+i));
|
|
}
|
|
|
|
void convertchar(PARSEINFO *pi, const int c) {
|
|
if(pi->inlist && pi->inlist != pi->liststart)
|
|
return;
|
|
else if(pi->intags[pi->curtag] == HTML)
|
|
fprintf(pi->dest, "%c", c);
|
|
else if(pi->intags[pi->curtag] == IMG || pi->intags[pi->curtag] == IMG2) {
|
|
char tmp[10];
|
|
convertchararg(tmp, c);
|
|
fprintf(pi->dest, tmp);
|
|
}
|
|
else if(c == '\n')
|
|
fprintf(pi->dest, "<br />\n");
|
|
else if(c == '&')
|
|
fprintf(pi->dest, "&");
|
|
else if(c == '<')
|
|
fprintf(pi->dest, "<");
|
|
else if(c == '>')
|
|
fprintf(pi->dest, ">");
|
|
else
|
|
fprintf(pi->dest, "%c", c);
|
|
}
|
|
|
|
void convertchararg(char *to, const int c) {
|
|
switch(c) {
|
|
case '\n' :
|
|
sprintf(to, ""); break;
|
|
case '&' :
|
|
sprintf(to, "&"); break;
|
|
case '"' :
|
|
sprintf(to, """); break;
|
|
default :
|
|
sprintf(to, "%c", c);
|
|
}
|
|
}
|
|
|
|
char istagchar(const int c) {
|
|
int i;
|
|
int c2 = tolower(c);
|
|
for(i=0;i<TAGCHARS;i++)
|
|
if(c2 == *(tagchars+i))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
void err(const char *msg) {
|
|
fprintf(stderr, "ERROR: %s\n", msg);
|
|
exit(1);
|
|
}
|