Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtHelp / FormatUtil.c
1 /* $XConsortium: FormatUtil.c /main/9 1996/11/01 10:12:14 drk $ */
2 /************************************<+>*************************************
3  ****************************************************************************
4  **
5  **   File:     FormatUtil.c
6  **
7  **   Project:     Text Graphic Display Library
8  **
9  **  
10  **   Description: Semi private format utility functions that do not
11  **                require the Display Area, Motif, Xt or X11.
12  **
13  **
14  **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
15  **
16  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
17  **  (c) Copyright 1993, 1994 International Business Machines Corp.
18  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
19  **  (c) Copyright 1993, 1994 Novell, Inc.
20  **
21  **
22  ****************************************************************************
23  ************************************<+>*************************************/
24
25 /*
26  * system includes
27  */
28 #include <errno.h>
29 #include <limits.h>
30 #include <locale.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35
36 #include <X11/Xos.h>
37 #ifdef X_NOT_STDC_ENV
38 extern int errno;
39 #endif
40
41 /*
42  * Canvas Engine includes
43  */
44 #include "CanvasP.h"
45 #include "CanvasSegP.h"
46
47 /*
48  * private includes
49  */
50 #include "CanvasError.h"
51 #include "bufioI.h"
52 #include "FormatUtilI.h"
53
54 #if defined(NLS16) || !defined(NO_MESSAGE_CATALOG)
55 #include <nl_types.h>
56 #endif
57
58 #ifndef NL_CAT_LOCALE
59 static const int NL_CAT_LOCALE = 0;
60 #endif
61
62 /********    Private Function Declarations    ********/
63 /********    End Private Function Declarations    ********/
64
65 /******************************************************************************
66  *
67  * Private variables and defines.
68  *
69  *****************************************************************************/
70 #define GROW_SIZE        5
71
72 /******************************************************************************
73  *
74  * Private Functions
75  *
76  *****************************************************************************/
77 /******************************************************************************
78  *
79  * Semi Public Functions
80  *
81  *****************************************************************************/
82 /******************************************************************************
83  * Function: int _DtHelpCeAddOctalToBuf (char *src, char **dst,
84  *                                      int *dst_size,
85  *                                      int *dst_max, int grow_size)
86  *
87  * Parameters:
88  *              src             Specifies a pointer to a string.
89  *              dst             Specifies a pointer to the buffer to
90  *                                      to hold the information.
91  *              dst_size        Specifies the current size of 'dst'.
92  *                              Returns the new size of 'dst'.
93  *              dst_max         Specifies the current maximum size of 'dst'.
94  *                              Returns the new maximum size of 'dst'.
95  *              grow_size       Specifies the minimum grow size of 'dst'
96  *                                      when a malloc/realloc occurs.
97  *                              If this is less than one, 'dst' will
98  *                                      grow only large enough to hold
99  *                                      the new character.
100  *
101  * Returns:     0 if successful, -1 if errors.
102  *
103  * errno Values:
104  *              EINVAL
105  *              CEErrorMalloc
106  *
107  * Purpose:     (Re-)Allocates, if necessary, enough memory to hold the old
108  *                      information plus the byte.
109  *              Coverts the 0xXX value pointed to by src to a 0-256 by value.
110  *              Appends the character to the buffer pointed to 'dst'.
111  *              Updates 'dst_size' to include the new character.
112  *              Updates 'dst_max' to the new size of 'dst' if a
113  *                      malloc/realloc occurred.
114  *
115  *****************************************************************************/
116 int
117 _DtHelpCeAddOctalToBuf(
118         char     *src,
119         char    **dst,
120         int      *dst_size,
121         int      *dst_max,
122         int       grow_size )
123 {
124     char     tmp;
125     char    *dstPtr;
126     unsigned long value;
127
128     if (src == NULL ||
129                 dst == NULL || dst_size == NULL || dst_max == NULL ||
130                                 (*dst == NULL && (*dst_size || *dst_max)))
131       {
132         errno = EINVAL;
133         return -1;
134       }
135
136     dstPtr = *dst;
137     if ((*dst_size + 2) >= *dst_max)
138       {
139         if (grow_size > *dst_size + 3 - *dst_max)
140             *dst_max = *dst_max + grow_size;
141         else
142             *dst_max = *dst_size + 3;
143
144         if (dstPtr)
145             dstPtr = (char *) realloc ((void *) dstPtr, *dst_max);
146         else
147           {
148             dstPtr = (char *) malloc (sizeof(char) * (*dst_max));
149             *dst_size = 0;
150           }
151         *dst = dstPtr;
152       }
153
154     /*
155      * check to see if we have good memory
156      */
157     if (!dstPtr)
158       {
159         errno = CEErrorMalloc;
160         return -1;
161       }
162
163     tmp = src[4];
164     src[4] = '\0';
165     value = strtoul (src, NULL, 16);
166     src[4] = tmp;
167
168     if ((value == ULONG_MAX && errno == ERANGE) || value > 255 || value < 1)
169       {
170         errno = CEErrorFormattingValue;
171         return -1;
172       }
173
174     /*
175      * copy the source into the destination
176      */
177     dstPtr[*dst_size] = (char ) value;
178
179     /*
180      * adjust the pointers.
181      */
182     *dst_size = *dst_size + 1;
183
184     /*
185      * null the end of the buffer.
186      */
187     dstPtr[*dst_size] = '\0';
188
189     return 0;
190 }
191
192 /******************************************************************************
193  * Function: int __CEAppendCharToInfo (char **src, char **dst, int *dst_size,
194  *                                      int *dst_max, int grow_size)
195  *
196  * Parameters:
197  *              src             Specifies a pointer to a string.
198  *              dst             Specifies a pointer to the buffer to
199  *                                      to hold the information.
200  *              dst_size        Specifies the current size of 'dst'.
201  *                              Returns the new size of 'dst'.
202  *              dst_max         Specifies the current maximum size of 'dst'.
203  *                              Returns the new maximum size of 'dst'.
204  *              grow_size       Specifies the minimum grow size of 'dst'
205  *                                      when a malloc/realloc occurs.
206  *                              If this is less than one, 'dst' will
207  *                                      grow only large enough to hold
208  *                                      the new character.
209  *
210  * Returns:     0 if successful, -1 if errors.
211  *
212  * errno Values:
213  *              EINVAL
214  *              CEErrorMalloc
215  *
216  * Purpose:     (Re-)Allocates, if necessary, enough memory to hold the old
217  *                      information plus the new.
218  *              Appends the character pointed to by 'src' to the buffer
219  *                      pointed to 'dst'.
220  *              Updates 'src' to point to the next character after the
221  *                      one appended to 'dst'.
222  *              Updates 'dst_size' to include the new character.
223  *              Updates 'dst_max' to the new size of 'dst' if a
224  *                      malloc/realloc occurred.
225  *
226  *****************************************************************************/
227 int
228 _DtHelpCeAddCharToBuf(
229         char    **src,
230         char    **dst,
231         int      *dst_size,
232         int      *dst_max,
233         int       grow_size )
234 {
235     char    *srcPtr;
236     char    *dstPtr;
237
238     if (src == NULL || *src == NULL ||
239                 dst == NULL || dst_size == NULL || dst_max == NULL ||
240                                 (*dst == NULL && (*dst_size || *dst_max)))
241       {
242         errno = EINVAL;
243         return -1;
244       }
245
246     srcPtr = *src;
247     dstPtr = *dst;
248     if ((*dst_size + 2) >= *dst_max)
249       {
250         if (grow_size > *dst_size + 3 - *dst_max)
251             *dst_max = *dst_max + grow_size;
252         else
253             *dst_max = *dst_size + 3;
254
255         if (dstPtr)
256             dstPtr = (char *) realloc ((void *) dstPtr, *dst_max);
257         else
258           {
259             dstPtr = (char *) malloc (*dst_max);
260             *dst_size = 0;
261           }
262         *dst = dstPtr;
263       }
264
265     /*
266      * check to see if we have good memory
267      */
268     if (!dstPtr)
269       {
270         errno = CEErrorMalloc;
271         return -1;
272       }
273
274     /*
275      * copy the source into the destination
276      */
277     dstPtr[*dst_size] = *srcPtr++;
278
279     /*
280      * adjust the pointers.
281      */
282     *src = srcPtr;
283     *dst_size = *dst_size + 1;
284
285     /*
286      * null the end of the buffer.
287      */
288     dstPtr[*dst_size] = '\0';
289
290     return 0;
291 }
292
293 /******************************************************************************
294  * Function: int _DtHelpCeAddStrToBuf (char **src, char **dst, int *dst_size,
295  *                              int *dst_max, int copy_size, int grow_size)
296  * 
297  * Parameters:
298  *              src             Specifies a pointer to a string.
299  *              dst             Specifies a pointer to the buffer to
300  *                                      to hold the information.
301  *              dst_size        Specifies the current size of 'dst'.
302  *                              Returns the new size of 'dst'.
303  *              dst_max         Specifies the current maximum size of 'dst'.
304  *                              Returns the new maximum size of 'dst'.
305  *              copy_size       Specifies the number of characters to
306  *                                      copy from 'src' to 'dst'.
307  *              grow_size       Specifies the minimum grow size of 'dst'
308  *                                      when a malloc/realloc occurs.
309  *                              If this is less than one, 'dst' will
310  *                                      grow only large enough to hold
311  *                                      the new character.
312  *
313  * Returns:     0 if successful, -1 if errors.
314  *
315  * errno Values:
316  *              EINVAL
317  *              CEErrorMalloc
318  *
319  * Purpose:     Copys 'copy_size' number of characters of 'src'
320  *                      to 'dst'.
321  *              Updates 'src', to point after 'copy_size' number of
322  *                      characters.
323  *              Updates the 'dst_size' to reflect the number of characters
324  *                      copied.
325  *              If required, increments dst_max and (re)allocs memory
326  *                      to hold the extra 'copy_size' number of characters.
327  *
328  *****************************************************************************/
329 int
330 _DtHelpCeAddStrToBuf (
331         char    **src,
332         char    **dst,
333         int      *dst_size,
334         int      *dst_max,
335         int       copy_size,
336         int       grow_size )
337 {
338     char *srcPtr;
339     char *dstPtr;
340
341     /*
342      * check the input
343      */
344     if (src == NULL || *src == NULL || (((int)strlen(*src)) < copy_size)
345                 || dst == NULL || dst_size == NULL || dst_max == NULL
346                 || (*dst == NULL && (*dst_size || *dst_max)))
347       {
348         errno = EINVAL;
349         return -1;
350       }
351
352     srcPtr = *src;
353     dstPtr = *dst;
354
355     if ((*dst_size + copy_size + 1) >= *dst_max)
356       {
357         if (grow_size > (*dst_size + copy_size + 2 - *dst_max))
358             *dst_max = *dst_max + grow_size;
359         else
360             *dst_max = *dst_size + copy_size + 2;
361
362         if (dstPtr)
363             dstPtr = (char *) realloc ((void *) dstPtr, *dst_max);
364         else
365           {
366             dstPtr = (char *) malloc (*dst_max);
367             *dst_size = 0;
368           }
369         *dst = dstPtr;
370       }
371
372     if (!dstPtr)
373       {
374         errno = CEErrorMalloc;
375         return -1;
376       }
377
378     /*
379      * make sure there is a null byte to append to.
380      */
381     dstPtr[*dst_size] = '\0';
382
383     /*
384      * copy the source into the destination
385      */
386     strncat (dstPtr, srcPtr, copy_size);
387
388     /*
389      * adjust the pointers
390      */
391     *src = srcPtr + copy_size;
392     *dst_size = *dst_size + copy_size;
393
394     return 0;
395 }
396
397 /******************************************************************************
398  * Function: int _DtHelpCeGetNxtBuf (FILE *file, char *dst, char **src,
399  *                                      int max_size)
400  * 
401  * Parameters:
402  *              file            Specifies a stream to read from.
403  *              dst             Specifies the buffer where new information
404  *                                      is placed.
405  *              src             Specifies a pointer into 'dst'. If there
406  *                                      is information left over, it
407  *                                      is moved to the begining of 'dst'.
408  *                              Returns 'src' pointing to 'dst'.
409  *              max_size        Specifies the maximum size of 'dst'.
410  *
411  * Returns:      0 if this is the last buffer that can be read for the topic.
412  *              -1 if errors.
413  *              >0 if more to be read.
414  *
415  * errno Values:
416  *              read (2)        Errors set via a read call.
417  *              EINVAL
418  *              CEErrorReadEmpty
419  *
420  * Purpose:     Reads the next buffer of information.
421  *
422  *****************************************************************************/
423 int
424 _DtHelpCeGetNxtBuf(
425     BufFilePtr    file,
426     char         *dst,
427     char        **src,
428     int           max_size)
429 {
430     int leftOver;
431     int result;
432
433     if (file == NULL)
434       {
435         errno = EINVAL;
436         return -1;
437       }
438
439     (void ) strcpy (dst, (*src));
440     leftOver = strlen (dst);
441
442     result = _DtHelpCeReadBuf (file, &(dst[leftOver]), (max_size - leftOver));
443
444     /*
445      * check to see if we ran into trouble reading this buffer
446      * of information. If not reset the pointer to the beginning
447      * of the buffer.
448      */
449     if (result != -1)
450         *src = dst;
451
452     return result;
453 }
454
455
456 /******************************************************************************
457  * Function:    int  _DtHelpCeReadBuf (FILE *file, char *buffer, int size)
458  *
459  * Parameters:  FILE            Specifies the stream to read from.
460  *              buffer          Specifies a buffer to read information
461  *                                      into.
462  *              size            Specifies the maximum number of bytes
463  *                              'buffer' can contain. It should never be
464  *                              larger than 'buffer' can hold, but it can
465  *                                      be smaller.
466  *
467  * Returns:      0 if this is the last buffer that can be read for the topic.
468  *              -1 if errors.
469  *              >0 if more to be read.
470  *
471  * errno Values:
472  *              read (2)        Errors set via a read call.
473  *
474  * Purpose:     Get size-1 number of bytes into a buffer and possibly
475  *              check for page markers imbedded within the text.
476  *
477  *****************************************************************************/
478 int
479 _DtHelpCeReadBuf(
480     BufFilePtr   file,
481     char        *buffer,
482     int          size)
483 {
484     int    flag;
485
486     /*
487      * take into account the last byte must be an end of string marker.
488      */
489     size--;
490
491     flag = _DtHelpCeBufFileRd(file, buffer, size);
492
493     if (flag != -1)
494         buffer[flag] = '\0';
495
496     return flag;
497
498 } /* End _DtHelpCeReadBuf */
499
500 /******************************************************************************
501  * Function:    char *_DtHelpGetNxtToken (char *str, char **retToken)
502  *
503  * Parameters:
504  *              str             The string (in memory) which is being
505  *                                      parsed.
506  *              retToken        Returns the next token from the input.
507  *                              Valid tokens are strings of non-whitespace
508  *                              characters, newline ("\n"), and
509  *                              end-of-data (indicated by a zero length
510  *                              string).
511  *
512  *                              A NULL value indicates an error.
513  *
514  *                              Newline or zero length strings are
515  *                              not owned by the caller.
516  *
517  *                              Otherwise, the memory for the returned
518  *                              token is owned by the caller.
519  *
520  * Return Value:        Returns the pointer to the next unparsed character in
521  *                      the input string. A NULL value indicates an error.
522  *
523  * errno Values:
524  *              EINVAL
525  *              CEErrorMalloc
526  *
527  * Purpose:     Parse tokens in resource string values.
528  *
529  *****************************************************************************/
530 char *
531 _DtHelpGetNxtToken (
532     char        *str,
533     char        **retToken)
534 {
535     int          len = 1;
536     char        *start;
537     char        *token;
538     short        quote = False;
539     short        done = False;
540
541     if (retToken) *retToken = NULL;  /* tested in caller code */
542
543     if (str == NULL || *str == '\0' || retToken == NULL)
544       {
545         errno = EINVAL;
546         return NULL;
547       }
548
549     /* Find the next token in the string.  The parsing rules are:
550
551          - Whitespace (except for \n) separates tokens.
552          - \n is a token itself.
553          - The \0 at the end of the string is a token.
554      */
555
556     /* Skip all of the whitespace (except for \n). */
557     while (*str && (*str != '\n') && isspace (*str))
558         str++;
559
560     /* Str is pointing at the start of the next token.  Depending on the
561        type of token, malloc the memory and copy the token value. */
562     if (*str == '\0')
563         token = str;
564
565     else if (*str == '\n') {
566         token = str;
567         str++;
568     }
569
570     else {
571         /* We have some non-whitespace characters.  Find the end of */
572         /* them and copy them into new memory. */
573         if ((MB_CUR_MAX == 1 || mblen (str, MB_CUR_MAX) == 1) && *str == '\"')
574           {
575             /*
576              * found a quoted token - skip the quote.
577              */
578             quote = True;
579             str++;
580           }
581
582         start = str;
583         while (*str && !done)
584           {
585             /*
586              * get the length of the item.
587              */
588             len = 1;
589             if (MB_CUR_MAX != 1)
590               {
591                 len = mblen (str, MB_CUR_MAX);
592                 if (len < 0)
593                     len = 1;
594               }
595             if (len == 1)
596               {
597                 /*
598                  * check for the token terminator
599                  */
600                 if ((quote && *str == '\"') ||
601                                 (!quote && (isspace (*str) || *str == '\n')))
602                     done = True;
603                 else
604                     str++;
605               }
606             else
607                 str += len;
608           }
609
610         /*
611          * determine the length of the token.
612          */
613         token = (char *) malloc ((str - start + 1) * sizeof (char));
614         if (token)
615           {
616             strncpy (token, start, str - start);
617             *(token + (str - start)) = '\0';
618           }
619         else
620             errno = CEErrorMalloc;
621
622         /*
623          * skip the quote terminator
624          */
625         if (quote && len == 1 && *str == '\"')
626             str++;
627     }
628
629     *retToken = token;
630     return (str);
631 }
632
633 /******************************************************************************
634  * Function:    _DtCvSegment *_DtHelpAllocateSegments(int malloc_size)
635  *
636  * Parameters:
637  *              malloc_size     Specifies the number of segments to
638  *                              allocate if 'alloc_size' is NULL or less
639  *                              than 1 or if '*next_seg' is NULL.  The
640  *                              first one is returned to the caller with
641  *                              the rest in 'next_seg' if 'next_seg' is
642  *                              non-NULL.
643  *
644  * Return Value:
645  *              non-null   If succeeds.
646  *              Null       If failure.
647  *
648  * Purpose:  Allocate a block of segments, zeros the structures, and
649  *           sets the link_idx of each structure to -1.
650  *
651  *****************************************************************************/
652 _DtCvSegment *
653 _DtHelpAllocateSegments (
654      int          malloc_size)
655 {
656     int                  i;
657     _DtCvSegment        *newSeg;
658     FrmtPrivateInfo     *p;
659
660     /*
661      * don't allow zero or negative allocations
662      */
663     if (malloc_size < 1)
664         malloc_size = 1;
665
666     /*
667      * allocate the block of segments
668      */
669     newSeg = (_DtCvSegment *) calloc (sizeof(_DtCvSegment), malloc_size);
670     if (NULL != newSeg)
671       {
672         /*
673          * now allocate the same number of private information structures
674          */
675         p = (FrmtPrivateInfo *) calloc (sizeof(FrmtPrivateInfo), malloc_size);
676         if (NULL != p)
677           {
678             /*
679              * mark the first item as the top block.  Since it will be
680              * attached to the first segment, it marks both the segment
681              * and the private information blocks for later frees.
682              */
683             p->top_block = True;
684             for (i = 0; i < malloc_size; i++)
685               {
686                 /*
687                  * invalidate the link index and attach a private
688                  * information structure to each segment
689                  */
690                 newSeg[i].link_idx   = -1;
691                 newSeg[i].client_use = p++;
692               }
693           }
694         else
695           {
696             /*
697              * had trouble allocating the private information.
698              * free the new segment list and return NULL as an error.
699              */
700             free(newSeg);
701             newSeg = NULL;
702           }
703       }
704
705     return newSeg;
706 }
707
708 /******************************************************************************
709  * Function:    int _DtHelpFmtFindBreak (char *ptr, int mb_len, int *num_chars)
710  *
711  * Parameters:
712  *              ptr             Specifies the string to check.
713  *              mb_len          Specifies if the sequence should be single
714  *                              byte or multi-byte.
715  *              num_chars       Returns the character count.
716  *
717  * Returns      number of bytes in the sequence.
718  *
719  * errno Values:
720  *
721  * Purpose:     Find a length of 'ptr' comprised of multi or single byte
722  *              characters.
723  *
724  *****************************************************************************/
725 int
726 _DtHelpFmtFindBreak (
727     char *ptr,
728     int   mb_len,
729     int  *num_chars)
730 {
731     int   len = 0;
732     int   numChars = 0;
733     int   mySize;
734     short done = 0;
735
736     while (0 == done && '\0' != *ptr)
737       {
738         mySize = mblen(ptr, MB_CUR_MAX);
739         done   = 1;
740         if (0 < mySize &&
741                 ((1 != mb_len && 1 != mySize) || (1 == mb_len && 1 == mySize)))
742           {
743             numChars++;
744             ptr  += mySize;
745             len  += mySize;
746             done  = 0;
747           }
748       }
749
750     *num_chars = numChars;
751     return len;
752 }
753
754 /******************************************************************************
755  * Function:    _DtHelpLoadMultiInfo
756  *
757  * Returns:     Loads the multi-byte formatting table for the current locale.
758  *
759  *****************************************************************************/
760 void
761 _DtHelpLoadMultiInfo (
762     wchar_t  **cant_begin_chars,
763     wchar_t  **cant_end_chars,
764     short     *nl_to_space)
765 {
766 #ifndef NO_MESSAGE_CATALOG
767     int       len;
768     char     *ptr;
769     nl_catd   cat_fd;
770
771     cat_fd = catopen ("fmt_tbl", NL_CAT_LOCALE);
772     if (cat_fd != ((nl_catd) -1))
773       {
774         /*
775          * Get the list of characters that can't begin a line.
776          */
777         ptr = catgets (cat_fd, 1, 1, "");
778         len = strlen (ptr) + 1;
779         *cant_begin_chars = (wchar_t *) malloc (len * sizeof (wchar_t));
780         if (NULL != *cant_begin_chars &&
781                                 mbstowcs(*cant_begin_chars, ptr, len) == -1)
782           {
783             free (*cant_begin_chars);
784             *cant_begin_chars = NULL;
785           }
786
787         /*
788          * Get the list of characters that can't end a line.
789          */
790         ptr = catgets (cat_fd, 1, 2, "");
791         len = strlen (ptr) + 1;
792         *cant_end_chars = (wchar_t *) malloc (len * sizeof (wchar_t));
793         if (*cant_end_chars != NULL &&
794                                 mbstowcs(*cant_end_chars, ptr, len) == -1)
795           {
796             free (*cant_end_chars);
797             *cant_end_chars = NULL;
798           }
799
800         /*
801          * Get the spacing flag. I.E. when does a internal newline
802          * get turned into a space.
803          *      1 means all the time.
804          *      0 means only between a multibyte string and
805          *        a singlebyte string.
806          */
807         ptr = catgets (cat_fd, 1, 3, "1");
808         *nl_to_space = atoi(ptr);
809
810         catclose (cat_fd);
811       }
812     else
813 #endif
814       {
815         *cant_begin_chars = NULL;
816         *cant_end_chars   = NULL;
817         *nl_to_space      = 1;
818       }
819 }