//| //| HTML Browser for the Fast Light Tool Kit (FLTK). //| //| Copyright 1998-1999 by Vivality llc, Venice, CA, U.S.A. //| and Matthias Melcher. //| //| This library is free software; you can redistribute it and/or //| modify it under the terms of the GNU Library General Public //| License as published by the Free Software Foundation; either //| version 2 of the License, or (at your option) any later version. //| //| This library is distributed in the hope that it will be useful, //| but WITHOUT ANY WARRANTY; without even the implied warranty of //| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //| Library General Public License for more details. //| //| You should have received a copy of the GNU Library General Public //| License along with this library; if not, write to the Free Software //| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 //| USA. //| //| Please report all bugs and problems to "matthias@mediaone.net". //| // #include "Movido.h" //: for debugging PRINTF1 only #include "Fl_Help.H" #include #include #include #include #include #include #ifdef WIN32 # include #else # include # define stricmp(a, b) strcasecmp(a, b) # define strnicmp(a, b, c) strncasecmp(a, b, c) #endif #define LINE_SZE 256 #define WORD_WRAP 64 #define FLAG_CENTER 0x00000001 #define FLAG_RIGHT 0x00000002 #define FLAG_TITLE 0x00000004 #define FLAG_BULLET 0x00000008 #define FLAG_ITALICS 0x00000010 #define FLAG_BOLD 0x00000020 #define FLAG_TELE 0x00000040 #define FLAG_ULINE 0x00000080 #define FLAG_HREF 0x00000100 #define FLAG_FSIZE 0x00000200 struct Anchor { int line; char *name; }; struct Href { int line; char *name; }; static char *strndup(const char *src, int n) { char *dst = (char*)malloc(n+1); memcpy(dst, src, n); dst[n] = 0; return dst; } Fl_Simple_Html::Fl_Simple_Html(int xp, int yp, int wp, int hp, char *name) : Fl_Select_Browser(xp, yp, wp, hp, name) { inFile = 0L; inText = 0L; lineBuffer = 0L; crsr = 0L; anchor = 0L; nAnchor = NAnchor = 0; href = 0L; nHref = NHref = 0; docPath = 0L; url_ = prevUrl_ = 0L; fsize = 4; fontCnt = 0; } Fl_Simple_Html::~Fl_Simple_Html() { clear(); if (docPath) free(docPath); if (url_) free(url_); if (prevUrl_) free(prevUrl_); } void Fl_Simple_Html::clear() { int i; for (i=0; i' end of a tag //| void Fl_Simple_Html::gotoTagEnd() { char *c=crsr; while (readWord(c)) { if (*c=='>') return; } } //| //| read the next "word" in an HTML compatible way //| \014 = Formfeed //+ we have to treat '=' and '<>' differently if we are inside or outside a //| int Fl_Simple_Html::readWord(_FlString &c) { for (char done=0; done==0; ) { if (inFile) { if (!crsr) { if (!fgets(lineBuffer, LINE_SZE, inFile)) return 0; c = crsr = lineBuffer; } if ((*crsr==0) || strchr("\n\r", *crsr)) { crsr=0; continue; } } else if (inText) { if (!crsr) crsr = (char*)inText; if (*crsr==0) return 0; if (strchr("\n\r", *crsr)) { crsr++; continue; } } else { return 0; } //: skip white noise while (strchr(" \014\t", *crsr)) crsr++; if (*crsr=='<') { c = crsr; crsr++; break; } if (*crsr=='>' || *crsr=='=') { c = crsr; crsr++; return 1; } if ((*crsr==0) || strchr("\n\r", *crsr)) continue; c = crsr; break; } while ((*crsr) && (strchr("\n\r =<>\014\t", *crsr)==0)) crsr++; return crsr-c; } //| //| read the following argument to a tag word //| I assume that there is no CR/LF etc. here //| int Fl_Simple_Html::readArg(_FlString &c) { if (*crsr!='=') return 0; crsr++; if (*crsr=='"') { c = ++crsr; while ((*crsr) && (*crsr!='"')) crsr++; crsr++; //: skip closing quote return crsr-c-1; } else { c = crsr; while ((*crsr) && (strchr("\n\r \"<>\014\t", *crsr)==0)) crsr++; } return crsr-c; } //| //| flush the current line contents to the browser //| void Fl_Simple_Html::flush() { static char blue[] = "\0\0\0\0\0\0\0\0\0"; const char *H[] = { "", "@S28", "@S24", "@S19", "@S15", "@S13", "@S11" }; const char *S[] = { "", "@S9", "@S11", "@S13", "@S15", "@S19", "@S24", "@S28" }; char fmt[255] = ""; if (!*blue) sprintf((char*)blue, "@C%d", FL_BLUE); if (outCrsr == outBuffer) return; //: don't flush empty lines *outCrsr = 0; if (flags&FLAG_CENTER) strcat(fmt, "@c"); if (flags&FLAG_RIGHT) strcat(fmt, "@r"); if (flags&FLAG_TITLE) strcat(fmt, "@m@b@c"); if (flags&FLAG_ITALICS) strcat(fmt, "@i"); if (flags&FLAG_TELE) strcat(fmt, "@t"); if (flags&FLAG_FSIZE && fsize>=1 && fsize <=7) strcat(fmt, S[fsize]); if (flags&FLAG_HREF) strcat(fmt, blue); if (flags&(FLAG_ULINE/*|FLAG_HREF*/)) strcat(fmt, "@u"); //+ underlining the whole line looks pretty ugly! if ((flags&FLAG_BOLD)||heading) strcat(fmt, "@b"); if ((heading>0)&&(heading<=6)) strcat(fmt, H[heading]); strcat(fmt, "@."); for (int i=3*(td?td-1:0)+indent-1+dl; i>0; i--) strcat(fmt, "\t"); if (flags&FLAG_BULLET) strcat(fmt, "o"); if (indent||dl) strcat(fmt, "\t"); add(fmt, outBuffer); outCrsr = outBuffer; flags &= ~FLAG_BULLET; flags &= ~clearFlags; clearFlags = 0; } //| //| add a word to the current line. If the line gets to long, then flush it //| with the neccesary attributes //+ we currently define a word wrap when we hit 64 characters which is of course wrong. //+ We should keep track of fonts and pixel sizes instead! //| int Fl_Simple_Html::addWord(const char *word, int n) { int ret = 0; //: remove all nasty control characters except TAB char *buf = (char*)malloc(n+1); memcpy(buf, word, n); buf[n] = 0; char *src = buf, *dst = buf; while (*src) { if (*src=='&') { if (strnicmp(""", src, 6)==0) { *dst++ = '"'; src+=6; continue; } if (strnicmp(" ", src, 6)==0) { *dst++ = ' '; src+=6; continue; } if (strnicmp("©", src, 6)==0) { *dst++ = '('; *dst++ = 'C'; *dst++ = ')'; src+=6; continue; } if (strnicmp("®", src, 5)==0) { *dst++ = '('; *dst++ = 'R'; *dst++ = ')'; src+=5; continue; } if (strnicmp("&", src, 5)==0) { *dst++ = '&'; src+=5; continue; } if (strnicmp("<", src, 4)==0) { *dst++ = '<'; src+=4; continue; } if (strnicmp(">", src, 4)==0) { *dst++ = '>'; src+=4; continue; } } if (!iscntrl(*src)) *dst++ = *src; src++; } n = dst-buf; if (n>WORD_WRAP) { //: this word is too long to fit in the line, so we flush the current line and give this word it's very own line flush(); add("@.", buf, n); free(buf); return 2; //: we created 2 lines! } if (n+(outCrsr-outBuffer)>=WORD_WRAP) { //: >= for seperating ' ' character flush(); ret = 1; } if ((outBuffer!=outCrsr)&&(outCrsr[-1]!='\t')) *outCrsr++ = ' '; memmove(outCrsr, buf, n); free(buf); outCrsr += n; return ret; //: return 1 if we started a new line } //| //| tags proved that this is an HTML. Go and interprete it //| int Fl_Simple_Html::readHtml() { outBuffer = outCrsr = (char*)malloc(LINE_SZE); rewind(); flags = clearFlags = 0; indent = 0; td = dl = 0; heading = 0; fsize = 0; fontCnt = 0; char *c = 0; int n = 0; for (;;) { n = readWord(c); if (n==0) break; if (*c=='<') { if (n==2) { if (strnicmp(" tags... flush(); if (!addWord(" ", 1)) flush(); } else if (strnicmp("') { crsr = c; break; } else if (strnicmp("href=", c, n+1)==0) { flush(); flags |= FLAG_HREF; //+ sorry, but links currently need their own single line n = readArg(c); if (n) addHref(c, n); } else if (strnicmp("name=", c, n+1)==0) { n = readArg(c); if (n) addAnchor(c, n); } } } } else if (n==3) { if (strnicmp("0) indent--; } else if (strnicmp("0) indent--; } else if (strnicmp("') { crsr = c; break; } else if (strnicmp("size=", c, n+1)==0) { if (c[n+1]=='-') fsize-=atoi(c+n+2); else if (c[n+1]=='+') fsize+=atoi(c+n+2); else if (isdigit(c[n+1])) fsize=atoi(c+n+1); else fsize = 4; if (fsize<1) fsize = 1; if (fsize>7) fsize = 7; flush(); flags |= FLAG_FSIZE; readArg(c); fontCnt = 0; } } fontCnt++; } else if (strnicmp("0) fontCnt--; if (fontCnt==0) { clearFlags |= FLAG_FSIZE; flush(); fsize = 4; } //+ actually we need to track for the _corresponding_ } } else if (n==7) { if (strnicmp("0) indent--; if (!addWord(" ", 1)) flush(); } } /*else { char buf[80]; memcpy(buf, c, n); buf[n] = 0; PRINTF1("Skipped tag '%s>'\n", buf); } */ gotoTagEnd(); } else addWord(c, n); } flush(); free(outBuffer); return 1; } //| //| There were no signs of an HTML, so go and read simple text //| int Fl_Simple_Html::readText() { rewind(); if (inFile) { while (fgets(lineBuffer, LINE_SZE, inFile)) { add("@f@.", lineBuffer); } } else if (inText) { const char *src = inText, *start = inText; while (*src) { if (*src=='\n') { add("@f@.", start, src-start); start = src; } src++; } if (start!=src) add("@f@.", start, src-start); } return 1; } //| //| test for the input type and call the appropiate interpreter //| int Fl_Simple_Html::read() { clear(); char *crsr = 0L; int n = readWord(crsr); char isHtml = 0; if (n==9 && strnicmp(crsr, "o this: previously created Html widget, can be visible at this point //| ->o filename: absolute or relative filename of a local HTML file, optional #anchor //| ->o path: optional path to make filename relative to //| o-> return: 1 for success, 0 if it could not open the html file //| int Fl_Simple_Html::load(const char *filename, const char *path) { int ret = 1; char oldPath[256]; getcwd(oldPath, 256); //: remember the start directory to restore it later inText = 0L; crsr = 0L; if (path) chdir(path); else if (docPath) chdir(docPath); //: if we got a path, use it. Otherwise use the path of the previous doc char *fn = strdup(filename); //: copy the filename and clip the #anchor part off. char *a = strrchr(fn, '#'); if (a) *a++ = 0; if (strlen(fn)) { //: if we still have a filename, open the file. Else just jump to the #anchor clear(); //: clear the current document char newPath[1024], *fnn; if (docPath) free(docPath); //: remove the previous path name filename_absolute(newPath, fn); //: find the absolute filename and path to the given filename if (prevUrl_) free(prevUrl_); prevUrl_ = url_; //: (not really used yet) url_ = strdup(newPath); //: duplicate the new absolute filename and path fnn=(char*)filename_name(newPath); //: find the filename path docPath = strndup(newPath, fnn-newPath); //: copy the path part, so we know where links in this document are relative to. chdir(docPath); //: make that path current inFile = fopen(fnn, "rb"); //: now open the file if (!inFile) { //: if that fails, let the user know, why Fl_Browser::clear(); Fl_Browser::add("Can't open url"); Fl_Browser::add(newPath); Fl_Browser::add(strerror(errno)); ret = 0; goto bail; //: don't leave without freeing memory and restoring the current dir //: if you don't like the 'goto', think of it as throwing an exception - and implement it ;-) } lineBuffer = (char*)malloc(LINE_SZE); ret = read(); //: go ahead, read or interprete the file free(lineBuffer); fclose(inFile); } if (a) gotoAnchor(a); //: remember? We extracted an #anchor. Now jump to that anchor. bail: free(fn); if (path) chdir(oldPath); //: restore the old path return ret; } //| //| interprete a text string //| int Fl_Simple_Html::read(const char *html, const char *path) { char oldPath[256]; getcwd(oldPath, 256); if (path) { if (docPath) free(docPath); docPath = strdup(path); } if (docPath) chdir(docPath); inFile = 0; inText = html; crsr = 0L; int ret = read(); if (path) chdir(oldPath); return ret; }