From 95fab567efd05d644683ccebce26c918a3af45d5 Mon Sep 17 00:00:00 2001 From: "Jonah H. Harris" Date: Sun, 28 Jan 2018 01:33:59 -0500 Subject: [PATCH] first commit --- CHANGES | 21 + ChangeLog | 84 ++ INSTALL | 45 ++ Makefile | 34 + README | 132 ++++ TODO | 8 + configure | 327 ++++++++ examples/Makefile.in | 55 ++ examples/demo.c | 285 +++++++ examples/demo.ddl | 72 ++ examples/makefile.os2 | 15 + include/env_os2.h | 89 +++ include/typhoon.h | 175 +++++ man/Makefile | 66 ++ man/d_close.3 | 38 + man/d_crget.3 | 41 + man/d_crread.3 | 62 ++ man/d_crset.3 | 41 + man/d_dbdpath.3 | 37 + man/d_dbfpath.3 | 40 + man/d_dbget.3 | 60 ++ man/d_dbset.3 | 39 + man/d_delete.3 | 60 ++ man/d_fillnew.3 | 65 ++ man/d_getsequence.3 | 41 + man/d_keyfind.3 | 79 ++ man/d_keyfrst.3 | 78 ++ man/d_keylast.3 | 78 ++ man/d_keynext.3 | 81 ++ man/d_keyprev.3 | 81 ++ man/d_keyread.3 | 60 ++ man/d_open.3 | 57 ++ man/d_recfrst.3 | 68 ++ man/d_reclast.3 | 45 ++ man/d_recnext.3 | 68 ++ man/d_recprev.3 | 45 ++ man/d_recread.3 | 60 ++ man/d_recwrite.3 | 75 ++ man/d_setfiles.3 | 40 + man/ddlp.1 | 146 ++++ man/manual.asc | 1740 +++++++++++++++++++++++++++++++++++++++++ src/.tedhist | Bin 0 -> 1440 bytes src/Makefile.in | 92 +++ src/ansi.c | 100 +++ src/bt_del.c | 514 ++++++++++++ src/bt_funcs.c | 603 ++++++++++++++ src/bt_io.c | 87 +++ src/bt_open.c | 467 +++++++++++ src/btree.h | 118 +++ src/catalog.ddl | 123 +++ src/catalog.h | 89 +++ src/cmpfuncs.c | 246 ++++++ src/dos.c | 67 ++ src/makefile.os2 | 19 + src/os.c | 192 +++++ src/os2.c | 154 ++++ src/readdbd.c | 122 +++ src/record.c | 439 +++++++++++ src/sequence.c | 188 +++++ src/ty_auxfn.c | 448 +++++++++++ src/ty_dbd.h | 203 +++++ src/ty_find.c | 449 +++++++++++ src/ty_glob.h | 103 +++ src/ty_ins.c | 534 +++++++++++++ src/ty_io.c | 628 +++++++++++++++ src/ty_lock.c | 48 ++ src/ty_log.c | 216 +++++ src/ty_log.h | 155 ++++ src/ty_open.c | 633 +++++++++++++++ src/ty_prot.h | 203 +++++ src/ty_refin.c | 328 ++++++++ src/ty_repif.h | 142 ++++ src/ty_repl.c | 267 +++++++ src/ty_type.h | 230 ++++++ src/ty_util.c | 240 ++++++ src/typhoon.def | 61 ++ src/unix.c | 284 +++++++ src/vlr.c | 400 ++++++++++ util/.tedhist | Bin 0 -> 1440 bytes util/Makefile.in | 107 +++ util/backup.c | 528 +++++++++++++ util/dbdview.c | 314 ++++++++ util/ddl.y | 556 +++++++++++++ util/ddlp.c | 926 ++++++++++++++++++++++ util/ddlp.h | 77 ++ util/ddlpglob.h | 116 +++ util/ddlplex.c | 164 ++++ util/ddlpsym.c | 283 +++++++ util/ddlpsym.h | 111 +++ util/exp.y | 229 ++++++ util/export.c | 429 ++++++++++ util/export.h | 109 +++ util/exportlx.c | 117 +++ util/expspec.c | 170 ++++ util/fixlog.c | 322 ++++++++ util/imp.y | 235 ++++++ util/import.c | 591 ++++++++++++++ util/import.h | 108 +++ util/importlx.c | 124 +++ util/impspec.c | 169 ++++ util/lex.c | 172 ++++ util/lex.h | 43 + util/makefile.os2 | 51 ++ util/restore.c | 633 +++++++++++++++ util/util.c | 104 +++ util/util.h | 42 + 106 files changed, 20755 insertions(+) create mode 100644 CHANGES create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100755 configure create mode 100644 examples/Makefile.in create mode 100644 examples/demo.c create mode 100644 examples/demo.ddl create mode 100644 examples/makefile.os2 create mode 100644 include/env_os2.h create mode 100644 include/typhoon.h create mode 100644 man/Makefile create mode 100644 man/d_close.3 create mode 100644 man/d_crget.3 create mode 100644 man/d_crread.3 create mode 100644 man/d_crset.3 create mode 100644 man/d_dbdpath.3 create mode 100644 man/d_dbfpath.3 create mode 100644 man/d_dbget.3 create mode 100644 man/d_dbset.3 create mode 100644 man/d_delete.3 create mode 100644 man/d_fillnew.3 create mode 100644 man/d_getsequence.3 create mode 100644 man/d_keyfind.3 create mode 100644 man/d_keyfrst.3 create mode 100644 man/d_keylast.3 create mode 100644 man/d_keynext.3 create mode 100644 man/d_keyprev.3 create mode 100644 man/d_keyread.3 create mode 100644 man/d_open.3 create mode 100644 man/d_recfrst.3 create mode 100644 man/d_reclast.3 create mode 100644 man/d_recnext.3 create mode 100644 man/d_recprev.3 create mode 100644 man/d_recread.3 create mode 100644 man/d_recwrite.3 create mode 100644 man/d_setfiles.3 create mode 100644 man/ddlp.1 create mode 100644 man/manual.asc create mode 100644 src/.tedhist create mode 100644 src/Makefile.in create mode 100644 src/ansi.c create mode 100644 src/bt_del.c create mode 100644 src/bt_funcs.c create mode 100644 src/bt_io.c create mode 100644 src/bt_open.c create mode 100644 src/btree.h create mode 100644 src/catalog.ddl create mode 100644 src/catalog.h create mode 100644 src/cmpfuncs.c create mode 100644 src/dos.c create mode 100644 src/makefile.os2 create mode 100644 src/os.c create mode 100644 src/os2.c create mode 100644 src/readdbd.c create mode 100644 src/record.c create mode 100644 src/sequence.c create mode 100644 src/ty_auxfn.c create mode 100644 src/ty_dbd.h create mode 100644 src/ty_find.c create mode 100644 src/ty_glob.h create mode 100644 src/ty_ins.c create mode 100644 src/ty_io.c create mode 100644 src/ty_lock.c create mode 100644 src/ty_log.c create mode 100644 src/ty_log.h create mode 100644 src/ty_open.c create mode 100644 src/ty_prot.h create mode 100644 src/ty_refin.c create mode 100644 src/ty_repif.h create mode 100644 src/ty_repl.c create mode 100644 src/ty_type.h create mode 100644 src/ty_util.c create mode 100755 src/typhoon.def create mode 100644 src/unix.c create mode 100644 src/vlr.c create mode 100644 util/.tedhist create mode 100644 util/Makefile.in create mode 100644 util/backup.c create mode 100644 util/dbdview.c create mode 100644 util/ddl.y create mode 100644 util/ddlp.c create mode 100644 util/ddlp.h create mode 100644 util/ddlpglob.h create mode 100644 util/ddlplex.c create mode 100644 util/ddlpsym.c create mode 100644 util/ddlpsym.h create mode 100644 util/exp.y create mode 100644 util/export.c create mode 100644 util/export.h create mode 100644 util/exportlx.c create mode 100644 util/expspec.c create mode 100644 util/fixlog.c create mode 100644 util/imp.y create mode 100644 util/import.c create mode 100644 util/import.h create mode 100644 util/importlx.c create mode 100644 util/impspec.c create mode 100644 util/lex.c create mode 100644 util/lex.h create mode 100644 util/makefile.os2 create mode 100644 util/restore.c create mode 100644 util/util.c create mode 100644 util/util.h diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..29f532b --- /dev/null +++ b/CHANGES @@ -0,0 +1,21 @@ +Release 1.11.0 (Sun Oct 3 1999) + + 1. Cleanup release based on 1.10.3. (See ``ChangeLog''). Many, many + problems were fixed: + - use of implicit int in declarations; + - undeclared functions being called---many headers added; + - parameters not being converted to the correct type + if compiling without ANSI C prototypes; + - broken configure script not making correct decisions---for example + disabling prototypes even though GCC is being used; + - preference given to GNU tools, like bison over yacc; + - tools falling out of main() without returning exit status; + - use of invalid void main() construct; + - mismatched types in printf() calls; + 2. Fixed bug in tyexport and tyimport programs caused by + calling fclose on the wrong FILE * pointer. These tools were + totally broken because of this and wouldn't export or import; + they would crash after parsing the import or export spec. + 3. Added support for POSIX fcntl() locking, which is used in + preference to lockf(). The lockf() emulation on Linux using flock() + is gone. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ea62d4e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,84 @@ +This is Thomas B. Pedersen's original Changelog up to version 1.10.3. The +CHANGES file summarized changes done by Kaz Kylheku. +------------------------------------------------------------------------------- +Version 1.10.3 + + o A calloc() call in ddlp.c had only one argument. + +Version 1.10.2 + + o d_getsequence() would generate a SIGSEGV if the database was + closed and opened in between calls. + +Version 1.10.1 + + o Apparently, there was still a problem with variable length records. + +Version 1,10 + + o After moving global variables to the typhoon structure, foreign keys + didn't work properly. + o When the number of open files was 0, e.g. after closing a database, + an error message 'could not close a file' was printed on the screen. + o Added support for sequences. + o d_close() was not protected by ty_lock/ty_unlock calls. + o btree_close() wrote header which could corrupt the delete chain. + o vlr_close() wrote header which could corrupt the delete chain. + o ddlp now expands its tables dynamically. + +Version 1.09 + + o d_fillnew() wrote mostly zeros when writing variable length records + that spanned more than one page. + +Version 1.08 + + o Oops. Forgot to incorporate bug reported by duke@diku.dk. When multiple + databases were open, ty_closeafile() would only search the first + database in dbtab. This bug could cause inconsistent indexes. + +Version 1.07 + + o Fixed a couple of documentation errors. + o btree_add() was missing a call to btree_getheader() which could cause + indexes to get corrupted. This only happened when multiple processes + were doing heavy insert/delete operations on the same index. + o Many have requested that man pages be moved to section 3. This has now + been done. + +Version 1.06 + + o Added ASCII manual. + o In a variable length record table with foreign keys, the last few bytes + of each record would be lost. + +Version 1.05 + + o Added OS/2 makefiles. + +Version 1.04 + + o Minor modifications to configure script. + o typhoon.h no longer requires inclusion of sys/types.h. + o Corrected syntax error in RISC alignment code. + +Version 1.03 + + o Added configure script. + o Torsten Liermann reported a memory access error found with purify. The + error only occurred when closing a database, so it rarely occurred. + o The demonstration program in ./examples forgot to set the length + determinator of product.description. + o Support for platforms with sizeof(long)=8, e.g. Alpha. + o Tricky construct in sym_addmember() made calloc() return NULL on + some platforms. + +Version 1.02 + + o New makefiles by George Sipe should fix a lot of conflicts on platforms + I haven't been able to compile the code on. + +Version 1.01 + + o Just after releasing version 1.0 I was informed about two missing + include files. Stupid mistake which has now been fixed. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..594967a --- /dev/null +++ b/INSTALL @@ -0,0 +1,45 @@ +The Typhoon RDBMS has been compiled and tested on the following platforms: + + IBM RS/6000 running AIX + 386/486 running Linux 0.99,1.72 + 386/486 running OS/2 2.0, 2.1, 2.99 + 386/486 running SCO UNIX SVR3.2 Version 4.2 + Sun Solaris 2.3, 2.4 + Tandem S2 running NonStop Unix SVR4 B.22 + +The library has been compiled on the following platforms: + + UnixWare + HP-UX 9.01 + Digital OSF/1 AXP + SunOS 4.1.3 + Data General AViiON running DG/UX 5.4.2 + SGI + +------------------------------------------------------------------------------- + +Installation: + +Type 'configure' in the typhoon directory and then 'make'. The output from +the compilation consists of the following files. + + ./include/typhoon.h + ./include/ansi.h + ./src/libtyphoon.a + ./util/ddlp + ./util/dbdview + ./util/tyexport + ./util/tyimport + ./man/... + +If you are installing under OS/2 you must rename the file include/env_os2.h +to include/environ.h before making. To make enter the following: + + cd src + nmake -f makefile.os2 + cd ../util + nmake -f makefile.os2 + cd ../examples + nmake -f makefile.os2 + +Remember to place typhoon.dll in a directory that is in LIBPATH. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1c4fa2b --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +# Makefile for: typhoon - top level makefile + +MANEXT = l +PREFIX = /usr/local +DESTMAN = $(PREFIX)/man/man$(MANEXT) +DESTCAT = $(PREFIX)/man/cat$(MANEXT) +DESTOWN = root +DESTGRP = local +SHELL = /bin/sh +MAKE = make + +.PHONY: all install uninstall clean distclean + +all install uninstall: include/ansi.h include/environ.h + cd src; $(MAKE) $@ + cd util; $(MAKE) $@ + cd examples; $(MAKE) $@ + cd man; $(MAKE) $@ + +include/ansi.h include/environ.h: + ./configure + +clean: + cd src; $(MAKE) $@ + cd util; $(MAKE) $@ + cd examples; $(MAKE) $@ + cd man; $(MAKE) $@ + +distclean: clean + -rm -f include/ansi.h include/environ.h + cd src; $(MAKE) $@ + cd util; $(MAKE) $@ + cd examples; $(MAKE) $@ + cd man; $(MAKE) $@ diff --git a/README b/README new file mode 100644 index 0000000..818c7a1 --- /dev/null +++ b/README @@ -0,0 +1,132 @@ +Dear programmer, + +I have decided to make new releases of the Typhoon library, using +release Thomas Pedersen's 1.10.3 release as my starting point. Below is the +original copyright message which should be retained in all derived works at the +request of the original author, Thomas B. Pedersen. + +A number of years ago I used Typhoon successfully in a project that needed a +database. Recently, I did some searching around the net hoping to find a new +improved Typhoon library. To my dismay, nothing new had been released in about +four years. After trying to contact the author to no avail, I decided to take +over maintenance of the software. I'm numbering my releases 1.11.x for now. +The first one is 1.11.0 and is purely cleanup related. My aim was to produce a +clean build on modern GNU systems, like Linux with glibc2. I fixed up errors in +the configure script and many issues arising out of using crusty K&R C. I'm +contemplating converting everything to ANSI C, but I've decided not to do this +in the first cleanup release. + +Bug reports and other e-mail related to this software should be sent to +. + +Yours, + +Kaz Kylheku + +October 3, 1999. + +Original README text follows +------------------------------------------------------------------------------- + + + Typhoon Relational Database Management System v1.0 + + Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + + Permission is hereby granted, without written agreement and without + license or royalty fees, to use, copy, modify, and distribute this + software and its documentation for any purpose, provided that the above + copyright notice and the following two paragraphs appear (1) in all + source copies of this software and (2) in accompanying documentation + wherever the programatic interface of this software, or any derivative + of it, is described. + + IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +------------------------------------------------------------------------------- + +Typhoon is a freely available relational database management system for +the UNIX and OS/2 environments. + +This release has been prepared in a hurry to get it out as quickly as +possible. This means that the documentation is rather sparse, since I +haven't had the time to write extensive documentation yet. However, most +of you out there are pretty clever, so you'll probably do fine with the +man pages and the examples. + +The system works fine and is currently used in a number of professional +applications in Denmark, some of them mission critical. + +The system was originally inspired by Raima's db_VISTA (today Raima Data +Manager) but is relational rather than network based. Typhoon lacks some of +db_VISTA's features, but also contains a number of nice features not found in +db_VISTA. + +All relations are defined in a so called Data Definition Language (ddl) file. +You define the database relations like you would write a C structure with +chars, ints, strings, multidimensional arrays, nested union and structures, +etc. Then you define primary, alternate and foreign keys for each relation. +The Data Definition Language Processor (ddlp) compiles the database defintion +into a binary file which constitutes the database description. The database +relations are accessed via C subroutines which manipulate individual records +within a table. + + - Multiple open database + - Multi-field keys + - Nested structures in records + - Controlled unions + - Referential integrity + - Variable length fields + - Null keys (optional keys in db_VISTA, but easier to use) + - Dynamic opening and closing of database files + +At present the database has no locking mechanism, believe it or not! The +projects I have used it in, simply didn't need it, even though they were +multi-user environments. But it's in the works! + +I am currently working on extending the library to support the following: + + - Locking + - Logging + - Transactions + - Paging (to improve performance) + +The library functions will remain intact, except for a number of new functions. +I expect these things to be ready before the end of the year. Beta testers +are welcome. + +The library comes with three additional utilities: + + dbdview - Displays database definitions. + tyexport - Exports tables from a database. + tyimport - Imports tables into a database. + +Originally the library also contained online backup, restore and a replication +server. These tools would need a lot of extra documentation, so I have left +them out for now (the code still contains support for these utilities). +However, if enough people show interest I will probably release them also. + + +------------------------------------------------------------------------------- + +Bug reports and mail should be sent to + + Thomas B. Pedersen + zeppelin@login.dknet.dk + +Also, if you have any suggestions as to how I can improve the library, +documentation or anything else, please don't hesitate to mail me. + + +Regards, + +Thomas B. Pedersen diff --git a/TODO b/TODO new file mode 100644 index 0000000..a84a31c --- /dev/null +++ b/TODO @@ -0,0 +1,8 @@ +This is Thomas B. Pedersen's original TODO from release 1.10.3. +------------------------------------------------------------------------------- +This list contains a lot of things that needs to be done. The major goal of +the library has been functionality more than performance. I am working on the +last goal now. + +1. + diff --git a/configure b/configure new file mode 100755 index 0000000..0629724 --- /dev/null +++ b/configure @@ -0,0 +1,327 @@ +#!/bin/sh + +# +# configure script +# $Id: configure,v 1.14 1999/10/04 04:44:22 kaz Exp $ +# + +# +# Check for cc +# + +echo Checking for gcc + +# +# typhoon uses a mixture of POSIX, BSD and System V interfaces +# + +STANDARDS="-D_POSIX_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE" + +if type gcc >/dev/null 2>/dev/null ; then + CC=gcc + CARGS="-pedantic $STANDARDS" + CFLAGS="-g -Wall -pedantic -Wstrict-prototypes -Wmissing-prototypes $STANDARDS \$(DEFINES)" +else + CC=cc + CARGS="$STANDARDS" + CFLAGS="-g $STANDARDS \$(DEFINES)" +fi + +compile='$CC $CARGS -o conftest conftest.c >/dev/null 2>&1' +compiled_ok='test -s conftest && (./conftest) >/dev/null 2>/dev/null;' + +DEFS='' # additional -D compiler defines go here +ENVIRON_H=./include/environ.h +ANSI_H=./include/ansi.h + +# ------------------------------------------------------------ +# This section generates ./include/environ.h +# ------------------------------------------------------------ +echo Generating $ENVIRON_H +echo "/* + * environ.h + * + * Generated by configure + * + */ + +#ifndef TYPHOON_ENVIRON_H +#define TYPHOON_ENVIRON_H + +#include +#include +#define CONFIG_CREATMASK 0666 +#define CONFIG_O_BINARY 0 +#define CONFIG_DIR_SWITCH '/' +#ifndef offsetof +#define offsetof(s,m) ((int)&(((s *)0))->m) +#endif +#if defined(sparc) || defined(mips) || defined(HPUX) || defined(__alpha) +#define CONFIG_RISC 1 +#endif +#define CONFIG_UNIX 1" > $ENVIRON_H +# +# Check endianess +# +echo Checking endianess +echo "main() +{ +short u=0; +*(char *)&u = 1; +return u; +} +" > conftest.c +eval $compile +if test -s conftest && (./conftest) >/dev/null 2>/dev/null ; then ENDIAN="BIG"; +else ENDIAN="LITTLE" +fi +echo "#define CONFIG_${ENDIAN}_ENDIAN 1" >> $ENVIRON_H + +# +# Check for prototypes +# +echo Checking for prototypes + +rm -f conftest* +echo "int foo(int); +int foo(int a) { return ++a; } +int main(void) { (void)foo(2); return 0; } " > conftest.c +eval $compile +if ! test -s conftest || ! (./conftest) >/dev/null 2>/dev/null ; then +echo "#define PRM(x) (); +#define CONFIG_ELLIPSIS /**/" >> $ENVIRON_H ; +else echo "#define CONFIG_PROTOTYPES 1 +#define PRM(x) x +#define CONFIG_ELLIPSIS ,..." >> $ENVIRON_H +fi + +# +# Check for ANSI C const keyword +# + +echo Checking for const keyword + +rm -f conftest* +cat << END > conftest.c +int main() +{ + const int x = 24; + return 0; +} +END + +eval $compile +if test -s conftest && (./conftest) >/dev/null 2>/dev/null ; then + echo "#define CONFIG_CONST const" >> $ENVIRON_H +else + echo "#define CONFIG_CONST /**/" >> $ENVIRON_H +fi + +# +# Check for size_t type +# + +echo Checking for size_t type + +rm -f conftest* +cat << END > conftest.c +#include + +int main() +{ + size_t x = 42; + return 0; +} +END + +eval $compile +if ! test -s conftest || ! (./conftest) >/dev/null 2>/dev/null ; then + echo "typedef unsigned int size_t;" >> $ENVIRON_H +fi + +# +# Check for off_t type +# + +echo Checking for off_t type + +rm -f conftest* +cat << END > conftest.c +#include +#include + +int main() +{ + off_t x = 42; + return 0; +} +END + +eval $compile +if ! test -s conftest || ! (./conftest) >/dev/null 2>/dev/null ; then + echo "typedef unsigned long off_t;" >> $ENVIRON_H +fi + +# +# Check for mode_t type +# + +echo Checking for mode_t type + +rm -f conftest* +cat << END > conftest.c +#include +#include + +int main() +{ + mode_t x = 42; + return 0; +} +END + +eval $compile +if ! test -s conftest || ! (./conftest) >/dev/null 2>/dev/null ; then + echo "typedef unsigned mode_t;" >> $ENVIRON_H +fi + + +# +# Check for uchar, ushort and ulong +# + +for type in char short long +do + echo Checking for u$type + rm -f conftest* + echo "#include +int main() { u$type a; return 0; } " > conftest.c + eval $compile + if test -s conftest && (./conftest) >/dev/null 2>/dev/null ; then : + else echo "typedef unsigned $type u$type;" >> $ENVIRON_H + fi +done + +# +# Check for locking via fcntl(). +# + + +echo Checking for POSIX 'fcntl()' with F_SETLK and F_GETLK + +rm -f conftest* +cat << END > conftest.c +#include +#include + +int main() +{ + struct flock flk; + int fd = creat("conftest.data", O_RDWR); + char testdata[] = "aaaaaaaaaaaaaaaa"; + + if (fd == -1) + return 1; + + if (write(fd, testdata, sizeof testdata) != sizeof testdata) + return 1; + + flk.l_type = F_WRLCK; + flk.l_whence = SEEK_SET; + flk.l_start = 0; + flk.l_len = 16; + + if (fcntl(fd, F_SETLK, &flk) == -1) + return 1; + + return 0; +} +END + +eval $compile +if test -s conftest || ! (./conftest) >/dev/null 2>/dev/null ; then + echo "#define CONFIG_USE_FLOCK 1" >> $ENVIRON_H +fi + +rm -f conftest* + +echo "#endif +/* end-of-file */" >> $ENVIRON_H + +# ------------------------------------------------------------ +# This section generates ./include/ansi.h +# ------------------------------------------------------------ +echo Generating $ANSI_H +echo "/* + * ansi.h + * + * Generated by configure + * + */ + +#ifndef TYPHOON_ANSI_H +#define TYPHOON_ANSI_H +" > $ANSI_H + +# +# Check ansi C functions +# + +for func in strstr memmove +do + echo Checking for $func + rm -f conftest* + echo "#include +int main() {} foo() { $func(); return 0; } " > conftest.c + eval $compile + if test -s conftest && (./conftest) >/dev/null 2>/dev/null ; then : + echo "#define `echo CONFIG_${func}|tr '[a-z]' '[A-Z]'`_MISSING" >> $ANSI_H + fi +done + +rm -f conftest* +echo " +#endif + +/* end-of-file */" >> $ANSI_H + + +# ------------------------------------------------------------ +# This section generates Makefile +# ------------------------------------------------------------ +# +# Check for ranlib +# +echo Checking for ranlib +if test -z "$RANLIB" && type ranlib >/dev/null 2>/dev/null ; then + RANLIB=ranlib +else + RANLIB=true +fi + +# +# Check for bison/yacc, giving preference to bison +# +echo Checking for bison/yacc +if test -z "$YACC" && type bison >/dev/null 2>/dev/null ; then + YACC="bison --yacc" +else + YACC=yacc +fi + +# +# Now generate Makefile from Makefile.in by substituting the @xxx@ names. +# + +for dir in src util examples +do +echo Generating $dir/Makefile +echo "# Makefile generated by configure" > $dir/Makefile +cat $dir/Makefile.in | sed -e " +s/@defs@/$DEFS/ +s/@ranlib@/$RANLIB/ +s/@yacc@/$YACC/ +s/@cc@/$CC/ +s/@cflags@/$CFLAGS/ +" >> $dir/Makefile ; +done diff --git a/examples/Makefile.in b/examples/Makefile.in new file mode 100644 index 0000000..a88b7aa --- /dev/null +++ b/examples/Makefile.in @@ -0,0 +1,55 @@ +# Makefile for: demo - typhoon test and demonstration program + +DEFINES = -I../include @defs@ +CC = @cc@ +CFLAGS = @cflags@ +LIBS = -ltyphoon +PREFIX = /usr/local +LDFLAGS = -L../src +DESTBIN = $(PREFIX)/bin +DESTOWN = root +DESTGRP = local +SHELL = /bin/sh +PROGRAM = demo +SRCS = demo.c +HDRS = demo.h +OBJS = demo.o + +.DEFAULT: + co $@ + +.PHONY: all lint tags install uninstall clean distclean + +all: $(PROGRAM) + +$(PROGRAM): $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM) + +demo.h demo.dbd: demo.ddl + ../util/ddlp -a4 -f demo + +lint: + lint -u $(DEFINES) $(SRCS) + +tags: $(HDRS) $(SRCS) + ctags -w $(HDRS) $(SRCS) + +install: $(PROGRAM) + cp $(PROGRAM) $(DESTBIN) + -mcs -c $(DESTBIN)/$(PROGRAM) + strip $(DESTBIN)/$(PROGRAM) + chmod 755 $(DESTBIN)/$(PROGRAM) + chown $(DESTOWN) $(DESTBIN)/$(PROGRAM) + chgrp $(DESTGRP) $(DESTBIN)/$(PROGRAM) + +uninstall: + rm -f $(DESTBIN)/$(PROGRAM) + +clean: + -rm -rf $(PROGRAM) $(OBJS) demo.h demo.dbd data + +distclean: clean + -rm -f Makefile tags core a.out + +### Do NOT edit this or the following lines. +demo.o: demo.h diff --git a/examples/demo.c b/examples/demo.c new file mode 100644 index 0000000..6c2ae3b --- /dev/null +++ b/examples/demo.c @@ -0,0 +1,285 @@ +/*---------------------------------------------------------------------------- + * File : demo.c + * OS : UNIX + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * A small program that demonstrates some of the features of API. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "environ.h" +#ifdef CONFIG_OS2 +#include +#else +#include +#endif +#include "demo.h" +#include "typhoon.h" + +static CONFIG_CONST char rcsid[] = "$Id: demo.c,v 1.4 1999/10/04 04:11:29 kaz Exp $"; + +typedef enum { + TYPE_INT, + TYPE_LONG, + TYPE_STRING +} field_type; + +static int menu PRM ( (char *); ) +static void getfield PRM ( (char *, field_type, void *); ) +static void report PRM ( (char *); ) +static void print_company PRM ( (struct company *); ) +static void print_product PRM ( (struct product *); ) +static void find_menu PRM ( (void); ) +static void create_menu PRM ( (void); ) + int main PRM ( (void); ) + +static int menu(options) +char *options; +{ + char s[10]; + + printf("%s: ", options); + fflush(stdout); + fgets(s, sizeof s, stdin); + + return tolower(s[0]); +} + + +static void getfield(name, type, var) +char *name; +field_type type; +void *var; +{ + char s[2048]; + char *nl; + + printf("%s: ", name); + fflush(stdout); + fgets(s, sizeof s, stdin); + + if ((nl = strchr(s, '\n')) != 0) + *nl = 0; + + switch( type ) + { + case TYPE_INT: + *(int *)var = atoi(s); + break; + case TYPE_LONG: + *(long *)var = atol(s); + break; + case TYPE_STRING: + strcpy((char *)var, s); + break; + } +} + + +static void report(s) +char *s; +{ + printf("%s - db_status %d\n", s, db_status); +} + + +static void print_company(company) +struct company *company; +{ + printf("[id=%lu, name='%s']\n", company->id, company->name); +} + + +static void print_product(product) +struct product *product; +{ + printf("[company_id=%lu, name='%s', description='%s']\n", + product->company_id, + product->name, + product->description); +} + + +static void find_menu() +{ + struct product product; + struct company company; + int stop = 0; + int key = ID; + int listall = 0; + + while( !stop ) + { + switch( menu("First Last Prev Next Delete " + "Search list All Back") ) + { + case 'a': + d_keyfrst(key); + + listall = 1; + break; + case 'f': + d_keyfrst(key); + break; + case 'l': + d_keylast(key); + break; + case 'p': + d_keyprev(key); + break; + case 'n': + d_keynext(key); + break; + case 's': + switch( menu("Company Product") ) + { + case 'c': + getfield("Id", TYPE_LONG, &company.id); + d_keyfind(ID, &company.id); + key = ID; + break; + case 'p': + key = PRODUCT_NAME; + getfield("Name", TYPE_STRING, product.name); + d_keyfind(PRODUCT_NAME, product.name); + break; + default: + continue; + } + break; + case 'd': + if( db_status == S_OKAY ) { + if( d_delete() == S_OKAY ) + puts("Deleted."); + else + report("Could not delete"); + } + continue; + case 'b': + stop++; + default: + continue; + } + + if( db_status == S_OKAY ) + { + do + { + switch( key ) + { + case ID: + d_recread(&company); + print_company(&company); + break; + case PRODUCT_NAME: + d_recread(&product); + print_product(&product); + break; + } + + if( listall ) + d_keynext(key); + } + while( listall && db_status == S_OKAY ); + } + else + puts("Not found"); + + listall = 0; + } +} + + + +static void create_menu() +{ + struct product product; + struct company company ; + int stop = 0; + + while( !stop ) + { + switch( menu("Company Product Back") ) + { + case 'c': + getfield("Id ", TYPE_LONG, &company.id); + getfield("Name", TYPE_STRING, company.name); + if( d_fillnew(COMPANY, &company) != S_OKAY ) + report("Could not create company"); + break; + case 'p': + getfield("Company id ", TYPE_LONG, &product.company_id); + getfield("Product name", TYPE_STRING, product.name); + getfield("Description ", TYPE_STRING, product.description); + + /* Set the length determinator of description */ + product.descr_len = strlen(product.description) + 1; + + if( d_fillnew(PRODUCT, &product) != S_OKAY ) + report("Could not create product"); + break; + case 'b': + stop++; + } + } +} + +int main() +{ + int stop = 0; + +#ifdef CONFIG_UNIX + mkdir("data", 0777); +#else + mkdir("data"); +#endif + + d_dbfpath("data"); + if( d_open("demo", "s") != S_OKAY ) + { + fprintf(stderr, "Cannot open database (db_status %d)\n", db_status); + exit(1); + } + + while( !stop ) + { + switch( menu("Create Find Delete Quit") ) + { + case 'c': create_menu(); break; + case 'f': find_menu(); break; + case 'q': stop++; + } + } + + d_close(); + return 0; +} + +/* end-of-file */ + diff --git a/examples/demo.ddl b/examples/demo.ddl new file mode 100644 index 0000000..5942a16 --- /dev/null +++ b/examples/demo.ddl @@ -0,0 +1,72 @@ +/* + demonstration database +*/ + +database demo { + + define NAME_LEN 30 + + data file "company.dat" contains company; + key file "company.ix1" contains company.id; + key file "company.ix2" contains company.references; + data file "product.dat" contains product; + key file "product.ix1" contains product.name; + + data file "fancy.dat" contains fancy; + key file "fancy.ix1" contains fancy.fancy_key1; + key file "fancy.ix2" contains fancy.name; + key file "fancy.ix3" contains fancy.fancy_key2; + + record company { + ulong id; + char name[NAME_LEN+1]; + + primary key id; + } + + record product { + ulong company_id; + ushort descr_len; + char name[NAME_LEN+1]; + char description[2000] variable by descr_len; + + primary key name; + foreign key company_id references company + on update restrict + on delete restrict; + } + + + // This record just shows some of the things that are possible in + // a ddl-file. + + record fancy { + char name[20]; + ulong number; + + char key2_is_null; + + ushort a_count; + struct { + char a[2][20]; + uchar type; + + union controlled by type { + char s[80]; // If type is 0 + ulong a; // If type is 1 + struct { + int a; + long b; + } c; // If type is 0 + } u[2]; + + } a[10] variable by a_count; + + primary key fancy_key1 { name, number desc }; + alternate key name null by name; + alternate key fancy_key2 { number desc, name } null by key2_is_null; + } +} + +/* end-of-file */ + diff --git a/examples/makefile.os2 b/examples/makefile.os2 new file mode 100644 index 0000000..68d43ff --- /dev/null +++ b/examples/makefile.os2 @@ -0,0 +1,15 @@ +OBJS = demo.obj +CFLAGS = /DOS2 /I..\include +CC = icc /OS2 /Tdc /Sp1 /Q /Tx /Fi /Si /Gh /Ti /Gm /Gd /G4 /Tm \ + /Tl1 $(CFLAGS) + +demo.exe: demo.h $(OBJS) + $(CC) $(OBJS) ..\src\typhoon.lib + +demo.h: demo.ddl + ..\util\ddlp -a1 demo + +.c.obj: + $(CC) -c $< + +# end-of-file diff --git a/include/env_os2.h b/include/env_os2.h new file mode 100644 index 0000000..d39671f --- /dev/null +++ b/include/env_os2.h @@ -0,0 +1,89 @@ +/*---------------------------------------------------------------------------- + * File : environ.h + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Multi Operating System compatibility include file. + * + * $Id: env_os2.h,v 1.3 1999/10/03 23:28:28 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_ENVIRON_INCLUDED +#define TYPHOON_ENVIRON_INCLUDED + +/*--------------------------------------------------------------------------*/ +/* OS/2 v2.0 */ +/*--------------------------------------------------------------------------*/ + +#ifdef CONFIG_CONFIG_OS2 +#ifdef __BORLANDC__ +# define __STDC__ 1 +#endif +#define CONFIG_DIR_SWITCH '\\' +#define CONFIG_CREATMASK (S_IREAD|S_IWRITE) +#define CONFIG_PROTOTYPES 1 +#define CONFIG_LITTLE_ENDIAN 1 + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned long ulong; + +#endif + + +/*--------------------------------------------------------------------------*/ +/* DOS */ +/*--------------------------------------------------------------------------*/ + +#ifdef CONFIG_DOS + +#define CONFIG_DIR_SWITCH '\\' +#define CONFIG_CREATMASK (S_IREAD|S_IWRITE) +#define CONFIG_PROTOTYPES 1 +#define CONFIG_LITTLE_ENDIAN 1 + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned long ulong; + +#endif + + +#ifdef CONFIG_PROTOTYPES +# define PRM(x) x +# define CONFIG_ELLIPSIS ,... +#else +# define PRM(x) (); +# define CONFIG_ELLIPSIS /**/ +#endif + +#ifndef offsetof +# define offsetof(s,m) ((int)&(((s *)0))->m) +#endif + +#endif + +/* end-of-file */ diff --git a/include/typhoon.h b/include/typhoon.h new file mode 100644 index 0000000..a58e8f5 --- /dev/null +++ b/include/typhoon.h @@ -0,0 +1,175 @@ +/*---------------------------------------------------------------------------- + * File : typhoon.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Header file for Typhoon library. + * + * $Id: typhoon.h,v 1.4 1999/10/04 04:38:32 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_INCLUDED +#define TYPHOON_INCLUDED + +#ifndef TYPHOON_ENVIRON_H +#include "environ.h" +#endif + +/*---------- Status codes --------------------------------------------------*/ +#define S_NOCR -2 /* No current record */ +#define S_NOCD -1 /* No current database */ + +#define S_OKAY 0 /* Operation successful */ +#define S_NOTFOUND 1 /* Key not found */ +#define S_DUPLICATE 2 /* Duplicate key found */ +#define S_DELETED 3 /* Record is deleted */ +#define S_RESTRICT 4 /* Restrict rule encountered(db_subcode)*/ +#define S_FOREIGN 5 /* No foreign key (db_subcode) */ +#define S_LOCKED 8 /* Record is locked */ +#define S_UNLOCKED 9 /* Record is unlocked */ +#define S_VERSION 10 /* B-tree or data file has wrong version*/ +#define S_INVPARM 11 /* Invalid parameter */ + +#define S_NOMEM 200 /* Out of memory */ +#define S_NOTAVAIL 201 /* Database not available */ +#define S_IOFATAL 202 /* Fatal I/O operation */ +#define S_FATAL 203 /* Fatal error - recover */ +#define S_MAXCLIENTS 500 /* Too many clients */ +#define S_NOSERVER 501 /* No server is installed */ + +/*---------- User errors ---------------------------------------------------*/ +#define S_INVDB 1000 /* Invalid database */ +#define S_INVREC 1001 /* Invalid record */ +#define S_INVFLD 1002 /* Invalid field name */ +#define S_NOTKEY 1003 /* Field is not a key */ +#define S_RECSIZE 1004 /* Variable length record has invalid sz*/ +#define S_BADTYPE 1005 /* Bad parameter type */ +#define S_INVKEY 1006 /* Invalid key */ +#define S_INVADDR 1007 /* Invalid database address (rec number)*/ +#define S_INVSEQ 1008 /* Invalid sequence id */ + +/*---------- Lock types ----------------------------------------------------*/ +#define LOCK_TEST 1 /* Test if a record is locked */ +#define LOCK_UPDATE 2 /* Lock a record for update */ + +typedef struct { + unsigned long recid; + unsigned long recno; +} DB_ADDR; + +extern unsigned long curr_rec; +extern int db_status; /* See S_... constants */ +extern long db_subcode; + +#ifdef CONFIG_OS2 +# ifdef __BORLANDC__ +# define INCL_NOPMAPI +# endif +# ifdef __IBMC__ +# pragma map(db_status, "_db_status") +# pragma map(db_subcode, "_db_subcode") +# endif +# include +# define CL APIRET EXPENTRY +#else +# define CL int +#endif + +/*---------- Function prototypes -------------------------------------------*/ +CL d_block PRM( (void); ) +CL d_unblock PRM( (void); ) +CL d_setfiles PRM( (int); ) +CL d_keybuild PRM( (void (*)(char *, ulong, ulong)); ) +CL d_open PRM( (char *, char *); ) +CL d_close PRM( (void); ) +CL d_destroy PRM( (char *); ) +CL d_keyfind PRM( (unsigned long, void *); ) +CL d_keyfrst PRM( (unsigned long); ) +CL d_keylast PRM( (unsigned long); ) +CL d_keynext PRM( (unsigned long); ) +CL d_keyprev PRM( (unsigned long); ) +CL d_keyread PRM( (void *); ) +CL d_fillnew PRM( (unsigned long, void *); ) +CL d_keystore PRM( (unsigned long); ) +CL d_recwrite PRM( (void *); ) +CL d_recread PRM( (void *); ) +CL d_crread PRM( (unsigned long, void *); ) + +CL d_delete PRM( (void); ) +CL d_recfrst PRM( (unsigned long); ) +CL d_reclast PRM( (unsigned long); ) +CL d_recnext PRM( (unsigned long); ) +CL d_recprev PRM( (unsigned long); ) + +CL d_crget PRM( (DB_ADDR *); ) +CL d_crset PRM( (DB_ADDR *); ) + +CL d_dbget PRM( (int *); ) +CL d_dbset PRM( (int); ) + +CL d_records PRM( (unsigned long, unsigned long *); ) +CL d_keys PRM( (unsigned long); ) + +CL d_dbdpath PRM( (char *); ) +CL d_dbfpath PRM( (char *); ) + +CL d_reclock PRM( (DB_ADDR *, int); ) +CL d_recunlock PRM( (DB_ADDR *); ) + +CL d_keyfrst PRM( (unsigned long); ) +CL d_keylast PRM( (unsigned long); ) +CL d_keyprev PRM( (unsigned long); ) +CL d_keynext PRM( (unsigned long); ) + +CL d_recfrst PRM( (unsigned long); ) +CL d_reclast PRM( (unsigned long); ) +CL d_recprev PRM( (unsigned long); ) +CL d_recnext PRM( (unsigned long); ) + +CL d_getsequence PRM( (unsigned long, unsigned long *); ) + +CL d_replicationlog PRM( (int); ) +CL d_addsite PRM( (unsigned long); ) +CL d_delsite PRM( (unsigned long); ) +CL d_deltable PRM( (unsigned long, unsigned long); ) + +CL d_getkeysize PRM( (unsigned long, unsigned *); ) +CL d_getrecsize PRM( (unsigned long, unsigned *); ) +CL d_getfieldtype PRM( (unsigned long, unsigned *); ) +CL ty_ustrcmp PRM( (unsigned char *, unsigned char *); ) +CL d_getkeyid PRM( (unsigned long, unsigned long *); ) +CL d_getforeignkeyid PRM( (unsigned long, unsigned long, unsigned long *); ) +CL d_makekey PRM( (unsigned long, void *, void *); ) + +CL d_seterrfn PRM( (void (*)(int, long)); ) + + +#undef CL + +#endif + +/* end-of-file */ diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 0000000..4bb0a1b --- /dev/null +++ b/man/Makefile @@ -0,0 +1,66 @@ +# Makefile for: man - manual pages for typhoon + +ROFF = nroff +RFLAGS = -man +MANEXT = l +PREFIX = /usr/local +DESTMAN = $(PREFIX)/man/man$(MANEXT) +DESTCAT = $(PREFIX)/man/cat$(MANEXT) +DESTOWN = root +DESTGRP = local +SHELL = /bin/sh +MANPAGES = d_close.3 d_crget.3 d_crread.3 d_crset.3 d_dbdpath.3 \ + d_dbfpath.3 d_dbget.3 d_dbset.3 d_delete.3 d_fillnew.3 \ + d_keyfind.3 d_keyfrst.3 d_keylast.3 d_keynext.3 d_keyprev.3 \ + d_keyread.3 d_open.3 d_recfrst.3 d_reclast.3 d_recnext.3 \ + d_recprev.3 d_recread.3 d_recwrite.3 d_setfiles.3 ddlp.1 \ + d_getsequence.3 +CATPAGES = d_close.cat d_crget.cat d_crread.cat d_crset.cat \ + d_dbdpath.cat d_dbfpath.cat d_dbget.cat d_dbset.cat \ + d_delete.cat d_fillnew.cat d_keyfind.cat d_keyfrst.cat \ + d_keylast.cat d_keynext.cat d_keyprev.cat d_keyread.cat \ + d_open.cat d_recfrst.cat d_reclast.cat d_recnext.cat \ + d_recprev.cat d_recread.cat d_recwrite.cat d_setfiles.cat \ + d_getsequence.cat ddlp.cat + +.DEFAULT: + co $@ + +.PHONY: all install uninstall clean + +.3.cat: + $(ROFF) $(RFLAGS) $< | col > $@ + +.SUFFIXES: .cat .3 + +all: $(CATPAGES) + +$(CATPAGES): $(MANPAGES) + +install: $(CATPAGES) + -for manpage in $(MANPAGES); do \ + basepage=`expr $$manpage : '\(.*\)\.[^.]*'`; \ +: cp $$manpage $(DESTMAN)/$$basepage.$(MANEXT); \ +: chmod 644 $(DESTMAN)/$$basepage.$(MANEXT); \ +: chown $(DESTOWN) $(DESTMAN)/$$basepage.$(MANEXT); \ +: chgrp $(DESTGRP) $(DESTMAN)/$$basepage.$(MANEXT); \ + compress -c $$basepage.cat \ + >$(DESTCAT)/$$basepage.$(MANEXT).Z; \ + chmod 644 $(DESTCAT)/$$basepage.$(MANEXT)*; \ + chown $(DESTOWN) $(DESTCAT)/$$basepage.$(MANEXT)*; \ + chgrp $(DESTGRP) $(DESTCAT)/$$basepage.$(MANEXT)*; \ + done + +uninstall: + -for manpage in $(MANPAGES); do \ + basepage=`expr $$manpage : '\(.*\)\.[^.]*'`; \ + rm -f $(DESTMAN)/$$basepage.$(MANEXT)*; \ + rm -f $(DESTCAT)/$$basepage.$(MANEXT)*; \ + done + +clean: + -rm -f $(CATPAGES) + +distclean: clean + +### Do NOT edit this or the following lines. diff --git a/man/d_close.3 b/man/d_close.3 new file mode 100644 index 0000000..04d8e08 --- /dev/null +++ b/man/d_close.3 @@ -0,0 +1,38 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_close.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_CLOSE 1 \*(Dt TYPHOON +.SH NAME +d_open \- open a database +.SH SYNOPSIS +.B #include +.br + +\fBd_close() +.SH DESCRIPTION +\fBd_close\fP closes the current database. If no database is currently +open, \fBS_NOCD\fP is returned. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Database closed successfully. +.TP +.B S_NOCD +No current database. +.SH CURRENCY CHANGES +There is no current database. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_open(1) + diff --git a/man/d_crget.3 b/man/d_crget.3 new file mode 100644 index 0000000..c9599f3 --- /dev/null +++ b/man/d_crget.3 @@ -0,0 +1,41 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_crget.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_CRGET 1 \*(Dt TYPHOON +.SH NAME +d_crget \- get the database address of the current record +.SH SYNOPSIS +.B #include +.br + +\fBd_crget(DB_ADDR *\fPaddr\fB) +.SH DESCRIPTION +\fBd_crget\fP gets the database address of the current record. +If no database is currently open, \fBS_NOCD\fP is returned. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_NOCR +There is no current record. +.SH CURRENCY CHANGES +None. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_crset(1) + diff --git a/man/d_crread.3 b/man/d_crread.3 new file mode 100644 index 0000000..5c7f3f7 --- /dev/null +++ b/man/d_crread.3 @@ -0,0 +1,62 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_crread.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_CRREAD 1 \*(Dt TYPHOON +.SH NAME +d_crread \- read a field from the current record +.SH SYNOPSIS +.B #include +.br + +\fBd_crread(ulong \fPfieldid\fB, void *\fPbuf\fB) +.SH DESCRIPTION +\fBd_crread\fP copies the contents of the field specified by \fIfieldid\fP +into the buffer \fIbuf\fP. If the field is a variable length field, only +the actual number of bytes in the field is copied. +.br +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_NOCR +There is no current record. +.TP +.B S_INVFLD +The id is not a valid field. +.SH CURRENCY CHANGES +None. +.SH EXAMPLE +/* Get Pedersen's account number */ +.br + +#include +.br + +if( d_keyfind(CUSTOMER_NAME, "Pedersen") == S_OKAY ) +.br +{ +.br + unsigned long account; +.br + d_crread(CUSTOMER_ACCOUNT, &account); + printf("Account number %lu\\n", account); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_recread(1), d_keyread(1) diff --git a/man/d_crset.3 b/man/d_crset.3 new file mode 100644 index 0000000..58a3df9 --- /dev/null +++ b/man/d_crset.3 @@ -0,0 +1,41 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_crset.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_CRSET 1 \*(Dt TYPHOON +.SH NAME +d_crset \- set the current record +.SH SYNOPSIS +.B #include +.br + +\fBd_crset(DB_ADDR *\fPaddr\fB) +.SH DESCRIPTION +\fBd_crset\fP sets the current record. If no database is currently open, +\fBS_NOCD\fP is returned. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVADDR +The specified address is invalid. +.SH CURRENCY CHANGES +The record at the specified database address becomes the current record. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_crget(1) + diff --git a/man/d_dbdpath.3 b/man/d_dbdpath.3 new file mode 100644 index 0000000..5dc76d3 --- /dev/null +++ b/man/d_dbdpath.3 @@ -0,0 +1,37 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_dbdpath.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_DBDPATH 1 \*(Dt TYPHOON +.SH NAME +d_dbdpath \- set the path of the dbd-files +.SH SYNOPSIS +.B #include +.br + +\fBd_dbdpath(char *\fPpath\fB) +.SH DESCRIPTION +\fBd_dbdpath\fP sets the path of the database definition files. This +function should be called prior to calling \fBd_open(1)\fP. \fIpath\fP +can be either a relative or an absolute path name. The validity of +\fIpath\fP is not checked until \fBd_open(1)\fP is called. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +This value is always returned. +.SH CURRENCY CHANGES +None. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_dbfpath(1), d_open(1). + diff --git a/man/d_dbfpath.3 b/man/d_dbfpath.3 new file mode 100644 index 0000000..49151b4 --- /dev/null +++ b/man/d_dbfpath.3 @@ -0,0 +1,40 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_dbfpath.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_DBFPATH 1 \*(Dt TYPHOON +.SH NAME +d_dbfpath \- set the path of the database files +.SH SYNOPSIS +.B #include +.br + +\fBd_dbfpath(char *\fPpath\fB) +.SH DESCRIPTION +\fBd_dbdpath\fP sets the path of the database files. This +function should be called prior to calling \fBd_open(1)\fP as it +determines where database files are stored. \fIpath\fP +can be either a relative or an absolute path name. The validity of +\fIpath\fP is not checked until \fBd_open(1)\fP is called. + +Setting the dbd-path does not affect previously opened databases. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +This value is always returned. +.SH CURRENCY CHANGES +None. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_dbdpath(1), d_open(1). + diff --git a/man/d_dbget.3 b/man/d_dbget.3 new file mode 100644 index 0000000..eb9a630 --- /dev/null +++ b/man/d_dbget.3 @@ -0,0 +1,60 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_dbget.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_DBGET 1 \*(Dt TYPHOON +.SH NAME +d_dbget \- get the id of the current database +.SH SYNOPSIS +.B #include +.br + +\fBd_dbget() +.SH DESCRIPTION +When a database is opened it is assigned an internal id which it retains +until it is closed. \fBd_dbget\fP gets the id of the current database. +This id can be used in \fBd_dbset(1)\fP to make another open database the +current database. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_NOCD +There is no current database. +.SH CURRENCY CHANGES +None. +.SH EXAMPLE +#include +.br + +int cust_dbid; +.br + +d_open("customer", "s"); +.br +d_dbget(&cust_dbid); +.br +\|.\|.\|. +.br +d_open(....); +.br +\|.\|.\|. +.br +/* Make the customer database the current database */ +.br +d_dbset(cust_dbid); +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_dbset(1) + diff --git a/man/d_dbset.3 b/man/d_dbset.3 new file mode 100644 index 0000000..399f811 --- /dev/null +++ b/man/d_dbset.3 @@ -0,0 +1,39 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_dbset.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_DBSET 1 \*(Dt TYPHOON +.SH NAME +d_dbset \- set the current database +.SH SYNOPSIS +.B #include +.br + +\fBd_dbset(int \fPdbid\fB) +.SH DESCRIPTION +Sets the database with id \fBdbid\fP to the current database. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_INVPARM +There database id specified is invalid. +.SH CURRENCY CHANGES +None. +.SH EXAMPLE +See d_dbget(1). +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_dbget(1) + diff --git a/man/d_delete.3 b/man/d_delete.3 new file mode 100644 index 0000000..f3dd377 --- /dev/null +++ b/man/d_delete.3 @@ -0,0 +1,60 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_delete.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_DELETE 1 \*(Dt TYPHOON +.SH NAME +d_delete \- delete the current record +.SH SYNOPSIS +.B #include +.br + +\fBd_delete() +.SH DESCRIPTION +\fBd_delete\fP removes the current record from its table. +.br +If there is no current record, \fBS_NOCR\fP is returned. +If the record has references, i.e. records with foreign keys that reference +this record, \fBS_RESTRICT\fP is returned. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The record was successfully deleted. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_NOCR +There is no current record. +.TP +.B S_RESTRICT +One or more records currently reference this record and the record cannot +be deleted. +.SH CURRENCY CHANGES +None. +.SH EXAMPLE +#include +.br + +if( d_keyfind(CUSTOMER_NAME, "Pedersen") == S_OKAY ) +.br +{ +.br + if( d_delete() != S_OKAY ) +.br + /* handle error */ +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_fillnew(1) diff --git a/man/d_fillnew.3 b/man/d_fillnew.3 new file mode 100644 index 0000000..96db010 --- /dev/null +++ b/man/d_fillnew.3 @@ -0,0 +1,65 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_fillnew.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_FILLNEW 1 \*(Dt TYPHOON +.SH NAME +d_fillnew \- insert a new record +.SH SYNOPSIS +.B #include +.br + +\fBd_fillnew(ulong \fPrecid\fB, void *\fPbuf\fB) +.SH DESCRIPTION +\fBd_fillnew\fP inserts a new record in a table. \fIrecid\fP specifies the +type of the record stored in \fIbuf\fP. The inserted record retains the +same database address throughout its life in the database. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The record was successfully inserted. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVREC +The record id is not valid. +.TP +.B S_DUPLICATE +One of the keys in the record would cause duplicates in a unique index. +\fIdb_subcode\fP contains the id of the conflicting field or key. +.TP +.B S_RECSIZE +A length determinator of a variable length field contained a illegal +value. \fIdb_subcode\fP contains the id of the conflicting field. +.TP +.B S_FOREIGN +The target of a foreign key could not be found. \fIdb_subcode\fP contains +the id of the target table. +.SH CURRENCY CHANGES +If \fBd_fillnew\fP returned \fBS_OKAY\fP the record becomes the current record. +.SH EXAMPLE +#include + +struct customer cust; +.br +strcpy(cust.name, "Pedersen"); +.br +cust.account = 10002; +if( d_fillnew(CUSTOMER, &cust) != S_OKAY ) +.br + /* handle error */ +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_recwrite(1) + diff --git a/man/d_getsequence.3 b/man/d_getsequence.3 new file mode 100644 index 0000000..e7b5372 --- /dev/null +++ b/man/d_getsequence.3 @@ -0,0 +1,41 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_getsequence.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_CRGET 1 \*(Dt TYPHOON +.SH NAME +d_getsequence \- get the next number in a sequence +.SH SYNOPSIS +.B #include +.br + +\fBd_getsequence(ulong \fPsequenceid\fB, unsigned long *\fPnumber\fB) +.SH DESCRIPTION +\fBd_getsequence\fP gets the next number in the sequence specified +by \fIsequenceid\fP and increments or decrements the sequence. Sequences +are guaranteed not to be reused. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVSEQ +Invalid sequence id. +.SH CURRENCY CHANGES +Current sequence is increased or descreased by the step specified in the +ddl-file. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1995 Thomas B. Pedersen. + diff --git a/man/d_keyfind.3 b/man/d_keyfind.3 new file mode 100644 index 0000000..3c995f0 --- /dev/null +++ b/man/d_keyfind.3 @@ -0,0 +1,79 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_keyfind.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_KEYFIND 1 \*(Dt TYPHOON +.SH NAME +d_keyfind \- search an index for a specific key value +.SH SYNOPSIS +.B #include +.br + +\fBd_keyfind(ulong \fPkeyid\fB, void *\fPbuf\fB) +.SH DESCRIPTION +\fBd_keyfind\fP is used to lookup a record in a table via one of its +indexes. \fIkeyid\fP specifies which index to search and \fIbuf\fP contains +the value to search for. If the index contains more than +one occurrence of the key value (only possible for non-unique indexes) +\fBd_keyfind\fP returns the first one. +.br + +The id can be either the id of a compound key or a field that is a key +by itself. +.br + +If the key value was not found, \fBd_keyfind\fP returns \fBS_NOTFOUND\fP. +A subsequent call to \fBd_keynext(1)\fP returns next value in the sorting +order. +.br + +The actual record is not read from the database until \fBd_recread(1)\fP is +called. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The key value was found. +.TP +.B S_NOTFOUND +The key value was not found. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVFLD +The id is not a valid field. +.TP +.B S_NOTKEY +The field id is not a key itself. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned. the record found becomes the current record. +.SH EXAMPLE +/* Find the customer called 'Pedersen' */ +.br + +#include +.br + +if( d_keyfind(CUSTOMER_NAME, "Pedersen") == S_OKAY ) +.br +{ +.br + struct customer cust; +.br + d_recread(&cust); + printf("Account number %lu\\n", cust.account); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_keynext(1), d_keyprev(1), d_keyfrst(1), d_keylast(1), d_recread(1). diff --git a/man/d_keyfrst.3 b/man/d_keyfrst.3 new file mode 100644 index 0000000..66afdf1 --- /dev/null +++ b/man/d_keyfrst.3 @@ -0,0 +1,78 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_keyfrst.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_KEYFRST 1 \*(Dt TYPHOON +.SH NAME +d_keyfrst \- find the first key value in an index +.SH SYNOPSIS +.B #include +.br + +\fBd_keyfrst(ulong \fPkeyid\fB) +.SH DESCRIPTION +\fBd_keyfrst\fP finds the first key value in the index specified by +\fIkeyid\fP. If \fBd_keyfrst\fP returns \fBS_NOTFOUND\fP the index is empty. +.br + +The id can be either the id of a compound key or a field that is a key +by itself. +.br + +The actual record is not read from the database until \fBd_recread(1)\fP is +called. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The key value was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVFLD +The id is not a valid field. +.TP +.B S_NOTKEY +The field id is not a key itself. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH EXAMPLE +/* Traverse the customers in alphabetical, ascending order */ + +#include +.br + +d_keyfrst(CUSTOMER_NAME); +.br + +while( db_status == S_OKAY ) +.br +{ +.br + struct customer cust; +.br + + d_recread(&cust); +.br + printf("%s\\n", cust.name); +.br + d_keynext(CUSTOMER_NAME); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_keynext(1), d_keyfind(1), d_keyprev(1), d_keylast(1), d_recread(1). + diff --git a/man/d_keylast.3 b/man/d_keylast.3 new file mode 100644 index 0000000..92e2736 --- /dev/null +++ b/man/d_keylast.3 @@ -0,0 +1,78 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_keylast.3,v 1.1.1.1 1999/09/30 04:45:50 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_KEYLAST 1 \*(Dt TYPHOON +.SH NAME +d_keylast \- find the last key value in an index +.SH SYNOPSIS +.B #include +.br + +\fBd_keylast(ulong \fPkeyid\fB) +.SH DESCRIPTION +\fBd_keyfrst\fP finds the last key value in the index specified by +\fIkeyid\fP. If \fBd_keyfrst\fP returns \fBS_NOTFOUND\fP the index is empty. +.br + +The id can be either the id of a compound key, or a field that is a key +by itself. +.br + +The actual record is not read from the database until \fBd_recread(1)\fP is +called. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The key value was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVFLD +The id is not a valid field. +.TP +.B S_NOTKEY +The field id is not a key itself. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH EXAMPLE +/* Traverse the customers in alphabetical, descending order */ + +#include +.br + +d_keylast(CUSTOMER_NAME); +.br + +while( db_status == S_OKAY ) +.br +{ +.br + struct customer cust; +.br + + d_recread(&cust); +.br + printf("%s\\n", cust.name); +.br + d_keyprev(CUSTOMER_NAME); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_keynext(1), d_keyfind(1), d_keyprev(1), d_keylast(1), d_recread(1). + diff --git a/man/d_keynext.3 b/man/d_keynext.3 new file mode 100644 index 0000000..652bc2b --- /dev/null +++ b/man/d_keynext.3 @@ -0,0 +1,81 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_keynext.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_KEYNEXT 1 \*(Dt TYPHOON +.SH NAME +d_keynext \- find the next key value in an index +.SH SYNOPSIS +.B #include +.br + +\fBd_keynext(ulong \fPkeyid\fB) +.SH DESCRIPTION +\fBd_keynext\fP finds the next key value greater than or equal to the +current key value in the index specified by +\fIkeyid\fP. If \fBd_keynext\fP returns \fBS_NOTFOUND\fP the end of the index +has been passed. A subsequent call to \fBd_keynext\fP will return the first +key value in the index. +.br + +The id can be either the id of a compound key or a field that is a key +by itself. +.br + +The actual record is not read from the database until \fBd_recread(1)\fP is +called. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The key value was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVFLD +The id is not a valid field. +.TP +.B S_NOTKEY +The field id is not a key itself. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH EXAMPLE +/* Traverse the customers in alphabetical order */ + +#include +.br + +d_keyfrst(CUSTOMER_NAME); +.br + +while( db_status == S_OKAY ) +.br +{ +.br + struct customer cust; +.br + + d_recread(&cust); +.br + printf("%s\\n", cust.name); +.br + d_keynext(CUSTOMER_NAME); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_keyfind(1), d_keyprev(1), d_keyfrst(1), d_keylast(1), d_recread(1). + diff --git a/man/d_keyprev.3 b/man/d_keyprev.3 new file mode 100644 index 0000000..3ba500e --- /dev/null +++ b/man/d_keyprev.3 @@ -0,0 +1,81 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_keyprev.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_KEYPREV 1 \*(Dt TYPHOON +.SH NAME +d_keyprev \- find the previous key value in an index +.SH SYNOPSIS +.B #include +.br + +\fBd_keyprev(ulong \fPkeyid\fB) +.SH DESCRIPTION +\fBd_keyprevf\fP finds the next key value smaller than or equal to the +current key value in the index specified by +\fIkeyid\fP. If \fBd_keyprev\fP returns \fBS_NOTFOUND\fP the start of the +index has been passed. A subsequent call to \fBd_keyprev\fP will return the +last key value in the index. +.br + +The id can be either the id of a compound key or a field that is a key +by itself. +.br + +The actual record is not read from the database until \fBd_recread(1)\fP is +called. +.SH EXAMPLE +/* Traverse the customers in alphabetical, descending order */ + +#include +.br + +d_keylast(CUSTOMER_NAME); +.br + +while( db_status == S_OKAY ) +.br +{ +.br + struct customer cust; +.br + + d_recread(&cust); +.br + printf("%s\\n", cust.name); +.br + d_keyprev(CUSTOMER_NAME); +.br +} +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The key value was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVFLD +The id is not a valid field. +.TP +.B S_NOTKEY +The field id is not a key itself. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_keyfind(1), d_keynext(1), d_keylast(1), d_recread(1). + diff --git a/man/d_keyread.3 b/man/d_keyread.3 new file mode 100644 index 0000000..2c9b1ff --- /dev/null +++ b/man/d_keyread.3 @@ -0,0 +1,60 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_keyread.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_KEYREAD 1 \*(Dt TYPHOON +.SH NAME +d_keyread \- read the most recently found key +.SH SYNOPSIS +.B #include +.br + +\fBd_keyread(void *\fPbuf\fB) +.SH DESCRIPTION +\fBd_keyread\fP copies the contents of the most recently accessed key +field specified by \fIfieldid\fP +into the buffer \fIbuf\fP. This can be used to determine the contents +of a key's fields without actually reading the record. +.br +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_NOCR +There is no current record. +.TP +None. +.SH EXAMPLE +/* Find the smallest customer name */ +.br + +#include +.br + +if( d_keyfrst(CUSTOMER_NAME) == S_OKAY ) +.br +{ +.br + char name[30]; +.br + d_keyread(name); + printf("Name %s\\n", name); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_recread(1), d_crread(1) diff --git a/man/d_open.3 b/man/d_open.3 new file mode 100644 index 0000000..71edb50 --- /dev/null +++ b/man/d_open.3 @@ -0,0 +1,57 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_open.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_OPEN 1 \*(Dt TYPHOON +.SH NAME +d_open \- open a database +.SH SYNOPSIS +.B #include +.br + +\fBd_open(char *\fPdbname\fB, char *\fPmode\fB) +.SH DESCRIPTION +\fBd_open\fP opens a database in either shared or exclusive mode. +If the database does not already exist it is created without warning. +The dbd-file must be placed in the path specified by d_dbdpath(1) and +the database files must be placed in the path specified by d_dbfpath(1). +\fIdbname\fP is the name of the dbd-file without extension. +\fImode\fP is "s" or "x" for shared and exclusive mode, respectively. + +If \fBd_open\fP returns \fBS_OKAY\fP the database becomes the current +database. + +If the database has already been opened by another process in exclusive +mode, d_open returns S_UNAVAIL. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Database opened successfully. +.TP +.B S_NOMEM +Out of memory. +.TP +.B S_INVDB +Invalid database name. +.TP +.B S_IOFATAL +Fatal file i/o error. +.TP +.B S_NOTAVAIL +The database has been opened in exclusive mode by another process. +.SH CURRENCY CHANGES +The opened database becomes the current database. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_dbdpath(1), d_dbfpath(1), d_setfiles(1), d_close(1), d_destroy(1), d_dbget(1). + diff --git a/man/d_recfrst.3 b/man/d_recfrst.3 new file mode 100644 index 0000000..c676c0c --- /dev/null +++ b/man/d_recfrst.3 @@ -0,0 +1,68 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_recfrst.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_RECFRST 1 \*(Dt TYPHOON +.SH NAME +d_recfrst \- find the first record in a table +.SH SYNOPSIS +.B #include +.br + +\fBd_recfrst(ulong \fPrecid\fB) +.SH DESCRIPTION +\fBd_recfrst\fP finds the first record in the table specified by +\fIrecid\fP. If \fBd_recfrst\fP returns \fBS_NOTFOUND\fP the table +is empty. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +A record was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVREC +The id is not a record id. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH EXAMPLE +/* Traverse the customers in randomorder */ + +#include +.br + +d_recfrst(CUSTOMER); +.br + +while( db_status == S_OKAY ) +.br +{ +.br + struct customer cust; +.br + + d_recread(&cust); +.br + printf("%s\\n", cust.name); +.br + d_recnext(CUSTOMER); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_reclast(1), d_recnext(1), d_recprev(1), d_recread(1). + diff --git a/man/d_reclast.3 b/man/d_reclast.3 new file mode 100644 index 0000000..8cc3f16 --- /dev/null +++ b/man/d_reclast.3 @@ -0,0 +1,45 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_reclast.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_RECLAST 1 \*(Dt TYPHOON +.SH NAME +d_reclast \- find the last record in a table +.SH SYNOPSIS +.B #include +.br + +\fBd_reclast(ulong \fPrecid\fB) +.SH DESCRIPTION +\fBd_reclast\fP finds the last record in the table specified by +\fIrecid\fP. If \fBd_reclast\fP returns \fBS_NOTFOUND\fP the table +is empty. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +A record was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVREC +The id is not a record id. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_recprev(1), d_recnext(1), d_recprev(1), d_recread(1). + diff --git a/man/d_recnext.3 b/man/d_recnext.3 new file mode 100644 index 0000000..21aaf29 --- /dev/null +++ b/man/d_recnext.3 @@ -0,0 +1,68 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_recnext.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_RECNEXT 1 \*(Dt TYPHOON +.SH NAME +d_recnext \- find the next record in a table +.SH SYNOPSIS +.B #include +.br + +\fBd_recnext(ulong \fPrecid\fB) +.SH DESCRIPTION +\fBd_recnext\fP finds the next record in the table specified by +\fIrecid\fP. If \fBd_recnext\fP returns \fBS_NOTFOUND\fP the table +is empty. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +A record was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVREC +The id is not a record id. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH EXAMPLE +/* Traverse the customers in randomorder */ + +#include +.br + +d_recfrst(CUSTOMER); +.br + +while( db_status == S_OKAY ) +.br +{ +.br + struct customer cust; +.br + + d_recread(&cust); +.br + printf("%s\\n", cust.name); +.br + d_recnext(CUSTOMER); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_recfrst(1), d_reclast(1), d_recprev(1), d_recread(1). + diff --git a/man/d_recprev.3 b/man/d_recprev.3 new file mode 100644 index 0000000..e63e531 --- /dev/null +++ b/man/d_recprev.3 @@ -0,0 +1,45 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_recprev.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_RECPREV 1 \*(Dt TYPHOON +.SH NAME +d_recprev \- find the previous record in a table +.SH SYNOPSIS +.B #include +.br + +\fBd_recprev(ulong \fPrecid\fB) +.SH DESCRIPTION +\fBd_recprev\fP finds the previous record in the table specified by +\fIrecid\fP. If \fBd_recprev\fP returns \fBS_NOTFOUND\fP the table +is empty. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +A record was found. +.TP +.B S_NOTFOUND +The key value was not found, i.e. the index is empty. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_INVREC +The id is not a record id. +.SH CURRENCY CHANGES +If \fBS_OKAY\fP is returned, the record found becomes the current record. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_recfrst(1), d_reclast(1), d_recnext(1), d_recread(1). + diff --git a/man/d_recread.3 b/man/d_recread.3 new file mode 100644 index 0000000..39eccae --- /dev/null +++ b/man/d_recread.3 @@ -0,0 +1,60 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_recread.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_RECREAD 1 \*(Dt TYPHOON +.SH NAME +d_recread \- read the contents of the current record +.SH SYNOPSIS +.B #include +.br + +\fBd_recread(void *\fPbuf\fB) +.SH DESCRIPTION +\fBd_recread\fP reads the contents of the current record into \fIbuf\fP. +If there is no current record, \fBS_NOCR\fP is returned. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The record was successfully read. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_NOCR +There is no current record. +.TP +.B S_INVREC +The record id is not valid. +.TP +.SH CURRENCY CHANGES +None. +.SH EXAMPLE +#include + +struct customer cust; +.br +strcpy(cust.name, "Pedersen"); +.br +if( d_keyfind(CUSTOMER_NAME, &cust.name) == S_OKAY ) +.br +{ +.br + d_recread(&cust); +.br + printf("account number is %lu\\n", cust.account); +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_recwrite(1) diff --git a/man/d_recwrite.3 b/man/d_recwrite.3 new file mode 100644 index 0000000..b68d2f0 --- /dev/null +++ b/man/d_recwrite.3 @@ -0,0 +1,75 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_recwrite.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_RECWRITE 1 \*(Dt TYPHOON +.SH NAME +d_recwrite \- update the current record +.SH SYNOPSIS +.B #include +.br + +\fBd_recwrite(void *\fPbuf\fB) +.SH DESCRIPTION +\fBd_recwrite\fP updates the contents of the current record. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +The record was successfully updated. +.TP +.B S_NOCD +There is no current database. +.TP +.B S_NOCR +There is no current record. +.TP +.B S_INVREC +The record id is not valid. +.TP +.B S_DUPLICATE +One of the keys in the record would cause duplicates in a unique index. +\fIdb_subcode\fP contains the id of the conflicting field or key. +.TP +.B S_RECSIZE +A length determinator of a variable length field contained a illegal +value. \fIdb_subcode\fP contains the id of the conflicting field. +.TP +.B S_FOREIGN +The target of a foreign key could not be found. \fIdb_subcode\fP contains +the id of the target table. +.TP +.SH CURRENCY CHANGES +None. +.SH EXAMPLE +#include + +struct customer cust; +.br +strcpy(cust.name, "Pedersen"); +.br +if( d_keyfind(CUSTOMER_NAME, &cust.name) == S_OKAY ) +.br +{ +.br + d_recread(&cust); +.br + cust.account = 2000; +.br + if( d_recwrite(&cust) != S_OKAY ) +.br + /* handle error */ +.br +} +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_fillnew(1), d_recread(1) diff --git a/man/d_setfiles.3 b/man/d_setfiles.3 new file mode 100644 index 0000000..6ae8edb --- /dev/null +++ b/man/d_setfiles.3 @@ -0,0 +1,40 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: d_setfiles.3,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH D_SETFILES 1 \*(Dt TYPHOON +.SH NAME +d_setfiles \- set the maximum number of open files +.SH SYNOPSIS +.B #include +.br + +\fBd_setfiles(int \fPmaxfiles\fB) +.SH DESCRIPTION +\fBd_setfiles\fP sets the maximum number of files that typhoon may have +open at the same time. If the current number of open files exceeds +\fImaxfiles\fP, +Typhoon closes the least recently used files. +.SH DIAGNOSTICS +The status code returned by the function is also stored in the global +variable \fIdb_status\fP. +.TP +.B S_OKAY +Operation successful. +.TP +.B S_INVPARM +The parameter is invalid. +.SH CURRENCY CHANGES +None. +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" +d_open(1) + diff --git a/man/ddlp.1 b/man/ddlp.1 new file mode 100644 index 0000000..68dd8e9 --- /dev/null +++ b/man/ddlp.1 @@ -0,0 +1,146 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: ddlp.1,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ +.ds r \s-1TYPHOON\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH DDLP 1 \*(Dt TYPHOON +.SH NAME +ddlp \- Data Definition Language Processor +.SH SYNOPSIS +\fBddlp \fP[\fB-a\fP[1|2|4]] [\fB-f\fP] [\fB-h\fP] file +.SH DESCRIPTION +\fBddlp\fP processes a ddl-file and generates a dbd-file and a header file +with structures and ids for records, fields and keys. +.br + +The \fB-a\fP option sets the structure alignment which must match +the one used by the C compiler. Specifying the \fB-f\fP +options causes \fBddlp\fP to only generate constants for those fields +that are keys. The \fB-h\fP option overrides the default header file name +by the one specified by the user. +.br +.SH DATA DEFINITION LANGUAGE GRAMMER +.TP +database +-> "database" name "{" decl {decl} "}" +.TP +decl +-> "data" "file" "[" pagesize "]" name "contains" +.br + name ";" +.br +| "key" "file" "[" pagesize "]" name "contains" +.br + name "." name +";" +.br +| "define" name expr +.br +| "sequence" name int [sortorder] "by" int ';' +.br +| "record" name "{" field {field} [key_decls] "}" +.br +.TP +key_decls +-> primary_key {alternate_key} {foreign_key} +.TP +primary_key +-> "primary" "key" name key_def ";" +.TP +alternate_key +-> "alternate" ["optional"] ["unique"] "key" name +.br + key_def +";" +.TP +foreign_key +-> "foreign" ["optional"] foreign_keydef name +.br + "references" name ";" +.TP +foreign_keydef +-> name +| name "{" name { "," name } "}" +.TP +key_def +-> "{" key_field { "," key_field } "}" +.br + "on" "update" action +.br + "on" "delete" action ";" +.TP +key_field +-> name [sortorder] +.TP +sortorder +-> "asc" | "desc" +.TP +action +-> "restrict" | "cascade" +.TP +pagesize +-> "[" int "]" +.TP +field +-> type name [dimension] ";" +.TP +dimension +-> array {array} [ "variable" "by" name ] +.TP +array +-> "[" integer "]" +.TP +type +-> int_type +.br +| "signed" int_type +.br +| "unsigned" int_type +.br +| float_type +.br +| struct_type +.TP +int_type +-> "char" +.br +| "int" +.br +| "long" +.TP +float_type +-> "float" +.br +| "double" +.TP +struct_type +-> struct_head [name] "{" field {field} "}" name +.br + [dimension] +.TP +struct_head +-> "struct" +.br +| "union" +.TP +expr +-> expr "+" expr +.br +| expr "-" expr +.br +| expr "/" expr +.br +| expr "*" expr +.br +| "(" expr ")" +.br +| integer +.SH IDENTIFICATION +Author: Thomas B. Pedersen. +.br +Copyright (c) 1994 Thomas B. Pedersen. +.SH "SEE ALSO" diff --git a/man/manual.asc b/man/manual.asc new file mode 100644 index 0000000..677daa8 --- /dev/null +++ b/man/manual.asc @@ -0,0 +1,1740 @@ + + + + + + + Typhoon Relational Database Management System + + User's Manual + + + + + + + + + + +1 INTRODUCTION. . . . . . . . . . . . . . . . . . . . . . . . 3 + +2 DATABASE DEFINITION . . . . . . . . . . . . . . . . . . . . 3 + 2.1 Grammar . . . . . . . . . . . . . . . . . . . . . . 3 + 2.2 Files . . . . . . . . . . . . . . . . . . . . . . . 5 + 2.3 Tables. . . . . . . . . . . . . . . . . . . . . . . 5 + 2.4 Key declarations. . . . . . . . . . . . . . . . . . 7 + 2.4.1 Primary. . . . . . . . . . . . . . . . . . 7 + 2.4.2 Alternate. . . . . . . . . . . . . . . . . 7 + 2.4.3 Foreign . . . . . . . . . . . . . . . . . 8 + 2.5 Sorting map . . . . . . . . . . . . . . . . . . . . 8 + +3 TOOLS . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 + 3.1 Data Definition Language Processor. . . . . . . . . 9 + 3.2 Database Definition Viewer. . . . . . . . . . . . . 9 + 3.3 Export tool . . . . . . . . . . . . . . . . . . . . 9 + 3.4 Import tool . . . . . . . . . . . . . . . . . . . . 9 + +4 APPLICATION PROGRAMMING INTERFACE . . . . . . . . . . . . . 11 + 4.1 Currency concept. . . . . . . . . . . . . . . . . . 11 + 4.2 Status codes. . . . . . . . . . . . . . . . . . . . 11 + 4.3 Opening and closing . . . . . . . . . . . . . . . . 12 + 4.3.1 d_open . . . . . . . . . . . . . . . . . . 12 + 4.3.2 d_close. . . . . . . . . . . . . . . . . . 12 + 4.3.3 d_dbget. . . . . . . . . . . . . . . . . . 13 + 4.3.4 d_dbset. . . . . . . . . . . . . . . . . . 13 + 4.3.5 d_dbdpath. . . . . . . . . . . . . . . . . 14 + 4.3.6 d_dbfpath. . . . . . . . . . . . . . . . . 14 + 4.3.7 d_setfiles . . . . . . . . . . . . . . . . 15 + 4.4 Record operations . . . . . . . . . . . . . . . . . 15 + 4.4.1 d_fillnew. . . . . . . . . . . . . . . . . 15 + 4.4.2 d_recwrite . . . . . . . . . . . . . . . . 16 + 4.4.3 d_recread. . . . . . . . . . . . . . . . . 17 + 4.4.4 d_delete . . . . . . . . . . . . . . . . . 17 + 4.4.5 d_recfrst. . . . . . . . . . . . . . . . . 18 + 4.4.6 d_reclast. . . . . . . . . . . . . . . . . 19 + 4.4.7 d_recnext. . . . . . . . . . . . . . . . . 19 + 4.4.8 d_recprev. . . . . . . . . . . . . . . . . 20 + 4.4.9 d_crget. . . . . . . . . . . . . . . . . . 20 + 4.4.10 d_crread. . . . . . . . . . . . . . . . . 21 + 4.5 Key operations. . . . . . . . . . . . . . . . . . . 21 + 4.5.1 d_keyfind. . . . . . . . . . . . . . . . . 21 + 4.5.2 d_keyfrst. . . . . . . . . . . . . . . . . 22 + 4.5.3 d_keylast. . . . . . . . . . . . . . . . . 23 + 4.5.4 d_keynest. . . . . . . . . . . . . . . . . 24 + 4.5.5 d_keyprev. . . . . . . . . . . . . . . . . 24 + 4.5.6 d_keyread. . . . . . . . . . . . . . . . . 25 + + 1 INTRODUCTION + + Typhoon is a relational database management system intended for C +programmers on Unix and OS/2 platforms. + + The database tables are defined in a DDL (Data Definition Language) +file which is a plain ASCII file, containing C language structure +declarations. The DDL is processed by a Data Definition Language Processor +(ddlp) which produces a Database Definition file (DBD) and a header file. + + The DBD file contains a description of all database objects; tables, +fields, indexes and files, and the header file contains C language constants +used in the application program to reference these objects. + + +------------+ + | demo.ddl | + +------------+ + | + v + + ddlp + + / \ + / \ + v v + +----------+ +------------+ + | demo.h | | demo.dbd | + +----------+ +------------+ + + The rest of this manual describes how to create databases and how the +C library functions are used. + + +2 DATABASE DEFINITION + + A database is described in a Data Definition Language file which is an +ASCII file with the extension ".ddl". The file consists of C language +structure declarations that directly reflect the way records are stored in the +database. + + +2.1 Grammar + + The following Extended BNF grammar describes the syntax of the DDL +file. The grammar is divided into five logical sections. + + File declarations + Table declarations + Structure declarations + Field declarations + Sorting map + + Some of the keywords, e.g. "record" and "key", may seen a bit strange, +but are called so for historic reasons. The format of the DDL was derived from +db_VISTA's DDL file, which uses these keywords. + + C++ comments (//) and nested C comments (/* .. */) can be used in the +DDL file. + + +File declarations + +database -> "database" ident '{' decl { decl } '}' + +decl -> file_decl + | record_decl + | map_decl + | "define" ident expr + +file_decl -> "data" "file" [ size ] string "contains" ident ';' + | "key" "file" [ size ] string "contains" ident '.' key_type ';' + +key_type -> ident + | "references" + + +Record declarations + +record_decl -> "record" ident '{' member { member } key_decls '}' + +member -> membertype ident [ dimen ] ';' + | struct_decl ident [ dimen ] ';' + +dimen -> dimension [ "variable" "by" ident ] + +dimension -> array { array } + +array -> '[' expr ']' + + +key_decls -> [ primary_key_decl ] + { alternate_key_decl } + { foreign_key_decl } + + +Key declarations + +primary_key_decl + -> "primary" key_decl ';' + +alternate_key_decl + -> "alternate" [ "unique" ] key_decl [ null_stmt ] ';' + +foreign_key_decl + -> "foreign" key_decl "references" ident + "on" "update" action + "on" "delete" action [ null_stmt ] ';' + +action -> "restrict" + | "cascade" + +null_stmt -> "null" "by" ident + + +key_decl -> "key" ident [ '{' key_member {key_member} '}' ] + +key_member -> ident [ "asc" | "desc" ] + + +Structure member definitions + +membertype -> int_type + | int_sign + | int_sign int_type + | float_type + | u_type + | "long" float_type + +int_type -> "char" + | "short" + | "int" + | "long" + +int_sign -> "signed" + | "unsigned" + +float_type -> "float" + | "double" + +u_type -> "uchar" + | "ushort" + | "ulong" + +expr -> expr '+' expr + | expr '-' expr + | expr '*' expr + | expr '/' expr + | '(' expr ')' + | number + + +Struct or union declaration + +struct_decl -> struct_or_union [ident] '{' member_list '}' + | struct_or_union ident + +struct_or_union + -> "struct" + | "union" "controlled" "by" ident + + +Map declaration + +map_decl -> "map" '{' map { map } '}' + +map -> map_id "->" map_id ';' + +map_id -> "'" char "'" + | number + + + +2.2 Files + +The file declaration part describes the files that tables and indexes are +stored in. A file can be either a data file or a key file. + +file_decl -> "data" "file" [ size ] string "contains" ident ';' + | "key" "file" [ size ] string "contains" ident '.' key_type ';' + +key_type -> ident + | "references" + + The size following the "file" keyword, determines the page size of an +index file or variable length record file. The default page size is 512 bytes. +If an index contains very large keys (e.g. > 40 bytes) the page size should be +set to 1024, 2048 or 4096 to reduce the depth of the B-tree. To gain maximum +efficiency, the number of levels in the B-trees should be kept at minimum, +preferably below 5. The number of keys a page can hold is + + order = (page size - sizeof(long)) / (key size + sizeof(long) * 2) + +and the number of levels in a B-tree is + + ln keys / ln order + 1 + + For variable length record files, the page size determines the size of +each block a record is divided into. The size should always be greater than +the static part of the record, i.e. the total size of the fixed length fields. + + If a variable length record of a certain type typically is between +1000 and 2000 bytes, the page size should be set to 2048, which means that +most records can be read in a single I/O operation. Records bigger than that +would require two or more I/O operations. It also means that the minimum size +a record of that type will occupy is the page size. Thus, the page size chosen +for a variable length record file, depends very much on the characteristics of +the data stored in it. + + A table that has dependent tables, i.e. tables that reference its +primary key, also needs a "references" file, which specifies the file that +dependencies are stored in for that table's primary key. + + +2.3 Tables + + The table declaration part describes the actual database, namely the +tables where information is stored in. A record is a sequence of member +declarations followed by a sequence of key declarations. The key declaration +part is described in a subsequent section. + +record_decl -> "record" ident '{' member { member } key_decls '}' + +member -> membertype ident [ dimen ] ';' + | struct_decl ident [ dimen ] ';' + +dimen -> dimension [ "variable" "by" ident ] + +dimension -> array { array } + +array -> '[' expr ']' + +key_decls -> [ primary_key_decl ] + { alternate_key_decl } + { foreign_key_decl } + + The names of records, fields and keys will appear in uppercase in the +header file produced by ddlp. If a field name is used in more than one record +declaration, its name will be preceded by the record name and an underscore, +for example CUSTOMER_NAME. + + The syntax of the record is almost the same as the one for C language +structures. A record can contain chars, ints, longs, floats, arrays and nested +structures and unions. Enumerated types are not supported. The structures are +stored in exactly the same way in the database as in the computer's memory. +For space considerations, structure members should be ordered so that minimum +alignment is done by the compiler. On platforms where a long is four bytes, +struct a would take up 16 bytes, whereas the rearranged struct b only would +take up 10 bytes. + + struct { struct ; + char a; long b; + long b; long d; + char c; char a; + long d; char c; + } a; } b; + + Typhoon will correctly spot where alignment is done by the compiler, +but there is no need to waste valuable space where not necessary. + + Only the fields in the outermost scope of the record are significant +to Typhoon. In the demo record below, only the members a, b, c, d and x can be +referenced from the application. This means that members in nested structures +or unions cannot be used in key declarations. Nested structures are only +supported to make life easier to the programmer and to enable the tools +tyimport and tyexport to read and write comma-files correctly. + + record demo { + float a; + long b; + ushort x_count; + char d[21]; + struct { + char e; + struct { + union controlled by e { + long r; + char s[3]; + } u[2]; + long p; + int f[20][2]; + char g; + + } e[10]; + } x[10] variable by c; + + ... + ... + } + + + The union declaration differs from its C language equivalent in that +it has a control statement. This is required for tyexport and tyimport to know +which member in a union is active. In the above example, e determines whether +r or s is the active member of u. The members inside a union are numbered from +zero, so if r is active, e must be zero, otherwise 1. The 'control field' must +be a char. + + An array can have more than one dimension, but Typhoon sees it as a +single dimension, since there is no way to reference specific array elements +from the API. An array at the outermost level can have a "variable by" +statement which specifies a member that will be its 'size determinator'. This +member determines the number of elements in the array that should be stored in +the database. The size determinator must be an unsigned short. A record can +contain more than one variable length array which must all be placed at the +end of a record declaration. + + +2.4 Key declarations + + A table can have one or more key declarations. A primary key, one or +more alternate keys, and one or more foreign keys. The format of the key +declarations are almost the same, but the semantics are quite different. + +primary_key_decl + -> "primary" key_decl ';' + +alternate_key_decl + -> "alternate" [ "unique" ] key_decl [ null_stmt ] ';' + + +foreign_key_decl + -> "foreign" key_decl "references" ident + "on" "update" action + "on" "delete" action [ null_stmt ] ';' + +action -> "restrict" + | "cascade" + +null_stmt -> "null" "by" ident + + +key_decl -> "key" ident [ '{' key_member {key_member} '}' ] + +key_member -> ident [ "asc" | "desc" ] + + +2.4.1 Primary + + A table can have at most one primary key, hence its name. The primary +key is used to uniquely identify a record in a table. A primary key can also +be referenced from other tables. These tables are called dependent tables and +the referenced table is called the parent or target table. + + A primary key can consist of more than one field, in which case the +key must be given a unique name. The employee_key below also specifies that +salary should be sorted in descending order. + + primary key employee_key { name, salary desc }; + + +2.4.2 Alternate + + If a table needs more indexes than the primary, one or more alternate +indexes can be defined. These indexes can contain duplicate values. The use of +non-unique indexes is discouraged, however, since they are slower and often +reflect a poor design. However, there are situations where duplicates are a +necessary evil and therefore Typhoon supports them. + + An alternate key can null, in which case it is not stored in the +index. If the field is a string it can be the null indicator itself, otherwise +it must be another char field. If the null indicator is zero, the key is also +null. + + The following example shows a table that contains accounts. If an +account is blocked, blocked_on_date contains the date on which it was blocked. +That way, only blocked accounts will be available through the block_on_date +index. + + record account { + long blocked_on_date; + long balance; + char name[21]; + char is_blocked; + + primary key id; + alternate key blocked_on_date null by blocked_on_date; + }; + + +2.4.3 Foreign + + Tables in a database are often interrelated and some integrity +checking is therefore necessary. Consider the following example. + + record company { + long id; + char name[21] + + primary key id; + } + + record product { + long company; + char name[21]; + + primary key product_key { company, name }; + foreign key company references company + on update restrict + on delete restrict; + } + + There is a one-to-many relationship between the company and the +product table. A company can have many products, but a product can only be +manufactured by one company. The compound primary key allows us to find all +products manufactured by a certain company. The foreign key itself ensures +that a product cannot be created for a nonexisting company. Conversely, the +delete clause ensures that a company which has products cannot be deleted. + + The update clause ensures that the name in a company record cannot +change if that company has products, because that would invalidate the +dependent record's foreign key. The cascade rule is not supported. + + A foreign key can also be null which is often useful. For example in a +self-referencing table. The following table implements a category tree, where +each category is a subcategory of another category. This doesn't work for the +root record, so we set the parent to null. If has_parent is null, the target +of the parent key is not being checked for. + + record category { + long id; + long parent; + char has_parent; + char name[21]; + + primary key id; + foreign key parent references category null by has_parent; + } + + +2.5 Sorting map + + Countries other than the English speaking often have special +characters in their alphabet that are not present in the ASCII character set. +These might be characters in the latin part of the ISO-8859 character set. +However, the ordinal values of these character do not reflect their alphabetic +ordering. This problem can be solved by a character map. By default, all ASCII +characters have been set to case-insensitive (lower case). + +map_decl -> "map" '{' map { map } '}' + +map -> map_id "->" map_id ';' + +map_id -> "'" char "'" + | number + + The character left to the arrow is the character to translate and the +value to the right is the value is should be compared as. It is possible to +map several characters onto the same value. The following map makes the ASCII +characters case sensitive again. + + map { + 'A' -> 'A'; + 'B' -> 'B'; + 'C' -> 'C'; + 'D' -> 'D'; + 'E' -> 'E'; + 'F' -> 'G'; + + ... + + 'Z' -> 'Z'; + + } + + +3 TOOLS + +Typhoon has four tools. dllp which is used to process DDL files. dbdview which +displays DBD files. tyexport which exports a database and tyimport which +imports a database. + + +3.1 Data Definition Language Processor + + The DDL Processor is used to compile DDL files. ddlp will report +syntactic and semantic errors as they are encountered. It has the following +command line options: + + -a + -f + -h
+ + The -a option specifies the alignment to use. Some architectures, most +notably Intel, allows structure members to be aligned on 1 byte boundaries. +However, on RISC architectures it is a requirement that a value can be read +from memory in a single read operation and thus, cannot cross an word +boundary. In other words, an integer (or float for that mattter) must be +stored on an address that is a multiple of its size. + + The -f option causes ddlp to only generate constants for fields that +are also keys. + + The -h option overrides the default header file name. + + +3.2 Database Definition Viewer + +This tool displays the tables that make up a DBD file. + + +3.3 Export tool + + The contents of a database can be exported to ASCII files, where each +field is separated by a comma. To export tables, an export specification must +be made. It can be handwritten, but it is easier to let tyexport generate it. +This can be done with the following command: + + tyexport -g + + The export specification is written to a file with ".exp" extension. +The export specification contains the tables and fields to export. String will +be enclosed in double quotes and characters in single quotes if printable, +otherwise the ordinal value is written. For unions, the control field +determines which union member is written. + + An export specification can also be used to import a database, just +change the "export" keyword first in the file to "import". + +NOTE! floats are not supported. + + +3.4 Import tool + + Data can be imported from ASCII files where the fields are separated +by commas and the strings enclosed in double quotes. Character fields can be +either integers or characters enclosed in single quotes. Otherwise the format +of the import specification is the same as the one for tyexport. + +NOTE! floats are not supported. + + + + 4 APPLICATION PROGRAMMING INTERFACE + + This section describes the Application Programming Interface (API) +that is used by the programmer to modify and search a database. + + +4.1 Currency concept + + Typhoon uses the concept of 'currency' in the sense that it has a +current database, a current record and a current position in each index. For +instance, after opening a database that database becomes the current database. +All subsequence operations are performed on that database until it is closed +or another database is opened. + + The same principle applies to records. After creation or lookup of a +record that record becomes the current record. Subsequence read, write or +delete operations are performed on that record. + + +4.2 Status codes + + The API functions all return a status code that indicates the status +of the operation. db_status contains the status code until the next API +function call. + + db_subcode is used to hold elaborated status information not provided +by db_status. For example, if db_status returns S_DUPLICATE, the conflicting +field or key ID in stored in db_subcode. + + +NOTE! In a multi-threaded program, special care must be taken that two + threads do not destroy each other's db_status and db_subcode value. + + +The following constants indicate the status of an operation. + +S_OKAYThe operation was successful. + +S_NOTFOUND + The function called was not able to find the desired object. + This value can be returned by all key and record navigation + functions. + +S_DUPLICATE + The operation would cause duplicate keys in a unique index. The + conflicting field or key id is stored in db_subcode. + +S_NOMEM The operation could not complete because of insufficient + memory. + +S_NOTAVAIL + The database is not available because another process has + opened it in exclusive mode. + +S_IOFATAL A fatal I/O operation occurred. + +S_FOREIGN A record could not be created or updated because the target key + in a parent table did not exist. The ID of the target table is + stored in db_subcode. + +S_RESTRICT + The primary key in the object record has changed, and would + cause dependent tables to contain records with invalid + references. The ID of the dependent table causing the problem + is stored in db_subcode. + +The following constants are returned due to a programming error. For example, +a record id was specified where a key id was expected. These errors should +therefore not be returned when an application has been debugged. + +S_NOCRThere is no current record. For example, returned by d_recread() after + a d_keyfind() that returned S_NOTFOUND. + +S_NOCDThere is no current database. + +S_INVDB Invalid database specified. + +S_INVREC Invalid record ID specified. + +S_INVFLD Invalid field ID specified. + +S_NOTKEY The specified ID is not a key ID. + +S_RECSIZE The size determinator of a variable length field contains a + value greater than the maximum size. + +S_BADTYPE Some parameter contained a bad type. + +S_BADPARM + Some parameter had an invalid value. + + + +4.3 Opening and closing + +4.3.1 d_open + + Open a database + +SYNOPSIS + #include + + d_open(char *dbname, char *mode) + +DESCRIPTION + d_open opens a database in either shared or exclusive mode. If the + database does not already exist it is created without warning. The + dbd-file must be placed in the path specified by d_dbdpath(1) and the + database files must be placed in the path specified by d_dbfpath(1). + dbname is the name of the dbd-file without extension. mode is "s" or + "x" for shared and exclusive mode, respectively. + + If d_open returns S_OKAY the database becomes the current database. + + If the database has already been opened by another process in + exclusive mode, d_open returns S_UNAVAIL. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Database opened successfully. + + S_NOMEM Out of memory. + + S_INVDB Invalid database name. + + S_FATALIO Fatal file i/o error. + + S_UNAVAIL The database has been opened in exclusive mode by + another process. + +CURRENCY CHANGES + The opened database becomes the current database. + + +SEE ALSO + d_dbdpath(1), d_dbfpath(1), d_setfiles(1), d_close(1), d_destroy(1). + + +4.3.2 d_close + + Closes a database + +SYNOPSIS + #include + + d_close() + +DESCRIPTION + d_close closes the current database. If no database is currently open, + S_NOCD is returned. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Database closed successfully. + + S_NOCD No current database. + +CURRENCY CHANGES + There is no current database. + +SEE ALSO + d_open(1) + + + +4.3.3 d_dbget + + Get the id of the current database. + +SYNOPSIS + #include + + d_crget() + +DESCRIPTION + When a database is opened it is assigned an internal id which it + retains until it is closed. d_dbget gets the id of the current + database. This id can be used in d_dbset(1) to make another open + database the current database. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Operation successful. + + S_NOCD There is no current database. + +CURRENCY CHANGES + None. + +EXAMPLE + #include + + int cust_dbid; + + d_open("customer", "s"); + d_dbget(&cust_dbid); + ... + d_open(....); + ... + /* Make the customer database the current database */ + d_dbset(cust_dbid); + + +SEE ALSO + d_dbset(1) + + + + +4.3.4 d_dbset + + + Set the current database + +SYNOPSIS + #include + + d_dbset(int dbid) + +DESCRIPTION + Sets the database with id dbid to the current database. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Operation successful. + + S_INVPARM There database id specified is invalid. + +CURRENCY CHANGES + None. + +EXAMPLE + See d_dbget(1). + + +SEE ALSO + d_dbget(1) + + + +4.3.5 d_dbdpath + + Set the path of the dbd-files. + +SYNOPSIS + #include + + d_dbdpath(char *path) + +DESCRIPTION + d_dbdpath sets the path of the database definition files. This + function should be called prior to calling d_open(1). path can be + either a relative or an absolute path name. The validity of path is + not checked until d_open(1) is called. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY This value is always returned. + +CURRENCY CHANGES + None. + + +SEE ALSO + d_dbfpath(1), d_open(1). + + + +4.3.6 d_dbfpath + + Set the path of the database files. + +SYNOPSIS + #include + + d_dbfpath(char *path) + +DESCRIPTION + d_dbdpath sets the path of the database files. This function should be + called prior to calling d_open(1) as it determines where database + files are stored. path can be either a relative or an absolute path + name. The validity of path is not checked until d_open(1) is called. + + Setting the dbd-path does not affect previously opened databases. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY This value is always returned. + +CURRENCY CHANGES + None. + + +SEE ALSO + d_dbdpath(1), d_open(1). + + + +4.3.7 d_setfiles + + Set the maximum number of open files + +SYNOPSIS + #include + + d_setfiles(int maxfiles) + +DESCRIPTION + d_setfiles sets the maximum number of files that typhoon may have open + at the same time. If the current number of open files exceeds + maxfiles, Typhoon closes the least recently used files. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Operation successful. + + S_INVPARM The parameter is invalid. + +CURRENCY CHANGES + None. + + +SEE ALSO + d_open(1) + + + + +4.4 Record operations + + +4.4.1 d_fillnew + + Insert a new record. + +SYNOPSIS + #include + + d_fillnew(ulong recid, void *buf) + +DESCRIPTION + d_fillnew inserts a new record in a table. recid specifies the type of + the record stored in buf. The inserted record retains the same + database address throughout its life in the database. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The record was successfully inserted. + + S_NOCD There is no current database. + + S_INVREC The record id is not valid. + + S_DUPLICATE + One of the keys in the record would cause duplicates in + a unique index. db_subcode contains the id of the + conflicting field or key. + + S_RECSIZE A length determinator of a variable length field + contained a illegal value. db_subcode contains the id + of the conflicting field. + + S_FOREIGN The target of a foreign key could not be found. + db_subcode contains the id of the target table. + +CURRENCY CHANGES + If d_fillnew returned S_OKAY the record becomes the current record. + +EXAMPLE + #include + + struct customer cust; + strcpy(cust.name, "Pedersen"); + cust.account = 10002; + if( d_fillnew(CUSTOMER, &cust) != S_OKAY ) + /* handle error */ + + +SEE ALSO + d_recwrite + + + + +4.4.2 d_recwrite + + Update the current record. + +SYNOPSIS + #include + + d_recwrite(void *buf) + +DESCRIPTION + d_recwrite updates the contents of the current record. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The record was successfully updated. + + S_NOCD There is no current database. + + S_NOCR There is no current record. + + S_INVREC The record id is not valid. + + S_DUPLICATE + One of the keys in the record would cause duplicates in + a unique index. db_subcode contains the id of the + conflicting field or key. + + S_RECSIZE A length determinator of a variable length field + contained a illegal value. db_subcode contains the id + of the conflicting field. + + S_FOREIGN The target of a foreign key could not be found. + db_subcode contains the id of the target table. + + +CURRENCY CHANGES + None. + +EXAMPLE + #include + + struct customer cust; + strcpy(cust.name, "Pedersen"); + if( d_keyfind(CUSTOMER_PURPOSE, &cust.name) == S_OKAY ) + { + d_recread(&cust); + cust.account = 2000; + if( d_recwrite(&cust) != S_OKAY ) + /* handle error */ + } +SEE ALSO + d_fillnew(1), d_recread(1) + + + +4.4.3 d_recread + + Read the contents of the current record. + +SYNOPSIS + #include + + d_recread(void *buf) + +DESCRIPTION + d_recread reads the contents of the current record into buf. If there + is no current record, S_NOCR is returned. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The record was successfully read. + + S_NOCD There is no current database. + + S_NOCR There is no current record. + + S_INVREC The record id is not valid. + + +CURRENCY CHANGES + None. + +EXAMPLE + #include + + struct customer cust; + strcpy(cust.name, "Pedersen"); + if( d_keyfind(CUSTOMER_PURPOSE, &cust.name) == S_OKAY ) + { + d_recread(&cust); + printf("account number is %lu\n", cust.account); + } + + +SEE ALSO + d_recwrite(1) + + +4.4.4 d_delete + + Delete the current record. + +SYNOPSIS + #include + + d_delete() + +DESCRIPTION + d_delete removes the current record from its table. If there is no + current record, S_NOCR is returned. If the record has references, i.e. + records with foreign keys that reference this record, S_RESTRICT is + returned. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The record was successfully deleted. + + S_NOCD There is no current database. + + S_NOCR There is no current record. + + S_RESTRICT One or more records currently reference this record and + the record cannot be deleted. + +CURRENCY CHANGES + None. + +EXAMPLE + #include + + if( d_keyfind(CUSTOMER_NAME, "Pedersen") == S_OKAY ) + { + if( d_delete() != S_OKAY ) + /* handle error */ + } + + + +SEE ALSO + d_fillnew(1) + + +4.4.5 d_recfrst + + Find the first record in a table. + +SYNOPSIS + #include + + d_recfrst(ulong recid) + +DESCRIPTION + d_recfrst finds the first record in the table specified by recid. If + d_recfrst returns S_NOTFOUND the table is empty. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY A record was found. + + S_NOTFOUND + The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVREC The id is not a record id. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + +EXAMPLE + /* Traverse the customers in randomorder */ + + #include + + d_recfrst(CUSTOMER); + + while( db_status == S_OKAY ) + { + struct customer cust; + + d_recread(&cust); + printf("%s\n", cust.name); + d_recnext(CUSTOMER); + } + +SEE ALSO + d_reclast(1), d_recnext(1), d_recprev(1), d_recread(1). + + +4.4.6 d_reclast + + Find the last record in a table. + +SYNOPSIS + #include + + d_reclast(ulong recid) + +DESCRIPTION + d_reclast finds the last record in the table specified by recid. If + d_reclast returns S_NOTFOUND the table is empty. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY A record was found. + + S_NOTFOUND + The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVREC The id is not a record id. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + + +SEE ALSO + d_recprev(1), d_recnext(1), d_recprev(1), d_recread(1). + + +4.4.7 d_recnext + + Find the next record in a table + +SYNOPSIS + #include + + d_recnext(ulong recid) + +DESCRIPTION + d_recnext finds the next record in the table specified by recid. If + d_recnext returns S_NOTFOUND the table is empty. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY A record was found. + + S_NOTFOUND + The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVREC The id is not a record id. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + +EXAMPLE + /* Traverse the customers in random order */ + + #include + + d_recfrst(CUSTOMER); + + while( db_status == S_OKAY ) + { + struct customer cust; + + d_recread(&cust); + printf("%s\n", cust.name); + d_recnext(CUSTOMER); + } + +SEE ALSO + d_recfrst(1), d_reclast(1), d_recprev(1), d_recread(1). + + +4.4.8 d_recprev + + Find the previous record in a table. + +SYNOPSIS + #include + + d_recprev(ulong recid) + +DESCRIPTION + d_recprev finds the previous record in the table specified by recid. + If d_recprev returns S_NOTFOUND the table is empty. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY A record was found. + + S_NOTFOUND + The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVREC The id is not a record id. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + + +SEE ALSO + d_recfrst(1), d_reclast(1), d_recnext(1), d_recread(1). + + +4.4.9 d_crget + + Get the database address of the current record. + +SYNOPSIS + #include + + d_crget() + +DESCRIPTION + d_crget gets the database address of the current record. If no + database is currently open, S_NOCD is returned. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Operation successful. + + S_NOCD There is no current database. + + S_NOCR There is no current record. + +CURRENCY CHANGES + None. + + +SEE ALSO + d_crset(1) + + +4.4.10 d_crread + + Read a field from the current record. + +SYNOPSIS + #include + + d_crread(ulong fieldid, void *buf) + +DESCRIPTION + d_crread copies the contents of the field specified by fieldid into + the buffer buf. If the field is a variable length field, only the + actual number of bytes in the field is copied. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Operation successful. + + S_NOCD There is no current database. + + S_NOCR There is no current record. + + S_INVFLD The id is not a valid field. + +CURRENCY CHANGES + None. + +EXAMPLE + /* Get Pedersen's account number */ + + #include + + if( d_keyfind(CUSTOMER_PURPOSE, "Pedersen") == S_OKAY ) + { + unsigned long account; + + d_crread(CUSTOMER_ACCOUNT, &account); + printf("Account number %lu\n", account); + } + + +SEE ALSO + d_recread(1), d_keyread(1) + + +4.5 Key operations + + +4.5.1 d_keyfind + + Search an index for a specific key value. + +SYNOPSIS + #include + + d_keyfind(ulong keyid, void *buf) + +DESCRIPTION + d_keyfind is used to lookup a record in a table via one of its + indexes. keyid specifies which index to search and buf contains the + value to search for. If the index contains more than one occurrence of + the key value (only possible for non-unique indexes) d_keyfind returns + the first one. + + The id can be either the id of a compound key or a field that is a key + by itself. + + If the key value was not found, d_keyfind returns S_NOTFOUND. A + subsequent call to d_keynext(1) returns next value in the sorting + order. + + The actual record is not read from the database until d_recread(1) is + called. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The key value was found. + + S_NOTFOUND + The key value was not found. + + S_NOCD There is no current database. + + S_INVFLD The id is not a valid field. + + S_NOTKEY The field id is not a key itself. + +CURRENCY CHANGES + If S_OKAY is returned. the record found becomes the current record. + +EXAMPLE + /* Find the customer called 'Pedersen' */ + + #include + + if( d_keyfind(CUSTOMER_NAME, "Pedersen") == S_OKAY ) + { + struct customer cust; + d_recread(&cust); + printf("Account number %lu\n", cust.account); + } + + +SEE ALSO + d_keynext(1), d_keyprev(1), d_keyfrst(1), d_keylast(1), d_recread(1). + + + +4.5.2 d_keyfrst + + Find the first key value in an index. + +SYNOPSIS + #include + + d_keyfrst(ulong keyid) + +DESCRIPTION + d_keyfrst finds the first key value in the index specified by keyid. + If d_keyfrst returns S_NOTFOUND the index is empty. + + The id can be either the id of a compound key or a field that is a key + by itself. + + The actual record is not read from the database until d_recread(1) is + called. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The key value was found. + + S_NOTFOUND + The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVFLD The id is not a valid field. + + S_NOTKEY The field id is not a key itself. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + +EXAMPLE + /* Traverse the customers in alphabetical, ascending + * order + */ + + #include + + d_keyfrst(CUSTOMER_NAME); + + while( db_status == S_OKAY ) + { + struct customer cust; + + d_recread(&cust); + printf("%s\n", cust.name); + d_keynext(CUSTOMER_NAME); + } + +SEE ALSO + d_keynext(1), d_keyfind(1), d_keyprev(1), d_keylast(1), d_recread(1). + + + +4.5.3 d_keylast + + Find the last key value in an index. + +SYNOPSIS + #include + + d_keylast(ulong keyid) + +DESCRIPTION + d_keyfrst finds the last key value in the index specified by keyid. If + d_keyfrst returns S_NOTFOUND the index is empty. + + The id can be either the id of a compound key, or a field that is a + key by itself. + + The actual record is not read from the database until d_recread(1) is + called. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The key value was found. + + S_NOTFOUND The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVFLD The id is not a valid field. + + S_NOTKEY The field id is not a key itself. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + +EXAMPLE + /* Traverse the customers in alphabetical, descending + * order. + */ + + #include + + d_keylast(CUSTOMER_NAME); + + while( db_status == S_OKAY ) + { + struct customer cust; + + d_recread(&cust); + printf("%s\n", cust.name); + d_keyprev(CUSTOMER_NAME); + } + +SEE ALSO + d_keynext(1), d_keyfind(1), d_keyprev(1), d_keylast(1), d_recread(1). + + + +4.5.4 d_keynest + + Find the next key value in an index + +SYNOPSIS + #include + + d_keynext(ulong keyid) + +DESCRIPTION + d_keynext finds the next key value greater than or equal to the + current key value in the index specified by keyid. If d_keynext + returns S_NOTFOUND the end of the index has been passed. A subsequent + call to d_keynext will return the first key value in the index. + + The id can be either the id of a compound key or a field that is a key + by itself. + + The actual record is not read from the database until d_recread(1) is + called. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY The key value was found. + + S_NOTFOUND The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVFLD The id is not a valid field. + + S_NOTKEY The field id is not a key itself. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + +EXAMPLE + /* Traverse the customers in alphabetical order */ + + #include + + d_keyfrst(CUSTOMER_NAME); + + while( db_status == S_OKAY ) + { + struct customer cust; + d_recread(&cust); + printf("%s\n", cust.name); + d_keynext(CUSTOMER_NAME); + } + +SEE ALSO + d_keyfind(1), d_keyprev(1), d_keyfrst(1), d_keylast(1), d_recread(1). + + + +4.5.5 d_keyprev + + Find the previous key value in an index. + +SYNOPSIS + #include + + d_keyprev(ulong keyid) + +DESCRIPTION + d_keyprev finds the next key value smaller than or equal to the + current key value in the index specified by keyid. If d_keyprev + returns S_NOTFOUND the start of the index has been passed. A + subsequent call to d_keyprev will return the last key value in the + index. + + The id can be either the id of a compound key or a field that is a key + by itself. + + The actual record is not read from the database until d_recread(1) is + called. + +EXAMPLE + /* Traverse the customers in alphabetical, descending + * order. + */ + + #include + + d_keylast(CUSTOMER_NAME); + + while( db_status == S_OKAY ) + { + struct customer cust; + + d_recread(&cust); + printf("%s\n", cust.name); + d_keyprev(CUSTOMER_NAME); + } + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + + S_OKAY The key value was found. + + S_NOTFOUND + The key value was not found, i.e. the index is empty. + + S_NOCD There is no current database. + + S_INVFLD The id is not a valid field. + + S_NOTKEY The field id is not a key itself. + +CURRENCY CHANGES + If S_OKAY is returned, the record found becomes the current record. + + +SEE ALSO + d_keyfind(1), d_keynext(1), d_keylast(1), d_recread(1). + + +4.5.6 d_keyread + + Read the most recently found key. + +SYNOPSIS + #include + + d_keyread(void *buf) + +DESCRIPTION + d_keyread copies the contents of the most recently accessed key field + specified by fieldid into the buffer buf. This can be used to + determine the contents of a key's fields without actually reading the + record. + +DIAGNOSTICS + The status code returned by the function is also stored in the global + variable db_status. + + S_OKAY Operation successful. + + S_NOCD There is no current database. + + S_NOCR There is no current record. + +CURRENCY CHANGES + None. + +EXAMPLE + /* Find the smallest customer name */ + + #include + + if( d_keyfrst(CUSTOMER_PURPOSE) == S_OKAY ) + { + char name[30]; + d_keyread(name); + printf("Name %s\n", name); + } + +SEE ALSO + d_recread(1), d_crread(1) + diff --git a/src/.tedhist b/src/.tedhist new file mode 100644 index 0000000000000000000000000000000000000000..8d877cb7193642533182e626ad3c58ce742e0125 GIT binary patch literal 1440 zcmdPX(|68D%u7%8$xmmX0$>E1p#{V&P+Xi^SelxboT`^h1;c?xR0A={4iE;hODf~@ y3sUo_Wjq5T6U1y@pxL}YHqiX^octuc3?QEx0OocWO${?g%@_@V(GVD(Apig{q!2;? literal 0 HcmV?d00001 diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..3c6f7ad --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,92 @@ +# Makefile for: libtyphoon.a + +DEFINES = -I../include @defs@ +CC = @cc@ +CFLAGS = @cflags@ +RANLIB = @ranlib@ +PREFIX = /usr/local +DESTLIB = $(PREFIX)/lib +DESTHDR = $(PREFIX)/include +DESTOWN = root +DESTGRP = local +SHELL = /bin/sh +LIBRARY = libtyphoon.a +LIBHDRS = ../include/environ.h ../include/typhoon.h +LIBID = TYPHOON 1.0 $(DESTLIB)/$(LIBRARY) +SRCS = bt_del.c bt_funcs.c bt_io.c bt_open.c cmpfuncs.c os.c \ + readdbd.c record.c ty_auxfn.c ty_find.c ty_ins.c \ + ty_io.c ty_log.c ty_open.c ty_refin.c ty_repl.c \ + ty_util.c unix.c vlr.c ansi.c sequence.c +HDRS = btree.h catalog.h ty_dbd.h ty_glob.h ty_log.h ty_prot.h \ + ty_repif.h ty_type.h +OBJS = bt_del.o bt_funcs.o bt_io.o bt_open.o cmpfuncs.o \ + os.o readdbd.o record.o ty_auxfn.o ty_find.o \ + ty_ins.o ty_io.o ty_log.o ty_open.o ty_refin.o \ + ty_repl.o ty_util.o unix.o vlr.o ansi.o sequence.o +UNUSED = dos.c os2.c ty_lock.c + +.DEFAULT: + co $@ + +.PHONY: all lint tags install uninstall clean distclean + +.c.o: + $(CC) -c $(CFLAGS) $< +# -mcs -d -a '@(#)$(LIBID)' $@ + +all: $(LIBRARY) + +$(LIBRARY): $(OBJS) + ar cru $(LIBRARY) $(OBJS) + $(RANLIB) $(LIBRARY) + +#catalog.dbd: catalog.ddl +# ddlp -f -hcatalog.h -a4 catalog + +lint: + lint -u $(DEFINES) $(SRCS) + +tags: $(HDRS) $(SRCS) + ctags -w $(HDRS) $(SRCS) + +install: $(LIBRARY) + cp $(LIBRARY) $(DESTLIB) + -ranlib $(DESTLIB)/$(LIBRARY) + chmod 644 $(DESTLIB)/$(LIBRARY) + chown $(DESTOWN) $(DESTLIB)/$(LIBRARY) + chgrp $(DESTGRP) $(DESTLIB)/$(LIBRARY) + cp $(LIBHDRS) $(DESTHDR) + cd $(DESTHDR) && chmod 644 $(LIBHDRS) + cd $(DESTHDR) && chown $(DESTOWN) $(LIBHDRS) + cd $(DESTHDR) && chgrp $(DESTGRP) $(LIBHDRS) + +uninstall: + rm -f $(DESTLIB)/$(LIBRARY) + cd $(DESTHDR) && rm -f $(LIBHDRS) + +clean: + -rm -f $(LIBRARY) $(OBJS) + +distclean: clean + -rm Makefile tags made + +### Do NOT edit this or the following lines. +bt_del.o: ty_dbd.h ty_type.h ty_prot.h ty_glob.h btree.h +bt_funcs.o: ty_dbd.h ty_type.h ty_prot.h ty_glob.h btree.h +bt_io.o: ty_dbd.h ty_type.h btree.h +bt_open.o: ty_dbd.h ty_type.h ty_prot.h ty_glob.h btree.h +cmpfuncs.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +readdbd.o: ty_dbd.h ty_type.h ty_glob.h +record.o: ty_dbd.h ty_type.h ty_prot.h ty_glob.h +ty_auxfn.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +ty_find.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +ty_ins.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +ty_io.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +ty_log.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h ty_log.h +ty_open.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +ty_refin.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +ty_repl.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h ty_repif.h catalog.h +ty_util.o: ty_dbd.h ty_type.h ty_glob.h ty_prot.h +unix.o: ty_dbd.h ty_type.h +vlr.o: ty_dbd.h ty_type.h ty_prot.h ty_glob.h +sequence.o: ty_dbd.h ty_type.h ty_prot.h ty_glob.h diff --git a/src/ansi.c b/src/ansi.c new file mode 100644 index 0000000..faf2067 --- /dev/null +++ b/src/ansi.c @@ -0,0 +1,100 @@ +/*---------------------------------------------------------------------------- + * File : ansi.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contiains functions specific to UNIX. + * + * Functions: + * strstr - ANSI C strstr + * memmove - ANSI C memmove + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#include "ansi.h" + +static CONFIG_CONST char rcsid[] = "$Id: ansi.c,v 1.3 1999/10/03 23:28:28 kaz Exp $"; + +#ifdef STRSTR_MISSING +#define NOT_EMPTY + +char *strstr(s1, s2) +char *s1, *s2; +{ + while( *s1 ) + { + if( *s2 == *s1 ) + { + char *ss1 = s1; + char *ss2 = s2; + + while( *ss2 && *ss1++ == *ss2++ ) + ; + + if( !*ss2 ) + return s1; + } + + s1++; + } + + return (char *)0; +} + +#endif + + + +#ifdef MEMMOVE_MISSING +#define NOT_EMPTY + +void memmove(dest, src, len) +char *dest, *src; +int len; +{ + if( dest < src ) + { + while( len-- ) + *dest++ = *src++; + } + else + { + dest += len; + src += len; + + while( len-- ) + *--dest = *--src; + } +} + +#endif + +#ifndef NOT_EMPTY +struct dummy; /* ANSI C forbids empty translation units */ +#endif + +/* end-of-file */ diff --git a/src/bt_del.c b/src/bt_del.c new file mode 100644 index 0000000..15b8501 --- /dev/null +++ b/src/bt_del.c @@ -0,0 +1,514 @@ +/*---------------------------------------------------------------------------- + * File : bt_del + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains function for removing a tuple from a B-tree index file. + * The algorithm is the one is described in "Data structures in Pascal", + * Horowitz & Sahni, Computer Science Press. + * + * Functions: + * delchain_insert - Add a node to the delete chain. + * merge_siblings - Merge two sibling nodes to a single node. + * move_parentkey - Move a parent key to another tuple. + * find_ref - Find tuple with correct reference. + * replace_with_leftmost_tuple- Copy the leftmost tuple in a subtree. + * btree_del - + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include "environ.h" +#ifndef CONFIG_UNIX +# include +# include +#else +# include +# ifdef __STDC__ +# include +# endif +#endif +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" +#include "ty_glob.h" +#include "btree.h" + +static CONFIG_CONST char rcsid[] = "$Id: bt_del.c,v 1.8 1999/10/04 03:45:07 kaz Exp $"; + +/*--------------------------- Function prototypes ---------------------------*/ +static void delchain_insert PRM( (INDEX *, ix_addr); ) +static void merge_siblings PRM( (INDEX *I, + ix_addr lsib, + ix_addr rsib, + ix_addr z, + int zi, + char *znode, + ix_addr *y, + char *ynode, + ix_addr *p, + int *i); ) + +static void move_parentkey PRM( (INDEX *I, + ix_addr rsib, + int zi, + ix_addr z, + char *znode, + ix_addr y, + char *ynode); ) + +static int find_ref PRM( (INDEX *I, + ulong ref, + ix_addr *addr, + int *idx, + void *key); ) + +static void replace_with_leftmost_tuple + PRM( (INDEX *I, + ix_addr *y, + char *ynode, + ix_addr *p, + int *i); ) + + +/*----------------------------- delchain_insert ----------------------------*\ + * + * Purpose : Inserts a deleted B-tree node in the delete chain. + * + * Parameters: I - B-tree index file descriptor. + * addr - Address of node to insert in delete chain. + * + * Returns : Nothing. + * + */ + +static void delchain_insert(I, addr) +INDEX *I; +ix_addr addr; +{ + lseek(I->fh, (off_t) ((ulong)I->H.nodesize * (ulong)addr), SEEK_SET); + write(I->fh, &I->H.first_deleted, sizeof I->H.first_deleted); + I->H.first_deleted = addr; +} + + + +/*--------------------------------------------------------------------------*\ + * + * Function : merge_siblings + * + * Purpose : Merge two siblings nodes. + * + * Parameters: I - Index handle. + * lsib - Address of left sibling node. + * rsib - Address of right sibling node. + * z - + * zi - + * znode - + * y - + * ynode - + * p - + * i - + * + * Returns : Nothing. + * + */ +static void merge_siblings(I, lsib, rsib, z, zi, znode, y, ynode, p, i) +INDEX *I; +ix_addr lsib, rsib, *y, z, *p; +int zi, *i; +char *znode; +char *ynode; +{ + if( rsib ) + { + /* copy parent key */ + keycopy(I->node, NSIZE(I->node), znode, zi); + + /* copy sibling keys */ + tuplecopy(I->node, NSIZE(I->node)+1, ynode, 0, (size_t) NSIZE(ynode)); + + CHILD(I->node,NSIZE(I->node)+1+NSIZE(ynode)) = CHILD(ynode,NSIZE(ynode)); + + delchain_insert(I, *p); + } + else + { + /* make room for left sibling */ + tupleins(I->node, 0, NSIZE(ynode)+1); + + /* copy parent key */ + keycopy(I->node, NSIZE(ynode), znode, zi); + + /* copy sibling keys */ + tuplecopy(I->node, 0, ynode, 0, (size_t) NSIZE(ynode)); + + CHILD(I->node,NSIZE(ynode)) = CHILD(ynode,NSIZE(ynode)); + *y = *p; + + delchain_insert(I, lsib); + } + + tupledel(znode, zi); /* remove parent key */ + NSIZE(znode)--; + NSIZE(I->node) += 1 + NSIZE(ynode); + + nodewrite(I, I->node, *y); + + /* create new root? */ + if( z == 1 && !NSIZE(znode) ) + { + *p = 1; + delchain_insert(I, *y); + } + else + { + /* process parent */ + nodecopy(I->node, znode); + *p = z; + *i = zi; + I->level--; + } +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : move_parentkey + * + * Purpose : + * + * Parameters: I - + * rsib - + * zi - + * z - + * znode - + * y - + * ynode - + * + * Returns : Nothing. + * + */ +static void move_parentkey(I, rsib, zi, z, znode, y, ynode) +INDEX *I; +ix_addr rsib, y, z; +int zi; +char *znode, *ynode; +{ + if( rsib ) + { + /* copy key from parent to p */ + keycopy(I->node, NSIZE(I->node), znode, zi); + + /* copy reference of sibling moved to parent */ + CHILD(I->node, NSIZE(I->node)+1) = CHILD(ynode,0); + + /* copy sibling key to parent */ + keycopy(znode, zi, ynode, 0); + tupledel(ynode, 0); + } + else + { + tupleins(I->node, 0, 1); + + /* copy key from parent */ + keycopy(I->node, 0, znode, zi); + + /* copy reference of sibling moved to parent */ + CHILD(I->node,0) = CHILD(ynode, NSIZE(ynode)); + + /* copy sibling key to parent */ + keycopy(znode, zi, ynode, NSIZE(ynode)-1); + } + + NSIZE(ynode)--; + NSIZE(I->node)++; + + nodewrite(I, ynode, y); /* update nodes */ + nodewrite(I, znode, z); +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : find_ref + * + * Purpose : Find a tuple with a specified reference. + * + * Parameters: I - INDEX handle. + * ref - Reference number. + * addr - Address of node to start search in. If the tuple + * is found, the address of the tuple is returned herein + * + * Returns : S_OKAY - Reference found. + * + */ + +#define Keys NSIZE(I->node) +#define Child(i) CHILD(I->node, (i)) +#define Key(i) KEY(I->node, (i)) +#define Ref(i) REF(I->node, (i)) +#define Pos (I->path[I->level].i) +#define Addr (I->path[I->level].a) +#define Level (I->level) + + + + +static int find_ref(I, ref, addr, idx, key) +INDEX *I; +ulong ref; +ix_addr *addr; +int *idx; +void *key; +{ + for( ;; ) + { + *idx = Pos; + *addr = Addr; + + if( (*I->cmpfunc)(key, Key(*idx)) ) + { + puts("key mismatch"); + break; + } + + if( Ref(*idx) == ref ) + return S_OKAY; + + if( Child(Pos) > 0 ) /* Non-leaf node */ + { + /* Get the leftmost child in the left subtree */ + Pos++; + get_leftmostchild(I, Child(Pos)); + } + else if( Pos >= Keys-1 ) /* Leaf node at first pos */ + { + if( Pos >= Keys-1 && Addr == 1 ) + { + I->curr = 0; + RETURN S_NOTFOUND; + } + + /* Move upward until a node with Posnode, Addr); + } + while( Pos >= Keys && Addr != 1 ); + + if( Pos == Keys && Addr == 1 ) + { + I->curr = 0; + RETURN S_NOTFOUND; + } + } + else /* Leaf node */ + Pos++; + } + + RETURN S_NOTFOUND; +} + +#undef Keys +#undef Child +#undef Key +#undef Ref +#undef Pos +#undef Addr +#undef Level + + + + +/*--------------------------------------------------------------------------*\ + * + * Function : replace_with_leftmost_tuple + * + * Purpose : Replaces the current tuple [p, I->node, i] with the leftmost + * key in the right subtree (of the current tuple). + * + * Parameters: I - Index handle. + * y - + * ynode - + * p - Address of I->node. + * i - Index of I->node. + * + * Returns : + * + */ +static void replace_with_leftmost_tuple(I, y, ynode, p, i) +INDEX *I; +ix_addr *y; +ix_addr *p; +char *ynode; +int *i; +{ + I->path[I->level].i++; + + *y = noderead(I, ynode, CHILD(I->node, *i+1)); + + I->path[++I->level].a = CHILD(I->node, *i+1); + I->path[ I->level].i = 0; + + while( CHILD(ynode,0) ) + { + *y = noderead(I, ynode, CHILD(ynode, 0)); + + I->path[++I->level].a = *y; + I->path[ I->level].i = 0; + } + + keycopy(I->node, *i, ynode, 0); /* copy leftmost key to p,i */ + + nodewrite(I, I->node, *p); /* update node p */ + nodecopy(I->node, ynode); + + *p = *y; + *i = 0; +} + + + + +/*-------------------------------- btree_del -------------------------------*\ + * + * Purpose : Deletes a key in a B-tree. If the deletion causes underflow in + * a node, two nodes are merged and the B-tree possibly shrunk. + * + * Parameters: I - B-tree index file descriptor. + * key - Key value to delete. + * ref - Reference of key value. Only used if the B-tree + * contains duplicates. + * + * Returns : S_OKAY - Key value successfully deleted. + * S_NOTFOUND - The key was not in the B-tree. + * + */ +int btree_del(I, key, ref) +INDEX *I; +void *key; +ulong ref; +{ + ix_addr p, y, z, lsib, rsib; + int i, zi, rc; + char *ynode, *znode; + + I->curr = 0; + I->hold = 0; + + btree_getheader(I); + + if( !d_search(I, key, &p, &i) ) + RETURN S_NOTFOUND; + + if( I->H.dups ) + if( (rc = find_ref(I, ref, &p, &i, key)) != S_OKAY ) + return rc; + + /* Allocate temporaty node buffers */ + if( !(ynode = (char *)malloc((size_t) (I->H.nodesize + I->tsize))) ) + RETURN S_NOMEM; + if( !(znode = (char *)malloc((size_t) (I->H.nodesize + I->tsize))) ) + { + free(ynode); + RETURN S_NOMEM; + } + + /* if node is a nonleaf, replace key with leftmost key in right subtree */ + if( CHILD(I->node, 0) ) + replace_with_leftmost_tuple(I, &y, ynode, &p, &i); + + tupledel(I->node, i); /* remove key from leaf */ + NSIZE(I->node)--; /* decrease node size by 1 */ + + /* run loop as long there is underflow in p and p is not root */ + while( NSIZE(I->node) < (ulong)I->H.order/2 && p != 1 ) + { + z = I->path[I->level-1].a; /* set z = parent */ + zi = I->path[I->level-1].i; /* set zi = parent i */ + + noderead(I,znode,z); /* read parent node from disk */ + + lsib = zi ? CHILD(znode, zi-1) : 0; + rsib = zi < NSIZE(znode) ? CHILD(znode, zi+1) : 0; + + y = rsib ? rsib : lsib; + + noderead(I, ynode, y); + + if( !rsib ) + zi--; + + if( NSIZE(ynode) > (ulong)I->H.order/2 ) + { + /* move parent key to p, move nearest key in sibling to p */ + move_parentkey(I, rsib, zi, z, znode, y, ynode); + + goto out; + } + else + { + /* there is underflow in leaf p - merge with a sibling */ + merge_siblings(I, lsib, rsib, z, zi, znode, &y, ynode, &p, &i); + } + } + + I->H.keys--; + +out: + + if( !NSIZE(I->node) ) /* if index is empty, truncate */ + { + I->H.first_deleted = 0; + I->H.keys = 0; +#if defined(CONFIG_DOS) || defined(CONFIG_OS2) + chsize(I->fh, 0); +#else +#if defined(CONFIG_SCO) || defined(CONFIG_NEED_FTRUNCATE) + os_close(os_open(I->fname, O_RDWR|O_TRUNC, CREATMASK)); +#else + ftruncate(I->fh, I->H.nodesize); +#endif +#endif + } + else + nodewrite(I, I->node, p); /* else update node p */ + + I->H.timestamp++; + btree_putheader(I); + + free(znode); + free(ynode); + + RETURN S_OKAY; +} + +/* end-of-file */ diff --git a/src/bt_funcs.c b/src/bt_funcs.c new file mode 100644 index 0000000..cf5a671 --- /dev/null +++ b/src/bt_funcs.c @@ -0,0 +1,603 @@ +/*---------------------------------------------------------------------------- + * File : bt_funcs + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * + * Functions: + * btree_add - Insert a new key in a B-tree. + * btree_find - Find a key in a B-tree. + * btree_exist - See if a key exists in a B-tree. + * btree_read - Read the last key found. + * btree_delall - Delete all keys in a B-tree. + * get_rightmostchild - Find the rightmost child in a subtree. + * get_leftmostchild - Find the leftmost child in a subtree. + * btree_frst - Find the key with the lowest key value in a B-tree. + * btree_last - Find the key with the highest key value in a B-tree. + * btree_prev - Find the previous key in a B-tree. + * btree_next - Find the next key in a B-btree. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "environ.h" +#ifdef CONFIG_UNIX +# include +#else +# include +#endif +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" +#include "ty_glob.h" +#include "btree.h" + +static CONFIG_CONST char rcsid[] = "$Id: bt_funcs.c,v 1.7 1999/10/04 03:45:07 kaz Exp $"; + +/*--------------------------- Function prototypes --------------------------*/ +static void synchronize PRM( (INDEX *); ) + + +/*------------------------------- btree_add --------------------------------*\ + * + * Purpose : Inserts the key in a B-tree index file. + * + * Parameters: I - B-tree index file descriptor. + * key - Key value to insert. + * ref - Reference to insert together with key. + * + * Returns : S_OKAY - Key value successfully inserted in B-tree. + * S_DUPLICATE - The key value is already in the B-tree and + * duplicates are not allowed. + * + */ + +int btree_add(I, key, ref) +INDEX *I; +void *key; +ulong ref; +{ + ulong Ref; + ix_addr Addr, moved, p; + int i, mid; + + I->curr = 0; + I->hold = 0; + + btree_getheader(I); + + if( d_search(I, key, &p, &i) ) + { + if( I->H.dups ) + { + /* When a duplicate key is already in the tree, the current node + * at this point may not be a leaf node. However, insertions + * must always be performed in leaf nodes, so we must find the + * rightmost entry in the left subtree + */ + + if( CHILD(I->node, i) ) + { + get_rightmostchild(I, CHILD(I->node, i)); + i = I->path[I->level].i; + p = I->path[I->level].a; + } + } + else + RETURN S_DUPLICATE; + } + + I->H.keys++; + Addr = 0; + Ref = ref; + memcpy(I->curkey, key, I->H.keysize); + + do + { + tupleins(I->node, i, 1); + memcpy(KEY(I->node,i), I->curkey, I->H.keysize); + CHILD(I->node,i+1) = Addr; + REF(I->node,i) = Ref; + + if( NSIZE(I->node) < (ulong)I->H.order ) + { + NSIZE(I->node)++; + nodewrite(I, I->node, p); + btree_putheader(I); + RETURN S_OKAY; + } + + /* split node */ + mid = I->H.order / 2; + + /* write left part of node at its old position */ + NSIZE(I->node) = mid; + nodewrite(I, I->node, p); + + /* Save mid K, A and R */ + memcpy(I->curkey, KEY(I->node,mid), I->H.keysize); + Addr = CHILD(I->node, mid); + Ref = REF(I->node, mid); + + /* write right part of node at new position */ + NSIZE(I->node) = I->H.order - mid; + memmove(&CHILD(I->node,0), &CHILD(I->node,mid+1), NSIZE(I->node) * I->tsize + sizeof(A_type)); + Addr = nodewrite(I, I->node, NEWPOS); + + if( (p = I->path[--I->level].a) != 0 ) + { + noderead(I, I->node, p); /* p = I->path[p] */ +/* nodesearch(I,I->curkey,&i);*/ + i = I->path[I->level].i; + } + } + while( p ); + + /* Create a new root */ + noderead(I, I->node, 1); + moved = nodewrite(I, I->node, NEWPOS); + + memcpy(KEY(I->node,0), I->curkey, I->H.keysize); + CHILD(I->node,0) = moved; + CHILD(I->node,1) = Addr; + REF(I->node,0) = Ref; + NSIZE(I->node) = 1; + nodewrite(I, I->node, 1); + I->H.timestamp++; + btree_putheader(I); + + RETURN S_OKAY; +} + + +/*------------------------------- btree_find -------------------------------*\ + * + * Purpose : Searches for the key value in a B-tree index file. + * + * Parameters: I - B-tree index file descriptor. + * key - Key value to find. + * ref - Contains reference when function returns. + * + * Returns : S_OKAY - The key value was found. contains + * reference. + * S_NOTFOUND - The key value was not found. + * + */ +int btree_find(I, key, ref) +INDEX *I; +void *key; +ulong *ref; +{ + ix_addr dummy; + int i; + + btree_getheader(I); /* Inserted temporarily */ + + if( !d_search(I, key, &dummy, &i) ) + { + /* Do only set hold if there are actually keys in the index */ + I->hold = I->H.keys > 0 ? 1 : 0; + I->curr = 0; + RETURN S_NOTFOUND; + } + + *ref = REF(I->node, i); + memcpy(I->curkey, KEY(I->node, I->path[I->level].i), I->H.keysize); + I->hold = 0; + I->curr = 1; + RETURN S_OKAY; +} + + +/*------------------------------- btree_keyread -------------------------------*\ + * + * Purpose : Copies the contents of the current key value to . + * + * Parameters: I - B-tree index file descriptor. + * buf - Buffer to copy current key value to. + * + * Returns : S_NOCR - There is no current key. + * S_OKAY - Key value copied to . + * + */ +int btree_keyread(I, buf) +INDEX *I; +void *buf; +{ + if( !I->curr ) + RETURN S_NOCR; + + memcpy(buf, I->curkey, I->H.keysize); + RETURN S_OKAY; +} + + +/*------------------------------ btree_delall ------------------------------*\ + * + * Purpose : Deletes all key values in a B-tree index file. + * + * Parameters: I - B-tree index file descriptor. + * + * Returns : S_OKAY - All keys deleted. + * + */ +int btree_delall(I) +INDEX *I; +{ + btree_getheader(I); + I->H.first_deleted = 0; + I->H.keys = 0; +#ifdef CONFIG_UNIX + os_close(open(I->fname, O_TRUNC)); +#else + chsize(I->fh, I->H.nodesize); +#endif + I->curr = 0; + I->hold = 0; + btree_putheader(I); + + RETURN S_OKAY; +} + + +/* + * The following macros are used to enhance the readability of the rest of the + * functions in this file. B-tree traversal can be rather tricky, with lots + * of pitfalls in it, but these macros should help a great deal. Here is a + * short explanation of the macros. + * + * Keys The number of keys in the current node. + * Child(i) The address of the i'th child in the current node. + * Key(i) Pointer to the i'th key in the current node. + * Ref(i) The reference of the i'th key in the current node. + * Pos The current position within the current node. + * Addr The current node address at the current level in the tree. + * Level The current level in the tree. + * + */ + +#define Keys NSIZE(I->node) +#define Child(i) CHILD(I->node, i) +#define Key(i) KEY(I->node, i) +#define Ref(i) REF(I->node, i) +#define Pos (I->path[I->level].i) +#define Addr (I->path[I->level].a) +#define Level (I->level) + + +/*--------------------------- get_rightmostchild ---------------------------*\ + * + * Purpose : Reads the rightmost key in a subtree with root address . + * + * Parameters: I - B-tree index file descriptor. + * addr - The root address of the subtree. + * + * Returns : Nothing. + * + */ + +void get_rightmostchild(I, addr) +INDEX *I; +ulong addr; +{ + /* No tree has root at address 0 */ + if( !addr ) + return; + + do + { + noderead(I, I->node, addr); + + Level++; + Addr = addr; + Pos = Keys; + } + while( (addr = Child(Keys)) ); +} + + +/*--------------------------- get_leftmostchild ----------------------------*\ + * + * Purpose : Reads the leftmost key in a subtree with root address . + * + * Parameters: I - B-tree index file descriptor. + * addr - The root address of the subtree. + * + * Returns : Nothing. + * + */ + +void get_leftmostchild(I, addr) +INDEX *I; +ulong addr; +{ + /* No tree has root at address 0 */ + if( !addr ) + return; + + do + { + noderead(I, I->node, addr); + + Level++; + Addr = addr; + Pos = 0; + } + while( (addr = Child(0)) ); +} + + +/*------------------------------- btree_frst -------------------------------*\ + * + * Purpose : Read the smallest key value in a B-tree, i.e. the leftmost key. + * + * Parameters: I - B-tree index file descriptor. + * ref - Contains reference when function returns. + * + * Returns : S_OKAY - The key was found. contains reference. + * S_NOTFOUND - The B-tree is empty. + * + */ +int btree_frst(I, ref) +INDEX *I; +ulong *ref; +{ + I->curr = 0; + I->hold = 0; + Level = 1; + Addr = 1; + Pos = 0; + + /* Get the nost recent sequence number */ + btree_getheader(I); + + if( noderead(I, I->node, 1) == (ix_addr)-1 ) + RETURN S_NOTFOUND; + + get_leftmostchild(I, Child(0)); + + I->curr = 1; + *ref = Ref(Pos); + memcpy(I->curkey, Key(Pos), I->H.keysize); + + RETURN S_OKAY; +} + + +/*------------------------------- btree_last -------------------------------*\ + * + * Purpose : Read the greatest key value in a B-tree, i.e. the rightmost key. + * + * Parameters: I - B-tree index file descriptor. + * ref - Contains reference when function returns. + * + * Returns : S_OKAY - The key was found. contains reference. + * S_NOTFOUND - The B-tree is empty. + * + */ +int btree_last(I, ref) +INDEX *I; +ulong *ref; +{ + I->curr = 0; + I->hold = 0; + Level = 1; + Addr = 1; + + /* Get the nost recent sequence number */ + btree_getheader(I); + + if( noderead(I, I->node, 1) == (ix_addr)-1 ) + RETURN S_NOTFOUND; + + Pos = Keys; + get_rightmostchild(I, Child(Keys)); + + /* Move pos one step leftwards so that Pos is the current key */ + Pos--; + I->curr = 1; + *ref = Ref(Pos); + memcpy(I->curkey, Key(Pos), I->H.keysize); + + RETURN S_OKAY; +} + + +static void synchronize(I) +INDEX *I; +{ + ulong old_ts = I->H.timestamp; + ulong ref; + + btree_getheader(I); + + if( old_ts != I->H.timestamp ) + btree_find(I, I->curkey, &ref); +} + + + + +/*------------------------------- btree_prev -------------------------------*\ + * + * Purpose : Find key value in a B-tree with less or equal (if duplicates) + * key value that the current key. If there is no current key, + * the function is equivalent to btree_last(). If the current + * position before the call is the leftmost position in the tree, + * S_NOTFOUND is returned. + * + * Parameters: I - B-tree index file descriptor. + * ref - Contains reference if a key is found. + * + * Returns : S_OKAY - Key value found. contains reference. + * S_NOTFOUND - The key with the smallest value has already + * been reached. + * + */ + +int btree_prev(I,ref) +INDEX *I; +ulong *ref; +{ + if( I->shared ) + synchronize(I); + + if( I->hold ) + goto out; + + if( !I->curr ) + return btree_last(I, ref); + + if( Child(Pos) > 0 ) /* Non-leaf node */ + { + /* Get the rightmost child in the left subtree */ + get_rightmostchild(I, Child(Pos)); + } + else if( Pos == 0 ) /* Leaf node at first pos */ + { + /* Move upwards until a node with Pos > 0 or root is reached */ + while( Pos == 0 && Addr != 1 ) + { + Level--; + noderead(I, I->node, Addr); + } + + if( Pos == 0 && Addr == 1 ) + { + I->curr = 0; + RETURN S_NOTFOUND; + } + } + Pos--; + +out: + I->curr = 1; + I->hold = 0; + + *ref = Ref(Pos); + memcpy(I->curkey, Key(Pos), I->H.keysize); + + RETURN S_OKAY; +} + + +/*------------------------------- btree_next -------------------------------*\ + * + * Purpose : Find key value in a B-tree with greater or equal (if duplicates) + * key value that the current key. If there is no current key, + * the function is equivalent to btree_frst(). If the current + * position before the call is the rightmost position in the tree, + * S_NOTFOUND is returned. + * + * Parameters: I - B-tree index file descriptor. + * ref - Contains reference if a key is found. + * + * Returns : S_OKAY - Key value found. contains reference. + * S_NOTFOUND - The key with the greatest value has already + * been reached. + * + */ +int btree_next(I,ref) +INDEX *I; +ulong *ref; +{ + if( I->shared ) + synchronize(I); + + if( I->hold ) + { + /* An unsuccessful keyfind must be handled as a special case: + * If we are at the rightmost position in a node, move upwards until + * we reach a node where the position is not the rightmost. This + * key should be the proper next key in the B-tree. + */ + + while( Pos == Keys && Level > 1 ) + { + Level--; + noderead(I, I->node, Addr); + } + + if( Level == 1 && Pos == Keys ) + RETURN S_NOTFOUND; + + goto out; + } + + if( !I->curr ) + return btree_frst(I, ref); + + if( Child(Pos) > 0 ) /* Non-leaf node */ + { + /* Get the leftmost child in the left subtree */ + Pos++; + get_leftmostchild(I, Child(Pos)); + } + else if( Pos >= Keys-1 ) /* Leaf node at first pos */ + { +#if 0 + if( Pos == Keys-1 && Addr == 1 ) 94/03/17 TBP +#else + if( Pos >= Keys-1 && Addr == 1 ) +#endif + { + I->curr = 0; + RETURN S_NOTFOUND; + } + + /* Move upwards until a node with Pos < Keys-1 or root is reached */ + do + { + Level--; + noderead(I, I->node, Addr); + } + while( Pos >= Keys && Addr != 1 ); + + if( Pos == Keys && Addr == 1 ) + { + I->curr = 0; + RETURN S_NOTFOUND; + } + } + else /* Leaf node */ + Pos++; + +out: + I->curr = 1; + I->hold = 0; + + *ref = Ref(Pos); + memcpy(I->curkey, Key(Pos), I->H.keysize); + + RETURN S_OKAY; +} + +/* end-of-file */ + diff --git a/src/bt_io.c b/src/bt_io.c new file mode 100644 index 0000000..4cd40cb --- /dev/null +++ b/src/bt_io.c @@ -0,0 +1,87 @@ +/*---------------------------------------------------------------------------- + * File : bt_io.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains functions for opening and closing B-tree index files. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include "environ.h" +#ifndef CONFIG_UNIX +# include +#else +# include +#endif +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "btree.h" + +static CONFIG_CONST char rcsid[] = "$Id: bt_io.c,v 1.5 1999/10/03 23:28:28 kaz Exp $"; + +ix_addr noderead(I, node, page) +INDEX *I; +char *node; +ix_addr page; +{ + lseek(I->fh, (long)page * I->H.nodesize, SEEK_SET); + if( read(I->fh, node, I->H.nodesize) < I->H.nodesize ) + return (ix_addr)-1; + + return page; +} + + +ix_addr nodewrite(I, node, page) +INDEX *I; +char *node; +ix_addr page; +{ + if( page == NEWPOS ) + { + if( I->H.first_deleted ) + { + page = I->H.first_deleted; + lseek(I->fh, (off_t) ((long)I->H.nodesize * page), SEEK_SET); + read(I->fh, &I->H.first_deleted, sizeof I->H.first_deleted); + } + else + page = (lseek(I->fh, 0L, SEEK_END) / I->H.nodesize); + } + + lseek(I->fh, (long)page * I->H.nodesize, SEEK_SET); + write(I->fh, node, I->H.nodesize); + + return page; +} + +/* end-of-file */ diff --git a/src/bt_open.c b/src/bt_open.c new file mode 100644 index 0000000..6cc8519 --- /dev/null +++ b/src/bt_open.c @@ -0,0 +1,467 @@ +/*---------------------------------------------------------------------------- + * File : bt_open.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains functions for opening and closing B-tree index files. + * + * Functions: + * db_keygetheader - Read B-tree inde xfile header. + * db_keyputheader - Write B-tree index file header. + * d_keyopen - Open a B-tree index file. + * d_keyclose - Close a B-tree index file. + * nodesearch - Perform binary search in the node. + * d_search - Find a key. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "environ.h" +#ifdef CONFIG_UNIX +# include +# ifdef __STDC__ +# include +# endif +#else +# include +# include +# include +#endif +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" +#include "ty_glob.h" +#include "btree.h" + +static CONFIG_CONST char rcsid[] = "$Id: bt_open.c,v 1.8 1999/10/04 03:45:07 kaz Exp $"; + +int find_firstoccurrence PRM( (INDEX *, void *, ix_addr *, int *); ) + +/*----------------------------- btree_getheader ----------------------------*\ + * + * Purpose : Reads the header of a B-tree index file. + * + * Parameters: I - Pointer to index file descriptor. + * + * Returns : Nothing. + * + */ + +void btree_getheader(I) +INDEX *I; +{ + lseek(I->fh, 0L, SEEK_SET); + read(I->fh, &I->H, sizeof I->H); +} + + +/*----------------------------- btree_getheader ----------------------------*\ + * + * Purpose : Writes the header of a B-tree index file. + * + * Parameters: I - Pointer to index file descriptor. + * + * Returns : Nothing. + * + */ + +void btree_putheader(I) +INDEX *I; +{ + lseek(I->fh, 0L, SEEK_SET); + write(I->fh, &I->H, sizeof I->H); +} + + +/*-------------------------------- btree_open -------------------------------*\ + * + * Purpose : Opens a B-tree index file with the name . If the file + * does not already exist, the file is created. If the version of + * the existing file does not match the version of the B-tree + * library db_status is set to S_VERSION, and NULL is returned. + * + * Parameters: fname - File name. + * keysize - Key size. + * nodesize - Node size. Multiples of 512 are recommended. + * cmpfunc - Comparison function. This function must take two + * parameters, i.e. like strcmp(). + * dups - True if duplicates are allowed. + * shared - Open the index file in shared mode?. + * + * Returns : If the file was successfully opened, a pointer to a B-tree + * index file descriptor is returned, otherwise NULL is returned + * and db_status is set to one of following: + * + * S_NOMEM - Out of memory. + * S_IOFATAL - File could not be opened. + * S_VERSION - B-tree file on disk has wrong version. + * S_UNAVAIL - The file is already opened in non-shared mode. + * + */ + +INDEX *btree_open(fname, keysize, nodesize, cmpfunc, dups, shared) +char *fname; +int keysize, dups, nodesize, shared; +CMPFUNC cmpfunc; +{ + INDEX *I; + int tuplesize, isnew, fh; + int aligned_keysize; + + /* See if file exists and then open it */ + isnew = access(fname, 0); + if( (fh=os_open(fname, CONFIG_O_BINARY|O_CREAT|O_RDWR,CONFIG_CREATMASK)) == -1 ) + { + db_status = S_IOFATAL; + return NULL; + } + + /* Lock the file if it is not opened in shared mode */ + if( !shared ) + if( os_lock(fh, 0L, 1, 't') == -1 ) + { + db_status = S_NOTAVAIL; + return NULL; + } + + /* Ensure that the size of a tuple is multiple of four */ + aligned_keysize = keysize; +#ifdef CONFIG_RISC + if( aligned_keysize & (sizeof(long)-1) ) + aligned_keysize += sizeof(long) - (aligned_keysize & (sizeof(long)-1)); +#endif + + /* calculate memory requirements */ + tuplesize = sizeof(A_type) + sizeof(R_type) + aligned_keysize; + + /* allocate memory for INDEX structure */ + if( (I = (INDEX *)calloc(sizeof(*I) + nodesize + tuplesize,1)) == NULL ) + { + os_close(fh); + db_status = S_NOMEM; + return NULL; + } + + /* allocate memory for current key */ + if( (I->curkey = (char *)malloc((size_t) keysize)) == NULL ) + { + os_close(fh); + free(I); + db_status = S_NOMEM; + return NULL; + } + + I->fh = fh; + + if( isnew ) + { + I->H.version = KEYVERSION_NUM; + I->H.first_deleted = 0; + I->H.order = ((nodesize-sizeof(N_type)-sizeof(A_type)) / tuplesize) & 0xfffe; + I->H.keysize = keysize; + I->H.dups = dups; + I->H.nodesize = nodesize; + I->H.keys = 0; + strcpy(I->H.id, KEYVERSION_ID); + memset(I->H.spare, 0, sizeof I->H.spare); + btree_putheader(I); + } + else + { + btree_getheader(I); + + if( I->H.version != KEYVERSION_NUM ) + { + db_status = S_VERSION; + os_close(fh); + free(I->curkey); + free(I); + return NULL; + } + } + + I->cmpfunc = cmpfunc; + I->tsize = tuplesize; + I->hold = 0; + I->shared = shared; + I->aligned_keysize = aligned_keysize; + strcpy(I->fname, fname); + + db_status = S_OKAY; + + return I; +} + + +/*-------------------------------- btree_close ------------------------------*\ + * + * Purpose : Closes a B-tree index file previously opened with d_keyopen(). + * All nodes that might be in the cache are written to disk. + * + * Parameters: I - Index file descriptor. + * + * Returns : Nothing. + * + */ +void btree_close(I) +INDEX *I; +{ + if( I->fh != -1 ) + os_close(I->fh); + + free(I->curkey); + free(I); +} + + +int btree_dynclose(I) +INDEX *I; +{ + if( I->fh != -1 ) + { + close(I->fh); + I->fh = -1; + } + + RETURN S_OKAY; +} + + +int btree_dynopen(I) +INDEX *I; +{ + if( I->fh == -1 ) + if( (I->fh=os_open(I->fname, CONFIG_O_BINARY|O_CREAT|O_RDWR, CONFIG_CREATMASK)) == -1 ) + RETURN S_IOFATAL; + + RETURN S_OKAY; +} + + +/*------------------------------- nodesearch -------------------------------*\ + * + * Purpose : Performs a binary search for the key value pointed to by + * in the node in I. When the function returned contains the + * entry in the node where the searched stopped. If the key was + * found REF(i) contains the reference to be returned by the + * calling, otherwise CHILD(i) contains the node address of the + * child to be processed next. + * + * Parameters: I - B-tree index file descriptor. + * key - Key value being searched for. + * i - Contains node index when function returns. + * + * Returns : 0 - The key was found. + * not 0 - The key was not found. + * + */ +int nodesearch(I, key, i) +INDEX *I; +void *key; +int *i; +{ + int cmp, mid, upr, lwr; + + upr = NSIZE(I->node) - 1; + lwr = 0; + + /* Perform binary search in node */ + while( lwr <= upr ) + { + mid = (lwr + upr) >> 1; + cmp = (*I->cmpfunc)(key, KEY(I->node, mid)); + + if( cmp > 0 ) + lwr = mid + 1; + else if( cmp < 0 ) + upr = mid - 1; + else + { + if( I->H.dups ) + { + /* Find the leftmost occurrence */ + while( mid > 0 ) + { + mid--; + if( (cmp = (*I->cmpfunc)(key, KEY(I->node, mid))) ) + break; + } + if( cmp ) + mid++; + + *i = mid; + return 0; + } + break; + } + } + + /* If the comparison yielded greater than, move a step to the right */ + if( cmp > 0 ) + mid++; + + *i = mid; + return cmp; +} + + +/*-------------------------------- d_search --------------------------------*\ + * + * Purpose : Searches a B-tree for the key value pointed to by . If + * the key is found the reference is in REF(i). The path from the + * root to the last access node is stored in I->path[], which + * enables key traversal from the current point in the tree. + * + * The search starts at the root, which is always at address 1. For + * each node nodesearch is called to perform a binary search in the + * node. If the key is not found, the search ends in a leaf node. + * + * If duplicates are allowed, the first occurrence of the key + * value must be found. + * + * Parameters: I - B-tree index file descriptor. + * key - Key value being searched for. + * addr - Contains node address when function returns. + * i - Contains node index when function returns. + * + * Returns : 0 - The key was found. + * 1 - The key was not found. + * + */ +int d_search(I, key, addr, i) +INDEX *I; +void *key; +ix_addr *addr; +int *i; +{ + int cmp; + + /* Start the search at the root */ + *addr = 1; + *i = 0; + I->level = 0; + + + for( ;; ) + { + /* Save the addresses and indexes of the traversed nodes */ + I->path[++I->level].a = *addr; + + if( noderead(I, I->node, *addr) == (ix_addr)-1 ) + { + /* The node could not be read - zero the number of keys */ + memset(I->node, 0, I->H.nodesize); + return 0; + } + + cmp = nodesearch(I,key,i); + + I->path[I->level].i = *i; + + if( !cmp ) + { + if( I->H.dups ) + return find_firstoccurrence(I, key, addr, i); + return 1; + } + + if( CHILD(I->node, *i) ) + *addr = CHILD(I->node, *i); + else + return 0; + } +} + + +/*-------------------------- find_firstoccurrence --------------------------*\ + * + * Purpose : This function is called in order to find the first occurrence + * of a duplicate key in a non-unique index. + * + * Parameters: I - B-tree index file descriptor. + * key - Key value being searched for. + * addr - Contains node address when function returns. + * i - Contains node index when function returns. + * + * Returns : 0 - The key was found. + * 1 - The key was not found. + * + */ +int find_firstoccurrence(I, key, addr, i) +INDEX *I; +void *key; +ix_addr *addr; +int *i; +{ + int first_occurlevel = I->level; + int cmp = 0; + + while( CHILD(I->node, 0) ) + { + I->level++; + I->path[I->level].a = CHILD(I->node, *i); + I->path[I->level].i = *i; + + noderead(I, I->node, I->path[I->level].a); + + cmp = nodesearch(I,key,i); + + I->path[I->level].i = *i; + + /* If the key was found in the current node we search the left + * subtree of the key. Otherwise, we search the right subtree of + * the rightmost key. + */ + + if( !cmp ) + first_occurlevel = I->level; + else + *i = NSIZE(I->node); + } + + if( cmp ) + { + I->level = first_occurlevel; + *i = I->path[I->level].i; + *addr = I->path[I->level].a; + + noderead(I, I->node, I->path[I->level].a); + } + else + { + *i = I->path[I->level].i; + *addr = I->path[I->level].a; + } + + return 1; +} + +/* end-of-file */ + diff --git a/src/btree.h b/src/btree.h new file mode 100644 index 0000000..1ff62f4 --- /dev/null +++ b/src/btree.h @@ -0,0 +1,118 @@ +/*---------------------------------------------------------------------------- + * File : btree.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains miscellaneous constants and macros used by B-tree functions. + * + * $Id: btree.h,v 1.5 1999/10/04 05:29:45 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_BTREE_H +#define TYPHOON_BTREE_H + +#ifndef TYPHOON_TY_TYPE_H +#include "ty_type.h" +#endif + +/*--------------------------------------------------------------------------*/ +/* miscellaneous constants */ +/*--------------------------------------------------------------------------*/ +#define KEYVERSION_ID "KeyMan121" /* Version ID */ +#define KEYVERSION_NUM 121 /* Version number */ + +#define NEWPOS (ix_addr)-1 /* Indicates new pos for nodewrite */ +#define ROOT 1 /* Root is always node 1 */ + +typedef ix_addr A_type; /* node address type */ +typedef long R_type; /* record reference type */ +#ifdef CONFIG_RISC +typedef long N_type; +#else +typedef short N_type; +#endif + + +/* + * The format of a node is illustrated below. is the number of tuples in + * the node. We call the set [A,K,R] a tuple, since the reference is considered + * a part of the key. + * + * +---+----+--------+----+----+--------+----+-- - -+------+---------+------+ + * | n ! A0 | K0 | R0 | A1 | K1 | R1 | | An-1 | Kn-1 | Rn-1 | + * +---+----+--------+----+----+--------+----+-- - -+------+---------+------+ + * + */ + +/* + * The following macros are used to easily access the elements of a node. The + * macros KEY, CHILD and REF assume that a variable node> points to the + * node operated on. + */ + +#define NSIZE(N) (*(N_type *)(N)) +#define KEY(N,i) (void *) (N+sizeof(N_type)+sizeof(A_type) + \ + I->tsize * (i)) +#define CHILD(N,i) (*(A_type *)(N+sizeof(N_type) + I->tsize * (i))) +#define REF(N,i) (*(R_type *) (N+sizeof(N_type)+sizeof(A_type) + \ + I->aligned_keysize + I->tsize * (i))) + + + +/* + * The following macros are used to insert, delete and copy tuples in nodes. + * + * tupledel(N,i) - Delete the i'th tuple of the node N. + * tupleins(N,i,n) - Insert the tuple n in the i'th position in the + * node N. + * tuplecopy(N1,i1,N2,i2,n) - Copy n tuples starting from the i2'th position + * of node N2 to the i1'th position of the node N1. + * nodecopy(N1,N2) - Copy node N2 to node N1. + * keycopy(N1,i1,N2,i2) - Copy the i2'th key of node N2 to the i1'th key + * of N1. + */ + +#define tupledel(N,i) memmove(&CHILD(N,i), &CHILD(N,(i)+1), \ + I->tsize * (NSIZE(N) - (i) - 1) + sizeof(A_type)) +#define tupleins(N,i,n) memmove(&CHILD(N,(i)+n), &CHILD(N,i), \ + I->tsize * (NSIZE(N) - (i)) + sizeof(A_type)) +#define tuplecopy(N1,i1,N2,i2,n)memcpy(&CHILD(N1,i1), &CHILD(N2,i2), I->tsize*(n)) +#define nodecopy(N1,N2) memcpy(N1, N2, sizeof(N_type) + sizeof(A_type) \ + + (I->tsize * NSIZE(N2))) +#define keycopy(N1,i1,N2,i2) memcpy(KEY(N1,i1),KEY(N2,i2),I->aligned_keysize + sizeof(R_type)) + + +/*--------------------------------- bt_open --------------------------------*/ +int nodesearch PRM( (INDEX *, void *, int *); ) +int d_search PRM( (INDEX *, void *, ix_addr *, int *); ) + +/*--------------------------------- bt_io.c --------------------------------*/ +ix_addr noderead PRM( (INDEX *, char *, ix_addr); ) +ix_addr nodewrite PRM( (INDEX *, char *, ix_addr); ) + +#endif +/* end-of-file */ diff --git a/src/catalog.ddl b/src/catalog.ddl new file mode 100644 index 0000000..4405df9 --- /dev/null +++ b/src/catalog.ddl @@ -0,0 +1,123 @@ +/*---------------------------------------------------------------------------- + * File : catalog.ddl + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Typhoon Database Definition Language file for System Catalog. + * + * $Id: catalog.ddl,v 1.1.1.1 1999/09/30 04:45:51 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +database catalog { + + data file "dissite.dat" contains sys_dissite; + key file "dissite.ix1" contains sys_dissite.id; + data file "distable.dat" contains sys_distab; + key file "distable.ix1" contains sys_distab.sys_distab_key; + data file "dischg.dat" contains sys_dischange; + key file "dischg.ix1" contains sys_dischange.sys_dischange_key; + data file "disprkey.dat" contains sys_disprkey; + + data file "disfultb.dat" contains sys_disfulltab; + key file "disfultb.ix1" contains sys_disfulltab.fulltab_key; + + define SITES_MAX 296 + define SITEBITMAP_SIZE ((SITES_MAX+7)/8) + define SITENAME_LEN 20 + define NUA_LEN 16 + define UDATA_LEN 16 + define KEYSIZE_MAX 255 + + record sys_dissite { + ulong id; // Site ID + long last_call; // Time of last call. + ushort tables; // Number of distributed tables on + // this site + char name[SITENAME_LEN+1]; // Mnemonic + char nua[NUA_LEN+1]; // X.25 address + char udata_len; + char udata[UDATA_LEN]; // X.25 Call User Data + char up_to_date; // 1=Site is completely up-to-date + char in_error; // 1=Site is malfunctioning + char spare[99]; + + primary key id; + } + + record sys_distab { + long id; // Table ID + long site_id; // Site ID + long last_seqno; // Last seqno sent + char tag; // Record identification tag + char up_to_date; // 1=Table is up-to-date on site + char spare[10]; + + primary key sys_distab_key { site_id, id }; + } + + record sys_dischange { + ulong seqno; // Sequence number + long table_id; // Table ID + long recno; // action='u': address of record + // action='d': address of primary key + // in sys_disprkey + ushort sites; // Number of 1-bits in site[] + uchar prog_id; // Program ID + char action; // 'u'=update, 'd'=delete + // Bitmap of unupdated sites, indexes + // by Site ID. 0=up-to-date + uchar site[SITEBITMAP_SIZE]; + + primary key sys_dischange_key { table_id, recno }; + } + + record sys_disprkey { + ushort size; + uchar buf[KEYSIZE_MAX]; + } + + /* + * When a new site is inserted in the catalog, a record of type + * is created for each record inserted in . + * + * The is used to control that the site receives all tables + * from scratch, before being updated from . When entries + * are present in the , the flags in + * must be 0. + */ + + record sys_disfulltab { + long site_id; // Site ID + ulong table_id; // Table ID + ulong curr_rec; // Last record read in seq. scan + + primary key fulltab_key { site_id, table_id }; + } + +} + +/* end-of-file */ diff --git a/src/catalog.h b/src/catalog.h new file mode 100644 index 0000000..5ba46c9 --- /dev/null +++ b/src/catalog.h @@ -0,0 +1,89 @@ +#ifndef TYPHOON_CATALOG_H +#define TYPHOON_CATALOG_H + +/*---------- headerfile for catalog.ddl ----------*/ +/* alignment is 4 */ + +/*---------- structures ----------*/ +struct sys_dissite { /* size 166 */ + unsigned long id; + long last_call; + unsigned short tables; + char name[21]; + char nua[17]; + char udata_len; + char udata[16]; + char up_to_date; + char in_error; + char spare[99]; +}; + +struct sys_distab { /* size 24 */ + long id; + long site_id; + long last_seqno; + char tag; + char up_to_date; + char spare[10]; +}; + +struct sys_distab_key { /* size 8 */ + long site_id; + long id; +}; + +struct sys_dischange { /* size 53 */ + unsigned long seqno; + long table_id; + long recno; + unsigned short sites; + unsigned char prog_id; + char action; + unsigned char site[37]; +}; + +struct sys_dischange_key { /* size 8 */ + long table_id; + long recno; +}; + +struct sys_disprkey { /* size 257 */ + unsigned short size; + unsigned char buf[255]; +}; + +struct sys_disfulltab { /* size 12 */ + long site_id; + unsigned long table_id; + unsigned long curr_rec; +}; + +struct fulltab_key { /* size 8 */ + long site_id; + unsigned long table_id; +}; + +/*---------- record names ----------*/ +#define SYS_DISSITE 1000L +#define SYS_DISTAB 2000L +#define SYS_DISCHANGE 3000L +#define SYS_DISPRKEY 4000L +#define SYS_DISFULLTAB 5000L + +/*---------- field names ----------*/ +#define SYS_DISSITE_ID 1001L + +/*---------- key constants ----------*/ +#define SYS_DISTAB_KEY 1 +#define SYS_DISCHANGE_KEY 2 +#define FULLTAB_KEY 3 + +/*---------- integer constants ----------*/ +#define SITES_MAX 296 +#define SITEBITMAP_SIZE 37 +#define SITENAME_LEN 20 +#define NUA_LEN 16 +#define UDATA_LEN 16 +#define KEYSIZE_MAX 255 + +#endif diff --git a/src/cmpfuncs.c b/src/cmpfuncs.c new file mode 100644 index 0000000..d93977c --- /dev/null +++ b/src/cmpfuncs.c @@ -0,0 +1,246 @@ +/*---------------------------------------------------------------------------- + * File : cmpfuncs.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains comparison functions for all the key types supported by + * Typhoon as well as compound keys. + * + * Functions: + * ucharcmp(a,b) - Compare two unsigned chars. + * charcmp(a, b) - Compare two chars. + * ustrcmp(s1, s2) - Compare two unsigned char strings. + * shortcmp(a,b) - Compare two shorts. + * intcmp(a,b) - Compare two ints. + * longcmp(a,b) - Compare two longs. + * floatcmp(a,b) - Compare two floats. + * doublecmp(a,b) - Compare two doubles. + * ushortcmp(a,b) - Compare two unsigned shorts. + * uintcmp(a,b) - Compare two unsigned ints. + * ulongcmp(a,b) - Compare two unsigned longs. + * compoundkeycmp(a,b)- Compare two compound keys. + * refentrycmp(a,b) - Compare two REF_ENTRY items. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include "typhoon.h" +#include +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" + +#define CMP return *a > *b ? 1 : *a < *b ? -1 : 0 + +static CONFIG_CONST char rcsid[] = "$Id: cmpfuncs.c,v 1.5 1999/10/03 23:28:29 kaz Exp $"; + +/*--------------------------- Function prototypes --------------------------*/ +static int charcmp PRM( (char *, char *); ) +static int shortcmp PRM( (short *, short *); ) +static int intcmp PRM( (int *, int *); ) +static int longcmp PRM( (long *, long *); ) +static int ucharcmp PRM( (uchar *, uchar *); ) +static int ushortcmp PRM( (ushort *, ushort *); ) +static int uintcmp PRM( (unsigned *, unsigned *); ) +static int ulongcmp PRM( (ulong *, ulong *); ) +static int ustrcmp PRM( (uchar *, uchar *); ) +static int floatcmp PRM( (float *, float *); ) +static int doublecmp PRM( (double *, double *); ) + int refentrycmp PRM( (REF_ENTRY *, REF_ENTRY *); ) + + +CMPFUNC keycmp[] = { + NULL, + (CMPFUNC)charcmp, + (CMPFUNC)ustrcmp, /* This should be strcmp, I think... */ + (CMPFUNC)shortcmp, + (CMPFUNC)intcmp, + (CMPFUNC)longcmp, + (CMPFUNC)floatcmp, + (CMPFUNC)doublecmp, + NULL, + (CMPFUNC)refentrycmp, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + (CMPFUNC)ucharcmp, + (CMPFUNC)ustrcmp, + (CMPFUNC)ushortcmp, + (CMPFUNC)uintcmp, + (CMPFUNC)ulongcmp +}; + + +static int charcmp(a, b) +char *a, *b; +{ + CMP; +} + + +static int ucharcmp(a, b) +uchar *a, *b; +{ + CMP; +} + + +FNCLASS int ty_ustrcmp(s1, s2) +uchar *s1, *s2; +{ + return ustrcmp(s1, s2); +} + +static int ustrcmp(s1, s2) +uchar *s1, *s2; +{ + uchar *sorttable = typhoon.db->header.sorttable; + + while( *s1 ) + { + if( sorttable[*s1] - sorttable[*s2] ) + break; + + s1++; + s2++; + } + + return sorttable[*s1] - sorttable[*s2]; +} + + +static int shortcmp(a,b) +short *a, *b; +{ + CMP; +} + +static int intcmp(a,b) +int *a, *b; +{ + CMP; +} + +static int longcmp(a,b) +long *a, *b; +{ + CMP; +} + +static int floatcmp(a,b) +float *a, *b; +{ + CMP; +} + +static int doublecmp(a,b) +double *a, *b; +{ + CMP; +} + +static int ushortcmp(a,b) +ushort *a, *b; +{ + CMP; +} + +static int uintcmp(a,b) +unsigned *a, *b; +{ + CMP; +} + +static int ulongcmp(a,b) +ulong *a, *b; +{ + CMP; +} + + +/*----------------------------- compoundkeycmp -----------------------------*\ + * + * Purpose : This function compares two compound keys. The global variable + * is set to the ID of the keys being compared. This + * is because the call to the key comparison function in the B-tree + * functions only passes two pointers and not the type. + * + * Params : a - The first key + * b - The second key + * + * Returns : < 0 - a < b + * 0 - a = b + * > 0 - a > 0 + * + */ + +int compoundkeycmp(a, b) +void *a, *b; +{ + Key *key = typhoon.db->key + typhoon.curr_key; + KeyField *keyfld= typhoon.db->keyfield + key->first_keyfield; + int fields = key->fields; + int type, diff; + + while( fields-- ) + { + type = typhoon.db->field[ keyfld->field ].type & (FT_BASIC|FT_UNSIGNED); + + if( (diff = (*keycmp[type])((char *)a + keyfld->offset, (char *)b + keyfld->offset)) ) + break; + + keyfld++; + } + + /* If the field is sorted in descending order, invert the sign */ + if( !keyfld->asc ) + diff = -diff; + + return diff; +} + + +int refentrycmp(a, b) +REF_ENTRY *a, *b; +{ + if( a->parent > b->parent ) return 1; + if( a->parent < b->parent ) return -1; + if( a->dependent.recid > b->dependent.recid ) return 1; + if( a->dependent.recid < b->dependent.recid ) return -1; + if( a->dependent.recno > b->dependent.recno ) return 1; + if( a->dependent.recno < b->dependent.recno ) return -1; + + return 0; +} + +/* end-of-file */ diff --git a/src/dos.c b/src/dos.c new file mode 100644 index 0000000..30d74e1 --- /dev/null +++ b/src/dos.c @@ -0,0 +1,67 @@ +/*---------------------------------------------------------------------------- + * File : dos.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contiains functions specific to DOS. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" + +static char rcsid[] = "$Id: dos.c,v 1.2 1999/10/03 23:28:29 kaz Exp $"; + +#ifdef CONFIG_DOS + +ty_openlock() +{ + return 0; +} + + +ty_closelock() +{ + return 0; +} + + +ty_lock() +{ + return 0; +} + + +ty_unlock() +{ + return 0; +} + +#endif + +/* end-of-file */ + diff --git a/src/makefile.os2 b/src/makefile.os2 new file mode 100644 index 0000000..29fe2fc --- /dev/null +++ b/src/makefile.os2 @@ -0,0 +1,19 @@ +LIB_OBJS = bt_del.obj bt_funcs.obj bt_io.obj bt_open.obj \ + cmpfuncs.obj os.obj os2.obj readdbd.obj record.obj \ + ty_ins.obj ty_find.obj ty_util.obj ty_auxfn.obj ty_io.obj ty_open.obj ty_refin.obj \ + ty_repl.obj vlr.obj +CFLAGS = /DOS2 /I..\include +CC = icc /OS2 /Tdc /Sp1 /Q /Tx /Fi /Si /Gh /Ti /Gm /Gd /Ge- /G4 /Tm \ + /Tl1 $(CFLAGS) + +IMPLIB = \toolkt20\os2bin\implib + +typhoon.dll: $(LIB_OBJS) + $(CC) /B"/de /nologo /noe /m:full" /Fe"typhoon.dll" /fm"typhoon.map" \ + typhoon.def $(LIB_OBJS) + $(IMPLIB) typhoon.lib typhoon.dll + +.c.obj: + $(CC) -c $< + +# end-of-file diff --git a/src/os.c b/src/os.c new file mode 100644 index 0000000..140c0bc --- /dev/null +++ b/src/os.c @@ -0,0 +1,192 @@ +/*---------------------------------------------------------------------------- + * File : os.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains functions that may vary between different operating systems + * such as opening and locking files. + * + * Functions: + * os_lock Get exclusive access to the database. + * os_unlock Release the lock. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +# include +# include +#endif +#ifdef _MSC_VER +# include +# include +# include +#endif +#ifdef __BORLANDC__ +# include +# include +#endif +#ifdef __IBMC__ +#include +#include +#endif +#include +#include + +#include "ty_type.h" +#include "ty_prot.h" + +static CONFIG_CONST char rcsid[] = "$Id: os.c,v 1.7 1999/10/04 03:45:07 kaz Exp $"; + + +/*--------------------------------- os_lock --------------------------------*\ + * + * Purpose : Lock a region of a file. + * + * Parameters: fh - File handle. + * offset - Offset from start of file. + * bytes - Number of bytes to lock. + * type - Lock type. 't'=test and lock, 'w'=wait and lock + * + * + * Returns : -1 - The region could not be locked. + * 0 - Region locked. + * + */ + +int os_lock(fh, offset, bytes, type) +int fh, type; +long offset; +unsigned bytes; +{ +#ifdef CONFIG_USE_FLOCK + struct flock flk; +#endif + lseek(fh, offset, SEEK_SET); +#ifdef CONFIG_CONFIG_DOS + /* Microsoft C */ +# ifdef _MSC_VER + if( locking(fh, type == 't' ? LK_NBLCK : LK_LOCK, bytes) == -1 ) + return -1; +# endif +#endif +#ifdef CONFIG_UNIX +#ifdef CONFIG_USE_FLOCK + flk.l_type = F_WRLCK; + flk.l_whence = SEEK_SET; + flk.l_start = 0; + flk.l_len = bytes; + + if (fcntl(fh, F_SETLK, &flk) == -1) + puts("fcntl() F_SETLK failed"); +#else + if( lockf(fh, type == 't' ? F_TLOCK : F_LOCK, bytes) == -1 ) + puts("lockf failed"); +#endif +#endif +#ifdef CONFIG_OS2 +# ifdef __BORLANDC__ + if( locking(fh, type == 't' ? LK_NBLCK : LK_LOCK, bytes) == -1 ) + return -1; +# endif +#endif + + return 0; +} + +/*-------------------------------- os_unlock -------------------------------*\ + * + * Purpose : Unlock a region of a file. + * + * Parameters: fh - File handle. + * offset - Offset from start of file. + * bytes - Number of bytes to lock. + * type - Lock type. 't'=test and lock, 'w'=wait and lock + * + * + * Returns : -1 - The region could not be unlocked. + * 0 - Region unlocked. + * + */ + +int os_unlock(fh, offset, bytes) +int fh; +long offset; +unsigned bytes; +{ + lseek(fh, offset, SEEK_SET); +#ifdef CONFIG_DOS + /* Microsoft C */ +# ifdef _MSC_VER + return locking(fh, LK_UNLCK, bytes); +# endif +#endif +#ifdef CONFIG_UNIX + return lockf(fh, F_ULOCK, bytes); +#endif +#ifdef CONFIG_OS2 +# ifdef __BORLANDC__ + return locking(fh, LK_UNLCK, bytes); +# endif +#endif +} + + + +int os_open(fname, flags, creatflags) +char *fname; +int flags, creatflags; +{ + +#ifdef __IBMC__ + return sopen(fname, flags, SH_DENYNO, creatflags); +#else + return open(fname, flags, creatflags); +#endif +} + + + +int os_close(fh) +int fh; +{ + return close(fh); +} + + +int os_access(fname, mode) +char *fname; +int mode; +{ +#ifdef __IBMC__ + return _access(fname, mode); +#else + return access(fname, mode); +#endif +} + +/* end-of-file */ diff --git a/src/os2.c b/src/os2.c new file mode 100644 index 0000000..95a1f28 --- /dev/null +++ b/src/os2.c @@ -0,0 +1,154 @@ +/*---------------------------------------------------------------------------- + * File : os2.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains functions specific to OS/2. + * + * Functions: + * ty_openlock - Create/open the locking resource. + * ty_closelock - Close the locking resource. + * ty_lock - Obtain the lock. + * ty_unlock - Release the lock. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" + +static char rcsid[] = "$Id: os2.c,v 1.3 1999/10/04 03:45:07 kaz Exp $"; + +#ifdef CONFIG_OS2 + +#define INCL_NOPMAPI +#define INCL_DOSSEMAPHORES +#include + +#define TIMEOUT -1 /* Wait maximum 30 seconds for excl. access */ + +/*---------------------------- Global variables ----------------------------*/ +static HMTX block_sem; /* Blocking semaphore used by d_block() */ +static HMTX ty_sem; /* API function semaphore */ + + +#ifdef __BORLANDC__ +ULONG _dllmain(ULONG termflag, HMODULE modhandle) +#endif +#ifdef __IBMC__ +void _CRT_init(void); +ULONG _System _DLL_InitTerm(ULONG modhandle, ULONG termflag) +#endif +{ + static char fname[] = "\\SEM32\\TYPHOON"; + + switch( termflag ) + { + case 0: +#ifdef __IBMC__ + _CRT_init(); +#endif + if( DosOpenMutexSem(fname, &ty_sem) ) + if( DosCreateMutexSem(fname, &ty_sem, 0, 0) ) + return 0; + + if( DosCreateMutexSem(NULL, &block_sem, 0, 0) ) + return 0; + break; + + case 1: + /* Never release the ty_sem semaphore */ + DosCloseMutexSem(ty_sem); + DosCloseMutexSem(block_sem); + break; + } + + return 1; +} + +/*------------------------------ ty_openlock ------------------------------*\ + * + * Purpose : This function ensures that only one instance of a Typhoon + * function is active at the time, at least in its critical + * section. + * + * Parameters: None. + * + * Returns : -1 - Semaphore could not be created. + * 0 - Successful. + * + */ + +ty_openlock() +{ +/* + static char fname[] = "\\SEM32\\TYPHOON"; + + if( DosOpenMutexSem(fname, &ty_sem) ) + if( DosCreateMutexSem(fname, &ty_sem, 0, 0) ) + return -1; + + locked = 0; +*/ + return 0; +} + + +ty_closelock() +{ +/* DosCloseMutexSem(ty_sem);*/ + return 0; +} + + +ty_lock() +{ + if( DosRequestMutexSem(ty_sem, TIMEOUT) ) + return -1; + + return 0; +} + + +ty_unlock() +{ + DosReleaseMutexSem(ty_sem); + return 0; +} + + +void os2_block() +{ + DosRequestMutexSem(block_sem, -1L); +} + + +void os2_unblock() +{ + DosReleaseMutexSem(block_sem); +} + +#endif + +/* end-of-file */ diff --git a/src/readdbd.c b/src/readdbd.c new file mode 100644 index 0000000..2986503 --- /dev/null +++ b/src/readdbd.c @@ -0,0 +1,122 @@ +/*---------------------------------------------------------------------------- + * File : readdbd.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains a function that reads the dbd-file. + * + * Functions: + * read_dbdfile - Read the database description file. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +# include +# ifdef __STDC__ +# include +# endif +#else +# include +# include +#endif +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" +#include "ty_glob.h" + +static CONFIG_CONST char rcsid[] = "$Id: readdbd.c,v 1.9 1999/10/04 03:45:07 kaz Exp $"; + + +/*------------------------------ read_dbdfile ------------------------------*\ + * + * Purpose : Reads the dbd-file into a available database slot provided + * by the calling function. The table pointers in the slot are + * also set up. + * + * Parameters: _db - Pointer to database entry slot. + * fname - The name of the dbd-file. + * + * Returns : S_OKAY - dbd-file successfully read. + * S_NOMEM - Not enough memory to allocate tables. + * S_INVDB - Cannot find dbd-file. + * S_IOFATAL - The dbd-file is corrupted. + * + */ + +int read_dbdfile(_db, fname) +Dbentry *_db; +char *fname; +{ + int dbdfile, size; + + if( (dbdfile = os_open(fname, O_RDONLY|CONFIG_O_BINARY, 0)) == -1 ) + RETURN S_INVDB; + + /* + * Get the size of the dbd-file, read the header and expand to buffer + * to hold a table of file handles. + */ + size = lseek(dbdfile, 0, SEEK_END); + lseek(dbdfile, 0, SEEK_SET); + + if( read(dbdfile, &_db->header, sizeof _db->header) < sizeof(_db->header) ) + RETURN S_IOFATAL; + + if( strcmp(_db->header.version, DBD_VERSION) ) + RETURN S_VERSION; + + size -= sizeof _db->header; + + if( !(_db->dbd = (void *)malloc(size + _db->header.files * sizeof(Fh))) ) + { + close(dbdfile); + RETURN S_NOMEM; + } + + read(dbdfile, _db->dbd, (size_t) size); + close(dbdfile); + + /* Set up pointers to tables */ + _db->file = (File *)_db->dbd; + _db->key = (Key *)(_db->file + _db->header.files); + _db->keyfield = (KeyField *)(_db->key + _db->header.keys); + _db->record = (Record *)(_db->keyfield + _db->header.keyfields); + _db->field = (Field *)(_db->record + _db->header.records); + _db->structdef = (Structdef*)(_db->field + _db->header.fields); + _db->sequence = (Sequence *)(_db->structdef+ _db->header.structdefs); + _db->fh = (Fh *)(_db->sequence + _db->header.sequences); + + return S_OKAY; +} + + +/* end-of-file */ diff --git a/src/record.c b/src/record.c new file mode 100644 index 0000000..ea03e31 --- /dev/null +++ b/src/record.c @@ -0,0 +1,439 @@ +/*---------------------------------------------------------------------------- + * File : record.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains record file functions. + * + * Functions: + * rec_open - Open a record file. + * rec_close - Close a record file. + * rec_add - Add a record to a file. + * rec_write - Write a record to a file. + * rec_delete - Delete a record. + * rec_read - Read a record. + * rec_frst - Read the first record in a file. + * rec_last - Read the last record in a file. + * rec_next - Read the next record in a file. + * rec_prev - Read the previous record in a file. + * rec_numrecords - Return the number of records in a file. + * rec_reccurr - Return the record number of the current record. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "environ.h" +#ifdef CONFIG_UNIX +# include +# ifdef __STDC__ +# include +# endif +#else +# include +# include +# include +# include +#endif + +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" +#include "ty_glob.h" + +static CONFIG_CONST char rcsid[] = "$Id: record.c,v 1.8 1999/10/04 03:45:07 kaz Exp $"; + + +/*--------------------------- Function prototypes --------------------------*/ +static void putheader PRM( (RECORD *); ) +static void getheader PRM( (RECORD *); ) + +/*--------------------------------- Macros ---------------------------------*/ +#define recseek(R,pos) lseek(R->fh, R->H.recsize * (pos), SEEK_SET) +#define RECVERSION_ID "RecMan120" +#define RECVERSION_NUM 120 + + +static void putheader(R) +RECORD *R; +{ + recseek(R, 0L); + write(R->fh, &R->H, sizeof(R->H)); +} + + +static void getheader(R) +RECORD *R; +{ + recseek(R, 0L); + read(R->fh, &R->H, sizeof(R->H)); +} + + +/*-------------------------------- rec_open ----=---------------------------*\ + * + * Purpose : Opens a record file. + * + * Parameters: fname - File name. + * recsize - Record size. + * shared - Open file in shared mode? + * + * Returns : NULL - File could not be opened. db_status contains reason. + * else - Pointer to record file descriptor. + * + */ + +RECORD *rec_open(fname, recsize, shared) +char *fname; +unsigned recsize; +int shared; +{ + RECORD *R; + int isnew, fh; + + /* if file exists then read header record, otherwise create */ + isnew = access(fname, 0); + + if( (fh=os_open(fname, CONFIG_O_BINARY|O_RDWR|O_CREAT,CONFIG_CREATMASK)) == -1 ) + { + db_status = S_IOFATAL; + return NULL; + } + + /* Lock the file if it is not opened in shared mode */ + if( !shared ) + if( os_lock(fh, 0L, 1, 't') == -1 ) + { + db_status = S_NOTAVAIL; + return NULL; + } + + if( (R=(RECORD *)calloc(sizeof(RECORD)+recsize, 1)) == NULL ) + { + os_close(fh); + db_status = S_NOMEM; + return NULL; + } + + R->fh = fh; + R->recno = 0; + + if( isnew ) + { + unsigned headersize; + + R->H.datasize = recsize; + R->H.recsize = recsize + (int)offsetof(RECORDHEAD, data[0]); + R->H.first_deleted = 0; + R->H.first = 0; + R->H.last = 0; + R->H.numrecords = 0; + R->H.version = RECVERSION_NUM; + strcpy(R->H.id, RECVERSION_ID); + + R->first_possible_rec = (sizeof(R->H) + R->H.recsize - 1) / R->H.recsize; + + /* Beware that the record can be smaller than the header */ + if( R->H.recsize < sizeof(R->H) ) + headersize = R->first_possible_rec * R->H.recsize; + else + headersize = R->H.recsize; + + recseek(R, 0L); + write(fh, &R->H, headersize); + } + else + { + read(R->fh, &R->H, sizeof(R->H)); + + R->first_possible_rec = (sizeof(R->H) + R->H.recsize - 1) / R->H.recsize; + + if( R->H.version != RECVERSION_NUM ) + { + db_status = S_VERSION; + os_close(fh); + free(R); + return NULL; + } + } + + strcpy(R->fname, fname); + + db_status = S_OKAY; + + return R; +} + + +int rec_close(R) +RECORD *R; +{ + if( R->fh != -1 ) + os_close(R->fh); + free(R); + R = NULL; + + RETURN S_OKAY; +} + + +int rec_dynclose(R) +RECORD *R; +{ + if( R->fh != -1 ) + { + close(R->fh); + R->fh = -1; + } + + RETURN S_OKAY; +} + +int rec_dynopen(R) +RECORD *R; +{ + if( R->fh == -1 ) + if( (R->fh=os_open(R->fname, CONFIG_O_BINARY|O_RDWR|O_CREAT,CONFIG_CREATMASK)) == -1 ) + RETURN S_IOFATAL; + + RETURN S_OKAY; +} + + +int rec_add(R,data,rec) +RECORD *R; /* record file */ +void *data; +ulong *rec; +{ + long recno; + + getheader(R); + + if( R->H.first_deleted ) + { + recno = R->H.first_deleted; + + /* Get recno of next deleted */ + lseek(R->fh, recno * R->H.recsize + (long)offsetof(RECORDHEAD, next), SEEK_SET); + read(R->fh, &R->H.first_deleted, sizeof(R->H.first_deleted)); + } + else + recno = (lseek(R->fh, 0L, SEEK_END) + R->H.recsize - 1) / R->H.recsize; + + if( R->H.numrecords ) + { + long pos; + + /* Adjust next-pointer of last record */ + pos = R->H.last * R->H.recsize; + pos += offsetof(RECORDHEAD, next); + + lseek(R->fh, pos, SEEK_SET); + write(R->fh, &recno, sizeof recno); + + /* Set prev-pointer of new record */ + R->rec.prev = R->H.last; + } + else + { + R->H.first = recno; /* Set new first pointer */ + R->rec.prev = 0; /* There's no previous record */ + } + + R->rec.next = 0; /* No next record since at end of chain */ + + R->H.last = recno; /* Set last-pointer to new record */ + R->H.numrecords++; + + R->rec.flags = 0; + memcpy(R->rec.data, data, R->H.datasize); /* Copy data to buffer */ + lseek(R->fh, recno * R->H.recsize, SEEK_SET); + if( write(R->fh, &R->rec, R->H.recsize) != R->H.recsize ) /* Write chain and record */ + RETURN S_IOFATAL; + + putheader(R); + *rec = recno; + + return S_OKAY; +} + + +int rec_write(R, data, recno) +RECORD *R; +void *data; +ulong recno; +{ + if( recno < R->first_possible_rec ) + RETURN S_INVADDR; + + lseek(R->fh, (off_t) (R->H.recsize * recno + (long)offsetof(RECORDHEAD, data[0])), SEEK_SET); + write(R->fh, data, R->H.datasize); + + RETURN S_OKAY; +} + + +int rec_delete(R, recno) +RECORD *R; +ulong recno; +{ + getheader(R); + + /* Get previous and next pointers of record to be deleted */ + lseek(R->fh, (off_t) (R->H.recsize * recno), SEEK_SET); + read(R->fh, &R->rec, sizeof R->rec); + + if( R->rec.flags & BIT_DELETED ) + RETURN S_DELETED; + + /* Adjust previous pointer */ + if( R->H.first == recno ) + R->H.first = R->rec.next; + else + { + lseek(R->fh, (off_t) (R->H.recsize * R->rec.prev + (long)offsetof(RECORDHEAD, next)), SEEK_SET); + write(R->fh, &R->rec.next, sizeof R->rec.next); + } + + /* Adjust next pointer */ + if( R->H.last == recno ) + R->H.last = R->rec.prev; + else + { + lseek(R->fh, (off_t) (R->H.recsize * R->rec.next + (long)offsetof(RECORDHEAD, prev)), SEEK_SET); + write(R->fh, &R->rec.prev, sizeof R->rec.prev); + } + + /* Delete-mark the record and insert it in delete chain */ + R->rec.flags |= BIT_DELETED; + R->rec.next = R->H.first_deleted; + R->rec.prev = 0; + + lseek(R->fh, (off_t) (R->H.recsize * recno), SEEK_SET); + write(R->fh, &R->rec, sizeof R->rec); + R->H.first_deleted = recno; + R->H.numrecords--; + + putheader(R); + + RETURN S_OKAY; +} + + +int rec_read(R,data,recno) +RECORD *R; +void *data; +ulong recno; +{ + if( recno < R->first_possible_rec ) + RETURN S_INVADDR; + + recseek(R, (off_t) recno); + if( read(R->fh, &R->rec, R->H.recsize) < R->H.recsize ) + RETURN S_NOTFOUND; + + if( R->rec.flags & BIT_DELETED ) + RETURN S_DELETED; + + memcpy(data, R->rec.data, R->H.datasize); + R->recno = recno; + + RETURN S_OKAY; +} + + +int rec_frst(R, data) +RECORD *R; +void *data; +{ + getheader(R); + + return rec_read(R, data, R->H.first); +} + + +int rec_last(R, data) +RECORD *R; +void *data; +{ + getheader(R); + + return rec_read(R, data, R->H.last); +} + + +int rec_next(R, data) +RECORD *R; +void *data; +{ + if( CURR_REC ) + return rec_read(R, data, R->rec.next); + else + return rec_frst(R, data); +} + + +int rec_prev(R, data) +RECORD *R; +void *data; +{ + if( CURR_REC ) + return rec_read(R, data, R->rec.prev); + else + return rec_last(R, data); +} + + +ulong rec_numrecords(R, number) +RECORD *R; +ulong *number; +{ + getheader(R); + + if( number ) + *number = R->H.numrecords; + + return R->H.numrecords; +} + + +int rec_curr(R, recno) +RECORD *R; +ulong *recno; +{ + if( recno ) + *recno = R->recno; + + RETURN R->recno ? S_OKAY : S_NOCR; +} + +/* end-of-file */ + + diff --git a/src/sequence.c b/src/sequence.c new file mode 100644 index 0000000..453988d --- /dev/null +++ b/src/sequence.c @@ -0,0 +1,188 @@ +/*---------------------------------------------------------------------------- + * File : sequence.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * This file contains all the code that handles sequences. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include "environ.h" +#ifdef CONFIG_UNIX +# include +# ifdef __STDC__ +# include +# endif +#else +# include +# include +# include +# include +#endif + +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" +#include "ty_glob.h" + +static CONFIG_CONST char rcsid[] = "$Id: sequence.c,v 1.5 1999/10/03 23:28:29 kaz Exp $"; + +/*---------------------------- Global varibles ----------------------------*/ +static int seq_max = 0; +static ulong *seq_tab = NULL; + + +/*----------------------------- sequence_open -----------------------------*\ + * + * Purpose : Opens the sequence file. + * + * Parameters: db - Pointer to db struct. + * + * Returns : -1 - Could not open sequence file. + * 0 - Successful. + * + */ +int seq_open(db) +Dbentry *db; +{ + int isnew, i; + char fname[128]; + + sprintf(fname, "%ssequence.dat", db->dbfpath); + + /* if file exists then read header record, otherwise create */ + isnew = access(fname, 0); + + if( (db->seq_fh=os_open(fname, CONFIG_O_BINARY|O_RDWR|O_CREAT,CONFIG_CREATMASK)) == -1 ) + { + db_status = S_IOFATAL; + return -1; + } + + if( db->header.sequences > seq_max ) + { + void *ptr; + + if( !(ptr = (void *)realloc(seq_tab, sizeof(*seq_tab) * db->header.sequences)) ) + { + close(db->seq_fh); + db_status = S_NOMEM; + return -1; + } + + seq_tab = (ulong *)ptr; + seq_max = db->header.sequences; + } + + if( isnew ) + { + /* Initialize sequences */ + for( i=0; iheader.sequences; i++ ) + seq_tab[i] = db->sequence[i].start; + + write(db->seq_fh, seq_tab, sizeof(*seq_tab) * DB->header.sequences); + } + + return 0; +} + + +/*----------------------------- sequence_close ----------------------------*\ + * + * Purpose : Closes the sequence file. + * + * Parameters: db - Pointer to db struct. + * + * Returns : 0 - Successful. + * + */ +int seq_close(db) +Dbentry *db; +{ + close(DB->seq_fh); + + if( typhoon.dbs_open == 1 ) + { + free(seq_tab); + seq_tab = NULL; + seq_max = 0; + } + + return 0; +} + + +/*----------------------------- d_getsequence -----------------------------*\ + * + * Purpose : Gets the next number in a sequence. + * + * Parameters: id - Sequence id. + * number - Will contain number on return. + * + * Returns : S_NOCD - No current database. + * S_INVSEQ - Invalid sequence id. + * S_OKAY - Succesful. + * + */ +int d_getsequence(id, number) +Id id; +ulong *number; +{ + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + if( id >= DB->header.sequences ) + RETURN_RAP(S_INVSEQ); + + ty_lock(); + + lseek(DB->seq_fh, 0, SEEK_SET); + read(DB->seq_fh, seq_tab, sizeof(*seq_tab) * DB->header.sequences); + + *number = seq_tab[id]; + + if( DB->sequence[id].asc ) + seq_tab[id] += DB->sequence[id].step; + else + seq_tab[id] -= DB->sequence[id].step; + + lseek(DB->seq_fh, 0, SEEK_SET); + write(DB->seq_fh, seq_tab, sizeof(*seq_tab) * DB->header.sequences); + + ty_unlock(); + + RETURN S_OKAY; +} + +/* end-of-file */ diff --git a/src/ty_auxfn.c b/src/ty_auxfn.c new file mode 100644 index 0000000..9c64a35 --- /dev/null +++ b/src/ty_auxfn.c @@ -0,0 +1,448 @@ +/*---------------------------------------------------------------------------- + * File : ty_auxfn.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Auxiliary functions for the API functions. + * + * Functions: + * set_keyptr - Return a pointer to a key or compound key. + * reckeycmp - Compare two (compound) keys in separate records. + * set_subcode - Set . + * set_recfld - + * keyfind + * keyadd + * keydel + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_auxfn.c,v 1.5 1999/10/03 23:28:29 kaz Exp $"; + +int aux_getkey(id, key) +Id id; +Key **key; +{ + Field *fld; + int rc; + + /* Determine whether this id is a key id or a compound key id */ + if( id < REC_FACTOR ) + { + if( id >= DB->header.keys ) + RETURN_RAP(S_NOTKEY); + + *key = &DB->key[id]; + } + else + { + if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY ) + return rc; + + if( !(fld->type & FT_KEY) ) + RETURN_RAP(S_NOTKEY); + + *key = &DB->key[fld->keyid]; + } + + return S_OKAY; +} + + + +/*------------------------------- set_keyptr -------------------------------*\ + * + * Purpose : Returns a pointer to the key value of . This function + * is necessary because the fields of a compound can be spread + * all over the record. + * + * Parameters: key - Pointer to key table entry. + * buf - Buffer to store key value in. + * + * + * Returns : A pointer to the key value. + * + */ + +void *set_keyptr(key, buf) +Key *key; +void *buf; +{ + static char keybuf[KEYSIZE_MAX]; + int n; + + CURR_KEY = key - DB->key; + + if( (n = key->fields) > 1 ) + { + KeyField *keyfld = DB->keyfield + key->first_keyfield; + + /* Build compound key from record */ + while( n-- ) + { + memcpy(keybuf + keyfld->offset, + (char *)buf + DB->field[ keyfld->field ].offset, + DB->field[ keyfld->field ].size); + keyfld++; + } + + return (void *)keybuf; + } + + return (void *)((char *)buf + DB->field[ DB->keyfield[ key->first_keyfield ].field ].offset); +} + + + + + +/*-------------------------------- reckeycmp -------------------------------*\ + * + * Purpose : The function compares two fields in separate records. This + * function is necessary because the fields of a compound can be + * spread all over the record (i.e. compound key). + * + * Parameters: key - Pointer to key struct. + * a - Pointer to first record buffer. + * b - Pointer to seconds record buffer. + * + * Returns : 0 - The keys match. + * !=0 - The keys do not match. + * + */ + +int reckeycmp(key, a, b) +Key *key; +void *a, *b; +{ + KeyField *keyfld = DB->keyfield + key->first_keyfield; + Field *field; + int fields = key->fields; + int type, diff; + + CURR_KEY = key - DB->key; + + /* An optional key is regarded as changed if + * a) One and only one of the null determinatator is not set + * b) If both null determinators are set and the key values different + */ + if( key->type & KT_OPTIONAL ) + { + int a_null = null_indicator(key, a); + int b_null = null_indicator(key, b); + + if( a_null && b_null ) + return 0; + + if( a_null || b_null ) + return 1; + + /* Otherwise both keys are not null - compare */ + } + + do + { + field = DB->field + keyfld->field; + type = field->type & (FT_BASIC|FT_UNSIGNED); + + if( (diff = (*keycmp[type])((char *)a + field->offset, (char *)b + field->offset)) ) + break; + + keyfld++; + } + while( --fields ); + + /* Ascending or descending is not significant here */ + return diff; +} + + + +/*------------------------------- set_subcode ------------------------------*\ + * + * Purpose : This function sets to the id of . The id + * can either be a compound key id or a field id. + * + * Parameters: key - Pointer to key table entry. + * + * Returns : Nothing. + * + */ +void set_subcode(key) +Key *key; +{ + Field *fld; + + if( key->fields > 1 ) + db_subcode = key - DB->key /* + 1 */; + else + { + fld = DB->field + DB->keyfield[ key->first_keyfield ].field; + + db_subcode = (fld->recid + 1) * REC_FACTOR + + (fld - DB->field) - DB->record[ fld->recid ].first_field + 1; + } +} + + + +/*------------------------------- set_recfld -------------------------------*\ + * + * Purpose : This function sets the pointers and to the + * record and field denoted by . The following calls are + * legal: + * + * If is -1, will be set to the current record () a + * to the first field of that record. + * + * Both and may be NULL. + * + * Parameters: id - Record or field id. + * recptr - Pointer to record pointer. + * fldptr - Pointer to field pointer. + * + * Returns : S_OKAY - recid and fldid were okay, recptr and fldptr set. + * S_NOCD - No current database. + * S_INVREC - Invalid record id. + * S_INVFLD - Invalid field id. + * + */ + +int set_recfld(id, recptr, fldptr) +Id id; +Record **recptr; +Field **fldptr; +{ + Id recid, fldid; + + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + /* If is -1 the return pointers to the current record */ + if( id == -1 ) + { + recid = CURR_RECID; + fldid = DB->record[recid].first_field; + } + else + { + /* Remove record factor */ + recid = id / REC_FACTOR - 1; + + if( (fldid = id % REC_FACTOR) ) + fldid--; + + /* Validate record id */ + if( recid >= DB->header.records ) + RETURN_RAP(S_INVREC); + + /* Validate field id */ + if( fldid >= DB->record[recid].fields ) + RETURN_RAP(S_INVFLD); + + fldid += DB->record[recid].first_field; + CURR_RECID = recid; + } + + if( recptr ) + *recptr = DB->record + recid; + + if( fldptr ) + *fldptr = DB->field + fldid; + + RETURN S_OKAY; +} + + +int keyfind(key, buf, ref) +Key *key; +void *buf; +ulong *ref; +{ + CURR_KEY = key - DB->key; + + return ty_keyfind(key, set_keyptr(key, buf), ref); +} + + +int keyadd(key, buf, ref) +Key *key; +void *buf; +ulong ref; +{ + CURR_KEY = key - DB->key; + + return ty_keyadd(key, set_keyptr(key, buf), ref); +} + + +int keydel(key, buf, ref) +Key *key; +void *buf; +ulong ref; +{ + CURR_KEY = key - DB->key; + + return ty_keydel(key, set_keyptr(key, buf), ref); +} + + +/*------------------------------ compress_vlr ------------------------------*\ + * + * Purpose : Compresses or uncompresses a the variable length record + * defined by . + * + * Parameters: action - COMPRESS/UNCOMPRESS. + * rec - Pointer to record table entry. + * src - Pointer to source buffer. + * dest - Pointer to destination buffer. + * size - Will contain compressed size when the function returns. + * + * Returns : -1 - Error. db_status set to cause. + * > 0 - Size of compress record in . + * + */ +int compress_vlr(action, rec, dest, src, size) +int action; +Record *rec; +void *src, *dest; +unsigned *size; +{ + Field *fld; + unsigned offset; + ushort count; + int fields = rec->fields; + + /* Find the first variable length field (this cannot be 0)*/ + fld = &DB->field[rec->first_field]; + + while( fields ) + if( fld->type & FT_VARIABLE ) + break; + else + fld++, fields--; + + /* Copy the static part of the record */ + memcpy(dest, src, offset = fld->offset); + + while( fields ) + { + /* Get the value of the size field (stored in keyid) */ + count = *(ushort *)((char *)src + DB->field[ fld->keyid ].offset) * fld->elemsize; + + /* If the size value is too high report an error to the user */ + if( count > fld->size ) + { + db_subcode = (fld->recid + 1) * REC_FACTOR + fld->keyid + 1; + RETURN S_RECSIZE; + } + + if( action == COMPRESS ) + memcpy((char *)dest + offset, (char *)src + fld->offset, count); + else + memcpy((char *)dest + fld->offset, (char *)src + offset, count); + + offset += count; + + /* Find next variable length field */ + if( !--fields ) + break; + + fld++; + while( fld->nesting ) + fld++, fields--; + } + + if( action == COMPRESS ) + *size = offset; + + return S_OKAY; +} + + +/*----------------------------- null_indicator -----------------------------*\ + * + * Purpose : This function checks whether the null indicator of an optional + * key is set. + * + * Parameters: key - Pointer to key. + * buf - Pointer to record buffer. + * + * Returns : 0 - The null indicator is set. + * 1 - The null indicator is not set. + * + */ +int null_indicator(key, buf) +Key *key; +void *buf; +{ + return !(((char *)buf)[ DB->field[ key->null_indicator ].offset ]); +} + + + +/*------------------------------ update_recbuf -----------------------------*\ + * + * Purpose : Makes sure that the contents of the current record is the same + * in DB->recbuf as on the disk. + * + * Parameters: None. + * + * Returns : None. + * + */ + +int update_recbuf() +{ + Record *rec = DB->record + CURR_RECID; + int rc; + unsigned recsize; + +/* if( curr_bufrec == CURR_REC && curr_bufrecid == CURR_RECID ) + return S_OKAY;*/ + + DB->recbuf = DB->real_recbuf + rec->preamble; + + if( rec->is_vlr ) + rc = ty_vlrread(rec, DB->real_recbuf, CURR_REC, &recsize); + else + rc = ty_recread(rec, DB->real_recbuf, CURR_REC); + + CURR_BUFREC = CURR_REC; + CURR_BUFRECID = CURR_RECID; + + return rc; +} + +/* end-of-file */ diff --git a/src/ty_dbd.h b/src/ty_dbd.h new file mode 100644 index 0000000..930b508 --- /dev/null +++ b/src/ty_dbd.h @@ -0,0 +1,203 @@ +/*---------------------------------------------------------------------------- + * File : ty_dbd.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains the definitions for the dbd-file. + * + * $Id: ty_dbd.h,v 1.3 1999/10/04 03:45:07 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_TY_DBD_H +#define TYPHOON_TY_DBD_H + +#ifndef TY_ENVIRON_H +#include "environ.h" +#endif + +/*-------------------------------- Constants -------------------------------*/ +#define DBD_VERSION "Typhoon 2.02" +#define DBDVERSION_LEN 20 /* Version name of dbd-file */ +#define DBNAME_LEN 8 /* Maximum database name length */ +#define IDENT_LEN 32 /* Identifier length */ +#define FILENAME_LEN 12 /* OS dependent file name length */ +#define KEYSIZE_MAX 255 /* Maximum size of a key */ +#define RECSIZE_MAX 64000 /* Maximum size of a record */ +#define RECKEYS_MAX 32 /* Maximum number of keys per record */ +#define REC_FACTOR 1000L /* rec 1 is 1000, rec 2 is 2000 etc. */ +#define SORTTABLE_SIZE 256 /* Number of entries in sort table */ + +/*------------------------------- Field types ------------------------------*/ +#define FT_CHAR 0x01 /* The first three bits are for type */ +#define FT_CHARSTR 0x02 /* char string */ +#define FT_SHORT 0x03 /* short */ +#define FT_INT 0x04 /* int */ +#define FT_LONG 0x05 /* long */ +#define FT_FLOAT 0x06 /* float */ +#define FT_DOUBLE 0x07 /* double */ +#define FT_LDOUBLE 0x08 /* long double (not used) */ +#define FT_STRUCT 0x09 /* The type is a structure */ +#define FT_UNSIGNED 0x10 /* Bit 4 is unsigned bit */ +#define FT_KEY 0x20 /* Field is a key field */ +#define FT_UNIQUE 0x40 /* Field is a unique key field */ +#define FT_VARIABLE 0x80 /* Field has variable length (only 1-arrays)*/ + +#define FT_BASIC 0x0f /* Bits occupied by integral types */ +#define FT_GETBASIC(f) ((f)& FT_BASIC) /* Extracts the integral type */ +#define FT_GETSIGNEDBASIC(f) ((f) & (FT_BASIC|FT_UNSIGNED)) + +/*-------------------------------- Key types -------------------------------*/ +#define KT_PRIMARY 0x01 /* Primary key (preceedes alternate in key[]*/ +#define KT_ALTERNATE 0x02 /* Alternate key (preceedes foreign in key[]*/ +#define KT_FOREIGN 0x03 /* Foreign key */ +#define KT_CASCADE 0x08 /* Used with KT_FOREIGN */ +#define KT_RESTRICT 0x10 /* Used with KT_FOREIGN */ +#define KT_OPTIONAL 0x20 /* Used with KT_FOREIGN and KT_ALTERNATE */ +#define KT_UNIQUE FT_UNIQUE /* Must be the same bit as FT_UNIQUE */ + +#define KT_BASIC 0x03 /* The bits occupied by basic types */ +#define KT_GETBASIC(k) ((k)&KT_BASIC) /* Extracts the type of the key */ + + +#define KEY_ISFOREIGN(key) (KT_GETBASIC(key->type) == KT_FOREIGN) +#define KEY_ISALTERNATE(key) (KT_GETBASIC(key->type) == KT_ALTERNATE) +#define KEY_ISOPTIONAL(key) (key->type & KT_OPTIONAL) + + +#define RECID_TO_INTERN(id) ((id)/REC_FACTOR-1) +#define INTERN_TO_RECID(id) (((id)+1)*REC_FACTOR) + + +typedef unsigned long Id; /* Identifies an element in the dbd file */ + +typedef struct { + char version[DBDVERSION_LEN]; /* Version of dbd-file */ + ushort files; /* Number of files in database */ + ushort keys; /* Number of key definitions */ + ushort keyfields; /* Number of key fields */ + ushort records; /* Number of records in database */ + ushort fields; /* Number of fields in database */ + ushort structdefs; /* Number of structdefs in database */ + uchar sorttable[SORTTABLE_SIZE]; + char alignment; /* Structure alignment */ + char spare0; + ushort sequences; /* Number of sequences in database */ + char spare[16]; /* Not used */ +} Header; + +typedef struct { + Id fileid; /* File id */ + ushort entry; /* Entry in field[] or record[] */ + ushort line; /* The line where it was defined */ + char type; /* 'd'=data, 'k'=key, 'r'=ref */ + char record[IDENT_LEN+1];/* Record name */ + char key[IDENT_LEN+1]; /* Key name. Only applicable if type is 'k' */ +} Contains; + + +typedef struct { + Id id; /* Record/Key id */ + ushort pagesize; /* Page size */ + char type; /* 'd'=data, 'v'=vlr, 'k'=key, 'r'=ref file */ + char name[FILENAME_LEN+1];/* Name of file */ + char spare[16]; +} File; + +typedef struct { + Id recid; /* The record it is a member of */ + Id keyid; /* a) Key ID if field is a key */ + /* b) If the field is of variable length */ + /* keyid denotes the size field (since a */ + /* variable length field cannot be a key)*/ + /* field.type & FT_VARIABLE */ + Id structid; /* ID of struct if type is FT_STRUCT */ + ushort offset; /* Byte offset in record */ + ushort size; /* Size of field */ + ushort elemsize; /* Size of each element */ + ushort type; /* Field type. See FT_.. constants */ + uchar nesting; /* Structure nesting. 0=first level */ + uchar spare2[4]; + char name[IDENT_LEN+1]; /* Field name */ +} Field; + +typedef struct { + Id fileid; + Id first_keyfield; /* Index of KeyField */ + Id parent; /* Only applicable if type = KT_FOREIGN */ + ushort fields; /* Number of fields in compound key */ + ushort size; /* Total key size */ + ushort null_indicator; /* ID of null indicator field of FT_OPTIONAL*/ + uchar spare[14]; + uchar type; /* See KT_... flags */ + char name[IDENT_LEN+1]; /* Name of key */ +} Key; + +typedef struct { + Id field; /* Field id */ + ushort offset; /* Offset in compound key */ + short asc; /* 1=ascending, '0'=descending */ + uchar spare[4]; +} KeyField; + +typedef struct { + Id fileid; /* Id of file record is contained in */ + Id first_field; /* Id of first field */ + Id first_key; /* Id of first key */ + Id first_foreign; /* Id of first foreign key (-1 = no foreign)*/ + Id ref_file; /* Id of reference file (-1 = no ref file) */ + Id structid; /* Id of structure defining record */ + ushort dependents; /* Number of dependent tables */ + ushort fields; /* Number of fields */ + ushort keys; /* Number of keys */ + ushort size; /* Size of record */ + ushort preamble; /* Number of bytes used preamble information*/ + uchar aux; /* Not used by Typhoon */ + uchar spare[15]; + char is_vlr; /* Is record of variable length? */ + char name[IDENT_LEN+1]; +} Record; + +typedef struct { + Id first_member; /* First struct member (Field[]) */ + Id members; /* First struct member (Field[]) */ + Id control_field; /* If is_union this is the control field */ + ushort size; /* Struct size */ + char is_union; /* Is the structure a union? */ + char name[IDENT_LEN+1]; /* Structure name */ +} Structdef; + +typedef struct { + ulong start; /* Starting number */ + ulong step; /* Number the sequence is in/decreased by */ + char asc; /* 1=ascending, '0'=descending */ + char name[IDENT_LEN+1]; /* Sequence name */ + char spare[2]; +} Sequence; + +#endif + +/* end-of-file */ diff --git a/src/ty_find.c b/src/ty_find.c new file mode 100644 index 0000000..78dacad --- /dev/null +++ b/src/ty_find.c @@ -0,0 +1,449 @@ +/*---------------------------------------------------------------------------- + * File : ty_find.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains API functions. + * + * Functions: + * report_err - Report an error to the user. + * d_keyread - Read the value of the last retrieved key. + * d_keyfind - Find a key. + * d_keymove - Perform a d_keyfrst(), d_keylast(), d_keyprev() or + * d_keynext(). + * d_recmove - Perform a d_recfrst(), d_reclast(), d_recprev() or + * d_recnext(). + * d_crread - Read the value of a field of the current record. + * d_recwrite - Update a record. + * d_recread - Read the current record. + * d_fillnew - Add a new record to the database. + * d_delete - Delete the current record. + * d_crget - Get the database address of the current record. + * d_crset - Set the database address of the current record. + * d_records - Return the number of records in a file. + * d_getkeysize - Return the size of a key. + * d_getrecsize - Return the size of a record. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +#endif +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_find.c,v 1.9 1999/10/04 04:39:42 kaz Exp $"; + +/*--------------------------- Function prototypes --------------------------*/ +static int d_keymove PRM( (Id, int); ) +static int d_recmove PRM( (Id, int); ) + + +/*-------------------------------- d_keyread -------------------------------*\ + * + * Purpose : Copies the contents of the current key into the buffer . + * + * Parameters: buf - Pointer to key buffer. This buffer must be large + * enough to hold the entire key. + * + * Returns : S_NOCR - No current record. + * S_OKAY - Key copied ok. + * + */ + +FNCLASS int d_keyread(buf) +void *buf; +{ + /* Make sure that we have a current record */ + if( db_status != S_OKAY ) + RETURN_RAP(S_NOCR); + + /* Make sure that we have performed a key operation .... + + ????????????????????????????????????? + + RETURN_RAP(S_KEYSEQ); + + */ + + /*return db_keyread(&db->key[curr-key], buf);*/ + + memcpy(buf, typhoon.curr_keybuf, DB->key[CURR_KEY].size); + + RETURN S_OKAY; +} + + + +/*-------------------------------- d_keyfind -------------------------------*\ + * + * Purpose : Find a key in an index. + * + * Parameters: id - Either key id or field id that is also a key. + * + * Returns : S_OKAY - The key was found. The record can be read by + * d_recread(). + * S_NOTFOUND - The key could not be found. + * S_NOCD - No current database. + * S_NOTKEY - The id is not a key id. + * + */ + +FNCLASS int d_keyfind(id, keyptr) +Id id; +void *keyptr; +{ + Key *key; + int rc; + + /* Make sure that a database is open */ + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + /* Determine whether this id is a key id or a compound key id */ + if( id < REC_FACTOR ) + { + if( id >= DB->header.keys ) + RETURN_RAP(S_NOTKEY); + + key = &DB->key[id]; + + CURR_RECID = DB->field[ DB->keyfield[ key->first_keyfield ].field ].recid; + } + else + { + Field *fld; + + if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY ) + return rc; + + if( !(fld->type & FT_KEY) ) + RETURN_RAP(S_NOTKEY); + + key = &DB->key[ fld->keyid ]; + } + + ty_lock(); + + CURR_KEY = key - DB->key; + + rc = ty_keyfind(key, keyptr, &CURR_REC); + + ty_unlock(); + + RETURN rc; +} + + + +/*-------------------------------- d_keymove -------------------------------*\ + * + * Purpose : Perform a d_keyfrst(), d_keylast(), d_keyprev() or d_keylast() + * on an index. This function is called the macros defined in + * typhoon.h. + * + * Parameters: id - Field id. + * direction - 0 = next, 1 = prev, 2 = first, 3 = last + * + * Returns : S_OKAY - Operation performed successfully. + * S_NOTFOUND - Not found. + * S_NOTKEY - The id is not a key. + * S_NOCD - No current database. + * + */ + +static int d_keymove(id, direction) +Id id; +int direction; +{ + static int (*movefunc[]) PRM((Key *, ulong *)) = + { ty_keynext, ty_keyprev, ty_keyfrst, ty_keylast }; + Key *key; + int rc; + + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + /* Determine whether this id is a key id or a compound key id */ + if( id < REC_FACTOR ) + { + if( id >= DB->header.keys ) + RETURN_RAP(S_NOTKEY); + + key = &DB->key[id]; + + CURR_RECID = DB->field[ DB->keyfield[ key->first_keyfield ].field ].recid; + } + else + { + Field *fld; + + if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY ) + return rc; + + if( !(fld->type & FT_KEY) ) + RETURN_RAP(S_NOTKEY); + + key = &DB->key[ fld->keyid ]; + } + + ty_lock(); + CURR_KEY = key - DB->key; + rc = (*movefunc[direction])(key, &CURR_REC); + ty_unlock(); + + RETURN rc; +} + + +FNCLASS int d_keyfrst(field) +ulong field; +{ + RETURN d_keymove(field, 2); +} + +FNCLASS int d_keylast(field) +ulong field; +{ + RETURN d_keymove(field, 3); +} + +FNCLASS int d_keynext(field) +ulong field; +{ + RETURN d_keymove(field, 0); +} + +FNCLASS int d_keyprev(field) +ulong field; +{ + RETURN d_keymove(field, 1); +} + + +/*-------------------------------- d_recmove -------------------------------*\ + * + * Purpose : Perform a d_recnext(), d_recprev(), d_recfrst() or d_reclast() + * on a data file. + * + * Parameters: record - Record id. + * direction - 0 = next, 1 = prev, 2 = first, 3 = last + * + * Returns : S_OKAY - Operation performed successfully. + * S_NOTFOUND - Not found. + * S_NOCD - No current database. + * + */ + +int d_recmove(record, direction) +Id record; +int direction; +{ + static int (*movefunc[]) PRM((Record *, void *)) = + { ty_recnext, ty_recprev, ty_recfrst, ty_reclast }; + Record *rec; + int rc; + + if( (rc = set_recfld(record, &rec, NULL)) != S_OKAY ) + return rc; + + ty_lock(); + if( (rc = (*movefunc[direction])(rec, DB->recbuf)) == S_OKAY ) + { + ty_reccurr(rec, &CURR_REC); + CURR_BUFREC = CURR_REC; /* Mark buffer as updated */ + } + else + CURR_REC = 0; + ty_unlock(); + + RETURN rc; +} + + +FNCLASS int d_recfrst(record) +ulong record; +{ + RETURN d_recmove(record, 2); +} + +FNCLASS int d_reclast(record) +ulong record; +{ + RETURN d_recmove(record, 3); +} + +FNCLASS int d_recnext(record) +ulong record; +{ + RETURN d_recmove(record, 0); +} + +FNCLASS int d_recprev(record) +ulong record; +{ + RETURN d_recmove(record, 1); +} + + + +/*-------------------------------- d_crread --------------------------------*\ + * + * Purpose : Read the contents of a field of the current record. + * + * Parameters: field - Field id. + * buf - Pointer to buffer where field will be stored. + * + * Returns : S_OKAY - Operation performed successfully. + * S_NOCD - No current database. + * S_NOCR - No current record. + * S_DELETED - The record has been deleted. + * + */ + +FNCLASS int d_crread(field, buf) +Id field; +void *buf; +{ + Record *rec; + Field *fld; + int size, rc; + + if( (rc = set_recfld(field, &rec, &fld)) != S_OKAY ) + return rc; + + if( !CURR_REC ) + RETURN_RAP(S_NOCR); + + if( (rc = update_recbuf()) != S_OKAY ) + return rc; + + if( fld->type & FT_VARIABLE ) + /* Get the value of the size field (stored in keyid) */ + size = *(ushort *)((char *)buf + DB->field[ fld->keyid ].offset) * fld->elemsize; + else + size = fld->size; + + memcpy(buf, (char *)DB->recbuf + fld->offset, (size_t) size); + + RETURN S_OKAY; +} + + + +/*--------------------------------- d_crget --------------------------------*\ + * + * Purpose : Get the database of the current record. + * + * Parameters: addr - Pointer to location where address will be stored. + * + * Returns : S_OKAY - Successful. + * + */ + +FNCLASS int d_crget(addr) +DB_ADDR *addr; +{ + addr->recno = CURR_REC; + addr->recid = INTERN_TO_RECID(CURR_RECID); + + RETURN S_OKAY; +} + + + +/*--------------------------------- d_crset --------------------------------*\ + * + * Purpose : Set the database of the current record. + * + * Parameters: addr - Pointer to address of new record. + * + * Returns : S_OKAY - Successful. + * + */ + +FNCLASS int d_crset(addr) +DB_ADDR *addr; +{ + ulong recid = addr->recid; + + recid = RECID_TO_INTERN(addr->recid); + + /* Validate record id */ + if( recid >= DB->header.records ) + RETURN S_INVREC; + + CURR_REC = addr->recno; + CURR_RECID = recid; + + RETURN S_OKAY; +} + + + +/*-------------------------------- d_records -------------------------------*\ + * + * Purpose : Return the number of records in a file. + * + * Parameters: record - Record id. + * number - Pointer to location where the value will be stored. + * + * Returns : S_OKAY - Number of records stored in . + * S_NOCD - No current database. + * S_INVREC - Invalid record id. + * + */ + +FNCLASS int d_records(record, number) +Id record; +ulong *number; +{ + Record *rec; + int rc; + + if( (rc = set_recfld(record, &rec, NULL)) != S_OKAY ) + return rc; + + RETURN ty_reccount(rec, number); +} + + +FNCLASS int d_seterrfn(fn) +void (*fn) PRM( (int, long); ) +{ + typhoon.ty_errfn = fn; + return S_OKAY; +} + + +/* end-of-file */ diff --git a/src/ty_glob.h b/src/ty_glob.h new file mode 100644 index 0000000..bdebea4 --- /dev/null +++ b/src/ty_glob.h @@ -0,0 +1,103 @@ +/*---------------------------------------------------------------------------- + * File : ty_glob.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains the global variables for the library. + * + * $Id: ty_glob.h,v 1.6 1999/10/04 03:45:07 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +/*---------------------------------- OS/2 ----------------------------------*\ + * + * If Typhoon is compiled as an OS/2 DLL, the functions must be a special + * class in order to be known to other programs. + * + */ + +#ifndef TYPHOON_TY_GLOB_H +#define TYPHOON_TY_GLOB_H + +#ifndef TYPHOON_TY_TYPE_H +#include "ty_type.h" +#endif + +#ifdef CONFIG_OS2 +# define INCL_NOPMAPI +# include +# define FNCLASS APIRET EXPENTRY +#else +# define FNCLASS +# define VAR +#endif + +#ifdef DEFINE_GLOBALS + +TyphoonGlobals typhoon = { + { { { 0 } } }, /* dbtab */ + NULL, /* db */ + 0, /* do_rebuild */ + 0, /* dbs_open */ + 0, /* cur_open */ + 20, /* max_open */ + { 0 }, /* curr_keybuf */ + 0, /* curr_key */ + -1, /* curr_db */ + NULL, /* ty_errfn */ + { '.', CONFIG_DIR_SWITCH, 0 }, /* dbfpath */ + { '.', CONFIG_DIR_SWITCH, 0 } /* dbdpath */ +}; + +int db_status = 0; /* Status code */ +long db_subcode = 0; /* Sub error code */ + +#else + + +extern TyphoonGlobals typhoon; +extern int db_status; +extern long db_subcode; + + +#endif + +extern CMPFUNC keycmp[]; /* Comparison function table */ + + +#define DB typhoon.db +#define CURR_DB typhoon.curr_db +#define CURR_KEY typhoon.curr_key +#define CURR_KEYBUF typhoon.curr_keybuf +#define CURR_REC typhoon.db->curr_rec +#define CURR_RECID typhoon.db->curr_recid +#define CURR_BUFREC typhoon.db->curr_bufrec +#define CURR_BUFRECID typhoon.db->curr_bufrecid + +#endif + +/* end-of-file */ + diff --git a/src/ty_ins.c b/src/ty_ins.c new file mode 100644 index 0000000..01e206c --- /dev/null +++ b/src/ty_ins.c @@ -0,0 +1,534 @@ +/*---------------------------------------------------------------------------- + * File : ty_ins.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains API functions. + * + * Functions: + * report_err - Report an error to the user. + * d_keyread - Read the value of the last retrieved key. + * d_keyfind - Find a key. + * d_keymove - Perform a d_keyfrst(), d_keylast(), d_keyprev() or + * d_keynext(). + * d_recmove - Perform a d_recfrst(), d_reclast(), d_recprev() or + * d_recnext(). + * d_crread - Read the value of a field of the current record. + * d_recwrite - Update a record. + * d_recread - Read the current record. + * d_fillnew - Add a new record to the database. + * d_delete - Delete the current record. + * d_crget - Get the database address of the current record. + * d_crset - Set the database address of the current record. + * d_records - Return the number of records in a file. + * d_getkeysize - Return the size of a key. + * d_getrecsize - Return the size of a record. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +#endif +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#define DEFINE_GLOBALS +#include "ty_glob.h" +#include "ty_prot.h" +#include "ty_log.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_ins.c,v 1.7 1999/10/03 23:28:29 kaz Exp $"; + +int report_err(v) +int v; +{ + if( typhoon.ty_errfn ) + typhoon.ty_errfn(db_status, db_subcode); + else + { +#ifdef CONFIG_UNIX + printf("** pid %d - db_status = %d **\n", getpid(), db_status = v); +#endif +#ifdef CONFIG_OS2 + printf("** db_status = %d **\n", db_status = v); +#endif + } + + return v; +} + + + +/*--------------------------------- d_block ---------------------------------*\ + * + * Purpose : Requests exclusive access to the API. The second time another + * thread in a program calls this function it will block, until + * d_unblock() is called. + * + * Parameters: None. + * + * Returns : S_OKAY - Ok. + * + */ + +FNCLASS int d_block() +{ +#ifdef CONFIG_OS2 + os2_block(); +#endif + + RETURN S_OKAY; +} + + + +FNCLASS int d_unblock() +{ +#ifdef CONFIG_OS2 + os2_unblock(); +#endif + + RETURN S_OKAY; +} + + +/*------------------------------- d_recwrite -------------------------------*\ + * + * Purpose : Updates the contents of the current record. + * + * Parameters: buf - Buffer containing current record. + * + * Returns : S_OKAY - Operation performed successfully. + * S_DUPLICATE - The record contained a duplicate key. db_subcode + * contains the id of the conflicting field or key. + * S_NOCD - No current database. + * S_NOCR - No current record. + * S_RECSIZE - Invalid record size (if variable size). + * db_subcode contains the ID of the size field. + * S_FOREIGN - A foreign key was not found (db_subcode holds + * the foreing key ID). + * S_RESTRICT - The primary key was updated, but a dependent + * table with restrict rule had a record which + * referenced the record to be deleted. + * (db_subcode holds the foreign key ID). + * + */ +FNCLASS int d_recwrite(buf) +void *buf; +{ + Record *rec; + Key *key; + Key *keyptr[RECKEYS_MAX]; + int keys_changed=0, rc; + int n; + ulong ref; + + /* Set pointers to current record and first field */ + if( (rc = set_recfld((Id) -1, &rec, NULL)) != S_OKAY ) + return rc; + + ty_lock(); + if( (rc = update_recbuf()) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + /* Check foreign keys (if any) */ + if( (rc = check_foreign_keys(rec, buf, 0)) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + /* Check dependent tables (if any) */ + if( (rc = check_dependent_tables(rec, buf, 'u')) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + /* Have any keys changed? */ + key = DB->key + rec->first_key; + + /* Find out which keys have changed and must be updated. An optional key + * that has changed from null to not null, or not null to null, or + * has changed its value is regarded as changed. + */ + + for( n = rec->keys; n-- && KT_GETBASIC(key->type) != KT_FOREIGN; key++ ) + { + if( reckeycmp(key, buf, DB->recbuf) ) + { + keyptr[keys_changed++] = key; + + if( key->type & KT_UNIQUE ) + { + if( (key->type & KT_OPTIONAL) && null_indicator(key, buf) ) + continue; + + if( keyfind(key, buf, &ref) == S_OKAY ) + { + set_subcode(key); + ty_unlock(); + RETURN S_DUPLICATE; + } + } + } + } + + for( n=0; nrecbuf) ) + { + /* Don't remove null keys */ + if( !(key->type & KT_OPTIONAL) || !null_indicator(key, DB->recbuf) ) + keydel(key, DB->recbuf, CURR_REC); + + /* Don't insert null keys */ + if( (key->type & KT_OPTIONAL) && null_indicator(key, buf) ) + continue; + + if( (rc = keyadd(key, buf, CURR_REC)) != S_OKAY ) + { + set_subcode(key); + ty_unlock(); + RETURN rc; + } + } + } + + if( rec->is_vlr ) + { + unsigned size; + + if( (rc = compress_vlr(COMPRESS, rec, DB->recbuf, buf, &size)) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + ty_vlrwrite(rec, DB->real_recbuf, size, CURR_REC); + } + else + { + memcpy(DB->recbuf, buf, rec->size); + + ty_recwrite(rec, DB->real_recbuf, CURR_REC); + } + + /* Store changed references to parent records */ + update_foreign_keys(rec, 0); + +#ifdef CONFIG_UNIX + if( DB->logging ) + ty_log('u'); + + log_update(CURR_RECID, CURR_REC, rec->size, buf); +#endif + + ty_unlock(); + + RETURN S_OKAY; +} + + + +/*-------------------------------- d_recread -------------------------------*\ + * + * Purpose : Read contents of the current record. + * + * Parameters: buf - Buffer containing current record. + * + * Returns : S_OKAY - Operation performed successfully. + * S_DELETED - Record has been deleted since last accessed. + * S_NOCD - No current database. + * S_NOCR - No current record. + * + */ + +FNCLASS int d_recread(buf) +void *buf; +{ + Record *rec; + int rc; + + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + if( CURR_REC == 0 ) + RETURN_RAP(S_NOCR); + + ty_lock(); + rec = DB->record + CURR_RECID; + + if( (rc = update_recbuf()) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + if( rec->is_vlr ) + rc = compress_vlr(UNCOMPRESS, rec, buf, DB->recbuf, NULL); + else + { + memcpy(buf, DB->recbuf, rec->size); + rc = S_OKAY; + } + + ty_unlock(); + + RETURN rc; +} + + + +/*------------------------------- d_fillnew --------------------------------*\ + * + * Purpose : Adds a new record to the current database and updates indexes. + * + * Parameters: record - Record id. + * buf - Pointer to record buffer. + * + * Returns : S_OKAY - Ok. + * S_NOCD - No current database. + * S_INVREC - Invalid record id. + * S_DUPLICATE - The record contained a duplicate key. The id + * of the field or compound key is stored in + * db_subcode. + * S_RECSIZE - Invalid record size (if variable size). + * db_subcode contains the ID of the size field. + * S_FOREIGN - A foreign key was not found (db_subcode holds + * the foreing key ID. + * + */ + +FNCLASS int d_fillnew(record, buf) +Id record; +void *buf; +{ + Record *rec; + Field *fld; + Key *key; + ulong ref; + int rc, n; + + if( (rc = set_recfld(record, &rec, &fld)) != S_OKAY ) + return rc; + + /* So far we have no current record */ + CURR_REC = 0; + + ty_lock(); + + /* Set pointer to actual data */ + DB->recbuf = DB->real_recbuf + rec->preamble; + + /* Check foreign keys (if any) */ + if( (rc = check_foreign_keys(rec, buf, 1)) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + /* Make sure that there are no duplicate keys in this record */ + key = DB->key + rec->first_key; + + for( n = rec->keys; n-- && !KEY_ISFOREIGN(key); key++ ) + { + if( key->type & KT_UNIQUE ) + { + if( KEY_ISOPTIONAL(key) && null_indicator(key, buf) ) + continue; + + if( keyfind(key, buf, &ref) == S_OKAY ) + { + set_subcode(key); + ty_unlock(); + RETURN S_DUPLICATE; + } + } + } + + /* Update data and index files */ + if( rec->is_vlr ) + { + unsigned size; + + if( (rc = compress_vlr(COMPRESS, rec, DB->recbuf, buf, &size)) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + if( (rc = ty_vlradd(rec, DB->real_recbuf, size, &CURR_REC)) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + /* The record in DB->recbuf is still compressed */ + } + else + { + memcpy(DB->recbuf, buf, rec->size); + if( (rc=ty_recadd(rec, DB->real_recbuf, &CURR_REC)) != S_OKAY ) + { + ty_unlock(); + return rc; + } + } + + CURR_RECID = rec - DB->record; + n = rec->keys; + key = DB->key + rec->first_key; + + for( n=rec->keys; n-- && !KEY_ISFOREIGN(key); key++ ) + { + /* Don't store null keys */ + if( KEY_ISOPTIONAL(key) && null_indicator(key, buf) ) + continue; + + if( (rc = keyadd(key, buf, CURR_REC)) != S_OKAY ) + { + ty_unlock(); + RETURN rc; + } + } + + /* Store references to parent records */ + update_foreign_keys(rec, 1); + +#ifdef CONFIG_UNIX + if( DB->logging ) + ty_log('u'); + + log_update(CURR_RECID, CURR_REC, rec->size, buf); +#endif + + ty_unlock(); + + RETURN S_OKAY; +} + + +/*-------------------------------- d_delete --------------------------------*\ + * + * Purpose : Delete the current record. + * + * Parameters: None. + * + * Returns : S_OKAY - The was successfully deleted. + * S_NOCD - No current database. + * S_NOCR - No current record. + * S_RESTRICT - A dependent table had a foreign key which + * referenced the primary key of the record to + * to be deleted. db_subcode holds the foreign + * key ID. + * + */ + +FNCLASS int d_delete() +{ + Record *rec; + Key *key; + int n, rc; + + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + if( CURR_REC == 0 ) + RETURN_RAP(S_NOCR); + + /* We must update recbuf in order to access the record's keys */ + ty_lock(); + + rec = DB->record + CURR_RECID; + DB->recbuf = DB->real_recbuf + rec->preamble; + + if( (rc = update_recbuf()) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + /* Check dependent tables (if any) */ + if( (rc = check_dependent_tables(rec, DB->recbuf, 'd')) != S_OKAY ) + { + ty_unlock(); + return rc; + } + + key = DB->key + rec->first_key; + + if( DB->fh[rec->fileid].any->type == 'd' ) + rc = ty_recdelete(rec, CURR_REC); + else + rc = ty_vlrdel(rec, CURR_REC); + + if( rc != S_OKAY ) + { + ty_unlock(); + RETURN rc; + } + + for( n=rec->keys; n-- && !KEY_ISFOREIGN(key); key++ ) + { + if( KEY_ISOPTIONAL(key) && null_indicator(key, DB->recbuf) ) + continue; + + if( (rc = keydel(key, DB->recbuf, CURR_REC)) != S_OKAY ) + { + printf("typhoon: could not delete key %s.%s (db_status %d)\n", + rec->name, key->name, rc); + ty_unlock(); + RETURN rc; + } + } + + delete_foreign_keys(rec); + +#ifdef CONFIG_UNIX + if( DB->logging ) + ty_log('d'); + + log_delete(CURR_RECID, CURR_REC); +#endif + + CURR_REC = 0; + + ty_unlock(); + + RETURN S_OKAY; +} + +/* end-of-file */ diff --git a/src/ty_io.c b/src/ty_io.c new file mode 100644 index 0000000..e0ab69d --- /dev/null +++ b/src/ty_io.c @@ -0,0 +1,628 @@ +/*---------------------------------------------------------------------------- + * File : bt_io + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * This file contains the layer between the low-level B-tree and record + * routines and the Typhoon API functions. The low-level routines are never + * called directly from the API functions. + * + * +-----------------------------+ + * | Typhoon API functions | + * +-----------------------------+ + * | Dynamic open files layer | <----- this layer + * +----------+------------------+ + * | B-tree | Record | VLR | + * +----------+------------------+ + * | C language read/write | + * +-----------------------------+ + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +#endif +#include +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_io.c,v 1.9 1999/10/04 03:45:08 kaz Exp $"; + +#define PTRTORECID(rec) ((((rec) - DB->record)+1) * REC_FACTOR) + +/*-------------------------- Function prototypes ---------------------------*/ +static int checkfile PRM( (Id); ) + +/*---------------------------- Global variables ----------------------------*/ +static ulong seqno = 1; /* Current sequence number (always > 0) */ + + + + +int ty_closeafile() +{ + ulong lowest_seqno = seqno; + int dbs, fhs; /* Used for scanning tables */ + Dbentry *db =typhoon.dbtab; /* Used for scanning tables */ + Fh *fh; /* Used for scanning tables */ + Fh *foundfh = NULL; /* The filehandle found to be closed */ + + /* Find the file least recently accessed open file */ + dbs = DB_MAX; + + while( dbs-- ) + { + if( db->clients ) + { + fh = db->fh; + fhs = db->header.files; + + while( fhs-- ) + { + if( fh->any && fh->any->fh != -1 ) + { + if( lowest_seqno > fh->any->seqno ) + { + lowest_seqno = fh->any->seqno; + foundfh = fh; + } + } + fh++; + } + } + db++; + } + + if( foundfh == NULL ) + { + printf("\a*** Could not close a file **"); + return -1; + } + + /* If the file is already closed, we just return */ + if( foundfh->any->fh == -1 ) + return S_OKAY; + + switch( foundfh->any->type ) + { + case 'k': + case 'r': + btree_dynclose(foundfh->key); + break; + case 'd': + rec_dynclose(foundfh->rec); + break; + case 'v': + vlr_dynclose(foundfh->vlr); + break; + } + + /* Mark the file as closed */ + typhoon.cur_open--; + + return 0; +} + + + + +static int checkfile(fileid) +Id fileid; +{ + Fh *fh; + int rc; + + fh = &DB->fh[fileid]; + + /* If the file is open we set the file's sequence number to seqno to + * indicate that it is the most recently accessed file. + */ + if( fh->any->fh != -1 ) + { + fh->any->seqno = seqno++; + return S_OKAY; + } + + if( typhoon.cur_open == typhoon.max_open ) + { + if( ty_closeafile() == -1 ) + { + puts("checkfile: could not find a file to close"); + RETURN S_IOFATAL; + } + } + + /* Now should be less than so we can call + * ty_openfile() to reopen the file. If ty_openfile() cannot open the + * file the API function will return S_IOFATAL. + */ + + switch( fh->any->type ) + { + case 'k': + case 'r': + rc = btree_dynopen(fh->key); + break; + case 'd': + rc = rec_dynopen(fh->rec); + break; + case 'v': + rc = vlr_dynopen(fh->vlr); + break; + } + + fh->any->seqno = seqno++; + typhoon.cur_open++; + + return rc; +} + + +/*------------------------------ ty_openfile -------------------------------*\ + * + * Purpose : Opens a database file. + * + * Parameters: fp - Pointer to file definition table entry. + * fh - Pointer to file handle table entry. + * shared - Open in shared mode? + * + * Returns : db_status from d_keyopen(), d_recopen() or vlr_open(). + * + */ + +int ty_openfile(fp, fh, shared) +File *fp; +Fh *fh; +int shared; +{ + char fname[255]; + Key *key; + CMPFUNC cmp; + + /* If the maximum number of open files has been reached we return the + * CLOSEDPTR to indicate to the other ty_.. functions that the file + * descriptor points to a closed file and must be reopened. + */ + if( typhoon.cur_open == typhoon.max_open ) + { + if( ty_closeafile() == -1 ) + { + puts("ty_openfile: could not find a file to close"); + RETURN S_IOFATAL; + } + } + + /* File is located in dbfpath> */ + sprintf(fname, "%s%s", DB->dbfpath, fp->name); + + switch( fp->type ) + { + case 'r': + fh->key = btree_open(fname, sizeof(REF_ENTRY), fp->pagesize, (CMPFUNC)refentrycmp, 0, shared); + break; + case 'k': + key = DB->key + fp->id; + + /* If the key has multiple fields or is sorted in descending order + * we use the compoundkeycmp function for key value comparisons. + */ + if( key->fields > 1 || !DB->keyfield[key->first_keyfield ].asc ) + cmp = compoundkeycmp; + else + { + Field *fld = &DB->field[ DB->keyfield[key->first_keyfield].field ]; + + cmp = keycmp[ fld->type & (FT_BASIC|FT_UNSIGNED) ]; + } + + fh->key = btree_open(fname, key->size, fp->pagesize, cmp, + (key->type & KT_UNIQUE) ? 0 : 1, shared); + break; + case 'd': + /* Add the preamble to the size of the record */ + fh->rec = rec_open(fname, (unsigned) (DB->record[fp->id].size + + DB->record[fp->id].preamble), shared); + break; + case 'v': + fh->vlr = vlr_open(fname, fp->pagesize, shared); + break; + } + + if( db_status == S_OKAY ) + { + fh->any->type = fp->type; + fh->any->seqno = seqno++; + typhoon.cur_open++; + } +/* + else + printf("cannot open '%s' (db_status %d, errno %d)\n", fname, db_status, errno); +*/ + return db_status; +} + + +/*------------------------------- d_closefile ------------------------------*\ + * + * Purpose : Closes a database file. + * + * Parameters: fh - Pointer to file handle table entry. + * + * Returns : db_status from d_keyclose(), d_recclose() or vlr_close(). + * + */ + +int ty_closefile(fh) +Fh *fh; +{ + /* If the file is already closed, we just return */ + if( fh->any->fh != -1 ) + typhoon.cur_open--; + + switch( fh->any->type ) + { + case 'k': + case 'r': + btree_close(fh->key); + break; + case 'd': + rec_close(fh->rec); + break; + case 'v': + vlr_close(fh->vlr); + break; + } + + return db_status; +} + + + +int ty_keyadd(key, value, ref) +Key *key; +void *value; +ulong ref; +{ + INDEX *idx; + int rc; + + if( (rc = checkfile(key->fileid)) != S_OKAY ) + return rc; + + idx = DB->fh[key->fileid].key; + rc = btree_add(idx, value, ref); + btree_keyread(idx, CURR_KEYBUF); + + return rc; +} + + +int ty_keyfind(key, value, ref) +Key *key; +void *value; +ulong *ref; +{ + INDEX *idx; + int rc; + + if( (rc = checkfile(key->fileid)) != S_OKAY ) + return rc; + + idx = DB->fh[key->fileid].key; + rc = btree_find(idx, value, ref); + btree_keyread(idx, CURR_KEYBUF); + + return rc; +} + + + +int ty_keyfrst(key, ref) +Key *key; +ulong *ref; +{ + INDEX *idx; + int rc; + + if( (rc = checkfile(key->fileid)) != S_OKAY ) + return rc; + + idx = DB->fh[key->fileid].key; + rc = btree_frst(idx, ref); + btree_keyread(idx, CURR_KEYBUF); + + return rc; +} + + +int ty_keylast(key, ref) +Key *key; +ulong *ref; +{ + INDEX *idx; + int rc; + + if( (rc = checkfile(key->fileid)) != S_OKAY ) + return rc; + + idx = DB->fh[key->fileid].key; + rc = btree_last(idx, ref); + btree_keyread(idx, CURR_KEYBUF); + + return rc; +} + + +int ty_keyprev(key, ref) +Key *key; +ulong *ref; +{ + INDEX *idx; + int rc; + + if( (rc = checkfile(key->fileid)) != S_OKAY ) + return rc; + + idx = DB->fh[key->fileid].key; + rc = btree_prev(idx, ref); + btree_keyread(idx, CURR_KEYBUF); + + return rc; +} + + +int ty_keynext(key, ref) +Key *key; +ulong *ref; +{ + INDEX *idx; + int rc; + + if( (rc = checkfile(key->fileid)) != S_OKAY ) + return rc; + + idx = DB->fh[key->fileid].key; + rc = btree_next(idx, ref); + btree_keyread(idx, CURR_KEYBUF); + + return rc; +} + + +int ty_keydel(key, value, ref) +Key *key; +void *value; +ulong ref; +{ + INDEX *idx; + int rc; + + if( (rc = checkfile(key->fileid)) != S_OKAY ) + return rc; + + idx = DB->fh[key->fileid].key; + rc = btree_del(idx, value, ref); + btree_keyread(idx, CURR_KEYBUF); + + return rc; +} + + +int ty_recadd(rec, buf, recno) +Record *rec; +void *buf; +ulong *recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_add(DB->fh[rec->fileid].rec, buf, recno); +} + + +int ty_recwrite(rec, buf, recno) +Record *rec; +void *buf; +ulong recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_write(DB->fh[rec->fileid].rec, buf, recno); +} + + +int ty_recread(rec, buf, recno) +Record *rec; +void *buf; +ulong recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_read(DB->fh[rec->fileid].rec, buf, recno); +} + + +int ty_recdelete(rec, recno) +Record *rec; +ulong recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_delete(DB->fh[rec->fileid].rec, recno); +} + + +int ty_recfrst(rec, buf) +Record *rec; +void *buf; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_frst(DB->fh[rec->fileid].rec, buf); +} + + +int ty_reclast(rec, buf) +Record *rec; +void *buf; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_last(DB->fh[rec->fileid].rec, buf); +} + + +int ty_recnext(rec, buf) +Record *rec; +void *buf; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_next(DB->fh[rec->fileid].rec, buf); +} + +int ty_recprev(rec, buf) +Record *rec; +void *buf; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_prev(DB->fh[rec->fileid].rec, buf); +} + +ulong ty_reccount(rec, count) +Record *rec; +ulong *count; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_numrecords(DB->fh[rec->fileid].rec, count); +} + +int ty_reccurr(rec, recno) +Record *rec; +ulong *recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return rec_curr(DB->fh[rec->fileid].rec, recno); +} + +int ty_vlradd(rec, buf, size, recno) +Record *rec; +void *buf; +unsigned size; +ulong *recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return vlr_add(DB->fh[rec->fileid].vlr, buf, size + rec->preamble, recno); +} + +int ty_vlrwrite(rec, buf, size, recno) +Record *rec; +void *buf; +unsigned size; +ulong recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return vlr_write(DB->fh[rec->fileid].vlr, buf, size + rec->preamble, recno); +} + + +unsigned ty_vlrread(rec, buf, recno, size) +Record *rec; +void *buf; +ulong recno; +unsigned *size; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return vlr_read(DB->fh[rec->fileid].vlr, buf, recno, size); +} + +int ty_vlrdel(rec, recno) +Record *rec; +ulong recno; +{ + int rc; + + if( (rc = checkfile(rec->fileid)) != S_OKAY ) + return rc; + + return vlr_del(DB->fh[rec->fileid].vlr, recno); +} + +/* end-of-file */ diff --git a/src/ty_lock.c b/src/ty_lock.c new file mode 100644 index 0000000..4d75496 --- /dev/null +++ b/src/ty_lock.c @@ -0,0 +1,48 @@ +/*---------------------------------------------------------------------------- + * File : @(#)ty_lock.c 93/11/04 Copyright (c) 1991-93 CT Danmark + * Library : typhoon + * Compiler: UNIX C, Turbo C, Microsoft C + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Description: + * Locking functions. + * + * Functions: + * d_reclock + * d_recunlock + * d_tablock + * d_tabunlock + * + * History + * ------------------------------ + * 23-Dec-1993 tbp Initial version. + * + *--------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: ty_lock.c,v 1.3 1999/10/03 23:28:29 kaz Exp $"; + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +#endif +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" + + +FNCLASS d_reclock() +{ +} + + +FNCLASS d_recunlock() +{ +} + + +/* end-of-file */ diff --git a/src/ty_log.c b/src/ty_log.c new file mode 100644 index 0000000..a3bcb9a --- /dev/null +++ b/src/ty_log.c @@ -0,0 +1,216 @@ +/*---------------------------------------------------------------------------- + * File : ty_log.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains function that write to the Replication Server log. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" +#include "ty_log.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_log.c,v 1.6 1999/10/04 03:45:08 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ +static int do_log PRM( (ulong, ulong); ) + + +/*---------------------------- Global variables ----------------------------*/ +static int log_fh = -1; /* Log file handle */ +static int log_trans = 0; /* True if the current transaction was */ + /* started during backup */ + +/*--------------------------------------------------------------------------*\ + * + * Function : do_log + * + * Purpose : This function checks whether an operation should be logged or + * not. If yes, the log file is opened if not already open. + * If the backup is no longer active, the log file is closed. + * + * Parameters: + * + * Returns : + * + */ +static int do_log(recid, recno) +ulong recid, recno; +{ + if( !DB->shm->backup_active && !log_trans ) + { + if( log_fh != -1 ) + { + close(log_fh); + log_fh = -1; + } + return -1; + } + + if( recid > DB->shm->curr_recid && recno > DB->shm->curr_recno ) + return -1; + + if( log_fh == -1 ) + { + log_fh = os_open(LOG_FNAME, O_RDWR|O_APPEND|O_CREAT, CONFIG_CREATMASK); + + if( log_fh == -1 ) + { + puts("cannot open log"); + return -1; + } + } + + return 0; +} + + + +/*--------------------------------------------------------------------------*\ + * + * Function : Log a record update. + * + * Purpose : + * + * Parameters: + * + * Returns : + * + */ +int log_update(recid, recno, size, data) +ulong recid; +ulong recno; +unsigned size; +void *data; +{ + LogUpdate update; + + if( do_log(recid, recno) == -1 ) + return 0; + + os_lock(log_fh, 0, 1, 'u'); + +#ifdef CONFIG_RISC + if( size & (sizeof(long)-1) ) + size += sizeof(long) - (size & (sizeof(long)-1)); +#endif + + update.id = LOG_UPDATE; + update.len = size + sizeof update; + update.recid = recid; + update.recno = recno; + + write(log_fh, &update, sizeof update); + write(log_fh, data, size); + + os_unlock(log_fh, 0, 1); + + return 0; +} + + +int log_delete(recid, recno) +ulong recid, recno; +{ + LogDelete delete; + + if( do_log(recid, recno) == -1 ) + return 0; + + os_lock(log_fh, 0, 1, 'u'); + + delete.id = LOG_UPDATE; + delete.len = sizeof delete; + delete.recid = recid; + delete.recno = recno; + + write(log_fh, &delete, sizeof delete); + + os_unlock(log_fh, 0, 1); + + return 0; +} + +#if UNUSED_CODE +/*--------------------------------------------------------------------------*\ + * + * Function : + * + * Purpose : + * + * Parameters: + * + * Returns : + * + */ +FNCLASS int d_beginwork() +{ + if( DB->shm->backup_active ) + { + DB->shm->num_trans_active++; + log_trans = 1; + } + + return S_OKAY; +} + + +FNCLASS int d_commitwork() +{ + if( log_trans ) + { + log_trans = 0; + DB->shm->num_trans_active--; + } + + return S_OKAY; +} + +#endif + + +FNCLASS int d_abortwork() +{ + if( log_trans ) + { + log_trans =0 ; + DB->shm->num_trans_active--; + } + + return S_OKAY; +} + +/* end-of-file */ diff --git a/src/ty_log.h b/src/ty_log.h new file mode 100644 index 0000000..4463ada --- /dev/null +++ b/src/ty_log.h @@ -0,0 +1,155 @@ +/*---------------------------------------------------------------------------- + * File : ty_log.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains the definitions used for online backup. + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_TY_LOG_H +#define TYPHOON_TY_LOG_H + +#ifndef TYPHOON_ENVIRON_H +#include "environ.h" +#endif + +#ifndef TYPHOON_TY_GLOB_H +#include "ty_glob.h" +#endif + +#define LOG_FNAME "typhoon.log" +#define LOG_UPDATE 1 +#define LOG_DELETE 2 + +typedef struct { + short id; /* = LOG_INSERT */ + ushort len; /* Length of entire block */ + ulong recid; + ulong recno; +/* char data[1];*/ +} LogUpdate; + +typedef struct { + short id; /* = LOG_DELETE */ + ushort len; /* Length of entire block */ + ulong recid; + ulong recno; +} LogDelete; + + +#define ARCHIVE_MEDIA 1 +#define ARCHIVE_RECORD 2 +#define ARCHIVE_FILE 3 +#define ARCHIVE_FILEDATA 4 +#define ARCHIVE_TABLE 5 +#define ARCHIVE_END 6 + +#define ARCHIVE_BLOCK 99 + +typedef struct { + ulong id; /* = ARCHIVE_MEDIA */ + char dbname[DBNAME_LEN+1]; + char spare[3]; + long date; /* Date of backup */ + ulong seqno; /* Media number in backup */ + char spare2[512]; +} ArchiveMediaHeader; + +typedef struct { + ulong id; /* = ARCHIVE_RECORD */ + ulong recid; + ulong recno; +} ArchiveRecordHeader; + +typedef struct { + ulong id; /* = ARCHIVE_FILE */ + ulong recsize; + char table[IDENT_LEN+1]; + char fname[128]; +} ArchiveTableHeader; + +typedef struct { + ulong id; /* = ARCHIVE_FILE */ + char fname[128]; +} ArchiveFileHeader; + +typedef struct { + ulong id; /* = ARCHIVE_FILEDATA */ + ulong size; +} ArchiveFileDataHeader; + +typedef struct { + ulong id; /* = ARCHIVE_END */ +} ArchiveEnd; + +typedef struct { + ulong id; + ulong size; +} ArchiveBlockHeader; + + +/* + Archive structure + ============================== + + [MediaHeader] + + [FileHeader] ----+ + [FileDataHeader] | + data +--- dbd-file + [FileDataHeader] | + data | + ... | + [FileDataHeader size=0] ----+ + + [TableHeader] ----+ + [RecordHeader] | + record | + [RecordHeader] +--- database + record | + ... | + [RecordHeader] | + record ----+ + + [FileHeader] ----+ + [FileDataHeader] | + data +--- log file + [FileDataHeader] | + data | + ... | + [FileDataHeader size=0] ----+ + + [End] +*/ + +FNCLASS int d_abortwork PRM ( (void); ) +int log_update PRM ( (ulong recid, ulong recno, unsigned size, void *data); ) +int log_delete PRM ( (ulong recid, ulong recno); ) + +#endif + +/* end-of-file */ diff --git a/src/ty_open.c b/src/ty_open.c new file mode 100644 index 0000000..e15ebe4 --- /dev/null +++ b/src/ty_open.c @@ -0,0 +1,633 @@ +/*---------------------------------------------------------------------------- + * File : ty_open.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * + * Functions: + * Contains functions for opening and closing the database. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +# include +# ifdef __STDC__ +# include +# endif +# define DIR_SWITCH '/' +#else +# include +# include +# define DIR_SWITCH '\\' +#endif +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" +#include "ty_log.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_open.c,v 1.8 1999/10/04 03:45:08 kaz Exp $"; + +/*--------------------------- Function prototypes --------------------------*/ +static void fixpath PRM( (char *, char *); ) + int read_dbdfile PRM( (Dbentry *, char *); ) +static int perform_rebuild PRM( (unsigned); ) + + +static void (*rebuildverbose_fn) PRM((char *, ulong, ulong);) + + +static void fixpath(path, s) +char *path, *s; +{ + int len = strlen(s); + + if( len > 0 ) + { + strcpy(path, s); + + if( path[len-1] != DIR_SWITCH ) + { + path[len++] = DIR_SWITCH; + path[len] = 0; + } + } + else + { + path[0] = '.'; + path[1] = DIR_SWITCH; + path[2] = 0; + } +} + + + +/*-------------------------------- d_dbdpath -------------------------------*\ + * + * Purpose : Set the path of the dbd-file. This function is called prior + * d_open(). + * + * Parameters: path - The path of the dbd-file. + * + * Returns : S_OKAY - Successful. + * + */ + + +FNCLASS int d_dbdpath(path) +char *path; +{ + fixpath(typhoon.dbdpath, path); + + return S_OKAY; +} + + + +/*-------------------------------- d_dbfpath -------------------------------*\ + * + * Purpose : Set the path of the database files. This function is called + * prior to d_open(). + * + * Parameters: path - The path of the database files. + * + * Returns : S_OKAY - Successful. + * + */ + + +FNCLASS int d_dbfpath(path) +char *path; +{ + fixpath(typhoon.dbfpath, path); + + return S_OKAY; +} + + + +/*--------------------------------- d_dbget --------------------------------*\ + * + * Purpose : Gets the current database ID. + * + * Parameters: id - Pointer to database ID. + * + * Returns : S_OKAY - The database ID is stored in . + * S_NOCD - There is no current database. + * + */ + +FNCLASS int d_dbget(id) +int *id; +{ + if( CURR_DB == -1 ) + RETURN S_NOCD; + + *id = CURR_DB; + + RETURN S_OKAY; +} + + + +/*--------------------------------- d_dbset --------------------------------*\ + * + * Purpose : Sets the current database ID. + * + * Parameters: id - Database ID obtained by call to d_dbget(). + * + * Returns : S_OKAY - The database ID is stored in . + * S_INVDB - Invalid database ID. + * + */ + +FNCLASS int d_dbset(id) +int id; +{ + /* Ensure that the id is valid */ + if( id < 0 || id >= DB_MAX ) + RETURN S_INVDB; + + /* Ensure that the database is actually open */ + if( typhoon.dbtab[id].clients == 0 ) + RETURN S_INVDB; + + DB->db_status = db_status; + + typhoon.curr_db = id; + typhoon.db = typhoon.dbtab + id; + + db_status = DB->db_status; + + RETURN S_OKAY; +} + + +/*------------------------------- d_setfiles -------------------------------*\ + * + * Purpose : Set the maximum number of open files. + * + * Parameters: maxfiles - The maximum number of open files allowed. + * + * Returns : S_OKAY - Ok. + * S_INVPARM- The maximum is invalid. + * + */ +FNCLASS int d_setfiles(maxfiles) +int maxfiles; +{ + if( maxfiles < 2 ) + RETURN S_INVPARM; + + if( maxfiles < typhoon.max_open ) + { + /* maxfiles is less than max_open, so we need minimize the number + * of open files right away. + */ + int diff = typhoon.max_open - maxfiles; + + while( typhoon.cur_open > maxfiles && diff-- ) + ty_closeafile(); + + /* If it was not possible to closed the required number of files + * anyway, an error code is returned + */ + if( typhoon.cur_open > maxfiles ) + RETURN S_INVPARM; + } + + typhoon.max_open = maxfiles; + + RETURN S_OKAY; +} + + + +FNCLASS int d_keybuild(fn) +void (*fn)PRM((char *, ulong, ulong);) +{ + typhoon.do_rebuild = 1; + rebuildverbose_fn = fn; + return S_OKAY; +} + + +static int perform_rebuild(biggest_rec) +unsigned biggest_rec; +{ + ulong recno; + ulong recid; + ulong datasize; + ulong records; + char *buf; + int fh, i; + Record *rec; + RECORD filehd; + char fname[128]; + int preamble; + int foreign_keys; + + if( (buf = (void *)malloc(biggest_rec)) == NULL ) + RETURN S_NOMEM; + + for( i=0, rec=DB->record; iheader.records; i++, rec++ ) + { + sprintf(fname, "%s%c%s_tmp", DB->dbfpath, DIR_SWITCH, + DB->file[rec->fileid].name); + + fh = os_open(fname, O_RDWR|CONFIG_O_BINARY, 0); + read(fh, &filehd.H, sizeof filehd.H); + + recno = (sizeof(filehd.H) + filehd.H.recsize - 1) / filehd.H.recsize; + recid = INTERN_TO_RECID(i); + records = lseek(fh, 0, SEEK_END) / filehd.H.recsize; + + rebuildverbose_fn(rec->name, records, 0); + + if( rec->first_foreign == -1 ) + foreign_keys = 0; + else + foreign_keys = rec->keys - (rec->first_foreign - rec->first_key); + preamble= sizeof(long) * foreign_keys + offsetof(RECORDHEAD, data[0]); + datasize= filehd.H.recsize - preamble; + + for( ;; ) + { + lseek(fh, (off_t) (filehd.H.recsize * recno + preamble), SEEK_SET); + if( read(fh, buf, datasize) != datasize ) + break; + + if( d_fillnew(recid, buf) != S_OKAY ) + printf("%s: d_fillnew failed\n", rec->name); + + recno++; + + rebuildverbose_fn(rec->name, records, recno); + } + + close(fh); + unlink(fname); + + rebuildverbose_fn(rec->name, records, records); + } + free(buf); + + RETURN S_OKAY; +} + + + +/*--------------------------------- d_open ---------------------------------*\ + * + * Purpose : Opens a database of the name . The dbd-file is + * expected to be in and the data and index file are + * expected to be in (or will be created in) , set by + * d_dbdpath() and d_dbfpath(). + * + * If the database is opened in exclusive or one-user mode the + * first file is locked. This will make future calls to d_open() + * return S_UNAVAIL. + * + * Parameters: dbname - Database name. + * mode - [s]hared, e[x]clusive or [o]ne user mode. + * + * Returns : S_OKAY - Database successfully opened. + * S_NOTAVAIL - Database is currently not available. + * S_NOMEM - Not enough memory to open database. + * S_INVDB - Invalid database name. + * S_BADTYPE - The mode parmeter was invalid. + * + */ + +FNCLASS int d_open(dbname, mode) +char *dbname, *mode; +{ + char fname[129]; + Record *rec; + int i, n; + unsigned biggest_rec = 0; + Dbentry *_db; + + db_status = S_OKAY; + + /* Validate the mode parameter */ + if( *mode != 's' && *mode != 'x' && *mode != 'o' ) + RETURN_RAP(S_BADTYPE); + + /* Initialize locking resource */ + if( ty_openlock() == -1 ) + RETURN S_FATAL; + + ty_lock(); + + /* Find an available database slot */ + for( i=0, _db=typhoon.dbtab; iclients ) + break; + + if( i == DB_MAX ) + { + ty_unlock(); + RETURN S_NOTAVAIL; + } + + DB = _db; + DB->mode = *mode; + strcpy(DB->name, dbname); + strcpy(DB->dbfpath, typhoon.dbfpath); + + /* dbd-file is located in */ + sprintf(fname, "%s%s.dbd", typhoon.dbdpath, dbname); + if( read_dbdfile(DB, fname) != S_OKAY ) + { + ty_unlock(); + return db_status; + } + + /* Open sequence file */ + if( seq_open(_db) == -1 ) + { + ty_unlock(); + return db_status; + } + + /* Allocate 'current record' buffer (add room for record numbers of + * parent records (one per foreign key) + */ + for( i=0, rec=DB->record; iheader.records; i++, rec++ ) + { + if( rec->first_foreign == -1 ) + n = rec->size; + else + n = rec->size + (rec->keys - (rec->first_foreign - rec->first_key)) + * sizeof(ulong); + + if( n > biggest_rec ) + biggest_rec = n; + } + + if( (DB->real_recbuf = (char *)malloc(biggest_rec)) == NULL ) + { + seq_close(DB); + ty_unlock(); + free(DB->dbd); + RETURN S_NOMEM; + } + +#ifdef CONFIG_UNIX + if( shm_alloc(DB) == -1 ) + { + seq_close(DB); + ty_unlock(); + free(DB->dbd); + RETURN S_NOMEM; + } + + /* The database cannot be opened during a restore (except for rebuild) */ + if( DB->shm->restore_active && !typhoon.do_rebuild ) + { + seq_close(DB); + ty_unlock(); + free(DB->dbd); + shm_free(DB); + RETURN S_NOTAVAIL; + } +#endif + + DB->recbuf = DB->real_recbuf; + DB->clients++; + + /* If the indices should be rebuilt we'll remove them first */ + if( typhoon.do_rebuild ) + { + char fname[256]; + char new_fname[256]; + + for( i=0; iheader.files; i++ ) + if( DB->file[i].type == 'k' ) + { + sprintf(fname, "%s%c%s", typhoon.dbfpath, DIR_SWITCH, DB->file[i].name); + unlink(fname); + } + else + { + sprintf(fname, "%s%c%s", typhoon.dbfpath, DIR_SWITCH, DB->file[i].name); + sprintf(new_fname, "%s_tmp", fname); + unlink(new_fname); +#ifndef CONFIG_UNIX + rename(fname, new_fname); +#else + if (link(fname, new_fname) == 0) + if (unlink(fname) != 0) + unlink(new_fname); +#endif + } + } + + /* Before opening the database we mark all file handles are closed */ + for( i=0; iheader.files; i++ ) + DB->fh[i].any = NULL; + + /* Open all the database files */ + for( i=0; iheader.files && db_status == S_OKAY; i++ ) + ty_openfile(DB->file + i, DB->fh + i, *mode == 's'); + + /* Roll back if a file could not be opened */ + if( db_status != S_OKAY ) + { + i--; + while( i-- ) + ty_closefile(DB->fh + i); + + DB->clients--; + CURR_DB = -1; +#ifdef CONFIG_UNIX + shm_free(DB); +#endif + free(DB->real_recbuf); + free(DB->dbd); + seq_close(DB); + ty_unlock(); + RETURN db_status; + } + + CURR_DB = _db - typhoon.dbtab; + CURR_REC = 0; + CURR_RECID = 0; + CURR_BUFREC = 0; + CURR_BUFRECID = 0; + + if( typhoon.do_rebuild ) + { + perform_rebuild(biggest_rec); + typhoon.do_rebuild = 0; + } + + typhoon.dbs_open++; + ty_unlock(); + + /* Return the status of the last db_open command */ + return db_status; +} + + + +/*--------------------------------- d_close --------------------------------*\ + * + * Purpose : Close the current database. + * + * Parameters: None. + * + * Returns : S_NOCD - No current database. + * S_OKAY - Database successfully closed. + * + */ + +FNCLASS int d_close() +{ + int i; + + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + ty_lock(); + + DB->clients--; + + for( i=0; iheader.files; i++ ) + ty_closefile(DB->fh + i); + + seq_close(DB); + + /* If there is currently a transaction active the transaction counter + * in shared memory must be decremented. + */ +#ifdef CONFIG_UNIX + d_abortwork(); + shm_free(DB); +#endif + FREE(DB->dbd); + FREE(DB->real_recbuf); + + CURR_DB = -1; + CURR_REC = 0; + + ty_unlock(); + + if( !--typhoon.dbs_open ) + ty_closelock(); + + RETURN S_OKAY; +} + + + +/*-------------------------------- d_destroy -------------------------------*\ + * + * Purpose : Destroy a database. + * + * Parameters: dbname - Database name. + * + * Returns : S_NOMEM - Not enough memory to open database and destroy. + * S_INVDB - Invalid database name. + * S_OKAY - Database successfully closed. + * S_NOTAVAIL - The database is already opened in non-shared + * mode. + * + * + * INSERT LOCK CHECK HERE!!!!! + * + * + */ + +FNCLASS int d_destroy(dbname) +char *dbname; +{ + int i, dbdfile; + char fname[80]; + Header header; + File *file; + Dbentry *_db = typhoon.dbtab; + + ty_lock(); + + for( i=0; iname, dbname) ) + break; + + if( i == DB_MAX ) + { /* Database currently not open */ + sprintf(fname, "%s%s.dbd", typhoon.dbdpath, dbname); + + if( (dbdfile = open(fname, CONFIG_O_BINARY|O_RDONLY)) == -1 ) + { + ty_unlock(); + RETURN S_INVDB; /* Database name not found */ + } + read(dbdfile, &header, sizeof header); + + if( !(file = (File *)malloc(sizeof(File) * header.files)) ) + { + close(dbdfile); + ty_unlock(); + RETURN S_NOMEM; + } + + read(dbdfile, file, sizeof(File) * header.files); + close(dbdfile); + + for( i=0; i < header.files; i++ ) /* Now remove all files */ + unlink(file[i].name); + + ty_unlock(); + RETURN S_OKAY; + } + + /*---------- Destroy current database ----------------------------------*/ + for( i=0; iheader.files; i++ ) + { + ty_closefile(DB->fh + i); /* Close all database files */ + unlink(DB->file[i].name); /* and remove them */ + } + + FREE(DB->dbd); + + _db->clients = 0; + CURR_DB = -1; + CURR_REC = 0; + + ty_unlock(); + RETURN S_OKAY; +} + +/* end-of-file */ diff --git a/src/ty_prot.h b/src/ty_prot.h new file mode 100644 index 0000000..203f317 --- /dev/null +++ b/src/ty_prot.h @@ -0,0 +1,203 @@ +/*---------------------------------------------------------------------------- + * File : ty_prot.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains function prototypes. + * + * $Id: ty_prot.h,v 1.5 1999/10/04 05:29:45 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_TY_PROT_H +#define TYPHOON_TY_PROT_H + +#ifndef TYPHOON_ENVIRON_H +#include "environ.h" +#endif + +#ifndef TYPHOON_TY_DBD_H +#include "ty_dbd.h" +#endif + +#ifndef TYPHOON_TY_TYPE_H +#include "ty_type.h" +#endif + +/*-------------------------------- Constants -------------------------------*/ +#define UNCOMPRESS 0 /* Command to compress_vlr() */ +#define COMPRESS 1 /* Command to compress_vlr() */ + +/*------------------------------- ty_auxfn.c -------------------------------*/ +int aux_getkey PRM( (Id, Key **); ) +int report_err PRM( (int); ) +void set_subcode PRM( (Key *); ) +int set_recfld PRM( (Id, Record **, Field **); ) +void *set_keyptr PRM( (Key *, void *); ) +int keyfind PRM( (Key *, void *, ulong *); ) +int keyadd PRM( (Key *, void *, ulong); ) +int keydel PRM( (Key *, void *, ulong); ) +int reckeycmp PRM( (Key *, void *, void *); ) +int compress_vlr PRM( (int, Record *, void *, void *, unsigned *);) +int null_indicator PRM( (Key *, void *); ) +int update_recbuf PRM( (void); ) + + +/*------------------------------- ty_refin.c -------------------------------*/ +void update_foreign_keys PRM( (Record *, int); ) +int check_foreign_keys PRM( (Record *, void *, int); ) +void delete_foreign_keys PRM( (Record *); ) +int check_dependent_tables PRM( (Record *, void *, int); ) + + +/*--------------------------------- ty_io.c --------------------------------*/ +int ty_openfile PRM( (File *, Fh *, int); ) +int ty_closefile PRM( (Fh *); ) +int ty_keyadd PRM( (Key *, void *, ulong); ) +int ty_keydel PRM( (Key *, void *, ulong); ) +int ty_keyfind PRM( (Key *, void *, ulong *); ) +int ty_keyread PRM( (Key *, void *); ) +int ty_keyfrst PRM( (Key *, ulong *); ) +int ty_keylast PRM( (Key *, ulong *); ) +int ty_keynext PRM( (Key *, ulong *); ) +int ty_keyprev PRM( (Key *, ulong *); ) +int ty_recadd PRM( (Record *, void *, ulong *); ) +int ty_recwrite PRM( (Record *, void *, ulong); ) +int ty_recread PRM( (Record *, void *, ulong); ) +int ty_recread PRM( (Record *, void *, ulong); ) +int ty_recdelete PRM( (Record *, ulong); ) +ulong ty_numrecords PRM( (Record *, ulong *); ) +int ty_recfrst PRM( (Record *, void *); ) +int ty_reclast PRM( (Record *, void *); ) +int ty_recnext PRM( (Record *, void *); ) +int ty_recprev PRM( (Record *, void *); ) +ulong ty_reccount PRM( (Record *, ulong *); ) +int ty_vlradd PRM( (Record *, void *, unsigned, ulong *); ) +int ty_vlrwrite PRM( (Record *, void *, unsigned, ulong); ) +unsigned ty_vlrread PRM( (Record *, void *, ulong, unsigned *); ) +int ty_vlrdel PRM( (Record *, ulong); ) +int ty_reccurr PRM( (Record *, ulong *); ) +int ty_closeafile PRM( (void); ) + +void ty_logerror PRM( (char *, ...); ) + +/*------------------------------- ty_repl.c --------------------------------*/ +void ty_log PRM( (int); ) + + +/*--------------------------------- bt_open --------------------------------*/ +void btree_getheader PRM( (INDEX *); ) +void btree_putheader PRM( (INDEX *); ) +INDEX *btree_open PRM( (char *, int, int, CMPFUNC, int, int); ) +void btree_close PRM( (INDEX *); ) +int btree_dynopen PRM( (INDEX *); ) +int btree_dynclose PRM( (INDEX *); ) +int keydynclose PRM( (INDEX *); ) +int nodesearch PRM( (INDEX *, void *, int *); ) +int d_search PRM( (INDEX *, void *, ix_addr *, int *); ) + +/*------------------------------- bt_funcs.c -------------------------------*/ +int btree_add PRM( (INDEX *, void *, ulong); ) +int btree_find PRM( (INDEX *, void *, ulong *); ) +int btree_read PRM( (INDEX *, void *); ) +int btree_write PRM( (INDEX *, void *); ) +int btree_delall PRM( (INDEX *); ) +int btree_frst PRM( (INDEX *, ulong *); ) +int btree_last PRM( (INDEX *, ulong *); ) +int btree_next PRM( (INDEX *, ulong *); ) +int btree_prev PRM( (INDEX *, ulong *); ) +int btree_exist PRM( (INDEX *, void *, ulong); ) +void get_rightmostchild PRM( (INDEX *, ulong); ) +void get_leftmostchild PRM( (INDEX *, ulong); ) +int btree_keyread PRM( (INDEX *, void *); ) + +/*-------------------------------- bt_del.c --------------------------------*/ +int btree_del PRM( (INDEX *, void *, ulong); ) + +/*--------------------------------- bt_io.c --------------------------------*/ +ix_addr noderead PRM( (INDEX *, char *, ix_addr); ) +ix_addr nodewrite PRM( (INDEX *, char *, ix_addr); ) + +/*-------------------------------- record.c --------------------------------*/ +RECORD *rec_open PRM( (char *, unsigned, int); ) +int rec_close PRM( (RECORD *); ) +int rec_dynopen PRM( (RECORD *); ) +int rec_dynclose PRM( (RECORD *); ) +int rec_add PRM( (RECORD *, void *, ulong *); ) +int rec_write PRM( (RECORD *, void *, ulong); ) +int rec_read PRM( (RECORD *, void *, ulong); ) +int rec_delete PRM( (RECORD *, ulong); ) +int rec_curr PRM( (RECORD *, ulong *); ) +ulong rec_numrecords PRM( (RECORD *, ulong *); ) +int rec_frst PRM( (RECORD *, void *); ) +int rec_last PRM( (RECORD *, void *); ) +int rec_next PRM( (RECORD *, void *); ) +int rec_prev PRM( (RECORD *, void *); ) +int rec_lock PRM( (RECORD *, ulong, int); ) +int rec_unlock PRM( (RECORD *, ulong); ) + +/*------------------------------- sequence.c -------------------------------*/ +int seq_open PRM( (Dbentry *); ) +int seq_close PRM( (Dbentry *); ) + +/*---------------------------------- vlr.c ---------------------------------*/ +void vlr_close PRM( (VLR *); ) +VLR *vlr_open PRM( (char *, unsigned, int); ) +int vlr_add PRM( (VLR *, void *, unsigned, ulong *); ) +int vlr_write PRM( (VLR *, void *, unsigned, ulong); ) +int vlr_read PRM( (VLR *, void *, ulong, unsigned *); ) +int vlr_del PRM( (VLR *, ulong); ) +int vlr_dynclose PRM( (VLR *); ) +int vlr_dynopen PRM( (VLR *); ) + +/*---------------------------------- readdbd.c -----------------------------*/ + +int read_dbdfile PRM( (Dbentry *, char *); ) + +/*---------------------------------- os.c ----------------------------------*/ +int os_lock PRM ( (int, long, unsigned, int); ) +int os_unlock PRM ( (int, long, unsigned); ) +int os_open PRM ( (char *, int, int); ) +int os_close PRM ( (int); ) +int os_access PRM ( (char *, int); ) + + +/*--------------------------------- osxxx.c --------------------------------*/ +int ty_openlock PRM( (void); ) +int ty_closelock PRM( (void); ) +void ty_lock PRM( (void); ) +int ty_unlock PRM( (void); ) +int shm_alloc PRM( (Dbentry *); ) +int shm_free PRM( (Dbentry *); ) + + +/*------------------------------- cmpfuncs.c -------------------------------*/ +int compoundkeycmp PRM( (void *, void *); ) +int refentrycmp PRM( (REF_ENTRY *, REF_ENTRY *); ) +void InitLowerTable PRM( (void); ) + +#endif +/* end-of-file */ diff --git a/src/ty_refin.c b/src/ty_refin.c new file mode 100644 index 0000000..c49fee5 --- /dev/null +++ b/src/ty_refin.c @@ -0,0 +1,328 @@ +/*---------------------------------------------------------------------------- + * File : bt_refin + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * This file contains function for handling referential integrity. + * + * Functions: + * update_foreign_keys + * check_foreign_keys + * delete_foreign_keys + * check_dependent_tables + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_refin.c,v 1.5 1999/10/03 23:28:29 kaz Exp $"; + +/*--------------------------- Function prototypes --------------------------*/ + +/*---------------------------- Global variables ----------------------------*/ +/* The ca[] table is a Communications Area used to pass information between + check_foreign_keys() and modify_refentries(). Because foreign keys + are modified in two steps, this table is necessary +*/ +struct { + ulong ref_file; /* Id of reference file. 0=skip this entry */ + ulong del_parent; /* If a foreign key has been changed this */ + /* entry contains the recno of the old */ + /* parent. This should be used to remove */ + /* the old. REFENTRY. 0=no parent to delete */ + uchar null; /* Determines whether a reference should be */ + /* inserted, e.g. the key is not null */ +} ca[RECKEYS_MAX]; + + + + +/*--------------------------- update_foreign_keys --------------------------*\ + * + * Purpose : This function is called after check_foreign_keys has set up + * the communications area (ca) with the foreign keys that should + * be updated, removed or added. + * + * Parameters: rec - Pointer to record. + * is_new - Called by d_fillnew() or d_recwrite(). + * + * Returns : Nothing. + * + */ +void update_foreign_keys(rec, is_new) +Record *rec; +int is_new; +{ + int n; + Key key; + REF_ENTRY refentry; + + /* If the record has no foreign keys we'll just return now */ + if( rec->first_foreign == -1 ) + return; + + key.size = sizeof(REF_ENTRY); + n = rec->keys - (rec->first_foreign - rec->first_key); + + refentry.dependent.recid = CURR_RECID; + refentry.dependent.recno = CURR_REC; + + while( n-- ) + { + if( ca[n].ref_file ) + { + key.fileid = ca[n].ref_file; + + if( !is_new ) + { + if( ( refentry.parent = ca[n].del_parent ) ) + ty_keydel(&key, &refentry, CURR_REC); + } + + if( !ca[n].null ) + { + refentry.parent = ((ulong *)DB->real_recbuf)[n]; + ty_keyadd(&key, &refentry, CURR_REC); + } + } + } + +} + + + +/*--------------------------- check_foreign_keys ---------------------------*\ + * + * Purpose : This function checks whether the foreign keys of a record + * exist in its parent tables. + * + * First the existence of all foreign keys are checked. Only + * the necessary checks are performed. If is_new all foreign + * keys are checked, otherwise only foreign keys which have + * changed are checked. Null keys are never checked. + * + * The record numbers of the parents record are stored in the + * preamble. For each entry in the preamble, the corresponding + * entries in save_ref[] and old_preamble[] contains the file id + * of the parent's reference file and the old preamble value, + * respectively. + * + * Parameters: rec - Pointer to record. + * buf - Buffer of dependent record. + * new - Is this record being created? This is set to 1 + * d_fillnew(). + * + * Returns : S_OKAY - All foreign keys existed. + * S_NOTFOUND - A foreign did not exist. db_subcode contains the + * ID of the parent table. + */ +int check_foreign_keys(rec, buf, is_new) +Record *rec; +void *buf; +int is_new; +{ + Key *key; + int n; + int foreign_keys; + ulong ref; + + /* If the record has no foreign keys we'll just return now */ + if( rec->first_foreign == -1 ) + return S_OKAY; + + key = DB->key + rec->first_foreign; + foreign_keys = rec->keys - (rec->first_foreign - rec->first_key); + + /* If this is a new record the preamble must be cleared */ + if( is_new ) + memset(DB->real_recbuf, 0, foreign_keys * sizeof(ulong)); + + for( n=0; KEY_ISFOREIGN(key) && n < foreign_keys; n++, key++ ) + { + Key *primary_key = DB->key + DB->record[key->parent].first_key; + + ca[n].null = 0; + + if( is_new || reckeycmp(key, buf, DB->recbuf) ) + { + if( KEY_ISOPTIONAL(key) ) + { + ca[n].ref_file = DB->record[key->parent].ref_file; + + /* If the record is being created or the old value was null + * no key need to be deleted + */ + if( is_new || null_indicator(key, DB->recbuf) ) + ca[n].del_parent = 0; + else + ca[n].del_parent = ((ulong *)DB->real_recbuf)[n]; + + if( null_indicator(key, buf) ) + { + ca[n].null = 1; + continue; + } + } + + CURR_KEY = primary_key - DB->key; + + if( ty_keyfind(primary_key, set_keyptr(key, buf), &ref) != S_OKAY ) + { + db_subcode = (key->parent+1) * REC_FACTOR; + RETURN S_FOREIGN; + } + + ca[n].del_parent = ((ulong *)DB->real_recbuf)[n]; + ca[n].ref_file = DB->record[key->parent].ref_file; + + /* Store the reference to the parent record in the preamble */ + ((ulong *)DB->real_recbuf)[n] = ref; + } + else + ca[n].ref_file = 0; + } + + return S_OKAY; +} + + + +/*-------------------------- delete_foreign_keys ---------------------------*\ + * + * Purpose : This function removes all refentries from a dependent record's + * parent reference files. + * + * Parameters: rec - Pointer to record. + * + * Returns : Nothing. + * + */ +void delete_foreign_keys(rec) +Record *rec; +{ + Key *key, refkey; + REF_ENTRY refentry; + int n; + int foreign_keys; + + /* If the record has no foreign keys we'll just return now */ + if( rec->first_foreign == -1 ) + return; + + key = DB->key + rec->first_foreign; + foreign_keys = rec->keys - (rec->first_foreign - rec->first_key); + refkey.size = sizeof(REF_ENTRY); + + refentry.dependent.recid = CURR_RECID; + refentry.dependent.recno = CURR_REC; + + for( n=0; KEY_ISFOREIGN(key) && n < foreign_keys; n++, key++ ) + { + if( !(key->type & KT_OPTIONAL) || !null_indicator(key, DB->recbuf) ) + { + refentry.parent = ((ulong *)DB->real_recbuf)[n]; + refkey.fileid = DB->record[key->parent].ref_file; + + ty_keydel(&refkey, &refentry, CURR_REC); + } + } +} + + + +/*------------------------- check_dependent_tables -------------------------*\ + * + * Purpose : This function is called by d_fillnew() and d_delete() to check + * whether this update/delete operation will have any effect on + * records in dependent tables. + * + * Parameters: parent - Pointer to parent table's record pointer. + * buf - Buffer of dependent record. + * for_action - 'd'=delete, 'u'=update + * + * Returns : S_OKAY - No conflicts. + * S_RESTRICT - A dependent table with restrict rule has rows + * which referenced the parent table. db_subcode + * contains the ID of the dependent table. + * + */ +int check_dependent_tables(parent, buf, for_action) +Record *parent; +void *buf; +int for_action; +{ + Key *primary_key, refkey; + REF_ENTRY refentry; + ulong ref; + int rc; + + /* Does this table have any dependent tables? */ + if( !parent->dependents ) + return S_OKAY; + + /* If the table has dependent tables, it follows that it must also have + * a primary key, i.e. no need to check for that. + */ + primary_key = DB->key + parent->first_key; + + /* Only perform check if the primary key has changed (if update) */ + if( for_action == 'u' ) + if( !reckeycmp(primary_key, buf, DB->recbuf) ) + return S_OKAY; + + refentry.parent = CURR_REC; + refentry.dependent.recid = 0; + refentry.dependent.recno = 0; + + refkey.size = sizeof(REF_ENTRY); + refkey.fileid = parent->ref_file; + + if( (rc = ty_keyfind(&refkey, &refentry, &ref)) != S_OKAY ) + rc = ty_keynext(&refkey, &ref); + + if( rc != S_OKAY ) + return S_OKAY; + + refentry = *(REF_ENTRY *)CURR_KEYBUF; + + if( refentry.parent == CURR_REC ) + { + /* Set db_subcode to the ID of the dependent table */ + db_subcode = (refentry.dependent.recid+1) * REC_FACTOR; + RETURN S_RESTRICT; + } + + return S_OKAY; +} + +/* end-of-file */ diff --git a/src/ty_repif.h b/src/ty_repif.h new file mode 100644 index 0000000..1c4ce9d --- /dev/null +++ b/src/ty_repif.h @@ -0,0 +1,142 @@ +/*---------------------------------------------------------------------------- + * File : ty_repif.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains structures used in the interface between the database and the + * Replication Server. + * + * $Id: ty_repif.h,v 1.4 1999/10/04 05:29:45 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_TY_REPIF_INCLUDED +#define TYPHOON_TY_REPIF_INCLUDED + +#ifndef TYPHOON_INCLUDED +#include "typhoon.h" +#endif + +#define REPLLOG_NAME "replserv.log" + +#define ACTION_UPDATE 'u' /* Update or create a record */ +#define ACTION_DELETE 'd' /* Delete a record */ +#define ACTION_NEWSITE 'n' /* Scan catalog for new site */ +#define ACTION_DELSITE 'e' /* Remove a site from memory */ +#define ACTION_DELTABLE 't' /* Remove a table fro memmory */ + +typedef struct { + char action; /* See ACTION_... */ + char prog_id; /* Program ID */ + ulong recid; /* Record ID */ + union { + DB_ADDR addr; /* action = UPDATE */ + char key[KEYSIZE_MAX]; /* action = DELETE */ + ulong site_id; /* action = NEWSITE, DELSITE or DELTABLE*/ + } u; +} LOGENTRY; + + +/*--------------------------- Protocol block IDs ---------------------------*/ +#define REPL_ACKNOWLEDGE 0 /* ID of acknowledge block */ +#define REPL_UPDATE 100 /* ID of update block */ +#define REPL_DELETE 101 /* ID of delete block */ +#define REPL_CLEARTABLE 102 +#define REPL_ERROR 103 +#define REPL_PROTBUF_SIZE 25000 /* Max buffer size passed */ +#define REPL_HEADER_SIZE 8 + +struct repl_header { + ulong seqno; /* Sequence number */ + ulong len; /* Length of rest of block */ +}; + +struct repl_acknowledge { + ulong seqno; /* Sequence number */ + ulong len; /* Length of rest of block */ + uchar id; /* Must be 0 */ + uchar spare[3]; /* Used to maintain dword alignment */ + ulong sequence; /* Sequence number acknowledged */ +}; + +struct repl_update { + ulong seqno; /* Sequence number */ + ulong len; /* Length of rest of block */ + uchar id; /* Must be 100 */ + uchar prog_id; /* Program ID */ + ushort rec_len; /* Number of bytes in rec[] */ + ulong recid; /* Record ID */ + ulong sequence; /* Update sequence number */ + uchar rec[1]; /* Record buffer */ +}; + +struct repl_delete { + ulong seqno; /* Sequence number */ + ulong len; /* Length of rest of block */ + uchar id; /* Must be 101 */ + uchar prog_id; /* Program ID */ + ushort key_len; /* Number of bytes in buf[] */ + ulong recid; /* Record ID */ + ulong sequence; /* Update sequence number */ + uchar key[1]; /* Key buffer */ +}; + + +struct repl_cleartable { + ulong seqno; /* Sequence number */ + ulong len; /* Length of rest of block */ + uchar id; /* Must be 102 */ + uchar spare[3]; + ulong recid; /* ID of table to clear */ +}; + + + +/*--------------------------------------------------------------------------*\ + * + * Block : repl_error + * + * Purpose : This protocol block is used to report an error to the + * Replication Server. + * + * Direction: Site -> Replication Server. + * + */ +struct repl_error { + ulong seqno; /* Sequence number */ + ulong len; /* Length of rest of block */ + uchar id; /* Must be 103 */ + uchar error; /* 0=Record contains unknown reference */ + /* 1=Record is referenced by other rec */ + uchar spare[2]; /* Used to maintain dword alignment */ + ulong arg; /* error=0: ID of referenced table */ + ulong sequence; /* Sequence number of erroneous block */ +}; + +#endif + +/* end-of-file */ + diff --git a/src/ty_repl.c b/src/ty_repl.c new file mode 100644 index 0000000..35eb66b --- /dev/null +++ b/src/ty_repl.c @@ -0,0 +1,267 @@ +/*---------------------------------------------------------------------------- + * File : ty_repl.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * + * Functions: + * Contains functions for logging updates and deletions. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +#else +# include +# include +# include +#endif +#include +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" +#include "ty_repif.h" +#include "catalog.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_repl.c,v 1.6 1999/10/03 23:28:29 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ +static int read_distables PRM( (void); ) +static void add_recid PRM( (Id); ) +static int get_recid PRM( (Id); ) +static void write_logentry PRM( (LOGENTRY *, unsigned); ) + +/*---------------------------- Global variables ----------------------------*/ +static int dis_dbid = -1; /* Distributed database ID */ +static int dis_records = 0; /* Number of entries in dis_record[]*/ +static Id dis_record[100]; + + +static int get_recid(id) +Id id; +{ + int i; + + for( i=0; ilogging = on; + + if( on ) + { + if( read_distables() == -1 ) + RETURN S_IOFATAL; + dis_dbid = CURR_DB; + } + + RETURN S_OKAY; +} + + +FNCLASS int d_addsite(id) +ulong id; +{ + LOGENTRY entry; + + entry.action = ACTION_NEWSITE; + entry.u.site_id = id; + + write_logentry(&entry, offsetof(LOGENTRY, u) + sizeof entry.u.site_id); + + RETURN S_OKAY; +} + + +FNCLASS int d_delsite(id) +ulong id; +{ + LOGENTRY entry; + + entry.action = ACTION_DELSITE; + entry.u.site_id = id; + + write_logentry(&entry, offsetof(LOGENTRY, u) + sizeof entry.u.site_id); + + RETURN S_OKAY; +} + + +FNCLASS int d_deltable(site_id, table_id) +ulong site_id, table_id; +{ + LOGENTRY entry; + + entry.action = ACTION_DELTABLE; + entry.recid = table_id; + entry.u.site_id = site_id; + + write_logentry(&entry, offsetof(LOGENTRY, u) + sizeof entry.u.site_id); + + RETURN S_OKAY; +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : ty_log + * + * Purpose : Add an entry to the Replication Server log. If site_id == -1 + * ty_log is called to add or delete a site. + * + * Parameters: action - 'u'=Update, 'd'=delete. + * + * Returns : Nothing. + * + */ +void ty_log(action) +int action; +{ + LOGENTRY entry; + Id recid = INTERN_TO_RECID(CURR_RECID); + ushort size, keysize; + Record *rec = &DB->record[RECID_TO_INTERN(recid)]; + + /* Return if the current database is not distributed */ + if( CURR_DB != dis_dbid ) + return; + + /* Return here if the record is not distributed */ + if( get_recid(recid) == -1 ) + return; + + entry.action= action; + entry.recid = recid; + size = offsetof(LOGENTRY, u); + + switch( action ) + { + case ACTION_UPDATE: + /* Store the database address of the modified record */ + size += sizeof(entry.u.addr); + d_crget(&entry.u.addr); + break; + case ACTION_DELETE: + /* Store the primary key of the deleted record */ + keysize = DB->key[ rec->first_key ].size; + + /* Copy the record's first key to */ + memcpy(entry.u.key, + set_keyptr(&DB->key[ rec->first_key ], DB->recbuf), + keysize); + size += keysize; + break; + } + + write_logentry(&entry, size); +} + + + +static void write_logentry(entry, size) +LOGENTRY *entry; +unsigned size; +{ + int fh; + short shsize; + + if( (fh=open(REPLLOG_NAME, CONFIG_O_BINARY|O_CREAT|O_RDWR|O_APPEND, CONFIG_CREATMASK)) == -1 ) + { +#ifdef CONFIG_UNIX + printf("PANIC: cannot open '%s' (pid %d)\n\a", REPLLOG_NAME, getpid()); +#else + printf("PANIC: cannot open '%s'\n\a", REPLLOG_NAME); +#endif + return; + } + + shsize = size; + write(fh, &shsize, sizeof shsize); + write(fh, entry, size); + + close(fh); +} + +/* end-of-file */ diff --git a/src/ty_type.h b/src/ty_type.h new file mode 100644 index 0000000..88829c8 --- /dev/null +++ b/src/ty_type.h @@ -0,0 +1,230 @@ +/*---------------------------------------------------------------------------- + * File : ty_type.h + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains file descriptor definition for the B-tree, record and vlr + * modules. + * + * $Id: ty_type.h,v 1.4 1999/10/04 03:45:08 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +#ifndef TYPHOON_TY_TYPE_H +#define TYPHOON_TY_TYPE_H + +#ifndef TYPHOON_INCLUDED +#include "typhoon.h" +#endif + +#ifndef TYPHOON_TY_DBD_H +#include "ty_dbd.h" +#endif + +/*---------- Internal constants --------------------------------------------*/ +#define DB_MAX 10 /* Maximum number of concurrent databases */ +#define BTREE_DEPTH_MAX 10 /* Maximum B-tree depth */ +#define BIT_DELETED 0x01 + +/*---------- Macros --------------------------------------------------------*/ +#define FREE(p) if( p ) free(p) +#define RETURN return db_status = +#define RETURN_RAP(v) return report_err(v); + +/*---------- Structures ----------------------------------------------------*/ +typedef ulong ix_addr; +typedef int (*CMPFUNC)PRM((void *, void *)); +typedef struct { + char type; /* = 'k' */ + ulong seqno; /* Sequence number */ + int fh; /* File handle */ + char fname[80]; /* File name */ + struct { /* Index file header */ + char id[16]; /* Version id */ + ushort version; /* Version number */ + ix_addr first_deleted; /* Pointer to first node in delete list */ + ushort nodesize; /* Node size */ + ushort keysize; /* Size of key in bytes */ + ushort order; /* Node order */ + ushort dups; /* Duplicate keys allowed? */ + ulong keys; /* Number of keys in index */ + ulong timestamp; /* Timestamp. Changed by d_keyadd/del() */ + char spare[2]; /* Not used */ + } H; + CMPFUNC cmpfunc; /* Comparison function */ + struct { /* Path to current node and key */ + ix_addr a; /* Node address */ + ushort i; /* Node index */ + } path[BTREE_DEPTH_MAX+1]; + int level; /* Path level */ + int shared; /* Opened in shared mode? */ + int tsize; /* Tuple size */ + int aligned_keysize; /* Aligned keysize */ + int curr; /* Do we have a current key? */ + int hold; /* Used by d_keynext and d_keyprev */ + char *curkey; /* 'current key' buffer */ + char node[1]; /* This array is size nodesize */ +} INDEX; + +typedef struct { /* Record head (found in every record) */ + ulong prev; /* Pointer to previous record */ + ulong next; /* Pointer to next record */ + char flags; /* Delete bit */ + char data[1]; /* Record data. this field MUST be the */ +} RECORDHEAD; /* Last field in the RECORD structure */ + +typedef struct { + char type; /* = 'd' */ + ulong seqno; /* Sequence number */ + int fh; /* File handle */ + char fname[80]; /* File name */ + struct { + char id[16]; /* Version id */ + ushort version; /* Record file version number */ + ulong first_deleted; /* Pointer to first deleted record */ + ulong first; /* Pointer to first record in chain */ + ulong last; /* Pointer to last record in chain */ + ulong numrecords; /* Number of records in file */ + ushort datasize; /* Size of data block */ + ushort recsize; /* Size of record (and chain) */ + } H; + int first_possible_rec; /* Record number of the first */ + /* record in the file */ + int share; /* Opened in shared mode? */ + ulong recno; /* Current record number. 0 = no current*/ + RECORDHEAD rec; +} RECORD; + +/*--------------------------------------------------------------------------*/ +/* Variable Length Record file structures */ +/*--------------------------------------------------------------------------*/ +typedef struct { + ulong nextblock; /* Pointer to next block */ + unsigned recsize; /* Size of record */ + char data[1]; /* Data */ +} VLRBLOCK; + +typedef struct { + char type; /* = 'v' */ + ulong seqno; /* Sequence number */ + int fh; /* File handle */ + char fname[80]; /* File name */ + int shared; /* Opened in shared mode? */ + unsigned datasize; /* Number of bytes in each block */ + VLRBLOCK *block; /* Pointer to buffer */ + struct { + char version[32]; /* VLR version number */ + char id[32]; /* User provided ID */ + unsigned blocksize; /* Block size */ + ulong firstfree; /* First free data block */ + ulong numrecords; /* Number of records in file */ + } header; +} VLR; + +typedef union { + struct { + char type; /* 'd' = data,, 'k' = key, 'v'=vlr file */ + ulong seqno; /* Sequence number */ + int fh; + } *any; + INDEX *key; /* Index File Descriptor */ + RECORD *rec; /* Record File Descriptor */ + VLR *vlr; /* Variable Length Record Descriptor */ +} Fh; + +typedef struct { + int use_count; /* First remove shared memory when 0 */ + int backup_active; + int restore_active; + ulong curr_recid; + ulong curr_recno; + ulong num_trans_active; + char spare[96]; +} TyphoonSharedMemory; + +typedef struct { /* Database table entry */ + char name[15]; /* Database name */ + char mode; /* [s]hared, [o]ne user, e[x]clusive */ + char clients; /* Number of clients using this database*/ + char dbfpath[256]; /* Database file path */ + char logging; /* Is replication logging on? */ + uchar prog_id; /* Program ID (used with logging) */ + ulong curr_rec; /* These 4 fields hold curr_ variables */ + ulong curr_recid; /* when the database is not the current */ + ulong curr_bufrec; + ulong curr_bufrecid; + int db_status; + Header header; + void *dbd; + Fh *fh; /* Array [dbentry.files] of handles */ + File *file; + Record *record; + Field *field; + Key *key; + KeyField *keyfield; + Structdef *structdef; + Sequence *sequence; + TyphoonSharedMemory *shm; + int seq_fh; + int shm_id; + char *recbuf; /* This points to where the actual data */ + /* starts (bypassing foreign key refs) */ + char *real_recbuf; /* This points to the real start of the */ + /* buffer */ +} Dbentry; + +typedef struct { + ulong parent; /* Address of parent record */ + DB_ADDR dependent; /* Address of dependent record */ +} REF_ENTRY; + + + +typedef struct { + Dbentry dbtab[DB_MAX]; /* Database table */ + Dbentry *db; /* Current database */ + + int do_rebuild; /* Rebuild indexes on d_open()? */ + int dbs_open; + + int cur_open; /* Current number of open files */ + int max_open; /* Maximum number of open files */ + + ulong curr_keybuf[KEYSIZE_MAX/sizeof(long)]; + + Id curr_key; /* Current key. It is */ + /* used to tell compoundkeycmp */ + /* which key is being compared */ + int curr_db; /* Current database */ + void (*ty_errfn) PRM( (int,long); ) + + char dbfpath[256]; + char dbdpath[256]; +} TyphoonGlobals; + +#endif + +/* end-of-file */ diff --git a/src/ty_util.c b/src/ty_util.c new file mode 100644 index 0000000..70e3b8d --- /dev/null +++ b/src/ty_util.c @@ -0,0 +1,240 @@ +/*---------------------------------------------------------------------------- + * File : ty_util.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" +#ifdef CONFIG_UNIX +# include +#endif +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" + +static CONFIG_CONST char rcsid[] = "$Id: ty_util.c,v 1.5 1999/10/03 23:28:29 kaz Exp $"; + + +/*------------------------------ d_getkeysize ------------------------------*\ + * + * Purpose : Return the size of a key. + * + * Parameters: id - Field ID or compound key ID. + * size - Pointer to var in which size will be returned. + * + * Returns : S_NOTKEY - The ID is not a key ID. + * S_NOCD - No current database. + * + */ + +FNCLASS int d_getkeysize(id, size) +Id id; +unsigned *size; +{ + Field *fld; + int rc; + + /* Make sure that a database is open */ + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + /* Determine whether this id is a key id or a compound key id */ + if( id < REC_FACTOR ) + { + if( id >= DB->header.keys ) + RETURN_RAP(S_NOTKEY); + + *size = DB->key[id].size; + } + else + { + if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY ) + return rc; + + if( !(fld->type & FT_KEY) ) + RETURN_RAP(S_NOTKEY); + + *size = DB->key[fld->keyid].size; + } + + RETURN S_OKAY; +} + + + +FNCLASS int d_getfieldtype(id, type) +Id id; +unsigned *type; +{ + Field *fld; + int rc; + + /* Make sure that a database is open */ + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + /* Determine whether this id is a key id or a compound key id */ + if( id < REC_FACTOR ) + { + if( id >= DB->header.keys ) + RETURN_RAP(S_NOTKEY); + } + else + { + if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY ) + return rc; + + id = fld->keyid; + } + + *type = DB->field[ DB->keyfield[ DB->key[id].first_keyfield ].field ].type; + + RETURN S_OKAY; +} + + + + +/*------------------------------ d_getrecsize ------------------------------*\ + * + * Purpose : Return the size of a record. + * + * Parameters: recid - Record ID. + * size - Pointer to var in which size will be returned. + * + * Returns : S_NOTKEY - The ID is not a record ID. + * S_NOCD - No current database. + * + */ + +FNCLASS int d_getrecsize(recid, size) +Id recid; +unsigned *size; +{ + Record *rec; + int rc; + + /* Make sure that a database is open */ + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + if( (rc = set_recfld(recid, &rec, NULL)) != S_OKAY ) + return rc; + + *size = rec->size; + + RETURN S_OKAY; +} + + + + +FNCLASS int d_makekey(id, recbuf, keybuf) +Id id; +void *recbuf, *keybuf; +{ + KeyField *keyfld; + Key *key; + int n, rc; + + /* Make sure that a database is open */ + if( CURR_DB == -1 ) + RETURN_RAP(S_NOCD); + + if( (rc=aux_getkey(id, &key)) != S_OKAY ) + return rc; + + keyfld = DB->keyfield + key->first_keyfield; + n = key->fields; + + /* Build compound key from record */ + while( n-- ) + { + memcpy((char *)keybuf + keyfld->offset, + (char *)recbuf + DB->field[ keyfld->field ].offset, + DB->field[ keyfld->field ].size); + keyfld++; + } + + RETURN S_OKAY; +} + + +FNCLASS int d_getkeyid(recid, keyid) +Id recid, *keyid; +{ + Record *rec; + int rc; + + if( (rc = set_recfld(recid, &rec, NULL)) != S_OKAY ) + return rc; + + *keyid = rec->first_key; + + RETURN S_OKAY; +} + + + +FNCLASS int d_getforeignkeyid(recid, parent_table, keyid) +Id recid, parent_table, *keyid; +{ + Record *rec; + Key *key; + int rc, n; + + if( (rc = set_recfld(recid, &rec, NULL)) != S_OKAY ) + return rc; + + parent_table= RECID_TO_INTERN(parent_table); + n = rec->keys; + key = DB->key + rec->first_key; + + while( n-- ) + { + if( KEY_ISFOREIGN(key) && key->parent == parent_table ) + { + *keyid = key - DB->key; + RETURN S_OKAY; + } + key++; + } + + RETURN S_NOTFOUND; +} + + + +/* end-of-file */ diff --git a/src/typhoon.def b/src/typhoon.def new file mode 100755 index 0000000..d6f202b --- /dev/null +++ b/src/typhoon.def @@ -0,0 +1,61 @@ +;---------------------------------------------------------------------------- +; File : typhoon.def Copyright (c) 1991-93, CT Danmark +; Library : typhoon.dll +; Compiler: Borland C++ for OS/2 +; OS : OS/2 +; Author : Thomas B. Pedersen +; +; Description: +; This file is a DEF file for the Typhoon DBMS used as a Dynamic Link +; Library under OS/2. +; +; History +; ------------------------------ +; 16-Jun-1993 tbp Initial version. +; 15-Jul-1993 tbp DLL has now nonshared data segment. +; +;--------------------------------------------------------------------------*/ + +LIBRARY TYPHOON INITINSTANCE + +DESCRIPTION 'Typhoon Relational Database Management System Library' + +PROTMODE + +DATA MULTIPLE READWRITE LOADONCALL NONSHARED + +CODE LOADONCALL + +EXPORTS d_dbdpath @1 + d_dbfpath @2 + d_dbget @3 + d_dbset @4 + d_open @5 + d_close @6 + d_destroy @7 + d_keyread @8 + d_keyfind @9 + d_keyfrst @10 + d_keylast @11 + d_keynext @12 + d_keyprev @13 + d_recfrst @14 + d_reclast @15 + d_recnext @16 + d_recprev @17 + d_crread @18 + d_recwrite @19 + d_recread @20 + d_fillnew @21 + d_delete @22 + d_crget @23 + d_crset @24 + d_records @25 + d_block @26 + d_unblock @27 + d_getkeysize @28 + d_setfiles @29 + _db_status @30 + _db_subcode @31 + d_getrecsize @33 + d_keybuild @34 diff --git a/src/unix.c b/src/unix.c new file mode 100644 index 0000000..3ce4e58 --- /dev/null +++ b/src/unix.c @@ -0,0 +1,284 @@ +/*---------------------------------------------------------------------------- + * File : unix.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contiains functions specific to UNIX. + * + * Locking with lockf is *not* the solution. But it'll fix the + * problem with semop. + * + * Functions: + * ty_openlock - Create/open the locking resource. + * ty_closelock - Close the locking resource. + * ty_lock - Obtain the lock. + * ty_unlock - Release the lock. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" + +/*#define SEMLOCK quick fix */ + +#define TIMEOUT 10000L /* Wait maximum 10 seconds for excl. access */ + +static CONFIG_CONST char rcsid[] = "$Id: unix.c,v 1.10 1999/10/04 04:39:42 kaz Exp $"; + +/*---------------------------- Global variables ----------------------------*/ +#ifdef SEMLOCK +static struct sembuf sem_wait_buf[2] = { + 0, 0, 0, /* wait for sem to become 0 */ + 0, 1, SEM_UNDO /* then increment sem by 1 */ +}; + +static struct sembuf sem_clear_buf[1] = { + 0,-1, IPC_NOWAIT | SEM_UNDO, /* decrease sem by 1 */ +}; + + +static int sem_id; +#else +static int lock_fh = -1; +#endif + + +/*------------------------------ ty_openlock ------------------------------*\ + * + * Purpose : This function ensures that only one instance of a Typhoon + * function is active at the time, at least in its critical + * section. + * + * Parameters: None. + * + * Returns : -1 - Semaphore could not be created. + * 0 - Successful. + * + */ + +int ty_openlock() +{ +#ifndef SEMLOCK + static char lockfname[] = "/tmp/typhoonsem"; + int pid = getpid(); + mode_t old_umask = umask(0); + + if( lock_fh == -1 ) + { + if( (lock_fh = open(lockfname, O_RDWR|O_CREAT, 0666)) == -1 ) + { + printf("Cannot open %s\n", lockfname); + umask(old_umask); + return -1; + } + + write(lock_fh, &pid, sizeof pid); + } + + umask(old_umask); + +#endif + return 0; +} + + +int ty_closelock() +{ + close(lock_fh); + lock_fh = -1; + + return 0; +} + + +void ty_lock() +{ +#ifdef CONFIG_USE_FLOCK + struct flock flk; +#endif + +#ifdef SEMLOCK +#if 1 + while( semop(sem_id, sem_wait_buf, 2) == -1 && errno == EINTR ) + puts("ty_lock EAGAIN"); +#else + if( sem_wait(sem_id) == -1 ) + puts("ty_lock failed"); +#endif +#endif + +#ifndef SEMLOCK + lseek(lock_fh, 0, SEEK_SET); + +#ifdef CONFIG_USE_FLOCK + flk.l_type = F_WRLCK; + flk.l_whence = SEEK_SET; + flk.l_start = 0; + flk.l_len = 1; + + while (fcntl(lock_fh, F_SETLK, &flk) == -1) + { + if (errno != EINTR) + { + printf("ty_lock failed (errno %d, lock_fh %d)\n", errno, lock_fh); + break; + } + } +#else + + while( lockf(lock_fh, F_LOCK, 1) == -1 ) + { + if( errno != EINTR && errno != EAGAIN ) + { + printf("ty_lock failed (errno %d, lock_fh %d)\n", errno, lock_fh); + break; + } + } +#endif + +#endif +} + + +int ty_unlock() +{ +#ifdef SEMLOCK +#if 1 + while( semop(sem_id, sem_clear_buf, 1) == -1 && errno == EINTR ) + puts("ty_unlock EAGAIN"); +#else + if( sem_clear(sem_id) == -1 ) + puts("ty_unlock failed"); +#endif +#endif + +#ifndef SEMLOCK + lseek(lock_fh, 0, SEEK_SET); + + while( lockf(lock_fh, F_ULOCK, 1) == -1 ) + { + if( errno != EINTR && errno != EAGAIN ) + { + printf("ty_unlock failed (errno %d, lock_fh %d)\n", errno, lock_fh); + break; + } + } +#endif + + + return 0; +} + + + + +int shm_alloc(db) +Dbentry *db; +{ + char dbdname[128]; + key_t key; + long flags = IPC_CREAT|0770; + int created = 0; + + sprintf(dbdname, "%s.dbd", db->name); + key = ftok(dbdname, 30); + + if( (db->shm_id = shmget(key, sizeof(*db->shm), 0)) == -1 ) { + if( (db->shm_id = shmget(key, sizeof(*db->shm), flags)) == -1 ) + return -1; + else + created = 1; + } + +#ifdef SEMLOCK +#if 1 + if( (sem_id = semget(key, 1, 0)) == -1 ) + if( (sem_id = semget(key, 1, IPC_CREAT|0660)) == -1 ) + { + shmdt((char *)db->shm); + if( created ) + shmctl(db->shm_id, IPC_RMID, NULL); + return -1; + } +#else + if( (sem_id = sem_open(key)) == -1 ) + if( (sem_id = sem_create(key, 1)) == -1 ) + { + shmdt((char *)db->shm); + if( created ) + shmctl(db->shm_id, IPC_RMID, NULL); + return -1; + } +#endif +#endif + + if( (db->shm = (TyphoonSharedMemory *)shmat(db->shm_id,0,0)) == (void *)-1 ) + { + if( created ) + shmctl(db->shm_id, IPC_RMID, NULL); + return -1; + } + + if( created ) + memset(db->shm, 0, sizeof *db->shm); + db->shm->use_count++; + + return 0; +} + + +int shm_free(db) +Dbentry *db; +{ + if( --db->shm->use_count == 0 ) + { + shmdt((char *)db->shm); + shmctl(db->shm_id, IPC_RMID, NULL); +#ifdef SEMLOCK + semctl(sem_id, 0, IPC_RMID, 0); +#endif + } + else + shmdt((char *)db->shm); +#if 0 + sem_close(sem_id); +#endif + return 0; +} + + +/* end-of-file */ diff --git a/src/vlr.c b/src/vlr.c new file mode 100644 index 0000000..faa4c2f --- /dev/null +++ b/src/vlr.c @@ -0,0 +1,400 @@ +/*---------------------------------------------------------------------------- + * File : vlr.c + * Library : typhoon + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains functions for Variable Length Records. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "environ.h" +#ifdef CONFIG_UNIX +# include +# ifdef __STDC__ +# include +# endif +#else +# include +# include +#endif +#include +#include +#include + +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" +#include "ty_glob.h" + +#define VLR_VERSION "1.00" +#define SEM_LEN 0 + +#define _BLOCKSIZE (vlr->header.blocksize) +#define _FIRSTFREE (vlr->header.firstfree) +#define _NEXTBLOCK (vlr->block->nextblock) +#define _RECSIZE (vlr->block->recsize) +#define filelength(fd) lseek(fd, 0, SEEK_END) + + +static CONFIG_CONST char rcsid[] = "$Id: vlr.c,v 1.8 1999/10/04 03:45:08 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ +static void get_block PRM( (VLR *, ulong); ) +static void put_block PRM( (VLR *, ulong); ) +static ulong get_nextblock PRM( (VLR *, ulong); ) +static void get_header PRM( (VLR *); ) +static void put_header PRM( (VLR *); ) + + + +static void get_block(vlr, blockno) +VLR *vlr; +ulong blockno; +{ + lseek(vlr->fh, (off_t) (blockno * vlr->header.blocksize), SEEK_SET); + read(vlr->fh, vlr->block, vlr->header.blocksize - SEM_LEN); +} + +static void put_block(vlr, blockno) +VLR *vlr; +ulong blockno; +{ + lseek(vlr->fh, (off_t) (blockno * vlr->header.blocksize), SEEK_SET); + write(vlr->fh, vlr->block, vlr->header.blocksize - SEM_LEN); +} + + +static ulong get_nextblock(vlr, blockno) +VLR *vlr; +ulong blockno; +{ + ulong nextblock; + + lseek(vlr->fh, (off_t) (blockno * vlr->header.blocksize), SEEK_SET); + read(vlr->fh, &nextblock, sizeof nextblock); + + return nextblock; +} + + +/*------------------------------- get_header -------------------------------*\ + * + * Read header from VLR file. + * + */ + +static void get_header(vlr) +VLR *vlr; +{ + lseek(vlr->fh, 0, SEEK_SET); + read(vlr->fh, &vlr->header, sizeof vlr->header); +} + + +/*------------------------------- put_header -------------------------------*\ + * + * Write header to VLR file. + * + */ + +static void put_header(vlr) +VLR *vlr; +{ + lseek(vlr->fh, 0, SEEK_SET); + write(vlr->fh, &vlr->header, sizeof vlr->header); +} + + +/*------------------------------- vlr_close -------------------------------*\ + * + * Write header to VLR file and close file. + * + */ + +void vlr_close(vlr) +VLR *vlr; +{ + free(vlr->block); + if( vlr->fh != -1 ) + os_close(vlr->fh); + free(vlr); +} + + +/*------------------------------- vlr_open --------------------------------*\ + * + * Open VRL file. If the file does not already exist it is created. + * + */ + +VLR *vlr_open(fname, blocksize, shared) +char *fname; +unsigned blocksize; +int shared; +{ + VLR *vlr; + int fh, isnew; + + isnew = access(fname, 0); + if( (fh = os_open(fname, CONFIG_O_BINARY|O_CREAT|O_RDWR, CONFIG_CREATMASK)) == -1 ) + { + db_status = S_IOFATAL; + return NULL; + } + + if( !(vlr = (VLR *)calloc(sizeof *vlr, 1)) ) + { + os_close(fh); + db_status = S_NOMEM; + return NULL; + } + + vlr->fh = fh; + + if( !(vlr->block = (VLRBLOCK *)malloc(blocksize)) ) + { + os_close(fh); + free(vlr); + db_status = S_NOMEM; + return NULL; + } + + if( isnew ) + { + strcpy(vlr->header.version, VLR_VERSION); + vlr->header.id[0] = 0; + vlr->header.blocksize = blocksize; + vlr->header.firstfree = 1; + vlr->header.numrecords = 0; + put_header(vlr); + lseek(vlr->fh, (off_t) (blocksize-1L), SEEK_SET); + write(vlr->fh, "", 1); + } + else + get_header(vlr); + + vlr->datasize = blocksize - offsetof(VLRBLOCK, data[0]) - SEM_LEN; + vlr->shared = shared; + strcpy(vlr->fname, fname); + + db_status = S_OKAY; + + return vlr; +} + + +int vlr_dynclose(vlr) +VLR *vlr; +{ + if( vlr->fh != -1 ) + { + close(vlr->fh); + vlr->fh = -1; + } + + RETURN S_OKAY; +} + + +int vlr_dynopen(vlr) +VLR *vlr; +{ + if( vlr->fh == -1 ) + if( (vlr->fh = os_open(vlr->fname, CONFIG_O_BINARY|O_CREAT|O_RDWR, CONFIG_CREATMASK)) == -1 ) + RETURN S_IOFATAL; + + RETURN S_OKAY; +} + + + +/*------------------------------- vlr_add ---------------------------------*\ + * + * Add a record to vlr file. If the delete-chain is non-empty, blocks are + * taken from there, otherwise blocks are appended to the file. + * + */ + +int vlr_add(vlr, buf, bufsize, recno) +VLR *vlr; +void *buf; +unsigned bufsize; +ulong *recno; +{ + ulong old_firstfree = _FIRSTFREE; + ulong tmp_firstfree = _FIRSTFREE; + + get_header(vlr); + + _RECSIZE = bufsize; + + while( bufsize ) + { + unsigned copy = bufsize > vlr->datasize ? vlr->datasize : bufsize; + memcpy(vlr->block->data, buf, copy); + bufsize -= copy; + + /* Some day, I have to optimize this */ + + if( (vlr->header.firstfree) == filelength(vlr->fh)/vlr->header.blocksize ) + { + _NEXTBLOCK = bufsize ? filelength(vlr->fh) / _BLOCKSIZE + 1 : 0; + put_block(vlr, _FIRSTFREE); + _FIRSTFREE = filelength(vlr->fh)/_BLOCKSIZE; + } + else + { + tmp_firstfree = get_nextblock(vlr, _FIRSTFREE); + _NEXTBLOCK = bufsize ? tmp_firstfree : 0; + put_block(vlr, _FIRSTFREE); + _FIRSTFREE = tmp_firstfree; + } + buf = (void *)((char *)buf + vlr->datasize); + _RECSIZE = 0; + } + + vlr->header.numrecords++; + put_header(vlr); + + *recno = old_firstfree; + + return S_OKAY; +} + + + +/*------------------------------- vlr_write -------------------------------*\ + * + * Update a record. The record is first deleted then inserted. This is not + * the most efficient way to do it, but it works! + * + */ + +int vlr_write(vlr, buf, bufsize, blockno) +VLR *vlr; +void *buf; +unsigned bufsize; +ulong blockno; +{ + ulong dummy; + + vlr_del(vlr, blockno); + vlr_add(vlr, buf, bufsize, &dummy); + + RETURN S_OKAY; +} + + +/*------------------------------- vlr_read --------------------------------*\ + * + * Read a record. + * + */ + +int vlr_read(vlr, buf, blockno, sizeptr) +VLR *vlr; +void *buf; +ulong blockno; +unsigned *sizeptr; +{ + unsigned size = 0; + unsigned rest, copy; + + get_header(vlr); + _NEXTBLOCK = blockno; + + if( (blockno+1) * _BLOCKSIZE > filelength(vlr->fh) ) + return 0; + + do + { + get_block(vlr, _NEXTBLOCK); + + if( _RECSIZE > 0 ) + rest = size = _RECSIZE; + + if( !size ) + break; + + copy = rest > vlr->datasize ? vlr->datasize : rest; + rest -= copy; + + memcpy(buf, vlr->block->data, copy); + + buf = (void *)((char *)buf + vlr->datasize); + } + while( _NEXTBLOCK ); + + *sizeptr = size; + + RETURN S_OKAY; +} + +/*-------------------------------- vlr_del --------------------------------*\ + * + * Delete a record. The blocks used by the record deleted, are inserted in + * front of the delete chain. + * + */ + +int vlr_del(vlr, blockno) +VLR *vlr; +ulong blockno; +{ + ulong tmp_firstfree = _FIRSTFREE; + ulong cur_block = blockno; + + get_header(vlr); + + _FIRSTFREE = blockno; + get_block(vlr, blockno); + + _RECSIZE = 0; + put_block(vlr, blockno); + + while( _NEXTBLOCK > 0 ) + { + cur_block = _NEXTBLOCK; + _NEXTBLOCK = get_nextblock(vlr, _NEXTBLOCK); + } + + _NEXTBLOCK = tmp_firstfree; + put_block(vlr, cur_block); + + vlr->header.numrecords--; + put_header(vlr); + + RETURN S_OKAY; +} + +/* end-of-file */ diff --git a/util/.tedhist b/util/.tedhist new file mode 100644 index 0000000000000000000000000000000000000000..69c771c98489f51611bb325652225ab9b9889adf GIT binary patch literal 1440 ocmYdE$tloFW}q4<1e&A`#Ed`;VvmB+5Eu=C(GVC70rEls0RQX*L;wH) literal 0 HcmV?d00001 diff --git a/util/Makefile.in b/util/Makefile.in new file mode 100644 index 0000000..0c6ec28 --- /dev/null +++ b/util/Makefile.in @@ -0,0 +1,107 @@ +# Makefile for: util - programs to support typhoon + +DEFINES = -I../include -I../src @defs@ +YACC = @yacc@ +YFLAGS = -d +CC = @cc@ +CFLAGS = @cflags@ +LIBS = -ltyphoon +PREFIX = /usr/local +LDFLAGS = -L../src +DESTBIN = $(PREFIX)/bin +DESTOWN = root +DESTGRP = local +SHELL = /bin/sh +PROGRAMS = ddlp dbdview tyexport tyimport # tybackup tyrestore +MADESRCS = ddl.c exp.c imp.c ddl.h exp.h imp.h +SRCS = backup.c dbdview.c ddl.y ddlp.c ddlplex.c ddlpsym.c exp.y \ + export.c exportlx.c expspec.c fixlog.c imp.y import.c \ + importlx.c impspec.c restore.c util.c +HDRS = ddl.h ddlp.h ddlpglob.h ddlpsym.h exp.h export.h \ + imp.h import.h lex.h util.h +DDLP_OBJS = ddl.o ddlp.o ddlplex.o ddlpsym.o +DBDVIEW_OBJS = dbdview.o +EXPORT_OBJS = exp.o export.o exportlx.o expspec.o +IMPORT_OBJS = imp.o import.o importlx.o impspec.o +BACKUP_OBJS = backup.o util.o ../src/readdbd.o ../src/os.o ../src/unix.o +RESTORE_OBJS = restore.o util.o fixlog.o ../src/readdbd.o ../src/os.o \ + ../src/unix.o + +.DEFAULT: + co $@ + +.PHONY: all lint tags install uninstall clean distclean + +.y.c .y.h: + $(YACC) $(YFLAGS) $< + mv y.tab.c $*.c + -mv y.tab.h $*.h + +.y.o: + $(YACC) $(YFLAGS) $< + -mv y.tab.h $*.h + $(CC) -c $(CFLAGS) y.tab.c -o $@ + +.SUFFIXES: .y + +all: $(PROGRAMS) $(MADESRCS) + +ddlp: $(DDLP_OBJS) + $(CC) $(LDFLAGS) $(DDLP_OBJS) $(LIBS) -o $@ + +dbdview: $(DBDVIEW_OBJS) + $(CC) $(LDFLAGS) $(DBDVIEW_OBJS) $(LIBS) -o $@ + +tyexport: $(EXPORT_OBJS) + $(CC) $(LDFLAGS) $(EXPORT_OBJS) $(LIBS) -o $@ + +tyimport: $(IMPORT_OBJS) + $(CC) $(LDFLAGS) $(IMPORT_OBJS) $(LIBS) -o $@ + +tybackup: $(BACKUP_OBJS) + $(CC) $(LDFLAGS) $(BACKUP_OBJS) $(LIBS) -o $@ + +tyrestore: $(RESTORE_OBJS) + $(CC) $(LDFLAGS) $(RESTORE_OBJS) $(LIBS) -o $@ + +lint: + lint -u $(DEFINES) $(SRCS) + +tags: $(HDRS) $(SRCS) + ctags -w $(HDRS) $(SRCS) + +install: $(PROGRAMS) + cp $(PROGRAMS) $(DESTBIN) + cd $(DESTBIN) && -mcs -c $(PROGRAMS) + cd $(DESTBIN) && strip $(PROGRAMS) + cd $(DESTBIN) && chmod 755 $(PROGRAMS) + cd $(DESTBIN) && chown $(DESTOWN) $(PROGRAMS) + cd $(DESTBIN) && chgrp $(DESTGRP) $(PROGRAMS) + +uninstall: + cd $(DESTBIN) && rm -f $(PROGRAMS) + +clean: + -rm -f $(PROGRAMS) $(DDLP_OBJS) $(DBDVIEW_OBJS) $(EXPORT_OBJS) + -rm -f $(IMPORT_OBJS) $(BACKUP_OBJS) $(RESTORE_OBJS) + -rm -f $(MADESRCS) y.tab.[ch] + +distclean: clean + -rm -f Makefile tags core a.out + +### Do NOT edit this or the following lines. +backup.o: util.h +dbdview.o: ddlp.h +ddl.o: ddlp.h ddlpsym.h ddlpglob.h +ddlp.o: ddlp.h ddlpsym.h ddlpglob.h ddl.h +ddlplex.o: ddlp.h ddl.h ddlpsym.h ddlpglob.h lex.h lex.c +ddlpsym.o: ddlp.h ddlpsym.h ddlpglob.h +exp.o: export.h +export.o: export.h +exportlx.o: ddlp.h exp.h ddlpsym.h ddlpglob.h lex.h lex.c +expspec.o: export.h +imp.o: import.h +import.o: import.h +importlx.o: ddlp.h imp.h ddlpsym.h ddlpglob.h lex.h lex.c +impspec.o: import.h +restore.o: util.h diff --git a/util/backup.c b/util/backup.c new file mode 100644 index 0000000..bcc7e17 --- /dev/null +++ b/util/backup.c @@ -0,0 +1,528 @@ +/*---------------------------------------------------------------------------- + * File : backup.c + * Program : tybackup + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains the tybackup utility. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: backup.c,v 1.2 1999/10/03 23:36:49 kaz Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" +#include "ty_log.h" +#include "util.h" + +/*------------------------------- Constants --------------------------------*/ +#define VERSION "1.00" +#define BLOCK_SIZE 512 +#define OUTBUF_SIZE (BLOCK_SIZE * 512) +#define INBUF_SIZE (64 * 1024) + +/*-------------------------- Function prototypes ---------------------------*/ +static void SignalHandler PRM( (int); ) +static void Write PRM( (void *, ulong); ) +static void Flush PRM( (void); ) +static void DisplayProgress PRM( (char *, ulong); ) +static void BackupDatabase PRM( (void); ) +static void BackupFile PRM( (char *); ) +static void help PRM( (void); ) + +/*---------------------------- Global variables ----------------------------*/ +static ArchiveMediaHeader mediahd; /* Archive media header */ +static jmp_buf err_jmpbuf; /* Jumped to on error */ +static Dbentry dbd; /* DBD anchor */ +static int verbose = 0; /* Be verbose? */ +static int archive_fh = -1; /* Archive file handle */ +static char *dbname = ""; /* Database name */ +static char *datapath = ""; /* Path for database files */ +static char *device = "/dev/null"; /* Backup device */ +static char *outbuf; /* Output buffer */ +static ulong outbytes; /* Number of bytes in outbuf */ +static ulong total_written = 0; /* Total number of bytes written */ + int db_status; /* Required by ../read_dbd.c */ + + +/*--------------------------------------------------------------------------*\ + * + * Function : SignalHandler + * + * Purpose : + * + * Parameters: + * + * Returns : + * + */ +static void SignalHandler(sig) +int sig; +{ + if( sig == SIGSEGV ) + puts("Segmentation violation"); + else if( sig == SIGBUS ) + puts("Bus error"); + + dbd.shm->backup_active = 0; + shm_free(&dbd); + unlink(LOG_FNAME); + puts("Restore aborted."); + exit(1); +} + + + +/*--------------------------------------------------------------------------*\ + * + * Function : Write + * + * Purpose : Write a block of data to the archive. If data cannot be + * written because the backup media is full, the user will + * be prompted to insert a new media. + * + * Parameters: buf - Buffer to write. + * size - Number of bytes in buffer. + * + * Returns : Nothing. + * + */ +static void Write(buf, size) +void *buf; +ulong size; +{ + ArchiveBlockHeader blockhd; + char s[20]; + ulong copymax; + long rc; + + /* Copy as much as possible to the output buffer */ + copymax = size; + if( copymax > OUTBUF_SIZE-outbytes ) + copymax = OUTBUF_SIZE-outbytes; + + memcpy(outbuf+outbytes, buf, copymax); + outbytes += copymax; + +retry: + while( archive_fh == -1 ) + { + clock_off(); + printf("Insert backup media no %d [enter, q]", mediahd.seqno); + fflush(stdout); + gets(s); + clock_on(); + + if( s[0] == 'q' ) + return; + + if( (archive_fh = open(device, O_WRONLY)) == -1 ) + printf("Cannot open '%s' (errno %d)\n", device, errno); + } + + if( outbytes == OUTBUF_SIZE ) + { + rc = write(archive_fh, outbuf, OUTBUF_SIZE); + + if( rc == -1 ) + { + printf("Write error (errno %d)\n", errno); + longjmp(err_jmpbuf, 1); + } + else if( rc != OUTBUF_SIZE ) + { + close(archive_fh); + goto retry; + } + + /* Now copy the rest of the buffer */ + outbytes = size - copymax; + memcpy(outbuf, (char *)buf + copymax, outbytes); + + total_written += rc; + } +} + + +static void Flush() +{ + long rc; + + /* Ensure that a complete block is written to archive */ + outbytes += (BLOCK_SIZE - (outbytes % BLOCK_SIZE)); + + if( (rc = write(archive_fh, outbuf, outbytes)) != outbytes ) + { + printf("Write error (errno %d, rc %ld)\n", errno, rc); + longjmp(err_jmpbuf, 1); + } + + total_written += rc; + outbytes = 0; +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : DisplayProgress + * + * Purpose : Prints the number of bytes read so far from the current table. + * + * Parameters: table - Table name. + * bytes - Number of bytes read. + * + * Returns : Nothing. + * + */ +static void DisplayProgress(table, bytes) +char *table; +ulong bytes; +{ + printf("\rReading %-26.26s %10s bytes", table, printlong(bytes)); + fflush(stdout); +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : BackupDatabase + * + * Purpose : Controls the backup. + * + * Parameters: None. + * + * Returns : Nothing. + * + */ +static void BackupDatabase() +{ + ArchiveRecordHeader recordhd; + ArchiveTableHeader tablehd; + Record *rec; + ulong recid; /* Record ID of current table */ + ulong recno; /* Current record in current table */ + ulong recsize; /* Record size in current table */ + ulong bytecount; /* Number of bytes read in current table*/ + ulong numread; /* Number of bytes read */ + ulong timeout; + int fh; + int maxread, i, rc; + char fname[128], objname[128]; + char buf[INBUF_SIZE]; + RECORD filehd; + + dbd.shm->backup_active = 1; + + for( rec=dbd.record, recid=0; recidcurr_recid = recid; + + sprintf(fname, "%s/%s", datapath, dbd.file[rec->fileid].name); + sprintf(objname, "table %s", rec->name); + + if( (fh = os_open(fname, O_RDONLY|O_BINARY, 0)) == -1 ) + { + printf("Cannot open '%s'\n", fname); + return; + } + + /* Because of referential integrity data, the record size may + * be different from rec->size, so we read the header for H.recsize. + */ + read(fh, &filehd.H, sizeof filehd.H); + + recsize = filehd.H.recsize; + maxread = INBUF_SIZE / recsize; + bytecount = 0; + recno = 0; + + /* Write table header */ + strcpy(tablehd.fname, dbd.file[rec->fileid].name); + strcpy(tablehd.table, rec->name); + tablehd.id = ARCHIVE_TABLE; + tablehd.recsize = recsize; + Write(&tablehd, sizeof tablehd); + + if( verbose ) + DisplayProgress(objname, 0); + + do + { + for( i=0; icurr_recno = recno; + + lseek(fh, recno * recsize, SEEK_SET); + numread = read(fh, buf, recsize); + + if( numread != recsize ) + break; + + recordhd.id = ARCHIVE_RECORD; + recordhd.recid = recid; + recordhd.recno = recno; + + Write(&recordhd, sizeof recordhd); + Write(buf, numread); + + bytecount += recsize; + recno++; + } + + if( verbose ) + DisplayProgress(objname, bytecount); + } + while( i == maxread ); + + if( verbose ) + puts(""); + + close(fh); + } + + /* Set curr_recid to max value, so that all changes are logged */ + dbd.shm->curr_recid = 0xffffffff; + dbd.shm->backup_active = 0; + + /* Wait for last transaction to complete */ + timeout = 120; + while( dbd.shm->num_trans_active > 0 && timeout-- ) + sleep(1); +} + + + +/*--------------------------------------------------------------------------*\ + * + * Function : BackupFile + * + * Purpose : Write a sequential file to archive. + * + * Parameters: fname - File name. + * + * Returns : Nothing. + * + */ +static void BackupFile(fname) +char *fname; +{ + ArchiveFileHeader filehd; + ArchiveFileDataHeader datahd; + int fh; + char buf[INBUF_SIZE]; + ulong numread; + ulong bytecount=0; + + if( (fh = open(fname, O_RDONLY)) == -1 ) + { + printf("Cannot open '%s'\n", fname); + return; + } + + datahd.id = ARCHIVE_FILEDATA; + filehd.id = ARCHIVE_FILE; + strcpy(filehd.fname, fname); + Write(&filehd, sizeof filehd); + + while( numread = read(fh, buf, INBUF_SIZE) ) + { + datahd.size = numread; + Write(&datahd, sizeof datahd); + Write(buf, numread); + + bytecount += numread; + + if( verbose ) + DisplayProgress(fname, bytecount); + } + + datahd.size = 0; + Write(&datahd, sizeof datahd); + + if( verbose ) + puts(""); + + close(fh); +} + + + + +static void help() +{ + puts("Syntax: tybackup database [option]...\n" + "Options:\n" + " -d Backup device\n" + " -f Path for data files\n" + " -v Be verbose\n"); + exit(1); +} + + + +main(argc, argv) +int argc; +char *argv[]; +{ + ArchiveEnd end; + char dbdname[20]; + int i; + + /* The output buffer MUST be bigger than the input buffer */ + assert(INBUF_SIZE < OUTBUF_SIZE); + + printf("Typhoon Online Backup version %s\n", VERSION); + + if( argc < 3 ) + help(); + + for( i=2; i +#else +# include +#endif +#include +#include +#include +#include +#include +#include "ty_dbd.h" +#include "ddlp.h" +#define FREE(p) if( p ) free(p) + +static char CONFIG_CONST rcsid[] = "$Id: dbdview.c,v 1.6 1999/10/04 04:11:31 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ +static void viewdbd PRM( (int); ) +int main PRM( (int, char **); ) + +/*---------------------------- Global variables ----------------------------*/ +static Header header; +static Field *fieldtab; +static Record *recordtab; +static File *filetab; +static Key *keytab; +static KeyField *keyfieldtab; +static Structdef *structtab; +static Sequence *seqtab; + + +/*--------------------------------------------------------------------------*\ + * + * Function : viewdbd + * + * Purpose : View the tables in the dbd-file. + * + * Parameters: dbdfile - File handle. + * + * Returns : Nothing. + * + */ +static void viewdbd(dbdfile) +int dbdfile; +{ + static char *key_type[] = { + "primary", + "altern.", + "foreign", + "referen" + }; + char type[50]; + int i; + Key *key; + Field *field; + Record *record; + Structdef *struc; + Sequence *seq; + + read(dbdfile, &header, sizeof header); + + if( strcmp(header.version, DBD_VERSION) ) + { + puts("Illegal version ID"); + return; + } + + + printf("%d files\n", header.files); + printf("%d keys\n", header.keys); + printf("%d keyfields\n", header.keyfields); + printf("%d records\n", header.records); + printf("%d fields\n", header.fields); + printf("%d structdefs\n", header.structdefs); + printf("%d sequences\n", header.sequences); + + /*---------- allocate memory for tables ----------*/ + filetab = (void *)malloc(sizeof(File) * header.files); + keytab = (void *)malloc(sizeof(Key) * header.keys); + keyfieldtab = (void *)malloc(sizeof(KeyField) * header.keyfields); + fieldtab = (void *)malloc(sizeof(Field) * header.fields); + recordtab = (void *)malloc(sizeof(Record) * header.records); + structtab = (void *)malloc(sizeof(Structdef) * header.structdefs); + seqtab = (void *)malloc(sizeof(Structdef) * header.structdefs); + + if( (header.files && !filetab) || + (header.keys && !keytab) || + (header.keyfields && !keyfieldtab) || + (header.fields && !fieldtab) || + (header.records && !recordtab) || + (header.structdefs && !structtab) || + (header.sequences && !seqtab) ) + { + puts("out of memory"); + FREE(filetab); + FREE(fieldtab); + FREE(recordtab); + FREE(keytab); + FREE(keyfieldtab); + FREE(structtab); + FREE(seqtab); + return; + } + + /*---------- read tables ----------*/ + read(dbdfile, filetab, sizeof(File) * header.files); + read(dbdfile, keytab, sizeof(Key) * header.keys); + read(dbdfile, keyfieldtab, sizeof(KeyField) * header.keyfields); + read(dbdfile, recordtab, sizeof(Record) * header.records); + read(dbdfile, fieldtab, sizeof(Field) * header.fields); + read(dbdfile, structtab, sizeof(Structdef) * header.structdefs); + read(dbdfile, seqtab, sizeof(Sequence) * header.sequences); + + puts("----------------------------------- FILES -------------------------------------"); + printf(" ID NAME PGSIZE REC/KEY ID TYPE\n"); + for( i=0; itype)-1]); + + if( key->type & KT_OPTIONAL ) + strcat(type, " opt"); + + if( KT_GETBASIC(key->type) == KT_ALTERNATE && (key->type & KT_UNIQUE) ) + strcat(type, " unique"); + + printf("%3d %-20s %-18s %4ld %4u %10ld %6d", + i, + key->name, + type, + key->fileid, + key->size, + key->first_keyfield, + key->fields); + if( KT_GETBASIC(key->type) == KT_FOREIGN ) + printf(" %ld", key->parent); + puts(""); + } + + puts(""); + puts("--------------------------------- KEY FIELDS ----------------------------------"); + printf(" ID FIELD OFFSET ASCENDING\n"); + for( i=0; iname, + record->size, + record->is_vlr ? "Yes" : "No", + record->fileid, + record->first_field, + record->fields, + record->keys, + record->first_key, + record->structid); + if( record->first_foreign != -1 ) + printf(" %10ld", record->first_foreign); + puts(""); + } + + puts("\n----------------------------------- STRUCTS -----------------------------------"); + puts(" ID NAME SIZE 1ST_MEMBER MEMBERS UNION CONTROL"); + for( i=0, struc=structtab; iname, + struc->size, + struc->first_member, + struc->members, + struc->is_union ? "yes" : "no"); + + if( struc->is_union ) + printf(" %7lu", struc->control_field); + + puts(""); + } + + + puts("\n----------------------------------- FIELDS ------------------------------------"); + puts(" ID NAME REC NEST OFFSET ELEMSZ SIZE TYPE STRID KEYID SIZE_FLD"); + for( i=0, field=fieldtab; iname, + field->recid, + field->nesting, + field->offset, + field->elemsize, + field->size, + field->type); + + if( field->type == FT_STRUCT ) + printf(" %5ld", field->structid); + else + printf(" "); + + if( field->type & FT_KEY ) + printf(" %5ld", field->keyid); + else + if( fieldtab[i].type & FT_VARIABLE ) + printf(" %8lu", field->keyid); + puts(""); + } + + puts("\n---------------------------------- SEQUENCES ----------------------------------"); + puts(" ID NAME START STEP ORDER"); + for( i=0, seq=seqtab; iname, + seq->start, + seq->step, + seq->asc ? "asc" : "desc"); + } + + free(filetab); + free(fieldtab); + free(recordtab); + free(keytab); + free(keyfieldtab); + free(structtab); + free(seqtab); +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ + int dbdfile; + char fname[80]; + + if( argc < 2 ) + { + puts("Syntax: dbdview dbd-file"); + exit(1); + } + + /* check extension */ + strcpy(fname, argv[argc-1]); + if( strstr(fname, ".dbd") == NULL ) + strcat(fname, ".dbd"); + + if( (dbdfile=open(fname, O_RDONLY|CONFIG_O_BINARY)) == -1 ) + { + printf("cannot open %s\n", fname); + exit(1); + } + + viewdbd(dbdfile); + + close(dbdfile); + return 0; +} + +/* end-of-file */ + diff --git a/util/ddl.y b/util/ddl.y new file mode 100644 index 0000000..b66953d --- /dev/null +++ b/util/ddl.y @@ -0,0 +1,556 @@ +/*---------------------------------------------------------------------------- + * File : ddl.y + * Program : ddlp + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Grammar for ddl-files. + * + * $Id: ddl.y,v 1.6 1999/10/03 23:36:49 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +%{ + +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" + +#include "ddlp.h" +#include "ddlpsym.h" +#include "ddlpglob.h" + +/*--------------------------- Function prototypes --------------------------*/ +int yylex PRM( (void); ) +static void add_keymember PRM( (sym_struct *, char *, int); ) + +/*---------------------------- Global variables ----------------------------*/ +static unsigned type; /* Holds FT_.. flags of current field */ +static sym_member *control_field; /* Field that controls current union */ + + +%} + +%union { + int val; + char is_union; + char s[IDENT_LEN+1]; +} + +%start database + +%token T_DATABASE T_KEY T_DATA T_FILE T_CONTAINS T_RECORD +%token T_UNIQUE T_DEFINE T_CONTROLLED T_MAP T_ARROW +%token T_PRIMARY T_ALTERNATE T_FOREIGN T_ON T_DELETE T_RESTRICT +%token T_REFERENCES T_UPDATE T_CASCADE T_NULL T_SEQUENCE +%token T_CHAR T_SHORT T_INT T_LONG T_SIGNED T_UNSIGNED T_FLOAT +%token T_DOUBLE T_UCHAR T_USHORT T_ULONG T_STRUCT T_UNION +%token T_COMPOUND T_ASC T_DESC T_VARIABLE T_BY +%token T_IDENT T_STRING +%token T_NUMBER T_CHARCONST +%token '[' ']' '{' '}' ';' ',' '.' '>' +%token '+' '-' '*' '/' '(' ')' + +%type expr opt_sortorder opt_unique opt_null pagesize action +%type map_id +%type struct_or_union +%type opt_ident key_type + +%left '+' '-' +%left '*' '/' + +%% + +database : T_DATABASE T_IDENT '{' decl_list '}' + { strcpy(dbname, $2); } + ; + +pagesize : /* use default page size */ + { $$ = 512; } + | '[' expr ']' + { + if( $2 < 512 ) + { + printf("Page size too small\n"); + $$ = 512; + } + else + $$ = $2; + } + ; + +decl_list : decl + | decl_list decl + ; + +decl : T_DATA T_FILE pagesize T_STRING T_CONTAINS T_IDENT ';' + { + add_contains('d', $6, ""); + add_file('d', $4, (unsigned) $3); + } + | T_KEY T_FILE pagesize T_STRING T_CONTAINS T_IDENT '.' key_type ';' + { + char type = strcmp($8, "") ? 'k' : 'r'; + + add_contains(type, $6, $8); + add_file(type, $4, (unsigned) $3); + } + | record_head '{' member_list opt_key_list '}' + { + sym_endstruct(); + record[records-1].size = structnest[curnest+1]->size; + structdef[record[records-1].structid].size = record[records-1].size; + } + | T_DEFINE T_IDENT expr { add_define($2, $3); } + | sequence + | T_MAP '{' map_list '}' + ; + +record_head : T_RECORD T_IDENT { add_record($2); + add_structdef($2, 0, 0); + sym_addstruct($2, 0); } + ; + +key_type : T_IDENT { strcpy($$, $1); } + | T_REFERENCES { strcpy($$, ""); } + ; + +member_list : member + | member_list member + ; + +member : membertype T_IDENT opt_dimen ';' + { + sym_addmember($2, (int) type, NULL); + cur_str->last_member->id = fields; + + if( type != FT_STRUCT ) + add_field($2, (int) type); + + /* Increase the number of level-0 fields of the record */ + if( curnest == 0 ) + structdef[record[records-1].structid].members++; + + type = 0; + } + | struct_specifier ';' + ; + +opt_dimen : + | dimension opt_varlen_decl + ; + +dimension : array + | dimension array + ; + +array : '[' expr ']' { dim[dims++] = $2; } + ; + + +opt_varlen_decl + : + | T_VARIABLE T_BY T_IDENT + { +/* if( curnest != 1 ) + yyerror("variable size fields cannot be nested");*/ + + if( !(size_field = sym_findmember(structnest[0], $3)) ) + yyerror("unknown struct member '%s'", $3); + } + ; + + +/*--------------------------------------------------------------------------*/ +/* Key declaration part */ +/*--------------------------------------------------------------------------*/ + +opt_key_list: opt_primary_key_decl + opt_alternate_key_decl_list + opt_foreign_key_decl_list + ; + +/*--------------------------------------------------------------------------*/ +/* Primary key */ +/*--------------------------------------------------------------------------*/ + +opt_primary_key_decl + : /* No primary key */ + | T_PRIMARY key_decl ';' + { + key[keys-1].type = KT_PRIMARY|KT_UNIQUE; + } + ; + + +/*--------------------------------------------------------------------------*/ +/* Alternate key */ +/*--------------------------------------------------------------------------*/ + +opt_alternate_key_decl_list + : + | alternate_key_decl_list + ; + +alternate_key_decl_list + : alternate_key_decl + | alternate_key_decl_list alternate_key_decl + ; + +alternate_key_decl + : T_ALTERNATE opt_unique key_decl opt_null ';' + { + key[keys-1].type = KT_ALTERNATE | $2 | $4; + } + ; + + +/*--------------------------------------------------------------------------*/ +/* Foreign key */ +/*--------------------------------------------------------------------------*/ + +opt_foreign_key_decl_list + : + | foreign_key_decl_list + ; + +foreign_key_decl_list + : foreign_key_decl + | foreign_key_decl_list foreign_key_decl + ; + + +foreign_key_decl + : T_FOREIGN key_decl T_REFERENCES T_IDENT + T_ON T_UPDATE action + T_ON T_DELETE action opt_null ';' + { + /* Set the record's first_foreign field */ + if( record[records-1].first_foreign == -1 ) + record[records-1].first_foreign = keys-1; + + key[keys-1].type = KT_FOREIGN | $7 | $10 | $11; + check_foreign_key($4, &key[keys-1]); + } + ; + +action : T_RESTRICT { $$ = KT_RESTRICT; } + | T_CASCADE { $$ = KT_CASCADE; + yyerror("'cascade' not supported"); + } + ; + + +/*--------------------------------------------------------------------------*/ +/* Key declaration rules used by primary, alternate and foreign keys */ +/*--------------------------------------------------------------------------*/ + +opt_null : /* not optional */ + { + $$ = 0; + } + | T_NULL T_BY T_IDENT + { + sym_member *mem; + int type; + + if( !(mem = sym_findmember(cur_str, $3)) ) + yyerror("'%s' is not a member the record", $3); + else + { + key[keys-1].null_indicator = mem->id; + type = FT_GETBASIC(field[mem->id].type); + + if( type != FT_CHAR && type != FT_CHARSTR ) + yyerror("field determiner must be char or string"); + } + + $$ = KT_OPTIONAL; + } + ; + + +key_decl : key_decl_head comkey_member_list '}' + { + sym_endstruct(); + + /* Set the size of the compound key. + * Foreign keys have special size. + */ + if( KT_GETBASIC(key[keys-1].type) == KT_FOREIGN ) + key[keys-1].size = sizeof(REF_ENTRY); + else + key[keys-1].size = last_str->size; + } + | T_KEY T_IDENT + { + sym_member *mem; + + if( !(mem = sym_findmember(cur_str, $2)) ) + yyerror("unknown field '%s'", $2); + + add_key($2); + add_keyfield(mem->id, 1); + + key[keys-1].size = mem->size; + field[mem->id].type |= FT_KEY; + field[mem->id].keyid = keys-1; + } + ; + +key_decl_head + : T_KEY T_IDENT '{' + { + /* Ensure that the key name is not also a field name */ + if( sym_findmember(cur_str, $2) ) + yyerror("the key '%s' is already a field name", $2); + + sym_addstruct($2, 0); + add_key($2); + } + ; + + +/*--------------------------------------------------------------------------*/ +/* Compound key declaration */ +/*--------------------------------------------------------------------------*/ + +comkey_member_list + : comkey_member + | comkey_member_list ',' comkey_member + ; + +comkey_member + : T_IDENT opt_sortorder + { + add_keymember(structnest[curnest-1], $1, $2); + } + ; + +opt_unique : { $$ = 0; /* Not unique */} + | T_UNIQUE { $$ = KT_UNIQUE; /* Unique */ } + ; + +opt_sortorder + : /* default */ { $$ = 1; /* Ascending */ } + | T_ASC { $$ = 1; /* Ascending */ } + | T_DESC { $$ = 0; /* Descending */ } + ; + +/*--------------------------------------------------------------------------*/ +/* Structure member definition */ +/*--------------------------------------------------------------------------*/ + +membertype : int_type + | int_sign { if( type == FT_UNSIGNED ) + type |= FT_INT; } + | int_sign int_type + | float_type + | u_type + | T_LONG float_type { type |= FT_LONG; } + ; + +int_type : T_CHAR { type |= FT_CHAR; } + | T_SHORT { type |= FT_SHORT; } + | T_INT { type |= FT_INT; } + | T_LONG { type |= FT_LONG; } + ; + +int_sign : T_SIGNED + | T_UNSIGNED { type |= FT_UNSIGNED; } + ; + +float_type : T_FLOAT { type |= FT_FLOAT; } + | T_DOUBLE { type |= FT_DOUBLE; } + ; + +u_type : T_UCHAR { type |= FT_UNSIGNED|FT_CHAR; } + | T_USHORT { type |= FT_UNSIGNED|FT_SHORT; } + | T_ULONG { type |= FT_UNSIGNED|FT_LONG; } + ; + +expr : expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { $$ = $1 / $3; } + | '(' expr ')' { $$ = $2; } + | T_NUMBER + ; + +/*--------------------------------------------------------------------------*/ +/* struct or union declaration */ +/*--------------------------------------------------------------------------*/ + +struct_specifier + : struct_or_union_opt_ident '{' member_list '}' T_IDENT opt_dimen + { + sym_struct *str; + Field *fld; + Structdef *strdef; + + sym_endstruct(); + + str = structnest[curnest + 1]; + fld = &field[str->fieldid]; + strdef = &structdef[ fld->structid ]; + + strcpy(fld->name, $5); + strdef->size = str->size; + strdef->members = str->members; + + sym_addmember($5, FT_STRUCT, structnest[curnest+1]); + + fld->size = cur_str->last_member->size; + fld->elemsize = cur_str->last_member->elemsize; + fld->offset = cur_str->last_member->offset; + + if( size_field ) + { + fld->type |= FT_VARIABLE; + fld->keyid = size_field->id; + size_field = NULL; + } + } + | struct_or_union T_IDENT T_IDENT opt_dimen + { + sym_struct *str = sym_findstruct($2, $1); + + if( str ) + { + sym_addmember($3, FT_STRUCT, str); +/* add_field($3, FT_STRUCT);*/ + } + /* else error message */ + } + ; + +struct_or_union_opt_ident + : struct_or_union opt_ident + { + add_field("", FT_STRUCT); + sym_addstruct($2, $1); + cur_str->fieldid = fields-1; + field[fields-1].structid = structdefs; + + add_structdef($2, $1, (int) ($1 ? control_field->id : 0)); + + /* Increase the number of level-0 field of the record. + * Actually, at this point the grammar we have moved to + * level 1. + */ + if( curnest == 1 ) + structdef[record[records-1].structid].members++; + } + ; + +struct_or_union + : T_STRUCT + { + $$ = 0; + } + | T_UNION T_CONTROLLED T_BY T_IDENT + { + $$ = 1; + if( !(control_field = sym_findmember(cur_str, $4)) ) + yyerror("'%s' is not a member of the union/struct '%s'", + $4, cur_str->name); + } + ; + +opt_ident : { *$$ = 0; } + | T_IDENT { strcpy($$, $1); } + ; + + +/*--------------------------------------------------------------------------*/ +/* sequence */ +/*--------------------------------------------------------------------------*/ + +sequence : T_SEQUENCE T_IDENT T_NUMBER opt_sortorder T_BY T_NUMBER ';' + { add_sequence($2, (unsigned long) $3,$4, (unsigned long) $6); } + ; + +/*--------------------------------------------------------------------------*/ +/* map_list */ +/*--------------------------------------------------------------------------*/ + +map_list : map + | map_list map + ; + +map : map_id T_ARROW map_id ';' { header.sorttable[$1] = $3; } + ; + +map_id : T_CHARCONST { $$ = $1; } + | T_NUMBER { $$ = $1; } + ; + + +%% + +#include + + + +static void add_keymember(str, name, sortorder) +sym_struct *str; +char *name; +int sortorder; +{ + sym_member *mem; + + if( ( mem = sym_findmember(str, name) ) ) + { + dims = mem->dims; + memcpy(dim, mem->dim, sizeof(*dim) * dims); + sym_addmember(name, mem->type, mem->struc); + add_keyfield(mem->id, sortorder); + } + else + yyerror("unknown struct member '%s'", name); +} + + + + +extern int errors; + +int yyerror(char *fmt CONFIG_ELLIPSIS) +{ + va_list ap; + + printf("%s %d: ", ddlname, lex_lineno); + va_start(ap, fmt); + vprintf(fmt, ap); + puts(""); + va_end(ap); + errors++; + return 0; +} + +/* end-of-file */ diff --git a/util/ddlp.c b/util/ddlp.c new file mode 100644 index 0000000..1303c0c --- /dev/null +++ b/util/ddlp.c @@ -0,0 +1,926 @@ +/*---------------------------------------------------------------------------- + * File : ddlp.c + * Program : ddlp + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Grammar for ddl-files. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include "environ.h" +#ifndef CONFIG_UNIX +# include +# include +# include +#else +# include +# include +# include +#endif +#define DEFINE_GLOBALS +#include + +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" + +#include "ddlp.h" +#include "ddlpsym.h" +#include "ddlpglob.h" +#include "ddl.h" + +static CONFIG_CONST char rcsid[] = "$Id: ddlp.c,v 1.6 1999/10/04 04:11:31 kaz Exp $"; + + +/*-------------------------- Function prototypes ---------------------------*/ +int yyparse PRM( (void); ) +void *extend_table PRM( (void **, int *, int); ) +void align_offset PRM( (unsigned *, int); ) +void check_consistency PRM( (void); ) +void print_fieldname PRM( (FILE *, int, Id); ) +void fix_file PRM( (void); ) +void fix_fields PRM( (void); ) + +int main PRM( (int, char **); ) + +static void init_vars PRM( (void); ) +static char *strupr PRM( (char *); ) +static int istrcmp PRM( (char *, char *); ) + +/*---------------------------- Global variables ----------------------------*/ +FILE *lex_file, *hfile; +int dbdfile; + +static char paramhelp[] = "\ +Syntax: ddlp [option]... file[.ddl]\n\ +Options:\n\ + -a[1|2|4|8] Set structure alignment (default is %d)\n\ + -f Only generate field constants for keys\n\ + -h Use as header file\n"; + + + +static char *strupr(s) +char *s; +{ + char *olds = s; + + while( *s ) + { + *s = toupper(*s); + s++; + } + + return olds; +} + + +static int istrcmp(s1, s2) +char *s1, *s2; +{ + while( tolower(*s1) == tolower(*s2) && *s1 && *s2 ) + s1++, s2++; + + return tolower(*s1) - tolower(*s2); +} + + +void *extend_table(table, elems, elemsize) +void **table; +int *elems, elemsize; +{ + /* Some implementations of realloc() choke if they get a NULL pointer, + * so we handle an empty table as a special case. + */ + if( !*table ) + { + if( !(*table = (void *)calloc((size_t) 50, (size_t) elemsize)) ) + { + yyerror("out of memory"); + exit(1); + } + } + else if( !(*elems % 50) ) + { + if( !(*table = (void *)realloc((void *) *table, (size_t) ((*elems + 50) * elemsize))) ) + { + yyerror("out of memory"); + exit(1); + } + + memset((char *)*table + elemsize * (*elems), 0, (size_t) (50 * elemsize)); + } + + return (void *)((char *)*table + elemsize * ((*elems)++)); +} + + + +/*------------------------------ align_offset ------------------------------*\ + * + * Purpose : Align an address to fit the next field type. + * + * Parameters: offset - Offset to align. + * type - Field type (FT_...). + * + * Returns : offset - The aligned offset. + * + */ +void align_offset(offset, type) +unsigned *offset; +int type; +{ + int rem = *offset % align; + int size; + +/* if( type == FT_STRUCT ) + return;*/ + + if( !rem || FT_GETBASIC(type) == FT_CHAR || !*offset ) + return; + + if( type == FT_STRUCT ) + size = sizeof(long); + else + size = typeinfo[FT_GETBASIC(type) - 1].size; + + if( rem == size ) + return; + + if( size < align - rem ) + *offset += rem; + else + *offset += align - rem; +} + + +/*--------------------------------- add_key --------------------------------*\ + * + * Purpose : Adds a key definition to the record currently being defined. + * + * Parameters: name - Key name + * + * Returns : Nothing. + * + */ +void add_key(name) +char *name; +{ + Key *k = extend_table((void **)&key, &keys, sizeof *key); + + /* Add an entry to the key table */ + k->first_keyfield = keyfields; + k->fields = 0; + k->size = 0; + k->fileid = -1; + strcpy(k->name, name); + + /* Increase the number of keys in the record */ + record[records-1].keys++; +} + + +/*------------------------------- add_keyfield -----------------------------*\ + * + * Purpose : Adds a field to the key currently being defined. + * + * Parameters: id - Field id. + * ascending - Is key sorted in ascending order? + * + * Returns : Nothing. + * + */ + +void add_keyfield(id, ascending) +Id id; +int ascending; +{ + KeyField *keyfld = extend_table((void **)&keyfield, &keyfields, + sizeof *keyfld); + + keyfld->field = id; + keyfld->asc = ascending; + + /* The offset is only used in compound keys */ + if( key[keys-1].fields ) + keyfld->offset = cur_str->last_member->offset; + else + keyfld->offset = 0; + + /* Increase the number of fields of the key currently being defined */ + key[keys-1].fields++; +} + + +void add_define(name, value) +char *name; +int value; +{ + strcpy(define[defines].name, name); + define[defines++].value = value; +} + + +void add_file(type, name, pagesize) +int type; +char *name; +unsigned pagesize; +{ + File *fil = extend_table((void **)&file, &files, sizeof *file); + + fil->id = -1; /* Unresolved */ + fil->type = type; + fil->pagesize = pagesize; + strcpy(fil->name, name); +} + + +/*------------------------------ add_contains ------------------------------*\ + * + * Purpose : Add a new entry to the contains table. This table is used to + * determine which records and keys references and are referenced + * by the file table entries. + * + * Parameters: type - 'k' = key, 'r' = record. + * record - Record name. + * key - Key name ("" if type is 'r'). + * + * Returns : Nothing. + * + */ +void add_contains(type, record, key) +int type; +char *record, *key; +{ + Contains *con = extend_table((void **)&contains, &conts, sizeof *con); + + strcpy(con->record, record); + strcpy(con->key, key); + con->fileid = files; + con->type = type; + con->line = lex_lineno; +} + + +/*------------------------------- add_record -------------------------------*\ + * + * Purpose : This function adds a new entry to the record table. The fileid + * is set to -1 to indicate that the index into file[] is + * unresolved. This file be fixed by fix_file(). + * + * Parameters: name - Record name. + * + * Returns : Nothing. + * + */ +void add_record(name) +char *name; +{ + Record *rec = extend_table((void **)&record, &records, sizeof *record); + int i; + + /* Add an entry to the record table */ + rec->first_field = fields; + rec->first_key = keys; + rec->first_foreign = -1; + rec->size = 0; + rec->is_vlr = 0; + rec->fileid = -1; + rec->structid = structdefs; + strcpy(rec->name, name); + + /* If this record has a reference file, must be set */ + for( i=0; i") ) + break; + + if( i < conts ) + rec->ref_file = contains[i].fileid; + else + rec->ref_file = -1; + + /* No variable length fields have occurred in this record so far */ + varlen_field_occurred = 0; +} + + +/*------------------------------- add_field --------------------------------*\ + * + * Purpose : Adds a new entry to the field table. + * + * If the variable is not empty it contains the + * name of the field that determines the size of the field being + * added. Thus, if is non-empty, the field being + * added is of variable length. + * + * Parameters: name - Field name. + * type - Field type. Contains FT_... flags. + * + * Returns : Nothing. + * + */ +void add_field(name, type) +char *name; +int type; +{ + sym_member *mem = cur_str->last_member; + Field *fld = extend_table((void **)&field, &fields, sizeof *field); + + /* If the field is a char string the FT_CHARSTR flag must be set */ + if( FT_GETBASIC(type) == FT_CHAR && mem->size > 1 ) + { + type &= ~FT_BASIC; /* Clear the basic flags and set */ + type |= FT_CHARSTR; /* the FT_CHARSTR flag instead */ + } + + fld->recid = records-1; /* Link field to current record */ + fld->type = type; + fld->nesting = curnest; + strcpy(fld->name, name); + + if( type != FT_STRUCT ) + { + fld->size = mem->size; + fld->offset = mem->offset; + fld->elemsize = mem->elemsize; + } + + record[records-1].fields++; + + /* Check if the current field is of variable length */ + if( size_field ) + { + if( mem->dims != 1 ) + yyerror("variable size fields must be one-dimension arrays"); + + if( (size_field->type & (FT_SHORT|FT_UNSIGNED)) != (FT_SHORT|FT_UNSIGNED) ) + yyerror("size field '%s' must be 'unsigned short'", size_field->name); + + /* Store id of size field in keyid (dirty, but saves space) */ + fld->keyid = size_field->id; + fld->type |= FT_VARIABLE; + record[records-1].is_vlr = 1; + + size_field = NULL; + + /* Ensure that no fixed length fields follow */ + varlen_field_occurred = 1; + } + else if( varlen_field_occurred && curnest == 0 ) + yyerror("fixed length field '%s' follows variable length field", name); +} + + + +/*------------------------------ add_structdef -----------------------------*\ + * + * Purpose : Add a structure definition. + * + * Parameters: name - Structure name. + * is_union - Is the structure a union. + * control_field- If is true this is the id of the + * control field. + * + * Returns : Nothing. + * + */ +void add_structdef(name, is_union, control_field) +char *name; +int is_union, control_field; +{ + Structdef *str = extend_table((void **)&structdef, &structdefs, + sizeof *structdef); + + strcpy(str->name, name); + str->first_member = fields; + str->is_union = is_union; + + if( is_union ) + str->control_field = control_field; +} + + +/*------------------------------ add_sequence -----------------------------*\ + * + * Purpose : Add a sequence definition. + * + * Parameters: name - Sequence name. + * start - Starting number. + * asc - 1 = ascending, 0 = descending. + * step - Step. + * + * Returns : Nothing. + * + */ +void add_sequence(name, start, asc, step) +char *name; +ulong start, step; +int asc; +{ + Sequence *seq = extend_table((void **)&sequence, &sequences, + sizeof *sequence); + + strcpy(seq->name, name); + seq->start = start; + seq->asc = asc; + seq->step = step; +} + + +/*---------------------------- check_foreign_key ---------------------------*\ + * + * Purpose : Check that a foreign key matches its target (primary key). + * + * Parameters: name - Target record name. + * foreign_key - Pointer to foreign key. + * + * Returns : Nothing. + * + */ +void check_foreign_key(name, foreign_key) +char *name; +Key *foreign_key; +{ + Record *rec; /* Ptr to parent record */ + Key *primary_key; /* Ptr to parent's primary key */ + KeyField *primary_fld; /* Ptr to first field of primary_key */ + KeyField *foreign_fld; /* Ptr to first field of foreign_key */ + int i; + + /* Check if the record has a primary key */ + for( rec=record, i=0; iname, name) ) + break; + + if( i == records ) + { + yyerror("unknown target record '%s'", name); + return; + } + + if( !rec->keys ) + { + yyerror("The target '%s' has no primary key", name); + return; + } + + primary_key = &key[ rec->first_key ]; + + if( KT_GETBASIC(primary_key->type) != KT_PRIMARY ) + { + yyerror("The target '%s' has no primary key", name); + return; + } + + if( primary_key->fields != foreign_key->fields ) + { + yyerror("The foreign key '%s' does not match its target", name); + return; + } + + primary_fld = &keyfield[ primary_key->first_keyfield ]; + foreign_fld = &keyfield[ foreign_key->first_keyfield ]; + + /* Find the reference file of the parent */ + foreign_key->parent = rec - record; + rec->dependents++; + + + if( rec->ref_file == -1 ) + { + yyerror("The parent table '%s' has no reference file", name); + return; + } + + foreign_key->fileid = rec->ref_file; + + /* Compare the format of the primary key with that of the foreign key */ + for( i=0; ifields; i++, foreign_fld++, primary_fld++ ) + { + if( FT_GETSIGNEDBASIC(field[primary_fld->field].type) != + FT_GETSIGNEDBASIC(field[foreign_fld->field].type) ) + break; + + /* Set sort order */ + foreign_fld->asc = primary_fld->asc; + } + + if( i < primary_key->fields ) + { + yyerror("The foreign key '%s' does not match its target", name); + return; + } +} + + +/*-------------------------------- fix_file --------------------------------*\ + * + * Purpose : This function sets the fileid of all the records and the keys. + * + * Parameters: None. + * + * Returns : Nothing. + * + */ +void fix_file() +{ + Contains *con; + Record *rec; + int i, j, n; + + for( i=0, con=contains; iname, con->record) ) + break; + + if( j == records ) + { + printf("unknown record '%s' in contains statement\n", con->record); + continue; + } + + /* Link the record or the key to a file and vice versa */ + switch( con->type ) + { + case 'r': + break; + + case 'd': + rec->fileid = con->fileid; + + if( rec->is_vlr ) + file[i].type = 'v'; + break; + + case 'k': + for( n=rec->keys, j=rec->first_key; n--; j++ ) + if( !strcmp( key[j].name, con->key) ) + break; + + if( KT_GETBASIC(key[j].type) == KT_FOREIGN ) + { + int tmp = lex_lineno; + + lex_lineno = con->line; + yyerror("a foreign key cannot be contained in a file"); + lex_lineno = tmp; + break; + } + + if( n < 0 && strcmp(con->key, "") ) + { + printf("unknown key '%s' contained in file '%s'\n", + con->key, file[con->fileid].name); + continue; + } + + key[j].fileid = con->fileid; + break; + } + + con->entry = j; + file[i].id = j; + } + + /* Check that each record is contained is contained in a data file and + * each key is contained in a key file + */ + for( i=0, rec=record; ifirst_foreign != -1 ) + rec->preamble = (rec->keys - (rec->first_foreign - rec->first_key)) + * sizeof(ulong); + + if( rec->fileid == -1 ) + printf("record '%s' is not contained in a file\n", rec->name); + + /* If the key is not a foreign key, it must be contained in a file */ + for( keyptr=&key[rec->first_key], j=0; jkeys; j++, keyptr++ ) + { + if( keyptr->fileid == -1 && KT_GETBASIC(keyptr->type) != KT_FOREIGN ) + { + printf("key '%s.%s' is not contained in a file\n", + rec->name, keyptr->name); + } + } + } +} + + + +/*------------------------------- fix_fields -------------------------------*\ + * + * Purpose : The parser sets the FT_KEY flags on all fields that is a key + * or part of a key. This flag should be removed for foreign keys. + * + * Parameters: None. + * + * Returns : Nothing. + * + */ +void fix_fields() +{ + Field *fld = field; + int n = fields; + + while( n-- ) + { + if( fld->type & FT_KEY ) + { + if( KT_GETBASIC(key[fld->keyid].type) == KT_FOREIGN ) + fld->type &= ~FT_KEY; + } + fld++; + } +} + + +/*---------------------------- print_fieldname -----------------------------*\ + * + * Purpose : Print a field name and its field id in the header file. If the + * field name is defined in more than one record it is prefixed + * with "_" to prevent name clashes. + * + * Parameters: file - File descriptor. + * fieldno - Field number. + * fieldid - Field id. + * + * Returns : Nothing. + * + */ +void print_fieldname(file, fieldno, fieldid) +FILE *file; +int fieldno; +Id fieldid; +{ + int i; + Field *fld = field + fieldno; + char *name = fld->name; + + if( only_keys && !(field[fieldno].type & FT_KEY) ) + return; + + fprintf(file, "#define "); + + for( i=0; i +#include +#include +#include +#include "environ.h" +#ifndef CONFIG_UNIX +# include +#endif + +#include "typhoon.h" +#include "ty_dbd.h" + +#include "ddlp.h" +#include "ddl.h" +#include "ddlpsym.h" +#include "ddlpglob.h" +#include "lex.h" +#include "lex.c" + +static CONFIG_CONST char rcsid[] = "$Id: ddlplex.c,v 1.6 1999/10/04 04:11:31 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ + +int yylex PRM ( (void); ) + +/*---------------------------- Global variables ----------------------------*/ +extern FILE *lex_file; /* input file */ +extern int lex_lineno; /* current line number */ + +LEX_KEYWORD lex_keywordtab[] = { + { T_ALTERNATE, "alternate", }, + { T_ASC, "asc", }, + { T_BY, "by", }, + { T_CASCADE, "cascade", }, + { T_CHAR, "char", }, + { T_CONTAINS, "contains", }, + { T_CONTROLLED, "controlled", }, + { T_DATA, "data", }, + { T_DATABASE, "database", }, + { T_DEFINE, "define", }, + { T_DELETE, "delete", }, + { T_DESC, "desc", }, + { T_DOUBLE, "double", }, + { T_FILE, "file", }, + { T_FLOAT, "float", }, + { T_FOREIGN, "foreign", }, + { T_INT, "int", }, + { T_KEY, "key", }, + { T_LONG, "long", }, + { T_MAP, "map", }, + { T_NULL, "null", }, + { T_ON, "on", }, + { T_PRIMARY, "primary", }, + { T_RECORD, "record", }, + { T_REFERENCES, "references", }, + { T_RESTRICT, "restrict", }, + { T_SEQUENCE, "sequence", }, + { T_SHORT, "short", }, + { T_SIGNED, "signed", }, + { T_STRUCT, "struct", }, + { T_UCHAR, "uchar", }, + { T_ULONG, "ulong", }, + { T_UNION, "union", }, + { T_UNIQUE, "unique", }, + { T_UNSIGNED, "unsigned", }, + { T_UPDATE, "update", }, + { T_USHORT, "ushort", }, + { T_VARIABLE, "variable" } +}; + +size_t lex_keywords = sizeof(lex_keywordtab) / sizeof(lex_keywordtab[0]); + +int yylex() +{ + int c; + + for( ;; ) + { + c = getc(lex_file); + + if( c == ' ' || c == '\t' ) /* skip whitespace */ + ; + else if( isalpha(c) ) /* keyword */ + return lex_parse_keyword(c); + else if( isdigit(c) ) /* number */ + return lex_parse_number(c); + else if( c == '"' ) /* string */ + return lex_parse_string(); + else if ( c== '\'' ) + return lex_parse_charconst(); /* character constant */ + else if( c == '\n' ) /* increase line count */ + lex_lineno++; + else if( c == EOF ) + return EOF; + else if( c == '/' ) + { + if( (c = getc(lex_file)) == '*' ) /* C comment */ + lex_skip_comment(); + else if( c == '/' ) /* C++ comment */ + { + while( getc(lex_file) != '\n' && !feof(lex_file) ) + ; + lex_lineno++; + } + else + { + ungetc(c, lex_file); + return '/'; + } + } + else if( c == '-' ) + { + if( (c=getc(lex_file)) == '>' ) + return T_ARROW; + ungetc(c, lex_file); + return '-'; + } + else if( strchr("[]{};,+*().", c) ) + return c; + else if( c == '#' ) + { + while( getc(lex_file) != '\n' && !feof(lex_file) ) + ; + lex_lineno++; + } + else + yyerror("syntax error"); + } +} + +/* e +nd-of-file */ diff --git a/util/ddlpsym.c b/util/ddlpsym.c new file mode 100644 index 0000000..240bfc7 --- /dev/null +++ b/util/ddlpsym.c @@ -0,0 +1,283 @@ +/*---------------------------------------------------------------------------- + * File : ddlpsym.c + * Program : ddlp + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Symbol table functions for ddlp. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "environ.h" +#include "ty_dbd.h" + +#include "ddlp.h" +#include "ddlpsym.h" +#include "ddlpglob.h" + +static CONFIG_CONST char rcsid[] = "$Id: ddlpsym.c,v 1.5 1999/10/03 23:36:49 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ +static void print_struct PRM( (sym_struct *, int); ) + +/*---------------------------- Global variables ----------------------------*/ +sym_struct *first_str = NULL; /* Pointer to start of struct table */ +sym_struct *last_str = NULL; /* Pointer to last struct */ +sym_struct *cur_str = NULL; /* Pointer to current struct */ +sym_struct *structnest[NEST_MAX]; /* Structure nesting table */ +int curnest = -1; /* Current structure nesting */ + + +/*------------------------------ sym_addmember -----------------------------*\ + * + * Purpose: This function adds a member to the structure currently + * being defined. The member is aligned according to . + * + * Params : name - struct member name. + * type - member type, see FT_.. constants. + * ... - If == FT_STRUCT the 3rd parameter is a + * pointer to a structdef record, otherwise it is + * the size of the field. + * + * Returns : Nothing + * + */ + +void sym_addmember(name, type, struc) +char *name; +int type; +sym_struct *struc; +{ + sym_struct *str = structnest[curnest]; + sym_member *mem; + int size; + + /* Make sure that this member name is not already used in this struct */ + for( mem = str->first_member; mem; mem = mem->next ) + if( !strcmp(name, mem->name) ) + { + yyerror("duplicate member name '%s'", name); + return; + } + + size = sizeof(*mem) + (dims-1) * sizeof(*mem->dim); + + if( !(mem = (sym_member *)calloc((size_t) 1, (size_t) size)) ) + err_quit("out of memory"); + + if( type == FT_STRUCT ) + { + mem->struc = struc; + mem->size = struc->size; + } + else + mem->size = typeinfo[(type & FT_BASIC)-1].size; + + strcpy(mem->name, name); + mem->type = type; + mem->dims = dims; + mem->elemsize = mem->size; + + /* If this member has arrays they must be stored and the size adjusted */ + if( dims ) + { + memcpy(mem->dim, dim, sizeof(*dim) * dims); + + while( --dims ) + dim[0] *= dim[dims]; + mem->size *= dim[0]; + } + + /* Add the new member to the member list */ + if( str->last_member ) + str->last_member->next = mem; + else + str->first_member = mem; + str->last_member = mem; + str->members++; + + /* + * If the structure is a union all members must be at offset 0, and + * the union must have the size of the biggest member. + */ + if( str->is_union ) + { + if( mem->size > str->size ) + str->size = mem->size; + } + else + { + align_offset(&str->size, type); + mem->offset = str->size; + str->size += mem->size; + } +} + + +void sym_addstruct(name, is_union) +char *name; +int is_union; +{ + sym_struct *str; + + /* Allocate new structdef and clear all fields */ + if( !(str = (sym_struct *)calloc(1, sizeof *str)) ) + err_quit("out of memory"); + + strcpy(str->name, name); + str->is_union = is_union; + + /* Insert structure in list */ + if( !first_str ) + first_str = str; + else + last_str->next = str; + cur_str = last_str = str; + + structnest[++curnest] = str; +} + + +void sym_endstruct() +{ + /* The size of a structure depends depends on the type of the last member + */ +/* align_offset(&structnest[curnest]->size, FT_LONG);*/ + align_offset(&structnest[curnest]->size, cur_str->last_member->type); + + cur_str = structnest[--curnest]; +} + + +sym_struct *sym_findstruct(name, is_union) +char *name; +int is_union; +{ + sym_struct *str = first_str; + + while( str ) + { + if( !strcmp(name, str->name) && str->is_union == is_union ) + return str; + + str = str->next; + } + + yyerror("unknown struct '%s'", name); + return NULL; +} + + +sym_member *sym_findmember(str, name) +sym_struct *str; +char *name; +{ + sym_member *mem = str->first_member; + + while( mem ) + { + if( !strcmp(name, mem->name) ) + return mem; + + mem = mem->next; + } + + return NULL; +} + + +static void print_struct(str, nesting) +sym_struct *str; +int nesting; +{ + static char *type[] = { "struct", "union" }; + sym_member *mem = str->first_member; + extern FILE *hfile; + int i; + + fprintf(hfile, "%*s%s %s { /* size %d */\n", nesting * 4, "", + type[str->is_union], + str->name, + str->size); + + while( mem ) + { + if( mem->type == FT_STRUCT ) + { + if( mem->struc->printed ) + { + fprintf(hfile, "%*s%s %s ", + (nesting+1)*4, "", + type[mem->struc->is_union], + mem->struc->name); + } + else + { + mem->struc->printed = 1; + print_struct(mem->struc, nesting+1); + fprintf(hfile, "%*s} ", (nesting+1)*4, ""); + } + } + else + { + fprintf(hfile, "%*s", (nesting+1) * 4, ""); + if( mem->type & FT_UNSIGNED ) + fprintf(hfile, "unsigned "); + fprintf(hfile, "%-8s", typeinfo[(mem->type & FT_BASIC) - 1].name); + } + + fprintf(hfile, "%s", mem->name); + + for( i=0; idims; i++ ) + fprintf(hfile, "[%d]", mem->dim[i]); + + fprintf(hfile, ";\n"); + mem = mem->next; + } + + if( nesting == 0 ) + fprintf(hfile, "};\n\n"); +} + + +void print_structures() +{ + sym_struct *str = first_str; + + while( str ) + { + if( !str->printed ) + print_struct(str, 0); + str = str->next; + } +} + + +/* end-of-file */ diff --git a/util/ddlpsym.h b/util/ddlpsym.h new file mode 100644 index 0000000..af3c2fc --- /dev/null +++ b/util/ddlpsym.h @@ -0,0 +1,111 @@ +/*---------------------------------------------------------------------------- + * File : ddlpsym.h + * Program : ddlp + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Symbol table definitions. + * + * $Id: ddlpsym.h,v 1.3 1999/10/04 05:29:45 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +/* The following constants define translation limits of the parser according to + * ISO/IEC DIS 9899 section 2.2.41. + */ + +#define NEST_MAX 15 /* Max nesting level */ +#define NAME_LEN 31 /* Number of significant initial*/ + /* characters in a name */ + +typedef struct sym_member { + char type; /* See FT_.. constants */ + char name[NAME_LEN+1]; /* Member name */ + unsigned elemsize; /* Size of each element */ + unsigned size; /* sizeof (total size incl. dim)*/ + unsigned offset; /* Offset from start of struct */ + struct sym_member *next; /* Next member */ + struct sym_struct *struc; /* If type is FT_STRUCT this */ + /* points to the structdef that */ + /* defines the struct type */ + /*---------- ddlp extensions -------------------------------------------*/ + Id id; /* Field id */ + /*----------------------------------------------------------------------*/ + unsigned dims; /* Number of dimensions */ + unsigned dim[1]; /* Dimensions if > 0 */ +} sym_member; + +typedef struct sym_struct { + char name[NAME_LEN+1]; /* Struct name */ + char printed; /* This field ensures that the */ + /* full structure definition is */ + /* printed once only. */ + unsigned char is_union; /* True if this is a union */ + unsigned size; /* sizeof */ + unsigned members; /* Number of members */ + sym_member *first_member; /* First member */ + sym_member *last_member; /* Last member */ + struct sym_struct *next; /* Next structdef */ + /*---------- ddlp extensions -------------------------------------------*/ + Id fieldid; /* Field id */ + /*----------------------------------------------------------------------*/ +} sym_struct; + + + +/* + +--------+ +--------+ +--------+ +--------+ + | struct |--->| member |--->| member |--->| member |--->NULL + +--------+ +--------+ +--------+ +--------+ + | + | + v + +--------+ +--------+ +--------+ +--------+ + | struct |--->| member |--->| member |--->| member |--->NULL + +--------+ +--------+ +--------+ +--------+ + | + | + v + +--------+ +--------+ + | struct |--->| member | + +--------+ +--------+ + | + | + v + NULL +*/ + +extern sym_struct *structnest[], *cur_str, *last_str; +extern int curnest; + +void sym_addmember PRM( (char *, int, sym_struct *); ) +void sym_addstruct PRM( (char *, int); ) +void sym_endstruct PRM( (void); ) +sym_struct *sym_findstruct PRM( (char *, int); ) +sym_member *sym_findmember PRM( (sym_struct *, char *); ) +void print_structures PRM( (void); ) + + +/* end-of-file */ diff --git a/util/exp.y b/util/exp.y new file mode 100644 index 0000000..0d2f89b --- /dev/null +++ b/util/exp.y @@ -0,0 +1,229 @@ +/*---------------------------------------------------------------------------- + * File : exp.y + * Program : tyexport + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Grammar for export specification. + * + * $Id: exp.y,v 1.7 1999/10/04 05:29:46 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +%{ + +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "export.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define NEST_MAX 15 + +/*--------------------------- Function prototypes --------------------------*/ +Record *GetRecord PRM( (char *); ) +Field *GetField PRM( (Structdef *, char *); ) +Structdef *GetStruct PRM( (Structdef *, char *); ) +int yylex PRM( (void); ) + +/*---------------------------- Global variables ----------------------------*/ +static Record *cur_rec = NULL; /* Current record */ +static Field *cur_fld = NULL; /* Current field */ +static Structdef *cur_str = NULL; /* Current structure */ +static Structdef *strnest[NEST_MAX]; /* Pointers to structures */ +static int cur_nest = -1; /* Current nesting */ + +%} + +%union { + char s[IDENT_LEN+1]; +} + +%start export_spec + +%token T_EXPORT T_RECORD T_STRUCT T_UNION T_IN +%token T_IDENT T_STRING +%token '{' '}' ';' + +%% + +export_spec : T_EXPORT T_IDENT '{' record_list '}' + ; + +record_list : record + | record_list record + ; + +record : record_head '{' field_list '}' + { + cur_nest--; + } + ; + +record_head : T_RECORD T_IDENT T_IN T_STRING + { + if( ( cur_rec = GetRecord($2) ) != 0 ) + { + cur_rec->aux = 1; + cur_str = &dbd.structdef[cur_rec->structid]; + } + else + cur_str = NULL; + strnest[++cur_nest] = cur_str; + } + ; + +field_list : field + | field_list field + ; + +field : T_IDENT ';' + { + if( cur_str ) + cur_fld = GetField(cur_str, $1); + } + + | struct_head '{' field_list '}' ';' + { + cur_str = strnest[--cur_nest]; + } + ; + +struct_head : struct_or_union T_IDENT + { + if( cur_str ) + cur_str = GetStruct(cur_str, $2); + strnest[++cur_nest] = cur_str; + } + ; + +struct_or_union : T_STRUCT + | T_UNION + ; + + +%% + + +#include + +extern int errors; + +int yyerror(char *fmt CONFIG_ELLIPSIS) +{ + va_list ap; + + printf("%s %d: ", spec_fname, lex_lineno); + va_start(ap, fmt); + vprintf(fmt, ap); + puts(""); + va_end(ap); + errors++; + return 0; +} + + + + +Record *GetRecord(name) +char *name; +{ + int i; + + for( i=0; ifirst_member]; + int n = str->members; + + while( n ) + { + if( fld->nesting == cur_nest ) + { + if( !strcmp(fld->name, name) ) + { + fld->type |= FT_INCLUDE; + return fld; + } + n--; + } + fld++; + } + + yyerror("'%s' is not a member of '%s'", name, str->name); + exit(1); + return NULL; +} + + +Structdef *GetStruct(str, name) +Structdef *str; +char *name; +{ + Field *fld; + Structdef *struc; + + if( !(fld = GetField(str, name)) || + FT_GETBASIC(fld->type) != FT_STRUCT ) + return NULL; + + struc = &dbd.structdef[fld->structid]; + + /* If the structure is a union the control field must also have been + * specified + */ + if( struc->is_union ) + { + if( !(dbd.field[struc->control_field].type & FT_INCLUDE) ) + { + yyerror("The control field of the union '%s' is not included", + name); + exit(1); + } + } + + return struc; +} + +/* end-of-file */ + diff --git a/util/export.c b/util/export.c new file mode 100644 index 0000000..23c8512 --- /dev/null +++ b/util/export.c @@ -0,0 +1,429 @@ +/*---------------------------------------------------------------------------- + * File : export.c + * Program : tyexport + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Typhoon export utility. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include "environ.h" +#ifndef CONFIG_UNIX +# include +# include +#else +# include +# include +#endif +#define DEFINE_GLOBALS +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" + +#include "export.h" + +static CONFIG_CONST char rcsid[] = "$Id: export.c,v 1.7 1999/10/04 04:11:31 kaz Exp $"; + +/*-------------------------------- prototypes ------------------------------*/ +static void PrintString PRM( (uchar *, Field *fld); ) +static void PrintField PRM( (Field *, unsigned); ) +static int GetControlField PRM( (Structdef *, unsigned); ) +static int PrintFields PRM( (Structdef *, int, unsigned, int); ) +static void Export PRM( (char *); ) +static void ExportTable PRM( (ulong); ) + int yyparse PRM( (void); ) + int main PRM( (int, char **); ) + +/*------------------------------ public variables --------------------------*/ +int dbdfile; +int nocomma=0; +int nonull=0; +char *recbuf; +FILE *outfile; +FILE *lex_file; + +/*------------------------------ local variables ---------------------------*/ +static char paramhelp[] = "\ +Syntax: tyexport [option]... database[.dbd]\n\ +Options:\n\ + -f Specify data files path\n\ + -g Generate export specification\n\ + -n Strings are not null-terminated\n"; + +#ifdef CONFIG_PROTOTYPES +void err_quit(char *s, ...) +#else +void err_quit(s) +char *s; +#endif +{ + va_list ap; + + va_start(ap, s); + vfprintf(stderr, s, ap); + puts(""); + va_end(ap); + exit(1); +} + +static void PrintString(s, fld) +uchar *s; +Field *fld; +{ + int len; + + if( fld->type & FT_VARIABLE ) + len = *(ushort *)(recbuf + dbd.field[ fld->keyid ].offset) * fld->elemsize; + else + len = fld->size; + + putc('"', outfile); + + while( len-- ) + { + if( isprint(*s) ) + { + if( *s == '"' ) + fputs("\\\"", outfile); + else + if( *s == '\\' ) + fputs("\\\\", outfile); + else + putc(*s, outfile); + } + else + { + if( !*s && !nonull ) + break; + + fprintf(outfile, "\\x%02X", *s); + } + s++; + } + putc('"', outfile); +} + + +static void PrintField(fld, offset) +Field *fld; +unsigned offset; +{ + void *ptr = (void *)(recbuf + offset); + + /* + Check for nonprintable characters in strings and character constants + */ + + if( nocomma ) + nocomma = 0; + else + fprintf(outfile, ", "); + + if( fld->type & FT_UNSIGNED ) + { + switch( FT_GETBASIC(fld->type) ) + { + case FT_CHARSTR: + PrintString(ptr, fld); + break; + case FT_CHAR: + fprintf(outfile, "%u", (unsigned)*(uchar *)ptr); + break; + case FT_SHORT: + fprintf(outfile, "%hu", *(ushort *)ptr); + break; + case FT_INT: + fprintf(outfile, "%u", *(unsigned *)ptr); + break; + case FT_LONG: + fprintf(outfile, "%lu", *(ulong *)ptr); + break; + } + } + else + switch( FT_GETBASIC(fld->type) ) + { + case FT_CHARSTR: + PrintString(ptr, fld); + break; + case FT_CHAR: + fprintf(outfile, "%d", (int)*(char *)ptr); + break; + case FT_SHORT: + fprintf(outfile, "%hd", *(short *)ptr); + break; + case FT_INT: + fprintf(outfile, "%d", *(int *)ptr); + break; + case FT_UNSIGNED: + fprintf(outfile, "%u", *(int *)ptr); + break; + case FT_LONG: + fprintf(outfile, "%ld", *(long *)ptr); + break; + case FT_FLOAT: + fprintf(outfile, "%f", *(float *)ptr); + break; + case FT_DOUBLE: + fprintf(outfile, "%f", *(double *)ptr); + break; + } +} + + +static int GetControlField(str, offset) +Structdef *str; +unsigned offset; +{ + unsigned id = recbuf[offset + dbd.field[str->control_field].offset]; + + if( id >= str->members ) + { + printf("Setting invalid control filed to 0\n"); + id = 0; + } + + return id; +} + + +static int PrintFields(str, nest, offset, control_value) +Structdef *str; +int nest, control_value; +unsigned offset; +{ + Field *fld = dbd.field + str->first_member; + int fields = str->members; + int old_fields = fields; + int i, n, rc; + + if( str->is_union ) + fld += control_value; + + while( fields-- ) + { + if( fld->size != fld->elemsize && FT_GETBASIC(fld->type) != FT_CHARSTR ) + { + if( fld->type & FT_VARIABLE ) + n = *(ushort *)(recbuf + dbd.field[ fld->keyid ].offset); + else + n = fld->size / fld->elemsize; + } + else + n = 1; + + for( i=0; itype) == FT_STRUCT ) + { + Structdef *struc = dbd.structdef + fld->structid; + + if( !nocomma ) + fprintf(outfile, ", "); + if( fld->type & FT_INCLUDE ) + fprintf(outfile, "{ "); + nocomma = 1; + + rc = PrintFields(struc, nest+1, + offset + fld->offset + i * fld->elemsize, + struc->is_union ? GetControlField(struc, offset) : 0); + + if( fld->type & FT_INCLUDE ) + fprintf(outfile, " }"); + } + else if( fld->nesting == nest && fld->type & FT_INCLUDE ) + PrintField(fld, offset + fld->offset + fld->elemsize * i); + } + + /* If n was 0 this array was a variable length array of size 0. + * Move fld to the next field at the same nesting. + */ + if( n == 0 ) + { + fprintf(outfile, ", { }"); + rc = 0; + + if( !fields ) + break; + + do + rc++; + while( fld[rc].nesting != fld->nesting ); + rc--; + } + + + if( FT_GETBASIC(fld->type) == FT_STRUCT ) + { + old_fields += rc; + fld += rc; + } + + fld++; + + if( str->is_union ) + break; + } + + return old_fields; +} + + +static void ExportTable(recid) +ulong recid; +{ + Record *rec = &dbd.record[recid]; + Id keyid; + + recid = INTERN_TO_RECID(recid); + keyid = rec->first_key; + + for( d_keyfrst(keyid); db_status == S_OKAY; d_keynext(keyid) ) + { + d_recread(recbuf); + + nocomma = 1; + PrintFields(&dbd.structdef[rec->structid], 0, 0, 0); + fprintf(outfile, "\n"); + } +} + + +static void Export(dbname) +char *dbname; +{ + char export_fname[256]; + int i; + + if( d_open(dbname, "s") != S_OKAY ) + err_quit("Cannot open database '%s'", dbname); + + for( i=0; i +#include +#include +#include +#include + +#include "environ.h" +#include "ty_dbd.h" + +#include "ddlp.h" +#include "exp.h" +#include "ddlpsym.h" +#include "ddlpglob.h" +#include "lex.h" +#include "lex.c" + +static CONFIG_CONST char rcsid[] = "$Id: exportlx.c,v 1.6 1999/10/04 04:11:31 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ + +int yylex PRM( (void); ) + +/*---------------------------- Global variables ----------------------------*/ +FILE *lex_file; /* input file */ + + +LEX_KEYWORD lex_keywordtab[] = { + { T_EXPORT, "export", }, + { T_IN, "in", }, + { T_RECORD, "record", }, + { T_STRUCT, "struct", }, + { T_UNION, "union" } +}; + +size_t lex_keywords = sizeof(lex_keywordtab) / sizeof(lex_keywordtab[0]); + + + +int yylex() +{ + int c; + + for( ;; ) + { + c = getc(lex_file); + + if( c == ' ' || c == '\t' ) /* skip whitespace */ + ; + else if( isalpha(c) ) /* keyword */ + return lex_parse_keyword(c); +#if 0 + else if( isdigit(c) ) /* number */ + return lex_parse_number(c); +#endif + else if( c == '"' ) /* string */ + return lex_parse_string(); + else if( c == '\n' ) /* increase line count */ + lex_lineno++; + else if( c == EOF ) + return EOF; + else if( strchr("[]{};,+*-().", c) ) + return c; + else if( c == '/' ) + { + if( (c = getc(lex_file)) == '*' )/* C comment */ + lex_skip_comment(); + else if( c == '/' ) /* C++ comment */ + { + while( getc(lex_file) != '\n' && !feof(lex_file) ) + ; + lex_lineno++; + } + else + { + ungetc(c, lex_file); + return '/'; + } + } + else + yyerror("unexpected character '%s'", c); + } +} + +/* end-of-file */ + diff --git a/util/expspec.c b/util/expspec.c new file mode 100644 index 0000000..609686a --- /dev/null +++ b/util/expspec.c @@ -0,0 +1,170 @@ +/*---------------------------------------------------------------------------- + * File : expspec.c + * Program : tyexport + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Functions for reading and generating the specification. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" + +#include "export.h" + +static char CONFIG_CONST rcsid[] = "$Id: expspec.c,v 1.5 1999/10/04 00:03:23 kaz Exp $"; + + +/*-------------------------- Function prototypes ---------------------------*/ +static void Indent PRM( (int); ) +static int PrintFields PRM( (Structdef *, int); ) + + +/*---------------------------- Global variables ----------------------------*/ +static FILE *outfile; +extern FILE *lex_file; + + + +/*----------------------------- ReadExportSpec -----------------------------*\ + * + * Purpose : This function reads an export specification. + * + * Parameters: exportspec_fname - Output file name. + * + * Returns : Nothing. + * + */ + +extern int yyparse PRM ( (void); ) + +void ReadExportSpec(dbname) +char *dbname; +{ + char exportspec_fname[256]; + + sprintf(exportspec_fname, "%s.exp", dbname); + + if( !(lex_file = fopen(exportspec_fname, "r")) ) + err_quit("Cannot open '%s'", exportspec_fname); + + yyparse(); + + fclose(lex_file); +} + + +static void Indent(level) +int level; +{ + fprintf(outfile, "%*s", (level+2) * 4, ""); +} + + +static int PrintFields(str, nest) +Structdef *str; +int nest; +{ + Field *fld = dbd.field + str->first_member; + int fields = str->members; + int old_fields = fields; + int rc; + + while( fields-- ) + { + if( FT_GETBASIC(fld->type) == FT_STRUCT ) + { + Structdef *struc = dbd.structdef + fld->structid; + + Indent(nest); + fprintf(outfile, "%s %s {\n", struc->is_union ? "union" : "struct", fld->name); + rc = PrintFields(struc, nest+1); + Indent(nest); + fprintf(outfile, "};\n"); + old_fields += rc; + fld += rc; + } + else if( fld->nesting == nest ) + { + Indent(nest); + fprintf(outfile, "%s;\n", fld->name); + } + + fld++; + } + + return old_fields; +} + + + + +/*--------------------------- GenerateExportSpec ---------------------------*\ + * + * Purpose : This function automatically generates a full export + * specification. + * + * Parameters: exprotspec_fname - Output file name. + * + * Returns : Nothing. + * + */ +void GenerateExportSpec(dbname) +char *dbname; +{ + char exportspec_fname[256]; + int i; + Record *rec = dbd.record; + + printf("Generating export specification..."); + fflush(stdout); + + sprintf(exportspec_fname, "%s.exp", dbname); + + if( !(outfile = fopen(exportspec_fname, "w")) ) + err_quit("Cannot write to '%s'", exportspec_fname); + + fprintf(outfile, "export %s {\n\n", dbname); + + for( i=0; iname, rec->name); + + PrintFields(&dbd.structdef[rec->structid], 0); + + fprintf(outfile, " }\n\n"); + } + + fprintf(outfile, "}\n"); + fclose(outfile); + + puts("done"); +} + +/* end-of-file */ diff --git a/util/fixlog.c b/util/fixlog.c new file mode 100644 index 0000000..865c5c2 --- /dev/null +++ b/util/fixlog.c @@ -0,0 +1,322 @@ +/*---------------------------------------------------------------------------- + * File : fixlog.c + * Program : tyrestore + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains functions used to applying the log to the database after a + * restore. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: fixlog.c,v 1.2 1999/10/03 23:36:49 kaz Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" +#include "ty_log.h" + + + +/*------------------------------- Constants --------------------------------*/ +#define LOGBUF_SIZE (64 * 1024) +#define RECHEAD_SIZE offsetof(RECORDHEAD, data[0]) +#define FILES_MAX 512 + + +/*-------------------------- Function prototypes ---------------------------*/ +static void *ReadLog PRM( (ulong); ) +static void insert_in_lru PRM( (ulong); ) +static void delete_from_lru PRM( (ulong); ) +static void update_in_lru PRM( (ulong); ) + +/*---------------------------- Global variables ----------------------------*/ +static char *logbuf; /* Log buffer */ +static ulong logbytes = 0; /* Number of bytes in buffer */ +static ulong logreadpos = 0; /* Read position in log buffer */ +static int log_fh; /* Log file handle */ +static Dbentry dbd; /* DBD-anchor */ +extern jmp_buf err_jmpbuf; +extern int verbose; +extern char *datapath; + +static struct file_t { + int fh; + unsigned recsize; + unsigned blocksize; + unsigned preamble; + int prev_in_lru; + int next_in_lru; +} file[FILES_MAX]; +static int mru_file = -1; +static int lru_file = -1; +static int r_cur_open = 0; +static int r_max_open = 40; + + +/*--------------------------------------------------------------------------*\ + * + * Function : ReadLog + * + * Purpose : Returns a pointer to the next x bytes in the log. + * + * Parameters: bytes - Number of bytes requested. + * + * Returns : Nothing. + * + */ +static void *ReadLog(bytes) +ulong bytes; +{ + ulong numread; + char *p; + + if( bytes > logbytes - logreadpos ) + { + memmove(logbuf, logbuf+logreadpos, logbytes - logreadpos); + logbytes -= logreadpos; + logreadpos = 0; + + numread = read(log_fh, logbuf + logbytes, LOGBUF_SIZE - logbytes); + + if( numread <= 0 ) + return NULL; + + logbytes += numread; + } + + p = logbuf + logreadpos; + logreadpos += bytes; + + return p; +} + + +static void insert_in_lru(id) +ulong id; +{ + struct file_t *f = file + id; + + f->prev_in_lru = -1; + + if( mru_file != -1 ) + { + f->next_in_lru = mru_file; + file[mru_file].prev_in_lru = id; + } + else + { + lru_file = id; + f->prev_in_lru = -1; + } + + mru_file = id; +} + +static void delete_from_lru(id) +ulong id; +{ + struct file_t *f = file + id; + + if( f->next_in_lru != -1 ) + file[f->next_in_lru].prev_in_lru = f->prev_in_lru; + + if( f->prev_in_lru != -1 ) + file[f->prev_in_lru].next_in_lru = f->next_in_lru; + + if( mru_file == id ) + mru_file = f->next_in_lru; + + if( lru_file == id ) + lru_file = f->prev_in_lru; +} + + +static void update_in_lru(id) +ulong id; +{ + delete_from_lru(id); + insert_in_lru(id); +} + + + +static void CheckFile(fileid) +ulong fileid; +{ + char fname[128]; + + if( file[fileid].fh == -1 ) + { + if( r_cur_open == r_max_open ) + { + /* Close least recently used file */ + close(file[lru_file].fh); + file[lru_file].fh = -1; + + delete_from_lru(lru_file); + r_cur_open--; + } + + sprintf(fname, "%s/%s", datapath, dbd.file[fileid].name); + if( (file[fileid].fh = os_open(fname, O_RDWR, 0)) == -1 ) + { + printf("Cannot open '%s' (errno %d)\n", dbd.file[fileid].name, errno); + longjmp(err_jmpbuf, 1); + } + + insert_in_lru(fileid); + r_cur_open++; + } +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : FixLog + * + * Purpose : Copies all the updates from the log to the database tables. + * + * Parameters: dbname - Database name. + * + * Returns : Nothing. + * + */ +void FixLog(dbname) +char *dbname; +{ + ushort *logblock; + char dbdname[DBNAME_LEN+5]; + unsigned i, fileid; + RECORDHEAD recordhd = { 0 }; + char fname[128]; + + /* Open log file */ + if( (log_fh = open(LOG_FNAME, O_RDONLY)) == -1 ) + { + if( verbose ) + puts("No log"); + return; + } + + if( verbose ) + printf("Fixing log"); + + /* Read dbd-file */ + sprintf(dbdname, "%s.dbd", dbname); + if( read_dbdfile(&dbd, dbdname) != S_OKAY ) + { + printf("Cannot open '%s'\n", dbdname); + longjmp(err_jmpbuf, 1); + } + + /* Allocate log buffer */ + if( !(logbuf = (char *)malloc(LOGBUF_SIZE)) ) + { + puts("out of memory"); + longjmp(err_jmpbuf, 1); + } + + for( i=0; ifirst_foreign == -1 ) + foreign_keys = 0; + else + foreign_keys = rec->keys - (rec->first_foreign - rec->first_key); + preamble= sizeof(long) * foreign_keys; + + fileid = dbd.record[i].fileid; + file[fileid].fh = -1; + file[fileid].next_in_lru = -1; + file[fileid].recsize = dbd.record[i].size; + file[fileid].blocksize = dbd.record[i].size + RECHEAD_SIZE + + preamble; + file[fileid].preamble = preamble; + } + + while( logblock = (ushort *)ReadLog(sizeof *logblock) ) + { + switch( *logblock ) + { + case LOG_UPDATE: { + LogUpdate *update = (void *)logblock; + + /* Skip rest of LogUpdate structure and record */ + ReadLog(update->len - sizeof(update->id)); + + fileid = dbd.record[update->recid].fileid; + CheckFile(fileid); + + recordhd.flags = 0; + + lseek(file[fileid].fh, update->recno * file[fileid].blocksize, SEEK_SET); + write(file[fileid].fh, &recordhd, RECHEAD_SIZE); + + lseek(file[fileid].fh, file[fileid].preamble, SEEK_CUR); + write(file[fileid].fh, update+1, file[fileid].recsize); + } + break; + + case LOG_DELETE: { + LogDelete *delete = (void *)logblock; + + /* Skip rest of LogDelete structure */ + ReadLog(delete->len - sizeof(delete->id)); + + fileid = dbd.record[delete->recid].fileid; + CheckFile(fileid); + + recordhd.flags = BIT_DELETED; + + lseek(file[fileid].fh, delete->recno * file[fileid].blocksize, SEEK_SET); + write(file[fileid].fh, &recordhd, RECHEAD_SIZE); + } + break; + } + } + + close(log_fh); + free(logbuf); + free(dbd.dbd); + + if( verbose ) + puts(""); +} + +/* end-of-file */ diff --git a/util/imp.y b/util/imp.y new file mode 100644 index 0000000..24a5b83 --- /dev/null +++ b/util/imp.y @@ -0,0 +1,235 @@ +/*---------------------------------------------------------------------------- + * File : imp.y + * Program : tyimport + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Grammar for import specification. + * + * $Id: imp.y,v 1.6 1999/10/04 05:29:46 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +%{ + +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" + +#include "import.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define NEST_MAX 15 + +/*--------------------------- Function prototypes --------------------------*/ +Record *GetRecord PRM( (char *); ) +Field *GetField PRM( (Structdef *, char *); ) +Structdef *GetStruct PRM( (Structdef *, char *); ) +int yylex PRM( (void); ) + +/*---------------------------- Global variables ----------------------------*/ +static Record *cur_rec = NULL; /* Current record */ +static Field *cur_fld = NULL; /* Current field */ +static Structdef *cur_str = NULL; /* Current structure */ +static Structdef *strnest[NEST_MAX]; /* Pointers to structures */ +static int cur_nest = -1; /* Current nesting */ + +%} + +%union { + char s[IDENT_LEN+1]; +} + +%start import_spec + +%token T_IMPORT T_RECORD T_STRUCT T_UNION T_IN +%token T_IDENT T_STRING +%token '{' '}' ';' + +%% + +import_spec : T_IMPORT T_IDENT '{' record_list '}' + ; + +record_list : record + | record_list record + ; + +record : record_head '{' field_list '}' + { + cur_nest--; + } + ; + +record_head : T_RECORD T_IDENT T_IN T_STRING + { + if( ( cur_rec = GetRecord($2) ) != 0 ) + { + cur_rec->aux = 1; + cur_str = &dbd.structdef[cur_rec->structid]; + } + else + cur_str = NULL; + strnest[++cur_nest] = cur_str; + } + ; + +field_list : field + | field_list field + ; + +field : T_IDENT ';' + { + if( cur_str ) + cur_fld = GetField(cur_str, $1); + } + + | struct_head '{' field_list '}' ';' + { + cur_str = strnest[--cur_nest]; + } + ; + +struct_head : struct_or_union T_IDENT + { + if( cur_str ) + cur_str = GetStruct(cur_str, $2); + strnest[++cur_nest] = cur_str; + } + ; + +struct_or_union : T_STRUCT + | T_UNION + ; + + +%% + + +#include + +extern int errors; + +#ifdef CONFIG_PROTOTYPES +int yyerror(char *fmt, ...) +#else +int yyerror(fmt) +char *fmt; +#endif +{ + va_list ap; + + printf("%s %d: ", spec_fname, lex_lineno); + va_start(ap, fmt); + vprintf(fmt, ap); + puts(""); + va_end(ap); + errors++; + return 0; +} + + + + +Record *GetRecord(name) +char *name; +{ + int i; + + for( i=0; ifirst_member]; + int n = str->members; + + while( n ) + { + if( fld->nesting == cur_nest ) + { + if( !strcmp(fld->name, name) ) + { + fld->type |= FT_INCLUDE; + return fld; + } + n--; + } + fld++; + } + + yyerror("'%s' is not a member of '%s'", name, str->name); + exit(1); + return NULL; +} + + +Structdef *GetStruct(str, name) +Structdef *str; +char *name; +{ + Field *fld; + Structdef *struc; + + if( !(fld = GetField(str, name)) || + FT_GETBASIC(fld->type) != FT_STRUCT ) + return NULL; + + struc = &dbd.structdef[fld->structid]; + + /* If the structure is a union the control field must also have been + * specified + */ + if( struc->is_union ) + { + if( !(dbd.field[struc->control_field].type & FT_INCLUDE) ) + { + yyerror("The control field of the union '%s' is not included", + name); + exit(1); + } + } + + return struc; +} + +/* end-of-file */ + diff --git a/util/import.c b/util/import.c new file mode 100644 index 0000000..f8758d2 --- /dev/null +++ b/util/import.c @@ -0,0 +1,591 @@ +/*---------------------------------------------------------------------------- + * File : import.c + * Program : tyimport + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Typhoon import utility. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include "environ.h" +#ifndef CONFIG_UNIX +# include +# include +#else +# include +# include +#endif +#define DEFINE_GLOBALS +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_prot.h" + +#include "lex.h" +#include "import.h" + +static CONFIG_CONST char rcsid[] = "$Id: import.c,v 1.7 1999/10/04 04:11:32 kaz Exp $"; + +/*-------------------------------- prototypes ------------------------------*/ +static int ReadValue PRM( (int); ) +static int ReadString PRM( (void); ) +static int ReadChar PRM( (void); ) +static int ReadField PRM( (Field *, unsigned); ) +static int GetControlField PRM( (Structdef *, unsigned); ) +static int ReadFields PRM( (Structdef *, int, unsigned, int); ) +static void Import PRM( (char *); ) +static void ImportTable PRM( (ulong); ) + int yyparse PRM( (void); ) +static void import_error PRM( (char * CONFIG_ELLIPSIS); ) + int main PRM( (int, char **); ) + +/*------------------------------ public variables --------------------------*/ + FILE *lex_file; +static FILE *infile; +static char *recbuf; +static char *fldptr; +static int fldtype; +static int lineno; +static char import_fname[256]; + +/*------------------------------ local variables ---------------------------*/ +static char paramhelp[] = "\ +Syntax: tyimport [option]... database[.dbd]\n\ +Options:\n\ + -f Specify data files path\n\ + -g Generate import specification\n"; + +#ifdef CONFIG_PROTOTYPES +void err_quit(char *s, ...) +#else +void err_quit(s) +char *s; +#endif +{ + va_list ap; + + va_start(ap, s); + vfprintf(stderr, s, ap); + puts(""); + va_end(ap); + exit(1); +} + + +#ifdef CONFIG_PROTOTYPES +void import_error(char *fmt, ...) +#else +void import_error(fmt) +char *fmt; +#endif +{ + va_list ap; + + printf("%s %d: ", import_fname, lineno); + va_start(ap, fmt); + vprintf(fmt, ap); + puts(""); + va_end(ap); + errors++; +} + + + +/* Floats not supported!!! */ + +static int ReadValue(c) +int c; +{ + /* Integer formats: 002 (octal) 0x29 (hex) 231 (decimal) */ + ulong value; + int negate = 0; + + if( c == '-' ) + { + negate = 1; + c = getc(infile); + } + + if( c == '0' ) + { + value = 0; + + c = getc(infile); + + if( c == 'x' ) + { + while( (c = getc(infile)) && isxdigit(c) ) + { + if( isdigit(c) ) + value = value * 16 + c - '0'; + else + value = value * 16 + 6 + tolower(c) - 'a'; + } + ungetc(c, infile); + } + else if( isdigit(c) ) + { + do + { + value = value * 8 + c - '0'; + c = getc(infile); + } + while( isdigit(c) ); + ungetc(c, infile); + } + else + ungetc(c, infile); + } + else + { + value = c - '0'; + + c = getc(infile); + + while( isdigit(c) ) + { + value = value * 10 + c - '0'; + c = getc(infile); + } + +/* + do + { + value = value * 10 + c - '0'; + c = getc(infile); + } + while( isdigit(c) );*/ + ungetc(c, infile); + } + + if( fldtype & FT_UNSIGNED ) + { + switch( fldtype ) + { + case FT_UNSIGNED|FT_CHAR: *(uchar *)fldptr = value; break; + case FT_UNSIGNED|FT_SHORT: *(ushort *)fldptr = value; break; + case FT_UNSIGNED|FT_INT: *(unsigned *)fldptr = value; break; + case FT_UNSIGNED|FT_LONG: *(ulong *)fldptr = value; break; + } + } + else + { + long svalue = negate ? -value : value; + + switch( fldtype ) + { + case FT_CHAR: *(char *)fldptr = svalue; break; + case FT_SHORT: *(short *)fldptr = svalue; break; + case FT_INT: *(int *)fldptr = svalue; break; + case FT_LONG: *(long *)fldptr = svalue; break; + case FT_FLOAT: + case FT_DOUBLE: puts("floats not supported"); exit(1); + } + } + + return 0; +} + + + +/* check for max length */ + +static int ReadString() +{ + char *p = fldptr; + int c; + + while( (*p = getc(infile)) != '"' ) + { + if( *p == '\\' ) + { + switch( c = getc(infile) ) + { + case 'n': /* Newline */ + *p = '\n'; + break; + case '\\': /* Backslash */ + *p = '\\'; + break; + case '\"': /* Double-quote */ + *p = '"'; + break; + case 'x': /* Hexadecimal number */ + case 'X': + c = getc(infile); + if( isxdigit(c) ) + { + *p = isalpha(c) ? tolower(c) - 'a' + 10 : c - '0'; + c = getc(infile); + if( isxdigit(c) ) + { + *p <<= 4; + *p += isalpha(c) ? tolower(c) - 'a' + 10 : c - '0'; + } + else + ungetc(c, infile); + } +/* *p = 0; + c = getc(infile); + + while( isxdigit(c) ) + { + if( isdigit(c) ) + *p = *p * 16 + c - '0'; + else + *p = *p * 16 + 10 + tolower(c) - 'a'; + c = getc(infile); + } + + ungetc(c, infile);*/ + break; + default: + import_error("illegal character '%c' following '\\'", c); + break; + } + } + + p++; + } + *p = 0; + + return 0; +} + + +static int ReadChar() +{ + int c = getc(infile); + int value, tmp; + + if( c == '\\' ) + { + switch( tmp = getc(infile) ) + { + case 'n': value = '\n'; break; + case 'r': value = '\r'; break; + case 't': value = '\t'; break; + case '\"': + case '\\': + case '\'': value = tmp; break; + case 'x': + c = getc(infile); + value = 0; + + while( isxdigit(c) ) + { + if( isdigit(c) ) + value = value * 16 + c - '0'; + else + value = value * 16 + 10 + tolower(c) - 'a'; + c = getc(infile); + } + + ungetc(c, infile); + break; + default: + import_error("invalid character constant"); + return -1; + } + } + else + value = c; + + if( getc(infile) != '\'' ) + import_error("unterminated character constant"); + + switch( FT_GETBASIC(fldtype) ) + { + case FT_CHAR: *(char *)fldptr = value; break; + case FT_INT: *(int *)fldptr = value; break; + case FT_SHORT: *(short *)fldptr = value; break; + case FT_LONG: *(long *)fldptr = value; break; + } + + return 0; +} + + +static int ReadField(fld, offset) +Field *fld; +unsigned offset; +{ + int c; + + fldptr = recbuf + offset; + fldtype = FT_GETBASIC(fld->type); + + for( ;; ) + { + c = getc(infile); + + if( c == ' ' || c == '\t' || c == '{' || c == '}' || c == ',' ) + ; + else if( isdigit(c) || c == '-' ) + return ReadValue(c); + else if( c == '"' ) + return ReadString(); + else if( c == '\'' ) + return ReadChar(); + else if( c == '\n' ) + lineno++; + else if( c == '/' ) + { + if( (c = getc(infile)) == '*' ) /* C comment */ + lex_skip_comment(); + else if( c == '/' ) /* C++ comment */ + { + while( getc(infile) != '\n' && !feof(infile) ) + ; + lineno++; + } + else + import_error("unexpected '/'\n"); + } + else if( c == EOF ) + return -1; + else + import_error("unexpected '%c'\n", c); + } +} + + + +static int GetControlField(str, offset) +Structdef *str; +unsigned offset; +{ + return recbuf[offset + dbd.field[str->control_field].offset]; +} + + + +static int ReadFields(str, nest, offset, control_value) +Structdef *str; +int nest, control_value; +unsigned offset; +{ + Field *fld = dbd.field + str->first_member; + int fields = str->members; + int old_fields = fields; + int i, n, rc; + + if( str->is_union ) + fld += control_value; + + while( fields-- ) + { + if( fld->size != fld->elemsize && FT_GETBASIC(fld->type) != FT_CHARSTR ) + { + if( fld->type & FT_VARIABLE ) + n = *(ushort *)(recbuf + dbd.field[ fld->keyid ].offset); + else + n = fld->size / fld->elemsize; + } + else + n = 1; + + for( i=0; itype) == FT_STRUCT ) + { + Structdef *struc = dbd.structdef + fld->structid; + + rc = ReadFields(struc, nest+1, + offset + fld->offset + i * fld->elemsize, + struc->is_union ? GetControlField(struc, offset) : 0); + } + else if( fld->nesting == nest && fld->type & FT_INCLUDE ) + { + if( ReadField(fld, offset + fld->offset + fld->elemsize * i) == -1 ) + return -1; + } + } + + /* If n was 0 this array was a variable length array of size 0. + * Move fld to the next field at the same nesting. + */ + if( n == 0 ) + { + rc = 0; + + if( !fields ) + break; + + do + rc++; + while( fld[rc].nesting != fld->nesting ); + rc--; + } + + if( FT_GETBASIC(fld->type) == FT_STRUCT ) + { + old_fields += rc; + fld += rc; + } + + fld++; + + if( str->is_union ) + break; + } + + return old_fields; +} + + +static void ImportTable(recid) +ulong recid; +{ + Record *rec = &dbd.record[recid]; + + recid = INTERN_TO_RECID(recid); + + memset(recbuf, 0, rec->size); + + while( ReadFields(&dbd.structdef[rec->structid], 0, 0, 0) != -1 ) + { + if( d_fillnew(recid, recbuf) != S_OKAY ) + printf("d_fillnew: db_status %d, db_subcode %ld\n", + db_status, db_subcode); + memset(recbuf, 0, rec->size); + } +} + + +static void Import(dbname) +char *dbname; +{ + int i; + + if( d_open(dbname, "s") != S_OKAY ) + err_quit("Cannot open database '%s'", dbname); + + for( i=0; i +#include +#include +#include +#include + +#include "environ.h" +#include "ty_dbd.h" + +#include "ddlp.h" +#include "imp.h" +#include "ddlpsym.h" +#include "ddlpglob.h" +#include "lex.h" +#include "lex.c" + +#if YYDEBUG +#define D(x) x +#else +#define D(x) +#endif + + +static CONFIG_CONST char rcsid[] = "$Id: importlx.c,v 1.6 1999/10/04 04:11:32 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ + +int yylex PRM ( (void); ) + +/*---------------------------- Global variables ----------------------------*/ +FILE *lex_file; /* input file */ + + +LEX_KEYWORD lex_keywordtab[] = { + { T_IMPORT, "import", }, + { T_IN, "in", }, + { T_RECORD, "record", }, + { T_STRUCT, "struct", }, + { T_UNION, "union" } +}; + +size_t lex_keywords = sizeof(lex_keywordtab) / sizeof(lex_keywordtab[0]); + + + +int yylex() +{ + int c; + + for( ;; ) + { + c = getc(lex_file); + + if( c == ' ' || c == '\t' ) /* skip whitespace */ + ; + else if( isalpha(c) ) /* keyword */ + return lex_parse_keyword(c); +#if 0 + else if( isdigit(c) ) /* number */ + return lex_parse_number(c); +#endif + else if( c == '"' ) /* string */ + return lex_parse_string(); + else if( c == '\n' ) /* increase line count */ + lex_lineno++; + else if( c == EOF ) + return EOF; + else if( strchr("[]{};,+*-().", c) ) + return c; + else if( c == '/' ) + { + if( (c = getc(lex_file)) == '*' )/* C comment */ + lex_skip_comment(); + else if( c == '/' ) /* C++ comment */ + { + while( getc(lex_file) != '\n' && !feof(lex_file) ) + ; + lex_lineno++; + } + else + { + ungetc(c, lex_file); + return '/'; + } + } + else + yyerror("syntax error"); + } +} + +/* end-of-file */ + diff --git a/util/impspec.c b/util/impspec.c new file mode 100644 index 0000000..d88d0b7 --- /dev/null +++ b/util/impspec.c @@ -0,0 +1,169 @@ +/*---------------------------------------------------------------------------- + * File : impspec.c + * Program : tyimport + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Functions for reading and generating the specification. + * + *--------------------------------------------------------------------------*/ + +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" + +#include "import.h" + +static CONFIG_CONST char rcsid[] = "$Id: impspec.c,v 1.5 1999/10/04 01:47:41 kaz Exp $"; + +/*-------------------------- Function prototypes ---------------------------*/ +static void Indent PRM( (int); ) +static int PrintFields PRM( (Structdef *, int); ) + + +/*---------------------------- Global variables ----------------------------*/ +static FILE *outfile; +extern FILE *lex_file; + +/*----------------------------- ReadImportSpec -----------------------------*\ + * + * Purpose : This function reads an import specification. + * + * Parameters: importspec_fname - Output file name. + * + * Returns : Nothing. + * + */ + +int yyparse PRM ( (void); ) + +void ReadImportSpec(dbname) +char *dbname; +{ + char importspec_fname[256]; + + sprintf(importspec_fname, "%s.imp", dbname); + + if( !(lex_file = fopen(importspec_fname, "r")) ) + err_quit("Cannot open '%s'", importspec_fname); + + yyparse(); + + fclose(lex_file); +} + + + +static void Indent(level) +int level; +{ + fprintf(outfile, "%*s", (level+2) * 4, ""); +} + + +static int PrintFields(str, nest) +Structdef *str; +int nest; +{ + Field *fld = dbd.field + str->first_member; + int fields = str->members; + int old_fields = fields; + int rc; + + while( fields-- ) + { + if( FT_GETBASIC(fld->type) == FT_STRUCT ) + { + Structdef *struc = dbd.structdef + fld->structid; + + Indent(nest); + fprintf(outfile, "%s %s {\n", + struc->is_union ? "union" : "struct", fld->name); + rc = PrintFields(struc, nest+1); + Indent(nest); + fprintf(outfile, "};\n"); + old_fields += rc; + fld += rc; + } + else if( fld->nesting == nest ) + { + Indent(nest); + fprintf(outfile, "%s;\n", fld->name); + } + + fld++; + } + + return old_fields; +} + + + + +/*--------------------------- GenerateImportSpec ---------------------------*\ + * + * Purpose : This function automatically generates a full import + * specification. + * + * Parameters: improtspec_fname - Output file name. + * + * Returns : Nothing. + * + */ +void GenerateImportSpec(dbname) +char *dbname; +{ + char importspec_fname[256]; + int i; + Record *rec = dbd.record; + + printf("Generating import specification..."); + fflush(stdout); + + sprintf(importspec_fname, "%s.imp", dbname); + + if( !(outfile = fopen(importspec_fname, "w")) ) + err_quit("Cannot write to '%s'", importspec_fname); + + fprintf(outfile, "import %s {\n\n", dbname); + + for( i=0; iname, rec->name); + + PrintFields(&dbd.structdef[rec->structid], 0); + + fprintf(outfile, " }\n\n"); + } + + fprintf(outfile, "}\n"); + fclose(outfile); + + puts("done"); +} + +/* end-of-file */ diff --git a/util/lex.c b/util/lex.c new file mode 100644 index 0000000..70df747 --- /dev/null +++ b/util/lex.c @@ -0,0 +1,172 @@ +/*---------------------------------------------------------------------------- + * File : @(#)lex.c 93/11/08 Copyright (c) 1993-94 CT Danmark + * Compiler: UNIX C, Turbo C, Microsoft C + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Description: + * General functions for lexical analysers. + * + * Revision History + * ---------------------------------------- + * 11-Nov-1993 tbp Initial version. + * + *--------------------------------------------------------------------------*/ + +/*-------------------------- Function prototypes ---------------------------*/ + +#include + +static int keywordcmp PRM( (char *, char *); ) + + + +void lex_skip_comment() +{ + int c, start; + + start = lex_lineno; + for( ;; ) + { + switch( getc(lex_file) ) + { + case '*': + if( (c=getc(lex_file)) == '/' ) + return; + else + ungetc(c, lex_file); + break; + case '\n': + lex_lineno++; + break; + case '/': + if( (c=getc(lex_file)) == '*' ) /* nested comment */ + lex_skip_comment(); + else + ungetc(c, lex_file); + break; + case EOF: + fprintf(stderr, "unterminated comment starting in line %d\n", start); + exit(1); + } + } +} + + +static int keywordcmp(ck,ce) +char *ck; +char *ce; +{ + return strcmp(ck, ((LEX_KEYWORD *)ce)->s); +} + + + +int lex_parse_keyword(c) +int c; +{ + char *p = yylval.s; + LEX_KEYWORD *kword; + + *p = c; + + do + *++p = getc(lex_file); + while( isalnum(*p) || *p=='_' ); + + ungetc(*p, lex_file); + *p = '\0'; + + if( p - yylval.s > sizeof(yylval.s)-1 ) + { + fprintf(stderr, "line %d: identifier too long, truncated\n", lex_lineno); + yylval.s[sizeof(yylval.s)-1] = '\0'; + } + + kword = (LEX_KEYWORD *)bsearch(yylval.s, + lex_keywordtab, + lex_keywords, + sizeof(LEX_KEYWORD), + (LEX_CMPFUNC)keywordcmp); + if( kword == NULL ) + { +#ifdef T_NUMBER + int i; + /* See if identifier is in define table */ + for( i=0; itoken; +} + +#ifdef T_NUMBER +int lex_parse_number(c) +int c; +{ + yylval.val = 0; + do + { + yylval.val = yylval.val * 10 + c - '0'; + c = getc(lex_file); + } + while( isdigit(c) ); + ungetc(c, lex_file); + + return T_NUMBER; +} +#endif + + +#ifdef T_STRING +int lex_parse_string() +{ + char *p = yylval.s; + + while( (*p = getc(lex_file)) != '"' ) + { + if( *p == '\n' ) + yyerror("newline in string"); + else + p++; + } + + *p = '\0'; + + return T_STRING; +} +#endif + +#ifdef T_CHARCONST +int lex_parse_charconst() +{ + /* ' has already been read */ + yylval.val = getc(lex_file); + + if( getc(lex_file) != '\'' ) + yyerror("unterminated character constant"); + + return T_CHARCONST; +} +#endif + +void lex_skip_line() +{ + int c; + + while( (c = getc(lex_file)) != '\n' && c != EOF ) + ; + if( c == EOF ) + ungetc(EOF, lex_file); + + lex_lineno++; +} + +/* end-of-file */ + diff --git a/util/lex.h b/util/lex.h new file mode 100644 index 0000000..6eb70f0 --- /dev/null +++ b/util/lex.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------------------------- + * File : @(#)lex.h 93/11/08 Copyright (c) 1993-94 CT Danmark + * Compiler: UNIX C, Turbo C, Microsoft C + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Description: + * General functions for lexical analysers. + * + * Revision History + * ---------------------------------------- + * 11-Nov-1993 tbp Initial version. + * + *--------------------------------------------------------------------------*/ + +#include "environ.h" + +/*-------------------------- Function prototypes ---------------------------*/ +void lex_skip_comment PRM( (void); ) +int lex_parse_keyword PRM( (int); ) +int lex_parse_number PRM( (int); ) +int lex_parse_string PRM( (void); ) +int lex_parse_charconst PRM( (void); ) +void lex_skip_line PRM( (void); ) + +typedef int (*LEX_CMPFUNC) PRM( (const void *, const void *); ) + +typedef struct { + short token; + char *s; +} LEX_KEYWORD; + + +/* These variables must be defined in the program that uses lex.c + */ +extern LEX_KEYWORD lex_keywordtab[]; +extern size_t lex_keywords; +extern FILE *lex_file; +extern int lex_lineno; + + +/* end-of-file */ + diff --git a/util/makefile.os2 b/util/makefile.os2 new file mode 100644 index 0000000..05b381e --- /dev/null +++ b/util/makefile.os2 @@ -0,0 +1,51 @@ +SRC_OBJS = ..\src\readdbd.obj ..\src\os.obj +DDLP_OBJS = ddl.obj ddlp.obj ddlplex.obj ddlpsym.obj +DBDVIEW_OBJS = dbdview.obj +TYIMPORT_OBJS = imp.obj import.obj impspec.obj importlx.obj +TYEXPORT_OBJS = exp.obj export.obj expspec.obj exportlx.obj +DDLP = ddlp.exe +DBDVIEW = dbdview.exe +TYIMPORT = tyimport.exe +TYEXPORT = tyexport.exe +LIBS = ..\src\typhoon.lib +CFLAGS = /DOS2 /I..\include /I..\src +YACC = bison -d -y +CC = icc /OS2 /Tdc /Sp1 /Q /Tx /Fi /Si /Gh /Ti /Gm /Gd /G4 /Tm \ + /Tl1 $(CFLAGS) + +all: $(DDLP) $(DBDVIEW) $(TYIMPORT) $(TYEXPORT) + +clean: + del ddl.c ddl.h exp.c exp.h imp.c imp.h *.obj *.exe y_tab.* + +$(DDLP): $(DDLP_OBJS) + $(CC) /Fe"ddlp.exe" $(DDLP_OBJS) $(LIBS) + +$(DBDVIEW): $(DBDVIEW_OBJS) + $(CC) $(DBDVIEW_OBJS) $(LIBS) + +$(TYIMPORT): $(TYIMPORT_OBJS) + $(CC) /Fe"tyimport.exe" $(TYIMPORT_OBJS) $(SRC_OBJS) $(LIBS) + +$(TYEXPORT): $(TYEXPORT_OBJS) + $(CC) /Fe"tyexport.exe" $(TYEXPORT_OBJS) $(SRC_OBJS) $(LIBS) + +ddl.c: ddl.y + $(YACC) ddl.y + ren y_tab.c ddl.c + ren y_tab.h ddl.h + +exp.c: exp.y + $(YACC) exp.y + ren y_tab.c exp.c + ren y_tab.h exp.h + +imp.c: imp.y + $(YACC) imp.y + ren y_tab.c imp.c + ren y_tab.h imp.h + +.c.obj: + $(CC) -c $< + +# end-of-file diff --git a/util/restore.c b/util/restore.c new file mode 100644 index 0000000..7ed01b1 --- /dev/null +++ b/util/restore.c @@ -0,0 +1,633 @@ +/*---------------------------------------------------------------------------- + * File : restore.c + * Program : tybackup + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains the tyrestore utility. + * + * Functions: + * + *--------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: restore.c,v 1.2 1999/10/03 23:36:49 kaz Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "typhoon.h" +#include "ty_dbd.h" +#include "ty_type.h" +#include "ty_glob.h" +#include "ty_prot.h" +#include "ty_log.h" +#include "util.h" + +/*------------------------------- Constants --------------------------------*/ +#define VERSION "1.00" +#define BLOCK_SIZE 512 +#define OUTBUF_SIZE (BLOCK_SIZE * 128) +#define INBUF_SIZE (256 * 1024) + +/*-------------------------- Function prototypes ---------------------------*/ +static void SignalHandler PRM( (int); ) +static void Read PRM( (void *, ulong); ) +static void DisplayProgress PRM( (char *, ulong); ) +static void RestoreDatabase PRM( (void); ) +static void RestoreFile PRM( (char *); ) +static void RestoreDbdFile PRM( (void); ) +static void RestoreLogFile PRM( (void); ) +static void help PRM( (void); ) + void FixLog PRM( (char *); ) + +/*---------------------------- Global variables ----------------------------*/ +static ArchiveMediaHeader mediahd; /* Archive media header */ +static Dbentry dbd; /* DBD anchor */ +static int archive_fh = -1; /* Archive file handle */ +static char dbname[DBNAME_LEN+1]; /* Database name */ +static char *device = "/dev/null"; /* Archive device */ +static ulong curr_id; /* Current block header id */ +static char *inbuf; /* Input buffer */ +static ulong inreadpos; /* Current read position in buffer */ +static ulong inbytes; /* Number of bytes in outbuf */ +static ulong total_read = 0; /* Total number of bytes read */ + jmp_buf err_jmpbuf; /* Jumped to on error */ + char *datapath = ""; /* Database file path */ + int db_status; /* Required by ../read_dbd.c */ + int verbose = 0; /* Be verbose? */ + + + +/*--------------------------------------------------------------------------*\ + * + * Function : SignalHandler + * + * Purpose : + * + * Parameters: + * + * Returns : + * + */ +static void SignalHandler(sig) +int sig; +{ + printf("signal %d\n", sig); + + if( sig == SIGSEGV ) + puts("Segmentation violation"); + else if( sig == SIGBUS ) + puts("Bus error"); + + dbd.shm->restore_active = 0; + shm_free(&dbd); + d_close(); + unlink(LOG_FNAME); + puts("Restore aborted."); + exit(1); +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : Read + * + * Purpose : Read x bytes from the archive. If the data is not entirely + * in the buffer, the rest will read immediately. If the end + * of the current media has been reached, the user is promted + * for the next media. + * + * Parameters: buf - Buffer to write. + * size - Number of bytes in buffer. + * + * Returns : Nothing. + * + */ +static void Read(buf, size) +void *buf; +ulong size; +{ + char s[20]; + ulong copymax; + long numread; + + +/*printf("Read: size=%d, inbytes=%ld, inreadpos=%ld\n", + size, inbytes, inreadpos);*/ + + /* Copy as much as possible to the output buffer */ + if( inbytes > inreadpos ) + { + copymax = size; + if( copymax > inbytes-inreadpos ) + copymax = inbytes-inreadpos; + + memcpy(buf, inbuf+inreadpos, copymax); + inreadpos += copymax; + + if( copymax == size ) + return; + } + else + copymax = 0; + + while( archive_fh == -1 ) + { + clock_off(); + printf("Insert backup media no %d [enter]", mediahd.seqno); + fflush(stdout); + gets(s); + + if( s[0] == 'q' ) + longjmp(err_jmpbuf, 2); + + clock_on(); + if( (archive_fh = open(device, O_RDONLY)) == -1 ) + printf("Cannot open '%s' (errno %d)\n", device, errno); + } + + if( inreadpos == inbytes ) + { + numread = read(archive_fh, inbuf, INBUF_SIZE); +/*printf("size=%ld, read=%ld, copymax=%ld, inreadpos=%ld, inbytes=%ld\n", + size, numread, copymax, inreadpos, inbytes);*/ + + if( numread == -1 ) + { + printf("Read error (errno %d)\n", errno); + longjmp(err_jmpbuf, 1); + } + else if( numread != INBUF_SIZE ) + { + /* The number of bytes read is less than requested. If the + * Read request can be satisfied by the number of bytes actually + * returned by read(), we assume that the end of the current + * media has been reached. + */ + if( numread + copymax < size ) + { + printf("Read error (errno %d)\n", errno); + longjmp(err_jmpbuf, 1); + } + + mediahd.seqno++; + close(archive_fh); + archive_fh = -1; + } + + /* Now copy the rest of the buffer */ + inbytes = numread; + inreadpos = size - copymax; + memcpy((char *)buf + copymax, inbuf, inreadpos); + + total_read += numread; + +/*printf("size=%ld, read=%ld, copymax=%ld, inreadpos=%ld, inbytes=%ld\n", + size, numread, copymax, inreadpos, inbytes);*/ + } +} + + + + +/*--------------------------------------------------------------------------*\ + * + * Function : DisplayProgress + * + * Purpose : Prints the number of bytes read so far from the current table. + * + * Parameters: table - Table name. + * bytes - Number of bytes read. + * + * Returns : Nothing. + * + */ +static void DisplayProgress(table, bytes) +char *table; +ulong bytes; +{ + printf("\rWriting %-26.26s %10s bytes", table, printlong(bytes)); + fflush(stdout); +} + + + +/*--------------------------------------------------------------------------*\ + * + * Function : RestoreDatabase + * + * Purpose : + * + * Parameters: None. + * + * Returns : Nothing. + * + */ +static void RestoreDatabase() +{ + ArchiveTableHeader tablehd; + ArchiveRecordHeader recordhd; + ulong prev_bytecount; + ulong bytecount; + char outbuf[OUTBUF_SIZE]; + char fname[128]; + char objname[128]; + int fh; + + Read(&curr_id, sizeof curr_id); + + for( ;; ) + { + if( curr_id != ARCHIVE_TABLE ) + break; + + Read(&tablehd.recsize, sizeof(tablehd) - sizeof(tablehd.id)); + + sprintf(fname, "%s/%s", datapath, tablehd.fname); + sprintf(objname, "table %s", tablehd.table); + + if( (fh = open(fname, O_WRONLY|O_TRUNC|O_CREAT|O_BINARY, CREATMASK)) == -1 ) + { + printf("Cannot open file '%s'\n", fname); + longjmp(err_jmpbuf, 1); + } + prev_bytecount = bytecount = 0; + + for( ;; ) + { + Read(&curr_id, sizeof curr_id); + + if( curr_id != ARCHIVE_RECORD ) + break; + + Read(&recordhd.recid, sizeof(recordhd) - sizeof(recordhd.id)); + Read(outbuf, tablehd.recsize); + + lseek(fh, tablehd.recsize * recordhd.recno, SEEK_SET); + write(fh, outbuf, tablehd.recsize); + + bytecount += tablehd.recsize; + + if( verbose && bytecount > prev_bytecount + 100000 ) + { + prev_bytecount = bytecount; + DisplayProgress(objname, bytecount); + } + } + + if( verbose ) + { + DisplayProgress(objname, bytecount); + puts(""); + } + + + close(fh); + } +} + + + +/*--------------------------------------------------------------------------*\ + * + * Function : RestoreFile + * + * Purpose : Read a sequential file from archive. + * + * Parameters: fname - File name. + * + * Returns : Nothing. + * + */ +static void RestoreFile(fname) +char *fname; +{ + ArchiveFileDataHeader datahd; + int fh; + char buf[OUTBUF_SIZE]; + ulong numread; + ulong readmax; + ulong bytecount=0; + + if( (fh = open(fname, O_WRONLY|O_TRUNC|O_CREAT, CREATMASK)) == -1 ) + { + printf("Cannot open '%s'\n", fname); + return; + } + + for( ;; ) + { + Read(&curr_id, sizeof curr_id); + + if( curr_id != ARCHIVE_FILEDATA ) + { + printf("Unexpected header id %d in middle of file \n", curr_id); + longjmp(err_jmpbuf, 1); + } + + Read(&datahd.size, sizeof(datahd) - sizeof(datahd.size)); + + if( !datahd.size ) /* End-of-file reached */ + break; + + while( datahd.size > 0 ) + { + /* Determine number of bytes to read */ + readmax = sizeof buf; + if( readmax > datahd.size ) + readmax = datahd.size; + + Read(buf, readmax); + write(fh, buf, readmax); + + bytecount += readmax; + + if( verbose ) + DisplayProgress(fname, bytecount); + + datahd.size -= readmax; + } + } + + if( verbose ) + puts(""); + + close(fh); +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : RestoreDbdFile + * + * Purpose : Restores the dbd-file from the archive. + * + * Parameters: None. + * + * Returns : Nothing. + * + */ +static void RestoreDbdFile() +{ + ArchiveFileHeader filehd; + + /* Restore dbd-file */ + Read(&filehd, sizeof filehd); + + if( filehd.id != ARCHIVE_FILE ) + { + printf("Unexpected header id %d\n", filehd.id); + longjmp(err_jmpbuf, 1); + } + + RestoreFile(filehd.fname); +} + + +/*--------------------------------------------------------------------------*\ + * + * Function : RestoreLogFile + * + * Purpose : Restores the log file from the archive. + * + * Parameters: None. + * + * Returns : Nothing. + * + */ +static void RestoreLogFile() +{ + ArchiveFileHeader filehd; + + if( curr_id == ARCHIVE_END ) + return; + + /* Restore the database */ + if( curr_id != ARCHIVE_FILE ) + { + printf("Unexpected header id %ld\n", curr_id); + longjmp(err_jmpbuf, 1); + } + + Read(filehd.fname, sizeof(filehd) - sizeof(filehd.id)); + + RestoreFile(filehd.fname); +} + + + +static void VerboseFn(table, records, curr_rec) +char *table; +ulong records, curr_rec; +{ + static int old_percent = 0; + int percent = curr_rec * 100 / records; + + if( curr_rec == 0 ) + { + printf("\nRebuilding %-32.32s 0%%", table); + fflush(stdout); + old_percent = 0; + } + else if( percent != old_percent ) + { + printf("\b\b\b\b%3d%%", percent); + fflush(stdout); + old_percent = percent; + } +} + + + +static void help() +{ + puts("Syntax: tyrestore [option]...\n" + "Options:\n" + " -d Backup device\n" + " -f Path for data files\n" + " -v Be verbose\n"); + exit(1); +} + + + +main(argc, argv) +int argc; +char *argv[]; +{ + char dbdname[20]; + int i; + struct tm *tm; + + /* The input buffer MUST be bigger than the output buffer */ + assert(OUTBUF_SIZE < INBUF_SIZE); + + printf("Typhoon Restore version %s\n", VERSION); + + if( argc < 2 ) + help(); + + for( i=1; irestore_active = 1; + + signal(SIGINT, SignalHandler); + signal(SIGTERM, SignalHandler); + signal(SIGQUIT, SignalHandler); + signal(SIGSEGV, SignalHandler); + signal(SIGBUS, SignalHandler); + signal(SIGHUP, SIG_IGN); + + if( dbd.shm->use_count > 1 ) + { + puts("The database is currenly in use. Cannot restore"); + goto out; + } + + /* Confirm restore */ + clock_off(); + tm = localtime(&mediahd.date); + printf("Database '%s' from %s", mediahd.dbname, asctime(tm)); + printf("Restore? [y/n]: "); + fflush(stdout); + if( getchar() != 'y' ) + goto out; + clock_on(); + + RestoreDbdFile(); + RestoreDatabase(); + RestoreLogFile(); + clock_off(); + + if( verbose ) + { + ulong secs = clock_secs(); + + /* Guard against division by zero */ + if( !secs ) + secs = 1; + + printf("\rTotal %40s bytes\n", printlong(total_read)); + printf("%s bytes/second\n", printlong(total_read / secs)); + } + + FixLog(mediahd.dbname); + + printf("Rebuilding index files"); + d_keybuild(VerboseFn); + d_dbfpath(datapath); + + if( d_open(mediahd.dbname, "o") != S_OKAY ) + puts("Cannot open database"); + else + { + d_close(); + puts(""); + } + +out: + free(inbuf); + dbd.shm->restore_active = 0; + shm_free(&dbd); + free(dbd.dbd); + unlink(LOG_FNAME); + + return 0; +} + +/* end-of-file */ diff --git a/util/util.c b/util/util.c new file mode 100644 index 0000000..5820f09 --- /dev/null +++ b/util/util.c @@ -0,0 +1,104 @@ +/*---------------------------------------------------------------------------- + * File : util.c + * Program : tybackup, tyrestore + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Contains miscellaneous function used by tybackup and tyrestore. + * + * Functions: + * clock_on - Start timer. + * clock_off - Stop timer. + * clock_secs - Report number of seconds passed. + * printlong - Print a long with 1000 separators. + * + *--------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: util.c,v 1.2 1999/10/03 23:36:49 kaz Exp $"; + +#include +#include +#include "environ.h" + +#ifndef NULL +#define NULL 0 +#endif + + +/*---------------------------- Global variables ----------------------------*/ +static ulong secs_used = 0; +static ulong clock_start = 0; + + + +void clock_on() +{ + clock_start = time(NULL); +} + + +void clock_off() +{ + if( clock_start ) + secs_used += time(NULL) - clock_start; +} + + +ulong clock_secs() +{ + return secs_used; +} + + +char *printlong(number) +ulong number; +{ + static char s[20]; + char *p = s + sizeof(s)-1; + int n = 3; + + s[19] = 0; + if( !number ) + { + p = s + sizeof(s)-2; + *p = '0'; + } + + while( number ) + { + *--p = '0' + (number % 10); + number /= 10; + + if( !--n && number ) + { + *--p = ','; + n = 3; + } + } + + return p; +} + +/* end-of-file */ diff --git a/util/util.h b/util/util.h new file mode 100644 index 0000000..9ad87d4 --- /dev/null +++ b/util/util.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------------------------- + * File : util.h + * Program : tybackup, tyrestore + * OS : UNIX, OS/2, DOS + * Author : Thomas B. Pedersen + * + * Copyright (c) 1994 Thomas B. Pedersen. All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the above + * copyright notice and the following two paragraphs appear (1) in all + * source copies of this software and (2) in accompanying documentation + * wherever the programatic interface of this software, or any derivative + * of it, is described. + * + * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT, + * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF + * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Description: + * Prototypes for functions in util.c. + * + * $Id: util.h,v 1.2 1999/10/04 05:29:46 kaz Exp $ + * + *--------------------------------------------------------------------------*/ + +/*-------------------------- Function prototypes ---------------------------*/ +void clock_on PRM( (void); ) +void clock_off PRM( (void); ) +ulong clock_secs PRM( (void); ) +char *printlong PRM( (ulong); ) + + +/* end-of-file */