include "path.sol" /** Outline by D.G. Sureau www.scriptol.net Require the Common Scriptol Compiler OSS (Open-Source Scripting) Free Licence. Make a html manual with outline from a simple unformatted text file of a tutorial with table of content from a list of text files. Put your files into a separate directory. --------------------------------------------------- How the parser works - For each line, one store the status: * blank * indented * line of hyphens - According to the analysis, a status is assigned to the previous line: * title if blank current and blank previous * sub-title if title if previous indented * other line - Titles and sub-titles are copied into the summary */ enum g : "\"" enum INDENT0, ` start at 0 INDENT1, INDENT2, INDENT3, INDENT4, INDENT5, PARAG = 16, ` restarting at 16 TITLELINE, BLANK /enum enum size0 : "18pt", size1 : "16pt", size2 : "14pt", size3 : "12pt", size4 : "11pt", size5 : "10pt" /enum boolean LOGFLAG = false array logfile = () text logname ` path and name of the log file void display(text t) if LOGFLAG logfile.push(t + "\n") else print t /if return void logDie(text t) display(t) if LOGFLAG let logfile.store(logname) exit() return class Tools ` change extension for target text extension = "htm" text makeTarget(text source) text node, ext node, ext = Path.splitExt(source) if ext = extension ? node + ("." + ext) return node + "." + extension ` static function to save a file static void save(text fname, array content) file f f.open(fname, "w") error ? logDie("enable to open " + fname) for text l in content f.write(l) /for f.close() return ` read an ".ini" file, parse lines and return a dict of options static dict readIni(text path) array ini ini.load(Path.changeExt(path, "ini")) if(ini.size() = 0) display("ini file not loaded") return () /if dict d = () for text option in ini option = option.trim() ` remove trailing spaces and eol int i = option.find("=") ` parse a line if i = nil: i = option.find(":") if i = nil ? continue ` no valid couple key:value /if text k = option[--i].trim() ` key text v = option[i+1..].trim() ` value d[k] = v ` put them in dict /for return d /class class Outline int width int height file fw file fsum text htmlFile // filename including path text htmlName // filename text htmlDir text sumFile = "index.htm" text pageTitle = "" text textFile text pageColor = "none" text textColor = "#000000" text textSize = "12pt" text titleSize = "18pt" text titleColor = "#339900" text sumColor = "#33cc66" text sumVisited = "#009933" text sumHover = "#66ff99" array title = () array lines = () array body = () array summary = () array mainTitles = () int sumIndex int previousStatus = 0 int status int count int scount = 0 text codeTitle int no ` title number boolean closing = false boolean first = true ` for the page title boolean isProject ` different processing for project boolean indexOpened = false ` flag int count2 array lessTags = ("", "", "", "", "", "" + "" + s.ltrim() + "" summary.push(ref) if isProject if indentation = 0 mainTitles[sumIndex] = s /if /if ` adding a text line if closing ? body.push("") body.push("<" + codeTitle + ">") body.push(s + "
")
   closing = true

return


