root/branches/New_Config_Format-branch/gsdl/lib/display.cpp @ 1279

Revision 1279, 47.3 KB (checked in by sjboddie, 20 years ago)

merged changes to trunk into New_Config_Format branch

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