source: trunk/gsdl/src/library/display.cpp@ 4

Last change on this file since 4 was 4, checked in by sjboddie, 25 years ago

Initial revision

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