dthelp: compiler warning and coverity warning 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 librararies 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       char * name = NULL;
2277       int    offset;
2278
2279       /* get the top topic of the volume */
2280       ret = _DtHelpCeGetTopTopicId(state->volHandle, &state->currentLocId);
2281       if (ret != True) state->currentLocId = strdup("_HOMETOPIC");
2282       if(name) free(name);
2283    }
2284    else
2285    {   /* otherwise, process only where needed */
2286        state->currentLocId = strdup(outputFromLocId);
2287    }
2288
2289    /* set the other state values */
2290    if (   NULL != outputFromLocId
2291        && (ret = _DtHelpCeIsTopTopic(state->volHandle,outputFromLocId)) != 0 )
2292       state->outputFromLocId = outputFromLocId;
2293    else
2294       state->outputFromLocId = state->currentLocId;
2295    state->inhibitOutput = inhibitTopicsOutput;
2296    state->curPageNumber = 1;
2297    state->curLineNumber = 1;
2298
2299    /* output topics but dont inhibit */
2300    if ( processSubTopics )
2301    {
2302       state->sectNums[0] = 1;      /* support section num printing */
2303       /* 0: level of the top topic; note tha this is diff from the
2304          level of the top topic in an SDL volume, which is 1.  This
2305          is merely for convenience, to make addressing into the
2306          sectNums array directly rather than subtracting 1 or 2 to
2307          get the array index from the level. */
2308       ret = ProcessSubTopics(options,fp,toc,0,state);
2309    }
2310    else
2311    {
2312       /* start new page but don't eject; output header;
2313          output right number of blank lines to get to where topic starts */
2314
2315       state->sectNums[0] = 0;      /* inhibit section num printing */
2316       NewPage(options,fp,state,False);        /* for top margin */
2317       PrintHeader(options,fp,state,&options->bodyHF,&state->bodyHFF);
2318
2319       ret = ProcessOneTopic(options,fp,state,True);
2320
2321       /* output footer */
2322       PrintFooter(options,fp,state,&options->bodyHF,&state->bodyHFF);
2323    }
2324
2325    /* if the file was opened, close & check if anything written to it */
2326    /* if empty, pretend the file wasn't created. */
2327    if (fp)
2328    {
2329       Boolean empty = False;
2330       long int pos;
2331       pos = ftell(fp);
2332       if (pos <= 0) empty = True;
2333       fclose(fp);
2334       if (empty)
2335       {
2336          unlink(*ret_resultsFile);
2337          free(*ret_resultsFile);
2338          *ret_resultsFile = NULL;
2339       }
2340    }
2341
2342    return ret;
2343 }       /*$END$*/
2344
2345 \f
2346 #if DOC
2347 ===================================================================
2348 $PFUNBEG$:  MakePageBreakFile()
2349 $1LINER$:  Creates a file with only a page break char in it
2350 $DESCRIPT$:
2351 Creates a file with only a page break char in it.
2352 If fails, file ptr is NULL
2353 $RETURNS$:
2354 $ARGS$:
2355 ========================================================$SKIP$=====*/
2356 #endif /*DOC*/
2357
2358 static
2359 void MakePageBreakFile(
2360   _DtHPrOptions * options,
2361   char * *        ret_resultsFile)
2362 {       /*$CODE$*/
2363    FILE * fp;
2364
2365    fp = OpenTmpFile(options,ret_resultsFile);
2366    if (NULL == fp) 
2367    { 
2368       free(*ret_resultsFile); 
2369       *ret_resultsFile = NULL; 
2370       return; 
2371    }
2372    fprintf(fp,"\f\n");
2373    fclose(fp);
2374 }       /*$END$*/
2375
2376 \f
2377 #if DOC
2378 ===================================================================
2379 $PFUNBEG$:  DoHelpTopicsProcessing()
2380 $1LINER$:  Passes through entire volume and generates toc and topic output
2381 $DESCRIPT$:
2382 Passes through entire volume and generates toc and topic output
2383 $RETURNS$:
2384  0:  success
2385 -1:  could not create or open a temp file
2386 -2:  could not get help topic info
2387 -3:  could not alloc memory
2388 $ARGS$:
2389 ========================================================$SKIP$=====*/
2390 #endif /*DOC*/
2391
2392 static
2393 int DoHelpTopicsProcessing(
2394   _DtHPrOptions * options,
2395   char * *        ret_resultsFile, 
2396   Boolean         processToc,
2397   Boolean         processIndex,
2398   Boolean         processFrontMatter,
2399   Boolean         processSubTopics,
2400   char *          outputFromLocId,
2401   Boolean         inhibitTopicsOutput)
2402 {       /*$CODE$*/
2403 #define FM     0
2404 #define TOC    1
2405 #define TOPICS 2
2406 #define INDEX  3
2407 #define NUMPARTS  4
2408    
2409    PrintState  state;
2410    Toc         toc;
2411    int         ret = 0;
2412    int         i;
2413    char *      buf;
2414    char *      start;
2415    char *      next;
2416    char *      pgbrkFile;
2417    char *      partFiles[NUMPARTS];
2418    Boolean     validFile = False;
2419
2420    /* init larger vars to 0 */
2421    memset(&toc,0,sizeof(Toc));
2422    memset(&state,0,sizeof(state));
2423    memset(partFiles,0,sizeof(partFiles));
2424
2425    /* create terminal-based canvas for text retrieval */
2426    ret = _DtHelpTermCreateCanvas(options->colsTextWidth,&state.canvasHandle);
2427    if ( ret != 0 ) return ret;             /* RETURN: error */
2428
2429    /* open volume and get state.volHandle */
2430    ret = _DtHelpCeOpenVolume(state.canvasHandle, 
2431                              options->helpVolume, &state.volHandle);
2432    if ( ret != 0 ) return ret;             /* RETURN: error */
2433
2434    /* do some prepatory format string generation */
2435    GenAllHeadFootFormatStrs(options,&state);
2436
2437    /* after this point, if we get an error, we don't return--we do the best we can */
2438    /* so don't monitor the return values. */
2439
2440    /*** process a help volume; maybe create a topics file ***/
2441    /* always do this, as all other processing (except FM) needs the data */
2442    ret=ProcessTopics(options,&partFiles[TOPICS],&state,&toc,
2443                processSubTopics,outputFromLocId,inhibitTopicsOutput);
2444
2445    /*** process toc to create an index and add an index entry to toc ***/
2446    /* Do this right after processing topics so that page number info is right */
2447    if (processIndex)
2448       ret=ProcessIndex(options,&partFiles[INDEX],&state,&toc);
2449
2450    /*** process volume to create front matter ***/
2451    if (processFrontMatter)
2452       ret=ProcessFrontMatter(options,&partFiles[FM],&state);
2453
2454    /*** process toc to create a toc; do this last to get all entries ***/
2455    if (processToc)
2456       ret=ProcessToc(options,&partFiles[TOC],&state,&toc);
2457
2458    /*** create the final output file ***/
2459
2460    MakePageBreakFile(options,&pgbrkFile);
2461
2462    /* create a temporary file in $HOME/.dt/tmp to hold everything */
2463    /* Put in $HOME/.dt/tmp so that if the printer operation is running
2464       of a remote system, it can get to the (local) temp file. 
2465       This would not be possible if the file were put in /tmp */
2466    *ret_resultsFile = _DtHPrCreateTmpFile(TMPFILE_PREFIX,TMPFILE_SUFFIX);
2467    if (NULL == *ret_resultsFile) { ret = -1; goto cleanup; } /* RETURN: error */
2468
2469    buf = malloc(3000 * sizeof(char));
2470    if (NULL == buf) { ret = -3; goto cleanup; }      /* RETURN: error */
2471
2472    /* cat together the part files that got created in the array order */
2473    /* separate them with a file containing only a page break */
2474    strcpy(buf,"cat ");
2475    start = buf + strlen(buf);
2476    next = start;
2477    for (i=0; i<NUMPARTS; i++)
2478    {
2479      if (partFiles[i]) 
2480      { 
2481         /* if at least one file in cat list, separate from it with a pgbrk */
2482         if (start != next && pgbrkFile)
2483         {
2484            sprintf(next,"%s ", pgbrkFile);
2485            next += strlen(next);
2486         }
2487         sprintf(next,"%s ", partFiles[i]);
2488         next += strlen(next);
2489         validFile = True;
2490      }
2491    }
2492    /* only do the operation if there are valid files */
2493    if (validFile)
2494    {
2495       int rv;
2496       sprintf(next,"> %s", *ret_resultsFile);
2497       if(options->debugHelpPrint) printf("%s\n",buf);
2498       rv = system(buf);
2499    }
2500    free(buf);
2501    ret = 0;
2502
2503    /* if needed, iconv file and change filenames */
2504
2505    /* close the volume when done */
2506    _DtHelpCeCloseVolume(state.canvasHandle,state.volHandle);
2507
2508    /*** cleanup ***/
2509 cleanup:
2510    /* delete all part files */
2511    for (i=0; i<NUMPARTS; i++)
2512      if (partFiles[i]) 
2513      { 
2514         unlink(partFiles[i]); 
2515         free(partFiles[i]);
2516      }
2517    unlink(pgbrkFile);
2518
2519    /* NOTE: should free Toc here if interested in no leaks */
2520
2521    return ret;
2522 }       /*$END$*/
2523
2524 \f
2525 #if DOC
2526 ===================================================================
2527 $PFUNBEG$:  GenerateTopicsFile()
2528 $1LINER$:  Recovers and formats help topic text and then sends to printer
2529 $DESCRIPT$:
2530 Recovers and formats help topic text and then sends to printer
2531 $RETURNS$:
2532 $ARGS$:
2533 ========================================================$SKIP$=====*/
2534 #endif /*DOC*/
2535
2536 static
2537 int GenerateTopicsFile(
2538   Display *       dpy,
2539   _DtHPrOptions * options,
2540   char * *        ret_resultsFile)
2541 {       /*$CODE$*/
2542    int ret = 0;
2543    Boolean allTopics = (options->allTopics != NULL);
2544    Boolean subTopics = (options->subTopics != NULL);
2545    Boolean toc = (options->toc != NULL);
2546    Boolean index = (options->index != NULL);
2547    Boolean frontMatter = (options->frontMatter != NULL);
2548    Boolean oneTopic = (options->oneTopic != NULL);
2549    Boolean dfltOneTopic;
2550    char *  locationId = options->locationId;
2551
2552    /* one topic is selected if other options aren't selected */
2553    dfltOneTopic = !(allTopics || subTopics || toc || index);
2554
2555    /* This is sort of a kludge, but since we don't require the -One flag,
2556       this keeps the _Hometopic from being printed along with 
2557       the front matter, when only front matter was specified */
2558    if (frontMatter && !oneTopic && dfltOneTopic)
2559       locationId = NULL;   /* prevent printing the hometopic */
2560
2561    /* to generate a toc or index, we need to process all topics; if
2562       processing all or subtopics wasn't requested, request it;
2563       and set a locationId that will never match (an empty string) */
2564    if ( (toc || index) && !allTopics ) 
2565    { 
2566       if (!subTopics) locationId = ""; 
2567       subTopics = True;
2568    }
2569
2570    /* check the flags and do right thing */
2571    if (oneTopic || dfltOneTopic)
2572       /* False: no subtopics--output only locationId; False: don't inhibit output */
2573       ret = DoHelpTopicsProcessing(
2574                    options, ret_resultsFile, 
2575                    False, False, frontMatter,
2576                    False, locationId, False);
2577    else if (allTopics)
2578       /* start at root and don't inhibit output */
2579       ret = DoHelpTopicsProcessing(
2580                    options, ret_resultsFile, 
2581                    True, True, True,
2582                    True, NULL, False);
2583    else
2584       ret = DoHelpTopicsProcessing(
2585                    options, ret_resultsFile, 
2586                    toc, index, frontMatter,
2587                    subTopics, locationId,True);
2588
2589    return ret;
2590 }       /*$END$*/
2591
2592
2593 \f
2594 #if DOC
2595 ===================================================================
2596 $FUNBEG$:  _DtHPrPrintHelpTopic()
2597 $1LINER$:  Recovers and formats help topic text and then sends to printer
2598 $DESCRIPT$:
2599 Recovers and formats help topic text and then sends to printer
2600 $RETURNS$:
2601 $ARGS$:
2602 ========================================================$SKIP$=====*/
2603 #endif /*DOC*/
2604
2605 int _DtHPrPrintHelpTopic(
2606    Display *      dpy,
2607   _DtHPrOptions * options)
2608 {       /*$CODE$*/
2609    char * printCommand;
2610    char   cmdFormat[100];
2611    char   prOffsetArg[30];
2612    char * tmpFile;
2613    int    status;
2614    char * path;
2615
2616
2617     if ( NULL == options->helpVolume )
2618     {
2619          fprintf(stderr, _DTGETMESSAGE(PTSET,1,
2620                              "%s: Error: helpType is topic, "
2621                              "but no helpVolume specified.\n"), 
2622                              options->programName);
2623
2624          return 1;                      /* RETURN error */
2625     }
2626
2627    /* try to locate the volume */
2628    path = _DtHelpFileLocate(DtHelpVOLUME_TYPE, options->helpVolume,
2629                                _DtHelpFileSuffixList,False,R_OK);
2630    if (path == NULL)
2631    {
2632          fprintf(stderr, _DTGETMESSAGE(PTSET,2,
2633                              "%s Error: unable to locate help volume '%s'\n"),
2634                              options->programName, options->helpVolume);
2635
2636          return 1;                      /* RETURN error */
2637    }
2638    options->helpVolume = path;  /* don't free old helpVolume: owned by Xrm */
2639
2640    /* generate a file containing the help topic */
2641    status = GenerateTopicsFile(dpy,options,&tmpFile);
2642    if (status != 0)
2643    {
2644          fprintf(stderr, _DTGETMESSAGE(PTSET,3,
2645                              "%s Error: problem processing help volume '%s'\n"),
2646                              options->programName, options->helpVolume);
2647
2648          return 1;                      /* RETURN error */
2649    }
2650
2651    /* Alloc max shell command line len */
2652    printCommand = malloc(MAX_COMMAND_LENGTH*sizeof(char));
2653    if (printCommand == NULL)
2654    {
2655          fprintf(stderr, _DTGETMESSAGE(PTSET,4,
2656                              "%s Error: memory allocation failed\n"),
2657                              options->programName);
2658
2659          return 1;                      /* RETURN error */
2660    }
2661    
2662
2663    /** generate the file **/
2664    sprintf(printCommand,"cat %s",tmpFile);
2665
2666    status = _DtHPrGenFileOrPrint(options,"Help Topic(s)",printCommand);
2667
2668    unlink(tmpFile);
2669
2670    return(status);
2671 } /*$END$*/
2672
2673
2674 \f
2675 #if 0  /* This is left-over code that used to deal with printing
2676           raster images.  We don't need it for printing text-only.
2677           I left it here for future reference. */
2678 #if DOC
2679 ===================================================================
2680 $FUNBEG$:  _DtHPrPrintHelpTopicWithXvp()
2681 $1LINER$:  Recovers and formats help topic text and then sends to printer
2682 $DESCRIPT$:
2683 Recovers and formats help topic text and then sends to printer
2684 $STATUS$:  
2685 Code left over from VUE 3.0 help print
2686 Has not been updated.
2687 $RETURNS$:
2688 $ARGS$:
2689 ========================================================$SKIP$=====*/
2690 #endif /*DOC*/
2691
2692 int _DtHPrPrintHelpTopicWithXvp(
2693    Display *    dpy,
2694    VolumeHandle volHandle,
2695    char *       locationId)
2696 {       /*$CODE$*/
2697   extern _DtHelpDisplayAreaStruct *Spoof();
2698   GC             gc;
2699   GC             pageNumberGC;
2700   int            screen;
2701   Window         main_window;
2702   Display       *dpy;
2703   XFontStruct   *defaultFont;
2704   XFontStruct   *pageNumberFont;
2705   _DtHelpDisplayAreaStruct *pDAS;
2706   char *lpDocName;
2707
2708 #ifdef RASTER_PRINT
2709    char         *displayNameX;
2710    Display      *dpyX;
2711    Window       main_windowX;
2712    GC           gcX;
2713    GC           invert_gcX;
2714    int          widthX;
2715    int          heightX;
2716    XFontStruct  *defaultFontX;
2717 #endif
2718
2719 #define MY_WIDTH        400
2720 #define MY_HEIGHT       400
2721 #define DEFAULT_FONT "-agfa-courier-normal-r-normal-93950-0-120-300-300-m-0-hp-roman8"
2722
2723 #ifdef RASTER_PRINT
2724    /* set color to bitonal */
2725    value.addr = RN_bitonal;
2726    value.size = strlen(RN_BITONAL) + 1;
2727    XrmPutResource(&XvpDB, STAR_RN_helpColorUse, *str_type, &value);
2728 #endif
2729
2730 #ifdef RASTER_PRINT
2731    /********************/
2732    /* Get display name */
2733    /********************/
2734
2735    strcpy(resource_name, name_prefix);
2736    strcat(resource_name, RN_display);
2737    strcpy(resource_class, class_prefix);
2738    strcat(resource_class, RC_display);
2739    if (XrmGetResource(appDB, resource_name,
2740                       resource_class,
2741                       str_type, &value) == True)
2742       displayNameX = value.addr;
2743    else displayNameX = EMPTY_STR;
2744 #endif
2745
2746    strcpy(resource_name, name_prefix);
2747    strcat(resource_name, RN_jobTitle);
2748    strcpy(resource_class, class_prefix);
2749    strcat(resource_class, RC_jobTitle);
2750    if (XrmGetResource(appDB, resource_name, 
2751                       resource_class, 
2752                       str_type, &value) == True)
2753       lpDocName = (char *)value.addr;
2754    else
2755       lpDocName = DFLT_JOB_TITLE_STR;
2756    
2757    status = XvpEscape(dpy, STARTDOC, strlen(lpDocName), lpDocName, NULL);
2758    screen = DefaultScreen(dpy);
2759    
2760   /*    Calculate application page size         */
2761
2762    _DtHPrCalculatePageSize(dpy, &x, &y, &width, &height);
2763
2764    /* create a window; x, y, width, and height are recorded */
2765    main_window = XCreateSimpleWindow(dpy, XRootWindow(dpy, screen),
2766                 x, y,
2767                 width,
2768                 height,
2769                 10, 0, XWhitePixel (dpy, screen));
2770                 /* 0, 0, MY_WIDTH, MY_HEIGHT, 10, 0, XWhitePixel (dpy, screen)); */
2771
2772   XSelectInput (dpy, main_window, ExposureMask);
2773   XMapWindow (dpy, main_window);
2774   XvpEscape(dpy, SET_IMAGE_RESOLUTION, 4, 100, NULL);
2775
2776   defaultFont = XLoadQueryFont (dpy, DEFAULT_FONT);
2777   if (defaultFont == NULL)
2778     {
2779         printf ("Unable to get a font\n");
2780         exit (1);
2781     }
2782
2783   gc = DefaultGC(dpy, screen);
2784   XSetForeground (dpy, gc, XBlackPixel (dpy, screen));
2785   XSetBackground (dpy, gc, XWhitePixel (dpy, screen));
2786   XSetFont(dpy, gc, defaultFont->fid);
2787   XSetLineAttributes(dpy, gc, 2, LineSolid, CapButt, JoinMiter);
2788  
2789 #ifndef RASTER_PRINT
2790   pageNumberFont = 
2791      XLoadQueryFont (dpy, 
2792              "-agfa-univers-normal-r-normal-94021-0-100-300-300-p-0-iso8859-1");
2793   if (pageNumberFont == NULL)
2794     {
2795         printf ("Unable to get a font\n");
2796         exit (1);
2797     }
2798
2799   pageNumberGC = XCreateGC(dpy, RootWindow(dpy, screen), NULL, NULL);
2800   XSetForeground (dpy, pageNumberGC, XBlackPixel (dpy, screen));
2801   XSetBackground (dpy, pageNumberGC, XWhitePixel (dpy, screen));
2802   XSetFont(dpy, pageNumberGC, pageNumberFont->fid);
2803   XSetLineAttributes(dpy, pageNumberGC, 2, LineSolid, CapButt, JoinMiter);
2804 #endif
2805
2806 #ifdef RASTER_PRINT
2807   widthX = width/3; /* hard coded for 300 dpi printers */
2808   heightX = height/3;
2809   status = CreateRealXObjects(displayNameX, widthX, heightX, 
2810                               &dpyX, &main_windowX, &gcX,
2811                               &invert_gcX, &defaultFontX);
2812   dpyX->db = dpy->db;   /* carry over data base */
2813   
2814   pDAS = Spoof (dpyX, gcX, invert_gcX, defaultFontX, widthX, heightX, lineLeading);
2815 #else
2816   pDAS = Spoof (dpy, gc, gc, defaultFont, width, height, lineLeading);
2817 #endif
2818   if (pDAS == NULL)
2819   {
2820      fprintf(stderr, "%s: Internal error.\n");
2821      exit(1);
2822   }
2823
2824   if (helpType == DtHELP_TYPE_TOPIC)
2825 #ifdef RASTER_PRINT
2826      status = printTopicAndChildrenX(helpVolume, locationId, allTopics, pDAS, 
2827                                     dpy, main_window, gc, 
2828                                     dpyX, main_windowX, gcX,
2829                                     widthX, heightX);
2830 #else
2831      status = printTopicAndChildren(helpVolume, locationId, allTopics, pDAS, 
2832                                     dpy, main_window, gc, pageNumberGC );
2833 #endif
2834      /*
2835      pDAS = (_DtHelpDisplayAreaStruct *) SetUp (pDAS, dpy, main_window, gc, defaultFont,
2836      width,
2837      height,
2838      helpVolume,
2839      locationId);
2840      */
2841      else if (helpType == DtHELP_TYPE_DYNAMIC_STRING)
2842      {
2843 #ifdef RASTER_PRINT
2844      XImage     *image;
2845      
2846      pDAS = (_DtHelpDisplayAreaStruct *) SetUpDynamicString (pDAS, dpy, gc, 
2847                                                          defaultFont,
2848                                                          width,
2849                                                          height,
2850                                                          stringData);
2851      RenderLinesX (dpyX, main_windowX, pDAS, widthX, heightX, &image);
2852      XvpPutImage(dpy, main_window, gcX, image, 0, 0, 0, 0, 
2853                  (image->width)*3, (image->height*3) );
2854      status = XvpEscape(dpy, NEWFRAME, NULL, NULL, NULL);
2855      if (status > 0) ;
2856   else if (status < 0)
2857      fprintf (stdout, "XvpEscape(NEWFRAME) not implemented.\n");
2858   else fprintf(stdout, "XvpEscape(NEWFRAME) not successful.\n");
2859 #else
2860         pDAS = (_DtHelpDisplayAreaStruct *) SetUpDynamicString (pDAS, dpy, gc, 
2861                                                             defaultFont,
2862                                                             width,
2863                                                             height,
2864                                                             stringData);
2865         RenderLines (dpy, main_window, pDAS);
2866 #endif
2867      }
2868      else 
2869      {
2870         fprintf(stderr, "%s Error: Illegal helpType %d.\n",
2871                 argv[0], helpType);
2872         exit(1);
2873      }
2874   
2875   if (status == -1)
2876   {
2877      fprintf (stdout, "Error occurred in XvpEscape(STARTDOC)\n");
2878      exit(1);
2879   }
2880
2881   status = XvpEscape(dpy, ENDDOC, NULL, NULL, NULL);
2882   if (status == -1)
2883   {
2884      fprintf (stdout, "Error occurred in XvpEscape(ENDDOC)\n");
2885      exit(1);
2886   }
2887   
2888   exit(0);
2889 } /*$END$*/
2890 #endif  /* #if 0 at function start */