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