1: /*
   2:  *	U N I X   2 . 9 B S D   C R A S H   A N A L Y Z E R   S U B S
   3:  *
   4:  * More proc struct flag changes.  No progress (no effort either ;)) on
   5:  * getting the program to compile - 1999/9/14
   6:  *
   7:  * Another proc struct flag went away.  Program still doesn't run or
   8:  * compile ;-(  1999/8/11
   9:  *
  10:  * The proc structure flags cleaned up.  This program still doesn't run
  11:  * (or compile) under the current system.  1997/9/2
  12:  *
  13:  * All the tty delay bits went away.  1997/3/28
  14:  *
  15:  * 'LCASE' and 'LTILDE' went away.  Some day this program should be
  16:  * rewritten to reflect the current system.  12/9/94
  17:  */
  18: 
  19: #include <stdio.h>
  20: #include <sys/param.h>
  21: #include <sys/fs.h>
  22: #include <sys/mount.h>
  23: #include <sys/inode.h>
  24: #include <sys/stat.h>
  25: #include <sys/dir.h>
  26: #include <pwd.h>
  27: #include <grp.h>
  28: #include "crash.h"
  29: 
  30: /*
  31:  * structure to access a word as bytes
  32:  */
  33: struct byteof {
  34:     char    lobyte;
  35:     char    hibyte;
  36: };
  37: 
  38: /* These globals are used in crash.c */
  39: char    *subhead;           /* pntr to sub-heading */
  40: int line;               /* current line number */
  41: 
  42: /*
  43:  *			S T R O C T
  44:  *
  45:  *	stroct		Mark Kampe	7/2/75
  46:  *	returns:	int
  47:  *	takes:		*char
  48:  *	it does:	If string represents an octal integer,
  49:  *			the value of that integer is returned.
  50:  *			If not, a zero is returned.
  51:  *			The string should be null terminated and
  52:  *			contain no non-octal-numerics.
  53:  */
  54: int stroct(s1)
  55:     char *s1;
  56: {
  57:     register char *p;
  58:     register char thisn;
  59:     register int value;
  60: 
  61:     p = s1;
  62:     value = 0;
  63:     while (thisn = *p++)
  64:         if ((thisn >= '0') && (thisn <= '7')) {
  65:             value <<= 3;
  66:             value += thisn - '0';
  67:         }
  68:         else return(0);
  69: 
  70:     return(value);
  71: }
  72: 
  73: /*
  74:  *			O C T O U T
  75:  *
  76:  *
  77:  *	octout		Mark Kampe	7/2/75
  78:  *	returns:	nothing
  79:  *	takes:		int
  80:  *	it does:	print the integer as a six digit
  81:  *			octal number with leading zeroes
  82:  *			as required.  I wrote this because
  83:  *			I found the octal output produced
  84:  *			by printf to be very hard to read.
  85:  *			maybe I'm a pervert, but I like the leading
  86:  *			zeroes.  If you dont, replace this
  87:  *			routine with "printf("%6o",arg);"
  88:  *
  89:  */
  90: octout(value)
  91: register unsigned value;
  92: {
  93:     char outbuf[7];
  94:     register char *c;
  95:     register int i;
  96: 
  97:     c = &outbuf[5];
  98: 
  99:     for(i = 0; i<6; i++) {
 100:         *c-- = (value & 07) + '0';
 101:         value >>= 3;
 102:     }
 103: 
 104:     outbuf[6] = 0;      /* Null terminate string */
 105:     printf("%s", outbuf );
 106:     return;
 107: }
 108: /*
 109:  *			L O C T O U T
 110:  *
 111:  *
 112:  *	loctout		John Stewart 3 Mar 83
 113:  *	returns:	nothing
 114:  *	takes:		long int
 115:  *	it does:	print the integer as an eleven digit
 116:  *			octal number with leading zeroes
 117:  *			as required.  (See octout, above.)
 118:  *
 119:  */
 120: loctout(value)
 121: long value;
 122: {
 123:     char outbuf[12];
 124:     register char *c;
 125:     register int i;
 126: 
 127:     c = &outbuf[10];
 128: 
 129:     for(i = 0; i<10; i++) {
 130:         *c-- = (value & 07) + '0';
 131:         value >>= 3;
 132:     }
 133:     /* no unsigned long on this machine */
 134:     *outbuf = (value & 03) + '0';
 135: 
 136:     outbuf[11] = 0;     /* Null terminate string */
 137:     printf("%s", outbuf );
 138:     return;
 139: }
 140: 
 141: hexout(value)
 142: unsigned value;
 143: {
 144:     hexdump((long)value, 4);
 145: }
 146: 
 147: hexdump(value, digs)
 148: long value;
 149: register int digs;
 150: {
 151:     char outbuf[12];
 152:     register char *c;
 153:     register int i;
 154: 
 155:     c = &outbuf[digs];
 156:     *c-- = 0;       /* null terminate */
 157:     for(; digs--;) {
 158:         *c-- = "0123456789ABCDEF"[value & 0x0f];
 159:         value >>= 4;
 160:     }
 161: 
 162:     printf("%s", outbuf );
 163:     return;
 164: }
 165: 
 166: /*
 167:  *			B A R F
 168:  *
 169:  *
 170:  *	Print a diagnostic, flush buffers, and exit
 171:  */
 172: barf( c1 )
 173: register char *c1;
 174: {
 175:     printf("%s\n", c1);
 176:     exit(10);
 177: }
 178: 
 179: /*
 180:  *			N E W P A G E
 181:  *
 182:  *
 183:  *	New page processor
 184:  */
 185: newpage() {
 186:     static int  page = 0;           /* current page number */
 187: 
 188:     page++;
 189:     line = 0;
 190:     putchar( NEWPAGE );
 191: 
 192:     printf("\n\t\t* * * UNIX Crash Dump Analyzer * * *\t\tPage %d\n\n",
 193:         page);
 194:     printf("\t\t\t%s\n\n", subhead);
 195: }
 196: 
 197: 
 198: /*
 199:  *			S H O W
 200:  *
 201:  *	This routine takes an address and a format type, and arranges
 202:  * to have the value at the given address printed in the appropriate
 203:  * format.
 204:  *	Mike Muuss, 6/28/77.
 205:  */
 206: 
 207: show( addr, fmt )
 208: register unsigned *addr;
 209: register fmt;
 210: {
 211:     register char *byte;
 212:     int i,j;
 213: 
 214:     switch( fmt ) {
 215: 
 216:     /* Special code to just return */
 217:     case IGNORE:
 218:         return;
 219: 
 220:     /* Octal.  Use Mark Kampe's nice routine */
 221:     case OCT:
 222:         octout( *addr );
 223:         return;
 224: 
 225:     /* Long Octal.  Use John Stewart's nice routine */
 226:     case LONGOCT:
 227:         loctout(*(long *)addr);
 228:         return;
 229: 
 230:     case HEXL:
 231:         hexdump(*(long *)addr, 8);
 232:         return;
 233:     case HEXW:
 234:         hexdump((long)*(int *)addr, 4);
 235:         return;
 236:     case HEXB:
 237:         hexdump((long)*(char *)addr, 2);
 238:         return;
 239: 
 240:     /* Interupt Address Symbolicaly */
 241:     case TADDR:
 242:         symbol( *addr, ISYM, 0 );
 243:         return;
 244:     case DADDR:
 245:         symbol( *addr, DSYM, 0 );
 246:         return;
 247: 
 248: 
 249:     /* Decimal.  Append a dot for ease of reading */
 250:     case DEC:
 251:         printf("%6d.", *addr);
 252:         return;
 253: 
 254:     /* Unsigned Decimal */
 255:     case UDEC:
 256:         printf("%6u.", *addr);
 257:         return;
 258: 
 259:     /* Show both halves of the word */
 260:     case DEV:
 261:         printf("%2d./%2d.", ((struct byteof *)addr)->hibyte,
 262:             (((struct byteof *)addr)->lobyte)&0377);
 263:         return;
 264: 
 265:     /* Show the byte */
 266:     case ONEBYTE:
 267:         byte = (char *) addr;       /* better safe than sorry */
 268:         printf("%6o", (*byte) & 0377 );
 269:         return;
 270: 
 271:     /* Show printable characters */
 272:     case CHARS:
 273:         byte = (char *) addr;
 274:         for( i=0; i < CBSIZE; i++ )  {
 275:             j = *byte++ & 0377;
 276:             if( (j<' ') || (j>'~') )
 277:                 printf("0%o", j);
 278:             else
 279:                 putchar( j );
 280:             putchar(' ');
 281:         }
 282:         return;
 283: 
 284:     /* Show the byte in decimal */
 285:     case HALFDEC:
 286:         byte = (char *) addr;
 287:         j = *byte & 0377;
 288:         printf("%d.", j);
 289:         return;
 290: 
 291:     /* Show the long in decimal */
 292:     case LONGDEC:
 293:         printf("%ld.", *((long *)addr));
 294:         return;
 295: 
 296:     /* Just in case */
 297:     default:
 298:         printf("I can't show that!");
 299:         return;
 300: 
 301:     }
 302: }
 303: 
 304: /*
 305:  *			P U T B I T S
 306:  *
 307:  *	This routine accepts a table of 16 strings representing each
 308:  * bit in a word.  For every bit set in the given word, the
 309:  * associated message is printed.
 310:  *	Mike Muuss, 6/28/77.
 311:  */
 312: 
 313: putbits( array, word )
 314: register char *array[];
 315: register int word;
 316: {
 317:     register int i;         /* current bit # */
 318: 
 319:     for( i=0; i<16; i++)
 320:         if( (word >> i) & 01 )  printf("%s", array[i] );
 321: }
 322: 
 323: /*
 324:  *			C O L
 325:  *
 326:  *	Print a region in nice columns
 327:  *
 328:  *	Each line is preceeded with 'indent' spaces,
 329:  * and 8 octal values are printed on each line.  Output
 330:  * comences at 'base' and proceeds for 'offset' words.
 331:  */
 332: 
 333: col( indent, base, offset )
 334: register unsigned *base;
 335: register offset;
 336: {
 337:     register i;         /* Current offset */
 338:     int here;           /* Current line pointer */
 339:     int j;          /* indent counter */
 340: 
 341:     here = 50;          /* force end of line */
 342: 
 343:     for( i=0; i < offset; i++ ) {
 344:         if( ++here >= 8 ) {
 345:             /* End of Line */
 346:             here = 0;
 347:             putchar( '\n' );
 348:             j = indent;
 349:             while( j-- )  putchar( ' ' );
 350:         }
 351:         else putchar ('\t');
 352:         octout( *( base + i ) );
 353:     }
 354: }
 355: 
 356: 
 357: /*
 358:  *			E Q U A L
 359:  *
 360:  *
 361:  *	Determine if the first 8 characters (or up to NULL if
 362:  * shorter than 8 chars) are equal.  True return if yes.
 363:  */
 364: 
 365: equal( a, b )
 366: register char *a, *b;
 367: {
 368:     register i;
 369:     register wrong;
 370: 
 371:     wrong = 0;
 372:     for( i=0; i < 8; i++ ) {
 373:         if( !*a && !*b ) break;
 374:         if( *a++ != *b++ )  wrong++;
 375:     }
 376:     if( wrong ) return( 0 );            /* mismatch */
 377:     return( 1 );                    /* match */
 378: }
 379: 
 380: 
 381: /*
 382:  *			P R I N T S
 383:  *
 384:  *   This function converts the 'number' argument into decimal
 385:  * and outprintf it at location 'pointer'.  Leading zeros are
 386:  * suppressed.
 387:  *	Mike Muuss, 7/8/77.
 388:  */
 389: 
 390: char *
 391: prints( pointer, number )
 392: register char *pointer;
 393: register int  number;
 394: {
 395:     register left;          /* whats left */
 396: 
 397:     if( left = number/10 )  pointer = prints( pointer, left );
 398:     *pointer++ = ( number%10 ) + '0';
 399:     *pointer = 0;           /* string terminator */
 400:     return( pointer );
 401: }
 402: 
 403: /*
 404:  *			D I S P L A Y
 405:  *
 406:  *	This routine takes a structure of 'display' format,
 407:  * and generates output for each element until an END element
 408:  * is encountered.  The offset field is added to the base
 409:  * address of each structure element, in case this routine
 410:  * is being repeatedly called to display various structure
 411:  * elements from the core dump.
 412:  *	Mike Muuss, 6/27/77.
 413:  */
 414: 
 415: display( table, offset )
 416: register struct display *table;
 417: register offset;
 418: {
 419: 
 420:     while( table -> fmt ) {
 421:         /* Display Prefix */
 422:         printf("%s:\t", table -> msg );
 423: 
 424:         /*
 425: 		 *  Format item
 426: 		 *
 427: 		 * offset is taken to be a byte pointer
 428: 		 * place is now defined as a byte pointer
 429: 		 */
 430:         show((unsigned *)((table->place)+offset), table->fmt);
 431:         if (table->routine)
 432:             (*table->routine)((table->place)+offset);
 433:         table++;
 434:     }
 435: }
 436: 
 437: /*
 438:  *	P R I N T D E V
 439:  *
 440:  * This routine takes a pointer to a mount structure and spins through
 441:  * the /dev/directory look for a matching file with the same major/minor
 442:  * numbers. It also prints out the plain file name from the super-block.
 443:  *
 444:  */
 445: 
 446: #define DEVDIR "/dev"
 447: 
 448: printdev(mp)
 449: struct mount *mp;
 450: {
 451:     struct stat stb;
 452:     register struct direct *dirent;
 453:     struct DIR *dirfd;
 454:     char filename[20];
 455: 
 456:     dirfd = opendir(DEVDIR);
 457:     while ((dirent = readdir(dirfd)) != NULL) {
 458:         if (dirent->d_ino) {
 459:             sprintf(filename, "%s/%.*s", DEVDIR,
 460:                 dirent->d_namlen, dirent->d_name);
 461:             if (stat(filename, &stb) == 0 &&
 462:                 (stb.st_mode & S_IFMT) == S_IFBLK &&
 463:                 stb.st_rdev == mp->m_dev) {
 464:                 printf("\t%s\t%s\n", filename,
 465:                     mp->m_filsys.fs_fsmnt);
 466:                 break;
 467:             }
 468:         }
 469:     }
 470:     closedir(dirfd);
 471: }
 472: 
 473: /*
 474:  * Print a value a la the %b format of the kernel's printf
 475:  */
 476: printb(v, bits)
 477:     u_long v;
 478:     register char *bits;
 479: {
 480:     register int i, any = 0;
 481:     register char c;
 482: 
 483:     if (v && bits)
 484:         if (*bits == 8)
 485:             printf("0%lo=", v);
 486:         else
 487:             if (*bits == 16)
 488:                 printf("0x%lx=", v);
 489:             else
 490:                 putchar(' ');
 491:     bits++;
 492:     if (v && bits) {
 493:         putchar('<');
 494:         while (i = *bits++) {
 495:             if (v & (1 << (i-1))) {
 496:                 if (any)
 497:                     putchar(',');
 498:                 any = 1;
 499:                 for (; (c = *bits) > 32; bits++)
 500:                     putchar(c);
 501:             } else
 502:                 for (; *bits > 32; bits++)
 503:                     ;
 504:         }
 505:         putchar('>');
 506:     }
 507: }
 508: 
 509: /*
 510:  * Print out information about the PDR passed as argument
 511:  */
 512: 
 513: char *acffld[] = {
 514:     "non-resident",
 515:     "read-only",
 516:     "read-only",
 517:     "unused",
 518:     "read/write",
 519:     "read/write",
 520:     "read/write",
 521:     "unused"
 522: };
 523: 
 524: pdrprint(pdr)
 525: unsigned *pdr;
 526: {
 527:     char plf, a, w, ed, acf;
 528: 
 529:     plf = (*pdr & 077400) >> 8;
 530:     a = (*pdr & 0200) >> 7;
 531:     w = (*pdr & 0100) >> 6;
 532:     ed = (*pdr & 010) >> 3;
 533:     acf = (*pdr & 07);
 534:     printf(" plf: %d.%s%s%s acf: 0%o %s", plf, (a ? " A" : ""),
 535:           (w ? " W" : ""), (ed ? " ED" : ""), acf, acffld[acf]);
 536: }
 537: 
 538: /*
 539:  * Print the uid passed
 540:  */
 541: 
 542: printuid(uid)
 543: unsigned *uid;
 544: {
 545:     struct passwd *pwd, *getpwuid();
 546: 
 547:     pwd = getpwuid(*uid);
 548:     if (pwd)
 549:         printf("\t%s", pwd->pw_name);
 550: }
 551: 
 552: /*
 553:  * Print the gid passed
 554:  */
 555: 
 556: printgid(gid)
 557: unsigned *gid;
 558: {
 559:     struct group *gwd, *getgrgid();
 560: 
 561:     gwd = getgrgid(*gid);
 562:     if (gwd)
 563:         printf("\t%s", gwd->gr_name);
 564: }
 565: 
 566: /*
 567:  * Print out sinals by name.
 568:  */
 569: 
 570: prtsig(sigm)
 571: long *sigm;
 572: {
 573: #define SIGM_FLAGS "\0\1HUP\2INT\3QUIT\4ILL\5TRAP\6IOT\7EMT\10FPE\11KILL\12BUS\13SEGV\14SYS\15PIPE\16ALRM\17TERM\20URG\21STOP\22TSTP\23CONT\24CHLD\25TTIN\26TTOU\27IO\30XCPU\31XFSZ\32VTALRM\33PROF\34WINCH\36USR1\37USR2"
 574:     printb((u_long) *sigm, SIGM_FLAGS);
 575: }
 576: 
 577: /*
 578:  * Print out flags in the proc structure
 579:  */
 580: 
 581: procflg(flgs)
 582: int *flgs;
 583: {
 584: #define PROC_FLAGS "\0\1SLOAD\2SSYS\3SLOCK\4SSWAP\5P_TRACED\6P_WAITED\7SULOCK\8P_SINTR\11SVFORK\12SVFPRNT\13SVFDONE\15P_TIMEOUT\16P_NOCLDSTOP\17P_SELECT"
 585:     printb((u_long) *flgs, PROC_FLAGS);
 586: }
 587: 
 588: /*
 589:  * Print out the flags in the tty structure
 590:  */
 591: 
 592: ttyflg(flgs)
 593: unsigned *flgs;
 594: {
 595: #define TTY_FLAGS "\0\1TANDEM\2CBREAK\4ECHO\5CRMOD\6RAW\7ODDP\10EVENP\21CRTBS\22PRTERA\23CRTERA\25MDMBUF\26LITOUT\27TOSTOP\30FLUSHO\31NOHANG\32RTSCTS\33CRTKILL\34PASS8\35CTLECH\36PENDIN\DECCTQ"
 596:     printb((u_long) *flgs, TTY_FLAGS);
 597: }
 598: 
 599: ttystat(flgs)
 600: unsigned *flgs;
 601: {
 602: #define TTY_STATE "\0\1TIMEOUT\2WOPEN\3ISOPEN\4FLUSH\5CARR_ON\6BUSY\7ASLEEP\10XCLUDE\11TTSTOP\12HUPCLS\13TBLOCK\14RCOLL\15WCOLL\16NBIO\17ASYNC\21BKSL\22QUOT\23ERASE\24LNCH\25TYPEN\26CNTTB"
 603:     printb((u_long) *flgs, TTY_STATE);
 604: }
 605: 
 606: /*
 607:  * Ths routine prints out the flags in the buf structure
 608:  */
 609: 
 610: bufflg(flgs)
 611: unsigned *flgs;
 612: {
 613: #define BUF_FLAGS "\0\1READ\2DONE\3ERROR\4BUSY\5PHYS\6MAP\7WANTED\10AGE\11ASYNC\12DELWRI\13TAPE\14INVAL\15bad\16RH70\17UBAREMAP\18RAMREMAP"
 614:     printb((u_long) *flgs, BUF_FLAGS);
 615: }
 616: 
 617: /*
 618:  * This routine prints out the flags in the inode structure
 619:  */
 620: 
 621: inoflg(flgs)
 622: unsigned *flgs;
 623: {
 624: #define INO_FLAGS "\0\1ILOCKED\2IUPD\3IACC\4IMOUNT\5IWANT\6ITEXT\7ICHG\10SHLOCK\11IEXLOCK\12ILWAIT\13IMOD\14IRENAME\15IPIPE\20IXMOD"
 625:     printb((u_long) *flgs, INO_FLAGS);
 626: }
 627: 
 628: /*
 629:  * This routine prints out the file mode from the inode structure
 630:  */
 631: 
 632: inomod(ip)
 633: struct inode *ip;
 634: {
 635:     char special = 0;
 636:     unsigned dev;
 637: 
 638:     show(&ip->i_mode, OCT);
 639:     switch (ip->i_mode & IFMT) {
 640:         case IFDIR:
 641:                 printf(" (Directory)");
 642:                 break;
 643:         case IFCHR:
 644:                 special++;
 645:                 printf(" (Character Device)");
 646:                 break;
 647:         case IFBLK:
 648:                 special++;
 649:                 printf(" (Block Device)");
 650:                 break;
 651:         case IFREG:
 652:                 printf(" (Regular)");
 653:                 break;
 654:         case IFLNK:
 655:                 printf(" (Symbolic Link)");
 656:                 break;
 657:         case IFSOCK:
 658:                 printf(" (Socket)");
 659:                 break;
 660:         case 0:
 661:                 printf(" (Free-inode)");
 662:                 break;
 663:         default:
 664:                 printf(" (Garbage!)");
 665:                 break;
 666:     }
 667:     if (special) {
 668:         printf("\tdev:\t");
 669:         dev = (unsigned) ip->i_rdev;
 670:         show(&dev, DEV);
 671:         putchar('\n');
 672:     } else {
 673:         printf("\tsize:\t");
 674:         show(&ip->i_size, LONGDEC);
 675:         printf("\n\t\tlastr:\t");
 676:         show(&ip->i_lastr, LONGDEC);
 677:         printf("\taddr[0]:\t");
 678:         show(&ip->i_addr[0], LONGDEC);
 679:     }
 680:     putchar('\n');
 681: }
 682: 
 683: /*
 684:  * Print out the processor status in a symbolic format
 685:  */
 686: 
 687: char *mode[] = {
 688:     "kernel",
 689:     "supervisor",
 690:     "illegal",
 691:     "user"
 692: };
 693: 
 694: printps(ps)
 695: unsigned ps;
 696: {
 697:     char curmode, prevmode, cis, prio, t, n, z, v, c;
 698: 
 699:     curmode = (ps >> 14) & 03;
 700:     prevmode = (ps >> 12) & 03;
 701:     cis = (ps >> 8) & 01;
 702:     prio = (ps >> 5) & 07;
 703:     t = (ps >> 4) & 01;
 704:     n = (ps >> 3) & 01;
 705:     z = (ps >> 2) & 01;
 706:     v = (ps >> 1) & 01;
 707:     c = ps & 01;
 708: 
 709:     printf(" <%s %s%s spl: %d.%s%s%s%s%s>", mode[curmode], mode[prevmode],
 710:         (cis ? " CIS" : ""), prio, (t ? " T" : ""), (n ? " N" : ""),
 711:         (z ? " Z" : ""), (v ? " V" : ""), (c ? " C" : ""));
 712: }

