1 |
dpavlin |
237 |
/* |
2 |
|
|
openisis - an open implementation of the CDS/ISIS database |
3 |
|
|
Version 0.8.x (patchlevel see file Version) |
4 |
|
|
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org |
5 |
|
|
|
6 |
|
|
This library is free software; you can redistribute it and/or |
7 |
|
|
modify it under the terms of the GNU Lesser General Public |
8 |
|
|
License as published by the Free Software Foundation; either |
9 |
|
|
version 2.1 of the License, or (at your option) any later version. |
10 |
|
|
|
11 |
|
|
This library is distributed in the hope that it will be useful, |
12 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 |
|
|
Lesser General Public License for more details. |
15 |
|
|
|
16 |
|
|
You should have received a copy of the GNU Lesser General Public |
17 |
|
|
License along with this library; if not, write to the Free Software |
18 |
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
|
|
20 |
|
|
see README for more information |
21 |
|
|
EOH */ |
22 |
|
|
|
23 |
|
|
/* |
24 |
|
|
$Id: ldb.c,v 1.95 2003/06/10 11:00:34 kripke Exp $ |
25 |
|
|
implementation of general db access functions. |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
#include <stdlib.h> |
29 |
|
|
#include <string.h> |
30 |
|
|
#include <limits.h> /* PATH_MAX */ |
31 |
|
|
#include <errno.h> |
32 |
|
|
|
33 |
|
|
|
34 |
|
|
/* special */ |
35 |
|
|
#if defined( __GNUC__ ) && defined ( alloca ) |
36 |
|
|
#include <alloca.h> |
37 |
|
|
#endif |
38 |
|
|
|
39 |
|
|
/* gcc defines always a cpu type - this we use for byteorder checking */ |
40 |
|
|
#if defined( sparc ) || defined( __ppc__ ) |
41 |
|
|
# define LDB_BIG_ENDIAN |
42 |
|
|
/* TODO: figure out fastest "htonl" on those boxes that usually don't swap */ |
43 |
|
|
static int rvi ( int i ) { |
44 |
|
|
int r; |
45 |
|
|
((char*)&r)[0] = ((char*)&i)[3]; |
46 |
|
|
((char*)&r)[1] = ((char*)&i)[2]; |
47 |
|
|
((char*)&r)[2] = ((char*)&i)[1]; |
48 |
|
|
((char*)&r)[3] = ((char*)&i)[0]; |
49 |
|
|
return r; |
50 |
|
|
} |
51 |
|
|
static short rvs ( short i ) { |
52 |
|
|
short r; |
53 |
|
|
((char*)&r)[0] = ((char*)&i)[1]; |
54 |
|
|
((char*)&r)[1] = ((char*)&i)[0]; |
55 |
|
|
return r; |
56 |
|
|
} |
57 |
|
|
#define SWI( i ) i = rvi( i ) |
58 |
|
|
#define SWS( s ) s = rvs( s ) |
59 |
|
|
#else |
60 |
|
|
# define rvi(i) i |
61 |
|
|
# define rvs(s) s |
62 |
|
|
#define SWI( i ) |
63 |
|
|
#define SWS( s ) |
64 |
|
|
#endif |
65 |
|
|
#if defined( sparc ) |
66 |
|
|
# define LDB_NEEDALIGN |
67 |
|
|
#endif |
68 |
|
|
#if defined( LDB_NEEDALIGN ) |
69 |
|
|
static unsigned GETINT ( const void *m ) |
70 |
|
|
{ |
71 |
|
|
unsigned l; |
72 |
|
|
memcpy( &l, m, 4 ); |
73 |
|
|
return l; |
74 |
|
|
} |
75 |
|
|
static unsigned short GETSHORT ( const void *m ) |
76 |
|
|
{ |
77 |
|
|
unsigned short s; |
78 |
|
|
memcpy( &s, m, 2 ); |
79 |
|
|
return s; |
80 |
|
|
} |
81 |
|
|
#else |
82 |
|
|
#define GETINT( m ) (*(unsigned*)(m)) |
83 |
|
|
#define GETSHORT( m ) (*(unsigned short*)(m)) |
84 |
|
|
#endif |
85 |
|
|
|
86 |
|
|
#include "lstr.h" |
87 |
|
|
#include "lio.h" |
88 |
|
|
#include "lbt.h" |
89 |
|
|
#include "lcs.h" |
90 |
|
|
#include "ldb.h" |
91 |
|
|
#include "lfdt.h" |
92 |
|
|
#include "luti.h" |
93 |
|
|
|
94 |
|
|
#ifdef WIN32 |
95 |
|
|
#define IsAbsPath(p) \ |
96 |
|
|
((p) && *(p) && ( \ |
97 |
|
|
'/' == *(p) || '\\' == *(p) || ( \ |
98 |
|
|
':' == (p)[1] && ( \ |
99 |
|
|
'/' == (p)[2] || '\\' == (p)[2] \ |
100 |
|
|
)))) |
101 |
|
|
#else |
102 |
|
|
#define IsAbsPath(p) \ |
103 |
|
|
((p) && '/' == *(p)) |
104 |
|
|
#endif |
105 |
|
|
|
106 |
|
|
|
107 |
|
|
#define LF 10 /* LineFeed a.k.a. newline - '\n' isn't really well defined */ |
108 |
|
|
#define TAB 9 /* horizontal, that is */ |
109 |
|
|
#define VT 11 /* vertical, used as newline replacement */ |
110 |
|
|
|
111 |
|
|
/* ************************************************************ |
112 |
|
|
private types |
113 |
|
|
*/ |
114 |
|
|
|
115 |
|
|
/** extension of master file proper. */ |
116 |
|
|
static const char EXT_MST_MST[] = ".mst"; |
117 |
|
|
/** extension of master file xref. */ |
118 |
|
|
static const char EXT_MST_XRF[] = ".xrf"; |
119 |
|
|
|
120 |
|
|
typedef enum { |
121 |
|
|
MST_MST, |
122 |
|
|
MST_XRF, |
123 |
|
|
MST_FILES |
124 |
|
|
} mst_file; |
125 |
|
|
|
126 |
|
|
static const char * const EXT_MST[MST_FILES] = { |
127 |
|
|
EXT_MST_MST, |
128 |
|
|
EXT_MST_XRF |
129 |
|
|
}; |
130 |
|
|
|
131 |
|
|
/** extension of inverted file short term nodes. */ |
132 |
|
|
static const char EXT_INV_N01[] = ".n01"; |
133 |
|
|
/** extension of inverted file short term leaves. */ |
134 |
|
|
static const char EXT_INV_L01[] = ".l01"; |
135 |
|
|
/** extension of inverted file long term nodes. */ |
136 |
|
|
static const char EXT_INV_N02[] = ".n02"; |
137 |
|
|
/** extension of inverted file long term leaves. */ |
138 |
|
|
static const char EXT_INV_L02[] = ".l02"; |
139 |
|
|
/** extension of inverted file control records. */ |
140 |
|
|
static const char EXT_INV_CNT[] = ".cnt"; |
141 |
|
|
/** extension of inverted file postings. */ |
142 |
|
|
static const char EXT_INV_IFP[] = ".ifp"; |
143 |
|
|
|
144 |
|
|
typedef enum { |
145 |
|
|
INV_N01, |
146 |
|
|
INV_L01, |
147 |
|
|
INV_N02, |
148 |
|
|
INV_L02, |
149 |
|
|
INV_CNT, |
150 |
|
|
INV_IFP, |
151 |
|
|
INV_FILES |
152 |
|
|
} inv_file; |
153 |
|
|
|
154 |
|
|
static const char * const EXT_INV[INV_FILES] = { |
155 |
|
|
EXT_INV_N01, |
156 |
|
|
EXT_INV_L01, |
157 |
|
|
EXT_INV_N02, |
158 |
|
|
EXT_INV_L02, |
159 |
|
|
EXT_INV_CNT, |
160 |
|
|
EXT_INV_IFP |
161 |
|
|
}; |
162 |
|
|
|
163 |
|
|
/** extension of lbt B-Link-Tree. |
164 |
|
|
It's named oxi because that is nicer than oix for OpenIsis indeX. |
165 |
|
|
however, see http://www.oxicenter.com.br/ |
166 |
|
|
*/ |
167 |
|
|
static const char EXT_LBT_OXI[] = ".oxi"; |
168 |
|
|
static const char * const EXT_LBT[] = { |
169 |
|
|
EXT_LBT_OXI |
170 |
|
|
}; |
171 |
|
|
|
172 |
|
|
|
173 |
|
|
/** plaintext master file |
174 |
|
|
*/ |
175 |
|
|
static const char EXT_TXT_TXT[] = ".txt"; |
176 |
|
|
static const char EXT_TXT_PTR[] = ".ptr"; |
177 |
|
|
static const char EXT_TXT_OPT[] = ".opt"; |
178 |
|
|
typedef enum { |
179 |
|
|
TXT_TXT, |
180 |
|
|
TXT_PTR, |
181 |
|
|
TXT_FILES |
182 |
|
|
} txt_file; |
183 |
|
|
static const char * const EXT_TXT[] = { |
184 |
|
|
EXT_TXT_TXT, |
185 |
|
|
EXT_TXT_PTR |
186 |
|
|
}; |
187 |
|
|
|
188 |
|
|
static const char ISIX[] = "ISIX"; /* ptr magic */ |
189 |
|
|
|
190 |
|
|
|
191 |
|
|
/** extension of supporting file alpha character table. */ |
192 |
|
|
static const char EXT_SUP_ACT[] = ".act"; |
193 |
|
|
/** extension of supporting file uppercase table. */ |
194 |
|
|
static const char EXT_SUP_UCT[] = ".uct"; |
195 |
|
|
|
196 |
|
|
typedef enum { |
197 |
|
|
SUP_ACT, |
198 |
|
|
SUP_UCT, |
199 |
|
|
SUP_FILES |
200 |
|
|
} sup_file; |
201 |
|
|
|
202 |
|
|
static const char * const EXT_SUP[SUP_FILES] = { |
203 |
|
|
EXT_SUP_ACT, |
204 |
|
|
EXT_SUP_UCT |
205 |
|
|
}; |
206 |
|
|
|
207 |
|
|
|
208 |
|
|
typedef int lblk[128]; |
209 |
|
|
|
210 |
|
|
|
211 |
|
|
typedef struct { |
212 |
|
|
Db head; |
213 |
|
|
int flags; |
214 |
|
|
const char *path; |
215 |
|
|
int mst[MST_FILES]; /* master file */ |
216 |
|
|
int inv[INV_FILES]; /* primary inverted file */ |
217 |
|
|
int mfc[LMFC__FL]; /* master file control record */ |
218 |
|
|
unsigned mflen; /* master file length */ |
219 |
|
|
int xrf[129]; /* last used xrf block : THREAD THREAT */ |
220 |
|
|
int xrlen; /* length of xrf (in blocks) */ |
221 |
|
|
unsigned short ptr; /* type of pointer file (new style xrf) */ |
222 |
|
|
unsigned short ptrl; /* pointer bytes, 512 for old xrf */ |
223 |
|
|
char *mmap; /* memory map of xrf/ptr */ |
224 |
|
|
int mmlen; /* length of map (in ptrl) */ |
225 |
|
|
int cnt[LDB_INDEXES][LCNT__FL]; /* two cnt records */ |
226 |
|
|
short tlen[LDB_INDEXES]; /* max term length for each index */ |
227 |
|
|
LcsTab ctab[LCS__TABS]; |
228 |
|
|
Idx oxi; |
229 |
|
|
} LDb; |
230 |
|
|
|
231 |
|
|
|
232 |
|
|
typedef union { |
233 |
|
|
lll bar; |
234 |
|
|
char r[16]; |
235 |
|
|
} Ptr; |
236 |
|
|
|
237 |
|
|
|
238 |
|
|
/* db flags */ |
239 |
|
|
#define DB_OPEN 0x010000 |
240 |
|
|
#define DB_INVOPEN 0x020000 |
241 |
|
|
#define DB_LBTOPEN 0x040000 |
242 |
|
|
#define DB_TXTOPEN 0x080000 |
243 |
|
|
#define DB_WRITABLE 0x100000 |
244 |
|
|
#define DB_MODIFIED 0x200000 |
245 |
|
|
|
246 |
|
|
#define DB_TXTMODE 0x20 |
247 |
|
|
#define DB_MMAP 0x10 |
248 |
|
|
#define DB_VARI 0xf /* mask for variant */ |
249 |
|
|
|
250 |
|
|
|
251 |
|
|
/* get xstr for record rec in set */ |
252 |
|
|
#define DB_XSTR( db, set, rec ) \ |
253 |
|
|
lstrlib[ set ].desc[ DB_VARI & (db)->flags ][ rec ] |
254 |
|
|
/* get record names for record rec in set */ |
255 |
|
|
#define DB_RNAM( db, set, rec ) \ |
256 |
|
|
lstrlib[ set ].name[ rec ] |
257 |
|
|
|
258 |
|
|
|
259 |
|
|
/** packed little endian masterfile control structure. |
260 |
|
|
*/ |
261 |
|
|
typedef struct Mfc { |
262 |
|
|
int ctlm; |
263 |
|
|
int nmfn; |
264 |
|
|
int nmfb; |
265 |
|
|
short nmfp; |
266 |
|
|
short type; |
267 |
|
|
int rcnt; |
268 |
|
|
int mfx1; |
269 |
|
|
int mfx2; |
270 |
|
|
int mfx3; |
271 |
|
|
} Mfc; |
272 |
|
|
|
273 |
|
|
|
274 |
|
|
/** packed little endian masterfile record. |
275 |
|
|
*/ |
276 |
|
|
typedef struct Dict { |
277 |
|
|
short tag; |
278 |
|
|
short pos; |
279 |
|
|
short len; |
280 |
|
|
} Dict; |
281 |
|
|
|
282 |
|
|
/** packed little endian masterfile record. |
283 |
|
|
*/ |
284 |
|
|
typedef struct Mfr { |
285 |
|
|
int mfn; |
286 |
|
|
short recl; /* a.k.a. mfrl */ |
287 |
|
|
short bwbl; /* low part of int */ |
288 |
|
|
short bwbh; /* high part of int */ |
289 |
|
|
short bwp; |
290 |
|
|
/* it is believed, that this first five fields up to here (12 bytes packed) |
291 |
|
|
are to be in one 512-byte block; the manual mentiones even 14 bytes ... ??? |
292 |
|
|
*/ |
293 |
|
|
short base; |
294 |
|
|
short nvf; |
295 |
|
|
short stat; |
296 |
|
|
Dict dict[1]; |
297 |
|
|
} Mfr; |
298 |
|
|
|
299 |
|
|
|
300 |
|
|
|
301 |
|
|
/* ************************************************************ |
302 |
|
|
private data |
303 |
|
|
*/ |
304 |
|
|
|
305 |
|
|
static LDb defdbspace[32]; |
306 |
|
|
/* array of open dbs. should expand dynamically. */ |
307 |
|
|
static LDb *dbs = defdbspace; |
308 |
|
|
static int dbs_len = sizeof(defdbspace)/sizeof(defdbspace[0]); |
309 |
|
|
|
310 |
|
|
static int init; |
311 |
|
|
|
312 |
|
|
|
313 |
|
|
/* ************************************************************ |
314 |
|
|
private functions |
315 |
|
|
*/ |
316 |
|
|
static LDb *getDb ( int id ) |
317 |
|
|
{ |
318 |
|
|
if ( 0 <= id && id < dbs_len && dbs[id].flags ) { |
319 |
|
|
return &dbs[id]; |
320 |
|
|
} |
321 |
|
|
log_msg( LOG_ERROR, "attempt to access bad db id %d", id ); |
322 |
|
|
return 0; |
323 |
|
|
} /* getDb */ |
324 |
|
|
|
325 |
|
|
|
326 |
|
|
/* ************************************************************ |
327 |
|
|
start of io section |
328 |
|
|
*/ |
329 |
|
|
enum { |
330 |
|
|
/* additional flags in the LIO_FD range */ |
331 |
|
|
OPEN_TRY = 1, /* try writable, open readonly else */ |
332 |
|
|
OPEN_UC = 2, /* use uppercase ext */ |
333 |
|
|
/* commonly used combinations */ |
334 |
|
|
/* 1) open as is, do not complain about any failure, do not create */ |
335 |
|
|
OPEN_ASIS = LIO_SEEK|LIO_RDWR|OPEN_TRY, |
336 |
|
|
/* 2) open readonly, do not complain about any failure, do not create */ |
337 |
|
|
OPEN_RDIF = LIO_SEEK|LIO_RD|LIO_TRY, |
338 |
|
|
/* 3) open readonly, complain about any failure */ |
339 |
|
|
OPEN_RD = LIO_SEEK|LIO_RD, |
340 |
|
|
/* 4) open or create writable, complain on failure */ |
341 |
|
|
OPEN_NEW = LIO_SEEK|LIO_RDWR|LIO_CREAT, |
342 |
|
|
OPEN_BLANK = LIO_SEEK|LIO_RDWR|LIO_CREAT|LIO_TRUNC |
343 |
|
|
}; |
344 |
|
|
|
345 |
|
|
/* figure out wether to use uppercase extension on path. |
346 |
|
|
if last path component (everything after the last / and \) |
347 |
|
|
does contain an uppercase ascii and does not contain a lowercase ascii, |
348 |
|
|
return OPEN_UC, else 0. |
349 |
|
|
*/ |
350 |
|
|
static int autocase ( const char *path ) |
351 |
|
|
{ |
352 |
|
|
int ret = 0; |
353 |
|
|
const char *e = path + strlen( path ); |
354 |
|
|
while ( e-- > path ) |
355 |
|
|
if ( 'A'<=*e && *e<= 'Z' ) |
356 |
|
|
ret = OPEN_UC; |
357 |
|
|
else if ( 'a'<=*e && *e<= 'z' ) |
358 |
|
|
return 0; |
359 |
|
|
else if ( '/'==*e || '\\' == *e ) |
360 |
|
|
break; |
361 |
|
|
return ret; |
362 |
|
|
} |
363 |
|
|
|
364 |
|
|
/* set extension. fname MUST already end with .xxx. |
365 |
|
|
if how has OPEN_UC set, use uppercase extension |
366 |
|
|
*/ |
367 |
|
|
static char *setext ( char *fname, const char *ext, int how ) |
368 |
|
|
{ |
369 |
|
|
int l = strlen( fname ) - 4; |
370 |
|
|
memcpy( fname+l, ext, 4 ); |
371 |
|
|
if ( OPEN_UC & how ) { |
372 |
|
|
char *p = fname+l; |
373 |
|
|
for ( ;*p; p++ ) /* use uppercase extensions */ |
374 |
|
|
if ( 'a' <= *p && *p <= 'z' ) |
375 |
|
|
*p -= 'a'-'A'; |
376 |
|
|
} |
377 |
|
|
return fname; |
378 |
|
|
} |
379 |
|
|
|
380 |
|
|
/** |
381 |
|
|
try to open all files according to how. |
382 |
|
|
ldb is only interested in seekable readable true files. |
383 |
|
|
@return |
384 |
|
|
1 if all files could be opened writable |
385 |
|
|
0 if all files could be opened readonly, |
386 |
|
|
and that was requested by a RD mode or try write |
387 |
|
|
something negative else |
388 |
|
|
*/ |
389 |
|
|
static int openfiles ( int *fid, char *path, |
390 |
|
|
const char * const *ext, int nfiles, int how ) |
391 |
|
|
{ |
392 |
|
|
int i; |
393 |
|
|
int wr = LIO_WR&how ? 1 : 0, mode = LIO_WANT & how; |
394 |
|
|
|
395 |
|
|
for ( i=0; i<nfiles; i++ ) { |
396 |
|
|
setext( path, ext[i], how ); |
397 |
|
|
fid[i] = lio_open( path, mode & LIO_WANT ); |
398 |
|
|
log_msg( LOG_INFO, "opening file '%s' %c 0x%x", |
399 |
|
|
path, wr ? 'w' : 'r', fid[i] ); |
400 |
|
|
if ( 0 < fid[i] ) { /* ok */ |
401 |
|
|
mode &= ~LIO_FLOCK; /* lock only leading file */ |
402 |
|
|
continue; |
403 |
|
|
} |
404 |
|
|
fid[i] = 0; |
405 |
|
|
while ( i-- ) /* close others */ |
406 |
|
|
lio_close( &fid[i], LIO_INOUT ); |
407 |
|
|
if ( OPEN_TRY & how ) |
408 |
|
|
return openfiles( fid, path, ext, nfiles, |
409 |
|
|
(how & ~(OPEN_TRY|LIO_WR)) | LIO_TRY ); |
410 |
|
|
return LIO_TRY&how ? -ERR_BADF /* silent */ |
411 |
|
|
: log_msg( LOG_SYSERR, "could not open file '%s' for %sing", |
412 |
|
|
path, wr ? "writ" : "read" ); |
413 |
|
|
} |
414 |
|
|
return wr; /* good */ |
415 |
|
|
} /* openfiles */ |
416 |
|
|
|
417 |
|
|
|
418 |
|
|
static int closefiles ( int *fid, int nfiles ) |
419 |
|
|
{ |
420 |
|
|
int ret = 0, i; |
421 |
|
|
for ( i=0; i<nfiles; i++ ) |
422 |
|
|
if ( 0 < fid[i] && LIO_INOUT & fid[i] ) |
423 |
|
|
lio_close( &fid[i], LIO_INOUT ); |
424 |
|
|
return ret; |
425 |
|
|
} /* closefiles */ |
426 |
|
|
|
427 |
|
|
|
428 |
|
|
static int readblk ( void *dst, int siz, int fid, int where ) |
429 |
|
|
{ |
430 |
|
|
int got; |
431 |
|
|
got = lio_pread( &fid, dst, siz, where ); |
432 |
|
|
if ( 0 > got ) |
433 |
|
|
return got; |
434 |
|
|
#ifndef NDEBUG |
435 |
|
|
if ( LOG_DO( LOG_ALL ) ) |
436 |
|
|
LOG_HEX( dst, got ); |
437 |
|
|
#endif |
438 |
|
|
if ( siz == (int)got ) |
439 |
|
|
return 0; |
440 |
|
|
log_msg( LOG_WARN, "got %u bytes wanted %d at %d in 0x%x", |
441 |
|
|
got, siz, where, fid ); |
442 |
|
|
return 1+(int)got; |
443 |
|
|
} /* readblk */ |
444 |
|
|
|
445 |
|
|
|
446 |
|
|
/* ************************************************************ |
447 |
|
|
end of io section |
448 |
|
|
*/ |
449 |
|
|
|
450 |
|
|
static int *nrec ( int *xstr ) |
451 |
|
|
{ |
452 |
|
|
int *dst = (int*)mAlloc( xstr[LSTR_ILEN] ); |
453 |
|
|
if ( dst ) |
454 |
|
|
*dst = *xstr; |
455 |
|
|
return dst; |
456 |
|
|
} /* nrec */ |
457 |
|
|
|
458 |
|
|
|
459 |
|
|
|
460 |
|
|
typedef struct b8 { char x[8]; } b8; |
461 |
|
|
typedef struct b4 { char x[4]; } b4; |
462 |
|
|
typedef struct b2 { char x[2]; } b2; |
463 |
|
|
|
464 |
|
|
|
465 |
|
|
static int convert ( int *dst, char *src, int *xstr ) |
466 |
|
|
{ |
467 |
|
|
static int pow2[] = { 1, 2, 4, 8 }; |
468 |
|
|
int occ = -1; |
469 |
|
|
int *xmbrs = xstr+LSTR_XMBR; |
470 |
|
|
int nmbrs = LSTRFIX(*xstr); |
471 |
|
|
int *mbr; |
472 |
|
|
char *buf, *part = src, *srcend = src + xstr[LSTR_XLEN]; |
473 |
|
|
|
474 |
|
|
if ( ! dst ) |
475 |
|
|
return log_msg( ERR_NOMEM, "out of memory (no dst) in convert" ); |
476 |
|
|
if ( LSTRLEN(*xstr) > xstr[LSTR_ILEN] ) |
477 |
|
|
return log_msg( ERR_INVAL, "bad ilen %d need %d", |
478 |
|
|
xstr[LSTR_ILEN], (int)LSTRLEN(*xstr) ); |
479 |
|
|
if ( *dst != *xstr ) |
480 |
|
|
return log_msg( ERR_INVAL, "bad *dst 0x%08x need 0x%08x", *dst, *xstr ); |
481 |
|
|
/* clean and re-init */ |
482 |
|
|
memset( dst, 0, xstr[LSTR_ILEN] ); |
483 |
|
|
*dst = *xstr; |
484 |
|
|
mbr = dst+1; |
485 |
|
|
buf = ((char*)dst) + LSTRLEN(*dst); |
486 |
|
|
|
487 |
|
|
/* cvt the fixed part (occ==-1) and each occurrence of repeated part. */ |
488 |
|
|
for ( ;/* occ < LSTROCC(*dst) */; ) { /* cvt one part */ |
489 |
|
|
int i; |
490 |
|
|
for ( i=0; i<nmbrs; i++, mbr++ ) { /* assign one xmbr */ |
491 |
|
|
int xmbr = xmbrs[i]; |
492 |
|
|
char *s = part + LONG2OFF(xmbr); |
493 |
|
|
int sbytes = srcend - s; |
494 |
|
|
int len,j; |
495 |
|
|
union { |
496 |
|
|
char buf[8]; |
497 |
|
|
b8 x8; |
498 |
|
|
b4 x4; |
499 |
|
|
b2 x2; |
500 |
|
|
short s; |
501 |
|
|
int i; |
502 |
|
|
lll ll; |
503 |
|
|
} num; |
504 |
|
|
if ( LMBRISNUM( xmbr ) ) { |
505 |
|
|
/* numeric data */ |
506 |
|
|
#ifdef LDB_BIG_ENDIAN |
507 |
|
|
# define NEEDSWAP(xmbr) ( ! ( LMBR_FHIE & (xmbr) ) ) |
508 |
|
|
#else |
509 |
|
|
# define NEEDSWAP(xmbr) (LMBR_FHIE & (xmbr)) |
510 |
|
|
#endif |
511 |
|
|
int ld = LMBRLD(xmbr); |
512 |
|
|
#ifndef LDB_NEEDALIGN |
513 |
|
|
if ( ! NEEDSWAP( xmbr ) ) { |
514 |
|
|
/* much faster this way ... */ |
515 |
|
|
/* TODO: len and bit checks; actually not needed yet ... */ |
516 |
|
|
switch ( ld ) { |
517 |
|
|
case 3: *mbr = *(lll*)s; break; |
518 |
|
|
case 2: *mbr = *(int*)s; break; |
519 |
|
|
case 1: *mbr = *(short*)s; break; |
520 |
|
|
case 0: *mbr = *s; break; |
521 |
|
|
} |
522 |
|
|
continue; |
523 |
|
|
} |
524 |
|
|
#endif |
525 |
|
|
len = pow2[ LMBRLD(xmbr) ]; |
526 |
|
|
if ( len > sbytes ) |
527 |
|
|
return log_msg( ERR_INVAL, |
528 |
|
|
"srcbuf too short %d have %d need %d occ %d mbr %d", |
529 |
|
|
xstr[LSTR_XLEN], sbytes, len, occ, i ); |
530 |
|
|
if ( !NEEDSWAP( xmbr ) ) |
531 |
|
|
/* for ( j = len; j--; ) num.buf[j] = s[j]; */ |
532 |
|
|
switch ( ld ) { |
533 |
|
|
case 3: num.x8 = *(b8*)s; break; |
534 |
|
|
case 2: num.x4 = *(b4*)s; break; |
535 |
|
|
case 1: num.x2 = *(b2*)s; break; |
536 |
|
|
case 0: num.buf[0] = *s; break; |
537 |
|
|
} |
538 |
|
|
else /* swap bytes */ |
539 |
|
|
for ( j = len; j--; ) |
540 |
|
|
num.buf[j] = s[len - 1 - j]; |
541 |
|
|
switch ( len ) { |
542 |
|
|
case 8: *mbr = num.ll; break; |
543 |
|
|
/* TODO: defines for 16 and 64 bit compilers */ |
544 |
|
|
case 4: *mbr = num.i; break; |
545 |
|
|
case 2: *mbr = num.s; break; |
546 |
|
|
case 1: *mbr = num.buf[0]; break; |
547 |
|
|
} |
548 |
|
|
if ( LMBRISBITS( xmbr ) ) { /* apply bit shift and mask */ |
549 |
|
|
*mbr >>= LMBRBITOFF( xmbr ); |
550 |
|
|
*mbr &= ~(-1L << LMBRBITLEN( xmbr )); |
551 |
|
|
} |
552 |
|
|
continue; |
553 |
|
|
} |
554 |
|
|
/* else raw data -- that's easy :) */ |
555 |
|
|
{ |
556 |
|
|
int offset = buf - (char*)dst; |
557 |
|
|
int need; |
558 |
|
|
len = LONG2LEN(xmbr); |
559 |
|
|
need = offset + len; |
560 |
|
|
if ( need > xstr[LSTR_ILEN] ) |
561 |
|
|
return log_msg( ERR_INVAL, |
562 |
|
|
"bad buflen %d need %d+%d in occ %d mbr %d", |
563 |
|
|
xstr[LSTR_ILEN], offset, len, occ, i ); |
564 |
|
|
if ( len > sbytes ) |
565 |
|
|
return log_msg( ERR_INVAL, |
566 |
|
|
"srcbuf too short %d have %d need %d occ %d mbr %d", |
567 |
|
|
xstr[LSTR_XLEN], sbytes, len, occ, i ); |
568 |
|
|
memcpy( buf, s, len ); |
569 |
|
|
*mbr = buf - (char*)dst; |
570 |
|
|
buf += len; |
571 |
|
|
} |
572 |
|
|
} /* for mbrs */ |
573 |
|
|
|
574 |
|
|
if ( ++occ >= LSTROCC(*dst) ) |
575 |
|
|
break; |
576 |
|
|
if ( occ ) |
577 |
|
|
part += (short)xstr[LSTR_XRLO]; /* adv. rep. part len */ |
578 |
|
|
else { /* was the fixed part, setup for repeated */ |
579 |
|
|
nmbrs = LSTRREP(*xstr); |
580 |
|
|
part += (short)(xstr[LSTR_XRLO]>>16); /* adv. rep. part off */ |
581 |
|
|
xmbrs += i; |
582 |
|
|
} |
583 |
|
|
} |
584 |
|
|
return 0; |
585 |
|
|
} /* convert */ |
586 |
|
|
|
587 |
|
|
|
588 |
|
|
static int readrec ( int *dst, int fid, int where, int *xstr ) |
589 |
|
|
{ |
590 |
|
|
char *buf = (char *) |
591 |
|
|
#ifdef alloca |
592 |
|
|
alloca( xstr[LSTR_XLEN] ) |
593 |
|
|
#else |
594 |
|
|
mAlloc( xstr[LSTR_XLEN] ) |
595 |
|
|
#endif |
596 |
|
|
; |
597 |
|
|
int got = 0; |
598 |
|
|
int ret = 0; |
599 |
|
|
|
600 |
|
|
if ( ! buf ) |
601 |
|
|
return -ERR_NOMEM; |
602 |
|
|
if ( 0 > where ) |
603 |
|
|
where = -where * xstr[LSTR_XLEN]; |
604 |
|
|
got = readblk( buf, xstr[LSTR_XLEN], fid, where ); |
605 |
|
|
ret = got ? got : convert( dst, buf, xstr ); |
606 |
|
|
#ifndef alloca |
607 |
|
|
mFree( buf ); |
608 |
|
|
#endif |
609 |
|
|
|
610 |
|
|
return ret; |
611 |
|
|
} /* readrec */ |
612 |
|
|
|
613 |
|
|
|
614 |
|
|
/* read and log */ |
615 |
|
|
static int readlog ( int *dst, int fid, int where, LDb *db, int set, int rec ) |
616 |
|
|
{ |
617 |
|
|
int ret = readrec( dst, fid, where, DB_XSTR( db, set, rec ) ); |
618 |
|
|
if ( !ret && LOG_DO( LOG_TRACE ) ) |
619 |
|
|
LOG_STR( dst, lstrlib[ set ].name[ rec ] ); |
620 |
|
|
return ret; |
621 |
|
|
} /* readlog */ |
622 |
|
|
|
623 |
|
|
|
624 |
|
|
|
625 |
|
|
/* ************************************************************ |
626 |
|
|
access functions for the record types |
627 |
|
|
*/ |
628 |
|
|
|
629 |
|
|
static int getOff ( LDb *db, int rowid, int xr ) |
630 |
|
|
{ |
631 |
|
|
int rowix = rowid - 1; /* mfns count from 1 */ |
632 |
|
|
int xrf_block = rowix / 127; |
633 |
|
|
int blkix = xrf_block + 1; /* ... so do xrf blocks */ |
634 |
|
|
int off; |
635 |
|
|
if ( xrf_block < db->mmlen ) { |
636 |
|
|
if ( xr ) |
637 |
|
|
((lblk*)db->mmap)[xrf_block][1+(int)(rowix % 127)] = rvi( xr ); |
638 |
|
|
else |
639 |
|
|
xr = rvi( ((lblk*)db->mmap)[xrf_block][1+(int)(rowix % 127)] ); |
640 |
|
|
} else { |
641 |
|
|
int err = 0; |
642 |
|
|
/* if ( LIO_LOCK() ) return -3; */ |
643 |
|
|
if ( xr ) { /* write */ |
644 |
|
|
if ( blkix <= db->xrlen ) { |
645 |
|
|
SWI( xr ); |
646 |
|
|
if ( 4 != lio_pwrite( &db->mst[MST_XRF], &xr, 4, |
647 |
|
|
xrf_block*512 + 4*(1 + (rowix % 127))) ) |
648 |
|
|
return 0; |
649 |
|
|
} else { /* extent */ |
650 |
|
|
lblk extend; |
651 |
|
|
memset( extend, 0, sizeof(extend) ); |
652 |
|
|
while ( db->xrlen < blkix ) { /* extend */ |
653 |
|
|
extend[0] = db->xrlen + 1; /* set blk id */ |
654 |
|
|
if ( blkix == extend[0] ) |
655 |
|
|
extend[1+(int)(rowix % 127)] = rvi( xr ); |
656 |
|
|
SWI( extend[0] ); |
657 |
|
|
if ( 512 != lio_pwrite( &db->mst[MST_XRF], extend, 512, |
658 |
|
|
db->xrlen*512) ) |
659 |
|
|
return 0; |
660 |
|
|
db->xrlen++; |
661 |
|
|
} |
662 |
|
|
} |
663 |
|
|
if ( blkix == db->xrf[LXRF_XPOS] ) |
664 |
|
|
db->xrf[ LXRF_XREC + (int)(rowix % 127) ] = xr; |
665 |
|
|
/* and go on read it back, just to check ... */ |
666 |
|
|
} |
667 |
|
|
if ( blkix != db->xrf[LXRF_XPOS] ) { |
668 |
|
|
int ret; |
669 |
|
|
LOG_DBG( LOG_VERBOSE, "fetching xrf block %d had %d", |
670 |
|
|
blkix, ! db->xrf ? -1 : db->xrf[LXRF_XPOS] ); |
671 |
|
|
ret = readlog( db->xrf, db->mst[MST_XRF], |
672 |
|
|
-xrf_block, db, LSET_MST, LSTR_XRF ); |
673 |
|
|
if ( ret ) { |
674 |
|
|
log_msg( LOG_ERROR, "\twhen fetching xrf block %d", blkix ); |
675 |
|
|
err = -1; |
676 |
|
|
} else if ( blkix == -db->xrf[LXRF_XPOS] ) { |
677 |
|
|
LOG_DBG( LOG_DEBUG, "hmmm ... negative" ); |
678 |
|
|
db->xrf[LXRF_XPOS] = blkix; |
679 |
|
|
} else if ( blkix != db->xrf[LXRF_XPOS] ) { |
680 |
|
|
log_msg( LOG_WARN, "bad xrf %d wanted %d", |
681 |
|
|
db->xrf[LXRF_XPOS], blkix ); |
682 |
|
|
err = -2; |
683 |
|
|
} |
684 |
|
|
} |
685 |
|
|
xr = db->xrf[ LXRF_XREC + (int)(rowix % 127) ]; |
686 |
|
|
/* LIO_RELE(); */ |
687 |
|
|
if ( err ) |
688 |
|
|
return err; |
689 |
|
|
} |
690 |
|
|
/* |
691 |
|
|
21 bits (<<11) signed for the (512 byte) block ("xrmfb") |
692 |
|
|
1 for the first block (offset 0) |
693 |
|
|
0 means, never had such a record |
694 |
|
|
-1 and xrmfp=0: record removed from MST |
695 |
|
|
(there is no record at pos 0 in 1st block, |
696 |
|
|
since there resides the MST header) |
697 |
|
|
other negative value -x or pos!=0: |
698 |
|
|
record logically deleted, was at +x |
699 |
|
|
1 bit (1<<10): this record is new and not yet inverted |
700 |
|
|
1 bit (1<<9): this record is changed and not yet re-inverted |
701 |
|
|
9 bits for the block-relative position ("xrmfp") |
702 |
|
|
*/ |
703 |
|
|
off = (((xr & 0xfffff800) >> 2) - 0x200) | (0x1ff & xr); |
704 |
|
|
if ( 0 < (xr & ~0x600) ) { |
705 |
|
|
LOG_DBG( LOG_DEBUG, |
706 |
|
|
"offset for rowid %d is %d (blk %d pos %d) flg 0x%08x at %d[%d]", |
707 |
|
|
rowid, off, (xr>>11)&0xfffff, xr&0x1ff, xr&0x80000600, blkix, rowix%127 ); |
708 |
|
|
return off; |
709 |
|
|
} |
710 |
|
|
/* deleted */ |
711 |
|
|
log_msg( LOG_INFO, |
712 |
|
|
"offset for rowid %d is %d (blk %d pos %d) flg 0x%08x at %d[%d]", |
713 |
|
|
rowid, off, (xr>>11)&0xfffff, xr&0x1ff, xr&0x80000600, blkix, rowix%127 ); |
714 |
|
|
return 0; |
715 |
|
|
} /* getOff */ |
716 |
|
|
|
717 |
|
|
|
718 |
|
|
static int* getMfr ( LDb *db, int off, int *nxtoff ) |
719 |
|
|
{ |
720 |
|
|
struct mfcxstr { |
721 |
|
|
int xstr[LSTR_LONGS(7+3)]; |
722 |
|
|
} my = *(struct mfcxstr *)DB_XSTR( db, LSET_MST, LSTR_MFR ); |
723 |
|
|
int head[1+7+3]; |
724 |
|
|
int len = 0, base; |
725 |
|
|
int *rec = 0; |
726 |
|
|
char *buf = 0; |
727 |
|
|
#ifdef alloca |
728 |
|
|
int notalloca = 0; |
729 |
|
|
#endif |
730 |
|
|
|
731 |
|
|
LOG_DBG( LOG_VERBOSE, "getting MFR at off %d", off ); |
732 |
|
|
if ( 498 < off % 512 ) |
733 |
|
|
log_msg( LOG_WARN, "blk pos > 498 in offset 0x%08x", off ); |
734 |
|
|
*head = *my.xstr; |
735 |
|
|
if ( readrec( head, db->mst[MST_MST], off, my.xstr ) ) { |
736 |
|
|
log_msg( LOG_ERROR, "\twhen reading MFR head at %d", off ); |
737 |
|
|
return 0; |
738 |
|
|
} |
739 |
|
|
/* log_str( LOG_VERBOSE, head, lstrlib[LSET_MST].name[LSTR_MFR] ); */ |
740 |
|
|
len = head[LMFR_RECL]; |
741 |
|
|
LOG_DBG( LOG_VERBOSE, "got MFR %d reclen %d", head[LMFR_MFN], len ); |
742 |
|
|
if ( nxtoff ) { |
743 |
|
|
*nxtoff = off + (0 < len ? len : -len); /* an odditiy */ |
744 |
|
|
if ( 1 & *nxtoff ) /* an odditiy */ |
745 |
|
|
(*nxtoff)++; /* round up to even */ |
746 |
|
|
if ( 498 < *nxtoff % 512 ) { |
747 |
|
|
*nxtoff += 512; |
748 |
|
|
*nxtoff &= ~0x1ff; |
749 |
|
|
} |
750 |
|
|
if ( *nxtoff > db->mfc[LMFC_NMFB]*512 + db->mfc[LMFC_NMFP] ) { |
751 |
|
|
LOG_DBG( LOG_VERBOSE, "at end of db: %d > %d*512+%hd", |
752 |
|
|
*nxtoff, db->mfc[LMFC_NMFB], db->mfc[LMFC_NMFP] ); |
753 |
|
|
*nxtoff = -1; |
754 |
|
|
} |
755 |
|
|
} |
756 |
|
|
if ( len < 0 ) { |
757 |
|
|
log_msg( LOG_INFO, "found deleted rec len %hd at offset %d", len, off ); |
758 |
|
|
return 0; |
759 |
|
|
} |
760 |
|
|
|
761 |
|
|
/* check external base length */ |
762 |
|
|
base = LONG2OFF(my.xstr[LSTR_XRLO]) |
763 |
|
|
+ head[LMFR_NVF]*LONG2LEN(my.xstr[LSTR_XRLO]); |
764 |
|
|
if ( 0 > head[LMFR_BASE] || 0 > head[LMFR_NVF] |
765 |
|
|
|| 0x8fff < head[LMFR_NVF] |
766 |
|
|
|| len < head[LMFR_BASE] || len < base |
767 |
|
|
|| (head[LMFR_NVF] && head[LMFR_BASE] < base) |
768 |
|
|
) { |
769 |
|
|
log_msg( LOG_ERROR, |
770 |
|
|
"bad len %d base %d nvf %d need base %d at offset %d", |
771 |
|
|
len, head[LMFR_BASE], head[LMFR_NVF], base, off ); |
772 |
|
|
/* check alignment problem */ |
773 |
|
|
base = LONG2OFF(my.xstr[LSTR_XRLO]) |
774 |
|
|
+ head[LMFR_STAT]*LONG2LEN(my.xstr[LSTR_XRLO]); |
775 |
|
|
if ( 0 > head[LMFR_NVF] || 0 > head[LMFR_STAT] |
776 |
|
|
|| 0x8fff < head[LMFR_STAT] |
777 |
|
|
|| len < head[LMFR_NVF] || len < base |
778 |
|
|
|| (head[LMFR_STAT] && head[LMFR_NVF] < base) |
779 |
|
|
) |
780 |
|
|
; |
781 |
|
|
else |
782 |
|
|
log_msg( LOG_ERROR, "probably alignment problem, try -format aligned" ); |
783 |
|
|
goto cleanup; |
784 |
|
|
} |
785 |
|
|
|
786 |
|
|
/* set up external structure for this rec */ |
787 |
|
|
my.xstr[LSTR_SIZE] |= head[LMFR_NVF] << 16; /* occ of rep. part */ |
788 |
|
|
my.xstr[LSTR_XLEN] = len; |
789 |
|
|
/* internal base length */ |
790 |
|
|
base = LSTRLEN( *my.xstr ); |
791 |
|
|
/* internal len adjusted for the slightly longer base */ |
792 |
|
|
my.xstr[LSTR_ILEN] = len + base - head[LMFR_BASE]; /* the buffer */ |
793 |
|
|
|
794 |
|
|
rec = nrec( my.xstr ); |
795 |
|
|
if ( ! rec ) { |
796 |
|
|
log_msg( LOG_SYSERR, "could not alloc MFR of len %hd", my.xstr[LSTR_ILEN] ); |
797 |
|
|
goto cleanup; |
798 |
|
|
} |
799 |
|
|
buf = (char *) |
800 |
|
|
#ifdef alloca |
801 |
|
|
alloca( len ); |
802 |
|
|
/* first try faster alloca, but stack may be too limited for large records */ |
803 |
|
|
notalloca = ! buf; |
804 |
|
|
if ( notalloca ) |
805 |
|
|
buf = |
806 |
|
|
#endif |
807 |
|
|
mAlloc( len ); |
808 |
|
|
|
809 |
|
|
if ( ! buf ) { |
810 |
|
|
log_msg( LOG_SYSERR, "could not alloc MFR of len %hd", len ); |
811 |
|
|
goto cleanup; |
812 |
|
|
} |
813 |
|
|
if ( readblk( buf, len, db->mst[MST_MST], off ) ) { |
814 |
|
|
log_msg( LOG_ERROR, "\twhen reading MFR" ); |
815 |
|
|
goto cleanup; |
816 |
|
|
} |
817 |
|
|
#ifndef LDB_BIG_ENDIAN |
818 |
|
|
if ( LVAR_PAC != (DB_VARI & db->flags) ) { |
819 |
|
|
#endif |
820 |
|
|
if ( convert( rec, buf, my.xstr ) ) { |
821 |
|
|
log_msg( LOG_ERROR, "\twhen converting MFR" ); |
822 |
|
|
goto cleanup; |
823 |
|
|
} |
824 |
|
|
#ifndef LDB_BIG_ENDIAN |
825 |
|
|
} else { /* 10% faster */ |
826 |
|
|
Mfr *mfr = (Mfr*)buf; |
827 |
|
|
short *s = &mfr->dict->tag; |
828 |
|
|
int *f = rec + LMFR__FL; |
829 |
|
|
int *e = f + 3*head[LMFR_NVF]; |
830 |
|
|
rec[LMFR_MFN] = mfr->mfn; |
831 |
|
|
rec[LMFR_RECL] = mfr->recl; |
832 |
|
|
/* |
833 |
|
|
rec[LMFR_BWB] = mfr->bwbh<<16 | mfr->bwbl; |
834 |
|
|
rec[LMFR_BWP] = mfr->bwp; |
835 |
|
|
*/ |
836 |
|
|
rec[LMFR_BASE] = mfr->base; |
837 |
|
|
rec[LMFR_NVF] = mfr->nvf; |
838 |
|
|
rec[LMFR_STAT] = mfr->stat; |
839 |
|
|
while ( f < e ) { |
840 |
|
|
*f++ = *s++; |
841 |
|
|
*f++ = *s++; |
842 |
|
|
*f++ = *s++; |
843 |
|
|
} |
844 |
|
|
} |
845 |
|
|
#endif |
846 |
|
|
|
847 |
|
|
if ( rec[LMFR_STAT] ) { |
848 |
|
|
log_msg( LOG_WARN, "found status %hd", rec[LMFR_STAT] ); |
849 |
|
|
goto cleanok; |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
/* do a consistency check */ |
853 |
|
|
if ( rec[LMFR_NVF] < 0 || rec[LMFR_BASE] < 0 ) { |
854 |
|
|
log_msg( LOG_ERROR, "found neg. field nvf %hd base %hd", |
855 |
|
|
rec[LMFR_NVF], rec[LMFR_BASE] ); |
856 |
|
|
goto cleanup; |
857 |
|
|
} |
858 |
|
|
|
859 |
|
|
/* now care for the field values */ |
860 |
|
|
{ |
861 |
|
|
char *valsrc = buf+rec[LMFR_BASE]; |
862 |
|
|
char *recsta = ((char*)rec); |
863 |
|
|
char *valdst = recsta + base; |
864 |
|
|
int xbufl = rec[LMFR_RECL] - rec[LMFR_BASE]; |
865 |
|
|
int sumlens = 0; |
866 |
|
|
int i; |
867 |
|
|
for ( i=0; i < rec[LMFR_NVF]; i++ ) { |
868 |
|
|
int *d = &rec[LMFR__FL + i*LMFR__RL]; |
869 |
|
|
if ( d[LMFR_POS] < 0 || d[LMFR_LEN] < 0 ) { |
870 |
|
|
log_msg( LOG_ERROR, |
871 |
|
|
"bad field %d at off %d: negativ pos %hd or len %hd", |
872 |
|
|
i, off, d[LMFR_POS], d[LMFR_LEN] ); |
873 |
|
|
goto cleanup; |
874 |
|
|
} |
875 |
|
|
if ( d[LMFR_POS] + d[LMFR_LEN] > xbufl ) { |
876 |
|
|
log_msg( LOG_ERROR, |
877 |
|
|
"bad field %d at off %d: pos %hd + len %hd > buf %d", |
878 |
|
|
i, off, d[LMFR_POS], d[LMFR_LEN], xbufl ); |
879 |
|
|
goto cleanup; |
880 |
|
|
} |
881 |
|
|
sumlens += d[LMFR_LEN]; |
882 |
|
|
if ( sumlens > xbufl ) { |
883 |
|
|
log_msg( LOG_ERROR, |
884 |
|
|
"bad fields at off %d: sum of lengths %d > buf %d", |
885 |
|
|
off, sumlens, xbufl ); |
886 |
|
|
goto cleanup; |
887 |
|
|
} |
888 |
|
|
memcpy( valdst, valsrc+d[LMFR_POS], d[LMFR_LEN] ); |
889 |
|
|
d[LMFR_POS] = valdst - recsta; |
890 |
|
|
valdst += d[LMFR_LEN]; |
891 |
|
|
} |
892 |
|
|
} /* consistency check */ |
893 |
|
|
rec[LMFR_BWB] = /* "used" bytes */ |
894 |
|
|
rec[LMFR_RECL] = my.xstr[LSTR_ILEN]; |
895 |
|
|
rec[LMFR_BWP] = rec[LMFR_NVF]; /* avail fields = used fields */ |
896 |
|
|
rec[LMFR_BASE] = base; |
897 |
|
|
|
898 |
|
|
if ( LOG_TRACE <= log_lev ) |
899 |
|
|
LOG_STR( rec, lstrlib[LSET_MST].name[LSTR_MFR] ); |
900 |
|
|
goto done; |
901 |
|
|
|
902 |
|
|
cleanup: |
903 |
|
|
if ( nxtoff ) |
904 |
|
|
*nxtoff = -1; |
905 |
|
|
cleanok: |
906 |
|
|
if ( rec ) { |
907 |
|
|
mFree( rec ); |
908 |
|
|
rec = 0; |
909 |
|
|
} |
910 |
|
|
done: |
911 |
|
|
if ( buf |
912 |
|
|
#ifdef alloca |
913 |
|
|
&& notalloca |
914 |
|
|
#endif |
915 |
|
|
) |
916 |
|
|
mFree( buf ); |
917 |
|
|
if ( rec ) |
918 |
|
|
*rec = db->head.dbid; |
919 |
|
|
return rec; |
920 |
|
|
} /* getMfr */ |
921 |
|
|
|
922 |
|
|
|
923 |
|
|
/** write the record. |
924 |
|
|
If it doesn't yet have a mfn, assign one. |
925 |
|
|
NOTE: on a BIG_ENDIAN, anything but the mfn and recl will be frobbed |
926 |
|
|
after this call |
927 |
|
|
*/ |
928 |
|
|
static int putMfr ( LDb *db, Mfr *mfr ) |
929 |
|
|
{ |
930 |
|
|
int oldpos, newpos; |
931 |
|
|
int ret; |
932 |
|
|
|
933 |
|
|
if ( !(db->flags & DB_WRITABLE) ) { |
934 |
|
|
log_msg( LOG_ERROR, "db is not writable" ); |
935 |
|
|
return -1; |
936 |
|
|
} |
937 |
|
|
/* minimalist sanity check */ |
938 |
|
|
if ( mfr->nvf < 0 |
939 |
|
|
|| mfr->base != 18 + 6*mfr->nvf |
940 |
|
|
|| mfr->base > mfr->recl |
941 |
|
|
) { |
942 |
|
|
log_msg( LOG_ERROR, "bad nvf/base/recl %d/%d/%d ", |
943 |
|
|
mfr->nvf, mfr->base, mfr->recl ); |
944 |
|
|
return -2; |
945 |
|
|
} |
946 |
|
|
db->flags |= DB_MODIFIED; |
947 |
|
|
if ( mfr->mfn ) { |
948 |
|
|
int block; |
949 |
|
|
if ( db->mfc[LMFC_NMFN] > mfr->mfn ) |
950 |
|
|
oldpos = getOff( db, mfr->mfn, 0 ); |
951 |
|
|
else { |
952 |
|
|
db->mfc[LMFC_NMFN] = mfr->mfn+1; |
953 |
|
|
oldpos = 0; |
954 |
|
|
} |
955 |
|
|
mfr->bwp = 511 & oldpos; |
956 |
|
|
block = 1 + (oldpos >> 9); /* blockno counting from 1 */ |
957 |
|
|
mfr->bwbl = 0xffff & block; |
958 |
|
|
mfr->bwbh = block >> 16; |
959 |
|
|
if ( db->mfc[LMFC_NMFN] <= mfr->mfn ) |
960 |
|
|
db->mfc[LMFC_NMFN] = mfr->mfn+1; |
961 |
|
|
} else { |
962 |
|
|
mfr->mfn = db->mfc[LMFC_NMFN]++; /* assign new mfn */ |
963 |
|
|
oldpos = 0; |
964 |
|
|
mfr->bwbl = mfr->bwbh = mfr->bwp = 0; |
965 |
|
|
} |
966 |
|
|
mfr->stat = 0; |
967 |
|
|
newpos = db->mflen; |
968 |
|
|
if ( 498 < (newpos & 511) ) /* round up to next block boundary */ |
969 |
|
|
newpos = ~511 & (newpos + 14); |
970 |
|
|
if ( 1 & newpos ) |
971 |
|
|
newpos++; |
972 |
|
|
db->mflen = newpos + mfr->recl; |
973 |
|
|
#ifdef LDB_BIG_ENDIAN |
974 |
|
|
{ /* swap swap swap */ |
975 |
|
|
Dict *d = mfr->dict; |
976 |
|
|
short nvf = mfr->nvf; |
977 |
|
|
SWI( mfr->mfn ); SWS( mfr->recl ); SWS( mfr->bwbl ); SWS( mfr->bwbh ); |
978 |
|
|
SWS( mfr->bwp ); SWS( mfr->base ); SWS( mfr->nvf ); SWS( mfr->stat ); |
979 |
|
|
for ( ; nvf--; d++ ) { |
980 |
|
|
SWS( d->tag ); |
981 |
|
|
SWS( d->pos ); |
982 |
|
|
SWS( d->len ); |
983 |
|
|
} |
984 |
|
|
} |
985 |
|
|
#endif |
986 |
|
|
ret = lio_pwrite( &db->mst[MST_MST], (char*)mfr, rvs(mfr->recl), newpos ); |
987 |
|
|
#ifdef LDB_BIG_ENDIAN |
988 |
|
|
/* restore mnf, recl */ |
989 |
|
|
SWI( mfr->mfn ); |
990 |
|
|
SWS( mfr->recl ); |
991 |
|
|
#endif |
992 |
|
|
if ( ret != mfr->recl ) |
993 |
|
|
return log_msg( ERR_TRASH, "could not write Mfr %d bytes got %d", |
994 |
|
|
mfr->recl, ret ); |
995 |
|
|
getOff( db, mfr->mfn, (1 << (oldpos ? 9 : 10)) |
996 |
|
|
| (((newpos & 0xfffffe00) + 0x200) << 2) | (0x1ff & newpos) ); |
997 |
|
|
|
998 |
|
|
return 0; |
999 |
|
|
} /* putMfr */ |
1000 |
|
|
|
1001 |
|
|
|
1002 |
|
|
static int putRec ( LDb *db, Rec *rec ) |
1003 |
|
|
{ |
1004 |
|
|
int ret = 0, i; |
1005 |
|
|
Mfr *mfr = 0; |
1006 |
|
|
int buflen = 0; |
1007 |
|
|
int reclen = 0; |
1008 |
|
|
int contig = 1; |
1009 |
|
|
#ifdef alloca |
1010 |
|
|
int notalloca = 0; |
1011 |
|
|
#endif |
1012 |
|
|
const char *rbase = ((char *)rec) + rec->base; |
1013 |
|
|
Dict *d; |
1014 |
|
|
Field *f = rec->field; |
1015 |
|
|
/* TODO: if not rec->len, delete ? */ |
1016 |
|
|
for ( i = rec->len; i--; f++ ) { |
1017 |
|
|
if ( ! f->len ) |
1018 |
|
|
continue; |
1019 |
|
|
if ( ! f->val ) |
1020 |
|
|
LOG_OTO( cleanup, ( ERR_FAULT, "bad rec NULL val" ) ); |
1021 |
|
|
contig = contig && (f->val == rbase + buflen); |
1022 |
|
|
buflen += f->len; |
1023 |
|
|
} |
1024 |
|
|
reclen = 18 + 6*rec->len + buflen; |
1025 |
|
|
if ( 1 & reclen ) |
1026 |
|
|
reclen++; |
1027 |
|
|
mfr = (Mfr*) |
1028 |
|
|
#ifdef alloca |
1029 |
|
|
alloca( reclen ); |
1030 |
|
|
notalloca = ! mfr; |
1031 |
|
|
if ( notalloca ) |
1032 |
|
|
mfr = (Mfr*) |
1033 |
|
|
#endif |
1034 |
|
|
mAlloc( reclen ); |
1035 |
|
|
if ( ! mfr ) |
1036 |
|
|
LOG_OTO( cleanup, |
1037 |
|
|
( ERR_NOMEM, "could not alloc MFR of len %hd", reclen ) ); |
1038 |
|
|
mfr->mfn = rec->rowid; |
1039 |
|
|
mfr->recl = reclen; |
1040 |
|
|
mfr->bwbl = mfr->bwbh = mfr->bwp = 0; |
1041 |
|
|
mfr->base = 18 + 6*rec->len; |
1042 |
|
|
mfr->nvf = rec->len; |
1043 |
|
|
mfr->stat = 0; |
1044 |
|
|
d = mfr->dict; |
1045 |
|
|
f = rec->field; |
1046 |
|
|
buflen = 0; |
1047 |
|
|
for ( i = rec->len; i--; d++, f++ ) { |
1048 |
|
|
d->tag = f->tag; |
1049 |
|
|
d->pos = buflen; |
1050 |
|
|
buflen += (d->len = f->len); |
1051 |
|
|
} |
1052 |
|
|
if ( contig ) |
1053 |
|
|
memcpy( ((char*)mfr)+mfr->base, rbase, buflen ); |
1054 |
|
|
else { |
1055 |
|
|
char *mbase = ((char*)mfr)+mfr->base; |
1056 |
|
|
d = mfr->dict; |
1057 |
|
|
f = rec->field; |
1058 |
|
|
for ( i = rec->len; i--; d++, f++ ) |
1059 |
|
|
if ( d->len ) |
1060 |
|
|
memcpy( mbase + d->pos, f->val, d->len ); |
1061 |
|
|
} |
1062 |
|
|
ret = putMfr( db, mfr ); |
1063 |
|
|
if ( !ret && !rec->rowid ) |
1064 |
|
|
rec->rowid = mfr->mfn; |
1065 |
|
|
|
1066 |
|
|
cleanup: |
1067 |
|
|
if ( mfr |
1068 |
|
|
#ifdef alloca |
1069 |
|
|
&& notalloca |
1070 |
|
|
#endif |
1071 |
|
|
) |
1072 |
|
|
mFree( mfr ); |
1073 |
|
|
return ret; |
1074 |
|
|
} /* putRec */ |
1075 |
|
|
|
1076 |
|
|
|
1077 |
|
|
|
1078 |
|
|
/* ************************************************************ |
1079 |
|
|
access functions for plaintext db |
1080 |
|
|
*/ |
1081 |
|
|
|
1082 |
|
|
/* |
1083 |
|
|
create a pointer from the least significant bytes of pos, len, fld |
1084 |
|
|
buf must have db->ptrl bytes (up to 16 = 8+4+4) |
1085 |
|
|
and the most strict alignment (i.e. 4 or 8) possible for db->ptrl |
1086 |
|
|
returns buf |
1087 |
|
|
*/ |
1088 |
|
|
static char *mkptr ( char *buf, LDb *db, |
1089 |
|
|
unsigned pos, unsigned len, unsigned fld ) |
1090 |
|
|
{ |
1091 |
|
|
switch ( db->ptr ) { |
1092 |
|
|
case 0x0134: /* '4' */ |
1093 |
|
|
((unsigned*)buf)[0] = pos; |
1094 |
|
|
if ( ~0xff & fld ) fld = 0; |
1095 |
|
|
#ifdef LDB_BIG_ENDIAN /* the first = high order 3 bytes are len */ |
1096 |
|
|
((unsigned*)buf)[1] = (0xff&fld) | len<<8; |
1097 |
|
|
#else /* the first = low order 3 bytes are len */ |
1098 |
|
|
((unsigned*)buf)[1] = (0xffffff&len) | fld<<24; |
1099 |
|
|
#endif |
1100 |
|
|
return buf; |
1101 |
|
|
case 0x0044: /* 'D' */ |
1102 |
|
|
((unsigned*)buf)[0] = pos; |
1103 |
|
|
((unsigned*)buf)[1] = len; |
1104 |
|
|
return buf; |
1105 |
|
|
case 0x0035: /* '5' */ |
1106 |
|
|
#ifdef LDB_BIG_ENDIAN /* the first = high order 5 bytes are pos */ |
1107 |
|
|
*(lll*)buf = (0xffffff&len) | ((lll)pos)<<24; |
1108 |
|
|
#else /* the first = low order 5 bytes are pos */ |
1109 |
|
|
*(lll*)buf = pos | ((lll)len)<<40; |
1110 |
|
|
#endif |
1111 |
|
|
return buf; |
1112 |
|
|
} |
1113 |
|
|
/* TODO */ |
1114 |
|
|
(void)fld; |
1115 |
|
|
assert( 0 ); |
1116 |
|
|
return 0; |
1117 |
|
|
} |
1118 |
|
|
|
1119 |
|
|
|
1120 |
|
|
/* |
1121 |
|
|
read pointer, return len |
1122 |
|
|
if 0x0f00 & db->ptr, fld must not be 0, else *fld is untouched |
1123 |
|
|
*/ |
1124 |
|
|
static unsigned rdptr ( unsigned *pos, unsigned *fld, LDb *db, char *buf ) |
1125 |
|
|
{ |
1126 |
|
|
switch ( db->ptr ) { |
1127 |
|
|
case 0x0134: |
1128 |
|
|
*pos = *(unsigned*)buf; |
1129 |
|
|
*fld = ((unsigned char *)buf)[7]; |
1130 |
|
|
#ifdef LDB_BIG_ENDIAN |
1131 |
|
|
return ((unsigned*)buf)[1] >> 8; |
1132 |
|
|
#else |
1133 |
|
|
return 0xffffff & ((unsigned*)buf)[1]; |
1134 |
|
|
#endif |
1135 |
|
|
case 0x0044: |
1136 |
|
|
*pos = *(unsigned*)buf; |
1137 |
|
|
return ((unsigned*)buf)[1]; |
1138 |
|
|
case 0x0035: |
1139 |
|
|
#ifdef LDB_BIG_ENDIAN |
1140 |
|
|
/* *pos = (*(unsigned*)(buf+1)); would bus error on sparc */ |
1141 |
|
|
*pos = (unsigned) (*(lll*)buf >> 24); |
1142 |
|
|
return 0xffffff & (unsigned)*(lll*)buf; /* last 3 bytes */ |
1143 |
|
|
#else /* guess there is no little endian that needs alignment ? */ |
1144 |
|
|
*pos = *(unsigned*)buf; /* use low order = first 4 of first 5 bytes */ |
1145 |
|
|
return 0xffffff & (*(unsigned*)(buf+5)); |
1146 |
|
|
#endif |
1147 |
|
|
} |
1148 |
|
|
/* TODO */ |
1149 |
|
|
(void)fld; |
1150 |
|
|
assert( 0 ); |
1151 |
|
|
return 0; |
1152 |
|
|
} |
1153 |
|
|
|
1154 |
|
|
|
1155 |
|
|
static void setPtr ( LDb *db, int mfn, |
1156 |
|
|
unsigned pos, unsigned len, unsigned fld ) |
1157 |
|
|
{ |
1158 |
|
|
Ptr pt; |
1159 |
|
|
if ( mfn < db->mmlen ) { |
1160 |
|
|
mkptr( db->mmap + mfn*db->ptrl, db, pos, len, fld ); |
1161 |
|
|
return; |
1162 |
|
|
} |
1163 |
|
|
lio_pwrite( &db->mst[MST_XRF], |
1164 |
|
|
mkptr( pt.r, db, pos, len, fld), db->ptrl, mfn*db->ptrl ); |
1165 |
|
|
} /* setPtr */ |
1166 |
|
|
|
1167 |
|
|
|
1168 |
|
|
static unsigned getPtr ( unsigned *pos, unsigned *fld, LDb *db, int mfn ) |
1169 |
|
|
{ |
1170 |
|
|
Ptr pt; |
1171 |
|
|
return mfn < db->mmlen |
1172 |
|
|
? rdptr( pos, fld, db, db->mmap + mfn*db->ptrl ) |
1173 |
|
|
: db->ptrl == lio_pread( &db->mst[MST_XRF], |
1174 |
|
|
pt.r, db->ptrl, mfn*db->ptrl ) |
1175 |
|
|
? rdptr( pos, fld, db, pt.r ) |
1176 |
|
|
: 0; |
1177 |
|
|
} /* getPtr */ |
1178 |
|
|
|
1179 |
|
|
|
1180 |
|
|
#if 0 |
1181 |
|
|
static int putPlain ( LDb *db, Rec *rec ) |
1182 |
|
|
{ |
1183 |
|
|
return 0; |
1184 |
|
|
} /* putPlain */ |
1185 |
|
|
#endif |
1186 |
|
|
|
1187 |
|
|
/** |
1188 |
|
|
get text |
1189 |
|
|
the original text is read contigously at base. |
1190 |
|
|
the record is then cooked as requested: |
1191 |
|
|
0 well done: do full fixup, |
1192 |
|
|
apply conversions and create fields. |
1193 |
|
|
1 english: do not create fields (rec->fields is 0), apply no conversions, |
1194 |
|
|
but set rec->len to actual number of fields (counting if necessary). |
1195 |
|
|
2 raw: |
1196 |
|
|
set len only if it's known from the pointer |
1197 |
|
|
*/ |
1198 |
|
|
static Rec *dText ( LDb *db, int mfn, int raw ) |
1199 |
|
|
{ |
1200 |
|
|
unsigned base, sz, pos, len, fld; /* #fields actually used */ |
1201 |
|
|
unsigned n = 0; /* known #fields */ |
1202 |
|
|
Rec *r, *x; |
1203 |
|
|
Field *f, *fe; |
1204 |
|
|
char *p, *q, *e; |
1205 |
|
|
|
1206 |
|
|
len = getPtr( &pos, &n, db, mfn ); |
1207 |
|
|
LOG_DBG( LOG_TRACE, "dText %d pos %d len %d fld %d", mfn, pos, len, n ); |
1208 |
|
|
if ( !len ) |
1209 |
|
|
return 0; |
1210 |
|
|
if ( raw ) |
1211 |
|
|
fld = 0; |
1212 |
|
|
else if ( !(fld = n) ) { |
1213 |
|
|
fld = len / 36; /* assume one (costing 12 bytes) per 36 bytes data */ |
1214 |
|
|
if ( fld < 8 ) /* small record is likely to have some short fields */ |
1215 |
|
|
fld = 8; |
1216 |
|
|
} |
1217 |
|
|
base = BASESZ(fld); |
1218 |
|
|
sz = base + len; |
1219 |
|
|
r = (Rec*)mAlloc( sz ); |
1220 |
|
|
p = ((char*)r) + base; |
1221 |
|
|
if ( (int)len != lio_pread( &db->mst[MST_MST], p, len, pos ) ) { |
1222 |
|
|
mFree( r ); |
1223 |
|
|
return 0; |
1224 |
|
|
} |
1225 |
|
|
LOG_DBG( LOG_TRACE, "'%.*s'", len, p ); |
1226 |
|
|
r->dbid = db->head.dbid; |
1227 |
|
|
r->rowid = mfn; |
1228 |
|
|
r->used = r->bytes = sz; |
1229 |
|
|
r->fields = fld; |
1230 |
|
|
r->base = base; |
1231 |
|
|
r->len = n; |
1232 |
|
|
if ( raw && (n || 1 != raw) ) |
1233 |
|
|
return r; |
1234 |
|
|
e = p + len; |
1235 |
|
|
f = r->field; /* next field to assign */ |
1236 |
|
|
fe = f + fld; /* end of assignable fields */ |
1237 |
|
|
/* |
1238 |
|
|
loop through buffer lines to count a/o assign |
1239 |
|
|
count them in n |
1240 |
|
|
while f < fe, also fix and assign them |
1241 |
|
|
*/ |
1242 |
|
|
for ( n=0;;) { /* possibly 2 passes needed */ |
1243 |
|
|
for ( ;p < e; p = q+1 ) { |
1244 |
|
|
if ( !(q = memchr( p, LF, e-p )) ) |
1245 |
|
|
q = e; /* > p */ |
1246 |
|
|
if ( TAB != *p || !n ) { |
1247 |
|
|
if ( f < fe ) { |
1248 |
|
|
p += a2il( p, q-p, &f->tag ); |
1249 |
|
|
if ( p < q && TAB == *p ) |
1250 |
|
|
p++; |
1251 |
|
|
f->len = q - (f->val = p); |
1252 |
|
|
f++; /* f == r->field+n, as long as we don't hit fe */ |
1253 |
|
|
} |
1254 |
|
|
n++; |
1255 |
|
|
continue; |
1256 |
|
|
} |
1257 |
|
|
/* continuation line */ |
1258 |
|
|
if ( f != r->field+n ) |
1259 |
|
|
continue; |
1260 |
|
|
/* we ARE assigning & didn't loose sync at fe */ |
1261 |
|
|
/* append to previous */ { |
1262 |
|
|
char *dest = (char*)f[-1].val + f[-1].len; |
1263 |
|
|
int dist = p - dest, l = q-p; |
1264 |
|
|
*p = LF; |
1265 |
|
|
memmove( dest, p, l ); |
1266 |
|
|
memset( q-dist, ' ', dist ); /* cleanup */ |
1267 |
|
|
f[-1].len += l; |
1268 |
|
|
} |
1269 |
|
|
} |
1270 |
|
|
/* now n != 0, since initially p < e, since len != 0 */ |
1271 |
|
|
if ( r->len && r->len != (int)n ) { |
1272 |
|
|
log_msg( LOG_WARN, "rec %d len %d != ptr %d", mfn, n, r->len ); |
1273 |
|
|
break; |
1274 |
|
|
} |
1275 |
|
|
if ( raw || (int)n <= r->fields ) /* all counted/assigned */ |
1276 |
|
|
break; |
1277 |
|
|
/* extend the record to n fields */ |
1278 |
|
|
log_msg( LOG_INFO, "extending rec %d %d -> %d fields", mfn, fld, n ); |
1279 |
|
|
fld = n; |
1280 |
|
|
base = BASESZ(fld); |
1281 |
|
|
sz = base + len; |
1282 |
|
|
x = (Rec*)mAlloc( sz ); |
1283 |
|
|
x->dbid = db->head.dbid; |
1284 |
|
|
x->rowid = mfn; |
1285 |
|
|
x->used = x->bytes = sz; |
1286 |
|
|
x->fields = fld; |
1287 |
|
|
x->base = base; |
1288 |
|
|
x->len = n; |
1289 |
|
|
p = ((char*)x) + base; |
1290 |
|
|
e = p + len; |
1291 |
|
|
q = ((char*)r) + r->base; |
1292 |
|
|
memcpy( p, q, len ); |
1293 |
|
|
memcpy( x->field, r->field, r->fields*sizeof(Field) ); |
1294 |
|
|
for ( f=x->field, n=r->fields; n--; ) |
1295 |
|
|
(f++)->val += p-q; |
1296 |
|
|
n = r->fields; |
1297 |
|
|
mFree( r ); |
1298 |
|
|
r = x; |
1299 |
|
|
f = r->field + n; |
1300 |
|
|
fe = r->field + fld; |
1301 |
|
|
p = (char*)f[-1].val + f[-1].len; |
1302 |
|
|
/* seek behind the LF that delimited the last field */ |
1303 |
|
|
while (LF != *p++) |
1304 |
|
|
; |
1305 |
|
|
} |
1306 |
|
|
r->len = n; |
1307 |
|
|
if ( !raw && (DB_TXTMODE & db->flags) ) |
1308 |
|
|
for ( f = r->field, fe = r->field + r->fields; f < fe; f++ ) |
1309 |
|
|
for ( p = (char*)f->val, e = p+f->len; (p = memchr(p,VT,e-p)); ) |
1310 |
|
|
*p++ = LF; |
1311 |
|
|
return r; |
1312 |
|
|
} /* dText */ |
1313 |
|
|
|
1314 |
|
|
|
1315 |
|
|
static int pText ( LDb *db, Rec *r, const char *mark ) |
1316 |
|
|
{ |
1317 |
|
|
char buf[128 + 65536]; |
1318 |
|
|
unsigned pos = 0, len = 0, fld, off; |
1319 |
|
|
char *p, *b; |
1320 |
|
|
int ret; |
1321 |
|
|
|
1322 |
|
|
if ( !(db->flags & DB_WRITABLE) ) { |
1323 |
|
|
log_msg( LOG_ERROR, "db is not writable" ); |
1324 |
|
|
return -1; |
1325 |
|
|
} |
1326 |
|
|
if ( ! r->rowid ) |
1327 |
|
|
r->rowid = db->mfc[LMFC_NMFN]++; /* assign new mfn */ |
1328 |
|
|
else if ( db->mfc[LMFC_NMFN] <= r->rowid ) |
1329 |
|
|
db->mfc[LMFC_NMFN] = r->rowid + 1; |
1330 |
|
|
else { |
1331 |
|
|
fld = 0; |
1332 |
|
|
len = getPtr( &pos, &fld, db, r->rowid ); |
1333 |
|
|
} |
1334 |
|
|
p = b = 32768 >= r->used ? buf : mAlloc(128+2*r->used); |
1335 |
|
|
*p++ = 'W'; |
1336 |
|
|
*p++ = TAB; |
1337 |
|
|
p += u2a( p, r->rowid ); |
1338 |
|
|
*p++ = TAB; |
1339 |
|
|
if ( pos ) { |
1340 |
|
|
p += u2a( p, pos ); |
1341 |
|
|
*p++ = '.'; |
1342 |
|
|
p += u2a( p, len ); |
1343 |
|
|
if ( fld ) { |
1344 |
|
|
*p++ = '.'; |
1345 |
|
|
p += u2a( p, fld ); |
1346 |
|
|
} |
1347 |
|
|
} |
1348 |
|
|
*p++ = TAB; |
1349 |
|
|
if ( mark ) { |
1350 |
|
|
int l = strlen(mark); |
1351 |
|
|
if ( l > 31 ) { |
1352 |
|
|
log_msg( LOG_WARN, "mark '%.48s'%s has length %d", |
1353 |
|
|
mark, l<48 ? "" : "...", l ); |
1354 |
|
|
l = 31; |
1355 |
|
|
} |
1356 |
|
|
memcpy( p, mark, l ); |
1357 |
|
|
p += l; |
1358 |
|
|
} else { |
1359 |
|
|
timeGtfm( p, 0 ); |
1360 |
|
|
p += 17; |
1361 |
|
|
} |
1362 |
|
|
*p++ = LF; |
1363 |
|
|
off = p - b; |
1364 |
|
|
p += len = rSerB( p, r ); |
1365 |
|
|
if ( len > 1 ) /* don't count 2 trailing LFs */ |
1366 |
|
|
len -= 2; |
1367 |
|
|
db->flags |= DB_MODIFIED; |
1368 |
|
|
pos = db->mflen; |
1369 |
|
|
db->mflen += p - b; |
1370 |
|
|
ret = lio_pwrite( &db->mst[MST_MST], b, p - b, pos ); |
1371 |
|
|
if ( ret == p - b ) { |
1372 |
|
|
setPtr( db, r->rowid, pos+off, len, r->len ); |
1373 |
|
|
ret = 0; |
1374 |
|
|
} |
1375 |
|
|
if ( buf != b ) |
1376 |
|
|
mFree( b ); |
1377 |
|
|
return ret; |
1378 |
|
|
} /* pText */ |
1379 |
|
|
|
1380 |
|
|
|
1381 |
|
|
/* ************************************************************ |
1382 |
|
|
utilities |
1383 |
|
|
*/ |
1384 |
|
|
|
1385 |
|
|
static int search ( LDb *db, const char *key, LdbPost *post, |
1386 |
|
|
Rec *rec, DXLoop *lp ) |
1387 |
|
|
{ |
1388 |
|
|
int i, j, prefix, idx, ret, ock; |
1389 |
|
|
int pos; |
1390 |
|
|
int *leaf, *entry; |
1391 |
|
|
char *term; |
1392 |
|
|
int *xstr; |
1393 |
|
|
struct { /* terms cursor */ |
1394 |
|
|
char key[LDB_MAX_KEYLEN+1]; /* key or key prefix */ |
1395 |
|
|
short klen; /* key length to compare */ |
1396 |
|
|
char imin; /* minimum index to search */ |
1397 |
|
|
char imax; /* maximum index to search */ |
1398 |
|
|
int leaf[LDB_INDEXES][LDB_TERMBUF]; /* one leaf buffer per index */ |
1399 |
|
|
short lpos[LDB_INDEXES]; /* next position in leaf, -1 if done */ |
1400 |
|
|
} crs; |
1401 |
|
|
short klen; /* length for initial locate */ |
1402 |
|
|
int block[128]; /* buffer to read one block */ |
1403 |
|
|
int blockpos = 0; |
1404 |
|
|
LdbP *p = 0; |
1405 |
|
|
|
1406 |
|
|
if ( ! key ) |
1407 |
|
|
key = "$"; |
1408 |
|
|
/* prepare cursor struct */ |
1409 |
|
|
memset( &crs, 0, sizeof(crs) ); /* tabula rasa */ |
1410 |
|
|
crs.klen = strlen( key ); |
1411 |
|
|
/* check for prefix match */ |
1412 |
|
|
if ( post ) |
1413 |
|
|
prefix = LDB_PFX & post->mode; |
1414 |
|
|
else if ( (prefix = crs.klen && '$' == key[crs.klen - 1]) ) |
1415 |
|
|
crs.klen--; |
1416 |
|
|
/* check out minimum index to search */ |
1417 |
|
|
for ( crs.imin=0; crs.klen > db->tlen[(int)crs.imin]; ) |
1418 |
|
|
if ( LDB_INDEXES == ++(crs.imin) ) |
1419 |
|
|
return log_msg( ERR_INVAL, "bad keylen %d key '%.64s'", crs.klen, key ); |
1420 |
|
|
/* prepare key */ |
1421 |
|
|
memset( crs.key, ' ', sizeof(crs.key)-1 ); |
1422 |
|
|
{ |
1423 |
|
|
unsigned char *uc = (unsigned char*)crs.key; |
1424 |
|
|
unsigned char *uk = (unsigned char*)key; |
1425 |
|
|
for ( i=crs.klen; i--; ) |
1426 |
|
|
uc[i] = db->ctab[LCS_UCASE].c[ uk[i] ]; |
1427 |
|
|
} |
1428 |
|
|
if ( prefix ) |
1429 |
|
|
crs.imax = LDB_INDEXES-1; |
1430 |
|
|
else { |
1431 |
|
|
crs.imax = crs.imin; |
1432 |
|
|
crs.klen = db->tlen[(int)crs.imin]; |
1433 |
|
|
} |
1434 |
|
|
log_msg( LOG_INFO, "search for '%.*s'%c", crs.klen, crs.key, prefix?'$':' ' ); |
1435 |
|
|
key = crs.key; |
1436 |
|
|
klen = crs.klen; |
1437 |
|
|
if ( rec && rec->len ) { |
1438 |
|
|
/* use last key from record to locate starting position */ |
1439 |
|
|
key = rec->field[rec->len-1].val; |
1440 |
|
|
klen = rec->field[rec->len-1].len; |
1441 |
|
|
rec->len = 0; |
1442 |
|
|
} |
1443 |
|
|
|
1444 |
|
|
for ( i=crs.imin; i<=crs.imax; i++ ) { /* find leaf positions */ |
1445 |
|
|
int nFile = INV_N01 + 2*i; /* node file index */ |
1446 |
|
|
int nStr = LSTR_N01 + 2*i; /* node struct index */ |
1447 |
|
|
int *nstr = DB_XSTR( db, LSET_INV, nStr ); |
1448 |
|
|
int lvl; |
1449 |
|
|
short cmplen = klen <= db->tlen[i] ? klen : db->tlen[i]; |
1450 |
|
|
pos = db->cnt[i][LCNT_POSR]; /* pos of root record */ |
1451 |
|
|
j = 0; |
1452 |
|
|
for ( lvl = 0; 0<pos; lvl++ ) { /* traverse node levels */ |
1453 |
|
|
int node[102]; |
1454 |
|
|
LOG_DBG( LOG_DEBUG, "node %d at %d lvl %d", pos, j, lvl ); |
1455 |
|
|
assert( (int)sizeof(node) >= nstr[LSTR_ILEN] ); |
1456 |
|
|
*node = *nstr; |
1457 |
|
|
ret = readlog( node, db->inv[nFile], 1-pos, db, LSET_INV, nStr ); |
1458 |
|
|
if ( pos != node[LN0X_POS] /* wrong address */ |
1459 |
|
|
|| i+1 != node[LN0X_TYPE] /* wrong type */ |
1460 |
|
|
|| node[LN0X_OCK] < 1 /* no keys */ |
1461 |
|
|
|| 2*db->cnt[i][LCNT_ORDN] < node[LN0X_OCK] /* too many keys */ |
1462 |
|
|
) |
1463 |
|
|
return log_msg( ERR_TRASH, "bad node pos %d type %d keys %d", |
1464 |
|
|
node[LN0X_POS], node[LN0X_TYPE], node[LN0X_OCK] |
1465 |
|
|
); |
1466 |
|
|
ock = node[LN0X_OCK]; |
1467 |
|
|
for ( j=1; |
1468 |
|
|
j<ock && 0 < (ret = memcmp( key, |
1469 |
|
|
((char*)node)+node[j*LN0X__RL+LN0X__FL+LN0X_KEY], cmplen )); |
1470 |
|
|
j++ ) |
1471 |
|
|
; |
1472 |
|
|
/* now j is at end or on next index not less */ |
1473 |
|
|
if ( j==ock /* end */ |
1474 |
|
|
|| ret /* index is greater than key */ |
1475 |
|
|
|| prefix /* backtrack even on exact match */ |
1476 |
|
|
) |
1477 |
|
|
j--; /* step into last ock with lower key */ |
1478 |
|
|
pos = node[LN0X__FL + j*LN0X__RL + LN0X_REF]; |
1479 |
|
|
} /* for lvl */ |
1480 |
|
|
/* got some negative ref to leaf; set leaf pos */ |
1481 |
|
|
crs.leaf[i][LL0X_PS] = -pos; |
1482 |
|
|
/* |
1483 |
|
|
since the lpos and LL0X_OCK are both 0 by the memset above, |
1484 |
|
|
we will initially load the leaves |
1485 |
|
|
*/ |
1486 |
|
|
} /* for indexes */ |
1487 |
|
|
/* done preparing cursor */ |
1488 |
|
|
|
1489 |
|
|
if ( post ) /* prepare for postings */ |
1490 |
|
|
p = post->p; |
1491 |
|
|
xstr = DB_XSTR( db, LSET_INV, LSTR_IFP ); |
1492 |
|
|
|
1493 |
|
|
for (;;) { /* loop terms in prefix mode */ |
1494 |
|
|
/* vars for postings: */ |
1495 |
|
|
int infb, infp; /* block and pos where to read postings */ |
1496 |
|
|
int added; /* postings added or marked per term */ |
1497 |
|
|
int blkno; /* postings block number */ |
1498 |
|
|
int remain = 0; /* postings to fetch from next block of segment */ |
1499 |
|
|
int ifp[LIFP__FL]; /* postings header */ |
1500 |
|
|
|
1501 |
|
|
idx = -1; /* index to use */ |
1502 |
|
|
/* compare index terms, load leafes if needed */ |
1503 |
|
|
for ( i = crs.imin; i <= crs.imax; i++ ) { |
1504 |
|
|
short cmplen = klen <= db->tlen[i] ? klen : db->tlen[i]; |
1505 |
|
|
leaf = crs.leaf[i]; |
1506 |
|
|
if ( leaf[LL0X_OCK] <= crs.lpos[i] ) { /* load */ |
1507 |
|
|
int lFile = INV_L01 + 2*i; /* leaf file index */ |
1508 |
|
|
int lStr = LSTR_L01 + 2*i; /* leaf struct index */ |
1509 |
|
|
int *lstr = DB_XSTR( db, LSET_INV, lStr ); |
1510 |
|
|
|
1511 |
|
|
crs.lpos[i] = -1; |
1512 |
|
|
reread: |
1513 |
|
|
if ( ! (pos = leaf[LL0X_PS]) ) |
1514 |
|
|
continue; |
1515 |
|
|
LOG_DBG( LOG_DEBUG, "leaf %d", pos ); |
1516 |
|
|
assert( (int)sizeof(crs.leaf[i]) >= lstr[LSTR_ILEN] ); |
1517 |
|
|
*leaf = *lstr; |
1518 |
|
|
ret = readlog( leaf, db->inv[lFile], 1-pos, db, LSET_INV, lStr ); |
1519 |
|
|
if ( pos != leaf[LL0X_POS] /* wrong address */ |
1520 |
|
|
|| i+1 != leaf[LL0X_TYPE] /* wrong type */ |
1521 |
|
|
|| leaf[LL0X_OCK] < 1 /* no keys */ |
1522 |
|
|
|| 2*db->cnt[i][LCNT_ORDN] < leaf[LL0X_OCK] /* too many keys */ |
1523 |
|
|
) |
1524 |
|
|
return log_msg( ERR_TRASH, "bad leaf pos %d type %d keys %d", |
1525 |
|
|
leaf[LL0X_POS], leaf[LL0X_TYPE], leaf[LL0X_OCK] ); |
1526 |
|
|
ock = leaf[LL0X_OCK]; |
1527 |
|
|
/* advance to first term which is not too small |
1528 |
|
|
(should be needed only for first leaf of an index) |
1529 |
|
|
*/ |
1530 |
|
|
for ( j=0; |
1531 |
|
|
j<ock && (0 < (ret = memcmp( key, |
1532 |
|
|
((char*)leaf)+leaf[LL0X__FL + j*LL0X__RL + LL0X_KEY], cmplen )) |
1533 |
|
|
|| (!ret && key!=crs.key) ); /* skip exact while locating */ |
1534 |
|
|
j++ ) |
1535 |
|
|
; |
1536 |
|
|
if ( ock == j ) |
1537 |
|
|
goto reread; /* start over w/ next leaf of same index */ |
1538 |
|
|
if ( 0 <= ret |
1539 |
|
|
|| (key!=crs.key && !memcmp( crs.key, |
1540 |
|
|
((char*)leaf)+leaf[LL0X__FL + j*LL0X__RL + LL0X_KEY], crs.klen )) |
1541 |
|
|
) |
1542 |
|
|
crs.lpos[i] = j; |
1543 |
|
|
/* else let -1 */ |
1544 |
|
|
} /* if reload */ |
1545 |
|
|
if ( 0 > crs.lpos[i] ) |
1546 |
|
|
continue; |
1547 |
|
|
if ( 0 > idx ) { |
1548 |
|
|
idx = i; |
1549 |
|
|
continue; |
1550 |
|
|
} |
1551 |
|
|
/* compare this index next term to that of index idx */ |
1552 |
|
|
/* assume that index w/ lower number has shorter keys */ |
1553 |
|
|
ret = memcmp( |
1554 |
|
|
((char*)leaf)+leaf[LL0X__FL + crs.lpos[i]*LL0X__RL + LL0X_KEY], |
1555 |
|
|
((char*)crs.leaf[idx])+ |
1556 |
|
|
crs.leaf[idx][LL0X__FL + crs.lpos[idx]*LL0X__RL + LL0X_KEY], |
1557 |
|
|
db->tlen[idx] ); |
1558 |
|
|
if ( 0 > ret ) |
1559 |
|
|
idx = i; |
1560 |
|
|
} |
1561 |
|
|
if ( 0 > idx ) |
1562 |
|
|
goto done; |
1563 |
|
|
j = crs.lpos[idx]; |
1564 |
|
|
leaf = crs.leaf[idx]; |
1565 |
|
|
entry = leaf + LL0X__FL + j*LL0X__RL; |
1566 |
|
|
term = ((char*)leaf) + entry[LL0X_KEY]; |
1567 |
|
|
if ( memcmp( crs.key, term, crs.klen ) ) |
1568 |
|
|
goto done; |
1569 |
|
|
crs.lpos[idx]++; |
1570 |
|
|
|
1571 |
|
|
if ( rec ) { /* record the term */ |
1572 |
|
|
/* field to assign */ |
1573 |
|
|
Field *f = rec->field + rec->len; |
1574 |
|
|
short tlen = db->tlen[idx]; |
1575 |
|
|
/* end of available buffer */ |
1576 |
|
|
char *b = rec->len |
1577 |
|
|
? (char*)f[-1].val /* before previously assigned field */ |
1578 |
|
|
: ((char*)rec + rec->bytes); /* end of record */ |
1579 |
|
|
while ( tlen && ' ' == term[tlen-1] ) |
1580 |
|
|
tlen--; |
1581 |
|
|
b -= tlen; |
1582 |
|
|
if ( b < (char*)(f+1) ) /* no space left on device */ |
1583 |
|
|
goto done; |
1584 |
|
|
/* probably we're nuking the locator now: */ |
1585 |
|
|
memcpy( b, term, tlen ); |
1586 |
|
|
f->tag = 0; |
1587 |
|
|
f->val = b; |
1588 |
|
|
f->len = tlen; |
1589 |
|
|
rec->len++; |
1590 |
|
|
/* reset key from locator to prefix */ |
1591 |
|
|
key = crs.key; |
1592 |
|
|
klen = crs.klen; |
1593 |
|
|
} |
1594 |
|
|
|
1595 |
|
|
if ( ! post && ! lp ) |
1596 |
|
|
continue; |
1597 |
|
|
/* collect postings */ |
1598 |
|
|
infb = entry[LL0X_INFB]; |
1599 |
|
|
infp = entry[LL0X_INFP]; |
1600 |
|
|
/* the IFP file is organized in blocks of 128 longs. |
1601 |
|
|
1st int is block number followed by 127 data. |
1602 |
|
|
postings are organized in chained segments so that each segment |
1603 |
|
|
fits within one such block. a segment has five longs header, |
1604 |
|
|
giving number of postings and pointer to next segment. |
1605 |
|
|
*/ |
1606 |
|
|
added = 0; |
1607 |
|
|
for ( blkno=0; infb; blkno++ ) { /* segments */ |
1608 |
|
|
LdbP merge[127/2]; /* buffer to collect new postings */ |
1609 |
|
|
int *base; /* start of data */ |
1610 |
|
|
int *b; /* start of postings */ |
1611 |
|
|
int n; /* max postings in this seg's 1st block */ |
1612 |
|
|
int xlen; /* external length to read */ |
1613 |
|
|
int f = post ? post->fil - 1 : 0; /* highest pos to consider in given postings */ |
1614 |
|
|
int m = 0; /* fill merge buffer */ |
1615 |
|
|
int k; /* loop segment */ |
1616 |
|
|
|
1617 |
|
|
if ( infp > 127-2-5 ) { |
1618 |
|
|
return log_msg( ERR_TRASH, "found bad IFP pos %d blk %d for %.*s", |
1619 |
|
|
infp, blkno, klen, key ); |
1620 |
|
|
} |
1621 |
|
|
if ( remain ) { /* consecutive block of same segment */ |
1622 |
|
|
n = remain; |
1623 |
|
|
if ( n > 127/2 ) |
1624 |
|
|
n = 127/2; |
1625 |
|
|
xlen = 8*n; |
1626 |
|
|
} else { |
1627 |
|
|
n = (127 - 5 - infp)/2; |
1628 |
|
|
xlen = 20 + 8*n; |
1629 |
|
|
} |
1630 |
|
|
pos = (infb - 1) * 512 + (infp + 1) * 4; |
1631 |
|
|
if ( blockpos |
1632 |
|
|
&& !((pos-blockpos) >> 9) /* 0 <= (pos-blockpos) < 512 */ |
1633 |
|
|
&& pos+xlen <= blockpos+ 1 + (0x1ff & ~blockpos) |
1634 |
|
|
) |
1635 |
|
|
base = block + (pos - blockpos)/sizeof(int); |
1636 |
|
|
else { |
1637 |
|
|
int blklen = 1 + (0x1ff & ~pos); |
1638 |
|
|
assert( xlen <= blklen ); |
1639 |
|
|
assert( blklen <= (int)sizeof(block) ); |
1640 |
|
|
assert( 0 == (0x1ff & (pos + blklen)) ); |
1641 |
|
|
ret = readblk( block, blklen, db->inv[INV_IFP], pos ); |
1642 |
|
|
if ( ret ) |
1643 |
|
|
return log_msg( ERR_IO, "\twhen reading IFP" ); |
1644 |
|
|
blockpos = pos; |
1645 |
|
|
base = block; |
1646 |
|
|
} |
1647 |
|
|
if ( remain ) { /* no header to convert */ |
1648 |
|
|
remain -= n; |
1649 |
|
|
b = base; /* no header */ |
1650 |
|
|
} else { |
1651 |
|
|
assert( (int)sizeof(ifp) >= xstr[LSTR_ILEN] ); |
1652 |
|
|
*ifp = *xstr; |
1653 |
|
|
ret = convert( ifp, (char *)base, xstr ); |
1654 |
|
|
if ( ret ) |
1655 |
|
|
return log_msg( ERR_TRASH, "\twhen converting IFP header" ); |
1656 |
|
|
if ( n > ifp[LIFP_SEGP] ) |
1657 |
|
|
n = ifp[LIFP_SEGP]; |
1658 |
|
|
remain = ifp[LIFP_SEGP] - n; |
1659 |
|
|
b = base+5; /* after header */ |
1660 |
|
|
} |
1661 |
|
|
LOG_DBG( LOG_VERBOSE, |
1662 |
|
|
"key %d.%d '%.*s' blk %d post %d/%d r %d xlen %d at b/p %d.%d=%d", |
1663 |
|
|
leaf[LL0X_PS], j, db->tlen[idx], term, blkno, |
1664 |
|
|
n, ifp[LIFP_TOTP], remain, xlen, infb, infp, pos ); |
1665 |
|
|
if ( LOG_DO( LOG_TRACE ) ) |
1666 |
|
|
LOG_STR( ifp, lstrlib[ LSET_INV ].name[ LSTR_IFP ] ); |
1667 |
|
|
assert( (size_t)n <= sizeof(merge)/sizeof(merge[0]) ); |
1668 |
|
|
if ( lp ) { |
1669 |
|
|
Key kbf; |
1670 |
|
|
Hit hit; |
1671 |
|
|
unsigned char tlen = (unsigned char) db->tlen[idx]; |
1672 |
|
|
while ( tlen && ' ' == term[tlen-1] ) |
1673 |
|
|
tlen--; |
1674 |
|
|
memcpy( kbf.byt, term, kbf.len = tlen ); |
1675 |
|
|
for ( k=0; k<n; k++ ) { /* callback needs 'em sorted */ |
1676 |
|
|
int ppos; |
1677 |
|
|
unsigned char *c = (unsigned char *)&b[k*2]; |
1678 |
|
|
LdbP e; /* the entry */ |
1679 |
|
|
#ifdef LDB_BIG_ENDIAN |
1680 |
|
|
memcpy(e.bytes,c,8); |
1681 |
|
|
#else |
1682 |
|
|
e.bytes[0] = c[7]; e.bytes[1] = c[6]; |
1683 |
|
|
e.bytes[2] = c[5]; e.bytes[3] = c[4]; |
1684 |
|
|
e.bytes[4] = c[3]; e.bytes[5] = c[2]; |
1685 |
|
|
e.bytes[6] = c[1]; e.bytes[7] = c[0]; |
1686 |
|
|
#endif |
1687 |
|
|
ppos = LDBP_POS( &e ); |
1688 |
|
|
hit.mfn = (unsigned)LDBP_ROW( &e ); |
1689 |
|
|
hit.tag = (unsigned short)LDBP_TAG( &e ); |
1690 |
|
|
hit.occ = (unsigned short)(ppos >> 16); |
1691 |
|
|
hit.pos = (unsigned short)ppos; |
1692 |
|
|
if ( lp->cb( lp->me, &kbf, &hit ) ) |
1693 |
|
|
goto done; |
1694 |
|
|
} |
1695 |
|
|
} |
1696 |
|
|
if ( post ) for ( k=n; k--; ) { |
1697 |
|
|
/* loop backwards (for the fun of it) postings in segment */ |
1698 |
|
|
int prow, ptag, ppos; |
1699 |
|
|
unsigned char *c = (unsigned char *)&b[k*2]; |
1700 |
|
|
LdbP e; /* the entry */ |
1701 |
|
|
LdbP samerow; /* highest possible entry w/ same row as e */ |
1702 |
|
|
#ifdef LDB_BIG_ENDIAN |
1703 |
|
|
/* the 8 bytes of a posting are BIG ENDIAN ! */ |
1704 |
|
|
memcpy(e.bytes,c,8); |
1705 |
|
|
#else |
1706 |
|
|
e.bytes[0] = c[7]; e.bytes[1] = c[6]; |
1707 |
|
|
e.bytes[2] = c[5]; e.bytes[3] = c[4]; |
1708 |
|
|
e.bytes[4] = c[3]; e.bytes[5] = c[2]; |
1709 |
|
|
e.bytes[6] = c[1]; e.bytes[7] = c[0]; |
1710 |
|
|
#endif |
1711 |
|
|
prow = LDBP_ROW( &e ); |
1712 |
|
|
ptag = LDBP_TAG( &e ); |
1713 |
|
|
ppos = LDBP_POS( &e ); |
1714 |
|
|
LOG_DBG( LOG_VERBOSE, "post %d.%hd pos %06x key '%.*s'", |
1715 |
|
|
prow, ptag, ppos, db->tlen[idx], term ); |
1716 |
|
|
if ( 0 >= ptag /* bad tag */ |
1717 |
|
|
|| !prow || prow >= db->mfc[LMFC_NMFN] /* bad mfn */ |
1718 |
|
|
) |
1719 |
|
|
continue; |
1720 |
|
|
if ( ! post |
1721 |
|
|
|| (post->cut && prow >= post->cut) |
1722 |
|
|
|| (post->tag && post->tag != ptag) |
1723 |
|
|
) |
1724 |
|
|
continue; |
1725 |
|
|
if ( prow < post->skp ) /* quickly bail out on skip condition */ |
1726 |
|
|
break; |
1727 |
|
|
LDBP_SETROWTOP( &samerow, &e ); /* for mfn comparison */ |
1728 |
|
|
/* sweep down to postings for the same row as e ... */ |
1729 |
|
|
while ( f >= 0 && LDBP_GT( p+f, &samerow ) ) |
1730 |
|
|
f--; |
1731 |
|
|
if ( LDB_AND & post->mode ) { |
1732 |
|
|
int l; |
1733 |
|
|
/* loop postings for same row, mark all (that are near enough) */ |
1734 |
|
|
LDBP_SETROWBOT( &samerow, &e ); /* for mfn comparison */ |
1735 |
|
|
/* NOTE: postings for row are GT than bottom even if marked */ |
1736 |
|
|
for ( l = f; l>=0 && LDBP_GT( p+l, &samerow ); l-- ) { |
1737 |
|
|
if ( post->near ) { |
1738 |
|
|
int dist; |
1739 |
|
|
if ( ptag != LDBP_TAG( p+l ) ) continue; |
1740 |
|
|
if ( LDB_NEAR_G != post->near ) { |
1741 |
|
|
dist = LDBP_POS( p+l ) - LDBP_POS( &e ); |
1742 |
|
|
if ( dist < 0 ) dist = -dist; |
1743 |
|
|
if ( 0 < post->near |
1744 |
|
|
? post->near < dist |
1745 |
|
|
: -post->near != dist /* exact $$$$ */ |
1746 |
|
|
) continue; |
1747 |
|
|
} |
1748 |
|
|
} |
1749 |
|
|
LDBP_SETMARK( p+l ); |
1750 |
|
|
added++; |
1751 |
|
|
} |
1752 |
|
|
} else { /* OR mode */ |
1753 |
|
|
int add; |
1754 |
|
|
if ( ! post->near ) /* add if row not found: ignore details */ |
1755 |
|
|
add = 0 > f || prow > LDBP_ROW( p+f ); |
1756 |
|
|
else { /* add if no exact match */ |
1757 |
|
|
int l; |
1758 |
|
|
/* NOTE: we don't use mark bit in OR mode, do we ? */ |
1759 |
|
|
for ( l = f; l>=0 && LDBP_GT( p+l, &e ); l-- ) |
1760 |
|
|
; |
1761 |
|
|
add = 0 > l || LDBP_GT( &e, p+l ); |
1762 |
|
|
} |
1763 |
|
|
if ( add ) |
1764 |
|
|
merge[ m++ ] = e; |
1765 |
|
|
} |
1766 |
|
|
} /* for postings in segment */ |
1767 |
|
|
if ( m ) { /* merge in the merge buffer */ |
1768 |
|
|
LdbP *mm = merge; |
1769 |
|
|
added += m; |
1770 |
|
|
for ( k = post->fil += m; m && k--; ) { |
1771 |
|
|
LdbP src; |
1772 |
|
|
if ( k < m || LDBP_GT( mm, &p[k-m] ) ) { |
1773 |
|
|
src = *mm++; |
1774 |
|
|
m--; |
1775 |
|
|
LOG_DBG( LOG_DEBUG, "merging %d at %d", LDBP_ROW(&src), k ); |
1776 |
|
|
} else |
1777 |
|
|
src = p[k-m]; |
1778 |
|
|
if ( k < post->len ) |
1779 |
|
|
p[k] = src; |
1780 |
|
|
else { /* set cut */ |
1781 |
|
|
int row = LDBP_ROW( &src ); |
1782 |
|
|
if ( row < post->cut || !post->cut ) |
1783 |
|
|
post->cut = row; |
1784 |
|
|
} |
1785 |
|
|
} |
1786 |
|
|
if ( post->fil > post->len ) |
1787 |
|
|
post->fil = post->len; |
1788 |
|
|
if ( post->cut ) /* postings for cut row are unreliable */ |
1789 |
|
|
while ( post->fil && post->cut <= LDBP_ROW(p+post->fil-1) ) |
1790 |
|
|
post->fil--; |
1791 |
|
|
} |
1792 |
|
|
if ( remain ) { /* advance to start of next block */ |
1793 |
|
|
infb++; |
1794 |
|
|
infp = 0; |
1795 |
|
|
} else { |
1796 |
|
|
infb = ifp[LIFP_NXTB]; |
1797 |
|
|
infp = ifp[LIFP_NXTP]; |
1798 |
|
|
} |
1799 |
|
|
} /* for segments */ |
1800 |
|
|
LOG_DBG( LOG_VERBOSE, "added %d postings for key '%.*s'", |
1801 |
|
|
added, db->tlen[idx], term ); |
1802 |
|
|
} /* for terms in prefix/postings mode */ |
1803 |
|
|
done: |
1804 |
|
|
if ( post /* fixup */ |
1805 |
|
|
&& LDB_AND & post->mode && !(LDB_KEEPMARKS & post->mode) |
1806 |
|
|
) { |
1807 |
|
|
int mark = LDB_NOT & post->mode ? 0 : 0x8000; |
1808 |
|
|
j=0; |
1809 |
|
|
for ( i=0; i<post->fil; i++ ) |
1810 |
|
|
if ( mark == LDBP_MARK(p+i) ) { |
1811 |
|
|
LDBP_CLRMARK(p+i); |
1812 |
|
|
p[j++] = p[i]; |
1813 |
|
|
} |
1814 |
|
|
post->fil = j; |
1815 |
|
|
} |
1816 |
|
|
return ! rec ? 0 : rec->len; |
1817 |
|
|
} /* search */ |
1818 |
|
|
|
1819 |
|
|
|
1820 |
|
|
static int ldb_last_path_sep (const char *path) { |
1821 |
|
|
char *p2; |
1822 |
|
|
int i2; |
1823 |
|
|
#ifdef WIN32 |
1824 |
|
|
char *p3; |
1825 |
|
|
int i3; |
1826 |
|
|
#endif |
1827 |
|
|
if (! path) { |
1828 |
|
|
return -1; |
1829 |
|
|
} |
1830 |
|
|
p2 = strrchr (path, '/'); |
1831 |
|
|
i2 = p2 ? p2 - path : -1; |
1832 |
|
|
#ifdef WIN32 |
1833 |
|
|
p3 = strrchr (path, '\\'); |
1834 |
|
|
i3 = p3 ? p3 - path : -1; |
1835 |
|
|
if (i3 > i2) { |
1836 |
|
|
i2 = i3; |
1837 |
|
|
} |
1838 |
|
|
#endif |
1839 |
|
|
return i2; |
1840 |
|
|
} /* ldb_last_path_sep */ |
1841 |
|
|
|
1842 |
|
|
|
1843 |
|
|
static int ldb_open (const char *dbname, Rec *dbpar, Rec *syspar, Fdt *fdt) |
1844 |
|
|
{ |
1845 |
|
|
LDb ndb, *db; |
1846 |
|
|
int i, plen, sz, dbid, lck = LIO_TLOCK; /* WLOCK only on special demand */ |
1847 |
|
|
int ret = 0, invret = -1, lbtret = 0, autoformat = 1, writable = -1; |
1848 |
|
|
int uc = -1, gotopt = 0, txtfd = 0, copyidx = 0; |
1849 |
|
|
char *autoenc = 0; |
1850 |
|
|
char *p, *q; |
1851 |
|
|
char buf[65536+1]; /* need 64K buf for copying DO NOT SHRINK !!! */ |
1852 |
|
|
char path[ PATH_MAX ]; |
1853 |
|
|
|
1854 |
|
|
memset( &ndb, 0, sizeof(ndb) ); |
1855 |
|
|
/* these should be 0 by memsetting to 0 anyway ... */ |
1856 |
|
|
ndb.path = 0; ndb.mmap = 0; |
1857 |
|
|
ndb.flags |= DB_MMAP; /* it mean's: we'll try */ |
1858 |
|
|
|
1859 |
|
|
/* loglevel */ |
1860 |
|
|
if ( 0 <= (i = rInt2(dbpar, syspar, OPENISIS_SLOGV, -1)) ) |
1861 |
|
|
cLog( i, 0 ); |
1862 |
|
|
|
1863 |
|
|
/* prepare name ... */ |
1864 |
|
|
if (! dbname) { |
1865 |
|
|
if (! dbpar) |
1866 |
|
|
return log_msg( ERR_FAULT, "ldb_open: dbname not given"); |
1867 |
|
|
dbname = rString (dbpar, OPENISIS_DNAME, 0, buf, sizeof(buf)); |
1868 |
|
|
if (! dbname) |
1869 |
|
|
return log_msg( ERR_FAULT, "ldb_open: no dbname parameter"); |
1870 |
|
|
} |
1871 |
|
|
plen = strlen (dbname); |
1872 |
|
|
if (0 >= plen) |
1873 |
|
|
return log_msg( ERR_FAULT, "ldb_open: empty dbname"); |
1874 |
|
|
if ( 4 < plen ) { |
1875 |
|
|
if ( !memcmp( ".mst", dbname+plen-4, 4 ) ) { |
1876 |
|
|
uc = 0; |
1877 |
|
|
plen -= 4; |
1878 |
|
|
} else if ( !memcmp( ".MST", dbname+plen-4, 4 ) ) { |
1879 |
|
|
uc = OPEN_UC; |
1880 |
|
|
plen -= 4; |
1881 |
|
|
} |
1882 |
|
|
} |
1883 |
|
|
if ( sizeof(buf) <= (unsigned)plen |
1884 |
|
|
|| sizeof(path) <= (unsigned)(plen + 4 + 1) |
1885 |
|
|
) |
1886 |
|
|
return log_msg( ERR_FAULT, "ldb_open: dbname too long '%s'", dbname); |
1887 |
|
|
if ('/' == dbname[plen - 1] |
1888 |
|
|
#ifdef WIN32 |
1889 |
|
|
|| '\\' == dbname[plen - 1] |
1890 |
|
|
#endif |
1891 |
|
|
) |
1892 |
|
|
return log_msg( ERR_FAULT, |
1893 |
|
|
"ldb_open: must not specify directory as dbname '%s'", dbname); |
1894 |
|
|
if (DBNLEN > plen) |
1895 |
|
|
strcpy(ndb.head.name, dbname); |
1896 |
|
|
else { |
1897 |
|
|
int i1 = 1 + plen - DBNLEN ; |
1898 |
|
|
int i2 = ldb_last_path_sep (dbname); |
1899 |
|
|
if (0 <= i2 && plen > ++i2 && i2 > i1) { |
1900 |
|
|
i1 = i2; |
1901 |
|
|
} |
1902 |
|
|
strncpy(ndb.head.name, dbname + i1, DBNLEN - 1) [DBNLEN - 1] = 0; |
1903 |
|
|
log_msg( LOG_WARN, "ldb_open: truncating dbname '%s' to '%s'", |
1904 |
|
|
dbname, ndb.head.name); |
1905 |
|
|
} |
1906 |
|
|
/* ... and path */ |
1907 |
|
|
strcpy(path, dbname); |
1908 |
|
|
if (! IsAbsPath (path)) { |
1909 |
|
|
int plen2; |
1910 |
|
|
if ( (dbpar || syspar) |
1911 |
|
|
&& (p = rString2 (dbpar, syspar, OPENISIS_DPATH, buf, sizeof(buf))) |
1912 |
|
|
) { |
1913 |
|
|
plen2 = strlen (p); |
1914 |
|
|
if (sizeof(path) <= (unsigned)(plen + plen2 + 4 + 1 + 1)) |
1915 |
|
|
return log_msg( ERR_FAULT, |
1916 |
|
|
"ldb_open: dbname or dbpath too long: %d %d '%s'", |
1917 |
|
|
plen, plen2, path); |
1918 |
|
|
memmove (path + 1 + plen2, path, 1 + plen); |
1919 |
|
|
path[plen2] = '/'; |
1920 |
|
|
memcpy (path, p, plen2); |
1921 |
|
|
plen += 1 + plen2; |
1922 |
|
|
} |
1923 |
|
|
if ( !IsAbsPath(path) |
1924 |
|
|
&& syspar |
1925 |
|
|
&& (p = rString(syspar, OPENISIS_SPATH, 0, buf, sizeof(buf))) |
1926 |
|
|
) { |
1927 |
|
|
plen2 = strlen(p); |
1928 |
|
|
if (sizeof(path) <= (unsigned)(plen + plen2 + 4 + 1 + 1)) |
1929 |
|
|
return log_msg( ERR_FAULT, |
1930 |
|
|
"ldb_open: dbname or syspath too long: %d %d '%s'", |
1931 |
|
|
plen, plen2, path); |
1932 |
|
|
memmove(path + 1 + plen2, path, 1 + plen); |
1933 |
|
|
path[plen2] = '/'; |
1934 |
|
|
memcpy(path, p, plen2); |
1935 |
|
|
plen += 1 + plen2; |
1936 |
|
|
} |
1937 |
|
|
} /* name and path */ |
1938 |
|
|
|
1939 |
|
|
/* more init AFTER honoring verbosity */ |
1940 |
|
|
if ( ! init ) { |
1941 |
|
|
lstr_auto(0); |
1942 |
|
|
init = !0; |
1943 |
|
|
} |
1944 |
|
|
|
1945 |
|
|
for ( dbid=0; dbid<dbs_len; dbid++ ) { |
1946 |
|
|
if ( dbs[dbid].flags && |
1947 |
|
|
!strcmp( ndb.head.name, dbs[dbid].head.name ) ) { |
1948 |
|
|
log_msg( LOG_INFO, "reopening %d '%s'", dbid, ndb.head.name ); |
1949 |
|
|
return dbid; |
1950 |
|
|
} |
1951 |
|
|
} |
1952 |
|
|
/* go for slot */ |
1953 |
|
|
if ( dbid == dbs_len ) |
1954 |
|
|
for ( dbid=0; dbid<dbs_len && dbs[dbid].flags; dbid++ ) |
1955 |
|
|
; |
1956 |
|
|
if ( dbid == dbs_len ) |
1957 |
|
|
return -1; |
1958 |
|
|
db = &dbs[dbid]; |
1959 |
|
|
/* got slot */ |
1960 |
|
|
*db = ndb; |
1961 |
|
|
db->head.dbid = dbid; |
1962 |
|
|
|
1963 |
|
|
/* preset record sizes */ |
1964 |
|
|
db->mfc[0] = *DB_XSTR( db, LSET_MST, LSTR_MFC ); |
1965 |
|
|
db->xrf[0] = *DB_XSTR( db, LSET_MST, LSTR_XRF ); |
1966 |
|
|
db->cnt[0][0] = |
1967 |
|
|
db->cnt[1][0] = *DB_XSTR( db, LSET_INV, LSTR_CNT ); |
1968 |
|
|
/* isis-1 index term lengths */ |
1969 |
|
|
db->tlen[0] = 10; |
1970 |
|
|
db->tlen[1] = 30; |
1971 |
|
|
|
1972 |
|
|
/* only the packed little endian ("DOS") format is writable |
1973 |
|
|
test later ... |
1974 |
|
|
if ( LVAR_PAC != (DB_VARI & db->flags) ) |
1975 |
|
|
writable = 0; |
1976 |
|
|
*/ |
1977 |
|
|
|
1978 |
|
|
db->path = mDup( path, plen+1 ); /* save path */ |
1979 |
|
|
memcpy( path+plen, ".???", 5 ); |
1980 |
|
|
|
1981 |
|
|
if ( dbpar ) |
1982 |
|
|
dbpar = rDup(dbpar, 0, 0); |
1983 |
|
|
/* check options file and extension case */ |
1984 |
|
|
if ( 0 <= uc ) /* use case from dbname */ |
1985 |
|
|
i = lio_open( setext(path,EXT_TXT_OPT,uc), OPEN_RDIF ); |
1986 |
|
|
else if ( 0 > (i = lio_open( setext(path,EXT_TXT_OPT,uc=0), OPEN_RDIF )) |
1987 |
|
|
&& 0 > (i = lio_open( setext(path,EXT_TXT_OPT,uc=OPEN_UC), OPEN_RDIF )) |
1988 |
|
|
) |
1989 |
|
|
uc = autocase( db->path ); |
1990 |
|
|
if ( 0 < i ) { |
1991 |
|
|
if ( 0 < (sz = lio_size(i)) ) { |
1992 |
|
|
p = sz < (int)sizeof(buf) ? buf : mAlloc(sz); |
1993 |
|
|
if ( (gotopt = (sz == lio_read( &i, p, sz ))) ) |
1994 |
|
|
rDeser( &dbpar, p, sz, 0 ); |
1995 |
|
|
log_msg( LOG_INFO, "reading %d bytes options from '%s' %s", |
1996 |
|
|
sz, path, gotopt ? "ok" : "nok" ); |
1997 |
|
|
if ( buf != p ) |
1998 |
|
|
mFree( p ); |
1999 |
|
|
} |
2000 |
|
|
lio_close( &i, LIO_INOUT ); |
2001 |
|
|
} |
2002 |
|
|
lck |= uc; |
2003 |
|
|
|
2004 |
|
|
if ( (dbpar || syspar) && 0 <= (i = rInt2(dbpar, syspar, OPENISIS_DRO, -1))) |
2005 |
|
|
writable = !i; /* explicit 0/1 */ |
2006 |
|
|
|
2007 |
|
|
/* open files */ |
2008 |
|
|
/* trad. index is never openend writable. */ |
2009 |
|
|
invret = openfiles( db->inv, path, EXT_INV, INV_FILES, uc|OPEN_RDIF ); |
2010 |
|
|
if (dbpar || syspar) { |
2011 |
|
|
char fmtstr[32]; |
2012 |
|
|
if (rString2 (dbpar, syspar, OPENISIS_DTYPE, fmtstr, sizeof(fmtstr))) { |
2013 |
|
|
if (! strcmp ("aligned", fmtstr)) { |
2014 |
|
|
db->flags |= LVAR_ALI; |
2015 |
|
|
autoformat = 0; |
2016 |
|
|
} else if (! strcmp ("naligned", fmtstr)) |
2017 |
|
|
autoformat = 0; |
2018 |
|
|
} |
2019 |
|
|
} |
2020 |
|
|
if ( autoformat ) { |
2021 |
|
|
if ( invret ) |
2022 |
|
|
log_msg( LOG_WARN, "cannot guess format -- no inverted file" ); |
2023 |
|
|
else { |
2024 |
|
|
unsigned len = lio_size( db->inv[INV_CNT] ); |
2025 |
|
|
if ( 56L == len ) { |
2026 |
|
|
db->flags |= LVAR_ALI; |
2027 |
|
|
autoenc = "iso8859-1"; |
2028 |
|
|
/* writable = 0; we do not write aligned format */ |
2029 |
|
|
} else if ( 52L == len ) |
2030 |
|
|
autoenc = "cp850"; |
2031 |
|
|
else |
2032 |
|
|
log_msg( LOG_WARN, "cannot guess format -- bad .cnt len %d", len ); |
2033 |
|
|
log_msg( LOG_INFO, "using autoformat %saligned for .cnt len %d", |
2034 |
|
|
(db->flags & LVAR_ALI) ? "":"un", len ); |
2035 |
|
|
} |
2036 |
|
|
} |
2037 |
|
|
|
2038 |
|
|
/* data */ |
2039 |
|
|
#ifdef NOTXTDB |
2040 |
|
|
if ( !(ret = openfiles( db->mst, path, EXT_MST, MST_FILES, |
2041 |
|
|
lck|OPEN_ASIS|LIO_CREAT )) |
2042 |
|
|
) |
2043 |
|
|
writable = 0; |
2044 |
|
|
else if (0 > ret) |
2045 |
|
|
#else |
2046 |
|
|
if ( 0 <= (ret = openfiles( &txtfd, path, EXT_TXT, 1, |
2047 |
|
|
lck|LIO_SYNC|(writable?OPEN_ASIS:OPEN_RDIF) )) |
2048 |
|
|
) { /* .txt exists: use it */ |
2049 |
|
|
if ( ret ) |
2050 |
|
|
writable = 1; |
2051 |
|
|
else if (1 == writable) { |
2052 |
|
|
log_msg( LOG_ERROR, "file '%s' is readonly", path ); |
2053 |
|
|
goto cleanup; |
2054 |
|
|
} else |
2055 |
|
|
writable = 0; |
2056 |
|
|
} else if ( |
2057 |
|
|
0 <= (ret = openfiles( db->mst, path, EXT_MST, MST_FILES, |
2058 |
|
|
lck|((writable && !(db->flags & LVAR_ALI))?OPEN_ASIS:OPEN_RDIF) )) |
2059 |
|
|
&& (ret || 1!=writable) |
2060 |
|
|
) { /* trad. files are ok */ |
2061 |
|
|
if ( !ret ) |
2062 |
|
|
writable = 0; |
2063 |
|
|
} else if ( 1 != (ret = openfiles( &txtfd, path, EXT_TXT, 1, |
2064 |
|
|
lck|(ret ? LIO_SYNC : 0)|OPEN_NEW )) ) /* don't sync on autoconv */ |
2065 |
|
|
#endif |
2066 |
|
|
goto cleanup; |
2067 |
|
|
|
2068 |
|
|
/* MW: creation mode? KR: ugo+rw & ~umask */ |
2069 |
|
|
if ( 1 == (lbtret = openfiles( &db->oxi.fd, path, EXT_LBT, 1, |
2070 |
|
|
lck|(writable?OPEN_ASIS:OPEN_RDIF) )) |
2071 |
|
|
) |
2072 |
|
|
lbtret = 0; |
2073 |
|
|
else if ( !writable ) |
2074 |
|
|
;/* no problem */ |
2075 |
|
|
else if ( !lbtret ) { /* exists ro */ |
2076 |
|
|
log_msg( LOG_ERROR, "file '%s' is readonly", path ); |
2077 |
|
|
goto cleanup; |
2078 |
|
|
} else { /* create and copy to oxi */ |
2079 |
|
|
if ( 1 != openfiles( &db->oxi.fd, path, EXT_LBT, 1, lck|OPEN_NEW ) ) |
2080 |
|
|
goto cleanup; |
2081 |
|
|
lbtret = 0; |
2082 |
|
|
copyidx = 1; |
2083 |
|
|
} |
2084 |
|
|
|
2085 |
|
|
if ( db->mst[MST_MST] ) { /* care for the traditionals */ |
2086 |
|
|
if ( (ret = readlog( |
2087 |
|
|
db->mfc, db->mst[MST_MST], 0, db, LSET_MST, LSTR_MFC )) |
2088 |
|
|
) { |
2089 |
|
|
/* NEW goto cleanup; */ |
2090 |
|
|
memset( db->mfc, 0, sizeof(db->mfc) ); |
2091 |
|
|
db->mfc[LMFC_NMFN] = 1; |
2092 |
|
|
db->mfc[LMFC_NMFB] = 1; |
2093 |
|
|
db->mfc[LMFC_NMFP] = 64; |
2094 |
|
|
db->mflen = 64; |
2095 |
|
|
} else { |
2096 |
|
|
/* |
2097 |
|
|
int lastblock = (db->mflen = lio_size( db->mst[MST_MST] ))/512; |
2098 |
|
|
if ( 511 & db->mflen ) lastblock++; |
2099 |
|
|
counting from 1 |
2100 |
|
|
the next record's block should be either the last one we have |
2101 |
|
|
or the next one to follow |
2102 |
|
|
if ( db->mfc[LMFC_NMFB] != lastblock |
2103 |
|
|
&& db->mfc[LMFC_NMFB] != lastblock+1 |
2104 |
|
|
) |
2105 |
|
|
log_msg( LOG_VERBOSE, "NMFB mismatch: NMFB %d ~ %d", |
2106 |
|
|
db->mfc[LMFC_NMFB], lastblock ); |
2107 |
|
|
*/ |
2108 |
|
|
/* set LOGICAL mf length */ |
2109 |
|
|
db->mflen = (db->mfc[LMFC_NMFB]-1)*512 + db->mfc[LMFC_NMFP]; |
2110 |
|
|
} |
2111 |
|
|
db->ptrl = 512; |
2112 |
|
|
db->xrlen = lio_size( db->mst[MST_XRF] ) / 512; |
2113 |
|
|
if ( (DB_MMAP & db->flags) |
2114 |
|
|
&& db->xrlen |
2115 |
|
|
&& db->xrlen*512 |
2116 |
|
|
== lio_mmap( &db->mst[MST_XRF], (void**)&db->mmap, db->xrlen*512 ) |
2117 |
|
|
) |
2118 |
|
|
db->mmlen = db->xrlen; |
2119 |
|
|
} |
2120 |
|
|
|
2121 |
|
|
if ( txtfd ) { |
2122 |
|
|
int remake = 0; |
2123 |
|
|
/* TODO: make on-demand preparation even faster using buffered IO */ |
2124 |
|
|
if ( !lio_size(txtfd) ) { |
2125 |
|
|
const char newline = LF; |
2126 |
|
|
if ( gotopt |
2127 |
|
|
&& 0 < (i = lio_open( setext(path,EXT_TXT_OPT,uc), LIO_RD )) |
2128 |
|
|
) { /* copy the options file */ |
2129 |
|
|
log_msg( LOG_INFO, "copying %d bytes options", lio_size(i) ); |
2130 |
|
|
while ( 0 < (sz = lio_read( &i, buf, sizeof(buf)-1 )) ) |
2131 |
|
|
lio_write( &txtfd, buf, sz ); |
2132 |
|
|
if ( LIO_INOUT & i ) { /* is supposed to autoclose */ |
2133 |
|
|
log_msg( LOG_WARN, "tss tss tss ..." ); |
2134 |
|
|
lio_close( &i, LIO_INOUT ); |
2135 |
|
|
} |
2136 |
|
|
} |
2137 |
|
|
lio_write( &txtfd, &newline, 1 ); |
2138 |
|
|
} |
2139 |
|
|
|
2140 |
|
|
if ( db->mst[MST_MST] ) { /* copy to new empty txt */ |
2141 |
|
|
int end = db->mfc[LMFC_NMFN]; |
2142 |
|
|
/* |
2143 |
|
|
max recsize for traditionals is 32K. |
2144 |
|
|
field values may double, if consisting entirely of newlines. |
2145 |
|
|
rec->used may be more than 32K, since we 12 bytes per field. |
2146 |
|
|
However, we know there are only sign+5digits+tab+newline used per tag, |
2147 |
|
|
fitting within 2* the original 6 bytes per field. |
2148 |
|
|
*/ |
2149 |
|
|
|
2150 |
|
|
log_msg( LOG_INFO, "copying traditional data" ); |
2151 |
|
|
db->flags |= DB_OPEN; /* pretend */ |
2152 |
|
|
for ( i=1; i<end; i++ ) { |
2153 |
|
|
Rec *r = dRead( dbid, i ); |
2154 |
|
|
if ( !r ) |
2155 |
|
|
sz = 1; |
2156 |
|
|
else if ( (int)sizeof(buf) <= (sz = rSerB( buf, r )) ) { |
2157 |
|
|
log_msg( ERR_IDIOT, "serialized %d bytes" ); |
2158 |
|
|
exit(42); |
2159 |
|
|
} |
2160 |
|
|
lio_write( &txtfd, buf, sz ); |
2161 |
|
|
} |
2162 |
|
|
db->flags &= ~DB_OPEN; /* pret end */ |
2163 |
|
|
remake = 1; |
2164 |
|
|
if ( db->mmap ) |
2165 |
|
|
lio_mmap( 0, (void**)&db->mmap, db->mmlen*512 ); |
2166 |
|
|
db->mmlen = 0; |
2167 |
|
|
closefiles( db->mst, MST_FILES ); |
2168 |
|
|
} /* copying */ |
2169 |
|
|
db->mst[MST_MST] = txtfd; |
2170 |
|
|
db->mflen = lio_size( db->mst[MST_MST] ); |
2171 |
|
|
|
2172 |
|
|
db->ptr = 0x0134; /* should be config opt */ |
2173 |
|
|
if ( !remake ) { /* other reasons why we should remake */ |
2174 |
|
|
unsigned short ptr; |
2175 |
|
|
unsigned isix = GETINT(ISIX); |
2176 |
|
|
unsigned magic; |
2177 |
|
|
|
2178 |
|
|
remake = 1; |
2179 |
|
|
if ( 0 > (db->mst[MST_XRF] = lio_open( setext(path,EXT_TXT_PTR,uc), |
2180 |
|
|
LIO_SEEK|(writable?LIO_RDWR:LIO_RD) )) |
2181 |
|
|
) |
2182 |
|
|
log_msg( LOG_INFO, "'%s' not found", path ); |
2183 |
|
|
else if ( 6 != lio_read(&db->mst[MST_XRF],buf,6) ) |
2184 |
|
|
log_msg( LOG_WARN, "'%s' too short", path ); |
2185 |
|
|
else if ( isix != (magic = GETINT(buf)) ) /* FOO! */ |
2186 |
|
|
log_msg( LOG_WARN, "'%s' has black magic 0x%08x", path, magic ); |
2187 |
|
|
/* TODO: save that foo if it doesn't read ISIX ? */ |
2188 |
|
|
else if ( 0xf000 & (ptr = GETSHORT(buf+4)) ) /* bad endianess */ |
2189 |
|
|
log_msg( LOG_WARN, "'%s' has bad endianess type 0x%04x", path, ptr ); |
2190 |
|
|
else if ( (db->ptr && db->ptr != ptr) ) /* other type configured */ |
2191 |
|
|
log_msg( LOG_WARN, "'%s' type 0x%04x != cfg 0x%04x", path, ptr, db->ptr ); |
2192 |
|
|
else if ( lio_time(db->mst[MST_XRF]) < lio_time(db->mst[MST_MST]) ) |
2193 |
|
|
log_msg( LOG_WARN, "'%s' older than data", path ); |
2194 |
|
|
else { |
2195 |
|
|
db->ptr = ptr; |
2196 |
|
|
remake = 0; |
2197 |
|
|
} |
2198 |
|
|
} |
2199 |
|
|
if ( ! db->ptr ) { |
2200 |
|
|
db->ptr = 0x0134; /* m*256 + l*16 + k, doc/Serialized */ |
2201 |
|
|
/* BTW: 0x34 is ASCII digit '4', so it's ISIX4^A on little endian */ |
2202 |
|
|
db->ptrl = 8; |
2203 |
|
|
} else { /* fix unsupported type */ |
2204 |
|
|
unsigned m = 0xf&(db->ptr>>8); |
2205 |
|
|
unsigned l = 0xf&(db->ptr>>4); |
2206 |
|
|
unsigned k = 0xf&db->ptr; |
2207 |
|
|
int mod = 0; |
2208 |
|
|
if ( m > 4 ) { m = 4; mod = 1; } |
2209 |
|
|
if ( l > 4 ) { l = 4; mod = 1; } |
2210 |
|
|
if ( k > 4 ) { k = 4; mod = 1; } /* TODO: allow 8 with large files */ |
2211 |
|
|
/* total ptr bytes = sum(nibbles) <= 45, but won't use more than 8+4+4 */ |
2212 |
|
|
if ( mod ) { |
2213 |
|
|
log_msg( LOG_WARN, "fixing unsupported ptr type 0x%04x", db->ptr ); |
2214 |
|
|
db->ptr = (unsigned short)(m<<8 | l<<4 | k); |
2215 |
|
|
remake = 1; |
2216 |
|
|
} |
2217 |
|
|
db->ptrl = k+l+m; |
2218 |
|
|
} |
2219 |
|
|
if ( remake ) { |
2220 |
|
|
Ptr pt; |
2221 |
|
|
unsigned base = 0; /* of current block */ |
2222 |
|
|
unsigned pos = 0; /* of last record */ |
2223 |
|
|
unsigned fld = 0; /* of last record */ |
2224 |
|
|
unsigned nmfn = 0; /* next mfn = maxmfn+1 */ |
2225 |
|
|
unsigned xmfn = 0; /* explicitly given */ |
2226 |
|
|
char op = 0; |
2227 |
|
|
int more; /* buf not empty flag */ |
2228 |
|
|
char *last; /* of current block */ |
2229 |
|
|
|
2230 |
|
|
lio_close( &db->mst[MST_XRF], LIO_INOUT ); |
2231 |
|
|
if ( 0 > (db->mst[MST_XRF] = lio_open( |
2232 |
|
|
setext(path,EXT_TXT_PTR,uc), OPEN_BLANK )) |
2233 |
|
|
) |
2234 |
|
|
goto cleanup; |
2235 |
|
|
/* write signature */ |
2236 |
|
|
memcpy( pt.r, "ISIX", 4 ); |
2237 |
|
|
memcpy( pt.r+4, &db->ptr, 2 ); |
2238 |
|
|
memcpy( pt.r+6, ":)", 2 ); |
2239 |
|
|
if ( 8 < db->ptrl ) |
2240 |
|
|
memset( pt.r+8, ')', db->ptrl - 8 ); |
2241 |
|
|
lio_pwrite( &db->mst[MST_XRF], pt.r, db->ptrl, 0 ); |
2242 |
|
|
/* loop the masterfile */ |
2243 |
|
|
lio_seek( &db->mst[MST_MST], 0 ); |
2244 |
|
|
last = (p = buf) + lio_read( &db->mst[MST_MST], buf, 8192 ) - 1; |
2245 |
|
|
more = last > buf; /* one byte is no byte ;) */ |
2246 |
|
|
if ( more && LF == *p ) { /* no options: no \n\n */ |
2247 |
|
|
nmfn = pos = 1; |
2248 |
|
|
p++; |
2249 |
|
|
} |
2250 |
|
|
for (;;) { /* records */ |
2251 |
|
|
unsigned len, mfn; |
2252 |
|
|
for (;;) { /* lines and stuff to end of record */ |
2253 |
|
|
if ( p < last ) { /* have one lookahead */ |
2254 |
|
|
if ( LF != *p++ ) |
2255 |
|
|
continue; /* the tight loop ... or use memchr ? */ |
2256 |
|
|
if ( LF != *p ) { /* now p <= last */ |
2257 |
|
|
if ( fld || !(0xc0 & *p) ) { /* < '@', 'A', ... */ |
2258 |
|
|
if ( TAB != *p ) /* no continuation */ |
2259 |
|
|
fld++; |
2260 |
|
|
continue; |
2261 |
|
|
} |
2262 |
|
|
fld++; /* count field, unless we really recognize a opline */ |
2263 |
|
|
if ( 'Z' < *p ) |
2264 |
|
|
continue; |
2265 |
|
|
/* now we have '@'...'Z' at start of 1st line */ |
2266 |
|
|
sz = last - p; /* avail after p */ |
2267 |
|
|
if ( sz && TAB != p[1] ) /* no opline */ |
2268 |
|
|
continue; |
2269 |
|
|
switch (*p) { |
2270 |
|
|
case 'D': |
2271 |
|
|
case 'I': |
2272 |
|
|
case 'W': |
2273 |
|
|
break; /* give it a try */ |
2274 |
|
|
default: |
2275 |
|
|
log_msg( LOG_WARN, "unknown opline %c at mfn %d", *p, nmfn ); |
2276 |
|
|
continue; |
2277 |
|
|
} |
2278 |
|
|
if ( sz > 127 ) /* longer -> no opline */ |
2279 |
|
|
sz = 127; |
2280 |
|
|
if ( ! sz || ! (q = memchr(p+1, LF, sz)) ) { |
2281 |
|
|
if ( sz >= 127 || ! more ) |
2282 |
|
|
continue; /* too long or undelimited last */ |
2283 |
|
|
p--; /* back to \n, so we come here again */ |
2284 |
|
|
goto gimmemore; |
2285 |
|
|
} |
2286 |
|
|
if ( q < p+3 || p[2] < '0' || '9' < p[2] ) |
2287 |
|
|
continue; |
2288 |
|
|
/* TODO: |
2289 |
|
|
take a closer look at whether the whole line makes sense |
2290 |
|
|
*/ |
2291 |
|
|
if ( op ) { /* yeah, two metas in sequence! weird stuff! */ |
2292 |
|
|
p--; /* step back to newline */ |
2293 |
|
|
pos = base+(p-buf); /* fake pos as if we had no line at all */ |
2294 |
|
|
break; /* go handle the PREVIOUS opline */ |
2295 |
|
|
} |
2296 |
|
|
op = *p; |
2297 |
|
|
xmfn = a2i( p+2, q-p-2 ); |
2298 |
|
|
fld--; /* uncount this line */ |
2299 |
|
|
pos = base + (q-buf) + 1; /* start after q */ |
2300 |
|
|
continue; |
2301 |
|
|
} |
2302 |
|
|
break; |
2303 |
|
|
} |
2304 |
|
|
gimmemore: |
2305 |
|
|
LOG_DBG( LOG_DEBUG, "MORE %d at pos %d base %d p +%d last +%d", |
2306 |
|
|
more, pos, base, p-buf, last-buf ); |
2307 |
|
|
if ( !more ) |
2308 |
|
|
goto schicht; /* german: done */ |
2309 |
|
|
base += p - buf; /* shift out bytes before p */ |
2310 |
|
|
len = last-p; /* bytes to keep after p; < 128 */ |
2311 |
|
|
if ( len ) /* we're probing for more lookahead */ |
2312 |
|
|
memmove( buf, p, 1+last-p ); |
2313 |
|
|
else /* typically */ |
2314 |
|
|
*buf = *p; /* but save the last dance */ |
2315 |
|
|
p = buf; |
2316 |
|
|
last = buf + len; |
2317 |
|
|
/* reload */ |
2318 |
|
|
if ( 0 < (sz = lio_read( &db->mst[MST_MST], buf+1+len, 8192 )) ) { |
2319 |
|
|
last += sz; |
2320 |
|
|
continue; |
2321 |
|
|
} |
2322 |
|
|
more = 0; /* but yet, finish this up */ |
2323 |
|
|
/* since *buf = *last was the files last character, |
2324 |
|
|
we'd expect a newline |
2325 |
|
|
*/ |
2326 |
|
|
if ( last == p ) |
2327 |
|
|
p = buf+(LF==*buf ? 1 : 2); /* pretend buf started \n */ |
2328 |
|
|
if ( ! len ) |
2329 |
|
|
break; |
2330 |
|
|
/* else try again opline */ |
2331 |
|
|
} /* lines and stuff */ |
2332 |
|
|
/* now p is on a delimiting blank lines \n -- or such ... */ |
2333 |
|
|
len = base + (p-buf) - pos; /* >= 0 */ |
2334 |
|
|
mfn = xmfn ? xmfn : nmfn; |
2335 |
|
|
log_msg( LOG_INFO, "ptr %c %d(%d/%d) pos %d len %d", |
2336 |
|
|
op?op:'>', mfn, xmfn, nmfn, pos, len ); |
2337 |
|
|
if ( base + (p-buf) < pos ) /* FOO !!! */ |
2338 |
|
|
len = 0; |
2339 |
|
|
if ( len ) /* could have been completely empty */ |
2340 |
|
|
len--; /* mute last \n */ |
2341 |
|
|
if ( 'D' == op && len ) /* FOO !!! */ |
2342 |
|
|
len = 0; |
2343 |
|
|
if ( mfn && (len || op) ) |
2344 |
|
|
lio_pwrite( &db->mst[MST_XRF], |
2345 |
|
|
mkptr( pt.r, db, pos, len, fld), db->ptrl, mfn*db->ptrl ); |
2346 |
|
|
pos = base + (p-buf) + 1; /* next starts after p */ |
2347 |
|
|
if ( 'D' != op ) { /* 'D'elete does not lead to implicit reuse */ |
2348 |
|
|
if ( op && nmfn < xmfn ) |
2349 |
|
|
nmfn = xmfn; |
2350 |
|
|
nmfn++; /* continue after this */ |
2351 |
|
|
} |
2352 |
|
|
xmfn = fld = op = 0; |
2353 |
|
|
} |
2354 |
|
|
schicht: ; |
2355 |
|
|
} /* remake */ |
2356 |
|
|
db->mfc[LMFC_NMFN] = |
2357 |
|
|
db->xrlen = lio_size( db->mst[MST_XRF] ) / db->ptrl; |
2358 |
|
|
if ( (DB_MMAP & db->flags) |
2359 |
|
|
&& db->xrlen |
2360 |
|
|
&& db->xrlen*db->ptrl |
2361 |
|
|
== lio_mmap( &db->mst[MST_XRF], (void**)&db->mmap, db->xrlen*db->ptrl ) |
2362 |
|
|
) |
2363 |
|
|
db->mmlen = db->xrlen; |
2364 |
|
|
log_msg( LOG_INFO, "mapped %d*%d = %d", |
2365 |
|
|
db->xrlen, db->ptrl, db->xrlen*db->ptrl ); |
2366 |
|
|
db->flags |= DB_TXTOPEN; |
2367 |
|
|
db->flags &= ~DB_VARI; /* clear alignment and such */ |
2368 |
|
|
} /* if ( txtfd ) */ |
2369 |
|
|
|
2370 |
|
|
/* supporting files, ctables */ |
2371 |
|
|
p = buf; |
2372 |
|
|
if ( 0 >= (sz = lio_slurp( &p, sizeof(buf), setext(path,EXT_SUP_ACT,uc), 1 )) |
2373 |
|
|
|| lcs_mktab( db->ctab+LCS_CTYPE, p, sz, LCS_A ) |
2374 |
|
|
) |
2375 |
|
|
memcpy( db->ctab+LCS_CTYPE, lcs_latin1_ct, sizeof(db->ctab[0]) ); |
2376 |
|
|
if ( 0 >= (sz = lio_slurp( &p, sizeof(buf), setext(path,EXT_SUP_UCT,uc), 1 )) |
2377 |
|
|
|| lcs_mktab( db->ctab+LCS_UCASE, p, sz, 0 ) |
2378 |
|
|
) |
2379 |
|
|
memcpy( db->ctab+LCS_UCASE, lcs_latin1_uc, sizeof(db->ctab[0]) ); |
2380 |
|
|
/* fill header */ |
2381 |
|
|
|
2382 |
|
|
if (! fdt) { |
2383 |
|
|
if ( (p = rString (dbpar, OPENISIS_DFDT, 0, buf, sizeof(buf))) ) { |
2384 |
|
|
Rec *recfdt = 0; |
2385 |
|
|
Db *dbfdt = nDbByName (openisis_stub0, p); |
2386 |
|
|
if ( dbfdt) |
2387 |
|
|
recfdt = dRead (dbfdt->dbid, 1); |
2388 |
|
|
else { |
2389 |
|
|
int idfdt = ldb_open (p, 0, syspar, 0); |
2390 |
|
|
if (0 <= idfdt) { |
2391 |
|
|
recfdt = dRead (idfdt, 1); /*MMM*/ |
2392 |
|
|
cDClose (idfdt); |
2393 |
|
|
} |
2394 |
|
|
} |
2395 |
|
|
if (recfdt) |
2396 |
|
|
fdt = fRec2Fdt (recfdt); |
2397 |
|
|
} else if ( gotopt ) |
2398 |
|
|
fdt = fRec2Fdt(dbpar); |
2399 |
|
|
if (! fdt) |
2400 |
|
|
fdt = fFromFile (path); |
2401 |
|
|
} |
2402 |
|
|
db->head.fdt = fdt; |
2403 |
|
|
if (fdt) |
2404 |
|
|
log_msg( LOG_INFO, "have %d fdt entries for %s", |
2405 |
|
|
fdt->len, db->head.name); |
2406 |
|
|
else |
2407 |
|
|
log_msg( LOG_INFO, "have no fdt for %s", db->head.name); |
2408 |
|
|
|
2409 |
|
|
db->head.tms = timeUpd(0); /* what watch? */ |
2410 |
|
|
log_msg( LOG_INFO, "tms %d for %s", db->head.tms, db->head.name); |
2411 |
|
|
|
2412 |
|
|
|
2413 |
|
|
/* set path and name */ |
2414 |
|
|
if (0 <= (i = ldb_last_path_sep (db->path))) { |
2415 |
|
|
if (i) |
2416 |
|
|
strncpy(path, db->path, i)[i] = 0; |
2417 |
|
|
else |
2418 |
|
|
strcpy (path, "/"); |
2419 |
|
|
dbpar = rSet (dbpar, RCHG | RDIS, OPENISIS_DPATH, path, 0); |
2420 |
|
|
} |
2421 |
|
|
dbpar = rSet (dbpar, RCHG | RDIS, OPENISIS_DNAME, db->head.name, 0); |
2422 |
|
|
|
2423 |
|
|
/* set encoding */ |
2424 |
|
|
if (!(p = rString (dbpar, OPENISIS_DENC, 0, buf, sizeof(buf)))) |
2425 |
|
|
if ( (syspar |
2426 |
|
|
&& (p = rString (syspar, OPENISIS_DENC, 0, buf, sizeof(buf)))) |
2427 |
|
|
|| (p = autoenc) |
2428 |
|
|
) |
2429 |
|
|
dbpar = rSet(dbpar, RDIS, OPENISIS_DENC, p, 0); |
2430 |
|
|
if ( p ) |
2431 |
|
|
log_msg( LOG_INFO, "using encoding %s for %s", p, db->head.name); |
2432 |
|
|
|
2433 |
|
|
db->head.cfg = dbpar; |
2434 |
|
|
|
2435 |
|
|
/* done */ |
2436 |
|
|
db->flags |= DB_OPEN; |
2437 |
|
|
|
2438 |
|
|
if ( writable && LVAR_PAC == (DB_VARI & db->flags) ) |
2439 |
|
|
db->flags |= DB_WRITABLE; |
2440 |
|
|
|
2441 |
|
|
/* |
2442 |
|
|
if ( (dbpar || syspar) && 0 < rInt2(dbpar, syspar, OPENISIS_DDUMP, -1) ) { |
2443 |
|
|
int off = 0; |
2444 |
|
|
int *r; |
2445 |
|
|
do { |
2446 |
|
|
if ( (r = ldb_readRecAtOff(dbid,off,&off)) ) |
2447 |
|
|
mFree( r ); |
2448 |
|
|
} while ( 0 < off ); |
2449 |
|
|
exit(0); |
2450 |
|
|
} |
2451 |
|
|
*/ |
2452 |
|
|
|
2453 |
|
|
/* init oxi */ |
2454 |
|
|
if ( writable ) |
2455 |
|
|
db->oxi.flg |= LBT_WRITE; |
2456 |
|
|
if ( (p = getenv("OXITYP")) && 0 < (i = atoi(p)) && 4 > i ) |
2457 |
|
|
db->oxi.typ = i << 4; |
2458 |
|
|
if ( !lbtret && !lbt_init( &db->oxi ) ) |
2459 |
|
|
db->flags |= DB_LBTOPEN; |
2460 |
|
|
|
2461 |
|
|
if ( ! invret |
2462 |
|
|
&& ! (ret = readlog( db->cnt[0], db->inv[INV_CNT], |
2463 |
|
|
0, db, LSET_INV, LSTR_CNT )) |
2464 |
|
|
&& ! (ret = readlog( db->cnt[1], db->inv[INV_CNT], |
2465 |
|
|
-1, db, LSET_INV, LSTR_CNT )) |
2466 |
|
|
) { |
2467 |
|
|
if ( lbtret ) |
2468 |
|
|
db->flags |= DB_INVOPEN; |
2469 |
|
|
else { |
2470 |
|
|
if ( copyidx ) { |
2471 |
|
|
DXLoop l; |
2472 |
|
|
log_msg( LOG_INFO, "copying traditional index" ); |
2473 |
|
|
lbtret = 0; |
2474 |
|
|
memset( &l, 0, sizeof(l) ); |
2475 |
|
|
l.me = & db->oxi; |
2476 |
|
|
l.cb = (DXCb*)cXAdd; |
2477 |
|
|
lbt_batch( & db->oxi, 5 ); |
2478 |
|
|
search( db, 0, 0, 0, &l ); |
2479 |
|
|
cXAdd( & db->oxi, 0, 0 ); |
2480 |
|
|
} |
2481 |
|
|
closefiles( db->inv, INV_FILES ); |
2482 |
|
|
} |
2483 |
|
|
} |
2484 |
|
|
|
2485 |
|
|
return dbid; |
2486 |
|
|
|
2487 |
|
|
cleanup: |
2488 |
|
|
/* cleanup ... */ |
2489 |
|
|
db->flags = 0; |
2490 |
|
|
closefiles( &db->oxi.fd, 1 ); |
2491 |
|
|
closefiles( &txtfd, 1 ); |
2492 |
|
|
closefiles( db->inv, INV_FILES ); |
2493 |
|
|
closefiles( db->mst, MST_FILES ); |
2494 |
|
|
return 0 > ret ? ret : ret ? -ret : -1; |
2495 |
|
|
} /* ldb_open */ |
2496 |
|
|
|
2497 |
|
|
|
2498 |
|
|
/* ************************************************************ |
2499 |
|
|
package data |
2500 |
|
|
*/ |
2501 |
|
|
|
2502 |
|
|
|
2503 |
|
|
|
2504 |
|
|
/* ************************************************************ |
2505 |
|
|
package functions |
2506 |
|
|
*/ |
2507 |
|
|
|
2508 |
|
|
int *ldb_readRecAtOff ( int dbid, lxref off, int *nxtoff ) |
2509 |
|
|
{ |
2510 |
|
|
int *rec; |
2511 |
|
|
LDb *db = getDb( dbid ); |
2512 |
|
|
if ( ! db ) { |
2513 |
|
|
log_msg( LOG_ERROR, "\tat ldb_readRecAtOff" ); |
2514 |
|
|
return 0; |
2515 |
|
|
} |
2516 |
|
|
if ( 0 == off ) |
2517 |
|
|
off = 64; |
2518 |
|
|
rec = getMfr( db, off, nxtoff ); |
2519 |
|
|
if ( ! rec ) |
2520 |
|
|
return 0; |
2521 |
|
|
LOG_DBG( LOG_VERBOSE, "db %d off %d: got %hd bytes", |
2522 |
|
|
dbid, off, !rec ? -1 : rec[LMFR_RECL] ); |
2523 |
|
|
return rec; |
2524 |
|
|
} /* ldb_readRecAtOff */ |
2525 |
|
|
|
2526 |
|
|
|
2527 |
|
|
|
2528 |
|
|
int ldb_search ( int dbid, const char *key, LdbPost *post, Rec *rec ) |
2529 |
|
|
{ |
2530 |
|
|
LDb *db = getDb( dbid ); |
2531 |
|
|
Key k; |
2532 |
|
|
|
2533 |
|
|
if ( ! db ) |
2534 |
|
|
return -ERR_BADF; |
2535 |
|
|
if ( post ) { /* prepare for postings */ |
2536 |
|
|
if ( ! post->len ) |
2537 |
|
|
post->len = sizeof(post->p)/sizeof(post->p[0]); /* standard length */ |
2538 |
|
|
if ( LDB_NOT & post->mode ) |
2539 |
|
|
post->mode |= LDB_AND; |
2540 |
|
|
} |
2541 |
|
|
if ( DB_INVOPEN & db->flags ) |
2542 |
|
|
return search( db, key, post, rec, 0 ); |
2543 |
|
|
if ( !(DB_LBTOPEN & db->flags) ) |
2544 |
|
|
return -ERR_BADF; |
2545 |
|
|
if ( db->oxi.bat ) |
2546 |
|
|
return -ERR_BUSY; |
2547 |
|
|
memset( &k, 0, sizeof(k) ); |
2548 |
|
|
if ( ! key ) { |
2549 |
|
|
k.byt[0] = '$'; |
2550 |
|
|
k.len = 1; |
2551 |
|
|
} else { |
2552 |
|
|
unsigned char *uk = (unsigned char*)key; |
2553 |
|
|
int l = strlen( key ); |
2554 |
|
|
if ( l > 255 ) |
2555 |
|
|
l = 255; |
2556 |
|
|
k.len = (unsigned char)l; |
2557 |
|
|
while ( l-- ) |
2558 |
|
|
k.byt[l] = db->ctab[LCS_UCASE].c[ uk[l] ]; |
2559 |
|
|
} |
2560 |
|
|
return lbt_search( &db->oxi, &k, post, rec ); |
2561 |
|
|
} /* ldb_search */ |
2562 |
|
|
|
2563 |
|
|
|
2564 |
|
|
int ldb_p2s ( Set *set, LdbPost *post ) |
2565 |
|
|
{ |
2566 |
|
|
int *s = set->id; |
2567 |
|
|
int last=0, max = set->len; |
2568 |
|
|
int i; |
2569 |
|
|
set->len = 0; |
2570 |
|
|
if ( ! max ) |
2571 |
|
|
max = OPENISIS_SETLEN; |
2572 |
|
|
max--; |
2573 |
|
|
if ( !post->fil ) |
2574 |
|
|
return 0L; |
2575 |
|
|
s[0] = LDBP_ROW(post->p); |
2576 |
|
|
for ( i=1; i<post->fil && last < max; i++ ) { |
2577 |
|
|
int row = LDBP_ROW(post->p+i); |
2578 |
|
|
if ( s[last] != row ) |
2579 |
|
|
s[++last] = row; |
2580 |
|
|
} |
2581 |
|
|
return set->len = last+1; |
2582 |
|
|
} /* ldb_p2s */ |
2583 |
|
|
|
2584 |
|
|
|
2585 |
|
|
#if 0 |
2586 |
|
|
LcsTab *ldb_tabs( int dbid ) |
2587 |
|
|
{ |
2588 |
|
|
LDb *db = getDb( dbid ); |
2589 |
|
|
return ! db ? 0 : db->ctab; |
2590 |
|
|
} /* ldb_tabs */ |
2591 |
|
|
#endif |
2592 |
|
|
|
2593 |
|
|
|
2594 |
|
|
Db *ldb_getdb (int dbid) { |
2595 |
|
|
LDb *db = getDb (dbid); |
2596 |
|
|
return db ? &db->head : 0; |
2597 |
|
|
} |
2598 |
|
|
|
2599 |
|
|
/* ************************************************************ |
2600 |
|
|
public functions |
2601 |
|
|
*/ |
2602 |
|
|
int dMaxId ( int dbid ) |
2603 |
|
|
{ |
2604 |
|
|
LDb *db = getDb( dbid ); |
2605 |
|
|
if ( ! db ) |
2606 |
|
|
return -ERR_BADF; |
2607 |
|
|
return db->mfc[LMFC_NMFN] - 1; |
2608 |
|
|
} /* dMaxId */ |
2609 |
|
|
|
2610 |
|
|
|
2611 |
|
|
Raw *dRaw ( int dbid, int rowid ) |
2612 |
|
|
{ |
2613 |
|
|
int off; |
2614 |
|
|
int *rec = 0; |
2615 |
|
|
LDb *db; |
2616 |
|
|
|
2617 |
|
|
if ( LIO_LOCK() ) return 0; |
2618 |
|
|
db = getDb( dbid ); |
2619 |
|
|
if ( ! db ) { |
2620 |
|
|
log_msg( LOG_ERROR, "\tat openIsisReadRaw %d", rowid ); |
2621 |
|
|
goto done; |
2622 |
|
|
} |
2623 |
|
|
off = getOff( db, rowid, 0 ); |
2624 |
|
|
log_msg( LOG_INFO, "found xref 0x%08x for %d", off, rowid ); |
2625 |
|
|
if ( 0 >= off ) { |
2626 |
|
|
log_msg( LOG_INFO, "found deleted xref 0x%08x for %d", off, rowid ); |
2627 |
|
|
goto done; |
2628 |
|
|
} |
2629 |
|
|
rec = getMfr( db, off, 0 ); |
2630 |
|
|
if ( ! rec ) { |
2631 |
|
|
log_msg( LOG_WARN, "\tno record at %d rowid %d", off, rowid ); |
2632 |
|
|
goto done; |
2633 |
|
|
} |
2634 |
|
|
LOG_DBG( LOG_VERBOSE, "db %d row %d: got %hd bytes", |
2635 |
|
|
dbid, rowid, !rec ? -1 : rec[LMFR_RECL] ); |
2636 |
|
|
if ( rec[LMFR_MFN] != rowid ) { |
2637 |
|
|
log_msg( LOG_ERROR, "got mfn %d expected %d", rec[LMFR_MFN], rowid ); |
2638 |
|
|
mFree( rec ); |
2639 |
|
|
rec = 0; |
2640 |
|
|
goto done; |
2641 |
|
|
} |
2642 |
|
|
done: |
2643 |
|
|
(void)LIO_RELE(); |
2644 |
|
|
return (Raw*)rec; |
2645 |
|
|
} /* dRaw */ |
2646 |
|
|
|
2647 |
|
|
|
2648 |
|
|
Rec *dRead ( int dbid, int rowid ) |
2649 |
|
|
{ |
2650 |
|
|
LDb *db = getDb( dbid ); |
2651 |
|
|
Rec *r; |
2652 |
|
|
if ( DB_TXTOPEN & db->flags ) |
2653 |
|
|
return dText( db, rowid, 0 ); |
2654 |
|
|
if ( (r = (Rec *) dRaw( dbid, rowid )) ) { |
2655 |
|
|
char * base = (char*)r; |
2656 |
|
|
Field *f = r->field; |
2657 |
|
|
int i = r->len; |
2658 |
|
|
for ( ; i--; f++ ) |
2659 |
|
|
f->val = base + (int)f->val; |
2660 |
|
|
assert( RECOK( r ) ); |
2661 |
|
|
} |
2662 |
|
|
return r; |
2663 |
|
|
} /* dRead */ |
2664 |
|
|
|
2665 |
|
|
|
2666 |
|
|
int dWritex ( int dbid, Rec *rec, Rec *idx ) |
2667 |
|
|
{ |
2668 |
|
|
LDb *db = getDb( dbid ); |
2669 |
|
|
int ret = 0; |
2670 |
|
|
|
2671 |
|
|
if ( ! db ) |
2672 |
|
|
return -ERR_BADF; |
2673 |
|
|
if ( !(DB_WRITABLE & db->flags) ) |
2674 |
|
|
return log_msg( ERR_INVAL, "db %d not writable", dbid ); |
2675 |
|
|
if ( rec && (ret = |
2676 |
|
|
DB_TXTOPEN & db->flags ? pText( db, rec, 0 ) : putRec( db, rec ) |
2677 |
|
|
) ) |
2678 |
|
|
return ret; |
2679 |
|
|
if ( idx ) { |
2680 |
|
|
const unsigned char *const uc = db->ctab[LCS_UCASE].c; |
2681 |
|
|
int delmode = 0; |
2682 |
|
|
int tag = -1; |
2683 |
|
|
int mode = 'f'; /* 'w', 's' */ |
2684 |
|
|
int occ = 0; |
2685 |
|
|
int pos = 0; |
2686 |
|
|
int cut = 30; |
2687 |
|
|
int mfn = rec ? rec->rowid : 0; |
2688 |
|
|
Hit h; |
2689 |
|
|
Key k; |
2690 |
|
|
Field *f = idx->field, *last = f + idx->len - 1; |
2691 |
|
|
|
2692 |
|
|
for ( ; f <= last; f++ ) { |
2693 |
|
|
const char *val = f->val; |
2694 |
|
|
int len = f->len; |
2695 |
|
|
int del = delmode; |
2696 |
|
|
|
2697 |
|
|
k.val.len = 0; |
2698 |
|
|
switch ( f->tag ) { |
2699 |
|
|
case XCTL: { /* index cmd [opt] */ |
2700 |
|
|
const char *cmd = val, *e = val + len; |
2701 |
|
|
int cmdlen, opt = 0, haveopt; |
2702 |
|
|
while ( val < e && 64 < *val ) /* eat ASCII letters */ |
2703 |
|
|
val++; |
2704 |
|
|
cmdlen = val - cmd; |
2705 |
|
|
if ( val < e && (TAB == *val || ' ' == *val) ) |
2706 |
|
|
val++; |
2707 |
|
|
haveopt = val < e && a2il( val, e-val, &opt ); |
2708 |
|
|
if ( ! cmdlen ) { |
2709 |
|
|
cut = haveopt ? opt : 30; |
2710 |
|
|
continue; |
2711 |
|
|
} |
2712 |
|
|
switch (*cmd) { |
2713 |
|
|
case 'f': /* fields */ |
2714 |
|
|
mode = 'f'; |
2715 |
|
|
occ = opt; |
2716 |
|
|
pos = 0; |
2717 |
|
|
continue; |
2718 |
|
|
case 'w': /* words */ |
2719 |
|
|
mode = 'w'; |
2720 |
|
|
pos = opt; |
2721 |
|
|
continue; |
2722 |
|
|
case 's': /* split */ |
2723 |
|
|
mode = 's'; |
2724 |
|
|
pos = opt; |
2725 |
|
|
continue; |
2726 |
|
|
case 'a': /* add */ |
2727 |
|
|
delmode = 0; |
2728 |
|
|
occ = pos = 0; |
2729 |
|
|
continue; |
2730 |
|
|
case 'd': /* del */ |
2731 |
|
|
delmode = 1; |
2732 |
|
|
occ = pos = 0; |
2733 |
|
|
continue; |
2734 |
|
|
case 'm': /* mfn */ |
2735 |
|
|
mfn = opt; |
2736 |
|
|
occ = pos = 0; |
2737 |
|
|
continue; |
2738 |
|
|
} |
2739 |
|
|
return log_msg( ERR_INVAL, "bad index control '%.*s'", cmdlen, cmd ); |
2740 |
|
|
} |
2741 |
|
|
case XHIT: { |
2742 |
|
|
int i = 0, v[5], *pv = v; |
2743 |
|
|
const char *e = val + len; |
2744 |
|
|
if ( len ) |
2745 |
|
|
switch (*val) { |
2746 |
|
|
case '+': del = 0; val++; break; |
2747 |
|
|
case '-': del = 1; val++; break; |
2748 |
|
|
} |
2749 |
|
|
for ( ; val < e && i<5; i++ ) { |
2750 |
|
|
int dig = a2il( val, e-val, v+i ); |
2751 |
|
|
val += dig; |
2752 |
|
|
if ( val >= e || TAB == *val ) |
2753 |
|
|
break; |
2754 |
|
|
if ( '.' != *val ) |
2755 |
|
|
return log_msg( ERR_INVAL, |
2756 |
|
|
"bad HIT '%.*s' after %d", e-val, val, v[i] ); |
2757 |
|
|
val++; |
2758 |
|
|
} |
2759 |
|
|
h.dbn = 0; |
2760 |
|
|
h.mfn = mfn; |
2761 |
|
|
h.pos = pos; |
2762 |
|
|
h.occ = occ; |
2763 |
|
|
h.tag = tag; |
2764 |
|
|
switch ( i ) { |
2765 |
|
|
case 5: h.dbn = (unsigned short)*pv++; |
2766 |
|
|
case 4: h.mfn = (unsigned)*pv++; |
2767 |
|
|
case 3: h.pos = (unsigned short)pv[2]; |
2768 |
|
|
case 2: h.occ = (unsigned short)pv[1]; |
2769 |
|
|
case 1: h.tag = (unsigned short)pv[0]; |
2770 |
|
|
/* case 0: ! f->len */ |
2771 |
|
|
} |
2772 |
|
|
if ( val < e && TAB == *val ) |
2773 |
|
|
val++; |
2774 |
|
|
len = e - val; |
2775 |
|
|
} break; /* case XHIT */ |
2776 |
|
|
case XFST: |
2777 |
|
|
return log_msg( ERR_IDIOT, "sorry, XFST not implemented" ); |
2778 |
|
|
#if 0 |
2779 |
|
|
case XADD: /* binary key */ |
2780 |
|
|
/* if ( f->len < db->oxi.vsz ) |
2781 |
|
|
memset( k.val.byt, 0, db->oxi.vsz - f->len ); |
2782 |
|
|
*/ |
2783 |
|
|
memcpy( k.val.byt |
2784 |
|
|
+ (f->len < (int)db->oxi.vsz ? (int)db->oxi.vsz - f->len : 0), |
2785 |
|
|
f->val, f->len > (int)db->oxi.vsz ? (int)db->oxi.vsz : f->len ); |
2786 |
|
|
k.val.len = db->oxi.vsz; |
2787 |
|
|
break; |
2788 |
|
|
#endif |
2789 |
|
|
default: |
2790 |
|
|
if ( 0 > f->tag ) |
2791 |
|
|
return log_msg( ERR_INVAL, "bad index control tag %d", f->tag ); |
2792 |
|
|
switch ( mode ) { /* check for tag change */ |
2793 |
|
|
case 'f': |
2794 |
|
|
if ( tag == f->tag ) |
2795 |
|
|
occ++; |
2796 |
|
|
else |
2797 |
|
|
occ = 0; |
2798 |
|
|
break; |
2799 |
|
|
case 'w': |
2800 |
|
|
if ( tag == f->tag ) |
2801 |
|
|
pos++; |
2802 |
|
|
else |
2803 |
|
|
pos = 0; |
2804 |
|
|
break; |
2805 |
|
|
} |
2806 |
|
|
tag = f->tag; |
2807 |
|
|
h.dbn = 0; |
2808 |
|
|
h.mfn = mfn; |
2809 |
|
|
h.pos = pos; |
2810 |
|
|
h.occ = occ; |
2811 |
|
|
h.tag = f->tag; |
2812 |
|
|
} |
2813 |
|
|
if ( ! k.val.len ) { /* not ADD/DEL: use hit, val */ |
2814 |
|
|
unsigned char *dst = k.byt; |
2815 |
|
|
const unsigned char *src = (const unsigned char *)val; |
2816 |
|
|
if ( cut < len ) |
2817 |
|
|
len = cut; |
2818 |
|
|
k.len = (unsigned char)len; |
2819 |
|
|
while ( len-- ) |
2820 |
|
|
*dst++ = uc[ *src++ ]; |
2821 |
|
|
cXMkVal( &db->oxi, &k.val, &h ); |
2822 |
|
|
LOG_DBG( LOG_DEBUG, "#%d %c key '%.*s' hit %d.%d.%d.%d.%d", |
2823 |
|
|
f - idx->field, del ? '-' : '+', k.len, k.byt, |
2824 |
|
|
h.dbn, h.mfn, h.tag, h.occ, h.pos ); |
2825 |
|
|
} |
2826 |
|
|
ret = del ? lbt_del( &db->oxi, &k ) : lbt_add( &db->oxi, &k ); |
2827 |
|
|
} |
2828 |
|
|
} |
2829 |
|
|
return ret; |
2830 |
|
|
} /* dWritex */ |
2831 |
|
|
|
2832 |
|
|
|
2833 |
|
|
int dWrite ( int dbid, Rec *rec ) |
2834 |
|
|
{ |
2835 |
|
|
/* TODO: use FST lines as idx */ |
2836 |
|
|
return dWritex( dbid, rec, 0 ); |
2837 |
|
|
} /* dWrite */ |
2838 |
|
|
|
2839 |
|
|
|
2840 |
|
|
Rec* dTerm ( Rec *rec, int dbid, const char *key ) |
2841 |
|
|
{ |
2842 |
|
|
return 0 > ldb_search( dbid, key, 0, rec ) ? 0 : rec; |
2843 |
|
|
} /* dTerm */ |
2844 |
|
|
|
2845 |
|
|
|
2846 |
|
|
int dXLoop ( int dbid, DXLoop *l ) |
2847 |
|
|
{ |
2848 |
|
|
LDb *db = getDb( dbid ); |
2849 |
|
|
|
2850 |
|
|
if ( !db ) |
2851 |
|
|
return -ERR_BADF; |
2852 |
|
|
if ( OPENISIS_IDXTRAD & l->flg ) { |
2853 |
|
|
if ( !(db->flags & DB_INVOPEN) ) |
2854 |
|
|
return -ERR_BUSY; |
2855 |
|
|
return search( db, 0, 0, 0, l ); |
2856 |
|
|
} |
2857 |
|
|
if ( !(db->flags & DB_LBTOPEN) || db->oxi.bat ) |
2858 |
|
|
return -ERR_BUSY; |
2859 |
|
|
return lbt_loop( & db->oxi, l ); |
2860 |
|
|
} /* dXLoop */ |
2861 |
|
|
|
2862 |
|
|
|
2863 |
|
|
|
2864 |
|
|
int cInit ( int argc, const char **argv, CLockFunc lockfunc ) |
2865 |
|
|
{ |
2866 |
|
|
(void)argc; (void)argv; |
2867 |
|
|
cOpen( 0 ); |
2868 |
|
|
if ( lockfunc ) |
2869 |
|
|
lio_lock = lockfunc; |
2870 |
|
|
return 0; |
2871 |
|
|
} |
2872 |
|
|
|
2873 |
|
|
|
2874 |
|
|
Db* cDOpen (const char *dbname, Rec *dbpar, Rec *syspar, Fdt *fdt) { |
2875 |
|
|
int dbid; |
2876 |
|
|
if ( ! init ) |
2877 |
|
|
cOpen( 0 ); |
2878 |
|
|
dbid = ldb_open (dbname, dbpar, syspar, fdt); |
2879 |
|
|
if (0 <= dbid) { |
2880 |
|
|
return &dbs[dbid].head; |
2881 |
|
|
} |
2882 |
|
|
return 0; |
2883 |
|
|
} |
2884 |
|
|
|
2885 |
|
|
int cDOpenv ( const char *dbname, const char **argv, int argc ) |
2886 |
|
|
{ |
2887 |
|
|
Rec *dbpar = 0; |
2888 |
|
|
int rt; |
2889 |
|
|
if ( ! init ) |
2890 |
|
|
cOpen( 0 ); |
2891 |
|
|
if (argc) { |
2892 |
|
|
dbpar = rSet (0, RARGV | RFDT | RNOC | RIGN | argc, |
2893 |
|
|
openIsisFdtDbpar, argv); |
2894 |
|
|
} |
2895 |
|
|
rt = ldb_open (dbname, dbpar, 0, 0); |
2896 |
|
|
if (dbpar) { |
2897 |
|
|
mFree (dbpar); |
2898 |
|
|
} |
2899 |
|
|
return rt; |
2900 |
|
|
} |
2901 |
|
|
|
2902 |
|
|
|
2903 |
|
|
int cDClose ( int dbid ) |
2904 |
|
|
{ |
2905 |
|
|
LDb *db = getDb( dbid ); |
2906 |
|
|
if ( ! db ) |
2907 |
|
|
return -ERR_BADF; |
2908 |
|
|
if ( LIO_LOCK() ) return -ERR_BUSY; |
2909 |
|
|
if ( DB_MODIFIED == ((DB_MODIFIED|DB_TXTOPEN) & db->flags) ) { |
2910 |
|
|
/* write back the MF control */ |
2911 |
|
|
Mfc mfc; |
2912 |
|
|
/* if ( 498 < (db->mflen & 511) ) db->mflen = ~511 & (db->mflen + 14); */ |
2913 |
|
|
mfc.ctlm = rvi( db->mfc[LMFC_CTLM] ); |
2914 |
|
|
mfc.nmfn = rvi( db->mfc[LMFC_NMFN] ); |
2915 |
|
|
mfc.nmfb = rvi( 1 + (db->mflen >> 9) ); |
2916 |
|
|
mfc.nmfp = rvs( 511 & db->mflen ); |
2917 |
|
|
mfc.type = rvs( db->mfc[LMFC_TYPE] ); |
2918 |
|
|
mfc.rcnt = rvi( db->mfc[LMFC_RCNT] ); |
2919 |
|
|
mfc.mfx1 = rvi( db->mfc[LMFC_MFX1] ); |
2920 |
|
|
mfc.mfx2 = rvi( db->mfc[LMFC_MFX2] ); |
2921 |
|
|
mfc.mfx3 = rvi( db->mfc[LMFC_MFX3] ); |
2922 |
|
|
if ( sizeof(mfc) != lio_pwrite( &db->mst[MST_MST], &mfc, sizeof(mfc), 0) ) |
2923 |
|
|
log_msg( ERR_TRASH, "could not write MST header" ); |
2924 |
|
|
} |
2925 |
|
|
if ( db->mmap ) { |
2926 |
|
|
if ( (DB_MODIFIED|DB_TXTOPEN) == ((DB_MODIFIED|DB_TXTOPEN) & db->flags) ) |
2927 |
|
|
memcpy( db->mmap, ISIX, 4 ); /* force newer mtime on proper close */ |
2928 |
|
|
lio_mmap( 0, (void**)&db->mmap, db->mmlen*db->ptrl ); |
2929 |
|
|
} |
2930 |
|
|
db->mmlen = 0; |
2931 |
|
|
closefiles( db->mst, MST_FILES ); |
2932 |
|
|
if ( DB_INVOPEN & db->flags ) |
2933 |
|
|
closefiles( db->inv, INV_FILES ); |
2934 |
|
|
if ( DB_LBTOPEN & db->flags ) |
2935 |
|
|
lbt_close( &db->oxi ); |
2936 |
|
|
db->flags = 0L; |
2937 |
|
|
if ( db->path ) mFree( (char*)db->path ); |
2938 |
|
|
if (db->head.cfg) mFree (db->head.cfg); |
2939 |
|
|
if (db->head.fdt) fFree (db->head.fdt); |
2940 |
|
|
memset( db, 0, sizeof(db) ); |
2941 |
|
|
(void)LIO_RELE(); |
2942 |
|
|
return 0; |
2943 |
|
|
} /* cDClose */ |
2944 |
|
|
|
2945 |
|
|
|
2946 |
|
|
int cDCheck ( int dbid, int flags ) |
2947 |
|
|
{ |
2948 |
|
|
static char dot = '.'; |
2949 |
|
|
int *r; |
2950 |
|
|
LDb *db = getDb( dbid ); |
2951 |
|
|
int nxtoff = 64, off; |
2952 |
|
|
|
2953 |
|
|
if ( ! db ) |
2954 |
|
|
return -ERR_BADF; |
2955 |
|
|
(void)flags; |
2956 |
|
|
do { |
2957 |
|
|
lio_write( &lio_out, &dot, 1 ); |
2958 |
|
|
if ( (r = ldb_readRecAtOff(dbid,off=nxtoff,&nxtoff)) ) { |
2959 |
|
|
int o = getOff( db, r[LMFR_MFN], 0 ); |
2960 |
|
|
if ( o != off ) { |
2961 |
|
|
log_msg( LOG_WARN, "mfn %d xrf %d != real %d\n", |
2962 |
|
|
r[LMFR_MFN], o, off ); |
2963 |
|
|
} |
2964 |
|
|
mFree( r ); |
2965 |
|
|
} |
2966 |
|
|
} while ( 0 < nxtoff ); |
2967 |
|
|
return 0; |
2968 |
|
|
} /* cDCheck */ |
2969 |
|
|
|
2970 |
|
|
|
2971 |
|
|
OpenIsisIdx *cXOpen ( int dbid, int mode ) |
2972 |
|
|
{ |
2973 |
|
|
LDb *db = getDb( dbid ); |
2974 |
|
|
if ( !db |
2975 |
|
|
|| !(db->flags & DB_LBTOPEN) |
2976 |
|
|
|| !(db->oxi.flg & LBT_WRITE) /* may be writable if db is not */ |
2977 |
|
|
|| db->oxi.bat |
2978 |
|
|
/* |
2979 |
|
|
preliminary undocumented feature: |
2980 |
|
|
mode -1 gives direct access in non-batch mode |
2981 |
|
|
*/ |
2982 |
|
|
|| (0 <= mode && lbt_batch( & db->oxi, (unsigned char)mode )) |
2983 |
|
|
) |
2984 |
|
|
return 0; |
2985 |
|
|
return & db->oxi; |
2986 |
|
|
} /* cXOpen */ |