source: trunk/gsdl/lib/display.cpp@ 115

Last change on this file since 115 was 114, checked in by rjmcnab, 25 years ago

Made the source more portable.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 38.1 KB
Line 
1/**********************************************************************
2 *
3 * display.cpp -- Context sensitive macro language
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * PUT COPYRIGHT NOTICE HERE
7 *
8 * $Id: display.cpp 114 1999-01-19 01:38:20Z rjmcnab $
9 *
10 *********************************************************************/
11
12/*
13 $Log$
14 Revision 1.5 1999/01/19 01:38:12 rjmcnab
15
16 Made the source more portable.
17
18 Revision 1.4 1999/01/12 01:50:57 rjmcnab
19
20 Standard header.
21
22 Revision 1.3 1999/01/08 02:33:13 rjmcnab
23
24 Added standard header to source files.
25
26 */
27
28
29#include "display.h"
30#include "gsdlunicode.h"
31#include <assert.h>
32
33// include to get NULL
34#include <stdlib.h>
35
36
37
38/////////////////////////////////////
39// misc classes
40/////////////////////////////////////
41
42
43// precedencetype defines a 'precedence value' for each parameter
44typedef map<text_t, double, lttext_t> precedencetype;
45
46
47// macro value structure
48struct mvalue
49{
50 text_t filename;
51 text_t value;
52};
53
54inline bool operator==(const mvalue &x, const mvalue &y) {
55 return (x.filename==y.filename && x.value==y.value);
56}
57
58inline bool operator<(const mvalue &x, const mvalue &y) {
59 return (x.filename<y.filename ||
60 (x.filename==y.filename && x.filename<y.filename));
61}
62
63
64
65/////////////////////////////////////
66// stuff for defaultmacros_t
67/////////////////////////////////////
68
69typedef map<text_t, mvalue, lttext_t> defparammap;
70typedef map<text_t, defparammap, lttext_t> defmacromap;
71typedef map<text_t, defmacromap, lttext_t> defpackagemap;
72
73// all methods in defaultmacros_t assume the parameters
74// and packages in a standard form and not empty
75class defaultmacros_t
76{
77protected:
78 defpackagemap macros;
79
80public:
81 typedef defpackagemap::const_iterator const_package_iterator;
82 typedef defpackagemap::size_type package_size_type;
83
84 // setmacro returns 0 if there was no error,
85 // -1 if it redefined a macro
86 // -2 if it hid a Global macro
87 // -3 if it redefined a macro and hid a Global macro
88 // -4 if either a package, macroname, or params were not supplied
89 int setmacro(const text_t &package,
90 const text_t &macroname,
91 const text_t &params,
92 const text_t &filename,
93 const text_t &macrovalue);
94
95 void delmacro(const text_t &package,
96 const text_t &macroname,
97 const text_t &params);
98
99 void clear () {macros.erase(macros.begin(), macros.end());}
100
101 // top level stuff
102 const_package_iterator package_begin () const {return macros.begin();}
103 const_package_iterator package_end () const {return macros.end();}
104 bool package_empty () const {return macros.empty();}
105 package_size_type package_size() const {return macros.size();}
106
107 // methods to find things
108 defmacromap *package_find (const text_t &packagename);
109 defparammap *macro_find (const text_t &packagename,
110 const text_t &macroname);
111 mvalue *parameter_find (const text_t &packagename,
112 const text_t &macroname,
113 const text_t &parametername);
114};
115
116
117// setmacro returns 0 if there was no error,
118// -1 if it redefined a macro
119// -2 if it hid a Global macro
120// -3 if it redefined a macro and hid a Global macro
121// -4 if either a package, macroname, or params were not supplied
122int defaultmacros_t::setmacro(const text_t &package,
123 const text_t &macroname,
124 const text_t &params,
125 const text_t &filename,
126 const text_t &macrovalue)
127{
128 text_t constGlobal("Global");
129 paramhashtype paramhash;
130
131 defmacromap *macromapptr;
132 defparammap *parammapptr;
133 mvalue *mvalueptr;
134
135 int warning = 0;
136
137 // check to see if we would be hiding a Global macro with the
138 // same name. Note: it doesn't matter what parameters are used
139 // a Global macro will still be hid by a non-Global one with
140 // the same name.
141 if ((package != constGlobal) &&
142 (macro_find (constGlobal, macroname) != NULL))
143 {
144 warning -= 2; // found the macroname
145 }
146
147 // define this macro
148 macromapptr = &(macros[package]); // -- package
149 parammapptr = &((*macromapptr)[macroname]); // -- macro name
150 // -- parameters
151 if ((*parammapptr).find(params) != (*parammapptr).end()) {
152 warning -= 1; // found the parameters
153 }
154 mvalueptr = &((*parammapptr)[params]);
155
156 // -- value
157 (*mvalueptr).filename = filename;
158 (*mvalueptr).value = macrovalue;
159
160 return warning;
161}
162
163void defaultmacros_t::delmacro(const text_t &package,
164 const text_t &macroname,
165 const text_t &params)
166{
167 // make sure everything was supplied
168 if (package.empty() || macroname.empty() || params.empty()) return;
169
170 // find the package and macroname
171 defparammap *parammapptr = macro_find(package, macroname);
172 if (parammapptr == NULL) return;
173
174 // find the parameters
175 defparammap::iterator parammapit = parammapptr->find(params);
176 if (parammapit == parammapptr->end()) return;
177
178 // finally delete this element
179 parammapptr->erase(parammapit);
180}
181
182defmacromap *defaultmacros_t::package_find (const text_t &packagename)
183{
184 if (packagename.empty()) return NULL;
185
186 defpackagemap::iterator it = macros.find(packagename);
187 if (it == macros.end()) return NULL;
188
189 return &((*it).second);
190}
191
192defparammap *defaultmacros_t::macro_find (const text_t &packagename,
193 const text_t &macroname)
194{
195 defmacromap *macromapptr = package_find(packagename);
196 if (macromapptr == NULL || macroname.empty()) return NULL;
197
198 defmacromap::iterator it = (*macromapptr).find(macroname);
199 if (it == (*macromapptr).end()) return NULL;
200
201 return &((*it).second);
202}
203
204mvalue *defaultmacros_t::parameter_find (const text_t &packagename,
205 const text_t &macroname,
206 const text_t &parametername)
207{
208 defparammap *parammapptr = macro_find(packagename, macroname);
209 if (parammapptr == NULL || parametername.empty()) return NULL;
210
211 defparammap::iterator it = (*parammapptr).find(parametername);
212 if (it == (*parammapptr).end()) return NULL;
213
214 return &((*it).second);
215}
216
217
218
219/////////////////////////////////////
220// stuff for currentmacros_t
221/////////////////////////////////////
222
223typedef map<text_t, mvalue, lttext_t> curmacromap;
224typedef map<text_t, curmacromap, lttext_t> curpackagemap;
225
226// all methods in currentmacros_t assume the parameters
227// and packages in a standard form and not empty
228class currentmacros_t
229{
230protected:
231 curpackagemap macros;
232
233public:
234 typedef curpackagemap::const_iterator const_package_iterator;
235 typedef curpackagemap::size_type package_size_type;
236
237 // setmacro returns 0 if there was no error,
238 // -1 if it redefined a macro
239 // -4 if either a package, or macroname were not supplied
240 int setmacro(const text_t &package,
241 const text_t &macroname,
242 const text_t &filename,
243 const text_t &macrovalue);
244
245 void delmacro(const text_t &package,
246 const text_t &macroname);
247
248 void clear () {macros.erase(macros.begin(), macros.end());}
249
250 // top level stuff
251 const_package_iterator package_begin () const {return macros.begin();}
252 const_package_iterator package_end () const {return macros.end();}
253 bool package_empty () const {return macros.empty();}
254 package_size_type package_size() const {return macros.size();}
255
256 // methods to find things
257 curmacromap *package_find (const text_t &packagename);
258 mvalue *macro_find (const text_t &packagename,
259 const text_t &macroname);
260};
261
262
263// setmacro returns 0 if there was no error,
264// -1 if it redefined a macro
265// -4 if either a package, or macroname were not supplied
266int currentmacros_t::setmacro(const text_t &package,
267 const text_t &macroname,
268 const text_t &filename,
269 const text_t &macrovalue)
270{
271 int warning = 0;
272
273 // make sure everything was supplied
274 if (package.empty() || macroname.empty()) return -4;
275
276 // define this macro
277 curmacromap *macromapptr = &(macros[package]);
278 if ((*macromapptr).find(macroname) != (*macromapptr).end())
279 {
280 warning -= 1; // found the macroname
281 }
282 mvalue *mvalueptr = &((*macromapptr)[macroname]);
283
284 // -- value
285 (*mvalueptr).filename = filename;
286 (*mvalueptr).value = macrovalue;
287
288 return warning;
289}
290
291void currentmacros_t::delmacro(const text_t &package,
292 const text_t &macroname)
293{
294 // make sure everything was supplied
295 if (package.empty() || macroname.empty()) return;
296
297 // find the package
298 curmacromap *macromapptr = package_find(package);
299 if (macromapptr == NULL) return;
300
301 // find the macroname
302 curmacromap::iterator macromapit = macromapptr->find(macroname);
303 if (macromapit == macromapptr->end()) return;
304
305 // finally delete this element
306 macromapptr->erase(macromapit);
307}
308
309curmacromap *currentmacros_t::package_find (const text_t &packagename)
310{
311 if (packagename.empty()) return NULL;
312
313 curpackagemap::iterator it = macros.find(packagename);
314 if (it == macros.end()) return NULL;
315
316 return &((*it).second);
317}
318
319mvalue *currentmacros_t::macro_find (const text_t &packagename,
320 const text_t &macroname)
321{
322 curmacromap *macromapptr = package_find(packagename);
323 if (macromapptr == NULL || macroname.empty()) return NULL;
324
325 curmacromap::iterator it = (*macromapptr).find(macroname);
326 if (it == (*macromapptr).end()) return NULL;
327
328 return &((*it).second);
329}
330
331
332
333
334/////////////////////////////////////
335// support functions
336/////////////////////////////////////
337
338// this calculation of precedence will make one occurance of a high
339// precedence item override many occurances of lower precedence items
340void calcprecedence (const text_t &precedstr, precedencetype &precedhash)
341{
342 text_t param;
343 double multiple = 5.0;
344 double nextprec = multiple;
345
346 text_t::const_iterator here, end;
347
348 // set precedhash to an empty hash
349 precedhash.erase(precedhash.begin(), precedhash.end());
350
351 // iterate through paramstring ignoring space and extracting
352 // out key value pairs.
353 here = precedstr.begin();
354 end = precedstr.end();
355 while (here != end)
356 {
357 // get the next parameter
358 param.clear(); // set param to an empty string
359
360 while (here != end)
361 {
362 if (*here == ',')
363 {
364 // found the end of the parameter
365 here++;
366 break;
367 }
368 else if (*here == ' ')
369 {
370 // do nothing (ignore spaces)
371 }
372 else
373 {
374 // character in parameter
375 param.push_back (*here); // copy this character
376 }
377
378 here++;
379 }
380
381 // if a parameter was found insert its value
382 if (!param.empty())
383 {
384 precedhash[param] = nextprec;
385 nextprec *= multiple;
386 }
387 }
388}
389
390
391// decides how specific any given parameter is to the page parameters. Returns
392// negative if any options are different or else the sum of the precedence for
393// the options which match.
394double getspecificness(const paramhashtype &pageparams,
395 const paramhashtype &theseparams,
396 const precedencetype &precedhash)
397{
398 double specificness = 0.0;
399 paramhashtype::const_iterator here, pagevalueit;
400 precedencetype::const_iterator precedit;
401 text_t thesekey, thesevalue, constignore("ignore");
402
403 here = theseparams.begin();
404 while (here != theseparams.end())
405 {
406 thesekey = (*here).first;
407 thesevalue = (*here).second;
408
409 if (thesekey == constignore)
410 {
411 // do nothing, this is a placeholder
412 }
413 else
414 {
415 pagevalueit = pageparams.find(thesekey);
416 if ((pagevalueit != pageparams.end()) &&
417 ((*pagevalueit).second == thesevalue))
418 {
419 // this parameter fits, add its precedence value
420 precedit = precedhash.find(thesekey);
421 if (precedit == precedhash.end())
422 specificness += 1.0; // no precedence defined
423 else
424 specificness += (*precedit).second;
425 }
426 else
427 {
428 return -1.0; // did not match
429 }
430 }
431
432 here++;
433 }
434
435 return specificness;
436}
437
438
439void splitparams (const text_t &paramstring, paramhashtype &paramhash)
440{
441 text_t key;
442 text_t value;
443
444 text_t::const_iterator here, end;
445
446 // set paramhash to an empty hash
447 paramhash.erase(paramhash.begin(), paramhash.end());
448
449 // iterate through paramstring ignoring space and extracting
450 // out key value pairs.
451 here = paramstring.begin();
452 end = paramstring.end();
453 while (here != end)
454 {
455 // reset key and value
456 key.clear();
457 value.clear();
458
459 // get key
460 while (here != end)
461 {
462 if (*here == ',')
463 {
464 // have a key with no value
465 break;
466 }
467 else if (*here == '=')
468 {
469 // found the end of the key
470 here ++;
471 break;
472 }
473 else if (*here == ' ')
474 {
475 // do nothing (ignore spaces)
476 }
477 else
478 {
479 // character in key
480 key.push_back (*here); // copy this character
481 }
482 here ++;
483 }
484
485 // get value
486 while (here != end)
487 {
488 if (*here == '=')
489 {
490 // multiple values for one key, ignore previous
491 // values
492 value.clear();
493 }
494 else if (*here == ',')
495 {
496 // found the end of the value
497 here ++;
498 break;
499 }
500 else if (*here == ' ')
501 {
502 // do nothing (ignore spaces)
503 }
504 else
505 {
506 // character in value
507 value.push_back (*here); // copy this character
508 }
509 here ++;
510 }
511
512 // if a key was found insert the key/value pair in
513 // the map
514 if (!key.empty())
515 {
516 paramhash[key] = value;
517 }
518 }
519}
520
521
522void joinparams (const paramhashtype &paramhash, text_t &paramstring)
523{
524 //set paramstring to an empty string
525 paramstring.clear();
526
527 //iterate through the paramhash
528 paramhashtype::const_iterator thepair;
529 int firstparam = 1;
530
531 for (thepair=paramhash.begin();thepair!=paramhash.end(); thepair++)
532 {
533 if (!firstparam) paramstring.push_back(',');
534 firstparam = 0;
535
536 // insert pairs of "key=value"
537 text_t thevalue;
538 paramstring.append((*thepair).first);
539 paramstring.push_back('=');
540 paramstring.append((*thepair).second);
541 }
542}
543
544
545
546/////////////////////////////////////
547// parsing support functions
548/////////////////////////////////////
549
550inline int my_isspace (char ch)
551{
552 unsigned char c = ch;
553 return (((c > 0) && (c <= 31)) || (c == ' '));
554}
555
556
557inline int my_isalpha (char c)
558{
559 return ((c >= 'A' && c <= 'Z') ||
560 (c >= 'a' && c <= 'z'));
561}
562
563
564// as we are using one character lookahead the
565// value of line might be off by one.
566inline char my_get (istream &fin, int &line)
567{
568 char c;
569 fin.get(c);
570 if (c == '\n') line++;
571 return c;
572}
573
574
575inline unsigned short my_ttnextchar (text_t::const_iterator &here,
576 text_t::const_iterator &end)
577{
578 if (here != end)
579 {
580 here++;
581 if (here != end) return (*here);
582 }
583 return '\0';
584}
585
586
587
588/////////////////////////////////////
589// methods for display
590/////////////////////////////////////
591
592// public methods for display
593
594displayclass::displayclass ()
595{
596 defaultmacros = new defaultmacros_t;
597 defaultfiles = new fileinfomap;
598
599 orderparamlist = new paramspeclist;
600 currentmacros = new currentmacros_t;
601
602 outc = NULL;
603
604 logout = &cerr;
605}
606
607
608displayclass::~displayclass ()
609{
610 delete defaultmacros;
611 delete defaultfiles;
612
613 delete orderparamlist;
614 delete currentmacros;
615}
616
617// setdefaultmacro adds an entry to the list of default macros
618// returns 0 if there was no error,
619// -1 if it redefined a macro
620// -2 if it hid a Global macro
621// -3 if it redefined a macro and hid a Global macro
622// -4 if no macroname was supplied
623int displayclass::setdefaultmacro (text_t package, const text_t &macroname,
624 text_t params, const text_t &macrovalue)
625{
626 return setdefaultmacro (macroname, package, params, macrovalue, "memory");
627}
628
629
630
631// loads a default macro file (if it isn't already loaded)
632// returns 0 if didn't need to load the file (it was already loaded)
633// 1 if was (re)loaded
634// -1 an error occurred while trying to load the file
635int displayclass::loaddefaultmacros (text_t thisfilename)
636{
637 // convert the filename to a C string
638 char *filenamestr = thisfilename.getcstr();
639 outconvertclass text_t2ascii;
640
641 // see if we need to open this file -- this isn't implemented yet
642
643 // open the file
644 ifstream fin(filenamestr);
645 if (fin.fail()) return -1; // read failed
646
647 text_t package = "Global";
648 int line = 1;
649 char c = my_get(fin, line); // pre-fetch the next character
650
651 text_t macropackage, macroname, macroparameters, macrovalue;
652 int err; // for keeping track of whether an error occurred somewhere
653
654 while (!fin.eof())
655 {
656 // expect: white space, comment, "package", or macroname
657 if (my_isspace(c))
658 {
659 // found some white-space
660 c = my_get(fin, line);
661 }
662 else if (c == '#')
663 {
664 // found the start of a comment
665 // skip all characters up to the end of the line
666 c = my_get(fin, line); // skip the '#'
667 while (!fin.eof ())
668 {
669 if (c == '\n') break;
670 c = my_get(fin, line);
671 }
672
673 }
674 else if (c == 'p')
675 {
676 // found the start of 'package' (hopefully)
677 // get everything up to the next space
678 text_t tmp;
679 while (!fin.eof() && my_isalpha(c))
680 {
681 tmp.push_back((unsigned char)c);
682 c = my_get(fin, line);
683 }
684 // see if we have a package name
685 if (tmp == "package")
686 {
687 // skip all white space
688 while (!fin.eof() && my_isspace(c))
689 c = my_get(fin, line);
690
691 // get the package name
692 tmp.clear(); // init tmp
693 while (!fin.eof() && my_isalpha(c))
694 {
695 tmp.push_back((unsigned char)c);
696 c = my_get(fin, line);
697 }
698 package = to_uni(tmp); // convert from utf-8 to unicode
699 if (package.empty()) package = "Global";
700
701 }
702 else
703 {
704 // error
705 if (logout != NULL) {
706 (*logout) << "Expected 'package' on line " << line << "\n";
707 }
708 }
709
710 }
711 else if (c == '_')
712 {
713 // found the start of a macro (hopefully)
714 c = my_get(fin, line); // skip the _
715
716 // init variables
717 err = 0;
718 macropackage = package;
719 macroname.clear(); // init macroname
720 macroparameters.clear(); // init macroname
721 macrovalue.clear(); // init macroname
722
723 // get the macro name
724 while ((!fin.eof()) && (!my_isspace(c)) &&
725 (c != '\\') && (c != '_') &&(c != ':') &&
726 (macroname.size() < 80))
727 {
728 macroname.push_back((unsigned char)c);
729 c = my_get(fin, line);
730 }
731 macroname = to_uni(macroname); // convert from utf-8 to unicode
732
733 if (c == ':')
734 {
735 // we actually had the macro package
736 c = my_get(fin, line); // skip :
737 macropackage = macroname;
738 macroname.clear ();
739
740 // get the macro name (honest!)
741 while ((!fin.eof()) && (!my_isspace(c)) &&
742 (c != '\\') && (c != '_') &&(c != ':') &&
743 (macroname.size() < 80))
744 {
745 macroname.push_back((unsigned char)c);
746 c = my_get(fin, line);
747 }
748 macroname = to_uni(macroname); // convert from utf-8 to unicode
749 }
750
751 if (!err && c == '_')
752 {
753 c = my_get(fin, line); // skip the _
754
755 // skip all white space
756 while (!fin.eof() && my_isspace(c)) c = my_get(fin, line);
757 }
758 else err = 1;
759
760 // get the macro parameters (optional)
761 if (!err && c == '[')
762 {
763 c = my_get(fin, line); // skip the [
764 while ((!fin.eof()) && (c != '\n') && (c != '\\') && (c != ']'))
765 {
766 macroparameters.push_back((unsigned char)c);
767 c = my_get(fin, line);
768 }
769 macroparameters = to_uni(macroparameters);
770
771 if (c == ']')
772 {
773 c = my_get(fin, line); // skip the ]
774
775 // skip all white space
776 while (!fin.eof() && my_isspace(c)) c = my_get(fin, line);
777 }
778 else err = 2;
779 }
780
781 // get the macro value
782 if (!err && c == '{')
783 {
784 c = my_get(fin, line); // skip the {
785 while ((!fin.eof()) && (c != '}'))
786 {
787 if (c == '\\')
788 {
789 macrovalue.push_back((unsigned char)c); // keep the '\'
790 c = my_get(fin, line); // store the *next* value regardless
791 if (!fin.eof()) macrovalue.push_back((unsigned char)c);
792 c = my_get(fin, line);
793 }
794 macrovalue.push_back((unsigned char)c);
795 c = my_get(fin, line);
796 }
797 macrovalue = to_uni(macrovalue);
798
799 if (c == '}')
800 {
801 c = my_get(fin, line); // skip the }
802
803 // define the macro
804 err = setdefaultmacro (macropackage, macroname, macroparameters,
805 thisfilename, macrovalue);
806 if ((err == -1 || err == -3) && logout != NULL)
807 {
808 (*logout) << text_t2ascii << "Warning: redefinition of _" <<
809 package << ":" << macroname << "_[" << macroparameters <<
810 "] on line ";
811 (*logout) << line;
812 (*logout) << text_t2ascii << " of " << thisfilename << "\n";
813
814 }
815 else if (err == -2 && logout != NULL)
816 {
817 (*logout) << text_t2ascii << "Warning: _" <<
818 package << ":" << macroname << "_[" << macroparameters <<
819 "] on line ";
820 (*logout) << line;
821 (*logout) << text_t2ascii << " of " <<
822 thisfilename << " hides a Global macro with the same name\n";
823 }
824 else if (err == -4 && logout != NULL)
825 {
826 (*logout) << text_t2ascii << "Error: macro name expected on line ";
827 (*logout) << line ;
828 (*logout) << text_t2ascii << " of " << thisfilename << "\n";
829 }
830
831 err = 0; // for the test below
832 }
833 else err = 3;
834 }
835 else err = 4;
836
837 if (err)
838 {
839 // found an error, skip to the end of the line
840 if (logout != NULL) {
841 (*logout) << text_t2ascii << "Err: Unexpected input on line ";
842 (*logout) << line;
843 (*logout) << text_t2ascii << " (";
844 (*logout) << err;
845 (*logout) << text_t2ascii << ")\n";
846 }
847 while (!fin.eof ())
848 {
849 if (c == '\n') break;
850 c = my_get(fin, line);
851 }
852 }
853
854 }
855 else
856 {
857 // found an error, skip to the end of the line
858 if (logout != NULL) {
859 (*logout) << "Error: Unexpected input on line " << line << "\n";
860 }
861 while (!fin.eof ())
862 {
863 if (c == '\n') break;
864 c = my_get(fin, line);
865 }
866
867 }
868 }
869
870 fin.close ();
871
872 // free up memory
873 delete filenamestr;
874 return 0;
875}
876
877
878// prepares to create a page.
879void displayclass::openpage (const text_t &thispageparams,
880 const text_t &thisprecedence)
881{
882 // init variables
883 currentmacros->clear();
884
885 // reload any default macro files which have changed.
886 if (checkdefaultmacrofiles() < 0)
887 {
888 // print error message
889 }
890
891 setpageparams (thispageparams, thisprecedence);
892}
893
894
895// changes the parameters for the current page.
896void displayclass::setpageparams (text_t thispageparams,
897 text_t thisprecedence)
898{
899 paramhashtype thissplitparams, splittheseparams;
900 precedencetype precedhash;
901 text_tset::iterator text_tsetit;
902 paramspec tmpps;
903
904 precedence = thisprecedence;
905 calcprecedence(thisprecedence, precedhash);
906
907 splitparams(thispageparams, thissplitparams);
908 joinparams(thissplitparams, thispageparams);
909 params = thispageparams;
910
911 // get a list of parameters with their specificness
912 orderparamlist->erase(orderparamlist->begin(), orderparamlist->end());
913 for (text_tsetit=allparams.begin();
914 text_tsetit!=allparams.end();
915 text_tsetit++)
916 {
917 tmpps.param = (*text_tsetit);
918 splitparams(tmpps.param, splittheseparams);
919 tmpps.spec = getspecificness(thissplitparams, splittheseparams, precedhash);
920 if (tmpps.spec >= 0)
921 {
922 orderparamlist->push_back (tmpps);
923 }
924 }
925
926 // sort the list
927 sort(orderparamlist->begin(), orderparamlist->end());
928
929// paramspeclist::iterator pshere = orderparamlist->begin();
930// paramspeclist::iterator psend = orderparamlist->end();
931// while (pshere != psend)
932// {
933// cerr << text_t2ascii << "param=" << (*pshere).param;
934// cerr << " spec=" << (int)((*pshere).spec) << "\n";
935// pshere++;
936// }
937}
938
939
940// overrides (or sets) a macro for the current page.
941// returns 0 if there was no error,
942// -1 if it redefined a macro
943// -4 if no macroname was supplied
944int displayclass::setmacro (const text_t &macroname,
945 text_t package,
946 const text_t &macrovalue)
947{
948 // make sure a macroname was supplied
949 if (macroname.empty()) return -4;
950
951 // make package "Global" if it doesn't point to anything yet
952 if (package.empty()) package = "Global";
953
954 // set the macro
955 return currentmacros->setmacro(package, macroname, "memory", macrovalue);
956}
957
958
959void displayclass::expandstring (const text_t &inputtext, text_t &outputtext)
960{
961 expandstring("", inputtext, outputtext);
962}
963
964
965void displayclass::expandstring (text_t package, const text_t &inputtext,
966 text_t &outputtext, int recursiondepth)
967{
968 text_t macroname, macropackage, macroargs;
969 text_t::const_iterator tthere = inputtext.begin();
970 text_t::const_iterator ttend = inputtext.end();
971 unsigned short c;
972
973 if (package.empty()) package = "Global";
974
975 outputtext.clear();
976
977 // use one-character lookahead
978 if (tthere != ttend) c = (*tthere);
979 while (tthere != ttend)
980 {
981 if (c == '\\')
982 {
983 // found an escape character
984 c = my_ttnextchar (tthere, ttend); // skip the escape character
985 if (tthere != ttend)
986 {
987 if (c == 'n') outputtext.push_back('\n');
988 else if (c == 't') outputtext.push_back('\t');
989 else outputtext.push_back(c);
990 }
991 c = my_ttnextchar (tthere, ttend);
992
993 }
994 else if (c == '_')
995 {
996 // might have found the start of a macro
997
998 // embedded macros (macros within the names
999 // of other macros) are no longer supported -- Rodger.
1000
1001 // save the current state (in case we need to back out)
1002 text_t::const_iterator savedtthere = tthere;
1003 unsigned short savedc = c;
1004
1005 macroname.clear();
1006 macropackage = package;
1007 macroargs.clear();
1008
1009 c = my_ttnextchar (tthere, ttend); // skip the '_'
1010
1011 // get the macroname
1012 while (tthere != ttend && (!my_isspace(c)) &&
1013 (c != '\\') && (c != '_') &&(c != ':') &&
1014 (macroname.size() < 80))
1015 {
1016 macroname.push_back((unsigned char)c);
1017 c = my_ttnextchar (tthere, ttend);
1018 }
1019
1020 if (c == ':')
1021 {
1022 // we actually had the macro package
1023 c = my_ttnextchar (tthere, ttend);
1024 macropackage = macroname;
1025 macroname.clear ();
1026
1027 // get the macro name (honest!)
1028 while ((tthere != ttend) && (!my_isspace(c)) &&
1029 (c != '\\') && (c != '_') &&(c != ':') &&
1030 (macroname.size() < 80))
1031 {
1032 macroname.push_back((unsigned char)c);
1033 c = my_ttnextchar (tthere, ttend);
1034 }
1035 }
1036
1037 if ((tthere != ttend) && (c == '_') &&
1038 (macroname.size() > 0))
1039 {
1040 // found a macro!!!! (maybe)
1041
1042 c = my_ttnextchar (tthere, ttend); // skip the '_'
1043
1044 // get the parameters (if there are any)
1045 if ((tthere != ttend) && (c == '('))
1046 {
1047 c = my_ttnextchar (tthere, ttend); // skip '('
1048
1049 // have to be careful of quotes
1050 unsigned short quote = '\0';
1051 while (tthere != ttend)
1052 {
1053 if (c == '\\')
1054 {
1055 // have an escape character, save escape
1056 // character and next character as well
1057 macroargs.push_back (c);
1058 c = my_ttnextchar (tthere, ttend);
1059
1060 }
1061 else if (quote == '\0')
1062 {
1063 // not in a quote at the moment
1064 if (c == ')')
1065 {
1066 // found end of the arguments
1067 c = my_ttnextchar (tthere, ttend); // skip ')'
1068 break;
1069
1070 }
1071 else if (c == '\'' || c == '\"')
1072 {
1073 // found a quote
1074 quote = c;
1075 }
1076
1077 }
1078 else
1079 {
1080 // we are in a quote
1081 if (c == quote) quote = '\0';
1082 }
1083
1084 // save this character
1085 if (tthere != ttend) macroargs.push_back (c);
1086 c = my_ttnextchar (tthere, ttend);
1087 }
1088 }
1089
1090 //
1091 // we now have the macropackage, macroname, and macroargs
1092 //
1093
1094 // try to expand out this macro
1095 text_t expandedmacro;
1096 if (macro (macroname, macropackage, macroargs, expandedmacro, recursiondepth))
1097 {
1098 // append the expanded macro
1099 outputtext += expandedmacro;
1100 }
1101 else
1102 {
1103 // wasn't a macro
1104 tthere = savedtthere;
1105 c = savedc;
1106 outputtext.push_back(c); // the '_'
1107 c = my_ttnextchar (tthere, ttend);
1108 }
1109
1110
1111 }
1112 else
1113 {
1114 // wasn't a macro
1115 tthere = savedtthere;
1116 c = savedc;
1117 outputtext.push_back(c); // the '_'
1118 c = my_ttnextchar (tthere, ttend);
1119 }
1120
1121 }
1122 else
1123 {
1124 // nothing of interest
1125 outputtext.push_back(c); // the '_'
1126 c = my_ttnextchar (tthere, ttend);
1127 }
1128 }
1129}
1130
1131
1132// protected methods for display
1133
1134// reloads any default macro files which have changed
1135// returns 0 no errors occurred
1136// -1 an error occurred while trying to load one of the files
1137int displayclass::checkdefaultmacrofiles ()
1138{
1139 // this isn't implemented yet
1140 return 0;
1141}
1142
1143// setdefaultmacro adds an entry to the list of default macros
1144// returns 0 if there was no error,
1145// -1 if it redefined a macro
1146// -2 if it hid a Global macro
1147// -3 if it redefined a macro and hid a Global macro
1148// -4 if no macroname was supplied
1149int displayclass::setdefaultmacro (text_t package, const text_t &macroname,
1150 text_t params, const text_t &filename,
1151 const text_t &macrovalue)
1152{
1153 // make sure a macroname was supplied
1154 if (macroname.empty()) return -4;
1155
1156 // put the parameters in a standard form
1157 paramhashtype paramhash;
1158 if (params.empty()) params = "ignore=yes";
1159 splitparams (params, paramhash);
1160 joinparams (paramhash, params);
1161
1162 // make package "Global" if it doesn't point to anything yet
1163 if (package.empty()) package = "Global";
1164
1165 // remember these parameters
1166 allparams.insert (params);
1167
1168 // remember this filename (this part isn't finished yet -- Rodger).
1169
1170 // set the macro
1171 return defaultmacros->setmacro(package, macroname, params, filename, macrovalue);
1172}
1173
1174// (recursively) expand out a macro
1175// returns true if the macro was found
1176// false if the macro was not found
1177bool displayclass::macro (const text_t &macroname, text_t macropackage,
1178 const text_t &macroparam, text_t &outputtext,
1179 int recursiondepth)
1180{
1181 outconvertclass text_t2ascii;
1182 text_tlist splitmacroparam;
1183 text_t::const_iterator hereit, endit;
1184 text_t aparam;
1185 unsigned short c, quote;
1186
1187 // cerr << "r: " << recursiondepth << "\n";
1188
1189 // check for deep recursion
1190 if (recursiondepth >= MAXRECURSIONDEPTH)
1191 {
1192 if (logout != NULL)
1193 (*logout) << "Warning: deep recursion, limiting macro expansion\n";
1194 return false;
1195 }
1196
1197 // check the macropackage
1198 if (macropackage.empty()) macropackage = "Global";
1199
1200 // get the parameters (but don't expand them)
1201 if (macroparam.size() > 0) {
1202 // set up for loop
1203 hereit = macroparam.begin();
1204 endit = macroparam.end();
1205 if (hereit != endit) c = (*hereit);
1206
1207 while (hereit != endit) {
1208 // get the next parameter
1209 aparam.clear();
1210 quote = '\0'; // not-quoted unless proven quoted
1211
1212 // ignore initial whitespace
1213 while ((hereit!=endit)&&my_isspace(c)) c=my_ttnextchar(hereit,endit);
1214
1215 // look for the end of the parameter
1216 while (hereit != endit) {
1217 if (c == '\\') {
1218 // found escape character, also get next character
1219 aparam.push_back(c);
1220 c = my_ttnextchar (hereit, endit);
1221 if (hereit != endit) aparam.push_back(c);
1222
1223 } else if (quote=='\0' && (c=='\'' || c=='"')) {
1224 // found a quoted section
1225 quote = c;
1226
1227 } else if (quote!='\0' && c==quote) {
1228 // found the end of a quote
1229 quote = '\0';
1230
1231 } else if (quote=='\0' && c==',') {
1232 // found the end of a parameter
1233 c = my_ttnextchar (hereit, endit);
1234 break;
1235
1236 } else {
1237 // ordinary character
1238 aparam.push_back(c);
1239 }
1240
1241 c = my_ttnextchar (hereit, endit);
1242 }
1243
1244 // add this parameter to the list
1245 splitmacroparam.push_back(aparam);
1246 }
1247 }
1248
1249 // I'm only supporting a simple version of _If_ at the moment
1250 if (macroname == "If")
1251 {
1252 // get the condition, then clause and else clause
1253 text_tlist::iterator paramcond = splitmacroparam.begin();
1254 text_tlist::iterator paramend = splitmacroparam.end();
1255 text_tlist::iterator paramthen = paramend;
1256 text_tlist::iterator paramelse = paramend;
1257
1258 if (paramcond != paramend)
1259 {
1260 paramthen = paramcond;
1261 paramthen++;
1262 }
1263 if (paramthen != paramend)
1264 {
1265 paramelse = paramthen;
1266 paramelse++;
1267 }
1268
1269 // will always output something
1270 outputtext.clear();
1271
1272 // expand out the first parameter
1273 if (paramcond != paramend)
1274 {
1275 text_t tmpoutput;
1276 expandstring (macropackage, *paramcond, tmpoutput, recursiondepth+1);
1277
1278 // test the expanded string
1279 if (tmpoutput.size() > 1 ||
1280 (tmpoutput.size() == 1 && tmpoutput[0] != '0'))
1281 {
1282 // true
1283 if (paramthen != paramend)
1284 expandstring (macropackage, *paramthen, outputtext, recursiondepth+1);
1285 }
1286 else
1287 {
1288 // false
1289 if (paramelse != paramend)
1290 expandstring (macropackage, *paramelse, outputtext, recursiondepth+1);
1291 }
1292 }
1293
1294 return true;
1295 }
1296
1297 // try and find this macro
1298
1299 // this list might be replaced by something a little more
1300 // sophisticated in the future.
1301 text_tlist packagelist;
1302 packagelist.push_back (macropackage);
1303 packagelist.push_back ("Global");
1304
1305 paramspeclist::iterator paramhere, paramend;
1306 text_tlist::iterator packagehere = packagelist.begin();
1307 text_tlist::iterator packageend = packagelist.end();
1308 mvalue *macrovalue = NULL;
1309 while ((macrovalue == NULL) && (packagehere != packageend))
1310 {
1311 // first look in the currentmacros
1312 macrovalue = currentmacros->macro_find(*packagehere, macroname);
1313 if (macrovalue == NULL)
1314 macrovalue = currentmacros->macro_find("Style", macroname);
1315
1316 // look in the default macros
1317 if (macrovalue == NULL)
1318 {
1319 // next look in the defaultmacros
1320 if (defaultmacros->macro_find (*packagehere, macroname) != NULL)
1321 {
1322 paramhere = orderparamlist->begin(); paramend = orderparamlist->end();
1323 while ((macrovalue == NULL) && (paramhere != paramend))
1324 {
1325// cerr << text_t2ascii << "\npackage: " << *packagehere << "\n";
1326// cerr << text_t2ascii << "macroname: " << macroname << "\n";
1327// cerr << text_t2ascii << "param: " << (*paramhere).param << "\n";
1328// cerr << "spec: " << (int)((*paramhere).spec) << "\n";
1329 macrovalue = defaultmacros->parameter_find(*packagehere, macroname,
1330 (*paramhere).param);
1331 paramhere++;
1332 }
1333 }
1334 }
1335
1336 // and again in the package "Style"
1337 if (macrovalue == NULL)
1338 {
1339 // next look in the defaultmacros
1340 if (defaultmacros->macro_find ("Style", macroname) != NULL)
1341 {
1342 paramhere = orderparamlist->begin(); paramend = orderparamlist->end();
1343 while ((macrovalue == NULL) && (paramhere != paramend))
1344 {
1345 macrovalue = defaultmacros->parameter_find("Style", macroname,
1346 (*paramhere).param);
1347 paramhere++;
1348 }
1349 }
1350 }
1351 if (macrovalue == NULL) packagehere++;
1352 }
1353
1354 if (macrovalue != NULL)
1355 {
1356 // we found a macro
1357
1358 // replace all the option macros (_1_, _2_, ...) in the
1359 // value of this macro
1360 text_t tempmacrovalue;
1361 tempmacrovalue.clear();
1362 hereit = macrovalue->value.begin();
1363 endit = macrovalue->value.end();
1364 if (hereit != endit) c = (*hereit);
1365 while (hereit != endit)
1366 {
1367 if (c == '\\')
1368 {
1369 // found an escape character
1370 tempmacrovalue.push_back(c);
1371 c = my_ttnextchar (hereit, endit);
1372 if (hereit != endit) tempmacrovalue.push_back(c);
1373 c = my_ttnextchar (hereit, endit);
1374 }
1375 else if (c == '_')
1376 {
1377 text_t::const_iterator savedhereit = hereit;
1378 unsigned short c2 = my_ttnextchar (hereit, endit);
1379 unsigned short c3 = my_ttnextchar (hereit, endit);
1380 if ((c2 >= '1') && (c2 <= '9') && (c3 == '_'))
1381 {
1382 // found an option macro, append the appropriate text
1383 text_tlist::iterator ttlisthere = splitmacroparam.begin();
1384 text_tlist::iterator ttlistend = splitmacroparam.end();
1385 while ((c2 > '1') && (ttlisthere != ttlistend))
1386 {
1387 c2--;
1388 ttlisthere++;
1389 }
1390 if (ttlisthere != ttlistend)
1391 tempmacrovalue.append(*ttlisthere);
1392
1393 c = my_ttnextchar (hereit, endit);
1394
1395 }
1396 else
1397 {
1398 // wasn't a option macro
1399 tempmacrovalue.push_back(c);
1400 hereit = savedhereit;
1401 c = my_ttnextchar (hereit, endit);
1402 }
1403 }
1404 else
1405 {
1406 tempmacrovalue.push_back(c);
1407 c = my_ttnextchar (hereit, endit);
1408 }
1409 }
1410
1411 // recursively replace this macro
1412 expandstring (*packagehere, tempmacrovalue, outputtext, recursiondepth+1);
1413
1414 return true;
1415 }
1416
1417 if (logout != NULL) {
1418 (*logout) << text_t2ascii << "Warning: _" <<
1419 macropackage << ":" << macroname << "_ not found\n";
1420 }
1421
1422 return false; // didn't find the macro
1423}
1424
1425
1426void displayclass::printdefaultmacros ()
1427{
1428 defpackagemap::const_iterator packagemapit;
1429 defmacromap::const_iterator macromapit;
1430 defparammap::const_iterator parammapit;
1431 const mvalue *mvalueptr;
1432 outconvertclass text_t2ascii;
1433
1434 text_t packagename, macroname, macroparam;
1435
1436 for (packagemapit = defaultmacros->package_begin();
1437 packagemapit != defaultmacros->package_end();
1438 packagemapit++)
1439 {
1440 packagename = (*packagemapit).first;
1441 // cerr << "***package : \"" << packagename << "\"\n";
1442
1443 for (macromapit = (*packagemapit).second.begin();
1444 macromapit != (*packagemapit).second.end();
1445 macromapit++)
1446 {
1447 macroname = (*macromapit).first;
1448 // cerr << "***macroname : \"" << macroname << "\"\n";
1449
1450 for (parammapit = (*macromapit).second.begin();
1451 parammapit != (*macromapit).second.end();
1452 parammapit++)
1453 {
1454 macroparam = (*parammapit).first;
1455 mvalueptr = &((*parammapit).second);
1456 cerr << text_t2ascii << "package : \"" << packagename << "\"\n";
1457 cerr << text_t2ascii << "macroname : \"" << macroname << "\"\n";
1458 cerr << text_t2ascii << "parameters: [" << macroparam << "]\n";
1459 cerr << text_t2ascii << "filename : \"" << mvalueptr->filename << "\"\n";
1460 cerr << text_t2ascii << "value : {" << mvalueptr->value << "}\n\n";
1461 }
1462 }
1463 }
1464}
1465
1466void displayclass::printallparams ()
1467{
1468 text_tset::iterator text_tsetit;
1469 outconvertclass text_t2ascii;
1470
1471 cerr << text_t2ascii << "** allparams\n";
1472 for (text_tsetit=allparams.begin();
1473 text_tsetit!=allparams.end();
1474 text_tsetit++) {
1475 cerr << text_t2ascii << (*text_tsetit) << "\n";
1476 }
1477 cerr << text_t2ascii << "\n";
1478}
1479
1480
1481
1482/////////////////////////////////////
1483// stuff to do tricky output
1484/////////////////////////////////////
1485
1486displayclass &operator<< (outconvertclass &theoutc, displayclass &display)
1487{
1488 display.setconvertclass (&theoutc);
1489 return display;
1490}
1491
1492displayclass &operator<< (displayclass &display, const text_t &t)
1493{
1494 outconvertclass *theoutc = display.getconvertclass();
1495
1496 if (theoutc == NULL) return display;
1497
1498 text_t output;
1499 display.expandstring (t, output);
1500 (*theoutc) << output;
1501
1502 return display;
1503}
1504
Note: See TracBrowser for help on using the repository browser.