/*
* 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
#include
#include
#include
#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", "", "", FALSE },
{ "i", "", "", FALSE },
{ "u", "", "", FALSE },
{ "size", "", "", TRUE },
{ "color", "", "", TRUE },
{ "url", "", "", TRUE },
{ "quote", "", FALSE },
{ "quote", "", TRUE },
{ "img", "", FALSE },
{ "img", "", TRUE },
{ "email", "", "", TRUE },
{ "list", "", FALSE },
{ "list", "", TRUE },
{ "code", "", 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, "");
else
pi->liststart = pi->inlist;
fprintf(pi->dest, "");
return; /* no need to parse more */
}
/* begin a tag */
if(*pi->tag != '/') {
int i; int got = -1;
for(i=0 ; itag, 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 ; idest, "%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;iinlist && 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, "
\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