home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume33 / mbase / part07 < prev    next >
Encoding:
Text File  |  1992-11-23  |  55.4 KB  |  1,642 lines

  1. Newsgroups: comp.sources.misc
  2. From: richid@owlnet.rice.edu (Richard Parvin Jernigan)
  3. Subject:  v33i125:  mbase - MetalBase 5.0, Portable database engine, Part07/08
  4. Message-ID: <1992Nov23.233051.9685@sparky.imd.sterling.com>
  5. X-Md4-Signature: 1aa82636e6b6f8f67fc49580123863d2
  6. Date: Mon, 23 Nov 1992 23:30:51 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: richid@owlnet.rice.edu (Richard Parvin Jernigan)
  10. Posting-number: Volume 33, Issue 125
  11. Archive-name: mbase/part07
  12. Environment: AMIGA, MS-DOS, HP-UX, XENIX, UNIX, ULTRIX, SGI, SU, Curses
  13. Supersedes: mbase: Volume 28, Issue 40-44
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then feed it
  17. # into a shell via "sh file" or similar.  To overwrite existing files,
  18. # type "sh file -c".
  19. # Contents:  dox/crypt.dox dox/flow.dox dox/format.dox dox/struct.dox
  20. #   dox/time.dox sample/bench.c sample/readme sample/sample.frm
  21. #   sample/sample.s src/blast.c src/cache.c src/makefile
  22. #   src/makefile.dos src/parse.c src/struct.c
  23. # Wrapped by kent@sparky on Mon Nov 23 16:33:15 1992
  24. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  25. echo If this archive is complete, you will see the following message:
  26. echo '          "shar: End of archive 7 (of 8)."'
  27. if test -f 'dox/crypt.dox' -a "${1}" != "-c" ; then 
  28.   echo shar: Will not clobber existing file \"'dox/crypt.dox'\"
  29. else
  30.   echo shar: Extracting \"'dox/crypt.dox'\" \(3346 characters\)
  31.   sed "s/^X//" >'dox/crypt.dox' <<'END_OF_FILE'
  32. XEncryption Method                                                 MetalBase 5.0
  33. X-------------------------------------------------------------------------------
  34. X
  35. X  -- Note: This information is provided for your information alone, and --
  36. X  -- is sufficient to crack the (rather simple) encryption method.  If  --
  37. X  --  you are going to be encrypting sensitive data, please delete or   --
  38. X  --               change the permissions on this file.                 --
  39. X
  40. XNote that the encryption is apparently working for ALL supported machines
  41. Xnow--however, this has always been the hardest part to port between machines.
  42. XIf necessary (or if you simply want to), compile mbase.c (and vr.c/sample.c/
  43. Xwhatever) with -DNOENCRYPT, and it'll be turned off.
  44. X
  45. X-------------------------------------------------------------------------------
  46. X
  47. XIf the user has specifed a nonzero key when calling mb_inc(), the following
  48. Xencryption operations take place at any necessary time before the next call
  49. Xto mb_rmv():
  50. X o  All records to be written are encrypted on a field-by-field basis
  51. X o  Records being read for return to the user are decrypted
  52. X o  Records being compared during an internal search are decrypted as needed
  53. X
  54. XIn this way, as long as mb_inc() is always called with the same key, programs
  55. Xwill never see any effect of encryption--records passed to MetalBase routines
  56. Xare not encrypted by the user, and records returned from these routines are
  57. Xalready decrypted.  However, the relation's data will be unintelligible if
  58. Xviewed using either an external file display utility or MetalBase routines
  59. Xwhich have not passed the proper key to mb_inc().
  60. X
  61. XThe key used for encryption is passed as an integer argument to mb_inc(),
  62. Xalong with the relation name which is to be opened.  If this key is 0, no
  63. Xencryption will take place for that session (note that already-encrypted
  64. Xinformation will not be decrypted without the proper key--passing 0 is simply
  65. Xan alternative to encrypting the relation in the first place).  To facilitate
  66. Xmatters, MetalBase 4.0 and up include the routine strtokey() to convert a text
  67. Xstring to a key for mb_inc().
  68. X
  69. XEncryption is performed as follows:
  70. X o During mb_inc(), the key passed in is converted to an 8-bit mask by
  71. X     shuffling the bits around:
  72. X        Bits 12345678  become bits  63417825
  73. X   Okay, so it's not really useful.  So it places limits on portability, and
  74. X   is generally ugly.  But hey--I always wanted an excuse to do some good
  75. X   bit-shuffling...
  76. X
  77. X o When a record is encrypted or decrypted, each field is handled
  78. X     individually.  This is because MB 4.0 originally used a memoried
  79. X     encryption method; i.e., a byte's mask was dependent on that of the byte
  80. X     before it.  Portability problems with uchars made a change to it before
  81. X     the original 4.0 release, but the style is still the same.  In the
  82. X     interests of speed, it is necessary to decrypt only indexed fields
  83. X     during an internal search; thus, each field must be a starting point for
  84. X     the algorithm.
  85. X   To encrypt a field consisting of N bytes (be they for longs, strings,
  86. X     or whatever):
  87. X
  88. X     mask = relation.mask; (set by MetalBase during mb_inc())
  89. X
  90. X     if (mask)
  91. X        for (i=0; i<N; i++)
  92. X         {
  93. X           byte[i] ^= mask;
  94. X           mask     = (mask+1) & (int)0xFF;
  95. X         }
  96. X
  97. X   Decryption uses the exact same routine.
  98. X
  99. END_OF_FILE
  100.   if test 3346 -ne `wc -c <'dox/crypt.dox'`; then
  101.     echo shar: \"'dox/crypt.dox'\" unpacked with wrong size!
  102.   fi
  103.   # end of 'dox/crypt.dox'
  104. fi
  105. if test -f 'dox/flow.dox' -a "${1}" != "-c" ; then 
  106.   echo shar: Will not clobber existing file \"'dox/flow.dox'\"
  107. else
  108.   echo shar: Extracting \"'dox/flow.dox'\" \(4341 characters\)
  109.   sed "s/^X//" >'dox/flow.dox' <<'END_OF_FILE'
  110. XAVL-Balanced Binary Tree Processes                                MetalBase 5.0
  111. X-------------------------------------------------------------------------------
  112. X
  113. XMetalBase 3.2 used straight, unbalanced binary-trees to keep track of data.
  114. X4.0 and up have migrated to AVL-balancing; the routines for keeping a tree
  115. Xin balance, and rebalancing it when needed, are below.  I developed these
  116. Xby hand, just from common sense; if there's a flaw, it hasn't shown up after
  117. Xhundreds of thousands of uses.
  118. X
  119. XAVL balancing means that, at any node in a tree, the number of records to
  120. Xthe left of a node is almost exactly equal to the number on the right; to
  121. Xmake it, work, they can be off by 1.  So the following are balanced:
  122. X
  123. X                     ___5___           ___4___
  124. X                   _2_     _8_       _2_     _6_
  125. X                  1   4   7         1   3   5   7
  126. X
  127. XAnd the following aren't (the first is too heavy to the left at 5, the second
  128. Xis too heavy to the right at 4):
  129. X
  130. X                     ___5___           ___4___
  131. X                   _2_      8                 6_
  132. X                  1   4                          7
  133. X
  134. XThis kind of balancing ensures, in effect, that only the last two levels of
  135. Xthe tree can ever have any empty nodes... so a tree will always stay as shallow
  136. Xas possible, providing the following speeds for algorithms:
  137. X
  138. X                                Worst-Case      Best-Case
  139. X                                ------------    ---------
  140. X               SEARCH...........O(ln(n))........O(1)
  141. X               ADD..............O(n * ln (n))...O(ln (n))
  142. X               UPDATE...........O(n * ln (n))...O(ln (n))
  143. X               DELETE...........O(n * ln (n))...O(ln (n))
  144. X
  145. XI'm particularly interested in getting a better rebalancing routine; mine is
  146. Xthe elegant solution, but I can't believe it's the only, or the fastest.  If
  147. Xyou have any ideas, lemme know.
  148. X
  149. X-------------------------------------------------------------------------------
  150. X
  151. X        Note that some names have changed [to protect the innocent?]  :)
  152. X
  153. X          (*) indicates a procedure must be called once for each index
  154. X
  155. X-------------------------------------------------------------------------------
  156. X
  157. Xupdate (rcd) :
  158. X*  del_preserve (rcd, <index>)
  159. X   change records instance on disk
  160. X*  link (rcd, <index>)
  161. X
  162. Xdelete (rcd) :
  163. X*  del_preserve (rcd, <index>)
  164. X   remove (rcd)
  165. X
  166. Xadd (data) :
  167. X   rcd = append (data)
  168. X*  link (rcd, <index>)
  169. X
  170. Xlink (rcd) :
  171. X*| drop (rcd, <index>)            (these are called, one after the other,
  172. X*| check (rcd, top, <index>)       for each index in the relation)
  173. X
  174. Xdrop (pos, index) :
  175. X   for (loc = top; ; )
  176. X    { dir = compare (rec (loc), rec(pos), index)  -- (-1,0,1)
  177. X      loc->balance += dir;
  178. X      if ( loc->child[dir] == 0 )
  179. X       { loc->child[dir] = rec
  180. X         rec->parent = loc
  181. X         break
  182. X       }
  183. X    }
  184. X
  185. Xcheck (st, ed, index) :
  186. X   for (loc = st; ; loc=loc->parent)
  187. X    { if ( rec (loc) ->unbalanced(index) )
  188. X         balance (loc, index)
  189. X      if (loc == ed)  break;
  190. X    };
  191. X
  192. Xunlink (loc, index) :
  193. X   ch=loc->left || loc->right
  194. X   if (loc->pardir)  loc->parent->right = ch;
  195. X   else              loc->parent->left  = ch;
  196. X   if (ch)  ch->parent = loc->parent, ch->pardir = loc->pardir;
  197. X                     /* DO NOT re-read loc */
  198. X   for (dir=loc->pardir,tmp=loc->parent;tmp!=0;dir=tmp->pardir,tmp=tmp->parent)
  199. X      tmp->balance -= (dir == 1) ? 1 : -1;
  200. X
  201. Xbalance (loc, index) :
  202. X   if (! rep = find_seq (loc, rec(loc)->balance) )  ERROR!!! -- bal>0?Next:Prev
  203. X   rp=rep->parent
  204. X   unlink (rep, index)
  205. X   replace (loc, rep, index) -- Replace LOC with REP
  206. X   drop (loc, index)
  207. X   if (rp != loc)  check (rp, rep)
  208. X                    /* re-read loc */
  209. X   check (loc->parent, rep)
  210. X
  211. Xdelete_preserve (bad) :
  212. X   bp = bad->parent
  213. X   if (bad->balance != 0)
  214. X      rep = find_seq (bad, bad->balance)  --bal>0?Next:Prev
  215. X   else
  216. X      if (! rep = find_seq (bad, toggle (lastmove)))
  217. X         rep = find_seq (bad, toggle (lastmove))
  218. X   if (! rep)
  219. X      unlink (bad)
  220. X   else
  221. X    { rp= rep->parent;
  222. X      unlink (rep)
  223. X      replace (bad, rep)
  224. X      if (rp != bad)  check (rp, bp)
  225. X    }
  226. X   check (bp, top)
  227. X
  228. Xreplace (old, new) :
  229. X   new->left_c = old->left_c;
  230. X   new->right_ = old->right_;
  231. X   new->parent = old->parent;  par=new->parent;
  232. X   new->pardir = old->pardir;
  233. X   par->[pardir]_c = new;
  234. X   new->left_c->parent = new;
  235. X   new->right_c->parent = new;
  236. X
  237. END_OF_FILE
  238.   if test 4341 -ne `wc -c <'dox/flow.dox'`; then
  239.     echo shar: \"'dox/flow.dox'\" unpacked with wrong size!
  240.   fi
  241.   # end of 'dox/flow.dox'
  242. fi
  243. if test -f 'dox/format.dox' -a "${1}" != "-c" ; then 
  244.   echo shar: Will not clobber existing file \"'dox/format.dox'\"
  245. else
  246.   echo shar: Extracting \"'dox/format.dox'\" \(4400 characters\)
  247.   sed "s/^X//" >'dox/format.dox' <<'END_OF_FILE'
  248. XRelation Format                                                   MetalBase 5.0
  249. X-------------------------------------------------------------------------------
  250. X
  251. X              Number   Enum Name    Storage    Typedef From
  252. X              ------  -----------  ---------  --------------
  253. X                 0.....T_CHAR.......char []....
  254. X                 1.....T_SHORT......short......
  255. X                 2.....T_USHORT.....ushort.....unsigned short
  256. X                 3.....T_LONG.......long.......
  257. X                 4.....T_ULONG......ulong......unsigned long
  258. X                 5.....T_FLOAT......float......
  259. X                 6.....T_DOUBLE.....double.....
  260. X                 7.....T_MONEY......double.....
  261. X                 8.....T_TIME.......mb_time....long
  262. X                 9.....T_DATE.......mb_date....long
  263. X                10.....T_SERIAL.....long.......
  264. X                11.....T_PHONE......mb_phone...char[20]
  265. X
  266. X
  267. XFIELD DESCRIPTIONS ------------------------------------------------------------
  268. X
  269. X
  270. XBits in time field:
  271. X   00000000 hhhhhmmm mmmsssss suuuuuuu --h=hour,m=min,s=sec,u=microseconds
  272. X
  273. XBits in date field:
  274. X   00000000 00yyyyyy yyyyyyym mmmddddd --y=year,m=month,d=day
  275. X
  276. XMoney:
  277. X   Money is treated exactly as double, but is rounded to 2 decimal places
  278. X   before being written.
  279. X
  280. XSerial:
  281. X   Serial fields are automatically filled before an 'add' instruction (not
  282. X   before updates), by the system--the number starts with 0 (unless specified
  283. X   otherwise in the schema) and increments on each successive add.  It never
  284. X   decrements (even if a record is deleted), nor is the same number ever used
  285. X   twice.
  286. X
  287. XPhone:
  288. X   Phone number fields are stored as pure text; there's only a special field
  289. X   because scn_phone() and fmt_phone() are there, and it's awfully convenient.
  290. X
  291. X
  292. XRELATION FORMAT ---------------------------------------------------------------
  293. X
  294. X
  295. XPos  #/Bytes   Description
  296. X
  297. X  0   1.......(char)50 : 5.0 signature
  298. X  1   1.......Unused in MB 5.0 (was number of users on the system, 0 == none)
  299. X  2   2.......Unused in MB 5.0 (was PID of temporary lock, 0 == none)
  300. X  4   2.......Unused in MB 5.0 (was PID of exclusive lock, 0 == none)
  301. X  6   4.......Pointer to fields' decriptions  => :A
  302. X 10   4.......Pointer to indices' decriptions => :B
  303. X 14   4.......Pointer to record 0             => :C
  304. X 18   4.......Number of records
  305. X 22   4.......Next serial field value
  306. X 26   2.......Number of fields
  307. X 28   2.......Number of indices
  308. X 30   4*I.....Pointers to index-heading records (top rcd in each tree)
  309. X A:   var*F...Fields' descriptions:
  310. X                 byte    0 : Type (0-10, as listed above)
  311. X                 bytes 1-2 : Size (short/ used only for char fields)
  312. X                 bytes 3-? : Name (max len = 20, terminated by '|')
  313. X B:   var*I...Indices' descriptions:
  314. X                 byte    0 : Type (0-1, 0==nodups, 1==dups)
  315. X                 bytes   1 : Number of fields in this index
  316. X                 bytes 2-? : Name (max len = 20, terminated by ':')
  317. X                       --- : Each field's sequential # (as short, 0-based)
  318. X      1.......Separator ('\n')
  319. X
  320. X ?:   128.....Unused in 5.0 (reserved space for later MB versions)
  321. X                (at runtime, this position is pointed to by {rel->hack})
  322. X
  323. X C:   var*R...Records:
  324. X                 13 bytes for each index: Index information
  325. X                    Bytes  0-3 : Left-child (record #)
  326. X                    Bytes  4-7 : Right-child (record #)
  327. X                    Bytes 8-11 : Parent (record #)
  328. X                    Byte    12 : Balance (';'=-2 '<'=-1 '='=0 '>'=1 '?'=2)
  329. X                 recsize bytes: Record information (no field separators)
  330. X                 1 byte separator ('\n')
  331. X
  332. X
  333. XLOCKFILE FORMAT ---------------------------------------------------------------
  334. X
  335. X
  336. XThere is exactly one lockfile for each relation, kept in a common temporary
  337. Xdirectory.  You can delete the lockfile at any time, as long as you're sure
  338. Xthere is no one using the relation at the time (this would be a Bad Thing to
  339. Xdelete if the relation is in use).  Deleting the lockfile will erase any
  340. Xremaining exclusive lock, and reset the number of users on the relation to
  341. Xzero.
  342. X
  343. XPos  #/Bytes   Description
  344. X
  345. X  0   2.......Number of users on relation, 0 == none
  346. X  2   2.......PID of exclusive lock, 0 == none
  347. X  4   6.......Three hacklock PIDs (see lock.dox)
  348. X 10  60.......Thirty temporary-lock queue positions (see lock.dox)
  349. X 70  30.......Thirty temporary-lock strobes (see lock.dox)
  350. X
  351. END_OF_FILE
  352.   if test 4400 -ne `wc -c <'dox/format.dox'`; then
  353.     echo shar: \"'dox/format.dox'\" unpacked with wrong size!
  354.   fi
  355.   # end of 'dox/format.dox'
  356. fi
  357. if test -f 'dox/struct.dox' -a "${1}" != "-c" ; then 
  358.   echo shar: Will not clobber existing file \"'dox/struct.dox'\"
  359. else
  360.   echo shar: Extracting \"'dox/struct.dox'\" \(2763 characters\)
  361.   sed "s/^X//" >'dox/struct.dox' <<'END_OF_FILE'
  362. XC-Structure Interface                                             MetalBase 5.0
  363. X-------------------------------------------------------------------------------
  364. X
  365. XMetalBase 4.0 and up read and write database information directly from C
  366. Xstructures, using the following assumptions:
  367. X
  368. X            sizeof(long)   = sizeof(ulong)  = 4 bytes
  369. X            sizeof(short)  = sizeof(ushort) = 2 bytes
  370. X            sizeof(float)  = 4 bytes
  371. X            sizeof(double) = 8 bytes
  372. X
  373. XIf any of the above are not valid for your system, MetalBase 5.0 will not work
  374. Xproperly; however, this is the defacto standard, and should produce little
  375. Xdifficulty in porting.  So far, I have yet to receive even one complaint that
  376. Xthis assumption has limited a user's use of this package (one Mac user had
  377. Xto change the defaults for his compiler from a 12-byte double to an 8-byte,
  378. Xbut it worked fine after that).
  379. X
  380. XThe main problem between machines is therefore not type sizes, but byte-
  381. Xaligning in C structures.  At first, one would expect that the following:
  382. X   struct
  383. X    { char   A[3];
  384. X      long   B;
  385. X      char   C[5];
  386. X      char   D[5];
  387. X      double E;
  388. X      char   F[2];
  389. X    }
  390. Xwould appear like this in memory:
  391. X      AAABBBBCCCCCDDDDDEEEEEEEEFF
  392. XUnfortunately, this is not the case for most systems.  On this machine
  393. X(Sun4 SunOs/cc), structures are aligned comme ca:
  394. X      AAAxBBBBCCCCCDDDDDxxxxxxEEEEEEEEFFxxxxxx
  395. XWhile Xenix 2.3.2 would align them as:
  396. X      AAAxBBBBCCCCCDDDDDxxEEEEEEEEFF
  397. XWhy?  Because, for the Sun, each field is aligned on 4- or 8- byte boundaries,
  398. Xdepending on size; shorts, longs and floats to 4-byte boundaries, and doubles
  399. Xto 8-byte ones.  Essentially, the algorithm is:
  400. X
  401. X      start_pos = &base_of_structure;
  402. X      for (each field)
  403. X       { if (type != char)
  404. X            if (sizeof (type) < 8)  align_to_4_byte_boundary (start_pos);
  405. X            else                    align_to_8_byte_boundary (start_pos);
  406. X         add_field
  407. X       }
  408. X      align_to_8_byte_boundary
  409. X
  410. XThe last alignment is to obtain the structure size.
  411. X
  412. XAll this crap was discovered through trial and error (yick!)  If anyone
  413. Xencounters a structure which does not seem to meet the standards shown
  414. Xby struct.c, please contact me (virtual!richid@owlnet.rice.edu) and describe
  415. Xthe structure and your machine type.  If you decide to make any necessary
  416. Xmodifications yourself, you'll find the above algorithm (in essentially that
  417. Xform) in the routine _fill_info() [mbase.c]--but let me know if you get it
  418. Xworking, so I can add machine-dependency to the code with #defines.
  419. X
  420. XCurrently, there are 4 different version supported, as shown by "make struct".
  421. XIn general, these conform to Sun, Coherent/Zortec, Xenix, and Mac/Microsoft C
  422. Xrespectively (definitely not an exhaustive list of compatibility there).
  423. X
  424. END_OF_FILE
  425.   if test 2763 -ne `wc -c <'dox/struct.dox'`; then
  426.     echo shar: \"'dox/struct.dox'\" unpacked with wrong size!
  427.   fi
  428.   # end of 'dox/struct.dox'
  429. fi
  430. if test -f 'dox/time.dox' -a "${1}" != "-c" ; then 
  431.   echo shar: Will not clobber existing file \"'dox/time.dox'\"
  432. else
  433.   echo shar: Extracting \"'dox/time.dox'\" \(3925 characters\)
  434.   sed "s/^X//" >'dox/time.dox' <<'END_OF_FILE'
  435. XTime/Date Functions                                               MetalBase 5.0
  436. X-------------------------------------------------------------------------------
  437. X
  438. XMetalBase handles time and date rather cleanly.  Two new types have been
  439. Xdefined, mb_time and mb_date ... each is actually a parsed-out long, so they
  440. Xmay be passed and returned by value as well as by reference.  There are several
  441. Xfunctions available for use with date and time fields (note that {struct tm *}
  442. Xis a system-supplied structure, and many many many functions are available for
  443. Xits use, completely independant of MetalBase):
  444. X
  445. X   mb_date tmtodate (struct tm *)
  446. X      If the argument passed is a valid {struct tm*} pointer, its information
  447. X      is converted to mb_date format, and the new variable returned.  If the
  448. X      pointer is NULL, the current date is returned.
  449. X
  450. X   mb_time tmtotime (struct tm *)
  451. X      If the argument passed is a valid {struct tm*} pointer, its information
  452. X      is converted to mb_time format, and the new variable returned.  If the
  453. X      pointer is NULL, the current time is returned.
  454. X
  455. X   struct tm *datetimetotm (mb_date, mb_time)
  456. X      If the arguments passed represent valid dates and times, a static local
  457. X      variable is filled out to conform to {struct tm*} conventions and
  458. X      returned.  If either component is zero, that component is replaced by
  459. X      the current date or time.
  460. X
  461. XNote that these functions have been #define'd to the following equivalents
  462. Xas well:
  463. X
  464. X      mb_date    curdate();
  465. X         Returns the current date (assign with "mb_date x = curdate()" or
  466. X         whatever strikes your fancy).
  467. X
  468. X      mb_time    curtime();
  469. X         Returns the current time.
  470. X
  471. X      struct tm *curdatetime();
  472. X         Returns the curent date and time in a {struct tm *} structure.
  473. X
  474. XThere are also two format functions:
  475. X
  476. X   char *fmt_date (mb_date, opt)
  477. X      The date does _not_ default to the current date if it ==0L.  This command
  478. X      returns a pointer to a static local character array, in which the date
  479. X      represented will be formated to one of the following conventions:
  480. X          opt:           format:
  481. X         default          mm/dd/yyyy
  482. X           1              mm/dd/yy
  483. X           2              yymmdd
  484. X      Note that we're approaching 2000, so I don't suggest using mm/dd/yy or
  485. X      yymmdd... just a personal peeve.  Not that my code will be around that
  486. X      long or anything, but it's only what, 8 years away?
  487. X
  488. X   char *fmt_time (mb_time, opt)
  489. X      The time does _not_ default to the current time if it ==0L.  This command
  490. X      returns a pointer to a static local character array, in which the time
  491. X      represented will be formated to one of the following conventions:
  492. X          opt:           format:
  493. X         default          hh:mm:ss   24-hour
  494. X           1              hh:mm xx   where "xx" == "am" or "pm"
  495. X           2              hh:mm      24-hour
  496. X
  497. XAnd, accordingly, there are two scanning functions:
  498. X
  499. X   mb_date scn_date (char *)
  500. X      The string must contain a date, which may be in any of the formats
  501. X      supported by fmt_date, and will be scanned appropriately into an
  502. X      mb_date structure.  Note that this function assumes the separator
  503. X      WILL BE '/' ... if you want hyphens as well, you'll have to add
  504. X      that.
  505. X
  506. X   mb_time scn_time (char *)
  507. X      The string must contain a time, which may be in any of the formats
  508. X      supported by fmt_time, and will be scanned appropriately into an
  509. X      mb_time structure.
  510. X
  511. XThere is also a function to find the number of seconds that have elapsed since
  512. Xa given mb_time:
  513. X   long   elap_t (mb_time)
  514. XIf you pass it 0L, it will return the number of seconds since midnight.
  515. X
  516. XYou can simulate a timer by doing something like this:
  517. X         mb_time  a;
  518. X         a = curtime();   /* Start timer */
  519. X         for (;;)
  520. X            printf ("It's been %ld seconds\n", elap_t(a));
  521. XRather useful at times. :)  Ha!  Times!  A pun!  Get it?  Hooo...
  522. X
  523. END_OF_FILE
  524.   if test 3925 -ne `wc -c <'dox/time.dox'`; then
  525.     echo shar: \"'dox/time.dox'\" unpacked with wrong size!
  526.   fi
  527.   # end of 'dox/time.dox'
  528. fi
  529. if test -f 'sample/bench.c' -a "${1}" != "-c" ; then 
  530.   echo shar: Will not clobber existing file \"'sample/bench.c'\"
  531. else
  532.   echo shar: Extracting \"'sample/bench.c'\" \(2552 characters\)
  533.   sed "s/^X//" >'sample/bench.c' <<'END_OF_FILE'
  534. X/*
  535. X * METALBASE 5.0
  536. X *
  537. X * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  538. X *                                       [ t-richj@microsoft.com ]
  539. X */
  540. X
  541. X#include <mbase.h>
  542. X#include "bench.h"             /* Created during "% build bench.s"  */
  543. X
  544. X#define NUM 10 /* When building chart, each '.' == how many adds? */
  545. X
  546. X#ifdef LONGARGS
  547. X   void  main (int, char **);
  548. X#else
  549. X   void  main ();
  550. X#endif
  551. X
  552. Xrelation  *rel;
  553. X
  554. Xlong   elaparr[79];
  555. X
  556. Xvoid
  557. Xmain  (argc, argv)
  558. Xint    argc;
  559. Xchar **argv;
  560. X{
  561. X   int     i, num, x, top;
  562. X   long    sec;
  563. X   mb_time start;
  564. X
  565. X/*
  566. X * First, parse the command line, and set {num}.
  567. X *
  568. X */
  569. X
  570. X   if (argc == 1)
  571. X      num = 0;
  572. X   else if (argc == 2)
  573. X      num = (int)atoi (argv[1]);
  574. X   else
  575. X      {
  576. X      fprintf (stderr, "format: bench [numberofadds]%s", SNGCR);
  577. X      mb_exit (1);
  578. X      }
  579. X   if (num < 0)
  580. X      {
  581. X      fprintf (stderr, "You gotta add at least one record, idiot.%s", SNGCR);
  582. X      mb_exit (1);
  583. X      }
  584. X
  585. X/*
  586. X * Great.  Now open the database and start it rolling...
  587. X *
  588. X */
  589. X
  590. X   if ((rel = mb_inc ("bench", 0)) == RNULL)
  591. X      {
  592. X      fprintf (stderr, "%s.%s", mb_error, SNGCR);
  593. X      mb_exit (2);
  594. X      }
  595. X
  596. X   start = curtime();
  597. X
  598. X   if (num != 0)
  599. X      {
  600. X      printf ("Number of records: %ld%s", mb_num(rel), SNGCR);
  601. X      printf ("Start time:        %s%s", fmt_time (start, 0), SNGCR);
  602. X      }
  603. X
  604. X   for (x = 0; x < 79; x++)
  605. X      {
  606. X      top = num ? num : NUM;
  607. X      for (i = 0; i < top; i++)
  608. X         {
  609. X         if ((mb_add (rel, &bench_rec)) != MB_OKAY)
  610. X            {
  611. X            printf ("ADD FAILED: %s%s", mb_error, SNGCR);
  612. X            mb_exit(3);
  613. X            }
  614. X         }
  615. X      if (! num)
  616. X         fprintf (stderr, ".");
  617. X
  618. X      sec = elap_t (start);
  619. X
  620. X      if (num != 0)  break;
  621. X
  622. X      elaparr[x] = sec;
  623. X      }
  624. X
  625. X/*
  626. X * All done.  Print the tally.
  627. X *
  628. X */
  629. X
  630. X   if (! num)  printf ("\n");
  631. X   printf ("Number of records: %ld%s", mb_num (rel), SNGCR);
  632. X   if (num)
  633. X      {
  634. X      printf ("End time:          %s%s", fmt_time (curtime(), 0), SNGCR);
  635. X
  636. X      if (i != 0)
  637. X         {
  638. X         printf ("Seconds per add:   %.2lf%s", (double)sec/(double)i, SNGCR);
  639. X         }
  640. X      }
  641. X   else if (elaparr[78] != 0)
  642. X      {
  643. X      printf ("Average seconds per add = %.2lf:\n",
  644. X              ((double)elaparr[78]/(double)(NUM*79)));
  645. X      for (x = 19; x >= 0; x--)
  646. X         {
  647. X         for (i = 0; i < 79; i++)
  648. X            {
  649. X            if ( ((elaparr[i] * 20) / elaparr[78]) >= x )
  650. X               printf ("X");
  651. X            else
  652. X               printf (" ");
  653. X            }
  654. X         printf ("\n");
  655. X         }
  656. X      }
  657. X
  658. X   mb_exit (0);
  659. X}
  660. X
  661. END_OF_FILE
  662.   if test 2552 -ne `wc -c <'sample/bench.c'`; then
  663.     echo shar: \"'sample/bench.c'\" unpacked with wrong size!
  664.   fi
  665.   # end of 'sample/bench.c'
  666. fi
  667. if test -f 'sample/readme' -a "${1}" != "-c" ; then 
  668.   echo shar: Will not clobber existing file \"'sample/readme'\"
  669. else
  670.   echo shar: Extracting \"'sample/readme'\" \(2634 characters\)
  671.   sed "s/^X//" >'sample/readme' <<'END_OF_FILE'
  672. XThe makefiles under this directory are (if it's possible) even harder to
  673. Xdeal with than in ../src --but I guarantee it's all compilable.  :)
  674. X
  675. XEventually.
  676. X
  677. XThere are two primary toys under here, sample and bench.  Sample is really
  678. Xjust there for example code; it happens to work, which is nice, but it's
  679. Xnot doing its intended job unless you look under the hood.  So have at it;
  680. Xit's ugly code (amazing how you can always look back and say, "God, I
  681. Xcan't believe I wrote THAT", isn't it?), but, as I said, it does work.
  682. X                          ^^
  683. X                          Note that I didn't put the comma inside the
  684. X                          quotes.  I think that looks really stupid; it's
  685. X                          not part of the quote.  New standard.  Anyone
  686. X                          wanna draw up a petition?
  687. X
  688. XBench is a benchmark utility.  Run it without any arguments and it'll proceed
  689. Xto add 790 very simple records, one after another, to bench.rel--each '.' it
  690. Xprints represents 10.  Then it'll draw a pretty graph that looks like a plot
  691. Xof any O(n * ln(n)) function (for 5.0 anyway--try it with 4.1a and it looks
  692. Xpretty!  All kindsa lumps and valleys...) and tell you what the average add
  693. Xtime is for all 790 records.  Some typical numbers, with the cache at 500
  694. X(as shipped):
  695. X
  696. X                                      Average time per add,  Multiple that
  697. X Machine   Software                   Versions 5.0 vs 4.1a   it's faster by
  698. X --------  -------------------------  ---------------------  --------------
  699. X NeXT      Non-turbo, 25Mhz            .27 sec vs  ???? sec      x ????
  700. X Sparc 1+  SunOS 4.1 (zzzooooom!!!)    .37 sec vs  1.16 sec      x 3.13
  701. X 386/25dx  Dos 5, SMARTDRV, DublDisk  1.15 sec vs  3.17 sec      x 2.75
  702. X 386/16sx  Xenix of some sort         1.27 sec vs  2.67 sec      x 2.10
  703. X
  704. XSo basically, the faster your processor, the better 5.0 beats the crap out of
  705. X4.1a... just what you'd expect, 'cause its primary change is that all tree
  706. Xbalancing is done cached.  Beware, though; 790 records at even 1 sec per add
  707. Xis just over 13 minutes; this benchmark will take a bit of time to run.  If you
  708. Xchange the cache size in the library it'll change the results a good bit; play
  709. Xwith MAX_CACHE in mbase.h and see.  Optimally you'd want MAX_CACHE to be
  710. Xexactly as big as the number of records you intend to store; more than that
  711. Xwould never be used, less than that and it flushes too often.  If you cut the
  712. Xnumber of records added by the benchmark to around 520, you may be able to see
  713. Xa bit of a peak in the plot where the cache size is...  if you have no more of
  714. Xa life than I do, you may wanna give it a whirl.  :|
  715. X
  716. END_OF_FILE
  717.   if test 2634 -ne `wc -c <'sample/readme'`; then
  718.     echo shar: \"'sample/readme'\" unpacked with wrong size!
  719.   fi
  720.   # end of 'sample/readme'
  721. fi
  722. if test -f 'sample/sample.frm' -a "${1}" != "-c" ; then 
  723.   echo shar: Will not clobber existing file \"'sample/sample.frm'\"
  724. else
  725.   echo shar: Extracting \"'sample/sample.frm'\" \(4088 characters\)
  726.   sed "s/^X//" >'sample/sample.frm' <<'END_OF_FILE'
  727. X#
  728. X# This is form sample--designed as a demonstration data-entry form.  It
  729. X# must be compiled with 'form'; sample_fm.h (built from this by form) is used
  730. X# in sample.c to control data-entry.
  731. X#
  732. X# Released October 1st, 1992 by Huan-Ti [ virtual!root@owlnet.rice.edu ]
  733. X#                                       [ t-richj@microsoft.com ]
  734. X#
  735. X
  736. XData sample;
  737. X
  738. XDefine A credit;    # These names are too long for their DE slots,
  739. XDefine B temp;      # So they're defined to something shorter.
  740. XDefine C num
  741. X
  742. X#
  743. X# Oh yeah.  I added the line below for Number Purchased on a whim... you'll
  744. X# find no mention of it in sample.c; but it works just perfectly, data moving
  745. X# from data-entry template to the relation and back without any specific
  746. X# coding at all.  Good example of some hefty flexibility here...
  747. X#
  748. X# The "4 2" below are the Y,X coordinates of the upper-left corner of this
  749. X# data-entry template on the screen.  Just thought I'd mention that.
  750. X#
  751. X
  752. XScreen 2 2
  753. X{
  754. X     +--------------------------------------------------------------+
  755. X     |                                                              |
  756. X     |  Customer Number...[custnum]   Name...[custname           ]  |
  757. X     |  Customer Phone....[phone               ]                    |
  758. X     |                                                              |
  759. X     |  Current Balance...${balance  }  Accept Credit...[A]..[B    ]  |
  760. X     |  Number Purchased..[C  ]                                     |
  761. X     |                                                              |
  762. X     |  Date Entered......[date_en   ]  Time...[time_en ]           |
  763. X     |                                                              |
  764. X     +--------------------------------------------------------------+
  765. X
  766. X            Ctrl-U          -  Undo (in this field only)
  767. X            Ctrl-Q, Ctrl-C  -  Abort a change
  768. X            Ctrl-A, Ctrl-D  -  Accept transaction; Delete this record
  769. X            Ctrl-N, Ctrl-P  -  Find next record; Find previous record
  770. X
  771. X      Type CTRL-Q (to leave edit mode), then "new" as a customer name
  772. X            to add a new record.
  773. X}
  774. X
  775. X#
  776. X# Note the screwed-up right side on the template above.  The braces
  777. X# surrounding "balance" will be actually -removed- from the display, while
  778. X# the brackets around the others are simply spaced over.  So the template
  779. X# looks perfectly rectangular during data-entry.
  780. X#
  781. X# I only used braces because I wanted the number for the customer's balance
  782. X# to buck right up against the dollar sign.  I know--picky picky...
  783. X#
  784. X
  785. XField credit type choice ("Yy" "Nn" "?");
  786. XField temp   type link to credit ("Yes" "No" "Maybe");
  787. X
  788. X#
  789. X# Ah.  The two lines above create a choice-and-link pair... something I
  790. X# noticed I'd been writing a lot of hard-coded a while back, and decided to
  791. X# support.  Without the "Field temp type link to credit..." thing, the
  792. X# field "sample.credit" would continue to work as you'd expect from a choice
  793. X# field--it only allows characters from the set "YyNn?" in it.  But, add the
  794. X# link to credit, and field "temp" is filled in automatically depending on
  795. X# what "sample.credit" is:  "Yy" maps to "Yes", "Nn" to "No", and "?" to
  796. X# "Maybe".
  797. X#
  798. X# Note that, an alternate setup (if the relation had sample.credit defined
  799. X# to have a few more characters) might be:
  800. X#
  801. X# Field temp   type choice ("Yy" "Nn" "?");
  802. X# Field credit type link to temp ("Yes" "No" "Maybe");
  803. X#
  804. X#    Accept Credit?....[temp]..[credit ]
  805. X#
  806. X# That way, the Yes/No/Maybe is stored in the relation, instead of Y/N/?.
  807. X# Nifty, eh?  Links can only be made to choice fields, and choices need not
  808. X# have links.  A field can't be both a link and a choice, nor can a single
  809. X# field be a link to more than one choice field... but a choice field can
  810. X# have multiple links.  Confused yet?  Play with it.
  811. X#
  812. X
  813. XMode 1 inout  custnum out,   temp out, date_en out, time_en  out; # Add
  814. XMode 2 out    custnum inout, custname inout;                      # Query
  815. XMode 3 inout                 temp out, date_en out, time_en  out; # Update
  816. X
  817. X#
  818. X# Mode 1 is for adding new records,
  819. X# Mode 2 is for finding old records,
  820. X# Mode 3 is for updating existing records.
  821. X#
  822. X
  823. XEnd
  824. X
  825. END_OF_FILE
  826.   if test 4088 -ne `wc -c <'sample/sample.frm'`; then
  827.     echo shar: \"'sample/sample.frm'\" unpacked with wrong size!
  828.   fi
  829.   # end of 'sample/sample.frm'
  830. fi
  831. if test -f 'sample/sample.s' -a "${1}" != "-c" ; then 
  832.   echo shar: Will not clobber existing file \"'sample/sample.s'\"
  833. else
  834.   echo shar: Extracting \"'sample/sample.s'\" \(1571 characters\)
  835.   sed "s/^X//" >'sample/sample.s' <<'END_OF_FILE'
  836. Xrelation sample
  837. X
  838. X#
  839. X#  Each customer at a store, for example, has one record in this relation--the
  840. X#  record contains their name and a customer code (assigned by the system, with
  841. X#  the first customer being 100), their balance and number of purchases made,
  842. X#  the date and time the record was established, and a 3-character string that
  843. X#  describes whether or not they're allowed credit at the store.
  844. X#
  845. X
  846. Xfield custname type string length 30; # A fairly short name field, but hey...
  847. Xfield custnum  type serial start 100; # Assigned automatically (from 100)
  848. Xfield balance  type money;            # Standard double, to .XX resolution
  849. Xfield date_en  date;                  # Date customer entered into database
  850. Xfield time_en  type time;             # Time customer entered into database
  851. Xfield credit   char * 3;              # "y"/"n"/"?" etc.
  852. Xfield num      ushort;                # Number of purchases customer has made
  853. Xfield phone    type phone;            # Phone number of client
  854. X
  855. Xindex ix_name    on custname with duplicates;
  856. Xindex ix_number  on custnum;
  857. Xindex ix_balance on balance            with duplicates;
  858. Xindex ix_entered on date_en, time_en   with duplicates;  # DATE_EN FIRST!!!
  859. X
  860. X#
  861. X# Why date_en first?  Because a composite index, like ix_entered, will
  862. X# sort merrily along the first field you give it... as soon as it finds two
  863. X# records which have the same first field, it'll sort 'em by the second.  So
  864. X# ix_entered will sort things by date, and when the date's the same, by time..
  865. X# if you did it the other way around, it'd be a pretty stupid index.
  866. X#
  867. X
  868. Xend
  869. X
  870. END_OF_FILE
  871.   if test 1571 -ne `wc -c <'sample/sample.s'`; then
  872.     echo shar: \"'sample/sample.s'\" unpacked with wrong size!
  873.   fi
  874.   # end of 'sample/sample.s'
  875. fi
  876. if test -f 'src/blast.c' -a "${1}" != "-c" ; then 
  877.   echo shar: Will not clobber existing file \"'src/blast.c'\"
  878. else
  879.   echo shar: Extracting \"'src/blast.c'\" \(2299 characters\)
  880.   sed "s/^X//" >'src/blast.c' <<'END_OF_FILE'
  881. X/*
  882. X * METALBASE 5.0
  883. X *
  884. X * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  885. X *                                       [ t-richj@microsoft.com ]
  886. X */
  887. X
  888. X#include "stdinc.h"
  889. X
  890. X#ifndef MSDOS
  891. X#ifdef LONGARGS
  892. X   extern char *getenv (char _FAR_ *);
  893. X#else
  894. X   extern char *getenv();
  895. X#endif
  896. X#endif
  897. X
  898. Xvoid
  899. Xmain  (argc, argv)
  900. Xint    argc;
  901. Xchar **argv;
  902. X{
  903. X   char  buf[128], name[128], *pch;
  904. X   int   i;
  905. X
  906. X   if (argc == 1)
  907. X      {
  908. X      fprintf (stderr, "format: blast relation [relation]*\n");
  909. X      exit (1);
  910. X      }
  911. X
  912. X   for (--argc, argv++; argc; argc--, argv++)
  913. X      {
  914. X      if ((*argv)[0] == '-')
  915. X         {
  916. X         fprintf (stderr, "blast: unrecognized switch -%c\n", (*argv)[1]);
  917. X         exit (2);
  918. X         }
  919. X
  920. X      if (access (*argv, 0) == -1)
  921. X         {
  922. X         fprintf (stderr, "blast: cannot find relation %s\n", *argv);
  923. X         continue;
  924. X         }
  925. X
  926. X/*
  927. X * First, get the basename for the relation (take off the path and/or
  928. X * extension)--put it in buf:
  929. X *
  930. X */
  931. X
  932. X      name[0] = 0;
  933. X
  934. X      if ((pch = strrchr (*argv, DIRSEP)) == NULL)
  935. X         pch = *argv;
  936. X      else
  937. X         pch++;
  938. X      strcpy (buf, pch);
  939. X
  940. X      if (! strcmp (buf, ".rel") || ! strcmp (buf, ".REL"))
  941. X         {
  942. X         *(strrchr (buf, '.')) = 0;
  943. X         }
  944. X
  945. X/*
  946. X * Great.  Now get the temporary directory where the lockfile will be...
  947. X *
  948. X */
  949. X
  950. X      if ((pch = getenv ("TMP")) != NULL || (pch = getenv ("TEMP")) != NULL)
  951. X         {
  952. X         strcpy (name, pch);  /* If they define a directory, use it. */
  953. X         }
  954. X      else                   /* Otherwise, try to guess a default directory. */
  955. X         {
  956. X#ifdef UNIX
  957. X         strcpy (name, "/tmp");
  958. X#endif
  959. X         }
  960. X      if (! name[0])
  961. X         {
  962. X         fputs ("blast: you must have a TMP directory defined.\n", stderr);
  963. X         exit (3);
  964. X         }
  965. X      if (name[(i = strlen(name))-1] != DIRSEP)
  966. X         {
  967. X         name[i] = DIRSEP;
  968. X         name[i+1] = 0;
  969. X         }
  970. X
  971. X/*
  972. X * And attach the filename + .LCK to get a lockfile name.  Then delete the
  973. X * stupid thing.
  974. X *
  975. X */
  976. X
  977. X      strcat (name, buf);
  978. X      strcat (name, ".lck");
  979. X
  980. X      if (access (name, 0) == -1)
  981. X         {
  982. X         fprintf (stderr, "blast: %s was not locked\n", *argv);
  983. X         continue;
  984. X         }
  985. X
  986. X      unlink (name);
  987. X
  988. X      fprintf (stderr, "%s blasted successfully\n", *argv);
  989. X      }
  990. X}
  991. X
  992. END_OF_FILE
  993.   if test 2299 -ne `wc -c <'src/blast.c'`; then
  994.     echo shar: \"'src/blast.c'\" unpacked with wrong size!
  995.   fi
  996.   # end of 'src/blast.c'
  997. fi
  998. if test -f 'src/cache.c' -a "${1}" != "-c" ; then 
  999.   echo shar: Will not clobber existing file \"'src/cache.c'\"
  1000. else
  1001.   echo shar: Extracting \"'src/cache.c'\" \(2960 characters\)
  1002.   sed "s/^X//" >'src/cache.c' <<'END_OF_FILE'
  1003. X/*
  1004. X * METALBASE 5.0
  1005. X *
  1006. X * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  1007. X *                                       [ t-richj@microsoft.com ]
  1008. X */
  1009. X
  1010. X#define UTIL_C
  1011. X#include "mbase.h"
  1012. X#include "internal.h"
  1013. X
  1014. Xcache  mb_cache[MAX_CACHE];
  1015. Xcache  mb_ctop;
  1016. Xint    ncache = 0;
  1017. Xcache *mb_clast = NULL;
  1018. Xlong   mb_nlast = 0L;
  1019. X
  1020. Xcache *mb_hash[MAX_CACHE];  /* Attempted */
  1021. X
  1022. X/****************************************************************************/
  1023. X
  1024. Xcache *
  1025. X_read_cache (rel, rcd, idx)
  1026. Xrelation    *rel;
  1027. Xlong              rcd;
  1028. Xint                    idx;
  1029. X{
  1030. X   cache *ptr, *end;
  1031. X   long   n;
  1032. X
  1033. X   end = &mb_cache[ncache];  /* Can't check when ptr==end */
  1034. X
  1035. X   if (mb_nlast == rcd && mb_clast != NULL)  /* Now in practice, we seem to */
  1036. X      {                                      /* query the same one twice    */
  1037. X      return mb_clast;                       /* in a row a lot, so here's   */
  1038. X      }                                      /* a hack to optimize for it.  */
  1039. X
  1040. X   mb_nlast = rcd;
  1041. X
  1042. X   if (rcd == 0L)
  1043. X      {
  1044. X      if (mb_ctop.num != 0L)
  1045. X         {
  1046. X         return (mb_clast = &mb_ctop);
  1047. X         }
  1048. X
  1049. X      GO_TOP (rel, idx);
  1050. X      readx  (rel->relcode, &(mb_ctop.num), 4);
  1051. X      mb_ctop.changed = 0;
  1052. X      return (mb_clast = &mb_ctop);
  1053. X      }
  1054. X
  1055. X   if ((ptr = mb_hash[ n=(rcd % MAX_CACHE) ]) != NULL && ptr->num == rcd)
  1056. X      {
  1057. X      return (mb_clast = ptr);
  1058. X      }
  1059. X
  1060. X   for (ptr = mb_cache; ptr < end; ptr++)
  1061. X      if (ptr->num == rcd)
  1062. X         {
  1063. X         return (mb_clast = mb_hash[n] = ptr);
  1064. X         }
  1065. X
  1066. X   ptr = _new_cache (rel, idx);
  1067. X
  1068. X   GO_INDEX (rel, rcd, idx);
  1069. X   readx (rel->relcode, &(ptr->left),   4);
  1070. X   readx (rel->relcode, &(ptr->right),  4);
  1071. X   readx (rel->relcode, &(ptr->parent), 4);
  1072. X   readx (rel->relcode, &(ptr->parbal), 1);
  1073. X
  1074. X   ptr->num     = rcd;
  1075. X   ptr->changed = 0;
  1076. X
  1077. X   return (mb_clast = mb_hash[n] = ptr);
  1078. X}
  1079. X
  1080. Xvoid
  1081. X_flush_cache (rel, idx)
  1082. Xrelation     *rel;
  1083. Xint                idx;
  1084. X{
  1085. X   cache *ptr, *end;
  1086. X
  1087. X   if (mb_ctop.changed)
  1088. X      {
  1089. X      GO_TOP (rel, idx);
  1090. X      writx  (rel->relcode, &(mb_ctop.num), 4);
  1091. X      }
  1092. X
  1093. X   end = &mb_cache[ncache];  /* Can't check when ptr==end */
  1094. X
  1095. X   for (ptr = mb_cache; ptr < end; ptr++)
  1096. X      if (ptr->changed)
  1097. X         {
  1098. X         GO_INDEX (rel, ptr->num, idx);
  1099. X         writx (rel->relcode, &(ptr->left),   4);
  1100. X         writx (rel->relcode, &(ptr->right),  4);
  1101. X         writx (rel->relcode, &(ptr->parent), 4);
  1102. X         writx (rel->relcode, &(ptr->parbal), 1);
  1103. X         }
  1104. X
  1105. X   _free_cache ();
  1106. X}
  1107. X
  1108. Xcache *
  1109. X_new_cache (rel, idx)
  1110. Xrelation   *rel;
  1111. Xint              idx;
  1112. X{
  1113. X   if (ncache >= MAX_CACHE) /* If this happens, expand cache for performance */
  1114. X      {
  1115. X      _flush_cache (rel, idx);
  1116. X      }
  1117. X   ncache ++;
  1118. X
  1119. X   return &mb_cache[ncache-1];
  1120. X}
  1121. X
  1122. Xvoid
  1123. X_free_cache ()
  1124. X{
  1125. X   register int  i;
  1126. X   mb_clast = NULL;  /* If we free the cache, it invalidates all cache*'s. */
  1127. X   ncache = 0;
  1128. X   for (i = 0; i < MAX_CACHE; i++)
  1129. X      mb_hash[i] = NULL;
  1130. X   mb_ctop.num = 0L;
  1131. X   mb_ctop.changed = 0;
  1132. X}
  1133. X
  1134. END_OF_FILE
  1135.   if test 2960 -ne `wc -c <'src/cache.c'`; then
  1136.     echo shar: \"'src/cache.c'\" unpacked with wrong size!
  1137.   fi
  1138.   # end of 'src/cache.c'
  1139. fi
  1140. if test -f 'src/makefile' -a "${1}" != "-c" ; then 
  1141.   echo shar: Will not clobber existing file \"'src/makefile'\"
  1142. else
  1143.   echo shar: Extracting \"'src/makefile'\" \(4498 characters\)
  1144.   sed "s/^X//" >'src/makefile' <<'END_OF_FILE'
  1145. X#
  1146. X#  METALBASE 5.0
  1147. X#
  1148. X#  Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  1149. X#                                        [ t-richj@microsoft.com ]
  1150. X#
  1151. X#  Generic Makefile for 5.0 Library and Utilities
  1152. X#
  1153. X###############################################################################
  1154. X#
  1155. X# CFLAGS=
  1156. X#    -DSTRUCT_1      -- Read lower for an explanation of these, and how to
  1157. X#    -DSTRUCT_2      --    determine which is appropriate for your system.
  1158. X#    -DSTRUCT_3
  1159. X#    -DSTRUCT_4
  1160. X#    -DLONGARGS      -- To produce ansi-style prototypes ("void fn(int)")
  1161. X#    -DNOSYNC        -- Removes calls to sync() and fsync(), and in-line _asm
  1162. X#    -DNOVOIDPTR     -- To use char* instead of void* (automatic for COHERENT)
  1163. X#    -DNOENCRYPT     -- To remove encryption crap from library and utilities
  1164. X#    -DNEED_USHORT   -- If your compiler doesn't have ushort yet (COH again)
  1165. X#    -DNEED_ULONG    -- If your compiler doesn't have ulong yet (most don't)
  1166. X#    -DUNIX_LOCKS    -- To enable Unix-style locking
  1167. X#    -DSIG_TYPE=void -- void or int; needed only if you define UNIX_LOCKS
  1168. X#    -DVI_EMU        -- To add vi emulation to input.c
  1169. X#    -DMSDOS         -- MS-DOS users should define this if their CC doesn't.
  1170. X#
  1171. X# EXEDIR=            -- Directory where executables should go
  1172. X# INCDIR=            -- Directory where include files should go
  1173. X# LIBDIR=            -- Directory where libmb.a / mbase.lib should go
  1174. X#
  1175. X# LDOPTS=-f          -- To include floating point stuff for printf()
  1176. X#
  1177. X###############################################################################
  1178. X#
  1179. X# All users: Update the flags just below here FIRST (don't worry about
  1180. X#            setting -DSTRUCT_?); then just type "make".  It will compile and
  1181. X#            run struct/struct.exe, which will tell you how to determine how
  1182. X#            -DSTRUCT_? should be set for your system.  Update this in the
  1183. X#            Makefile and type "make install".  You may delete struct/
  1184. X#            struct.exe after you've used it.
  1185. X#
  1186. X# DOS users: Try adding -DMSDOS to CFLAGS=; if you get a compiler error,
  1187. X#            take it back out.  The code expects MSDOS to be defined for all
  1188. X#            DOS compilers--most already set it, but some may not.
  1189. X#
  1190. X# Unix users: set -DUNIX_LOCKS to use flock() for file locking; otherwise,
  1191. X#             MetalBase's inherent system will be used (which MAY cause
  1192. X#             problems with code which does not exit properly, but which is
  1193. X#             operationally identical).
  1194. X#
  1195. X###############################################################################
  1196. X
  1197. XEXEDIR=/usr/bin
  1198. XINCDIR=/usr/include
  1199. XLIBDIR=/usr/lib
  1200. XOBJ=.o
  1201. XEXE=
  1202. XLIB=libmb.a
  1203. XCURSES=-lcurses -lterm
  1204. XCOPY=cp
  1205. XCC=cc
  1206. XLDOPTS=-f
  1207. X
  1208. XCFLAGS=-DSTRUCT_2 -DNEED_ULONG -ML -I$(INCDIR)
  1209. X
  1210. X###############################################################################
  1211. X
  1212. XBLAST   = $(EXEDIR)/blast$(EXE)
  1213. XBUILD   = $(EXEDIR)/build$(EXE)
  1214. XFORM    = $(EXEDIR)/form$(EXE)
  1215. XMBCONV  = $(EXEDIR)/mbconv$(EXE)
  1216. XREPORT  = $(EXEDIR)/report$(EXE)
  1217. XVR      = $(EXEDIR)/vr$(EXE)
  1218. XLIBRARY = $(LIBDIR)/libmb.a
  1219. X
  1220. XHEADERS=$(INCDIR)/stdinc.h $(INCDIR)/mbase.h
  1221. XTARGETS=$(BLAST) $(BUILD) $(FORM) $(MBCONV) $(REPORT) $(SAMPLE) $(VR)
  1222. X
  1223. X###############################################################################
  1224. X
  1225. XARCHIVE = ar rv $(LIBRARY)
  1226. XRANLIB  = ranlib $(LIBRARY)
  1227. X
  1228. X###############################################################################
  1229. X
  1230. Xstruct$(EXE) : struct$(OBJ)
  1231. X    $(CC) -o $@ struct$(OBJ)
  1232. X    @./struct
  1233. X    @echo Now update the Makefile and make install
  1234. X
  1235. Xinstall : all
  1236. X    @mailscr
  1237. X
  1238. Xall : $(HEADERS) $(TARGETS)
  1239. X    
  1240. X
  1241. X$(INCDIR)/mbase.h : mbase.h
  1242. X    $(COPY) mbase.h $(INCDIR)
  1243. X
  1244. X$(INCDIR)/stdinc.h : stdinc.h
  1245. X    $(COPY) stdinc.h $(INCDIR)
  1246. X
  1247. X###############################################################################
  1248. X
  1249. X$(BLAST)   : blast$(OBJ)
  1250. X    $(CC) -o $@ blast$(OBJ)
  1251. X
  1252. X$(BUILD)   : build$(OBJ) $(LIBRARY)
  1253. X    $(CC) $(LDOPTS) -o $@ build$(OBJ) $(LIBRARY)
  1254. X
  1255. X$(FORM)    : form$(OBJ) form_wr$(OBJ) $(LIBRARY)
  1256. X    $(CC) $(LDOPTS) -o $@ form$(OBJ) form_wr$(OBJ) $(LIBRARY)
  1257. X
  1258. X$(MBCONV)  : mbconv$(OBJ) $(LIBRARY)
  1259. X    $(CC) $(LDOPTS) -o $@ mbconv$(OBJ) $(LIBRARY)
  1260. X
  1261. X$(REPORT)  : report$(OBJ) $(LIBRARY)
  1262. X    $(CC) $(LDOPTS) -o $@ report$(OBJ) $(LIBRARY)
  1263. X
  1264. X$(VR)      : vr$(OBJ) $(LIBRARY)
  1265. X    $(CC) $(LDOPTS) -o $@ vr$(OBJ) $(LIBRARY) $(CURSES)
  1266. X
  1267. X$(LIBRARY) : entry$(OBJ) lock$(OBJ) input$(OBJ) mbase$(OBJ) parse$(OBJ) timedate$(OBJ) util1$(OBJ) util2$(OBJ) cache$(OBJ) create$(OBJ)
  1268. X    $(ARCHIVE) entry$(OBJ) lock$(OBJ) input$(OBJ) mbase$(OBJ) parse$(OBJ) timedate$(OBJ) util1$(OBJ) util2$(OBJ) cache$(OBJ) create$(OBJ)
  1269. X    $(RANLIB)
  1270. X
  1271. END_OF_FILE
  1272.   if test 4498 -ne `wc -c <'src/makefile'`; then
  1273.     echo shar: \"'src/makefile'\" unpacked with wrong size!
  1274.   fi
  1275.   # end of 'src/makefile'
  1276. fi
  1277. if test -f 'src/makefile.dos' -a "${1}" != "-c" ; then 
  1278.   echo shar: Will not clobber existing file \"'src/makefile.dos'\"
  1279. else
  1280.   echo shar: Extracting \"'src/makefile.dos'\" \(3856 characters\)
  1281.   sed "s/^X//" >'src/makefile.dos' <<'END_OF_FILE'
  1282. X#
  1283. X#  METALBASE 5.0
  1284. X#
  1285. X#  Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  1286. X#                                        [ t-richj@microsoft.com ]
  1287. X#
  1288. X#  Microsoft C6 makefile (for use with NMAKE) for 5.0 library and utilities
  1289. X#
  1290. X###############################################################################
  1291. X#
  1292. X# CFLAGS=
  1293. X#    -DSTRUCT_1      -- Read lower for an explanation of these, and how to
  1294. X#    -DSTRUCT_2      --    determine which is appropriate for your system.
  1295. X#    -DSTRUCT_3
  1296. X#    -DSTRUCT_4
  1297. X#    -DLONGARGS      -- To produce ansi-style prototypes ("void fn(int)")
  1298. X#    -DNOVOIDPTR     -- To use char* instead of void* (automatic for COHERENT)
  1299. X#    -DNOENCRYPT     -- To remove encryption crap from library and utilities
  1300. X#    -DNEED_USHORT   -- If your compiler doesn't have ushort yet (COH again)
  1301. X#    -DNEED_ULONG    -- If your compiler doesn't have ulong yet (most don't)
  1302. X#    -DUNIX_LOCKS    -- To enable Unix-style locking
  1303. X#    -DSIG_TYPE=void -- void or int; needed only if you define UNIX_LOCKS
  1304. X#    -DVI_EMU        -- To add vi emulation to input.c
  1305. X#    -DMSDOS         -- MS-DOS users should define this if their CC doesn't.
  1306. X#
  1307. X# LDOPTS=-f          -- To include floating point stuff for printf()
  1308. X#
  1309. X###############################################################################
  1310. X#
  1311. X# All users: Update the flags just below here FIRST (don't worry about
  1312. X#            setting -DSTRUCT_?); then just type "make".  It will compile and
  1313. X#            run struct/struct.exe, which will tell you how to determine how
  1314. X#            -DSTRUCT_? should be set for your system.  Update this in the
  1315. X#            Makefile and type "make install".  You may delete struct/
  1316. X#            struct.exe after you've used it.
  1317. X#
  1318. X# DOS users: Try adding -DMSDOS to CFLAGS=; if you get a compiler error,
  1319. X#            take it back out.  The code expects MSDOS to be defined for all
  1320. X#            DOS compilers--most already set it, but some may not.
  1321. X#
  1322. X# NOTE: This makefile is set up to use \INCLUDE, \LIB and \UTIL... change the
  1323. X#       names in here or create those directories, as you see fit.
  1324. X#
  1325. X###############################################################################
  1326. X
  1327. XCFLAGS = -nologo -c -W3 -AL -DSTRUCT_4 -DNEED_USHORT -DNEED_ULONG
  1328. X
  1329. XCC     = CL $(CFLAGS)
  1330. X
  1331. X.C.OBJ:
  1332. X    $(CC) $*.c
  1333. X
  1334. X###############################################################################
  1335. X
  1336. Xstruct.exe : struct.obj
  1337. X    @link /nologo struct.obj, struct.exe;
  1338. X    @.\struct
  1339. X    @echo Now update the Makefile and 'make install'
  1340. X
  1341. Xall : \INCLUDE\stdinc.h \INCLUDE\mbase.h blast.exe build.exe form.exe \
  1342. X      mbconv.exe report.exe vr.exe
  1343. X    
  1344. X
  1345. Xinstall : final
  1346. X    @echo MetalBase 5.0 has been installed.
  1347. X
  1348. X###############################################################################
  1349. X
  1350. X\INCLUDE\mbase.h : mbase.h
  1351. X    copy mbase.h \INCLUDE
  1352. X
  1353. X\INCLUDE\stdinc.h : stdinc.h
  1354. X    copy stdinc.h \INCLUDE
  1355. X
  1356. X\LIB\mbase.lib : mbase.lib
  1357. X    copy mbase.lib \LIB
  1358. X
  1359. Xfinal : all
  1360. X    copy *.exe \UTIL
  1361. X
  1362. X###############################################################################
  1363. X
  1364. Xblast.exe   : blast.obj
  1365. X    link/nologo blast.obj, blast.exe;
  1366. X
  1367. Xbuild.exe   : build.obj \LIB\mbase.lib
  1368. X    link/nologo build.obj, build.exe,, mbase.lib;
  1369. X
  1370. Xform.exe    : form.obj form_wr.obj \LIB\mbase.lib
  1371. X    link/nologo form.obj form_wr.obj, form.exe,, mbase.lib;
  1372. X
  1373. Xmbconv.exe  : mbconv.obj \LIB\mbase.lib
  1374. X    link/nologo mbconv.obj, mbconv.exe,, mbase.lib;
  1375. X
  1376. Xreport.exe  : report.obj \LIB\mbase.lib
  1377. X    link/nologo report.obj, report.exe,, mbase.lib;
  1378. X
  1379. Xvr.exe      : vr.obj \LIB\mbase.lib
  1380. X    link/nologo vr.obj, vr.exe,, mbase.lib lcurses.lib;
  1381. X
  1382. Xmbase.lib : entry.obj    lock.obj  input.obj mbase.obj parse.obj \
  1383. X            timedate.obj util1.obj util2.obj cache.obj cache.obj \
  1384. X        create.obj
  1385. X    lib mbase.lib -+entry.obj  -+parse.obj -+input.obj -+mbase.obj;
  1386. X    lib mbase.lib -+util1.obj  -+cache.obj -+util2.obj -+lock.obj;
  1387. X    lib mbase.lib -+create.obj -+timedate.obj;
  1388. X
  1389. END_OF_FILE
  1390.   if test 3856 -ne `wc -c <'src/makefile.dos'`; then
  1391.     echo shar: \"'src/makefile.dos'\" unpacked with wrong size!
  1392.   fi
  1393.   # end of 'src/makefile.dos'
  1394. fi
  1395. if test -f 'src/parse.c' -a "${1}" != "-c" ; then 
  1396.   echo shar: Will not clobber existing file \"'src/parse.c'\"
  1397. else
  1398.   echo shar: Extracting \"'src/parse.c'\" \(1868 characters\)
  1399.   sed "s/^X//" >'src/parse.c' <<'END_OF_FILE'
  1400. X/*
  1401. X * METALBASE 5.0
  1402. X *
  1403. X * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  1404. X *                                       [ t-richj@microsoft.com ]
  1405. X */
  1406. X
  1407. X#define PARSE_C
  1408. X#include "mbase.h"
  1409. X
  1410. Xlong  _lpos = 0L;
  1411. Xint   quoted = 0;
  1412. X
  1413. Xint
  1414. Xskip (f, s)  /* 0 means didn't skip the word, 1 means we did. */
  1415. Xint   f;
  1416. Xchar    *s;
  1417. X{
  1418. X   int   i;
  1419. X   char  a;
  1420. X
  1421. X   i = 0;
  1422. X   _lpos = lseek (f, 0L, 1);
  1423. X
  1424. X   while (s[i] != 0)
  1425. X    {
  1426. X      if (read (f, &a, 1) != 1)  return -1;
  1427. X
  1428. X      if (i != 0 || (i==0 && !iswhite(a)))
  1429. X       { if (s[i] != tolower(a))
  1430. X          { lseek (f, -1L -(long)i, 1);
  1431. X            break;
  1432. X          }
  1433. X         else
  1434. X            i++;
  1435. X       }
  1436. X    }
  1437. X
  1438. X   return (s[i] == 0);
  1439. X}
  1440. X
  1441. Xchar *
  1442. Xgetword (fle)
  1443. Xint      fle;
  1444. X{
  1445. X   int          okay = 1, go = 0;
  1446. X   static char  buffer[256];
  1447. X   char        *ptr,   a;
  1448. X
  1449. X   while (read (fle, &a, 1) == 1)
  1450. X      if (! iswhite (a))  break;
  1451. X   _lpos = lseek (fle, 0L, 1)-1L;
  1452. X
  1453. X   quoted = 0;
  1454. X
  1455. X   for (ptr = buffer; okay; okay = read (fle, &a, 1))
  1456. X    { if (go == 1 && !quoted && istoken (a))
  1457. X       { lseek (fle, -1L, 1);  /* Backup--we don't want the token yet */
  1458. X         break;
  1459. X       }
  1460. X      if (a == '\"')
  1461. X         if (quoted)  break;
  1462. X         else
  1463. X          { quoted = 1;
  1464. X            continue;
  1465. X          }
  1466. X
  1467. X      if (quoted)  *ptr = a;
  1468. X      else
  1469. X         if (iswhite(a)) break;
  1470. X         else            *ptr = tolower(a);
  1471. X
  1472. X      ptr++; go = 1;
  1473. X      if (! quoted && istoken (a))  break;
  1474. X    }
  1475. X   *ptr = 0;
  1476. X
  1477. X   return buffer;
  1478. X}
  1479. X
  1480. Xvoid
  1481. Xgoeol (fle, str)
  1482. Xint    fle;
  1483. Xchar       *str;
  1484. X{
  1485. X   char  a, *ptr;
  1486. X   char  f;
  1487. X
  1488. X   _lpos = lseek (fle, 0L, 1);
  1489. X
  1490. X   for (ptr = str; read (fle, &a, 1) == 1; )
  1491. X    { if (a == '\n' || a == '\r')  break;
  1492. X      if (ptr != NULL)  { *ptr = a; ptr++; }
  1493. X    }
  1494. X   if (ptr != NULL)  *ptr = 0;
  1495. X
  1496. X   if (read (fle, &f, 1) == 1)
  1497. X      if ((f != '\n' && f != '\r') || f == a)
  1498. X         lseek (fle, -1L, 1);
  1499. X}
  1500. X
  1501. END_OF_FILE
  1502.   if test 1868 -ne `wc -c <'src/parse.c'`; then
  1503.     echo shar: \"'src/parse.c'\" unpacked with wrong size!
  1504.   fi
  1505.   # end of 'src/parse.c'
  1506. fi
  1507. if test -f 'src/struct.c' -a "${1}" != "-c" ; then 
  1508.   echo shar: Will not clobber existing file \"'src/struct.c'\"
  1509. else
  1510.   echo shar: Extracting \"'src/struct.c'\" \(2655 characters\)
  1511.   sed "s/^X//" >'src/struct.c' <<'END_OF_FILE'
  1512. X/*
  1513. X * METALBASE 5.0
  1514. X *
  1515. X * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  1516. X *                                       [ t-richj@microsoft.com ]
  1517. X */
  1518. X
  1519. X#ifdef MSDOS
  1520. X#include <stdio.h>
  1521. X#include <stdlib.h>
  1522. X#endif
  1523. X
  1524. Xstruct              /* MP_OPTS: -o=str */
  1525. X { char   a[13];
  1526. X   long   b;
  1527. X   char   c[7];
  1528. X   double d;
  1529. X   char   e[3];
  1530. X } test;
  1531. X
  1532. X#define STDPOS(x) x-(char*)&test
  1533. X#define CHRPOS(x) (char *)&x-(char *)&test
  1534. X
  1535. X/*
  1536. X * In general:
  1537. X *
  1538. X * STRUCT_1 -- SunOS 4, DECStation/ULTRIX
  1539. X *
  1540. X * STRUCT_2 -- COHERENT, DOS/Zortec C
  1541. X *
  1542. X * STRUCT_3 -- SunOS 3, Xenix
  1543. X *
  1544. X * STRUCT_4 -- DOS/Microsoft C, Mac Programmers' Workshop
  1545. X *
  1546. X */
  1547. X
  1548. Xint match_1 = 0;  /* Incremented 1 for each that matches */
  1549. Xint match_2 = 0;  /* Incremented 1 for each that matches */
  1550. Xint match_3 = 0;  /* Incremented 1 for each that matches */
  1551. Xint match_4 = 0;  /* Incremented 1 for each that matches */
  1552. X
  1553. Xint array[][4] =
  1554. X   { {  0,    0,    0,    0 },
  1555. X     { 16,   13,   16,   14 },
  1556. X     { 20,   17,   20,   18 },
  1557. X     { 32,   24,   28,   26 },
  1558. X     { 40,   32,   36,   34 },
  1559. X     { 48,   35,   40,   38 }  };
  1560. X
  1561. Xvoid
  1562. Xprintl (s, n, l)
  1563. Xchar   *s;
  1564. Xint        n, l;
  1565. X{
  1566. X   printf ("%-6.6s : %2d -- %2d,   %2d,   %2d,   %2d\n", s, n,
  1567. X            array[l][0], array[l][1], array[l][2], array[l][3]);
  1568. X
  1569. X   if (n == array[l][0])  match_1++;
  1570. X   if (n == array[l][1])  match_2++;
  1571. X   if (n == array[l][2])  match_3++;
  1572. X   if (n == array[l][3])  match_4++;
  1573. X}
  1574. X
  1575. Xvoid
  1576. Xmain ()
  1577. X{
  1578. X   int  n;
  1579. X
  1580. X   printf ("\n");
  1581. X   printf ("        REAL  STR1  STR2  STR3  STR4\n");
  1582. X   printf ("        ----  ----  ----  ----  ----\n");
  1583. X   printl ("test.a", STDPOS(test.a), 0);
  1584. X   printl ("test.b", CHRPOS(test.b), 1);
  1585. X   printl ("test.c", STDPOS(test.c), 2);
  1586. X   printl ("test.d", CHRPOS(test.d), 3);
  1587. X   printl ("test.e", STDPOS(test.e), 4);
  1588. X   printl ("size  ", sizeof(test),   5);
  1589. X   printf ("\n");
  1590. X
  1591. X   n = 0;
  1592. X   if (match_1 == 6)  n |= 0x01;
  1593. X   if (match_2 == 6)  n |= 0x02;
  1594. X   if (match_3 == 6)  n |= 0x04;
  1595. X   if (match_4 == 6)  n |= 0x08;
  1596. X
  1597. X   if (! n || (n != 1 && n != 2 && n != 4 && n != 8))
  1598. X      {
  1599. X      printf ("Your compiler does not match any of the expected values.\n");
  1600. X      printf ("Look in your compiler manuals for any compile-time switches\n");
  1601. X      printf ("which control structure-packing; add such a switch to\n");
  1602. X      printf ("CFLAGS in the makefile, and recompile struct.\n");
  1603. X      n = 0;
  1604. X      }
  1605. X   else
  1606. X      {
  1607. X      if (n == 4)  n = 3;
  1608. X      if (n == 8)  n = 4;
  1609. X
  1610. X      printf("For your compiler, you need to #define STRUCT_%d when\n", n);
  1611. X      printf("compiling the library... do this by setting -DSTRUCT_%d in\n",n);
  1612. X      printf("the makefile.\n");
  1613. X      }
  1614. X   printf ("\n");
  1615. X
  1616. X   exit (0);
  1617. X}
  1618. X
  1619. END_OF_FILE
  1620.   if test 2655 -ne `wc -c <'src/struct.c'`; then
  1621.     echo shar: \"'src/struct.c'\" unpacked with wrong size!
  1622.   fi
  1623.   # end of 'src/struct.c'
  1624. fi
  1625. echo shar: End of archive 7 \(of 8\).
  1626. cp /dev/null ark7isdone
  1627. MISSING=""
  1628. for I in 1 2 3 4 5 6 7 8 ; do
  1629.     if test ! -f ark${I}isdone ; then
  1630.     MISSING="${MISSING} ${I}"
  1631.     fi
  1632. done
  1633. if test "${MISSING}" = "" ; then
  1634.     echo You have unpacked all 8 archives.
  1635.     rm -f ark[1-9]isdone
  1636. else
  1637.     echo You still must unpack the following archives:
  1638.     echo "        " ${MISSING}
  1639. fi
  1640. exit 0
  1641. exit 0 # Just in case...
  1642.