home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / comp / sources / misc / 4125 < prev    next >
Encoding:
Text File  |  1992-11-23  |  55.2 KB  |  1,756 lines

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