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

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

istream.get(char&) caused funny problems with gcc3 so they've been replaced
with char=istream.get() instead.

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