2 * COMPONENT_NAME: austext
4 * FUNCTIONS: austext_malloc
18 * (C) COPYRIGHT International Business Machines Corp. 1991,1995
20 * Licensed Materials - Property of IBM
21 * US Government Users Restricted Rights - Use, duplication or
22 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
24 /*************************** MSGUTIL.C *************************
25 * $XConsortium: msgutil.c /main/9 1996/11/25 18:47:48 drk $
27 * Utilites for generic manipulation of linked lists and binary trees.
28 * All utilities require that the link fields be the first fields
29 * in each structure. Also includes an error message mechanism
30 * that is absolutely independent of user interface (UI)--
31 * not even stdio is used. The trick is to append all
32 * error/information messages to the end of a linked list that
33 * eventually will be returned to the UI to be displayed
34 * in whatever manner is appropriate. (However a generic stdio
35 * print facility for the linked msglists is also provided
36 * for dumping the list before crashing or when stdio is ok).
38 * With 2 exceptions, all messages should contain only
39 * printable ascii characters and the space character.
40 * Control characters and extended ascii graphics chars are verboten.
41 * The exceptions are \n and \r, which are always permitted.
43 * The most common fatal error in opera and other text analysis
44 * systems occurs when a memory allocation fails. Therefore
45 * this module contains a generic 'safe malloc' function
46 * which tests for failure and prints all outstanding messages
47 * before exiting if malloc fails. It also uses DtSearchExit()
48 * for the actual abort so other system dependent stuff can be
49 * performed before going down.
52 * Revision 2.4 1996/03/05 17:58:29 miker
53 * Replace isspace() with ref to locale independent ascii_charmap[].
55 * Revision 2.3 1995/10/25 16:46:00 miker
58 * Revision 2.2 1995/10/19 20:58:05 miker
59 * Fix segfault in cleanwrap() if text contains single word > wraplen.
61 * Revision 2.1 1995/09/22 21:20:35 miker
62 * Freeze DtSearch 0.1, AusText 2.1.8
64 * Revision 1.8 1995/09/05 18:21:23 miker
65 * For DtSearch, rename and move universal msglist functions to msgs.c.
68 /****#include <ctype.h>****/
71 #define XOS_USE_NO_LOCKING
72 #define X_INCLUDE_TIME_H
73 #include <X11/Xos_r.h>
75 #define PROGNAME "MSGUTIL"
76 #define MAX_LINELEN 77
79 CMPLL compare_llist = NULL; /* global function pointer */
81 /************************************************/
85 /************************************************/
86 /* Returns ptr to static string where the current time
87 * is formatted in human readable form. Used in audit
88 * records, debugging logs, and error messages.
90 char *nowstring (time_t * now)
95 _Xltimeparams localtime_buf;
101 time_ptr = _XLocaltime(now, localtime_buf);
102 strftime (buf, sizeof (buf),
103 catgets (dtsearch_catd, MS_misc, 2, "%Y/%m/%d,%H:%M:%S"),
109 /************************************************/
113 /************************************************/
114 /* Frees storage for all items in an LLIST and
115 * sets it top-of-list pointer to NULL. This works only for lists
116 * created by append_msglist, append_textblobs, and other LLIST
117 * structures where the data and the node itself
118 * are allocated in one call to malloc().
120 void free_llist (LLIST ** llhead)
133 /************************************************/
137 /************************************************/
138 /* Merges two list by appending sublist to end of mainlist,
139 * then setting sublist to NULL. Originally either list can be NULL.
140 * Mainlist is represented by ptr to ptr so it can be modified if NULL.
141 * Sublist is ptr to ptr so it can be SET to NULL after join.
143 * Init pp = ptr to first 'link' field in list,
144 * i.e. the ptr to a ptr passed by the caller.
145 * Usually this will be the addr of the top of the list pointer.
146 * Then advance it to point to the last link in the list.
148 * Note that this function works for any LLIST including those
149 * whose 'data' pointer is not allocated concurrently with the node.
151 void join_llists (LLIST ** mainlist, LLIST ** sublist)
154 for (pp = mainlist; *pp != NULL; pp = &((*pp)->link));
158 } /* join_llists() */
161 /************************************************/
165 /************************************************/
166 /* Detaches first node in an llist and returns it.
167 * If *llistp is empty return NULL, else set *llistp to the link
168 * cell of the first LLIST node on *llistp and return a pointer to
169 * the first LLIST node on *llistp. Used mainly by merge_llist(),
170 * which is itself called by sort_llist(), but can be called by
171 * anyone needing to remove the first element of an llist.
173 LLIST *pop_llist (LLIST ** llistp)
175 LLIST *first_node = *llistp;
176 if (first_node != NULL)
177 *llistp = first_node->link;
182 /************************************************/
186 /************************************************/
187 /* Detaches any specified node in an llist and rejoins the
188 * loose ends of the llist. *llistp may become NULL.
189 * Returns NULL if *llistp is initially NULL or if node is not on llist,
190 * else returns the detached node.
192 LLIST *cutnode_llist (LLIST * node, LLIST ** llistp)
194 LLIST **pp; /* link addr pointing to current node */
195 for (pp = llistp; *pp != NULL; pp = &((*pp)->link)) {
201 *pp = node->link; /* join the loose ends */
203 } /* cutnode_llist() */
206 /************************************************/
210 /************************************************/
211 /* Subroutine of sort_llist().
212 * Find the middle node in lst. Set its 'link' pointer to NULL.
213 * Return the remainder of lst, i.e. a pointer to the
214 * next node after the middle node.
216 static LLIST *split_llist (LLIST * lst)
218 LLIST *tail = lst->link;
219 if (lst == NULL || tail == NULL)
221 /* advance 'tail' to end of list, and advance 'lst' only half as often */
222 while ((tail != NULL) && ((tail = tail->link) != NULL)) {
229 } /* split_llist() */
232 /************************************************/
236 /************************************************/
237 /* Subroutine of sort_llist(). Merges two sorted LLISTs together. */
238 static LLIST *merge_llist (LLIST * l1, LLIST * l2)
240 LLIST *myqueue = NULL;
244 while ((l1 != NULL) && (l2 != NULL)) {
246 * Perform ENQUEUE function. Next item popped off a list is
247 * the next one in sorted order. It is added to END of
248 * myqueue to maintain order. THIS IS WHERE THE ACTUAL SORT
249 * COMPARE FUNCTION IS PERFORMED.
251 mynext = (compare_llist (l1, l2) < 0) ?
252 pop_llist (&l1) : pop_llist (&l2);
257 myend->link = mynext;
261 /* attach the remainder of whichever list is left to the end of queue */
267 } /* merge_llist() */
270 /************************************************/
274 /************************************************/
275 /* Sorts a list of LLIST structures and returns ptr to sorted list.
276 * The basic idea is to sort by recursively splitting a list
277 * into two equal halves and sorting each of those. The recursion
278 * ends when there are only two small lists which are either
279 * already sorted or are swapped. This sort rarely runs out
280 * of stack space because each recursion cuts the list length in
281 * half so there are at most 1 + log-N-to-the-base-2 items on the stack.
282 * (e.g. 64,000 nodes = max stack depth of 16: 2**16 = 64K).
284 * The compare function accepts pointers to two LLIST structures.
285 * It returns <0, =0, or >0 based on whether the first structure (left)
286 * is <, =, or > the second (right) structure in sort order.
287 * For efficiency's sake, a pointer to the compare function is placed
288 * in the global variable 'compare_llist' before calling
289 * sort_llist the first time, rather than continually
290 * passing it to all these nested functions.
292 LLIST *sort_llist (LLIST * lst)
295 if ((lst == NULL) || (lst->link == NULL))
297 lst2 = split_llist (lst);
298 return merge_llist (sort_llist (lst), sort_llist (lst2));
302 /************************************************/
306 /************************************************/
307 /* 'location' may be NULL. Last arg (formerly msglist) is isgnored.
308 * Renamed from safe_malloc() to force compile errors if args not changed.
310 void *austext_malloc (size_t size, char *location, void *ignore)
315 if ((ptr = malloc (size)) != NULL)
318 ptr = ((aa_argv0) ? aa_argv0 : "");
319 if (location == NULL)
320 location = catgets (dtsearch_catd, MS_misc, 1, "<null>");
321 outofmem_msg = catgets (dtsearch_catd, MS_misc, 3,
322 "*** %sOut of Memory at %s asking for %lu bytes! ***\n");
323 fprintf (aa_stderr, outofmem_msg, ptr, location, size);
326 fprintf (aa_stderr, "%s\n", DtSearchGetMessages ());
330 } /* austext_malloc */
332 /************************************************/
336 /************************************************/
337 /* Utility which provides a way of breaking up long
338 * messages which contain no control characters into lines
339 * whose linefeed breaks occur at even word boundaries,
340 * and whose lengths are controlled by the caller.
341 * It should only be called when text itself may be
342 * modified and may safely contain a linefeed (ascii 0x0A).
343 * Converts a long text string into several lines by overlaying
344 * the whitespace char nearest to the end of every line with \n.
345 * Restarts length count if \n is found already within string.
346 * Does nothing if passed wrap length = 0.
347 * Does not append \n to end of string. Does not alter string length.
348 * Returns total number of lines (\n's + trailing piece of last line),
349 * or returns 0 if wraplen == 0.
351 int clean_wrap (char *string, int wraplen)
353 char *nlptr, *breakptr;
358 while (strlen (string) > wraplen) {
359 breakptr = string + wraplen;
361 /* Look for \n within the next wraplen */
362 for (nlptr = string; nlptr < breakptr; nlptr++)
365 if (nlptr < breakptr) {
370 /* Otherwise back up to the first whitespace before last word */
371 for (nlptr = breakptr - 1; nlptr > string; nlptr--)
372 if (ascii_charmap[*nlptr] & WHITESPACE) {
378 /* No whitespace at all in "text" before wraplen!
379 * No choice but to overlay the last char.
381 *(--breakptr) = '\n';
388 /* Done wrapping. now just count remaining lines in string. */
390 if (*string++ == '\n')
392 if (*(string - 1) != '\n')
397 /********************** MSGUTIL.C *************************/