Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtSearch / dtsrjoint.c
1 /*
2  *   COMPONENT_NAME: austext
3  *
4  *   FUNCTIONS: DtSearchFormatObjdate
5  *              DtSearchFreeResults
6  *              DtSearchGetMaxResults
7  *              DtSearchMergeResults
8  *              DtSearchSetMaxResults
9  *              DtSearchSortResults
10  *              DtSearchValidDateString
11  *              aa_check_initialization
12  *              aa_envars
13  *              aa_get_passwd
14  *              ditto_pop
15  *              ditto_sort
16  *              ditto_split
17  *              merge_by_date
18  *              merge_by_prox
19  *
20  *   ORIGINS: 27
21  *
22  *
23  *   (C) COPYRIGHT International Business Machines Corp. 1995, 1996
24  *   All Rights Reserved
25  *   Licensed Materials - Property of IBM
26  *   US Government Users Restricted Rights - Use, duplication or
27  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
28  */
29 /**************************** DTSRJOINT.C *************************
30  * $XConsortium: dtsrjoint.c /main/5 1996/06/23 16:47:42 cde-ibm $
31  * February 1995.
32  * Split off of ausapi.c when I converted to client/server model.
33  * Ausclt.c is virtual copy of ausapi functions in RPC client stub.
34  * This module contains ausapi utilities that are common to both
35  * sides (ie do not result in RPC call to server or require
36  * access to real usrblk).  My original version of this program
37  * was called "aacom" for "ausapi common" but I renamed it to avoid
38  * conflict with aacomm, the rpc/sockets communications module.
39  *
40  * $Log$
41  * Revision 2.5  1996/04/10  19:52:16  miker
42  * Added locale independent ISDIGIT macro.  Changed function name
43  * from aa_merge_dittolists to DtSearchMergeResults.
44  *
45  * Revision 2.4  1995/12/27  16:11:54  miker
46  * Remove save_init_dbxxx globals for DtSearchReinit().
47  *
48  * Revision 2.3  1995/10/25  22:16:09  miker
49  * Renamed from aajoint.c.  Added prolog.
50  *
51  * Log: aajoint.c,v
52  * Revision 2.2  1995/10/02  20:14:39  miker
53  * DtSearch function name changes.
54  * Added const to strdup() prototype for greater portability.
55  * Changed to more uniform output format in DtSearchFormatObjdate().
56  *
57  * Revision 2.1  1995/09/22  15:18:08  miker
58  * Freeze DtSearch 0.1, AusText 2.1.8
59  *
60  * Revision 1.3  1995/08/31  21:31:48  miker
61  * Renames and minor changes for DtSearch.
62  *
63  * Revision 1.2  1995/06/22  18:01:09  miker
64  * 2.1.6: RPC version no longer user specifiable.  Removed aa_versnum etc.
65  * Added aa_sort_dittolist, date sorting of hitlists by any client.
66  */
67 #include "SearchE.h"
68 #include <termios.h>
69 #include <unistd.h>
70 #include <string.h>
71 #include <stdlib.h>
72
73 #ifndef strdup
74 extern char    *strdup (const char *s);
75 #endif
76
77 #define PROGNAME        "DTSRJOINT"
78 #define ISDIGIT(c) ((ascii_charmap [(unsigned int) (c)] & NUMERAL) != 0)
79
80 /*------------- GLOBALS VISIBLE TO CALLER -------------------*/
81 /* see also globals.c */
82 char          **ausapi_dbnamesv = NULL; /* array of database names */
83 int             ausapi_dbnamesc = 0;    /* size of dbnames array */
84 int             aa_maxhits = DtSrMAXHITS;       /* num hits retnd after
85                                                  * srch */
86
87 /*------------------- PRIVATE GLOBALS ---------------------*/
88 int             aa_is_initialized = FALSE;
89 int             aa_getnews_flag = 1;
90 long            save_init_switches = 0L;
91 int             ditsort_type = 0;
92
93
94 /************************************************/
95 /*                                              */
96 /*             aa_check_initialization          */
97 /*                                              */
98 /************************************************/
99 /* Confirms ausapi_init() was first function called. */
100 void            aa_check_initialization (void)
101 {
102     if (aa_is_initialized)
103         return;
104     fprintf (aa_stderr,
105         catgets (dtsearch_catd, 2, 37,
106             "%s First API function call must be DtSearchInit().\n"),
107         PROGNAME"37");
108     DtSearchExit (37);
109 }  /* aa_check_initialization() */
110
111
112 /************************************************/
113 /*                                              */
114 /*           DtSearchValidDateString            */
115 /*                                              */
116 /************************************************/
117 /* Subroutine of aa_both_valid_dates(), called once for each date string,
118  * or can be called from user interface to validate a date string.
119  * Converts passed date string into valid AusText DtSrObjdate.
120  * Date string format:  "[yy]yy [mm [dd]]",
121  * 1, 2, or 3 numeric tokens separated by one
122  * or more nonnumeric chars (whitespace, slashes, etc).
123  * The first token is a complete year number integer yyyy
124  * in the range 1900 <= yyyy <= 5995.  If the first token contains
125  * less than 4 digits it is presumed to be the number of years
126  * since 1900.  The month number is the second token mm,
127  * in the range 1 - 12, and the third token dd is the day, 1 - 31.
128  * If only two tokens are in the string they are presumed to
129  * be year and month; the day is presumed to be to 1.
130  * If only one token is in the string it is presumed to
131  * be the year; the month is presumed to be 1 and the day
132  * is presumed to be 1.  NULL and empty strings are always
133  * valid.  They mean no date exclusion in the search
134  * and this function returns objdate 0 for them.
135  * Returns objdate on successful parse and conversion.
136  * Returns -1 and err msg if date string is invalid.
137  */
138 DtSrObjdate     DtSearchValidDateString (char *datestr)
139 {
140     DtSrObjdate     objdate = 0L;
141     char            parsebuf[64];
142     char           *startp, *endp;
143     int             yy, mm, dd;
144     int             stop_parse = FALSE;
145     char            msgbuf[256];
146
147     aa_check_initialization();
148     if (datestr == NULL)        /* null string is valid */
149         return 0L;
150     if (datestr[0] == 0)        /* empty string is valid */
151         return 0L;
152
153     strncpy (parsebuf, datestr, sizeof (parsebuf));
154     parsebuf[sizeof (parsebuf) - 1] = 0;
155
156     /* yyyy */
157     for (startp = parsebuf; *startp != 0 && !ISDIGIT(*startp); startp++);
158     if (*startp == 0) { /* no numeric digits in string */
159 #ifdef DEBUG_VALDATE
160         fprintf (aa_stderr, PROGNAME"269 No numeric digits in string\n");
161 #endif
162 INVALID_DATESTR:
163         sprintf (msgbuf,
164             catgets (dtsearch_catd, 2, 115,
165                 "%s '%s' is invalid or incomplete date string.\n"
166                 "The correct format is '[yy]yy [mm [dd]]'."),
167             PROGNAME"115", datestr);
168         DtSearchAddMessage (msgbuf);
169 #ifdef DEBUG_VALDATE
170         fprintf (aa_stderr, PROGNAME"278 Returning objdate -1\n");
171         fflush (aa_stderr);
172 #endif
173         return -1L;
174     }
175     for (endp = startp; ISDIGIT(*endp); endp++);
176     if (*endp == 0) {   /* mm and dd both missing */
177         mm = 1;
178         dd = 1;
179         stop_parse = TRUE;
180 #ifdef DEBUG_VALDATE
181         fprintf (aa_stderr, PROGNAME"286 mm and dd both missing\n");
182 #endif
183     }
184     *endp = 0;
185     yy = atoi (startp);
186 #ifdef DEBUG_VALDATE
187         fprintf (aa_stderr, PROGNAME"293 yystr='%s' yy=%d\n", startp, yy);
188 #endif
189     if (strlen (startp) < 4)
190         yy += 1900;
191     if (yy < 1900 || yy > 5995)
192         goto INVALID_DATESTR;
193     if (stop_parse)
194         goto DATESTR_OK;
195
196     /* mm */
197     for (startp = ++endp; *startp != 0 && !ISDIGIT(*startp); startp++);
198     if (*startp == 0) { /* no mm in string */
199         mm = 1;
200         dd = 1;
201 #ifdef DEBUG_VALDATE
202         fprintf (aa_stderr, PROGNAME"309 No mm in string\n");
203 #endif
204         goto DATESTR_OK;
205     }
206     for (endp = startp; ISDIGIT(*endp); endp++);
207     if (*endp == 0) {   /* no dd in string */
208         dd = 1;
209         stop_parse = TRUE;
210 #ifdef DEBUG_VALDATE
211         fprintf (aa_stderr, PROGNAME"318 No dd in string\n");
212 #endif
213     }
214     *endp = 0;
215     mm = atoi (startp);
216 #ifdef DEBUG_VALDATE
217         fprintf (aa_stderr, PROGNAME"293 mmstr='%s' mm=%d\n", startp, mm);
218 #endif
219     if (mm < 1 || mm > 12)
220         goto INVALID_DATESTR;
221     if (stop_parse)
222         goto DATESTR_OK;
223
224     /* dd */
225     for (startp = ++endp; *startp != 0 && !ISDIGIT(*startp); startp++);
226     if (*startp == 0) { /* no dd in string */
227         dd = 1;
228 #ifdef DEBUG_VALDATE
229         fprintf (aa_stderr, PROGNAME"336 No dd in string\n");
230 #endif
231         goto DATESTR_OK;
232     }
233     for (endp = startp; ISDIGIT(*endp); endp++);
234     *endp = 0;
235     dd = atoi (startp);
236 #ifdef DEBUG_VALDATE
237         fprintf (aa_stderr, PROGNAME"344 ddstr='%s' dd=%d\n", startp, dd);
238 #endif
239     if (dd < 1 || dd > 31)
240         goto INVALID_DATESTR;
241
242 DATESTR_OK:
243     objdate = (yy - 1900) << 20 | (mm-1) << 16 | dd << 11;
244 #ifdef DEBUG_VALDATE
245         fprintf (aa_stderr,
246             PROGNAME"352 Returning objdate %08lx, %d/%d/%d, %s\n",
247             objdate, yy, mm, dd, objdate2fzkstr(objdate));
248         fflush (aa_stderr);
249 #endif
250     return objdate;
251 }  /* DtSearchValidDateString() */
252
253
254 /************************************************/
255 /*                                              */
256 /*            DtSearchFormatObjdate             */
257 /*                                              */
258 /************************************************/
259 /* Converts objdate in hitlist to displayable string */
260 char    *DtSearchFormatObjdate (DtSrObjdate objdate)
261 {
262     static char     datestr_buf[24];
263     if (objdate == 0L)
264         return "(undated)";
265     strftime (datestr_buf, sizeof(datestr_buf), "%Y.%m.%d",
266         objdate2tm (objdate));
267     return datestr_buf;
268 }  /* DtSearchFormatObjdate() */
269
270
271 /************************************************/
272 /*                                              */
273 /*              DtSearchGetMaxResults           */
274 /*              DtSearchSetMaxResults           */
275 /*                                              */
276 /************************************************/
277 int     DtSearchGetMaxResults (void)
278 { return aa_maxhits; }
279
280 void    DtSearchSetMaxResults (int newmax)
281 { aa_maxhits = newmax; }
282
283
284 /************************************************/
285 /*                                              */
286 /*              DtSearchFreeResults             */
287 /*                                              */
288 /************************************************/
289 /* Frees storage allocated for the dittolist created
290  * by DtSearchQuery(), and sets dittolist pointer to NULL.
291  * Always returns DtSrOK.
292  */
293 int             DtSearchFreeResults (DtSrResult ** dittolist)
294 {
295     free_llist ((LLIST **) dittolist);
296     return DtSrOK;
297 }  /* DtSearchFreeResults() */
298
299
300 /************************************************/
301 /*                                              */
302 /*              DtSearchMergeResults            */
303 /*                                              */
304 /************************************************/
305 /* Merges one dittolist into another using proximity
306  * for sort order.  Sets 'src' dittolist pointer to NULL.
307  * Does not change dittocount of either list, user must do that.
308  * Returns DtSrOK on successful merge,
309  * else returns DtSrERROR for programming error.
310  * Formerly named aa_merge_dittolists.
311  */
312 int     DtSearchMergeResults (DtSrResult **targ_list, DtSrResult **src_list)
313 {
314     DtSrResult     *targ, *src, *nextsrc, **prevtargl;
315
316     if (src_list == NULL || targ_list == NULL)
317         return DtSrERROR;
318
319     /* If there's no 'src' list, return with no changes.
320      * In other words, merge was successful before we started.
321      */
322     if (*src_list == NULL)
323         return DtSrOK;
324
325     /* At this point we know there is a src_list.
326      * If there's no targ_list, just swap the pointers.
327      */
328     if (*targ_list == NULL) {
329         *targ_list = *src_list;
330         *src_list = NULL;
331         return DtSrOK;
332     }
333
334     /* We have two nonempty lists.  Now do a real merge.
335      * Insert src node into targ list only if it's
336      * proximity is smaller.  Otherwise just
337      * advance to the next targ node.
338      */
339     src = *src_list;    /* curr src_list node to compare */
340     nextsrc = src->link;        /* next src_list node to compare */
341     targ = *targ_list;  /* curr targ_list node to compare */
342     prevtargl = targ_list;      /* prev targ_list node link pointer */
343     while (src != NULL && targ != NULL) {
344         if (src->proximity < targ->proximity) {
345             /* Insert src node into targ list */
346             *prevtargl = src;
347             src->link = targ;
348             prevtargl = &src->link;
349             src = nextsrc;
350             if (src)
351                 nextsrc = src->link;
352         }
353         else {
354             /* just advance to next targ node */
355             prevtargl = &targ->link;
356             targ = targ->link;
357         }
358     }
359
360     /* If there's any src_list left,
361      *  just tack it onto the end of the targ_list.
362      */
363     if (src) {
364         /* advance to end of targ_list */
365         while (targ) {
366             prevtargl = &targ->link;
367             targ = targ->link;
368         }
369         *prevtargl = src;
370     }
371
372     *src_list = NULL;
373     return DtSrOK;
374 }  /* DtSearchMergeResults() */
375
376
377 /************************************************************
378  * Aa_sort_dittolist(): basically a copy of ditto_sort() from engine,
379  * but will sort on several different ditto fields
380  * (currently only date fields implemented),
381  * and is only used as convenience function on client/gui side.
382  * The proximity field is always the minor sort key.
383  * All functions with similar names to engine functions are static
384  * in this module so they won't collide with same function in engine.
385  * Performs a recursive split-merge sort on ditto lists.
386  ************************************************************/
387
388 /************************************************/
389 /*                                              */
390 /*                 ditto_pop                    */
391 /*                                              */
392 /************************************************/
393 /* Subroutine of DtSearchSortResults().
394  * Detaches first node in a list and returns it...
395  * If *lst is empty return NULL, else set *lst to the link
396  * cell of the first DtSrResult node on *lst and return a pointer to
397  * the first DtSrResult node on *lst.
398  */
399 static DtSrResult *ditto_pop (DtSrResult ** lst)
400 {
401     DtSrResult     *first_node;
402
403     first_node = *lst;
404     if (first_node != NULL)
405         *lst = first_node->link;
406     return first_node;
407 }  /* ditto_pop() */
408
409
410 /************************************************/
411 /*                                              */
412 /*                 ditto_split                  */
413 /*                                              */
414 /************************************************/
415 /* Subroutine of DtSearchSortResults().
416  * Find the middle node in lst. Set its 'next' pointer to NULL.
417  * Return the remainder of lst, i.e. a pointer to the
418  * next node after the middle node.
419  */
420 static DtSrResult *ditto_split (DtSrResult * lst)
421 {
422     DtSrResult     *tail = lst->link;
423
424     if (lst == NULL || tail == NULL)
425         return lst;
426
427     /*
428      * Advance 'tail' to end of list, and advance 'lst' only half
429      * as often 
430      */
431     while ((tail != NULL) && ((tail = tail->link) != NULL)) {
432         lst = lst->link;
433         tail = tail->link;
434     }
435     tail = lst->link;
436     lst->link = NULL;
437     return tail;
438 }  /* ditto_split() */
439
440
441 /************************************************/
442 /*                                              */
443 /*               merge_by_prox                  */
444 /*                                              */
445 /************************************************/
446 /* Subroutine of DtSearchSortResults().
447  * Merges two sorted DtSrResult lists together in proximity order.
448  */
449 static DtSrResult *merge_by_prox (DtSrResult * l1, DtSrResult * l2)
450 {
451     DtSrResult     *myqueue = NULL;
452     DtSrResult     *myend = NULL;
453     DtSrResult     *mynext;
454
455     while ((l1 != NULL) && (l2 != NULL)) {
456         /*
457          * Perform ENQUEUE function. Next item popped off a list is
458          * the next one in sorted order. It is added to END of
459          * myqueue to maintain order. THIS IS WHERE THE ACTUAL SORT
460          * COMPARE FUNCTION IS PERFORMED. 
461          */
462         mynext = (l1->proximity < l2->proximity) ?
463             ditto_pop (&l1) : ditto_pop (&l2);
464
465         mynext->link = NULL;
466         if (myqueue == NULL)
467             myqueue = mynext;
468         else
469             myend->link = mynext;
470         myend = mynext;
471     }
472
473     /*
474      * Perform JOIN QUEUE function. Append entire list to end of
475      * queue. 
476      */
477     if (l1 != NULL)
478         myend->link = l1;
479     if (l2 != NULL)
480         myend->link = l2;
481     return myqueue;
482 }  /* merge_by_prox() */
483
484
485 /************************************************/
486 /*                                              */
487 /*               merge_by_date                  */
488 /*                                              */
489 /************************************************/
490 /* Subroutine of DtSearchSortResults().
491  * Merges two sorted DtSrResult lists together in objdate order.
492  */
493 static DtSrResult *merge_by_date (DtSrResult * l1, DtSrResult * l2)
494 {
495     DtSrResult     *myqueue = NULL;
496     DtSrResult     *myend = NULL;
497     DtSrResult     *mynext;
498
499     while ((l1 != NULL) && (l2 != NULL)) {
500         /*
501          * Perform ENQUEUE function. Next item popped off a list is
502          * the next one in sorted order. It is added to END of
503          * myqueue to maintain order. THIS IS WHERE THE ACTUAL SORT
504          * COMPARE FUNCTION IS PERFORMED. 
505          */
506         if (l1->objdate == l2->objdate)
507             mynext = (l1->proximity < l2->proximity) ?
508                 ditto_pop (&l1) : ditto_pop (&l2);
509         else
510             mynext = (l1->objdate > l2->objdate) ?
511                 ditto_pop (&l1) : ditto_pop (&l2);
512
513         mynext->link = NULL;
514         if (myqueue == NULL)
515             myqueue = mynext;
516         else
517             myend->link = mynext;
518         myend = mynext;
519     }
520
521     /*
522      * Perform JOIN QUEUE function. Append entire list to end of
523      * queue. 
524      */
525     if (l1 != NULL)
526         myend->link = l1;
527     if (l2 != NULL)
528         myend->link = l2;
529     return myqueue;
530 }  /* merge_by_date() */
531
532
533 /************************************************/
534 /*                                              */
535 /*                 ditto_sort                   */
536 /*                                              */
537 /************************************************/
538 /* Subroutine of DtSearchSortResults().
539  * Sorts a list of DtSrResult structures and returns ptr to sorted list.
540  * The basic idea is to sort by recursively splitting a list
541  * into two equal halves and sorting each of those.  The recursion
542  * ends when there are only two small lists which are either
543  * already sorted or are swapped.  This sort rarely runs out
544  * of stack space because each recursion cuts the list length in
545  * half so there are at most 1 + log-N-to-the-base-2 items on the stack.
546  * (e.g. 64,000 nodes = max stack depth of 16:  2**16 = 64K).
547  */
548 static DtSrResult *ditto_sort (DtSrResult * lst)
549 {
550     DtSrResult     *lst2;
551
552     if ((lst == NULL) || (lst->link == NULL))
553         return lst;
554     lst2 = ditto_split (lst);
555     switch (ditsort_type) {
556         case DtSrSORT_PROX:
557             return merge_by_prox (ditto_sort (lst), ditto_sort (lst2));
558         case DtSrSORT_DATE:
559             return merge_by_date (ditto_sort (lst), ditto_sort (lst2));
560         default:
561             fprintf (aa_stderr, PROGNAME "525 Invalid Sort Type %d.\n",
562                 ditsort_type);
563             DtSearchExit (32);
564     }
565 }  /* ditto_sort() */
566
567
568 /************************************************/
569 /*                                              */
570 /*               DtSearchSortResults            */
571 /*                                              */
572 /************************************************/
573 /* Only publicly visible sort function.
574  * DtSearchSortResults() was formerly named aa_sort_dittolist.
575  */
576 int             DtSearchSortResults (DtSrResult ** dittolist, int sort_type)
577 {
578     switch (sort_type) {
579         case DtSrSORT_PROX:
580         case DtSrSORT_DATE:
581             ditsort_type = sort_type;
582             *dittolist = ditto_sort (*dittolist);       /* recursive call */
583             return DtSrOK;
584         default:
585             DtSearchAddMessage (PROGNAME "140 "
586                 "Program Error: Invalid sort type.");
587             return DtSrERROR;
588     }
589 }  /* DtSearchSortResults() */
590
591 /**************************** DTSRJOINT.C *************************/