dthelp: Further coverity fixes
[oweals/cde.git] / cde / programs / dthelp / dthelpprint / PrintTopics.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 #if DOC
24 /*===================================================================
25 $FILEBEG$:   PrintTopics.c
26 $COMPONENT$: dthelpprint
27 $PROJECT$:   Cde1
28 $SYSTEM$:    HPUX 9.0; AIX 3.2; SunOS 5.3
29 $REVISION$:  $TOG: PrintTopics.c /main/12 1998/04/01 17:26:51 mgreess $
30 $COPYRIGHT$:
31    (c) Copyright 1996 Digital Equipment Corporation.
32    (c) Copyright 1993,1994,1996 Hewlett-Packard Company.
33    (c) Copyright 1993,1994,1996 International Business Machines Corp.
34    (c) Copyright 1993,1994,1996 Sun Microsystems, Inc.
35    (c) Copyright 1993,1994,1996 Novell, Inc. 
36    (c) Copyright 1996 FUJITSU LIMITED.
37    (c) Copyright 1996 Hitachi.
38 ==$END$==============================================================*/
39 #endif
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <sys/stat.h>    /* for stat() */
46 #if defined(sun) || defined(USL) || defined(__uxp__)
47 #include <widec.h>
48 #else
49 #include <wchar.h>
50 #endif
51 #include <locale.h>
52 #if !defined(sun)
53 #include <langinfo.h>
54 #endif
55
56 /* Iconv not defined for linux.  Use the EUSCompat stubs instead. */
57 #if !defined(linux)
58 #  include <iconv.h>
59 #else
60 #  include <EUSCompat.h>
61 #endif
62 #include <errno.h>
63
64 #include <time.h>
65
66 #include "HelpPrintI.h"  /* helpprint */
67
68 #include "HelpTermP.h"  /* from libDtHelp */
69 #include "CanvasP.h"    /* from libDtHelp */
70 #include "StringFuncsI.h" /* from libDtHelp */
71 #include "LocaleXlate.h" /* from libDtHelp */
72 #include "bufioI.h" /* from libDtHelp; required for AccessI.h */
73 #include "FileUtilsI.h" /* from libDtHelp */
74 #include "GenUtilsP.h" /* from libDtHelp */
75
76 /*#include "AccessI.h" ** from libDtHelp */
77 /* I can't include AccessI.h because it redefines the Boolean type,
78 which is also defined in Xt/Intrisincs.h.  I'm just including the
79 prototypes from AccessI.h here. */
80 extern char * _DtHelpCeGetVolumeLocale(VolumeHandle helpVolumeHandle);
81 extern const char * _DtHelpCeGetVolumeCharSet(VolumeHandle helpVolumeHandle);
82
83 /*======== platform adjustments ==============*/
84 #ifdef sun
85 #define wcswidth(s,n) wscol(s)
86 #define wcslen(s) wslen(s)
87 #endif
88
89 /*======== boundary values ==============*/
90 #define MAXSECTS                50         /* max number of section nesting */
91
92 #define MAXVOLTITLEWIDTH 50
93 #define MAXTOPICTITLEWIDTH 50
94 #define MAXPAGENUMWIDTH 3
95 #define MAXSECTNUMWIDTH 8
96
97 /*======== helper values ===============*/
98 #define EOS           '\0'
99 #define EMPTY_STR     s_EmptyStr
100
101 #define PTSET       3               /* message set */
102
103
104 /*======== helper variables ===============*/
105 static char s_EmptyStr[1] = { EOS };
106
107 /* To do:
108         * check roman 8/Latin 1
109         * check PAGER env variable
110         * do character wrap
111 */
112
113 /*======== data structs ==============*/
114
115 /* These data structs manage the table of contents (Toc)
116    of a help volume.  The Toc uses two organizational mechanisms:
117    a linear table of entries, which gives sequential order
118    and is used to generate the TOC; and a binary tree of entries
119    that is sorted alphabetically by location id, and is used to
120    speed lookup of the page number of a locationId for use in
121    generating the index. */
122
123 #if DOC
124 ===================================================================
125 $PTYPEBEG$:  TocEntry
126 $1LINER$:  All data for a single toc entry
127 $DESCRIPT$:
128 $ARGS$:
129 ========================================================$SKIP$=====*/
130 #endif /*DOC*/
131 /*$DEF$*/
132 typedef struct TocEntry
133 {
134    struct TocEntry * nextEntry;         /* next entry in the linear list */
135    struct TocEntry * leftLoc;           /* left location of sorted binary tree */
136    struct TocEntry * rightLoc;          /* right location of sorted binary tree */
137    char * locationId;                   /* locationId of the topic */
138    int    pageNumber;                   /* page the topic located on */
139    int    level;                        /* nesting level */
140    int    sectNums[MAXSECTS];           /* section numbers */
141    char * sectStr;                      /* alternate section string */
142 } TocEntry;
143 /*$END$*/
144
145 #if DOC
146 ===================================================================
147 $PTYPEBEG$:  Toc
148 $1LINER$:  Manages the toc entries in a volume
149 $DESCRIPT$:
150 $ARGS$:
151 ========================================================$SKIP$=====*/
152 #endif /*DOC*/
153 /*$DEF$*/
154 typedef struct Toc
155 {
156    TocEntry * linearEntries;            /* ptr to head of linear list of entries */
157    TocEntry * lastLinearEntry;          /* ptr to tail of linear list of entries */
158    TocEntry * sortedEntries;            /* ptr to top of sorted btree of entries */
159 } Toc;
160 /*$END$*/
161
162 #if DOC
163 ===================================================================
164 $PTYPEBEG$:  HeadFootFormat
165 $1LINER$:  All data related to the header/footer formatting
166 $DESCRIPT$:
167 $ARGS$:
168 ========================================================$SKIP$=====*/
169 #endif /*DOC*/
170 /*$DEF$*/
171 typedef struct HeadFootFormat
172 {
173    char *  formattedEvenHeader; /* formatted even-page header when printing volume */
174    char *  formattedOddHeader;  /* formatted odd-page header when printing volume */
175    char *  formattedEvenFooter; /* formatted even-page footer when printing volume */
176    char *  formattedOddFooter;  /* formatted odd-page footer when printing volume */
177    int     evenHeaderLineCnt;   /* num lines in even-page header when printing volume */
178    int     oddHeaderLineCnt;    /* num lines in odd-page header when printing volume */
179    int     evenFooterLineCnt;   /* num lines in even-page footer when printing volume */
180    int     oddFooterLineCnt;    /* num lines in odd-page footer when printing volume */
181 } HeadFootFormat;
182 /*$END$*/
183
184 #if DOC
185 ===================================================================
186 $PTYPEBEG$:  HeadFootFormatArgs
187 $1LINER$:  Arguments used in head/foot formatting
188 $DESCRIPT$:
189 $ARGS$:
190 ========================================================$SKIP$=====*/
191 #endif /*DOC*/
192 /*$DEF$*/
193 typedef struct HeadFootFormatArgs
194 {
195    char *   volumeTitle;
196    int      volumeTitleColsWidth;
197    char *   topicTitle;
198    int      topicTitleColsWidth;
199    char *   volumeDate;
200    int      volumeDateColsWidth;
201    char *   todaysDate;
202    int      todaysDateColsWidth;
203    int      sectNumColsWidth;
204    int      pageNumColsWidth;
205 }
206 HeadFootFormatArgs;
207 /*$END$*/
208
209 #if DOC
210 ===================================================================
211 $PTYPEBEG$:  PrintState
212 $1LINER$:  All data related to the on-going printing operation
213 $DESCRIPT$:
214 $ARGS$:
215 ========================================================$SKIP$=====*/
216 #endif /*DOC*/
217 /*$DEF$*/
218 typedef struct PrintState
219 {
220    char *   currentLocId;               /* current loc Id in volume */
221    char *   outputFromLocId;            /* start output at this location */
222    HeadFootFormatArgs hffArgs;          /* arguments used in head/foot formatting */
223    HeadFootFormat tocHFF;               /* HF formatting info for TOC */
224    HeadFootFormat bodyHFF;              /* HF formatting info for body */
225    HeadFootFormat indexHFF;             /* HF formatting info for index */
226    Boolean  inhibitOutput;              /* inhibit output */
227    int      curPageNumber;              /* page number of current output */
228    int      curLineNumber;              /* line number of current page of output */
229    VolumeHandle volHandle;              /* volume handle of volume being printed */
230    CanvasHandle canvasHandle;           /*canvas handle of terminal format canvas*/
231    int      sectNums[MAXSECTS];         /* current section number */
232    char *   sectStr;                    /* current section string value */
233    int      level;                      /* level of section nesting */
234 } PrintState;
235 /*$END$*/
236
237 /*======== static variables ===============*/
238
239 #if DOC
240 ===================================================================
241 $PTYPEBEG$:  SymValue
242 $1LINER$:  Maps a symbolic string to a printf-format string.
243 $DESCRIPT$:
244 $ARGS$:
245 ========================================================$SKIP$=====*/
246 #endif /*DOC*/
247 /*$DEF$*/
248 typedef struct SymValue
249 {
250    char *  symbol;
251    char *  argref;
252 } SymValue;
253
254 /* List of symbols that can be used in header/footer strings
255    and the printf args that are used to access the data. */
256 /* NOTE: ***IT IS CRITICAL THAT THE SYMBOL STRING HAVE THE 
257    SAME OR LONGER LENGTH THAN THE ARGUMENT STRING. ***/
258 /* NOTE: ***ALWAYS UPDATE THE UNUSED_ARGREF AND THE sprintf() CALL 
259             IN PrintHeadFootStr() WHEN ADDING NEW SYMBOLS***/
260 /* NOTE: The replacement values use a trick to get around the limitations
261    of printf(), which only allows args numbered 1 to 9.  The strategy
262    is to do two printfs, and to use the %% construct in the first string
263    to generate a % construct in the second string. */
264 SymValue g_HeadFootSymsList[] = 
265 {
266   { "$VOLUMEFILL", "%%2$*1$c" },  /* filler for fixed sized volume name */
267   { "$TOPICFILL", "%%4$*3$c" },   /* filler for fixed sized current topic title */
268   { "$PAGENUMFILL", "%%6$*5$c" }, /* filler for fixed sized page number */
269   { "$SECTNUMFILL", "%%8$*7$c" }, /* filler for fixed sized section number or name */
270   { "$TODAY", "%1$s" },       /* today's date */
271   { "$VOLUME", "%2$s" },      /* volume name */
272   { "$TOPIC", "%3$s" },       /* current topic title */
273   { "$PAGENUM", "%4$d" },     /* page number */
274   { "$VOLDATE", "%5$s" },     /* date on the help volume file */
275   { "$SECTNUM", "%6$s" },     /* section number or name */
276   { "$LMARGIN", "%8$*7$c" },      /* left margin blanks; args 7 & 8 */
277   { NULL, NULL }
278 };
279 #define  UNUSED_ARGREF   "%%9$n"    /* arg to hold the num of unused args */
280 /*$END$*/
281
282
283 /*======== functions ==============*/
284 \f
285 #if DOC
286 ===================================================================
287 $PFUNBEG$:  TocNextEntry()
288 $1LINER$:  Get next entry in the toc
289 $DESCRIPT$:
290 $ARGS$:
291 $RETURNS$:
292 != NULL:   next entry
293 NULL:      no more entires
294 ========================================================$SKIP$=====*/
295 #endif /*DOC*/
296
297 static
298 TocEntry * TocNextEntry(
299   Toc *      toc,
300   TocEntry * entry)
301 {       /*$CODE$*/
302   if (NULL == entry) return toc->linearEntries;
303   return entry->nextEntry;
304 }       /*$END$*/
305
306 \f
307 #if DOC
308 ===================================================================
309 $PFUNBEG$:  TocFindSortedEntry()
310 $1LINER$:  Finds an entry using the sorted btree of entries
311 $DESCRIPT$:
312 $RETURNS$:
313 $ARGS$:
314 0:   ok
315 -1:  entry not found
316 ========================================================$SKIP$=====*/
317 #endif /*DOC*/
318
319 static
320 int TocFindSortedEntry(
321   Toc *        toc,
322   char *       locationId,
323   TocEntry * * ret_parent,
324   TocEntry * * ret_entry)
325 {       /*$CODE$*/
326    TocEntry * prev;
327    TocEntry * cur;
328    TocEntry * tmp;
329
330    if (NULL == ret_parent) ret_parent = &tmp;
331    if (NULL == ret_entry) ret_entry = &tmp;
332
333    *ret_parent = NULL;
334    *ret_entry = NULL;
335
336    prev = toc->sortedEntries;
337    cur = prev;
338    while ( NULL != cur )
339    {
340       int cmp;
341       cmp = _DtHelpCeStrCaseCmp(locationId,cur->locationId);
342       if ( cmp < 0 )
343          prev = cur, cur = cur->leftLoc;
344       else if ( cmp > 0 )
345          prev = cur, cur = cur->rightLoc;
346       else
347       {
348          *ret_parent = prev, *ret_entry = cur;
349          return 0;                            /* RETURN: found */
350       }
351    }
352    *ret_parent = prev;
353    return -1;                                 /* RETURN: not found */
354 }       /*$END$*/
355
356 \f
357 #if DOC
358 ===================================================================
359 $PFUNBEG$:  TocNewEntry()
360 $1LINER$:  Add a Toc entry to the Toc
361 $DESCRIPT$:
362 $RETURNS$:
363 $ARGS$:
364 0:   ok
365 -1:  memory allocation error
366 ========================================================$SKIP$=====*/
367 #endif /*DOC*/
368
369 static
370 int TocNewEntry(
371   Toc *      toc,
372   char *     locationId,
373   int        pageNumber,
374   int        level,
375   int *      sectNums,
376   char *     sectStr)
377 {       /*$CODE$*/
378    TocEntry * new;
379
380    new = calloc(1,sizeof(TocEntry));
381    if (NULL == new) return -1;        /* RETURN: mem alloc err */
382
383    /* init contents */
384    new->locationId = strdup(locationId);
385    new->pageNumber = pageNumber;
386    new->level = level;
387    new->sectStr = sectStr;
388    memcpy(new->sectNums, sectNums, sizeof(new->sectNums));
389
390    if (NULL == new->locationId)
391       { free(new); return -1; }       /* RETURN: mem alloc err */
392
393    /*** insert into toc ***/
394
395    /* if first in list */
396    if (NULL == toc->linearEntries)
397    {
398       toc->linearEntries = new;
399       toc->lastLinearEntry = new;
400       toc->sortedEntries = new;
401    }
402    else /* not first in list */
403    {
404       TocEntry * parent, * entry;
405
406       if ( TocFindSortedEntry(toc,locationId,&parent,&entry) == 0 )
407          return -2;                        /* RETURN: duplicate entry */
408       /* insert into the btree (not balanced) */
409       if( _DtHelpCeStrCaseCmp(locationId,parent->locationId) < 0 )
410          parent->leftLoc = new;
411       else
412          parent->rightLoc = new;
413       /* insert into the linear list */
414       toc->lastLinearEntry->nextEntry = new;
415       toc->lastLinearEntry = new;
416    }
417    return 0;        /* RETURN: ok */
418 }       /*$END$*/
419
420 \f
421 #if DOC
422 ===================================================================
423 $PFUNBEG$:  IconvFile()
424 $1LINER$:  Compares cur env and volume and iconv file if necessary 
425 $DESCRIPT$:
426 $RETURNS$:
427 0:  no conversion needed or not possible to determine if needed
428 1:  conversion needed & successful
429 -1: conversion needed and failed
430 $ARGS$:
431 ========================================================$SKIP$=====*/
432 #endif /*DOC*/
433
434 static
435 int IconvFile(
436       _DtHPrOptions * options,
437       VolumeHandle helpVolumeHandle,
438       char * * srcFile)
439 {       /*$CODE$*/
440 #define CUR_LOCALE    0
441 #define CUR_CODESET   1
442 #define VOL_LOCALE    2
443 #define VOL_CODESET   3
444 #define FROM_CODESET  4
445 #define TO_CODESET    5
446 #define NUMSTRS       6
447
448    int    ret;
449    int    i;
450    char * loc[NUMSTRS];
451    char * destFile = NULL;
452    char * codeset;
453    char   buf[1000];
454
455    for (i=0; i<NUMSTRS; i++) loc[i] = NULL;
456    
457    /* get the normalized current codeset */
458    _DtHelpCeXlateOpToStdLocale (
459                      DtLCX_OPER_SETLOCALE, setlocale(LC_CTYPE,NULL),
460                      &loc[CUR_LOCALE], NULL, &loc[CUR_CODESET]);
461
462    /* get the normalized volume codeset */
463    loc[VOL_LOCALE] = _DtHelpCeGetVolumeLocale(helpVolumeHandle);
464
465    /* codeset begins after the '.'; find it */
466    codeset = NULL;
467    if (    loc[VOL_LOCALE]
468         && _DtHelpCeStrchr(loc[VOL_LOCALE], ".", 1, &codeset) == 0)
469    {
470        codeset++;
471    }
472    loc[VOL_CODESET] = (NULL != codeset ? strdup(codeset) : NULL);
473
474    /* if either locale is NULL or if they are the same string
475       then don't iconv the file */
476    if (   NULL == loc[CUR_CODESET]
477        || NULL == loc[VOL_CODESET]
478        || strcmp(loc[CUR_CODESET],loc[VOL_CODESET]) == 0 )
479    {
480        ret = 0;                   /* RETURN:  no iconv needed/possible */
481        goto cleanup;
482    }
483
484    /* get the source codeset */
485    _DtHelpCeXlateStdToOpLocale (
486                      DtLCX_OPER_ICONV1, loc[VOL_LOCALE],
487                      "iso8859_1", &loc[FROM_CODESET]);
488
489    /* get the target codeset */
490    _DtHelpCeXlateStdToOpLocale (
491                      DtLCX_OPER_ICONV1, loc[CUR_LOCALE],
492                      "iso8859_1", &loc[TO_CODESET]);
493
494    /* construct the command line */
495    destFile = _DtHPrCreateTmpFile(TMPFILE_PREFIX,TMPFILE_SUFFIX);
496    if (NULL == destFile)
497    {
498        ret = -1;                   /* error */
499        goto cleanup;
500    }
501
502    sprintf(buf,options->iconvCmdAndArgs,
503                  loc[FROM_CODESET],loc[TO_CODESET],*srcFile, destFile);
504
505    /* do the conversion */
506    if(options->debugHelpPrint) printf("%s\n",buf);
507    ret = system(buf);
508    ret = (ret == 0 ? 1 : -1);    /* 1: success; -1: failure */
509
510    /* if successful conversion, change the src file */
511    if (ret >= 0)
512    {
513       unlink(*srcFile);
514       free(*srcFile);
515       *srcFile = destFile;
516    }
517    else
518    {
519       unlink(destFile);
520    }
521
522 cleanup:
523    /* free memory */
524    for (i=0; i<NUMSTRS; i++) if (loc[i]) free(loc[i]);
525
526    return ret;
527 } /* count lines */
528
529 #ifdef ICONV_INBUF_CONST
530 # define ICONV_INBUF_TYPE       const char **
531 #else
532 # define ICONV_INBUF_TYPE       char **
533 #endif
534
535 #define WORKSIZE 1024*10        /* 10k */
536
537 /*
538  * _i18nwork1[] is used to convert the passed string with CD iconv.
539  * in _converter_().
540  *
541  */
542 static void           *_i18nwork1 = NULL;
543 static unsigned long  _i18nsize1 = 0;
544 static int            shouldAlloc1 = ~0;
545
546
547 static void _converter_( iconv_t CD,
548                         void *from, unsigned long from_len,
549                         void **to )
550 {
551     char          *InBuf;
552     size_t        InBytesLeft;
553     char          *OutBuf = NULL;
554     size_t        OutBytesLeft = 0;
555     size_t        _OutBytesLeft = 0;
556     size_t        iconv_ret;
557     size_t        converted_num = 0;
558     unsigned long to_len;
559
560
561     *to = NULL;
562     to_len = 0;
563
564     if ( shouldAlloc1 ) {
565         /* Obtain work area */
566         _i18nwork1 = (size_t *)malloc( WORKSIZE );
567         if ( !_i18nwork1 ) {
568             _i18nwork1 = NULL;
569             return;
570         }
571         _i18nsize1 = WORKSIZE; 
572         shouldAlloc1 = 0;
573     }
574
575     InBuf        = (char *)from;
576     InBytesLeft  = from_len;
577     OutBytesLeft = _i18nsize1;
578     OutBuf = (char *)_i18nwork1;
579
580     /*
581      * Need to place iconv state to the initial one by
582      * setting inbuf to NULL of iconv().
583      */
584     iconv( CD, (ICONV_INBUF_TYPE)NULL, 0, NULL, 0 );
585     while( 1 ) {
586         /*
587          * InBuf
588          *  v
589          * +----------------------------+
590          * | |                        | |
591          * +----------------------------+
592          *  <-------------------------->
593          *          InBytesLeft
594          *
595          *             |
596          *             | iconv()
597          *             V
598          * (_i18nwork1)
599          * OutBuf
600          *  v
601          * +----------------------------+
602          * | |                        | |
603          * +----------------------------+
604          *  <-------------------------->
605          *          InBytesLeft
606          */
607
608         iconv_ret = iconv( CD, (ICONV_INBUF_TYPE)&InBuf, &InBytesLeft,
609                                &OutBuf, &OutBytesLeft );
610         if ( iconv_ret == 0 ) {
611             /* iconv done
612              *                             InBuf
613              *                               v
614              * +----------------------------+
615              * |XXXXXXXXXXXXXXXXXXXXXXXXXXXX|
616              * +----------------------------+
617              *                               
618              *                               InBytesLeft=0
619              *
620              * (_i18nwork1)
621              *  |               OutBuf
622              *  V                 v
623              * +----------------------------+
624              * |XXXXXXXXXXXXXXXXX| |      | |
625              * +----------------------------+
626              *  <---------------> <-------->
627              *   converted_num    OutBytesLeft
628              */
629             converted_num = (unsigned long)((char *)OutBuf-(char *)_i18nwork1);
630             *to = (void *)_i18nwork1;
631             to_len = (unsigned long)converted_num;
632             break;
633         } else {
634             if ( errno == E2BIG ) {
635                 /* Overflow. still data is left.
636                  *               InBuf
637                  *                 v
638                  * +----------------------------+
639                  * |XXXXXXXXXXXXXX| |         | |
640                  * +----------------------------+
641                  *                 <----------->
642                  *                  InBytesLeft
643                  *
644                  * (_i18nwork1)
645                  *  |                         OutBuf
646                  *  V                          v
647                  * +----------------------------+
648                  * |XXXXXXXXXXXXXXXXXXXXXXXXXXX |
649                  * +----------------------------+
650                  *  <-------------------------> 
651                  *          converted_num      OutBytesLeft=?
652                  */
653                 void *_p;
654
655                 /* Check how many converted already. */
656                 converted_num =
657                         (unsigned long)((char *)OutBuf - (char *)_i18nwork1);
658                 _i18nsize1 += WORKSIZE;
659                 _p = realloc( _i18nwork1, _i18nsize1 );
660                 if ( !_p ) {
661                     *to = NULL;
662                     to_len = 0;
663                     free( _i18nwork1 );
664                     _i18nwork1 = NULL;
665                     _i18nsize1 = 0;
666                     shouldAlloc1 = ~0;
667                     break;
668                 } else {
669                     _i18nwork1 = _p;
670                     OutBuf = (char *)((char*)_i18nwork1 + converted_num);
671                     OutBytesLeft += WORKSIZE;
672                 }  
673             } else {
674                 *to = NULL;
675                 to_len = 0;
676                 break;
677             }
678         }
679     }
680
681     /*
682      * Null terminate
683      */
684
685     if ( *to != NULL ) {
686         if ( _i18nsize1 >= to_len + 1 ) {
687             ((char *)_i18nwork1)[to_len] = '\0';
688         } else {
689             void *_p;
690
691             _i18nsize1++;
692             _p = realloc( _i18nwork1, _i18nsize1 );
693             if ( !_p ) {
694                 *to = NULL;
695                 to_len = 0;
696                 free( _i18nwork1 );
697                 _i18nwork1 = NULL;
698                 _i18nsize1 = 0;
699                 shouldAlloc1 = ~0;
700             } else {
701                 _i18nwork1 = _p;
702                 ((char *)_i18nwork1)[to_len] = '\0';
703             }  
704        }
705     }
706 }
707
708 /*
709  * Iconv for buffer
710  */
711 static
712 int IconvBuffer(
713       VolumeHandle helpVolumeHandle,
714       char *src,
715       char **dest )
716 {       /*$CODE$*/
717 #define CUR_LOCALE    0
718 #define CUR_CODESET   1
719 #define VOL_LOCALE    2
720 #define VOL_CODESET   3
721 #define FROM_CODESET  4
722 #define TO_CODESET    5
723 #define NUMSTRS       6
724
725    int    ret;
726    int    i;
727    char * loc[NUMSTRS];
728    char * codeset;
729    char   buf[1000];
730    static int isFirst = ~0;
731    static iconv_t CD = (iconv_t)-1;
732
733    for (i=0; i<NUMSTRS; i++) loc[i] = NULL;
734    
735    /* get the normalized current codeset */
736    _DtHelpCeXlateOpToStdLocale (
737                      DtLCX_OPER_SETLOCALE, setlocale(LC_CTYPE,NULL),
738                      &loc[CUR_LOCALE], NULL, &loc[CUR_CODESET]);
739
740    /* get the normalized volume codeset */
741    loc[VOL_LOCALE] = _DtHelpCeGetVolumeLocale(helpVolumeHandle);
742
743    /* codeset begins after the '.'; find it */
744    codeset = NULL;
745    if (    loc[VOL_LOCALE]
746         && _DtHelpCeStrchr(loc[VOL_LOCALE], ".", 1, &codeset) == 0)
747    {
748        codeset++;
749    }
750    loc[VOL_CODESET] = (NULL != codeset ? strdup(codeset) : NULL);
751
752    /* if either locale is NULL or if they are the same string
753       then don't iconv the file */
754    if (   NULL == loc[CUR_CODESET]
755        || NULL == loc[VOL_CODESET]
756        || strcmp(loc[CUR_CODESET],loc[VOL_CODESET]) == 0 )
757    {
758        ret = 0;                   /* RETURN:  no iconv needed/possible */
759        goto cleanup;
760    }
761
762    /* get the source codeset */
763    _DtHelpCeXlateStdToOpLocale (
764                      DtLCX_OPER_ICONV1, loc[VOL_LOCALE],
765                      "iso8859_1", &loc[FROM_CODESET]);
766
767    /* get the target codeset */
768    _DtHelpCeXlateStdToOpLocale (
769                      DtLCX_OPER_ICONV1, loc[CUR_LOCALE],
770                      "iso8859_1", &loc[TO_CODESET]);
771
772    if ( isFirst ) {
773       CD = iconv_open( loc[TO_CODESET], loc[FROM_CODESET] );
774       isFirst = 0;
775    }
776    if ( CD == (iconv_t)-1 ) {
777       ret = 0;
778    } else {
779       ret = 1;
780        _converter_( CD, (void *)src, (unsigned long)strlen( src ),
781                 (void **)dest );
782        if ( *dest == NULL )
783            ret = 0;
784    }
785 cleanup:
786    /* free memory */
787    for (i=0; i<NUMSTRS; i++) if (loc[i]) free(loc[i]);
788
789    return ret;
790 } /* count lines */
791
792
793 \f
794 #if DOC
795 ===================================================================
796 $PFUNBEG$:  OpenTmpFile()
797 $1LINER$:  Opens temporary file
798 $DESCRIPT$:
799 opens a temporary file
800 $RETURNS$:
801 $ARGS$:
802 ========================================================$SKIP$=====*/
803 #endif /*DOC*/
804
805 static
806 FILE * OpenTmpFile(
807       _DtHPrOptions * options,
808       char * * ret_tmpFile)
809 {       /*$CODE$*/
810    FILE * fp;
811
812    /* Put in $HOME/.dt/tmp so that if the printer operation is running
813       of a remote system, it can get to the (local) temp file. 
814       This would not be possible if the file were put in /tmp */
815    *ret_tmpFile = _DtHPrCreateTmpFile(TMPFILE_PREFIX,TMPFILE_SUFFIX);
816    if (NULL == *ret_tmpFile) return NULL;     /* RETURN: error */
817
818    fp = fopen(*ret_tmpFile,"w");
819    if (NULL == fp)
820    {
821       fprintf(stderr,_DTGETMESSAGE(PTSET,6,
822                            "%s Error: unable to open temporary file %s\n"),
823                            options->programName, *ret_tmpFile);
824    }
825
826    return fp;
827 } /* open tmp file */
828
829 \f
830 #if DOC
831 ===================================================================
832 $PFUNBEG$:  CountLines()
833 $1LINER$:  counters number of CRs in a string
834 $DESCRIPT$:
835 counters number of CRs in a string
836 $RETURNS$:
837 $ARGS$:
838 ========================================================$SKIP$=====*/
839 #endif /*DOC*/
840
841 static
842 int CountLines(
843       char * str)
844 {       /*$CODE$*/
845    char * substr;
846    int    lineCount = 0;
847    static char * newLine = "\n";
848
849    substr = str;
850    while( _DtHelpCeStrchr(substr,newLine,MB_CUR_MAX,&substr) == 0 )
851    {
852       lineCount++;
853       substr++;
854    }
855    /* return line count to caller */
856    return lineCount;
857 } /* count lines */
858
859
860 \f
861 #if DOC
862 ===================================================================
863 $PFUNBEG$:  AvailContentLines()
864 $1LINER$:  count number of lines available between top of page & footer
865 $DESCRIPT$:
866 Count number of lines available between top of page & footer
867 $RETURNS$:
868 $ARGS$:
869 ========================================================$SKIP$=====*/
870 #endif /*DOC*/
871
872 int AvailContentLines(
873   _DtHPrOptions * options,
874   PrintState *    state,
875   HeadFootFormat * hff)
876 {      /*$DEF$*/
877     return options->rowsTextHeight - 
878                  ( (state->curPageNumber % 2) == 0 
879                      ? hff->evenFooterLineCnt 
880                      : hff->oddFooterLineCnt);  
881 }      /*$END$*/
882
883
884 \f
885 #if DOC
886 ===================================================================
887 $PFUNBEG$:  SectNumStr()
888 $1LINER$:  generates string ver of section number
889 $DESCRIPT$:
890  generates string ver of section number
891 $RETURNS$:
892 $ARGS$:
893 buf:       must be at least 4 * MAXSECTS chars long
894 ========================================================$SKIP$=====*/
895 #endif /*DOC*/
896
897 static
898 char * SectNumStr(
899   int *    sectNums,
900   char *   sectStr,
901   char *          buf)
902 {       /*$CODE$*/
903     char partial[5];
904
905      /* recall: if sectNums[0] == 0, then no section number is defined
906         and none should be printed; try sectStr as alternate. */
907
908     /* generate the section number */
909     buf[0] = EOS;
910     if (sectNums && sectNums[0] != 0 )
911     {
912        int sect;
913        for ( sect = 1; sectNums[sect] != 0; sect++ )
914        {
915           sprintf(partial, "%d.", sectNums[sect]);
916           strcat(buf,partial);
917        } 
918     }
919     else  /* if no section number, take the section string, if avail */
920     {
921        if (sectStr) strcpy(buf,sectStr);
922        else buf[0] = EOS;
923     }
924     return buf;
925 }       /*$END$*/
926
927
928 \f
929 #if DOC
930 ===================================================================
931 $PFUNBEG$:  OutputBlankSpaces()
932 $1LINER$:  Outputs blank Spaces
933 $DESCRIPT$:
934 Outputs blank Spaces
935 $RETURNS$:
936 $ARGS$:
937 topicsFP:  pointer to output stream.  If NULL, do nothing
938 ========================================================$SKIP$=====*/
939 #endif /*DOC*/
940
941 static
942 void OutputBlankSpaces(
943   FILE *  topicsFP,
944   int     spaceCount)
945 {       /*$CODE$*/
946      if (NULL == topicsFP || spaceCount < 0) return;
947      while (spaceCount--) fprintf(topicsFP," "); 
948 } /* $END$ */
949
950 \f
951 #if DOC
952 ===================================================================
953 $PFUNBEG$:  OutputBlankLines()
954 $1LINER$:  Outputs blank lines
955 $DESCRIPT$:
956 Outputs blank lines
957 $RETURNS$:
958 $ARGS$:
959 topicsFP:  pointer to output stream.  If NULL, do nothing
960 ========================================================$SKIP$=====*/
961 #endif /*DOC*/
962
963 static
964 void OutputBlankLines(
965   FILE *  topicsFP,
966   PrintState * state,
967   int     lineCount)
968 {       /*$CODE$*/
969      if (lineCount < 0) return;                 /* RETURN */
970      state->curLineNumber += lineCount;
971      if (NULL == topicsFP) return;              /* RETURN */
972      while (lineCount--) fprintf(topicsFP,"\n"); 
973 } /* $END$ */
974
975 \f
976 #if DOC
977 ===================================================================
978 $PFUNBEG$:  DoStrColsWidth()
979 $1LINER$:  Calculates number of cols used by a string; truncate if needed
980 $DESCRIPT$:
981 Calculates the number of cols used by the string and truncates
982 the string to the specified maxWidth if this is exceeded.
983 $NOTE$:
984 $RETURNS$:
985 The number of columns this string requires for printing.
986 $ARGS$:
987 ========================================================$SKIP$=====*/
988 #endif /*DOC*/
989
990 static
991 int DoStrColsWidth(
992       char *   str,
993       int      maxWidth,
994       Boolean  truncateStr)
995 {       /*$CODE$*/
996    int len;
997    wchar_t * wcstr;
998    int wclen;
999    int width;
1000
1001    /* alloc memory for the wchar_t string */
1002    len = strlen(str);
1003    wcstr = malloc(sizeof(wchar_t) * (len+1));
1004
1005    /* convert str to wchar_t and get width in chars */
1006    mbstowcs(wcstr,str,len+1);
1007    wclen = wcslen(wcstr);
1008
1009    /* get col width of the string and truncate if necessary */
1010    while (    (width = wcswidth(wcstr,wclen+1)) > maxWidth 
1011            && truncateStr == True)
1012       wcstr[--wclen] = EOS;
1013    wcstombs(str,wcstr,len+1);
1014
1015    free(wcstr);
1016
1017    return wclen;
1018 }       /*$END$*/
1019
1020
1021 \f
1022 #if DOC
1023 ===================================================================
1024 $PFUNBEG$:  GenHeadFootFormatArgs()
1025 $1LINER$:  Generates args used by a header/footer format string
1026 $DESCRIPT$:
1027 Generates args used by a header/footer format string
1028 $NOTE$:
1029 $RETURNS$:
1030 $ARGS$:
1031 ========================================================$SKIP$=====*/
1032 #endif /*DOC*/
1033
1034 static
1035 void GenHeadFootFormatArgs(
1036   _DtHPrOptions * options,
1037   PrintState *    state,
1038   Boolean         updateTopicTitle,
1039   char *          sectNumStr,
1040   Boolean         updatePageNum)
1041 {       /*$CODE$*/
1042    char * str;
1043    int    width;
1044    time_t date = 0;
1045    struct tm * pTm;
1046    char   buf[100];
1047
1048     /* get the volume title and its size */
1049     if (NULL == state->hffArgs.volumeTitle)
1050     {
1051        /* get the volume title */
1052        str = NULL;
1053        _DtHelpCeGetVolumeTitle(state->canvasHandle,state->volHandle,&str);
1054        if (NULL == str) str = strdup("");
1055
1056        width = DoStrColsWidth(str,MAXVOLTITLEWIDTH,True);
1057
1058        /* put into state data */
1059        state->hffArgs.volumeTitle = str;
1060        state->hffArgs.volumeTitleColsWidth = width;
1061     }
1062
1063     /* get the volume's date */
1064     if (NULL == state->hffArgs.volumeDate)
1065     {
1066        char * locDocId = NULL;
1067        char * locDateStamp = NULL;
1068        long long_date = 0;
1069
1070        /* locDocId & locDateStamp will point to private memory; do not modify */
1071        _DtHelpCeGetDocStamp(state->volHandle,&locDocId, &locDateStamp);
1072        if (    NULL != locDateStamp
1073             && sscanf(locDateStamp, "%ld", &long_date) != 1 )
1074           locDateStamp = NULL;     /* make invalid */
1075        else
1076          date = (time_t)long_date;
1077
1078        /* if no vol date, try getting from the help volume file */
1079        if ( NULL == locDateStamp )
1080        {
1081           struct stat stats;
1082           date = 0;
1083           if ( stat(options->helpVolume,&stats) == 0 )
1084              date = stats.st_mtime;
1085        }
1086
1087        /* convert the int into a string */
1088        pTm = localtime(&date);
1089        strftime(buf,sizeof(buf),"%x",pTm);
1090
1091        width = DoStrColsWidth(buf,0,False);
1092
1093        /* put into state data */
1094        state->hffArgs.volumeDate = strdup(buf);
1095        state->hffArgs.volumeDateColsWidth = width;
1096        
1097        free(locDocId);
1098        free(locDateStamp);
1099     }
1100
1101     /* get today's date */
1102     if (NULL == state->hffArgs.todaysDate)
1103     {
1104        /* convert the int into a string */
1105        date = time(NULL);
1106        pTm = localtime(&date);
1107        strftime(buf,sizeof(buf),"%x",pTm);
1108
1109        width = DoStrColsWidth(buf,0,False);
1110
1111        /* put into state data */
1112        state->hffArgs.todaysDate = strdup(buf);
1113        state->hffArgs.todaysDateColsWidth = width;
1114     }
1115
1116     /* get the topic title and its size */
1117     if (updateTopicTitle)
1118     {
1119        /* get the topic title */
1120        str = NULL;
1121        _DtHelpCeGetTopicTitle(state->canvasHandle, state->volHandle,
1122                               state->currentLocId, &str);
1123        if (NULL == str) str = strdup("");
1124
1125        width = DoStrColsWidth(str,MAXTOPICTITLEWIDTH,True);
1126
1127        /* put into state data */
1128        if (state->hffArgs.topicTitle) free(state->hffArgs.topicTitle);
1129        state->hffArgs.topicTitle = str;
1130        state->hffArgs.topicTitleColsWidth = width;
1131     }
1132
1133     /* get the size of the section number */
1134     if (sectNumStr)
1135     {
1136        width = DoStrColsWidth(sectNumStr,0,False);
1137        state->hffArgs.sectNumColsWidth = width;
1138     }
1139
1140     /* get the size of the page number */
1141     if (updatePageNum)
1142     {
1143        int num;
1144        for ( width = 1, num = state->curPageNumber;
1145              (num >= 0) && ((num / 10) > 0);
1146              num /= 10, width++ )
1147        { /* do nothing */ }
1148        state->hffArgs.pageNumColsWidth = width;
1149     }
1150 }       /*$END$*/
1151
1152 \f
1153 #if DOC
1154 ===================================================================
1155 $PFUNBEG$:  GenHeadFootFormatStr()
1156 $1LINER$:  Generates a printf-ready format string for the header/footer
1157 $DESCRIPT$:
1158 Generates a printf-ready format string for the header/footer
1159 $NOTE$:
1160 This function generates a string that uses the "%n$" prefix 
1161 supported by printf(3S) to allow a footer/header string to 
1162 ref any argument out of order.  This functionality requires 
1163 that a ref to *every* argument be part of the format string.  
1164 Because not all args may be part of the user-specified format 
1165 string, this function appends the unused strings to the end 
1166 of the format string for purposes of sprintf(), then truncates 
1167 the unused portion before output.
1168 $RETURNS$:
1169 $ARGS$:
1170 ========================================================$SKIP$=====*/
1171 #endif /*DOC*/
1172
1173 static
1174 void GenHeadFootFormatStr(
1175   char *          specStr,
1176   char * *        io_formattedStr,
1177   int *           io_lineCnt)
1178 {       /*$CODE$*/
1179     char * lowLevelFormatStr;
1180     SymValue * sym;
1181     char * substr;
1182     char   unusedSyms[60];
1183
1184     if (NULL == specStr) 
1185     {
1186        *io_formattedStr = NULL;
1187        *io_lineCnt = 0;
1188        return;                        /* RETURN */
1189     }
1190
1191     /*** create the formatted string ***/
1192
1193     /* Make a working copy of the string; I assume that the
1194        values of 'argref' string always be shorter than 'symbol'. */
1195     lowLevelFormatStr = malloc(strlen(specStr) + sizeof(unusedSyms) + 10);
1196     if (NULL == lowLevelFormatStr) return;
1197     strcpy(lowLevelFormatStr,specStr);
1198
1199     /* replace the symbolic names with printf argument refs */
1200     strcpy(unusedSyms,UNUSED_ARGREF);
1201     for ( sym = g_HeadFootSymsList; sym->symbol != NULL; sym++ )
1202     {
1203        Boolean unused = True;
1204
1205        /* look for the symbol string */
1206        while ( (substr = strstr(lowLevelFormatStr,sym->symbol)) != NULL )
1207        {  /* and replace it with the argref */
1208           unused = False;
1209           strcpy(substr, sym->argref);
1210           strcpy(substr + strlen(sym->argref), substr + strlen(sym->symbol));
1211        }
1212        /* if unused, add to unused list */
1213        if (unused) strcat(unusedSyms,sym->argref);
1214     }
1215
1216     /* append unused syms to end of format str */
1217     strcat(lowLevelFormatStr,unusedSyms);
1218
1219     /* store in caller's location */
1220     *io_formattedStr = lowLevelFormatStr;
1221     *io_lineCnt = CountLines(lowLevelFormatStr);
1222 }       /*$END$*/
1223
1224 \f
1225 #if DOC
1226 ===================================================================
1227 $PFUNBEG$:  GenAllHeadFootFormatStrs()
1228 $1LINER$:  Generates printf-ready format strings for all headers/footers
1229 $DESCRIPT$:
1230 Generates printf-ready format strings for all headers/footers
1231 $NOTE$:
1232 $RETURNS$:
1233 $ARGS$:
1234 ========================================================$SKIP$=====*/
1235 #endif /*DOC*/
1236
1237 static
1238 void GenAllHeadFootFormatStrs(
1239   _DtHPrOptions * options,
1240   PrintState *    state)
1241 {       /*$CODE$*/
1242    /* Toc */
1243    GenHeadFootFormatStr(options->tocHF.evenHeader,
1244             &state->tocHFF.formattedEvenHeader, &state->tocHFF.evenHeaderLineCnt);
1245    GenHeadFootFormatStr(options->tocHF.oddHeader,
1246             &state->tocHFF.formattedOddHeader, &state->tocHFF.oddHeaderLineCnt);
1247    GenHeadFootFormatStr(options->tocHF.evenFooter,
1248             &state->tocHFF.formattedEvenFooter, &state->tocHFF.evenFooterLineCnt);
1249    GenHeadFootFormatStr(options->tocHF.oddFooter,
1250             &state->tocHFF.formattedOddFooter, &state->tocHFF.oddFooterLineCnt);
1251    /* Body */
1252    GenHeadFootFormatStr(options->bodyHF.evenHeader,
1253             &state->bodyHFF.formattedEvenHeader, &state->bodyHFF.evenHeaderLineCnt);
1254    GenHeadFootFormatStr(options->bodyHF.oddHeader,
1255             &state->bodyHFF.formattedOddHeader, &state->bodyHFF.oddHeaderLineCnt);
1256    GenHeadFootFormatStr(options->bodyHF.evenFooter,
1257             &state->bodyHFF.formattedEvenFooter, &state->bodyHFF.evenFooterLineCnt);
1258    GenHeadFootFormatStr(options->bodyHF.oddFooter,
1259             &state->bodyHFF.formattedOddFooter, &state->bodyHFF.oddFooterLineCnt);
1260    /* Index */
1261    GenHeadFootFormatStr(options->indexHF.evenHeader,
1262             &state->indexHFF.formattedEvenHeader, &state->indexHFF.evenHeaderLineCnt);
1263    GenHeadFootFormatStr(options->indexHF.oddHeader,
1264             &state->indexHFF.formattedOddHeader, &state->indexHFF.oddHeaderLineCnt);
1265    GenHeadFootFormatStr(options->indexHF.evenFooter,
1266             &state->indexHFF.formattedEvenFooter, &state->indexHFF.evenFooterLineCnt);
1267    GenHeadFootFormatStr(options->indexHF.oddFooter,
1268             &state->indexHFF.formattedOddFooter, &state->indexHFF.oddFooterLineCnt);
1269 }       /*$END$*/
1270
1271
1272 \f
1273 #if DOC
1274 ===================================================================
1275 $PFUNBEG$:  PrintHeadFootStr()
1276 $1LINER$:  Formats and prints the header/footer string
1277 $DESCRIPT$:
1278 Takes a format string, replaces the place holders with actual values,
1279 and prints the output to topicsFP.
1280 $NOTE$:
1281 This function uses the "%n$" prefix supported by printf(3S)
1282 to allow a footer/header string to ref any argument out of
1283 order.  This functionality requires that a ref to *every*
1284 argument be part of the format string.  Because not all args
1285 may be part of the user-specified format string, this function
1286 appends the unused strings to the end of the format string
1287 for purposes of sprintf(), then truncates the unused portion
1288 before output.
1289 $RETURNS$:
1290 number of lines in the header or footer
1291 $ARGS$:
1292 topicsFP:  pointer to output stream.  If NULL, increment
1293            page and reset line number), but do not output;
1294            If not NULL, request new page as well
1295 ========================================================$SKIP$=====*/
1296 #endif /*DOC*/
1297
1298 static
1299 int PrintHeadFootStr(
1300   _DtHPrOptions * options,
1301   FILE *          topicsFP,
1302   PrintState *    state,
1303   char *          formattedStr,
1304   int             lineCnt)
1305 {       /*$CODE$*/
1306     char * newLine = "\n";
1307     int    lastValid = 0;
1308     char   sectNumStr[MAXSECTS * 4 + 5];        /* $SECTNUM */
1309     char   buf[3000];
1310
1311     if (NULL == formattedStr) return 0;        /* RETURN */
1312     if (NULL == topicsFP) return lineCnt;      /* RETURN */
1313
1314     /*** generate dynamic data ***/
1315
1316     /* get the section number */
1317     SectNumStr(state->sectNums,state->sectStr,sectNumStr);
1318
1319     /* update args; FIX: impove efficiency by processing topic title only when needed */
1320     GenHeadFootFormatArgs(options,state,True,sectNumStr,True);
1321
1322     /* guidelines on string size and construction: 
1323        The objective is to allow one set of headers & footers to apply
1324        to many different volumes and topics.  This is made possible by
1325        allowing for fixed length strings.  To get fixed length strings,
1326        the header/footer spec should include not only the string but
1327        also the fill for that string.
1328
1329        The fill size is calculated based on the following widths:
1330          volTitle   : 50 printing chars    e.g. "title        "
1331          topicTitle : 50 printing chars    e.g. "title        "
1332          sectNumStr :  8 printing chars    e.g. "  3.10.4"
1333          pageNum    :  3 printing chars    e.g. "  3"
1334          volDate    : constant by locale--no fill needed e.g. "Mon, Jul 4, 1988"
1335          dateStr    : constant by locale--no fill needed e.g. "Mon, Jul 4, 1988"
1336     */
1337
1338     /*** generate the str ***/
1339     /* IMPT: the order of these arguments MUST match the argument numbers
1340        given in the definition of the g_HeadFootSymsList variable. */
1341     /* print the first set */
1342     sprintf(buf, formattedStr,
1343                state->hffArgs.todaysDate,state->hffArgs.volumeTitle,
1344                state->hffArgs.topicTitle, (int) state->curPageNumber,
1345                state->hffArgs.volumeDate, sectNumStr,
1346                (int) options->colsAdjLeftMargin,(int) ' ' );
1347
1348     /* move the format string for the second set into new memory */
1349     formattedStr = strdup(buf);
1350     if(NULL == formattedStr) return 0;            /* RETURN */
1351
1352     /* print the second set */
1353     sprintf(buf, formattedStr,
1354         MAXVOLTITLEWIDTH - state->hffArgs.volumeTitleColsWidth, (int) ' ',
1355         MAXTOPICTITLEWIDTH - state->hffArgs.topicTitleColsWidth, (int) ' ',
1356         MAXPAGENUMWIDTH - state->hffArgs.pageNumColsWidth, (int) ' ',
1357         MAXSECTNUMWIDTH - state->hffArgs.sectNumColsWidth, (int) ' ',
1358         &lastValid);
1359     buf[lastValid] = EOS;       /* truncate unused args */
1360     free(formattedStr);
1361
1362     /*** output the str ***/
1363     fprintf(topicsFP, "%s", buf);
1364
1365     return lineCnt;
1366 }       /*$END$*/
1367
1368
1369 \f
1370 #if DOC
1371 ===================================================================
1372 $PFUNBEG$:  PrintFooter()
1373 $1LINER$:  Print footer and use right form for odd/even pages
1374 $DESCRIPT$:
1375 Print footer and use right form for odd/even pages
1376 $RETURNS$:
1377 $ARGS$:
1378 topicsFP:  pointer to output stream.  If NULL, increment
1379            lines), but do not output;
1380 ========================================================$SKIP$=====*/
1381 #endif /*DOC*/
1382
1383 static
1384 void PrintFooter(
1385   _DtHPrOptions * options,
1386   FILE *          topicsFP,
1387   PrintState *    state,
1388   _DtHPrHeadFoot * headFootInfo,
1389   HeadFootFormat * headFootFormatting)
1390 {       /*$CODE$*/
1391    /* fill to bottom of page */
1392    OutputBlankLines(topicsFP,state,
1393         (options->rowsTextHeight - state->curLineNumber) - 
1394               ( (state->curPageNumber % 2) == 0 
1395                   ? headFootFormatting->evenFooterLineCnt 
1396                   : headFootFormatting->oddFooterLineCnt));  
1397
1398    if ( (state->curPageNumber % 2) == 0)  /* Even page */
1399        state->curLineNumber += PrintHeadFootStr(options,topicsFP,state,
1400                                  headFootFormatting->formattedEvenFooter,
1401                                  headFootFormatting->evenFooterLineCnt);
1402    else  /* odd page */
1403        state->curLineNumber += PrintHeadFootStr(options,topicsFP,state,
1404                                  headFootFormatting->formattedOddFooter,
1405                                  headFootFormatting->oddFooterLineCnt);
1406 }  /*$END$*/
1407
1408
1409 \f
1410 #if DOC
1411 ===================================================================
1412 $PFUNBEG$:  PrintHeader()
1413 $1LINER$:  Print footer and use right form for odd/even pages
1414 $DESCRIPT$:
1415 Print footer and use right form for odd/even pages
1416 $RETURNS$:
1417 $ARGS$:
1418 topicsFP:  pointer to output stream.  If NULL, increment
1419            lines), but do not output;
1420 ========================================================$SKIP$=====*/
1421 #endif /*DOC*/
1422
1423 static
1424 void PrintHeader(
1425   _DtHPrOptions * options,
1426   FILE *          topicsFP,
1427   PrintState *    state,
1428   _DtHPrHeadFoot * headFootInfo,
1429   HeadFootFormat * headFootFormatting)
1430 {       /*$CODE$*/
1431
1432    if ( (state->curPageNumber % 2) == 0)  /* Even page */
1433        state->curLineNumber += PrintHeadFootStr(options,topicsFP,state,
1434                                  headFootFormatting->formattedEvenHeader,
1435                                  headFootFormatting->evenHeaderLineCnt);
1436    else
1437        state->curLineNumber += PrintHeadFootStr(options,topicsFP,state,
1438                                  headFootFormatting->formattedOddHeader,
1439                                  headFootFormatting->oddHeaderLineCnt);
1440 }  /*$END$*/
1441
1442 \f
1443 #if DOC
1444 ===================================================================
1445 $PFUNBEG$:  NewPage()
1446 $1LINER$:  Outputs paper form feed, increments page count, resets line cnt
1447 $DESCRIPT$:
1448 Outputs paper form feed, increments page count, resets line cnt
1449 $RETURNS$:
1450 $ARGS$:
1451 topicsFP:  pointer to output stream.  If NULL, increment
1452            page and reset line number), but do not output;
1453            If not NULL, request new page as well
1454 ========================================================$SKIP$=====*/
1455 #endif /*DOC*/
1456
1457 static
1458 void NewPage(
1459   _DtHPrOptions * options,
1460   FILE *          topicsFP,
1461   PrintState *    state,
1462   Boolean         advancePage)
1463 {       /*$CODE$*/
1464     /* start new page: form feed */
1465     if (topicsFP) 
1466     {
1467        if (advancePage) fprintf(topicsFP,"\f\n");
1468
1469        /* print top margin but don't add it to line count because the rowsTextHeight
1470           value is calculated post-topMargin */
1471        OutputBlankLines(topicsFP,state,options->rowsAdjTopMargin);
1472     }
1473
1474     /* adjust page and line numbers */
1475     if (advancePage) state->curPageNumber++;
1476     state->curLineNumber = 1;
1477 }       /*$END$*/
1478
1479 \f
1480 #if DOC
1481 ===================================================================
1482 $PFUNBEG$:  ProcessOneTopic()
1483 $1LINER$:  Recovers and formats help text for one topic
1484 $DESCRIPT$:
1485 Recovers and formats help text for one topic
1486 $RETURNS$:
1487  0:  The number of lines output.
1488 -2:  could not get topic information
1489 $ARGS$:
1490 topicsFP:  pointer to output stream.  If NULL, process topic
1491            (e.g. count lines and inc page numbers), but do not output;
1492            If not NULL, output lines and page headers
1493 ========================================================$SKIP$=====*/
1494 #endif /*DOC*/
1495
1496 static
1497 int ProcessOneTopic(
1498   _DtHPrOptions * options,
1499   FILE *          topicsFP,
1500   PrintState *    state,
1501   Boolean         printHeaderFooter)
1502 {       /*$CODE$*/
1503    char * * helpList = NULL;
1504    char * * ptrToLst;
1505    int      lineCount;
1506    int      availLines;
1507    char sectNumStr[MAXSECTS * 4 + 5];
1508
1509    /* retrieve the text (but not the hyperlinks) from the volume */
1510    if ( _DtHelpTermGetTopicData(state->canvasHandle, state->volHandle,
1511                        state->currentLocId, &helpList, NULL) != 0 )
1512    {
1513       fprintf(stderr,_DTGETMESSAGE(PTSET,5,
1514                            "%s Error: unable to get topic information:\n"
1515                            "volume %s, locationId %s\n"),
1516                      options->programName, options->helpVolume, options->locationId);
1517       return -2;                        /* RETURN error */
1518    }
1519
1520    /* output topic section number */
1521    /* this operates on the assumption that topic title is first line */
1522    ptrToLst = helpList;
1523    SectNumStr(state->sectNums,state->sectStr,sectNumStr);
1524    if (sectNumStr[0] != EOS)  
1525    {
1526       /* NOTE: if allow the sect num string to be resource-defined,
1527          then count the number of \n chars in it; don't assume how many. */
1528       if (topicsFP) fprintf(topicsFP,"\n");
1529       OutputBlankSpaces(topicsFP,options->colsAdjLeftMargin);
1530       if (topicsFP) fprintf(topicsFP,"%s ", sectNumStr);
1531       state->curLineNumber++;
1532
1533       /* and put the title (must be on first two lines) on the same line */
1534       if (   (*helpList && (*helpList)[0] != EOS)
1535           || (*(++helpList) && (*helpList)[0] != EOS) )
1536       {
1537         if (topicsFP) {
1538            char *_p;
1539            int ret;
1540
1541            ret = IconvBuffer( state->volHandle, *helpList, &_p );
1542            if ( ret ) {
1543                fprintf(topicsFP,"%s\n", _p); /* output title */
1544            } else {
1545                fprintf(topicsFP,"%s\n", *helpList); /* output title */
1546            }
1547         }
1548          state->curLineNumber += CountLines(*helpList) + 1; /* 1=the known \n */
1549          helpList++;
1550       }
1551    }
1552
1553    /* calc number of available lines from top of page to footer */
1554    availLines = AvailContentLines(options,state,&state->bodyHFF);
1555
1556    /* cycle through the lines; add new pages where necessary */
1557    for ( ; *helpList != NULL; helpList++ )
1558    {
1559       /* NOTE: it's impt to calc the final line before outputting it,
1560          as the line may contain embedded newlines */
1561       int linesCnt = CountLines(*helpList) + 1;     /* 1=the known \n */ 
1562
1563       /* calc what line that will leave us on */
1564       state->curLineNumber += linesCnt;
1565  
1566       /* if at the end of a page, print footer, eject, and print header */
1567       if (state->curLineNumber >= availLines)
1568       {
1569          /* output any filler blank lines */
1570          OutputBlankLines(topicsFP,state,
1571                    availLines - (state->curLineNumber - linesCnt) );
1572          if (printHeaderFooter) 
1573             PrintFooter(options,topicsFP,state,&options->bodyHF,&state->bodyHFF);
1574          NewPage(options,topicsFP,state,True);
1575          if (printHeaderFooter) 
1576             PrintHeader(options,topicsFP,state,&options->bodyHF,&state->bodyHFF);
1577
1578          /* recalc the line we're on */
1579          state->curLineNumber += linesCnt;
1580
1581          /* calc number of available lines from top of page to footer */
1582          availLines = AvailContentLines(options,state,&state->bodyHFF);
1583       }
1584
1585       /* output the lines */
1586       if (topicsFP) 
1587       {
1588          OutputBlankSpaces(topicsFP,options->colsAdjLeftMargin);
1589         {
1590             char *_p;
1591             int ret;
1592
1593             ret = IconvBuffer( state->volHandle, *helpList, &_p );
1594             if ( ret ) {
1595                fprintf(topicsFP,"%s\n", _p );
1596             } else {
1597                fprintf(topicsFP,"%s\n",*helpList);
1598             }
1599         }
1600       }
1601    }
1602
1603    /* free the memory of helpList */
1604    _DtHelpFreeTopicData(ptrToLst,NULL);
1605
1606    return 0;
1607 }       /*$END$*/
1608
1609
1610 \f
1611 #if DOC
1612 ===================================================================
1613 $PFUNBEG$:  ProcessSubTopics()
1614 $1LINER$:  Recovers and formats help text for current & sub topics
1615 $DESCRIPT$:
1616      ond none should be printed. */
1617
1618 Subsections are numbered according to the legal method (e.g. 1.5.4.3)
1619
1620 Recovers and formats help text for current & sub topics
1621 $RETURNS$:
1622  0:  success
1623 -1:  could not create or open a temp file
1624 -2:  could not get help topic info
1625 INHIBIT_OUTPUT:  stop outputting children; we printed all we need to
1626 $ARGS$:
1627 level:   may range from 1 to (MAXSECTS-1)
1628 ========================================================$SKIP$=====*/
1629 #endif /*DOC*/
1630 static
1631 int ProcessSubTopics(
1632   _DtHPrOptions * options,
1633   FILE *          topicsFP,
1634   Toc *           toc,
1635   int             level,
1636   PrintState *    state)
1637 {       /*$CODE$*/
1638    char * * children = NULL;
1639    int      ret = 0;
1640    int      firstSubSectNum;
1641    int      subSectNumIndex;
1642    FILE *   curFP;
1643
1644 #define INHIBIT_OUTPUT (-3)
1645    /* remember, don't turn inhibitOutput on if there's no match */
1646    if ( _DtHelpCeStrCaseCmp(state->outputFromLocId,state->currentLocId) == 0 )
1647    {
1648       int curPos;
1649
1650       /* toggle the flag and set ret value */
1651       state->inhibitOutput = False;
1652       ret = INHIBIT_OUTPUT;     /* stop printing after this topic & its children */
1653
1654       curPos = state->curLineNumber;  /* save off the current line position */
1655
1656       /* start new page but don't eject; output header;
1657          output right number of blank lines to get to where topic starts */
1658       NewPage(options,topicsFP,state,False);
1659       PrintHeader(options,topicsFP,state,&options->bodyHF,&state->bodyHFF);
1660       OutputBlankLines(topicsFP,state,curPos - state->curLineNumber);
1661    }
1662
1663    /* init file ptr */
1664    curFP = (state->inhibitOutput ? (FILE *) NULL : topicsFP);
1665
1666    /* init according to level */
1667    if(level == 0)   /* the top topic? */
1668    {  /* put top topic at same level as children */
1669       firstSubSectNum = 2;
1670       subSectNumIndex = 1;
1671       state->level = 1;
1672       /* no prior instance of ProcessSubTopics() set this */
1673       state->sectNums[subSectNumIndex] = 1;  
1674    }
1675    else /* not the top topic */
1676    {
1677       firstSubSectNum = 1;
1678       subSectNumIndex = level;
1679       state->level = level;
1680    }
1681
1682    /* add this topic to the toc */
1683    TocNewEntry(toc,state->currentLocId,state->curPageNumber,
1684                            state->level,state->sectNums,state->sectStr);
1685
1686    /* retrieve and possibly output the current topic */
1687    ProcessOneTopic(options,curFP,state,True);
1688
1689    /* output the sub topics */
1690    if (_DtHelpCeGetTopicChildren(state->volHandle,
1691                             state->currentLocId,&children) > 0)
1692    {
1693       char * * topic;
1694       Boolean  initSubSect = True;
1695
1696       /* cycle through the topics and output each one */
1697       for (topic = children; *topic != NULL && (*topic)[0]; topic++ )
1698       {
1699          /* start level 0 & 1 topics on a new page */
1700          if ( subSectNumIndex < 2 )
1701          {
1702             PrintFooter(options,curFP,state,&options->bodyHF,&state->bodyHFF);
1703             NewPage(options,curFP,state,True);
1704             PrintHeader(options,curFP,state,&options->bodyHF,&state->bodyHFF);
1705          }
1706
1707          /* avoid orphans (i.e. require more than 4 lines left on page) */
1708          if ( (AvailContentLines(options,state,&state->bodyHFF) 
1709                                                - state->curLineNumber) < 4 )
1710          {
1711              PrintFooter(options,curFP,state,&options->bodyHF,&state->bodyHFF);
1712              NewPage(options,curFP,state,True);
1713              PrintHeader(options,curFP,state,&options->bodyHF,&state->bodyHFF);
1714          }
1715
1716          /* init new sub sect, if haven't yet done so */
1717          if (initSubSect)
1718          {
1719             /* start new subsection; -1: make up for ++ later */
1720             state->sectNums[subSectNumIndex] = firstSubSectNum - 1;
1721             initSubSect = False;
1722          }
1723
1724          /* make this the current topic */
1725          state->currentLocId = *topic;
1726          /* use subSectNumIndex, not level, so that the top topic and its
1727             immediate children are at the same level */
1728          /* inc the sect num before the call so that the
1729             sect used below in the INHIBIT_OUTPUT wrapup is correct. */
1730          /* pass in topicsFP, not curFP */
1731          state->sectNums[subSectNumIndex]++;     /* next subsection */
1732          /* even though ProcessSubTopics() can return 'INHIBIT_OUTPUT',
1733             don't stop processing, as this would cause the Toc to
1734             be incomplete. */
1735          ProcessSubTopics(options,topicsFP,toc, subSectNumIndex+1,state);
1736          /* ProcessSubTopics(options,topicsFP,toc, state->level+1,state); */
1737       }
1738    }
1739
1740    /* if this is the last topic to be output, then finish up */
1741    if (ret == INHIBIT_OUTPUT)
1742    {
1743       /* save off line num */
1744       int curLine = state->curLineNumber;
1745
1746       /* output footer */
1747       PrintFooter(options,curFP,state,&options->bodyHF,&state->bodyHFF);
1748       state->inhibitOutput = True;      /* inhibit again */
1749
1750       /* restore line num */
1751       state->curLineNumber = curLine;
1752    }
1753
1754    /* if processing subtopics, reset subsection number */
1755    if(subSectNumIndex > 1) state->sectNums[subSectNumIndex] = 0;
1756    state->level = level;        /* state->level was modified by the FOR loop */
1757
1758    free(children);
1759    return ret;
1760 }
1761
1762 \f
1763 #if DOC
1764 ===================================================================
1765 $PFUNBEG$:  ProcessFrontMatter()
1766 $1LINER$:  Process the help volume to generate a file of front matter
1767 $DESCRIPT$:
1768 Process the help volume to generate a file of front matter
1769 $RETURNS$:
1770  0:  success
1771 -1:  could not create or open a temp file
1772 -2:  could not get help topic info
1773 $ARGS$:
1774 ========================================================$SKIP$=====*/
1775 #endif /*DOC*/
1776
1777 static
1778 int ProcessFrontMatter(
1779   _DtHPrOptions * options,
1780   char * *        ret_resultsFile,
1781    PrintState *   state)
1782 {       /*$CODE$*/
1783    FILE *  fp;
1784    int     ret;
1785
1786    /* open file */
1787    fp = OpenTmpFile(options,ret_resultsFile);
1788    if (NULL == fp) return -1;               /* RETURN: error */
1789
1790    /*** process text ***/
1791    /* setup the state */
1792    state->inhibitOutput = False;
1793    state->curPageNumber = 1;
1794    state->curLineNumber = 1;
1795    state->sectNums[0] = 0;      /* inhibit section num printing */
1796    state->sectStr = NULL;       /* inhibit section string printing */
1797
1798    OutputBlankLines(fp,state, options->rowsTextHeight / 3);
1799
1800    /* NOTE: the code below causes the memory allocated for currentLocId
1801       to be lost.  I didn't fix this to save a few bytes of code space. */
1802
1803    /* generate volume title */
1804    state->currentLocId = strdup("_TITLE");
1805    state->outputFromLocId = state->currentLocId;
1806    ret = ProcessOneTopic(options,fp,state,False);
1807    /*free(state->currentLocId);*/
1808
1809    NewPage(options,fp,state,True);
1810
1811    /* generate abstract */
1812    state->currentLocId = strdup("_ABSTRACT");
1813    state->outputFromLocId = state->currentLocId;
1814    ret = ProcessOneTopic(options,fp,state,False);
1815    /*free(state->currentLocId);*/ 
1816
1817    /* Make a space between abstract and copyright */
1818    if ( AvailContentLines(options,state,&state->indexHFF) > 3 )
1819       OutputBlankLines(fp,state, 3);
1820    else
1821       NewPage(options,fp,state,True);
1822
1823    /* generate copyright */
1824    state->currentLocId = strdup("_COPYRIGHT");
1825    state->outputFromLocId = state->currentLocId;
1826    ret = ProcessOneTopic(options,fp,state,False);
1827    /*free(state->currentLocId);*/
1828    /*state->currentLocId = NULL;*/
1829
1830    if (fp) fclose(fp);
1831
1832    return ret;
1833 }       /*$END$*/
1834
1835
1836 \f
1837 #if DOC
1838 ===================================================================
1839 $PFUNBEG$:  ProcessIndex()
1840 $1LINER$:  Process the TOC data to generate an index file
1841 $DESCRIPT$:
1842 Process the TOC data to generate an index file
1843 $WARNING$:
1844 This function uses state->pageNumber+1 as the first page
1845 of the index.
1846 $RETURNS$:
1847  0:  success
1848 -1:  could not create or open a temp file
1849 -2:  could not get help topic info
1850 $ARGS$:
1851 ========================================================$SKIP$=====*/
1852 #endif /*DOC*/
1853
1854 static
1855 int ProcessIndex(
1856   _DtHPrOptions * options,
1857   char * *        ret_resultsFile,
1858    PrintState *   state,
1859    Toc *          toc)
1860 {       /*$CODE$*/
1861    FILE *   fp;
1862    int      ret;
1863    int      availLines;
1864    char * * indexEntriesList;
1865    char *   entryStr;
1866    int      entriesCnt;
1867
1868    *ret_resultsFile = NULL;
1869
1870    /* Get the index.  Recall that the array and strings pointed to by
1871       the indexEntriesList is owned by the open volume */
1872    entriesCnt = _DtHelpCeGetKeywordList(state->volHandle,&indexEntriesList);
1873    if (entriesCnt <= 0) return 0;
1874
1875    /* open file */
1876    fp = OpenTmpFile(options,ret_resultsFile);
1877    if (NULL == fp) return -1;               /* RETURN: error */
1878
1879    /*** process index ***/
1880
1881    /* setup the state */
1882    state->curPageNumber++;         /* inc page number from prev. value */
1883    state->inhibitOutput = False;
1884    state->curLineNumber = 1;
1885    state->sectNums[0] = 0;      /* inhibit section num printing */
1886    state->sectStr = _DTGETMESSAGE(PTSET,10,"Index");  /* $SECTNUM name */
1887
1888    /* add an index entry to the TOC */
1889    TocNewEntry(toc,_DTGETMESSAGE(PTSET,30,"__GENERATED-INDEX"),
1890                  state->curPageNumber, 1,state->sectNums,state->sectStr);
1891
1892    /* start new page (but don't eject) and output header */
1893    NewPage(options,fp,state,False);
1894    PrintHeader(options,fp,state,&options->indexHF,&state->indexHFF);
1895
1896    /* calc number of available lines from top of page to footer */
1897    availLines = AvailContentLines(options,state,&state->indexHFF);
1898
1899    /*** loop through the entries ***/
1900    for ( entryStr = *indexEntriesList++;
1901          NULL != entryStr;
1902          entryStr = *indexEntriesList++ )
1903    {
1904       char * * topicIdsList;
1905       int      topicCnt;
1906       int      i;
1907       TocEntry * tocEntry;
1908
1909       /* get the topics associated with this entry */
1910       /* topicIdsList is set but not allocated & need not be freed */
1911       topicCnt = _DtHelpCeFindKeyword(state->volHandle,
1912                                entryStr,&topicIdsList);
1913       if (topicCnt <= 0) continue;             /* CONTINUE */
1914
1915       /*** if only one topic, just put page number after the entry ***/
1916       if (topicCnt == 1)
1917       {
1918          /* find the toc entry of the topic */
1919          ret = TocFindSortedEntry(toc, *topicIdsList, NULL, &tocEntry);
1920          if (ret < 0) continue;                 /* CONTINUE */
1921
1922          /* output the entry */
1923          OutputBlankSpaces(fp,options->colsAdjLeftMargin);
1924          {
1925              char *_p;
1926              int ret;
1927
1928              ret = IconvBuffer( state->volHandle, entryStr, &_p );
1929              if ( ret ) {
1930                  fprintf(fp, _DTGETMESSAGE(PTSET,20,"%s, %d\n"), 
1931                          _p, tocEntry->pageNumber); 
1932              } else {
1933                  fprintf(fp, _DTGETMESSAGE(PTSET,20,"%s, %d\n"), 
1934                          entryStr, tocEntry->pageNumber); 
1935              }
1936          }
1937          state->curLineNumber++;
1938
1939          /* if at the end of a page, print footer, eject, and print header */
1940          if (state->curLineNumber >= availLines)
1941          {
1942              PrintFooter(options,fp,state,&options->indexHF,&state->indexHFF);
1943              NewPage(options,fp,state,True);
1944              PrintHeader(options,fp,state,&options->indexHF,&state->indexHFF);
1945              /* calc number of available lines from top of page to footer */
1946              availLines = AvailContentLines(options,state,&state->indexHFF);
1947          }
1948
1949          continue;                              /* CONTINUE */
1950       }
1951
1952       /*** if more than one topic, list topic title & page number under the entry ***/
1953
1954       /* do we need a new page to start this entry? */
1955       if ( (availLines - state->curLineNumber) < 3 )
1956       {
1957           PrintFooter(options,fp,state,&options->indexHF,&state->indexHFF);
1958           NewPage(options,fp,state,True);
1959           PrintHeader(options,fp,state,&options->indexHF,&state->indexHFF);
1960           /* calc number of available lines from top of page to footer */
1961           availLines = AvailContentLines(options,state,&state->indexHFF);
1962       }
1963
1964       OutputBlankSpaces(fp,options->colsAdjLeftMargin);
1965       {
1966           char *_p;
1967           int  ret;
1968
1969           ret = IconvBuffer( state->volHandle, entryStr, &_p );
1970           if ( ret ) {
1971               fprintf(fp,"%s\n", _p ); 
1972           } else {
1973               fprintf(fp,"%s\n", entryStr); 
1974           }
1975       }
1976       state->curLineNumber++;
1977
1978       /* for all topics in an index entry */
1979       for ( i=0; 
1980             i<topicCnt; 
1981             i++, topicIdsList++ )
1982       {
1983          char * topicTitle;
1984
1985          if (NULL == *topicIdsList) continue;   /* CONTINUE */
1986
1987          /* find the toc entry of the topic */
1988          ret = TocFindSortedEntry(toc, *topicIdsList, NULL, &tocEntry);
1989          if (ret < 0) continue;                 /* CONTINUE */
1990
1991          /* get the topic title */
1992          _DtHelpCeGetTopicTitle(state->canvasHandle, state->volHandle,
1993                               tocEntry->locationId, &topicTitle);
1994          if (NULL == topicTitle) 
1995             topicTitle = _DTGETMESSAGE(PTSET,31,EMPTY_STR);
1996
1997          /* output the entry */
1998          OutputBlankSpaces(fp,options->colsAdjLeftMargin);
1999          {
2000              char *_p;
2001              int ret;
2002
2003              ret = IconvBuffer( state->volHandle, topicTitle, &_p );
2004              if ( ret ) {
2005                  fprintf(fp,_DTGETMESSAGE(PTSET,21,"     %s, %d\n"), 
2006                       _p, tocEntry->pageNumber); 
2007              } else {
2008                  fprintf(fp,_DTGETMESSAGE(PTSET,21,"     %s, %d\n"), 
2009                       topicTitle, tocEntry->pageNumber); 
2010              }
2011          }
2012          state->curLineNumber++;
2013
2014          /* if at the end of a page, print footer, eject, and print header */
2015          if (state->curLineNumber >= availLines)
2016          {
2017              PrintFooter(options,fp,state,&options->indexHF,&state->indexHFF);
2018              NewPage(options,fp,state,True);
2019              PrintHeader(options,fp,state,&options->indexHF,&state->indexHFF);
2020              /* calc number of available lines from top of page to footer */
2021              availLines = AvailContentLines(options,state,&state->indexHFF);
2022          }
2023       }  /* for all topics in an index entry */
2024
2025    }  /* for all index entries */
2026
2027    /* output footer */
2028    PrintFooter(options,fp,state,&options->indexHF,&state->indexHFF);
2029
2030    if (fp) fclose(fp);
2031
2032    return 0;
2033 }       /*$END$*/
2034
2035
2036 \f
2037 #if DOC
2038 ===================================================================
2039 $PFUNBEG$:  ProcessToc()
2040 $1LINER$:  Process the TOC data to generate a toc file
2041 $DESCRIPT$:
2042 Process the TOC data to generate a toc file
2043 $RETURNS$:
2044  0:  success
2045 -1:  could not create or open a temp file
2046 -2:  could not get help topic info
2047 $ARGS$:
2048 ========================================================$SKIP$=====*/
2049 #endif /*DOC*/
2050
2051 static
2052 int ProcessToc(
2053   _DtHPrOptions * options,
2054   char * *        ret_resultsFile,
2055    PrintState *   state,
2056    Toc *          toc)
2057 {       /*$CODE$*/
2058    FILE *  fp;
2059    int     availLines;
2060    TocEntry * entry;
2061
2062    /* open file */
2063    fp = OpenTmpFile(options,ret_resultsFile);
2064    if (NULL == fp) return -1;               /* RETURN: error */
2065
2066    /*** process toc ***/
2067
2068    /* setup the state */
2069    state->inhibitOutput = False;
2070    state->curPageNumber = 1;
2071    state->curLineNumber = 1;
2072    state->sectNums[0] = 0;      /* inhibit section num printing */
2073    state->sectStr = _DTGETMESSAGE(PTSET,11,"Table of Contents"); /* $SECTNUM name */
2074
2075    /* start new page and output header */
2076    NewPage(options,fp,state,False);
2077    PrintHeader(options,fp,state,&options->tocHF,&state->tocHFF);
2078
2079    /* calc number of available lines from top of page to footer */
2080    availLines = AvailContentLines(options,state,&state->tocHFF);
2081
2082    /* walk through the toc; output the label, title string, and page num */
2083    for ( entry = TocNextEntry(toc,NULL);
2084          NULL != entry;
2085          entry = TocNextEntry(toc,entry) )
2086    {
2087        char   sectNumStr[MAXSECTS * 4 + 5];     /* $SECTNUM */
2088        char * title = NULL;
2089        wchar_t * wctitle;
2090        int    lhsWidth;
2091        int    titlelen;
2092        int    fillerChar;
2093        int    blanksCnt;
2094
2095        /* get the data to print */
2096        SectNumStr(entry->sectNums,entry->sectStr,sectNumStr);
2097        _DtHelpCeGetTopicTitle(state->canvasHandle, state->volHandle,
2098                               entry->locationId, &title);
2099        if (NULL == title) title = EMPTY_STR;
2100
2101        /* set max length of title and calc num blanks needed */
2102        lhsWidth = strlen(sectNumStr) + 1;   /* 1=blank after sect num */
2103        titlelen = strlen(title);
2104        wctitle = malloc(sizeof(wchar_t) * (titlelen+1));
2105        /* truncate the title using wchar_t */
2106        if(wctitle)
2107        {
2108           /* convert title to wchar_t and get num chars */
2109           mbstowcs(wctitle,title,titlelen+1);
2110           titlelen = wcslen(wctitle);
2111           /* truncate the title to fit on the line */
2112           if (titlelen > options->colsTextWidth - lhsWidth)
2113           {
2114              wctitle[options->colsTextWidth - lhsWidth - 1] = EOS;
2115              titlelen = wcslen(wctitle);
2116           }
2117           wcstombs(title,wctitle,titlelen+1);
2118           free(wctitle);
2119        }
2120        /* truncate the title assuming single-byte */
2121        else if (titlelen > options->colsTextWidth - lhsWidth)
2122        { 
2123           title[options->colsTextWidth - lhsWidth - 1] = EOS;
2124           titlelen = strlen(title);
2125        }
2126        lhsWidth += titlelen;
2127
2128        /* add a blank line before a major section */
2129        if ( 0 == entry->sectNums[2] )
2130        {
2131           fprintf(fp,"\n");
2132           state->curLineNumber++;
2133
2134           /* do we need a new page to start this section? */
2135           if ( (availLines - state->curLineNumber) < 3 )
2136           {
2137               PrintFooter(options,fp,state,&options->tocHF,&state->tocHFF);
2138               NewPage(options,fp,state,True);
2139               PrintHeader(options,fp,state,&options->tocHF,&state->tocHFF);
2140               /* calc number of available lines from top of page to footer */
2141               availLines = AvailContentLines(options,state,&state->tocHFF);
2142           }
2143        }
2144
2145        /* output the beginning of the line */
2146        if ( sectNumStr[0] == EOS )      /* no section number */
2147        {
2148           /* space between title and page num */
2149           /* 1: no sect num so no space betw sect num & title */
2150           /* -3: reserve 3 spaces for the page number */
2151           blanksCnt = ((options->colsTextWidth - lhsWidth) - 3) + 1;
2152
2153           {
2154               char *_p;
2155               int ret;
2156
2157               ret = IconvBuffer( state->volHandle, title, &_p );
2158               if ( ret ) {
2159                   fprintf(fp,"%*c%s", options->colsAdjLeftMargin, ' ', _p );
2160               } else {
2161                   fprintf(fp,"%*c%s", options->colsAdjLeftMargin, ' ', title);
2162               }
2163           }
2164        }
2165
2166        else     /* valid section number */
2167        {
2168           /* space between title and page num */
2169           /* -3: reserve 3 spaces for the page number */
2170           blanksCnt = (options->colsTextWidth - lhsWidth) - 3;
2171
2172           {
2173               char *_p;
2174               int ret;
2175
2176               ret = IconvBuffer( state->volHandle, title, &_p );
2177               if ( ret ) {
2178                 fprintf(fp,"%*c%s %s",
2179                       options->colsAdjLeftMargin, ' ', sectNumStr, _p );
2180               } else {
2181                 fprintf(fp,"%*c%s %s",
2182                       options->colsAdjLeftMargin, ' ', sectNumStr, title);
2183               }
2184           }
2185        }
2186
2187        /* output the filler and page number */
2188        if ( (blanksCnt % 2) != 0) { fputc(' ',fp); blanksCnt--; }
2189        for( ;blanksCnt > 0; blanksCnt -= 2) fprintf(fp," .");
2190        fprintf(fp,"%3d\n", entry->pageNumber);
2191
2192        state->curLineNumber++;
2193  
2194        /* if at the end of a page, print footer, eject, and print header */
2195        if (state->curLineNumber >= availLines)
2196        {
2197            PrintFooter(options,fp,state,&options->tocHF,&state->tocHFF);
2198            NewPage(options,fp,state,True);
2199            PrintHeader(options,fp,state,&options->tocHF,&state->tocHFF);
2200            /* calc number of available lines from top of page to footer */
2201            availLines = AvailContentLines(options,state,&state->tocHFF);
2202        }
2203    }  /* for all entries */
2204
2205    /* output footer */
2206    PrintFooter(options,fp,state,&options->tocHF,&state->tocHFF);
2207
2208    if (fp) fclose(fp);
2209
2210    return 0;
2211 }       /*$END$*/
2212
2213 \f
2214 #if DOC
2215 ===================================================================
2216 $PFUNBEG$:  ProcessTopics()
2217 $1LINER$:  Process the help volume to generate a topics file
2218 $DESCRIPT$:
2219 Process the TOC data to generate a toc file
2220 $RETURNS$:
2221  0:  success
2222 -1:  could not create or open a temp file
2223 -2:  could not get help topic info
2224 $ARGS$:
2225 outputFromLocId:  output of topics occurs for this topic and all its
2226                   subtopics.  This topic Id is strcasecmp()ed against the
2227                   current Id to determine a match.  If NULL, the volume
2228                   top topic is used.
2229 inhibitTopicsOutput:    if true, output only starts when outputFromLocId is found
2230
2231 outputFromLocId  inhibitOutput   action
2232 ---------------  -------------   -------------------------------------
2233 NULL              True            no output is generated
2234 NULL              False           output begins with the top topic
2235 != NULL           True            output begins with outputFromLocId
2236                                     and stops after last subtopic of
2237                                     outputFromLocId
2238 != NULL           False           output begins with the top topic
2239                                     and stops after last subtopic of
2240                                     outputFromLocId
2241 ========================================================$SKIP$=====*/
2242 #endif /*DOC*/
2243
2244 static
2245 int ProcessTopics(
2246   _DtHPrOptions * options,
2247   char * *        ret_resultsFile,
2248    PrintState *   state,
2249    Toc *          toc,
2250   Boolean         processSubTopics,
2251   char *          outputFromLocId,
2252   Boolean         inhibitTopicsOutput)
2253 {       /*$CODE$*/
2254    FILE *  fp;
2255    int     ret;
2256
2257    /* don't print a single topic with no locId */
2258    if (    processSubTopics == False
2259         && (outputFromLocId == NULL || *outputFromLocId == EOS) )
2260        return 0;                                /* RETURN: ok */
2261
2262    /* if no output is desired, set ptr to NULL */
2263    if ( NULL == outputFromLocId && inhibitTopicsOutput )
2264        fp = NULL;
2265    else
2266    {  /* output is desired...open file */
2267       fp = OpenTmpFile(options,ret_resultsFile);
2268       if (NULL == fp) return -1;               /* RETURN: error */
2269    }
2270
2271    /*** process text ***/
2272
2273    /* if processing subtopics, start processing at the top */
2274    if ( processSubTopics )
2275    {
2276       int    offset;
2277
2278       /* get the top topic of the volume */
2279       ret = _DtHelpCeGetTopTopicId(state->volHandle, &state->currentLocId);
2280       if (ret != True) state->currentLocId = strdup("_HOMETOPIC");
2281    }
2282    else
2283    {   /* otherwise, process only where needed */
2284        state->currentLocId = strdup(outputFromLocId);
2285    }
2286
2287    /* set the other state values */
2288    if (   NULL != outputFromLocId
2289        && (ret = _DtHelpCeIsTopTopic(state->volHandle,outputFromLocId)) != 0 )
2290       state->outputFromLocId = outputFromLocId;
2291    else
2292       state->outputFromLocId = state->currentLocId;
2293    state->inhibitOutput = inhibitTopicsOutput;
2294    state->curPageNumber = 1;
2295    state->curLineNumber = 1;
2296
2297    /* output topics but dont inhibit */
2298    if ( processSubTopics )
2299    {
2300       state->sectNums[0] = 1;      /* support section num printing */
2301       /* 0: level of the top topic; note tha this is diff from the
2302          level of the top topic in an SDL volume, which is 1.  This
2303          is merely for convenience, to make addressing into the
2304          sectNums array directly rather than subtracting 1 or 2 to
2305          get the array index from the level. */
2306       ret = ProcessSubTopics(options,fp,toc,0,state);
2307    }
2308    else
2309    {
2310       /* start new page but don't eject; output header;
2311          output right number of blank lines to get to where topic starts */
2312
2313       state->sectNums[0] = 0;      /* inhibit section num printing */
2314       NewPage(options,fp,state,False);        /* for top margin */
2315       PrintHeader(options,fp,state,&options->bodyHF,&state->bodyHFF);
2316
2317       ret = ProcessOneTopic(options,fp,state,True);
2318
2319       /* output footer */
2320       PrintFooter(options,fp,state,&options->bodyHF,&state->bodyHFF);
2321    }
2322
2323    /* if the file was opened, close & check if anything written to it */
2324    /* if empty, pretend the file wasn't created. */
2325    if (fp)
2326    {
2327       Boolean empty = False;
2328       long int pos;
2329       pos = ftell(fp);
2330       if (pos <= 0) empty = True;
2331       fclose(fp);
2332       if (empty)
2333       {
2334          unlink(*ret_resultsFile);
2335          free(*ret_resultsFile);
2336          *ret_resultsFile = NULL;
2337       }
2338    }
2339
2340    return ret;
2341 }       /*$END$*/
2342
2343 \f
2344 #if DOC
2345 ===================================================================
2346 $PFUNBEG$:  MakePageBreakFile()
2347 $1LINER$:  Creates a file with only a page break char in it
2348 $DESCRIPT$:
2349 Creates a file with only a page break char in it.
2350 If fails, file ptr is NULL
2351 $RETURNS$:
2352 $ARGS$:
2353 ========================================================$SKIP$=====*/
2354 #endif /*DOC*/
2355
2356 static
2357 void MakePageBreakFile(
2358   _DtHPrOptions * options,
2359   char * *        ret_resultsFile)
2360 {       /*$CODE$*/
2361    FILE * fp;
2362
2363    fp = OpenTmpFile(options,ret_resultsFile);
2364    if (NULL == fp) 
2365    { 
2366       free(*ret_resultsFile); 
2367       *ret_resultsFile = NULL; 
2368       return; 
2369    }
2370    fprintf(fp,"\f\n");
2371    fclose(fp);
2372 }       /*$END$*/
2373
2374 \f
2375 #if DOC
2376 ===================================================================
2377 $PFUNBEG$:  DoHelpTopicsProcessing()
2378 $1LINER$:  Passes through entire volume and generates toc and topic output
2379 $DESCRIPT$:
2380 Passes through entire volume and generates toc and topic output
2381 $RETURNS$:
2382  0:  success
2383 -1:  could not create or open a temp file
2384 -2:  could not get help topic info
2385 -3:  could not alloc memory
2386 $ARGS$:
2387 ========================================================$SKIP$=====*/
2388 #endif /*DOC*/
2389
2390 static
2391 int DoHelpTopicsProcessing(
2392   _DtHPrOptions * options,
2393   char * *        ret_resultsFile, 
2394   Boolean         processToc,
2395   Boolean         processIndex,
2396   Boolean         processFrontMatter,
2397   Boolean         processSubTopics,
2398   char *          outputFromLocId,
2399   Boolean         inhibitTopicsOutput)
2400 {       /*$CODE$*/
2401 #define FM     0
2402 #define TOC    1
2403 #define TOPICS 2
2404 #define INDEX  3
2405 #define NUMPARTS  4
2406    
2407    PrintState  state;
2408    Toc         toc;
2409    int         ret = 0;
2410    int         i;
2411    char *      buf;
2412    char *      start;
2413    char *      next;
2414    char *      pgbrkFile = NULL;
2415    char *      partFiles[NUMPARTS];
2416    Boolean     validFile = False;
2417
2418    /* init larger vars to 0 */
2419    memset(&toc,0,sizeof(Toc));
2420    memset(&state,0,sizeof(state));
2421    memset(partFiles,0,sizeof(partFiles));
2422
2423    /* create terminal-based canvas for text retrieval */
2424    ret = _DtHelpTermCreateCanvas(options->colsTextWidth,&state.canvasHandle);
2425    if ( ret != 0 ) return ret;             /* RETURN: error */
2426
2427    /* open volume and get state.volHandle */
2428    ret = _DtHelpCeOpenVolume(state.canvasHandle, 
2429                              options->helpVolume, &state.volHandle);
2430    if ( ret != 0 ) return ret;             /* RETURN: error */
2431
2432    /* do some prepatory format string generation */
2433    GenAllHeadFootFormatStrs(options,&state);
2434
2435    /* after this point, if we get an error, we don't return--we do the best we can */
2436    /* so don't monitor the return values. */
2437
2438    /*** process a help volume; maybe create a topics file ***/
2439    /* always do this, as all other processing (except FM) needs the data */
2440    ret=ProcessTopics(options,&partFiles[TOPICS],&state,&toc,
2441                processSubTopics,outputFromLocId,inhibitTopicsOutput);
2442
2443    /*** process toc to create an index and add an index entry to toc ***/
2444    /* Do this right after processing topics so that page number info is right */
2445    if (processIndex)
2446       ret=ProcessIndex(options,&partFiles[INDEX],&state,&toc);
2447
2448    /*** process volume to create front matter ***/
2449    if (processFrontMatter)
2450       ret=ProcessFrontMatter(options,&partFiles[FM],&state);
2451
2452    /*** process toc to create a toc; do this last to get all entries ***/
2453    if (processToc)
2454       ret=ProcessToc(options,&partFiles[TOC],&state,&toc);
2455
2456    /*** create the final output file ***/
2457
2458    MakePageBreakFile(options,&pgbrkFile);
2459
2460    /* create a temporary file in $HOME/.dt/tmp to hold everything */
2461    /* Put in $HOME/.dt/tmp so that if the printer operation is running
2462       of a remote system, it can get to the (local) temp file. 
2463       This would not be possible if the file were put in /tmp */
2464    *ret_resultsFile = _DtHPrCreateTmpFile(TMPFILE_PREFIX,TMPFILE_SUFFIX);
2465    if (NULL == *ret_resultsFile) { ret = -1; goto cleanup; } /* RETURN: error */
2466
2467    buf = malloc(3000 * sizeof(char));
2468    if (NULL == buf) { ret = -3; goto cleanup; }      /* RETURN: error */
2469
2470    /* cat together the part files that got created in the array order */
2471    /* separate them with a file containing only a page break */
2472    strcpy(buf,"cat ");
2473    start = buf + strlen(buf);
2474    next = start;
2475    for (i=0; i<NUMPARTS; i++)
2476    {
2477      if (partFiles[i]) 
2478      { 
2479         /* if at least one file in cat list, separate from it with a pgbrk */
2480         if (start != next && pgbrkFile)
2481         {
2482            sprintf(next,"%s ", pgbrkFile);
2483            next += strlen(next);
2484         }
2485         sprintf(next,"%s ", partFiles[i]);
2486         next += strlen(next);
2487         validFile = True;
2488      }
2489    }
2490    /* only do the operation if there are valid files */
2491    if (validFile)
2492    {
2493       int rv;
2494       sprintf(next,"> %s", *ret_resultsFile);
2495       if(options->debugHelpPrint) printf("%s\n",buf);
2496       rv = system(buf);
2497    }
2498    free(buf);
2499    ret = 0;
2500
2501    /* if needed, iconv file and change filenames */
2502
2503    /* close the volume when done */
2504    _DtHelpCeCloseVolume(state.canvasHandle,state.volHandle);
2505
2506    /*** cleanup ***/
2507 cleanup:
2508    /* delete all part files */
2509    for (i=0; i<NUMPARTS; i++)
2510      if (partFiles[i]) 
2511      { 
2512         unlink(partFiles[i]); 
2513         free(partFiles[i]);
2514      }
2515
2516    if(pgbrkFile) { 
2517      unlink(pgbrkFile);
2518    }
2519
2520    /* NOTE: should free Toc here if interested in no leaks */
2521
2522    return ret;
2523 }       /*$END$*/
2524
2525 \f
2526 #if DOC
2527 ===================================================================
2528 $PFUNBEG$:  GenerateTopicsFile()
2529 $1LINER$:  Recovers and formats help topic text and then sends to printer
2530 $DESCRIPT$:
2531 Recovers and formats help topic text and then sends to printer
2532 $RETURNS$:
2533 $ARGS$:
2534 ========================================================$SKIP$=====*/
2535 #endif /*DOC*/
2536
2537 static
2538 int GenerateTopicsFile(
2539   Display *       dpy,
2540   _DtHPrOptions * options,
2541   char * *        ret_resultsFile)
2542 {       /*$CODE$*/
2543    int ret = 0;
2544    Boolean allTopics = (options->allTopics != NULL);
2545    Boolean subTopics = (options->subTopics != NULL);
2546    Boolean toc = (options->toc != NULL);
2547    Boolean index = (options->index != NULL);
2548    Boolean frontMatter = (options->frontMatter != NULL);
2549    Boolean oneTopic = (options->oneTopic != NULL);
2550    Boolean dfltOneTopic;
2551    char *  locationId = options->locationId;
2552
2553    /* one topic is selected if other options aren't selected */
2554    dfltOneTopic = !(allTopics || subTopics || toc || index);
2555
2556    /* This is sort of a kludge, but since we don't require the -One flag,
2557       this keeps the _Hometopic from being printed along with 
2558       the front matter, when only front matter was specified */
2559    if (frontMatter && !oneTopic && dfltOneTopic)
2560       locationId = NULL;   /* prevent printing the hometopic */
2561
2562    /* to generate a toc or index, we need to process all topics; if
2563       processing all or subtopics wasn't requested, request it;
2564       and set a locationId that will never match (an empty string) */
2565    if ( (toc || index) && !allTopics ) 
2566    { 
2567       if (!subTopics) locationId = ""; 
2568       subTopics = True;
2569    }
2570
2571    /* check the flags and do right thing */
2572    if (oneTopic || dfltOneTopic)
2573       /* False: no subtopics--output only locationId; False: don't inhibit output */
2574       ret = DoHelpTopicsProcessing(
2575                    options, ret_resultsFile, 
2576                    False, False, frontMatter,
2577                    False, locationId, False);
2578    else if (allTopics)
2579       /* start at root and don't inhibit output */
2580       ret = DoHelpTopicsProcessing(
2581                    options, ret_resultsFile, 
2582                    True, True, True,
2583                    True, NULL, False);
2584    else
2585       ret = DoHelpTopicsProcessing(
2586                    options, ret_resultsFile, 
2587                    toc, index, frontMatter,
2588                    subTopics, locationId,True);
2589
2590    return ret;
2591 }       /*$END$*/
2592
2593
2594 \f
2595 #if DOC
2596 ===================================================================
2597 $FUNBEG$:  _DtHPrPrintHelpTopic()
2598 $1LINER$:  Recovers and formats help topic text and then sends to printer
2599 $DESCRIPT$:
2600 Recovers and formats help topic text and then sends to printer
2601 $RETURNS$:
2602 $ARGS$:
2603 ========================================================$SKIP$=====*/
2604 #endif /*DOC*/
2605
2606 int _DtHPrPrintHelpTopic(
2607    Display *      dpy,
2608   _DtHPrOptions * options)
2609 {       /*$CODE$*/
2610    char * printCommand;
2611    char   cmdFormat[100];
2612    char   prOffsetArg[30];
2613    char * tmpFile;
2614    int    status;
2615    char * path;
2616
2617
2618     if ( NULL == options->helpVolume )
2619     {
2620          fprintf(stderr, _DTGETMESSAGE(PTSET,1,
2621                              "%s: Error: helpType is topic, "
2622                              "but no helpVolume specified.\n"), 
2623                              options->programName);
2624
2625          return 1;                      /* RETURN error */
2626     }
2627
2628    /* try to locate the volume */
2629    path = _DtHelpFileLocate(DtHelpVOLUME_TYPE, options->helpVolume,
2630                                _DtHelpFileSuffixList,False,R_OK);
2631    if (path == NULL)
2632    {
2633          fprintf(stderr, _DTGETMESSAGE(PTSET,2,
2634                              "%s Error: unable to locate help volume '%s'\n"),
2635                              options->programName, options->helpVolume);
2636
2637          return 1;                      /* RETURN error */
2638    }
2639    options->helpVolume = path;  /* don't free old helpVolume: owned by Xrm */
2640
2641    /* generate a file containing the help topic */
2642    status = GenerateTopicsFile(dpy,options,&tmpFile);
2643    if (status != 0)
2644    {
2645          fprintf(stderr, _DTGETMESSAGE(PTSET,3,
2646                              "%s Error: problem processing help volume '%s'\n"),
2647                              options->programName, options->helpVolume);
2648
2649          return 1;                      /* RETURN error */
2650    }
2651
2652    /* Alloc max shell command line len */
2653    printCommand = malloc(MAX_COMMAND_LENGTH*sizeof(char));
2654    if (printCommand == NULL)
2655    {
2656          fprintf(stderr, _DTGETMESSAGE(PTSET,4,
2657                              "%s Error: memory allocation failed\n"),
2658                              options->programName);
2659
2660          return 1;                      /* RETURN error */
2661    }
2662    
2663
2664    /** generate the file **/
2665    sprintf(printCommand,"cat %s",tmpFile);
2666
2667    status = _DtHPrGenFileOrPrint(options,"Help Topic(s)",printCommand);
2668
2669    unlink(tmpFile);
2670
2671    return(status);
2672 } /*$END$*/
2673
2674
2675 \f
2676 #if 0  /* This is left-over code that used to deal with printing
2677           raster images.  We don't need it for printing text-only.
2678           I left it here for future reference. */
2679 #if DOC
2680 ===================================================================
2681 $FUNBEG$:  _DtHPrPrintHelpTopicWithXvp()
2682 $1LINER$:  Recovers and formats help topic text and then sends to printer
2683 $DESCRIPT$:
2684 Recovers and formats help topic text and then sends to printer
2685 $STATUS$:  
2686 Code left over from VUE 3.0 help print
2687 Has not been updated.
2688 $RETURNS$:
2689 $ARGS$:
2690 ========================================================$SKIP$=====*/
2691 #endif /*DOC*/
2692
2693 int _DtHPrPrintHelpTopicWithXvp(
2694    Display *    dpy,
2695    VolumeHandle volHandle,
2696    char *       locationId)
2697 {       /*$CODE$*/
2698   extern _DtHelpDisplayAreaStruct *Spoof();
2699   GC             gc;
2700   GC             pageNumberGC;
2701   int            screen;
2702   Window         main_window;
2703   Display       *dpy;
2704   XFontStruct   *defaultFont;
2705   XFontStruct   *pageNumberFont;
2706   _DtHelpDisplayAreaStruct *pDAS;
2707   char *lpDocName;
2708
2709 #ifdef RASTER_PRINT
2710    char         *displayNameX;
2711    Display      *dpyX;
2712    Window       main_windowX;
2713    GC           gcX;
2714    GC           invert_gcX;
2715    int          widthX;
2716    int          heightX;
2717    XFontStruct  *defaultFontX;
2718 #endif
2719
2720 #define MY_WIDTH        400
2721 #define MY_HEIGHT       400
2722 #define DEFAULT_FONT "-agfa-courier-normal-r-normal-93950-0-120-300-300-m-0-hp-roman8"
2723
2724 #ifdef RASTER_PRINT
2725    /* set color to bitonal */
2726    value.addr = RN_bitonal;
2727    value.size = strlen(RN_BITONAL) + 1;
2728    XrmPutResource(&XvpDB, STAR_RN_helpColorUse, *str_type, &value);
2729 #endif
2730
2731 #ifdef RASTER_PRINT
2732    /********************/
2733    /* Get display name */
2734    /********************/
2735
2736    strcpy(resource_name, name_prefix);
2737    strcat(resource_name, RN_display);
2738    strcpy(resource_class, class_prefix);
2739    strcat(resource_class, RC_display);
2740    if (XrmGetResource(appDB, resource_name,
2741                       resource_class,
2742                       str_type, &value) == True)
2743       displayNameX = value.addr;
2744    else displayNameX = EMPTY_STR;
2745 #endif
2746
2747    strcpy(resource_name, name_prefix);
2748    strcat(resource_name, RN_jobTitle);
2749    strcpy(resource_class, class_prefix);
2750    strcat(resource_class, RC_jobTitle);
2751    if (XrmGetResource(appDB, resource_name, 
2752                       resource_class, 
2753                       str_type, &value) == True)
2754       lpDocName = (char *)value.addr;
2755    else
2756       lpDocName = DFLT_JOB_TITLE_STR;
2757    
2758    status = XvpEscape(dpy, STARTDOC, strlen(lpDocName), lpDocName, NULL);
2759    screen = DefaultScreen(dpy);
2760    
2761   /*    Calculate application page size         */
2762
2763    _DtHPrCalculatePageSize(dpy, &x, &y, &width, &height);
2764
2765    /* create a window; x, y, width, and height are recorded */
2766    main_window = XCreateSimpleWindow(dpy, XRootWindow(dpy, screen),
2767                 x, y,
2768                 width,
2769                 height,
2770                 10, 0, XWhitePixel (dpy, screen));
2771                 /* 0, 0, MY_WIDTH, MY_HEIGHT, 10, 0, XWhitePixel (dpy, screen)); */
2772
2773   XSelectInput (dpy, main_window, ExposureMask);
2774   XMapWindow (dpy, main_window);
2775   XvpEscape(dpy, SET_IMAGE_RESOLUTION, 4, 100, NULL);
2776
2777   defaultFont = XLoadQueryFont (dpy, DEFAULT_FONT);
2778   if (defaultFont == NULL)
2779     {
2780         printf ("Unable to get a font\n");
2781         exit (1);
2782     }
2783
2784   gc = DefaultGC(dpy, screen);
2785   XSetForeground (dpy, gc, XBlackPixel (dpy, screen));
2786   XSetBackground (dpy, gc, XWhitePixel (dpy, screen));
2787   XSetFont(dpy, gc, defaultFont->fid);
2788   XSetLineAttributes(dpy, gc, 2, LineSolid, CapButt, JoinMiter);
2789  
2790 #ifndef RASTER_PRINT
2791   pageNumberFont = 
2792      XLoadQueryFont (dpy, 
2793              "-agfa-univers-normal-r-normal-94021-0-100-300-300-p-0-iso8859-1");
2794   if (pageNumberFont == NULL)
2795     {
2796         printf ("Unable to get a font\n");
2797         exit (1);
2798     }
2799
2800   pageNumberGC = XCreateGC(dpy, RootWindow(dpy, screen), NULL, NULL);
2801   XSetForeground (dpy, pageNumberGC, XBlackPixel (dpy, screen));
2802   XSetBackground (dpy, pageNumberGC, XWhitePixel (dpy, screen));
2803   XSetFont(dpy, pageNumberGC, pageNumberFont->fid);
2804   XSetLineAttributes(dpy, pageNumberGC, 2, LineSolid, CapButt, JoinMiter);
2805 #endif
2806
2807 #ifdef RASTER_PRINT
2808   widthX = width/3; /* hard coded for 300 dpi printers */
2809   heightX = height/3;
2810   status = CreateRealXObjects(displayNameX, widthX, heightX, 
2811                               &dpyX, &main_windowX, &gcX,
2812                               &invert_gcX, &defaultFontX);
2813   dpyX->db = dpy->db;   /* carry over data base */
2814   
2815   pDAS = Spoof (dpyX, gcX, invert_gcX, defaultFontX, widthX, heightX, lineLeading);
2816 #else
2817   pDAS = Spoof (dpy, gc, gc, defaultFont, width, height, lineLeading);
2818 #endif
2819   if (pDAS == NULL)
2820   {
2821      fprintf(stderr, "%s: Internal error.\n");
2822      exit(1);
2823   }
2824
2825   if (helpType == DtHELP_TYPE_TOPIC)
2826 #ifdef RASTER_PRINT
2827      status = printTopicAndChildrenX(helpVolume, locationId, allTopics, pDAS, 
2828                                     dpy, main_window, gc, 
2829                                     dpyX, main_windowX, gcX,
2830                                     widthX, heightX);
2831 #else
2832      status = printTopicAndChildren(helpVolume, locationId, allTopics, pDAS, 
2833                                     dpy, main_window, gc, pageNumberGC );
2834 #endif
2835      /*
2836      pDAS = (_DtHelpDisplayAreaStruct *) SetUp (pDAS, dpy, main_window, gc, defaultFont,
2837      width,
2838      height,
2839      helpVolume,
2840      locationId);
2841      */
2842      else if (helpType == DtHELP_TYPE_DYNAMIC_STRING)
2843      {
2844 #ifdef RASTER_PRINT
2845      XImage     *image;
2846      
2847      pDAS = (_DtHelpDisplayAreaStruct *) SetUpDynamicString (pDAS, dpy, gc, 
2848                                                          defaultFont,
2849                                                          width,
2850                                                          height,
2851                                                          stringData);
2852      RenderLinesX (dpyX, main_windowX, pDAS, widthX, heightX, &image);
2853      XvpPutImage(dpy, main_window, gcX, image, 0, 0, 0, 0, 
2854                  (image->width)*3, (image->height*3) );
2855      status = XvpEscape(dpy, NEWFRAME, NULL, NULL, NULL);
2856      if (status > 0) ;
2857   else if (status < 0)
2858      fprintf (stdout, "XvpEscape(NEWFRAME) not implemented.\n");
2859   else fprintf(stdout, "XvpEscape(NEWFRAME) not successful.\n");
2860 #else
2861         pDAS = (_DtHelpDisplayAreaStruct *) SetUpDynamicString (pDAS, dpy, gc, 
2862                                                             defaultFont,
2863                                                             width,
2864                                                             height,
2865                                                             stringData);
2866         RenderLines (dpy, main_window, pDAS);
2867 #endif
2868      }
2869      else 
2870      {
2871         fprintf(stderr, "%s Error: Illegal helpType %d.\n",
2872                 argv[0], helpType);
2873         exit(1);
2874      }
2875   
2876   if (status == -1)
2877   {
2878      fprintf (stdout, "Error occurred in XvpEscape(STARTDOC)\n");
2879      exit(1);
2880   }
2881
2882   status = XvpEscape(dpy, ENDDOC, NULL, NULL, NULL);
2883   if (status == -1)
2884   {
2885      fprintf (stdout, "Error occurred in XvpEscape(ENDDOC)\n");
2886      exit(1);
2887   }
2888   
2889   exit(0);
2890 } /*$END$*/
2891 #endif  /* #if 0 at function start */