File:  [Coherent Logic Development] / freem / src / global_bltin.c
Revision 1.19: download - view: text, annotated - select for diffs
Fri Apr 11 16:52:05 2025 UTC (11 months, 3 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Fix indentation in global handler

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>