source: tags/gsdl-2_21-distribution/gsdl/lib/display.cpp@ 1186

Last change on this file since 1186 was 1170, checked in by sjboddie, 24 years ago

minor modifications to get web library compiling under VC++ 6.0

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