File:  [Coherent Logic Development] / freem / src / newglobal_bltin.c
Revision 1.1: download - view: text, annotated - select for diffs
Sat May 17 17:00:05 2025 UTC (2 months, 2 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Make it possible to select the modernized global handler with the configure script and update documentation and README.md

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

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