a1d7ae5f942cdb08d9906e8ead40c7121d2d0aa6
[oweals/cde.git] / cde / programs / dthelp / parser / pass1 / helptag / help.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: help.c /main/5 1998/04/06 13:18:57 mgreess $ */
24 /*   Copyright (c) 1986, 1987, 1988, 1989 Hewlett-Packard Co. */
25 /* Miscellaneous Procedures for HP Tag/TeX translator */
26
27 #include <stdint.h>
28
29 #include "userinc.h"
30 #include "globdec.h"
31
32 #include "LocaleXlate.h"
33 #include "XlationSvc.h"
34
35 void assert_hometopic_exists(M_NOPAR)
36 {
37 static const char hometopic[] = "-HOMETOPIC";
38 char id[SDLNAMESIZ + sizeof(hometopic)];
39 char *string =
40     GetDefaultHeaderString("UntitledElementDefaultHeadingString",
41                            M_SDATA,
42                            "Untitled");
43 if (nohometopic)
44     {
45     char    *pc;
46
47     sprintf(id, "%s%s", sdlReservedName, hometopic);
48     mb_starthelpnode("_HOMETOPIC", id, 1);
49     nohometopic = FALSE;
50     pc = NULL;
51     if (savedtitle)
52         pc = MakeMByteString(savedtitle);
53     fprintf(outfile,
54             "<HEAD CLASS=\"HEAD\" ABBREV=\"%s\"></HEAD>\n</VIRPAGE>\n",
55             pc ? pc : string);
56     if (pc)
57         m_free(pc, "multi-byte string");
58     }
59 m_free(string, "GetDefaultHeaderString return");
60 }
61
62 /* Determine base name for files (i.e., input name without extension).
63    Open output and error files */
64 void fbasename(M_NOPAR)
65 {
66 char *p, *q;
67 int n;
68 char save;
69 char fileListErr[] = "filelist.err";
70
71 m_errfile = NULL;
72 if (m_argc < 2)
73     {
74     m_error("Specify input file");
75     exit(TRUE);
76     }
77
78 if ((m_argc > 2) && (strchr(m_argv[2], 'f') || strchr(m_argv[2], 'F')))
79     filelist = TRUE;
80
81 /* Get installation directory */
82 #define CONTRIB "/usr/dt/bin/"
83
84 /* get our path if we can */
85 /* A hacked up ``which'', just to find our directory */
86 /* fills ``install'' with path to ourself */
87 {
88 char *path, *cp;
89 char buf[200];
90 char patbuf[BUFSIZ];
91 int quit, none;
92
93 quit = 0;
94 none = 1;
95
96 if ( *(m_argv[0]) == '/' )
97     {
98     /* fully qualified path to ourself was specified */
99     if (access(m_argv[0],1) == 0)
100         {
101         /* if full path name exists and is executable */
102         /* get the dirname */
103         for (p = m_argv[0]; *p ; p++) ; /* end of string, (the hard way) */
104         /* backup to dirsep */
105         for (; ; p--)
106             {
107             if (p < m_argv[0]) m_error("Internal Error.");
108             if (*p == dirsep) break; 
109             }
110         p++; /* just past the dirsep */
111         save = *p;
112         *p = M_EOS;
113         install = (char *) m_malloc(strlen(m_argv[0]) + 1,
114                                     "installation directory");
115         strcpy(install, m_argv[0]);
116         *p = save;
117
118         none = 0; /* we've got it. */
119         }
120     else
121         {
122         m_error("Internal Error (which).");
123         }
124     }
125 else
126     {
127     /* not fully specified, check each component of path for ourself */
128     snprintf(patbuf, sizeof(patbuf), "%s", getenv("PATH"));
129     path = patbuf;
130     cp = path;
131
132     while(1)
133         {
134         cp = strchr(path, ':');
135         if (cp == NULL)
136         quit++;
137         else
138         *cp = '\0';
139         sprintf(buf, "%s/%s", path, m_argv[0]);
140
141         if (access(buf, 1) == 0)
142             {
143             install = (char*) m_malloc(strlen(path) + 1,
144                                        "installation directory");
145             strcpy(install, path);
146             none = 0;
147             }
148         /* else, not an error if we can't find a particular one. */
149
150         path = ++cp;
151         if (quit || !none)
152             {
153             break; /* either out of paths, or we found it. */
154             }
155         }  /* end while */
156     }
157
158 if (none)
159     {
160     /* can't get it, use default */
161     install = (char *) m_malloc(strlen(CONTRIB) + 1,
162                                 "installation directory");
163     strcpy(install, CONTRIB);
164     }
165 /* else -- we've got it */
166 }
167
168 /* Set default working directory (from input filename) */
169 for (p = strchr(m_argv[1], M_EOS); p > m_argv[1] ; p--)
170     if (*(p - 1) == dirsep)
171         break;
172 if (p > m_argv[1])
173     {
174     save = *p;
175     *p = M_EOS;
176     work = (char *) m_malloc(strlen(m_argv[1]) + 1, "working directory");
177     strcpy(work, m_argv[1]);
178     indir = (char *) m_malloc(strlen(m_argv[1]) + 1, "input directory");
179     strcpy(indir, m_argv[1]);
180     *p = save;
181     }
182 else
183     {
184     indir = NULL;
185     }
186
187 /* Build base name */
188 q = strchr(m_argv[1], M_EOS);
189 while ((q > m_argv[1]) && (*q != '.') && (*q != dirsep))
190     q--;
191 defaultext = (LOGICAL) (*q != '.');
192 if (! defaultext) *q = M_EOS;
193 nodirbase = (char *) m_malloc(strlen(p) + 1,
194                               "basename without directory");
195 strcpy(nodirbase, p);
196 /* Get working directory option, if specified */
197 n = strlen(p);
198 base = (char *) m_malloc(n + strlen(".htg") + 1, "basename");
199 strcpy(base, p);
200 base[n] = '.';
201 baseext = base + n + 1;
202 if (! defaultext)
203     if (*(q + 1))
204         *q = '.';
205
206 options(TRUE); /* pluck only length changing optins */
207 if (usingshortnames)
208     {
209     /* Build short versions of basename */
210     /* set up global helpbase and helpext */
211     helpbase = (char *) m_malloc(strlen(work)            +
212                                      BASENAME_LIMIT      +
213                                      PRE_EXTENSION_LIMIT +
214                                      strlen(".ext")      +
215                                      1,
216                                  "help basename");
217     strcpy(helpbase, work);
218     strncat(helpbase, nodirbase, BASENAME_LIMIT);
219     helpext = helpbase + strlen(helpbase);
220     }
221 else
222     { /* Build long names */
223     /* set up global helpbase and helpext */
224     helpbase = (char *) m_malloc(strlen(work)            +
225                                      strlen(nodirbase)   +
226                                      PRE_EXTENSION_LIMIT +
227                                      strlen(".ext")      +
228                                      1,
229                                  "help basename");
230     strcpy(helpbase, work);
231     strcat(helpbase, nodirbase);
232     helpext = helpbase + strlen(helpbase);
233     }
234
235 /* Open error files */
236 if (filelist)
237     {
238     p = mb_malloc(strlen(work)+sizeof(fileListErr));
239     strcpy(p, work);
240     strcat(p, fileListErr);
241     m_openchk(&m_errfile, p, "w");
242     mb_free(&p);
243     }
244 else
245     {
246     strcpy(helpext, ".err");
247     m_openchk(&m_errfile, helpbase, "w");
248     }
249 }
250
251
252 /* This procedure starts a CHAPTER */
253 void chapstart(id)
254 M_WCHAR *id;
255 {
256 M_WCHAR *p, *q, *wc;
257 int i;
258 char *mbyte, *pc;
259 int   length;
260
261 char *chapterPrefixString =
262   GetDefaultHeaderString("ChapterElementDefaultHeadingString",
263                          M_SDATA,
264                          "Chapter");
265 char *chapterSuffixString =
266   GetDefaultHeaderString("ChapterSuffixElementDefaultHeadingString",
267                          M_SDATA,
268                          "");
269
270 rsectseq = FALSE;
271 chapst = FALSE;
272 chapinc = 0;
273 chapter++;
274 m_itoa(chapter, chapstring);
275
276 savid = checkid(id);
277 iderr = FALSE;
278
279 figno = 0;
280 tableno = 0;
281 footnoteno = 1;
282 fprintf(stderr,
283         "\n%s %s%s. ",
284         chapterPrefixString,
285         chapstring,
286         chapterSuffixString);
287 m_free(chapterPrefixString, "GetDefaultHeaderString return");
288 if (*chapterSuffixString)
289     m_free(chapterSuffixString, "GetDefaultHeaderString return");
290 }
291
292
293 /* Called at end of manual to report terms that occurred in the document
294    but not entered in the glossary */
295 void checkgloss(M_NOPAR)
296 {
297 int n;
298 M_WCHAR id[MAXTERM + 1];
299 M_TRIE *node[MAXTERM + 1];
300 M_TRIE *current;
301
302 if (! gtree.data) return;
303 n = 0;
304 current = gtree.data;
305 while (TRUE)
306     {
307     id[n] = current->symbol;
308     node[n] = current->next;
309     if (! id[n])
310         {
311         if ((intptr_t) current->data >= 0)
312             m_err1("No glossary definition for %s", id);
313         current = current->next;
314         while (! current)
315             {
316             n--;
317             if (n < 0) return;
318             current = node[n];
319             }
320         }
321     else
322         {
323         current = current->data;
324         n++;
325         }
326     }
327 }
328
329                 
330 /* End Error Message macro \starterrmsg call, check to see if Error Message
331    head is user specified or default */
332 void checkmsghead(M_NOPAR)
333 {
334 char *string =
335     GetDefaultHeaderString("MessagesElementDefaultHeadingString",
336                            M_SDATA,
337                            "Messages");
338 if (emsghead == DEFHEAD)
339     {  /* head not output yet */
340     fprintf(outfile, "<HEAD CLASS=\"HEAD\">%s</HEAD>\n", string);
341     emsghead = FALSE;
342     }
343 else if (emsghead == USERHEAD)
344     {  /* user specified head */
345     emsghead = FALSE;
346     }
347 m_free(string, "GetDefaultHeaderString return");
348 }  /* end checkmsghead() */
349
350
351 /* construct a qualified file name */
352 #if defined(M_PROTO)
353 static int mb_getqualified(char *qualname, char *unqualname)
354 #else
355 static int mb_getqualified(qualname, unqualname)
356 char *qualname;
357 char *unqualname;
358 #endif
359 {
360 #if defined(hpux) || defined(_AIX) || defined(sun) || defined(USL) || defined(__uxp__) || defined(__osf__) || defined(linux) || defined(CSRG_BASED)
361 FILE *f;
362 #endif
363 char fn[FNAMELEN];
364 char tokstr [ 20 ], *gp, *p, *pp, *fnp, curdir[FNAMELEN-1];
365 int roomleft = FNAMELEN - 1;
366
367 if (!unqualname)
368     {
369     *qualname = M_EOS;
370     return( 0 );
371     }
372
373 if (strlen(unqualname) < (size_t) FNAMELEN)
374     strcpy(fn, unqualname );
375 else
376     {
377     m_mberr1("Internal Error. File name too long for qualifying: %s",
378              unqualname);
379     return (-1);
380     }
381
382 fnp = fn;
383
384 #if defined(hpux) || defined(_AIX) || defined(sun) || defined(USL) || defined(__uxp__) || defined(__osf__) || defined(linux) || defined(CSRG_BASED)
385 qualname[0] = '\0';
386 gp = qualname + strlen(qualname);
387 roomleft = roomleft - strlen(qualname);
388
389 /* if path is from root, tack that on, else tack on the current
390  directory (for the referenced drive, if MS-DOS) */
391 /* We assume FNAMELEN is at least three (3), so no range checking here */
392 if ( *fnp == CSEP )
393     {
394     strcat(qualname, SSEP);
395     roomleft--;
396     ++fnp;
397     }
398 else
399     {
400     f = popen("/bin/pwd", "r");
401     fscanf(f, "%s", gp);
402     pclose(f);
403     if (strlen(gp) >= (size_t) roomleft)
404         {
405         m_mberr1("Internal error: possible stray pointer in getqualified(): %s",
406                  gp);
407         return(-1);
408         }
409     strcat(qualname,SSEP);
410     roomleft--;
411     }
412 #else
413 /* if MS-DOS, force to upper case, then get drive spec */
414 strupr ( fn );
415 if ( fn[1] == ':' ) {
416 strncpy ( qualname, fn, 2 );
417 fnp += 2;
418 }
419 else {
420 getcwd(qualname, roomleft);
421 }
422 qualname[2] = '\0';
423 gp = qualname + strlen ( qualname );
424 roomleft = roomleft - strlen ( qualname );
425 /* if path is from root, tack that on, else tack on the current
426  directory (for the referenced drive, if MS-DOS) */
427 if ( *fnp == CSEP ) {
428 strcat ( qualname, SSEP );
429 roomleft--;
430 ++fnp;
431 }
432 else {
433 /* assume current directory always !!! */
434 *gp = CSEP;
435 getcwd(curdir, FNAMELEN-1);
436 if (*curdir != *qualname) {
437   m_err1("Relative directory %s for non-current drive, can't qualify",
438           unqualname);
439   return (-1);
440   }
441 if (strlen(curdir) > 3) {
442   if ((strlen(curdir+3)+1) < roomleft) {  /* "1" for SSEP */
443     strcpy( gp+1, curdir+3 );
444     strcat ( qualname, SSEP );
445     roomleft = roomleft - strlen(curdir+3) - 1;  /* "1" for SSEP */
446     }
447   else {
448     m_err1("Internal error. File name too long for qualifying: %s",
449       unqualname);
450     return (-1);
451     }
452   }
453 }
454 #endif
455
456 strcpy(tokstr, " \r\n\t");
457 strcat(tokstr, SSEP);
458 p = NULL;
459 do  {
460     p = strtok(( p == NULL ) ? fnp : NULL, tokstr);
461     if ( p == NULL ) break;
462     if ( *p == '.' ) /* alias */
463         {
464         if ( *(p+1) == '.' ) /* parent */
465             {
466             *strrchr(qualname, CSEP) = '\0';
467             pp = strrchr(qualname, CSEP);
468             if (pp == NULL) /* FAIL */
469                 {
470                 m_mberr1("Internal error. Failed in qualifying %s", unqualname);
471                 return ( -1 );
472                 }
473             else
474                 {
475                 *(pp+1) = '\0';
476                 }
477             }
478         }
479     else
480         {
481         if ((strlen(p)+1) < (size_t) roomleft)
482             {  /* "1" for SSEP */
483             strcat(qualname, p);
484             strcat(qualname, SSEP);
485             roomleft = roomleft - strlen(p) - 1;
486             }
487         else
488             {
489             m_mberr1("Internal error. File name too long for qualifying: %s",
490                      unqualname);
491             return (-1);
492             }
493         }
494     }
495 while (1);
496 *strrchr(qualname, CSEP) = '\0';
497
498 #if defined(hpux) || defined(_AIX) || defined(sun) || defined(USL) || defined(__uxp__) || defined(__osf__) || defined(linux) || defined(CSRG_BASED)
499 #else
500 strupr ( qualname );
501 #endif
502
503 return ( 0 );
504 }  /* end mb_getqualified */
505
506
507 int getqualified (qualname, unqualname)
508 M_WCHAR *qualname;
509 M_WCHAR *unqualname;
510 {
511 int retval;
512 char mb_qualname[FNAMELEN],
513      mb_unqualname[FNAMELEN];
514
515 wcstombs(mb_qualname, qualname, FNAMELEN);
516 wcstombs(mb_unqualname, unqualname, FNAMELEN);
517
518 retval = mb_getqualified(mb_qualname, mb_unqualname);
519
520 mbstowcs(qualname, mb_qualname, FNAMELEN);
521 mbstowcs(unqualname, mb_unqualname, FNAMELEN);
522
523 return retval;
524 }
525
526
527 /* handle the common link and graphic code for <p> and <image> */
528 void
529 handle_link_and_graphic(parent,
530                         gentity,
531                         gposition,
532                         ghyperlink,
533                         glinktype,
534                         gdescription)
535 M_WCHAR *parent, *gentity, *gposition, *ghyperlink, *glinktype, *gdescription;
536 {
537 unsigned char etype, wheredef;
538 char *mb_content, *ssi, id[32];
539 static M_WCHAR empty = M_EOS;
540 char *leftright;
541
542 /* handle graphic specific code */
543 /* initialize some stuff first:
544 - file is the entity name,
545 - f_file is the content of the entity, used only if f_content nonNULL
546 - f_content is f_file with the relative pathname, initialized to NULL,
547 - f_contqual is fully qualified f_file, assigned ONLY IF
548   f_content nonNULL
549 */
550 file_ent = FALSE;
551 f_content = NULL;
552 f_contqual[0] = M_EOS;
553
554 /* get the position, default to left */
555 leftright = "LEFT";
556 if (gposition)
557     {
558     M_WCHAR *wc_left, *wc_right;
559
560     wc_right = MakeWideCharString(QRIGHT);
561     if (!m_wcupstrcmp(gposition, wc_right))
562         {
563         leftright = "RIGHT";
564         }
565     else
566         {
567         wc_left = MakeWideCharString(QLEFT);
568         if (m_wcupstrcmp(gposition, wc_left))
569             {
570             m_err1("Invalid value for gposition: `%s'", gposition);
571             }
572         m_free(wc_left,"wide character string");
573         }
574     m_free(wc_right,"wide character string");
575     }
576
577 /* check ENTITY and determine the figure type  */
578 if (gentity)
579     {
580     m_lookent(gentity, &etype, &f_file, &wheredef);
581     if (etype != M_SYSTEM)
582         {
583         M_WCHAR *wc_entsystem, *wc_entkw, *wc_stago, *wc_tagc;
584
585         wc_entsystem = MakeWideCharString(m_entsystem);
586         wc_entkw = MakeWideCharString(m_entkw);
587         wc_stago = MakeWideCharString(m_stago);
588         wc_tagc = MakeWideCharString(m_tagc);
589         m_err6(
590             "%s not a %s %s, as required for the ENTITY parameter of %s%s%s",
591                gentity,
592                wc_entsystem,
593                wc_entkw,
594                wc_stago,
595                m_parent(0),
596                wc_tagc);
597         m_free(wc_entsystem,"wide character string");
598         m_free(wc_entkw,"wide character string");
599         m_free(wc_stago,"wide character string");
600         m_free(wc_tagc,"wide character string");
601         }
602     else
603         {
604         file_ent = TRUE;
605         f_content = searchforfile(f_file);
606         if (f_content)
607             {
608             if (getqualified(f_contqual, f_content))
609                 {
610                 /* unsuccessful qual */
611                 if (w_strlen(f_content) < FNAMELEN)
612                     w_strcpy(f_contqual, f_content);
613                 else
614                     {
615                     m_err1("Internal error. File name too long: %s",
616                            f_content);
617                     m_exit(m_errexit);
618                     }
619                 }
620             }
621         else
622             {
623             m_err2("Can't find file %s (declared in entity %s)",
624                    f_file,
625                    gentity);
626             }
627         }
628     if (!f_content) f_content = &empty;
629
630     mb_content = MakeMByteString(f_content);
631     sprintf(id, "%s%d", sdlReservedName, NextId());
632     ssi = MakeMByteString(m_parent(0));
633     fprintf(outfile, "<HEAD SSI=\"%s-GRAPHIC-%s\">", ssi, leftright);
634     if (ghyperlink)
635         HandleLink(ghyperlink, glinktype, gdescription);
636     fprintf(outfile, "<SNREF>\n<REFITEM RID=\"%s\" ", id);
637     fputs("CLASS=\"FIGURE\"", outfile);
638     AddToSNB(id, mb_content);
639     m_free(mb_content,"multi-byte string");
640     }
641
642 /* and finish the position now */
643 if (gposition)
644     {
645     fprintf(outfile, " SSI=\"GRPH-%s\"", leftright);
646     }
647
648 if (gentity)
649     {
650     fputs("></REFITEM></SNREF>", outfile);
651     if (ghyperlink)
652         fputs("</LINK>", outfile);
653     fputs("</HEAD>", outfile);
654     }
655
656 if (!ghyperlink && (glinktype || gdescription))
657     {
658     m_eprefix();
659     fprintf(stderr,
660            "Error: %sP%s ghyperlink was undefined.\n",
661            m_stago,
662            m_tagc);
663     fprintf(m_errfile,
664            "Error: %sP%s ghyperlink was undefined.\n",
665            m_stago,
666            m_tagc);
667     fprintf(stderr,
668      "Beware of using a reserved value such as:\n%s, %s, %s, %s, %s, or %s.\n",
669            QJUMP,
670            QJUMPNEWVIEW,
671            QDEFINITION,
672            QEXECUTE,
673            QAPPDEFINED,
674            QMAN);
675     fprintf(m_errfile,
676      "Beware of using a reserved value such as:\n%s, %s, %s, %s, %s, or %s.\n",
677            QJUMP,
678            QJUMPNEWVIEW,
679            QDEFINITION,
680            QEXECUTE,
681            QAPPDEFINED,
682            QMAN);
683     m_errline("Use ``ghyperlink='' if the value contains non-alphabetics");
684     m_esuffix();
685     }
686 }
687
688 /* Process an item in a list */
689 #if defined(M_PROTO)
690 void Item(M_WCHAR *id)
691 #else
692 void Item(id)
693 M_WCHAR *id;
694 #endif
695 {
696 char orderString[32];
697 static char *ROMAN0[] =
698     {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
699 static char *ROMAN10[] =
700     {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
701 static char *ROMAN100[] =
702     {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
703 static char ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
704 static char *roman0[] =
705     {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
706 static char *roman10[] =
707     {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
708 static char *roman100[] =
709     {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};
710 static char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
711 static char numbers[] = "0123456789";
712 static char romanString[]  = "ROMAN";
713 static char alphaString[]  = "ALPHA";
714 static char arabicString[] = "ARABIC";
715 int      count, metaCount;
716 char    *item_id;
717 char     label_id[SDLNAMESIZ+10];
718 int      listtype;
719 char    *type;
720 char    *loose;
721 char    *first;
722 LOGICAL  isBullet, isLoose, isFirst;
723 int     *pThisBulletId;
724 char     ssi[BIGBUF];
725
726 if (listitems[list].firstitem)
727     {
728     listitems[list].firstitem = FALSE;
729     }
730
731 count = ++lastlist->lastlist->count;
732 if (count > 999) count = 999; /* holy cow!  Big list. */
733 isFirst = FALSE;
734 if (count == 1)
735     {
736     isFirst = TRUE;
737     first = "FIRST-";
738     }
739 else
740     first = "";
741
742 isLoose = FALSE;
743 if (lastlist->lastlist->space == TIGHT)
744     loose = "TIGHT";
745 else
746     {
747     isLoose = TRUE;
748     loose = "LOOSE";
749     }
750
751 isBullet = FALSE;
752 listtype = lastlist->lastlist->type;
753 if (listtype == BULLET)
754     isBullet = TRUE;
755
756 if ((listtype != PLAIN) && (listtype != MILSPEC))
757     { /* don't emit label for type==PLAIN and type==MILSPEC */
758     if (isBullet)
759         {
760         if (isLoose)
761             {
762             if (isFirst)
763                 pThisBulletId = &firstLooseBulletId;
764             else
765                 {
766                 looseBulletIdIndex = (- looseBulletIdIndex) + 1;
767                 pThisBulletId = &looseBulletId[looseBulletIdIndex];
768                 }
769             }
770         else
771             {
772             if (isFirst)
773                 pThisBulletId = &firstBulletId;
774             else
775                 {
776                 bulletIdIndex = (- bulletIdIndex) + 1;
777                 pThisBulletId = &bulletId[bulletIdIndex];
778                 }
779             }
780         if (*pThisBulletId)
781             { /* use an existing block containing a bullet */
782             strcpy(label_id, sdlReservedName);
783             m_itoa(*pThisBulletId, label_id + SDLNAMESIZ - 1);
784             }
785         else
786             {
787             *pThisBulletId = NextId();
788             strcpy(label_id, sdlReservedName);
789             m_itoa(*pThisBulletId, label_id + SDLNAMESIZ - 1);
790             if (needFData)
791                 {
792                 fputs("<FDATA>\n", outfile);
793                 needFData = FALSE;
794                 }
795             fprintf(outfile,
796                     "<BLOCK ID=\"%s\" CLASS=\"ITEM\" TIMING=\"ASYNC\" ",
797                     label_id);
798             fprintf(outfile,
799                     "SSI=\"%s%s-BULLET\">\n<P><SPC NAME=\"[bull  ]\">",
800                     first,
801                     loose);
802             fputs("</P>\n</BLOCK>\n", outfile);
803             }
804         }
805     else
806         {
807         strcpy(label_id, sdlReservedName);
808         m_itoa(NextId(), label_id + SDLNAMESIZ - 1);
809         if (needFData)
810             {
811             fputs("<FDATA>\n", outfile);
812             needFData = FALSE;
813             }
814         fprintf(outfile, "<BLOCK ID=\"%s\" CLASS=\"ITEM\" SSI=\"", label_id);
815         }
816     }
817
818 item_id = NULL;
819 if (listtype == ORDER)
820     {
821     orderString[0] = '\0';
822     switch (lastlist->lastlist->order)
823         {
824         case UROMAN:
825             snprintf(orderString, sizeof(orderString), "%s%s%s", ROMAN100[count / 100], ROMAN10[(count / 10) % 10], ROMAN0[count % 10]);
826             type = romanString;
827             break;
828         case UALPHA:
829             metaCount = 1;
830             while ((count -= 26) > 0) metaCount++;
831             count = lastlist->lastlist->count;
832             if (count > 999) count = 999;
833             count -= 1;
834             count %= 26;
835             while (--metaCount >= 0) strncat(orderString, &ALPHABET[count], 1);
836             type = alphaString;
837             break;
838         case ARABIC:
839             if (metaCount = (count / 100))
840                 strncat(orderString, &numbers[metaCount], 1);
841             if (metaCount || ((count / 10) % 10))
842                 strncat(orderString, &numbers[(count / 10) % 10], 1);
843             strncat(orderString, &numbers[count % 10], 1);
844             type = arabicString;
845             break;
846         case LROMAN:
847             snprintf(orderString, sizeof(orderString), "%s%s%s", roman100[count / 100], roman10[(count / 10) % 10], roman0[count % 10]);
848             type = romanString;
849             break;
850         case LALPHA:
851             metaCount = 1;
852             while ((count -= 26) > 0) metaCount++;
853             count = lastlist->lastlist->count;
854             if (count > 999) count = 999;
855             count -= 1;
856             count %= 26;
857             while (--metaCount >= 0) strncat(orderString, &alphabet[count], 1);
858             type = alphaString;
859             break;
860         }
861     fprintf(outfile,
862             "%s%s-%s\">\n<P>%s%c",
863             first,
864             loose,
865             type,
866             orderString, 
867             lastlist->lastlist->punct == DOTPUNCT ? '.' : ')' );
868     if (id)
869         {
870         char buffer[400];
871         M_WCHAR *wc;
872
873         sprintf(buffer, "Item %s", orderString);
874         wc = MakeWideCharString(buffer);
875         w_strcpy(xrefstring, wc);
876         m_free(wc,"wide character string");
877
878         xstrlen = w_strlen(xrefstring);
879         m_getline(&xrffile, &xrfline);
880         if (xrffile == NULL)
881             {
882             /* set to primary input */
883             xrffile = inputname;
884             }
885
886         setid(id,
887              TRUE,
888              FALSE,
889              inchapter,
890              chapstring,
891              xrffile,
892              xrfline, TRUE);
893
894         item_id = MakeMByteString(id);
895         }
896     }
897 else
898     { /* Bullet, Check or Plain list */
899     if (id) m_error("Cross-reference ID in non-ORDER list not allowed");
900     /* already handled Bullet */
901     if (listtype == CHECK)
902         {
903         fprintf(outfile,
904                 "%s%s-CHECK\">\n<P><SPC NAME=\"[check ]\">",
905                 first,
906                 loose);
907         }
908     /* don't emit label for listtype==PLAIN and listtype==MILSPEC */
909     }
910
911 if ((listtype != PLAIN) && (listtype != MILSPEC) && (listtype != BULLET))
912     fputs("</P>\n</BLOCK>\n", outfile);
913
914 sprintf(ssi, "%s%s", first, loose);
915 if ((listtype == PLAIN) || (listtype == MILSPEC))
916     PushForm("ITEM", ssi, item_id);
917 else
918     PushForm2("ITEM", ssi, label_id, item_id);
919
920 if (item_id)
921     m_free(item_id, "multi-byte string");
922 }
923
924
925 /* Start a rsect */
926 void rsectstart(id)
927 M_WCHAR *id;
928 {
929 savid = checkid(id);
930 iderr = FALSE;
931 rsectseq = TRUE;
932 chapst = TRUE;
933 }  /* END procedure rsectstart  */
934
935
936 /* Called at the end of a chapter, appendix, or section to end an open
937    sequence of rsects */
938 void rseqend(M_NOPAR)
939 {
940 if (rsectseq)
941     --thisnodelevel;
942 rsectseq = FALSE;
943 }
944
945
946 /* Follow search path to find a file, returning qualified name */
947 M_WCHAR *searchforfile(file)
948 M_WCHAR *file;
949 {
950 M_WCHAR *filename;
951 SEARCH  *searchp;
952 char    *mb_file, mb_filename[2048];
953 int     dir_leng,file_leng;
954
955 mb_file = MakeMByteString(file);
956 if (! access(mb_file, READABLE))
957     {
958     filename = (M_WCHAR *)
959         m_malloc(w_strlen(file) + 1, "figure/icon filename");
960     w_strcpy(filename, file);
961     m_free(mb_file,"multi-byte string");
962     return(filename);
963     }
964
965 file_leng = strlen(mb_file);
966 for (searchp = path; searchp ; searchp = searchp->next)
967     {
968     if (((dir_leng = strlen(searchp->directory)) + file_leng) >= 2048)
969         {
970         M_WCHAR *wc;
971
972         wc = MakeWideCharString(searchp->directory);
973         m_err2("%s and %s overflow the file name space", wc, file);
974         m_free(wc,"wide character string");
975         continue;
976         }
977     strcpy(mb_filename, searchp->directory);
978     strcpy(mb_filename+dir_leng, mb_file);
979     if (! access(mb_filename, READABLE))
980         {
981         m_free(mb_file,"multi-byte string");
982         return(MakeWideCharString(mb_filename));
983         }
984     }
985
986 m_free(mb_file,"multi-byte string");
987 return(NULL);
988 }
989
990
991 /* Start a new helpnode */
992 #if defined(M_PROTO)
993 void starthelpnode(M_WCHAR *ssi,
994                    M_WCHAR *id,
995                    int      level)
996 #else
997 void starthelpnode(ssi, id, level)
998 M_WCHAR *ssi;
999 M_WCHAR *id;
1000 int      level;
1001 #endif
1002 {
1003 int i;
1004 char *mbyte, *mb_ssi;
1005 char mb_nodeid[NODEID_LENGTH+1], nodenum[32];
1006
1007 if (outfile != m_outfile)
1008     {
1009     m_error("Internal warning:  Outfile has been redirected.");
1010     m_errcnt--;
1011     }
1012
1013 fflush(m_outfile);
1014
1015 mb_ssi = MakeMByteString(ssi);
1016
1017 /* we can't use the bullet block across virpage boundaries */
1018 bulletId[0]        = 0;
1019 bulletId[1]        = 0;
1020 looseBulletId[0]   = 0;
1021 looseBulletId[1]   = 0;
1022 firstBulletId      = 0;
1023 firstLooseBulletId = 0;
1024
1025 /* might as well always start with id 0 (we flip first) */
1026 bulletIdIndex      = 1;
1027 looseBulletIdIndex = 1;
1028
1029 if ((id == NULL) || (*id == 0))
1030     {
1031     strcpy(mb_nodeid, sdlReservedName);
1032     m_itoa(NextId(), nodenum);
1033     strcpy(mb_nodeid + SDLNAMESIZ - 1, nodenum);
1034     mbstowcs(nodeid, mb_nodeid, NODEID_LENGTH);
1035     }
1036 else
1037     {
1038     w_strncpy(nodeid, id, NODEID_LENGTH);
1039     wcstombs(mb_nodeid, id, NODEID_LENGTH);
1040     }
1041
1042 fprintf(outfile,
1043 "<VIRPAGE ID=\"%s\" LEVEL=\"%d\" LANGUAGE=\"%s\" CHARSET=\"%s\" DOC-ID=\"%s\" SSI=\"%s\">\n",
1044         mb_nodeid,
1045         level,
1046         GetLanguage(),
1047         GetCharset(),
1048         docId,
1049         mb_ssi);
1050 m_free(mb_ssi,"multi-byte string");
1051 snbstart = ftell(outfile);
1052 } /* End starthelpnode(id) */
1053
1054
1055 #if defined(M_PROTO)
1056 void mb_starthelpnode(char   *ssi,
1057                       char   *id,
1058                       int     level)
1059 #else
1060 void mb_starthelpnode(ssi, id, level)
1061 char   *ssi;
1062 char   *id;
1063 int     level;
1064 #endif
1065 {
1066 M_WCHAR *wc_ssi, *wc_id;
1067
1068 wc_ssi = NULL;
1069 if (ssi)
1070     wc_ssi = MakeWideCharString(ssi);
1071 wc_id = NULL;
1072 if (id)
1073     wc_id  = MakeWideCharString(id);
1074 starthelpnode(wc_ssi, wc_id, level);
1075 if (wc_ssi)
1076     m_free(wc_ssi,"wide character string");
1077 if (wc_id)
1078     m_free(wc_id,"wide character string");
1079 }
1080
1081
1082 /* Start a labeled list */
1083 void StartLabList(spacing, longlabel)
1084 M_WCHAR *spacing;
1085 M_WCHAR *longlabel;
1086 {
1087        char *mb_spacing;
1088 static char  def_spacing[] = "LOOSE";
1089        char  ssi[32];
1090
1091 if (list >= MAXLISTLEV)
1092     m_error("Nesting of <LIST> and <LABLIST> too deep");
1093
1094 if (spacing)
1095     mb_spacing = MakeMByteString(spacing);
1096 else
1097     mb_spacing = def_spacing;
1098
1099 sprintf(ssi, "LABELED-%s", mb_spacing);
1100 PushForm("LIST", ssi, NULL);
1101
1102 if (mb_spacing != def_spacing)
1103     mb_free(&mb_spacing);
1104
1105 list++;
1106
1107 listitems[list].firstitem = TRUE;
1108 listitems[list].longlabel = vlonglabel(longlabel);
1109
1110 if (list <= MAXLISTLEV)
1111     {
1112     if (vspacing(spacing) == TIGHT)
1113         {
1114         lablisttight[list] = TRUE;
1115         }
1116     else
1117         {
1118         lablisttight[list] = FALSE;
1119         }
1120     }
1121 }
1122
1123
1124 /* Start a list */
1125 #if defined(M_PROTO)
1126 void StartList(M_WCHAR *type,
1127                M_WCHAR *order,
1128                M_WCHAR *spacing,
1129                M_WCHAR *cont)
1130 #else
1131 void StartList(type, order, spacing, cont)
1132 M_WCHAR *type, *order, *spacing, *cont;
1133 #endif
1134 {
1135 LIST *nextlist;
1136 CONTCHAIN *chain;
1137 CONTCHAIN *xchain;
1138 M_WCHAR *wc;
1139 char *mb_spacing;
1140 static char def_spacing[]  = "LOOSE";
1141 char *list_type;
1142 char  ssi[BIGBUF];
1143
1144 if (spacing)
1145     {
1146     mb_spacing = MakeMByteString(spacing);
1147     }
1148 else
1149     {
1150     mb_spacing = def_spacing;
1151     }
1152
1153 list++;
1154
1155 if (list > MAXLISTLEV)
1156     m_error("Nesting of <LIST> and <LABLIST> too deep");
1157
1158 if (! lastlist->started && cont)
1159     {
1160     m_error("No previous list to continue");
1161     cont = NULL;
1162     }
1163
1164 if (cont)
1165     {
1166     if (order && (vordertype(order) != lastlist->order))
1167         {
1168         m_error("Unable to continue a list and change the numbering scheme");
1169         cont = NULL;
1170         }
1171
1172     if (type && (vtype(type) != lastlist->type))
1173         {
1174         m_error("Unable to continue a list and change its type");
1175         cont = NULL;
1176         }
1177     }
1178
1179 if (! cont)
1180     { /* clear old list? */
1181     for (chain = lastlist->chain; chain ; chain = xchain)
1182         {
1183         xchain = chain->next;
1184         m_free(chain, "list chain");
1185         }
1186     lastlist->chain = NULL;
1187     lastlist->count = 0;
1188     }
1189
1190 /* If outermost list, initialize according to current defaults */
1191 if (! cont && lastlist == &outlist)
1192     {
1193     outlist.type       = vtype(NULL);
1194     outlist.order      = vordertype(NULL);
1195     outlist.count      = 0;
1196     outlist.space      = vspacing(NULL);
1197     outlist.punct      = DOTPUNCT;
1198     }
1199
1200 if (type) lastlist->type = vtype(type);
1201
1202 if (order)
1203     {
1204     lastlist->type = ORDER;
1205     lastlist->order = vordertype(order);
1206     }
1207
1208 switch (lastlist->type)
1209     {
1210     case MILSPEC:
1211     case PLAIN:
1212         list_type = "PLAIN";
1213         break;
1214     case CHECK:
1215         list_type = "CHECK";
1216         break;
1217     case ORDER:
1218         list_type = "ORDER";
1219         break;
1220     case BULLET:
1221     default:
1222         list_type = "BULLET";
1223     }
1224 sprintf(ssi, "%s-%s", list_type, mb_spacing);
1225
1226 PushForm("LIST", ssi, NULL);
1227
1228 if (mb_spacing != def_spacing)
1229     m_free(mb_spacing, "multi-byte string");
1230
1231
1232 lastlist->space = LOOSE;
1233 if (spacing && ! m_wcmbupstrcmp(spacing, QTIGHT)) lastlist->space = TIGHT;
1234
1235 if (type && order && m_wcmbupstrcmp(type, QORDER))
1236   m_err2("Incompatible specification for list: %s and %s", type, order);
1237
1238 if (lastlist->type == ORDER)
1239     {
1240     chain = (CONTCHAIN *) m_malloc(sizeof(CONTCHAIN), "list chain");
1241     chain->next = lastlist->chain;
1242     lastlist->chain = chain;
1243     chain->where = ftell(outfile);
1244     }
1245
1246 lastlist->started         = TRUE;
1247 listitems[list].firstitem = TRUE;
1248
1249 /* Prepare for sublist */
1250 nextlist = (LIST *) m_malloc(sizeof(LIST), "list structure");
1251 nextlist->lastlist = lastlist;
1252 nextlist->type = lastlist->type;
1253 nextlist->punct = lastlist->punct;
1254 if (lastlist->type == ORDER)
1255     {
1256     nextlist->order = lastlist->order + 1;
1257     if (nextlist->order > LROMAN)
1258         {
1259         nextlist->order = ARABIC;
1260         nextlist->punct = PARENPUNCT;
1261         }
1262     }
1263 else nextlist->order = lastlist->order;
1264 nextlist->count = 0;
1265 nextlist->space = lastlist->space;
1266 nextlist->started = FALSE;
1267 nextlist->where = FIRST;
1268 nextlist->chain = NULL;
1269 lastlist = nextlist;
1270 }
1271
1272
1273 void EndList()
1274 {
1275 LIST *curlist ;
1276 CONTCHAIN *chain, *xchain ;
1277 char *ncols;
1278
1279 curlist = lastlist->lastlist ;
1280
1281 if ((curlist->type == PLAIN) || (curlist->type == MILSPEC))
1282     PopForm();
1283 else
1284     PopForm2();
1285
1286 list-- ;
1287
1288 for (chain = lastlist->chain ; chain ; chain = xchain)
1289     {
1290     xchain = chain->next ;
1291     m_free(chain, "list chain") ;
1292     }
1293 m_free(lastlist, "list structure") ;
1294 lastlist = curlist ;
1295 for (chain = lastlist->chain ; chain ; chain = chain->next)
1296     {
1297     if (lastlist->count > 999)
1298         {
1299         m_error("Unable to support more than 999 items in an ordered list") ;
1300         lastlist->count = 999 ;
1301         }
1302     }
1303 if (lastlist->type == BULLET) bulcount-- ;
1304 }
1305
1306
1307 /* Open and initialize TeX file */
1308 void texinit(M_NOPAR)
1309 {
1310 LOGICAL init = TRUE;
1311 unsigned char type;
1312 M_WCHAR *content;
1313 unsigned char wheredef;
1314 M_WCHAR *name;
1315 M_WCHAR *qfile;
1316 LOGICAL icon;
1317 SEARCH *searchp;
1318 char *p;
1319 int ic;
1320 int i;
1321 M_WCHAR wsl;
1322 M_WCHAR *wc;
1323
1324 /* Check .XRF file */
1325 strcpy(helpext, ".xrh");
1326 xrf = fopen(helpbase, "r");
1327 if (! xrf) rebuild = TRUE;
1328 else
1329     {
1330     fscanf(xrf, "\\gobble\001");
1331     for (p = m_signon; *p ; p++)
1332         {
1333         ic = getc(xrf);
1334         if (ic != (int) *p)
1335             {
1336             m_errline(
1337         "Output files from different version of Tag, regenerating. . .\n");
1338             fclose(xrf);
1339             xrf = NULL;
1340             rebuild = TRUE;
1341             break;
1342             }
1343         }
1344     }
1345 if (xrf)
1346     while ((ic = getc(xrf)) != EOF)
1347         if (ic == '\n') break;
1348 if (xrf && ic !=EOF)
1349     while ((ic = getc(xrf)) != EOF)
1350         if (ic == '\n') break;
1351
1352 rebuild = TRUE;
1353
1354 /* Open output files */
1355 strcpy(helpext, ".sdl");
1356 m_openchk(&m_outfile, helpbase, "w");
1357 outfile = m_outfile;
1358
1359 savehelpfilename = (M_WCHAR *)m_malloc(strlen(helpbase)+1, "help file name");
1360 mbstowcs(savehelpfilename, helpbase, strlen(helpbase) + 1);
1361
1362 /* index file */
1363 strcpy(helpext, ".idx");
1364 m_openchk(&indexfp, helpbase, "wb");
1365
1366 /* system notation block file */
1367 strcpy(helpext, ".snb");
1368 m_openchk(&snbfp, helpbase, "wb");
1369
1370 while (name = m_cyclent(init, &type, &content, &wheredef))
1371     {
1372     init = FALSE;
1373     qfile = NULL;
1374     icon = FALSE;
1375
1376     if (type == M_SYSTEM)
1377         qfile = searchforfile(content);
1378
1379     if (qfile) m_free(qfile, "figure filename");
1380
1381     if (type == M_SYSTEM)
1382         {
1383         mbtowc(&wsl, "/", 1);
1384
1385         if (w_strchr(content, wsl))
1386             {
1387             m_err2("Avoid directory names in FILE entity %s: %s",
1388                    name,
1389                    content);
1390             m_errline("(Use the SEARCH option instead)\n");
1391             }
1392         }
1393     }
1394
1395 /* Include cross-reference file */
1396 strcpy(helpext, ".xrh");
1397 loadxref();
1398 postpreamble = ftell(outfile);
1399 }
1400
1401
1402 /* Lookup localized header string entity as defined (by default) in
1403    locallang.ent.  If the the header string was not found, or it was
1404    not of type "desiredType", return the default.
1405
1406    If the entity is of type file (M_SYSTEM) then if the content is not
1407    empty search the path for the file.  If the file is found, return
1408    its name else return an empty string.
1409
1410    If this routine returns anything other than the empty string (""),
1411    the string returned must be m_free'd.
1412 */
1413 char *
1414 #if defined(M_PROTO)
1415 GetDefaultHeaderString(
1416     char          *elementName,
1417     unsigned char  desiredType,
1418     char          *defaultString )
1419 #else
1420 GetDefaultHeaderString(elementName, desiredType, defaultString)
1421 char          *elementName;
1422 unsigned char  desiredType;
1423 char          *defaultString;
1424 #endif
1425 {
1426 unsigned char type,wheredef;
1427 M_WCHAR *content;
1428 M_WCHAR *path;
1429 M_WCHAR *wc_elementName;
1430 char    *mb_content;
1431 char    *retval;
1432
1433 wc_elementName = MakeWideCharString(elementName);
1434 if (m_lookent(wc_elementName, &type, &content, &wheredef))
1435     {
1436     if (type == desiredType)
1437         {
1438         if (type == M_SDATA)
1439             {
1440             m_free(wc_elementName,"wide character string");
1441             mb_content = MakeMByteString(content);
1442             if (!*mb_content)
1443                 {
1444                 m_free(mb_content,"multi-byte string");
1445                 return "";
1446                 }
1447             else
1448                 return mb_content;
1449             }
1450         if (*content)
1451             {
1452             path = searchforfile(content);
1453             if (!path)
1454                 {
1455                 m_err2("Can't find file %s (declared in entity %s)",
1456                        content,
1457                        wc_elementName);
1458                 m_free(wc_elementName,"wide character string");
1459                 return("");
1460                 }
1461             else
1462                 {
1463                 m_free(wc_elementName,"wide character string");
1464                 return MakeMByteString(path);
1465                 }
1466             }
1467         m_free(wc_elementName,"wide character string");
1468         return "";
1469         }
1470     }
1471
1472 m_free(wc_elementName,"wide character string");
1473 if (*defaultString)
1474     {
1475     retval = m_malloc(strlen(defaultString) + 1,
1476                       "GetDefaultHeaderString return");
1477     return strcpy(retval, defaultString);
1478     }
1479
1480 return "";
1481 }
1482
1483
1484 /* A function that takes a language/charset pair and:
1485  *     if they are standard, leave them unchanged but get local
1486  *                           versions and setlocale(3) using those
1487  *     if they are local, setlocale(3) with them and replace them with
1488  *                        standard versions.
1489 */
1490 static void
1491 #if defined(M_PROTO)
1492 SetStdLocale(char *pLang, char *pCharset)
1493 #else
1494 SetStdLocale(pLang, pCharset)
1495 char *pLang;
1496 char *pCharset;
1497 #endif
1498 {
1499 static const char *cString   = "C";
1500 static const char *isoString = "ISO-8859-1";
1501 _DtXlateDb  myDb = NULL;
1502 char        myPlatform[_DtPLATFORM_MAX_LEN+1];
1503 char        myLocale[256]; /* arbitrarily large */
1504 char        myLocaleTemp[sizeof(myLocale)]; /* non-arbitrarily the same */
1505 char       *locale;
1506 char       *lang;
1507 char       *charset;
1508 int         execVer;
1509 int         compVer;
1510 int         isStd;
1511
1512 snprintf(myLocale, sizeof(myLocale), "%s", pLang);
1513 if (*pCharset)
1514     {
1515     snprintf(myLocaleTemp, sizeof(myLocaleTemp), "%s.%s", myLocale, pCharset);
1516     strcpy(myLocale, myLocaleTemp);
1517     }
1518
1519 if ((_DtLcxOpenAllDbs(&myDb) != 0) ||
1520     (_DtXlateGetXlateEnv(myDb,myPlatform,&execVer,&compVer) != 0))
1521     {
1522     fprintf(stderr,
1523             "Warning: could not open locale translation database.\n");
1524     if (m_errfile != stderr)
1525         fprintf(m_errfile,
1526                 "Warning: could not open locale translation database.\n");
1527     strcpy(pLang, cString);
1528     strcpy(pCharset, isoString);
1529     if (myDb != 0)
1530         _DtLcxCloseDb(&myDb);
1531     return;
1532     }
1533
1534 isStd = !_DtLcxXlateOpToStd(myDb,
1535                             "CDE",
1536                             0,
1537                             DtLCX_OPER_STD,
1538                             myLocale,
1539                             NULL,
1540                             NULL,
1541                             NULL,
1542                             NULL);
1543
1544 if (isStd)
1545     { /* already standard - get local versions and set locale */
1546     if (_DtLcxXlateStdToOp(myDb,
1547                            myPlatform,
1548                            compVer,
1549                            DtLCX_OPER_SETLOCALE,
1550                            myLocale,
1551                            NULL,
1552                            NULL,
1553                            NULL,
1554                            &locale))
1555         {
1556         fprintf(stderr,
1557                 "Warning: could not translate CDE locale to local\n");
1558         if (m_errfile != stderr)
1559             fprintf(m_errfile,
1560                     "Warning: could not translate CDE locale to local\n");
1561         strcpy(pLang, cString);
1562         strcpy(pCharset, isoString);
1563         _DtLcxCloseDb(&myDb);
1564         return;
1565         }
1566     else
1567         {
1568         setlocale(LC_CTYPE, locale);
1569         mb_free(&locale);
1570         }
1571     }
1572 else
1573     { /* already local - set locale and get standard versions */
1574     if (_DtLcxXlateOpToStd(myDb,
1575                            myPlatform,
1576                            compVer,
1577                            DtLCX_OPER_SETLOCALE,
1578                            myLocale,
1579                            NULL,
1580                            &lang,
1581                            &charset,
1582                            NULL))
1583         {
1584         fprintf(stderr,
1585                 "Warning: could not translate local locale to CDE\n");
1586         if (m_errfile != stderr)
1587             fprintf(m_errfile,
1588                     "Warning: could not translate local locale to CDE\n");
1589         strcpy(pLang, cString);
1590         strcpy(pCharset, isoString);
1591         _DtLcxCloseDb(&myDb);
1592         return;
1593         }
1594     setlocale(LC_CTYPE, myLocale);
1595
1596     if (*lang)
1597         {
1598         strcpy(pLang, lang);
1599         mb_free(&lang);
1600         }
1601     else
1602         strcpy(pLang, cString);
1603
1604     if ( *charset)
1605         {
1606         strcpy(pCharset, charset);
1607         }
1608     else
1609         strcpy(pCharset, isoString);
1610     }
1611         mb_free(&charset);
1612
1613 _DtLcxCloseDb(&myDb);
1614 free(charset);
1615 free(lang);
1616 }
1617
1618
1619 /* A function that takes the return value from a call to setlocale()
1620  * and extracts the langterr.charset data from it in a vendor neutral
1621  * fashion.
1622 */
1623 static char *
1624 #if defined(M_PROTO)
1625 GetStdLocale()
1626 #else
1627 GetStdLocale()
1628 #endif
1629 {
1630 static char buffer[256];
1631 static char *cString   = "C";
1632 _DtXlateDb  myDb = NULL;
1633 char        myPlatform[_DtPLATFORM_MAX_LEN+1];
1634 char       *opLocale;
1635 char       *stdLocale;
1636 int         execVer;
1637 int         compVer;
1638
1639 if ((_DtLcxOpenAllDbs(&myDb) == 0) &&
1640     (_DtXlateGetXlateEnv(myDb,myPlatform,&execVer,&compVer) != 0))
1641     {
1642     fprintf(stderr,
1643             "Warning: could not open locale translation database.\n");
1644     if (m_errfile != stderr)
1645         fprintf(m_errfile,
1646                 "Warning: could not open locale translation database.\n");
1647     return cString;
1648     }
1649
1650 if (_DtLcxXlateOpToStd(myDb,
1651                        myPlatform,
1652                        compVer,
1653                        DtLCX_OPER_SETLOCALE,
1654                        setlocale(LC_ALL, NULL),
1655                        &stdLocale,
1656                        NULL,
1657                        NULL,
1658                        NULL))
1659     {
1660     fprintf(stderr,
1661             "Warning: could not translate local locale to CDE\n");
1662     if (m_errfile != stderr)
1663         fprintf(m_errfile,
1664                 "Warning: could not translate local locale to CDE\n");
1665     _DtLcxCloseDb(&myDb);
1666     return cString;
1667     }
1668
1669 if (_DtLcxXlateStdToOp(myDb,
1670                        myPlatform,
1671                        compVer,
1672                        DtLCX_OPER_SETLOCALE,
1673                        stdLocale,
1674                        NULL,
1675                        NULL,
1676                        NULL,
1677                        &opLocale))
1678     {
1679     fprintf(stderr,
1680             "Warning: could not translate CDE locale to local\n");
1681     if (m_errfile != stderr)
1682         fprintf(m_errfile,
1683                 "Warning: could not translate CDE locale to local\n");
1684     mb_free(&stdLocale);
1685     _DtLcxCloseDb(&myDb);
1686     return cString;
1687     }
1688
1689 _DtLcxCloseDb(&myDb);
1690
1691 strcpy(buffer, opLocale);
1692 mb_free(&stdLocale);
1693 mb_free(&opLocale);
1694 return buffer;
1695 }
1696
1697
1698 /*
1699  * Look for a entities by the name of "LanguageElementDefaultLocale".
1700  * and "LanguageElementDefaultCharset".  If not found, get the user's
1701  * locale.  If LanguageElementDefaultCharset was set, use that in
1702  * place of the charset of the local (if any).  Call SetStdLocale()
1703  * to insure the language and charset are in the normalized form.
1704  * SetStdLocale() will also set the current locale to the local
1705  * versions of the language and charset.
1706 */
1707 void
1708 SetDefaultLocale()
1709 {
1710 unsigned char type,wheredef;
1711 M_WCHAR *elementName;
1712 M_WCHAR *content;
1713 char    *locale;
1714 char    *charset;
1715 char    *dotPtr;
1716 char    *tmpStr;
1717 char     stdLang[256];    /* arbitrarily large */
1718 char     stdCharset[256]; /* arbitrarily large */
1719
1720 locale        = NULL;
1721 charset       = NULL;
1722 dotPtr        = NULL;
1723 stdLang[0]    = 0;
1724 stdCharset[0] = 0;
1725
1726 elementName = MakeWideCharString("LanguageElementDefaultLocale");
1727 if (m_lookent(elementName, &type, &content, &wheredef))
1728     {
1729     if (type == M_SDATA)
1730         {
1731         locale = MakeMByteString(content);
1732         }
1733     }
1734 m_free(elementName,"wide character string");
1735
1736 if (!locale)
1737     {
1738     tmpStr = GetStdLocale();
1739     locale = mb_malloc(strlen(tmpStr)+1);
1740     strcpy(locale, tmpStr);
1741     }
1742
1743 dotPtr = strchr(locale, '.');
1744
1745 if (helpcharset)
1746     {
1747     charset = MakeMByteString(helpcharset);
1748     m_free(helpcharset, "help charset");
1749     helpcharset = NULL;
1750     }
1751 else
1752     {
1753     elementName = MakeWideCharString("LanguageElementDefaultCharset");
1754     if (m_lookent(elementName, &type, &content, &wheredef))
1755         {
1756         if (type == M_SDATA)
1757             {
1758             charset = MakeMByteString(content);
1759             }
1760         }
1761     m_free(elementName,"wide character string");
1762     }
1763
1764 if (!charset)
1765     {
1766     if (dotPtr)
1767         charset = dotPtr + 1;
1768     }
1769 if (dotPtr)
1770     *dotPtr = '\0';
1771
1772 snprintf(stdLang, sizeof(stdLang), "%s", locale);
1773 if (charset)
1774     snprintf(stdCharset, sizeof(stdCharset), "%s", charset);
1775 SetStdLocale(stdLang, stdCharset);
1776
1777 if (*stdCharset)
1778     helpcharset = MakeWideCharString(stdCharset);
1779 helplang = MakeWideCharString(stdLang);
1780
1781 mb_free(&locale);
1782 if (charset && (charset != (dotPtr+1)))
1783     mb_free(&charset);
1784 }
1785
1786
1787 #if defined(M_PROTO)
1788 void paragraph(M_WCHAR *indent,
1789                M_WCHAR *id,
1790                M_WCHAR *gentity,
1791                M_WCHAR *gposition,
1792                M_WCHAR *ghyperlink,
1793                M_WCHAR *glinktype,
1794                M_WCHAR *gdescription)
1795 #else
1796 void paragraph(indent,
1797                id,
1798                gentity,
1799                gposition,
1800                ghyperlink,
1801                glinktype,
1802                gdescription)
1803 M_WCHAR *indent;
1804 M_WCHAR *id;
1805 M_WCHAR *gentity;
1806 M_WCHAR *gposition;
1807 M_WCHAR *ghyperlink;
1808 M_WCHAR *glinktype;
1809 M_WCHAR *gdescription;
1810 #endif
1811 {
1812 char *firstString, *indentString;
1813
1814 if (id)
1815     {
1816     savid = checkid(id);
1817     }
1818 chapst = TRUE;
1819 inSdlP = TRUE;
1820 if (!inBlock)
1821     StartBlock(NULL, NULL, NULL);
1822 fputs("<P", outfile);
1823 if (id)
1824     {
1825     char *mb_id;
1826
1827     mb_id = MakeMByteString(id);
1828     fprintf(outfile, " ID=\"%s\"", mb_id);
1829     m_free(mb_id,"multi-byte string");
1830     }
1831
1832 firstString = "";
1833 if (firstPInBlock)
1834     {
1835     firstString = "1";
1836     firstPInBlock = FALSE;
1837     }
1838
1839 indentString = "";
1840 if (indent)
1841     indentString = "-INDENT";
1842
1843 fprintf(outfile, " SSI=\"P%s%s\">", firstString, indentString);
1844
1845 handle_link_and_graphic(m_parent(0),
1846                         gentity,
1847                         gposition,
1848                         ghyperlink,
1849                         glinktype,
1850                         gdescription);
1851
1852 }
1853
1854 #if defined(M_PROTO)
1855 void figure(
1856        M_WCHAR *number,
1857        M_WCHAR *tonumber,
1858        M_WCHAR *id,
1859        M_WCHAR *file,
1860        M_WCHAR *figpos,
1861        M_WCHAR *cappos,
1862        M_WCHAR *ghyperlink,
1863        M_WCHAR *glinktype,
1864        M_WCHAR *gdescription)
1865 #else
1866 void figure(
1867        number,
1868        tonumber,
1869        id,
1870        file,
1871        figpos,
1872        cappos,
1873        ghyperlink,
1874        glinktype,
1875        gdescription)
1876 M_WCHAR *number;
1877 M_WCHAR *tonumber;
1878 M_WCHAR *id;
1879 M_WCHAR *file;
1880 M_WCHAR *figpos;
1881 M_WCHAR *cappos;
1882 M_WCHAR *border;
1883 M_WCHAR *ghyperlink;
1884 M_WCHAR *glinktype;
1885 M_WCHAR *gdescription;
1886 #endif
1887 {
1888 char snb_id[32];
1889 unsigned char etype,wheredef;
1890 char *string = 
1891     GetDefaultHeaderString("FigureElementDefaultHeadingString",
1892                            M_SDATA,
1893                            "Figure");
1894
1895 if (needFData)
1896     {
1897     fputs("<FDATA>\n", outfile);
1898     needFData = FALSE;
1899     }
1900 if (!inBlock)
1901     StartBlock(NULL, NULL, NULL);
1902
1903 inSdlP = TRUE;
1904 fputs("<P SSI=\"FIGURE\">\n", outfile);
1905
1906 if (ghyperlink)
1907     {
1908     HandleLink(ghyperlink, glinktype, gdescription);
1909     }
1910 else if (glinktype || gdescription)
1911     {
1912     m_eprefix();
1913     fprintf(stderr,
1914             "Error: %sP%s ghyperlink was undefined.\n",
1915             m_stago,
1916             m_tagc);
1917     fprintf(m_errfile,
1918             "Error: %sP%s ghyperlink was undefined.\n",
1919             m_stago,
1920             m_tagc);
1921     fprintf(stderr,
1922     "Beware of using a reserved value such as:\n%s, %s, %s, %s, %s, or %s.\n",
1923             QJUMP,
1924             QJUMPNEWVIEW,
1925             QDEFINITION,
1926             QEXECUTE,
1927             QAPPDEFINED,
1928             QMAN);
1929     fprintf(m_errfile,
1930     "Beware of using a reserved value such as:\n%s, %s, %s, %s, %s, or %s.\n",
1931             QJUMP,
1932             QJUMPNEWVIEW,
1933             QDEFINITION,
1934             QEXECUTE,
1935             QAPPDEFINED,
1936             QMAN);
1937     m_errline("Use ``ghyperlink='' if the value contains non-alphabetics");
1938     m_esuffix();
1939     }
1940 /* end of link specific code */
1941
1942 figcaption = FALSE;
1943 ftonumber = (!tonumber || (vnumber(tonumber) == NUMBER));
1944 if (id && ! ftonumber)
1945     {
1946     m_error("Figures with ID's must be numbered");
1947     ftonumber = NUMBER;
1948     }
1949 if (ftonumber) figno++;
1950
1951 if (number)
1952     {
1953     char *mb_number;
1954
1955     mb_number = MakeMByteString(number);
1956     figno = atoi(mb_number);
1957     m_free(mb_number,"multi-byte string");
1958     if (! ftonumber)
1959     m_err1("Explicit figure number %s inconsistent with NONUMBER option",
1960            number);
1961     }
1962 svheadlen = 0;
1963 *savehead = M_EOS;
1964 if (id)
1965     {
1966     char mb_xrefstring[400];
1967
1968     sprintf(mb_xrefstring, "%s %d", string, figno);
1969     mbstowcs(xrefstring, mb_xrefstring, 400);
1970     xstrlen = w_strlen(xrefstring);
1971     m_getline(&xrffile, &xrfline);
1972     if (xrffile == NULL)
1973         {
1974         /* set to primary input source */
1975         xrffile = inputname;
1976         }
1977     setid(id, TRUE, FALSE, inchapter, chapstring, xrffile, xrfline, TRUE);
1978     }
1979
1980 /* initialize some stuff first:
1981 - file is the entity name,
1982 - f_file is the content of the entity,
1983 used only if f_content nonNULL
1984 - f_content is f_file with the relative pathname, initialized to NULL,
1985 - f_contqual is fully qualified f_file, assigned ONLY IF
1986 f_content nonNULL
1987 */
1988 file_ent = FALSE;
1989 f_content = NULL;
1990 f_contqual[0] = M_EOS;
1991
1992 /* check ENTITY and determine the figure type  */
1993 if (file)
1994     {
1995     m_lookent(file, &etype, &f_file, &wheredef);
1996     if (etype != M_SYSTEM)
1997         {
1998         M_WCHAR *wc_stago, *wc_tagc;
1999         M_WCHAR *wc_entsystem, *wc_entkw;
2000
2001         wc_stago = MakeWideCharString(m_stago);
2002         wc_tagc  = MakeWideCharString(m_tagc);
2003         wc_entsystem  = MakeWideCharString(m_entsystem);
2004         wc_entkw  = MakeWideCharString(m_entkw);
2005         m_err6("%s not a %s %s, as required for the ENTITY parameter of %s%s%s",
2006                file,
2007                wc_entsystem,
2008                wc_entkw,
2009                wc_stago,
2010                m_parent(0),
2011                wc_tagc);
2012         m_free(wc_stago,"wide character string");
2013         m_free(wc_tagc,"wide character string");
2014         m_free(wc_entsystem,"wide character string");
2015         m_free(wc_entkw,"wide character string");
2016         }
2017     else
2018         {
2019         file_ent = TRUE;
2020         f_content = searchforfile(f_file);
2021         if (f_content)
2022             {
2023             if (getqualified(f_contqual, f_content))
2024                 {
2025                 /* unsuccessful qual */
2026                 if (w_strlen(f_content) < FNAMELEN)
2027                     w_strcpy(f_contqual, f_content);
2028                 else
2029                     {
2030                     m_err1("Internal error. File name too long: %s",
2031                            f_content);
2032                     m_exit(m_errexit);
2033                     }
2034                 }
2035             }
2036         else
2037             {
2038             m_err2("Can't find file %s (declared in entity %s)",
2039                    f_file,
2040                    file);
2041             }
2042         }
2043     }
2044
2045 sprintf(snb_id, "%s%d", sdlReservedName, NextId());
2046
2047 {
2048 static M_WCHAR empty = M_EOS;
2049 char *mb_content;
2050
2051 if (!f_content) f_content = &empty;
2052
2053 mb_content = MakeMByteString(f_content);
2054 AddToSNB(snb_id, mb_content);
2055 m_free(mb_content,"multi-byte string");
2056 }
2057
2058 fputs("<SNREF", outfile);
2059
2060 if (id)
2061     {
2062     char *mb_id;
2063
2064     mb_id = MakeMByteString(id);
2065     fprintf(outfile, " ID=\"%s\"", mb_id);
2066     m_free(mb_id,"multi-byte string");
2067     }
2068 fprintf(outfile, ">\n<REFITEM RID=\"%s\" CLASS=\"FIGURE\" SSI=\"FIG", snb_id);
2069
2070
2071 /* if caption position is not specified, have it track the figure position */
2072 if (!cappos)
2073     cappos = figpos;
2074
2075 if (figpos)
2076     switch (vcenter(figpos))
2077         {
2078         case LEFT:
2079             fputs("-LEFT", outfile);
2080             break;
2081         case CENTER:
2082             fputs("-CENTER", outfile);
2083             break;
2084         case RIGHT:
2085             fputs("-RIGHT", outfile);
2086             break;
2087         }
2088 fputs("\">\n", outfile);
2089
2090 capposition = "";
2091 if (cappos)
2092     switch (vcenter(cappos))
2093         {
2094         case LEFT:
2095             capposition = "-LEFT";
2096             break;
2097         case CENTER:
2098             capposition = "-CENTER";
2099             break;
2100         case RIGHT:
2101             capposition = "-RIGHT";
2102             break;
2103         }
2104
2105 if (ftonumber)
2106     {
2107     fprintf(outfile,
2108             "<HEAD TYPE=\"LINED\" CLASS=\"CAPTION\" SSI=\"NUMBER%s\">%s %d.",
2109             capposition,
2110             string,
2111             figno);
2112     }
2113 m_free(string, "default header string return");
2114 }
2115
2116 #if defined(M_PROTO)
2117 void endterm(M_WCHAR *base, M_WCHAR *gloss, char *linktype)
2118 #else
2119 void endterm( base, gloss, linktype)
2120 M_WCHAR *base;
2121 M_WCHAR *gloss;
2122 char    *linktype;
2123 #endif
2124 {
2125 M_WCHAR *p;
2126 M_WCHAR  dterm[MAXTERM+1];
2127 M_WCHAR  wnl, wsp;
2128 char     buffer[BIGBUF];
2129 int      idn;
2130
2131 if (!(m_mblevel("HEAD")       ||
2132       m_mblevel("EXAMPLESEG") ||
2133       m_mblevel("ANNOTATION")
2134      )
2135    )
2136     {
2137     /* Don't re-enable output yet if in a <HEAD>, <EX> or <ANNOTATION> */
2138     tooutput = TRUE;
2139     }
2140
2141 mbtowc(&wnl, "\n", 1);
2142 mbtowc(&wsp, " ", 1);
2143
2144 /* terminate the term string */
2145 if (termp - term > MAXTERM) termp = &term[MAXTERM];
2146 *termp = M_EOS;
2147
2148 /* make a copy - will transform to match definition transformations */
2149 w_strcpy(dterm, term);
2150 termp = dterm + (termp - term);
2151
2152 /* Check if it should be entered into the glossary structure */
2153 if (vgloss(gloss) == GLOSS)
2154     {
2155     /* strip possible newlines out of base form */
2156     if (base)
2157         {
2158         for (p = base; *p ; p++)
2159             {
2160             if ((p - base) >= MAXTERM)
2161                 {
2162                 if ((p - base) == MAXTERM)
2163                     {
2164                     M_WCHAR *wc_stago, *wc_tagc;
2165
2166                     wc_stago = MakeWideCharString(m_stago);
2167                     wc_tagc  = MakeWideCharString(m_tagc);
2168                     m_err2("Too many characters in BASEFORM for %sTERM%s",
2169                            wc_stago,
2170                            wc_tagc);
2171                     m_free(wc_stago,"wide character string");
2172                     m_free(wc_tagc,"wide character string");
2173
2174                     *p = M_EOS;
2175                     }
2176                 }
2177             else
2178                 {
2179                 if (*p == wnl) *p = wsp;
2180                 }
2181             }
2182         }
2183
2184     /* strip possible newlines out of dterm */
2185     for (p = dterm; *p ; p++)
2186         {
2187         if (*p == wnl) *p = wsp;
2188         }
2189
2190     /* trim possible last space */
2191     if (termp - dterm > 1 && *(termp-1) == wsp)
2192         {
2193         *(termp-1) = M_EOS;
2194         }
2195
2196     idn = (intptr_t) m_lookfortrie(base ? base : dterm, &gtree);
2197     if (idn < 0)
2198         idn = -idn;
2199     if (!idn)
2200         {
2201         idn = NextId();
2202         m_ntrtrie(base ? base : dterm, &gtree, (void *) (intptr_t) idn);
2203         }
2204     }
2205
2206 /* Handle the output */
2207 if (vgloss(gloss) == GLOSS)
2208     {
2209     sprintf(buffer,
2210             "<LINK WINDOW=\"%s\" RID=\"%s%d\">",
2211             linktype,
2212             sdlReservedName,
2213             idn);
2214     mb_strcode(buffer, outfile);
2215     }
2216
2217 mb_strcode("<KEY CLASS=\"TERM\">", outfile);
2218
2219 #if 0
2220 if (m_mblevel("EXAMPLESEG"))
2221     {
2222     /* Are we in one of these?  May need to number a line. */
2223     for(p = term; *p; p++)
2224         {
2225         exoutchar(*p);
2226         }
2227     }
2228 else
2229 #endif
2230     multi_cr_flag = FALSE;
2231     strcode(term, outfile);
2232
2233 mb_strcode("</KEY>", outfile);
2234
2235 if (vgloss(gloss) == GLOSS)
2236     {
2237     mb_strcode("</LINK>", outfile);
2238     }
2239
2240 if (echo) mb_echohead("++");
2241 }
2242
2243 #if defined(M_PROTO)
2244 M_WCHAR wc_toupper(M_WCHAR wc)
2245 #else
2246 M_WCHAR wc_toupper(wc)
2247 M_WCHAR wc;
2248 #endif
2249 {
2250 if ((wc >= 0) && (wc <= 255))
2251     {
2252     return _toupper(wc);
2253     }
2254 return wc;
2255 }
2256
2257 #if defined(M_PROTO)
2258 M_WCHAR *wc_stringtoupper(M_WCHAR *wcp)
2259 #else
2260 M_WCHAR *wc_stringtoupper(wcp)
2261 M_WCHAR *wcp;
2262 #endif
2263 {
2264 M_WCHAR *newstring, *nsp;
2265
2266 nsp = newstring =
2267     m_malloc(w_strlen(wcp) + 1, "wide character upper case string");
2268
2269 while (*wcp)
2270     {
2271     *nsp = wc_toupper(*wcp);
2272     nsp++;
2273     wcp++;
2274     }
2275 *nsp = 0;
2276
2277 return(newstring);
2278 }
2279
2280 int NextId()
2281 {
2282 static id = 0;
2283
2284 return ++id;
2285 }
2286
2287 char *GetLanguage()
2288 {
2289 static char *pLang = NULL;
2290
2291 if (!pLang)
2292     {
2293     pLang = MakeMByteString(helplang);
2294     }
2295
2296 return pLang;
2297 }
2298
2299 char *GetCharset()
2300 {
2301 static char *pCharset = NULL;
2302
2303 if (!pCharset)
2304     {
2305     pCharset = MakeMByteString(helpcharset);
2306     }
2307
2308 return pCharset;
2309 }
2310
2311
2312 #if defined(M_PROTO)
2313 void HandleLink(M_WCHAR *hyperlink, M_WCHAR *type, M_WCHAR *description)
2314 #else
2315 void HandleLink(hyperlink, type, description)
2316 M_WCHAR *hyperlink;
2317 M_WCHAR *type;
2318 M_WCHAR *description;
2319 #endif
2320 {
2321 char *mb_hyperlink, mb_undefined[64];
2322 char buffer[BIGBUF];
2323 static M_WCHAR wsp = 0;
2324
2325 if (!wsp)
2326     {
2327     mbtowc(&wsp, " ", 1);
2328     }
2329
2330 strcpy(mb_undefined, sdlReservedName);
2331 strcpy(mb_undefined + SDLNAMESIZ - 1, "-UNDEFINED");
2332
2333 mb_strcode("<LINK ", outfile);
2334 global_linktype = 0; /* default to type jump */
2335 if (type)
2336     {
2337     /* type is set, choose which is correct */
2338     if (m_wcmbupstrcmp(type, QJUMP))
2339         { /* not type jump */
2340         if (!m_wcmbupstrcmp(type, QJUMPNEWVIEW))
2341             { mb_strcode("WINDOW=\"NEW\" ", outfile); global_linktype = 1;}
2342         else if (!m_wcmbupstrcmp(type, QDEFINITION))
2343             { mb_strcode("WINDOW=\"POPUP\" ", outfile); global_linktype = 2;}
2344         else if (!m_wcmbupstrcmp(type, QEXECUTE))
2345             { global_linktype = 3;}
2346         else if (!m_wcmbupstrcmp(type, QMAN))
2347             { mb_strcode("WINDOW=\"POPUP\" ", outfile); global_linktype = 4;}
2348         else if (!m_wcmbupstrcmp(type, QAPPDEFINED))
2349             { global_linktype = 5;}
2350         }
2351     }
2352 mb_strcode("RID=\"", outfile);
2353 if (hyperlink)
2354     {
2355     mb_hyperlink = MakeMByteString(hyperlink);
2356     if (*mb_hyperlink == '_') /* must be metainfo */
2357         {
2358         strcpy(buffer, sdlReservedName);
2359         buffer[SDLNAMESIZ-1] = '-';
2360         strcpy(buffer+SDLNAMESIZ, mb_hyperlink+1);
2361         mb_free(&mb_hyperlink);
2362         }
2363     }
2364 else
2365     {
2366     m_eprefix();
2367     fprintf(stderr,
2368             "Error: %sLINK%s hyperlink was undefined.\n",
2369             m_stago,
2370             m_tagc);
2371     fprintf(m_errfile,
2372             "Error: %sLINK%s hyperlink was undefined.\n",
2373             m_stago,
2374             m_tagc);
2375     fprintf(stderr,
2376     "Beware of using a reserved value such as:\n%s, %s, %s, %s, %s, or %s.\n",
2377             QJUMP,
2378             QJUMPNEWVIEW,
2379             QDEFINITION,
2380             QEXECUTE,
2381             QAPPDEFINED,
2382             QMAN);
2383     fprintf(m_errfile,
2384     "Beware of using a reserved value such as:\n%s, %s, %s, %s, %s, or %s.\n",
2385             QJUMP,
2386             QJUMPNEWVIEW,
2387             QDEFINITION,
2388             QEXECUTE,
2389             QAPPDEFINED,
2390             QMAN);
2391     m_errline("Use ``hyperlink='' if the value contains non-alphabetics");
2392     m_esuffix();
2393     mb_hyperlink = mb_undefined;
2394     }
2395
2396 if ((global_linktype <= 2) && hyperlink && (!w_strchr(hyperlink, wsp)))
2397     {
2398     if (mb_hyperlink)
2399         sprintf(buffer, "%s", mb_hyperlink);
2400     }
2401 else
2402     {
2403     char tmpsnb[BIGBUF];
2404     int  snblen;
2405
2406     int nextId = NextId();
2407     sprintf(buffer, "%s%d", sdlReservedName, nextId);
2408     if (!savesnb && snbstart)
2409         {
2410         fprintf(snbfp, "%d\n", snbstart);
2411         snbstart = 0;
2412         }
2413     switch (global_linktype)
2414         {
2415         case 0: /* jump */
2416         case 1: /* jump new */
2417         case 2: /* definition */
2418             sprintf(tmpsnb,
2419                     "<CROSSDOC ID=\"%s%d\" XID",
2420                     sdlReservedName,
2421                     nextId);
2422             break;
2423         case 3: /* execute */
2424             sprintf(tmpsnb,
2425                     "<SYS-CMD ID=\"%s%d\" COMMAND",
2426                     sdlReservedName,
2427                     nextId);
2428             break;
2429         case 4: /* man */
2430             sprintf(tmpsnb,
2431                     "<MAN-PAGE ID=\"%s%d\" XID",
2432                     sdlReservedName,
2433                     nextId);
2434             break;
2435         case 5: /* app defined */
2436             sprintf(tmpsnb,
2437                     "<CALLBACK ID=\"%s%d\" DATA",
2438                     sdlReservedName,
2439                     nextId);
2440             break;
2441         }
2442     if (savesnb)
2443         {
2444         snblen = strlen(savesnb);
2445         savesnb = mb_realloc(savesnb, snblen + strlen(tmpsnb) + 1);
2446         strcpy(savesnb + snblen, tmpsnb);
2447         sprintf(tmpsnb, "=\"%s\">\n", mb_hyperlink);
2448         snblen = strlen(savesnb);
2449         savesnb = mb_realloc(savesnb, snblen + strlen(tmpsnb) + 1);
2450         strcpy(savesnb + snblen, tmpsnb);
2451         }
2452     else
2453         {
2454         fputs(tmpsnb, snbfp);
2455         fprintf(snbfp, "=\"%s\">\n", mb_hyperlink);
2456         }
2457     }
2458 mb_strcode(buffer, outfile);
2459 mb_strcode("\"", outfile);
2460 if (mb_hyperlink && (mb_hyperlink != mb_undefined))
2461     m_free(mb_hyperlink,"multi-byte string");
2462 if (description)
2463     {
2464     char *mb_description;
2465
2466     mb_description = MakeMByteString(description);
2467     sprintf(buffer, " DESCRIPT=\"%s\"", mb_description);
2468     mb_strcode(buffer, outfile);
2469     m_free(mb_description,"multi-byte string");
2470     }
2471 mb_strcode(">", outfile);
2472 }
2473
2474
2475 #if defined(M_PROTO)
2476 char *mb_realloc(char *ptr, long size)
2477 #else
2478 char *mb_realloc(ptr, size)
2479 char *ptr;
2480 long  size;
2481 #endif
2482 {
2483 void   *vptr;
2484 size_t  ssize;
2485
2486 ssize = (size_t) size;
2487 vptr = realloc((void *) ptr, ssize);
2488
2489 #if DEBUG_MB_MALLOC
2490 if (ptr != vptr)
2491     {
2492     fprintf(stdout, "realloc 0x%p 0x%p\n", ptr, vptr);
2493     fflush(stdout);
2494     }
2495 #endif
2496
2497 return (char *) vptr;
2498 }
2499
2500
2501 #if defined(M_PROTO)
2502 char *mb_malloc(long size)
2503 #else
2504 char *mb_malloc(size)
2505 long  size;
2506 #endif
2507 {
2508 char   *cptr;
2509 size_t  ssize;
2510
2511 ssize = (size_t) size;
2512 cptr = (char *) malloc(ssize);
2513
2514 #if DEBUG_MB_MALLOC
2515 fprintf(stdout, "malloc 0x%p\n", cptr);
2516 fflush(stdout);
2517 #endif
2518
2519 *cptr = '\0';
2520 return cptr;
2521 }
2522
2523
2524 #if defined(M_PROTO)
2525 void mb_free(char **pptr)
2526 #else
2527 void  *mb_free(pptr)
2528 char **pptr;
2529 #endif
2530 {
2531
2532 #if DEBUG_MB_MALLOC
2533 fprintf(stdout, "free 0x%p\n", *pptr);
2534 fflush(stdout);
2535 #endif
2536
2537 free((void *) *pptr);
2538 *pptr = NULL;
2539 }
2540
2541 #if defined(M_PROTO)
2542 static void AddToRowVec(int *length, char **rowvec, char *id)
2543 #else
2544 static void AddToRowVec(length, rowvec, id)
2545 int   *length;
2546 char **rowvec;
2547 char  *id;
2548 #endif
2549 {
2550 char tempRowVec[BIGBUF];
2551 int  rowVecLen;
2552
2553 sprintf(tempRowVec, "<FROWVEC CELLS=\"%s\">\n", id);
2554 rowVecLen = strlen(tempRowVec);
2555 *rowvec = mb_realloc(*rowvec, *length + rowVecLen);
2556 strcpy(*rowvec + *length - 1, tempRowVec);
2557 *length += rowVecLen;
2558 }
2559
2560
2561 #if defined(M_PROTO)
2562 void Add2ToRowVec(int *length, char **rowvec, char *id1, char *id2)
2563 #else
2564 void Add2ToRowVec(length, rowvec, id1, id2)
2565 int   *length;
2566 char **rowvec;
2567 char  *id1, *id2;
2568 #endif
2569 {
2570 char tempRowVec[BIGBUF];
2571 int  rowVecLen;
2572
2573 sprintf(tempRowVec, "<FROWVEC CELLS=\"%s %s\">\n", id1, id2);
2574 rowVecLen = strlen(tempRowVec);
2575 *rowvec = mb_realloc(*rowvec, *length + rowVecLen);
2576 strcpy(*rowvec + *length - 1, tempRowVec);
2577 *length += rowVecLen;
2578 }
2579
2580
2581 #if defined(M_PROTO)
2582 void StartEx(M_WCHAR *notes, M_WCHAR *lines, M_WCHAR *textsize)
2583 #else
2584 void StartEx(*notes, *lines, *textsize)
2585 M_WCHAR *notes;
2586 M_WCHAR *lines;
2587 M_WCHAR *textsize;
2588 #endif
2589 {
2590 exTextSize = vextextsize(textsize);
2591 stackex = vstack(notes);
2592
2593 StartBlock(NULL, "EX", NULL);
2594
2595 tonumexlines = (LOGICAL) (vnumber(lines) == NUMBER);
2596 firstAnnot   = TRUE;
2597 exLineNum    = 1;
2598 oldExLineNum = 1;
2599 saveex       = mb_malloc(1);
2600 svexlen      = 1;
2601 }
2602
2603
2604 #if defined(M_PROTO)
2605 void EndEx()
2606 #else
2607 void EndEx()
2608 #endif
2609 {
2610 char *annotation;
2611 int   annotLen;
2612 char *ssi;
2613 char  buffer[BIGBUF];
2614 int   length;
2615
2616 if (saveexseg)
2617     {
2618     if (exTextSize == SMALLEST)
2619         ssi = "SMLST";
2620     else if (exTextSize == SMALLER)
2621         ssi = "SMLR";
2622     else
2623         ssi = "NML";
2624     length = sprintf(buffer, "<P TYPE=\"LITERAL\" SSI=\"EX-%s\">", ssi);
2625
2626     if (tonumexlines)
2627         {
2628         length += sprintf(buffer + length,
2629                           "<HEAD TYPE=\"LINED\" SSI=\"EX-NUM\">");
2630         while (oldExLineNum <= exLineNum)
2631             {
2632             length += sprintf(buffer + length, "%2d:\n", oldExLineNum);
2633             oldExLineNum++;
2634             }
2635         length += sprintf(buffer + length, "</HEAD>");
2636         }
2637
2638     if (svheadlen)
2639         {
2640         annotation = MakeMByteString(savehead);
2641         annotLen = strlen(annotation);
2642         }
2643     else
2644         {
2645         annotation = "";
2646         annotLen   = 0;
2647         }
2648     saveex = mb_realloc(saveex,
2649                         svexlen + length + (svexseglen - 1) + annotLen + 5);
2650
2651     strcpy(saveex + svexlen - 1, buffer);
2652     svexlen += length;
2653
2654     if (svheadlen)
2655         {
2656         strcpy(saveex + svexlen - 1, annotation);
2657         svexlen += annotLen;
2658         }
2659
2660     strcpy(saveex + svexlen - 1, saveexseg);
2661     svexlen += svexseglen - 1;
2662
2663     strcpy(saveex + svexlen - 1, "</P>\n");
2664     svexlen += 5;
2665     mb_free(&saveexseg);
2666
2667     if (svheadlen)
2668         m_free(annotation, "multi-byte string");
2669     }
2670 }
2671
2672
2673 #if defined(M_PROTO)
2674 void StartNCW(char *which)
2675 #else
2676 void StartNCW(which)
2677 char *which;
2678 #endif
2679 {
2680 notehead = FALSE;
2681 PushForm(NULL, which, NULL);
2682 }
2683
2684
2685 #if defined(M_PROTO)
2686 void StartBlock(char *pclass, char *ssi, char *id)
2687 #else
2688 void StartBlock(pclass, ssi, id)
2689 char *pclass, *ssi, *id;
2690 #endif
2691 {
2692 char  localId[32];
2693 char *realId;
2694
2695 if (needFData)
2696     {
2697     fputs("<FDATA>\n", outfile);
2698     needFData = FALSE;
2699     }
2700
2701 if (inBlock)
2702     fputs("</BLOCK>\n", outfile);
2703
2704 realId = id;
2705 if (formStackBase && (formStackTop >= formStackBase))
2706     { /* there is a <form> in progress */
2707     if (!id)
2708         {
2709         sprintf(localId, "%s%d", sdlReservedName, NextId());
2710         realId = localId;
2711         }
2712     AddToRowVec(&(formStackTop->vecLen), &(formStackTop->rowVec), realId);
2713     }
2714
2715 fputs("<BLOCK", outfile);
2716 if (realId)
2717     fprintf(outfile, " ID=\"%s\"", realId);
2718 if (pclass)
2719     fprintf(outfile, " CLASS=\"%s\"", pclass);
2720 if (ssi)
2721     fprintf(outfile, " SSI=\"%s\"", ssi);
2722 fputs(">\n", outfile);
2723
2724 inBlock = TRUE;
2725 firstPInBlock = TRUE;
2726 }
2727
2728
2729 #if defined(M_PROTO)
2730 void StartNCWtext(char *which, char *iconFile, char *headingString)
2731 #else
2732 void StartNCWtext(which, iconFile, headingString)
2733 char *which;
2734 char *iconFile;
2735 char *headingString;
2736 #endif
2737 {
2738 char *icon, *heading;
2739
2740 /* Write default head if no user-specified head was encountered */
2741 if (! notehead)
2742     {
2743     heading = GetDefaultHeaderString(headingString, M_SDATA, which);
2744     fprintf(outfile, "<HEAD SSI=\"NCW\">%s", heading);
2745     fputs("</HEAD>\n", outfile);
2746     notehead = TRUE;
2747     m_free(heading, "default header string return");
2748     }
2749
2750 icon = GetDefaultHeaderString(iconFile, M_SYSTEM, "");
2751 if (icon && *icon)
2752     {
2753     char id[32];
2754
2755     sprintf(id, "%s%d", sdlReservedName, NextId());
2756     fprintf(outfile,
2757             "<HEAD SSI=\"NCW-ICON\"><SNREF>\n<REFITEM RID=\"%s\" ",
2758             id);
2759     fputs("CLASS=\"ICON\" SSI=\"NCW-ICON\">", outfile);
2760     fputs("</REFITEM>\n</SNREF></HEAD>\n", outfile);
2761     AddToSNB(id, icon);
2762     }
2763 m_free(icon, "icon name");
2764 }
2765
2766
2767 #if defined(M_PROTO)
2768 void AddToSNB(char *id, char *xid)
2769 #else
2770 void AddToSNB(id, xid)
2771 char *id;
2772 char *xid;
2773 #endif
2774 {
2775 char tmpsnb[BIGBUF];
2776 int  snblen;
2777
2778 if (savesnb)
2779     {
2780     sprintf(tmpsnb, "<GRAPHIC ID=\"%s\" XID=\"%s\">\n", id, xid);
2781     snblen = strlen(savesnb);
2782     savesnb = mb_realloc(savesnb, snblen + strlen(tmpsnb) + 1);
2783     strcpy(savesnb + snblen, tmpsnb);
2784     }
2785 else
2786     {
2787     if (snbstart)
2788         {
2789         fprintf(snbfp, "%d\n", snbstart);
2790         snbstart = 0;
2791         }
2792     fprintf(snbfp, "<GRAPHIC ID=\"%s\" XID=\"%s\">\n", id, xid);
2793     }
2794 }
2795
2796
2797 #if defined(M_PROTO)
2798 void IncludeToss()
2799 #else
2800 void IncludeToss()
2801 #endif
2802 {
2803 char     pathbuf[BIGBUF];
2804 char    *try = pathbuf;
2805 int      tryleng = sizeof(pathbuf);
2806 int      pathleng;
2807 int      fileleng;
2808 SEARCH  *thispath;
2809 char    *mb_inputname;
2810 M_WCHAR *wc_try, *wc_outputname;
2811 int      tossfile;
2812 char     filebuf[BIGBUF];
2813 size_t   bytesread;
2814 char   **tossline = toss;
2815
2816 /* the code below assume the extensions are .htg, .tss and .sdl */
2817 /* or at least that all extensions are 3 character plus a dot   */
2818
2819 fileleng = w_strlen(inputname);
2820 mb_inputname = mb_malloc(fileleng + 1);
2821 wcstombs(mb_inputname, inputname, fileleng);
2822 strcpy(mb_inputname + fileleng - 4, ".tss");
2823
2824 thispath = path;
2825 tossfile = -1;
2826 while (thispath)
2827     {
2828     pathleng = strlen(thispath->directory);
2829     if ((pathleng + fileleng) >= tryleng)
2830         {
2831         tryleng = pathleng + fileleng + 1;
2832         if (try == pathbuf)
2833             try = mb_malloc(tryleng);
2834         else
2835             try = mb_realloc(try, tryleng);
2836         }
2837     strcpy(try, thispath->directory);
2838     strcpy(try + pathleng, mb_inputname);
2839     tossfile = open(try, O_RDONLY);
2840     if (tossfile >= 0) break;
2841     thispath = thispath->next;
2842     }
2843
2844 if (tossfile >= 0)
2845     {
2846     fputs("<TOSS>\n", outfile);
2847     while ((bytesread = read(tossfile, filebuf, sizeof(filebuf))) > 0)
2848         if (fwrite(filebuf,
2849                    sizeof(*filebuf),
2850                    bytesread,
2851                    outfile) != bytesread) break;
2852     if (bytesread != 0)
2853         {
2854         strcpy(mb_inputname + fileleng - 4, ".sdl");
2855         wc_outputname = MakeWideCharString(mb_inputname);
2856         wc_try = MakeWideCharString(try);
2857         m_err2("error copying %s to output (%s) as the <toss> element",
2858                wc_try,
2859                wc_outputname);
2860         m_free(wc_try, "wide character toss input name");
2861         m_free(wc_outputname, "wide character output name");
2862         }
2863     close(tossfile);
2864     fputs("</TOSS>\n", outfile);
2865     }
2866 else
2867     {
2868     while (*tossline)
2869         {
2870         fputs(*tossline++, outfile);
2871         fputs("\n", outfile);
2872         }
2873     }
2874
2875 if (try != pathbuf) mb_free(&try);
2876 mb_free(&mb_inputname);
2877 }
2878
2879 /* Below is a modified version of m_cyclent() that returns a pointer
2880  * to the entity content rather than its value.  Returning a pointer
2881  * to the entity's content field allows it to be modified.
2882 */
2883 /* Cyclent.c contains procedure m_cyclent(), callable by interface
2884    designers, to cycle through all defined entities, returning information
2885    about them */
2886 #if defined(M_PROTO)
2887 M_WCHAR *CycleEnt(LOGICAL init,
2888                   unsigned char *type,
2889                   M_WCHAR ***content,
2890                   unsigned char *wheredef)
2891 #else
2892 M_WCHAR *m_cyclent(init, type, content, wheredef)
2893   LOGICAL init ;
2894   unsigned char *type ;
2895   M_WCHAR ***content ;
2896   unsigned char *wheredef ;
2897 #endif
2898 {
2899 static M_TRIE *current ;
2900 static M_TRIE *ancestor[M_NAMELEN + 1] ;
2901 static length = 0 ;
2902 static M_WCHAR name[M_NAMELEN + 1] ;
2903
2904 if (init)
2905     {
2906     current = m_enttrie->data ;
2907     length = 0 ;
2908     }
2909
2910 if (length < 0) return(NULL) ;
2911
2912 while (current->symbol)
2913     {
2914     ancestor[length] = current ;
2915     name[length++] = current->symbol ;
2916     current = current->data ;
2917     }
2918 name[length] = M_EOS ;
2919
2920 *type     =  ((M_ENTITY *)  current->data)->type ;
2921 *content  = &(((M_ENTITY *) current->data)->content) ;
2922 *wheredef =  ((M_ENTITY *)  current->data)->wheredef ;
2923
2924 while (length >= 0)
2925     {
2926     if (current->next)
2927         {
2928         current = current->next ;
2929         break ;
2930         }
2931     length-- ;
2932     if (length < 0) break ;
2933     current = ancestor[length] ;
2934     }
2935
2936 return(name) ;
2937 }
2938
2939 /* A routine to examine all defined entities looking for ones of type
2940  * M_SDATA.  When found, if the entity's content is of the form
2941  * [......] (six characters surrounded by square brackets), its
2942  * content is modified to be <SPC NAME="[......]"> so that it may be
2943  * emitted into the SDL output.
2944 */
2945 void ModifyEntities()
2946 {
2947 unsigned char type;
2948 unsigned char wheredef;
2949 M_WCHAR **content;
2950 M_WCHAR  *name;
2951 char     *mb_content;
2952 M_WCHAR  *newContent;
2953 static char mb_newContent[] = "<SPC NAME=\"[123456]\">";
2954
2955 name = CycleEnt(TRUE, &type, &content, &wheredef);
2956 if (!name) return;
2957
2958 mb_newContent[21] = '\0';
2959
2960 do  {
2961     if ((type == M_SDATA) && *content)
2962         {
2963         mb_content = MakeMByteString(*content);
2964         if ((strlen(mb_content) == 8) &&
2965             (mb_content[0] == '[')    &&
2966             (mb_content[7] == ']'))
2967             {
2968             strncpy(mb_newContent+11, mb_content, 8);
2969             if (wheredef == M_DPARSER)
2970                 m_free(*content, "old SDATA entity content");
2971             *content = MakeWideCharString(mb_newContent);
2972             }
2973         m_free(mb_content, "multi-byte SDATA entity content");
2974         }
2975     }
2976 while (name = CycleEnt(FALSE, &type, &content, &wheredef));
2977 }
2978
2979
2980 #if defined(M_PROTO)
2981 void PushForm(char *class, char *ssi, char *id)
2982 #else
2983 void PushForm(class, ssi, id)
2984      char *class;
2985      char *ssi;
2986      char *id;
2987 #endif
2988 {
2989 char  localId[SDLNAMESIZ+10];
2990 char *realId;
2991 int   stackSize;
2992
2993 if (needFData)
2994     {
2995     fputs("<FDATA>\n", outfile);
2996     needFData = FALSE;
2997     }
2998
2999 if (inBlock)
3000     {
3001     fputs("</BLOCK>\n", outfile);
3002     inBlock = FALSE;
3003     }
3004
3005 realId = id;
3006 if (formStackBase && (formStackTop >= formStackBase))
3007     { /* there is a <form> in progress */
3008     if (!id)
3009         {
3010         sprintf(localId, "%s%d", sdlReservedName, NextId());
3011         realId = localId;
3012         }
3013     AddToRowVec(&(formStackTop->vecLen), &(formStackTop->rowVec), realId);
3014     }
3015
3016 if (formStackTop == formStackMax)
3017     {
3018     if (!formStackBase)
3019         {
3020         formStackBase = (FORMINFO *) malloc(10 * sizeof(FORMINFO));
3021         formStackTop  = formStackBase;
3022         formStackMax  = formStackBase + 9;
3023         }
3024     else
3025         {
3026         stackSize = formStackMax - formStackBase + 1;
3027         formStackBase =
3028             realloc(formStackBase, (stackSize + 10) * sizeof(FORMINFO));
3029         formStackTop = formStackBase + stackSize;
3030         formStackMax = formStackBase + (stackSize + 9);
3031         }
3032     }
3033 else
3034     formStackTop++;
3035
3036 formStackTop->rowVec = mb_malloc(1);
3037 formStackTop->vecLen = 1;
3038
3039 fputs("<FORM", outfile);
3040 if (realId)
3041     fprintf(outfile, " ID=\"%s\"", realId);
3042 if (class)
3043     fprintf(outfile, " CLASS=\"%s\"", class);
3044 if (ssi)
3045     fprintf(outfile, " SSI=\"%s\"", ssi);
3046 fputs(">\n", outfile);
3047 needFData = TRUE;
3048 }
3049
3050
3051 #if defined(M_PROTO)
3052 void PushForm2(char *class, char *ssi, char *id1, char *id2)
3053 #else
3054 void PushForm2(class, ssi, id1, id2)
3055      char *class;
3056      char *ssi;
3057      char *id1;
3058      char *id2;
3059 #endif
3060 {
3061 char id[32];
3062 int  stackSize;
3063 int  formId;
3064
3065 if (needFData)
3066     {
3067     fputs("<FDATA>\n", outfile);
3068     needFData = FALSE;
3069     }
3070
3071 if (inBlock)
3072     {
3073     fputs("</BLOCK>\n", outfile);
3074     inBlock = FALSE;
3075     }
3076
3077 if (!id2)
3078     {
3079     sprintf(id, "%s%d", sdlReservedName, formId = NextId());
3080     id2 = id;
3081     }
3082
3083 if (formStackBase && (formStackTop >= formStackBase))
3084     { /* there is a <form> in progress */
3085     Add2ToRowVec(&(formStackTop->vecLen),
3086                  &(formStackTop->rowVec),
3087                  id1,
3088                  id2);
3089     }
3090
3091 if (formStackTop == formStackMax)
3092     {
3093     if (!formStackBase)
3094         {
3095         formStackBase = (FORMINFO *) malloc(10 * sizeof(FORMINFO));
3096         formStackTop  = formStackBase;
3097         formStackMax  = formStackBase + 9;
3098         }
3099     else
3100         {
3101         stackSize = formStackMax - formStackBase + 1;
3102         formStackBase =
3103             realloc(formStackBase, (stackSize + 10) * sizeof(FORMINFO));
3104         formStackTop = formStackBase + stackSize;
3105         formStackMax = formStackBase + (stackSize + 9);
3106         }
3107     }
3108 else
3109     formStackTop++;
3110
3111 formStackTop->rowVec = mb_malloc(1);
3112 formStackTop->vecLen = 1;
3113
3114 fprintf(outfile, "<FORM ID=\"%s\"", id2);
3115 if (class)
3116     fprintf(outfile, " CLASS=\"%s\"", class);
3117 if (ssi)
3118     fprintf(outfile, " SSI=\"%s\"", ssi);
3119 fputs(">\n", outfile);
3120 needFData = TRUE;
3121 }
3122
3123 void PopForm()
3124 {
3125 if (inBlock)
3126     {
3127     fputs("</BLOCK>\n", outfile);
3128     inBlock = FALSE;
3129     }
3130
3131 fprintf(outfile,
3132         "</FDATA>\n<FSTYLE>\n%s</FSTYLE>\n</FORM>\n",
3133         formStackTop->rowVec);
3134
3135 mb_free(&(formStackTop->rowVec));
3136 --formStackTop;
3137 }
3138
3139 void PopForm2()
3140 {
3141 if (inBlock)
3142     {
3143     fputs("</BLOCK>\n", outfile);
3144     inBlock = FALSE;
3145     }
3146
3147 fprintf(outfile,
3148         "</FDATA>\n<FSTYLE NCOLS=\"2\">\n%s</FSTYLE>\n</FORM>\n",
3149         formStackTop->rowVec);
3150
3151 mb_free(&(formStackTop->rowVec));
3152 --formStackTop;
3153 }
3154
3155 /* look to see if there's an open form with no data; if so, add a data
3156  * block and close the form.  This situation will happen when a form
3157  * is pushed but the source doesn't go to text either because the text
3158  * is explicitly optional or due to the fact that text can be null.
3159 */
3160 void PopFormMaybe()
3161 {
3162 if ((formStackTop >= formStackBase) && (formStackTop->vecLen == 1))
3163     {
3164     StartBlock(NULL, NULL, NULL);
3165     PopForm();
3166     }
3167 }
3168
3169 void EmitSavedAnchors()
3170 {
3171 char buffer[BIGBUF];
3172
3173 if (parTextId)
3174     {
3175     sprintf(buffer,
3176             "<ANCHOR ID=\"%s%d\">",
3177             sdlReservedName,
3178             parTextId);
3179     mb_strcode(buffer, outfile);
3180     parTextId = 0;
3181     }
3182 }
3183
3184 void CloseVirpage()
3185 {
3186 if (parTextId)
3187     {
3188     fputs("<BLOCK>\n<P>", outfile);
3189     EmitSavedAnchors();
3190     fputs("</P>\n</BLOCK>", outfile);
3191     }
3192 fputs("</VIRPAGE>\n", outfile);
3193 }