149062Sbostic /*- 263176Sbostic * Copyright (c) 1986, 1988, 1991, 1993 363176Sbostic * The Regents of the University of California. All rights reserved. 4*65771Sbostic * (c) UNIX System Laboratories, Inc. 5*65771Sbostic * All or some portions of this file are derived from material licensed 6*65771Sbostic * to the University of California by American Telephone and Telegraph 7*65771Sbostic * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8*65771Sbostic * the permission of UNIX System Laboratories, Inc. 923381Smckusick * 1049062Sbostic * %sccs.include.redist.c% 1149062Sbostic * 12*65771Sbostic * @(#)subr_prf.c 8.3 (Berkeley) 01/21/94 1323381Smckusick */ 1431Sbill 1551768Smarc #include <sys/param.h> 1651768Smarc #include <sys/systm.h> 1751768Smarc #include <sys/buf.h> 1851768Smarc #include <sys/conf.h> 1951768Smarc #include <sys/reboot.h> 2051768Smarc #include <sys/msgbuf.h> 2151768Smarc #include <sys/proc.h> 2251768Smarc #include <sys/ioctl.h> 2351768Smarc #include <sys/vnode.h> 2451768Smarc #include <sys/file.h> 2551768Smarc #include <sys/tty.h> 2651768Smarc #include <sys/tprintf.h> 2751768Smarc #include <sys/syslog.h> 2851768Smarc #include <sys/malloc.h> 2931Sbill 3049062Sbostic /* 3149062Sbostic * Note that stdarg.h and the ANSI style va_start macro is used for both 3249062Sbostic * ANSI and traditional C compilers. 3349062Sbostic */ 3449062Sbostic #include <machine/stdarg.h> 3549062Sbostic 3635282Skarels #ifdef KADB 3756517Sbostic #include <machine/kdbparam.h> 3830625Skarels #endif 3930625Skarels 4049062Sbostic #define TOCONS 0x01 4149062Sbostic #define TOTTY 0x02 4249062Sbostic #define TOLOG 0x04 4316724Sralph 4430549Skarels struct tty *constty; /* pointer to console "window" tty */ 4530549Skarels 4649062Sbostic extern cnputc(); /* standard console putc */ 4749953Sbostic int (*v_putc)() = cnputc; /* routine to putc on virtual console */ 4840808Smarc 4951768Smarc void logpri __P((int level)); 5049908Sbostic static void putchar __P((int ch, int flags, struct tty *tp)); 5149908Sbostic static char *ksprintn __P((u_long num, int base, int *len)); 5252392Smarc void kprintf __P((const char *fmt, int flags, struct tty *tp, va_list ap)); 5349062Sbostic 5451768Smarc int consintr = 1; /* Ok to handle console interrupts? */ 5551768Smarc 5631Sbill /* 5751768Smarc * Variable panicstr contains argument to first call to panic; used as flag 5851768Smarc * to indicate that the kernel has already called panic. 5931Sbill */ 6051768Smarc const char *panicstr; 6129946Skarels 6249062Sbostic /* 6349062Sbostic * Panic is called on unresolvable fatal errors. It prints "panic: mesg", 6449062Sbostic * and then reboots. If we are called twice, then we avoid trying to sync 6549062Sbostic * the disks as this often leads to recursive panics. 6649062Sbostic */ 6752392Smarc #ifdef __GNUC__ 6852406Smckusick volatile void boot(int flags); /* boot() does not return */ 6952392Smarc volatile /* panic() does not return */ 7052392Smarc #endif 7149062Sbostic void 7251768Smarc #ifdef __STDC__ 7351768Smarc panic(const char *fmt, ...) 7451768Smarc #else 7552782Sralph panic(fmt, va_alist) 7651768Smarc char *fmt; 7751768Smarc #endif 7849062Sbostic { 7952392Smarc int bootopt; 8051768Smarc va_list ap; 8148426Skarels 8251768Smarc bootopt = RB_AUTOBOOT | RB_DUMP; 8349062Sbostic if (panicstr) 8449062Sbostic bootopt |= RB_NOSYNC; 8549062Sbostic else 8651768Smarc panicstr = fmt; 8751768Smarc 8851768Smarc va_start(ap, fmt); 8952392Smarc printf("panic: %r\n", fmt, ap); 9051768Smarc va_end(ap); 9151768Smarc 9249062Sbostic #ifdef KGDB 9349062Sbostic kgdb_panic(); 9449062Sbostic #endif 9549062Sbostic #ifdef KADB 9652782Sralph if (boothowto & RB_KDB) 9752782Sralph kdbpanic(); 9849062Sbostic #endif 9949062Sbostic boot(bootopt); 10049062Sbostic } 10149062Sbostic 10249062Sbostic /* 10349062Sbostic * Warn that a system table is full. 10449062Sbostic */ 10549062Sbostic void 10649062Sbostic tablefull(tab) 10752406Smckusick const char *tab; 10831Sbill { 109285Sbill 11049062Sbostic log(LOG_ERR, "%s: table is full\n", tab); 111285Sbill } 112285Sbill 1132377Swnj /* 11439560Smarc * Uprintf prints to the controlling terminal for the current process. 11549062Sbostic * It may block if the tty queue is overfull. No message is printed if 11649062Sbostic * the queue does not clear in a reasonable time. 1172377Swnj */ 11849062Sbostic void 11949062Sbostic #ifdef __STDC__ 12049062Sbostic uprintf(const char *fmt, ...) 12149062Sbostic #else 12251767Smarc uprintf(fmt, va_alist) 1232781Swnj char *fmt; 12449062Sbostic #endif 125285Sbill { 12647540Skarels register struct proc *p = curproc; 12749062Sbostic va_list ap; 128285Sbill 12964584Sbostic if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { 13049953Sbostic va_start(ap, fmt); 13149062Sbostic kprintf(fmt, TOTTY, p->p_session->s_ttyp, ap); 13249953Sbostic va_end(ap); 13349953Sbostic } 134285Sbill } 135285Sbill 13644386Smarc tpr_t 13748426Skarels tprintf_open(p) 13848426Skarels register struct proc *p; 13944386Smarc { 14049953Sbostic 14164584Sbostic if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { 14244386Smarc SESSHOLD(p->p_session); 14348426Skarels return ((tpr_t) p->p_session); 14449062Sbostic } 14549062Sbostic return ((tpr_t) NULL); 14644386Smarc } 14744386Smarc 14848426Skarels void 14944386Smarc tprintf_close(sess) 15044386Smarc tpr_t sess; 15144386Smarc { 15249953Sbostic 15344386Smarc if (sess) 15448426Skarels SESSRELE((struct session *) sess); 15544386Smarc } 15644386Smarc 15718364Skarels /* 15844386Smarc * tprintf prints on the controlling terminal associated 15949062Sbostic * with the given session. 16018364Skarels */ 16149062Sbostic void 16249062Sbostic #ifdef __STDC__ 16349062Sbostic tprintf(tpr_t tpr, const char *fmt, ...) 16449062Sbostic #else 16551767Smarc tprintf(tpr, fmt, va_alist) 16648426Skarels tpr_t tpr; 16716724Sralph char *fmt; 16849062Sbostic #endif 16916724Sralph { 17048426Skarels register struct session *sess = (struct session *)tpr; 17148426Skarels struct tty *tp = NULL; 17244386Smarc int flags = TOLOG; 17349062Sbostic va_list ap; 17416724Sralph 17525389Skarels logpri(LOG_INFO); 17648426Skarels if (sess && sess->s_ttyvp && ttycheckoutq(sess->s_ttyp, 0)) { 17744386Smarc flags |= TOTTY; 17848426Skarels tp = sess->s_ttyp; 17948426Skarels } 18049062Sbostic va_start(ap, fmt); 18149062Sbostic kprintf(fmt, flags, tp, ap); 18249062Sbostic va_end(ap); 18325389Skarels logwakeup(); 18416724Sralph } 18516724Sralph 18649062Sbostic /* 18749062Sbostic * Ttyprintf displays a message on a tty; it should be used only by 18849062Sbostic * the tty driver, or anything that knows the underlying tty will not 18949062Sbostic * be revoke(2)'d away. Other callers should use tprintf. 19049062Sbostic */ 19149062Sbostic void 19249062Sbostic #ifdef __STDC__ 19349062Sbostic ttyprintf(struct tty *tp, const char *fmt, ...) 19449062Sbostic #else 19551767Smarc ttyprintf(tp, fmt, va_alist) 19649062Sbostic struct tty *tp; 19749062Sbostic char *fmt; 19849062Sbostic #endif 19949062Sbostic { 20049062Sbostic va_list ap; 20149062Sbostic 20249062Sbostic va_start(ap, fmt); 20349062Sbostic kprintf(fmt, TOTTY, tp, ap); 20449062Sbostic va_end(ap); 20549062Sbostic } 20649062Sbostic 20748426Skarels extern int log_open; 20844386Smarc 20916724Sralph /* 21049062Sbostic * Log writes to the log buffer, and guarantees not to sleep (so can be 21149062Sbostic * called by interrupt routines). If there is no process reading the 21249062Sbostic * log yet, it writes to the console also. 21316724Sralph */ 21449062Sbostic void 21549062Sbostic #ifdef __STDC__ 21649062Sbostic log(int level, const char *fmt, ...) 21749062Sbostic #else 21851767Smarc log(level, fmt, va_alist) 21949062Sbostic int level; 22016724Sralph char *fmt; 22149062Sbostic #endif 22216724Sralph { 22350268Sbostic register int s; 22449062Sbostic va_list ap; 22516724Sralph 22650268Sbostic s = splhigh(); 22725389Skarels logpri(level); 22849062Sbostic va_start(ap, fmt); 22949062Sbostic kprintf(fmt, TOLOG, NULL, ap); 23016724Sralph splx(s); 23149953Sbostic va_end(ap); 23249953Sbostic if (!log_open) { 23349953Sbostic va_start(ap, fmt); 23449062Sbostic kprintf(fmt, TOCONS, NULL, ap); 23549953Sbostic va_end(ap); 23649953Sbostic } 23716724Sralph logwakeup(); 23816724Sralph } 23916724Sralph 24051768Smarc void 24125389Skarels logpri(level) 24225389Skarels int level; 24325389Skarels { 24449908Sbostic register int ch; 24549908Sbostic register char *p; 24625389Skarels 24749062Sbostic putchar('<', TOLOG, NULL); 24849908Sbostic for (p = ksprintn((u_long)level, 10, NULL); ch = *p--;) 24949908Sbostic putchar(ch, TOLOG, NULL); 25049062Sbostic putchar('>', TOLOG, NULL); 25125389Skarels } 25225389Skarels 25349062Sbostic void 25449062Sbostic #ifdef __STDC__ 25549062Sbostic addlog(const char *fmt, ...) 25649062Sbostic #else 25751767Smarc addlog(fmt, va_alist) 25833479Skarels char *fmt; 25949062Sbostic #endif 26033479Skarels { 26150268Sbostic register int s; 26249062Sbostic va_list ap; 26333479Skarels 26450268Sbostic s = splhigh(); 26549062Sbostic va_start(ap, fmt); 26649062Sbostic kprintf(fmt, TOLOG, NULL, ap); 26733479Skarels splx(s); 26849953Sbostic va_end(ap); 26949953Sbostic if (!log_open) { 27049953Sbostic va_start(ap, fmt); 27149062Sbostic kprintf(fmt, TOCONS, NULL, ap); 27249953Sbostic va_end(ap); 27349953Sbostic } 27433479Skarels logwakeup(); 27533479Skarels } 27633479Skarels 27749062Sbostic void 27849062Sbostic #ifdef __STDC__ 27949062Sbostic printf(const char *fmt, ...) 28049062Sbostic #else 28151767Smarc printf(fmt, va_alist) 28249062Sbostic char *fmt; 28329946Skarels #endif 28449062Sbostic { 28549953Sbostic va_list ap; 28649062Sbostic register int savintr; 2872678Swnj 28849062Sbostic savintr = consintr; /* disable interrupts */ 28949062Sbostic consintr = 0; 29049062Sbostic va_start(ap, fmt); 29149062Sbostic kprintf(fmt, TOCONS | TOLOG, NULL, ap); 29249062Sbostic va_end(ap); 29349062Sbostic if (!panicstr) 29449062Sbostic logwakeup(); 29549062Sbostic consintr = savintr; /* reenable interrupts */ 29631Sbill } 29731Sbill 2982781Swnj /* 29949062Sbostic * Scaled down version of printf(3). 30049062Sbostic * 30149062Sbostic * Two additional formats: 30249062Sbostic * 30349062Sbostic * The format %b is supported to decode error registers. 30449062Sbostic * Its usage is: 30549062Sbostic * 30652392Smarc * printf("reg=%b\n", regval, "<base><arg>*"); 30749062Sbostic * 30849062Sbostic * where <base> is the output base expressed as a control character, e.g. 30949062Sbostic * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, 31049062Sbostic * the first of which gives the bit number to be inspected (origin 1), and 31149062Sbostic * the next characters (up to a control character, i.e. a character <= 32), 31249062Sbostic * give the name of the register. Thus: 31349062Sbostic * 31451768Smarc * kprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); 31549062Sbostic * 31649062Sbostic * would produce output: 31749062Sbostic * 31849062Sbostic * reg=3<BITTWO,BITONE> 31949062Sbostic * 32051768Smarc * The format %r passes an additional format string and argument list 32151768Smarc * recursively. Its usage is: 32249062Sbostic * 32351768Smarc * fn(char *fmt, ...) 32449953Sbostic * { 32549953Sbostic * va_list ap; 32649953Sbostic * va_start(ap, fmt); 32752392Smarc * printf("prefix: %r: suffix\n", fmt, ap); 32849953Sbostic * va_end(ap); 32951768Smarc * } 33049908Sbostic * 33149908Sbostic * Space or zero padding and a field width are supported for the numeric 33249908Sbostic * formats only. 3332781Swnj */ 33449909Sbostic void 33552392Smarc kprintf(fmt, flags, tp, ap) 33649094Sbostic register const char *fmt; 33749062Sbostic int flags; 33849094Sbostic struct tty *tp; 33949062Sbostic va_list ap; 34031Sbill { 34152877Skarels register char *p, *q; 34249062Sbostic register int ch, n; 34349908Sbostic u_long ul; 34449908Sbostic int base, lflag, tmp, width; 34549908Sbostic char padc; 34631Sbill 34749062Sbostic for (;;) { 34849908Sbostic padc = ' '; 34949908Sbostic width = 0; 35049919Skarels while ((ch = *(u_char *)fmt++) != '%') { 35149062Sbostic if (ch == '\0') 35249062Sbostic return; 35349094Sbostic putchar(ch, flags, tp); 35429946Skarels } 35549062Sbostic lflag = 0; 35649919Skarels reswitch: switch (ch = *(u_char *)fmt++) { 35749908Sbostic case '0': 35849908Sbostic padc = '0'; 35949908Sbostic goto reswitch; 36049908Sbostic case '1': case '2': case '3': case '4': 36149908Sbostic case '5': case '6': case '7': case '8': case '9': 36249908Sbostic for (width = 0;; ++fmt) { 36349908Sbostic width = width * 10 + ch - '0'; 36449908Sbostic ch = *fmt; 36549908Sbostic if (ch < '0' || ch > '9') 36649908Sbostic break; 36749908Sbostic } 36849908Sbostic goto reswitch; 36949062Sbostic case 'l': 37049062Sbostic lflag = 1; 37149062Sbostic goto reswitch; 37249062Sbostic case 'b': 37349062Sbostic ul = va_arg(ap, int); 37449062Sbostic p = va_arg(ap, char *); 37552877Skarels for (q = ksprintn(ul, *p++, NULL); ch = *q--;) 37649908Sbostic putchar(ch, flags, tp); 37731Sbill 37849062Sbostic if (!ul) 37949062Sbostic break; 3802377Swnj 38149908Sbostic for (tmp = 0; n = *p++;) { 38249062Sbostic if (ul & (1 << (n - 1))) { 38349908Sbostic putchar(tmp ? ',' : '<', flags, tp); 38449062Sbostic for (; (n = *p) > ' '; ++p) 38549094Sbostic putchar(n, flags, tp); 38649908Sbostic tmp = 1; 38749062Sbostic } else 38852911Storek for (; *p > ' '; ++p) 38952911Storek continue; 39049062Sbostic } 39149908Sbostic if (tmp) 39249094Sbostic putchar('>', flags, tp); 39349062Sbostic break; 39449062Sbostic case 'c': 39549094Sbostic putchar(va_arg(ap, int), flags, tp); 39649062Sbostic break; 39749062Sbostic case 'r': 39849062Sbostic p = va_arg(ap, char *); 39949094Sbostic kprintf(p, flags, tp, va_arg(ap, va_list)); 40049062Sbostic break; 40149062Sbostic case 's': 40249062Sbostic p = va_arg(ap, char *); 40349062Sbostic while (ch = *p++) 40449094Sbostic putchar(ch, flags, tp); 40549062Sbostic break; 40649062Sbostic case 'd': 40749908Sbostic ul = lflag ? va_arg(ap, long) : va_arg(ap, int); 40849062Sbostic if ((long)ul < 0) { 40949094Sbostic putchar('-', flags, tp); 41049062Sbostic ul = -(long)ul; 41149062Sbostic } 41249908Sbostic base = 10; 41349908Sbostic goto number; 41449062Sbostic case 'o': 41549908Sbostic ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); 41649908Sbostic base = 8; 41749953Sbostic goto number; 41849062Sbostic case 'u': 41949908Sbostic ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); 42049908Sbostic base = 10; 42149908Sbostic goto number; 42249062Sbostic case 'x': 42349908Sbostic ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); 42449908Sbostic base = 16; 42549908Sbostic number: p = ksprintn(ul, base, &tmp); 42649908Sbostic if (width && (width -= tmp) > 0) 42749908Sbostic while (width--) 42849908Sbostic putchar(padc, flags, tp); 42949908Sbostic while (ch = *p--) 43049908Sbostic putchar(ch, flags, tp); 43149062Sbostic break; 43249062Sbostic default: 43349094Sbostic putchar('%', flags, tp); 43449062Sbostic if (lflag) 43549094Sbostic putchar('l', flags, tp); 43649908Sbostic /* FALLTHROUGH */ 43749908Sbostic case '%': 43849094Sbostic putchar(ch, flags, tp); 43949062Sbostic } 44030625Skarels } 44131Sbill } 44231Sbill 4432941Swnj /* 44449062Sbostic * Print a character on console or users terminal. If destination is 44549062Sbostic * the console then the last MSGBUFS characters are saved in msgbuf for 44649062Sbostic * inspection later. 447285Sbill */ 44849062Sbostic static void 44949094Sbostic putchar(c, flags, tp) 4502377Swnj register int c; 45149062Sbostic int flags; 45249094Sbostic struct tty *tp; 453285Sbill { 45433479Skarels extern int msgbufmapped; 45550268Sbostic register struct msgbuf *mbp; 456285Sbill 45730549Skarels if (panicstr) 45849062Sbostic constty = NULL; 45949094Sbostic if ((flags & TOCONS) && tp == NULL && constty) { 46049094Sbostic tp = constty; 46130549Skarels flags |= TOTTY; 46230549Skarels } 46349094Sbostic if ((flags & TOTTY) && tp && tputchar(c, tp) < 0 && 46449094Sbostic (flags & TOCONS) && tp == constty) 46549062Sbostic constty = NULL; 46649062Sbostic if ((flags & TOLOG) && 46749062Sbostic c != '\0' && c != '\r' && c != 0177 && msgbufmapped) { 46850268Sbostic mbp = msgbufp; 46945733Smckusick if (mbp->msg_magic != MSG_MAGIC) { 47049953Sbostic bzero((caddr_t)mbp, sizeof(*mbp)); 47145733Smckusick mbp->msg_magic = MSG_MAGIC; 4722172Swnj } 47345733Smckusick mbp->msg_bufc[mbp->msg_bufx++] = c; 47445733Smckusick if (mbp->msg_bufx < 0 || mbp->msg_bufx >= MSG_BSIZE) 47545733Smckusick mbp->msg_bufx = 0; 476285Sbill } 47749062Sbostic if ((flags & TOCONS) && constty == NULL && c != '\0') 47830549Skarels (*v_putc)(c); 479285Sbill } 48049908Sbostic 48149908Sbostic /* 48249908Sbostic * Scaled down version of sprintf(3). 48349908Sbostic */ 48449908Sbostic #ifdef __STDC__ 48549919Skarels sprintf(char *buf, const char *cfmt, ...) 48649908Sbostic #else 48751767Smarc sprintf(buf, cfmt, va_alist) 48849919Skarels char *buf, *cfmt; 48949908Sbostic #endif 49049908Sbostic { 49149919Skarels register const char *fmt = cfmt; 49249908Sbostic register char *p, *bp; 49349908Sbostic register int ch, base; 49449908Sbostic u_long ul; 49549908Sbostic int lflag; 49649908Sbostic va_list ap; 49749908Sbostic 49849919Skarels va_start(ap, cfmt); 49949919Skarels for (bp = buf; ; ) { 50049919Skarels while ((ch = *(u_char *)fmt++) != '%') 50149908Sbostic if ((*bp++ = ch) == '\0') 50249919Skarels return ((bp - buf) - 1); 50349919Skarels 50449908Sbostic lflag = 0; 50549919Skarels reswitch: switch (ch = *(u_char *)fmt++) { 50649908Sbostic case 'l': 50749908Sbostic lflag = 1; 50849908Sbostic goto reswitch; 50949908Sbostic case 'c': 51049908Sbostic *bp++ = va_arg(ap, int); 51149908Sbostic break; 51249908Sbostic case 's': 51349908Sbostic p = va_arg(ap, char *); 51449919Skarels while (*bp++ = *p++) 51552392Smarc continue; 51649908Sbostic --bp; 51749908Sbostic break; 51849908Sbostic case 'd': 51949908Sbostic ul = lflag ? va_arg(ap, long) : va_arg(ap, int); 52049908Sbostic if ((long)ul < 0) { 52149908Sbostic *bp++ = '-'; 52249908Sbostic ul = -(long)ul; 52349908Sbostic } 52449908Sbostic base = 10; 52549908Sbostic goto number; 52649908Sbostic break; 52749908Sbostic case 'o': 52849908Sbostic ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); 52949908Sbostic base = 8; 53049908Sbostic goto number; 53149908Sbostic break; 53249908Sbostic case 'u': 53349908Sbostic ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); 53449908Sbostic base = 10; 53549908Sbostic goto number; 53649908Sbostic break; 53749908Sbostic case 'x': 53849908Sbostic ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int); 53949908Sbostic base = 16; 54049908Sbostic number: for (p = ksprintn(ul, base, NULL); ch = *p--;) 54149908Sbostic *bp++ = ch; 54249908Sbostic break; 54349908Sbostic default: 54449908Sbostic *bp++ = '%'; 54549908Sbostic if (lflag) 54649908Sbostic *bp++ = 'l'; 54749908Sbostic /* FALLTHROUGH */ 54849908Sbostic case '%': 54949908Sbostic *bp++ = ch; 55049908Sbostic } 55149908Sbostic } 55249908Sbostic va_end(ap); 55349908Sbostic } 55449908Sbostic 55549908Sbostic /* 55649908Sbostic * Put a number (base <= 16) in a buffer in reverse order; return an 55749908Sbostic * optional length and a pointer to the NULL terminated (preceded?) 55849908Sbostic * buffer. 55949908Sbostic */ 56049908Sbostic static char * 56149908Sbostic ksprintn(ul, base, lenp) 56249908Sbostic register u_long ul; 56349908Sbostic register int base, *lenp; 56449908Sbostic { /* A long in base 8, plus NULL. */ 56549908Sbostic static char buf[sizeof(long) * NBBY / 3 + 2]; 56649908Sbostic register char *p; 56749908Sbostic 56849908Sbostic p = buf; 56949908Sbostic do { 57049908Sbostic *++p = "0123456789abcdef"[ul % base]; 57149908Sbostic } while (ul /= base); 57249908Sbostic if (lenp) 57349908Sbostic *lenp = p - buf; 57449919Skarels return (p); 57549908Sbostic } 576