/* Push a formatted line of title */
void pushTitleLine(text s)
  text x = text("
" + s.trim() + "
" title.push(x) return /* Add the previous line to array */ void addLine(text s) if s = "" ? return previousStatus = status if status = TITLELINE: pushTitleLine(s) = INDENT0: indent(s, INDENT0, 3, size0) = INDENT1: indent(s, INDENT1, 3, size1) = INDENT2: indent(s, INDENT2, 4, size2) = INDENT3: indent(s, INDENT3, 5, size3) = INDENT4: indent(s, INDENT4, 6, size4) = INDENT5: indent(s, INDENT5, 6, size5) = BLANK: body.push(s) = PARAG: body.push(s) previousStatus = PARAG else: previousStatus = PARAG body.push(s) /if return /* Test if blank line */ boolean isBlank(int index) return isBlankLine(lines[index]) /* Parse the current line */ void parseLine(int index) text s = lines[index] /* if - beginning of text - current centered */ if summary.size() = 0 if s.length() > 6 if s[0..5] = " " // creating previous line status = TITLELINE addLine(s.trim()) if first first = false pageTitle = s.trim() ` title is first line, without spaces /if return /if /if /if ` Blank line, add it... if isBlankLine(s) status = BLANK addLine(s) return /if s = s.rTrim() ` remove spaces ant such stuff on right s = translate(s) /* Current line not blank, not centered if previous not blank then text else if following not blank then text else title */ ` ordinary line ` if previous not blank, then text if previousStatus != BLANK status = PARAG addLine(s) return /if ` if last following line not blank, this is text body boolean flag = false if index >= lines.size() : flag = true else if not isBlank(index + 1) ? flag = true /if if flag status = PARAG addLine(s) return /if ` single line is summary, indenting int l = min(s.length(), 5) ` max 5 levels of titles int i for i in 0 -- l if s[i] <> ' ' ? break /for status = i addLine(s) return /* Write in file */ void putTitle(int index) putSelect(title[index]) return void putSummary(int index) putSelect(summary[index] + "
") return void putBody(int index) putLine(body[index]) return /* For multi-files, put start of content file */ void openIndex() if not isProject ? return if indexOpened ? return putIndex("


") putIndex("") putIndex("
") indexOpened = true return /* Main function, create the output file */ int createHtml() lines.load(textFile) if lines.size() = 0 display(textFile + " not found") return 0 /if fw.open(htmlFile, "w") error ? logDie("Enable to write on " + htmlFile); no = 0 for int i in 0 -- lines.size() parseLine(i) /for putLine("") putLine("") putLine("" + pageTitle + "") ` the first line of title putLine("") putLine("") putLine("") putLine("") if pageColor = "none" putLine("") else putLine("") /if for int h in 0 -- title.size() let putTitle(h) openIndex() if not isProject putLine("
") putLine("

") putLine("") putLine("") putLine("
") /if for int s in 0 -- summary.size() ? putSummary(s) if not isProject putLine("
") putLine("

") /if for int b in 0 -- body.size() ? putBody(b) if closing ? putLine("") putLine("") putLine("") fw.close() return count /* For multi-files, create a table of content file */ void createIndex() if not isProject ? return fsum.open(sumFile, "w") error ? logDie("Enable to write on " + sumFile); putIndex("") putIndex("") putIndex("" + pageTitle + "") ` the first line of title putIndex("") putIndex("") if pageColor = "none" putIndex("") else putIndex("") /if return /* Put the end of table of content */ void closeIndex() if not isProject ? return putIndex("
"); putIndex("
") putIndex("

") fsum.close() display(text(scount) + " lines generated into " + sumFile) return /class class BannerClass int sumIndex text prevPara text nextPara text upTitle text toc text prevDisplay text nextDisplay array body array fileList = () array titles text header = "header.html" void init() body = () return ` build an array from the banner model, ` set values from toc, prevpara, nextpara, uptitle ` ***warning: variables can't be enclosed inside a text, as the compiler ` must process them as attributes of the class and not as variables void make() array temp temp.load(header) body = nil for text t in temp t.replace("_index.php", toc) t.replace("_prev.htm", prevPara) t.replace("_next.htm", nextPara) t.replace("_up.htm", upTitle) t.replace("_Previous", prevDisplay) t.replace("_Next", nextDisplay) body.push(t) /for return void setList(array a) : fileList = a; return void setTitles(array a) : titles = a; return void setPrev() prevDisplay = "Start" prevPara = toc prevPara := fileList[sumIndex - 1].trim() prevDisplay := titles[sumIndex - 1].trim() return void setNext() nextDisplay = "End" nextPara := fileList[sumIndex + 1].trim() nextDisplay := titles[sumIndex + 1].trim() return ` add banners to a page void process(text target, int i) array oldcontent array newcontent oldcontent.load(target) init() sumIndex = i setPrev() upTitle = target setNext() make() newcontent = oldcontent[0..19] + body + oldcontent[20..-3] + array("
", "
") + body + oldcontent[-2..] Tools.save(target, newcontent) return /class void usage() print "Outline 1.6 - www.scriptol.net" print "usage:" print " outline textfile [outfile]" print "or outline @project" print " (project is a simple file holding a list of text files)" print "option: -g output to a log file" print " -b book, no navigation bar" print " ex: outline -g textfile" exit() return int main(int argnum, array arg) if argnum not in 2..4 ? usage() logname = Path.changeExt(arg[0], "log") text source text target boolean BOOKFLAG LOGFLAG = false BOOKFLAG = false int fileindex = 1 if argnum in (3, 4) ` perhaps option presents text option = arg[1] if option[0] in ('-', '/') fileindex = 2 if option[1].lower() = "g" : LOGFLAG = true = "b" : BOOKFLAG = true else usage() /if /if /if source = arg[fileindex] boolean MULTIPLE = (source[0] = "@") boolean PRESET = (arg.size() = (2 + fileindex)) if PRESET if MULTIPLE ? usage() target = arg[fileindex + 1] /if Outline prog = Outline(MULTIPLE) Tools box BannerClass banner text destination = "" ` conditional assignments replace default options only ` when newer values are present inside the config file dict d = Tools.readIni(arg[0]) ` read options prog.pageColor := d["pageColor"] prog.titleColor := d["titleColor"] prog.textColor := d["textColor"] prog.textSize := d["textSize"] prog.titleSize := d["titleSize"] prog.sumColor := d["sumColor"] prog.sumVisited := d["sumVisited"] prog.sumHover := d["sumHover"] prog.sumFile := d["sumFile"] box.extension := d["extension"] banner.header := d["headerModel"] destination := d["destination"] banner.toc := prog.sumFile //d.display() array sourcelist array targetlist if MULTIPLE source = source[1..] if not file_exists(source) display(source + " not found") if LOGFLAG let logfile.store(logname) exit() /if display("loading " + source + " project") sourcelist.load(source) prog.createIndex() else sourcelist[0] = source destination = "" /if ` processing one or several texts int i = 0 for text s in sourcelist s = s.trim() if s = nil ? continue display("Processing " + s) prog.pageInit() prog.setSource("", s) prog.sumIndex = i if not PRESET ? target = box.makeTarget(s) targetlist.push(target) prog.setTarget(destination, target) prog.createHtml() display(text(prog.count) + " lines generated into " + target) i + 1 /for prog.closeIndex() targetlist.insert(0, banner.toc) ` add table of content in list of files text tocnode, tocext tocnode, tocext = Path.splitExt(banner.toc) prog.mainTitles.insert(0, tocnode) ` add toc to list of titles banner.setList(targetlist) ` set the list of files banner.setTitles(prog.mainTitles) ` set the list of titles ` adding banners if not BOOKFLAG if prog.isProject i = 0 for target in targetlist if target = nil ? continue banner.process(target, i) display("added banner to " + target) i + 1 /for /if /if if LOGFLAG let logfile.store(logname) return 0 main($argc, $argv)