Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / tt / mini_isam / isstart.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 /*%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                   */
24 /*%%  (c) Copyright 1993, 1994 International Business Machines Corp.     */
25 /*%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                    */
26 /*%%  (c) Copyright 1993, 1994 Novell, Inc.                              */
27 /*%%  $XConsortium: isstart.c /main/3 1995/10/23 11:45:08 rswiston $                                                     */
28 #ifndef lint
29 static char sccsid[] = "@(#)isstart.c 1.9 89/07/17 Copyr 1988 Sun Micro";
30 #endif
31 /*
32  * Copyright (c) 1988 by Sun Microsystems, Inc.
33  */
34
35 /*
36  * isstart.c
37  *
38  * Description:
39  *      Select index and set record position.
40  */
41
42
43 #include "isam_impl.h"
44 #include <sys/time.h>
45
46 static int _amstart();
47
48 /*
49  * err =  isstart(isfd, keydesc, length, record, mode)
50  *
51  * Isstart() selects index for subsequent read operations and set new
52  *      current record position.
53  *
54  * isrecnum is set to indicate the start record.
55  *
56  *
57  * Returns 0 if successful, or -1 of any error.
58  *
59  * Errors:
60  *      ENOTOPEN isfd does not correspond to an open ISAM file, or the
61  *              ISAM file was opened with ISOUTPUT mode.
62  *      EBADARG Bad mode parameter.
63  *      EBADARG keylen is out of range.
64  *      ENOREC  Specified record cannot be found (random access read).
65  *      EBADKEY Error in the key descriptor.
66  */
67
68 int 
69 isstart(isfd, keydesc, length, record, mode)
70     int                 isfd;
71     struct keydesc      *keydesc;
72     int                 length;
73     char                *record;
74     int                 mode;
75 {
76     register Fab        *fab;
77     int                 reclen;
78     Recno               recnum;
79     int                 ret;
80     enum readmode       readmode;
81     char                dummy_record [1];    /* used for ISFIRST and ISLAST */
82     char                *precord;
83
84     /*
85      * Get File Access Block.
86      */
87     if ((fab = _isfd_find(isfd)) == NULL) {
88         _setiserrno2(ENOTOPEN, '9', '0');
89         return (ISERROR);
90     }
91
92     /*
93      * Check that the open mode was ISINPUT, or ISINOUT.
94      */
95     if (fab->openmode != OM_INPUT && fab->openmode != OM_INOUT) {
96         _setiserrno2(ENOTOPEN, '9', '0');
97         return (ISERROR);
98     }
99
100     /*
101      * Extract read mode.
102      */
103     if ((readmode = _getreadmode(mode)) == RM_BADMODE) {
104         _setiserrno2(EBADARG, '9', '0');
105         return (ISERROR);
106     }
107
108     /*
109      * Some arguments are used only when a particular mode is specified.
110      */
111     if (readmode == RM_FIRST || readmode == RM_LAST || USE_PHYS_ORDER(keydesc)) {
112         precord = dummy_record;
113         reclen = 0;
114     }
115     else {
116         precord = record;
117         reclen = fab->minreclen;
118     }
119     
120     reclen = fab->minreclen;
121         
122     recnum = isrecnum;
123
124     if ((ret = _amstart(&fab->isfhandle, precord, reclen,
125                         readmode, keydesc, length, &fab->curpos,
126                         &recnum, &fab->errcode)) == ISOK) {
127         isrecnum = recnum;                   /* Set isrecnum */
128     }
129
130     _seterr_errcode(&fab->errcode);
131
132     return (ret);                            /* Successful start */
133 }
134
135 /*
136  * _amstart(isfhandle, record, reclen, readmode,
137  *          keydesc, keylen, curpos, recnum, errcode)
138  *
139  * _amstart() reads a record from ISAM file
140  *
141  * Input params:
142  *      isfhandle       Handle of ISAM file
143  *      readmode        Specifies access mode (random or sequential)
144  *      curpos          old record position
145  *      recnum          copy of isrecnum
146  *      keydesc         key descriptor
147  *      keylen          # of bytes of key to match
148  *      record          extract key from this record buffer
149  *
150  * Output params:
151  *      curpos          new current position
152  *      recnum          record number
153  *      errcode         error status of the operation
154  *      reclen          actual length of the record
155  *
156  * Note:
157  * Successfull isstart() returns the new curpos and frees the old curpos.
158  */
159
160 /* ARGSUSED */
161 static int
162 _amstart(isfhandle, record, reclen, readmode,
163          keydesc, keylen, curpos, recnum, errcode)
164     Bytearray           *isfhandle;
165     enum readmode       readmode;
166     char                *record;
167     int                 *reclen;
168     Bytearray           *curpos;
169     Recno               *recnum;
170     struct errcode      *errcode;
171     struct keydesc      *keydesc;
172     int                 keylen;
173 {
174     Fcb                 *fcb;
175     Recno               recnum2;
176     int                 err;
177     Crp                 *newcrp = NULL;
178     char                recbuf [ISMAXRECLEN];
179     Keydesc2            keydesc2;
180     Keydesc2            *pkeydesc2;
181     int                 newcrpsize = 0;
182     char                keybuf1 [MAXKEYSIZE], keybuf2 [MAXKEYSIZE];
183     int                 matchkeylen;
184     int                 skipbytes;
185     char                *pkey;
186     Btree               *btree = NULL;
187     int                 reclen2;
188     int                 (*rec_read)();
189
190     _isam_entryhook();
191
192     /*
193      * Get FCB corresponding to the isfhandle handle.
194      */
195     if ((fcb = _openfcb(isfhandle, errcode)) == NULL) {
196         _isam_exithook();
197         return (ISERROR);
198     }
199
200     rec_read = (fcb->varflag?_vlrec_read:_flrec_read);
201
202     /*
203      * Update information in FCB from CNTL page on the disk
204      */
205     (void)_isfcb_cntlpg_r2(fcb);
206
207     if (USE_PHYS_ORDER(keydesc)) {
208         /*
209          * Physical order in use.
210          */
211
212         /*
213          * Allocate new current position structure.
214          */
215         newcrpsize = sizeof(*newcrp);
216         newcrp = (Crp *) _ismalloc(sizeof(*newcrp));
217         memset ((char *)newcrp, 0, sizeof(*newcrp));
218         newcrp->keyid = PHYS_ORDER;
219         
220         switch (readmode) {
221         case RM_EQUAL:
222             recnum2 = *recnum;               /* passed from isrecnum */
223             if ((err = rec_read(fcb, recbuf, recnum2, &reclen2)) != ISOK) {
224                 _amseterrcode(errcode, ENOREC);
225                 goto ERROR;
226             }
227             newcrp->flag = CRP_BEFORE;
228             newcrp->recno = recnum2;
229             break;
230
231         case RM_GREAT:
232             recnum2 = *recnum + 1;
233             if (recnum2 < 1) recnum2 = 1;
234             /*
235              * Skip deleted records.
236              */
237             while ((err = rec_read(fcb, recbuf, recnum2, &reclen2)) != ISOK &&
238                    err == ENOREC) 
239                 recnum2++;
240  
241             if (err != ISOK) {
242                 _amseterrcode(errcode, ENOREC);
243                 goto ERROR;
244             }
245             newcrp->flag = CRP_BEFORE;
246             newcrp->recno = recnum2;
247             break;
248
249         case RM_GTEQ:
250             recnum2 = *recnum;
251             if (recnum2 < 1) recnum2 = 1;
252             /*
253              * Skip deleted records.
254              */
255             while ((err = rec_read(fcb, recbuf, recnum2, &reclen2)) != ISOK &&
256                    err == ENOREC) 
257                 recnum2++;
258  
259             if (err != ISOK) {
260                 _amseterrcode(errcode, ENOREC);
261                 goto ERROR;
262             }
263             newcrp->flag = CRP_BEFORE;
264             newcrp->recno = recnum2;
265             break;
266
267         case RM_LESS:
268             recnum2 = *recnum - 1;
269             if (recnum2 > fcb->lastrecno) recnum2 = fcb->lastrecno;
270             /*
271              * Skip deleted records.
272              */
273             while ((err = rec_read(fcb, recbuf, recnum2, &reclen2)) != ISOK &&
274                    err == ENOREC) 
275                 recnum2--;
276  
277             if (err != ISOK) {
278                 _amseterrcode(errcode, ENOREC);
279                 goto ERROR;
280             }
281             newcrp->flag = CRP_AFTER;
282             newcrp->recno = recnum2;
283             break;
284
285         case RM_LTEQ:
286             recnum2 = *recnum;
287             if (recnum2 > fcb->lastrecno) recnum2 = fcb->lastrecno;
288             /*
289              * Skip deleted records.
290              */
291             while ((err = rec_read(fcb, recbuf, recnum2, &reclen2)) != ISOK &&
292                    err == ENOREC) 
293                 recnum2--;
294  
295             if (err != ISOK) {
296                 _amseterrcode(errcode, ENOREC);
297                 goto ERROR;
298             }
299             newcrp->flag = CRP_AFTER;
300             newcrp->recno = recnum2;
301             break;
302
303         case RM_FIRST:
304             recnum2 = 1;
305             /*
306              * Skip deleted records.
307              */
308             while ((err = rec_read(fcb, recbuf, recnum2, &reclen2)) != ISOK &&
309                    err == ENOREC) 
310                 recnum2++;
311  
312             if (err == ISOK) {
313                 newcrp->flag = CRP_BEFORE;
314                 newcrp->recno = recnum2;
315             }
316             else {
317                 newcrp->flag = CRP_AFTERANY;
318             }
319             break;
320         case RM_LAST:
321             recnum2 = fcb->lastrecno;
322             /*
323              * Skip deleted records.
324              */
325             while ((err = rec_read(fcb, recbuf, recnum2, &reclen2)) != ISOK &&
326                    err == ENOREC) 
327                 recnum2--;
328
329             if (err == ISOK) {
330                 newcrp->flag = CRP_AFTER;
331                 newcrp->recno = recnum2;
332             }
333             else {
334                 newcrp->flag = CRP_BEFOREANY;
335             }
336             break;
337         default:
338             _isfatal_error("Invalid readmode");
339         }
340
341         *recnum = recnum2;
342
343         /*
344          * Build new curpos, deallocate old curpos.
345          */
346         _bytearr_free(curpos);
347         *curpos = _bytearr_new(sizeof(*newcrp), (char *)newcrp);
348
349     } /* physical order */
350     else {
351
352         /*
353          * Use order defined by some key.
354          */
355         
356         /*
357          * Check key descriptor for validity.
358          */
359         if (_validate_keydesc(keydesc, fcb->minreclen) != ISOK) {
360             _amseterrcode(errcode, EBADKEY);
361             goto ERROR;
362         }
363         
364         /*
365          * Convert key descriptor to internal form.
366          */
367         _iskey_xtoi (&keydesc2, keydesc);
368         
369         /* Find key decriptor in the FCB. */
370         if ((pkeydesc2 = _isfcb_findkey(fcb ,&keydesc2)) == NULL) {
371             _amseterrcode(errcode, EBADKEY);
372             goto ERROR;
373         }
374
375         /*
376          * skipkeybytes is set to the number of bytes in the beginning
377          * of the key:
378          *  RECNOSIZE for ISNODUPS keys to skip recno part
379          *  RECNOSIZE + DUPIDSIZE to skip recno and duplicate serial number
380          */
381         skipbytes = RECNOSIZE;
382         if (ALLOWS_DUPS2(pkeydesc2))
383             skipbytes += DUPIDSIZE;
384
385         /*
386          * Validate keylen.
387          */
388         if (keylen < 0 || keylen > pkeydesc2->k2_len - skipbytes) {
389             _amseterrcode(errcode, EBADARG);
390             goto ERROR;
391         }
392
393         /*
394          * Special case if keylen == 0: use the entire key.
395          */
396         if (keylen == 0)
397             matchkeylen = pkeydesc2->k2_len - skipbytes;
398         else
399             matchkeylen = keylen;
400
401         /*
402          * Allocate new current record position.
403          */
404         newcrpsize = sizeof(Crp) + pkeydesc2->k2_len;
405         newcrp = (Crp *) _ismalloc((unsigned)newcrpsize);
406         memset((char *)newcrp, 0, newcrpsize);
407
408         newcrp->keyid = pkeydesc2->k2_keyid; /* Key identifier in FCB */
409         newcrp->matchkeylen = matchkeylen;   /* number of bytes to match */
410
411         /*
412          * Create B tree object.
413          */
414         btree = _isbtree_create(fcb, pkeydesc2);
415
416         switch (readmode) {
417         case RM_EQUAL:
418         case RM_GTEQ:
419             /* 
420              * Make sure that you will read the first duplicate. 
421              */
422             _iskey_fillmin(pkeydesc2, keybuf1);
423  
424             /* 
425              * Extract key fields from record. 
426              */
427             _iskey_extract(pkeydesc2, record, keybuf2);
428             memcpy( keybuf1 + skipbytes,keybuf2 + skipbytes, matchkeylen);
429
430             /*
431              * Position pointer in the B-tree in before the searched value. 
432              */
433             _isbtree_search(btree, keybuf1);
434         
435             if ((pkey = _isbtree_next(btree)) == NULL) {
436                 _amseterrcode(errcode, ENOREC);
437                 goto ERROR;
438             }
439             
440             if (readmode == RM_EQUAL &&
441                 memcmp(keybuf1 + skipbytes, pkey + skipbytes, 
442                        matchkeylen) != 0) {
443                 _amseterrcode(errcode, ENOREC);
444                 goto ERROR;
445             }
446
447             newcrp->flag = CRP_BEFORE;
448             newcrp->recno = ldrecno(pkey + KEY_RECNO_OFF);
449             memcpy( newcrp->key,pkey, pkeydesc2->k2_len);
450             break;
451
452         case RM_GREAT:
453             /* 
454              * Make sure that you will read past all matching records.
455              */
456             _iskey_fillmax(pkeydesc2, keybuf1);
457  
458             /* 
459              * Extract key fields from record. 
460              */
461             _iskey_extract(pkeydesc2, record, keybuf2);
462             memcpy( keybuf1 + skipbytes,keybuf2 + skipbytes, matchkeylen);
463
464             /*
465              * Position pointer in the B-tree after the searched value. 
466              */
467             _isbtree_search(btree, keybuf1);
468         
469             if ((pkey = _isbtree_next(btree)) == NULL) {
470                 _amseterrcode(errcode, ENOREC);
471                 goto ERROR;
472             }
473             
474             newcrp->flag = CRP_BEFORE;
475             newcrp->recno = ldrecno(pkey + KEY_RECNO_OFF);
476             memcpy( newcrp->key,pkey, pkeydesc2->k2_len);
477             break;
478             
479         case RM_LESS:
480             /* 
481              * Make sure that you will read before all matching records.
482              */
483             _iskey_fillmin(pkeydesc2, keybuf1);
484  
485             /* 
486              * Extract key fields from record. 
487              */
488             _iskey_extract(pkeydesc2, record, keybuf2);
489             memcpy( keybuf1 + skipbytes,keybuf2 + skipbytes, matchkeylen);
490
491             /*
492              * Position pointer in the B-tree after the searched value. 
493              */
494             _isbtree_search(btree, keybuf1);
495         
496             if ((pkey = _isbtree_current(btree)) == NULL) {
497                 _amseterrcode(errcode, ENOREC);
498                 goto ERROR;
499             }
500             
501             newcrp->flag = CRP_AFTER;
502             newcrp->recno = ldrecno(pkey + KEY_RECNO_OFF);
503             memcpy( newcrp->key,pkey, pkeydesc2->k2_len);
504             break;
505
506         case RM_LTEQ:
507             /* 
508              * Make sure that you will the last duplicate.
509              */
510             _iskey_fillmax(pkeydesc2, keybuf1);
511  
512             /* 
513              * Extract key fields from record. 
514              */
515             _iskey_extract(pkeydesc2, record, keybuf2);
516             memcpy( keybuf1 + skipbytes,keybuf2 + skipbytes, matchkeylen);
517
518             /*
519              * Position pointer in the B-tree in before the searched value. 
520              */
521             _isbtree_search(btree, keybuf1);
522         
523             if ((pkey = _isbtree_current(btree)) == NULL) {
524                 _amseterrcode(errcode, ENOREC);
525                 goto ERROR;
526             }
527             
528             newcrp->flag = CRP_AFTER;
529             newcrp->recno = ldrecno(pkey + KEY_RECNO_OFF);
530             memcpy( newcrp->key,pkey, pkeydesc2->k2_len);
531             break;
532
533         case RM_FIRST:
534             /* 
535              * Fill key buffer with -infinity.
536              */
537             _iskey_fillmin(pkeydesc2, keybuf1);
538  
539             /*
540              * Position pointer in the B-tree before any key entry.
541              */
542             _isbtree_search(btree, keybuf1);
543         
544             if ((pkey = _isbtree_next(btree)) == NULL) {
545                 newcrp->flag = CRP_AFTERANY;
546             }
547             else {
548                 newcrp->flag = CRP_BEFORE;
549                 newcrp->recno = ldrecno(pkey + KEY_RECNO_OFF);
550                 memcpy( newcrp->key,pkey, pkeydesc2->k2_len);
551             }
552             break;
553
554         case RM_LAST:
555             /* 
556              * Fill key buffer with +infinity.
557              */
558             _iskey_fillmax(pkeydesc2, keybuf1);
559  
560             /*
561              * Position pointer in the B-tree after all entries.
562              */
563             _isbtree_search(btree, keybuf1);
564         
565             if ((pkey = _isbtree_current(btree)) == NULL) {
566                 newcrp->flag = CRP_BEFOREANY;
567             }
568             else {
569                 newcrp->flag = CRP_AFTER;
570                 newcrp->recno = ldrecno(pkey + KEY_RECNO_OFF);
571                 memcpy( newcrp->key,pkey, pkeydesc2->k2_len);
572             }
573             break;
574
575         default:
576             _isfatal_error("Invalid readmode");
577         }
578
579         *recnum = newcrp->recno;
580
581         /*
582          * Build new curpos, deallocate old curpos data.
583          */
584         _bytearr_free(curpos);
585         *curpos = _bytearr_new((u_short)newcrpsize, (char *)newcrp);
586
587         _isbtree_destroy(btree);
588     }
589
590     _amseterrcode(errcode, ISOK);
591
592     /* Clean-up work. */
593     if (newcrp != NULL)
594         free((char *)newcrp);
595
596     _isdisk_commit();                        /* This will only check
597                                               * that we unfixed all fixed
598                                               * buffers */
599     _isdisk_inval();
600
601     _isam_exithook();
602     return (ISOK);
603
604  ERROR:
605
606     if (btree != NULL)
607         _isbtree_destroy(btree);
608
609     /*
610      * If error is EBADKEY, make the current position undefined.
611      */
612     if (errcode->iserrno == EBADKEY) {
613         ((Crp *)curpos->data)->flag = CRP_UNDEF;
614     }
615
616     /*
617      * If error is ENOREC, switch to the new key, but set the current
618      * record position undefined.
619      */
620     if (errcode->iserrno == ENOREC && newcrp != NULL) {
621         _bytearr_free(curpos);
622         *curpos = _bytearr_new((u_short)newcrpsize, (char *)newcrp);
623         ((Crp *)curpos->data)->flag = CRP_UNDEF;
624     }
625
626     if (newcrp != NULL)
627         free((char *)newcrp);
628     _isdisk_inval();
629
630     _isam_exithook();
631     return (ISERROR);
632 }
633
634