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