2 * COMPONENT_NAME: austext
4 * FUNCTIONS: DtSearchFormatObjdate
6 * DtSearchGetMaxResults
8 * DtSearchSetMaxResults
10 * DtSearchValidDateString
11 * aa_check_initialization
23 * (C) COPYRIGHT International Business Machines Corp. 1995, 1996
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.
29 /**************************** DTSRJOINT.C *************************
30 * $XConsortium: dtsrjoint.c /main/5 1996/06/23 16:47:42 cde-ibm $
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.
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.
45 * Revision 2.4 1995/12/27 16:11:54 miker
46 * Remove save_init_dbxxx globals for DtSearchReinit().
48 * Revision 2.3 1995/10/25 22:16:09 miker
49 * Renamed from aajoint.c. Added prolog.
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().
57 * Revision 2.1 1995/09/22 15:18:08 miker
58 * Freeze DtSearch 0.1, AusText 2.1.8
60 * Revision 1.3 1995/08/31 21:31:48 miker
61 * Renames and minor changes for DtSearch.
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.
74 extern char *strdup (const char *s);
77 #define PROGNAME "DTSRJOINT"
78 #define ISDIGIT(c) ((ascii_charmap [(unsigned int) (c)] & NUMERAL) != 0)
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
87 /*------------------- PRIVATE GLOBALS ---------------------*/
88 int aa_is_initialized = FALSE;
89 int aa_getnews_flag = 1;
90 long save_init_switches = 0L;
94 /************************************************/
96 /* aa_check_initialization */
98 /************************************************/
99 /* Confirms ausapi_init() was first function called. */
100 void aa_check_initialization (void)
102 if (aa_is_initialized)
105 catgets (dtsearch_catd, 2, 37,
106 "%s First API function call must be DtSearchInit().\n"),
109 } /* aa_check_initialization() */
112 /************************************************/
114 /* DtSearchValidDateString */
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.
138 DtSrObjdate DtSearchValidDateString (char *datestr)
140 DtSrObjdate objdate = 0L;
144 int stop_parse = FALSE;
147 aa_check_initialization();
148 if (datestr == NULL) /* null string is valid */
150 if (datestr[0] == 0) /* empty string is valid */
153 strncpy (parsebuf, datestr, sizeof (parsebuf));
154 parsebuf[sizeof (parsebuf) - 1] = 0;
157 for (startp = parsebuf; *startp != 0 && !ISDIGIT(*startp); startp++);
158 if (*startp == 0) { /* no numeric digits in string */
160 fprintf (aa_stderr, PROGNAME"269 No numeric digits in string\n");
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);
170 fprintf (aa_stderr, PROGNAME"278 Returning objdate -1\n");
175 for (endp = startp; ISDIGIT(*endp); endp++);
176 if (*endp == 0) { /* mm and dd both missing */
181 fprintf (aa_stderr, PROGNAME"286 mm and dd both missing\n");
187 fprintf (aa_stderr, PROGNAME"293 yystr='%s' yy=%d\n", startp, yy);
189 if (strlen (startp) < 4)
191 if (yy < 1900 || yy > 5995)
192 goto INVALID_DATESTR;
197 for (startp = ++endp; *startp != 0 && !ISDIGIT(*startp); startp++);
198 if (*startp == 0) { /* no mm in string */
202 fprintf (aa_stderr, PROGNAME"309 No mm in string\n");
206 for (endp = startp; ISDIGIT(*endp); endp++);
207 if (*endp == 0) { /* no dd in string */
211 fprintf (aa_stderr, PROGNAME"318 No dd in string\n");
217 fprintf (aa_stderr, PROGNAME"293 mmstr='%s' mm=%d\n", startp, mm);
219 if (mm < 1 || mm > 12)
220 goto INVALID_DATESTR;
225 for (startp = ++endp; *startp != 0 && !ISDIGIT(*startp); startp++);
226 if (*startp == 0) { /* no dd in string */
229 fprintf (aa_stderr, PROGNAME"336 No dd in string\n");
233 for (endp = startp; ISDIGIT(*endp); endp++);
237 fprintf (aa_stderr, PROGNAME"344 ddstr='%s' dd=%d\n", startp, dd);
239 if (dd < 1 || dd > 31)
240 goto INVALID_DATESTR;
243 objdate = (yy - 1900) << 20 | (mm-1) << 16 | dd << 11;
246 PROGNAME"352 Returning objdate %08lx, %d/%d/%d, %s\n",
247 objdate, yy, mm, dd, objdate2fzkstr(objdate));
251 } /* DtSearchValidDateString() */
254 /************************************************/
256 /* DtSearchFormatObjdate */
258 /************************************************/
259 /* Converts objdate in hitlist to displayable string */
260 char *DtSearchFormatObjdate (DtSrObjdate objdate)
262 static char datestr_buf[24];
265 strftime (datestr_buf, sizeof(datestr_buf), "%Y.%m.%d",
266 objdate2tm (objdate));
268 } /* DtSearchFormatObjdate() */
271 /************************************************/
273 /* DtSearchGetMaxResults */
274 /* DtSearchSetMaxResults */
276 /************************************************/
277 int DtSearchGetMaxResults (void)
278 { return aa_maxhits; }
280 void DtSearchSetMaxResults (int newmax)
281 { aa_maxhits = newmax; }
284 /************************************************/
286 /* DtSearchFreeResults */
288 /************************************************/
289 /* Frees storage allocated for the dittolist created
290 * by DtSearchQuery(), and sets dittolist pointer to NULL.
291 * Always returns DtSrOK.
293 int DtSearchFreeResults (DtSrResult ** dittolist)
295 free_llist ((LLIST **) dittolist);
297 } /* DtSearchFreeResults() */
300 /************************************************/
302 /* DtSearchMergeResults */
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.
312 int DtSearchMergeResults (DtSrResult **targ_list, DtSrResult **src_list)
314 DtSrResult *targ, *src, *nextsrc, **prevtargl;
316 if (src_list == NULL || targ_list == NULL)
319 /* If there's no 'src' list, return with no changes.
320 * In other words, merge was successful before we started.
322 if (*src_list == NULL)
325 /* At this point we know there is a src_list.
326 * If there's no targ_list, just swap the pointers.
328 if (*targ_list == NULL) {
329 *targ_list = *src_list;
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.
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 */
348 prevtargl = &src->link;
354 /* just advance to next targ node */
355 prevtargl = &targ->link;
360 /* If there's any src_list left,
361 * just tack it onto the end of the targ_list.
364 /* advance to end of targ_list */
366 prevtargl = &targ->link;
374 } /* DtSearchMergeResults() */
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 ************************************************************/
388 /************************************************/
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.
399 static DtSrResult *ditto_pop (DtSrResult ** lst)
401 DtSrResult *first_node;
404 if (first_node != NULL)
405 *lst = first_node->link;
410 /************************************************/
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.
420 static DtSrResult *ditto_split (DtSrResult * lst)
422 DtSrResult *tail = lst->link;
424 if (lst == NULL || tail == NULL)
428 * Advance 'tail' to end of list, and advance 'lst' only half
431 while ((tail != NULL) && ((tail = tail->link) != NULL)) {
438 } /* ditto_split() */
441 /************************************************/
445 /************************************************/
446 /* Subroutine of DtSearchSortResults().
447 * Merges two sorted DtSrResult lists together in proximity order.
449 static DtSrResult *merge_by_prox (DtSrResult * l1, DtSrResult * l2)
451 DtSrResult *myqueue = NULL;
452 DtSrResult *myend = NULL;
455 while ((l1 != NULL) && (l2 != NULL)) {
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.
462 mynext = (l1->proximity < l2->proximity) ?
463 ditto_pop (&l1) : ditto_pop (&l2);
469 myend->link = mynext;
474 * Perform JOIN QUEUE function. Append entire list to end of
482 } /* merge_by_prox() */
485 /************************************************/
489 /************************************************/
490 /* Subroutine of DtSearchSortResults().
491 * Merges two sorted DtSrResult lists together in objdate order.
493 static DtSrResult *merge_by_date (DtSrResult * l1, DtSrResult * l2)
495 DtSrResult *myqueue = NULL;
496 DtSrResult *myend = NULL;
499 while ((l1 != NULL) && (l2 != NULL)) {
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.
506 if (l1->objdate == l2->objdate)
507 mynext = (l1->proximity < l2->proximity) ?
508 ditto_pop (&l1) : ditto_pop (&l2);
510 mynext = (l1->objdate > l2->objdate) ?
511 ditto_pop (&l1) : ditto_pop (&l2);
517 myend->link = mynext;
522 * Perform JOIN QUEUE function. Append entire list to end of
530 } /* merge_by_date() */
533 /************************************************/
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).
548 static DtSrResult *ditto_sort (DtSrResult * lst)
552 if ((lst == NULL) || (lst->link == NULL))
554 lst2 = ditto_split (lst);
555 switch (ditsort_type) {
557 return merge_by_prox (ditto_sort (lst), ditto_sort (lst2));
559 return merge_by_date (ditto_sort (lst), ditto_sort (lst2));
561 fprintf (aa_stderr, PROGNAME "525 Invalid Sort Type %d.\n",
568 /************************************************/
570 /* DtSearchSortResults */
572 /************************************************/
573 /* Only publicly visible sort function.
574 * DtSearchSortResults() was formerly named aa_sort_dittolist.
576 int DtSearchSortResults (DtSrResult ** dittolist, int sort_type)
581 ditsort_type = sort_type;
582 *dittolist = ditto_sort (*dittolist); /* recursive call */
585 DtSearchAddMessage (PROGNAME "140 "
586 "Program Error: Invalid sort type.");
589 } /* DtSearchSortResults() */
591 /**************************** DTSRJOINT.C *************************/