Defined functions

bufflg defined in line 610; used 2 times
col defined in line 333; used 2 times
display defined in line 415; used 15 times
equal defined in line 365; used 1 times
hexdump defined in line 147; used 4 times
hexout defined in line 141; used 1 times
inoflg defined in line 621; used 2 times
inomod defined in line 632; used 2 times
loctout defined in line 120; used 2 times
newpage defined in line 185; used 23 times
octout defined in line 90; used 34 times
pdrprint defined in line 524; used 17 times
printb defined in line 476; used 6 times
printdev defined in line 448; used 1 times
printgid defined in line 556; used 5 times
printps defined in line 694; used 1 times
prints defined in line 390; used 2 times
printuid defined in line 542; used 6 times
procflg defined in line 581; used 2 times
prtsig defined in line 570; used 4 times
putbits defined in line 313; used 1 times
show defined in line 207; used 6 times
stroct defined in line 54; used 3 times
ttyflg defined in line 592; used 2 times
ttystat defined in line 599; used 2 times

Defined variables

acffld defined in line 513; used 1 times
line defined in line 40; used 1 times
mode defined in line 687; used 2 times
  • in line 709(2)
subhead defined in line 39; used 1 times

Defined struct's

byteof defined in line 33; used 4 times

Defined macros

BUF_FLAGS defined in line 613; used 1 times
DEVDIR defined in line 446; used 2 times
INO_FLAGS defined in line 624; used 1 times
PROC_FLAGS defined in line 584; used 1 times
SIGM_FLAGS defined in line 573; used 1 times
TTY_FLAGS defined in line 595; used 1 times
TTY_STATE defined in line 602; used 1 times
Last modified: 1999-09-15
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3962
Valid CSS Valid XHTML 1.0 Strict