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

Last change on this file since 1270 was 1217, checked in by sjboddie, 24 years ago

fixed a bug that I'd introduced earlier when attempting to get things
compiling on VC++ 6.0 - the gsdl-2.22 release (and possibly 2.21) was
affected and wouldn't have displayed chinese and Arabic characters
correctly

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