Annotation of freem/src/global_bltin.c, revision 1.13
1.1 snw 1: /*
1.13 ! snw 2: * $Id: global_bltin.c,v 1.12 2025/04/09 00:43:07 snw Exp $
1.1 snw 3: * freem database engine
4: *
5: *
1.3 snw 6: * Author: Serena Willis <snw@coherent-logic.com>
1.1 snw 7: * Copyright (C) 1998 MUG Deutschland
1.4 snw 8: * Copyright (C) 2020, 2025 Coherent Logic Development LLC
1.1 snw 9: *
10: *
11: * This file is part of FreeM.
12: *
13: * FreeM is free software: you can redistribute it and/or modify
14: * it under the terms of the GNU Affero Public License as published by
15: * the Free Software Foundation, either version 3 of the License, or
16: * (at your option) any later version.
17: *
18: * FreeM is distributed in the hope that it will be useful,
19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: * GNU Affero Public License for more details.
22: *
23: * You should have received a copy of the GNU Affero Public License
24: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
25: *
1.5 snw 26: * $Log: global_bltin.c,v $
1.13 ! snw 27: * Revision 1.12 2025/04/09 00:43:07 snw
! 28: * Exit with fatal error if a header mismatch found
! 29: *
1.12 snw 30: * Revision 1.11 2025/04/08 21:41:13 snw
31: * Make insert, update, and splitp global handler functions take a ptr to a global_handle instead of a file descriptor
32: *
1.11 snw 33: * Revision 1.10 2025/04/08 20:00:56 snw
34: * Global handler now uses a header file and maintains the last journaling transaction ID
35: *
1.10 snw 36: * Revision 1.9 2025/04/08 16:46:11 snw
37: * Add global file header and offsets
38: *
1.9 snw 39: * Revision 1.8 2025/04/08 14:39:21 snw
40: * Initial work on global handler refactor
41: *
1.8 snw 42: * Revision 1.7 2025/03/24 04:13:11 snw
43: * Replace action macro dat with fra_dat to avoid symbol conflict on OS/2
44: *
1.7 snw 45: * Revision 1.6 2025/03/24 01:33:30 snw
46: * Guard declaration of time function in global_bltin.c for portability
47: *
1.6 snw 48: * Revision 1.5 2025/03/22 22:52:24 snw
49: * Add STRLEN_GBL macro to manage global string length
50: *
1.5 snw 51: * Revision 1.4 2025/03/09 19:14:25 snw
52: * First phase of REUSE compliance and header reformat
53: *
1.4 snw 54: *
55: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
56: * SPDX-License-Identifier: AGPL-3.0-or-later
1.1 snw 57: **/
58:
59: #include <sys/types.h>
60: #include <sys/stat.h>
61: #include <fcntl.h>
62: #include <unistd.h>
63: #include <string.h>
1.8 snw 64: #include <stdlib.h>
1.1 snw 65: #include <errno.h>
66:
1.8 snw 67: #include "mpsdef.h"
1.10 snw 68: #include "journal.h"
1.8 snw 69: #include "global_bltin.h"
1.1 snw 70:
1.8 snw 71: global_handle *global_handles_head;
1.13 ! snw 72: unsigned long gbl_cache_misses = 0;
! 73: unsigned long gbl_cache_hits = 0;
1.1 snw 74:
75: static void b_free (short filedes, unsigned long blknbr);
1.11 snw 76: static void splitp (global_handle *g, char *block, long *addr, long *offs, unsigned long *blknbr);
77: static void update (global_handle *g, char *ins_key, long keyl);
78: static void insert (global_handle *g, char *ins_key, long key_len, unsigned long blknbr);
1.1 snw 79: static void scanpblk (char *block, long *adr, long *fnd);
80: static void scandblk (char *block, long *adr, long *fnd);
81: static void getnewblk (int filedes, unsigned long *blknbr);
82: static short int g_collate (char *t);
83: //static short int g_numeric (char *str);
84: short g_numeric (char *str);
85: void close_all_globals(void);
86: static void panic (void);
87:
88: #define ROOT 0L
89:
90: /* end of line symbol in global module is 30, which is a code not */
91: /* otherwise used in subscripts */
92: #define g_EOL 30
93:
94: #define EOL1 EOL
95:
96: /* numerics (('.'<<1)&037)==28 ; (('-'<<1)&037)==26; */
97: #define POINT 28
98: #define MINUS 26
99:
100: /* ALPHA and OMEGA are dummy subscripts in $order processing */
101: /* ALPHA sorts before all other subscripts */
102: /* OMEGA sorts after all other subscripts */
103: /* e.g. ("abc") -> "abc",OMEGA ; ("abc","") -> "abc",ALPHA */
104: #define OMEGA 29
105: #define ALPHA 31
106:
107: /* length of blocks. status bytes defined as offset to blocklength */
108: /* BLOCKLEN 1024 is defined in mpsdef0 include file */
109: #define DATALIM (BLOCKLEN-11)
110: #define LLPTR (BLOCKLEN-10)
111: #define NRBLK LLPTR
112: #define COLLA (BLOCKLEN- 7)
113: #define RLPTR (BLOCKLEN- 6)
114: #define FREE RLPTR
115: #define BTYP (BLOCKLEN- 3)
116: #define OFFS (BLOCKLEN- 2)
117:
118: /* length of blockpointers in bytes */
119: #define PLEN 3
120:
121: #define EMPTY 0
122: #define FBLK 1
123: #define POINTER 2
124: #define BOTTOM 6
125: #define DATA 8
126:
1.6 snw 127: #if !defined(__OpenBSD__) && !defined(_AIX) && !defined(__osf__) && !defined(MSDOS) && !defined(__vax__) && !defined(__OS2__)
1.1 snw 128: long time ();
129: #endif
130:
1.8 snw 131: inline long gbl_path(char *key, char *buf)
132: {
133: long savj;
134: long savch; /* saved j and ch for multiple pathes */
135:
136: register long int i;
137: register long int j;
138: register long int k;
139: register long int ch;
140: long pathscan; /* flag for repeated scan of pathlist setting an undef global */
141:
142: /* construct full UNIX filename */
143: savj = 0;
144: savch = ch = EOL;
145: pathscan = TRUE;
146: nextpath:
147: k = 0;
148: j = savj;
149:
150: if (key[1] == '%' || key[1] == '$') { /* %-globals and SSVN backing storage, no explicit path */
151:
152: if (gloplib[0] != EOL) {
153:
154: /* append % global access path */
155: while ((ch = buf[k++] = gloplib[j++]) != ':' && ch != EOL);
156:
157: }
158:
159: }
160: else if (key[1] != '/') { /* no explicit path specified */
161:
162: if (glopath[0] != EOL) {
163:
164: /* append global access path */
165: while ((ch = buf[k++] = glopath[j++]) != ':' && ch != EOL);
166:
167: }
168:
169: }
170:
171: if (savj == 0 && ch == EOL) pathscan = FALSE; /* one path only: inhibit search */
172:
173: if (k > 0) {
174:
175: if (k == 1 || (k == 2 && buf[0] == '.')) {
176: k = 0;
177: }
178: else {
179: buf[k - 1] = '/';
180: }
181:
182: }
183:
184: savch = ch;
185: savj = j;
186: i = 0;
187: j = 0;
188:
189: while (key[i] != EOL) {
190:
191: if ((buf[k] = key[i]) == DELIM) break;
192:
193: if (buf[k] == '/') {
194:
195: j = i;
196:
197: if (k > i) {
198: i = 0;
199: j = 0;
200: k = 0;
201:
202: continue;
203: }
204:
205: }
206:
207: i++;
208: k++;
209:
210: }
211:
212: buf[k] = NUL; /* NUL not EOL !!! */
213:
214: return i;
1.9 snw 215: } /* gbl_path() */
1.8 snw 216:
1.13 ! snw 217: void gbl_cache_hit(global_handle *g)
! 218: {
! 219: g->cache_hits++;
! 220: gbl_cache_hits++;
! 221: } /* gbl_cache_hit() */
! 222:
! 223: void gbl_cache_miss(global_handle *g)
! 224: {
! 225: g->fast_path = 0;
! 226: g->cache_misses++;
! 227: gbl_cache_misses++;
! 228: } /* gbl_cache_miss() */
! 229:
1.8 snw 230: int gbl_lock(global_handle *g, int type)
231: {
232: if (g->locked == TRUE || lonelyflag == TRUE) {
233: return TRUE;
234: }
235:
236: locking (g->fd, type, 0L);
237: g->locked = TRUE;
1.9 snw 238: } /* gbl_lock() */
1.8 snw 239:
240: int gbl_unlock(global_handle *g)
241: {
242: if (g->locked == FALSE || lonelyflag == TRUE) {
243: return TRUE;
244: }
245:
246: locking (g->fd, 0, 0L);
247: g->locked = FALSE;
1.9 snw 248: } /* gbl_unlock() */
1.8 snw 249:
250: void gbl_close(global_handle *g)
251: {
252: if (g->opened == TRUE) {
253: close (g->fd);
254:
255: g->use_count = 0;
256: g->age = 0;
257: g->last_block = 0;
258: g->locked = FALSE;
259: g->opened = FALSE;
260: }
1.9 snw 261: } /* gbl_close() */
1.8 snw 262:
263: void gbl_close_all(void)
264: {
265: global_handle *g;
266:
267: for (g = global_handles_head; g != NULL; g = g->next) {
268: gbl_close (g);
269: }
1.9 snw 270: } /* gbl_close_all() */
271:
1.10 snw 272: int gbl_write_initial_header(global_handle *g)
1.9 snw 273: {
274: global_header hdr;
275: unsigned long old_position;
276: char m[5] = GBL_MAGIC;
277: char msg[256];
278:
279: if (g->opened == FALSE) {
280: return FALSE;
281: }
282:
283: memcpy (hdr.magic, m, 5);
284: hdr.format_version = GBL_FORMAT_VERSION;
285: strncpy (hdr.host_triplet, HOST, 40);
286: hdr.block_size = BLOCKLEN;
287: hdr.last_transaction_id = 0;
288: hdr.created = time (0L);
289: hdr.last_backup = -1;
290:
291: gbl_lock (g, 1);
292: old_position = lseek (g->fd, 0, SEEK_CUR);
293: lseek (g->fd, 0, SEEK_SET);
294:
295: if (write (g->fd, &hdr, sizeof (global_header)) == -1) {
1.10 snw 296: snprintf (msg, sizeof (msg), "error %d writing global header for %s", errno, g->global_name);
1.9 snw 297: m_fatal (msg);
298: }
299:
300: lseek (g->fd, old_position, SEEK_SET);
301: gbl_unlock (g);
302:
303: return TRUE;
1.10 snw 304: } /* gbl_write_initial_header() */
305:
306:
307: int gbl_write_header(global_handle *g, global_header *hdr)
308: {
309: unsigned long old_position;
310: char msg[256];
311:
312: if (g->opened == FALSE) {
313: return FALSE;
314: }
1.12 snw 315:
1.10 snw 316: gbl_lock (g, 1);
317: old_position = lseek (g->fd, 0, SEEK_CUR);
318: lseek (g->fd, 0, SEEK_SET);
319:
320: if (write (g->fd, hdr, sizeof (global_header)) == -1) {
321: snprintf (msg, sizeof (msg), "error %d writing global header for %s", errno, g->global_name);
322: m_fatal (msg);
323: }
324:
325: lseek (g->fd, old_position, SEEK_SET);
326: gbl_unlock (g);
1.12 snw 327:
328: gbl_read_header (g, &g->header);
1.10 snw 329:
330: return TRUE;
1.9 snw 331: } /* gbl_write_header() */
1.8 snw 332:
1.10 snw 333: int gbl_read_header(global_handle *g, global_header *h)
334: {
335: unsigned long old_position;
336: char m[5] = GBL_MAGIC;
337:
338:
339: if (g->opened == FALSE) {
340: return GBL_HDR_NOTOPEN;
341: }
342:
343: gbl_lock (g, 1);
344: old_position = lseek (g->fd, 0, SEEK_CUR);
345: lseek (g->fd, 0, SEEK_SET);
346:
347: read (g->fd, h, sizeof (global_header));
348:
349: lseek (g->fd, old_position, SEEK_SET);
350: gbl_unlock (g);
351:
352: if (strncmp (h->magic, m, 5) != 0) {
353: return GBL_HDR_BADMAGIC;
354: }
355: if (h->format_version != GBL_FORMAT_VERSION) {
356: return GBL_HDR_BADVERSION;
357: }
358: if (h->block_size != BLOCKLEN) {
359: return GBL_HDR_BADBLOCKSIZE;
360: }
361:
362: return GBL_HDR_OK;
363: } /* gbl_read_header() */
364:
365: int gbl_update_tid(global_handle *g)
366: {
367: global_header h;
368:
369: if (gbl_read_header (g, &h) != GBL_HDR_OK) {
370: return FALSE;
371: }
372:
373: h.last_transaction_id = jnl_tran_id;
374:
375: return gbl_write_header (g, &h);
376: } /* gbl_update_tid() */
377:
1.8 snw 378: int gbl_create(global_handle *g)
379: {
380: while (1) {
381: errno = 0;
382:
383: if ((g->fd = creat (g->global_path, 0666)) != -1) break;
384:
385: if (errno == EMFILE || errno == ENFILE) {
386: gbl_close_all ();
387: continue;
388: }
389:
390: return PROTECT;
391: }
392:
393: g->opened = TRUE;
394: g->age = time (0L);
395: g->last_block = 0;
396: g->use_count = 1;
1.13 ! snw 397: g->fast_path = 0;
! 398:
1.10 snw 399: gbl_write_initial_header (g);
1.8 snw 400:
401: return OK;
1.9 snw 402: } /* gbl_create() */
1.8 snw 403:
404: short gbl_open(global_handle *g, short action)
405: {
1.10 snw 406: int result;
407:
1.8 snw 408: if (g->opened == FALSE) {
1.13 ! snw 409: gbl_cache_miss (g);
1.8 snw 410: while (1) {
411: errno = 0;
412: g->fd = open (g->global_path, 2);
413:
414: if (g->fd != -1) break;
415:
416: switch (errno) {
417: case EINTR:
418: continue;
419:
420: case EMFILE:
421: case ENFILE:
1.10 snw 422: gbl_close_all ();
1.8 snw 423: continue;
424: }
425:
426: break;
427: }
428:
429: if (g->fd == -1) {
430: g->use_count = 0;
431: g->age = 0;
432: g->last_block = 0;
433: g->locked = FALSE;
434: g->opened = FALSE;
435: }
436: else {
437: g->opened = TRUE;
1.12 snw 438: result = gbl_read_header (g, &g->header);
439:
1.10 snw 440: if (result == GBL_HDR_OK) {
441: g->opened = TRUE;
442: }
1.12 snw 443: else {
444: gbl_close (g);
445: set_io (UNIX);
446:
447: switch (result) {
448:
449: case GBL_HDR_BADMAGIC:
450: fprintf (stderr, "gbl_open: bad magic value in %s [FATAL]\n", g->global_name);
451: exit (1);
452: break;
453:
454: case GBL_HDR_BADVERSION:
455: fprintf (stderr, "gbl_open: global version is %d in %s (must be %d) [FATAL]\n", g->header.format_version, g->global_name, GBL_FORMAT_VERSION);
456: exit (1);
457: break;
458:
459: }
460:
1.10 snw 461: return FALSE;
462: }
1.8 snw 463: }
464: }
465:
466: return g->opened;
467:
1.9 snw 468: } /* gbl_open() */
1.8 snw 469:
470: global_handle *gbl_handle(char *key)
471: {
472: global_handle *g;
473: char global_name[256];
474: int i;
475: long path_len;
476: char block[BLOCKLEN];
477: struct stat dinf;
478:
479: i = 0;
480: while (key[i] != EOL) {
481: if ((global_name[i] = key[i]) == DELIM) break;
482:
483: i++;
484: }
485: global_name[i] = NUL;
486:
487:
488: for (g = global_handles_head; g != NULL; g = g->next) {
489: if (strncmp (g->global_name, global_name, 256) == 0) {
490: g->use_count++;
491: if (!lonelyflag) {
492: g->fast_path = 0;
493: }
494:
495: fstat (g->fd, &dinf);
496: if (g->age > dinf.st_mtime) {
497: g->fast_path = 2;
498: return g;
499: }
500:
501: g->age = time (0L);
502: g->fast_path = 0;
503:
504: return g;
505: }
506: }
507: g = (global_handle *) malloc (sizeof (global_handle));
508: NULLPTRCHK(g,"gbl_open");
509:
510: g->use_count = 1;
511: g->locked = FALSE;
512: g->age = time (0L);
513: g->last_block = 0;
514: g->opened = FALSE;
515: g->fd = 0;
516: g->fast_path = -1;
1.13 ! snw 517: g->cache_misses = 0;
! 518: g->cache_hits = 0;
1.8 snw 519:
520: strcpy (g->global_name, global_name);
521: gbl_path (key, g->global_path);
522:
523: g->next = global_handles_head;
524: global_handles_head = g;
525:
526: return g;
1.9 snw 527: } /* gbl_handle() */
1.8 snw 528:
529:
1.1 snw 530: /* globals management */
531:
532: /* 0 = set_sym 1 = get_sym */
533: /* 2 = kill_sym 3 = $data */
534: /* 5 = $fra_order */
535: /* 7 = $fra_query */
536: /* */
537: /* 14=killone 13=getnext */
538: /* 16=merge_sym 17=$zdata */
539: /* gvn as ASCII-string */
540:
541: /* returns OK action fulfilled */
542: /* (ierr) UNDEF missing in action */
543: /* NAKED illegal naked reference */
544: /* SBSCR illegal subscript */
545: /* DBDGD data base degradation */
546:
547: /* The data is organized in a B* tree structure on external storage.
548: * For a description of the principles of the algorithms see
549: * Donald E. Knuth "The Art of Computer Programming" vol. 3 p. 478.
550: * This tree structure guarantees fast disk access and is the
551: * canonical way to implement M globals.
552: *
553: * Each M global occupies a separate UNIX file in the directory
554: * specified in the globals_path directive for the current namespace
555: * in /etc/freem.conf. The default namespace on a new installation
556: * of FreeM is called "USER".
557: *
558: * Any global whose name begins with "%" will always be stored in the
559: * SYSTEM namespace, in the directory specified in its "globals_path"
560: * directive in /etc/freem.conf (by default, /var/local/freem/SYSTEM/globals).
561: *
562: * The UNIX file names are the same as the corresponding M global
563: * names; i.e. beginning with an '^'. However it is possible to access
564: * globals in other directories if the path name is specified.
565: * E.g. "S ^/usr/mumps/test=1" does "S ^test=1" in the file /usr/mumps/^test.
566: * If FreeM is started with the -s/--standard switches, it is not possible
567: * to specify a directory. There is a syntactic ambiguity: the '/' character
568: * in the directory name is in conflict with the '/' divide operator. Use
569: * parentheses to make things clear:
570: *
571: * ^/usr/mumps/test/2 ; '/2' is part of the name
572: * (^/usr/mumps/test)/2 ; ambiguity resolved
573: * ^test/2 ; '/2' is a divide operation
574: * ^/usr/mumps/test("ok")/2 ; '/2' is a divide
575: *
576: * To prevent jobs from messing the globals up, access is regulated
577: * with the 'locking' mechanism. (that is different from mumps LOCKs)
578: *
579: * Data is organized in blocks of 1024 bytes (BLOCKLEN) with the following
580: * layout:
581: * byte 0 - 1013 'stuff' 0...DATALIM
582: * organization is:
583: * length of key (minus offset into previous key)
584: * offset into previous key
585: * key (without EOL character)
586: * length of data or two bytes as pointer
587: * data(without EOL character) in pointer blocks
588: *
589: * byte 1014 - 1016 leftlink pointer LLPTR
590: * in root block: number of blocks NRBLK
591: * byte 1017 <reserved>
592: * byte 1017 in root block: type of collating sequence COLLA
593: * LSB=0: numeric(standard) LSB=1: alphabetic
594: * byte 1018 - 1020 rightlink pointer RLPTR
595: * in root block: number of free blocks list FREE
596: * byte 1021 block type BTYP
597: * (2=POINTER,6=BOTTOM LEVEL POINTER,8=DATA)
598: * byte 1022 - 1023 offset OFFS
599: * (pointer to unused part of 'stuff')
600: *
601: * the file is *not* closed on return. since access is regulated by the
602: * locking mechanism, that will not spell trouble.
603: */
1.8 snw 604:
1.1 snw 605: void global_bltin (short action, char *key, char *data)
606: {
607:
1.8 snw 608: global_handle *g;
1.9 snw 609:
610: unsigned long hdr_offset;
1.8 snw 611:
1.1 snw 612: /* these must be static variables */
613: static short filedes; /* filedescr for global access */
614: static char filnam[256]; /* name of global/unix file */
615:
616: /* the following vars may be */
617: /* static or dynamic */
618: static unsigned long blknbr; /* block number */
619: static unsigned long newblk;
620: static unsigned long other;
621: static long j1;
622: static long limit;
623: static short typ; /* block type */
1.9 snw 624: static long keyl; /* length of compacted key */
625: static long datal; /* length of data */
626: static long olddatal;
627: static long offset;
628: static long found;
629: static long addr; /* address of key in 'block' */
630: static long needed; /* new bytes needed to ins. stuff */
631: static long ret_to; /* return code */
632: static long kill_again;
1.1 snw 633: static char key1[256];
634: static char tmp1[256]; /* intermediate storage for op= */
635: static char block[BLOCKLEN];
636: static int getnflag; /* flag 1=$ZO-variable 0=$Q-function */
637: static int tryfast; /* try fast access if get_sym on */
638: /* previous global */
639:
1.8 snw 640: int iresult;
641:
1.1 snw 642: struct stat dinf; /* get modification date */
643:
1.9 snw 644: long savj;
645: long savch; /* saved j and ch for multiple pathes */
646: register long int i;
647: register long int j;
648: register long int k;
649: register long int ch;
650: long pathscan; /* flag for repeated scan of pathlist setting an undef global */
1.1 snw 651:
1.9 snw 652: hdr_offset = sizeof (global_header);
653:
1.1 snw 654: /* process optional limitations */
655: if (glvnflag.all && key[0] >= '%' && key[0] <= 'z') {
656:
657: if ((i = glvnflag.one[0])) { /* number of significant chars */
658:
659: j = 0;
660: while ((k = key[j]) != DELIM && k != EOL) {
661:
662: if (j >= i) {
663:
664: while ((k = key[++j]) != DELIM && k != EOL);
665:
666: stcpy (&key[i], &key[j]);
667:
668: break;
669: }
670:
671: j++;
672:
673: }
674: }
675:
676: if (glvnflag.one[1]) { /* upper/lower sensitivity */
677:
678: j = 0;
679:
680: while ((k = key[j]) != DELIM && k != EOL) {
681:
682: if (k >= 'a' && k <= 'z') key[j] = k - 32;
683:
684: j++;
685:
686: }
687:
688: }
689:
690: if ((i = glvnflag.one[2])) {
691:
692: if (stlen (key) > i) {
693: merr_raise (M75);
694: return;
695: } /* key length limit */
696:
697: }
698:
699: if ((i = glvnflag.one[3])) { /* subscript length limit */
700:
701: j = 0;
702:
703: while ((k = key[j++]) != DELIM && k != EOL);
704:
705: if (k == DELIM) {
706:
707: k = 0;
708: for (;;) {
709:
710: k = key[j++];
711:
712: if (k == DELIM || k == EOL) {
713:
714: if (k > i) {
715: merr_raise (M75);
716: return;
717: }
718:
719: k = 0;
720: }
721:
722: if (k == EOL) break;
723:
724: k++;
725: }
726: }
727: }
728: }
729:
1.8 snw 730:
1.1 snw 731: if (action == getnext) {
732: getnflag = TRUE;
733: varnam[0] = EOL;
734:
735: if (zref[0] == EOL) {
736: merr_raise (M7);
737: data[0] = EOL;
738:
739: return;
740: }
741:
742: stcpy (key, zref);
743:
744: action = fra_query;
745: ordercnt = 1L;
746: }
747: else {
748:
749: getnflag = FALSE;
750:
751: /* naked reference section */
752:
753: if (key[1] == DELIM) { /* resolve naked reference */
754:
755: while (--nakoffs >= 0) { /* naked reference pointer */
756: if (zref[nakoffs] == DELIM) break;
757: }
758:
759: if ((i = ++nakoffs) == 0) { /* illegal naked reference */
760: data[0] = EOL1;
761: merr_raise (NAKED);
762:
763: return;
764: }
765:
766: j = 2;
767: while ((zref[i] = key[j++]) != EOL) {
768:
769: if ((++i) >= STRLEN) {
770: zref[255] = EOL;
771: merr_raise (M75);
772:
773: return;
774: }
775:
776: }
777: nakoffs = stcpy (key, zref);
778: }
779: else {
780:
781: /* only save off $REFERENCE if the global isn't part of SSVN backing storage */
782: if (key[1] != '$') {
783: nakoffs = stcpy (zref, key); /* save reference */
784: }
785:
786: }
787: }
788:
789: if (v22ptr) {
790:
791: procv22 (key);
792:
793: if (key[0] != '^') {
794: char losav[256];
795:
796: stcpy (losav, l_o_val);
797: symtab (action, key, data);
798: stcpy (g_o_val, l_o_val);
799: stcpy (l_o_val, losav);
800:
801: return;
802: }
803: }
804:
1.8 snw 805: g = gbl_handle (key);
806: i = gbl_path (key, filnam);
1.1 snw 807:
808: /* compact key to internal format: characters are shifted left */
809: /* delimiters become LSB of previous character */
810: /* test subscripts for being numeric or not */
811: /* numeric values are shifted into the code space */
812: /* that is available because of illegal CTRL subscipts */
813:
814: k = 0;
815:
816: if (key[i] == EOL) { /* unsubscripted variable */
817:
818: if (action == fra_order) {
819: g_o_val[0] = EOL;
820: merr_raise (NEXTER);
821:
822: return;
823: }
824:
825: }
826: else if (key[++i] == EOL) { /* empty (first) subscript */
827:
828: if ((action != fra_order) && (action != fra_query)) {
829: merr_raise (SBSCR);
830: return;
831: }
832:
833: }
834: else { /* non empty subscript */
835:
836: j1 = g_numeric (&key[i]);
837:
838: while ((ch = key[i++]) != EOL) {
839:
840: if (ch == DELIM) {
841:
842: if (k == 0) {
843: merr_raise (SBSCR);
844: return;
845: }
846:
847: if (compactkey[--k] & 01) {
848: merr_raise (SBSCR);
849: return;
850: }
851:
852: compactkey[k++] |= 01;
853: j1 = g_numeric (&key[i]);
854:
855: }
856: else if (UNSIGN (ch) >= DEL) { /* transform 8bit char to 7bit */
857:
858: compactkey[k++] = (DEL << 1);
859: ch = UNSIGN (ch) - DEL;
860:
861: if (UNSIGN (ch) >= DEL) {
862: compactkey[k++] = (DEL << 1);
863: ch = UNSIGN (ch) - DEL;
864: }
865:
866: compactkey[k++] = ch << 1;
867:
868: }
869: else if (ch < SP || ch >= DEL) {
870:
871: /*no CTRLs */
872:
873: merr_raise (SBSCR);
874: return;
875: }
876: else {
877: compactkey[k++] = (j1 ? (ch << 1) & 036 : ch << 1);
878: }
879: }
880:
881: }
882:
883: if (action == fra_order) {
884:
885: if (ordercnt > 0) {
886:
887: compactkey[k] = (k == 0 || (compactkey[k - 1] & 01) ? ALPHA : OMEGA);
888:
889: if (k > 0) compactkey[k - 1] |= 01;
890:
891: keyl = (++k);
892:
893: }
894: else if (ordercnt == 0) { /* no scan at all */
895:
896: k = 0;
897: i = 0;
898:
899: while ((ch = key[i++]) != EOL) {
900: if (ch == DELIM) k = i;
901: }
902:
903: stcpy (data, &key[k]);
904: g_o_val[0] = EOL;
905:
906: return;
907:
908: }
909: else { /* backward scanning */
910:
911: if (k == 0 || (compactkey[k - 1] & 01)) {
912:
913: compactkey[k] = OMEGA;
914:
915: if (k > 0) compactkey[k - 1] |= 01;
916:
917: k++;
918:
919: }
920: else {
921: compactkey[k - 1] |= 01;
922: }
923:
924: keyl = k;
925: }
926:
927: }
928: else {
929:
930: if ((keyl = k) > 0) {
931:
932: if ((compactkey[--k] & 01) && (action != fra_query)) {
933: merr_raise (SBSCR);
934: return;
935: }
936:
937: compactkey[k++] |= 01;
938: }
939: }
940:
941: compactkey[k] = g_EOL;
942:
943: reopen:
944:
1.8 snw 945: gbl_open (g, action);
946: if (g->fd == -1) {
1.1 snw 947:
948: /* file not found */
949: if (action != set_sym) {
950:
951: if (errno != ENOENT) {
952: merr_raise (PROTECT);
953: return;
954: }
955:
1.7 snw 956: if (action == fra_dat || action == zdata) {
1.1 snw 957: data[0] = '0';
958: data[1] = EOL1;
959:
960: return;
961: }
962:
963: data[0] = EOL1;
964:
965: if (action == get_sym || getnflag) {
966: merr_raise (M7);
967: data[0] = EOL;
968: }
969: else if (action == fra_order || action == fra_query) {
970: g_o_val[0] = EOL;
971: }
972:
973: return;
974: }
975:
976: if (errno != ENOENT) {
977: merr_raise (PROTECT);
978: return;
979: }
1.8 snw 980:
1.1 snw 981: if (setop) {
982:
983: tmp1[0] = EOL;
984: m_op (tmp1, data, setop);
985: setop = 0;
986:
987: if (merr () > OK) return;
988:
989: datal = stcpy (data, tmp1);
990: }
991:
992: for (i = 0; i < BLOCKLEN; block[i++] = 0); /* clear block */
993:
994: stcpy0 (&block[2], compactkey, (long) keyl);
995:
996: block[0] = keyl; /* $length of key */
997: j = i = keyl + 2;
998: block[i++] = 0;
999: block[i++] = 0;
1000: block[i++] = ROOT + 1; /* block 1 = data */
1001: block[BTYP] = BOTTOM;
1002: block[COLLA] = 0; /* collating sequence */
1003: block[OFFS] = i / 256;
1004: block[OFFS + 1] = i % 256;
1005: block[NRBLK] = 0;
1006: block[NRBLK + 1] = 0;
1007: block[NRBLK + 2] = ROOT + 1; /* nr. of blocks */
1008:
1009: /* create file, write_lock it and initialize root block */
1.8 snw 1010: gbl_lock (g, 1);
1.1 snw 1011:
1.8 snw 1012: if ((iresult = gbl_create (g)) != OK) {
1013: merr_raise (iresult);
1.1 snw 1014: return;
1.8 snw 1015: }
1.1 snw 1016:
1017: for (;;) {
1018:
1019: errno = 0;
1020:
1.9 snw 1021: lseek (g->fd, hdr_offset + (ROOT * BLOCKLEN), SEEK_SET);
1.8 snw 1022: write (g->fd, block, BLOCKLEN);
1.1 snw 1023:
1024: if (errno == 0) break;
1025:
1026: panic ();
1027: }
1028:
1029: block[NRBLK] = 0;
1030: block[NRBLK + 1] = 0;
1031: block[NRBLK + 2] = ROOT; /* clear */
1032:
1033: /* copy and write length of data */
1034: block[j] = k = stcpy (&block[j + 1], data);
1035: block[i = k + j + 1] = 0; /* clear EOL symbol */
1036: block[BTYP] = DATA; /* type */
1037: block[OFFS] = i / 256;
1038: block[OFFS + 1] = i % 256;
1039:
1040: for (;;) {
1041:
1042: errno = 0;
1.8 snw 1043: write (g->fd, block, BLOCKLEN);
1.1 snw 1044:
1045: if (errno == 0) break;
1046:
1.9 snw 1047: lseek (g->fd, hdr_offset + ((ROOT + 1L) * BLOCKLEN), SEEK_SET);
1.1 snw 1048: panic ();
1049:
1050: }
1051:
1.8 snw 1052: gbl_close (g);
1053: gbl_unlock (g);
1054: gbl_open (g, action);
1055:
1.1 snw 1056: /* close new file, so other users can find it */
1057: return;
1058: }
1059:
1060: /* request global for exclusive use */
1061: /* odd numbered actions get read access (get_sym,data,fra_order) 3 */
1062: /* even ones read/write access (set_sym,kill_sym) 1 */
1063:
1064: lock:
1065:
1066: if (action == get_sym) {
1067:
1068: tfast0:
1.8 snw 1069: gbl_lock (g, 3);
1.13 ! snw 1070:
! 1071: if (g->fast_path > 0) goto tfast1; /* try again last block */
1.1 snw 1072:
1.8 snw 1073: blknbr = g->last_block = ROOT; /* start with ROOT block */
1.1 snw 1074:
1075: for (;;) {
1076:
1077:
1078: tfast1:
1079:
1.9 snw 1080: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1081: read (g->fd, block, BLOCKLEN);
1.1 snw 1082:
1083:
1084: tfast2:
1085:
1086: if ((typ = block[BTYP]) == DATA) { /* scan data block: here we test for equality only */
1087:
1088: offset = UNSIGN (block[OFFS]) * 256 +
1089: UNSIGN (block[OFFS + 1]);
1090: j = UNSIGN (block[0]);
1091: i = 0;
1092:
1093: stcpy0 (key1, &block[2], j); /* get first key */
1094:
1095: ch = keyl; /* ch is a register! */
1096:
1097: while (i < offset) {
1098:
1099: j = UNSIGN (block[i++]); /* length of key - offset */
1100: k = UNSIGN (block[i++]); /* offset into previous entry */
1101:
1102: j += k;
1103:
1104: while (k < j) key1[k++] = block[i++]; /* get key */
1105:
1106: if (j != ch) { /* keys have different length */
1107:
1108: i += UNSIGN (block[i]);
1109: i++;
1110:
1111: continue;
1112:
1113: }
1114:
1115: j = ch;
1116:
1117: do {
1118: j--;
1119: } while (compactkey[j] == key1[j]); /* compare keys */
1120:
1121:
1122: if (j < 0) {
1123:
1124: k = UNSIGN (block[i++]);
1125: stcpy0 (data, &block[i], k); /* get value */
1126: data[k] = EOL1; /* append EOL */
1127:
1128: goto quit;
1129:
1130: }
1131:
1132: i += UNSIGN (block[i]);
1133: i++; /* skip data */
1134:
1135: }
1136:
1137: /* fast access failed. try normal path */
1138: if (tryfast) {
1.13 ! snw 1139: gbl_cache_miss (g);
1.1 snw 1140: goto tfast0;
1141: }
1142:
1143: merr_raise (M7);
1144: data[0] = EOL;
1145:
1146: goto quit; /* variable not found */
1147: }
1148: else {
1149:
1.13 ! snw 1150: if (g->fast_path > 0) {
! 1151: gbl_cache_miss (g);
1.1 snw 1152: goto tfast0;
1153: }
1154:
1155: if (typ == EMPTY) {
1156:
1157: if (blknbr == ROOT) {
1.8 snw 1158: //close (filedes);
1159: gbl_close (g);
1.1 snw 1160: goto reopen;
1161: }
1162:
1163: merr_raise (DBDGD);
1164: goto quit;
1165:
1166: }
1167:
1168: }
1169:
1170: scanpblk (block, &addr, &found);
1171:
1172: addr += UNSIGN (block[addr]) + 2; /* skip key */
1173:
1.8 snw 1174: if ((blknbr = UNSIGN (block[addr]) * 65536 + UNSIGN (block[addr + 1]) * 256 + UNSIGN (block[addr + 2])) == g->last_block) {
1.1 snw 1175: merr_raise (DBDGD);
1176: goto quit;
1177: }
1178:
1179: addr += PLEN; /* skip data */
1.8 snw 1180: g->last_block = blknbr;
1.13 ! snw 1181: g->fast_path = 1;
1.1 snw 1182:
1183: if (merr () == INRPT) goto quit;
1184:
1185: }
1186: } /* end of get_sym */
1187:
1.8 snw 1188: gbl_lock (g, action & 01 ? 3 : 1);
1189:
1.1 snw 1190: /* a KILL on an unsubscripted global deletes the entire file */
1191: if (action == kill_sym && compactkey[0] == g_EOL) {
1192:
1.9 snw 1193: lseek (g->fd, hdr_offset + ROOT, SEEK_SET);
1.1 snw 1194:
1195: /* note : UNIX does not tell other */
1196: block[BTYP] = EMPTY; /* jobs that a file has been unlinked */
1197:
1198: /* as long as they keep it open. */
1199: /* so we mark this global as EMPTY */
1.8 snw 1200: write (g->fd, block, BLOCKLEN);
1201:
1202: gbl_unlock (g);
1203: gbl_close (g);
1.1 snw 1204:
1205: unlink (filnam);
1206:
1207: return;
1208: }
1209:
1210: k_again: /* entry point for repeated kill operations */
1211:
1212: /* scan tree for the proper position of key */
1.8 snw 1213: blknbr = g->last_block = ROOT; /* start with ROOT block */
1.1 snw 1214: trx = (-1);
1215:
1216: for (;;) {
1217:
1218: if (++trx >= TRLIM) {
1219: merr_raise (STKOV);
1220: goto quit;
1221: }
1222:
1223: traceblk[trx] = blknbr;
1224: traceadr[trx] = 0;
1225:
1.9 snw 1226: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1227: read (g->fd, block, BLOCKLEN);
1.1 snw 1228:
1229: typ = block[BTYP];
1230:
1231: if (typ == DATA) {
1232: scandblk (block, &addr, &found);
1233: break;
1234: }
1235:
1236: if (typ == EMPTY) {
1237:
1238: if (blknbr == ROOT) {
1.8 snw 1239: gbl_close (g);
1.1 snw 1240: goto reopen;
1241: }
1242:
1243: merr_raise (DBDGD);
1244: goto quit;
1245: }
1246:
1247: scanpblk (block, &addr, &found);
1248:
1249: traceadr[trx] = addr;
1250: addr += UNSIGN (block[addr]);
1251: addr += 2; /* skip key */
1252:
1.8 snw 1253: if ((blknbr = UNSIGN (block[addr]) * 65536 + UNSIGN (block[addr + 1]) * 256 + UNSIGN (block[addr + 2])) == g->last_block) {
1.1 snw 1254: merr_raise (DBDGD);
1255: goto quit;
1256: }
1257:
1258: addr += PLEN; /* skip data */
1.8 snw 1259: g->last_block = blknbr;
1.13 ! snw 1260: g->fast_path = 1;
1.1 snw 1261: }
1262:
1263: traceadr[trx] = addr;
1264:
1265: switch (action) {
1266:
1267: case set_sym:
1268:
1269: datal = stlen (data);
1270: offset = UNSIGN (block[OFFS]) * 256 +
1271: UNSIGN (block[OFFS + 1]);
1272:
1273: if (found != 2) { /* new entry */
1274:
1275: if (setop) {
1276:
1277: tmp1[0] = EOL;
1278:
1279: m_op (tmp1, data, setop);
1280:
1281: setop = 0;
1282:
1283: if (merr () > OK) return;
1284:
1285: datal = stcpy (data, tmp1);
1286:
1287: }
1288:
1289: needed = keyl + datal + 3;
1290:
1291: if ((offset + needed) > DATALIM) {
1292: ret_to = 'n'; /* n=new */
1293: goto splitd;
1294: }
1295:
1296:
1297: s10: {
1298: long len; /* insert key */
1299: char key0[256];
1300:
1301: i = 0;
1302:
1303: while (i < addr) { /* compute offset into previous entry */
1304:
1305: len = UNSIGN (block[i++]);
1306:
1307: #ifdef VERSNEW
1308:
1309: k = UNSIGN (block[i++]);
1310: stcpy0 (&key0[k], &block[i], len);
1311:
1312: i += len;
1313: key0[k + len] = g_EOL;
1314:
1315: #else
1316:
1317: len += (k = UNSIGN (block[i++]));
1318:
1319: while (k < len) key0[k++] = block[i++];
1320:
1321: key0[k] = g_EOL;
1322:
1323: #endif /* VERSNEW */
1324:
1325: i += UNSIGN (block[i]);
1326: i++; /* skip data */
1327:
1328: }
1329:
1330: k = 0;
1331:
1332: if (addr > 0) {
1333:
1334: while (compactkey[k] == key0[k]) {
1335:
1336: if (key[k] == g_EOL) break;
1337:
1338: k++;
1339:
1340: }
1341:
1342: /* do *not* fully compact numerics */
1343: if ((i = UNSIGN (compactkey[k])) <= POINT) {
1344:
1345: while (--k >= 0 && (UNSIGN (compactkey[k]) & 01) == 0);
1346:
1347: k++;
1348: }
1349:
1350: }
1351:
1352: needed -= k;
1353: i = (offset += needed);
1354: block[OFFS] = i / 256;
1355: block[OFFS + 1] = i % 256;
1356:
1357: while (i >= addr) {
1358: block[i] = block[i - needed];
1359: i--;
1360: }
1361:
1362: #ifdef VERSNEW
1363:
1364: i = addr;
1365: block[i++] = j1 = keyl - k;
1366: block[i++] = k;
1367:
1368: stcpy0 (&block[i], &compactkey[k], j1);
1369:
1370: i += j1;
1371: block[i++] = datal;
1372:
1373: stcpy0 (&block[i], data, datal);
1374:
1375: #else
1376:
1377: block[addr + 1] = k;
1378: j1 = k;
1379: i = addr + 2;
1380:
1381: while (k < keyl) block[i++] = compactkey[k++];
1382:
1383: block[addr] = k - j1;
1384: addr = i++;
1385: k = 0;
1386:
1387: while (k < datal) block[i++] = data[k++];
1388:
1389: block[addr] = k;
1390:
1391: #endif /* VERSNEW */
1392:
1393: }
1394:
1.9 snw 1395: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1396: write (g->fd, block, BLOCKLEN);
1.1 snw 1397:
1.11 snw 1398: if (traceadr[trx] == 0) update (g, compactkey, keyl);
1.1 snw 1399:
1400: break;
1401: }
1402:
1403: /* there was a previous entry */
1404: addr += UNSIGN (block[addr]);
1405: addr += 2;
1406: olddatal = UNSIGN (block[addr]);
1407:
1408: if (setop) {
1409:
1410: stcpy0 (tmp1, &block[addr + 1], (long) olddatal);
1411:
1412: tmp1[olddatal] = EOL;
1413:
1414: m_op (tmp1, data, setop);
1415:
1416: setop = 0;
1417:
1418: if (merr () > OK) return;
1419:
1420: datal = stcpy (data, tmp1);
1421: }
1422:
1423: if ((j1 = olddatal - datal) != 0) {
1424:
1425: if (j1 > 0) { /* surplus space */
1426:
1427: i = addr + datal;
1428: k = offset;
1429: offset -= j1;
1430: j1 += i;
1431:
1432: stcpy0 (&block[i], &block[j1], offset - i);
1433:
1434: i = offset;
1435:
1436: while (i < k) block[i++] = 0; /* clear area */
1437:
1438: }
1439: else {
1440: /* we need more space */
1441:
1442: if ((offset - j1) > DATALIM) {
1443: /* block too small */
1444: ret_to = 'u'; /* u=update */
1445:
1446: goto splitd;
1447: }
1448:
1449: s20:
1450:
1451: i = offset;
1452: k = addr + olddatal;
1453: offset -= j1;
1454: j1 = offset;
1455:
1456: while (i > k) block[j1--] = block[i--];
1457:
1458: }
1459:
1460: /* overwrite */
1461: block[OFFS] = offset / 256;
1462: block[OFFS + 1] = offset % 256;
1463: block[addr] = datal;
1464:
1465: }
1466: else { /* if nothing changes, do not write */
1467:
1468: i = 0;
1469: j = addr + 1;
1470:
1471: while (i < datal) {
1472: if (block[j++] != data[i]) break;
1473:
1474: i++;
1475: }
1476:
1477: if (i == datal) goto quit;
1478:
1479: }
1480:
1481: stcpy0 (&block[++addr], data, (long) datal);
1.9 snw 1482: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1483: write (g->fd, block, BLOCKLEN);
1.10 snw 1484:
1485: gbl_update_tid (g);
1486:
1.1 snw 1487: break;
1488:
1489:
1.7 snw 1490: case fra_dat:
1.1 snw 1491:
1492: data[0] = '0';
1493: data[1] = EOL1;
1494: data[2] = EOL1;
1495:
1496: if (found == 2) { /* ... advance to next entry */
1497: addr += UNSIGN (block[addr]);
1498: addr += 2; /* skip key */
1499: addr += UNSIGN (block[addr]);
1500: addr++; /* skip data */
1501:
1502: data[0] = '1';
1503: }
1504:
1505: {
1506: long len;
1507: char key0[256];
1508:
1509: /* get following entry, eventually in the next blk */
1510: offset = UNSIGN (block[OFFS]) * 256 +
1511: UNSIGN (block[OFFS + 1]);
1512:
1513: if (addr >= offset) {
1514:
1515: if ((blknbr = UNSIGN (block[RLPTR]) * 65536 + UNSIGN (block[RLPTR + 1]) * 256 + UNSIGN (block[RLPTR + 2]))) {
1516:
1.9 snw 1517: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1518: read (g->fd, block, BLOCKLEN);
1.1 snw 1519: j1 = UNSIGN (block[0]);
1520:
1521: i = 0;
1522: j = 2;
1523:
1524: while (i < j1) key0[i++] = block[j++];
1525:
1526: key0[i] = g_EOL;
1527:
1528: }
1529: else {
1530: goto quit;
1531: }
1532:
1533: }
1534: else {
1535:
1536: i = 0;
1537:
1538: while (i <= addr) { /* compute offset complete key */
1539: len = UNSIGN (block[i++]);
1540:
1541: #ifdef VERSNEW
1542:
1543: k = UNSIGN (block[i++]);
1544: stcpy0 (&key0[k], &block[i], len);
1545: key0[k + len] = g_EOL;
1546: i += len;
1547:
1548: #else
1549:
1550: len += (j = UNSIGN (block[i++]));
1551:
1552: while (j < len) key0[j++] = block[i++];
1553:
1554: key0[j] = g_EOL;
1555:
1556: #endif /* VERSNEW */
1557:
1558: i += UNSIGN (block[i]);
1559: i++; /* skip data */
1560: }
1561:
1562: }
1563:
1564: /* is it a descendant? */
1565: if (compactkey[0] == g_EOL && key0[0] != g_EOL) {
1566: data[1] = data[0];
1567: data[0] = '1';
1568:
1569: break;
1570: }
1571:
1572: i = 0;
1573:
1574: while (compactkey[i] == key0[i]) i++;
1575:
1576: if (compactkey[i] == g_EOL) {
1577: data[1] = data[0];
1578: data[0] = '1';
1579: }
1580: }
1581:
1582: break;
1583:
1584:
1585: case fra_order:
1586:
1587: if (ordercnt < 0) goto zinv;
1588:
1589: offset = UNSIGN (block[OFFS]) * 256 + UNSIGN (block[OFFS + 1]);
1590:
1591: if (addr >= offset) { /* look in next block */
1592:
1593: if ((blknbr = UNSIGN (block[RLPTR]) * 65536 + UNSIGN (block[RLPTR + 1]) * 256 + UNSIGN (block[RLPTR + 2])) == 0) {
1594: data[0] = EOL1;
1595: g_o_val[0] = EOL;
1596:
1597: goto quit;
1598: } /* no next block */
1599:
1.9 snw 1600: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1601: read (g->fd, block, BLOCKLEN);
1.1 snw 1602: scandblk (block, &addr, &found);
1603:
1604: }
1605:
1606: {
1607: long len;
1608: int ch0;
1609: char scratch[256];
1610: char key0[256];
1611:
1612: i = 0;
1613:
1614: while (i <= addr) { /* compute offset complete key */
1615:
1616: len = UNSIGN (block[i++]);
1617: len += (j = UNSIGN (block[i++]));
1618:
1619: while (j < len) key0[j++] = block[i++];
1620:
1621: key0[j] = g_EOL;
1622: i += UNSIGN (block[i]);
1623:
1624: i++; /* skip data */
1625: }
1626:
1627: /* save data value for inspection with $V(111) */
1628: i = addr;
1629: i += UNSIGN (block[i]);
1630: i += 2; /* skip key */
1631: j = UNSIGN (block[i++]);
1632: stcpy0 (g_o_val, &block[i], j); /* get value */
1633: g_o_val[j] = EOL; /* append EOL */
1634:
1635: i = 0;
1636: j = 0;
1637:
1638: while ((scratch[j++] = UNSIGN (key0[i++])) != g_EOL);
1639:
1640: if (compactkey[--keyl] == ALPHA) keyl++;
1641:
1642: /* count subscripts of key */
1643: i = 0;
1644: j1 = 0;
1645:
1646: while (i < keyl) if (compactkey[i++] & 01)
1647:
1648: j1++;
1649: i = 0;
1650: j = 0;
1651: k = 0;
1652:
1653: while (i < keyl) {
1654:
1655: if (scratch[i] != compactkey[j++]) {
1656: k++;
1657: break;
1658: }
1659:
1660: if (scratch[i++] & 01) k++;
1661:
1662: }
1663:
1664: if (k < j1) {
1665: data[0] = EOL1;
1666: g_o_val[0] = EOL;
1667:
1668: goto quit;
1669: }
1670:
1671: while (--i >= 0) {
1672: if ((scratch[i] & 01)) break;
1673: }
1674:
1675: i++;
1676: j = 0;
1677:
1678: while ((ch = UNSIGN (scratch[i++])) != g_EOL) {
1679:
1680: ch0 = (ch >= SP ? (ch >> 1) : /* 'string' chars */
1681: (ch < 20 ? (ch >> 1) + '0' : /* 0...9 */
1682: (ch >> 1) + SP)); /* '.' or '-' */
1683:
1684:
1685: if (ch0 == DEL) {
1686:
1687: if (((ch = UNSIGN (scratch[i++])) >> 1) == DEL) {
1688: ch0 += DEL;
1689: ch = UNSIGN (scratch[i++]);
1690: }
1691:
1692: ch0 += (ch >> 1);
1693: }
1694:
1695: data[j++] = ch0;
1696:
1697: if (ch & 01) break;
1698:
1699: }
1700:
1701: /* forget that data value if $d=10 */
1702: if (UNSIGN (scratch[i]) != g_EOL) g_o_val[0] = EOL;
1703:
1704: data[j] = EOL1;
1705: ordercounter++;
1706:
1707: if (--ordercnt > 0) { /* repeated forward scanning */
1708:
1709: if (ch != g_EOL) scratch[i] = g_EOL;
1710:
1711: stcpy0 (compactkey, scratch, i + 1);
1712:
1713: compactkey[i - 1] |= 01;
1714: compactkey[i] = OMEGA;
1715: keyl = i + 1;
1716:
1717: goto k_again;
1718:
1719: }
1720:
1721: }
1722:
1723: break;
1724:
1725:
1726: case fra_query:
1727:
1728: if (ordercnt < 1) {
1729: merr_raise (ARGLIST);
1730: goto quit;
1731: }
1732:
1733: if (found == 2) { /* ... advance to next entry */
1734: addr += UNSIGN (block[addr]);
1735: addr += 2; /* skip key */
1736: addr += UNSIGN (block[addr]);
1737: addr++; /* skip data */
1738: }
1739:
1740: offset = UNSIGN (block[OFFS]) * 256 + UNSIGN (block[OFFS + 1]);
1741:
1742: while (--ordercnt > 0) { /* repeated forward query */
1743:
1744: addr += UNSIGN (block[addr]);
1745: addr += 2; /* skip key */
1746: addr += UNSIGN (block[addr]);
1747: addr++; /* skip data */
1748:
1749: if (addr >= offset) { /* look in next block */
1750:
1751: if ((blknbr = UNSIGN (block[RLPTR]) * 65536 + UNSIGN (block[RLPTR + 1]) * 256 + UNSIGN (block[RLPTR + 2])) == 0) {
1752: data[0] = EOL1;
1753:
1754: goto quit; /* no next block */
1755: }
1756:
1.9 snw 1757: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1758: read (g->fd, block, BLOCKLEN);
1.1 snw 1759:
1760: addr = 0;
1761: offset = UNSIGN (block[OFFS]) * 256 +
1762: UNSIGN (block[OFFS + 1]);
1763: }
1764:
1765: }
1766:
1767: if (addr >= offset) { /* look in next block */
1768:
1769: if ((blknbr = UNSIGN (block[RLPTR]) * 65536 + UNSIGN (block[RLPTR + 1]) * 256 + UNSIGN (block[RLPTR + 2])) == 0) {
1770:
1771: if (getnflag) {
1772: zref[0] = EOL;
1773: merr_raise (ARGER);
1774: }
1775:
1776: data[0] = EOL1;
1777:
1778: goto quit; /* no next block */
1779: }
1780:
1.9 snw 1781: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1782: read (g->fd, block, BLOCKLEN);
1.1 snw 1783:
1784: addr = 0;
1785: }
1786:
1787: {
1788: long len;
1789: char key0[256];
1790:
1791: i = 0;
1792:
1793: while (i <= addr) { /* compute offset complete key */
1794:
1795: len = UNSIGN (block[i++]);
1796: len += (j = UNSIGN (block[i++]));
1797:
1798: while (j < len) key0[j++] = block[i++];
1799:
1800: key0[j] = g_EOL;
1801: k = i; /* save i for getnflag processing */
1802: i += UNSIGN (block[i]);
1803: i++; /* skip data */
1804:
1805: }
1806:
1807: if (getnflag) {
1808:
1809: int ch0;
1810:
1811: i = k;
1812: k = UNSIGN (block[i++]);
1813:
1814: stcpy0 (data, &block[i], k); /* get value */
1815:
1816: data[k] = EOL1; /* append EOL */
1817: j = 0;
1818:
1819: while (zref[j] != DELIM && zref[j] != EOL) j++;
1820:
1821: if (zref[j] == EOL) zref[j] = DELIM;
1822:
1823: nakoffs = j;
1824: j++;
1825: i = 0; /* make this ref $ZR */
1826:
1827: while ((ch = UNSIGN (key0[i++])) != g_EOL) {
1828:
1829: ch0 = (ch >= SP ? (ch >> 1) : /* 'string' chars */
1830: (ch < 20 ? (ch >> 1) + '0' : /* 0...9 */
1831: (ch >> 1) + SP)); /* '.' or '-' */
1832:
1833:
1834: if (ch0 == DEL) {
1835:
1836: if (((ch = UNSIGN (key0[i++])) >> 1) == DEL) {
1837: ch0 += DEL;
1838: ch = UNSIGN (key0[i++]);
1839: }
1840:
1841: ch0 += (ch >> 1);
1842:
1843: }
1844:
1845: zref[j++] = ch0;
1.5 snw 1846:
1.1 snw 1847:
1848: if (j >= 252) {
1849: zref[j] = EOL;
1850: merr_raise (M75);
1851:
1852: goto quit;
1853: }
1854:
1855: if (ch & 01) {
1856: nakoffs = j;
1857: zref[j++] = DELIM;
1858: }
1859:
1860: }
1861:
1862: zref[--j] = EOL;
1863:
1864: break;
1865:
1866: }
1867: else { /* save data value for inspection with $V(111) */
1868:
1869: int ch0;
1870:
1871: i = addr;
1872: i += UNSIGN (block[i]);
1873: i += 2; /* skip key */
1874: j = UNSIGN (block[i++]);
1875:
1876: stcpy0 (g_o_val, &block[i], j); /* get value */
1877:
1878: g_o_val[j] = EOL; /* append EOL */
1879:
1880: j = 0;
1881: i = 0;
1882:
1883: while ((data[j] = zref[j]) != DELIM) {
1884:
1885: if (data[j] == EOL1) {
1886: data[j] = '(';
1887: break;
1888: }
1889:
1890: j++;
1891:
1892: }
1893:
1894: data[j++] = '(';
1895: k = 1;
1896:
1897: while ((ch = UNSIGN (key0[i++])) != g_EOL) {
1898: int typ;
1899:
1900: if (k) {
1901: k = 0;
1902:
1903: if ((typ = (ch > SP))) data[j++] = '"';
1904: }
1905:
1906: ch0 = (ch >= SP ? (ch >> 1) : /* 'string' chars */
1907: (ch < 20 ? (ch >> 1) + '0' : /* 0...9 */
1908: (ch >> 1) + SP)); /* '.' or '-' */
1909:
1910: if (ch0 == DEL) {
1911:
1912: if (((ch = UNSIGN (key0[i++])) >> 1) == DEL) {
1913: ch0 += DEL;
1914: ch = UNSIGN (key0[i++]);
1915: }
1916:
1917: ch0 += (ch >> 1);
1918: }
1919:
1920: data[j] = ch0;
1921:
1922: if (j >= 252) {
1923: data[j] = EOL1;
1924: merr_raise (M75);
1925:
1926: goto quit;
1927: }
1928:
1929: if (data[j++] == '"') data[j++] = '"';
1930:
1931: if (ch & 01) {
1932:
1933: if (typ) data[j++] = '"';
1934:
1935: data[j++] = ',';
1936: k = 1;
1937:
1938: }
1939: }
1940:
1941: data[j--] = EOL1;
1942: data[j] = ')';
1943:
1944: }
1945: }
1946:
1947: break;
1948:
1949:
1950: case kill_sym: /* search and destroy */
1951:
1952: killo: /* entry from killone section */
1953: offset = UNSIGN (block[OFFS]) * 256 + UNSIGN (block[OFFS + 1]);
1954:
1955: i = 0;
1956: key1[0] = g_EOL;
1957:
1958: while (i < addr) { /* compute complete key */
1959:
1960: k = UNSIGN (block[i++]);
1961: k += (j = UNSIGN (block[i++]));
1962:
1963: while (j < k) key1[j++] = block[i++];
1964:
1965: key1[j] = g_EOL;
1966: i += UNSIGN (block[i]);
1967:
1968: i++; /* skip data */
1969:
1970: }
1971:
1972: /* look whether there's something to do at all */
1973: if (found != 2) { /* is it a descendant ? */
1974:
1975: char key0[256];
1976: long trxsav;
1977:
1978: if (addr >= offset) { /* nothing to kill in that block */
1979:
1980: blknbr = UNSIGN (block[RLPTR]) * 65536 + UNSIGN (block[RLPTR + 1]) * 256 + UNSIGN (block[RLPTR + 2]);
1981:
1982: if (blknbr == 0) break; /* there is no next block */
1983:
1984: /* maybe there's something in the next block ... */
1985: trxsav = trx;
1986:
1987: for (;;) {
1988:
1989: other = traceblk[--trx];
1990: addr = traceadr[trx];
1991:
1.9 snw 1992: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 1993: read (g->fd, block, BLOCKLEN);
1.1 snw 1994:
1995: addr += UNSIGN (block[addr]);
1996: addr += (2 + PLEN); /* skip previous entry */
1997: offset = UNSIGN (block[OFFS]) * 256 +
1998: UNSIGN (block[OFFS + 1]);
1999: traceadr[trx] = addr;
2000:
2001: if (addr < offset) break;
2002:
2003: traceadr[trx] = 0;
2004: traceblk[trx] = UNSIGN (block[RLPTR]) * 65536 +
2005: UNSIGN (block[RLPTR + 1]) * 256 +
2006: UNSIGN (block[RLPTR + 2]);
2007:
2008: }
2009:
2010: trx = trxsav;
2011:
1.9 snw 2012: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2013: read (g->fd, block, BLOCKLEN);
1.1 snw 2014:
2015: offset = UNSIGN (block[OFFS]) * 256 +
2016: UNSIGN (block[OFFS + 1]);
2017: addr = 0;
2018: k = UNSIGN (block[0]);
2019: stcpy0 (key0, &block[2], k);
2020: key0[k] = g_EOL;
2021:
2022: }
2023: else { /* get following entry */
2024:
2025: stcpy0 (key0, key1, j);
2026: i = addr;
2027: k = UNSIGN (block[i++]);
2028: k += (j = UNSIGN (block[i++]));
2029:
2030: while (j < k) key0[j++] = block[i++];
2031:
2032: key0[j] = g_EOL;
2033: }
2034:
2035: /* is it a descendant? */
2036: i = 0;
2037:
2038: while (compactkey[i] == key0[i]) i++;
2039:
2040: if (compactkey[i] != g_EOL) break; /* nothing to kill */
2041: }
2042:
2043: /* scan this block for all descendants */
2044: {
2045:
2046: long begadr, endadr, len;
2047: char key0[256];
2048:
2049: stcpy0 (key0, compactkey, (long) keyl);
2050:
2051: begadr = endadr = i = addr;
2052:
2053: if (action == killone) {
2054:
2055: i += UNSIGN (block[i]);
2056: i += 2; /* skip key */
2057: i += UNSIGN (block[i]);
2058: i++; /* skip data */
2059:
2060: endadr = i;
2061: }
2062: else {
2063:
2064: while (i < offset) {
2065:
2066: len = UNSIGN (block[i++]);
2067: k = j = UNSIGN (block[i++]);
2068:
2069: if (k >= keyl) {
2070: i += len;
2071: }
2072: else {
2073:
2074: len += k;
2075:
2076: while (j < len) key0[j++] = block[i++];
2077:
2078: key0[j] = g_EOL;
2079:
2080: /* k=0; ueberfluessig */
2081: while (compactkey[k] == key0[k]) {
2082:
2083: if (compactkey[k] == g_EOL) break;
2084:
2085: k++;
2086: }
2087:
2088: if (compactkey[k] != g_EOL) break; /* no descendant */
2089:
2090: }
2091:
2092: i += UNSIGN (block[i]);
2093: i++; /* skip data */
2094: endadr = i;
2095:
2096: }
2097:
2098: }
2099:
2100: kill_again = (endadr == offset && action != killone); /* may be there's more to kill */
2101:
2102: if ((begadr == 0) && (endadr == offset)) { /* block becomes empty */
2103:
2104: long left,
2105: right;
2106: char block0[BLOCKLEN];
2107:
2108: p_empty: /* entry if pointer block goes empty */
2109:
2110: left = UNSIGN (block[LLPTR]) * 65536 +
2111: UNSIGN (block[LLPTR + 1]) * 256 +
2112: UNSIGN (block[LLPTR + 2]);
2113: right = UNSIGN (block[RLPTR]) * 65536 +
2114: UNSIGN (block[RLPTR + 1]) * 256 +
2115: UNSIGN (block[RLPTR + 2]);
2116:
2117: if (left) {
2118:
1.9 snw 2119: lseek (g->fd, hdr_offset + ((long) left * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2120: read (g->fd, block0, BLOCKLEN);
1.1 snw 2121:
2122: block0[RLPTR] = block[RLPTR];
2123: block0[RLPTR + 1] = block[RLPTR + 1];
2124: block0[RLPTR + 2] = block[RLPTR + 2];
2125:
1.9 snw 2126: lseek (g->fd, hdr_offset + ((long) left * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2127: write (g->fd, block0, BLOCKLEN);
1.1 snw 2128:
2129: }
2130:
2131: if (right) {
2132:
1.9 snw 2133: lseek (g->fd, hdr_offset + ((long) right * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2134: read (g->fd, block0, BLOCKLEN);
1.1 snw 2135:
2136: block0[LLPTR] = block[LLPTR];
2137: block0[LLPTR + 1] = block[LLPTR + 1];
2138: block0[LLPTR + 2] = block[LLPTR + 2];
2139:
1.9 snw 2140: lseek (g->fd, hdr_offset + ((long) right * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2141: write (g->fd, block0, BLOCKLEN);
1.1 snw 2142:
2143: }
2144:
1.8 snw 2145: b_free (g->fd, blknbr); /* modify free list */
1.1 snw 2146:
2147: /* delete pointer */
2148: /**************************/
2149: {
2150: long trxsav;
2151: long freecnt;
2152:
2153: trxsav = trx;
2154:
2155: blknbr = traceblk[--trx];
2156: addr = traceadr[trx];
2157:
1.9 snw 2158: lseek (g->fd, hdr_offset + ((long) (blknbr) * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2159: read (g->fd, block, BLOCKLEN);
1.1 snw 2160: offset = UNSIGN (block[OFFS]) * 256 +
2161: UNSIGN (block[OFFS + 1]);
2162: freecnt = UNSIGN (block[addr]) + 2 + PLEN;
2163:
2164: /* delete key */
2165: offset -= freecnt;
2166:
2167: if (offset == 0) { /* pointer block went empty */
2168:
2169: if (blknbr == ROOT) { /* global went empty */
2170:
1.9 snw 2171: lseek (g->fd, hdr_offset + 0L, SEEK_SET);
1.1 snw 2172:
2173: /* note : UNIX does not tell other */
2174: block[BTYP] = EMPTY; /* jobs that a file has been unlinked */
2175:
2176: /* as long as they keep it open. */
2177: /* so we mark this global as EMPTY */
1.8 snw 2178: write (g->fd, block, BLOCKLEN);
2179: gbl_close (g);
1.1 snw 2180: unlink (filnam);
1.8 snw 2181:
2182: gbl_unlock (g);
1.1 snw 2183:
2184: olddes[inuse] = 0;
2185: oldfil[inuse][0] = NUL;
2186: usage[inuse] = 0;
2187:
2188: return;
2189:
2190: }
2191:
2192: goto p_empty; /* clear pointer block */
2193:
2194: }
2195:
2196: block[OFFS] = offset / 256;
2197: block[OFFS + 1] = offset % 256;
2198:
2199: stcpy0 (&block[addr], &block[addr + freecnt], (long) (offset - addr));
2200:
2201: for (i = offset; i < offset + freecnt; block[i++] = 0);
2202:
1.9 snw 2203: lseek (g->fd, hdr_offset + ((long) (blknbr) * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2204: write (g->fd, block, BLOCKLEN);
1.1 snw 2205:
2206: if (addr == 0) { /* update of pointer */
2207: traceadr[trx] = 0;
2208:
1.11 snw 2209: update (g, &block[2], (long) UNSIGN (block[0]));
1.1 snw 2210: }
2211:
2212: trx = trxsav;
2213:
2214: }
2215:
2216: if (kill_again) goto k_again;
2217:
2218: break;
2219: }
2220:
2221: i = begadr;
2222: j = endadr;
2223:
2224: while (j < offset) block[i++] = block[j++];
2225: while (i < offset) block[i++] = 0;
2226:
2227: /** clear rest */
2228: offset += (begadr - endadr);
2229: if (begadr < offset && block[begadr + 1]) { /* new key_offset for next entry */
2230: i = block[begadr];
2231: j = block[begadr + 1];
2232: k = 0;
2233: if (begadr)
2234: while (key0[k] == key1[k])
2235: k++; /* new key_offset */
2236: if (k < j) {
2237: ch = j - k; /* space requirement */
2238: block[begadr] = i + ch; /* new key_length */
2239: block[begadr + 1] = k; /* new key_offset */
2240: i = offset;
2241: j = i + ch;
2242: offset = j;
2243: begadr++;
2244: while (i > begadr)
2245: block[j--] = block[i--];
2246: stcpy0 (&block[begadr + 1], &key0[k], ch);
2247: }
2248: }
2249: block[OFFS] = offset / 256;
2250: block[OFFS + 1] = offset % 256;
1.9 snw 2251: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2252: write (g->fd, block, BLOCKLEN);
1.1 snw 2253: if (addr < 3) { /* update of pointer */
2254: traceadr[trx] = 0;
1.11 snw 2255: update (g, &block[2], (long) UNSIGN (block[0]));
1.1 snw 2256: }
2257: }
2258:
2259: if (kill_again) goto k_again;
2260:
2261: break;
2262:
2263: zinv:
2264:
2265: {
2266: long len;
2267: int ch0;
2268: char scratch[256];
2269: char key0[256];
2270:
2271: if (addr <= 0) { /* look in previous block */
2272:
2273: if ((blknbr = UNSIGN (block[LLPTR]) * 65536 + UNSIGN (block[LLPTR + 1]) * 256 + UNSIGN (block[LLPTR + 2])) == 0) {
2274: data[0] = EOL1;
2275: goto quit;
2276: } /* no previous block */
2277:
1.9 snw 2278: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2279: read (g->fd, block, BLOCKLEN);
1.1 snw 2280:
2281: addr = UNSIGN (block[OFFS]) * 256 +
2282: UNSIGN (block[OFFS + 1]);
2283:
2284: }
2285:
2286: i = 0;
2287:
2288: while (i < addr) { /* compute offset complete key */
2289:
2290: len = UNSIGN (block[i++]);
2291: len += (j = UNSIGN (block[i++]));
2292:
2293: while (j < len) key0[j++] = block[i++];
2294:
2295: key0[j] = g_EOL;
2296: j1 = i;
2297: i += UNSIGN (block[i]);
2298: i++; /* skip data */
2299:
2300: }
2301:
2302: /* save data value for inspection with $V(111) */
2303: j = UNSIGN (block[j1++]);
2304:
2305: stcpy0 (g_o_val, &block[j1], j); /* get value */
2306: g_o_val[j] = EOL; /* append EOL */
2307:
2308: i = 0;
2309: j = 0;
2310:
2311: while ((scratch[j++] = UNSIGN (key0[i++])) != g_EOL);
2312:
2313: /* count subscripts of key */
2314: i = 0;
2315: j1 = 0;
2316:
2317: while (i < keyl) {
2318:
2319: if (compactkey[i++] & 01) {
2320: j1++;
2321: }
2322:
2323: }
2324:
2325: i = 0;
2326: j = 0;
2327: k = 0;
2328:
2329: while (i < keyl) {
2330:
2331: if (scratch[i] != compactkey[j++]) {
2332: k++;
2333: break;
2334: }
2335:
2336: if (scratch[i++] & 01) k++;
2337:
2338: }
2339:
2340: if (k < j1) {
2341: data[0] = EOL1;
2342: g_o_val[0] = EOL;
2343:
2344: goto quit;
2345: }
2346:
2347: while (--i >= 0) {
2348: if ((scratch[i] & 01)) break;
2349: }
2350:
2351: i++;
2352: j = 0;
2353:
2354: while ((ch = UNSIGN (scratch[i++])) != g_EOL) {
2355:
2356: ch0 = (ch >= SP ? (ch >> 1) : /* 'string' chars */
2357: (ch < 20 ? (ch >> 1) + '0' : /* 0...9 */
2358: (ch >> 1) + SP)); /* '.' or '-' */
2359:
2360: if (ch0 == DEL) {
2361:
2362: if (((ch = UNSIGN (scratch[i++])) >> 1) == DEL) {
2363: ch0 += DEL;
2364: ch = UNSIGN (scratch[i++]);
2365: }
2366:
2367: ch0 += (ch >> 1);
2368:
2369: }
2370:
2371: data[j++] = ch0;
2372:
2373: if (ch & 01) break;
2374:
2375: }
2376:
2377: data[j] = EOL1;
2378:
2379: if (j == 0) break;
2380:
2381: ordercounter++;
2382:
2383: if (ordercnt++ < (-1)) { /* repeated backward scanning */
2384:
2385: if (ch != g_EOL) scratch[i] = g_EOL;
2386:
2387: stcpy0 (compactkey, scratch, i + 1);
2388:
2389: compactkey[i - 1] |= 01;
2390: keyl = i;
2391:
2392: goto k_again;
2393:
2394: }
2395:
2396: }
2397:
2398: break;
2399:
2400:
2401: case zdata: /* nonstandard data function */
2402:
2403: {
2404: long counties[128];
2405: char key0[256];
2406: int icnt, icnt0, len;
2407:
2408: i = 0;
2409:
2410: while (i < 128) counties[i++] = 0L; /* init count; */
2411:
2412: if (found == 2) { /* ... advance to next entry */
2413: addr += UNSIGN (block[addr]);
2414: addr += 2; /* skip key */
2415: addr += UNSIGN (block[addr]);
2416: addr++; /* skip data */
2417:
2418: counties[0] = 1L;
2419: }
2420:
2421: offset = UNSIGN (block[OFFS]) * 256 + UNSIGN (block[OFFS + 1]);
2422: icnt = 0;
2423: i = 0;
2424:
2425: while ((ch = compactkey[i++]) != g_EOL) {
2426:
2427: if (ch & 01) {
2428: icnt++;
2429: }
2430:
2431: }
2432:
2433: len = i - 1;
2434: i = 0;
2435:
2436: while (i < addr) { /* compute offset complete key */
2437:
2438: icnt0 = UNSIGN (block[i++]);
2439: icnt0 += (j = UNSIGN (block[i++]));
2440:
2441: while (j < icnt0) key0[j++] = block[i++];
2442:
2443: key0[j] = g_EOL;
2444: i += UNSIGN (block[i]);
2445:
2446: i++; /* skip data */
2447:
2448: }
2449:
2450: for (;;) { /* is it still a descendant ??? */
2451:
2452: if (addr >= offset) { /* look in next block */
2453:
2454: if ((blknbr = UNSIGN (block[RLPTR]) * 65536 + UNSIGN (block[RLPTR + 1]) * 256 + UNSIGN (block[RLPTR + 2])) == 0) {
2455: break; /* no next block */
2456: }
2457:
1.9 snw 2458: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2459: read (g->fd, block, BLOCKLEN);
1.1 snw 2460:
2461: addr = 0;
2462: offset = UNSIGN (block[OFFS]) * 256 +
2463: UNSIGN (block[OFFS + 1]);
2464:
2465: }
2466:
2467: i = UNSIGN (block[addr++]);
2468: i += (j = UNSIGN (block[addr++]));
2469:
2470: while (j < i) key0[j++] = block[addr++];
2471:
2472: addr += UNSIGN (block[addr]);
2473: addr++; /* skip data */
2474: icnt0 = 0;
2475: i = 0;
2476:
2477: while (i < j) if (key0[i++] & 01)
2478:
2479: icnt0++;
2480:
2481: if (icnt0 <= icnt) break;
2482:
2483: i = 0;
2484:
2485: while (i < len) {
2486:
2487: if (key0[i] != compactkey[i]) break;
2488:
2489: i++;
2490:
2491: }
2492:
2493: if (i < len) break;
2494:
2495: counties[icnt0 - icnt]++;
2496:
2497: }
2498:
2499: i = 128;
2500:
2501: while (counties[--i] == 0L);
2502:
2503: lintstr (data, counties[0]);
2504:
2505: j = 1;
2506: tmp1[0] = ',';
2507:
2508: while (j <= i) {
2509: lintstr (&tmp1[1], counties[j++]);
2510: stcat (data, tmp1);
2511: }
2512:
2513: }
2514:
2515: break;
2516:
2517: case getinc:
2518:
2519: {
2520: int setopsav;
2521:
2522: setopsav = setop;
2523: setop = '+';
2524: data[0] = '1';
2525: data[1] = EOL;
2526:
2527: global (set_sym, key, data);
2528:
2529: setop = setopsav;
2530:
2531: return;
2532: }
2533:
2534: case killone:
2535:
2536: {
2537: if (found == 2) goto killo; /* entry found use normal kill routine */
2538:
2539: goto quit;
2540: }
2541:
2542: case merge_sym:
2543:
2544: printf("MERGE NOT IMPLEMENTED FOR GLOBALS\n");
2545:
2546: #ifdef DEBUG_GBL
2547:
2548: int loop;
2549:
2550: printf ("DEBUG MERGE: ");
2551: printf ("[key] is [");
2552:
2553: for (loop = 0; key[loop] != EOL; loop++) printf ("%c", (key[loop] == DELIM) ? '!' : key[loop]);
2554:
2555: printf ("]\r\n");
2556: printf ("[data] is [");
2557:
2558: for(loop = 0; data[loop] != EOL; loop++) printf ("%c", (data[loop] == DELIM) ? '!' : data[loop]);
2559:
2560: printf("]\r\n");
2561:
2562: #endif
2563: return;
2564:
2565: default:
2566:
2567: merr_raise (INVREF); /* accidental call with wrong action code (v22-stuff) */
2568: } /* end of switch */
2569:
2570: quit:
2571:
2572: /* clean things up */
2573:
1.9 snw 2574: lseek (g->fd, hdr_offset + ROOT, SEEK_SET);
1.8 snw 2575: gbl_unlock (g);
2576:
1.1 snw 2577: return;
2578:
2579:
2580: splitd: /* split data block in two sections */
2581:
2582: /* part of the data is taken to an other location. */
2583: /* old information in 'block' stored at 'blknbr' */
2584: /* 'addr' there I would like to insert, if possible (which is not) */
2585: /* 'offset' filled up to this limit */
2586:
1.8 snw 2587: getnewblk (g->fd, &newblk); /* get a new block */
1.1 snw 2588:
2589: /* if we have to insert at the begin or end of a block */
2590: /* we don't split - we just start a new block */
2591: /* if an insert in the midst of a block, we split */
2592:
2593: if (addr >= offset) {
2594: long right,
2595: right1,
2596: right2;
2597:
2598: right = UNSIGN (block[RLPTR]);
2599: right1 = UNSIGN (block[RLPTR + 1]);
2600: right2 = UNSIGN (block[RLPTR + 2]);
2601: block[RLPTR] = newblk / 65536;
2602: block[RLPTR + 1] = newblk % 65536 / 256;
2603: block[RLPTR + 2] = newblk % 256;
2604:
1.9 snw 2605: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2606: write (g->fd, block, BLOCKLEN);
1.1 snw 2607:
2608: block[RLPTR] = right;
2609: block[RLPTR + 1] = right1;
2610: block[RLPTR + 2] = right2;
2611: block[LLPTR] = blknbr / 65536;
2612: block[LLPTR + 1] = blknbr % 65536 / 256;
2613: block[LLPTR + 2] = blknbr % 256;
2614: offset = 0;
2615: addr = 0;
2616: blknbr = newblk;
2617:
1.11 snw 2618: insert (g, compactkey, keyl, newblk);
1.1 snw 2619:
2620: /* up-date LL-PTR of RL-block */
2621: if ((other = right * 65536 + right1 * 256 + right2)) {
2622:
2623: char block0[BLOCKLEN];
2624:
1.9 snw 2625: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2626: read (g->fd, block0, BLOCKLEN);
1.1 snw 2627:
2628: block0[LLPTR] = blknbr / 65536;
2629: block0[LLPTR + 1] = blknbr % 65536 / 256;
2630: block0[LLPTR + 2] = blknbr % 256;
2631:
1.9 snw 2632: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2633: write (g->fd, block0, BLOCKLEN);
1.1 snw 2634:
2635: }
2636:
2637: goto spltex;
2638: }
2639:
2640: if (addr == 0) {
2641: long left, left1, left2;
2642:
2643: left = UNSIGN (block[LLPTR]);
2644: left1 = UNSIGN (block[LLPTR + 1]);
2645: left2 = UNSIGN (block[LLPTR + 2]);
2646:
2647: block[LLPTR] = newblk / 65536;
2648: block[LLPTR + 1] = newblk % 65536 / 256;
2649: block[LLPTR + 2] = newblk % 256;
2650:
1.9 snw 2651: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2652: write (g->fd, block, BLOCKLEN);
1.1 snw 2653:
2654: block[LLPTR] = left;
2655: block[LLPTR + 1] = left1;
2656: block[LLPTR + 2] = left2;
2657: block[RLPTR] = blknbr / 65536;
2658: block[RLPTR + 1] = blknbr % 65536 / 256;
2659: block[RLPTR + 2] = blknbr % 256;
2660: offset = 0;
2661: blknbr = newblk;
2662: traceadr[trx] = (-1); /* inhibit second update of pointers */
2663:
1.11 snw 2664: insert (g, compactkey, keyl, newblk);
1.1 snw 2665:
2666: if (addr < 3) { /* update of pointer */
2667: traceadr[trx] = 0;
2668:
1.11 snw 2669: update (g, compactkey, keyl);
1.1 snw 2670: }
2671:
2672: /* other is ***always*** zero !!!
2673: * if (other=left*65536+left1*256+left2) up-date RL-PTR of LL-block
2674: * { char block0[BLOCKLEN];
2675: * lseek(filedes,(long)other*(long)(BLOCKLEN),0);
2676: * read(filedes,block0,BLOCKLEN);
2677: * block0[RLPTR ]=blknbr/65536;
2678: * block0[RLPTR+1]=blknbr%65536/256;
2679: * block0[RLPTR+2]=blknbr%256;
2680: * lseek(filedes,(long)other*(long)(BLOCKLEN),0);
2681: * write(filedes,block0,BLOCKLEN);
2682: * }
2683: */
2684:
2685: goto spltex;
2686:
2687: }
2688:
2689: {
2690: char block0[BLOCKLEN];
2691: char key0[256];
2692: int newlimit;
2693:
2694: block0[BTYP] = DATA;
2695:
2696: /* now search for a dividing line */
2697: limit = (offset + needed) / 2;
2698: i = (offset - needed) / 2;
2699:
2700: if (addr < i) limit = i;
2701:
2702: i = 0;
2703: newlimit = i;
2704:
2705: while (i < limit) {
2706:
2707: newlimit = i;
2708: j = UNSIGN (block[i++]); /* length of key - offset */
2709: k = UNSIGN (block[i++]); /* offset into previous entry */
2710: j += k;
2711:
2712: while (k < j) key0[k++] = block[i++]; /* get key */
2713:
2714: key0[k] = g_EOL;
2715: i += UNSIGN (block[i]);
2716: i++; /* skip data */
2717:
2718: }
2719:
2720: limit = newlimit;
2721: i = newlimit;
2722:
2723: j = i;
2724: i = 0; /* copy part of old to new blk */
2725:
2726: if ((k = UNSIGN (block[j + 1])) != 0) { /* expand key */
2727:
2728: block0[i++] = UNSIGN (block[j++]) + k;
2729: block0[i++] = 0;
2730:
2731: if (addr > limit) addr += k;
2732:
2733: j = 0;
2734:
2735: while (j < k) block0[i++] = key0[j++];
2736:
2737: j = limit + 2;
2738:
2739: }
2740:
2741: while (j < offset) {
2742:
2743: block0[i++] = block[j];
2744: block[j] = 0;
2745:
2746: j++;
2747:
2748: }
2749:
2750: while (i <= DATALIM) block0[i++] = 0; /* clear rest of block */
2751:
2752: offset -= limit;
2753:
2754: if (k > 0) offset += k; /* new offsets */
2755:
2756: block[OFFS] = limit / 256;
2757: block[OFFS + 1] = limit % 256;
2758: block0[OFFS] = offset / 256;
2759: block0[OFFS + 1] = offset % 256;
2760:
2761: if (addr <= limit) { /* save new block away */
2762:
2763: /* update rightlink and leftlink pointers */
2764: other = UNSIGN (block[RLPTR]) * 65536 +
2765: UNSIGN (block[RLPTR + 1]) * 256 +
2766: UNSIGN (block[RLPTR + 2]);
2767: block0[RLPTR] = block[RLPTR];
2768: block0[RLPTR + 1] = block[RLPTR + 1];
2769: block0[RLPTR + 2] = block[RLPTR + 2];
2770: block[RLPTR] = newblk / 65536;
2771: block[RLPTR + 1] = newblk % 65536 / 256;
2772: block[RLPTR + 2] = newblk % 256;
2773: block0[LLPTR] = blknbr / 65536;
2774: block0[LLPTR + 1] = blknbr % 65536 / 256;
2775: block0[LLPTR + 2] = blknbr % 256;
2776:
1.9 snw 2777: lseek (g->fd, hdr_offset + ((long) (newblk) * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2778: write (g->fd, block0, BLOCKLEN);
1.1 snw 2779:
2780: offset = limit;
2781: /* insert new block in pointer structure */
2782:
1.11 snw 2783: insert (g, &block0[2], (long) UNSIGN (block0[0]), newblk);
1.1 snw 2784:
2785: /* up-date LL-PTR of RL-block */
2786: if (other != 0) {
2787:
1.9 snw 2788: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2789: read (g->fd, block0, BLOCKLEN);
1.1 snw 2790:
2791: block0[LLPTR] = newblk / 65536;
2792: block0[LLPTR + 1] = newblk % 65536 / 256;
2793: block0[LLPTR + 2] = newblk % 256;
2794:
1.9 snw 2795: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2796: write (g->fd, block0, BLOCKLEN);
1.1 snw 2797:
2798: }
2799:
2800: }
2801: else {
2802: /* save old block away and make new block the current block */
2803: /* update rightlink and leftlink pointers */
2804: other = UNSIGN (block[RLPTR]) * 65536 +
2805: UNSIGN (block[RLPTR + 1]) * 256 +
2806: UNSIGN (block[RLPTR + 2]);
2807: block0[RLPTR] = block[RLPTR];
2808: block0[RLPTR + 1] = block[RLPTR + 1];
2809: block0[RLPTR + 2] = block[RLPTR + 2];
2810: block[RLPTR] = newblk / 65536;
2811: block[RLPTR + 1] = newblk % 65536 / 256;
2812: block[RLPTR + 2] = newblk % 256;
2813: block0[LLPTR] = blknbr / 65536;
2814: block0[LLPTR + 1] = blknbr % 65536 / 256;
2815: block0[LLPTR + 2] = blknbr % 256;
2816:
1.9 snw 2817: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2818: write (g->fd, block, BLOCKLEN);
1.1 snw 2819: stcpy0 (block, block0, (long) BLOCKLEN);
2820:
2821: traceadr[trx] = (addr -= limit);
2822: traceblk[trx] = (blknbr = newblk);
2823:
2824: /* insert new block in pointer structure */
1.11 snw 2825: insert (g, &block0[2], (long) UNSIGN (block0[0]), newblk);
1.1 snw 2826:
2827: /* up-date LL-PTR of RL-block */
2828: if (other != 0) {
2829:
1.9 snw 2830: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2831: read (g->fd, block0, BLOCKLEN);
1.1 snw 2832:
2833: block0[LLPTR] = newblk / 65536;
2834: block0[LLPTR + 1] = newblk % 65536 / 256;
2835: block0[LLPTR + 2] = newblk % 256;
2836:
1.9 snw 2837: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
1.8 snw 2838: write (g->fd, block0, BLOCKLEN);
1.1 snw 2839:
2840: }
2841:
2842: }
2843: }
2844:
2845: spltex:
2846:
2847: if (ret_to == 'n') goto s10;
2848:
2849: goto s20;
2850: } /* end global() */
2851:
2852: /*
2853: * split pointer block in two sections
2854: * filedes: global file descriptor
2855: * block: old block (which is too small)
2856: * addr: addr of entry where to insert
2857: * offs: offset of block
2858: * blknbr: number of old block
2859: *
2860: * part of the data is taken to an other location.
2861: * old information in 'block' stored at 'blknbr'
2862: * 'addr' there I would like to insert, if possible (which is not)
2863: * 'offset' filled up to this limit
2864: */
1.11 snw 2865: static void splitp (global_handle *g, char *block, long *addr, long *offs, unsigned long *blknbr)
1.1 snw 2866: {
2867:
2868: char block0[BLOCKLEN];
2869: long limit;
2870: unsigned long newblk;
2871: unsigned long other;
2872: register int i, j;
2873:
1.9 snw 2874: long hdr_offset;
2875:
2876: hdr_offset = sizeof (global_header);
2877:
1.11 snw 2878: getnewblk (g->fd, &newblk); /* get a new block */
1.1 snw 2879:
2880: if (*blknbr == ROOT) { /* ROOT overflow is special */
2881:
2882: stcpy0 (block0, block, (long) BLOCKLEN);
2883:
2884: /* clear pointers */
2885: block[LLPTR] = 0;
2886: block[LLPTR + 1] = 0;
2887: block[LLPTR + 2] = 0;
2888: block[RLPTR] = 0;
2889: block[RLPTR + 1] = 0;
2890: block[RLPTR + 2] = 0;
2891:
2892: /* old root block is a 'normal' block now */
2893: /* new root points to a single block */
2894: i = UNSIGN (block0[0]) + 2;
2895: block0[i++] = newblk / 65536;
2896: block0[i++] = newblk % 65536 / 256;
2897: block0[i++] = newblk % 256;
2898: block0[OFFS] = i / 256;
2899: block0[OFFS + 1] = i % 256;
2900:
2901: while (i < DATALIM) block0[i++] = 0; /* clear rest */
2902:
2903: /* update number of blocks ! */
2904: i = UNSIGN (block0[NRBLK]) * 65536 +
2905: UNSIGN (block0[NRBLK + 1]) * 256 +
2906: UNSIGN (block0[NRBLK + 2]) + 1;
2907:
2908: block0[NRBLK] = i / 65536;
2909: block0[NRBLK + 1] = i % 65536 / 256;
2910: block0[NRBLK + 2] = i % 256;
2911: block0[BTYP] = POINTER;
2912:
1.11 snw 2913: lseek (g->fd, hdr_offset + ROOT, SEEK_SET);
2914: write (g->fd, block0, BLOCKLEN);
1.1 snw 2915:
2916: /* shift trace_stack */
2917: j = trx++;
2918: i = trx;
2919:
2920: /** if (j>=TRLIM) 'global too big' */
2921: while (j >= 0) {
2922: traceblk[i] = traceblk[j];
2923: traceadr[i--] = traceadr[j--];
2924: }
2925:
2926: traceblk[0] = 0; /*ROOT */
2927: traceadr[0] = 0;
2928: traceblk[1] = newblk;
2929: *blknbr = newblk;
2930:
1.11 snw 2931: getnewblk (g->fd, &newblk); /* get a new block */
1.1 snw 2932:
2933: }
2934:
2935: block0[BTYP] = block[BTYP];
2936:
2937: /* now search for a dividing line */
2938: i = 0;
2939: limit = (*offs) / 2;
2940:
2941: while (i < limit) {
2942: i += UNSIGN (block[i]);
2943: i += 2; /* skip key */
2944: i += PLEN; /* skip data */
2945: }
2946:
2947: /* new offsets */
2948: limit = i;
2949:
2950: i = (*offs) - limit;
2951:
2952: block[OFFS] = limit / 256;
2953: block[OFFS + 1] = limit % 256;
2954: block0[OFFS] = i / 256;
2955: block0[OFFS + 1] = i % 256;
2956:
2957: for (i = 0; i <= DATALIM; block0[i++] = 0);
2958:
2959: i = 0;
2960: j = limit; /* copy part of old to new blk */
2961:
2962: while (j < (*offs)) {
2963: block0[i++] = block[j];
2964: block[j] = 0;
2965:
2966: j++;
2967: }
2968:
2969: if ((*addr) <= limit) { /* save new block away */
2970:
2971: /* update rightlink and leftlink pointers */
2972: other = UNSIGN (block[RLPTR]) * 65536 +
2973: UNSIGN (block[RLPTR + 1]) * 256 +
2974: UNSIGN (block[RLPTR + 2]);
2975: block0[RLPTR] = block[RLPTR];
2976: block0[RLPTR + 1] = block[RLPTR + 1];
2977: block0[RLPTR + 2] = block[RLPTR + 2];
2978: block[RLPTR] = newblk / 65536;
2979: block[RLPTR + 1] = newblk % 65536 / 256;
2980: block[RLPTR + 2] = newblk % 256;
2981: block0[LLPTR] = (*blknbr) / 65536;
2982: block0[LLPTR + 1] = (*blknbr) % 65536 / 256;
2983: block0[LLPTR + 2] = (*blknbr) % 256;
2984:
1.11 snw 2985: lseek (g->fd, hdr_offset + ((long) (newblk) * (long) (BLOCKLEN)), SEEK_SET);
2986: write (g->fd, block0, BLOCKLEN);
1.1 snw 2987:
2988: (*offs) = limit;
2989:
1.11 snw 2990: insert (g, &block0[2], (long) UNSIGN (block0[0]), newblk);
1.1 snw 2991:
2992: /* up-date LL-PTR of RL-block */
2993: if (other != 0) {
2994:
1.11 snw 2995: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
2996: read (g->fd, block0, BLOCKLEN);
1.1 snw 2997:
2998: block0[LLPTR] = newblk / 65536;
2999: block0[LLPTR + 1] = newblk % 65536 / 256;
3000: block0[LLPTR + 2] = newblk % 256;
3001:
1.11 snw 3002: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
3003: write (g->fd, block0, BLOCKLEN);
1.1 snw 3004:
3005: }
3006:
3007: }
3008: else { /* save old block away and make new block the current block */
3009:
3010: /* update rightlink and leftlink pointers */
3011: other = UNSIGN (block[RLPTR]) * 65536 +
3012: UNSIGN (block[RLPTR + 1]) * 256 +
3013: UNSIGN (block[RLPTR + 2]);
3014:
3015: block0[RLPTR] = block[RLPTR];
3016: block0[RLPTR + 1] = block[RLPTR + 1];
3017: block0[RLPTR + 2] = block[RLPTR + 2];
3018: block[RLPTR] = newblk / 65536;
3019: block[RLPTR + 1] = newblk % 65536 / 256;
3020: block[RLPTR + 2] = newblk % 256;
3021: block0[LLPTR] = (*blknbr) / 65536;
3022: block0[LLPTR + 1] = (*blknbr) % 65536 / 256;
3023: block0[LLPTR + 2] = (*blknbr) % 256;
3024:
3025: (*addr) -= limit;
3026: (*offs) -= limit;
3027:
1.11 snw 3028: lseek (g->fd, hdr_offset + ((long) (*blknbr) * (long) (BLOCKLEN)), SEEK_SET);
3029: write (g->fd, block, BLOCKLEN);
1.1 snw 3030: stcpy0 (block, block0, (long) BLOCKLEN);
3031:
3032: (*blknbr) = newblk;
3033:
1.11 snw 3034: insert (g, &block0[2], (long) UNSIGN (block0[0]), newblk);
1.1 snw 3035:
3036: /* up-date LL-PTR of RL-block */
3037: if (other != 0) {
3038:
1.11 snw 3039: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
3040: read (g->fd, block0, BLOCKLEN);
1.1 snw 3041:
3042: block0[LLPTR] = newblk / 65536;
3043: block0[LLPTR + 1] = newblk % 65536 / 256;
3044: block0[LLPTR + 2] = newblk % 256;
3045:
1.11 snw 3046: lseek (g->fd, hdr_offset + ((long) other * (long) (BLOCKLEN)), SEEK_SET);
3047: write (g->fd, block0, BLOCKLEN);
1.1 snw 3048:
3049: }
3050:
3051: }
3052:
3053: return;
3054:
3055: } /* end of splitp() */
3056:
3057: /* update pointer
3058: * filedes: file descriptor
3059: * ins_key: key to be inserted
3060: * keyl: length of that key
3061: */
1.11 snw 3062: static void update (global_handle *g, char *ins_key, long keyl)
1.1 snw 3063: {
3064: long offset;
3065: long addr;
3066: unsigned long blknbr;
3067: char block[BLOCKLEN];
3068: long i, j, k;
1.9 snw 3069: long hdr_offset;
1.1 snw 3070:
1.9 snw 3071: hdr_offset = sizeof (global_header);
3072:
1.1 snw 3073: while (traceadr[trx] == 0) { /* update of pointer blocks necessary */
3074:
3075: if (--trx < 0) break;
3076:
3077: blknbr = traceblk[trx];
3078: addr = traceadr[trx];
3079:
1.11 snw 3080: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
3081: read (g->fd, block, BLOCKLEN);
1.1 snw 3082:
3083: {
3084: long oldkeyl;
3085:
3086: oldkeyl = UNSIGN (block[addr]);
3087:
3088: i = addr + keyl + 1;
3089: j = oldkeyl - keyl;
3090:
3091: offset = UNSIGN (block[OFFS]) * 256 +
3092: UNSIGN (block[OFFS + 1]);
3093:
3094: if (j > 0) { /* surplus space */
3095:
3096: k = offset;
3097: offset -= j;
3098: j += i;
3099:
3100: while (i < offset) block[i++] = block[j++];
3101:
3102: while (i < k) block[i++] = 0; /* clear area */
3103:
3104: }
3105: else if (j < 0) { /* we need more space */
3106:
3107: /* block too small */
1.11 snw 3108: if ((offset - j) > DATALIM) splitp (g, block, &addr, &offset, &blknbr);
1.1 snw 3109:
3110: i = offset;
3111: offset -= j;
3112: j = offset;
3113: k = addr + oldkeyl;
3114:
3115: while (i > k) block[j--] = block[i--];
3116:
3117: }
3118:
3119: block[OFFS] = offset / 256;
3120: block[OFFS + 1] = offset % 256;
3121: block[addr] = keyl;
3122:
3123: /* overwrite */
3124: i = 0;
3125: j = (++addr);
3126: block[j++] = 0; /*!!! */
3127:
3128: while (i < keyl) block[j++] = ins_key[i++];
3129:
3130: /* block pointed to remains the same */
1.11 snw 3131: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
3132: write (g->fd, block, BLOCKLEN);
1.1 snw 3133:
3134: }
3135:
1.11 snw 3136: lseek (g->fd, hdr_offset + ((long) blknbr * (long) (BLOCKLEN)), SEEK_SET);
3137: read (g->fd, block, BLOCKLEN);
1.1 snw 3138:
3139: }
3140:
3141: return;
3142:
3143: } /* end of update() */
3144:
3145: /*
3146: * insert pointer
3147: * filedes: file descriptor
3148: * ins_key: key to be inserted
3149: * key_len: length of that key
3150: * blknbr: key points to this block
3151: */
1.11 snw 3152: static void insert (global_handle *g, char *ins_key, long key_len, unsigned long blknbr) /* insert pointer */
1.1 snw 3153: {
3154: unsigned long blk;
3155: char block[BLOCKLEN];
3156: long trxsav;
3157: long offset;
3158: long needed;
3159: long addr;
3160: register int i, k;
1.9 snw 3161: long hdr_offset;
1.1 snw 3162:
1.9 snw 3163: hdr_offset = sizeof (global_header);
3164:
1.1 snw 3165: trxsav = trx--;
3166: blk = traceblk[trx];
3167: addr = traceadr[trx];
3168:
1.11 snw 3169: lseek (g->fd, hdr_offset + ((long) (blk) * (long) (BLOCKLEN)), SEEK_SET);
3170: read (g->fd, block, BLOCKLEN);
1.1 snw 3171:
3172: offset = UNSIGN (block[OFFS]) * 256 +
3173: UNSIGN (block[OFFS + 1]);
3174:
3175: if (traceadr[trx + 1] != (-1)) {
3176: addr += UNSIGN (block[addr]);
3177: addr += (2 + PLEN);
3178: } /* advance to next entry */
3179:
3180: needed = key_len + 2 + PLEN;
3181:
1.11 snw 3182: if ((offset + needed) > DATALIM) splitp (g, block, &addr, &offset, &blk);
1.1 snw 3183:
3184: /* insert key */
3185: i = (offset += needed);
3186:
3187: block[OFFS] = i / 256;
3188: block[OFFS + 1] = i % 256;
3189:
3190: while (i >= addr) {
3191: block[i] = block[i - needed];
3192: i--;
3193: }
3194:
3195: i = addr + 2;
3196: k = 0;
3197:
3198: while (k < key_len) block[i++] = ins_key[k++];
3199:
3200: block[addr] = k;
3201: block[addr + 1] = 0; /* !!! */
3202: block[i++] = blknbr / 65536;
3203: block[i++] = blknbr % 65536 / 256;
3204: block[i] = blknbr % 256;
3205:
1.11 snw 3206: lseek (g->fd, hdr_offset + ((long) (blk) * (long) (BLOCKLEN)), SEEK_SET);
3207: write (g->fd, block, BLOCKLEN);
1.1 snw 3208:
3209: trx = trxsav;
3210:
3211: return;
3212: } /* end of insert() */
3213:
3214: /*
3215: * mark 'blknbr' as free
3216: * filedes: global file descriptor
3217: * blknbr: block to be freed
3218: */
3219: static void b_free (short filedes, unsigned long blknbr)
3220: {
3221: char block0[BLOCKLEN];
3222: unsigned long free;
3223: unsigned long other;
3224: long i;
3225: long offset;
1.9 snw 3226: long hdr_offset;
3227:
3228: hdr_offset = sizeof (global_header);
3229:
1.1 snw 3230: /* mark block as empty */
1.9 snw 3231: lseek (filedes, hdr_offset + ((long) (blknbr) * BLOCKLEN), SEEK_SET);
1.1 snw 3232: read (filedes, block0, BLOCKLEN);
3233:
3234: block0[BTYP] = EMPTY;
3235:
1.9 snw 3236: lseek (filedes, hdr_offset + ((long) (blknbr) * BLOCKLEN), SEEK_SET);
1.1 snw 3237: write (filedes, block0, BLOCKLEN);
3238:
3239: /* do we have a list of free blocks? */
1.9 snw 3240: lseek (filedes, hdr_offset + ROOT, SEEK_SET);
1.1 snw 3241: read (filedes, block0, BLOCKLEN);
3242:
3243: if ((free = UNSIGN (block0[FREE]) * 65536 + UNSIGN (block0[FREE + 1]) * 256 + UNSIGN (block0[FREE + 2]))) {
3244:
3245: for (;;) {
3246:
1.9 snw 3247: lseek (filedes, hdr_offset + ((long) free * (long) BLOCKLEN), SEEK_SET);
1.1 snw 3248: read (filedes, block0, BLOCKLEN);
3249:
3250: other = UNSIGN (block0[RLPTR]) * 65536 +
3251: UNSIGN (block0[RLPTR + 1]) * 256 +
3252: UNSIGN (block0[RLPTR + 2]);
3253:
3254: if (other == 0) break;
3255:
3256: free = other;
3257:
3258: }
3259:
3260: offset = UNSIGN (block0[OFFS]) * 256 + UNSIGN (block0[OFFS + 1]);
3261:
3262: /* if list is full, start new page */
3263: if (offset > (DATALIM - PLEN)) {
3264:
3265: offset -= PLEN;
3266: other = UNSIGN (block0[offset]) * 65536 +
3267:
3268: UNSIGN (block0[offset + 1]) * 256 +
3269: UNSIGN (block0[offset + 2]);
3270:
3271: block0[offset] = 0;
3272: block0[offset + 1] = 0;
3273: block0[offset + 2] = 0;
3274: block0[OFFS] = offset / 256;
3275: block0[OFFS + 1] = offset % 256;
3276: block0[RLPTR] = other / 65536;
3277: block0[RLPTR + 1] = other % 65536 / 256;
3278: block0[RLPTR + 2] = other % 256;
3279:
1.9 snw 3280: lseek (filedes, hdr_offset + ((long) free * (long) BLOCKLEN), SEEK_SET);
1.1 snw 3281: write (filedes, block0, BLOCKLEN);
3282:
3283: for (i = 0; i < BLOCKLEN; block0[i++] = 0); /* clear block */
3284:
3285: block0[BTYP] = FBLK;
3286: block0[LLPTR] = free / 65536;
3287: block0[LLPTR + 1] = free % 65536 / 256;
3288: block0[LLPTR + 2] = free % 256;
3289: offset = 0;
3290:
3291: free = other;
3292:
3293: }
3294:
3295: }
3296: else {
3297:
3298: getnewblk (filedes, &free);
3299:
3300: /* set FBLK free blocks pointer */
1.9 snw 3301: lseek (filedes, hdr_offset + ROOT, SEEK_SET);
1.1 snw 3302: read (filedes, block0, BLOCKLEN);
3303:
3304: block0[FREE] = free / 65536;
3305: block0[FREE + 1] = free % 65536 / 256;
3306: block0[FREE + 2] = free % 256;
3307:
1.9 snw 3308: lseek (filedes, hdr_offset + ROOT, SEEK_SET);
1.1 snw 3309: write (filedes, block0, BLOCKLEN);
3310:
3311: for (i = 0; i < BLOCKLEN; block0[i++] = 0); /* clear block */
3312:
3313: block0[BTYP] = FBLK;
3314: offset = 0;
3315: }
3316:
3317: /* enter 'blknbr' */
3318: block0[offset++] = blknbr / 65536;
3319: block0[offset++] = blknbr % 65536 / 256;
3320: block0[offset++] = blknbr % 256;
3321: block0[OFFS] = offset / 256;
3322: block0[OFFS + 1] = offset % 256;
3323:
1.9 snw 3324: lseek (filedes, hdr_offset + ((long) free * (long) BLOCKLEN), SEEK_SET);
1.1 snw 3325: write (filedes, block0, BLOCKLEN);
3326:
3327: return;
3328: } /* end of b_free() */
3329:
3330: /*
3331: * scan pointer 'block' for 'compactkey'
3332: *
3333: * 'adr' will return an adress
3334: * 2 heureka; key found at adr
3335: * 1 not found, adr=following entry
3336: */
3337: static void scanpblk (char *block, long *adr, long *fnd)
3338: {
3339: register int i = 0;
3340: register int k;
3341: long j, offset, len;
3342: char key0[256];
3343:
3344: *adr = 0;
3345: offset = UNSIGN (block[OFFS]) * 256 + UNSIGN (block[OFFS + 1]);
3346:
3347: while (i < offset) {
3348:
3349: #ifdef VERSNEW
3350:
3351: j = i; /* save adress of current entry */
3352: len = UNSIGN (block[i++]);
3353:
3354: stcpy0 (key0, &block[++i], len);
3355:
3356: key0[len] = g_EOL;
3357: i += len;
3358:
3359: #else
3360:
3361: j = i++;
3362: len = UNSIGN (block[j]);
3363: k = 0;
3364: i++;
3365:
3366: while (k < len) key0[k++] = block[i++];
3367:
3368: key0[k] = g_EOL;
3369:
3370: #endif /* VERSNEW */
3371:
3372: if (((*fnd) = g_collate (key0)) == 1) return;
3373:
3374: *adr = j;
3375:
3376: if ((*fnd) == 2) return;
3377:
3378: i += PLEN;
3379:
3380: }
3381:
3382: return;
3383:
3384: } /* end of scanpblk() */
3385:
3386: /*
3387: * scan 'block' for 'compactkey'
3388: * same stuff as scanpblk for the params.
3389: */
3390: static void scandblk (char *block, long *adr, long *fnd)
3391: {
3392: register int i = 0;
3393: register int k;
3394: long offset, len;
3395: char key0[256];
3396:
3397: offset = UNSIGN (block[OFFS]) * 256 +
3398: UNSIGN (block[OFFS + 1]);
3399:
3400: while (i < offset) {
3401:
3402: #ifdef VERSNEW
3403:
3404: *adr = i;
3405:
3406: len = UNSIGN (block[i++]);
3407: k = UNSIGN (block[i++]);
3408:
3409: stcpy0 (&key0[k], &block[i], len);
3410:
3411: key0[k + len] = g_EOL;
3412: i += len;
3413:
3414: #else
3415:
3416: *adr = i++;
3417:
3418: len = UNSIGN (block[*adr]) + (k = UNSIGN (block[i++]));
3419:
3420: while (k < len) key0[k++] = block[i++];
3421:
3422: key0[k] = g_EOL;
3423:
3424: #endif /* VERSNEW */
3425:
3426: if (((*fnd) = g_collate (key0)) != 0) return;
3427:
3428: i += UNSIGN (block[i]);
3429:
3430: i++; /* skip data */
3431:
3432: }
3433:
3434: *adr = i;
3435:
3436: return;
3437:
3438: } /* end of scandblk() */
3439:
3440: /*
3441: * get a new block
3442: * filedes: global file descriptor
3443: * blknbr: number of new block
3444: */
3445: static void getnewblk (int filedes, unsigned long *blknbr)
3446: {
3447: char nblock[BLOCKLEN];
3448: unsigned long freeblks, no_of_blks;
3449: long other;
3450: long offset;
1.9 snw 3451: long hdr_offset;
1.1 snw 3452:
1.9 snw 3453: hdr_offset = sizeof (global_header);
3454:
3455: lseek (filedes, hdr_offset + ROOT, SEEK_SET);
1.1 snw 3456: read (filedes, nblock, BLOCKLEN);
3457:
3458: freeblks = UNSIGN (nblock[FREE]) * 65536 + UNSIGN (nblock[FREE + 1]) * 256 + UNSIGN (nblock[FREE + 2]);
3459: no_of_blks = UNSIGN (nblock[NRBLK]) * 65536 + UNSIGN (nblock[NRBLK + 1]) * 256 + UNSIGN (nblock[NRBLK + 2]);
3460:
3461: if (freeblks) {
3462:
1.9 snw 3463: lseek (filedes, hdr_offset + ((long) (freeblks) * BLOCKLEN), SEEK_SET);
1.1 snw 3464: read (filedes, nblock, BLOCKLEN);
3465:
3466: offset = UNSIGN (nblock[OFFS]) * 256 + UNSIGN (nblock[OFFS + 1]);
3467:
3468: if (offset == 0) { /* free list is empty. return free list blk as new block. */
3469:
3470: *blknbr = freeblks;
3471: other = UNSIGN (nblock[RLPTR]) * 65536 + UNSIGN (nblock[RLPTR + 1]) * 256 + UNSIGN (nblock[RLPTR + 2]);
3472:
3473: /* update RL-block, if any */
3474: if (other) {
3475:
1.9 snw 3476: lseek (filedes, hdr_offset + ((long) (other) * BLOCKLEN), SEEK_SET);
1.1 snw 3477: read (filedes, nblock, BLOCKLEN);
3478:
3479: nblock[LLPTR] = 0;
3480: nblock[LLPTR + 1] = 0;
3481: nblock[LLPTR + 2] = 0;
3482:
1.9 snw 3483: lseek (filedes, hdr_offset + ((long) (other) * BLOCKLEN), SEEK_SET);
1.1 snw 3484: write (filedes, nblock, BLOCKLEN);
3485:
3486: }
3487:
3488: /* update ROOT block */
1.9 snw 3489: lseek (filedes, hdr_offset + ROOT, SEEK_SET);
1.1 snw 3490: read (filedes, nblock, BLOCKLEN);
3491:
3492: nblock[FREE] = other / 65536;
3493: nblock[FREE + 1] = other % 65536 / 256;
3494: nblock[FREE + 2] = other % 256;
3495:
1.9 snw 3496: lseek (filedes, hdr_offset + ROOT, SEEK_SET);
1.1 snw 3497: write (filedes, nblock, BLOCKLEN);
3498:
3499: return;
3500:
3501: }
3502:
3503: offset -= PLEN;
3504: *blknbr = UNSIGN (nblock[offset]) * 65536 + UNSIGN (nblock[offset + 1]) * 256 + UNSIGN (nblock[offset + 2]);
3505: nblock[offset] = 0;
3506: nblock[offset + 1] = 0;
3507: nblock[OFFS] = offset / 256;
3508: nblock[OFFS + 1] = offset % 256;
3509:
1.9 snw 3510: lseek (filedes, hdr_offset + ((long) (freeblks) * BLOCKLEN), SEEK_SET);
1.1 snw 3511: write (filedes, nblock, BLOCKLEN);
3512:
3513: return;
3514:
3515: }
3516:
3517: /* else ** freeblk==0 ** */
3518: no_of_blks++;
3519: nblock[NRBLK] = no_of_blks / 65536;
3520: nblock[NRBLK + 1] = no_of_blks % 65536 / 256;
3521: nblock[NRBLK + 2] = no_of_blks % 256;
3522:
1.9 snw 3523: lseek (filedes, hdr_offset + ROOT, SEEK_SET);
1.1 snw 3524: write (filedes, nblock, BLOCKLEN);
3525:
3526: *blknbr = no_of_blks;
3527:
3528: for (;;) {
3529:
3530: errno = 0;
3531:
1.9 snw 3532: lseek (filedes, hdr_offset + ((long) (no_of_blks) * BLOCKLEN), SEEK_SET);
1.1 snw 3533: write (filedes, nblock, BLOCKLEN);
3534:
3535: if (errno == 0) break;
3536:
3537: panic ();
3538:
3539: }
3540:
3541: return;
3542:
3543: } /* end of getnewblk() */
3544:
3545: /*
3546: * return TRUE if 't' follows 'compactkey' in MUMPS collating sequence
3547: */
3548: static short int g_collate (char *t)
3549: {
3550: char *s = compactkey;
3551: register int chs = *s;
3552: register int cht = *t;
3553: register int tx = 0;
3554: register int sx;
3555: short dif;
3556:
3557: /* the empty one is the leader! */
3558: if (chs == g_EOL) {
3559:
3560: if (cht == g_EOL) return 2;
3561:
3562: return TRUE;
3563:
3564: }
3565:
3566: if (cht == g_EOL) return FALSE;
3567:
3568: while (cht == s[tx]) {
3569:
3570: if (cht == g_EOL) return 2;
3571:
3572: cht = t[++tx];
3573:
3574: } /* (s==t) */
3575:
3576: chs = s[tx];
3577:
3578: if (chs == OMEGA) return FALSE;
3579: if (chs == ALPHA) return cht != g_EOL;
3580: if (chs == g_EOL && t[tx - 1] & 01) return TRUE;
3581: if (cht == g_EOL && s[tx - 1] & 01) return FALSE;
3582:
3583: if (tx > 0) {
3584:
3585: tx--;
3586:
3587: while ((t[tx] & 01) == 0) {
3588:
3589: tx--;
3590:
3591: if (tx < 0) break;
3592:
3593: }
3594:
3595: tx++;
3596:
3597: }
3598:
3599: chs = s[tx];
3600: cht = t[tx];
3601:
3602: if (UNSIGN (chs) <= POINT) { /* then come numerics */
3603:
3604: if (UNSIGN (cht) > POINT) return UNSIGN (cht) != g_EOL;
3605:
3606: /* both are numeric! now compare numeric values */
3607:
3608: if (chs == MINUS) {
3609: if (cht != MINUS) return TRUE;
3610: }
3611: else {
3612: if (cht == MINUS) return FALSE;
3613: }
3614:
3615: if (chs == 1 && cht == POINT) return TRUE;
3616: if (cht == 1 && chs == POINT) return FALSE;
3617:
3618: dif = sx = tx;
3619:
3620: while (s[sx] != POINT) {
3621: if (s[sx++] & 01) break;
3622: }
3623:
3624: while (t[tx] != POINT) {
3625: if (t[tx++] & 01) break;
3626: }
3627:
3628: if (tx > sx) return cht != MINUS;
3629: if (tx < sx) return cht == MINUS;
3630:
3631: tx = dif;
3632: while ((cht >> 1) == (chs >> 1)) {
3633:
3634: if (cht & 01) return t[dif] == MINUS;
3635: if (chs & 01) return t[dif] != MINUS;
3636:
3637: chs = s[++tx];
3638: cht = t[tx];
3639:
3640: }
3641:
3642: return (((cht >> 1) > (chs >> 1)) == (t[dif] != MINUS)) && (t[tx] != s[tx]);
3643:
3644: }
3645:
3646: if (UNSIGN (cht) <= POINT) return FALSE;
3647:
3648: while ((dif = (UNSIGN (cht) >> 1) - (UNSIGN (chs) >> 1)) == 0) { /* ASCII collating */
3649:
3650: if ((cht & 01) && ((chs & 01) == 0)) return FALSE;
3651: if ((chs & 01) && ((cht & 01) == 0)) return TRUE;
3652:
3653: chs = s[++tx];
3654: cht = t[tx];
3655:
3656: }
3657:
3658: if (chs == g_EOL) return TRUE;
3659: if (cht == g_EOL) return FALSE;
3660:
3661: return dif > 0;
3662:
3663: } /* end g_collate() */
3664:
3665: /*
3666: * test whether 'str' is canonical
3667: */
3668: //static short int g_numeric (char *str)
3669: short g_numeric (char *str)
3670: {
3671: register int ptr = 0, ch;
3672: register int point = 0;
3673:
3674: if (str[0] == '-') {
3675: if ((ch = str[++ptr]) == EOL || (ch == DELIM) || (ch == '0')) return FALSE;
3676: }
3677: else if (str[0] == '0') {
3678:
3679: if ((ch = str[ptr + 1]) == EOL || ch == DELIM) return TRUE;
3680:
3681: return FALSE; /* leading zero */
3682: }
3683:
3684: while ((ch = str[ptr++]) != EOL && ch != DELIM) {
3685:
3686: if (ch > '9') return FALSE;
3687:
3688: if (ch < '0') {
3689:
3690: if (ch != '.') return FALSE;
3691: if (point) return FALSE; /* multiple points */
3692:
3693: point = TRUE;
3694:
3695: }
3696:
3697: }
3698:
3699: if (point) {
3700:
3701: ch = str[ptr - 2];
3702:
3703: if (ch == '0' || ch == '.') return FALSE;
3704:
3705: }
3706:
3707: return TRUE;
3708:
3709: } /* end g_numeric() */
3710:
1.10 snw 3711:
3712: /* DEPRECATED: use gbl_close_all() instead */
1.1 snw 3713: void close_all_globals (void)
3714: {
1.10 snw 3715: gbl_close_all ();
3716:
1.1 snw 3717: return;
3718: } /* end close_all_globals() */
3719:
3720: static void panic (void)
1.8 snw 3721: {
3722: printf ("write failed\r\n");
3723:
1.1 snw 3724: printf ("\033[s\033[25H\033[5;7mwrite needs more disk space immediately\007");
3725: sleep (1);
3726: printf ("\033[m\007\033[2K\033[u");
3727:
3728: /* restore screen 'cause system messed up screen */
3729:
3730: #ifdef NOWRITEM
3731:
3732: write_m ("\033[4~\201");
3733:
3734: #endif /* NOWRITEM */
3735:
3736: return;
3737:
3738: } /* end panic() */
3739:
3740:
3741: void gbl_dump_stat(void)
3742: {
1.8 snw 3743: global_handle *g;
1.13 ! snw 3744: int ct;
! 3745: int miss_pct;
! 3746: int hit_pct;
! 3747: unsigned long access_total;
! 3748: unsigned long hit_total;
1.1 snw 3749:
1.13 ! snw 3750: printf ("\r\nFreeM Global Statistics [PID %d]\r\n\r\n", pid);
1.1 snw 3751:
1.13 ! snw 3752: printf ("%-20s%-10s%-12s%-20s%-10s%s\r\n", "GLOBAL", "USECT", "SLOW PTHCT", "AGE", "LAST BLK", "FILE");
! 3753: printf ("%-20s%-10s%-12s%-20s%-10s%s\r\n", "======", "=====", "==========", "===", "========", "====");
! 3754:
! 3755: access_total = 0;
! 3756: ct = 0;
1.8 snw 3757: for (g = global_handles_head; g != NULL; g = g->next) {
1.13 ! snw 3758: printf ("%-20s%-10d%-12d%-20d%-10d%s\r\n",
! 3759: g->global_name,
! 3760: g->use_count,
! 3761: g->cache_misses,
! 3762: g->age,
! 3763: g->last_block,
! 3764: g->global_path);
! 3765: ct++;
! 3766: access_total += g->use_count;
! 3767: }
! 3768: if (!ct) printf ("<no globals opened in this pid>\r\n");
! 3769:
! 3770: hit_total = access_total - gbl_cache_misses;
! 3771: miss_pct = (gbl_cache_misses * 100) / access_total;
! 3772: hit_pct = (hit_total * 100) / access_total;
! 3773:
! 3774: printf ("\r\nTotal accesses: %ld\r\n", access_total);
! 3775: printf ("Fast path hits %ld\t(%d%%)\r\n", hit_total, hit_pct);
! 3776: printf ("Fast path misses: %ld\t(%d%%)\r\n", gbl_cache_misses, miss_pct);
1.1 snw 3777: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>