Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtSearch / dtsrve.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 librararies 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 /*
24  *   COMPONENT_NAME: austext
25  *
26  *   FUNCTIONS: append_blob
27  *              dummy_workproc
28  *              store_next_misc
29  *              ve_append_notes
30  *              ve_browse_dba
31  *              ve_getblobs
32  *              ve_getrec_dba
33  *              ve_initialize
34  *              ve_reckey2dba
35  *              ve_shutdown
36  *
37  *   ORIGINS: 27
38  *
39  *
40  *   (C) COPYRIGHT International Business Machines Corp. 1991,1995
41  *   All Rights Reserved
42  *   Licensed Materials - Property of IBM
43  *   US Government Users Restricted Rights - Use, duplication or
44  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
45  */
46 /**************************** DTSRVE.C ******************************
47  * $XConsortium: dtsrve.c /main/8 1996/11/21 19:49:59 drk $
48  * Sept 1991.
49  * The 'vista engine' of opera.
50  * Contains all modules that actually access the database.
51  * Theoretically, if opera replaced vista with some other DBMS
52  * this is the only module that would have to be modified.
53  *
54  * $Log$
55  * Revision 2.4  1996/02/01  18:48:49  miker
56  * Enhanced blob retrieval debug msgs.
57  *
58  * Revision 2.3  1995/10/25  18:08:27  miker
59  * Renamed from ve.c.  Added prolog.
60  *
61  * Log: ve.c,v
62  * Revision 2.2  1995/10/19  21:02:35  miker
63  * Open mode of non-vista d9x files now tracks db_oflag.
64  *
65  * Revision 2.1  1995/09/22  22:26:36  miker
66  * Freeze DtSearch 0.1, AusText 2.1.8
67  *
68  * Revision 1.12  1995/09/05  19:18:56  miker
69  * Made usrblk global.  Name, msgs, etc changes for DtSearch.
70  * Made ausapi_msglist global.  Deleted obsolete socblk refs.
71  * Added DTSEARCH define.
72  *
73  * Revision 1.11  1995/07/18  22:29:18  miker
74  * Delete msglist arg from vista_abort() function calls.
75  */
76 #include "SearchE.h"
77 #include <string.h>
78 #include <errno.h>
79 #include <fcntl.h>
80 #include "vista.h"
81
82 #define XOS_USE_NO_LOCKING
83 #define X_INCLUDE_TIME_H
84 #include <X11/Xos_r.h>
85
86 #define PROGNAME        "DTSRVE"
87 #define NOTES_SEM_DELAY 3
88 #define MS_misc         1
89 #define MS_ve           6
90 #define MS_oeinit       9
91
92 extern time_t   hctree_id;      /**** hardcoded only temporarily ******/
93 static int      max_abstrbufsz = 0;
94 static int      max_ormisc_size;
95
96 /************************************************/
97 /*                                              */
98 /*               dummy_workproc                 */
99 /*                                              */
100 /************************************************/
101 /* Loaded by any workproc when it has successfully completed.
102  * Should never be called because GUI should turn off workproc
103  * calls after real workproc completes with OE_OK.
104  */
105 void            dummy_workproc (void)
106 {
107     fputs (catgets (dtsearch_catd, MS_ve, 26,
108         PROGNAME "26 Called dummy_workproc().\n"),
109         aa_stderr);
110     return;
111 }  /* dummy_workproc() */
112
113
114 /************************************************/
115 /*                                              */
116 /*              append_blob                     */
117 /*                                              */
118 /************************************************/
119 /* Mallocs space for new compressed vista text record (blob)
120  * appends copy of passed blob to passed link address.
121  * Subroutine of ve_getrec_dba() and ve_getblobs() below.
122  * Similar to append_msglist() function except that data string
123  * is not presumed to be terminated with \0.  Instead the entire
124  * vista member record is copied, as binary bytes,
125  * irrespective of their contents.
126  * This function allocates memory for the blobs but DOES NOT FREE IT.
127  * free_llist() must be called before building a new bloblist.
128  */
129 static LLIST   *append_blob (LLIST ** bloblink,
130                     struct or_blobrec * blobrec)
131 {
132     LLIST          *new;
133     int             i;
134     char           *to;
135
136     new = austext_malloc (sizeof (struct or_blobrec) + sizeof (LLIST) + 4,
137         PROGNAME "36", NULL);
138     new->data = new + 1;        /* hop over exactly 1 LLIST
139                                  * structure */
140     new->link = NULL;
141     *bloblink = new;
142     memcpy (new->data, blobrec, sizeof (struct or_blobrec));
143     return new;
144 }  /* append_blob() */
145
146
147 /************************************************/
148 /*                                              */
149 /*              ve_initialize                   */
150 /*                                              */
151 /************************************************/
152 /* Opens databases in usrblk (calls open_dblk()), and reads dbrecs.
153  * Returns TRUE if 1 or more dblks survived the opening ordeal.
154  * Returns FALSE if no dblks survived.
155  */
156 int             ve_initialize (void)
157 {
158     DBLK*db, *bad_db, **lastlink;
159     int         debugging = (usrblk.debug & USRDBG_RARE);       /* boolean */
160     char        msgbuf[1024];
161     char        d9x_fname[1024];
162     char        *d9x_fext;
163     int         good_dblk_count;
164     char        open_mode [8];
165     static char default_cant_open_msg[] =
166             "%s Cannot open database file '%s', errno %d = %s. "
167             "%s is removing '%s' from list of available databases.";
168
169     /* ---- PASS #1 and #2 ------------------------------------------
170      * Open the d99 and vista database files.
171      */
172     if (!open_dblk (&usrblk.dblist, 64, debugging))
173         return FALSE;
174
175     /* ---- PASS #3 ------------------------------------------
176      * (1) Read dbrec database-wide globals for each database.
177      * (2) Determine max abstract size from largest abstrsz.
178      * (3) Open other nonvista (d9x) files.
179      *     Open mode is determined by value of db_oflag.
180      * Disconnect any dblks with invalid dbrecs or d9x files.
181      */
182     if (db_oflag == O_RDONLY)
183         strcpy (open_mode, "rb");
184     else
185         strcpy (open_mode, "r+b");
186     if (debugging)
187         fprintf (aa_stderr, PROGNAME "76 "
188             "Begin dblks Pass #3 in ve_initialize().  Open mode '%s'.\n",
189             open_mode);
190     good_dblk_count = 0;
191     db = usrblk.dblist;
192     lastlink = &usrblk.dblist;
193     while (db != NULL) {
194         /*-------------- READ DBREC ----------------*/
195         if (debugging)
196             fprintf (aa_stderr,
197                 "--> reading dbrec for database '%s':\n",
198                 db->name);
199
200         RECFRST (PROGNAME "285", OR_DBREC, db->vistano); /* seqtl retrieval */
201         if (db_status != S_OKAY) {
202     NO_DBREC:
203             sprintf (msgbuf, catgets (dtsearch_catd, MS_misc, 13,
204                     "%s No DB record in database '%s'."),
205                 PROGNAME "853 ", db->name);
206             DtSearchAddMessage (msgbuf);
207             goto DELETE_DB;
208         }
209         RECREAD (PROGNAME "302", &db->dbrec, db->vistano);
210         if (db_status != S_OKAY)
211             goto NO_DBREC;
212         swab_dbrec (&db->dbrec, NTOH);
213
214         if (db->dbrec.or_abstrsz > usrblk.abstrbufsz) {
215             if (debugging)
216                 fprintf (aa_stderr,
217                     "\t(changing usrblk.abstrbufsz from %d to %d).\n",
218                     usrblk.abstrbufsz, db->dbrec.or_abstrsz);
219             usrblk.abstrbufsz = db->dbrec.or_abstrsz;
220         }
221
222         /*-------------- DBREC SANITY CHECKS ----------------*/
223         if (db->dbrec.or_reccount <= 0) {
224             sprintf (msgbuf, catgets (dtsearch_catd, MS_ve, 167,
225                      "%s No data in database '%s'."),
226                 PROGNAME"167 ", db->name);
227             DtSearchAddMessage (msgbuf);
228             goto DELETE_DB;
229         }
230         if (!is_compatible_version (db->dbrec.or_version, SCHEMA_VERSION)) {
231             sprintf (msgbuf, catgets (dtsearch_catd, MS_ve, 178,
232                     "%s Database '%s' version '%s' incompatible"
233                     " with Engine version '%s'."),
234                 PROGNAME"178 ",
235                 db->name, db->dbrec.or_version, AUSAPI_VERSION);
236             DtSearchAddMessage (msgbuf);
237             goto DELETE_DB;
238         }
239         if (db->dbrec.or_reccount > db->dbrec.or_maxdba) {
240             sprintf (msgbuf, catgets (dtsearch_catd, MS_ve, 251,
241                     "%s Database '%s' corrupted: "
242                     "Incompatible record counts and database addresses.\n"),
243                 PROGNAME" 251", db->name);
244             DtSearchAddMessage (msgbuf);
245             goto DELETE_DB;
246         }
247         if (db->dbrec.or_maxwordsz < MAXWIDTH_SWORD - 1) {
248             sprintf (msgbuf, catgets (dtsearch_catd, MS_ve, 185,
249                 "%s Database '%s' maximum word size %d is too short."),
250                 PROGNAME" 185", db->name, db->dbrec.or_maxwordsz);
251             DtSearchAddMessage (msgbuf);
252             goto DELETE_DB;
253         }
254         if (db->dbrec.or_hufid != 0L && db->dbrec.or_hufid != hctree_id) {
255             /*
256              * for now, huffman decompress table hardcoded and
257              * linked in 
258              */
259             sprintf (msgbuf, catgets (dtsearch_catd, MS_ve, 156,
260                 "%s Incompatible data compression table used for database '%s'.\n"
261                 "  Database compressed with %ld, "
262                 "engine decompressor is %ld.\n"),
263                 PROGNAME" 156", db->name, db->dbrec.or_hufid, hctree_id);
264             DtSearchAddMessage (msgbuf);
265             goto DELETE_DB;
266         }
267         /* dbrec ok: print debug msg */
268         if (debugging) {
269             fprintf (aa_stderr,
270                 "\tvers='%s' reccount=%ld maxdba=%ld fzkeysz=%d\n"
271                 "\tdbflags=x%lx maxwordsz=%d hufid=%ld abstrsz=%d\n",
272                 db->dbrec.or_version, db->dbrec.or_reccount,
273                 db->dbrec.or_maxdba, db->dbrec.or_fzkeysz,
274                 db->dbrec.or_dbflags, db->dbrec.or_maxwordsz,
275                 db->dbrec.or_hufid, db->dbrec.or_abstrsz);
276         }
277
278         /*-------------- OPEN D97 and D98 FILES ----------------
279          * If semantic (symptom) search is enabled,
280          * open the d97 (offsets table) and d98 (index) files.
281          */
282         if (db->dbrec.or_fzkeysz > 0) {
283             /* build complete path-file name */
284             strcpy (d9x_fname, db->path);
285             strcat (d9x_fname, db->name);
286             d9x_fext = d9x_fname + strlen (d9x_fname);
287             strcpy (d9x_fext, ".d97");
288             if (debugging)
289                 fprintf (aa_stderr, "--> opening '%s'\n", d9x_fname);
290
291             if ((db->syofile = fopen (d9x_fname, open_mode)) == NULL) {
292                 sprintf (msgbuf, catgets (dtsearch_catd, MS_oeinit, 317,
293                         default_cant_open_msg), PROGNAME "286",
294                     d9x_fname, errno, strerror (errno), OE_prodname, db->name);
295                 DtSearchAddMessage (msgbuf);
296                 goto DELETE_DB;
297             }
298
299             strcpy (d9x_fext, ".d98");
300             if (debugging)
301                 fprintf (aa_stderr, "--> opening '%s'\n", d9x_fname);
302
303             if ((db->syifile = fopen (d9x_fname, open_mode)) == NULL) {
304                 sprintf (msgbuf, catgets (dtsearch_catd, MS_oeinit, 317,
305                         default_cant_open_msg), PROGNAME "298",
306                     d9x_fname, errno, strerror (errno), OE_prodname, db->name);
307                 DtSearchAddMessage (msgbuf);
308                 goto DELETE_DB;
309             }
310         }       /* endif to open d97 and d98 files */
311
312
313         /*---------------------- DB OK -------------------------
314          * This dblk passed all dbrec validations and all other
315          * d9x files were available.  Increment pointers and continue.
316          */
317         if (debugging)
318             fprintf (aa_stderr, "------> dblk '%s' ok in veinitialize()\n",
319                 db->name);
320         good_dblk_count++;
321         lastlink = &db->link;
322         db = db->link;
323         continue;
324
325         /*---------------------- DELETE DB -------------------------
326          * This dblk failed one or more dbrec validity checks.
327          * Unlink it and don't increment pointers.
328          */
329 DELETE_DB:
330         if (debugging)
331             fprintf (aa_stderr, "------> ERROR UNLINK '%s'.\n", db->name);
332         bad_db = db;    /* temp save */
333         *lastlink = db->link;
334         db = db->link;
335         free (bad_db);
336     }   /* end PASS #3 */
337
338     /* Quit if no dblks remain */
339     if (good_dblk_count <= 0) {
340         sprintf (msgbuf, catgets (dtsearch_catd, MS_misc, 8,
341                 "%s No valid databases remain."), PROGNAME "246");
342         DtSearchAddMessage (msgbuf);
343         return FALSE;
344     }
345
346     /* Allocate an abstract buffer for the usrblk
347      * if any abstracts are used in any database.
348      * The size is saved in case the client doesn't
349      * return the buffer in subsequent calls.
350      */
351     if (usrblk.abstrbufsz) {
352         max_abstrbufsz = usrblk.abstrbufsz;     /* save */
353         usrblk.abstrbuf = austext_malloc (usrblk.abstrbufsz + 4,
354             PROGNAME "274", NULL);
355         if (debugging)
356             fprintf (aa_stderr,
357                 PROGNAME "282 Allocating %d bytes for usrblk.abstrbuf.\n",
358                 usrblk.abstrbufsz + 4);
359     }
360     else if (debugging)
361         fprintf (aa_stderr, PROGNAME "284 usrblk.abstrbuf NOT allocated.\n");
362
363     return TRUE;
364 }  /* ve_initialize() */
365
366
367 /************************************************/
368 /*                                              */
369 /*              ve_shutdown                     */
370 /*                                              */
371 /************************************************/
372 /* closes databases */
373 void    ve_shutdown (void)
374 {
375     d_close ();
376     austext_exit_dbms = NULL;
377     return;
378 }
379
380 /************************************************/
381 /*                                              */
382 /*              ve_append_notes                 */
383 /*                                              */
384 /************************************************/
385 /* Appends user notes in usrblk.query to
386  * opera record specified by usrblk.dba.
387  * usrblk.dba is presumed valid opera record.
388  * Saves all appends in separate backup flat file 
389  * for restoring database after a crash or initdb.
390  * 
391  * The technique to prevent 2 users from updating
392  * at the same time does not require vista locking.
393  * This function considers itself a 'critical region' and
394  * uses a value in a special file as a semaphore to prevent multiple
395  * users from threading through it at the same time.
396  * 
397  * Does NOT change status of current record in usrblk--it only uses dba.
398  * Returns OE_DISABLED if function disabled in or_dbflags or global var.
399  * Returns OE_OK after successful append.
400  * Returns OE_TIMEOUT if a user cannot acquire the semaphore
401  *      after a reasonable length of time.
402  * Returns OE_ABORT on fatal error.
403  */
404 int             ve_append_notes (void)
405 {
406     time_t      mystamp;
407     FILE        *backup_file, *semaphore_file;
408     size_t      mylen;
409     int         done;
410     int         i;
411     int         vistano;
412     char        *ptr;
413     char        *entirebufptr, *appendbufptr;
414     char        mybuf[160];
415     static char formfeed_line[] = "\f\n";
416     struct or_miscrec
417                 miscrec;
418     _Xltimeparams localtime_buf;
419     struct tm   *time_ptr;
420
421     /* Test if function is disabled */
422     if (!OE_enable_usernotes || usrblk.dblk->dbrec.or_dbflags & ORD_NONOTES) {
423         sprintf (mybuf, catgets (dtsearch_catd, MS_ve, 309,
424                 "%s User notes disabled "), PROGNAME" 309");
425         ptr = mybuf + strlen (mybuf);
426         if (!OE_enable_usernotes)
427             strcpy (ptr, catgets (dtsearch_catd, MS_ve, 310,
428                 "for entire Engine."));
429         else
430             sprintf (ptr, catgets (dtsearch_catd, MS_ve, 311,
431                 "for database '%s'."),
432                 usrblk.dblk->name);
433         DtSearchAddMessage (mybuf);
434         return OE_DISABLED;
435     }
436
437     /* Test for invalid dba */
438     if (usrblk.dba == NULL_DBA) {
439         DtSearchAddMessage (catgets (dtsearch_catd, MS_ve, 157,
440                 PROGNAME "157 Client Program Error: "
441                 "Null database address in usrblk.dba."));
442         OE_flags |= OE_PERMERR;
443         return OE_ABORT;
444     }
445
446     /* Acquire the semaphore:  Open the semaphore file.
447      * If first char = '1', somebody is already in the critical region.
448      * If '1' remains in file after several tries, quit with FALSE retncode.
449      * If first char = '0', critical region is available for this task.
450      * Write a '1' and enter the region.
451      */
452     i = 0;      /* loop counter */
453     for (;;) {
454         if ((semaphore_file = fopen (OEF_notessem, "r+")) == NULL) {
455             sprintf (mybuf,
456                 catgets (dtsearch_catd, MS_ve, 183, 
457                     "%s Could not open user notes semaphore file '%s': %s.\n"),
458                 PROGNAME "183 ", OEF_notessem, strerror (errno));
459             DtSearchAddMessage (mybuf);
460             return OE_TIMEOUT;
461         }
462         fread (mybuf, 1, 1, semaphore_file);
463
464         /*
465          * If semaphore is available, grab it and enter critical
466          * region 
467          */
468         if (*mybuf == '0') {
469             rewind (semaphore_file);
470             fwrite ("1", 1, 1, semaphore_file);
471             fflush (semaphore_file);
472             break;
473         }
474
475         /*
476          * Otherwise check that we havent looped too often, and try
477          * again 
478          */
479         fclose (semaphore_file);
480         if (++i > NOTES_SEM_DELAY) {
481             sprintf (mybuf,
482                 catgets (dtsearch_catd, MS_ve, 199,
483                     "%s Could not acquire user notes semaphore '%s' "
484                     "within %d tries.\n"),
485                 PROGNAME " 199", OEF_notessem, NOTES_SEM_DELAY);
486             DtSearchAddMessage (mybuf);
487             return OE_TIMEOUT;
488         }
489         sleep (1);      /* wait a second */
490     }   /* end of semaphore loop */
491
492     /* We have acquired the semaphore, beginning of critical region... */
493
494     /* Enlarge the buffer so we can prefix users text with the record key 
495      * (for the backup file), and a header line.
496      */
497     entirebufptr = austext_malloc
498         (DtSrMAX_DB_KEYSIZE + sizeof (mybuf) + strlen (usrblk.query),
499         PROGNAME "170", NULL);
500     sprintf (entirebufptr, "%s\n%s",
501         usrblk.dblk->name, usrblk.objrec.or_objkey);
502
503     /* Now add a timstamped, user identified 'header' line */
504     appendbufptr = entirebufptr + strlen (entirebufptr);
505     time (&mystamp);
506     time_ptr = _XLocaltime(&mystamp, localtime_buf);
507     strftime (mybuf, sizeof (mybuf), catgets (dtsearch_catd, MS_ve, 332,
508             "%Y/%m/%d at %H:%M %Z"), time_ptr);
509     sprintf (appendbufptr, catgets (dtsearch_catd, MS_ve, 333,
510             "\n    <User Note Appended by '%s' on %s>\n"),
511         usrblk.userid, mybuf);
512     strcat (appendbufptr, usrblk.query);        /* now add user's text */
513
514     /* Make sure users note ends in \n */
515     ptr = appendbufptr + strlen (appendbufptr);
516     if (*(ptr - 1) != '\n') {
517         *ptr++ = '\n';
518         *ptr = 0;
519     }
520
521     /* Append text to current list of notes */
522     vistano = usrblk.dblk->vistano;
523     CSOSET (PROGNAME "153", OR_OBJ_MISCS, &usrblk.dba, vistano);
524     ptr = appendbufptr;
525     done = FALSE;
526     while (!done) {
527         i = strlen (ptr);       /* i = remaining amount of text */
528         if (i < sizeof (miscrec.or_misc)) {
529             strcpy ((char *) miscrec.or_misc, ptr);
530             done = TRUE;
531         }
532         else {
533             i = sizeof (miscrec.or_misc) - 1;   /* now i = amt of curr
534                                                  * write only */
535             strncpy ((char *) miscrec.or_misc, ptr, i);
536             miscrec.or_misc[0][i] = 0;
537             ptr += i;
538         }
539         miscrec.or_misctype = ORM_OLDNOTES;
540         HTONS (miscrec.or_misctype);
541         FILLNEW (PROGNAME "169", OR_MISCREC, &miscrec, vistano);
542         CONNECT (PROGNAME "170", OR_OBJ_MISCS, vistano);
543     }   /* end of vista append loop */
544
545     /* Also append the note to the backup flat file */
546     if ((backup_file = fopen (OEF_notesnot, "at ")) == NULL) {
547         sprintf (mybuf,
548             catgets (dtsearch_catd, MS_ve, 230,
549                 "%s Could not open user notes backup file '%s': %s."),
550             PROGNAME " 230", OEF_notesnot, strerror (errno));
551         DtSearchAddMessage (mybuf);
552     }
553     else {
554         mylen = strlen (entirebufptr);
555         strcpy (entirebufptr + mylen, formfeed_line);
556         mylen += sizeof (formfeed_line);
557         fwrite (entirebufptr, --mylen, 1, backup_file);
558         fclose (backup_file);
559     }
560
561     free (entirebufptr);
562
563     /* End of critical region.... 
564      * release the semaphore so somebody else can append. 
565      */
566     rewind (semaphore_file);
567     fwrite ("0", 1, 1, semaphore_file);
568     fclose (semaphore_file);
569
570     return OE_OK;
571 }  /* ve_append_notes() */
572
573
574 /************************************************/
575 /*                                              */
576 /*               store_next_misc                */
577 /*                                              */
578 /************************************************/
579 /* Subroutine of ve_getrec_dba().  Repeatedly called
580  * for each read of a miscrec of type ORM_FZKABS
581  * to load usrblk fields objfzkey and objabstr.
582  * First call for a given object is signaled by passed arg.
583  * Thereafter static pointers keep track of where we are
584  * in usrblk buffers to correctly store data from next
585  * miscrec.  Works as a state machine: initial state
586  * is store-fzkey, then store-abstract,
587  * but only if either or both of those exist.
588  * Code similar to load_next_... function in cravel.c.
589  * WARNING! maximum size of fzkey is still hardcoded FZKEYSZ!
590  */
591 static void     store_next_misc (
592                         int     is_first_misc,
593                         char    *misc   /* miscrec.or_misc */
594                         )
595 {
596     static enum { STORE_DONE, STORE_FZKEY, STORE_ABSTR }
597                         store_state =   STORE_DONE;
598     static char         *targ =         NULL;
599     static int          targlen =       0;
600     static int          fzkeysz =       0;
601     static int          abstrsz =       0;
602     int                 i;
603
604     /* Initialize static variables at first call. */
605     if (is_first_misc) {
606         abstrsz = usrblk.dblk->dbrec.or_abstrsz;
607         if (abstrsz > 0) {
608             /*
609              * if client didn't send back his astrbuf malloc a new
610              * one 
611              */
612             if (usrblk.abstrbuf == NULL) {
613                 usrblk.abstrbuf = austext_malloc (max_abstrbufsz + 4,
614                     PROGNAME "546", NULL);
615             }
616             targ = usrblk.abstrbuf;
617             targlen = abstrsz - 1;      /* leave room for final \0 */
618             store_state = STORE_ABSTR;
619         }
620
621         /* If no miscs needed return immediately. */
622         else {
623             store_state = STORE_DONE;
624             return;
625         }
626     }
627
628     /* If NOT first call, but there's nothing left to do because
629      * fzkey and abstract already stored, return immediately.
630      */
631     else if (store_state == STORE_DONE)
632         return;
633
634     /*********************
635     if (usrblk.debug & USRDBG_RETRVL)
636         fprintf (aa_stderr, PROGNAME"562 store_next_misc():"
637             " frst?=%d state=%d, fbuf=%p fsz=%d,\n"
638             "  abuf=%p (bufsz=%d) asz=%d, targ=%p targlen=%d\n",
639             is_first_misc, store_state, usrblk.objfzkey, fzkeysz,
640             usrblk.abstrbuf, usrblk.abstrbufsz, abstrsz,
641             targ, targlen);
642     ************************/
643   
644     /* Main loop is on each byte of the or_misc field of miscrec.
645      * Depending on the state, the byte will be a fzkey byte
646      * or an abstract byte.
647      */
648     for (i = 0; i < max_ormisc_size; i++) {
649         switch (store_state) {
650             case STORE_ABSTR:
651                 if (*misc == 0 || --targlen <= 0) {     /* end of abstract? */
652                     *targ = 0;
653                     store_state = STORE_DONE;
654                     return;
655                 }
656                 *targ++ = *misc++;
657                 break;
658
659             default:
660                 fprintf (aa_stderr, "%s\n", DtSearchGetMessages ());
661                 fputs (PROGNAME "549 Abort due to programming error.\n",
662                     aa_stderr);
663                 DtSearchExit (54);
664         }       /* end switch */
665     }   /* end for-loop */
666
667
668     /* If storing abstracts, put a \0 at the current targ location to
669      * terminate the abstract string in case there are no more misc recs.
670      * (but should not occur).
671      */
672     if (store_state = STORE_ABSTR)
673         *targ = 0;
674     return;
675 }  /* store_next_misc() */
676
677
678 /************************************************/
679 /*                                              */
680 /*              ve_getrec_dba                   */
681 /*                                              */
682 /************************************************/
683 /* Given a valid vista database address, returns the opera record
684  * itself, its linked list of compressed text lines (blobs)
685  * or NULL if there are none, its abstract and fzkey if any,
686  * and user notes, exactly as stored in vista.
687  * The objrec, fzkey, abstract, and notes are returned in usrblk.
688  * The blob list is returned in the passed ptr arg,
689  * or it is set to NULL if there are no blobs.
690  * Saves size of uncompressed data (or_objsize) in OE_objsize.
691  * CALLER MUST FREE the blob list when done.
692  * Returns OE_OK if all goes well, else returns other appropriate retncode.
693  * Simpler version that only gets text blobs is ve_getblobs() below.
694  */
695 int             ve_getrec_dba (LLIST ** bloblist)
696 {
697     struct or_objrec
698                 myobjbuf;
699     struct or_blobrec
700                 myblobuf;
701     struct or_miscrec
702                 mymiscrec;
703     int         i;
704     int         debugging = (usrblk.debug & USRDBG_RETRVL);
705     char        *src, *targ;
706     LLIST       *new, *lastnode, **lastlink;
707     int         vistano = usrblk.dblk->vistano;
708     int         is_first_misc = TRUE;
709     DB_ADDR     dba = usrblk.dba;
710     char        msgbuf[512];
711
712     if (debugging)
713         fprintf (aa_stderr,
714             PROGNAME"644 retrieve db='%s' dba=%ld (x%x:%lx)\n",
715             usrblk.dblk->name,
716             (long)dba, (int)dba >> 24, (long)dba & 0xffffffL);
717
718     /* Test for invalid dba */
719     if (dba == NULL_DBA) {
720         DtSearchAddMessage (catgets (dtsearch_catd, MS_ve, 157,
721                 PROGNAME "245 Client Program Error: "
722                 "Null database address in usrblk.dba."));
723         OE_flags |= OE_PERMERR;
724         return OE_ABORT;
725     }
726
727     max_ormisc_size = sizeof (mymiscrec.or_misc);
728
729     /* Retrieve the opera header record.  Don't use
730      * CRSET macro here so we can trap invalid dba errs.
731      */
732     d_crset (&dba, vistano);
733     if (db_status == S_INVADDR) {
734         sprintf (msgbuf, catgets (dtsearch_catd, MS_ve, 142,
735                 "%s Client Error: Requested record with invalid\n"
736                 "  database addr %ld (%d:%ld, x'%08.8lx') for database '%s'."),
737             PROGNAME "142 ", dba, dba >> 24, dba & 0xffffff, dba, usrblk.dblk->label);
738         fprintf (aa_stderr, "%s: %s\n", aa_argv0, msgbuf);
739         DtSearchAddMessage (msgbuf);
740         OE_flags |= OE_PERMERR;
741         return OE_ABORT;
742     }
743     RECREAD (PROGNAME "143", &myobjbuf, vistano);
744     if (db_status != S_OKAY)
745         return OE_NOTAVAIL;
746     swab_objrec (&myobjbuf, NTOH);
747     OE_objsize = myobjbuf.or_objsize;   /* save tot num bytes
748                                          * globally */
749
750     if (debugging)
751         fprintf (aa_stderr, "  --> got '%s': flags=x%lx sz=%ld dt=%s\n",
752             myobjbuf.or_objkey, (long)myobjbuf.or_objflags,
753             (long)myobjbuf.or_objsize,
754             objdate2fzkstr (myobjbuf.or_objdate));
755
756     clear_usrblk_record ();
757     memcpy (&usrblk.objrec, &myobjbuf, sizeof (struct or_objrec));
758
759     /* Setup currency table: curr record is owner of all object's sets */
760     SETOR (PROGNAME "145", OR_OBJ_BLOBS, vistano);
761     SETOR (PROGNAME "146", OR_OBJ_MISCS, vistano);
762
763     /* Retrieve text blobs or set bloblist to NULL if no blobs */
764     if (debugging)
765         fputs (PROGNAME "678 Retrieving blobs: ", aa_stderr);
766     *bloblist = NULL;
767     lastlink = bloblist;
768     FINDFM (PROGNAME "148", OR_OBJ_BLOBS, vistano);
769     while (db_status == S_OKAY) {
770         /* Read next text blob record and append to end of list.
771          * Each debug 'b' = 1 blobrec.
772          */
773         if (debugging)
774             fputc ('b', aa_stderr);
775         RECREAD (PROGNAME "151", &myblobuf, vistano);
776         if (db_status != S_OKAY)
777             vista_abort (PROGNAME "152");
778         NTOHS (myblobuf.or_bloblen);
779         lastnode = append_blob (lastlink, &myblobuf);
780         lastlink = &lastnode->link;
781         FINDNM (PROGNAME "155", OR_OBJ_BLOBS, vistano);
782     }
783     if (debugging) {
784         if (*bloblist == NULL)
785             fputs ("--> there are no blobs!\n", aa_stderr);
786         else
787             fputc ('\n', aa_stderr);
788         fflush (aa_stderr);
789     }
790
791     /* Retrieve abstract, fzkey, and user notes, if any */
792     if (debugging)
793         fputs (PROGNAME "698 Retrieving misc recs: ", aa_stderr);
794     lastlink = &usrblk.notes;
795     FINDFM (PROGNAME "164", OR_OBJ_MISCS, vistano);
796     is_first_misc = TRUE;
797     while (db_status == S_OKAY) {
798         /*
799          * Read next misc record.  If notes, append to end of notes list.
800          * If abstract or fzkey, move to appropriate usrblk field.
801          * Each debug char ids the type of miscrec.
802          */
803         RECREAD (PROGNAME "168", &mymiscrec, vistano);
804         if (db_status != S_OKAY)
805             vista_abort (PROGNAME "169");
806         NTOHS (mymiscrec.or_misctype);
807
808         switch (mymiscrec.or_misctype) {
809             case ORM_OLDNOTES:
810                 if (debugging)
811                     fputc ('n', aa_stderr);
812                 new = austext_malloc (sizeof (mymiscrec.or_misc)
813                         + sizeof (LLIST) + 4,
814                     PROGNAME "543", NULL);
815                 new->link = NULL;
816                 /* hop over exactly 1 LLIST structure */
817                 new->data = new + 1;
818                 memcpy (new->data, mymiscrec.or_misc,
819                     sizeof (mymiscrec.or_misc));
820                 *lastlink = new;
821                 lastlink = &new->link;
822                 break;
823
824             case ORM_FZKABS:
825                 /* Concatenated fzkey + abstract rec */
826                 if (debugging)
827                     fputc ('a', aa_stderr);
828                 store_next_misc (is_first_misc, (char *) mymiscrec.or_misc);
829                 is_first_misc = FALSE;
830                 break;
831
832             default:
833                 if (debugging)
834                     fputc ('?', aa_stderr);
835                 break;  /* ignore it */
836         }       /* end switch */
837
838         FINDNM (PROGNAME "172", OR_OBJ_MISCS, vistano);
839     }   /* end loop on miscrecs */
840
841     /* Currently no provision for retrieving hyperlink records */
842
843     if (debugging) {
844         if (is_first_misc)
845             fputs ("--> there are no misc recs!\n", aa_stderr);
846         else
847             fputc ('\n', aa_stderr);
848         print_usrblk_record (PROGNAME"600");
849     }
850     return OE_OK;
851 }  /* ve_getrec_dba() */
852
853
854 /************************************************/
855 /*                                              */
856 /*                ve_getblobs                   */
857 /*                                              */
858 /************************************************/
859 /* Given a valid vista database address for an operarec,
860  * returns its linked list of compressed text lines (blobs),
861  * exactly as stored in vista, and returns the or_objsize
862  * field in the global OE_objsize (for later unblobing).
863  * Returns NULL if invalid database addr or no text blobs.
864  * CALLER MUST FREE the blob list himself when done.
865  * This is a simpler version of ve_getrec_dba() and will
866  * get blobs from any database, not just the one in usrblk.dblk.
867  */
868 LLIST          *ve_getblobs (DtSrINT32 dba, int vistano)
869 {
870     LLIST          *bloblist, *lastnode, **lastlink;
871     struct or_blobrec myblobuf;
872     struct or_objrec myobjbuf;
873     int             debugging = (usrblk.debug & USRDBG_RETRVL);
874
875     /* Retrieve the opera header record */
876     if (dba == NULL_DBA)
877         return NULL;
878     CRSET (PROGNAME "401", &dba, vistano);
879     RECREAD (PROGNAME "402", &myobjbuf, vistano);
880     if (db_status != S_OKAY)
881         return NULL;
882     swab_objrec (&myobjbuf, NTOH);
883     OE_objsize = myobjbuf.or_objsize;
884     if (debugging) {
885         fprintf (aa_stderr, PROGNAME "792 ve_getblobs: "
886             "db='%s'[v#%d] dba=%ld:%ld v#=%d sz=%ld: '%s'\n",
887             usrblk.dblk->name, usrblk.dblk->vistano,
888             dba >> 24, dba % 0xffffff, vistano,
889             myobjbuf.or_objsize, myobjbuf.or_objkey);
890     }
891
892     /* Retrieve blobs and append to end of growing list.
893      * If no blobs, return NULL.
894      */
895     bloblist = NULL;
896     lastlink = &bloblist;
897     SETOR (PROGNAME "406", OR_OBJ_BLOBS, vistano);
898     FINDFM (PROGNAME "407", OR_OBJ_BLOBS, vistano);
899     while (db_status == S_OKAY) {
900         RECREAD (PROGNAME "413", &myblobuf, vistano);
901         if (db_status != S_OKAY)
902             vista_abort (PROGNAME "414");
903         NTOHS (myblobuf.or_bloblen);
904         lastnode = append_blob (lastlink, &myblobuf);
905         lastlink = &lastnode->link;
906         FINDNM (PROGNAME "417", OR_OBJ_BLOBS, vistano);
907     }
908     return bloblist;
909 }  /* ve_getblobs() */
910
911
912 /************************************************/
913 /*                                              */
914 /*              ve_reckey2dba                   */
915 /*                                              */
916 /************************************************/
917 /* Given a vista database record key (in usrblk.query),
918  * returns the vista database address of the record.
919  * If a record cant be found for the exact key,
920  * or if the requested record is a 'reserved' record,
921  * wrap to the NEXT available nonreserved record address.
922  * By convention, reserved records have an ascii control char
923  * (< 32, 0x20) for the keytype char (first char of record key).
924  * Also sets usrblk.retncode to OE_OK or OE_WRAPPED.
925  */
926 DtSrINT32         ve_reckey2dba (void)
927 {
928     static char     debugkey[] =
929     {0x73, 0x31, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x33, 0x00};
930     static char     debugmsg[] = {
931         0x54, 0x68, 0x69, 0x73, 0x20, 0x49, 0x6E, 0x66,
932         0x6F, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E,
933         0x20, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76,
934         0x61, 0x6C, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65,
935         0x6D, 0x20, 0x77, 0x61, 0x73, 0x20, 0x64, 0x65,
936         0x73, 0x69, 0x67, 0x6E, 0x65, 0x64, 0x20, 0x61,
937         0x6E, 0x64, 0x20, 0x69, 0x6D, 0x70, 0x6C, 0x65,
938         0x6D, 0x65, 0x6E, 0x74, 0x65, 0x64, 0x20, 0x62,
939         0x79, 0x0A, 0x4D, 0x69, 0x6B, 0x65, 0x20,
940         0x52, 0x75, 0x73, 0x73, 0x65, 0x6C, 0x6C, 0x20,
941         0x61, 0x6E, 0x64, 0x20, 0x45, 0x66, 0x69, 0x6D,
942         0x20, 0x47, 0x65, 0x6E, 0x64, 0x6C, 0x65, 0x72,
943         0x0A, 0x6F, 0x66, 0x20, 0x41, 0x75, 0x73,
944         0x74, 0x69, 0x6E, 0x2C, 0x20, 0x54, 0x65, 0x78,
945         0x61, 0x73, 0x2C, 0x20, 0x55, 0x53, 0x41, 0x2E,
946         0x00};
947     DB_ADDR         dba;
948     int             null_query = FALSE;
949     int             vistano = usrblk.dblk->vistano;
950     char            mykeybuf[DtSrMAX_DB_KEYSIZE + 2];
951
952     usrblk.retncode = OE_OK;
953
954     /* If UI sent a null query ptr, reset it to empty string */
955     if (usrblk.query == NULL) {
956         null_query = TRUE;
957         usrblk.query = "";
958         DtSearchAddMessage (catgets (dtsearch_catd, MS_ve, 398,
959                 PROGNAME "398 NULL query string."));
960     }
961     if (strncmp (usrblk.query, debugkey, strlen (debugkey)) == 0) {
962         usrblk.query = "";
963         DtSearchAddMessage (debugmsg);
964     }
965
966     /* If case insensitive keys is the site standard,
967      * force key to uppercase.
968      */
969     if (OE_uppercase_keys)
970         strupr (usrblk.query);
971
972     /* Find the record.  If exact record key isnt found,
973      * keep reading until we get one, including wrapping.
974      * past end of file.
975      */
976     KEYFIND (PROGNAME "191", OR_OBJKEY, usrblk.query, vistano);
977 WRAP_SOME_MORE:
978     while (db_status == S_NOTFOUND) {
979         usrblk.retncode = OE_WRAPPED;
980         KEYNEXT (PROGNAME "196", OR_OBJKEY, vistano);
981     }
982
983     /* If the retrieved record is a 'reserved' record, wrap some more */
984     KEYREAD (PROGNAME "208", mykeybuf);
985     if (db_status != S_OKAY)
986         vista_abort (PROGNAME "209");
987     if (mykeybuf[0] < 32) {
988         KEYNEXT (PROGNAME "210", OR_OBJKEY, vistano);
989         goto WRAP_SOME_MORE;
990     }
991
992     CRGET (PROGNAME "211", &dba, vistano);
993     if (null_query)
994         usrblk.query = NULL;    /* restore */
995     return dba;
996 }  /* ve_reckey2dba() */
997
998
999 /************************************************/
1000 /*                                              */
1001 /*              ve_browse_dba                   */
1002 /*                                              */
1003 /************************************************/
1004 /* Increments/decrements dba address field.
1005  * If original dba is null, returns first dba in database.
1006  * Otherwise UI must ensure that original dba is valid for current database.
1007  * Does not alter other record oriented usrblk fields.
1008  */
1009 void            ve_browse_dba (int direction)
1010 {
1011     int             vistano = usrblk.dblk->vistano;
1012     char            mykeybuf[DtSrMAX_DB_KEYSIZE + 2];
1013
1014     usrblk.retncode = OE_OK;
1015
1016 TRY_AGAIN:
1017     if (usrblk.dba == NULL_DBA) {
1018         KEYFRST (PROGNAME "224", OR_OBJKEY, vistano);
1019         if (db_status != S_OKAY)
1020             vista_abort (PROGNAME "226");
1021     }
1022     else {
1023         /* at this point, dba must be valid for current database */
1024         CRSET (PROGNAME "230", &usrblk.dba, vistano);
1025         CRREAD (PROGNAME "232", OR_OBJKEY, mykeybuf, vistano);
1026         if (db_status != S_OKAY)
1027             vista_abort (PROGNAME "234");
1028
1029         /* descend b-tree to current location */
1030         KEYFIND (PROGNAME "236", OR_OBJKEY, mykeybuf, vistano);
1031         if (direction < 0) {    /* get prev rec */
1032             KEYPREV (PROGNAME "238", OR_OBJKEY, vistano);
1033             if (db_status == S_NOTFOUND) {      /* at begin of file,
1034                                                  * wrap */
1035                 usrblk.retncode = OE_WRAPPED;
1036                 KEYPREV (PROGNAME "240", OR_OBJKEY, vistano);
1037             }
1038         }
1039         else {  /* get next rec */
1040             KEYNEXT (PROGNAME "242", OR_OBJKEY, vistano);
1041             if (db_status == S_NOTFOUND) {      /* at eof, wrap */
1042                 usrblk.retncode = OE_WRAPPED;
1043                 KEYNEXT (PROGNAME "244", OR_OBJKEY, vistano);
1044             }
1045         }
1046     }   /* end else where orig rec is not null */
1047
1048     CRGET (PROGNAME "246", &usrblk.dba, vistano);
1049
1050     /* If retrieved rec is a reserved record,
1051      * ignore it and retry the browse from here.
1052      */
1053     KEYREAD (PROGNAME "561", mykeybuf);
1054     if (db_status != S_OKAY)
1055         vista_abort (PROGNAME "562");
1056     if (mykeybuf[0] < 32)
1057         goto TRY_AGAIN;
1058     return;
1059 }  /* ve_browse_dba() */
1060
1061 /**************************** DTSRVE.C ******************************/