home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume44
/
typhoon
/
part03
< prev
next >
Wrap
Internet Message Format
|
1994-09-17
|
62KB
From: zeppelin@login.dknet.dk (Thomas B. Pedersen)
Newsgroups: comp.sources.misc
Subject: v44i059: typhoon - Typhoon Relational Database Management System, Part03/09
Date: 17 Sep 1994 21:45:26 -0500
Organization: Sterling Software
Sender: kent@sparky.sterling.com
Approved: kent@sparky.sterling.com
Message-ID: <35g9k6$ohf@sparky.sterling.com>
X-Md4-Signature: 523b50b89b972700c8495c636ca5f5fc
Submitted-by: zeppelin@login.dknet.dk (Thomas B. Pedersen)
Posting-number: Volume 44, Issue 59
Archive-name: typhoon/part03
Environment: SCO UNIX, Tandem NonStop UNIX, Sun Solaris, AIX, Linux, OS/2
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# Contents: typhoon/src/bt_del.c typhoon/src/bt_open.c
# typhoon/src/ty_ins.c typhoon/src/ty_io.c
# typhoon/src/util/ddlpsym.c typhoon/src/util/imp_y.h
# Wrapped by kent@sparky on Sat Sep 17 21:38:16 1994
PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 3 (of 9)."'
if test -f 'typhoon/src/bt_del.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'typhoon/src/bt_del.c'\"
else
echo shar: Extracting \"'typhoon/src/bt_del.c'\" \(12035 characters\)
sed "s/^X//" >'typhoon/src/bt_del.c' <<'END_OF_FILE'
X/*----------------------------------------------------------------------------
X * File : bt_del
X * Library : typhoon
X * OS : UNIX, OS/2, DOS
X * Author : Thomas B. Pedersen
X *
X * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved.
X *
X * Permission is hereby granted, without written agreement and without
X * license or royalty fees, to use, copy, modify, and distribute this
X * software and its documentation for any purpose, provided that the above
X * copyright notice and the following two paragraphs appear (1) in all
X * source copies of this software and (2) in accompanying documentation
X * wherever the programatic interface of this software, or any derivative
X * of it, is described.
X *
X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X *
X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
X * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
X *
X * Description:
X * Contains function for removing a tuple from a B-tree index file.
X * The algorithm is the one is described in "Data structures in Pascal",
X * Horowitz & Sahni, Computer Science Press.
X *
X * Functions:
X * delchain_insert - Add a node to the delete chain.
X * merge_siblings - Merge two sibling nodes to a single node.
X * move_parentkey - Move a parent key to another tuple.
X * find_ref - Find tuple with correct reference.
X * replace_with_leftmost_tuple- Copy the leftmost tuple in a subtree.
X * btree_del -
X *
X * $Log: bt_del.c,v $
X * Revision 1.2 1994/09/17 16:00:11 tbp
X * typhoon.h and environ.h are now included from <>.
X *
X * Revision 1.1 1994/09/13 21:28:27 tbp
X * Added to repository.
X *
X *
X *--------------------------------------------------------------------------*/
X
Xstatic char rcsid[] = "$Id: bt_del.c,v 1.2 1994/09/17 16:00:11 tbp Exp $";
X
X#include <string.h>
X#include <stdio.h>
X#include <fcntl.h>
X#ifndef UNIX
X# include <io.h>
X# include <stdlib.h>
X#else
X# include <unistd.h>
X# ifdef __STDC__
X# include <stdlib.h>
X# endif
X#endif
X#include <typhoon.h>
X#include "ty_dbd.h"
X#include "ty_type.h"
X#include "ty_prot.h"
X#include "ty_glob.h"
X#include "btree.h"
X
X
X/*--------------------------- Function prototypes ---------------------------*/
Xstatic void delchain_insert PRM( (INDEX *, ix_addr); )
Xstatic void merge_siblings PRM( (INDEX *I,
X ix_addr lsib,
X ix_addr rsib,
X ix_addr z,
X int zi,
X char *znode,
X ix_addr *y,
X char *ynode,
X ix_addr *p,
X int *i); )
X
Xstatic void move_parentkey PRM( (INDEX *I,
X ix_addr rsib,
X int zi,
X ix_addr z,
X char *znode,
X ix_addr y,
X char *ynode); )
X
Xstatic find_ref PRM( (INDEX *I,
X ulong ref,
X ix_addr *addr,
X int *idx,
X void *key); )
X
Xstatic void replace_with_leftmost_tuple
X PRM( (INDEX *I,
X ix_addr *y,
X char *ynode,
X ix_addr *p,
X int *i); )
X
X
X/*----------------------------- delchain_insert ----------------------------*\
X *
X * Purpose : Inserts a deleted B-tree node in the delete chain.
X *
X * Parameters: I - B-tree index file descriptor.
X * addr - Address of node to insert in delete chain.
X *
X * Returns : Nothing.
X *
X */
X
Xstatic void delchain_insert(I, addr)
XINDEX *I;
Xix_addr addr;
X{
X lseek(I->fh, (ulong)I->H.nodesize * (ulong)addr, SEEK_SET);
X write(I->fh, &I->H.first_deleted, sizeof I->H.first_deleted);
X I->H.first_deleted = addr;
X}
X
X
X
X/*--------------------------------------------------------------------------*\
X *
X * Function : merge_siblings
X *
X * Purpose : Merge two siblings nodes.
X *
X * Parameters: I - Index handle.
X * lsib - Address of left sibling node.
X * rsib - Address of right sibling node.
X * z -
X * zi -
X * znode -
X * y -
X * ynode -
X * p -
X * i -
X *
X * Returns : Nothing.
X *
X */
Xstatic void merge_siblings(I, lsib, rsib, z, zi, znode, y, ynode, p, i)
XINDEX *I;
Xix_addr lsib, rsib, *y, z, *p;
Xint zi, *i;
Xchar *znode;
Xchar *ynode;
X{
X if( rsib )
X {
X /* copy parent key */
X keycopy(I->node, NSIZE(I->node), znode, zi);
X
X /* copy sibling keys */
X tuplecopy(I->node, NSIZE(I->node)+1, ynode, 0, NSIZE(ynode));
X
X CHILD(I->node,NSIZE(I->node)+1+NSIZE(ynode)) = CHILD(ynode,NSIZE(ynode));
X
X delchain_insert(I, *p);
X }
X else
X {
X /* make room for left sibling */
X tupleins(I->node, 0, NSIZE(ynode)+1);
X
X /* copy parent key */
X keycopy(I->node, NSIZE(ynode), znode, zi);
X
X /* copy sibling keys */
X tuplecopy(I->node, 0, ynode, 0, NSIZE(ynode));
X
X CHILD(I->node,NSIZE(ynode)) = CHILD(ynode,NSIZE(ynode));
X *y = *p;
X
X delchain_insert(I, lsib);
X }
X
X tupledel(znode, zi); /* remove parent key */
X NSIZE(znode)--;
X NSIZE(I->node) += 1 + NSIZE(ynode);
X
X nodewrite(I, I->node, *y);
X
X /* create new root? */
X if( z == 1 && !NSIZE(znode) )
X {
X *p = 1;
X delchain_insert(I, *y);
X }
X else
X {
X /* process parent */
X nodecopy(I->node, znode);
X *p = z;
X *i = zi;
X I->level--;
X }
X}
X
X
X/*--------------------------------------------------------------------------*\
X *
X * Function : move_parentkey
X *
X * Purpose :
X *
X * Parameters: I -
X * rsib -
X * zi -
X * z -
X * znode -
X * y -
X * ynode -
X *
X * Returns : Nothing.
X *
X */
Xstatic void move_parentkey(I, rsib, zi, z, znode, y, ynode)
XINDEX *I;
Xix_addr rsib, y, z;
Xint zi;
Xchar *znode, *ynode;
X{
X if( rsib )
X {
X /* copy key from parent to p */
X keycopy(I->node, NSIZE(I->node), znode, zi);
X
X /* copy reference of sibling moved to parent */
X CHILD(I->node, NSIZE(I->node)+1) = CHILD(ynode,0);
X
X /* copy sibling key to parent */
X keycopy(znode, zi, ynode, 0);
X tupledel(ynode, 0);
X }
X else
X {
X tupleins(I->node, 0, 1);
X
X /* copy key from parent */
X keycopy(I->node, 0, znode, zi);
X
X /* copy reference of sibling moved to parent */
X CHILD(I->node,0) = CHILD(ynode, NSIZE(ynode));
X
X /* copy sibling key to parent */
X keycopy(znode, zi, ynode, NSIZE(ynode)-1);
X }
X
X NSIZE(ynode)--;
X NSIZE(I->node)++;
X
X nodewrite(I, ynode, y); /* update nodes */
X nodewrite(I, znode, z);
X}
X
X
X/*--------------------------------------------------------------------------*\
X *
X * Function : find_ref
X *
X * Purpose : Find a tuple with a specified reference.
X *
X * Parameters: I - INDEX handle.
X * ref - Reference number.
X * addr - Address of node to start search in. If the tuple
X * is found, the address of the tuple is returned herein
X *
X * Returns : S_OKAY - Reference found.
X *
X */
X
X#define Keys NSIZE(I->node)
X#define Child(i) CHILD(I->node, (i))
X#define Key(i) KEY(I->node, (i))
X#define Ref(i) REF(I->node, (i))
X#define Pos (I->path[I->level].i)
X#define Addr (I->path[I->level].a)
X#define Level (I->level)
X
X
X
X
Xstatic find_ref(I, ref, addr, idx, key)
XINDEX *I;
Xulong ref;
Xix_addr *addr;
Xint *idx;
Xvoid *key;
X{
X for( ;; )
X {
X *idx = Pos;
X *addr = Addr;
X
X if( (*I->cmpfunc)(key, Key(*idx)) )
X {
X puts("key mismatch");
X break;
X }
X
X if( Ref(*idx) == ref )
X return S_OKAY;
X
X if( Child(Pos) > 0 ) /* Non-leaf node */
X {
X /* Get the leftmost child in the left subtree */
X Pos++;
X get_leftmostchild(I, Child(Pos));
X }
X else if( Pos >= Keys-1 ) /* Leaf node at first pos */
X {
X if( Pos >= Keys-1 && Addr == 1 )
X {
X I->curr = 0;
X RETURN S_NOTFOUND;
X }
X
X /* Move upward until a node with Pos<Keys-1 or root is reached */
X do
X {
X Level--;
X noderead(I, I->node, Addr);
X }
X while( Pos >= Keys && Addr != 1 );
X
X if( Pos == Keys && Addr == 1 )
X {
X I->curr = 0;
X RETURN S_NOTFOUND;
X }
X }
X else /* Leaf node */
X Pos++;
X }
X
X RETURN S_NOTFOUND;
X}
X
X#undef Keys
X#undef Child
X#undef Key
X#undef Ref
X#undef Pos
X#undef Addr
X#undef Level
X
X
X
X
X/*--------------------------------------------------------------------------*\
X *
X * Function : replace_with_leftmost_tuple
X *
X * Purpose : Replaces the current tuple [p, I->node, i] with the leftmost
X * key in the right subtree (of the current tuple).
X *
X * Parameters: I - Index handle.
X * y -
X * ynode -
X * p - Address of I->node.
X * i - Index of I->node.
X *
X * Returns :
X *
X */
Xstatic void replace_with_leftmost_tuple(I, y, ynode, p, i)
XINDEX *I;
Xix_addr *y;
Xix_addr *p;
Xchar *ynode;
Xint *i;
X{
X I->path[I->level].i++;
X
X *y = noderead(I, ynode, CHILD(I->node, *i+1));
X
X I->path[++I->level].a = CHILD(I->node, *i+1);
X I->path[ I->level].i = 0;
X
X while( CHILD(ynode,0) )
X {
X *y = noderead(I, ynode, CHILD(ynode, 0));
X
X I->path[++I->level].a = *y;
X I->path[ I->level].i = 0;
X }
X
X keycopy(I->node, *i, ynode, 0); /* copy leftmost key to p,i */
X
X nodewrite(I, I->node, *p); /* update node p */
X nodecopy(I->node, ynode);
X
X *p = *y;
X *i = 0;
X}
X
X
X
X
X/*-------------------------------- btree_del -------------------------------*\
X *
X * Purpose : Deletes a key in a B-tree. If the deletion causes underflow in
X * a node, two nodes are merged and the B-tree possibly shrunk.
X *
X * Parameters: I - B-tree index file descriptor.
X * key - Key value to delete.
X * ref - Reference of key value. Only used if the B-tree
X * contains duplicates.
X *
X * Returns : S_OKAY - Key value successfully deleted.
X * S_NOTFOUND - The key was not in the B-tree.
X *
X */
Xbtree_del(I, key, ref)
XINDEX *I;
Xvoid *key;
Xulong ref;
X{
X ix_addr p, y, z, lsib, rsib;
X int i, zi, rc;
X char *ynode, *znode;
X
X I->curr = 0;
X I->hold = 0;
X
X btree_getheader(I);
X
X if( !d_search(I, key, &p, &i) )
X RETURN S_NOTFOUND;
X
X if( I->H.dups )
X if( (rc = find_ref(I, ref, &p, &i, key)) != S_OKAY )
X return rc;
X
X /* Allocate temporaty node buffers */
X if( !(ynode = (char *)malloc(I->H.nodesize + I->tsize)) )
X RETURN S_NOMEM;
X if( !(znode = (char *)malloc(I->H.nodesize + I->tsize)) )
X {
X free(ynode);
X RETURN S_NOMEM;
X }
X
X /* if node is a nonleaf, replace key with leftmost key in right subtree */
X if( CHILD(I->node, 0) )
X replace_with_leftmost_tuple(I, &y, ynode, &p, &i);
X
X tupledel(I->node, i); /* remove key from leaf */
X NSIZE(I->node)--; /* decrease node size by 1 */
X
X /* run loop as long there is underflow in p and p is not root */
X while( NSIZE(I->node) < (ulong)I->H.order/2 && p != 1 )
X {
X z = I->path[I->level-1].a; /* set z = parent */
X zi = I->path[I->level-1].i; /* set zi = parent i */
X
X noderead(I,znode,z); /* read parent node from disk */
X
X lsib = zi ? CHILD(znode, zi-1) : 0;
X rsib = zi < NSIZE(znode) ? CHILD(znode, zi+1) : 0;
X
X y = rsib ? rsib : lsib;
X
X noderead(I, ynode, y);
X
X if( !rsib )
X zi--;
X
X if( NSIZE(ynode) > (ulong)I->H.order/2 )
X {
X /* move parent key to p, move nearest key in sibling to p */
X move_parentkey(I, rsib, zi, z, znode, y, ynode);
X
X goto out;
X }
X else
X {
X /* there is underflow in leaf p - merge with a sibling */
X merge_siblings(I, lsib, rsib, z, zi, znode, &y, ynode, &p, &i);
X }
X }
X
X I->H.keys--;
X
Xout:
X
X if( !NSIZE(I->node) ) /* if index is empty, truncate */
X {
X I->H.first_deleted = 0;
X I->H.keys = 0;
X#if defined(DOS) || defined(OS2)
X chsize(I->fh, 0);
X#else
X#ifdef SCO
X os_close(os_open(I->fname, O_RDWR|O_TRUNC, CREATMASK));
X#else
X ftruncate(I->fh, I->H.nodesize);
X#endif
X#endif
X }
X else
X nodewrite(I, I->node, p); /* else update node p */
X
X I->H.timestamp++;
X btree_putheader(I);
X
X free(znode);
X free(ynode);
X
X RETURN S_OKAY;
X}
X
X/* end-of-file */
END_OF_FILE
if test 12035 -ne `wc -c <'typhoon/src/bt_del.c'`; then
echo shar: \"'typhoon/src/bt_del.c'\" unpacked with wrong size!
fi
# end of 'typhoon/src/bt_del.c'
fi
if test -f 'typhoon/src/bt_open.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'typhoon/src/bt_open.c'\"
else
echo shar: Extracting \"'typhoon/src/bt_open.c'\" \(11821 characters\)
sed "s/^X//" >'typhoon/src/bt_open.c' <<'END_OF_FILE'
X/*----------------------------------------------------------------------------
X * File : bt_open.c
X * Library : typhoon
X * OS : UNIX, OS/2, DOS
X * Author : Thomas B. Pedersen
X *
X * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved.
X *
X * Permission is hereby granted, without written agreement and without
X * license or royalty fees, to use, copy, modify, and distribute this
X * software and its documentation for any purpose, provided that the above
X * copyright notice and the following two paragraphs appear (1) in all
X * source copies of this software and (2) in accompanying documentation
X * wherever the programatic interface of this software, or any derivative
X * of it, is described.
X *
X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X *
X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
X * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
X *
X * Description:
X * Contains functions for opening and closing B-tree index files.
X *
X * Functions:
X * db_keygetheader - Read B-tree inde xfile header.
X * db_keyputheader - Write B-tree index file header.
X * d_keyopen - Open a B-tree index file.
X * d_keyclose - Close a B-tree index file.
X * nodesearch - Perform binary search in the node.
X * d_search - Find a key.
X *
X * $Log: bt_open.c,v $
X * Revision 1.2 1994/09/17 16:00:13 tbp
X * typhoon.h and environ.h are now included from <>.
X *
X * Revision 1.1 1994/09/13 21:28:29 tbp
X * Added to repository.
X *
X *
X *--------------------------------------------------------------------------*/
X
Xstatic char rcsid[] = "$Id: bt_open.c,v 1.2 1994/09/17 16:00:13 tbp Exp $";
X
X#include <string.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <errno.h>
X#ifdef UNIX
X# include <unistd.h>
X# ifdef __STDC__
X# include <stdlib.h>
X# endif
X#else
X# include <sys/stat.h>
X# include <stdlib.h>
X# include <io.h>
X#endif
X#include <typhoon.h>
X#include "ty_dbd.h"
X#include "ty_type.h"
X#include "ty_prot.h"
X#include "ty_glob.h"
X#include "btree.h"
X
X
Xint find_firstoccurrence PRM( (INDEX *, void *, ix_addr *, int *); )
X
X/*----------------------------- btree_getheader ----------------------------*\
X *
X * Purpose : Reads the header of a B-tree index file.
X *
X * Parameters: I - Pointer to index file descriptor.
X *
X * Returns : Nothing.
X *
X */
X
Xvoid btree_getheader(I)
XINDEX *I;
X{
X lseek(I->fh, 0L, SEEK_SET);
X read(I->fh, &I->H, sizeof I->H);
X}
X
X
X/*----------------------------- btree_getheader ----------------------------*\
X *
X * Purpose : Writes the header of a B-tree index file.
X *
X * Parameters: I - Pointer to index file descriptor.
X *
X * Returns : Nothing.
X *
X */
X
Xvoid btree_putheader(I)
XINDEX *I;
X{
X lseek(I->fh, 0L, SEEK_SET);
X write(I->fh, &I->H, sizeof I->H);
X}
X
X
X/*-------------------------------- btree_open -------------------------------*\
X *
X * Purpose : Opens a B-tree index file with the name <fname>. If the file
X * does not already exist, the file is created. If the version of
X * the existing file does not match the version of the B-tree
X * library db_status is set to S_VERSION, and NULL is returned.
X *
X * Parameters: fname - File name.
X * keysize - Key size.
X * nodesize - Node size. Multiples of 512 are recommended.
X * cmpfunc - Comparison function. This function must take two
X * parameters, i.e. like strcmp().
X * dups - True if duplicates are allowed.
X * shared - Open the index file in shared mode?.
X *
X * Returns : If the file was successfully opened, a pointer to a B-tree
X * index file descriptor is returned, otherwise NULL is returned
X * and db_status is set to one of following:
X *
X * S_NOMEM - Out of memory.
X * S_IOFATAL - File could not be opened.
X * S_VERSION - B-tree file on disk has wrong version.
X * S_UNAVAIL - The file is already opened in non-shared mode.
X *
X */
X
XINDEX *btree_open(fname, keysize, nodesize, cmpfunc, dups, shared)
Xchar *fname;
Xint keysize, dups, nodesize, shared;
XCMPFUNC cmpfunc;
X{
X INDEX *I;
X int tuplesize, isnew, fh;
X int aligned_keysize;
X
X /* See if file exists and then open it */
X isnew = access(fname, 0);
X if( (fh=os_open(fname, O_BINARY|O_CREAT|O_RDWR,CREATMASK)) == -1 )
X {
X db_status = S_IOFATAL;
X return NULL;
X }
X
X /* Lock the file if it is not opened in shared mode */
X if( !shared )
X if( os_lock(fh, 0L, 1, 't') == -1 )
X {
X db_status = S_NOTAVAIL;
X return NULL;
X }
X
X /* Ensure that the size of a tuple is multiple of four */
X aligned_keysize = keysize;
X#ifdef RISC
X if( aligned_keysize & 3 )
X aligned_keysize += 4 - (aligned_keysize & 3);
X#endif
X
X /* calculate memory requirements */
X tuplesize = sizeof(A_type) + sizeof(R_type) + aligned_keysize;
X
X /* allocate memory for INDEX structure */
X if( (I = (INDEX *)calloc(sizeof(*I) + nodesize + tuplesize,1)) == NULL )
X {
X os_close(fh);
X db_status = S_NOMEM;
X return NULL;
X }
X
X /* allocate memory for current key */
X if( (I->curkey = (char *)malloc(keysize)) == NULL )
X {
X os_close(fh);
X free(I);
X db_status = S_NOMEM;
X return NULL;
X }
X
X I->fh = fh;
X
X if( isnew )
X {
X I->H.version = KEYVERSION_NUM;
X I->H.first_deleted = 0;
X I->H.order = ((nodesize-sizeof(N_type)-sizeof(A_type)) / tuplesize) & 0xfffe;
X I->H.keysize = keysize;
X I->H.dups = dups;
X I->H.nodesize = nodesize;
X I->H.keys = 0;
X strcpy(I->H.id, KEYVERSION_ID);
X memset(I->H.spare, 0, sizeof I->H.spare);
X btree_putheader(I);
X }
X else
X {
X btree_getheader(I);
X
X if( I->H.version != KEYVERSION_NUM )
X {
X db_status = S_VERSION;
X os_close(fh);
X free(I->curkey);
X free(I);
X return NULL;
X }
X }
X
X I->cmpfunc = cmpfunc;
X I->tsize = tuplesize;
X I->hold = 0;
X I->shared = shared;
X I->aligned_keysize = aligned_keysize;
X strcpy(I->fname, fname);
X
X db_status = S_OKAY;
X
X return I;
X}
X
X
X/*-------------------------------- btree_close ------------------------------*\
X *
X * Purpose : Closes a B-tree index file previously opened with d_keyopen().
X * All nodes that might be in the cache are written to disk.
X *
X * Parameters: I - Index file descriptor.
X *
X * Returns : Nothing.
X *
X */
Xvoid btree_close(I)
XINDEX *I;
X{
X int i;
X
X if( I->fh != -1 )
X {
X btree_putheader(I);
X os_close(I->fh);
X }
X
X free(I->curkey);
X free(I);
X}
X
X
Xbtree_dynclose(I)
XINDEX *I;
X{
X if( I->fh != -1 )
X {
X close(I->fh);
X I->fh = -1;
X }
X
X RETURN S_OKAY;
X}
X
X
Xbtree_dynopen(I)
XINDEX *I;
X{
X if( I->fh == -1 )
X if( (I->fh=os_open(I->fname, O_BINARY|O_CREAT|O_RDWR,CREATMASK)) == -1 )
X RETURN S_IOFATAL;
X
X RETURN S_OKAY;
X}
X
X
X/*------------------------------- nodesearch -------------------------------*\
X *
X * Purpose : Performs a binary search for the key value pointed to by <key>
X * in the node in I. When the function returned <i> contains the
X * entry in the node where the searched stopped. If the key was
X * found REF(i) contains the reference to be returned by the
X * calling, otherwise CHILD(i) contains the node address of the
X * child to be processed next.
X *
X * Parameters: I - B-tree index file descriptor.
X * key - Key value being searched for.
X * i - Contains node index when function returns.
X *
X * Returns : 0 - The key was found.
X * not 0 - The key was not found.
X *
X */
Xnodesearch(I, key, i)
XINDEX *I;
Xvoid *key;
Xint *i;
X{
X int cmp, mid, upr, lwr;
X
X upr = NSIZE(I->node) - 1;
X lwr = 0;
X
X /* Perform binary search in node */
X while( lwr <= upr )
X {
X mid = (lwr + upr) >> 1;
X cmp = (*I->cmpfunc)(key, KEY(I->node, mid));
X
X if( cmp > 0 )
X lwr = mid + 1;
X else if( cmp < 0 )
X upr = mid - 1;
X else
X {
X if( I->H.dups )
X {
X /* Find the leftmost occurrence */
X while( mid > 0 )
X {
X mid--;
X if( cmp = (*I->cmpfunc)(key, KEY(I->node, mid)) )
X break;
X }
X if( cmp )
X mid++;
X
X *i = mid;
X return 0;
X }
X break;
X }
X }
X
X /* If the comparison yielded greater than, move a step to the right */
X if( cmp > 0 )
X mid++;
X
X *i = mid;
X return cmp;
X}
X
X
X/*-------------------------------- d_search --------------------------------*\
X *
X * Purpose : Searches a B-tree for the key value pointed to by <key>. If
X * the key is found the reference is in REF(i). The path from the
X * root to the last access node is stored in I->path[], which
X * enables key traversal from the current point in the tree.
X *
X * The search starts at the root, which is always at address 1. For
X * each node nodesearch is called to perform a binary search in the
X * node. If the key is not found, the search ends in a leaf node.
X *
X * If duplicates are allowed, the first occurrence of the key
X * value must be found.
X *
X * Parameters: I - B-tree index file descriptor.
X * key - Key value being searched for.
X * addr - Contains node address when function returns.
X * i - Contains node index when function returns.
X *
X * Returns : 0 - The key was found.
X * 1 - The key was not found.
X *
X */
Xd_search(I, key, addr, i)
XINDEX *I;
Xvoid *key;
Xix_addr *addr;
Xint *i;
X{
X int cmp;
X
X /* Start the search at the root */
X *addr = 1;
X *i = 0;
X I->level = 0;
X
X
X for( ;; )
X {
X /* Save the addresses and indexes of the traversed nodes */
X I->path[++I->level].a = *addr;
X
X if( noderead(I, I->node, *addr) == (ix_addr)-1 )
X {
X /* The node could not be read - zero the number of keys */
X NSIZE(I->node) = 0;
X return 0;
X }
X
X cmp = nodesearch(I,key,i);
X
X I->path[I->level].i = *i;
X
X if( !cmp )
X {
X if( I->H.dups )
X return find_firstoccurrence(I, key, addr, i);
X return 1;
X }
X
X if( CHILD(I->node, *i) )
X *addr = CHILD(I->node, *i);
X else
X return 0;
X }
X}
X
X
X/*-------------------------- find_firstoccurrence --------------------------*\
X *
X * Purpose : This function is called in order to find the first occurrence
X * of a duplicate key in a non-unique index.
X *
X * Parameters: I - B-tree index file descriptor.
X * key - Key value being searched for.
X * addr - Contains node address when function returns.
X * i - Contains node index when function returns.
X *
X * Returns : 0 - The key was found.
X * 1 - The key was not found.
X *
X */
Xfind_firstoccurrence(I, key, addr, i)
XINDEX *I;
Xvoid *key;
Xix_addr *addr;
Xint *i;
X{
X int first_occurlevel = I->level;
X int cmp = 0;
X
X while( CHILD(I->node, 0) )
X {
X I->level++;
X I->path[I->level].a = CHILD(I->node, *i);
X I->path[I->level].i = *i;
X
X noderead(I, I->node, I->path[I->level].a);
X
X cmp = nodesearch(I,key,i);
X
X I->path[I->level].i = *i;
X
X /* If the key was found in the current node we search the left
X * subtree of the key. Otherwise, we search the right subtree of
X * the rightmost key.
X */
X
X if( !cmp )
X first_occurlevel = I->level;
X else
X *i = NSIZE(I->node);
X }
X
X if( cmp )
X {
X I->level = first_occurlevel;
X *i = I->path[I->level].i;
X *addr = I->path[I->level].a;
X
X noderead(I, I->node, I->path[I->level].a);
X }
X else
X {
X *i = I->path[I->level].i;
X *addr = I->path[I->level].a;
X }
X
X return 1;
X}
X
X/* end-of-file */
X
END_OF_FILE
if test 11821 -ne `wc -c <'typhoon/src/bt_open.c'`; then
echo shar: \"'typhoon/src/bt_open.c'\" unpacked with wrong size!
fi
# end of 'typhoon/src/bt_open.c'
fi
if test -f 'typhoon/src/ty_ins.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'typhoon/src/ty_ins.c'\"
else
echo shar: Extracting \"'typhoon/src/ty_ins.c'\" \(12105 characters\)
sed "s/^X//" >'typhoon/src/ty_ins.c' <<'END_OF_FILE'
X/*----------------------------------------------------------------------------
X * File : ty_ins.c
X * Library : typhoon
X * OS : UNIX, OS/2, DOS
X * Author : Thomas B. Pedersen
X *
X * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved.
X *
X * Permission is hereby granted, without written agreement and without
X * license or royalty fees, to use, copy, modify, and distribute this
X * software and its documentation for any purpose, provided that the above
X * copyright notice and the following two paragraphs appear (1) in all
X * source copies of this software and (2) in accompanying documentation
X * wherever the programatic interface of this software, or any derivative
X * of it, is described.
X *
X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X *
X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
X * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
X *
X * Description:
X * Contains API functions.
X *
X * Functions:
X * report_err - Report an error to the user.
X * d_keyread - Read the value of the last retrieved key.
X * d_keyfind - Find a key.
X * d_keymove - Perform a d_keyfrst(), d_keylast(), d_keyprev() or
X * d_keynext().
X * d_recmove - Perform a d_recfrst(), d_reclast(), d_recprev() or
X * d_recnext().
X * d_crread - Read the value of a field of the current record.
X * d_recwrite - Update a record.
X * d_recread - Read the current record.
X * d_fillnew - Add a new record to the database.
X * d_delete - Delete the current record.
X * d_crget - Get the database address of the current record.
X * d_crset - Set the database address of the current record.
X * d_records - Return the number of records in a file.
X * d_getkeysize - Return the size of a key.
X * d_getrecsize - Return the size of a record.
X *
X * $Log: ty_ins.c,v $
X * Revision 1.2 1994/09/17 16:00:16 tbp
X * typhoon.h and environ.h are now included from <>.
X *
X * Revision 1.1 1994/09/13 21:28:34 tbp
X * Added to repository.
X *
X *
X *--------------------------------------------------------------------------*/
X
Xstatic char rcsid[] = "$Id: ty_ins.c,v 1.2 1994/09/17 16:00:16 tbp Exp $";
X
X#ifdef UNIX
X# include <unistd.h>
X#endif
X#include <string.h>
X#include <stdio.h>
X#include <typhoon.h>
X#include "ty_dbd.h"
X#include "ty_type.h"
X#define DEFINE_GLOBALS
X#include "ty_glob.h"
X#include "ty_prot.h"
X
X
Xreport_err(v)
Xint v;
X{
X if( typhoon.ty_errfn )
X typhoon.ty_errfn(db_status, db_subcode);
X else
X {
X#ifdef UNIX
X printf("** pid %d - db_status = %d **\n", getpid(), db_status = v);
X#endif
X#ifdef OS2
X printf("** db_status = %d **\n", db_status = v);
X#endif
X }
X
X return v;
X}
X
X
X
X/*--------------------------------- d_block ---------------------------------*\
X *
X * Purpose : Requests exclusive access to the API. The second time another
X * thread in a program calls this function it will block, until
X * d_unblock() is called.
X *
X * Parameters: None.
X *
X * Returns : S_OKAY - Ok.
X *
X */
X
XFNCLASS d_block()
X{
X#ifdef OS2
X os2_block();
X#endif
X
X RETURN S_OKAY;
X}
X
X
X
XFNCLASS d_unblock()
X{
X#ifdef OS2
X os2_unblock();
X#endif
X
X RETURN S_OKAY;
X}
X
X
X/*------------------------------- d_recwrite -------------------------------*\
X *
X * Purpose : Updates the contents of the current record.
X *
X * Parameters: buf - Buffer containing current record.
X *
X * Returns : S_OKAY - Operation performed successfully.
X * S_DUPLICATE - The record contained a duplicate key. db_subcode
X * contains the id of the conflicting field or key.
X * S_NOCD - No current database.
X * S_NOCR - No current record.
X * S_RECSIZE - Invalid record size (if variable size).
X * db_subcode contains the ID of the size field.
X * S_FOREIGN - A foreign key was not found (db_subcode holds
X * the foreing key ID).
X * S_RESTRICT - The primary key was updated, but a dependent
X * table with restrict rule had a record which
X * referenced the record to be deleted.
X * (db_subcode holds the foreign key ID).
X *
X */
XFNCLASS d_recwrite(buf)
Xvoid *buf;
X{
X Record *rec;
X Key *key;
X Key *keyptr[RECKEYS_MAX];
X int keys_changed=0, rc;
X int i, n;
X ulong ref;
X
X /* Set pointers to current record and first field */
X if( (rc = set_recfld(-1, &rec, NULL)) != S_OKAY )
X return rc;
X
X ty_lock();
X if( (rc = update_recbuf()) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X /* Check foreign keys (if any) */
X if( (rc = check_foreign_keys(rec, buf, 0)) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X /* Check dependent tables (if any) */
X if( (rc = check_dependent_tables(rec, buf, 'u')) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X /* Have any keys changed? */
X key = DB->key + rec->first_key;
X
X /* Find out which keys have changed and must be updated. An optional key
X * that has changed from null to not null, or not null to null, or
X * has changed its value is regarded as changed.
X */
X
X for( n = rec->keys; n-- && KT_GETBASIC(key->type) != KT_FOREIGN; key++ )
X {
X if( reckeycmp(key, buf, DB->recbuf) )
X {
X keyptr[keys_changed++] = key;
X
X if( key->type & KT_UNIQUE )
X {
X if( (key->type & KT_OPTIONAL) && null_indicator(key, buf) )
X continue;
X
X if( keyfind(key, buf, &ref) == S_OKAY )
X {
X set_subcode(key);
X ty_unlock();
X RETURN S_DUPLICATE;
X }
X }
X }
X }
X
X for( n=0; n<keys_changed; n++ )
X {
X key = keyptr[n];
X
X if( reckeycmp(key, buf, DB->recbuf) )
X {
X /* Don't remove null keys */
X if( !(key->type & KT_OPTIONAL) || !null_indicator(key, DB->recbuf) )
X keydel(key, DB->recbuf, CURR_REC);
X
X /* Don't insert null keys */
X if( (key->type & KT_OPTIONAL) && null_indicator(key, buf) )
X continue;
X
X if( (rc = keyadd(key, buf, CURR_REC)) != S_OKAY )
X {
X set_subcode(key);
X ty_unlock();
X RETURN rc;
X }
X }
X }
X
X if( rec->is_vlr )
X {
X unsigned size;
X
X if( (rc = compress_vlr(COMPRESS, rec, DB->recbuf, buf, &size)) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X ty_vlrwrite(rec, DB->real_recbuf, size, CURR_REC);
X }
X else
X {
X memcpy(DB->recbuf, buf, rec->size);
X
X ty_recwrite(rec, DB->real_recbuf, CURR_REC);
X }
X
X /* Store changed references to parent records */
X update_foreign_keys(rec, 0);
X
X if( DB->logging )
X ty_log('u');
X
X log_update(CURR_RECID, CURR_REC, rec->size, buf);
X
X ty_unlock();
X
X RETURN S_OKAY;
X}
X
X
X
X/*-------------------------------- d_recread -------------------------------*\
X *
X * Purpose : Read contents of the current record.
X *
X * Parameters: buf - Buffer containing current record.
X *
X * Returns : S_OKAY - Operation performed successfully.
X * S_DELETED - Record has been deleted since last accessed.
X * S_NOCD - No current database.
X * S_NOCR - No current record.
X *
X */
X
XFNCLASS d_recread(buf)
Xvoid *buf;
X{
X Record *rec;
X int rc;
X
X if( CURR_DB == -1 )
X RETURN_RAP(S_NOCD);
X
X if( CURR_REC == 0 )
X RETURN_RAP(S_NOCR);
X
X ty_lock();
X rec = DB->record + CURR_RECID;
X
X if( (rc = update_recbuf()) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X if( rec->is_vlr )
X rc = compress_vlr(UNCOMPRESS, rec, buf, DB->recbuf, NULL);
X else
X {
X memcpy(buf, DB->recbuf, rec->size);
X rc = S_OKAY;
X }
X
X ty_unlock();
X
X RETURN rc;
X}
X
X
X
X/*------------------------------- d_fillnew --------------------------------*\
X *
X * Purpose : Adds a new record to the current database and updates indexes.
X *
X * Parameters: record - Record id.
X * buf - Pointer to record buffer.
X *
X * Returns : S_OKAY - Ok.
X * S_NOCD - No current database.
X * S_INVREC - Invalid record id.
X * S_DUPLICATE - The record contained a duplicate key. The id
X * of the field or compound key is stored in
X * db_subcode.
X * S_RECSIZE - Invalid record size (if variable size).
X * db_subcode contains the ID of the size field.
X * S_FOREIGN - A foreign key was not found (db_subcode holds
X * the foreing key ID.
X *
X */
X
XFNCLASS d_fillnew(record, buf)
XId record;
Xvoid *buf;
X{
X Record *rec;
X Field *fld;
X Key *key;
X ulong ref;
X int i, rc, n;
X
X if( (rc = set_recfld(record, &rec, &fld)) != S_OKAY )
X return rc;
X
X /* So far we have no current record */
X CURR_REC = 0;
X
X ty_lock();
X
X /* Set pointer to actual data */
X DB->recbuf = DB->real_recbuf + rec->preamble;
X
X /* Check foreign keys (if any) */
X if( (rc = check_foreign_keys(rec, buf, 1)) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X /* Make sure that there are no duplicate keys in this record */
X key = DB->key + rec->first_key;
X
X for( n = rec->keys; n-- && !KEY_ISFOREIGN(key); key++ )
X {
X if( key->type & KT_UNIQUE )
X {
X if( KEY_ISOPTIONAL(key) && null_indicator(key, buf) )
X continue;
X
X if( keyfind(key, buf, &ref) == S_OKAY )
X {
X set_subcode(key);
X ty_unlock();
X RETURN S_DUPLICATE;
X }
X }
X }
X
X /* Update data and index files */
X if( rec->is_vlr )
X {
X unsigned size;
X
X if( (rc = compress_vlr(COMPRESS, rec, DB->recbuf, buf, &size)) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X if( (rc = ty_vlradd(rec, DB->real_recbuf, size, &CURR_REC)) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X /* The record in DB->recbuf is still compressed */
X }
X else
X {
X memcpy(DB->recbuf, buf, rec->size);
X if( (rc=ty_recadd(rec, DB->real_recbuf, &CURR_REC)) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X }
X
X CURR_RECID = rec - DB->record;
X n = rec->keys;
X key = DB->key + rec->first_key;
X
X for( n=rec->keys; n-- && !KEY_ISFOREIGN(key); key++ )
X {
X /* Don't store null keys */
X if( KEY_ISOPTIONAL(key) && null_indicator(key, buf) )
X continue;
X
X if( (rc = keyadd(key, buf, CURR_REC)) != S_OKAY )
X {
X ty_unlock();
X RETURN rc;
X }
X }
X
X /* Store references to parent records */
X update_foreign_keys(rec, 1);
X
X if( DB->logging )
X ty_log('u');
X
X log_update(CURR_RECID, CURR_REC, rec->size, buf);
X
X ty_unlock();
X
X RETURN S_OKAY;
X}
X
X
X/*-------------------------------- d_delete --------------------------------*\
X *
X * Purpose : Delete the current record.
X *
X * Parameters: None.
X *
X * Returns : S_OKAY - The was successfully deleted.
X * S_NOCD - No current database.
X * S_NOCR - No current record.
X * S_RESTRICT - A dependent table had a foreign key which
X * referenced the primary key of the record to
X * to be deleted. db_subcode holds the foreign
X * key ID.
X *
X */
X
XFNCLASS d_delete()
X{
X Record *rec;
X Key *key;
X int n, rc;
X
X if( CURR_DB == -1 )
X RETURN_RAP(S_NOCD);
X
X if( CURR_REC == 0 )
X RETURN_RAP(S_NOCR);
X
X /* We must update recbuf in order to access the record's keys */
X ty_lock();
X
X rec = DB->record + CURR_RECID;
X DB->recbuf = DB->real_recbuf + rec->preamble;
X
X if( (rc = update_recbuf()) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X /* Check dependent tables (if any) */
X if( (rc = check_dependent_tables(rec, DB->recbuf, 'd')) != S_OKAY )
X {
X ty_unlock();
X return rc;
X }
X
X key = DB->key + rec->first_key;
X
X if( DB->fh[rec->fileid].any->type == 'd' )
X rc = ty_recdelete(rec, CURR_REC);
X else
X rc = ty_vlrdel(rec, CURR_REC);
X
X if( rc != S_OKAY )
X {
X ty_unlock();
X RETURN rc;
X }
X
X for( n=rec->keys; n-- && !KEY_ISFOREIGN(key); key++ )
X {
X if( KEY_ISOPTIONAL(key) && null_indicator(key, DB->recbuf) )
X continue;
X
X if( (rc = keydel(key, DB->recbuf, CURR_REC)) != S_OKAY )
X {
X printf("typhoon: could not delete key %s.%s (db_status %d)\n",
X rec->name, key->name, rc);
X ty_unlock();
X RETURN rc;
X }
X }
X
X delete_foreign_keys(rec);
X
X if( DB->logging )
X ty_log('d');
X
X log_delete(CURR_RECID, CURR_REC);
X
X CURR_REC = 0;
X
X ty_unlock();
X
X RETURN S_OKAY;
X}
X
X/* end-of-file */
END_OF_FILE
if test 12105 -ne `wc -c <'typhoon/src/ty_ins.c'`; then
echo shar: \"'typhoon/src/ty_ins.c'\" unpacked with wrong size!
fi
# end of 'typhoon/src/ty_ins.c'
fi
if test -f 'typhoon/src/ty_io.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'typhoon/src/ty_io.c'\"
else
echo shar: Extracting \"'typhoon/src/ty_io.c'\" \(12101 characters\)
sed "s/^X//" >'typhoon/src/ty_io.c' <<'END_OF_FILE'
X/*----------------------------------------------------------------------------
X * File : bt_io
X * Library : typhoon
X * OS : UNIX, OS/2, DOS
X * Author : Thomas B. Pedersen
X *
X * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved.
X *
X * Permission is hereby granted, without written agreement and without
X * license or royalty fees, to use, copy, modify, and distribute this
X * software and its documentation for any purpose, provided that the above
X * copyright notice and the following two paragraphs appear (1) in all
X * source copies of this software and (2) in accompanying documentation
X * wherever the programatic interface of this software, or any derivative
X * of it, is described.
X *
X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X *
X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
X * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
X *
X * Description:
X * This file contains the layer between the low-level B-tree and record
X * routines and the Typhoon API functions. The low-level routines are never
X * called directly from the API functions.
X *
X * +-----------------------------+
X * | Typhoon API functions |
X * +-----------------------------+
X * | Dynamic open files layer | <----- this layer
X * +----------+------------------+
X * | B-tree | Record | VLR |
X * +----------+------------------+
X * | C language read/write |
X * +-----------------------------+
X *
X * Functions:
X *
X * $Log: ty_io.c,v $
X * Revision 1.2 1994/09/17 16:00:16 tbp
X * typhoon.h and environ.h are now included from <>.
X *
X * Revision 1.1 1994/09/13 21:28:34 tbp
X * Added to repository.
X *
X *
X *--------------------------------------------------------------------------*/
X
Xstatic char rcsid[] = "$Id: ty_io.c,v 1.2 1994/09/17 16:00:16 tbp Exp $";
X
X#ifdef UNIX
X# include <unistd.h>
X#endif
X#include <string.h>
X#include <stdio.h>
X#include <errno.h>
X#include <stdarg.h>
X#include <time.h>
X#include <typhoon.h>
X#include "ty_dbd.h"
X#include "ty_type.h"
X#include "ty_glob.h"
X#include "ty_prot.h"
X
X#define PTRTORECID(rec) ((((rec) - DB->record)+1) * REC_FACTOR)
X
X/*-------------------------- Function prototypes ---------------------------*/
Xstatic int checkfile PRM( (int); )
X
X/*---------------------------- Global variables ----------------------------*/
Xstatic ulong seqno = 1; /* Current sequence number (always > 0) */
X
X
X
X
Xty_closeafile()
X{
X ulong lowest_seqno = seqno;
X int dbs, fhs; /* Used for scanning tables */
X Dbentry *db =typhoon.dbtab; /* Used for scanning tables */
X Fh *fh; /* Used for scanning tables */
X Fh *foundfh = NULL; /* The filehandle found to be closed */
X
X /* Find the file least recently accessed open file */
X dbs = DB_MAX;
X while( dbs-- )
X {
X if( DB->clients )
X {
X fh = DB->fh;
X fhs = DB->header.files;
X
X while( fhs-- )
X {
X if( fh->any && fh->any->fh != -1 )
X {
X if( lowest_seqno > fh->any->seqno )
X {
X lowest_seqno = fh->any->seqno;
X foundfh = fh;
X }
X }
X fh++;
X }
X }
X db++;
X }
X
X if( foundfh == NULL )
X {
X printf("\a*** Could not close a file **");
X return -1;
X }
X
X /* If the file is already closed, we just return */
X if( foundfh->any->fh == -1 )
X return S_OKAY;
X
X switch( foundfh->any->type )
X {
X case 'k':
X case 'r':
X btree_dynclose(foundfh->key);
X break;
X case 'd':
X rec_dynclose(foundfh->rec);
X break;
X case 'v':
X vlr_dynclose(foundfh->vlr);
X break;
X }
X
X /* Mark the file as closed */
X typhoon.cur_open--;
X
X return 0;
X}
X
X
X
X
Xstatic checkfile(fileid)
Xint fileid;
X{
X Fh *fh;
X int rc;
X
X fh = &DB->fh[fileid];
X
X /* If the file is open we set the file's sequence number to seqno to
X * indicate that it is the most recently accessed file.
X */
X if( fh->any->fh != -1 )
X {
X fh->any->seqno = seqno++;
X return S_OKAY;
X }
X
X if( ty_closeafile() == -1 )
X {
X puts("checkfile: could not find a file to close");
X RETURN S_IOFATAL;
X }
X
X /* Now <cur_open> should be less than <max_open> so we can call
X * ty_openfile() to reopen the file. If ty_openfile() cannot open the
X * file the API function will return S_IOFATAL.
X */
X
X switch( fh->any->type )
X {
X case 'k':
X case 'r':
X rc = btree_dynopen(fh->key);
X break;
X case 'd':
X rc = rec_dynopen(fh->rec);
X break;
X case 'v':
X rc = vlr_dynopen(fh->vlr);
X break;
X }
X
X fh->any->seqno = seqno++;
X typhoon.cur_open++;
X
X return rc;
X}
X
X
X/*------------------------------ ty_openfile -------------------------------*\
X *
X * Purpose : Opens a database file.
X *
X * Parameters: fp - Pointer to file definition table entry.
X * fh - Pointer to file handle table entry.
X * shared - Open in shared mode?
X *
X * Returns : db_status from d_keyopen(), d_recopen() or vlr_open().
X *
X */
X
Xty_openfile(fp, fh, shared)
XFile *fp;
XFh *fh;
Xint shared;
X{
X char fname[255];
X Key *key;
X CMPFUNC cmp;
X int rc;
X
X /* If the maximum number of open files has been reached we return the
X * CLOSEDPTR to indicate to the other ty_.. functions that the file
X * descriptor points to a closed file and must be reopened.
X */
X if( typhoon.cur_open == typhoon.max_open )
X {
X if( ty_closeafile() == -1 )
X {
X puts("ty_openfile: could not find a file to close");
X RETURN S_IOFATAL;
X }
X }
X
X /* File is located in <DB->dbfpath> */
X sprintf(fname, "%s%s", DB->dbfpath, fp->name);
X
X switch( fp->type )
X {
X case 'r':
X fh->key = btree_open(fname, sizeof(REF_ENTRY), fp->pagesize, (CMPFUNC)refentrycmp, 0, shared);
X break;
X case 'k':
X key = DB->key + fp->id;
X
X /* If the key has multiple fields or is sorted in descending order
X * we use the compoundkeycmp function for key value comparisons.
X */
X if( key->fields > 1 || !DB->keyfield[key->first_keyfield ].asc )
X cmp = compoundkeycmp;
X else
X {
X Field *fld = &DB->field[ DB->keyfield[key->first_keyfield].field ];
X
X cmp = keycmp[ fld->type & (FT_BASIC|FT_UNSIGNED) ];
X }
X
X fh->key = btree_open(fname, key->size, fp->pagesize, cmp,
X (key->type & KT_UNIQUE) ? 0 : 1, shared);
X break;
X case 'd':
X /* Add the preamble to the size of the record */
X fh->rec = rec_open(fname, DB->record[fp->id].size +
X DB->record[fp->id].preamble, shared);
X break;
X case 'v':
X fh->vlr = vlr_open(fname, fp->pagesize, shared);
X break;
X }
X
X if( db_status == S_OKAY )
X {
X fh->any->type = fp->type;
X fh->any->seqno = seqno++;
X typhoon.cur_open++;
X }
X/*
X else
X printf("cannot open '%s' (db_status %d, errno %d)\n", fname, db_status, errno);
X*/
X return db_status;
X}
X
X
X/*------------------------------- d_closefile ------------------------------*\
X *
X * Purpose : Closes a database file.
X *
X * Parameters: fh - Pointer to file handle table entry.
X *
X * Returns : db_status from d_keyclose(), d_recclose() or vlr_close().
X *
X */
X
Xty_closefile(fh)
XFh *fh;
X{
X /* If the file is already closed, we just return */
X if( fh->any->fh != -1 )
X typhoon.cur_open--;
X
X switch( fh->any->type )
X {
X case 'k':
X case 'r':
X btree_close(fh->key);
X break;
X case 'd':
X rec_close(fh->rec);
X break;
X case 'v':
X vlr_close(fh->vlr);
X break;
X }
X
X /* Mark the file as closed */
X fh->any->fh = -1;
X
X return db_status;
X}
X
X
X
Xty_keyadd(key, value, ref)
XKey *key;
Xvoid *value;
Xulong ref;
X{
X INDEX *idx;
X int rc;
X
X if( (rc = checkfile(key->fileid)) != S_OKAY )
X return rc;
X
X idx = DB->fh[key->fileid].key;
X rc = btree_add(idx, value, ref);
X btree_keyread(idx, CURR_KEYBUF);
X
X return rc;
X}
X
X
Xty_keyfind(key, value, ref)
XKey *key;
Xvoid *value;
Xulong *ref;
X{
X INDEX *idx;
X int rc;
X
X if( (rc = checkfile(key->fileid)) != S_OKAY )
X return rc;
X
X idx = DB->fh[key->fileid].key;
X rc = btree_find(idx, value, ref);
X btree_keyread(idx, CURR_KEYBUF);
X
X return rc;
X}
X
X
X
Xty_keyfrst(key, ref)
XKey *key;
Xulong *ref;
X{
X INDEX *idx;
X int rc;
X
X if( (rc = checkfile(key->fileid)) != S_OKAY )
X return rc;
X
X idx = DB->fh[key->fileid].key;
X rc = btree_frst(idx, ref);
X btree_keyread(idx, CURR_KEYBUF);
X
X return rc;
X}
X
X
Xty_keylast(key, ref)
XKey *key;
Xulong *ref;
X{
X INDEX *idx;
X int rc;
X
X if( (rc = checkfile(key->fileid)) != S_OKAY )
X return rc;
X
X idx = DB->fh[key->fileid].key;
X rc = btree_last(idx, ref);
X btree_keyread(idx, CURR_KEYBUF);
X
X return rc;
X}
X
X
Xty_keyprev(key, ref)
XKey *key;
Xulong *ref;
X{
X INDEX *idx;
X int rc;
X
X if( (rc = checkfile(key->fileid)) != S_OKAY )
X return rc;
X
X idx = DB->fh[key->fileid].key;
X rc = btree_prev(idx, ref);
X btree_keyread(idx, CURR_KEYBUF);
X
X return rc;
X}
X
X
Xty_keynext(key, ref)
XKey *key;
Xulong *ref;
X{
X INDEX *idx;
X int rc;
X
X if( (rc = checkfile(key->fileid)) != S_OKAY )
X return rc;
X
X idx = DB->fh[key->fileid].key;
X rc = btree_next(idx, ref);
X btree_keyread(idx, CURR_KEYBUF);
X
X return rc;
X}
X
X
Xty_keydel(key, value, ref)
XKey *key;
Xvoid *value;
Xulong ref;
X{
X INDEX *idx;
X int rc;
X
X if( (rc = checkfile(key->fileid)) != S_OKAY )
X return rc;
X
X idx = DB->fh[key->fileid].key;
X rc = btree_del(idx, value, ref);
X btree_keyread(idx, CURR_KEYBUF);
X
X return rc;
X}
X
X
Xty_recadd(rec, buf, recno)
XRecord *rec;
Xvoid *buf;
Xulong *recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_add(DB->fh[rec->fileid].rec, buf, recno);
X}
X
X
Xty_recwrite(rec, buf, recno)
XRecord *rec;
Xvoid *buf;
Xulong recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_write(DB->fh[rec->fileid].rec, buf, recno);
X}
X
X
Xty_recread(rec, buf, recno)
XRecord *rec;
Xvoid *buf;
Xulong recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_read(DB->fh[rec->fileid].rec, buf, recno);
X}
X
X
Xty_recdelete(rec, recno)
XRecord *rec;
Xulong recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_delete(DB->fh[rec->fileid].rec, recno);
X}
X
X
Xty_recfrst(rec, buf)
XRecord *rec;
Xvoid *buf;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_frst(DB->fh[rec->fileid].rec, buf);
X}
X
X
Xty_reclast(rec, buf)
XRecord *rec;
Xvoid *buf;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_last(DB->fh[rec->fileid].rec, buf);
X}
X
X
Xty_recnext(rec, buf)
XRecord *rec;
Xvoid *buf;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_next(DB->fh[rec->fileid].rec, buf);
X}
X
Xty_recprev(rec, buf)
XRecord *rec;
Xvoid *buf;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_prev(DB->fh[rec->fileid].rec, buf);
X}
X
Xulong ty_reccount(rec, count)
XRecord *rec;
Xulong *count;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_numrecords(DB->fh[rec->fileid].rec, count);
X}
X
Xty_reccurr(rec, recno)
XRecord *rec;
Xulong *recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return rec_curr(DB->fh[rec->fileid].rec, recno);
X}
X
Xty_vlradd(rec, buf, size, recno)
XRecord *rec;
Xvoid *buf;
Xunsigned size;
Xulong *recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return vlr_add(DB->fh[rec->fileid].vlr, buf, size, recno);
X}
X
Xty_vlrwrite(rec, buf, size, recno)
XRecord *rec;
Xvoid *buf;
Xunsigned size;
Xulong recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return vlr_write(DB->fh[rec->fileid].vlr, buf, size + rec->preamble, recno);
X}
X
X
Xunsigned ty_vlrread(rec, buf, recno, size)
XRecord *rec;
Xvoid *buf;
Xulong recno;
Xunsigned *size;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return vlr_read(DB->fh[rec->fileid].vlr, buf, recno, size);
X}
X
Xty_vlrdel(rec, recno)
XRecord *rec;
Xulong recno;
X{
X int rc;
X
X if( (rc = checkfile(rec->fileid)) != S_OKAY )
X return rc;
X
X return vlr_del(DB->fh[rec->fileid].vlr, recno);
X}
X
X/* end-of-file */
END_OF_FILE
if test 12101 -ne `wc -c <'typhoon/src/ty_io.c'`; then
echo shar: \"'typhoon/src/ty_io.c'\" unpacked with wrong size!
fi
# end of 'typhoon/src/ty_io.c'
fi
if test -f 'typhoon/src/util/ddlpsym.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'typhoon/src/util/ddlpsym.c'\"
else
echo shar: Extracting \"'typhoon/src/util/ddlpsym.c'\" \(6862 characters\)
sed "s/^X//" >'typhoon/src/util/ddlpsym.c' <<'END_OF_FILE'
X/*----------------------------------------------------------------------------
X * File : ddlpsym.c
X * Program : ddlp
X * OS : UNIX, OS/2, DOS
X * Author : Thomas B. Pedersen
X *
X * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved.
X *
X * Permission is hereby granted, without written agreement and without
X * license or royalty fees, to use, copy, modify, and distribute this
X * software and its documentation for any purpose, provided that the above
X * copyright notice and the following two paragraphs appear (1) in all
X * source copies of this software and (2) in accompanying documentation
X * wherever the programatic interface of this software, or any derivative
X * of it, is described.
X *
X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN
X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X *
X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
X * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
X *
X * Description:
X * Symbol table functions for ddlp.
X *
X * $Log: ddlpsym.c,v $
X * Revision 1.2 1994/09/17 16:00:52 tbp
X * typhoon.h and environ.h are now included from <>.
X *
X * Revision 1.1 1994/09/13 21:28:53 tbp
X * Added to repository.
X *
X * Added to repository.
X *
X *
X *--------------------------------------------------------------------------*/
X
Xstatic char rcsid[] = "$Id: ddlpsym.c,v 1.2 1994/09/17 16:00:52 tbp Exp $";
X
X#include <stdio.h>
X#include <string.h>
X#ifndef UNIX
X# include <stdlib.h>
X#endif
X#include <environ.h>
X#include "../ty_dbd.h"
X#include "ddlp.h"
X#include "ddlpsym.h"
X#include "ddlpglob.h"
X
X/*-------------------------- Function prototypes ---------------------------*/
Xstatic void print_struct PRM( (sym_struct *, int); )
X
X/*---------------------------- Global variables ----------------------------*/
Xsym_struct *first_str = NULL; /* Pointer to start of struct table */
Xsym_struct *last_str = NULL; /* Pointer to last struct */
Xsym_struct *cur_str = NULL; /* Pointer to current struct */
Xsym_struct *structnest[NEST_MAX]; /* Structure nesting table */
Xint curnest = -1; /* Current structure nesting */
X
X
X/*------------------------------ sym_addmember -----------------------------*\
X *
X * Purpose: This function adds a member to the structure currently
X * being defined. The member is aligned according to <align>.
X *
X * Params : name - struct member name.
X * type - member type, see FT_.. constants.
X * ... - If <type> == FT_STRUCT the 3rd parameter is a
X * pointer to a structdef record, otherwise it is
X * the size of the field.
X *
X * Returns : Nothing
X *
X */
X
Xvoid sym_addmember(name, type, struc)
Xchar *name;
Xint type;
Xsym_struct *struc;
X{
X sym_struct *str = structnest[curnest];
X sym_member *mem;
X
X /* Make sure that this member name is not already used in this struct */
X for( mem = str->first_member; mem; mem = mem->next )
X if( !strcmp(name, mem->name) )
X {
X yyerror("duplicate member name '%s'", name);
X return;
X }
X
X if( !(mem = (sym_member *)calloc(1,sizeof(*mem) + (dims-1) * sizeof(*mem->dim))) )
X err_quit("out of memory");
X
X if( type == FT_STRUCT )
X {
X mem->struc = struc;
X mem->size = struc->size;
X }
X else
X mem->size = typeinfo[(type & FT_BASIC)-1].size;
X
X strcpy(mem->name, name);
X mem->type = type;
X mem->dims = dims;
X mem->elemsize = mem->size;
X
X /* If this member has arrays they must be stored and the size adjusted */
X if( dims )
X {
X memcpy(mem->dim, dim, sizeof(*dim) * dims);
X
X while( --dims )
X dim[0] *= dim[dims];
X mem->size *= dim[0];
X }
X
X /* Add the new member to the member list */
X if( str->last_member )
X str->last_member->next = mem;
X else
X str->first_member = mem;
X str->last_member = mem;
X str->members++;
X
X /*
X * If the structure is a union all members must be at offset 0, and
X * the union must have the size of the biggest member.
X */
X if( str->is_union )
X {
X if( mem->size > str->size )
X str->size = mem->size;
X }
X else
X {
X align_offset(&str->size, type);
X mem->offset = str->size;
X str->size += mem->size;
X }
X}
X
X
Xvoid sym_addstruct(name, is_union)
Xchar *name;
Xint is_union;
X{
X sym_struct *str;
X
X /* Allocate new structdef and clear all fields */
X if( !(str = (sym_struct *)calloc(1, sizeof *str)) )
X err_quit("out of memory");
X
X strcpy(str->name, name);
X str->is_union = is_union;
X
X /* Insert structure in list */
X if( !first_str )
X first_str = str;
X else
X last_str->next = str;
X cur_str = last_str = str;
X
X structnest[++curnest] = str;
X}
X
X
Xvoid sym_endstruct()
X{
X /* The size of a structure depends depends on the type of the last member
X */
X/* align_offset(&structnest[curnest]->size, FT_LONG);*/
X align_offset(&structnest[curnest]->size, cur_str->last_member->type);
X
X cur_str = structnest[--curnest];
X}
X
X
Xsym_struct *sym_findstruct(name, is_union)
Xchar *name;
Xint is_union;
X{
X sym_struct *str = first_str;
X
X while( str )
X {
X if( !strcmp(name, str->name) && str->is_union == is_union )
X return str;
X
X str = str->next;
X }
X
X yyerror("unknown struct '%s'", name);
X return NULL;
X}
X
X
Xsym_member *sym_findmember(str, name)
Xsym_struct *str;
Xchar *name;
X{
X sym_member *mem = str->first_member;
X
X while( mem )
X {
X if( !strcmp(name, mem->name) )
X return mem;
X
X mem = mem->next;
X }
X
X return NULL;
X}
X
X
Xstatic void print_struct(str, nesting)
Xsym_struct *str;
X{
X static char *type[] = { "struct", "union" };
X sym_member *mem = str->first_member;
X extern FILE *hfile;
X int i;
X
X fprintf(hfile, "%*s%s %s { /* size %d */\n", nesting * 4, "",
X type[str->is_union],
X str->name,
X str->size);
X
X while( mem )
X {
X if( mem->type == FT_STRUCT )
X {
X if( mem->struc->printed )
X {
X fprintf(hfile, "%*s%s %s ",
X (nesting+1)*4, "",
X type[mem->struc->is_union],
X mem->struc->name);
X }
X else
X {
X mem->struc->printed = 1;
X print_struct(mem->struc, nesting+1);
X fprintf(hfile, "%*s} ", (nesting+1)*4, "");
X }
X }
X else
X {
X fprintf(hfile, "%*s", (nesting+1) * 4, "");
X if( mem->type & FT_UNSIGNED )
X fprintf(hfile, "unsigned ");
X fprintf(hfile, "%-8s", typeinfo[(mem->type & FT_BASIC) - 1].name);
X }
X
X fprintf(hfile, "%s", mem->name);
X
X for( i=0; i<mem->dims; i++ )
X fprintf(hfile, "[%d]", mem->dim[i]);
X
X fprintf(hfile, ";\n");
X mem = mem->next;
X }
X
X if( nesting == 0 )
X fprintf(hfile, "};\n\n");
X}
X
X
Xvoid print_structures()
X{
X sym_struct *str = first_str;
X
X while( str )
X {
X if( !str->printed )
X print_struct(str, 0);
X str = str->next;
X }
X}
X
X
X/* end-of-file */
END_OF_FILE
if test 6862 -ne `wc -c <'typhoon/src/util/ddlpsym.c'`; then
echo shar: \"'typhoon/src/util/ddlpsym.c'\" unpacked with wrong size!
fi
# end of 'typhoon/src/util/ddlpsym.c'
fi
if test -f 'typhoon/src/util/imp_y.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'typhoon/src/util/imp_y.h'\"
else
echo shar: Extracting \"'typhoon/src/util/imp_y.h'\" \(215 characters\)
sed "s/^X//" >'typhoon/src/util/imp_y.h' <<'END_OF_FILE'
X#define T_IMPORT 257
X#define T_RECORD 258
X#define T_STRUCT 259
X#define T_UNION 260
X#define T_IN 261
X#define T_IDENT 262
X#define T_STRING 263
Xtypedef union {
X char s[IDENT_LEN+1];
X} YYSTYPE;
Xextern YYSTYPE yylval;
END_OF_FILE
if test 215 -ne `wc -c <'typhoon/src/util/imp_y.h'`; then
echo shar: \"'typhoon/src/util/imp_y.h'\" unpacked with wrong size!
fi
# end of 'typhoon/src/util/imp_y.h'
fi
echo shar: End of archive 3 \(of 9\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 9 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0
exit 0 # Just in case...