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

  1. Newsgroups: comp.sources.misc
  2. From: richid@owlnet.rice.edu (Richard Parvin Jernigan)
  3. Subject:  v33i120:  mbase - MetalBase 5.0, Portable database engine, Part02/08
  4. Message-ID: <1992Nov23.231211.2333@sparky.imd.sterling.com>
  5. X-Md4-Signature: 77c922c5af725a12529175b9e9cf65ff
  6. Date: Mon, 23 Nov 1992 23:12:11 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 120
  11. Archive-name: mbase/part02
  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/lock.dox src/build.c src/report.c
  20. # Wrapped by kent@sparky on Mon Nov 23 16:33:12 1992
  21. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  22. echo If this archive is complete, you will see the following message:
  23. echo '          "shar: End of archive 2 (of 8)."'
  24. if test -f 'dox/lock.dox' -a "${1}" != "-c" ; then 
  25.   echo shar: Will not clobber existing file \"'dox/lock.dox'\"
  26. else
  27.   echo shar: Extracting \"'dox/lock.dox'\" \(11453 characters\)
  28.   sed "s/^X//" >'dox/lock.dox' <<'END_OF_FILE'
  29. XRelation Locking                                                  MetalBase 5.0
  30. X-------------------------------------------------------------------------------
  31. X
  32. X
  33. XBecause MetalBase does not run on a client/server basis (there is no huge
  34. Xsuperuser-running program which performs queries when requested), processes
  35. Xmust fight each other for access to relations.  In order to ensure database
  36. Xintegrity, a system of locking has been implemented which provides a timing
  37. Xfor enlarging atomic-level operations; those which should be accomplished in
  38. Xtheir entireity before another process may use the relation.  *nix-style file
  39. Xlocking is not portable enough to be useful here, much to my dismay... many
  40. Xsites don't have any such ability through the standard compiler; worse, on
  41. Xsome systems (Xenix among them), calls succeed when they don't do anything.
  42. X
  43. XVersion 5.0 supports two forms of locking, one of which is transparent and
  44. Xused by the system internally, the other being exposed to the user.  "Temporary
  45. Xlocks" are placed by the system to enlarge the timescale of atomic operations,
  46. Xto ensure concurrent querying/updating will not corrupt the database.  These
  47. Xlocks are placed on the relation automatically, during all operations--adding,
  48. Xupdating, deleting, and querying the database, as well as in the process of
  49. Xallowing the user to place the more permanent exclusive locks.
  50. X
  51. X
  52. XTEMPORARY LOCKS ---------------------------------------------------------------
  53. X
  54. X
  55. XTemporary locks are used to give a single process control over a relation for
  56. X   a short period of time; from a fraction of a second (needed at the beginning
  57. X   of a service call to ensure no exclusive lock is in place) to a few seconds
  58. X   or more (for the duration of an index update).  The basic algorithm relies
  59. X   on the fact that each process has a unique identifier in the range 0-65535;
  60. X   MetalBase 5.0 uses the process ID for this.  In essence, each relation
  61. X   stamps its identifier into a specific place in the relation, and reads back
  62. X   what is there--if it reads its own process ID, it continues with its work,
  63. X   leaving the stamp (and thus a temporary lock) in place.
  64. X
  65. XThat is a far oversimplified version.  In reality, because of the way most
  66. X   multi-user systems work, a scheme with only one such check will always grant
  67. X   a lock to any relation; the write-our-PID followed by read-the-byte will
  68. X   almost always return the PID simply because the write and read are so close
  69. X   together that they are almost guaranteed to be processed in sequence,
  70. X   without allowing other processes to vie for the lock.  There is also the
  71. X   issue of a process having terminated, leaving a temporary lock in place; in
  72. X   that case, the relation would be useless until such a lock could be cleared.
  73. X   Moreover, in practice, such a scheme would give control to the same process
  74. X   over and over, not allowing any other process a chance to work (in
  75. X   benchmarks, three terminals running the same program to add records
  76. X   constantly over 30 seconds ended up with results of the form: 1st==500
  77. X   records, 2nd==2 records, 3rd==0 records).
  78. X
  79. XThe first problem is the granting of a temporary lock to one process at any
  80. X   given time--this is done by iterating the check described above three times:
  81. X
  82. X     set_hack(): read the three hack-lock positions (6 bytes)
  83. X                 if our PID is in any, write a zero there and goto set_hack()
  84. X                 if all three aren't zeroes, goto set_hack()
  85. X                 write our PID in the third
  86. X
  87. X                 read the three hack-lock positions
  88. X                 if first and second aren't zeroes, goto set_hack()
  89. X                 if third isn't our PID, goto set_hack()
  90. X                 write our PID in the second
  91. X
  92. X                 read the three hack-lock positions
  93. X                 if first isn't zeroes, goto set_hack()
  94. X                 if second and third aren't our PID, goto set_hack()
  95. X                 write our PID in the first
  96. X
  97. X                 read the three hack-lock positions
  98. X                 if any of the three aren't our PID, goto set_hack()
  99. X
  100. X     clr_hack(): read the three hack-lock positions (6 bytes)
  101. X                 if all three aren't our PID,
  102. X                    (error case 1--abort)
  103. X                 write zeroes in all three
  104. X
  105. XIterating the process in this fashion shrinks the window for a race condition
  106. X   to such an extent that it's negligible... and that solves the first of the
  107. X   three problems.  The second would be distribution of resources; as the
  108. X   example above, just letting them take their chances doesn't cut it.  To
  109. X   more evenly distribute access to a relation, a hack lock, as described
  110. X   above, is used to gain access to a thirty-position queue, of the form:
  111. X
  112. X elcks hacklocks  queue
  113. X  [ ]   [ | | ]   [ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ]
  114. X
  115. XThe leftmost position in the queue is position #0, indicating a temporary lock
  116. X   is in place.  Once a relation has gained control via a hack lock, it reads
  117. X   the 60 bytes of queue and finds the first empty slot (denoted by a 0 PID).
  118. X   It then places its own PID in that position and clears the hacklock for any
  119. X   other process to use.  If the position found free is #0, it has just placed
  120. X   a temporary lock and the process can go about its service.  Otherwise, the
  121. X   process will enter the following loop:
  122. X
  123. X             A: read queue position (CURPOS -1)
  124. X                if non-zero, go to A
  125. X                write our PID in position (CURPOS -1)
  126. X                write zero in position (CURPOS)
  127. X                CURPOS -= 1
  128. X                if (CURPOS == 0)  break -- temporary lock is in place
  129. X                otherwise, goto A
  130. X
  131. XThis loop works without placing hacklocks before reading because exactly one
  132. X   process is guaranteed to be reading a position in the queue at any given
  133. X   time, and the free space will bubble from the left to right as temporary
  134. X   locks are freed.  Note that if a position in the queue can't be found to
  135. X   start with, the system will return MB_BUSY.  This method ensures equal time
  136. X   for equal requests, for up to thirty processes simultaneously; note that
  137. X   many more processes can be run at once on a relation, but only thirty
  138. X   queries will be serviced at any time.  This is an extremely reasonable
  139. X   restriction.
  140. X
  141. XThe third and final problem with regard to locking is the most nerve-wracking;
  142. X   if a process dies, leaving a lock in place, other processes will wait
  143. X   forever for the lock to be cleared.  Originally, the BLAST utility was the
  144. X   only way to remove these locks; pre-release 5.0 was able to detect this
  145. X   condition under some circumstances, but it was too flaky to rely upon.  In
  146. X   essence, since inter-process communication is a no-no for portability,
  147. X   MetalBase needed a way to determine if a process were still active or not...
  148. X   to that end, the temporary-lock queue has been equipped with a strobe byte
  149. X   for each position:
  150. X
  151. X elcks hacklocks  queue
  152. X  [ ]   [ | | ]   [ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ]
  153. X                  < : : : : : : : : : : : : : : : : : : : : : : : : : : : : : >
  154. X                  strobes
  155. X
  156. XWhenever a process is either waiting in the queue for a turn, or when a process
  157. X   has a temporary lock in place and is querying or updating the database, it
  158. X   is constantly incrementing a byte found in the current lock position's
  159. X   strobe... to be exact, within the queue, the strobe is changed every second;
  160. X   within a query, whenever the depth is a multiple of 5; within an update,
  161. X   at various locations initially followed by a call at every _balance() call.
  162. X   If a process waiting in the queue finds that three seconds go by without
  163. X   a strobe being changed, it determines itself justified in taking over the
  164. X   position, under the assumption that the old process is dead.  Note that
  165. X   this approach will not work well with DOS networks, which often bring long
  166. X   lag-times which would destroy concurrency... not always, but often enough
  167. X   to worry about.  IPC would be the best way to improve this, but there is
  168. X   no standard which does not require superuser access and device drivers on
  169. X   any *nix platform, and that's unacceptable for MB.
  170. X
  171. XWhen jockying for a hacklock, if three seconds elapse without a request being
  172. X   accepted, a process will erase all three bytes and try again.  If a process
  173. X   halts with an exclusive lock in place (mb_rmv(), mb_exit() and mb_die()
  174. X   remove any locks before closing, so that's not a problem--the process must
  175. X   be halted by a signal or power cycle), the exclusive lock must be removed
  176. X   with BLAST before the relation will be useful again.
  177. X
  178. X
  179. XEXCLUSIVE (RELATION-WIDE) LOCKS -----------------------------------------------
  180. X
  181. X
  182. XAn exclusive lock is placed by a user using mb_lck(), and removed with mb_unl()
  183. X   [these two functions were forgotten in the 4.0 release--sorry].  Once an
  184. X   exclusive lock is placed, any action requested by another process will fail
  185. X   with the error MB_LOCKED, until the lock is removed.
  186. X
  187. XThe flow for mb_lck() and mb_unl() are as follows:
  188. X
  189. X   mb_lck():  set temporary lock on relation
  190. X              read exclusive-lock PID
  191. X              if (PID == ours)  clear temp lock; stupid--you already locked it
  192. X              if (PID != 0)     clear temp lock; return MB_LOCKED by another
  193. X              write our PID in the exclusive-lock byte
  194. X              clear temp lock; return MB_OKAY
  195. X
  196. X   mb_unl():  set temporary lock on relation
  197. X              read exclusive-lock PID
  198. X              if (PID == ours)
  199. X                 write 0 there
  200. X              clear temp lock; return MB_OKAY
  201. X
  202. XThis simple procedure works, because all requests of the relation must pass
  203. Xthe following check before operating (a temporary lock must be in place before
  204. Xcalling this routine):
  205. X
  206. X   _chk_elck(): check exclusive-lock PID
  207. X                if (PID != 0 && PID != ours)  return MB_LOCKED
  208. X                return MB_OKAY
  209. X
  210. XThese routines are slightly more complicated in the source, because there is
  211. Xa bit of duality of information--each relation structure also retains flags
  212. Xindicating whether the relation is temp-locked and/or exclusive-locked.  There
  213. Xare more failure conditions because of this, which ensures that locks will
  214. Xnot be placed when they should not be.
  215. X
  216. X
  217. XEXCLUSIVE (RECORD-LEVEL) LOCKS ------------------------------------------------
  218. X
  219. X
  220. XThere are none in MetalBase 5.0--let me know if you need this, so I'll know
  221. Xwhat to spend my time on.  It'll be in a later version--just a matter of when.
  222. X
  223. X
  224. XLOCKFILES ---------------------------------------------------------------------
  225. X
  226. X
  227. XThe kind of work described above--all the busyloops and so forth--really,
  228. Xreally slow down access to a relation... the fewer things a file is doing at
  229. Xany given time, the more you can get accomplished.  So the locking mechanism
  230. Xhas been moved off to a separate file, named after the relation (with the
  231. Xextension replaced with ".LCK") and placed in a temporary directory (see
  232. Xtrouble.dox, under MB_TMPDIR).  This file is created every time mb_inc() is
  233. Xcalled, if it does not already exist.
  234. X
  235. XThere is exactly one lockfile for each relation, kept in a common temporary
  236. Xdirectory.  You can delete the lockfile at any time, as long as you're sure
  237. Xthere is no one using the relation at the time (this would be a Bad Thing to
  238. Xdelete if the relation is in use).  Deleting the lockfile will erase any
  239. Xremaining exclusive lock, and reset the number of users on the relation to
  240. Xzero.
  241. X
  242. END_OF_FILE
  243.   if test 11453 -ne `wc -c <'dox/lock.dox'`; then
  244.     echo shar: \"'dox/lock.dox'\" unpacked with wrong size!
  245.   fi
  246.   # end of 'dox/lock.dox'
  247. fi
  248. if test -f 'src/build.c' -a "${1}" != "-c" ; then 
  249.   echo shar: Will not clobber existing file \"'src/build.c'\"
  250. else
  251.   echo shar: Extracting \"'src/build.c'\" \(19729 characters\)
  252.   sed "s/^X//" >'src/build.c' <<'END_OF_FILE'
  253. X/*
  254. X * METALBASE 5.0
  255. X *
  256. X * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  257. X *                                       [ t-richj@microsoft.com ]
  258. X *
  259. X * Special thanks go to Mike Cuddy (mcuddy@fensende.rational.com) for his
  260. X * suggestions and code.
  261. X *
  262. X */
  263. X
  264. X#define BLAST_C  /* I know, I know... */
  265. X#include "mbase.h"
  266. X#include "internal.h"
  267. X
  268. X#define cr(x) ((x) == 0) ? DUBCR : SNGCR
  269. X
  270. X#ifdef MSDOS
  271. X#define DESCLINE "/*\r\n * This file was created by MetalBase version 5.0 to reflect the structure\r\n * of the relation \"%s\".\r\n *\r\n * MetalBase 5.0 released October 1st, 1992 by richid@owlnet.rice.edu\r\n *\r\n */\r\n\r\ntypedef struct\r\n { "
  272. X#else
  273. X#define DESCLINE "/*\n * This file was created by MetalBase version 5.0 to reflect the structure\n * of the relation \"%s\".\n *\n * MetalBase 5.0 released October 1st, 1992 by virtual!richid@owlnet.rice.edu\n *\n */\n\ntypedef struct\n { "
  274. X#endif
  275. X
  276. X#define lineF "Fields______________________________________________________%s"
  277. X#define lineI "\nIndices_____________________________________________________%s"
  278. X
  279. X#define RBC '}'
  280. X
  281. X#ifdef LONGARGS
  282. X   void   strlwrcpy (char *, char *);
  283. X   void   struprcpy (char *, char *);
  284. X   void   strmax    (char *, int);
  285. X   char  *repeat    (char,   int);
  286. X   void   main      (int,    char **);
  287. X   void   endoffile (int,    int);
  288. X   int    get_names (int,    char **);
  289. X   void   write_it  (int,    int);
  290. X   int    contains_serial (char *);
  291. X#else
  292. X   void   strlwrcpy();
  293. X   void   struprcpy();
  294. X   void   strmax();
  295. X   char  *repeat();
  296. X   void   main();
  297. X   void   endoffile();
  298. X   int    get_names();
  299. X   void   write_it();
  300. X   int    contains_serial();
  301. X#endif
  302. X
  303. X#define  Printf   if (!quiet)  printf
  304. X#define fPrintf   if (!quiet) fprintf
  305. X
  306. X#define qt(x) (quiet ? "" : x)
  307. X
  308. X#define usage() \
  309. X   fprintf (stderr, "build: format: build [-q] [-h] schema.s%s", SNGCR);
  310. X
  311. X#define fatal() { \
  312. X                fflush(stdout); \
  313. X                fprintf(stderr,"Cannot build relation--%s.%s",mb_error,SNGCR); \
  314. X                break; \
  315. X                }
  316. X
  317. X#define comment() skip(fh,";"); while (skip (fh, "#"))  goeol(fh,NULL);
  318. X
  319. X/*
  320. X ******************************************************************************
  321. X *
  322. X */
  323. X
  324. Xstatic char *types[] =
  325. X { "char *", "short", "ushort", "long", "ulong",  "float",
  326. X   "double", "money", "time",   "date", "serial", "phone"  };
  327. X
  328. X/*
  329. X ******************************************************************************
  330. X *
  331. X */
  332. X
  333. Xrelation *data;
  334. X
  335. Xchar   strname[40] = "";     /* Structure name (set by "typedef") */
  336. Xchar   rel[128],  hdr[128];  /* Filenames for relation and header */
  337. Xchar   names[20], nameb[40]; /* Name, and upper-case name         */
  338. Xint    column=1;             /* Column we're displaying data in   */
  339. Xint    header=0,  quiet=0;   /* Set by -q and -h on command-line  */
  340. Xint    num_f=0,   num_i=0;   /* Start with 0 fields and 0 indices */
  341. Xint    hasser=0;             /* 1 if we encounter a serial field  */
  342. X
  343. X
  344. X/*
  345. X ******************************************************************************
  346. X *
  347. X */
  348. X
  349. Xvoid
  350. Xmain  (argc, argv)
  351. Xint    argc;
  352. Xchar **argv;
  353. X{
  354. X   int        stage;     /* Processing stage; 1==fields, 2==indices, 3==done */
  355. X   int        fh;        /* File handle for schema                           */
  356. X   char       name[20];  /* Field/Index name                                 */
  357. X   ftype      typ;       /* Field type (or, for indices, 0==nodups, 1==dups) */
  358. X   int        siz;       /* Field size (for character arrays only)           */ 
  359. X   int        isExt;     /* TRUE if it's an external type, FALSE if not      */
  360. X   char       desc[128]; /* Character array of field numbers, for indices    */
  361. X
  362. X   long       nexts = 0L;
  363. X   char       temp[128];
  364. X   char       t2[128];
  365. X   int        i;
  366. X
  367. X
  368. X   fh = get_names (argc, argv);  /* fh = file handle of relation */
  369. X
  370. X   if ((data = mb_new()) == RNULL)
  371. X      {
  372. X      fprintf (stderr, "Cannot build relation--%s.%s", mb_error, SNGCR);
  373. X      exit(1);
  374. X      }
  375. X
  376. X   for (stage = 1; stage != 3; )
  377. X      {
  378. X      strlwrcpy (temp, getword (fh));   /* temp = keyword */
  379. X
  380. X      if (! strcmp (temp, "field"))
  381. X         {
  382. X         if (stage == 2)  /* Done with fields? */
  383. X            {
  384. X            fflush (stdout);
  385. X            fprintf (stderr, "%s%sField %s declared after indices.%s",
  386. X                     qt(SNGCR), qt(cr(column)), getword(fh), SNGCR);
  387. X            break;
  388. X            }
  389. X
  390. X         strlwrcpy (temp, getword (fh));  /* New field?  Obtain, in lower,  */
  391. X         strmax (temp, 20);            /* its name.  Put it in 'temp' first */
  392. X         strcpy (name, temp);          /* in case it's really long.         */
  393. X
  394. X         if (mb_getname (data, name, 0) != -1)
  395. X            {
  396. X            fflush  (stdout);
  397. X            fprintf (stderr, "%sField %s declared twice.%s", qt(cr(column)),
  398. X                             name, SNGCR);
  399. X            break;
  400. X            }
  401. X
  402. X         (void)skip (fh, "type");         /* Got its name, and it's new.  So */
  403. X         strlwrcpy (temp, getword (fh));  /* get its field type...           */
  404. X
  405. X         isExt = 0;
  406. X         if (! strcmp (temp, "extern") || ! strcmp (temp, "external"))
  407. X            {
  408. X            isExt = 1;
  409. X            strlwrcpy (temp, getword (fh));  /* External?  Get the next word. */
  410. X            }
  411. X
  412. X         typ = (ftype)-1;
  413. X         if (! strcmp (temp, "char") || ! strcmp (temp, "character") ||
  414. X             ! strcmp (temp, "string"))
  415. X            {
  416. X            typ = T_CHAR;
  417. X            }
  418. X         if (! strcmp (temp, "short"))    typ = T_SHORT;
  419. X         if (! strcmp (temp, "ushort"))   typ = T_USHORT;
  420. X         if (! strcmp (temp, "long"))     typ = T_LONG;
  421. X         if (! strcmp (temp, "ulong"))    typ = T_ULONG;
  422. X         if (! strcmp (temp, "float"))    typ = T_FLOAT;
  423. X         if (! strcmp (temp, "double"))   typ = T_DOUBLE;
  424. X         if (! strcmp (temp, "money"))    typ = T_MONEY;
  425. X         if (! strcmp (temp, "time"))     typ = T_TIME;
  426. X         if (! strcmp (temp, "date"))     typ = T_DATE;
  427. X         if (! strcmp (temp, "serial"))   typ = T_SERIAL;
  428. X         if (! strcmp (temp, "phone"))    typ = T_PHONE;
  429. X
  430. X         if (typ == (ftype)-1)
  431. X            {
  432. X            fflush  (stdout);
  433. X            fprintf (stderr, "%sType %s (field %s) undefined.%s",
  434. X                             qt(cr(column)), temp, name, SNGCR);
  435. X            break;
  436. X            }
  437. X
  438. X         if (isExt)
  439. X            {
  440. X            sprintf (temp, "ix_%s", name);
  441. X            sprintf (desc, "%d",    num_i);
  442. X
  443. X            if (mb_addindex (data, temp, 1, desc) != MB_OKAY)
  444. X               fatal();
  445. X
  446. X            if (typ == T_SERIAL)
  447. X               typ = T_LONG;
  448. X            }
  449. X
  450. X         if (typ == T_SERIAL)
  451. X            {
  452. X            if (hasser)
  453. X               {
  454. X               fflush  (stdout);
  455. X               fprintf (stderr, "%sMore than one serial field specified.%s",
  456. X                        qt(cr (column)), SNGCR);
  457. X               break;
  458. X               }
  459. X            hasser = 1;
  460. X
  461. X            if (skip (fh, "start"))
  462. X               nexts = atol (getword (fh));
  463. X            }
  464. X
  465. X         switch (typ)
  466. X            {
  467. X            case T_CHAR:
  468. X               (void)skip (fh, "length");
  469. X               (void)skip (fh, "*");
  470. X               siz = atoi (getword(fh));
  471. X               sprintf (temp, "%s [%s%d]", name, types[(int)typ], siz);
  472. X               mb_addfield (data, name, T_CHAR, siz);
  473. X               break;
  474. X
  475. X            case T_SERIAL:
  476. X               sprintf (temp, "%s [%s @%ld]", name, types[(int)typ], nexts);
  477. X               mb_addfield (data, name, T_SERIAL, nexts);
  478. X               break;
  479. X
  480. X            default:
  481. X               sprintf (temp, "%s [%s]", name, types[(int)typ]);
  482. X               mb_addfield (data, name, typ, 0);
  483. X               break;
  484. X            }
  485. X
  486. X         if (mb_errno)
  487. X            fatal();
  488. X
  489. X         if ((column = 1-column) == 0)
  490. X            { Printf ("%s%-30.30s%s", SUBD, temp, NORM); }
  491. X         else
  492. X            { Printf ("%s%s%s%s",     SUBD, temp, NORM, SNGCR); }
  493. X
  494. X         num_f ++;
  495. X
  496. X         comment();
  497. X
  498. X         continue;
  499. X         }
  500. X
  501. X      if (strcmp (temp, "index") == 0)
  502. X         {
  503. X         if (stage == 1)
  504. X            {
  505. X            if (column == 0)
  506. X               Printf (SNGCR);
  507. X
  508. X            if (num_f == 0)
  509. X               {
  510. X               fflush  (stdout);
  511. X               fprintf (stderr, "%sNo fields declared before indices.%s",
  512. X                                 qt(SNGCR), SNGCR);
  513. X               break;
  514. X               }
  515. X
  516. X            Printf (lineI, SNGCR);
  517. X
  518. X            stage  = 2;
  519. X            column = 1;
  520. X            }
  521. X
  522. X         strlwrcpy (temp, getword (fh));  /* New index?  Get the name (in   */
  523. X         strmax (temp, 20);            /* temp first in case it's long) and */
  524. X         strcpy (name, temp);          /* make sure it's unique.            */
  525. X
  526. X         if (mb_getname (data, name, 1) != -1)
  527. X            {
  528. X            fflush  (stdout);
  529. X            fprintf (stderr, "%sField %s declared twice.%s", qt(cr(column)),
  530. X                             name, SNGCR);
  531. X            break;
  532. X            }
  533. X
  534. X         (void)skip (fh, "on");
  535. X
  536. X         for (temp[0] = desc[0] = 0; ; )
  537. X            {
  538. X            strlwrcpy (t2, getword (fh));
  539. X
  540. X            if ((i = mb_getname (data, t2, 0)) == -1)
  541. X               {
  542. X               fflush  (stdout);
  543. X               fprintf (stderr, "%sIndex placed on undeclared field %s.%s",
  544. X                                qt(cr(column)), t2, SNGCR);
  545. X               exit (1);
  546. X               }
  547. X
  548. X            strcat  (temp, t2);
  549. X            sprintf (t2, "%d", i);
  550. X            strcat  (desc, t2);
  551. X
  552. X            if (! skip (fh, ","))
  553. X               break;
  554. X
  555. X            strcat (temp, ",");
  556. X            strcat (desc, ",");
  557. X            }
  558. X
  559. X         Printf ("%s%s", name, repeat ('.', 15-strlen (name)));
  560. X         Printf ("%s%s", temp, repeat ('.', 22-strlen (temp)));
  561. X
  562. X         typ = (ftype)0;
  563. X
  564. X         if (skip (fh, "without"))
  565. X            {
  566. X            if (skip (fh, "duplicates") || skip (fh, "dups"))
  567. X               typ = (ftype)0;
  568. X            else
  569. X               typ = (ftype)2;
  570. X            }
  571. X         else if (skip (fh, "with"))
  572. X            {
  573. X            if (skip (fh, "duplicates") || skip (fh, "dups"))
  574. X               typ = (ftype)1;
  575. X            else
  576. X               typ = (ftype)2;
  577. X            }
  578. X         if (typ == (ftype)2)
  579. X            {
  580. X            fflush  (stdout);
  581. X            fprintf (stderr, "?%sIncorrect syntax%s", qt(DUBCR), SNGCR);
  582. X            exit    (1);
  583. X            }
  584. X
  585. X         if ((int)typ)  { Printf ("Duplicates allowed%s", SNGCR);     }
  586. X         else           { Printf ("Duplicates not allowed%s", SNGCR); }
  587. X
  588. X         if (contains_serial (desc))
  589. X            typ = (ftype)1;
  590. X
  591. X         if (mb_addindex (data, name, (int)typ, desc) != MB_OKAY)
  592. X            {
  593. X            fatal();
  594. X            }
  595. X
  596. X         num_i ++;
  597. X
  598. X         comment();
  599. X
  600. X         continue;
  601. X         }
  602. X
  603. X      if (strcmp (temp, "end") == 0 || temp[0] == 0)
  604. X         {
  605. X         Printf ("%s", cr (column));
  606. X         endoffile (num_f, num_i);
  607. X         stage = 3;
  608. X
  609. X         continue;
  610. X         }
  611. X
  612. X      if (! strcmp (temp, "typedef"))
  613. X         {
  614. X         strlwrcpy (strname, getword (fh));
  615. X         continue;
  616. X         }
  617. X
  618. X      fflush  (stdout);
  619. X      fprintf (stderr, "%sIdentifier %s%s%s not recognized.%s",
  620. X                       qt(cr(column)), BOLD, temp, NORM, SNGCR);
  621. X      exit    (1);
  622. X      }
  623. X
  624. X   if (stage != 3)
  625. X      {
  626. X      exit (1);
  627. X      }
  628. X
  629. X   write_it (num_i, num_f);
  630. X
  631. X   Printf ("Relation created -- zero entries.%s", SNGCR);
  632. X
  633. X   exit (0);
  634. X}
  635. X
  636. Xvoid
  637. Xwrite_it (num_i, num_f)
  638. Xint       num_i, num_f;
  639. X{
  640. X   char  temp[512], temp2[30];
  641. X   int   R, H;
  642. X   int   i, j;
  643. X
  644. X
  645. X   if ((R = openx (rel, OPENMODE)) != -1)
  646. X      {
  647. X      if (read (R, temp, 1) != -1)
  648. X         {
  649. X         if (temp[0] != 50 && temp[0] != 42)  /* Check for 4.1a or 5.0 sig */
  650. X            {
  651. X            fPrintf (stderr, "%s%s%s%32.32s%-28.28s%s%s", SNGCR, SUBD, INVR,
  652. X                             "*** ERR", "OR ***", NORM, SNGCR);
  653. X            fprintf (stderr,
  654. X                     "%s   This relation is not in MetalBase 5.0 format.%s",
  655. X                     qt(SNGCR), DUBCR);
  656. X            close (R);
  657. X            exit (1);
  658. X            }
  659. X         }
  660. X
  661. X      Printf ("%s%s%32.32s%-28.28s%s%s", SUBD, INVR, "** WARN", "ING **", NORM,
  662. X                                         SNGCR);
  663. X      Printf ("%s   The file about to be created already exists under the%s",
  664. X               SNGCR, SNGCR);
  665. X      Printf ("         target directory!  This data will be lost!%s", DUBCR);
  666. X
  667. X      close (R);
  668. X      }
  669. X
  670. X/*
  671. X * That was ugly.  Now make sure they wanna continue first...
  672. X *
  673. X */
  674. X
  675. X   if (! quiet)
  676. X      {
  677. X      Printf ("Continue with the creation of the relation [Y/n] ? ");
  678. X      gets(temp);  i = (int)temp[0];
  679. X      if (i == 'n' || i == 'N' || i == 'q' || i == 'Q')  exit (0);
  680. X      }
  681. X
  682. X   if (header || quiet)
  683. X      {
  684. X      i = (header ? 'y' : 'n');
  685. X      }
  686. X   else
  687. X      {
  688. X      Printf ("Create header file for this relation       [y/N] ? ");
  689. X      fflush(stdin);  gets(temp);  i = (int)temp[0];
  690. X      }
  691. X   Printf (SNGCR);
  692. X
  693. X/*
  694. X * That was uglier.  At any rate, we now have permission to create the thing:
  695. X *
  696. X */
  697. X
  698. X   if (mb_create (data, rel, 0) != MB_OKAY)
  699. X      {
  700. X      fflush(stdout);
  701. X      fprintf (stderr, "Cannot build relation--%s%s.", mb_error, SNGCR);
  702. X      return;
  703. X      }
  704. X
  705. X/*
  706. X * Now if they want the header created, we've gotta do all kindsa special shit:
  707. X *
  708. X */
  709. X
  710. X   if (i != 'y' && i != 'Y')
  711. X      {
  712. X      return;
  713. X      }
  714. X
  715. X   if ((H = openx (hdr, O_RDWR)) != -1)
  716. X      {
  717. X      close  (H);
  718. X      unlink (hdr);
  719. X      }
  720. X   if ((H = creatx (hdr)) == -1)
  721. X      {
  722. X      fprintf (stderr, "%sSorry--cannot create header file%s", qt(DUBCR),
  723. X               SNGCR);
  724. X      return;
  725. X      }
  726. X   modex (hdr, 0666);   /* Make the file   -rw-rw-rw-  */
  727. X
  728. X   sprintf (temp, "#ifndef %s_H%s", nameb, SNGCR);
  729. X   writx   (H, temp, strlen(temp));
  730. X   sprintf (temp, "#define %s_H%s", nameb, DUBCR);
  731. X   writx   (H, temp, strlen(temp));
  732. X   sprintf (temp, DESCLINE, names);
  733. X   writx   (H, temp, strlen(temp));
  734. X
  735. X   for (j = 0; j < data->num_f; j++)
  736. X      {
  737. X      switch (data->type[j])
  738. X         {
  739. X         case T_CHAR:   sprintf (temp, "char     %s[%d];",
  740. X                                        data->name[j], data->siz[j]);   break;
  741. X         case T_SHORT:  sprintf (temp, "short    %s;", data->name[j]);  break;
  742. X         case T_USHORT: sprintf (temp, "ushort   %s;", data->name[j]);  break;
  743. X         case T_LONG:   sprintf (temp, "long     %s;", data->name[j]);  break;
  744. X         case T_ULONG:  sprintf (temp, "ulong    %s;", data->name[j]);  break;
  745. X         case T_FLOAT:  sprintf (temp, "float    %s;", data->name[j]);  break;
  746. X         case T_DOUBLE: sprintf (temp, "double   %s;", data->name[j]);  break;
  747. X         case T_MONEY:  sprintf (temp, "double   %s;", data->name[j]);  break;
  748. X         case T_TIME:   sprintf (temp, "mb_time  %s;", data->name[j]);  break;
  749. X         case T_DATE:   sprintf (temp, "mb_date  %s;", data->name[j]);  break;
  750. X         case T_PHONE:  sprintf (temp, "mb_phone %s;", data->name[j]);  break;
  751. X         default:       sprintf (temp, "long     %s;", data->name[j]);  break;
  752. X         }
  753. X
  754. X      i = 24;
  755. X      if (data->type[j] == T_CHAR)
  756. X         i -= 3 +(data->siz[j] >10) +(data->siz[j] >100) +(data->siz[j] >1000);
  757. X
  758. X      strcat (temp, repeat (' ', i-strlen(data->name[j])));
  759. X
  760. X      strcat (temp, "/");
  761. X      strcat (temp, "* field ");
  762. X      strcat (temp, data->name[j]);
  763. X      strcat (temp, " type ");
  764. X
  765. X      if (data->type[j] != T_CHAR)
  766. X         {
  767. X         strcat (temp, types[(int)data->type[j]]);
  768. X         }
  769. X      else
  770. X         {
  771. X         sprintf (nameb, "string length %d", data->siz[j]);
  772. X         strcat  (temp, nameb);
  773. X         }
  774. X      if (data->type[j] == T_SERIAL && data->serial != 0L)
  775. X         {
  776. X         sprintf (nameb, " start %ld", data->serial);
  777. X         strcat  (temp, nameb);
  778. X         }
  779. X      strcat (temp, repeat (' ', 73-strlen (temp)));
  780. X      strcat (temp, " *");
  781. X      strcat (temp, "/");
  782. X      strcat (temp, SNGCR);
  783. X      strcat (temp, "   ");
  784. X      writx (H, temp, strlen (temp));
  785. X      }
  786. X
  787. X   if (strname[0])
  788. X      {
  789. X      strcpy (temp2, strname);
  790. X      }
  791. X   else
  792. X      {
  793. X      strcpy (strname, names);
  794. X      strcat (strname, "_str");
  795. X      strcpy (temp2,   names);
  796. X      }
  797. X
  798. X   strcat (temp2, "_rec");
  799. X
  800. X   sprintf (temp, "%c %s;%s", RBC, strname, DUBCR);
  801. X   writx   (H, temp, strlen (temp));
  802. X
  803. X   sprintf (temp, "#ifndef MODULE%s   %s %s;%s",
  804. X            SNGCR, strname, temp2, SNGCR);
  805. X   writx   (H, temp, strlen (temp));
  806. X
  807. X   sprintf (temp, "#else%s   extern %s %s;%s#endif%s#endif%s",
  808. X            SNGCR, strname, temp2, SNGCR, DUBCR, DUBCR);
  809. X   writx   (H, temp, strlen (temp));
  810. X
  811. X   Printf  ("Header file created.%s", SNGCR);
  812. X   close   (H);
  813. X}
  814. X
  815. Xvoid
  816. Xendoffile (num_f, num_i)
  817. Xint        num_f, num_i;
  818. X{
  819. X   if (num_f == 0)
  820. X      {
  821. X      fprintf (stderr, "No fields declared before end reached%s", SNGCR);
  822. X      exit    (1);
  823. X      }
  824. X   if (num_i == 0)
  825. X      {
  826. X      fprintf (stderr, "No indices declared before end reached%s", SNGCR);
  827. X      exit    (1);
  828. X      }
  829. X}
  830. X
  831. Xvoid
  832. Xstrlwrcpy (new, old)
  833. Xchar      *new,*old;
  834. X{
  835. X   register char *a,*b;
  836. X   if (!new || !old)  return;
  837. X   for (a=new,b=old; *b; a++,b++)
  838. X      *a = tolower (*b);
  839. X   *a=0;
  840. X}
  841. X
  842. Xvoid
  843. Xstruprcpy (new, old)
  844. Xchar      *new,*old;
  845. X{
  846. X   register char *a,*b;
  847. X   if (!new || !old)  return;
  848. X   for (a=new,b=old; *b; a++,b++)
  849. X      *a = toupper (*b);
  850. X   *a=0;
  851. X}
  852. X
  853. Xvoid
  854. Xstrmax (str, siz)
  855. Xchar   *str;
  856. Xint          siz;
  857. X{
  858. X   register int   i;
  859. X   register char *a;
  860. X
  861. X   for (i=0, a=str; *a; i++, a++)
  862. X      if (i == siz)
  863. X         {
  864. X         *a = 0;
  865. X         break;
  866. X         }
  867. X}
  868. X
  869. Xint
  870. Xget_names (agc, agv)
  871. Xint        agc;
  872. Xchar          **agv;
  873. X{
  874. X   char  temp[128];
  875. X   int   i, fh;
  876. X
  877. X   while (agc > 1 && agv[1][0] == '-')
  878. X      {
  879. X      switch (agv[1][1])
  880. X         {
  881. X         case 'q':  quiet  = 1;  break;
  882. X         case 'h':  header = 1;  break;
  883. X         default:   fprintf (stderr,"unrecognized option '%s'%s",agv[1],SNGCR);
  884. X                    usage   ();
  885. X                    exit    (1);
  886. X                   break;
  887. X         }
  888. X      switch (agv[1][2])
  889. X         {
  890. X         case 'q':  quiet  = 1;  break;
  891. X         case 'h':  header = 1;  break;
  892. X         }
  893. X
  894. X      agc--;  agv++;
  895. X      }
  896. X
  897. X   if (agc != 2)
  898. X      {
  899. X      usage ();
  900. X      exit  (1);
  901. X      }
  902. X
  903. X   strcpy (temp, agv[1]);
  904. X   if (strcmp (&temp[strlen(temp)-2], ".s"))
  905. X      strcat (temp, ".s");
  906. X
  907. X   strcpy (rel, temp);
  908. X
  909. X   for (i = strlen(temp)-1; i > -1 && temp[i] != ':' && temp[i] != DIRSEP; i--)
  910. X      ;
  911. X   if (i < 0)  i = 0;
  912. X
  913. X   rel[i] = 0;
  914. X
  915. X   if ((fh = openx (temp, O_RDONLY)) == -1)
  916. X      {
  917. X      fprintf (stderr, "cannot open %s.%s", temp, SNGCR);
  918. X      exit    (1);
  919. X      }
  920. X
  921. X   comment();
  922. X
  923. X   (void)skip (fh, "relation");
  924. X
  925. X   strcpy (temp, getword (fh));
  926. X
  927. X   if (temp[0] == 0)
  928. X      {
  929. X      fprintf (stderr, "file holds no schema definition.%s",SNGCR);
  930. X      exit    (1);
  931. X      }
  932. X
  933. X   Printf ("%s", CLS);
  934. X   Printf ("Building relation %s under ", temp);
  935. X
  936. X   strlwrcpy (names, temp);
  937. X   struprcpy (nameb, temp);
  938. X
  939. X   if (rel[0] != 0)
  940. X      {
  941. X      Printf ("directory %s%s",   rel, DUBCR);
  942. X      sprintf (hdr, "%s%c%s.h",   rel, DIRSEP, temp);
  943. X      sprintf (rel, "%s%c%s.rel", rel, DIRSEP, temp);
  944. X      }
  945. X   else
  946. X      {
  947. X      Printf ("current directory%s", DUBCR);
  948. X      sprintf (hdr, "%s.h",   temp);
  949. X      sprintf (rel, "%s.rel", temp);
  950. X      }
  951. X
  952. X   Printf (lineF, SNGCR);
  953. X
  954. X   comment();
  955. X
  956. X   return fh;
  957. X}
  958. X
  959. Xchar *
  960. Xrepeat (ch, nm)
  961. Xchar    ch;
  962. Xint         nm;
  963. X{
  964. X   static char buf[80];
  965. X
  966. X   buf[(nm = (nm < 0) ? 0 : nm)] = 0;
  967. X
  968. X   for (nm--; nm >= 0; nm--)  buf[nm] = ch;
  969. X
  970. X   return buf;
  971. X}
  972. X
  973. Xint
  974. Xcontains_serial (desc)
  975. Xchar            *desc;
  976. X{
  977. X   char *line, *pch;
  978. X
  979. X   for (line = desc; (pch = strchr (line, ',')) != NULL; line = pch+1)
  980. X      {
  981. X      *pch = 0;
  982. X      if (data->type[atoi(line)] == T_SERIAL)
  983. X         return 1;
  984. X      }
  985. X   if (data->type[atoi(line)] == T_SERIAL)
  986. X      return 1;
  987. X
  988. X   return 0;
  989. X}
  990. X
  991. END_OF_FILE
  992.   if test 19729 -ne `wc -c <'src/build.c'`; then
  993.     echo shar: \"'src/build.c'\" unpacked with wrong size!
  994.   fi
  995.   # end of 'src/build.c'
  996. fi
  997. if test -f 'src/report.c' -a "${1}" != "-c" ; then 
  998.   echo shar: Will not clobber existing file \"'src/report.c'\"
  999. else
  1000.   echo shar: Extracting \"'src/report.c'\" \(20824 characters\)
  1001.   sed "s/^X//" >'src/report.c' <<'END_OF_FILE'
  1002. X/*
  1003. X * METALBASE 5.0
  1004. X *
  1005. X * Released October 1st, 1992 by Huan-Ti [ richid@owlnet.rice.edu ]
  1006. X *                                       [ t-richj@microsoft.com ]
  1007. X *
  1008. X * Special thanks go to Bruce Momjian (root@candle.uucp@ls.com) for his
  1009. X * suggestions and code.
  1010. X *
  1011. X */
  1012. X
  1013. X#include "mbase.h"
  1014. X
  1015. X/*
  1016. X * Definitions
  1017. X *
  1018. X */
  1019. X
  1020. X#define usage() fprintf(stderr,"format: report [-k encryption_key] [templatename]%s", SNGCR)
  1021. X#define syntax(x) { fprintf(stderr,"syntax error in template: expected keyword '%s'%s", x, SNGCR);  mb_exit(3); }
  1022. X
  1023. X#define comment() skip(templ,";"); while(skip(templ,"#")) goeol(templ,NULL);
  1024. X#define skipl(x) for (I=0; I<x; I++)  printf(SNGCR);
  1025. X
  1026. X#define MAX_WIDTH  80
  1027. X#define FULL_WIDTH "%-79.79s"
  1028. X
  1029. X#define NONE   0
  1030. X#define BEFORE 1
  1031. X#define DURING 2
  1032. X#define AFTER  4
  1033. X
  1034. X /* CREDIT is displayed as the variable system without "!date" or anything */
  1035. X
  1036. X#define CREDIT "MetalBase 5.0 Report Writer"
  1037. X
  1038. X/*
  1039. X * Prototypes
  1040. X *
  1041. X */
  1042. X
  1043. X#ifdef LONGARGS
  1044. X   void  parse_args (int, char **);
  1045. X   int   get_size   (long);
  1046. X   void  do_prints  (long, int);
  1047. X   int   fill_data  (dataptr, int, char *);
  1048. X#else
  1049. X   void  parse_args();
  1050. X   int   get_size();
  1051. X   void  do_prints();
  1052. X   int   fill_data();
  1053. X#endif
  1054. X
  1055. X/*
  1056. X * Variables
  1057. X *
  1058. X */
  1059. X
  1060. Xextern    long   _lpos;  /* These are from parse.c */
  1061. Xextern    int    quoted; /* Referenced in libmb.a  */
  1062. X
  1063. Xint       templ, I;
  1064. Xlong      lpos = 0L;
  1065. Xchar      key[MAX_WIDTH] = "";
  1066. Xrelation *rel;
  1067. Xlong      l_pos = 0L;
  1068. Xlong      h_pos = 0L;
  1069. Xlong      f_pos = 0L;
  1070. Xlong      o_pos = 0L;
  1071. Xint       keep_l = 0, keep_h = 0, keep_f = 0;
  1072. Xdataptr   rec, buf;
  1073. X
  1074. Xint       num_col = 80;   /* 80-column (std pagesize) paper */
  1075. Xint       num_row = 66;   /* 66-line (std pagesize) paper   */
  1076. Xint       top_mar =  4;   /* 2/3" top margin by default     */
  1077. Xint       bot_mar =  6;   /* 1" bottom margin by default    */
  1078. Xint       lef_mar = 10;   /* 1" left margin by default      */
  1079. Xint       rig_mar = 10;   /* 1" right margin by default     */
  1080. Xint       wid;
  1081. Xint       hgt;
  1082. Xint       pageno  =  1;   /* Start with page 1, obviously   */
  1083. X
  1084. X/*
  1085. X * Main code
  1086. X *
  1087. X */
  1088. X
  1089. Xvoid
  1090. Xmain  (argc, argv)
  1091. Xint    argc;
  1092. Xchar **argv;
  1093. X{
  1094. X   int    act, idx, numlines, lines, stop, dir, i, on_siz;
  1095. X   int    didother, doneone,  badrec;
  1096. X   char   temp[128];
  1097. X
  1098. X   parse_args (argc, argv);
  1099. X
  1100. X   comment();
  1101. X   if (! skip (templ, "data"))  syntax ("data");
  1102. X   strcpy (temp, getword(templ));
  1103. X   comment();
  1104. X
  1105. X   if (! strcmp (&temp[strlen(temp)-4], ".rel"))
  1106. X      temp[strlen(temp)-4] = 0;
  1107. X   if ((rel = mb_inc (temp, strtokey (key))) == RNULL)
  1108. X    { fprintf (stderr, "Cannot open relation '%s' : %s\n", temp, mb_error);
  1109. X      close   (templ);
  1110. X      exit    (4);
  1111. X    }
  1112. X   if ((rec = (dataptr)malloc (2+rel->rec_len)) == NULL)
  1113. X    { fprintf (stderr, "Out of memory\n");
  1114. X      close   (templ);
  1115. X      mb_exit (4);
  1116. X    }
  1117. X   if ((buf = (dataptr)malloc (2+rel->rec_len)) == NULL)
  1118. X    { fprintf (stderr, "Out of memory\n");
  1119. X      free    (rec);
  1120. X      close   (templ);
  1121. X      mb_exit (4);
  1122. X    }
  1123. X
  1124. X   for (didother = 0; ; )
  1125. X    {
  1126. X      if (skip (templ, ";") == -1)  break;
  1127. X      for (;;)
  1128. X       {
  1129. X         if (skip (templ, ";") == -1)  break;
  1130. X         comment();
  1131. X         if (skip (templ, "size"))
  1132. X          {
  1133. X            for (didother = 1; ; )
  1134. X             {
  1135. X               if (skip (templ, ";") == -1)  break;
  1136. X               comment();
  1137. X
  1138. X               strcpy (temp, getword (templ));
  1139. X               if (! strcmp (temp, "columns"))  num_col = atoi(getword(templ));
  1140. X               else
  1141. X               if (! strcmp (temp, "rows"))     num_row = atoi(getword(templ));
  1142. X               else
  1143. X               if (! strcmp (temp, "top"))
  1144. X                { skip (templ, "margin");  top_mar = atoi (getword (templ)); }
  1145. X               else
  1146. X               if (! strcmp (temp, "bottom"))
  1147. X                { skip (templ, "margin");  bot_mar = atoi (getword (templ)); }
  1148. X               else
  1149. X               if (! strcmp (temp, "left"))
  1150. X                { skip (templ, "margin");  lef_mar = atoi (getword (templ)); }
  1151. X               else
  1152. X               if (! strcmp (temp, "right"))
  1153. X                { skip (templ, "margin");  rig_mar = atoi (getword (templ)); }
  1154. X               else
  1155. X               if (!strcmp (temp, "page")   || !strcmp (temp, "newpage") ||
  1156. X                   !strcmp (temp, "header") || !strcmp (temp, "last")    ||
  1157. X                   !strcmp (temp, "on")     || !strcmp (temp, "footer"))
  1158. X                {
  1159. X                  putback(templ);  /* Go back before this word */
  1160. X                  break;
  1161. X                }
  1162. X               else
  1163. X                { fprintf (stderr, "token '%s' unrecognized in Size\n", temp);
  1164. X                  free    (rec);
  1165. X                  free    (buf);
  1166. X                  close   (templ);
  1167. X                  mb_exit (3);
  1168. X                }
  1169. X               continue;
  1170. X             }
  1171. X          }
  1172. X         if (skip (templ, "newpage"))
  1173. X          {
  1174. X            didother = 1;
  1175. X            printf ("\f");
  1176. X            comment();
  1177. X            continue;
  1178. X          }
  1179. X         if (skip (templ, "page"))
  1180. X          {
  1181. X            didother = 1;
  1182. X            pageno = atoi (getword (templ));
  1183. X            comment();
  1184. X            continue;
  1185. X          }
  1186. X         if (skip (templ, "header"))
  1187. X          {
  1188. X            keep_h = skip (templ, "keep");
  1189. X            didother = 2;  /* Ignore errors until next keyword */
  1190. X            comment();
  1191. X            h_pos = lseek (templ, 0L, 1);  (void)getword(templ);
  1192. X            continue;
  1193. X          }
  1194. X         if (skip (templ, "last"))
  1195. X          {
  1196. X            keep_l = skip (templ, "keep");
  1197. X            didother = 2;  /* Ignore errors until next keyword */
  1198. X            comment();
  1199. X            l_pos = lseek (templ, 0L, 1);  (void)getword(templ);
  1200. X            continue;
  1201. X          }
  1202. X         if (skip (templ, "footer"))
  1203. X          {
  1204. X            keep_f = skip (templ, "keep");
  1205. X            didother = 2;  /* Ignore errors until next keyword */
  1206. X            comment();
  1207. X            f_pos = lseek (templ, 0L, 1);  (void)getword(templ);
  1208. X            continue;
  1209. X          }
  1210. X
  1211. X         if (skip (templ, "on"))
  1212. X            break;
  1213. X
  1214. X         if (! didother)
  1215. X          { fprintf (stderr,
  1216. X               "release 5.0 cannot use more than one relation%s", SNGCR);
  1217. X            close (templ);
  1218. X            syntax ("size");
  1219. X          }
  1220. X
  1221. X         if (didother != 2)
  1222. X          { fprintf (stderr, "unexpected keyword '%s'%s",getword(templ),SNGCR);
  1223. X            close   (templ);
  1224. X            free    (rec);
  1225. X            free    (buf);
  1226. X            mb_exit (3);
  1227. X          }
  1228. X         (void)getword(templ);  /* Skip this one--it's in a header or etc */
  1229. X       }
  1230. X
  1231. X      if (skip (templ, ";") == -1)  break;
  1232. X      didother = 1;
  1233. X      wid = num_col - rig_mar - lef_mar;
  1234. X      hgt = num_row - top_mar - bot_mar;
  1235. X
  1236. X/*
  1237. X * Perform ON clause.  We are currently sitting right after the keyword On,
  1238. X * and the following are conditionals on the operation of this clause:
  1239. X *   h_pos:  0L==no header - else, location of first print/skip command
  1240. X *   l_pos:  0L==no last   - else, location of first print/skip command
  1241. X *   f_pos:  0L==no footer - else, location of first print/skip command
  1242. X *
  1243. X */
  1244. X
  1245. X      strcpy (temp, getword (templ));
  1246. X      if ((idx = idxnum (rel, temp)) < 0)
  1247. X       { fprintf (stderr, "invalid index '%s' referenced%s", temp, SNGCR);
  1248. X         free    (rec);
  1249. X         free    (buf);
  1250. X         close   (templ);
  1251. X         mb_exit (5);
  1252. X       }
  1253. X
  1254. X      act = FIRST, stop = NONE;
  1255. X      dir = 0;
  1256. X      if (skip (templ, "<="))  act = FIRST, stop=AFTER;
  1257. X      if (skip (templ, "=<"))  act = FIRST, stop=AFTER;
  1258. X      if (skip (templ, "<"))   act = FIRST, stop=DURING|AFTER;
  1259. X      if (skip (templ, ">="))  act = GTEQ,  stop=NONE;
  1260. X      if (skip (templ, "=>"))  act = GTEQ,  stop=NONE;
  1261. X      if (skip (templ, ">"))   act = GTHAN, stop=NONE;
  1262. X      if (skip (templ, "=="))  act = EQUAL, stop=AFTER;
  1263. X      if (skip (templ, "="))   act = EQUAL, stop=AFTER;
  1264. X
  1265. X      if (act != FIRST || stop != NONE)
  1266. X       { strcpy (temp, getword(templ));
  1267. X         if (!strcmp (temp, "print") || !strcmp (temp, "skip"))
  1268. X          { fprintf (stderr, "query requires comparison value%s", SNGCR);
  1269. X            free    (rec);
  1270. X            free    (buf);
  1271. X            close   (templ);
  1272. X            mb_exit (5);
  1273. X          }
  1274. X
  1275. X         if (fill_data (rec, idx, temp))
  1276. X            fill_data (buf, idx, temp);
  1277. X         else
  1278. X          { act = FIRST;
  1279. X            stop = NONE;
  1280. X          }
  1281. X       }
  1282. X
  1283. X      dir = NEXT;
  1284. X      if (skip (templ, "reverse"))
  1285. X       { dir = PREV;
  1286. X         switch (act)
  1287. X          { case FIRST:  if (stop == NONE)    act = LAST;
  1288. X                         if (stop == AFTER)   act = LTEQ,  stop = NONE;
  1289. X                         if (stop &  DURING)  act = LTHAN, stop = NONE;
  1290. X                        break;
  1291. X            case  GTEQ:  if (stop == NONE)    act = LAST,  stop = BEFORE;
  1292. X            case GTHAN:  if (stop == NONE)    act = LAST,  stop = DURING|BEFORE;
  1293. X          }
  1294. X       }
  1295. X
  1296. X      o_pos    = lseek(templ,0L,1);
  1297. X      on_siz   = get_size(o_pos);
  1298. X      numlines = hgt - get_size(h_pos) - get_size(f_pos);
  1299. X
  1300. X      doneone = 0;
  1301. X      for (lines = 0; ; )
  1302. X       { badrec = 0;
  1303. X         if (mb_sel (rel, idx, rec, act, buf) != MB_OKAY)
  1304. X            if (doneone || mb_errno != MB_NO_SUCH)  break;
  1305. X            else
  1306. X               badrec = 1;
  1307. X         doneone = 1;
  1308. X         if (badrec == 0 && stop != NONE)
  1309. X            if (((i = compare (rel, rec, buf, idx)) < 0 && stop&BEFORE) ||
  1310. X                 (i == 0 && stop&DURING) || (i >  0 && stop&AFTER))
  1311. X               break;
  1312. X         lseek (templ, o_pos, 0);
  1313. X
  1314. X         if (lines+on_siz > numlines-1)
  1315. X           { skipl(numlines-lines);
  1316. X             do_prints(f_pos, 1);
  1317. X             lines=0; skipl(bot_mar); pageno++;
  1318. X           }
  1319. X         if (lines == 0) { skipl(top_mar); do_prints (h_pos, 1); }
  1320. X
  1321. X         do_prints (o_pos, !badrec);
  1322. X         if (badrec)  break;
  1323. X         lines += on_siz;
  1324. X         act = dir;
  1325. X       }
  1326. X
  1327. X      if (mb_errno != MB_NO_SUCH && mb_errno != MB_OKAY)
  1328. X       { fprintf (stderr, "aborted -- %s%s", mb_error, SNGCR);
  1329. X         free    (rec);
  1330. X         free    (buf);
  1331. X         close   (templ);
  1332. X         mb_exit (6);
  1333. X       }
  1334. X
  1335. X      o_pos = lseek (templ, 0L, 1);
  1336. X      skipl (numlines-lines);
  1337. X      do_prints (l_pos ? l_pos : f_pos, 1);
  1338. X      lseek (templ, o_pos, 0);
  1339. X      skipl (bot_mar);  pageno++;
  1340. X
  1341. X   didother = 2;  /* Skip stuff until valid keyword */
  1342. X
  1343. X/*
  1344. X * Done with this clause.  Erase current header/footer/last positions--they
  1345. X * don't carry from one clause to another.  That is, unless they had 'keep'
  1346. X * keywords...
  1347. X *
  1348. X */
  1349. X
  1350. X      if (! keep_l)  l_pos = 0L;
  1351. X      if (! keep_f)  f_pos = 0L;
  1352. X      if (! keep_h)  h_pos = 0L;
  1353. X    }
  1354. X
  1355. X   close   (templ);
  1356. X   free    (rec);
  1357. X   free    (buf);
  1358. X   mb_exit (0);
  1359. X}
  1360. X
  1361. X/*
  1362. X * Utilities
  1363. X *
  1364. X */
  1365. X
  1366. Xvoid
  1367. Xparse_args (agc, agv)
  1368. Xint         agc;
  1369. Xchar           **agv;
  1370. X{
  1371. X   char name[256];
  1372. X
  1373. X   while (agc > 1 && agv[1][0] == '-')
  1374. X      {
  1375. X      switch (agv[1][1])
  1376. X         {
  1377. X         case 'k':  if (agv[1][2])
  1378. X                       strcpy (key, &agv[1][2]);
  1379. X                    else
  1380. X                     { agc--;  agv++;
  1381. X                       strcpy (key, agv[1]);
  1382. X                     }
  1383. X                   break;
  1384. X         default:   fprintf (stderr, "unrecognized option '%s'\n", agv[1]);
  1385. X                    usage   ();
  1386. X                    exit    (1);
  1387. X                   break;
  1388. X         }
  1389. X
  1390. X      agc--;  agv++;
  1391. X      }
  1392. X
  1393. X   if (agc != 2)
  1394. X    { usage ();
  1395. X      exit  (1);
  1396. X    }
  1397. X
  1398. X   strcpy (name, agv[1]);
  1399. X   if (strcmp (&name[strlen(name)-4], ".rpt"))  strcat (name, ".rpt");
  1400. X   if ((templ = openx (name, O_RDONLY)) < 0)
  1401. X    { fprintf (stderr, "cannot open template '%s'\n", name);
  1402. X      exit    (2);
  1403. X    }
  1404. X}
  1405. X
  1406. X/*
  1407. X * get_size() reads from position 'pos' until it doesn't see a print or
  1408. X * skip command--it keeps track of the number of lines used in the section,
  1409. X * and returns it.  It's used to make headers and footers work well with
  1410. X * top and bottom margins.
  1411. X *
  1412. X */
  1413. X
  1414. Xint
  1415. Xget_size (pos)
  1416. Xlong      pos;
  1417. X{
  1418. X   int  num = 0;
  1419. X
  1420. X   for (lseek (templ, pos, 0); ; )
  1421. X    {
  1422. X      if (skip (templ, ";") == -1)  break;
  1423. X      comment();
  1424. X      if (skip (templ, "print"))
  1425. X       {
  1426. X         if (! skip (templ, "continued"))
  1427. X          { if (! strcmp (":", getword(templ)))  num++;
  1428. X            else
  1429. X               if (! skip (templ, "continued"))  num++;
  1430. X          }
  1431. X
  1432. X         for (;;)
  1433. X            if (skip (templ, ";"))  break;
  1434. X            else
  1435. X               (void)getword(templ);
  1436. X       }
  1437. X      else
  1438. X         if (skip (templ, "skip"))
  1439. X          {
  1440. X            num += atoi (getword (templ));
  1441. X            skip (templ, "lines");  skip (templ, "line");
  1442. X          }
  1443. X         else
  1444. X            break;
  1445. X    }
  1446. X
  1447. X   return num;
  1448. X}
  1449. X
  1450. X/*
  1451. X * do_prints() actually performs the printings.  :)
  1452. X *
  1453. X */
  1454. X
  1455. Xvoid
  1456. Xdo_prints (pos, really)
  1457. Xlong       pos;
  1458. Xint             really;
  1459. X{
  1460. X   long    lx;  /* typ == 1 */
  1461. X   double  fx;  /* typ == 2 */
  1462. X   mb_time tx;  /* typ == 3 */
  1463. X   mb_date dx;  /* typ == 4 */
  1464. X   int     cnt, jst, x, w, l, typ, fmt;
  1465. X   char    spc[128], temp[256], t2[128], t3[128], *p;
  1466. X
  1467. X   if (! pos)  return;
  1468. X
  1469. X   sprintf (spc, FULL_WIDTH, "");  spc[lef_mar] = 0;
  1470. X
  1471. X   w = wid;  l = lef_mar;
  1472. X   for (lseek (templ, pos, 0); ; )
  1473. X      {
  1474. X      if (skip (templ, ";") == -1)  break;
  1475. X      comment();
  1476. X
  1477. X      if (skip (templ, "skip"))
  1478. X         {
  1479. X         x = atoi (getword (templ));
  1480. X         skip (templ, "lines");  skip (templ, "line");
  1481. X         skip (templ, ";");
  1482. X         if (really)
  1483. X            for ( ; x > 0; x--)  printf (SNGCR);
  1484. X         continue;
  1485. X         }
  1486. X
  1487. X      if (! skip (templ, "print"))
  1488. X         {
  1489. X         break;
  1490. X         }
  1491. X
  1492. X      cnt = jst = 0;
  1493. X      if (skip (templ, "continued"))  cnt = 1;
  1494. X      if (skip (templ, "centered"))   jst = 1;
  1495. X      if (skip (templ, "continued"))  cnt = 1;
  1496. X      if (skip (templ, "right"))      jst = 2;
  1497. X      if (skip (templ, "continued"))  cnt = 1;
  1498. X
  1499. X      skip (templ, ":");
  1500. X
  1501. X/*
  1502. X * process the print command
  1503. X *
  1504. X */
  1505. X
  1506. X      for (temp[0] = 0; ; )
  1507. X         {
  1508. X         strcpy (t2, getword(templ));
  1509. X         if (! quoted && ! strcmp (t2, ";"))  break;
  1510. X
  1511. X         if (quoted)
  1512. X            {
  1513. X            typ=0;
  1514. X            }
  1515. X         else
  1516. X            {
  1517. X            if (isdigit(t2[0]) || t2[0] == '.')
  1518. X               {
  1519. X               if (strchr (t2, '.'))  typ=2, fx=(double)atof(t2);
  1520. X               else                   typ=1, lx=(long)atol(t2);
  1521. X               }
  1522. X            else
  1523. X               {
  1524. X               if (!strcmp (t2, "column"))
  1525. X                { sprintf (t2, FULL_WIDTH, "");
  1526. X                  x = atoi(getword(templ))-l-strlen(temp);
  1527. X                  if (x < 0)  x = 0;
  1528. X                  t2[x] = 0;  typ = 0;
  1529. X                }
  1530. X               else
  1531. X                  {
  1532. X                  if (!strcmp (t2, ","))
  1533. X                     t2[0] = ' ', typ = 0;
  1534. X                  else
  1535. X                     {
  1536. X
  1537. X/*
  1538. X * It's a variable of some sort; make t2 the name and t3 any modifer.
  1539. X *
  1540. X */
  1541. X
  1542. X                     fmt = 0;
  1543. X                     t3[0] = 0;
  1544. X                     if (p = strchr (t2, '!'))
  1545. X                        {
  1546. X                        strcpy (t3, p+1);
  1547. X                        *p = 0;
  1548. X                        }
  1549. X
  1550. X/*
  1551. X * That done, figure out what type it should be and assign it.
  1552. X *
  1553. X */
  1554. X                     typ = -1;
  1555. X
  1556. X                     if (!strcmp (t2, "system"))
  1557. X                        {
  1558. X                        if (! strcmp (t3, "time"))
  1559. X                         { tx = curtime();  typ = 3; }
  1560. X                        else if (! strcmp (t3, "date"))
  1561. X                         { dx = curdate();  typ = 4; }
  1562. X                        else if (! strcmp (t3, "page"))
  1563. X                         { lx = pageno;     typ = 1; }
  1564. X                        else 
  1565. X                         { strcpy (t2, CREDIT);  typ = 0; }
  1566. X                        }
  1567. X
  1568. X/*
  1569. X* If they use the relation name, trash it...
  1570. X*
  1571. X*/
  1572. X
  1573. X                     if (typ == -1 && (p = strchr (t2, '.')))
  1574. X                        {
  1575. X                        strcpy (spc, 1+p);
  1576. X                        strcpy (t2, spc);
  1577. X                        sprintf (spc, FULL_WIDTH, "");  spc[lef_mar] = 0;
  1578. X                        }
  1579. X
  1580. X                     if (typ == -1)
  1581. X                        {
  1582. X                        for (x=0; x < rel->num_f; x++)
  1583. X                           if (! strcmp (rel->name[x], t2))  break;
  1584. X                        typ = 0;
  1585. X                        if (x != rel->num_f)
  1586. X                           {
  1587. X
  1588. X/*
  1589. X * It's a valid name, so use its data (field # is in x)
  1590. X *
  1591. X */
  1592. X
  1593. X                           if (! t3[0])
  1594. X                              {
  1595. X                              p = (char *)rec + rel->start[x];
  1596. X
  1597. X            switch (rel->type[x])
  1598. X               {
  1599. X               case T_SHORT:   lx = (long) *(short  *)p;  typ = 1;  break;
  1600. X               case T_USHORT:  lx = (long) *(ushort *)p;  typ = 1;  break;
  1601. X               case T_LONG:
  1602. X               case T_SERIAL:  lx = (long) *(long   *)p;  typ = 1;  break;
  1603. X               case T_ULONG:   lx = (long) *(ulong  *)p;  typ = 1;  break;
  1604. X               case T_FLOAT:   fx = (float)*(float  *)p;  typ = 2;  break;
  1605. X               case T_DOUBLE:
  1606. X               case T_MONEY:   fx = (float)*(double *)p;  typ = 2;  break;
  1607. X               case T_TIME:    tx =       *(mb_time *)p;  typ = 3;  break;
  1608. X               case T_DATE:    dx =       *(mb_date *)p;  typ = 4;  break;
  1609. X               default:        strcpy (t2, p);            typ = 0;  break;
  1610. X               }
  1611. X
  1612. X                              }
  1613. X                           }
  1614. X                        }
  1615. X                     }
  1616. X                  }
  1617. X               }
  1618. X            }
  1619. X
  1620. X         if (skip (templ, "using") || skip (templ, "format"))
  1621. X          { if (typ == 3 || typ == 4)
  1622. X               fmt = atoi (getword (templ));
  1623. X            else
  1624. X             { strcpy (t3, getword(templ));
  1625. X               switch (typ)
  1626. X                { case 0:  strcpy  (spc, t2);
  1627. X                           sprintf (t2, t3, spc);  typ = 0;
  1628. X                           sprintf (spc,FULL_WIDTH,"");  spc[lef_mar]=0;
  1629. X                          break;
  1630. X                  case 1:  sprintf (t2, t3, lx);  typ = 0;  break;
  1631. X                  case 2:  sprintf (t2, t3, fx);  typ = 0;  break;
  1632. X                }
  1633. X             }
  1634. X          }
  1635. X
  1636. X         if (typ == 1)  sprintf (t2, "%ld", lx);
  1637. X         if (typ == 2)  sprintf (t2, "%lg", fx);
  1638. X         if (typ == 3)  strcpy  (t2, fmt_time (tx, fmt));
  1639. X         if (typ == 4)  strcpy  (t2, fmt_date (dx, fmt));
  1640. X
  1641. X         if (skip (templ, "to"))
  1642. X          { sprintf (t3, FULL_WIDTH, "");
  1643. X            x = atoi (getword(templ)) - l - strlen (t2) - strlen (temp);
  1644. X            if (x < 0)  x = 0;
  1645. X            t3[x] = 0;
  1646. X            strcat (t2, t3);
  1647. X          }
  1648. X
  1649. X         strcat (temp, t2);
  1650. X         }
  1651. X
  1652. X/*
  1653. X * and actually print it.
  1654. X *
  1655. X */
  1656. X
  1657. X      if (really)
  1658. X         {
  1659. X         switch (jst)
  1660. X            {
  1661. X            case  0:  printf ("%s%s", spc, temp);  break;
  1662. X            case  1:  x = ((w - strlen (temp)) / 2);  x = max (x, 0);
  1663. X                      if (w == wid)  x += l;
  1664. X                      sprintf (t2, FULL_WIDTH, "");  t2[x] = 0;
  1665. X                      printf ("%s%s", t2, temp);   break;
  1666. X            case  2:  x = (w - strlen (temp)); x = max (x, 0);
  1667. X                      if (w == wid)  x += l;
  1668. X                      sprintf (t2, FULL_WIDTH, "");  t2[x] = 0;
  1669. X                      printf ("%s%s", t2, temp);   break;
  1670. X            }
  1671. X         if (! cnt)  w  = wid,           l  = lef_mar;
  1672. X         else        w -= strlen (temp), l += strlen (temp);
  1673. X         if (! cnt)  printf (SNGCR);
  1674. X         }
  1675. X      }
  1676. X}
  1677. X
  1678. Xint
  1679. Xfill_data (ptr, idx, str)
  1680. Xdataptr    ptr;
  1681. Xint             idx;
  1682. Xchar                *str;
  1683. X{
  1684. X   char    temp[128], t2[5];
  1685. X   dataptr x;
  1686. X   long    y;
  1687. X   int     i, j, k, f;
  1688. X
  1689. X   if (idx < 0 || idx >= rel->num_i)  return 0;
  1690. X
  1691. X   i = 0;  /* Left edge of word in str */
  1692. X   for (k = 0; ; k++)
  1693. X      {
  1694. X      if (! str[i])  break;
  1695. X      for (j = i; str[j] && str[j] != ','; j++)
  1696. X         ;
  1697. X      strcpy (temp, &str[i]);  temp[j-i] = 0;  i = j;
  1698. X
  1699. X      strzcpy (t2, &rel->idxs[idx][3*k +3], 3);  f = atoi (t2);
  1700. X
  1701. X      x = (dataptr)((char *)ptr + rel->start[f]);  /* Position of field */
  1702. X
  1703. X      switch (rel->type[f])
  1704. X         {
  1705. X         case T_SHORT:   *(short   *)x = (short) atoi (temp);  break;
  1706. X         case T_USHORT:  *(ushort  *)x = (ushort)atoi (temp);  break;
  1707. X         case T_LONG:    *(long    *)x = (long)  atol (temp);  break;
  1708. X         case T_ULONG:   *(ulong   *)x = (ulong) atol (temp);  break;
  1709. X         case T_FLOAT:   *(float   *)x = (float) atof (temp);  break;
  1710. X         case T_DOUBLE:
  1711. X         case T_MONEY:   *(double  *)x = (double)atof (temp);  break;
  1712. X         case T_TIME:    *(mb_time *)x = scn_time (temp);      break;
  1713. X         case T_DATE:    *(mb_date *)x = scn_date (temp);      break;
  1714. X         case T_SERIAL:  *(long    *)x = (long)  atol (temp);  break;
  1715. X         default:        strcpy (x, temp);                     break;
  1716. X         }
  1717. X
  1718. X      if (rel->type[f] == T_MONEY)
  1719. X         {
  1720. X         y = (long)(100.0 * (double)atof (temp));
  1721. X         *(double *)x = (double)y / 100.0;
  1722. X         }
  1723. X      }
  1724. X   return 1;
  1725. X}
  1726. X
  1727. END_OF_FILE
  1728.   if test 20824 -ne `wc -c <'src/report.c'`; then
  1729.     echo shar: \"'src/report.c'\" unpacked with wrong size!
  1730.   fi
  1731.   # end of 'src/report.c'
  1732. fi
  1733. echo shar: End of archive 2 \(of 8\).
  1734. cp /dev/null ark2isdone
  1735. MISSING=""
  1736. for I in 1 2 3 4 5 6 7 8 ; do
  1737.     if test ! -f ark${I}isdone ; then
  1738.     MISSING="${MISSING} ${I}"
  1739.     fi
  1740. done
  1741. if test "${MISSING}" = "" ; then
  1742.     echo You have unpacked all 8 archives.
  1743.     rm -f ark[1-9]isdone
  1744. else
  1745.     echo You still must unpack the following archives:
  1746.     echo "        " ${MISSING}
  1747. fi
  1748. exit 0
  1749. exit 0 # Just in case...
  1750.