Modules |
Files |
Inheritance Tree |
Inheritance Graph |
Name Index |
Config
File: Synopsis/Parser/C++/syn/link.cc
1| // Synopsis C++ Parser: link.cc source file
2| // Implements the link module/program that reads the stored syntax
3| // highlighting info and original file, and generates the output HTML file.
4|
5| // $Id: link.cc,v 1.25 2002/12/09 13:53:50 chalky Exp $
6| //
7| // This file is a part of Synopsis.
8| // Copyright (C) 2001, 2002 Stephen Davies
9| // Copyright (C) 2002 Stefan Seefeld
10| //
11| // Synopsis is free software; you can redistribute it and/or modify it
12| // under the terms of the GNU General Public License as published by
13| // the Free Software Foundation; either version 2 of the License, or
14| // (at your option) any later version.
15| //
16| // This program is distributed in the hope that it will be useful,
17| // but WITHOUT ANY WARRANTY; without even the implied warranty of
18| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19| // General Public License for more details.
20| //
21| // You should have received a copy of the GNU General Public License
22| // along with this program; if not, write to the Free Software
23| // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24| // 02111-1307, USA.
25|
26| // $Log: link.cc,v $
27| // Revision 1.25 2002/12/09 13:53:50 chalky
28| // Fixed to not use the PyString's internal data for the toc list
29| //
30| // Revision 1.24 2002/11/17 12:11:43 chalky
31| // Reformatted all files with astyle --style=ansi, renamed fakegc.hh
32| //
33| // Revision 1.23 2002/11/01 08:15:31 chalky
34| // Remove debug cout
35| //
36| // Revision 1.22 2002/11/01 07:15:56 chalky
37| // Reject duplicate links
38| //
39| // Revision 1.21 2002/11/01 04:04:29 chalky
40| // Clean up HTML: no empty spans, decode &2c; in URL, to-EOL comments.
41| //
42| // Revision 1.20 2002/10/28 18:03:54 chalky
43| // Properly decode scoped names, and avoid crash if not using links_scope
44| //
45| // Revision 1.19 2002/10/28 17:38:17 chalky
46| // Ooops, get rid of #
47| //
48| // Revision 1.18 2002/10/28 17:29:09 chalky
49| // Add anchor link with line number to each line
50| //
51| // Revision 1.17 2002/10/25 08:56:56 chalky
52| // Prevent bad output by clipping to end of line
53| //
54| // Revision 1.16 2002/10/11 11:09:12 chalky
55| // Remove debugging perror statement
56|
57| /*
58| * This is the 'link' program, that reads a .links file and the original file,
59| * and outputs a file with the links interspersed as HTML. It is designed such
60| * that this output be placed inside another HTML file, and as such has no
61| * other HTML except the actual output and the links.
62| */
63|
64| #include <iostream>
65| #include <fstream>
66| #include <iterator>
67| #include <vector>
68| #include <set>
69| #include <map>
70| #include <string>
71| #include <limits.h>
72|
73| #include <stdio.h>
74|
75|
76| namespace
77| {
78|
79|
80|
81| struct Link
82| {
83|
84| typedef std::vector<std::string> Name;
85|
86| int line, col;
87|
88|
89|
90| enum Type
91| {
92| LINK_START,
93| REF_START,
94| SPAN_START,
95| SPAN_END,
96| REF_END,
97| LINK_END
98| };
99|
100| Type type;
101|
102| Name name;
103|
104| std::string desc;
105|
106|
107| struct lt_col
108| {
109| bool operator() (const Link* a, const Link* b) const
110| {
111| if (a->col != b->col)
112| return a->col < b->col;
113| return a->type < b->type;
114| }
115| };
116|
117| typedef std::set
118| <Link*, lt_col> Line;
119|
120| typedef std::map<int, Line> Map;
121|
122| std::ostream& write(std::ostream& o);
123| };
124|
125|
126| std::ostream& operator<<(std::ostream& o, const Link::Map::value_type& linepair)
127| {
128| const Link::Line& line = linepair.second;
129| o << "Line " << (*line.begin())->line << "\n";
130| Link::Line::const_iterator iter = line.begin();
131| while (iter != line.end())
132| (*iter++)->write(o) << "\n";
133| return o;
134| }
135|
136|
137| std::ostream& Link::write(std::ostream& o)
138| {
139| o << " " << col << " (" << type << ") ";
140| Link::Name::const_iterator iter = name.begin();
141| if (iter == name.end())
142| {
143| o << "<no name>";
144| return o;
145| }
146| o << *iter++;
147| while (iter != name.end())
148| o << "::" << *iter++;
149| return o;
150| }
151|
152| const char* input_filename = 0;
153|
154| const char* output_filename = 0;
155|
156| const char* links_filename = 0;
157|
158| const char* links_scope = 0;
159|
160| std::vector<std::string> toc_filenames;
161|
162|
163| bool links_append = false;
164|
165| Link::Map links;
166|
167|
168| typedef std::map<std::string, std::string> TOC;
169|
170| TOC toc;
171|
172|
173|
174| void parse_args(int argc, char** argv)
175| {
176| for (int i = 1; i < argc; i++)
177| {
178| if (!strcmp(argv[i], "-i"))
179| {
180| if (++i >= argc)
181| {
182| std::cerr << "-i needs argument" << std::endl;
183| exit(1);
184| }
185| input_filename = argv[i];
186| }
187| else if (!strcmp(argv[i], "-o"))
188| {
189| if (++i >= argc)
190| {
191| std::cerr << "-o needs argument" << std::endl;
192| exit(1);
193| }
194| output_filename = argv[i];
195| }
196| else if (!strcmp(argv[i], "-l"))
197| {
198| if (++i >= argc)
199| {
200| std::cerr << "-l needs argument" << std::endl;
201| exit(1);
202| }
203| links_filename = argv[i];
204| }
205| else if (!strcmp(argv[i], "-a"))
206| links_append = true;
207| else if (!strcmp(argv[i], "-t"))
208| {
209| if (++i >= argc)
210| {
211| std::cerr << "-t needs argument" << std::endl;
212| exit(1);
213| }
214| toc_filenames.push_back(argv[i]);
215| }
216| else
217| {
218| std::cerr << "Unknown option: " << argv[i] << std::endl;
219| exit(1);
220| }
221| }
222| if (!input_filename || !output_filename || !links_filename)
223| {
224| std::cerr << "Usage:\n";
225| std::cerr << " link -i input.cc -o output.html -l links.file [ -a ]\n";
226| std::cerr << " -i in\n -o out\n -l links\n -a append to out\n";
227| std::cerr << std::endl;
228| exit(1);
229| }
230| }
231|
232|
233|
234|
235|
236| void write(std::ostream& out, int col, char* buf, int len, int buflen)
237| {
238| char* ptr = buf, *end = buf+len;
239| while (ptr != end && col < buflen)
240| {
241| char c = *ptr++;
242| switch (c)
243| {
244| case '<':
245| out << "<";
246| break;
247| case '>':
248| out << ">";
249| break;
250| case ' ':
251| out << " ";
252| break;
253| case '"':
254| out << """;
255| break;
256| case '&':
257| out << "&";
258| break;
259| case '\t':
260| {
261| int next = ((col/8)+1)*8;
262| while (col++ < next)
263| out << " ";
264| continue;
265| }
266| default:
267| out << c;
268| }
269| col++;
270| }
271| }
272|
273|
274| void write_lineno(std::ostream& out, int line)
275| {
276| // out << setw(4) << line << "| "; <-- but with& nbsp;'s
277| out << "<a name=\"" << line << "\"></a>";
278| out << "<span class=\"file-linenum\">";
279| int mag = 10000;
280| while (mag > 1)
281| {
282| int digit = line / mag;
283| if (digit)
284| break;
285| out << " ";
286| mag /= 10;
287| }
288| out << line << "| ";
289| out << "</span>";
290| }
291|
292|
293| std::string decode(const std::string& str)
294| {
295| std::string ret;
296| std::string::const_iterator iter = str.begin(), end = str.end();
297| while (iter != end)
298| {
299| char a, b, c = *iter++;
300| if (c == '%')
301| {
302| a = *iter++;
303| b = *iter++;
304| if (a >= 'a')
305| a -= 'a' - 10;
306| else if (a >= 'A')
307| a -= 'A' - 10;
308| else
309| a -= '0';
310| if (b >= 'a')
311| b -= 'a' - 10;
312| else if (b >= 'A')
313| b -= 'A' - 10;
314| else
315| b -= '0';
316| c = a * 16 + b;
317| }
318| ret.push_back(c);
319| }
320| return ret;
321| }
322|
323|
324|
325| void write_indent(std::ostream& out, char* buf, int& col, int buflen)
326| {
327| int len = 0;
328| char* ptr = buf;
329| while (*ptr && (*ptr == ' ' || *ptr == '\t'))
330| ptr++, len++;
331| if (!len)
332| return;
333| out << "<span class=\"file-indent\">";
334| write(out, col, buf, len, buflen);
335| out << "</span>";
336| col += len;
337| }
338|
339|
340| bool is_duplicate(Link* link, int len)
341| {
342| Link::Line& line = links[link->line];
343| Link::Line::iterator iter = line.find(link);
344| if (iter == line.end())
345| return false;
346|
347| // iter matches by col, but try to find match by name
348| while ((*iter)->name != link->name)
349| {
350| ++iter;
351| if (iter == line.end() || (*iter)->col != link->col)
352|
353| return false;
354| }
355|
356| // Matched start by name.. try and find end too
357| link->col += len;
358| iter = line.find(link);
359| if (iter == line.end())
360| return false;
361|
362| // iter matches by col, but try to find match by name
363| while ((*iter)->name != link->name)
364| {
365| ++iter;
366| if (iter == line.end() || (*iter)->col != link->col)
367|
368| return false;
369| }
370| // name matched at both ends
371| return true;
372| }
373|
374|
375| void read_links() throw (std::string)
376| {
377| std::ifstream in(links_filename);
378| char buf[4096];
379| if (!in)
380| {
381| return;
382| }
383| std::string word, type;
384| int line, len;
385| while (in)
386| {
387| if (!(in >> line))
388| break;
389| Link* link = new Link;
390| link->line = line;
391| in >> link->col >> len >> type;
392| link->col--; // we count at zero, file counts at one
393| if (len == -1)
394| len = INT_MAX/2; // div 2 to prevent wrap-around
395| if (type != "SPAN")
396| {
397| if (type == "DEF")
398| link->type = Link::LINK_START;
399| else if (type == "REF")
400| link->type = Link::REF_START;
401| else if (type == "CALL")
402| link->type = Link::REF_START;
403| else if (type == "IMPL")
404| link->type = Link::REF_START;
405| else if (type == "UDIR")
406| link->type = Link::REF_START;
407| else
408| link->type = Link::REF_START;
409| int c = -1;
410| // Use up field sep ' '
411| in.get();
412| // Loop over scoped name till next ' '
413| do
414| {
415| in >> word;
416| // Replace '160's with spaces
417| //for (std::string::size_type pos = word.find(160); pos != std::string::npos; pos = word.find(160, pos)) {
418| // word[pos] = ' ';
419| //}
420| word = decode(word);
421| size_t start = 0, end;
422| while ((end = word.find('\t', start)) != std::string::npos)
423| {
424| link->name.push_back(word.substr(start, end-start));
425| start = end + 1;
426| }
427| link->name.push_back(word.substr(start, end-start));
428| }
429| while (in && (c = in.get()) != '\n' && c != ' ');
430| // Read description
431| if (!in.getline(buf, 4096))
432| break;
433| link->desc = decode(buf);
434| }
435| else
436| {
437| link->type = Link::SPAN_START;
438| in >> type;
439| link->name.push_back(decode(type));
440| }
441| // Skip to next line if duplicate link
442| if (is_duplicate(link, len))
443| continue;
444| links[line].insert(link);
445|
446| Link* end = new Link;
447| end->line = line;
448| end->col = link->col + len;
449| switch (link->type)
450| {
451| case Link::LINK_START:
452| end->type = Link::LINK_END;
453| break;
454| case Link::REF_START:
455| end->type = Link::REF_END;
456| break;
457| case Link::SPAN_START:
458| end->type = Link::SPAN_END;
459| break;
460| default:
461| ;
462| }
463| links[line].insert(end);
464| }
465| }
466|
467|
468| void dump_links()
469| {
470| std::copy(links.begin(), links.end(), std::ostream_iterator<Link::Map::value_type>(std::cout, "\n"));
471| }
472|
473|
474|
475| void read_tocs() throw (std::string)
476| {
477| char buf[3][4096];
478| int url_len = 0;
479| std::vector<std::string>::iterator iter = toc_filenames.begin();
480| while (iter != toc_filenames.end())
481| {
482| std::string toc_filename = *iter++;
483| size_t pipe = toc_filename.find('|');
484| if (pipe != std::string::npos)
485| {
486| strcpy(buf[2], toc_filename.c_str() + pipe + 1);
487| url_len = toc_filename.size() - pipe - 1;
488| toc_filename = toc_filename.substr(0, pipe);
489| }
490| std::ifstream in(toc_filename.c_str());
491| if (!in)
492| {
493| throw std::string("Error opening toc file: ")+toc_filename;
494| }
495| while (in)
496| {
497| // Get line
498| if (!in.getline(buf[0], 4096, ','))
499| break;
500| if (!in.getline(buf[1], 4096, ','))
501| break;
502| if (!in.getline(buf[2]+url_len, 4096-url_len))
503| break;
504| // convert& 2c;'s to commas
505| for (char*s = buf[0]; *s; s++)
506| {
507| if (!strncmp(s, "&", 5))
508| {
509| *s = '&';
510| memmove(s+1, s+5, strlen(s+5)+1);
511| }
512| if (!strncmp(s, "&2c;", 4))
513| {
514| *s = ',';
515| memmove(s+1, s+4, strlen(s+4)+1);
516| }
517| }
518| for (char*s = buf[2]+url_len; *s; s++)
519| {
520| if (!strncmp(s, "&", 5))
521| {
522| *s = '&';
523| memmove(s+1, s+5, strlen(s+5)+1);
524| }
525| if (!strncmp(s, "&2c;", 4))
526| {
527| *s = ',';
528| memmove(s+1, s+4, strlen(s+4)+1);
529| }
530| }
531| // We cheat here and exclude lines that dont have
532| // 'links_scope' at the start, and then chop it from the ones
533| // that do
534| if (links_scope)
535| {
536| if (strncmp(buf[0], links_scope, strlen(links_scope)))
537| continue;
538| memmove(buf[0], buf[0] + strlen(links_scope), strlen(buf[0]) - strlen(links_scope) + 1);
539| }
540| // Store in toc map
541| toc[buf[0]] = buf[2];
542| }
543| }
544| }
545|
546|
547|
548|
549| void link_file() throw (std::string)
550| {
551| std::ifstream in(input_filename);
552| if (!in)
553| {
554| throw std::string("Error opening input file: ")+input_filename;
555| }
556| std::ofstream out(output_filename, links_append ? std::ios::app : std::ios::out);
557| if (!out)
558| {
559| throw std::string("Error opening output file: ")+output_filename;
560| }
561| char buf[4096];
562| int line = 1, buflen;
563| Link::Map::iterator iter = links.begin(), end = links.end();
564| while (in)
565| {
566| // Get line
567| if (!in.getline(buf, 4096))
568| break;
569| buflen = strlen(buf);
570| write_lineno(out, line);
571| // Get Link::Line
572| while (iter != end && iter->first < line)
573| ++iter;
574| if (iter != end && iter->first == line)
575| {
576| // Insert links and write at same time
577| int col = 0;
578| write_indent(out, buf, col, buflen);
579| out << "<span class=\"file-default\">";
580| Link::Line& line = iter->second;
581| Link::Line::iterator link_i = line.begin();
582| while (link_i != line.end())
583| {
584| Link* link = *link_i++;
585| if (col < link->col)
586| {
587| write(out, col, buf+col, link->col - col, buflen);
588| col = link->col;
589| }
590| switch (link->type)
591| {
592| case Link::LINK_START:
593| case Link::REF_START:
594| {
595| std::string name;
596| Link::Name::iterator name_iter = link->name.begin();
597| if (name_iter != link->name.end())
598| name = *name_iter++;
599| while (name_iter != link->name.end())
600| name += "::" + *name_iter++;
601| TOC::iterator toc_iter = toc.find(name);
602| if (toc_iter == toc.end())
603| {
604| if (link->type == Link::LINK_START)
605| out << "<a name=\"" << name;
606| else
607| out << "<a href=\"#" << name;
608| }
609| else
610| {
611| std::string href = toc_iter->second;
612| if (link->type == Link::LINK_START)
613| out << "<a class=\"file-def\" name=\""<<name<<"\"";
614| else
615| out << "<a class=\"file-ref\"";
616| out << " href=\"" << href;
617| }
618| out << "\" title=\"" << link->desc << "\">";
619| break;
620| }
621| case Link::REF_END:
622| case Link::LINK_END:
623| out << "</a>";
624| break;
625| case Link::SPAN_START:
626| out << "<span class=\"" << link->name[0] << "\">";
627| break;
628| case Link::SPAN_END:
629| out << "</span>";
630| break;
631| }
632| }
633| // Write any left-over buffer
634| write(out, col, buf+col, -1, buflen);
635| out << "</span>";
636| }
637| else
638| {
639| // Write buf
640| int col = 0;
641| write_indent(out, buf, col, buflen);
642| if (col < buflen)
643| {
644| out << "<span class=\"file-default\">";
645| write(out, col, buf+col, -1, buflen);
646| out << "</span>";
647| }
648| }
649| out << "<br>\n";
650| line++;
651| }
652| }
653| }
654| ;
655|
656|
657| void reset()
658| {
659| links.clear();
660| toc.clear();
661| toc_filenames.clear();
662| }
663|
664| #ifdef STANDALONE
665|
666| //. Method for a stand-alone 'link-synopsis' program
667| int main(int argc, char** argv)
668| {
669| parse_args(argc, argv);
670|
671| try
672| {
673| read_links();
674|
675| read_tocs();
676|
677| link_file();
678| }
679| catch (std::string err)
680| {
681| std::cerr << "Error: " << err << std::endl;
682| return 1;
683| }
684|
685| return 0;
686| }
687|
688| #else
689|
690| #include PYTHON_INCLUDE
691|
692| extern "C"
693| {
694| static PyObject* linkError;
695|
696|
697| static PyObject* py_link(PyObject* self, PyObject* args)
698| {
699| PyObject *py_tocs, *py_file;
700|
701| if (!PyArg_ParseTuple(
702| args, "Ossss",
703| &py_tocs,& input_filename,& output_filename,& links_filename,& links_scope
704| ))
705| return NULL;
706|
707| // Extract TOC array
708| int toc_size = PyList_Size(py_tocs);
709| for (int i = 0; i < toc_size; i++)
710| {
711| if (!(py_file = PyList_GetItem(py_tocs, i)))
712| return NULL;
713| char* filename = PyString_AsString(py_file);
714| if (!filename)
715| return NULL;
716| toc_filenames.push_back(filename);
717| }
718| // Extract filenames
719| if (!(input_filename))
720| return NULL;
721| if (!(output_filename))
722| return NULL;
723| if (!(links_filename))
724| return NULL;
725| if (!(links_scope))
726| return NULL;
727| // Do stuff
728| try
729| {
730| read_links();
731| read_tocs();
732| link_file();
733| reset();
734| }
735| catch (const std::string& err)
736| {
737| std::cerr << "Error: " << err << std::endl;
738| PyErr_SetString(linkError, err.c_str());
739| reset();
740| return NULL;
741| }
742|
743| Py_INCREF(Py_None);
744| return Py_None;
745| }
746|
747|
748| static PyMethodDef link_methods[] =
749| {
750| {(char*)"link", py_link, METH_VARARGS},
751| {NULL, NULL}
752| };
753|
754|
755| void initlink()
756| {
757| PyObject* m = Py_InitModule((char*)"link", link_methods);
758| PyObject_SetAttrString(m, (char*)"version", PyString_FromString("0.1"));
759|
760|
761| PyObject* d = PyModule_GetDict(m);
762| linkError = PyErr_NewException("link.error", NULL, NULL);
763| PyDict_SetItemString(d, "error", linkError);
764| }
765|
766| };
767|
768|
769| #endif
770|