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

Last change on this file since 3009 was 2965, checked in by jrm21, 22 years ago

Allow nested _If_ macros. We now count '(' and ')' characters, so you can now
do _If_(_foo_, _If_(_bar_,FOOBAR,FOONOBAR), _If_(_bar_,NOFOOBAR,NOFOONOBAR) )
inside a macro file. If you want to display a bracket, you can escape it.
Previously we had assumed that a ',' marked the end of a toplevel argument.

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