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