1 |
/* |
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: luti.c,v 1.48 2003/06/23 14:47:35 kripke Exp $ |
25 |
utilities |
26 |
*/ |
27 |
|
28 |
#include <string.h> /* strlen */ |
29 |
|
30 |
#include "openisis.h" |
31 |
#include "luti.h" |
32 |
|
33 |
|
34 |
/* ************************************************************ |
35 |
private types |
36 |
*/ |
37 |
|
38 |
typedef struct { |
39 |
char *name; |
40 |
int id; |
41 |
} LutiLTEnt; |
42 |
|
43 |
typedef struct OpenIsisLT { |
44 |
LutiLTEnt *arr; |
45 |
int siz; /* malloc'ed */ |
46 |
int num; /* used */ |
47 |
} LutiLTNod; |
48 |
|
49 |
#define LT_NUM 27 |
50 |
#define LT_SIZE (LT_NUM * sizeof (LutiLTNod)) |
51 |
|
52 |
#define LTIDX(name) ( \ |
53 |
('a' <= *(name) && 'z' >= *(name)) ? *(name) - 'a' : ( \ |
54 |
('A' <= *(name) && 'Z' >= *(name)) ? *(name) - 'A' : 26 \ |
55 |
)) |
56 |
|
57 |
#define LT_INCR 2 |
58 |
|
59 |
/* ************************************************************ |
60 |
private data |
61 |
*/ |
62 |
|
63 |
|
64 |
|
65 |
/* ************************************************************ |
66 |
private functions |
67 |
*/ |
68 |
|
69 |
|
70 |
/* ************************************************************ |
71 |
package data |
72 |
*/ |
73 |
const char luti_hex[16] = { |
74 |
'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' |
75 |
}; |
76 |
|
77 |
/* ************************************************************ |
78 |
package functions |
79 |
*/ |
80 |
|
81 |
void log_rec ( |
82 |
int level, Rec *rec, const char *msg, const char *delim |
83 |
) { |
84 |
Field *F; |
85 |
int ch = 2; |
86 |
if (0 > level) { |
87 |
level = -level; |
88 |
ch = 1; |
89 |
} |
90 |
if ((unsigned)level > log_lev) { |
91 |
return; |
92 |
} |
93 |
if (msg && *msg) { |
94 |
sMsg (ch, msg); |
95 |
} |
96 |
sMsg (ch, "%p", rec); |
97 |
if (rec) { |
98 |
if (! delim) { |
99 |
delim = "; "; |
100 |
} |
101 |
sMsg (ch, " #f=%d", (int)rec->len); |
102 |
for (F = rec->field; rec->len > F - rec->field; ++F) { |
103 |
sMsg (ch, delim); |
104 |
sMsg (ch, "%d", (int)F->tag); |
105 |
sMsg (ch, " %.*s", (int)F->len, F->val); |
106 |
} |
107 |
} |
108 |
sMsg (ch, "\n"); |
109 |
} |
110 |
|
111 |
|
112 |
int lprint ( void *buf, int i ) |
113 |
{ |
114 |
char *c = buf; |
115 |
if ( 0 > i ) { |
116 |
*c++ = '-'; |
117 |
i = -i; |
118 |
} |
119 |
if ( ! i ) |
120 |
*c++ = '0'; |
121 |
else { |
122 |
int dig = 0, j=i; |
123 |
char *p; |
124 |
for ( ; j; j /= 10 ) |
125 |
dig++; |
126 |
p = c += dig; |
127 |
for ( ; i; i /= 10 ) |
128 |
*--p = '0' + i % 10; |
129 |
} |
130 |
*c = 0; |
131 |
return c - (char*)buf; |
132 |
} /* lprint */ |
133 |
|
134 |
|
135 |
int a2il ( const char *p, int l, int *res ) |
136 |
{ |
137 |
const char *s = p; |
138 |
const char *e = p + (0<=l ? l : (int)strlen(p)); |
139 |
char op = 0; |
140 |
int ret = 0; |
141 |
while ( p < e && ' ' >= (unsigned char)*p ) /* skip white */ |
142 |
p++; |
143 |
if ( p < e ) |
144 |
switch ( *p ) { |
145 |
case '-': |
146 |
if ( p+1 == e ) ret = 1; /* so sole '-' is -1 */ |
147 |
case '~': /* sole ~ is ~0 */ |
148 |
case '+': |
149 |
op = *p++; |
150 |
} |
151 |
if ( p < e && '0' == *p && ++p < e && 'x' == *p ) { |
152 |
while ( ++p < e ) { |
153 |
int v = *p; |
154 |
if ( '0' <= v && v <= '9' ) |
155 |
v -= '0'; |
156 |
else if ( 'a' <= v && v <= 'f' ) |
157 |
v -= 'a'-10; |
158 |
else if ( 'A' <= v && v <= 'F' ) |
159 |
v -= 'A'-10; |
160 |
else |
161 |
break; |
162 |
ret = (ret<<4) + v; |
163 |
} |
164 |
} else { |
165 |
while ( p < e && '0' <= *p && *p <= '9' ) |
166 |
ret = 10*ret + *p++ - '0'; |
167 |
} |
168 |
if (0 != res) |
169 |
switch (op) { |
170 |
case '-': *res = -ret; break; |
171 |
case '~': *res = ~ret; break; |
172 |
default: *res = ret; |
173 |
} |
174 |
return p - s; |
175 |
} /* a2il */ |
176 |
|
177 |
int a2i ( const char *p, int l ) { |
178 |
int res; |
179 |
a2il (p, l, &res); |
180 |
return res; |
181 |
} |
182 |
|
183 |
int a2id ( const char *p, int l, int dflt ) { |
184 |
int res; |
185 |
if (0 > l) { |
186 |
l = (int) strlen (p); |
187 |
} |
188 |
if (l != a2il (p, l, &res)) { |
189 |
return dflt; |
190 |
} |
191 |
return res; |
192 |
} |
193 |
|
194 |
|
195 |
int i2a ( char *p, int i ) |
196 |
{ |
197 |
if ( i ) { |
198 |
int min, dig = 0, j; |
199 |
if ( 0 < i ) |
200 |
min = 0; |
201 |
else { |
202 |
min = 1; |
203 |
*p++ = '-'; |
204 |
i = -i; |
205 |
} |
206 |
for ( j=i; j; j/=10 ) |
207 |
dig++; |
208 |
for ( *(p+=dig)=0; i; i/=10 ) |
209 |
*--p = '0' + (i % 10); |
210 |
return min+dig; |
211 |
} |
212 |
*p++ = '0'; |
213 |
*p = 0; |
214 |
return 1; |
215 |
} /* i2a */ |
216 |
|
217 |
|
218 |
int u2a ( char *p, unsigned u ) |
219 |
{ |
220 |
if ( u ) { |
221 |
int dig = 0, j; |
222 |
for ( j=u; j; j/=10 ) |
223 |
dig++; |
224 |
for ( *(p+=dig)=0; u; u/=10 ) |
225 |
*--p = '0' + (u % 10); |
226 |
return dig; |
227 |
} |
228 |
*p++ = '0'; |
229 |
*p = 0; |
230 |
return 1; |
231 |
} /* u2a */ |
232 |
|
233 |
|
234 |
LutiLT luti_ltnew () { |
235 |
LutiLT res = (LutiLT) mAlloc (LT_SIZE); |
236 |
return res; |
237 |
} |
238 |
|
239 |
void luti_ltdel (LutiLT lt) { |
240 |
LutiLTNod *N; |
241 |
LutiLTEnt *E; |
242 |
if (lt) { |
243 |
for (N = lt + LT_NUM; --N >= lt; ) { |
244 |
if (N->arr) { |
245 |
for (E = N->arr + N->num; --E >= N->arr; ) { |
246 |
if (E->name) { |
247 |
mFree (E->name); |
248 |
} |
249 |
} |
250 |
mFree (N->arr); |
251 |
} |
252 |
} |
253 |
mFree (lt); |
254 |
} |
255 |
} |
256 |
|
257 |
void luti_ltadd (LutiLT lt, const char *name, int id) { |
258 |
if (0 != lt) { |
259 |
LutiLTNod *N; |
260 |
LutiLTEnt *E; |
261 |
int idx; |
262 |
if (!name) { |
263 |
name = ""; |
264 |
} |
265 |
idx = LTIDX (name); |
266 |
N = lt + idx; |
267 |
if (N->siz == N->num) { |
268 |
E = mAlloc ( (LT_INCR + N->siz) * sizeof (LutiLTEnt)); |
269 |
if (N->siz) { |
270 |
memcpy (E, N->arr, N->siz * sizeof (LutiLTEnt)); |
271 |
mFree (N->arr); |
272 |
} |
273 |
N->arr = E; |
274 |
N->siz += LT_INCR; |
275 |
} |
276 |
N->arr[N->num].name = mDup (name, -1); |
277 |
N->arr[N->num].id = id; |
278 |
++N->num; |
279 |
} |
280 |
} |
281 |
|
282 |
int luti_ltget (LutiLT lt, const char *name) { |
283 |
if (0 != lt) { |
284 |
LutiLTNod *N; |
285 |
LutiLTEnt *E; |
286 |
int idx; |
287 |
if (!name) { |
288 |
name = ""; |
289 |
} |
290 |
idx = LTIDX (name); |
291 |
N = lt + idx; |
292 |
if (N->num) { |
293 |
for (E = N->arr + N->num; --E >= N->arr; ) { |
294 |
if (! strcmp (E->name, name)) { |
295 |
return E->id; |
296 |
} |
297 |
} |
298 |
} |
299 |
} |
300 |
return -1; |
301 |
} |
302 |
|
303 |
void luti_ltrmv (LutiLT lt, const char *name) { |
304 |
if (0 != lt) { |
305 |
LutiLTNod *N; |
306 |
LutiLTEnt *E; |
307 |
int idx; |
308 |
if (!name) { |
309 |
name = ""; |
310 |
} |
311 |
idx = LTIDX (name); |
312 |
N = lt + idx; |
313 |
if (N->num) { |
314 |
for (E = N->arr + N->num; --E >= N->arr; ) { |
315 |
if (! strcmp (E->name, name)) { |
316 |
int len = N->num - (E - N->arr) - 1; |
317 |
mFree (E->name); |
318 |
if (len) { |
319 |
memmove (E, E + 1, len * sizeof (LutiLTEnt)); |
320 |
} |
321 |
--N->num; |
322 |
return; |
323 |
} |
324 |
} |
325 |
} |
326 |
log_msg (LOG_WARN, "luti_ltrmv: no such entry <%s>", name); |
327 |
} |
328 |
} |
329 |
|
330 |
static Rec *_unwrap ( Rec *env, int *pos, int tag, int dbid, int rdonly ) |
331 |
{ |
332 |
Field *F; |
333 |
Rec *rec = 0; |
334 |
int rlen, np; |
335 |
if (! env ) { |
336 |
return 0; |
337 |
} |
338 |
if (! pos) { |
339 |
np = 0; |
340 |
pos = &np; |
341 |
} |
342 |
F = rGet (env, tag, pos); |
343 |
if (!F) { |
344 |
return 0; |
345 |
} |
346 |
rlen = a2id (F->val, (int)F->len, -1); |
347 |
if (0 > rlen) { |
348 |
sMsg (LOG_ERROR, |
349 |
"luti_unwrap: illegal reclen %d, pos %d, tag %d", |
350 |
rlen, *pos, F->tag); |
351 |
return 0; |
352 |
} |
353 |
++F; |
354 |
if (env->len < *pos + rlen) { |
355 |
sMsg (LOG_ERROR, |
356 |
"luti_unwrap: illegal reclen %d, pos %d, tag %d, env %d", |
357 |
rlen, *pos, F->tag, env->len); |
358 |
return 0; |
359 |
} |
360 |
if (! rlen && ! rdonly) { |
361 |
OPENISIS_RSPACE (rec, 0, 0); |
362 |
if (0 == rec) { |
363 |
return 0; |
364 |
} |
365 |
} |
366 |
while (0 <= --rlen) { |
367 |
if (! rdonly) { |
368 |
RADD (rec, F->tag, F->val, F->len, !0); |
369 |
if (0 == rec) { |
370 |
return 0; |
371 |
} |
372 |
} |
373 |
++F; |
374 |
++(*pos); |
375 |
} |
376 |
if (rdonly) { |
377 |
return (Rec*)1; |
378 |
} |
379 |
rec->dbid = dbid; |
380 |
return rec; |
381 |
} |
382 |
|
383 |
Rec *luti_unwrap ( Rec *env, int *pos, int tag, int dbid) |
384 |
{ |
385 |
return _unwrap (env, pos, tag, dbid, 0); |
386 |
} |
387 |
|
388 |
Rec* luti_append (Rec *tgt, Rec *src) { |
389 |
Field *F, *E; |
390 |
if (!src) { |
391 |
return tgt; |
392 |
} |
393 |
E = src->field + src->len; |
394 |
for (F = src->field; tgt && E > F; ++F) { |
395 |
RADD (tgt, F->tag, F->val, F->len, !0); |
396 |
} |
397 |
return tgt; |
398 |
} |
399 |
|
400 |
Rec *luti_wrap (Rec *env, Rec *rec, int tag) { |
401 |
if (!rec) { |
402 |
return env; |
403 |
} |
404 |
env = rAddI (env, tag, rec->len, !0); |
405 |
return luti_append (env, rec); |
406 |
} |
407 |
|
408 |
int luti_ptrincr ( |
409 |
void *start, int *num, int incr, int siz, int maxn |
410 |
) { |
411 |
char **base = (char**)start; |
412 |
char *arr; |
413 |
int oldn = *num; |
414 |
if (0 < maxn && oldn >= maxn) { |
415 |
return -1; |
416 |
} |
417 |
arr = mAlloc ((oldn + incr) * siz); |
418 |
if (!arr) { |
419 |
return -1; |
420 |
} |
421 |
if (oldn) { |
422 |
memcpy (arr, *base, oldn * siz); |
423 |
mFree (*base); |
424 |
} |
425 |
memset (arr + oldn * siz, 0, incr * siz); |
426 |
*num += incr; |
427 |
*base = arr; |
428 |
return oldn; |
429 |
} |
430 |
|
431 |
void luti_free (void **arr, int num) { |
432 |
if (arr) { |
433 |
while (0 <= --num) { |
434 |
if (arr[num]) { |
435 |
mFree (arr[num]); |
436 |
} |
437 |
} |
438 |
mFree (arr); |
439 |
} |
440 |
} |
441 |
|
442 |
int luti_true (const char *str, int len) { |
443 |
if (! str || ! *str) { |
444 |
return -1; |
445 |
} |
446 |
if (0 > len) { |
447 |
len = strlen (str); |
448 |
} |
449 |
if (1 == len) { |
450 |
if ('0' == *str || |
451 |
'f' == *str || |
452 |
'F' == *str || |
453 |
'n' == *str || |
454 |
'N' == *str |
455 |
) { |
456 |
return 0; |
457 |
} |
458 |
if ('1' == *str || |
459 |
'y' == *str || |
460 |
'Y' == *str || |
461 |
't' == *str || |
462 |
'T' == *str |
463 |
) { |
464 |
return 1; |
465 |
} |
466 |
return -1; |
467 |
} |
468 |
if (! strncmp ("false", str, len) || |
469 |
! strncmp ("no", str, len)) { |
470 |
return 0; |
471 |
} |
472 |
if (! strncmp ("true", str, len) || |
473 |
! strncmp ("yes", str, len)) { |
474 |
return 1; |
475 |
} |
476 |
return -1; |
477 |
} |
478 |
|
479 |
/* ------------------------------------------------------------------------ |
480 |
* unwrap by path |
481 |
*/ |
482 |
|
483 |
Fdt* luti_fdt_from_rec (Rec *rec) { |
484 |
Db *dbh; |
485 |
if (rec && |
486 |
0 <= rec->dbid && |
487 |
(dbh = nDbById (rec->dbid)) |
488 |
) { |
489 |
return dbh->fdt; |
490 |
} |
491 |
return 0; |
492 |
} |
493 |
|
494 |
const char* luti_parse_path ( |
495 |
const char *path, const Fdt *fdt, Fd **fd, int *tag, int *occ |
496 |
) { |
497 |
Fd *_f = 0; |
498 |
int _t = -1; |
499 |
int _o = -1; |
500 |
int num, qsub; |
501 |
|
502 |
if (fd) { |
503 |
*fd = 0; |
504 |
} |
505 |
if (tag) { |
506 |
*tag = -1; |
507 |
} |
508 |
if (occ) { |
509 |
*occ = -1; |
510 |
} |
511 |
if (! path) { |
512 |
return 0; |
513 |
} |
514 |
|
515 |
if ('.' == *path |
516 |
|| ('-' == *path && (path[1]<'0' || '9'<path[1])) |
517 |
) { /* path or option style */ |
518 |
++path; |
519 |
} |
520 |
|
521 |
num = a2il (path, -1, &_t); |
522 |
if (num) { |
523 |
if (fdt) { |
524 |
_f = fById (fdt, _t, 0); |
525 |
} |
526 |
} |
527 |
else { |
528 |
char name[OPENISIS_FD_NAMELEN]; |
529 |
if (! fdt) { |
530 |
return 0; |
531 |
} |
532 |
for (num = 0; |
533 |
path[num] && '[' != path[num] && '(' != path[num]; |
534 |
++num) |
535 |
; |
536 |
if (num > OPENISIS_FD_NAMELEN - 1) { |
537 |
return 0; |
538 |
} |
539 |
strncpy (name, path, num) [num] = 0; |
540 |
_f = fByName ( fdt, name ); |
541 |
if (! _f) { |
542 |
return 0; |
543 |
} |
544 |
_t = _f->id; |
545 |
} |
546 |
path += num; |
547 |
|
548 |
if (tag) { |
549 |
*tag = _t; |
550 |
} |
551 |
if (fd) { |
552 |
*fd = _f; |
553 |
} |
554 |
|
555 |
if ('[' != *path && '(' != *path) { |
556 |
return path; |
557 |
} |
558 |
qsub = *path; |
559 |
|
560 |
++path; |
561 |
num = a2il (path, -1, &_o); |
562 |
if (! num || 0 > _o) { |
563 |
return 0; |
564 |
} |
565 |
path += num; |
566 |
if ((']' != *path && '[' == qsub) || (')' != *path && '(' == qsub)) { |
567 |
return 0; |
568 |
} |
569 |
|
570 |
if (occ) { |
571 |
*occ = _o; |
572 |
} |
573 |
return path + 1; |
574 |
} |
575 |
|
576 |
Rec* luti_getembed ( Rec *env, const char *path, const Fdt *fdt) |
577 |
{ |
578 |
Rec *res; |
579 |
int tag, occ, idx, pos; |
580 |
if ( ! env || ! path || ! *path) { |
581 |
return 0; |
582 |
} |
583 |
if (! fdt) { |
584 |
fdt = luti_fdt_from_rec (env); |
585 |
} |
586 |
path = luti_parse_path (path, fdt, 0, &tag, &occ); |
587 |
if (! path) { |
588 |
return 0; |
589 |
} |
590 |
res = 0; |
591 |
idx = pos = 0; |
592 |
while (1) { |
593 |
res = _unwrap (env, &pos, tag, -1, idx < occ); |
594 |
if (! res) { |
595 |
return 0; |
596 |
} |
597 |
if (idx >= occ) { |
598 |
break; |
599 |
} |
600 |
++idx; |
601 |
} |
602 |
if (*path) { |
603 |
Rec *r2 = luti_getembed (res, path, 0); |
604 |
mFree (res); |
605 |
return r2; |
606 |
} |
607 |
return res; |
608 |
} |
609 |
|
610 |
|
611 |
int lhash ( const char *str, int len ) |
612 |
{ |
613 |
int h = 0; |
614 |
while ( len-- ) |
615 |
h = 31*h + *str++; |
616 |
return 0<=h ? h : -h; |
617 |
} |
618 |
|
619 |
|
620 |
/* ************************************************************ |
621 |
public functions |
622 |
*/ |
623 |
|
624 |
char *toHtml ( const char *str, int len ) |
625 |
{ |
626 |
int l; |
627 |
char *p, *e, *ret; |
628 |
if ( ! str ) |
629 |
return 0; |
630 |
if ( 0 > len ) |
631 |
len = strlen(str); |
632 |
l = len + 1; |
633 |
for ( e = (p=(char*)str) + len; p<e; p++ ) |
634 |
switch ( *p ) { |
635 |
case '<': case '>': l += 3; break; |
636 |
case '&': l += 4; break; |
637 |
case '"': l += 5; break; |
638 |
/* do not replace ' w/ ' -- many clients don't grok it */ |
639 |
} |
640 |
ret = mAlloc( l ); |
641 |
if ( ! ret ) |
642 |
return 0; |
643 |
for ( p=ret; str<e; str++ ) |
644 |
switch ( *str ) { |
645 |
case '<': *p='&'; p[1]='l'; p[2]='t'; p[3]=';'; p+=4; break; |
646 |
case '>': *p='&'; p[1]='g'; p[2]='t'; p[3]=';'; p+=4; break; |
647 |
case '&': *p='&'; p[1]='a'; p[2]='m'; p[3]='p'; p[4]=';'; p+=5; break; |
648 |
case '"': *p='&';p[1]='q';p[2]='u';p[3]='o';p[4]='t';p[5]=';';p+=6;break; |
649 |
default: *p++ = *str; |
650 |
} |
651 |
*p = 0; |
652 |
return ret; |
653 |
} /* toHtml */ |
654 |
|
655 |
|
656 |
int utf8Chk ( void *mem, int len, int *tof ) |
657 |
{ |
658 |
unsigned char *c = (unsigned char *)mem; |
659 |
int f = 0; /* expected followers */ |
660 |
int l = len; |
661 |
if ( tof ) |
662 |
f = *tof; |
663 |
for ( ; l--; c++ ) { |
664 |
unsigned char u = *c; |
665 |
int tofollow = u < 128 ? 0 : u < 192 ? -1 : u < 224 ? 1 : 2; |
666 |
if ( f ) { |
667 |
if ( 0 > tofollow ) { f--; continue; } |
668 |
break; |
669 |
} |
670 |
if ( 0 <= tofollow ) { f = tofollow; continue; } |
671 |
goto croak; |
672 |
} |
673 |
if ( tof ) |
674 |
*tof = f; |
675 |
if ( ! f ) |
676 |
return 0; |
677 |
if ( 0 > l && tof ) /* exhausted buf, want follower */ |
678 |
return 0; |
679 |
sMsg( LOG_ERROR, "expected follower at %d got %x", len-l-1, (int)*c ); |
680 |
return len-l; |
681 |
croak: |
682 |
sMsg( LOG_ERROR, "expected no follower at %d got %x", len-l-1, (int)*c ); |
683 |
return len-l; |
684 |
} /* utf8Chk */ |