home *** CD-ROM | disk | FTP | other *** search
Wrap
// Lina - HTML compiler for VirtualDub help system // Copyright (C) 1998-2003 Avery Lee // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #pragma warning(disable: 4786) #include <sys/stat.h> #include <direct.h> #include <stdio.h> #include <stdarg.h> #include <ctype.h> #include <string> #include <list> #include <map> #include <set> #include <vector> #include <utility> #include "document.h" #include "parser.h" /////////////////////////////////////////////////////////////////////////// std::list<std::pair<std::string, bool> > g_truncateURLs; std::list<std::string> g_htmlHelpFiles; /////////////////////////////////////////////////////////////////////////// struct Context { TreeDocument *mpDocument; std::list<const TreeNode *> stack; std::list<const TreeNode *> invocation_stack; std::list<TreeNode *> construction_stack; int pre_count; int cdata_count; bool eat_next_space; bool holding_space; Context() : pre_count(0), cdata_count(0), eat_next_space(true), holding_space(false) {} const TreeNode *find_tag(std::string name) { std::list<const TreeNode *>::reverse_iterator it(invocation_stack.rbegin()), itEnd(invocation_stack.rend()); const TreeNode *t = NULL; for(; it!=itEnd; ++it) { t = (*it)->Child(name); if (t) break; if (!name.empty() && name[0]=='/') break; } return t; } }; void output_tag(Context& ctx, std::string *out, const TreeNode& tag); void output_tag_contents(Context& ctx, std::string *out, const TreeNode& tag); ////////////////////////////////////////////////////////////// std::string g_outputDir; typedef std::map<std::string, std::string> tFileCopies; tFileCopies g_fileCopies; ////////////////////////////////////////////////////////////// void error(const Context& ctx, const char *format, ...) { va_list val; std::list<const TreeNode *>::const_reverse_iterator it(ctx.stack.rbegin()), itEnd(ctx.stack.rend()); printf("%s(%d): Error! ", (*it)->mpLocation->mName.c_str(), (*it)->mLineno); va_start(val, format); vprintf(format, val); va_end(val); putchar('\n'); int indent = 3; for(++it; it!=itEnd; ++it) { const TreeNode& tag = **it; printf("%*c%s(%d): while processing tag <%s>\n", indent, ' ', tag.mpLocation->mName.c_str(), tag.mLineno, tag.mName.c_str()); indent += 3; } indent = 3; for(it=ctx.invocation_stack.rbegin(), itEnd=ctx.invocation_stack.rend(); it!=itEnd; ++it) { const TreeNode& tag = **it; printf("%*c%s(%d): while invoked from tag <%s> (%d children)\n", indent, ' ', tag.mpLocation->mName.c_str(), tag.mLineno, tag.mName.c_str(), tag.mChildren.size()); indent += 3; } exit(10); } ////////////////////////////////////////////////////////////// std::string create_output_filename(const std::string& name) { std::string filename(g_outputDir); if (!filename.empty()) { char c = filename[filename.size()-1]; if (c != '/' && c != '\\') filename += '/'; } filename += name; return filename; } void construct_path(const std::string& dstfile) { int idx = -1; for(;;) { int pos = dstfile.find_first_of("\\/", idx+1); if (pos == std::string::npos) break; std::string partialpath(dstfile.substr(0, pos)); struct _stat buffer; if (-1 == _stat(partialpath.c_str(), &buffer)) { printf("creating: %s\n", partialpath.c_str()); _mkdir(partialpath.c_str()); } idx = pos; } } void copy_file(const std::string& dstfile, const std::string& srcfile) { printf("copying: %s -> %s\n", srcfile.c_str(), dstfile.c_str()); FILE *fs = fopen(srcfile.c_str(), "rb"); if (!fs) error("couldn't open source file \"%s\"", srcfile.c_str()); std::string filename(g_outputDir); if (!filename.empty()) { char c = filename[filename.size()-1]; if (c != '/' && c != '\\') filename += '/'; } filename += dstfile; construct_path(filename); FILE *fd = fopen(filename.c_str(), "wb"); if (!fd) error("couldn't create \"%s\"", filename.c_str()); fseek(fs, 0, SEEK_END); std::vector<char> data(ftell(fs)); fseek(fs, 0, SEEK_SET); if (1 != fread(&data.front(), data.size(), 1, fs)) error("couldn't read from \"%s\"", srcfile.c_str()); fclose(fs); if (1 != fwrite(&data.front(), data.size(), 1, fd) || fclose(fd)) error("couldn't write to \"%s\"", dstfile.c_str()); } bool is_true(const std::string& name) { return name.empty() || name[0]=='y' || name[0]=='Y'; } void dump_parse_tree(const TreeNode& tag, int indent = 0) { if (tag.mbIsText) { } else if (tag.mChildren.empty()) { printf("%*c<%s/>\n", indent, ' ', tag.mName.c_str()); } else { printf("%*c<%s>\n", indent, ' ', tag.mName.c_str()); std::list<TreeNode *>::const_iterator it(tag.mChildren.begin()), itEnd(tag.mChildren.end()); for(; it!=itEnd; ++it) { dump_parse_tree(**it, indent+3); } printf("%*c</%s>\n", indent, ' ', tag.mName.c_str()); } } //////////////////////////////////////////////////////////////////////////////// void output_tag_attributes(std::string& out, const TreeNode& tag) { std::list<TreeAttribute>::const_iterator itAtt(tag.mAttribs.begin()), itAttEnd(tag.mAttribs.end()); bool is_anchor = (tag.mName == "a"); for(; itAtt!=itAttEnd; ++itAtt) { const TreeAttribute& att = *itAtt; out += ' '; out += att.mName; if (!att.mbNoValue) { std::string::const_iterator its(att.mValue.begin()), itsEnd(att.mValue.end()); for(;its!=itsEnd; ++its) if (!issafevaluechar(*its)) break; std::string value(att.mValue); if (is_anchor && att.mName == "href") { std::list<std::pair<std::string, bool> >::const_iterator it(g_truncateURLs.begin()), itEnd(g_truncateURLs.end()); for(; it!=itEnd; ++it) { const std::pair<std::string, bool>& entry = *it; if (value.length() >= entry.first.length() && !value.compare(0, entry.first.length(), entry.first)) { if (entry.second) { int l = value.length(); while(l>0) { char c = value[--l]; if (c == '/' || c == ':') break; if (c == '.') { if (value.substr(l+1, std::string::npos) == "html") value.erase(l, std::string::npos); break; } } printf("truncated link: %s\n", value.c_str()); } break; } } } if (att.mValue.empty() || its!=itsEnd) { out += "=\""; out += value; out += '"'; } else { out += '='; out += value; } } } } void output_tag_contents(Context& ctx, std::string *out, const TreeNode& tag) { static int recursion_depth = 0; ++recursion_depth; if (recursion_depth > 64) error(ctx, "recursion exceeded limits"); std::list<TreeNode *>::const_iterator it(tag.mChildren.begin()), itEnd(tag.mChildren.end()); for(; it!=itEnd; ++it) { output_tag(ctx, out, **it); } --recursion_depth; } void output_standard_tag(Context& ctx, std::string *out, const TreeNode& tag) { if (!tag.mbIsText && tag.mName == "pre") ++ctx.pre_count; if (out && (tag.mbIsControl || !tag.mbIsText)) { if (ctx.holding_space && ctx.cdata_count) { if (!ctx.eat_next_space) { *out += ' '; } ctx.eat_next_space = false; ctx.holding_space = false; } } if (!ctx.construction_stack.empty()) { TreeNode *new_tag = ctx.mpDocument->AllocNode(); new_tag->mpLocation = tag.mpLocation; new_tag->mLineno = tag.mLineno; new_tag->mName = tag.mName; new_tag->mAttribs = tag.mAttribs; new_tag->mbIsText = tag.mbIsText; new_tag->mbIsControl = tag.mbIsControl; ctx.construction_stack.back()->mChildren.push_back(new_tag); ctx.construction_stack.push_back(new_tag); output_tag_contents(ctx, out, tag); ctx.construction_stack.pop_back(); } else if (tag.mbIsText) { if (out) { if (tag.mbIsControl) { *out += tag.mName; } else if (ctx.cdata_count) { if (ctx.pre_count) { *out += tag.mName; } else { std::string::const_iterator it(tag.mName.begin()), itEnd(tag.mName.end()); for(; it!=itEnd; ++it) { const char c = *it; if (isspace(c)) { ctx.holding_space = true; } else { if (ctx.eat_next_space) ctx.eat_next_space = false; else if (ctx.holding_space) *out += ' '; ctx.holding_space = false; *out += c; } } } } else { std::string::const_iterator it(tag.mName.begin()), itEnd(tag.mName.end()); for(; it!=itEnd; ++it) { const char c = *it; if (!isspace(c)) error(ctx, "inline text not allowed"); } } } } else { bool cdata = tag.SupportsCDATA(); if (cdata) { if (!ctx.cdata_count) { ctx.holding_space = false; ctx.eat_next_space = true; } ++ctx.cdata_count; } if (!out) { output_tag_contents(ctx, out, tag); } else if (tag.mChildren.empty()) { *out += '<'; *out += tag.mName; output_tag_attributes(*out, tag); *out += '>'; } else { *out += '<'; *out += tag.mName; output_tag_attributes(*out, tag); *out += '>'; output_tag_contents(ctx, out, tag); *out += "</"; *out += tag.mName; *out += '>'; } if (cdata) --ctx.cdata_count; } if (!tag.mbIsText && tag.mName == "pre") --ctx.pre_count; } std::string HTMLize(const std::string& s) { std::string::const_iterator it(s.begin()), itEnd(s.end()); std::string t; for(; it!=itEnd; ++it) { char c = *it; switch(c) { case '"': t.append("""); break; case '<': t.append("<"); break; case '>': t.append(">"); break; case '&': t.append("&"); break; default: t += c; break; } } return t; } void output_source_tags(Context& ctx, std::string *out, const TreeNode& tag) { std::string s; if (tag.mbIsText) s = tag.mName; else if (tag.mChildren.empty()) { s = '<'; s += tag.mName; output_tag_attributes(s, tag); s += '>'; } else { s = '<'; s += tag.mName; output_tag_attributes(s, tag); s += '>'; out->append(HTMLize(s)); out->append("<ul marker=none>"); std::list<TreeNode *>::const_iterator itBegin(tag.mChildren.begin()), it(itBegin), itEnd(tag.mChildren.end()); for(; it!=itEnd; ++it) { out->append("<li>"); output_source_tags(ctx, out, **it); out->append("</li>"); } out->append("</ul>"); s = "</"; s += tag.mName; s += '>'; } out->append(HTMLize(s)); if (!tag.mbIsText) out->append("<br>"); } void dump_stack(Context& ctx) { std::list<const TreeNode *>::reverse_iterator it(ctx.stack.rbegin()), itEnd(ctx.stack.rend()); printf("Current execution stack:\n"); int indent = 3; for(++it; it!=itEnd; ++it) { const TreeNode& tag = **it; printf("%*c%s(%d): processing <%s>\n", indent, ' ', tag.mpLocation->mName.c_str(), tag.mLineno, tag.mName.c_str()); indent += 3; } indent = 3; std::list<TreeNode *>::reverse_iterator it2(ctx.construction_stack.rbegin()), it2End(ctx.construction_stack.rend()); for(; it2!=it2End; ++it2) { const TreeNode& tag = **it2; printf("%*c%s(%d): while creating tag <%s>\n", indent, ' ', tag.mpLocation->mName.c_str(), tag.mLineno, tag.mName.c_str()); indent += 3; } indent = 3; for(it=ctx.invocation_stack.rbegin(), itEnd=ctx.invocation_stack.rend(); it!=itEnd; ++it) { const TreeNode& tag = **it; printf("%*c%s(%d): while invoked from tag <%s>\n", indent, ' ', tag.mpLocation->mName.c_str(), tag.mLineno, tag.mName.c_str()); indent += 3; } } void output_toc_children(FILE *f, const TreeNode& node); void output_toc_node(FILE *f, const TreeNode& node) { if (node.mName != "tocnode") error("Invalid node <%s> found during HTML help TOC generation", node.mName.c_str()); const TreeAttribute *attrib = node.Attrib("name"); if (!attrib || attrib->mbNoValue) error("<tocnode> must have NAME attribute"); const TreeAttribute *target = node.Attrib("target"); fputs("<LI><OBJECT type=\"text/sitemap\">\n", f); fprintf(f, "<param name=\"Name\" value=\"%s\">\n", HTMLize(attrib->mValue).c_str()); if (target && !target->mbNoValue) fprintf(f, "<param name=\"Local\" value=\"%s\">\n", HTMLize(target->mValue).c_str()); fputs("</OBJECT>\n", f); output_toc_children(f, node); } void output_toc_children(FILE *f, const TreeNode& node) { bool nodesFound = false; TreeNode::Children::const_iterator it(node.mChildren.begin()), itEnd(node.mChildren.end()); for(; it!=itEnd; ++it) { const TreeNode& childNode = **it; if (!childNode.mbIsText) { if (!nodesFound) { nodesFound = true; fputs("<UL>\n", f); } output_toc_node(f, childNode); } } if (nodesFound) fputs("</UL>\n", f); } void output_toc(FILE *f, const TreeNode& root) { fputs( "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n" "<HTML>\n" "<HEAD>\n" "<!-- Sitemap 1.0 -->\n" "</HEAD><BODY>\n" "<OBJECT type=\"text/site properties\">\n" "\t<param name=\"ImageType\" value=\"Folder\">\n" "</OBJECT>\n" , f); output_toc_children(f, root); fputs( "</BODY>\n" "</HTML>\n" , f); } void output_special_tag(Context& ctx, std::string *out, const TreeNode& tag) { if (tag.mName == "lina:fireball") { const TreeAttribute *a1 = tag.Attrib("src"); const TreeAttribute *a2 = tag.Attrib("dst"); if (!a1 || !a2) error(ctx, "<lina:fireball> requires SRC and DST attributes"); g_fileCopies[a2->mValue] = a1->mValue; } else if (tag.mName == "lina:write") { const TreeAttribute *a = tag.Attrib("file"); if (!a) error(ctx, "<lina:write> must specify FILE"); std::string s; std::list<TreeNode *> tempStack; ctx.construction_stack.swap(tempStack); int cdataCount = ctx.cdata_count; int preCount = ctx.pre_count; ctx.cdata_count = 0; ctx.pre_count = 0; bool bHoldingSpace = ctx.holding_space; bool bEatNextSpace = ctx.eat_next_space; ctx.holding_space = false; ctx.eat_next_space = true; output_tag_contents(ctx, &s, tag); ctx.holding_space = bHoldingSpace; ctx.eat_next_space = bEatNextSpace; ctx.pre_count = cdataCount; ctx.cdata_count = preCount; ctx.construction_stack.swap(tempStack); std::string filename(create_output_filename(a->mValue)); FILE *f = fopen(filename.c_str(), "wb"); if (!f) error(ctx, "couldn't create \"%s\"", a->mValue.c_str()); fwrite(s.data(), s.length(), 1, f); fclose(f); printf("created file: %s\n", a->mValue.c_str()); } else if (tag.mName == "lina:body") { // printf("outputting:\n"); // dump_parse_tree(*ctx.invocation_stack.back(), 4); output_tag_contents(ctx, out, *ctx.invocation_stack.back()); } else if (tag.mName == "lina:tag") { const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:tag> must have NAME attribute"); ctx.construction_stack.push_back(ctx.mpDocument->AllocNode()); TreeNode *new_tag = ctx.construction_stack.back(); new_tag->mpLocation = tag.mpLocation; new_tag->mLineno = tag.mLineno; new_tag->mName = a->mValue; new_tag->mbIsText = false; new_tag->mbIsControl = false; // compatibility if (!new_tag->mName.compare(0, 2, "w:")) new_tag->mName.replace(0, 2, "lina:"); output_tag_contents(ctx, NULL, tag); ctx.construction_stack.pop_back(); output_tag(ctx, out, *new_tag); } else if (tag.mName == "lina:arg") { if (!out && ctx.construction_stack.empty()) error(ctx, "<lina:arg> can only be used in an output context"); const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:arg> must have NAME attribute"); if (ctx.invocation_stack.empty()) error(ctx, "<lina:arg> can only be used during macro expansion"); std::list<const TreeNode *>::const_iterator it(ctx.invocation_stack.end()); --it; int levels = 1; const char *name = a->mValue.c_str(); while(*name == '^') { ++levels; ++name; if (it == ctx.invocation_stack.begin()) error(ctx, "Number of up-scope markers in name exceeds macro nesting level"); --it; } const TreeNode& macrotag = **it; const TreeAttribute *a2 = macrotag.Attrib(name); if (!a2) error(ctx, "macro invocation <%s> does not have an attribute \"%s\"", macrotag.mName.c_str(), name); if (out) { *out += a2->mValue; ctx.eat_next_space = false; ctx.holding_space = false; } else { TreeNode *t = ctx.mpDocument->AllocNode(); t->mpLocation = tag.mpLocation; t->mLineno = tag.mLineno; t->mbIsControl = false; t->mbIsText = true; t->mName = a2->mValue; ctx.construction_stack.back()->mChildren.push_back(t); } } else if (tag.mName == "lina:if-arg") { const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:if-arg> must have NAME attribute"); if (ctx.invocation_stack.empty()) error(ctx, "<lina:if-arg> can only be used during macro expansion"); const TreeNode& macrotag = *ctx.invocation_stack.back(); const TreeAttribute *a2 = macrotag.Attrib(a->mValue); if (a2) output_tag_contents(ctx, out, tag); } else if (tag.mName == "lina:if-not-arg") { const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:if-not-arg> must have NAME attribute"); if (ctx.invocation_stack.empty()) error(ctx, "<lina:if-not-arg> can only be used during macro expansion"); const TreeNode& macrotag = *ctx.invocation_stack.back(); const TreeAttribute *a2 = macrotag.Attrib(a->mValue); if (!a2) output_tag_contents(ctx, out, tag); } else if (tag.mName == "lina:attrib") { if (ctx.construction_stack.empty()) error(ctx, "<lina:attrib> can only be used in a <lina:tag> element"); const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:attrib> must have NAME attribute"); std::string s; std::list<TreeNode *> tempStack; ctx.construction_stack.swap(tempStack); ++ctx.cdata_count; ++ctx.pre_count; bool bHoldingSpace = ctx.holding_space; bool bEatNextSpace = ctx.eat_next_space; ctx.holding_space = false; ctx.eat_next_space = true; output_tag_contents(ctx, &s, tag); ctx.holding_space = bHoldingSpace; ctx.eat_next_space = bEatNextSpace; --ctx.pre_count; --ctx.cdata_count; ctx.construction_stack.swap(tempStack); TreeNode *t = ctx.construction_stack.back(); TreeAttribute new_att; if (tag.Attrib("novalue")) { new_att.mbNoValue = true; } else { new_att.mbNoValue = false; new_att.mValue = s; } new_att.mName = a->mValue; t->mAttribs.push_back(new_att); } else if (tag.mName == "lina:pull") { if (ctx.invocation_stack.empty()) error(ctx, "<lina:pull> can only be used during macro expansion"); const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:pull> must have NAME attribute"); const TreeNode *t = ctx.find_tag(a->mValue); if (!t) error(ctx, "cannot find tag <%s> referenced in <lina:pull>", a->mValue.c_str()); output_tag_contents(ctx, out, *t); } else if (tag.mName == "lina:for-each") { const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:for-each> must have NAME attribute"); std::string node_name; const TreeNode *parent; if (ctx.invocation_stack.empty()) { if (!a->mValue.empty() && a->mValue[0] == '/') parent = ctx.mpDocument->mpRoot->ResolvePath(a->mValue.substr(1), node_name); else error(ctx, "path must be absolute if not in macro context"); } else { std::list<const TreeNode *>::reverse_iterator it(ctx.invocation_stack.rbegin()), itEnd(ctx.invocation_stack.rend()); for(; it!=itEnd; ++it) { parent = (*it)->ResolvePath(a->mValue, node_name); if(parent) break; if (!a->mValue.empty() && a->mValue[0] == '/') break; } } if (!parent) error(ctx, "cannot resolve path \"%s\"", a->mValue.c_str()); std::list<TreeNode *>::const_iterator it2(parent->mChildren.begin()), it2End(parent->mChildren.end()); ctx.invocation_stack.push_back(NULL); for(; it2!=it2End; ++it2) { if ((*it2)->mName == node_name) { ctx.invocation_stack.back() = *it2; output_tag_contents(ctx, out, tag); } } ctx.invocation_stack.pop_back(); } else if (tag.mName == "lina:apply") { const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:apply> must have NAME attribute"); std::map<std::string, TreeNode *>::const_iterator it(ctx.mpDocument->mMacros.find(a->mValue)); if (it == ctx.mpDocument->mMacros.end()) error(ctx, "macro \"%s\" undeclared", a->mValue.c_str()); std::list<TreeNode *>::const_iterator it2(tag.mChildren.begin()), it2End(tag.mChildren.end()); ctx.invocation_stack.push_back(NULL); for(; it2!=it2End; ++it2) { if (!(*it2)->mbIsText) { ctx.invocation_stack.back() = *it2; output_tag_contents(ctx, out, *(*it).second); } } ctx.invocation_stack.pop_back(); } else if (tag.mName == "lina:if-present") { if (ctx.invocation_stack.empty()) error(ctx, "<lina:if-present> can only be used during macro expansion"); const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:if-present> must have NAME attribute"); const TreeNode *t = ctx.find_tag(a->mValue); if (t) output_tag_contents(ctx, out, tag); } else if (tag.mName == "lina:if-not-present") { if (ctx.invocation_stack.empty()) error(ctx, "<lina:if-not-present> can only be used during macro expansion"); const TreeAttribute *a = tag.Attrib("name"); if (!a) error(ctx, "<lina:if-not-present> must have NAME attribute"); const TreeNode *t = ctx.find_tag(a->mValue); if (!t) output_tag_contents(ctx, out, tag); } else if (tag.mName == "lina:pre") { ++ctx.pre_count; ++ctx.cdata_count; if (!out) output_standard_tag(ctx, out, tag); else { output_tag_contents(ctx, out, tag); } --ctx.cdata_count; --ctx.pre_count; } else if (tag.mName == "lina:cdata") { ++ctx.cdata_count; if (!out) output_standard_tag(ctx, out, tag); else output_tag_contents(ctx, out, tag); --ctx.cdata_count; } else if (tag.mName == "lina:delay") { std::list<TreeNode *>::const_iterator it(tag.mChildren.begin()), itEnd(tag.mChildren.end()); for(; it!=itEnd; ++it) { output_standard_tag(ctx, out, **it); } } else if (tag.mName == "lina:dump-stack") { dump_stack(ctx); } else if (tag.mName == "lina:replace") { const TreeAttribute *a = tag.Attrib("from"); if (!a || a->mbNoValue) error(ctx, "<lina:replace> must have FROM attribute"); const TreeAttribute *a2 = tag.Attrib("to"); if (!a2 || a2->mbNoValue) error(ctx, "<lina:replace> must have TO attribute"); const std::string& x = a->mValue; const std::string& y = a2->mValue; std::string s, t; std::string::size_type i = 0; output_tag_contents(ctx, &s, tag); for(;;) { std::string::size_type j = s.find(x, i); if (j != i) t.append(s, i, j-i); if (j == std::string::npos) break; t.append(y); i = j + x.size(); } TreeNode *new_tag = ctx.mpDocument->AllocNode(); new_tag->mpLocation = tag.mpLocation; new_tag->mLineno = tag.mLineno; new_tag->mbIsText = true; new_tag->mbIsControl = false; new_tag->mName = t; output_tag(ctx, out, *new_tag); } else if (tag.mName == "lina:set-option") { const TreeAttribute *a_name = tag.Attrib("name"); if (!a_name) error(ctx, "<lina:set-option> must have NAME attribute"); if (a_name->mValue == "link-truncate") { const TreeAttribute *a_val = tag.Attrib("baseurl"); if (!a_val || a_val->mbNoValue) error(ctx, "option \"link-truncate\" requires BASEURL attribute"); bool bTruncate = !tag.Attrib("notruncate"); g_truncateURLs.push_back(std::make_pair(a_val->mValue, bTruncate)); } else if (a_name->mValue == "output-dir") { const TreeAttribute *a_val = tag.Attrib("target"); if (!a_val || a_val->mbNoValue) error(ctx, "option \"output-dir\" requires TARGET attribute"); g_outputDir = a_val->mValue; } else if (a_name->mValue == "tag-info") { const TreeAttribute *a_tagname = tag.Attrib("tag"); if (!a_tagname || a_tagname->mbNoValue) error(ctx, "option \"tag-info\" requires TAG attribute"); const TreeAttribute *a_cdata = tag.Attrib("cdata"); if (!a_cdata || a_cdata->mbNoValue) error(ctx, "option \"tag-info\" requires CDATA attribute"); TreeNode::SetSupportsCDATA(a_tagname->mValue, is_true(a_cdata->mValue)); } else error(ctx, "option \"%s\" unknown\n", a_name->mValue.c_str()); } else if (tag.mName == "lina:data") { // do nothing } else if (tag.mName == "lina:source") { if (out) { std::list<TreeNode *>::const_iterator itBegin(tag.mChildren.begin()), it(itBegin), itEnd(tag.mChildren.end()); for(; it!=itEnd; ++it) { output_source_tags(ctx, out, **it); } } } else if (tag.mName == "lina:htmlhelp-toc") { const TreeAttribute *a_val = tag.Attrib("file"); if (!a_val || a_val->mbNoValue) error(ctx, "<lina:htmlhelp-toc> requires FILE attribute"); const std::string filename(create_output_filename(a_val->mValue)); // build new tag with TOC contents ctx.construction_stack.push_back(ctx.mpDocument->AllocNode()); TreeNode *new_tag = ctx.construction_stack.back(); new_tag->mpLocation = tag.mpLocation; new_tag->mLineno = tag.mLineno; new_tag->mName = a_val->mValue; new_tag->mbIsText = false; new_tag->mbIsControl = false; output_tag_contents(ctx, NULL, tag); ctx.construction_stack.pop_back(); output_tag(ctx, out, *new_tag); FILE *f = fopen(filename.c_str(), "wb"); if (!f) error(ctx, "couldn't create htmlhelp toc \"%s\"", a_val->mValue.c_str()); output_toc(f, *new_tag); fclose(f); } else if (tag.mName == "lina:htmlhelp-project") { const TreeAttribute *file_val = tag.Attrib("file"); if (!file_val || file_val->mbNoValue) error(ctx, "<lina:htmlhelp-project> requires FILE attribute"); const TreeAttribute *output_val = tag.Attrib("output"); if (!output_val || output_val->mbNoValue) error(ctx, "<lina:htmlhelp-project> requires OUTPUT attribute"); const TreeAttribute *toc_val = tag.Attrib("toc"); if (!toc_val || toc_val->mbNoValue) error(ctx, "<lina:htmlhelp-project> requires TOC attribute"); const TreeAttribute *title_val = tag.Attrib("title"); if (!title_val || title_val->mbNoValue) error(ctx, "<lina:htmlhelp-project> requires TITLE attribute"); const std::string filename(create_output_filename(file_val->mValue)); FILE *f = fopen(filename.c_str(), "wb"); if (!f) error(ctx, "couldn't create htmlhelp project \"%s\"", file_val->mValue.c_str()); fprintf(f, "[OPTIONS]\n" "Auto Index=Yes\n" "Compatibility=1.1 or later\n" "Compiled file=%s\n" "Contents file=%s\n" "Default topic=index.html\n" "Display compile progress=no\n" "Full-text search=Yes\n" , output_val->mValue.c_str() , toc_val->mValue.c_str() ); const TreeAttribute *fullstop_val = tag.Attrib("fullstop"); if (fullstop_val && !fullstop_val->mbNoValue) fprintf(f, "Full text search stop list file=%s\n", fullstop_val->mValue.c_str()); fprintf(f, "Language=0x0409 English (United States)\n" "Title=%s\n" "\n" "[FILES]\n" , title_val->mValue.c_str() ); std::list<std::string>::const_iterator it(g_htmlHelpFiles.begin()), itEnd(g_htmlHelpFiles.end()); for(; it!=itEnd; ++it) { fprintf(f, "%s\n", (*it).c_str()); } fclose(f); } else if (tag.mName == "lina:htmlhelp-addfile") { const TreeAttribute *file_val = tag.Attrib("file"); if (!file_val || file_val->mbNoValue) error(ctx, "<lina:htmlhelp-addfile> requires FILE attribute"); g_htmlHelpFiles.push_back(file_val->mValue); } else { std::string macroName(tag.mName, 5, std::string::npos); std::map<std::string, TreeNode *>::const_iterator it = ctx.mpDocument->mMacros.find(macroName); if (it == ctx.mpDocument->mMacros.end()) error(ctx, "macro <lina:%s> not found", macroName.c_str()); // dump_stack(ctx); // printf("executing macro: %s (%s:%d)\n", tag.mName.c_str(), tag.mLocation->name.c_str(), tag.mLineno); ctx.invocation_stack.push_back(&tag); output_tag_contents(ctx, out, *(*it).second); ctx.invocation_stack.pop_back(); // printf("exiting macro: %s (%s:%d)\n", tag.mName.c_str(), tag.mLocation->name.c_str(), tag.mLineno); } } void output_tag(Context& ctx, std::string *out, const TreeNode& tag) { ctx.stack.push_back(&tag); if (!tag.mbIsText && !tag.mName.compare(0,5,"lina:")) { output_special_tag(ctx, out, tag); } else { output_standard_tag(ctx, out, tag); } ctx.stack.pop_back(); } int main(int argc, char **argv) { TreeNode::SetSupportsCDATA("p", true); TreeNode::SetSupportsCDATA("h1", true); TreeNode::SetSupportsCDATA("h2", true); TreeNode::SetSupportsCDATA("h3", true); TreeNode::SetSupportsCDATA("h4", true); TreeNode::SetSupportsCDATA("h5", true); TreeNode::SetSupportsCDATA("h6", true); TreeNode::SetSupportsCDATA("td", true); TreeNode::SetSupportsCDATA("th", true); TreeNode::SetSupportsCDATA("li", true); TreeNode::SetSupportsCDATA("style", true); TreeNode::SetSupportsCDATA("script", true); TreeNode::SetSupportsCDATA("title", true); TreeNode::SetSupportsCDATA("div", true); TreeNode::SetSupportsCDATA("dt", true); TreeNode::SetSupportsCDATA("dd", true); TreeDocument doc; TreeParser parser(&doc); parser.ParseFile(argv[1]); // dump_parse_tree(*doc.mpRoot); Context ctx; ctx.mpDocument = &doc; output_tag(ctx, NULL, *doc.mpRoot); // copy files tFileCopies::const_iterator it(g_fileCopies.begin()), itEnd(g_fileCopies.end()); for(; it!=itEnd; ++it) { const tFileCopies::value_type& info = *it; copy_file(info.first, info.second); } printf("No errors.\n"); return 0; }