home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume44 / typhoon / part04 < prev    next >
Encoding:
Internet Message Format  |  1994-09-17  |  60.4 KB

  1. From: zeppelin@login.dknet.dk (Thomas B. Pedersen)
  2. Newsgroups: comp.sources.misc
  3. Subject: v44i060:  typhoon - Typhoon Relational Database Management System, Part04/09
  4. Date: 17 Sep 1994 21:45:32 -0500
  5. Organization: Sterling Software
  6. Sender: kent@sparky.sterling.com
  7. Approved: kent@sparky.sterling.com
  8. Message-ID: <35g9kc$ohr@sparky.sterling.com>
  9. X-Md4-Signature: fdbca861c8379026e4074aaa3fd4d708
  10.  
  11. Submitted-by: zeppelin@login.dknet.dk (Thomas B. Pedersen)
  12. Posting-number: Volume 44, Issue 60
  13. Archive-name: typhoon/part04
  14. Environment: SCO UNIX, Tandem NonStop UNIX, Sun Solaris, AIX, Linux, OS/2
  15.  
  16. #! /bin/sh
  17. # This is a shell archive.  Remove anything before this line, then feed it
  18. # into a shell via "sh file" or similar.  To overwrite existing files,
  19. # type "sh file -c".
  20. # Contents:  typhoon/src/record.c typhoon/src/ty_auxfn.c
  21. #   typhoon/src/ty_find.c typhoon/src/ty_refin.c typhoon/src/unix.c
  22. #   typhoon/src/util/export.c
  23. # Wrapped by kent@sparky on Sat Sep 17 21:38:16 1994
  24. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
  25. echo If this archive is complete, you will see the following message:
  26. echo '          "shar: End of archive 4 (of 9)."'
  27. if test -f 'typhoon/src/record.c' -a "${1}" != "-c" ; then 
  28.   echo shar: Will not clobber existing file \"'typhoon/src/record.c'\"
  29. else
  30.   echo shar: Extracting \"'typhoon/src/record.c'\" \(9702 characters\)
  31.   sed "s/^X//" >'typhoon/src/record.c' <<'END_OF_FILE'
  32. X/*----------------------------------------------------------------------------
  33. X * File    : record.c
  34. X * Library : typhoon
  35. X * OS      : UNIX, OS/2, DOS
  36. X * Author  : Thomas B. Pedersen
  37. X *
  38. X * Copyright (c) 1994 Thomas B. Pedersen.  All rights reserved.
  39. X *
  40. X * Permission is hereby granted, without written agreement and without
  41. X * license or royalty fees, to use, copy, modify, and distribute this
  42. X * software and its documentation for any purpose, provided that the above
  43. X * copyright notice and the following two  paragraphs appear (1) in all 
  44. X * source copies of this software and (2) in accompanying documentation
  45. X * wherever the programatic interface of this software, or any derivative
  46. X * of it, is described.
  47. X *
  48. X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
  49. X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
  50. X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN 
  51. X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  52. X *
  53. X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
  54. X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  55. X * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 
  56. X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
  57. X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  58. X *
  59. X * Description:
  60. X *   Contains record file functions.
  61. X *
  62. X * Functions:
  63. X *   rec_open        - Open a record file.
  64. X *   rec_close        - Close a record file.
  65. X *   rec_add        - Add a record to a file.
  66. X *   rec_write        - Write a record to a file.
  67. X *   rec_delete        - Delete a record.
  68. X *   rec_read        - Read a record.
  69. X *   rec_frst        - Read the first record in a file.
  70. X *   rec_last        - Read the last record in a file.
  71. X *   rec_next        - Read the next record in a file.
  72. X *   rec_prev        - Read the previous record in a file.
  73. X *   rec_numrecords    - Return the number of records in a file.
  74. X *   rec_reccurr    - Return the record number of the current record.
  75. X *
  76. X * $Log: record.c,v $
  77. X * Revision 1.2  1994/09/17  16:00:14  tbp
  78. X * typhoon.h and environ.h are now included from <>.
  79. X *
  80. X * Revision 1.1  1994/09/13  21:28:32  tbp
  81. X * Added to repository.
  82. X * 
  83. X *
  84. X *--------------------------------------------------------------------------*/
  85. X
  86. Xstatic char rcsid[] = "$Id: record.c,v 1.2 1994/09/17 16:00:14 tbp Exp $";
  87. X
  88. X#include <fcntl.h>
  89. X#include <errno.h>
  90. X#include <stdio.h>
  91. X#include <string.h>
  92. X#include <errno.h>
  93. X#ifdef UNIX
  94. X#   include <unistd.h>
  95. X#    ifdef __STDC__
  96. X#        include <stdlib.h>
  97. X#    endif
  98. X#else
  99. X#   include <stdlib.h>
  100. X#   include <io.h>
  101. X#   include <sys\stat.h>
  102. X#    include <stddef.h>
  103. X#endif
  104. X
  105. X#include <typhoon.h>
  106. X#include "ty_dbd.h"
  107. X#include "ty_type.h"
  108. X#include "ty_prot.h"
  109. X#include "ty_glob.h"
  110. X
  111. X
  112. X/*--------------------------- Function prototypes --------------------------*/
  113. Xstatic void putheader   PRM( (RECORD *);            )
  114. Xstatic void getheader   PRM( (RECORD *);            )
  115. X
  116. X/*--------------------------------- Macros ---------------------------------*/
  117. X#define recseek(R,pos)  lseek(R->fh, R->H.recsize * (pos), SEEK_SET)
  118. X#define RECVERSION_ID   "RecMan120"
  119. X#define RECVERSION_NUM  120
  120. X
  121. X
  122. Xstatic void putheader(R)
  123. XRECORD *R;
  124. X{
  125. X    recseek(R, 0L);
  126. X    write(R->fh, &R->H, sizeof(R->H));
  127. X}
  128. X
  129. X
  130. Xstatic void getheader(R)
  131. XRECORD *R;
  132. X{
  133. X    recseek(R, 0L);
  134. X    read(R->fh, &R->H, sizeof(R->H));
  135. X}
  136. X
  137. X
  138. X/*-------------------------------- rec_open ----=---------------------------*\
  139. X *
  140. X * Purpose     : Opens a record file.
  141. X *
  142. X * Parameters: fname    - File name.
  143. X *               recsize    - Record size.
  144. X *               shared    - Open file in shared mode?
  145. X *
  146. X * Returns     : NULL        - File could not be opened. db_status contains reason.
  147. X *               else        - Pointer to record file descriptor.
  148. X *
  149. X */
  150. X
  151. XRECORD *rec_open(fname, recsize, shared)
  152. Xchar *fname;
  153. Xunsigned recsize;
  154. Xint shared;
  155. X{
  156. X    RECORD *R;
  157. X    int isnew, fh;
  158. X
  159. X    /* if file exists then read header record, otherwise create */
  160. X    isnew = access(fname, 0);
  161. X
  162. X    if( (fh=os_open(fname, O_BINARY|O_RDWR|O_CREAT,CREATMASK)) == -1 )
  163. X    {
  164. X        db_status = S_IOFATAL;
  165. X        return NULL;
  166. X    }
  167. X
  168. X    /* Lock the file if it is not opened in shared mode */
  169. X    if( !shared )
  170. X        if( os_lock(fh, 0L, 1, 't') == -1 )
  171. X        {
  172. X            db_status = S_NOTAVAIL;
  173. X            return NULL;
  174. X       }
  175. X
  176. X    if( (R=(RECORD *)malloc(sizeof(RECORD)+recsize)) == NULL )
  177. X    {
  178. X        os_close(fh);
  179. X        db_status = S_NOMEM;
  180. X        return NULL;
  181. X    }
  182. X
  183. X    R->fh = fh;
  184. X    R->recno = 0;
  185. X
  186. X    if( isnew )
  187. X    {
  188. X        unsigned headersize;
  189. X
  190. X        R->H.datasize       = recsize;
  191. X        R->H.recsize        = recsize + (int)offsetof(RECORDHEAD, data[0]);
  192. X        R->H.first_deleted  = 0;
  193. X        R->H.first          = 0;
  194. X        R->H.last           = 0;
  195. X        R->H.numrecords     = 0;
  196. X        R->H.version        = RECVERSION_NUM;
  197. X        strcpy(R->H.id, RECVERSION_ID);
  198. X
  199. X        R->first_possible_rec = (sizeof(R->H) + R->H.recsize - 1) / R->H.recsize;
  200. X   
  201. X        /* Beware that the record can be smaller than the header */
  202. X        if( R->H.recsize < sizeof(R->H) )
  203. X            headersize = R->first_possible_rec * R->H.recsize;
  204. X        else
  205. X            headersize = R->H.recsize;
  206. X
  207. X        recseek(R, 0L);
  208. X        write(fh, &R->H, headersize);
  209. X    }
  210. X    else
  211. X    {
  212. X        getheader(R);
  213. X
  214. X        R->first_possible_rec = (sizeof(R->H) + R->H.recsize - 1) / R->H.recsize;
  215. X
  216. X        if( R->H.version != RECVERSION_NUM )
  217. X        {
  218. X            db_status = S_VERSION;
  219. X            os_close(fh);
  220. X            free(R);
  221. X            return NULL;
  222. X        }
  223. X    }
  224. X
  225. X    strcpy(R->fname, fname);
  226. X
  227. X    db_status = S_OKAY;
  228. X
  229. X    return R;
  230. X}
  231. X
  232. X
  233. Xrec_close(R)
  234. XRECORD *R;
  235. X{
  236. X    if( R->fh != -1 )
  237. X        os_close(R->fh);
  238. X    free(R);
  239. X    R = NULL;
  240. X
  241. X    RETURN S_OKAY;
  242. X}
  243. X
  244. X
  245. Xrec_dynclose(R)
  246. XRECORD *R;
  247. X{
  248. X    if( R->fh != -1 )
  249. X    {
  250. X        close(R->fh);
  251. X        R->fh = -1;
  252. X    }
  253. X
  254. X    RETURN S_OKAY;
  255. X}
  256. X
  257. Xrec_dynopen(R)
  258. XRECORD *R;
  259. X{
  260. X    if( R->fh == -1 )
  261. X        if( (R->fh=os_open(R->fname, O_BINARY|O_RDWR|O_CREAT,CREATMASK)) == -1 )
  262. X            RETURN S_IOFATAL;
  263. X
  264. X    RETURN S_OKAY;
  265. X}
  266. X
  267. X
  268. Xrec_add(R,data,rec)
  269. XRECORD *R;                  /* record file                                  */
  270. Xvoid *data;
  271. Xulong *rec;
  272. X{
  273. X    long recno;
  274. X
  275. X    getheader(R);
  276. X
  277. X    if( R->H.first_deleted )
  278. X    {
  279. X        recno = R->H.first_deleted;
  280. X
  281. X        /* Get recno of next deleted */
  282. X        lseek(R->fh, recno * R->H.recsize + (long)offsetof(RECORDHEAD, next), SEEK_SET);
  283. X        read(R->fh, &R->H.first_deleted, sizeof(R->H.first_deleted));
  284. X    }
  285. X    else
  286. X        recno = (lseek(R->fh, 0L, SEEK_END) + R->H.recsize - 1) / R->H.recsize;
  287. X
  288. X    if( R->H.numrecords )
  289. X    {
  290. X        long pos; 
  291. X
  292. X        /* Adjust next-pointer of last record */
  293. X        pos = R->H.last * R->H.recsize;
  294. X        pos += offsetof(RECORDHEAD, next);
  295. X
  296. X        lseek(R->fh, pos, SEEK_SET);
  297. X        write(R->fh, &recno, sizeof recno);
  298. X
  299. X        /* Set prev-pointer of new record */
  300. X        R->rec.prev = R->H.last;        
  301. X    }
  302. X    else
  303. X    {
  304. X        R->H.first = recno;            /* Set new first pointer                */
  305. X        R->rec.prev = 0;            /* There's no previous record            */
  306. X    }
  307. X
  308. X    R->rec.next = 0;                /* No next record since at end of chain    */
  309. X
  310. X    R->H.last = recno;                /* Set last-pointer to new record        */
  311. X    R->H.numrecords++;
  312. X
  313. X    R->rec.flags = 0;
  314. X    memcpy(R->rec.data, data, R->H.datasize);    /* Copy data to buffer        */
  315. X    lseek(R->fh, recno * R->H.recsize, SEEK_SET);
  316. X    if( write(R->fh, &R->rec, R->H.recsize) != R->H.recsize )        /* Write chain and record    */
  317. X        RETURN S_IOFATAL;
  318. X    
  319. X    putheader(R);
  320. X    *rec = recno;
  321. X
  322. X    return S_OKAY;
  323. X}
  324. X
  325. X
  326. Xrec_write(R, data, recno)
  327. XRECORD *R;
  328. Xvoid *data;
  329. Xulong recno;
  330. X{
  331. X    if( recno < R->first_possible_rec )
  332. X        RETURN S_INVADDR;
  333. X
  334. X    lseek(R->fh, R->H.recsize * recno + (long)offsetof(RECORDHEAD, data[0]), SEEK_SET);
  335. X    write(R->fh, data, R->H.datasize);
  336. X
  337. X    RETURN S_OKAY;
  338. X}
  339. X
  340. Xrec_delete(R, recno)
  341. XRECORD *R;
  342. Xulong recno;
  343. X{
  344. X    getheader(R);
  345. X
  346. X    /* Get previous and next pointers of record to be deleted */
  347. X    lseek(R->fh, R->H.recsize * recno, SEEK_SET);
  348. X    read(R->fh, &R->rec, sizeof R->rec);
  349. X
  350. X    if( R->rec.flags & BIT_DELETED )
  351. X        RETURN S_DELETED;
  352. X
  353. X    /* Adjust previous pointer */
  354. X    if( R->H.first == recno )
  355. X        R->H.first = R->rec.next;
  356. X    else
  357. X    {
  358. X        lseek(R->fh, R->H.recsize * R->rec.prev + (long)offsetof(RECORDHEAD, next), SEEK_SET);
  359. X        write(R->fh, &R->rec.next, sizeof R->rec.next);
  360. X    }
  361. X    
  362. X    /* Adjust next pointer */
  363. X    if( R->H.last == recno )
  364. X        R->H.last = R->rec.prev;
  365. X    else
  366. X    {
  367. X        lseek(R->fh, R->H.recsize * R->rec.next + (long)offsetof(RECORDHEAD, prev), SEEK_SET);
  368. X        write(R->fh, &R->rec.prev, sizeof R->rec.prev);
  369. X    }    
  370. X
  371. X    /* Delete-mark the record and insert it in delete chain */
  372. X    R->rec.flags |= BIT_DELETED;
  373. X    R->rec.next = R->H.first_deleted;
  374. X    R->rec.prev = 0;
  375. X
  376. X    lseek(R->fh, R->H.recsize * recno, SEEK_SET);
  377. X    write(R->fh, &R->rec, sizeof R->rec);
  378. X    R->H.first_deleted = recno;
  379. X    R->H.numrecords--;
  380. X
  381. X    putheader(R);
  382. X
  383. X    RETURN S_OKAY;
  384. X}
  385. X
  386. X
  387. Xrec_read(R,data,recno)
  388. XRECORD *R;
  389. Xvoid *data;
  390. Xulong recno;
  391. X{
  392. X    if( recno < R->first_possible_rec )
  393. X        RETURN S_INVADDR;
  394. X
  395. X    recseek(R, recno);
  396. X    if( read(R->fh, &R->rec, R->H.recsize) < R->H.recsize )
  397. X        RETURN S_NOTFOUND;
  398. X
  399. X    if( R->rec.flags & BIT_DELETED )
  400. X        RETURN S_DELETED;    
  401. X   
  402. X    memcpy(data, R->rec.data, R->H.datasize);
  403. X    R->recno = recno;
  404. X
  405. X    RETURN S_OKAY;
  406. X}
  407. X
  408. X
  409. Xrec_frst(R, data)
  410. XRECORD *R;
  411. Xvoid *data;
  412. X{
  413. X    getheader(R);
  414. X
  415. X    return rec_read(R, data, R->H.first);
  416. X}
  417. X
  418. X
  419. Xrec_last(R, data)
  420. XRECORD *R;
  421. Xvoid *data;
  422. X{
  423. X    getheader(R);
  424. X
  425. X    return rec_read(R, data, R->H.last);
  426. X}
  427. X
  428. X
  429. Xrec_next(R, data)
  430. XRECORD *R;
  431. Xvoid *data;
  432. X{
  433. X    if( CURR_REC )
  434. X        return rec_read(R, data, R->rec.next);
  435. X    else
  436. X        return rec_frst(R, data);
  437. X}
  438. X
  439. X
  440. Xrec_prev(R, data)
  441. XRECORD *R;
  442. Xvoid *data;
  443. X{
  444. X    if( CURR_REC )
  445. X        return rec_read(R, data, R->rec.prev);
  446. X    else
  447. X        return rec_last(R, data);
  448. X}
  449. X
  450. X
  451. Xulong rec_numrecords(R, number)
  452. XRECORD *R;
  453. Xulong *number;
  454. X{
  455. X    getheader(R);
  456. X
  457. X    if( number )
  458. X        *number = R->H.numrecords;
  459. X
  460. X    return R->H.numrecords;
  461. X}
  462. X
  463. X
  464. Xrec_curr(R, recno)
  465. XRECORD *R;
  466. Xulong *recno;
  467. X{
  468. X    if( recno )
  469. X        *recno = R->recno;
  470. X
  471. X    RETURN R->recno ? S_OKAY : S_NOCR;
  472. X}
  473. X
  474. X/* end-of-file */
  475. X
  476. X
  477. END_OF_FILE
  478.   if test 9702 -ne `wc -c <'typhoon/src/record.c'`; then
  479.     echo shar: \"'typhoon/src/record.c'\" unpacked with wrong size!
  480.   fi
  481.   # end of 'typhoon/src/record.c'
  482. fi
  483. if test -f 'typhoon/src/ty_auxfn.c' -a "${1}" != "-c" ; then 
  484.   echo shar: Will not clobber existing file \"'typhoon/src/ty_auxfn.c'\"
  485. else
  486.   echo shar: Extracting \"'typhoon/src/ty_auxfn.c'\" \(10481 characters\)
  487.   sed "s/^X//" >'typhoon/src/ty_auxfn.c' <<'END_OF_FILE'
  488. X/*----------------------------------------------------------------------------
  489. X * File    : ty_auxfn.c
  490. X * Library : typhoon
  491. X * OS      : UNIX, OS/2, DOS
  492. X * Author  : Thomas B. Pedersen
  493. X *
  494. X * Copyright (c) 1994 Thomas B. Pedersen.  All rights reserved.
  495. X *
  496. X * Permission is hereby granted, without written agreement and without
  497. X * license or royalty fees, to use, copy, modify, and distribute this
  498. X * software and its documentation for any purpose, provided that the above
  499. X * copyright notice and the following two  paragraphs appear (1) in all 
  500. X * source copies of this software and (2) in accompanying documentation
  501. X * wherever the programatic interface of this software, or any derivative
  502. X * of it, is described.
  503. X *
  504. X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
  505. X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
  506. X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN 
  507. X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  508. X *
  509. X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
  510. X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  511. X * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 
  512. X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
  513. X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  514. X *
  515. X * Description:
  516. X *   Auxiliary functions for the API functions.
  517. X *
  518. X * Functions:
  519. X *   set_keyptr        - Return a pointer to a key or compound key.
  520. X *   reckeycmp        - Compare two (compound) keys in separate records.
  521. X *   set_subcode    - Set <db_subcode>.
  522. X *   set_recfld        - 
  523. X *   keyfind
  524. X *   keyadd
  525. X *   keydel
  526. X *
  527. X * $Log: ty_auxfn.c,v $
  528. X * Revision 1.2  1994/09/17  16:00:15  tbp
  529. X * typhoon.h and environ.h are now included from <>.
  530. X *
  531. X * Revision 1.1  1994/09/13  21:28:32  tbp
  532. X * Added to repository.
  533. X * 
  534. X *
  535. X *--------------------------------------------------------------------------*/
  536. X
  537. Xstatic char rcsid[] = "$Id: ty_auxfn.c,v 1.2 1994/09/17 16:00:15 tbp Exp $";
  538. X
  539. X#include <stdio.h>
  540. X#include <string.h>
  541. X#include <typhoon.h>
  542. X#include "ty_dbd.h"
  543. X#include "ty_type.h"
  544. X#include "ty_glob.h"
  545. X#include "ty_prot.h"
  546. X
  547. Xaux_getkey(id, key)
  548. XId id;
  549. XKey **key;
  550. X{
  551. X    Field *fld;
  552. X    int rc;
  553. X
  554. X    /* Determine whether this id is a key id or a compound key id */
  555. X    if( id < REC_FACTOR )
  556. X    {
  557. X        if( id >= DB->header.keys )
  558. X            RETURN_RAP(S_NOTKEY);
  559. X
  560. X        *key = &DB->key[id];
  561. X    }
  562. X    else
  563. X    {
  564. X        if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY )
  565. X            return rc;
  566. X
  567. X        if( !(fld->type & FT_KEY) )
  568. X            RETURN_RAP(S_NOTKEY);
  569. X
  570. X        *key = &DB->key[fld->keyid];
  571. X    }
  572. X
  573. X    return S_OKAY;
  574. X}
  575. X
  576. X
  577. X
  578. X/*------------------------------- set_keyptr -------------------------------*\
  579. X *
  580. X * Purpose     : Returns a pointer to the key value of <key>. This function
  581. X *               is necessary because the fields of a compound can be spread
  582. X *               all over the record.
  583. X *
  584. X * Parameters: key    - Pointer to key table entry.
  585. X *               buf    - Buffer to store key value in.
  586. X *               
  587. X *
  588. X * Returns     : A pointer to the key value.
  589. X *
  590. X */
  591. X
  592. Xvoid *set_keyptr(key, buf)
  593. XKey *key;
  594. Xvoid *buf;
  595. X{
  596. X    static char keybuf[KEYSIZE_MAX];
  597. X    int n;
  598. X
  599. X    CURR_KEY = key - DB->key;
  600. X
  601. X    if( (n = key->fields) > 1 )
  602. X    {
  603. X        KeyField *keyfld = DB->keyfield + key->first_keyfield;
  604. X
  605. X        /* Build compound key from record */
  606. X        while( n-- )
  607. X        {
  608. X            memcpy(keybuf + keyfld->offset,
  609. X                   (char *)buf + DB->field[ keyfld->field ].offset,
  610. X                                 DB->field[ keyfld->field ].size);
  611. X            keyfld++;
  612. X        }
  613. X
  614. X        return (void *)keybuf;
  615. X    }
  616. X
  617. X    return (void *)((char *)buf + DB->field[ DB->keyfield[ key->first_keyfield ].field ].offset);
  618. X}
  619. X
  620. X
  621. X
  622. X
  623. X
  624. X/*-------------------------------- reckeycmp -------------------------------*\
  625. X *
  626. X * Purpose     : The function compares two fields in separate records. This
  627. X *               function is necessary because the fields of a compound can be
  628. X *               spread all over the record (i.e. compound key).
  629. X *
  630. X * Parameters: key        - Pointer to key struct.
  631. X *               a        - Pointer to first record buffer.
  632. X *               b        - Pointer to seconds record buffer.
  633. X *
  634. X * Returns     : 0        - The keys match.
  635. X *               !=0        - The keys do not match.
  636. X *
  637. X */
  638. X
  639. Xreckeycmp(key, a, b)
  640. XKey *key;
  641. Xvoid *a, *b;
  642. X{
  643. X    KeyField *keyfld = DB->keyfield + key->first_keyfield;
  644. X    Field *field;
  645. X    int fields = key->fields;
  646. X    int type, diff;
  647. X
  648. X    CURR_KEY = key - DB->key;
  649. X
  650. X    /* An optional key is regarded as changed if 
  651. X     *   a) One and only one of the null determinatator is not set
  652. X     *   b) If both null determinators are set and the key values different
  653. X     */
  654. X    if( key->type & KT_OPTIONAL )
  655. X    {
  656. X        int a_null = null_indicator(key, a);
  657. X        int b_null = null_indicator(key, b);
  658. X
  659. X        if( a_null && b_null )
  660. X            return 0;
  661. X
  662. X        if( a_null || b_null )
  663. X            return 1;
  664. X
  665. X        /* Otherwise both keys are not null - compare */
  666. X    }
  667. X
  668. X    do
  669. X    {
  670. X        field    = DB->field + keyfld->field;
  671. X        type    = field->type & (FT_BASIC|FT_UNSIGNED);
  672. X
  673. X        if( diff = (*keycmp[type])((char *)a + field->offset, (char *)b + field->offset) )
  674. X            break;
  675. X
  676. X        keyfld++;
  677. X    }
  678. X    while( --fields );
  679. X
  680. X    /* Ascending or descending is not significant here */
  681. X    return diff;
  682. X}
  683. X
  684. X
  685. X
  686. X/*------------------------------- set_subcode ------------------------------*\
  687. X *
  688. X * Purpose     : This function sets <db_subcode> to the id of <key>. The id
  689. X *               can either be a compound key id or a field id.
  690. X *
  691. X * Parameters: key        - Pointer to key table entry.
  692. X *
  693. X * Returns     : Nothing.
  694. X *
  695. X */
  696. Xvoid set_subcode(key)
  697. XKey *key;
  698. X{
  699. X    Field *fld;
  700. X
  701. X    if( key->fields > 1 )
  702. X        db_subcode = key - DB->key /* + 1 */;
  703. X    else
  704. X    {
  705. X        fld = DB->field + DB->keyfield[ key->first_keyfield ].field;
  706. X
  707. X        db_subcode = (fld->recid + 1) * REC_FACTOR +
  708. X             (fld - DB->field) - DB->record[ fld->recid ].first_field + 1; 
  709. X    }
  710. X}
  711. X
  712. X
  713. X
  714. X/*------------------------------- set_recfld -------------------------------*\
  715. X *
  716. X * Purpose     : This function sets the pointers <recptr> and <fldptr> to the
  717. X *               record and field denoted by <id>. The following calls are
  718. X *               legal:
  719. X *
  720. X *               If <id> is -1, <rec> will be set to the current record (<CURR_RECID>) a
  721. X *               <fld> to the first field of that record.
  722. X *
  723. X *               Both <rec> and <fld> may be NULL.
  724. X *
  725. X * Parameters: id        - Record or field id.
  726. X *               recptr    - Pointer to record pointer.
  727. X *               fldptr    - Pointer to field pointer.
  728. X *
  729. X * Returns     : S_OKAY    - recid and fldid were okay, recptr and fldptr set.
  730. X *               S_NOCD    - No current database.
  731. X *               S_INVREC - Invalid record id.
  732. X *               S_INVFLD - Invalid field id.
  733. X *
  734. X */
  735. X
  736. Xset_recfld(id, recptr, fldptr)
  737. XId id;
  738. XRecord **recptr;
  739. XField **fldptr;
  740. X{
  741. X    Id recid, fldid;
  742. X
  743. X    if( CURR_DB == -1 )
  744. X        RETURN_RAP(S_NOCD);
  745. X
  746. X    /* If is -1 the return pointers to the current record */
  747. X    if( id == -1 )
  748. X    {
  749. X        recid = CURR_RECID;
  750. X        fldid = DB->record[recid].first_field;
  751. X    }
  752. X    else
  753. X    {
  754. X        /* Remove record factor */
  755. X        recid = id / REC_FACTOR - 1;
  756. X
  757. X        if( fldid = id % REC_FACTOR )
  758. X            fldid--;
  759. X
  760. X        /* Validate record id */
  761. X        if( recid >= DB->header.records )
  762. X            RETURN_RAP(S_INVREC);
  763. X
  764. X        /* Validate field id */
  765. X        if( fldid >= DB->record[recid].fields )
  766. X            RETURN_RAP(S_INVFLD);
  767. X
  768. X        fldid += DB->record[recid].first_field;
  769. X        CURR_RECID = recid;
  770. X    }
  771. X
  772. X    if( recptr )
  773. X        *recptr = DB->record + recid;
  774. X
  775. X    if( fldptr )
  776. X        *fldptr = DB->field + fldid;
  777. X
  778. X    RETURN S_OKAY;
  779. X}
  780. X
  781. X
  782. Xkeyfind(key, buf, ref)
  783. XKey *key;
  784. Xvoid *buf;
  785. Xulong *ref;
  786. X{
  787. X    CURR_KEY = key - DB->key;
  788. X
  789. X    return ty_keyfind(key, set_keyptr(key, buf), ref);
  790. X}
  791. X
  792. X
  793. Xkeyadd(key, buf, ref)
  794. XKey *key;
  795. Xvoid *buf;
  796. Xulong ref;
  797. X{
  798. X    CURR_KEY = key - DB->key;
  799. X
  800. X    return ty_keyadd(key, set_keyptr(key, buf), ref);
  801. X}
  802. X
  803. X
  804. Xkeydel(key, buf, ref)
  805. XKey *key;
  806. Xvoid *buf;
  807. Xulong ref;
  808. X{
  809. X    CURR_KEY = key - DB->key;
  810. X
  811. X    return ty_keydel(key, set_keyptr(key, buf), ref);
  812. X}
  813. X
  814. X
  815. X/*------------------------------ compress_vlr ------------------------------*\
  816. X *
  817. X * Purpose     : Compresses or uncompresses a the variable length record
  818. X *               defined by <rec>.
  819. X *
  820. X * Parameters: action    - COMPRESS/UNCOMPRESS.
  821. X *               rec        - Pointer to record table entry.
  822. X *               src        - Pointer to source buffer.
  823. X *               dest        - Pointer to destination buffer.
  824. X *               size        - Will contain compressed size when the function returns.
  825. X *
  826. X * Returns     : -1        - Error. db_status set to cause.
  827. X *               > 0        - Size of compress record in <dest>.
  828. X *
  829. X */
  830. Xcompress_vlr(action, rec, dest, src, size)
  831. Xint action;
  832. XRecord *rec;
  833. Xvoid *src, *dest;
  834. Xunsigned *size;
  835. X{
  836. X    Field *fld;
  837. X    unsigned offset;
  838. X    ushort count;
  839. X    int fields = rec->fields;
  840. X    int fldno = 0;
  841. X
  842. X    /* Find the first variable length field (this cannot be 0)*/
  843. X    fld = &DB->field[rec->first_field];
  844. X
  845. X    while( fields  )
  846. X        if( fld->type & FT_VARIABLE )
  847. X            break;
  848. X        else
  849. X            fld++, fields--;
  850. X    
  851. X    /* Copy the static part of the record */
  852. X    memcpy(dest, src, offset = fld->offset);
  853. X
  854. X    while( fields )
  855. X    {
  856. X        /* Get the value of the size field (stored in keyid) */
  857. X        count = *(ushort *)((char *)src + DB->field[ fld->keyid ].offset) * fld->elemsize;
  858. X
  859. X        /* If the size value is too high report an error to the user */
  860. X        if( count > fld->size )
  861. X        {
  862. X            db_subcode = (fld->recid + 1) * REC_FACTOR + fld->keyid + 1;
  863. X            RETURN S_RECSIZE;
  864. X        }
  865. X
  866. X        if( action == COMPRESS )
  867. X            memcpy((char *)dest + offset, (char *)src + fld->offset, count);
  868. X        else
  869. X            memcpy((char *)dest + fld->offset, (char *)src + offset, count);
  870. X
  871. X        offset += count;
  872. X
  873. X        /* Find next variable length field */
  874. X        if( !--fields )
  875. X            break;
  876. X
  877. X        fld++;
  878. X        while( fld->nesting )
  879. X            fld++, fields--;
  880. X    }
  881. X
  882. X    if( action == COMPRESS )
  883. X        *size = offset;
  884. X
  885. X    return S_OKAY;
  886. X}
  887. X                                         
  888. X
  889. X/*----------------------------- null_indicator -----------------------------*\
  890. X *
  891. X * Purpose     : This function checks whether the null indicator of an optional
  892. X *               key is set.
  893. X *
  894. X * Parameters: key        - Pointer to key.
  895. X *               buf        - Pointer to record buffer.
  896. X *
  897. X * Returns     : 0        - The null indicator is set.
  898. X *               1        - The null indicator is not set.
  899. X *
  900. X */
  901. Xnull_indicator(key, buf)
  902. XKey *key;
  903. Xvoid *buf;
  904. X{
  905. X    return !(((char *)buf)[ DB->field[ key->null_indicator ].offset ]);
  906. X}
  907. X
  908. X
  909. X
  910. X/*------------------------------ update_recbuf -----------------------------*\
  911. X *
  912. X * Purpose     : Makes sure that the contents of the current record is the same
  913. X *               in DB->recbuf as on the disk.
  914. X *
  915. X * Parameters: None.
  916. X *
  917. X * Returns     : None.
  918. X *
  919. X */
  920. X
  921. Xupdate_recbuf()
  922. X{
  923. X    Record *rec = DB->record + CURR_RECID;
  924. X    int rc;
  925. X    unsigned recsize;
  926. X
  927. X/*    if( curr_bufrec == CURR_REC && curr_bufrecid == CURR_RECID )
  928. X        return S_OKAY;*/
  929. X
  930. X    DB->recbuf = DB->real_recbuf + rec->preamble;
  931. X
  932. X    if( rec->is_vlr )
  933. X        rc = ty_vlrread(rec, DB->real_recbuf, CURR_REC, &recsize);
  934. X    else
  935. X        rc = ty_recread(rec, DB->real_recbuf, CURR_REC);
  936. X
  937. X    CURR_BUFREC        = CURR_REC;
  938. X    CURR_BUFRECID    = CURR_RECID;
  939. X    
  940. X    return rc;
  941. X}
  942. X
  943. X/* end-of-file */
  944. END_OF_FILE
  945.   if test 10481 -ne `wc -c <'typhoon/src/ty_auxfn.c'`; then
  946.     echo shar: \"'typhoon/src/ty_auxfn.c'\" unpacked with wrong size!
  947.   fi
  948.   # end of 'typhoon/src/ty_auxfn.c'
  949. fi
  950. if test -f 'typhoon/src/ty_find.c' -a "${1}" != "-c" ; then 
  951.   echo shar: Will not clobber existing file \"'typhoon/src/ty_find.c'\"
  952. else
  953.   echo shar: Extracting \"'typhoon/src/ty_find.c'\" \(10221 characters\)
  954.   sed "s/^X//" >'typhoon/src/ty_find.c' <<'END_OF_FILE'
  955. X/*----------------------------------------------------------------------------
  956. X * File    : ty_find.c
  957. X * Library : typhoon
  958. X * OS      : UNIX, OS/2, DOS
  959. X * Author  : Thomas B. Pedersen
  960. X *
  961. X * Copyright (c) 1994 Thomas B. Pedersen.  All rights reserved.
  962. X *
  963. X * Permission is hereby granted, without written agreement and without
  964. X * license or royalty fees, to use, copy, modify, and distribute this
  965. X * software and its documentation for any purpose, provided that the above
  966. X * copyright notice and the following two  paragraphs appear (1) in all 
  967. X * source copies of this software and (2) in accompanying documentation
  968. X * wherever the programatic interface of this software, or any derivative
  969. X * of it, is described.
  970. X *
  971. X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
  972. X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
  973. X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN 
  974. X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  975. X *
  976. X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
  977. X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  978. X * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 
  979. X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
  980. X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  981. X *
  982. X * Description:
  983. X *   Contains API functions.
  984. X *
  985. X * Functions:
  986. X *   report_err           - Report an error to the user.
  987. X *   d_keyread           - Read the value of the last retrieved key.
  988. X *   d_keyfind           - Find a key.
  989. X *   d_keymove           - Perform a d_keyfrst(), d_keylast(), d_keyprev() or
  990. X *                         d_keynext().
  991. X *   d_recmove           - Perform a d_recfrst(), d_reclast(), d_recprev() or
  992. X *                         d_recnext().
  993. X *   d_crread           - Read the value of a field of the current record.
  994. X *   d_recwrite        - Update a record.
  995. X *   d_recread        - Read the current record.
  996. X *   d_fillnew        - Add a new record to the database.
  997. X *   d_delete        - Delete the current record.
  998. X *   d_crget        - Get the database address of the current record.
  999. X *   d_crset        - Set the database address of the current record.
  1000. X *   d_records        - Return the number of records in a file.
  1001. X *   d_getkeysize    - Return the size of a key.
  1002. X *   d_getrecsize    - Return the size of a record.
  1003. X *
  1004. X * $Log: ty_find.c,v $
  1005. X * Revision 1.2  1994/09/17  16:00:15  tbp
  1006. X * typhoon.h and environ.h are now included from <>.
  1007. X *
  1008. X * Revision 1.1  1994/09/13  21:28:33  tbp
  1009. X * Added to repository.
  1010. X * 
  1011. X *
  1012. X *--------------------------------------------------------------------------*/
  1013. X
  1014. Xstatic char rcsid[] = "$Id: ty_find.c,v 1.2 1994/09/17 16:00:15 tbp Exp $";
  1015. X
  1016. X#ifdef UNIX
  1017. X#    include <unistd.h>
  1018. X#endif
  1019. X#include <string.h>
  1020. X#include <stdio.h>
  1021. X#include <typhoon.h>
  1022. X#include "ty_dbd.h"
  1023. X#include "ty_type.h"
  1024. X#include "ty_glob.h"
  1025. X#include "ty_prot.h"
  1026. X
  1027. X/*--------------------------- Function prototypes --------------------------*/
  1028. Xstatic int    d_keymove        PRM( (Id, int); )
  1029. Xstatic int    d_recmove        PRM( (Id, int); )
  1030. X
  1031. X
  1032. X/*-------------------------------- d_keyread -------------------------------*\
  1033. X *
  1034. X * Purpose     : Copies the contents of the current key into the buffer <buf>.
  1035. X *
  1036. X * Parameters: buf        - Pointer to key buffer. This buffer must be large
  1037. X *                          enough to hold the entire key.
  1038. X *
  1039. X * Returns     : S_NOCR    - No current record.
  1040. X *               S_OKAY    - Key copied ok.
  1041. X *
  1042. X */
  1043. X
  1044. XFNCLASS d_keyread(buf)
  1045. Xvoid *buf;
  1046. X{
  1047. X    /* Make sure that we have a current record */
  1048. X    if( db_status != S_OKAY )
  1049. X        RETURN_RAP(S_NOCR);
  1050. X
  1051. X    /* Make sure that we have performed a key operation ....
  1052. X
  1053. X        ?????????????????????????????????????
  1054. X
  1055. X        RETURN_RAP(S_KEYSEQ);
  1056. X
  1057. X    */
  1058. X
  1059. X    /*return db_keyread(&db->key[curr-key], buf);*/
  1060. X
  1061. X    memcpy(buf, typhoon.curr_keybuf, DB->key[CURR_KEY].size);
  1062. X
  1063. X    RETURN S_OKAY;
  1064. X}
  1065. X
  1066. X
  1067. X
  1068. X/*-------------------------------- d_keyfind -------------------------------*\
  1069. X *
  1070. X * Purpose     : Find a key in an index.
  1071. X *
  1072. X * Parameters: id            - Either key id or field id that is also a key.
  1073. X *
  1074. X * Returns     : S_OKAY        - The key was found. The record can be read by
  1075. X *                               d_recread().
  1076. X *               S_NOTFOUND    - The key could not be found.
  1077. X *               S_NOCD        - No current database.
  1078. X *               S_NOTKEY        - The id is not a key id.
  1079. X *
  1080. X */
  1081. X
  1082. XFNCLASS d_keyfind(id, keyptr)
  1083. XId id;
  1084. Xvoid *keyptr;
  1085. X{
  1086. X    Key *key;
  1087. X    int rc;
  1088. X
  1089. X    /* Make sure that a database is open */
  1090. X    if( CURR_DB == -1 )
  1091. X        RETURN_RAP(S_NOCD);
  1092. X
  1093. X    /* Determine whether this id is a key id or a compound key id */
  1094. X    if( id < REC_FACTOR )
  1095. X    {
  1096. X        if( id >= DB->header.keys )
  1097. X            RETURN_RAP(S_NOTKEY);
  1098. X
  1099. X        key = &DB->key[id];
  1100. X
  1101. X        CURR_RECID = DB->field[ DB->keyfield[ key->first_keyfield ].field ].recid;
  1102. X    }
  1103. X    else
  1104. X    {
  1105. X        Field *fld;
  1106. X
  1107. X        if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY )
  1108. X            return rc;
  1109. X
  1110. X        if( !(fld->type & FT_KEY) )
  1111. X            RETURN_RAP(S_NOTKEY);
  1112. X
  1113. X        key = &DB->key[ fld->keyid ];
  1114. X    }
  1115. X
  1116. X    ty_lock();
  1117. X
  1118. X    CURR_KEY = key - DB->key;
  1119. X
  1120. X    rc = ty_keyfind(key, keyptr, &CURR_REC);
  1121. X
  1122. X    ty_unlock();
  1123. X
  1124. X    RETURN rc;
  1125. X}
  1126. X
  1127. X
  1128. X
  1129. X/*-------------------------------- d_keymove -------------------------------*\
  1130. X *
  1131. X * Purpose     : Perform a d_keyfrst(), d_keylast(), d_keyprev() or d_keylast()
  1132. X *               on an index. This function is called the macros defined in
  1133. X *               typhoon.h.
  1134. X *
  1135. X * Parameters: id            - Field id.
  1136. X *               direction    - 0 = next, 1 = prev, 2 = first, 3 = last
  1137. X *
  1138. X * Returns     : S_OKAY        - Operation performed successfully.
  1139. X *               S_NOTFOUND    - Not found.
  1140. X *               S_NOTKEY        - The id is not a key.
  1141. X *               S_NOCD        - No current database.
  1142. X *
  1143. X */
  1144. X
  1145. Xstatic d_keymove(id, direction)
  1146. XId id;
  1147. Xint direction;
  1148. X{
  1149. X    static int (*movefunc[])_PRM((Key *, ulong *)) =
  1150. X        { ty_keynext, ty_keyprev, ty_keyfrst, ty_keylast };
  1151. X    Key *key;
  1152. X    int rc;
  1153. X
  1154. X    if( CURR_DB == -1 )
  1155. X        RETURN_RAP(S_NOCD);
  1156. X
  1157. X    /* Determine whether this id is a key id or a compound key id */
  1158. X    if( id < REC_FACTOR )
  1159. X    {
  1160. X        if( id >= DB->header.keys )
  1161. X            RETURN_RAP(S_NOTKEY);
  1162. X
  1163. X        key = &DB->key[id];
  1164. X
  1165. X        CURR_RECID = DB->field[ DB->keyfield[ key->first_keyfield ].field ].recid;
  1166. X    }
  1167. X    else
  1168. X    {
  1169. X        Field *fld;
  1170. X
  1171. X        if( (rc = set_recfld(id, NULL, &fld)) != S_OKAY )
  1172. X            return rc;
  1173. X
  1174. X        if( !(fld->type & FT_KEY) )
  1175. X            RETURN_RAP(S_NOTKEY);
  1176. X
  1177. X        key = &DB->key[ fld->keyid ];
  1178. X    }
  1179. X
  1180. X    ty_lock();
  1181. X    CURR_KEY = key - DB->key;
  1182. X    rc = (*movefunc[direction])(key, &CURR_REC);
  1183. X    ty_unlock();
  1184. X
  1185. X    RETURN rc;
  1186. X}
  1187. X
  1188. X
  1189. XFNCLASS d_keyfrst(field)
  1190. Xulong field;
  1191. X{
  1192. X    RETURN d_keymove(field, 2);
  1193. X}
  1194. X
  1195. XFNCLASS d_keylast(field)
  1196. Xulong field;
  1197. X{
  1198. X    RETURN d_keymove(field, 3);
  1199. X}
  1200. X
  1201. XFNCLASS d_keynext(field)
  1202. Xulong field;
  1203. X{
  1204. X    RETURN d_keymove(field, 0);
  1205. X}
  1206. X
  1207. XFNCLASS d_keyprev(field)
  1208. Xulong field;
  1209. X{
  1210. X    RETURN d_keymove(field, 1);
  1211. X}
  1212. X
  1213. X
  1214. X/*-------------------------------- d_recmove -------------------------------*\
  1215. X *
  1216. X * Purpose     : Perform a d_recnext(), d_recprev(), d_recfrst() or d_reclast()
  1217. X *               on a data file.
  1218. X *
  1219. X * Parameters: record        - Record id.
  1220. X *               direction    - 0 = next, 1 = prev, 2 = first, 3 = last
  1221. X *
  1222. X * Returns     : S_OKAY        - Operation performed successfully.
  1223. X *               S_NOTFOUND    - Not found.
  1224. X *               S_NOCD        - No current database.
  1225. X *
  1226. X */
  1227. X
  1228. Xd_recmove(record, direction)
  1229. XId record;
  1230. Xint direction;
  1231. X{
  1232. X    static int (*movefunc[])_PRM((Record *, void *)) =
  1233. X        { ty_recnext, ty_recprev, ty_recfrst, ty_reclast };
  1234. X    Record *rec;
  1235. X    int rc;
  1236. X
  1237. X    if( (rc = set_recfld(record, &rec, NULL)) != S_OKAY )
  1238. X        return rc;
  1239. X
  1240. X    ty_lock();
  1241. X    if( (rc = (*movefunc[direction])(rec, DB->recbuf)) == S_OKAY )
  1242. X    {
  1243. X        ty_reccurr(rec, &CURR_REC);
  1244. X        CURR_BUFREC = CURR_REC;                /* Mark buffer as updated        */
  1245. X    }
  1246. X    else
  1247. X        CURR_REC = 0;
  1248. X    ty_unlock();
  1249. X
  1250. X    RETURN rc;
  1251. X}
  1252. X
  1253. X
  1254. XFNCLASS d_recfrst(record)
  1255. Xulong record;
  1256. X{
  1257. X    RETURN d_recmove(record, 2);
  1258. X}
  1259. X
  1260. XFNCLASS d_reclast(record)
  1261. Xulong record;
  1262. X{
  1263. X    RETURN d_recmove(record, 3);
  1264. X}
  1265. X
  1266. XFNCLASS d_recnext(record)
  1267. Xulong record;
  1268. X{
  1269. X    RETURN d_recmove(record, 0);
  1270. X}
  1271. X
  1272. XFNCLASS d_recprev(record)
  1273. Xulong record;
  1274. X{
  1275. X    RETURN d_recmove(record, 1);
  1276. X}
  1277. X
  1278. X
  1279. X
  1280. X/*-------------------------------- d_crread --------------------------------*\
  1281. X *
  1282. X * Purpose     : Read the contents of a field of the current record.
  1283. X *
  1284. X * Parameters: field        - Field id.
  1285. X *               buf            - Pointer to buffer where field will be stored.
  1286. X *
  1287. X * Returns     : S_OKAY        - Operation performed successfully.
  1288. X *               S_NOCD        - No current database.
  1289. X *               S_NOCR        - No current record.
  1290. X *               S_DELETED    - The record has been deleted.
  1291. X *
  1292. X */
  1293. X
  1294. XFNCLASS d_crread(field, buf)
  1295. XId field;
  1296. Xvoid *buf;
  1297. X{
  1298. X    Record *rec;
  1299. X    Field *fld;
  1300. X    int size, rc;
  1301. X
  1302. X    if( (rc = set_recfld(field, &rec, &fld)) != S_OKAY )
  1303. X        return rc;
  1304. X
  1305. X    if( !CURR_REC )
  1306. X        RETURN_RAP(S_NOCR);
  1307. X
  1308. X    if( (rc = update_recbuf()) != S_OKAY )
  1309. X        return rc;
  1310. X
  1311. X    if( fld->type & FT_VARIABLE )
  1312. X        /* Get the value of the size field (stored in keyid) */
  1313. X        size = *(ushort *)((char *)buf + DB->field[ fld->keyid ].offset) * fld->elemsize;
  1314. X    else
  1315. X        size = fld->size;
  1316. X
  1317. X    memcpy(buf, (char *)DB->recbuf + fld->offset, size);
  1318. X
  1319. X    RETURN S_OKAY;
  1320. X}
  1321. X
  1322. X
  1323. X
  1324. X/*--------------------------------- d_crget --------------------------------*\
  1325. X *
  1326. X * Purpose     : Get the database of the current record.
  1327. X *
  1328. X * Parameters: addr        - Pointer to location where address will be stored.
  1329. X *
  1330. X * Returns     : S_OKAY    - Successful.
  1331. X *
  1332. X */
  1333. X
  1334. XFNCLASS d_crget(addr)
  1335. XDB_ADDR *addr;
  1336. X{
  1337. X    addr->recno = CURR_REC;
  1338. X    addr->recid = INTERN_TO_RECID(CURR_RECID);
  1339. X
  1340. X    RETURN S_OKAY;
  1341. X}
  1342. X
  1343. X
  1344. X
  1345. X/*--------------------------------- d_crset --------------------------------*\
  1346. X *
  1347. X * Purpose     : Set the database of the current record.
  1348. X *
  1349. X * Parameters: addr        - Pointer to address of new record.
  1350. X *
  1351. X * Returns     : S_OKAY    - Successful.
  1352. X *
  1353. X */
  1354. X
  1355. XFNCLASS d_crset(addr)
  1356. XDB_ADDR *addr;
  1357. X{
  1358. X    ulong recid = addr->recid;
  1359. X
  1360. X    recid = RECID_TO_INTERN(addr->recid);
  1361. X
  1362. X    /* Validate record id */
  1363. X    if( recid >= DB->header.records )
  1364. X        RETURN S_INVREC;
  1365. X
  1366. X    CURR_REC    = addr->recno;
  1367. X    CURR_RECID    = recid;
  1368. X
  1369. X    RETURN S_OKAY;
  1370. X}
  1371. X
  1372. X
  1373. X
  1374. X/*-------------------------------- d_records -------------------------------*\
  1375. X *
  1376. X * Purpose     : Return the number of records in a file.
  1377. X *
  1378. X * Parameters: record    - Record id.
  1379. X *               number    - Pointer to location where the value will be stored.
  1380. X *
  1381. X * Returns     : S_OKAY    - Number of records stored in <number>.
  1382. X *               S_NOCD    - No current database.
  1383. X *               S_INVREC    - Invalid record id.
  1384. X *
  1385. X */
  1386. X
  1387. XFNCLASS d_records(record, number)
  1388. XId record;
  1389. Xulong *number;
  1390. X{
  1391. X    Record *rec;
  1392. X    int rc;
  1393. X
  1394. X    if( (rc = set_recfld(record, &rec, NULL)) != S_OKAY )
  1395. X        return rc;
  1396. X
  1397. X    RETURN ty_reccount(rec, number);
  1398. X}
  1399. X
  1400. X
  1401. XFNCLASS d_seterrfn(fn)
  1402. Xvoid (*fn) PRM( (int, long); )
  1403. X{
  1404. X    typhoon.ty_errfn = fn;
  1405. X}
  1406. X
  1407. X
  1408. X/* end-of-file */
  1409. END_OF_FILE
  1410.   if test 10221 -ne `wc -c <'typhoon/src/ty_find.c'`; then
  1411.     echo shar: \"'typhoon/src/ty_find.c'\" unpacked with wrong size!
  1412.   fi
  1413.   # end of 'typhoon/src/ty_find.c'
  1414. fi
  1415. if test -f 'typhoon/src/ty_refin.c' -a "${1}" != "-c" ; then 
  1416.   echo shar: Will not clobber existing file \"'typhoon/src/ty_refin.c'\"
  1417. else
  1418.   echo shar: Extracting \"'typhoon/src/ty_refin.c'\" \(9607 characters\)
  1419.   sed "s/^X//" >'typhoon/src/ty_refin.c' <<'END_OF_FILE'
  1420. X/*----------------------------------------------------------------------------
  1421. X * File    : bt_refin
  1422. X * Library : typhoon
  1423. X * OS      : UNIX, OS/2, DOS
  1424. X * Author  : Thomas B. Pedersen
  1425. X *
  1426. X * Copyright (c) 1994 Thomas B. Pedersen.  All rights reserved.
  1427. X *
  1428. X * Permission is hereby granted, without written agreement and without
  1429. X * license or royalty fees, to use, copy, modify, and distribute this
  1430. X * software and its documentation for any purpose, provided that the above
  1431. X * copyright notice and the following two  paragraphs appear (1) in all 
  1432. X * source copies of this software and (2) in accompanying documentation
  1433. X * wherever the programatic interface of this software, or any derivative
  1434. X * of it, is described.
  1435. X *
  1436. X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
  1437. X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
  1438. X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN 
  1439. X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1440. X *
  1441. X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
  1442. X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  1443. X * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 
  1444. X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
  1445. X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  1446. X *
  1447. X * Description:
  1448. X *   This file contains function for handling referential integrity.
  1449. X *
  1450. X * Functions:
  1451. X *   update_foreign_keys
  1452. X *   check_foreign_keys
  1453. X *   delete_foreign_keys
  1454. X *   check_dependent_tables
  1455. X *
  1456. X * $Log: ty_refin.c,v $
  1457. X * Revision 1.2  1994/09/17  16:00:19  tbp
  1458. X * typhoon.h and environ.h are now included from <>.
  1459. X *
  1460. X * Revision 1.1  1994/09/13  21:28:37  tbp
  1461. X * Added to repository.
  1462. X * 
  1463. X *
  1464. X *--------------------------------------------------------------------------*/
  1465. X
  1466. Xstatic char rcsid[] = "$Id: ty_refin.c,v 1.2 1994/09/17 16:00:19 tbp Exp $";
  1467. X
  1468. X#include <stdio.h>
  1469. X#include <string.h>
  1470. X#include <typhoon.h>
  1471. X#include "ty_dbd.h"
  1472. X#include "ty_type.h"
  1473. X#include "ty_glob.h"
  1474. X#include "ty_prot.h"
  1475. X
  1476. X
  1477. X/*--------------------------- Function prototypes --------------------------*/
  1478. X
  1479. X/*---------------------------- Global variables ----------------------------*/
  1480. X/* The ca[] table is a Communications Area used to pass information between
  1481. X   check_foreign_keys() and modify_refentries(). Because foreign keys
  1482. X   are modified in two steps, this table is necessary
  1483. X*/
  1484. Xstruct {
  1485. X    ulong        ref_file;        /* Id of reference file. 0=skip this entry    */
  1486. X    ulong        del_parent;        /* If a foreign key has been changed this    */
  1487. X                                /* entry contains the recno of the old         */
  1488. X                                /* parent. This should be used to remove     */
  1489. X                                /* the old. REFENTRY. 0=no parent to delete    */
  1490. X    uchar        null;            /* Determines whether a reference should be    */
  1491. X                                /* inserted, e.g. the key is not null        */
  1492. X} ca[RECKEYS_MAX];
  1493. X
  1494. X
  1495. X
  1496. X
  1497. X/*--------------------------- update_foreign_keys --------------------------*\
  1498. X *
  1499. X * Purpose     : This function is called after check_foreign_keys has set up 
  1500. X *               the communications area (ca) with the foreign keys that should
  1501. X *               be updated, removed or added.
  1502. X *
  1503. X * Parameters: rec            - Pointer to record.
  1504. X *               is_new        - Called by d_fillnew() or d_recwrite().
  1505. X *
  1506. X * Returns     : Nothing.
  1507. X *
  1508. X */
  1509. Xvoid update_foreign_keys(rec, is_new)
  1510. XRecord *rec;
  1511. Xint is_new;
  1512. X{
  1513. X    int n;
  1514. X    Key key;
  1515. X    REF_ENTRY refentry;
  1516. X
  1517. X    /* If the record has no foreign keys we'll just return now */
  1518. X    if( rec->first_foreign == -1 )
  1519. X        return;
  1520. X
  1521. X    key.size = sizeof(REF_ENTRY);
  1522. X    n         = rec->keys - (rec->first_foreign - rec->first_key);
  1523. X
  1524. X    refentry.dependent.recid = CURR_RECID;
  1525. X    refentry.dependent.recno = CURR_REC;
  1526. X
  1527. X    while( n-- )
  1528. X    {
  1529. X        if( ca[n].ref_file )
  1530. X        {
  1531. X            key.fileid = ca[n].ref_file;
  1532. X
  1533. X            if( !is_new )
  1534. X            {
  1535. X                if( refentry.parent = ca[n].del_parent )
  1536. X                    ty_keydel(&key, &refentry, CURR_REC);
  1537. X            }
  1538. X
  1539. X            if( !ca[n].null )
  1540. X            {
  1541. X                refentry.parent = ((ulong *)DB->real_recbuf)[n];
  1542. X                ty_keyadd(&key, &refentry, CURR_REC);
  1543. X            }
  1544. X        }
  1545. X    }
  1546. X
  1547. X}
  1548. X
  1549. X
  1550. X
  1551. X/*--------------------------- check_foreign_keys ---------------------------*\
  1552. X *
  1553. X * Purpose     : This function checks whether the foreign keys of a record
  1554. X *               exist in its parent tables.
  1555. X *
  1556. X *               First the existence of all foreign keys are checked. Only
  1557. X *               the necessary checks are performed. If is_new all foreign
  1558. X *               keys are checked, otherwise only foreign keys which have
  1559. X *               changed are checked. Null keys are never checked.
  1560. X *
  1561. X *             The record numbers of the parents record are stored in the
  1562. X *               preamble. For each entry in the preamble, the corresponding
  1563. X *               entries in save_ref[] and old_preamble[] contains the file id
  1564. X *               of the parent's reference file and the old preamble value,
  1565. X *               respectively.
  1566. X *
  1567. X * Parameters: rec            - Pointer to record.
  1568. X *               buf            - Buffer of dependent record.
  1569. X *               new            - Is this record being created? This is set to 1
  1570. X *                              d_fillnew().
  1571. X *
  1572. X * Returns     : S_OKAY        - All foreign keys existed.
  1573. X *               S_NOTFOUND    - A foreign did not exist. db_subcode contains the
  1574. X *                              ID of the parent table.
  1575. X */
  1576. Xcheck_foreign_keys(rec, buf, is_new)
  1577. XRecord *rec;
  1578. Xvoid *buf;
  1579. Xint is_new;
  1580. X{
  1581. X    Key *key;
  1582. X    int n;
  1583. X    int foreign_keys;
  1584. X    ulong ref;
  1585. X
  1586. X    /* If the record has no foreign keys we'll just return now */
  1587. X    if( rec->first_foreign == -1 )
  1588. X        return S_OKAY;
  1589. X
  1590. X    key             = DB->key + rec->first_foreign;
  1591. X    foreign_keys = rec->keys - (rec->first_foreign - rec->first_key);
  1592. X
  1593. X    /* If this is a new record the preamble must be cleared */
  1594. X    if( is_new )
  1595. X        memset(DB->real_recbuf, 0, foreign_keys * sizeof(ulong));
  1596. X
  1597. X    for( n=0; KEY_ISFOREIGN(key) && n < foreign_keys; n++, key++ )
  1598. X    {
  1599. X        Key *primary_key = DB->key + DB->record[key->parent].first_key;
  1600. X
  1601. X        ca[n].null = 0;
  1602. X
  1603. X        if( is_new || reckeycmp(key, buf, DB->recbuf) )
  1604. X        {
  1605. X            if( KEY_ISOPTIONAL(key) )
  1606. X            {
  1607. X                ca[n].ref_file = DB->record[key->parent].ref_file;
  1608. X
  1609. X                /* If the record is being created or the old value was null
  1610. X                 * no key need to be deleted
  1611. X                 */
  1612. X                if( is_new || null_indicator(key, DB->recbuf) )
  1613. X                    ca[n].del_parent = 0;
  1614. X                else
  1615. X                    ca[n].del_parent = ((ulong *)DB->real_recbuf)[n];
  1616. X                    
  1617. X                if( null_indicator(key, buf) )
  1618. X                {
  1619. X                    ca[n].null = 1;
  1620. X                    continue;
  1621. X                }
  1622. X            }
  1623. X
  1624. X            CURR_REC = primary_key - DB->key;
  1625. X
  1626. X            if( ty_keyfind(primary_key, set_keyptr(key, buf), &ref) != S_OKAY )
  1627. X            {
  1628. X                db_subcode = (key->parent+1) * REC_FACTOR;
  1629. X                RETURN S_FOREIGN;
  1630. X            }
  1631. X
  1632. X            ca[n].del_parent = ((ulong *)DB->real_recbuf)[n];
  1633. X            ca[n].ref_file   = DB->record[key->parent].ref_file;
  1634. X
  1635. X            /* Store the reference to the parent record in the preamble */
  1636. X            ((ulong *)DB->real_recbuf)[n] = ref;
  1637. X        }
  1638. X        else
  1639. X            ca[n].ref_file = 0;
  1640. X    }
  1641. X
  1642. X    return S_OKAY;
  1643. X}
  1644. X
  1645. X
  1646. X
  1647. X/*-------------------------- delete_foreign_keys ---------------------------*\
  1648. X *
  1649. X * Purpose     : This function removes all refentries from a dependent record's
  1650. X *               parent reference files.
  1651. X *
  1652. X * Parameters: rec            - Pointer to record.
  1653. X *
  1654. X * Returns     : Nothing.
  1655. X *
  1656. X */
  1657. Xvoid delete_foreign_keys(rec)
  1658. XRecord *rec;
  1659. X{
  1660. X    Key *key, refkey;
  1661. X    REF_ENTRY refentry;
  1662. X    int n;
  1663. X    int foreign_keys;
  1664. X
  1665. X    /* If the record has no foreign keys we'll just return now */
  1666. X    if( rec->first_foreign == -1 )
  1667. X        return;
  1668. X
  1669. X    key             = DB->key + rec->first_foreign;
  1670. X    foreign_keys = rec->keys - (rec->first_foreign - rec->first_key);
  1671. X    refkey.size     = sizeof(REF_ENTRY);
  1672. X
  1673. X    refentry.dependent.recid    = CURR_RECID;
  1674. X    refentry.dependent.recno    = CURR_REC;
  1675. X
  1676. X    for( n=0; KEY_ISFOREIGN(key) && n < foreign_keys; n++, key++ )
  1677. X    {
  1678. X        if( !(key->type & KT_OPTIONAL) || !null_indicator(key, DB->recbuf) )
  1679. X        {
  1680. X            refentry.parent = ((ulong *)DB->real_recbuf)[n];
  1681. X            refkey.fileid   = DB->record[key->parent].ref_file;
  1682. X
  1683. X            ty_keydel(&refkey, &refentry, CURR_REC);
  1684. X        }
  1685. X    }
  1686. X}
  1687. X
  1688. X
  1689. X
  1690. X/*------------------------- check_dependent_tables -------------------------*\
  1691. X *
  1692. X * Purpose     : This function is called by d_fillnew() and d_delete() to check
  1693. X *               whether this update/delete operation will have any effect on
  1694. X *               records in dependent tables.
  1695. X *
  1696. X * Parameters: parent        - Pointer to parent table's record pointer.
  1697. X *               buf            - Buffer of dependent record.
  1698. X *               for_action    - 'd'=delete, 'u'=update
  1699. X *
  1700. X * Returns     : S_OKAY        - No conflicts.
  1701. X *               S_RESTRICT    - A dependent table with restrict rule has rows
  1702. X *                            which referenced the parent table. db_subcode
  1703. X *                              contains the ID of the dependent table.
  1704. X *
  1705. X */
  1706. Xcheck_dependent_tables(parent, buf, for_action)
  1707. XRecord *parent;
  1708. Xvoid *buf;
  1709. Xint for_action;
  1710. X{
  1711. X    Key *primary_key, refkey;
  1712. X    REF_ENTRY refentry;
  1713. X    ulong ref;
  1714. X    int rc;
  1715. X
  1716. X    /* Does this table have any dependent tables? */
  1717. X    if( !parent->dependents )
  1718. X        return S_OKAY;
  1719. X
  1720. X    /* If the table has dependent tables, it follows that it must also have
  1721. X     * a primary key, i.e. no need to check for that.
  1722. X     */
  1723. X    primary_key = DB->key + parent->first_key;
  1724. X
  1725. X    /* Only perform check if the primary key has changed (if update) */
  1726. X    if( for_action == 'u' )
  1727. X        if( !reckeycmp(primary_key, buf, DB->recbuf) )
  1728. X            return S_OKAY;
  1729. X
  1730. X    refentry.parent = CURR_REC;
  1731. X    refentry.dependent.recid = 0;
  1732. X    refentry.dependent.recno = 0;
  1733. X
  1734. X    refkey.size      = sizeof(REF_ENTRY);
  1735. X    refkey.fileid = parent->ref_file;
  1736. X
  1737. X    if( (rc = ty_keyfind(&refkey, &refentry, &ref)) != S_OKAY )
  1738. X        rc = ty_keynext(&refkey, &ref);
  1739. X
  1740. X    if( rc != S_OKAY )
  1741. X        return S_OKAY;
  1742. X
  1743. X    refentry = *(REF_ENTRY *)CURR_KEYBUF;
  1744. X
  1745. X    if( refentry.parent == CURR_REC )
  1746. X    {
  1747. X        /* Set db_subcode to the ID of the dependent table */
  1748. X        db_subcode = (refentry.dependent.recid+1) * REC_FACTOR;
  1749. X        RETURN S_RESTRICT;
  1750. X    }
  1751. X
  1752. X    return S_OKAY;
  1753. X}
  1754. X
  1755. X/* end-of-file */
  1756. END_OF_FILE
  1757.   if test 9607 -ne `wc -c <'typhoon/src/ty_refin.c'`; then
  1758.     echo shar: \"'typhoon/src/ty_refin.c'\" unpacked with wrong size!
  1759.   fi
  1760.   # end of 'typhoon/src/ty_refin.c'
  1761. fi
  1762. if test -f 'typhoon/src/unix.c' -a "${1}" != "-c" ; then 
  1763.   echo shar: Will not clobber existing file \"'typhoon/src/unix.c'\"
  1764. else
  1765.   echo shar: Extracting \"'typhoon/src/unix.c'\" \(5678 characters\)
  1766.   sed "s/^X//" >'typhoon/src/unix.c' <<'END_OF_FILE'
  1767. X/*----------------------------------------------------------------------------
  1768. X * File    : unix.c
  1769. X * Library : typhoon
  1770. X * OS      : UNIX, OS/2, DOS
  1771. X * Author  : Thomas B. Pedersen
  1772. X *
  1773. X * Copyright (c) 1994 Thomas B. Pedersen.  All rights reserved.
  1774. X *
  1775. X * Permission is hereby granted, without written agreement and without
  1776. X * license or royalty fees, to use, copy, modify, and distribute this
  1777. X * software and its documentation for any purpose, provided that the above
  1778. X * copyright notice and the following two  paragraphs appear (1) in all 
  1779. X * source copies of this software and (2) in accompanying documentation
  1780. X * wherever the programatic interface of this software, or any derivative
  1781. X * of it, is described.
  1782. X *
  1783. X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
  1784. X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
  1785. X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN 
  1786. X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1787. X *
  1788. X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
  1789. X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  1790. X * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 
  1791. X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
  1792. X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  1793. X *
  1794. X * Description:
  1795. X *   Contiains functions specific to UNIX.
  1796. X *
  1797. X * Functions:
  1798. X *   ty_openlock    - Create/open the locking resource.
  1799. X *   ty_closelock    - Close the locking resource.
  1800. X *   ty_lock        - Obtain the lock.
  1801. X *   ty_unlock        - Release the lock.
  1802. X *
  1803. X * $Log: unix.c,v $
  1804. X * Revision 1.3  1994/09/17  16:21:13  tbp
  1805. X * /tmp/typhoonsem was created even though SEMLOCK was defined.
  1806. X *
  1807. X * Revision 1.2  1994/09/17  16:00:22  tbp
  1808. X * typhoon.h and environ.h are now included from <>.
  1809. X *
  1810. X * Revision 1.1  1994/09/13  21:28:39  tbp
  1811. X * Added to repository.
  1812. X * 
  1813. X *
  1814. X *--------------------------------------------------------------------------*/
  1815. X
  1816. Xstatic char rcsid[] = "$Id: unix.c,v 1.3 1994/09/17 16:21:13 tbp Exp $";
  1817. X
  1818. X#include <sys/types.h>
  1819. X#include <sys/ipc.h>
  1820. X#include <sys/sem.h>
  1821. X#include <sys/shm.h>
  1822. X#include <unistd.h>
  1823. X#include <fcntl.h>
  1824. X#include <stdio.h>
  1825. X#include <errno.h>
  1826. X#include <typhoon.h>
  1827. X#include "ty_dbd.h"
  1828. X#include "ty_type.h"
  1829. X
  1830. X#define SEMLOCK
  1831. X
  1832. X#define TIMEOUT        10000L        /* Wait maximum 10 seconds for excl. access    */
  1833. X
  1834. X/*---------------------------- Global variables ----------------------------*/
  1835. Xstatic struct sembuf sem_wait_buf[2] = {
  1836. X    0, 0, 0,                        /* wait for sem to become 0            */
  1837. X    0, 1, SEM_UNDO                    /* then increment sem by 1            */
  1838. X};
  1839. X
  1840. Xstatic struct sembuf sem_clear_buf[1] = {
  1841. X    0,-1, IPC_NOWAIT | SEM_UNDO,    /* decrease sem by 1                */
  1842. X};
  1843. X
  1844. X
  1845. Xstatic int     sem_id;
  1846. X
  1847. Xstatic int lock_fh = -1;
  1848. X
  1849. X/*------------------------------ ty_openlock  ------------------------------*\
  1850. X *
  1851. X * Purpose     : This function ensures that only one instance of a Typhoon
  1852. X *             function is active at the time, at least in its critical
  1853. X *             section.
  1854. X *
  1855. X * Parameters: None.
  1856. X *
  1857. X * Returns     : -1        - Semaphore could not be created.
  1858. X *               0        - Successful.
  1859. X *
  1860. X */
  1861. X
  1862. Xty_openlock()
  1863. X{
  1864. X#ifndef SEMLOCK
  1865. X    static char lockfname[] = "/tmp/typhoonsem";
  1866. X    int pid = getpid();
  1867. X
  1868. X    if( lock_fh == -1 )
  1869. X    {
  1870. X        if( (lock_fh = open(lockfname, O_RDWR|O_CREAT, 0666)) == -1 )    
  1871. X        {
  1872. X            printf("Cannot open %s\n", lockfname);
  1873. X            return -1;
  1874. X        }
  1875. X
  1876. X        write(lock_fh, &pid, sizeof pid);
  1877. X    }
  1878. X#endif
  1879. X    return 0;
  1880. X}
  1881. X
  1882. X
  1883. Xty_closelock()
  1884. X{
  1885. X    close(lock_fh);
  1886. X    lock_fh = -1;
  1887. X
  1888. X    return 0;
  1889. X}
  1890. X
  1891. X
  1892. Xty_lock()
  1893. X{
  1894. X#ifdef SEMLOCK
  1895. X#if 1
  1896. X    while( semop(sem_id, sem_wait_buf, 2) == -1 && errno == EINTR )
  1897. X        puts("ty_lock EAGAIN");
  1898. X#else
  1899. X    if( sem_wait(sem_id) == -1 )
  1900. X        puts("ty_lock failed");
  1901. X#endif
  1902. X#endif
  1903. X
  1904. X#ifndef SEMLOCK
  1905. X    lseek(lock_fh, 0, SEEK_SET);
  1906. X
  1907. X    while( lockf(lock_fh, F_LOCK, 1) == -1 )
  1908. X    {
  1909. X        if( errno != EINTR && errno != EAGAIN )    
  1910. X        {
  1911. X            printf("ty_lock failed (errno %d, lock_fh %d)\n", errno, lock_fh);
  1912. X            break;
  1913. X        }
  1914. X    }
  1915. X#endif
  1916. X}
  1917. X
  1918. X
  1919. Xty_unlock()
  1920. X{
  1921. X#ifdef SEMLOCK
  1922. X#if 1
  1923. X    while( semop(sem_id, sem_clear_buf, 1) == -1 && errno == EINTR )
  1924. X        puts("ty_unlock EAGAIN");
  1925. X#else
  1926. X    if( sem_clear(sem_id) == -1 )    
  1927. X        puts("ty_unlock failed");
  1928. X#endif
  1929. X#endif
  1930. X
  1931. X#ifndef SEMLOCK
  1932. X    lseek(lock_fh, 0, SEEK_SET);
  1933. X
  1934. X    while( lockf(lock_fh, F_ULOCK, 1) == -1 )
  1935. X    {
  1936. X        if( errno != EINTR && errno != EAGAIN )
  1937. X        {
  1938. X            printf("ty_unlock failed (errno %d, lock_fh %d)\n", errno, lock_fh);
  1939. X            break;
  1940. X        }
  1941. X    }
  1942. X#endif
  1943. X
  1944. X
  1945. X    return 0;
  1946. X}
  1947. X
  1948. X
  1949. X
  1950. X
  1951. Xshm_alloc(db)
  1952. XDbentry *db;
  1953. X{
  1954. X    char dbdname[128];
  1955. X    key_t key;
  1956. X    long flags = IPC_CREAT|0770;
  1957. X    int created = 0;
  1958. X
  1959. X    sprintf(dbdname, "%s.dbd", db->name);
  1960. X    key = ftok(dbdname, 30);
  1961. X    
  1962. X    if( (db->shm_id = shmget(key, sizeof(*db->shm), 0)) == -1 )
  1963. X        if( (db->shm_id = shmget(key, sizeof(*db->shm), flags)) == -1 )
  1964. X            return -1;
  1965. X        else
  1966. X            created = 1;
  1967. X#ifdef SEMLOCK
  1968. X#if 1
  1969. X    if( (sem_id = semget(key, 1, 0)) == -1 )
  1970. X        if( (sem_id = semget(key, 1, IPC_CREAT|0660)) == -1 )
  1971. X        {
  1972. X            shmdt((char *)db->shm);
  1973. X            if( created )
  1974. X                shmctl(db->shm_id, IPC_RMID, NULL);
  1975. X            return -1;
  1976. X        }
  1977. X#else
  1978. X    if( (sem_id = sem_open(key)) == -1 )
  1979. X        if( (sem_id = sem_create(key, 1)) == -1 )
  1980. X        {
  1981. X            shmdt((char *)db->shm);
  1982. X            if( created )
  1983. X                shmctl(db->shm_id, IPC_RMID, NULL);
  1984. X            return -1;
  1985. X        }
  1986. X#endif
  1987. X#endif
  1988. X
  1989. X    if( (db->shm = (SharedMemory *)shmat(db->shm_id,0,0)) == NULL )
  1990. X    {
  1991. X        if( created )
  1992. X            shmctl(db->shm_id, IPC_RMID, NULL);
  1993. X        return -1;
  1994. X    }
  1995. X
  1996. X    if( created )
  1997. X        memset(db->shm, 0, sizeof *db->shm);
  1998. X    db->shm->use_count++;
  1999. X
  2000. X    return 0;
  2001. X}
  2002. X
  2003. X
  2004. Xshm_free(db)
  2005. XDbentry *db;
  2006. X{
  2007. X    if( --db->shm->use_count == 0 )
  2008. X    {
  2009. X        shmdt((char *)db->shm);
  2010. X        shmctl(db->shm_id, IPC_RMID, NULL);
  2011. X#ifdef SEMLOCK
  2012. X        semctl(sem_id, 0, IPC_RMID, 0);
  2013. X#endif
  2014. X    }
  2015. X    else
  2016. X        shmdt((char *)db->shm);
  2017. X#if 0
  2018. X    sem_close(sem_id);
  2019. X#endif
  2020. X}
  2021. X
  2022. X
  2023. X/* end-of-file */
  2024. END_OF_FILE
  2025.   if test 5678 -ne `wc -c <'typhoon/src/unix.c'`; then
  2026.     echo shar: \"'typhoon/src/unix.c'\" unpacked with wrong size!
  2027.   fi
  2028.   # end of 'typhoon/src/unix.c'
  2029. fi
  2030. if test -f 'typhoon/src/util/export.c' -a "${1}" != "-c" ; then 
  2031.   echo shar: Will not clobber existing file \"'typhoon/src/util/export.c'\"
  2032. else
  2033.   echo shar: Extracting \"'typhoon/src/util/export.c'\" \(9427 characters\)
  2034.   sed "s/^X//" >'typhoon/src/util/export.c' <<'END_OF_FILE'
  2035. X/*----------------------------------------------------------------------------
  2036. X * File    : export.c
  2037. X * Program : tyexport
  2038. X * OS      : UNIX, OS/2, DOS
  2039. X * Author  : Thomas B. Pedersen
  2040. X *
  2041. X * Copyright (c) 1994 Thomas B. Pedersen.  All rights reserved.
  2042. X *
  2043. X * Permission is hereby granted, without written agreement and without
  2044. X * license or royalty fees, to use, copy, modify, and distribute this
  2045. X * software and its documentation for any purpose, provided that the above
  2046. X * copyright notice and the following two  paragraphs appear (1) in all 
  2047. X * source copies of this software and (2) in accompanying documentation
  2048. X * wherever the programatic interface of this software, or any derivative
  2049. X * of it, is described.
  2050. X *
  2051. X * IN NO EVENT SHALL THOMAS B. PEDERSEN BE LIABLE TO ANY PARTY FOR DIRECT,
  2052. X * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
  2053. X * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF HE HAS BEEN 
  2054. X * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  2055. X *
  2056. X * THOMAS B. PEDERSEN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
  2057. X * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  2058. X * A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 
  2059. X * BASIS, AND THOMAS B. PEDERSEN HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
  2060. X * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  2061. X *
  2062. X * Description:
  2063. X *   Typhoon export utility.
  2064. X *
  2065. X * $Log: export.c,v $
  2066. X * Revision 1.2  1994/09/17  16:00:52  tbp
  2067. X * typhoon.h and environ.h are now included from <>.
  2068. X *
  2069. X * Revision 1.1  1994/09/13  21:28:55  tbp
  2070. X * Added to repository.
  2071. X *
  2072. X * Added to repository.
  2073. X * 
  2074. X *
  2075. X *--------------------------------------------------------------------------*/
  2076. X
  2077. Xstatic char rcsid[] = "$Id: export.c,v 1.2 1994/09/17 16:00:52 tbp Exp $";
  2078. X
  2079. X#include <stdio.h>
  2080. X#include <string.h>
  2081. X#include <ctype.h>
  2082. X#include <fcntl.h>
  2083. X#include <stdarg.h>
  2084. X#ifndef UNIX
  2085. X#  include <sys\types.h>
  2086. X#  include <sys\stat.h>
  2087. X#  include <stdlib.h>
  2088. X#  include <io.h>
  2089. X#endif
  2090. X#define DEFINE_GLOBALS
  2091. X#include <typhoon.h>
  2092. X#include "../ty_dbd.h"
  2093. X#include "../ty_type.h"
  2094. X#include "export.h"
  2095. X
  2096. X
  2097. X/*-------------------------------- prototypes ------------------------------*/
  2098. Xstatic void PrintString        PRM( (uchar *, Field *fld); )
  2099. Xstatic void    PrintField        PRM( (Field *, unsigned); )
  2100. Xstatic int    GetControlField PRM( (Structdef *, unsigned); )
  2101. Xstatic int    PrintFields        PRM( (Structdef *, int, unsigned, int); )
  2102. Xstatic void    Export            PRM( (char *); )
  2103. X       int    yyparse            PRM( (void); )
  2104. X
  2105. X/*------------------------------ public variables --------------------------*/
  2106. Xint dbdfile;
  2107. Xint nocomma=0;
  2108. Xint nonull=0;
  2109. Xchar *recbuf;
  2110. XFILE *outfile;
  2111. XFILE *lex_file;
  2112. X
  2113. X/*------------------------------ local variables ---------------------------*/
  2114. Xstatic char paramhelp[] = "\
  2115. XSyntax: tyexport [option]... database[.dbd]\n\
  2116. XOptions:\n\
  2117. X    -f<path>    Specify data files path\n\
  2118. X    -g          Generate export specification\n\
  2119. X    -n          Strings are not null-terminated\n";
  2120. X
  2121. X
  2122. X
  2123. Xistrcmp(s1, s2)
  2124. Xchar *s1, *s2;
  2125. X{
  2126. X    while( tolower(*s1) == tolower(*s2) && *s1 && *s2 )
  2127. X        s1++, s2++;
  2128. X    
  2129. X    return tolower(*s1) - tolower(*s2);
  2130. X}
  2131. X
  2132. X
  2133. X
  2134. X
  2135. X#ifdef PROTOTYPES
  2136. Xvoid err_quit(char *s, ...)
  2137. X#else
  2138. Xvoid err_quit(s)
  2139. Xchar *s;
  2140. X#endif
  2141. X{
  2142. X    va_list ap;
  2143. X    
  2144. X    va_start(ap, s);
  2145. X    vfprintf(stderr, s, ap);
  2146. X    puts("");
  2147. X    va_end(ap);
  2148. X    exit(1);
  2149. X}
  2150. X
  2151. Xstatic void PrintString(s, fld)
  2152. Xuchar *s;
  2153. XField *fld;
  2154. X{
  2155. X    int len;
  2156. X
  2157. X    if( fld->type & FT_VARIABLE )
  2158. X        len = *(ushort *)(recbuf + dbd.field[ fld->keyid ].offset) * fld->elemsize;
  2159. X    else
  2160. X        len = fld->size;
  2161. X
  2162. X    putc('"', outfile);
  2163. X
  2164. X    while( len-- )
  2165. X    {
  2166. X        if( isprint(*s) )
  2167. X        {
  2168. X            if( *s == '"' )
  2169. X                fputs("\\\"", outfile);
  2170. X            else
  2171. X            if( *s == '\\' )
  2172. X                fputs("\\\\", outfile);
  2173. X            else
  2174. X                putc(*s, outfile);
  2175. X        }
  2176. X        else
  2177. X        {
  2178. X            if( !*s && !nonull )
  2179. X                break;
  2180. X
  2181. X            fprintf(outfile, "\\x%02X", *s);
  2182. X        }
  2183. X        s++;
  2184. X    }
  2185. X    putc('"', outfile);
  2186. X}
  2187. X
  2188. X
  2189. Xstatic void PrintField(fld, offset)
  2190. XField *fld;
  2191. Xunsigned offset;
  2192. X{
  2193. X    void *ptr = (void *)(recbuf + offset);
  2194. X
  2195. X    /*
  2196. X       Check for nonprintable characters in strings and character constants
  2197. X    */
  2198. X
  2199. X    if( nocomma )
  2200. X        nocomma = 0;
  2201. X    else
  2202. X        fprintf(outfile, ", ");
  2203. X
  2204. X    if( fld->type & FT_UNSIGNED )
  2205. X    {
  2206. X        switch( FT_GETBASIC(fld->type) )
  2207. X        {
  2208. X            case FT_CHARSTR:
  2209. X                PrintString(ptr, fld);
  2210. X                break;
  2211. X            case FT_CHAR:        
  2212. X                fprintf(outfile, "%u", (unsigned)*(uchar *)ptr);    
  2213. X                break;
  2214. X            case FT_SHORT:
  2215. X                fprintf(outfile, "%hu", *(ushort *)ptr);
  2216. X                break;
  2217. X            case FT_INT:
  2218. X                fprintf(outfile, "%u", *(unsigned *)ptr);
  2219. X                break;
  2220. X            case FT_LONG:
  2221. X                fprintf(outfile, "%lu", *(ulong *)ptr);
  2222. X                break;
  2223. X        }    
  2224. X    }
  2225. X    else
  2226. X        switch( FT_GETBASIC(fld->type) )
  2227. X        {
  2228. X            case FT_CHARSTR:
  2229. X                PrintString(ptr, fld);
  2230. X                break;
  2231. X            case FT_CHAR:
  2232. X                fprintf(outfile, "%d", (int)*(char *)ptr);
  2233. X                break;
  2234. X            case FT_SHORT:
  2235. X                fprintf(outfile, "%hd", *(short *)ptr);
  2236. X                break;
  2237. X            case FT_INT:
  2238. X                fprintf(outfile, "%d", *(int *)ptr);
  2239. X                break;
  2240. X            case FT_UNSIGNED:
  2241. X                fprintf(outfile, "%u", *(int *)ptr);
  2242. X                break;
  2243. X            case FT_LONG:
  2244. X                fprintf(outfile, "%ld", *(long *)ptr);
  2245. X                break;
  2246. X            case FT_FLOAT:
  2247. X                fprintf(outfile, "%f", *(float *)ptr);
  2248. X                break;
  2249. X            case FT_DOUBLE:
  2250. X                fprintf(outfile, "%f", *(double *)ptr);
  2251. X                break;
  2252. X        }    
  2253. X}
  2254. X
  2255. X
  2256. Xstatic GetControlField(str, offset)
  2257. XStructdef *str;
  2258. Xunsigned offset;
  2259. X{
  2260. X    unsigned id = recbuf[offset + dbd.field[str->control_field].offset];
  2261. X
  2262. X    if( id >= str->members )
  2263. X    {
  2264. X        printf("Setting invalid control filed to 0\n");
  2265. X        id = 0;
  2266. X    }
  2267. X
  2268. X    return id;
  2269. X}
  2270. X
  2271. X
  2272. Xstatic PrintFields(str, nest, offset, control_value)
  2273. XStructdef *str;
  2274. Xint nest;
  2275. Xunsigned offset;
  2276. X{
  2277. X    Field    *fld        = dbd.field + str->first_member;
  2278. X    int     fields        = str->members;
  2279. X    int     old_fields    = fields;
  2280. X    int     i, n, rc;
  2281. X
  2282. X    if( str->is_union )
  2283. X        fld += control_value;
  2284. X
  2285. X    while( fields-- )
  2286. X    {
  2287. X        if( fld->size != fld->elemsize && FT_GETBASIC(fld->type) != FT_CHARSTR )
  2288. X        {
  2289. X            if( fld->type & FT_VARIABLE )
  2290. X                n = *(ushort *)(recbuf + dbd.field[ fld->keyid ].offset);
  2291. X            else
  2292. X                n = fld->size / fld->elemsize;
  2293. X        }
  2294. X        else
  2295. X            n = 1;
  2296. X
  2297. X        for( i=0; i<n; i++ )
  2298. X        {
  2299. X            if( FT_GETBASIC(fld->type) == FT_STRUCT )
  2300. X            {
  2301. X                Structdef *struc = dbd.structdef + fld->structid;
  2302. X
  2303. X                if( !nocomma )
  2304. X                    fprintf(outfile, ", ");
  2305. X                if( fld->type & FT_INCLUDE )
  2306. X                    fprintf(outfile, "{ ");
  2307. X                nocomma = 1;
  2308. X
  2309. X                rc  = PrintFields(struc, nest+1, 
  2310. X                        offset + fld->offset + i * fld->elemsize,
  2311. X                         struc->is_union ? GetControlField(struc, offset) : 0);
  2312. X
  2313. X                if( fld->type & FT_INCLUDE )
  2314. X                    fprintf(outfile, " }");
  2315. X            }
  2316. X            else if( fld->nesting == nest && fld->type & FT_INCLUDE )
  2317. X                PrintField(fld, offset + fld->offset + fld->elemsize * i);
  2318. X        }
  2319. X
  2320. X        /* If n was 0 this array was a variable length array of size 0.
  2321. X         * Move fld to the next field at the same nesting.
  2322. X          */
  2323. X        if( n == 0 )
  2324. X        {
  2325. X            fprintf(outfile, ", { }");
  2326. X            rc = 0;
  2327. X
  2328. X            if( !fields )
  2329. X                break;
  2330. X
  2331. X            do
  2332. X                rc++;
  2333. X            while( fld[rc].nesting != fld->nesting );
  2334. X            rc--;
  2335. X        }
  2336. X
  2337. X
  2338. X        if( FT_GETBASIC(fld->type) == FT_STRUCT )
  2339. X        {
  2340. X               old_fields += rc;
  2341. X            fld += rc;
  2342. X        }
  2343. X
  2344. X        fld++;
  2345. X
  2346. X        if( str->is_union )
  2347. X            break;
  2348. X    }
  2349. X
  2350. X    return old_fields;
  2351. X}
  2352. X
  2353. X
  2354. Xstatic void ExportTable(recid)
  2355. Xulong recid;
  2356. X{
  2357. X    Record *rec = &dbd.record[recid];
  2358. X    Id keyid;
  2359. X
  2360. X    recid = INTERN_TO_RECID(recid);
  2361. X    keyid = rec->first_key;
  2362. X
  2363. X    for( d_keyfrst(keyid); db_status == S_OKAY; d_keynext(keyid) )
  2364. X    {
  2365. X         d_recread(recbuf);
  2366. X
  2367. X        nocomma = 1;
  2368. X        PrintFields(&dbd.structdef[rec->structid], 0, 0, 0);
  2369. X        fprintf(outfile, "\n");
  2370. X    }
  2371. X}
  2372. X
  2373. X
  2374. Xstatic void Export(dbname)
  2375. Xchar *dbname;
  2376. X{
  2377. X    char export_fname[256];
  2378. X    int i;
  2379. X
  2380. X    if( d_open(dbname, "s") != S_OKAY )
  2381. X        err_quit("Cannot open database '%s'", dbname);
  2382. X
  2383. X    for( i=0; i<dbd.header.records; i++ )
  2384. X    {
  2385. X        if( dbd.record[i].aux )
  2386. X        {
  2387. X            sprintf(export_fname, "%s.kom", dbd.record[i].name);
  2388. X
  2389. X             if( !(outfile = fopen(export_fname, "w")) )
  2390. X                err_quit("Cannot write to '%s'", export_fname);
  2391. X
  2392. X            printf("exporting to '%s'\n", export_fname);
  2393. X            ExportTable(i);
  2394. X            fclose(outfile);
  2395. X        }
  2396. X    }
  2397. X
  2398. X    d_close();
  2399. X}
  2400. X
  2401. X
  2402. X
  2403. Xmain(argc, argv)
  2404. Xchar *argv[];
  2405. X{
  2406. X    char *p, *realname;
  2407. X    int i;
  2408. X    long n=0, prev;
  2409. X    unsigned biggest_rec=0;
  2410. X
  2411. X    puts("Typhoon Export Utility version 1.05");
  2412. X
  2413. X    if( argc == 1 )
  2414. X    {
  2415. X        printf(paramhelp);
  2416. X        exit(1);
  2417. X    }
  2418. X
  2419. X    /* Extract the real name of the file */
  2420. X    if( (realname = strrchr(argv[argc-1], DIR_SWITCH)) != NULL )
  2421. X        realname++;
  2422. X    else
  2423. X        realname = argv[argc-1];
  2424. X
  2425. X    /* remove extension if present */
  2426. X    if( p = strstr(realname, ".") )
  2427. X        *p = 0;
  2428. X
  2429. X    /* generate file names for .ddl-file, .dbd-file and header file */
  2430. X    sprintf(dbd_fname,    "%s.dbd", realname);
  2431. X    sprintf(spec_fname, "%s.exp", realname);
  2432. X
  2433. X    if( read_dbdfile(&dbd, dbd_fname) != S_OKAY )
  2434. X        err_quit("Cannot open '%s'\n", dbd_fname);
  2435. X
  2436. X    /* Find the size of the biggest record */
  2437. X    for( i=0; i<dbd.header.records; i++ )
  2438. X        if( biggest_rec < dbd.record[i].size )
  2439. X            biggest_rec = dbd.record[i].size;
  2440. X
  2441. X    /* Allocate record buffer */
  2442. X    if( !(recbuf = (char *)malloc(biggest_rec)) )
  2443. X        err_quit("Out of memory");
  2444. X
  2445. X    /* process command line options */
  2446. X    for( i=1; i<argc-1; i++ )
  2447. X    {
  2448. X        if( argv[i][0] == '-' || argv[i][0] == '/' )
  2449. X        {
  2450. X            switch( argv[i][1] )
  2451. X            {
  2452. X                case 'f':
  2453. X                    d_dbfpath(argv[i]+2);
  2454. X                    break;
  2455. X                case 'g':
  2456. X                    GenerateExportSpec(realname);
  2457. X                    exit(1);
  2458. X                case 'n':
  2459. X                    nonull = 1;
  2460. X                    break;
  2461. X                default:
  2462. X                    err_quit("unknown command line option");
  2463. X            }
  2464. X        }
  2465. X        else
  2466. X            err_quit("unknown command line option");
  2467. X    }
  2468. X
  2469. X    /* Read the export specification */
  2470. X    ReadExportSpec(realname);
  2471. X
  2472. X    if( !errors )
  2473. X        Export(realname);
  2474. X
  2475. X    free(dbd.dbd);
  2476. X    free(recbuf);
  2477. X}
  2478. X
  2479. X/* end-of-file */
  2480. END_OF_FILE
  2481.   if test 9427 -ne `wc -c <'typhoon/src/util/export.c'`; then
  2482.     echo shar: \"'typhoon/src/util/export.c'\" unpacked with wrong size!
  2483.   fi
  2484.   # end of 'typhoon/src/util/export.c'
  2485. fi
  2486. echo shar: End of archive 4 \(of 9\).
  2487. cp /dev/null ark4isdone
  2488. MISSING=""
  2489. for I in 1 2 3 4 5 6 7 8 9 ; do
  2490.     if test ! -f ark${I}isdone ; then
  2491.     MISSING="${MISSING} ${I}"
  2492.     fi
  2493. done
  2494. if test "${MISSING}" = "" ; then
  2495.     echo You have unpacked all 9 archives.
  2496.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2497. else
  2498.     echo You still must unpack the following archives:
  2499.     echo "        " ${MISSING}
  2500. fi
  2501. exit 0
  2502. exit 0 # Just in case...
  2503.