1: # include   "../ingres.h"
   2: # include   "../access.h"
   3: # include   "../aux.h"
   4: # include   "../catalog.h"
   5: # include   "../symbol.h"
   6: # include   "../lock.h"
   7: 
   8: 
   9: /*
  10: **  CREATE -- create new relation
  11: **
  12: **	This module creates a brand new relation in the current
  13: **	directory (database).  The relation is always created as
  14: **	a paged heap.  It may not redefine an existing relation,
  15: **	or rename a system catalog.
  16: **
  17: **	Defines:
  18: **		create -- driver
  19: **		chk_att -- check attribute for validity
  20: **		ins_att -- insert attribute into 'attribute' catalog
  21: **		dup_att -- check for duplicate attribute
  22: **		initstructs -- initialize relation and attribute
  23: **			tuples for insertion into catalog
  24: **		formck -- check for valid attribute type.
  25: **
  26: **	Requires:
  27: **		ingresname
  28: **		opencatalog
  29: **		openr
  30: **		insert
  31: **		setcsl, unlcs
  32: **		creat -- to create physical file
  33: **		Usercode -- to find out the current user.
  34: **		Reldes, Attdes -- from opencatalog
  35: **
  36: **	Required By:
  37: **		overlaya
  38: **		overlaym.c
  39: **
  40: **	Files:
  41: **		relname....cc -- where cc is the Usercode.
  42: **
  43: **	Trace Flags:
  44: **		2
  45: **
  46: **	Diagnostics:
  47: **		5102 -- duplicate relation name %0
  48: **			The relation named already exists (and is
  49: **			owned by you.
  50: **		5103 -- %0 is a system catalog
  51: **			It is not OK to declare a relation name which
  52: **			is the same as that of a system catalog, even
  53: **			if you do not own the system catalog.  This is
  54: **			so that when other parts of the system do an
  55: **			openr on system-catalog type stuff, it will not
  56: **			accidently get something it didn't expect.
  57: **		5104 -- %0: invalid attribute name %1
  58: **			An attribute name may not be called "tid",
  59: **			since that is magic.
  60: **		5105 -- %0: duplicate attribute name %1
  61: **			Naturally, all attribute names must be unique.
  62: **		5106 -- %0: invalid attribute format %2 on %1
  63: **			Attribute formats can only be the usual 'i1',
  64: **			'i2', 'i4', 'f4', 'f8', or 'cN', with N from
  65: **			one to 255.  N can be zero if the relation is
  66: **			generated internally ("_SYS..."); this is used
  67: **			by decomp to avoid some nasty bugs.
  68: **		5107 -- %0: excessive domain count on %1
  69: **			There is a magic number (MAXDOM - 1) of domains
  70: **			which is the maximum that may occur in a single
  71: **			relation.  As the error documentation says:
  72: **			"The origin of this magic number is obscure.
  73: **			This is very difficult to change."  Oh well.
  74: **		5108 -- %0: excessive relation width on attr %1
  75: **			I wonder who came up with these AWFUL error messages.
  76: **			Tuple widths may not exceed a magic number,
  77: **			based heavily on the size of a page in UNIX.
  78: **			The current value of THIS magic number (MAXTUP)
  79: **			is 498 bytes.
  80: **
  81: **	History:
  82: **		10/23/79 (6.2/8) (eric) -- null terminated relation filename
  83: **			before creat() call so that systems which check
  84: **			for names too long won't balk.
  85: **		12/7/78 (rse) -- removed call to sys_catalog and put code
  86: **				in line.
  87: **		11/3/78 (rse) -- formatpg now resets the page itself so
  88: **				call to resetacc removed.
  89: **		11/1/78 (rse) -- views have no limit on tuple width.
  90: **		10/30/78 (rse) -- added pageflush after inserting attributes.
  91: **		8/17/78 (rse) -- fixed call to formatpg().
  92: **			Changed calls to error() to have full error number
  93: **		8/1/78 (eric) -- call to 'noclose' (for concurrency
  94: **			reasons) moved to index.c, which is the only
  95: **			place it was needed.  This made creatdb
  96: **			simpler.
  97: **		2/27/78 (eric) -- modified to take 'relstat' as param
  98: **			zero, instead of the (ignored) storage struc-
  99: **			ture.
 100: */
 101: 
 102: 
 103: 
 104: 
 105: 
 106: struct domain
 107: {
 108:     char    *name;
 109:     char    frmt;
 110:     char    frml;
 111: };
 112: 
 113: /*
 114: **  CREATE -- create new relation
 115: **
 116: **	This routine is the driver for the create module.
 117: **
 118: **	Parameters:
 119: **		pc -- parameter count
 120: **		pv -- parameter vector:
 121: **			0 -- relation status (relstat) -- stored into
 122: **				the 'relstat' field in the relation
 123: **				relation, and used to determine the
 124: **				caller.  Interesting bits are:
 125: **
 126: **				S_INDEX -- means called by the index
 127: **					processor.  If set, the 'relindxd'
 128: **					field will also be set to -1
 129: **					(SECINDEX) to indicate that this
 130: **					relation is a secondary index.
 131: **				S_CATALOG -- this is a system catalog.
 132: **					If set, this create was called
 133: **					from creatdb, and the physical
 134: **					file is not created.  Also, the
 135: **					expiration date is set infinite.
 136: **				S_VIEW -- this is a view.  Create has
 137: **					been called by the 'define'
 138: **					statement, rather than the
 139: **					'create' statement.  The physical
 140: **					file is not created.
 141: **
 142: **			1 -- relation name.
 143: **			2 -- attname1
 144: **			3 -- format1
 145: **			4, etc -- attname, format pairs.
 146: **
 147: **	Returns:
 148: **		zero -- successful create.
 149: **		else -- failure somewhere.
 150: **
 151: **	Side Effects:
 152: **		A relation is created (this is a side effect?).  This
 153: **		means entries in the 'relation' and 'attribute' cata-
 154: **		logs, and (probably) a physical file somewhere, with
 155: **		one page already in it.
 156: **
 157: **	Requires:
 158: **		opencatalog -- to open the 'relation' and 'attribute'
 159: **			catalogs into 'Reldes' and 'Attdes' resp.
 160: **		initstructs -- to initialize relation and attribute
 161: **			tuples.
 162: **		chk_att -- to check each attribute for validity:
 163: **			good name, correct format, no dups, etc.
 164: **		setcsl -- to set a critical section lock around the
 165: **			section of code to physically create a file.
 166: **			For concurrency reasons, this is the "true"
 167: **			test for existance of a relation.
 168: **		unlcs -- to remove locks, of course.
 169: **		formatpg -- a mystic routine that outputs the initial
 170: **			page of the relation (empty) so that the
 171: **			access methods will not choke later.
 172: **		insert -- to insert tuples into the 'relation' and
 173: **			'attribute' catalogs.
 174: **
 175: **	Called By:
 176: **		overlaya, overlaym
 177: **		(maybe other overlay?)
 178: **		creatdb
 179: **		index
 180: **
 181: **	Trace Flags:
 182: **		2.* -- entry message
 183: **
 184: **	Diagnostics:
 185: **		5102 -- duplicate relation name
 186: **		5103 -- renaming system catalog
 187: **		5104 -- invalid attribute name
 188: **		5105 -- duplicate attribute name
 189: **		5106 -- invalid attribute format spec
 190: **		5107 -- too many domains
 191: **		5108 -- tuple too wide
 192: **
 193: **	Syserrs:
 194: **		create: creat %s
 195: **			The 'creat' call failed, probably meaning
 196: **			that the directory is not writable, the file
 197: **			exists mode zero, or this process is not
 198: **			running as 'ingres'.
 199: **		create: formatpg %d
 200: **			The 'formatpg' routine failed.  Could be
 201: **			because of a lack of disk space.  The number
 202: **			is the return from formatpg; check it for
 203: **			more details.
 204: **		create: insert(rel, %s) %d
 205: **			The insert for relid %s into the 'relation'
 206: **			catalog failed; %d is the return from insert.
 207: **			Check insert for details.
 208: **
 209: **	History:
 210: **		8/1/78 (eric) -- 'noclose' call moved to index.c.
 211: **		2/27/78 (eric) -- changed to take 'relstat' as pv[0]
 212: **			instead of the (ignored) relspec.
 213: */
 214: 
 215: create(pc, pv)
 216: int pc;
 217: char    **pv;
 218: {
 219:     register char           **pp;
 220:     register int            i;
 221:     int             bad;
 222:     struct domain           domain[MAXDOM];
 223:     struct domain           *dom;
 224:     char                *relname, tempname[MAXNAME+3];
 225:     struct tup_id           tid;
 226:     struct relation         rel, key;
 227:     struct attribute        att;
 228:     struct descriptor       desr;
 229:     extern char         *Usercode;
 230:     extern struct descriptor    Reldes, Attdes;
 231:     extern int          errno;
 232:     register int            relstat;
 233:     long                temptid;
 234:     long                npages;
 235:     int             fdes;
 236: 
 237: #	ifdef xZTR1
 238:     if (tTf(2, -1))
 239:         printf("creating %s\n", pv[1]);
 240: #	endif
 241:     pp = pv;
 242:     relstat = oatoi(pp[0]);
 243:     /*
 244: 	**	If this database has query modification, then default
 245: 	**	to denial on all user relations.
 246: 	**	(Since views cannot be protected, this doesn't apply to them)
 247: 	*/
 248:     if ((Admin.adhdr.adflags & A_QRYMOD) && ((relstat & (S_VIEW || S_CATALOG)) == 0))
 249:         relstat |= (S_PROTALL | S_PROTRET);
 250:     relname = *(++pp);
 251:     ingresname(relname, Usercode, rel.relid);
 252:     bmove(rel.relid, att.attrelid, MAXNAME + 2);
 253:     opencatalog("relation", 2);
 254: 
 255:     /* check for duplicate relation name */
 256:     if ((relstat & S_CATALOG) == 0)
 257:     {
 258:         if (openr(&desr, -1, relname) == 0)
 259:         {
 260:             if (bequal(desr.relowner, rel.relowner, 2))
 261:             {
 262:                 return (error(5102, relname, 0));   /* bad relname */
 263:             }
 264:             if (desr.relstat & S_CATALOG)
 265:             {
 266:                 return (error(5103, relname, 0));   /* attempt to rename system catalog */
 267:             }
 268:         }
 269:     }
 270:     opencatalog("attribute", 2);
 271: 
 272:     /* initialize structures for system catalogs */
 273:     initstructs(&att, &rel);
 274:     rel.relstat = relstat;
 275:     if ((relstat & S_CATALOG) != 0)
 276:         rel.relsave = 0;
 277:     else if ((relstat & S_INDEX) != 0)
 278:         rel.relindxd = SECINDEX;
 279: 
 280: #	ifdef xZTR3
 281:     if (tTf(2, 2))
 282:         printup(&Reldes, &rel);
 283: #	endif
 284: 
 285:     /* check attributes */
 286:     pp++;
 287:     for (i = pc - 2; i > 0; i -= 2)
 288:     {
 289:         bad = chk_att(&rel, pp[0], pp[1], domain);
 290:         if (bad != 0)
 291:         {
 292:             return (error(bad, relname, pp[0], pp[1], 0));
 293:         }
 294:         pp += 2;
 295:     }
 296: 
 297:     /*
 298: 	** Create files if appropriate. Concurrency control for
 299: 	** the create depends on the actual file. To prevent
 300: 	** to users with the same usercode from creating the
 301: 	** same relation at the same time, their is check
 302: 	** on the existence of the file. The important events are
 303: 	** (1) if a tuple exists in the relation relation then
 304: 	** the relation really exists. (2) if the file exists then
 305: 	** the relation is being created but will not exist for
 306: 	** use until the relation relation tuple is present.
 307: 	** For VIEWS, the file is used for concurrency control
 308: 	** during the create but is removed afterwards.
 309: 	*/
 310:     if ((relstat & S_CATALOG) == 0)
 311:     {
 312:         /* for non system named temporary relations
 313: 		** set a critical section lock while checking the
 314: 		** existence of a file.  If it exists, error return(5102)
 315: 		** else create file.
 316: 		*/
 317:         temptid = 0;
 318:         if (Lockrel && (!bequal(rel.relid,"_SYS",4)))
 319:         {
 320:             temptid = -1;
 321:             setcsl(temptid);    /* set critical section lock */
 322:             if ((fdes = open(rel.relid,0)) >= 0)
 323:             {
 324:                         /* file already exists */
 325:                 close(fdes);
 326:                 unlcs(temptid); /* release critical section lock */
 327:                 return (error(5102, relname, 0));
 328:             }
 329:             errno = 0;  /* file doesn't exist */
 330:         }
 331:         ingresname(rel.relid, rel.relowner, tempname);
 332:         desr.relfp = creat(tempname, FILEMODE);
 333:         if (temptid != 0)
 334:             unlcs(temptid); /* release critical section lock */
 335:         if (desr.relfp < 0)
 336:             syserr("create: creat %s", rel.relid);
 337:         desr.reltid = -1;   /* init reltid to unused */
 338:         if ((relstat & S_VIEW) == 0)
 339:         {
 340:             npages = 1;
 341:             if (i = formatpg(&desr, npages))
 342:                 syserr("syserr: formatpg %d", i);
 343:         }
 344: 
 345:         close(desr.relfp);
 346:     }
 347: 
 348:     /* insert attributes into attribute relation */
 349:     pp = pv + 2;
 350:     dom = domain;
 351:     for (i = pc - 2; i > 0; i -= 2)
 352:     {
 353:         ins_att(&Attdes, &att, dom++);
 354:         pp += 2;
 355:     }
 356: 
 357:     /*
 358: 	** Flush the attributes. This is necessary for recovery reasons.
 359: 	** If for some reason the relation relation is flushed and the
 360: 	** machine crashes before the attributes are flushed, then recovery
 361: 	** will not detect the error.
 362: 	** The call below cannot be a "noclose" without major changes to
 363: 	** creatdb.
 364: 	*/
 365:     if (i = pageflush(0))
 366:         syserr("create:flush att %d", i);
 367: 
 368:     if (i = insert(&Reldes, &tid, &rel, FALSE))
 369:         syserr("create: insert(rel, %.14s) %d", rel.relid, i);
 370: 
 371:     if (relstat & S_VIEW)
 372:         unlink(tempname);
 373:     return (0);
 374: }
 375: 
 376: 
 377: 
 378: /*
 379: **  CHK_ATT -- check attribute for validity
 380: **
 381: **	The attribute is checked to see if
 382: **	* it's name is ok (within MAXNAME bytes)
 383: **	* it is not a duplicate name
 384: **	* the format specified is legal
 385: **	* there are not a ridiculous number of attributes
 386: **	  (ridiculous being defined as anything over MAXDOM - 1)
 387: **	* the tuple is not too wide to fit on one page
 388: **
 389: **	Parameters:
 390: **		rel -- relation relation tuple for this relation.
 391: **		attname -- tentative name of attribute.
 392: **		format -- tentative format for attribute.
 393: **		domain -- a 'struct domain' used to determine dupli-
 394: **			cation, and to store the resulting name and
 395: **			format in.
 396: **
 397: **	Returns:
 398: **		zero -- OK
 399: **		5104 -- bad attribute name.
 400: **		5105 -- duplicate attribute name.
 401: **		5106 -- bad attribute format.
 402: **		5107 -- too many attributes.
 403: **		5108 -- tuple too wide.
 404: **
 405: **	Side Effects:
 406: **		'rel' has the relatts and relwid fields updated to
 407: **		reflect the new attribute.
 408: **
 409: **	Requires:
 410: **		length -- to check length of 'attname' against MAXNAME.
 411: **		dup_att -- to check for duplicate attribute and
 412: **			initialize 'name' field of 'domain' struct.
 413: **		formck -- to check and convert the attribute format.
 414: **
 415: **	Called By:
 416: **		create
 417: **
 418: **	Trace Flags:
 419: **		2.1 -- entry print
 420: **
 421: **	Diagnostics:
 422: **		as noted in the return.
 423: **
 424: **	Syserrs:
 425: **		none
 426: **
 427: **	History:
 428: **		11/1/78 (rse) -- views have no limit on tuple width.
 429: **		2/27/78 (eric) -- documented.
 430: */
 431: 
 432: chk_att(rel, attname, format, domain)
 433: struct relation     *rel;
 434: char            *attname, *format;
 435: struct domain       domain[];
 436: {
 437:     register int            i;
 438:     register struct relation    *r;
 439: 
 440:     r = rel;
 441: 
 442: #	ifdef xZTR3
 443:     if (tTf(2, 1))
 444:         printf("chk_att %s %s\n", attname, format);
 445: #	endif
 446: 
 447:     if (sequal(attname, "tid"))
 448:         return (5104);      /* bad attribute name */
 449:     if ((i = dup_att(attname, r->relatts, domain)) < 0)
 450:         return (5105);      /* duplicate attribute */
 451:     if (formck(format, &domain[i]))
 452:         return (5106);      /* bad attribute format */
 453:     r->relatts++;
 454:     r->relwid += domain[i].frml & 0377;
 455:     if (r->relatts >= MAXDOM)
 456:         return (5107);      /* too many attributes */
 457:     if (r->relwid > MAXTUP && (r->relstat & S_VIEW) == 0)
 458:         return (5108);      /* tuple too wide */
 459:     return (0);
 460: }
 461: 
 462: 
 463: 
 464: 
 465: /*
 466: **  INS_ATT -- insert attribute into attribute relation
 467: **
 468: **	Parameters:
 469: **		des -- relation descriptor for the attribute catalog.
 470: **		att -- attribute tuple, preinitialized with all sorts
 471: **			of good stuff (everything except 'attname',
 472: **			'attfrmt', and 'attfrml'; 'attid' and 'attoff'
 473: **			must be initialized to zero before this routine
 474: **			is called the first time.
 475: **		dom -- 'struct domain' -- the information needed about
 476: **			each domain.
 477: **
 478: **	Returns:
 479: **		none
 480: **
 481: **	Side Effects:
 482: **		The 'att' tuple is updated in the obvious ways.
 483: **		A tuple is added to the 'attribute' catalog.
 484: **
 485: **	Requires:
 486: **		insert
 487: **		pmove -- to make the attribute name nice and clean.
 488: **
 489: **	Called By:
 490: **		create
 491: **
 492: **	Trace Flags:
 493: **		none currently
 494: **
 495: **	Diagnostics:
 496: **		none
 497: **
 498: **	Syserrs:
 499: **		ins_att: insert(att, %s)
 500: **			The insert into the attribute catalog failed.
 501: **
 502: **	History:
 503: **		2/27/78 (eric) -- result of insert checked.
 504: */
 505: 
 506: ins_att(des, att, dom)
 507: struct descriptor   *des;
 508: struct attribute    *att;
 509: struct domain       *dom;
 510: {
 511:     register int        i;
 512:     struct tup_id       tid;
 513:     register struct domain  *d;
 514: 
 515:     d = dom;
 516: 
 517:     pmove(d->name, att->attname, MAXNAME, ' ');
 518:     att->attfrmt = d->frmt;
 519:     att->attfrml = d->frml;
 520:     att->attid++;
 521:     if (insert(des, &tid, att, FALSE))
 522:         syserr("ins_att: insert(att, %s)", d->name);
 523:     att->attoff += att->attfrml & 0377;
 524: }
 525: 
 526: 
 527: 
 528: 
 529: /*
 530: **  DUP_ATT -- check for duplicate attribute
 531: **
 532: **	The attribute named 'name' is inserted into the 'attalias'
 533: **	vector at position 'count'.  'Count' should be the count
 534: **	of existing entries in 'attalias'.  'Attalias' is checked
 535: **	to see that 'name' is not already present.
 536: **
 537: **	Parameters:
 538: **		name -- the name of the attribute.
 539: **		count -- the count of attributes so far.
 540: **		domain -- 'struct domain' -- the list of domains
 541: **			so far, names and types.
 542: **
 543: **	Returns:
 544: **		-1 -- attribute name is a duplicate.
 545: **		else -- index in 'domain' for this attribute (also
 546: **			the attid).
 547: **
 548: **	Side Effects:
 549: **		The 'domain' vector is extended.
 550: **
 551: **	Requires:
 552: **		none
 553: **
 554: **	Called By:
 555: **		chk_att
 556: **
 557: **	Trace Flags:
 558: **		none
 559: **
 560: **	History:
 561: **		2/27/78 (eric) -- documented.
 562: */
 563: 
 564: dup_att(name, count, domain)
 565: char        *name;
 566: int     count;
 567: struct domain   domain[];
 568: {
 569:     register struct domain  *d;
 570:     register int        lim;
 571:     register int        i;
 572: 
 573:     lim = count;
 574:     d = domain;
 575: 
 576:     for (i = 0; i < lim; i++)
 577:         if (sequal(name, d++->name))
 578:             return (-1);
 579:     if (count < MAXDOM)
 580:         d->name = name;
 581:     return (i);
 582: }
 583: 
 584: 
 585: 
 586: 
 587: /*
 588: **  INITSTRUCTS -- initialize relation and attribute tuples
 589: **
 590: **	Structures containing images of 'relation' relation and
 591: **	'attribute' relation tuples are initialized with all the
 592: **	information initially needed to do the create.  Frankly,
 593: **	the only interesting part is the the expiration date
 594: **	computation; longconst(9, 14976) is exactly the number
 595: **	of seconds in one week.
 596: **
 597: **	Parameters:
 598: **		att -- attribute relation tuple.
 599: **		rel -- relation relation tuple.
 600: **
 601: **	Returns:
 602: **		none
 603: **
 604: **	Side Effects:
 605: **		'att' and 'rel' are initialized.
 606: **
 607: **	Requires:
 608: **		time -- to get the current date.
 609: **
 610: **	Called By:
 611: **		create
 612: **
 613: **	Trace Flags:
 614: **		none
 615: **
 616: **	Diagnostics:
 617: **		none
 618: **
 619: **	Syserrs:
 620: **		none
 621: **
 622: **	History:
 623: **		2/27/78 (eric) -- documented.
 624: */
 625: 
 626: initstructs(att1, rel1)
 627: struct attribute    *att1;
 628: struct relation     *rel1;
 629: {
 630:     register struct relation    *rel;
 631:     register struct attribute   *att;
 632:     extern long         longconst();
 633: 
 634:     rel = rel1;
 635:     att = att1;
 636: 
 637:     /* setup expiration date (today + one week) */
 638:     time(&rel->relsave);
 639:     rel->relsave += longconst(9, 14976);
 640: 
 641:     rel->reltups = 0;
 642:     rel->relatts = 0;
 643:     rel->relwid = 0;
 644:     rel->relprim = 1;
 645:     rel->relspec = M_HEAP;
 646:     rel->relindxd = 0;
 647:     rel->relspare = 0;
 648: 
 649:     att->attxtra = 0;
 650:     att->attid = 0;
 651:     att->attoff = 0;
 652: }
 653: 
 654: 
 655: 
 656: /*
 657: **  CHECK ATTRIBUTE FORMAT AND CONVERT
 658: **
 659: **	The string 'a' is checked for a valid attribute format
 660: **	and is converted to internal form.
 661: **
 662: **	zero is returned if the format is good; one is returned
 663: **	if it is bad.  If it is bad, the conversion into a is not
 664: **	made.
 665: **
 666: **	A format of CHAR can be length zero but a format
 667: **	of 'c' cannot.
 668: */
 669: 
 670: formck(a, dom)
 671: char        *a;
 672: struct domain   *dom;
 673: {
 674:     int         len;
 675:     register int        i;
 676:     char            c;
 677:     register char       *p;
 678:     register struct domain  *d;
 679: 
 680:     p = a;
 681:     c = *p++;
 682:     d = dom;
 683: 
 684:     if (atoi(p, &len) != 0)
 685:         return (1);
 686:     i = len;
 687: 
 688:     switch (c)
 689:     {
 690: 
 691:       case INT:
 692:       case 'i':
 693:         if (i == 1 || i == 2 || i == 4)
 694:         {
 695:             d->frmt = INT;
 696:             d->frml = i;
 697:             return (0);
 698:         }
 699:         return (1);
 700: 
 701:       case FLOAT:
 702:       case 'f':
 703:         if (i == 4 || i == 8)
 704:         {
 705:             d->frmt = FLOAT;
 706:             d->frml = i;
 707:             return (0);
 708:         }
 709:         return (1);
 710: 
 711:       case 'c':
 712:         if (i == 0)
 713:             return (1);
 714:       case CHAR:
 715:         if (i > 255 || i < 0)
 716:             return (1);
 717:         d->frmt = CHAR;
 718:         d->frml = i;
 719:         return (0);
 720:     }
 721:     return (1);
 722: }

Defined functions

chk_att defined in line 432; used 1 times
dup_att defined in line 564; used 1 times
formck defined in line 670; used 1 times
initstructs defined in line 626; used 1 times
ins_att defined in line 506; used 1 times

Defined struct's

domain defined in line 106; used 18 times
Last modified: 1995-02-04
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3569
Valid CSS Valid XHTML 1.0 Strict