feisty meow concerns codebase 2.140
js_marks_maker.cpp
Go to the documentation of this file.
1/*****************************************************************************\
2* *
3* Name : marks_maker_javascript *
4* Author : Chris Koeritz *
5* *
6* Purpose: *
7* *
8* Turns a link database in HOOPLE format into a web page, when given a *
9* suitable template file. The template file must have the phrase: *
10* $INSERT_LINKS_HERE *
11* at the point where the generated links are supposed to be stored. *
12* *
13*******************************************************************************
14* Copyright (c) 2005-$now By Author. This program is free software; you can *
15* redistribute it and/or modify it under the terms of the GNU General Public *
16* License as published by the Free Software Foundation; either version 2 of *
17* the License or (at your option) any later version. This is online at: *
18* http://www.fsf.org/copyleft/gpl.html *
19* Please send any updates to: fred@gruntose.com *
20\*****************************************************************************/
21
22#include "bookmark_tree.h"
23
26#include <basis/astring.h>
27#include <basis/functions.h>
28#include <basis/guards.h>
30#include <filesystem/filename.h>
31#include <loggers/file_logger.h>
32#include <structures/stack.h>
35#include <timely/time_stamp.h>
36
37using namespace application;
38using namespace basis;
39using namespace filesystem;
40using namespace loggers;
41using namespace nodes;
42using namespace structures;
43using namespace textual;
44using namespace timely;
45
46//#define DEBUG_MARKS
47 // uncomment to have more debugging noise.
48
49#undef BASE_LOG
50#define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT)
51#undef LOG
52#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), \
53 a_sprintf("line %d: ", _categories._line_number) + s, ALWAYS_PRINT)
54
55const int MAX_FILE_SIZE = 4 * MEGABYTE;
56 // the largest file we'll read.
57
59
60class marks_maker_javascript : public application_shell
61{
62public:
63 marks_maker_javascript() : application_shell(), _need_closure(false),
64 _loader_count(0), _link_spool(0), _functions_pending(0) {}
65 DEFINE_CLASS_NAME("marks_maker_javascript");
66 virtual int execute();
67 int print_instructions(const filename &program_name);
68
69 int write_marks_page(const astring &output_filename,
70 const astring &template_filename);
71 // given a tree of links, this writes out a web page to "output_filename"
72 // using a template file "template_filename".
73
74private:
75 bookmark_tree _categories; // our tree of categories.
76 bool _need_closure; // true if our <div> needs a closure.
77 int _loader_count; // count of the loader functions.
78 int _link_spool; // count of which link we're writing.
79 stack<astring> _functions_pending; // used for javascript node functions.
80
81//this needs to gather any strings that would have gone into functions.
82//instead, they need to be written into the current node's string.
83//when a new function def would be seen, then we need to push a new node
84//for accumulating the text.
85
86 // these handle outputting text for categories and links.
87 void write_category(inner_mark_tree *node, astring &output);
88 void write_link(inner_mark_tree *node, const link_record &linko,
89 astring &output);
90};
91
93
94int marks_maker_javascript::print_instructions(const filename &program_name)
95{
96 a_sprintf to_show("%s:\n\
97This program needs three filenames as command line parameters. The -i flag\n\
98is used to specify the input filename, the -t flag specifies a template web\n\
99page which is used as the wrapper around the links, and the -o flag specifies\n\
100the web page to be created. The input file is expected to be in the HOOPLE\n\
101link database format. The output file will be created from the template file\n\
102by finding the phrase $INSERT_LINKS_HERE in it and replacing that with html\n\
103formatted link and categories from the input file. Another tag of $TODAYS_DATE\n\
104will be replaced with the date when the output file is regenerated.\n\
105The HOOPLE link format is documented here:\n\
106 http://feistymeow.org/guides/link_database/format_manifesto.txt\n\
107", program_name.basename().raw().s(), program_name.basename().raw().s());
108 program_wide_logger::get().log(to_show, ALWAYS_PRINT);
109 return 12;
110}
111
112void marks_maker_javascript::write_category(inner_mark_tree *node, astring &output)
113{
114 FUNCDEF("write_category");
115 // output a javascript line for the category.
116
117 int node_num = node->_uid;
118 inner_mark_tree *parent = dynamic_cast<inner_mark_tree *>(node->parent());
119 int parent_node = parent? parent->_uid : -1;
120 // the parent node for root is a negative one.
121 astring chewed_name = node->name();
122 for (int i = chewed_name.end(); i >= 0; i--) {
123 // escape any raw single quotes that we see.
124 if (chewed_name[i] == '\'') {
125 chewed_name.zap(i, i);
126 chewed_name.insert(i, "\\'");
127 }
128 }
129 output += a_sprintf(" b.add(%d, %d, '%s');\n", node_num, parent_node,
130 chewed_name.s());
131}
132
133void marks_maker_javascript::write_link(inner_mark_tree *node, const link_record &linko,
134 astring &output)
135{
136 FUNCDEF("write_link");
137 // write a javascript link definition.
138 int parent_node = node->_uid;
139 astring chewed_name = linko._description;
140 for (int i = chewed_name.end(); i >= 0; i--) {
141 // escape any raw single quotes that we see.
142 if (chewed_name[i] == '\'') {
143 chewed_name.zap(i, i);
144 chewed_name.insert(i, "\\'");
145 }
146 }
147
148 if (!linko._url) {
149 // this just appears to be a comment line.
150 if (!linko._description) return; // it's a nothing line.
151
152/*
153//hmmm: probably not what we want.
154//hmmm: why not, again?
155 output += linko._description;
156 output += "<br>";
157 output += parser_bits::platform_eol_to_chars();
158*/
159 return;
160 }
161
162 // generate a function header if the number of links is a multiple of 100.
163 if (! (_link_spool % 100) ) {
164 if (_link_spool) {
165 // close out the previous function and set a timeout.
166 output += " setTimeout('run_tree_loaders()', 0);\n";
167 output += "}\n";
168 }
169
170 output += a_sprintf("function tree_loader_%d() {\n", _loader_count++);
171 }
172 _link_spool++;
173
174 output += a_sprintf(" b.add(%d, %d, '%s', '%s');\n",
175 linko._uid, parent_node, chewed_name.s(), linko._url.s());
176}
177
178int marks_maker_javascript::execute()
179{
180 FUNCDEF("execute");
182
183 command_line cmds(_global_argc, _global_argv); // process the command line parameters.
184 astring input_filename; // we'll store our link database name here.
185 astring output_filename; // where the web page we're creating goes.
186 astring template_filename; // the wrapper html code that we'll stuff.
187 if (!cmds.get_value('i', input_filename, false))
188 return print_instructions(cmds.program_name());
189 if (!cmds.get_value('o', output_filename, false))
190 return print_instructions(cmds.program_name());
191 if (!cmds.get_value('t', template_filename, false))
192 return print_instructions(cmds.program_name());
193
194 BASE_LOG(astring("input file: ") + input_filename);
195 BASE_LOG(astring("output file: ") + output_filename);
196 BASE_LOG(astring("template file: ") + template_filename);
197
198 int ret = _categories.read_csv_file(input_filename);
199 if (ret) return ret;
200
201 ret = write_marks_page(output_filename, template_filename);
202 if (ret) return ret;
203
204 return 0;
205}
206
207int marks_maker_javascript::write_marks_page(const astring &output_filename,
208 const astring &template_filename)
209{
210 FUNCDEF("write_marks_page");
211 astring long_string;
212 // this is our accumulator of links. it is the semi-final result that will
213 // be injected into the template file.
214
215 // add our target layer so that we can write to a useful place.
216 long_string += "<div class=\"marks_target\" id=\"martarg\">Marks Target</div>\n";
217
218 // add the tree style and creation of the tree object into the text.
219 long_string += "\n<div class=\"dtree\">\n";
220 long_string += "<script type=\"text/javascript\">\n";
221 long_string += "<!--\n";
222
223 long_string += "function open_mark(url) {\n";
224 long_string += " window.open(url, '', '');\n";
225 long_string += "}\n";
226
227 // the list of functions is used for calling into the tree loaders
228 // without blocking.
229 long_string += " b = new dTree('b');\n";
231 long_string += " b.config.useCookies = false;\n";
232 long_string += " b.config.folderLinks = false;\n";
233
234 // traverse the tree in prefix order.
235 tree::iterator itty = _categories.access_root().start(tree::prefix);
236 tree *curr = NULL_POINTER;
237 while ( (curr = itty.next()) ) {
238 inner_mark_tree *nod = (inner_mark_tree *)curr;
239 // print out the category on this node.
240 write_category(nod, long_string);
241
242 // print the link for all of the ones stored at this node.
243 for (int i = 0; i < nod->_links.elements(); i++) {
244 link_record *lin = nod->_links.borrow(i);
245 write_link(nod, *lin, long_string);
246 }
247 }
248
249 // close the block of script in the output.
250 long_string += " setTimeout('run_tree_loaders()', 0);\n";
251 long_string += "}\n\n";
252
253 long_string += a_sprintf("function tree_loader_%d()"
254 "{ setTimeout('run_tree_loaders()', 0); }\n", _loader_count++);
255
256 long_string += "\nconst max_funcs = 1000;\n";
257 long_string += "var loader_functions = new Array(max_funcs);\n";
258 long_string += "var curr_func = 0;\n";
259 long_string += "var done_rendering = false;\n\n";
260
261 long_string += a_sprintf("for (var i = 0; i < %d; i++) {\n", _loader_count);
262 long_string += " loader_functions[curr_func++] "
263 "= 'tree_loader_' + i + '()';\n";
264 long_string += "}\n";
265
266 long_string += "var run_index = 0;\n";
267 long_string += "function run_tree_loaders() {\n";
268 long_string += " if (done_rendering) return;\n";
269 long_string += " if (run_index >= curr_func) {\n";
270
271 long_string += " if (document.getElementById) {\n";
272 long_string += " x = document.getElementById('martarg');\n";
273 long_string += " x.innerHTML = '';\n";
274 long_string += " x.innerHTML = b;\n";
275 long_string += " } else { document.write(b); }\n";
276//not a very graceful degradation. we should use the other options from:
277// http://www.quirksmode.org/js/layerwrite.html
278 long_string += " done_rendering = true;\n";
279 long_string += " return;\n";
280 long_string += " }\n";
281 long_string += " var next_func = loader_functions[run_index++];\n";
282 long_string += " setTimeout(next_func, 0);\n";
283 long_string += "}\n";
284
285 long_string += a_sprintf(" run_tree_loaders();\n", _loader_count);
286
287 long_string += "//-->\n";
288 long_string += "</script>\n";
289 long_string += "<p><a href=\"javascript: b.openAll();\">open all</a> | "
290 "<a href=\"javascript: b.closeAll();\">close all</a></p>\n";
291 long_string += "</div>\n";
292
293 byte_filer template_file(template_filename, "r");
294 astring full_template;
295 if (!template_file.good())
296 non_continuable_error(class_name(), func, "the template file could not be opened");
297 template_file.read(full_template, MAX_FILE_SIZE);
298 template_file.close();
299
300 // javascript output needs some extra junk added to the header section.
301 int indy = full_template.ifind("</title>");
302 if (negative(indy))
303 non_continuable_error(class_name(), func, "the template file is missing "
304 "a <head> declaration");
305//hmmm: the path here must be configurable!
306 full_template.insert(indy + 8, "\n\n"
307 "<link rel=\"StyleSheet\" href=\"/yeti/javascript/dtree/dtree.css\" "
308 "type=\"text/css\" />\n"
309 "<script type=\"text/javascript\" src=\"/yeti/javascript/"
310 "dtree/dtree.js\"></script>\n");
311
312 // replace the tag with the long string we created.
313 bool found_it = full_template.replace("$INSERT_LINKS_HERE", long_string);
314 if (!found_it)
315 non_continuable_error(class_name(), func, "the template file is missing "
316 "the insertion point");
317 full_template.replace("$TODAYS_DATE", time_stamp::notarize(true));
318
319 filename outname(output_filename);
320 if (outname.exists()) {
321 non_continuable_error(class_name(), func, astring("the output file ")
322 + output_filename + " already exists. It would be over-written if "
323 "we continued.");
324 }
325
326 byte_filer output_file(output_filename, "w");
327 if (!output_file.good())
328 non_continuable_error(class_name(), func, "the output file could not be opened");
329 // write the newly generated web page out now.
330 output_file.write(full_template);
331 output_file.close();
332
333// show the tree.
334// BASE_LOG("");
335// BASE_LOG(_categories.access_root().text_form());
336
337 BASE_LOG(a_sprintf("wrote %d links in %d categories.",
338 _categories.link_count(), _categories.category_count()));
339 BASE_LOG(astring(""));
340
341 return 0;
342}
343
345
346HOOPLE_MAIN(marks_maker_javascript, )
347
int print_instructions(bool good, const astring &program_name)
Definition checker.cpp:45
The application_shell is a base object for console programs.
virtual int execute()=0
< retrieves the command line from the /proc hierarchy on linux.
application_shell()
constructs an application_shell to serve as the root of the program.
a_sprintf is a specialization of astring that provides printf style support.
Definition astring.h:440
Provides a dynamically resizable ASCII character string.
Definition astring.h:35
bool replace(const astring &tag, const astring &replacement)
replaces the first occurrence of "tag" text with the "replacement".
Definition astring.cpp:908
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition astring.h:113
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition astring.cpp:524
void insert(int position, const astring &to_insert)
Copies "to_insert" into "this" at the "position".
Definition astring.cpp:895
int end() const
returns the index of the last (non-null) character in the string.
Definition astring.h:86
int ifind(char to_find, int position=0, bool reverse=false) const
like the find() methods above, but case-insensitive.
Definition astring.cpp:574
virtual outcome log(const base_string &info, int filter)=0
writes the information in "info" to the logger using the "filter".
Provides file managment services using the standard I/O support.
Definition byte_filer.h:32
Provides operations commonly needed on file names.
Definition filename.h:64
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
Definition filename.cpp:97
filename basename() const
returns the base of the filename; no directory.
Definition filename.cpp:385
listo_links _links
static loggers::standard_log_base & get()
Provided by the startup code within each application for logging.
An object representing the interstitial cell in most linked data structures.
Definition node.h:41
tree * next()
Returns a pointer to the next tree in the direction of traversal.
Definition tree.cpp:257
A dynamically linked tree with an arbitrary number of branches.
Definition tree.h:40
@ prefix
Definition tree.h:94
int elements() const
the maximum number of elements currently allowed in this amorph.
Definition amorph.h:66
contents * borrow(int field)
Returns a pointer to the information at the index "field".
Definition amorph.h:448
An abstraction that represents a stack data structure.
Definition stack.h:30
static basis::astring notarize(bool add_space=true)
a useful method for getting a textual version of the time "right now".
#define SETUP_COMBO_LOGGER
a macro that retasks the program-wide logger as a combo_logger.
#define non_continuable_error(c, f, i)
an extra piece of information used, if available, in bounds_halt below.
#define NULL_POINTER
The value representing a pointer to nothing.
Definition definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition enhance_cpp.h:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition hoople_main.h:61
#define BASE_LOG(s)
const int MAX_FILE_SIZE
Implements an application lock to ensure only one is running at once.
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
const int MEGABYTE
Number of bytes in a megabyte.
bool negative(const type &a)
negative returns true if "a" is less than zero.
Definition functions.h:43
A platform independent way to obtain the timestamp of a file.
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
#include <time.h>