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