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

Last change on this file since 185 was 181, checked in by sjboddie, 25 years ago

Added eq and ne functionality to _If_

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