130855Sbostic /* 239109Sbostic * Copyright (c) 1989 The Regents of the University of California. 339109Sbostic * All rights reserved. 439109Sbostic * 539109Sbostic * This code is derived from software contributed to Berkeley by 639109Sbostic * Hans Huebner. 739109Sbostic * 842689Sbostic * %sccs.include.redist.c% 930855Sbostic */ 1030855Sbostic 1112672Ssam #ifndef lint 1239109Sbostic char copyright[] = 1339109Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 1439109Sbostic All rights reserved.\n"; 1539109Sbostic #endif /* not lint */ 1630855Sbostic 1739109Sbostic #ifndef lint 18*48955Sbostic static char sccsid[] = "@(#)nm.c 5.8 (Berkeley) 05/02/91"; 1939109Sbostic #endif /* not lint */ 2030855Sbostic 21619Sbill #include <sys/types.h> 22653Sbill #include <a.out.h> 23619Sbill #include <stab.h> 2439109Sbostic #include <ar.h> 2530855Sbostic #include <ranlib.h> 2639109Sbostic #include <unistd.h> 2739109Sbostic #include <errno.h> 2839109Sbostic #include <ctype.h> 2939109Sbostic #include <stdio.h> 3042452Sbostic #include <stdlib.h> 3142034Sbostic #include <string.h> 32619Sbill 3340272Sbostic int ignore_bad_archive_entries = 1; 3440272Sbostic int print_only_external_symbols; 3540272Sbostic int print_only_undefined_symbols; 3640272Sbostic int print_all_symbols; 3740272Sbostic int print_file_each_line; 3839109Sbostic int fcount; 3930855Sbostic 40*48955Sbostic int rev; 41*48955Sbostic int fname(), rname(), value(); 42*48955Sbostic int (*sfunc)() = fname; 43*48955Sbostic 4439109Sbostic /* some macros for symbol type (nlist.n_type) handling */ 4539109Sbostic #define IS_DEBUGGER_SYMBOL(x) ((x) & N_STAB) 4639109Sbostic #define IS_EXTERNAL(x) ((x) & N_EXT) 4739109Sbostic #define SYMBOL_TYPE(x) ((x) & (N_TYPE | N_STAB)) 4830855Sbostic 49*48955Sbostic void *emalloc(); 50*48955Sbostic 5139109Sbostic /* 5239109Sbostic * main() 5339109Sbostic * parse command line, execute process_file() for each file 5439109Sbostic * specified on the command line. 5539109Sbostic */ 56619Sbill main(argc, argv) 5739109Sbostic int argc; 5839109Sbostic char **argv; 59619Sbill { 6039109Sbostic extern int optind; 6139109Sbostic int ch, errors; 62619Sbill 6339109Sbostic while ((ch = getopt(argc, argv, "agnopruw")) != EOF) { 6439109Sbostic switch (ch) { 6530855Sbostic case 'a': 6639109Sbostic print_all_symbols = 1; 6730855Sbostic break; 6830855Sbostic case 'g': 6939109Sbostic print_only_external_symbols = 1; 7030855Sbostic break; 71619Sbill case 'n': 72*48955Sbostic sfunc = value; 7330855Sbostic break; 7430855Sbostic case 'o': 7539109Sbostic print_file_each_line = 1; 7630855Sbostic break; 7730855Sbostic case 'p': 78*48955Sbostic sfunc = NULL; 7930855Sbostic break; 80619Sbill case 'r': 81*48955Sbostic rev = 1; 8230855Sbostic break; 8330855Sbostic case 'u': 8439109Sbostic print_only_undefined_symbols = 1; 8530855Sbostic break; 8639109Sbostic case 'w': 8739109Sbostic ignore_bad_archive_entries = 0; 8839109Sbostic break; 8930855Sbostic case '?': 90619Sbill default: 9139109Sbostic usage(); 92619Sbill } 9339109Sbostic } 9439109Sbostic fcount = argc - optind; 9530855Sbostic argv += optind; 9639109Sbostic 97*48955Sbostic if (rev && sfunc == fname) 98*48955Sbostic sfunc = rname; 99*48955Sbostic 10039109Sbostic if (!fcount) 10139109Sbostic errors = process_file("a.out"); 10239109Sbostic else { 10339109Sbostic errors = 0; 10439109Sbostic do { 10539109Sbostic errors |= process_file(*argv); 10639109Sbostic } while (*++argv); 107619Sbill } 10839109Sbostic exit(errors); 109619Sbill } 110619Sbill 11139109Sbostic /* 11239109Sbostic * process_file() 11339109Sbostic * show symbols in the file given as an argument. Accepts archive and 11439109Sbostic * object files as input. 11539109Sbostic */ 11639109Sbostic process_file(fname) 11739109Sbostic char *fname; 118619Sbill { 11939109Sbostic struct exec exec_head; 12039109Sbostic FILE *fp; 12139109Sbostic int retval; 12239109Sbostic char magic[SARMAG]; 12339109Sbostic 12439109Sbostic if (!(fp = fopen(fname, "r"))) { 12539109Sbostic (void)fprintf(stderr, "nm: cannot read %s.\n", fname); 12639109Sbostic return(1); 12739109Sbostic } 128619Sbill 12939109Sbostic if (fcount > 1) 13039109Sbostic (void)printf("\n%s:\n", fname); 13139109Sbostic 13230855Sbostic /* 13339109Sbostic * first check whether this is an object file - read a object 13439109Sbostic * header, and skip back to the beginning 13530855Sbostic */ 13642452Sbostic if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) { 13739109Sbostic (void)fprintf(stderr, "nm: %s: bad format.\n", fname); 13839109Sbostic (void)fclose(fp); 13939109Sbostic return(1); 140619Sbill } 14139109Sbostic rewind(fp); 14239109Sbostic 14339109Sbostic /* this could be an archive */ 14439109Sbostic if (N_BADMAG(exec_head)) { 14542452Sbostic if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 || 14639109Sbostic strncmp(magic, ARMAG, SARMAG)) { 14739109Sbostic (void)fprintf(stderr, 14839109Sbostic "nm: %s: not object file or archive.\n", fname); 14939109Sbostic (void)fclose(fp); 15039109Sbostic return(1); 15130855Sbostic } 15239109Sbostic retval = show_archive(fname, fp); 15339109Sbostic } else 15439109Sbostic retval = show_objfile(fname, fp); 15539109Sbostic (void)fclose(fp); 15639109Sbostic return(retval); 15739109Sbostic } 15839109Sbostic 15939109Sbostic /* 16039109Sbostic * show_archive() 16139109Sbostic * show symbols in the given archive file 16239109Sbostic */ 16339109Sbostic show_archive(fname, fp) 16439109Sbostic char *fname; 16539109Sbostic FILE *fp; 16639109Sbostic { 16739109Sbostic struct ar_hdr ar_head; 16839109Sbostic struct exec exec_head; 16942452Sbostic int i, rval; 17042452Sbostic long last_ar_off; 171*48955Sbostic char *p, *name; 17239109Sbostic 17342452Sbostic name = emalloc(sizeof(ar_head.ar_name) + strlen(fname) + 3); 17439109Sbostic 17539109Sbostic rval = 0; 17639109Sbostic 17739109Sbostic /* while there are more entries in the archive */ 17842452Sbostic while (fread((char *)&ar_head, sizeof(ar_head), (size_t)1, fp) == 1) { 17939109Sbostic /* bad archive entry - stop processing this archive */ 18039109Sbostic if (strncmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) { 18139109Sbostic (void)fprintf(stderr, 18239109Sbostic "nm: %s: bad format archive header", fname); 18339109Sbostic (void)free(name); 18439109Sbostic return(1); 18539109Sbostic } 18639109Sbostic 18746550Sbostic /* remember start position of current archive object */ 18846550Sbostic last_ar_off = ftell(fp); 18946550Sbostic 19046550Sbostic /* skip ranlib entries */ 19146550Sbostic if (!strncmp(ar_head.ar_name, RANLIBMAG, sizeof(RANLIBMAG) - 1)) 19246550Sbostic goto skip; 19346550Sbostic 19430855Sbostic /* 19539143Sbostic * construct a name of the form "archive.a:obj.o:" for the 19639109Sbostic * current archive entry if the object name is to be printed 19739109Sbostic * on each output line 19830855Sbostic */ 19942452Sbostic p = name; 20042452Sbostic if (print_file_each_line) 20142452Sbostic p += sprintf(p, "%s:", fname); 20239109Sbostic for (i = 0; i < sizeof(ar_head.ar_name); ++i) 20339109Sbostic if (ar_head.ar_name[i] && ar_head.ar_name[i] != ' ') 20439109Sbostic *p++ = ar_head.ar_name[i]; 20539109Sbostic *p++ = '\0'; 20630855Sbostic 20739109Sbostic /* get and check current object's header */ 20842452Sbostic if (fread((char *)&exec_head, sizeof(exec_head), 20942452Sbostic (size_t)1, fp) != 1) { 21039109Sbostic (void)fprintf(stderr, "nm: %s: premature EOF.\n", name); 21139109Sbostic (void)free(name); 21239109Sbostic return(1); 213619Sbill } 21446550Sbostic 21546550Sbostic if (N_BADMAG(exec_head)) { 21646550Sbostic if (!ignore_bad_archive_entries) { 21746550Sbostic (void)fprintf(stderr, 21846550Sbostic "nm: %s: bad format.\n", name); 21946550Sbostic rval = 1; 22039109Sbostic } 22146550Sbostic } else { 22246550Sbostic (void)fseek(fp, (long)-sizeof(exec_head), 22346550Sbostic SEEK_CUR); 22446550Sbostic if (!print_file_each_line) 22546550Sbostic (void)printf("\n%s:\n", name); 22646550Sbostic rval |= show_objfile(name, fp); 22746550Sbostic } 22830855Sbostic 22939109Sbostic /* 23042452Sbostic * skip to next archive object - it starts at the next 23142452Sbostic * even byte boundary 23239109Sbostic */ 23342452Sbostic #define even(x) (((x) + 1) & ~1) 23446550Sbostic skip: if (fseek(fp, last_ar_off + even(atol(ar_head.ar_size)), 23539109Sbostic SEEK_SET)) { 23639109Sbostic (void)fprintf(stderr, 23739109Sbostic "nm: %s: %s\n", fname, strerror(errno)); 23839109Sbostic (void)free(name); 23939109Sbostic return(1); 24030855Sbostic } 24139109Sbostic } 24239109Sbostic (void)free(name); 24339109Sbostic return(rval); 24439109Sbostic } 24530855Sbostic 24639109Sbostic /* 24739109Sbostic * show_objfile() 24839109Sbostic * show symbols from the object file pointed to by fp. The current 24939109Sbostic * file pointer for fp is expected to be at the beginning of an a.out 25039109Sbostic * header. 25139109Sbostic */ 25239109Sbostic show_objfile(objname, fp) 25339109Sbostic char *objname; 25439109Sbostic FILE *fp; 25539109Sbostic { 25642452Sbostic register struct nlist *names, *np; 25739109Sbostic register int i, nnames, nrawnames; 25839109Sbostic struct exec head; 25939109Sbostic long stabsize; 260*48955Sbostic char *stab; 26139109Sbostic 26239109Sbostic /* read a.out header */ 26342452Sbostic if (fread((char *)&head, sizeof(head), (size_t)1, fp) != 1) { 26439109Sbostic (void)fprintf(stderr, 26539109Sbostic "nm: %s: cannot read header.\n", objname); 26639109Sbostic return(1); 26739109Sbostic } 26839109Sbostic 26939109Sbostic /* 27039109Sbostic * skip back to the header - the N_-macros return values relative 27139109Sbostic * to the beginning of the a.out header 27239109Sbostic */ 27339109Sbostic if (fseek(fp, (long)-sizeof(head), SEEK_CUR)) { 27439109Sbostic (void)fprintf(stderr, 27539109Sbostic "nm: %s: %s\n", objname, strerror(errno)); 27639109Sbostic return(1); 27739109Sbostic } 27839109Sbostic 27939109Sbostic /* stop if this is no valid object file */ 28039109Sbostic if (N_BADMAG(head)) { 28139109Sbostic (void)fprintf(stderr, 28239109Sbostic "nm: %s: bad format.\n", objname); 28339109Sbostic return(1); 28439109Sbostic } 28539109Sbostic 28639109Sbostic /* stop if the object file contains no symbol table */ 28739109Sbostic if (!head.a_syms) { 28839109Sbostic (void)fprintf(stderr, 28939109Sbostic "nm: %s: no name list.\n", objname); 29039109Sbostic return(1); 29139109Sbostic } 29239109Sbostic 29339109Sbostic if (fseek(fp, (long)N_SYMOFF(head), SEEK_CUR)) { 29439109Sbostic (void)fprintf(stderr, 29539109Sbostic "nm: %s: %s\n", objname, strerror(errno)); 29639109Sbostic return(1); 29739109Sbostic } 29839109Sbostic 29939109Sbostic /* get memory for the symbol table */ 300*48955Sbostic names = emalloc((size_t)head.a_syms); 30139109Sbostic nrawnames = head.a_syms / sizeof(*names); 30242452Sbostic if (fread((char *)names, (size_t)head.a_syms, (size_t)1, fp) != 1) { 30339109Sbostic (void)fprintf(stderr, 30439109Sbostic "nm: %s: cannot read symbol table.\n", objname); 30539109Sbostic (void)free((char *)names); 30639109Sbostic return(1); 30739109Sbostic } 30839109Sbostic 30939109Sbostic /* 31039109Sbostic * Following the symbol table comes the string table. The first 31139109Sbostic * 4-byte-integer gives the total size of the string table 31239109Sbostic * _including_ the size specification itself. 31339109Sbostic */ 31442452Sbostic if (fread((char *)&stabsize, sizeof(stabsize), (size_t)1, fp) != 1) { 31539109Sbostic (void)fprintf(stderr, 31639109Sbostic "nm: %s: cannot read stab size.\n", objname); 31739109Sbostic (void)free((char *)names); 31839109Sbostic return(1); 31939109Sbostic } 32042452Sbostic stab = emalloc((size_t)stabsize); 32139109Sbostic 32239109Sbostic /* 32339109Sbostic * read the string table offset by 4 - all indices into the string 32439109Sbostic * table include the size specification. 32539109Sbostic */ 32639143Sbostic stabsize -= 4; /* we already have the size */ 32742452Sbostic if (fread(stab + 4, (size_t)stabsize, (size_t)1, fp) != 1) { 32839109Sbostic (void)fprintf(stderr, 32939109Sbostic "nm: %s: stab truncated..\n", objname); 33039109Sbostic (void)free((char *)names); 33139109Sbostic (void)free(stab); 33239109Sbostic return(1); 33339109Sbostic } 33439109Sbostic 33539109Sbostic /* 33639109Sbostic * fix up the symbol table and filter out unwanted entries 33739109Sbostic * 33839109Sbostic * common symbols are characterized by a n_type of N_UNDF and a 33939109Sbostic * non-zero n_value -- change n_type to N_COMM for all such 34039109Sbostic * symbols to make life easier later. 34139109Sbostic * 34239109Sbostic * filter out all entries which we don't want to print anyway 34339109Sbostic */ 34442452Sbostic for (np = names, i = nnames = 0; i < nrawnames; np++, i++) { 34542452Sbostic if (SYMBOL_TYPE(np->n_type) == N_UNDF && np->n_value) 34642452Sbostic np->n_type = N_COMM | (np->n_type & N_EXT); 34742452Sbostic if (!print_all_symbols && IS_DEBUGGER_SYMBOL(np->n_type)) 34830855Sbostic continue; 34942452Sbostic if (print_only_external_symbols && !IS_EXTERNAL(np->n_type)) 350619Sbill continue; 35139109Sbostic if (print_only_undefined_symbols && 35242452Sbostic SYMBOL_TYPE(np->n_type) != N_UNDF) 35339109Sbostic continue; 35430855Sbostic 35539109Sbostic /* 35639109Sbostic * make n_un.n_name a character pointer by adding the string 35739109Sbostic * table's base to n_un.n_strx 35839109Sbostic * 35942452Sbostic * don't mess with zero offsets 36039109Sbostic */ 36142452Sbostic if (np->n_un.n_strx) 36242452Sbostic np->n_un.n_name = stab + np->n_un.n_strx; 36339109Sbostic else 36442452Sbostic np->n_un.n_name = ""; 36542452Sbostic names[nnames++] = *np; 36639109Sbostic } 36730855Sbostic 36839109Sbostic /* sort the symbol table if applicable */ 369*48955Sbostic if (sfunc) 370*48955Sbostic qsort((char *)names, (size_t)nnames, sizeof(*names), sfunc); 37130855Sbostic 37239109Sbostic /* print out symbols */ 37342452Sbostic for (np = names, i = 0; i < nnames; np++, i++) 37442452Sbostic print_symbol(objname, np); 37530855Sbostic 37639109Sbostic (void)free((char *)names); 37739109Sbostic (void)free(stab); 37839109Sbostic return(0); 379619Sbill } 380619Sbill 38139109Sbostic /* 38239109Sbostic * print_symbol() 38339109Sbostic * show one symbol 38439109Sbostic */ 38539109Sbostic print_symbol(objname, sym) 38639109Sbostic char *objname; 38742452Sbostic register struct nlist *sym; 388619Sbill { 38939109Sbostic char *typestring(), typeletter(); 390619Sbill 39139109Sbostic if (print_file_each_line) 39242452Sbostic (void)printf("%s:", objname); 39339109Sbostic 39439109Sbostic /* 39539109Sbostic * handle undefined-only format seperately (no space is 39639109Sbostic * left for symbol values, no type field is printed) 39739109Sbostic */ 39839109Sbostic if (print_only_undefined_symbols) { 39942452Sbostic (void)puts(sym->n_un.n_name); 40039109Sbostic return; 401619Sbill } 40239109Sbostic 40339109Sbostic /* print symbol's value */ 40439109Sbostic if (SYMBOL_TYPE(sym->n_type) == N_UNDF) 40539109Sbostic (void)printf(" "); 40639109Sbostic else 40739109Sbostic (void)printf("%08lx", sym->n_value); 40839109Sbostic 40939109Sbostic /* print type information */ 41039109Sbostic if (IS_DEBUGGER_SYMBOL(sym->n_type)) 41139109Sbostic (void)printf(" - %02x %04x %5s ", sym->n_other, 41239109Sbostic sym->n_desc&0xffff, typestring(sym->n_type)); 41339109Sbostic else 41439109Sbostic (void)printf(" %c ", typeletter(sym->n_type)); 41539109Sbostic 41639109Sbostic /* print the symbol's name */ 41742452Sbostic (void)puts(sym->n_un.n_name); 418619Sbill } 419619Sbill 42039109Sbostic /* 42139109Sbostic * typestring() 42239109Sbostic * return the a description string for an STAB entry 42339109Sbostic */ 42439109Sbostic char * 42539109Sbostic typestring(type) 42639109Sbostic register u_char type; 427619Sbill { 42839109Sbostic switch(type) { 42939109Sbostic case N_BCOMM: 43039109Sbostic return("BCOMM"); 43139109Sbostic case N_ECOML: 43239109Sbostic return("ECOML"); 43339109Sbostic case N_ECOMM: 43439109Sbostic return("ECOMM"); 43539109Sbostic case N_ENTRY: 43639109Sbostic return("ENTRY"); 43739109Sbostic case N_FNAME: 43839109Sbostic return("FNAME"); 43939109Sbostic case N_FUN: 44039109Sbostic return("FUN"); 44139109Sbostic case N_GSYM: 44239109Sbostic return("GSYM"); 44339109Sbostic case N_LBRAC: 44439109Sbostic return("LBRAC"); 44539109Sbostic case N_LCSYM: 44639109Sbostic return("LCSYM"); 44739109Sbostic case N_LENG: 44839109Sbostic return("LENG"); 44939109Sbostic case N_LSYM: 45039109Sbostic return("LSYM"); 45139109Sbostic case N_PC: 45239109Sbostic return("PC"); 45339109Sbostic case N_PSYM: 45439109Sbostic return("PSYM"); 45539109Sbostic case N_RBRAC: 45639109Sbostic return("RBRAC"); 45739109Sbostic case N_RSYM: 45839109Sbostic return("RSYM"); 45939109Sbostic case N_SLINE: 46039109Sbostic return("SLINE"); 46139109Sbostic case N_SO: 46239109Sbostic return("SO"); 46339109Sbostic case N_SOL: 46439109Sbostic return("SOL"); 46539109Sbostic case N_SSYM: 46639109Sbostic return("SSYM"); 46739109Sbostic case N_STSYM: 46839109Sbostic return("STSYM"); 469619Sbill } 47039109Sbostic return("???"); 471619Sbill } 472619Sbill 47339109Sbostic /* 47439109Sbostic * typeletter() 47539109Sbostic * return a description letter for the given basic type code of an 47639109Sbostic * symbol table entry. The return value will be upper case for 47739109Sbostic * external, lower case for internal symbols. 47839109Sbostic */ 47939109Sbostic char 48039109Sbostic typeletter(type) 48139109Sbostic u_char type; 482619Sbill { 48339109Sbostic switch(SYMBOL_TYPE(type)) { 48439109Sbostic case N_ABS: 48539109Sbostic return(IS_EXTERNAL(type) ? 'A' : 'a'); 48639109Sbostic case N_BSS: 48739109Sbostic return(IS_EXTERNAL(type) ? 'B' : 'b'); 48839109Sbostic case N_COMM: 48939109Sbostic return(IS_EXTERNAL(type) ? 'C' : 'c'); 49039109Sbostic case N_DATA: 49139109Sbostic return(IS_EXTERNAL(type) ? 'D' : 'd'); 49239109Sbostic case N_FN: 49339109Sbostic return(IS_EXTERNAL(type) ? 'F' : 'f'); 49439109Sbostic case N_TEXT: 49539109Sbostic return(IS_EXTERNAL(type) ? 'T' : 't'); 49639109Sbostic case N_UNDF: 49739109Sbostic return(IS_EXTERNAL(type) ? 'U' : 'u'); 49839109Sbostic } 49939109Sbostic return('?'); 50039109Sbostic } 501619Sbill 502*48955Sbostic fname(a0, b0) 50342452Sbostic void *a0, *b0; 50439109Sbostic { 50542452Sbostic struct nlist *a = a0, *b = b0; 50642452Sbostic 507*48955Sbostic return(strcmp(a->n_un.n_name, b->n_un.n_name)); 508619Sbill } 509619Sbill 510*48955Sbostic rname(a0, b0) 51142452Sbostic void *a0, *b0; 51239109Sbostic { 513*48955Sbostic struct nlist *a = a0, *b = b0; 514*48955Sbostic 515*48955Sbostic return(strcmp(b->n_un.n_name, a->n_un.n_name)); 516*48955Sbostic } 517*48955Sbostic 518*48955Sbostic value(a0, b0) 519*48955Sbostic void *a0, *b0; 520*48955Sbostic { 52142452Sbostic register struct nlist *a = a0, *b = b0; 52242452Sbostic 52339109Sbostic if (SYMBOL_TYPE(a->n_type) == N_UNDF) 52439109Sbostic if (SYMBOL_TYPE(b->n_type) == N_UNDF) 52539109Sbostic return(0); 52639109Sbostic else 52739109Sbostic return(-1); 52839109Sbostic else if (SYMBOL_TYPE(b->n_type) == N_UNDF) 52939109Sbostic return(1); 530*48955Sbostic if (rev) { 531*48955Sbostic if (a->n_value == b->n_value) 532*48955Sbostic return(rname(a0, b0)); 533*48955Sbostic return(b->n_value > a->n_value ? 1 : -1); 534*48955Sbostic } else { 535*48955Sbostic if (a->n_value == b->n_value) 536*48955Sbostic return(fname(a0, b0)); 537*48955Sbostic return(a->n_value > b->n_value ? 1 : -1); 538*48955Sbostic } 53939109Sbostic } 540619Sbill 541*48955Sbostic void * 54239109Sbostic emalloc(size) 54342452Sbostic size_t size; 544619Sbill { 54542452Sbostic char *p; 546619Sbill 54739109Sbostic /* NOSTRICT */ 548*48955Sbostic if (p = malloc(size)) 549*48955Sbostic return(p); 550*48955Sbostic (void)fprintf(stderr, "nm: %s\n", strerror(errno)); 551*48955Sbostic exit(1); 552619Sbill } 55330855Sbostic 55439109Sbostic usage() 55530855Sbostic { 55642452Sbostic (void)fprintf(stderr, "usage: nm [-agnopruw] [file ...]\n"); 55739109Sbostic exit(1); 55830855Sbostic } 559