386BSD 0.1 development
[unix-history] / usr / src / usr.bin / nm / nm.c
index dfa4ec8..e72285b 100644 (file)
 /*
 /*
- * Copyright (c) 1987 Regents of the University of California.
- * All rights reserved.  The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hans Huebner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)nm.c 4.8 %G%";
-#endif
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
 
 
-/*
- * nm - print name list; VAX string table version
- */
+#ifndef lint
+static char sccsid[] = "@(#)nm.c       5.8 (Berkeley) 5/2/91";
+#endif /* not lint */
 
 #include <sys/types.h>
 
 #include <sys/types.h>
-#include <sys/file.h>
-#include <ar.h>
-#include <stdio.h>
-#include <ctype.h>
 #include <a.out.h>
 #include <stab.h>
 #include <a.out.h>
 #include <stab.h>
+#include <ar.h>
 #include <ranlib.h>
 #include <ranlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 
-#define        OARMAG          0177545         /* OLD archive magic number */
-#define        SELECT          (archive ? archdr.ar_name : *xargv)
-
-#define        YES             1
-#define        NO              0
-
-#define        u_strx          n_un.n_strx
-#define        u_name          n_un.n_name
-
-typedef struct nlist   NLIST;
-
-union {                                /* exec header, or magic string from library */
-       char    mag_armag[SARMAG + 1];
-       struct  exec mag_exp;
-} mag_un;
-
-struct ar_hdr  archdr;         /* archive file header structure */
-FILE   *fi;                    /* input file stream */
-off_t  off;                    /* offset into file */
-int    aflg,                   /* print debugger symbols */
-       gflg,                   /* print only global (external symbols */
-       nflg,                   /* sort numerically, not alphabetically */
-       oflg,                   /* prepend element name to each output line */
-       pflg,                   /* don't sort */
-       rflg = 1,               /* how to sort */
-       uflg,                   /* print only undefined symbols */
-       narg,                   /* global number of arguments */
-       errs,                   /* global error flag */
-       archive;                /* if file is an archive */
-char   **xargv;                /* global pointer to file name */
+int ignore_bad_archive_entries = 1;
+int print_only_external_symbols;
+int print_only_undefined_symbols;
+int print_all_symbols;
+int print_file_each_line;
+int fcount;
 
 
+int rev;
+int fname(), rname(), value();
+int (*sfunc)() = fname;
+
+/* some macros for symbol type (nlist.n_type) handling */
+#define        IS_DEBUGGER_SYMBOL(x)   ((x) & N_STAB)
+#define        IS_EXTERNAL(x)          ((x) & N_EXT)
+#define        SYMBOL_TYPE(x)          ((x) & (N_TYPE | N_STAB))
+
+void *emalloc();
+
+/*
+ * main()
+ *     parse command line, execute process_file() for each file
+ *     specified on the command line.
+ */
 main(argc, argv)
 main(argc, argv)
-       int     argc;
-       char    **argv;
+       int argc;
+       char **argv;
 {
 {
-       extern int      optind;
-       int     ch;                     /* getopts char */
+       extern int optind;
+       int ch, errors;
 
 
-       while ((ch = getopt(argc, argv, "agnopru")) != EOF)
-               switch((char)ch) {
+       while ((ch = getopt(argc, argv, "agnopruw")) != EOF) {
+               switch (ch) {
                case 'a':
                case 'a':
-                       aflg = YES;
+                       print_all_symbols = 1;
                        break;
                case 'g':
                        break;
                case 'g':
-                       gflg = YES;
+                       print_only_external_symbols = 1;
                        break;
                case 'n':
                        break;
                case 'n':
-                       nflg = YES;
+                       sfunc = value;
                        break;
                case 'o':
                        break;
                case 'o':
-                       oflg = YES;
+                       print_file_each_line = 1;
                        break;
                case 'p':
                        break;
                case 'p':
-                       pflg = YES;
+                       sfunc = NULL;
                        break;
                case 'r':
                        break;
                case 'r':
-                       rflg = -1;
+                       rev = 1;
                        break;
                case 'u':
                        break;
                case 'u':
-                       uflg = YES;
+                       print_only_undefined_symbols = 1;
+                       break;
+               case 'w':
+                       ignore_bad_archive_entries = 0;
                        break;
                case '?':
                default:
                        break;
                case '?':
                default:
-                       fputs("usage: nm [-agnopru] [file ...]\n", stderr);
-                       exit(2);
+                       usage();
                }
                }
-       argc -= optind;
+       }
+       fcount = argc - optind;
        argv += optind;
        argv += optind;
-       if (!argc) {
-               argc = 1;
-               argv[0] = "a.out";
+
+       if (rev && sfunc == fname)
+               sfunc = rname;
+
+       if (!fcount)
+               errors = process_file("a.out");
+       else {
+               errors = 0;
+               do {
+                       errors |= process_file(*argv);
+               } while (*++argv);
        }
        }
-       narg = argc;
-       for (xargv = argv; argc--; ++xargv)
-               if (fi = fopen(*xargv, "r")) {
-                       namelist();
-                       (void)fclose(fi);
-               }
-               else
-                       error(NO, "cannot open");
-       exit(errs);
+       exit(errors);
 }
 
 }
 
-namelist()
+/*
+ * process_file()
+ *     show symbols in the file given as an argument.  Accepts archive and
+ *     object files as input.
+ */
+process_file(fname)
+       char *fname;
 {
 {
-       register NLIST  *N, **L;
-       register int    symcount, nsyms;
-       static  NLIST   *symp, **list;
-       static int      lastnsyms = -1,
-                       laststrsiz = -1;
-       static char     *strp;
-       off_t   strsiz;
-       long    lseek();
-       int     compare();
-       char    *malloc(), *realloc();
+       struct exec exec_head;
+       FILE *fp;
+       int retval;
+       char magic[SARMAG];
+    
+       if (!(fp = fopen(fname, "r"))) {
+               (void)fprintf(stderr, "nm: cannot read %s.\n", fname);
+               return(1);
+       }
 
 
+       if (fcount > 1)
+               (void)printf("\n%s:\n", fname);
+    
        /*
        /*
-        * read first few bytes, determine if an archive,
-        * or executable; if executable, check magic number
+        * first check whether this is an object file - read a object
+        * header, and skip back to the beginning
         */
         */
-       /*NOSTRICT*/
-       if (!fread((char *)&mag_un, sizeof(mag_un), 1, fi)) {
-               error(NO, "unable to read file");
-               return;
-       }
-       if (mag_un.mag_exp.a_magic == OARMAG) {
-               error(NO, "old archive");
-               return;
+       if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) {
+               (void)fprintf(stderr, "nm: %s: bad format.\n", fname);
+               (void)fclose(fp);
+               return(1);
        }
        }
-       if (bcmp(mag_un.mag_armag, ARMAG, SARMAG)) {
-               if (N_BADMAG(mag_un.mag_exp)) {
-                       error(NO, "bad format");
-                       return;
+       rewind(fp);
+
+       /* this could be an archive */
+       if (N_BADMAG(exec_head)) {
+               if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 ||
+                   strncmp(magic, ARMAG, SARMAG)) {
+                       (void)fprintf(stderr,
+                           "nm: %s: not object file or archive.\n", fname);
+                       (void)fclose(fp);
+                       return(1);
                }
                }
-               archive = NO;
-               rewind(fi);
-       }
-       else {
-               /*
-                * if archive, skip first entry
-                * if ranlib'd, skip second entry
-                */
-               off = SARMAG;           /* see nextel() */
-               (void)nextel();
-               if (!strcmp(RANLIBMAG, archdr.ar_name))
-                       (void)nextel();
-               if (narg > 1)
-                       printf("\n%s:\n", *xargv);
-               archive = YES;
-       }
+               retval = show_archive(fname, fp);
+       } else
+               retval = show_objfile(fname, fp);
+       (void)fclose(fp);
+       return(retval);
+}
 
 
-       do {
-               /* check for bad magic number */
-               /*NOSTRICT*/
-               if (!fread((char *)&mag_un.mag_exp, sizeof(struct exec), 1, fi)) {
-                       error(NO, "unable to read magic number");
-                       return;
+/*
+ * show_archive()
+ *     show symbols in the given archive file
+ */
+show_archive(fname, fp)
+       char *fname;
+       FILE *fp;
+{
+       struct ar_hdr ar_head;
+       struct exec exec_head;
+       int i, rval;
+       long last_ar_off;
+       char *p, *name;
+
+       name = emalloc(sizeof(ar_head.ar_name) + strlen(fname) + 3);
+
+       rval = 0;
+
+       /* while there are more entries in the archive */
+       while (fread((char *)&ar_head, sizeof(ar_head), (size_t)1, fp) == 1) {
+               /* bad archive entry - stop processing this archive */
+               if (strncmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
+                       (void)fprintf(stderr,
+                           "nm: %s: bad format archive header", fname);
+                       (void)free(name);
+                       return(1);
                }
                }
-               if (N_BADMAG(mag_un.mag_exp))
-                       continue;
 
 
-               /* calculate number of symbols in object */
-               if (!(nsyms = mag_un.mag_exp.a_syms / sizeof(NLIST))) {
-                       error(NO, "no name list");
-                       continue;
+               /* remember start position of current archive object */
+               last_ar_off = ftell(fp);
+
+               /* skip ranlib entries */
+               if (!strncmp(ar_head.ar_name, RANLIBMAG, sizeof(RANLIBMAG) - 1))
+                       goto skip;
+
+               /*
+                * construct a name of the form "archive.a:obj.o:" for the
+                * current archive entry if the object name is to be printed
+                * on each output line
+                */
+               p = name;
+               if (print_file_each_line)
+                       p += sprintf(p, "%s:", fname);
+               for (i = 0; i < sizeof(ar_head.ar_name); ++i)
+                       if (ar_head.ar_name[i] && ar_head.ar_name[i] != ' ')
+                               *p++ = ar_head.ar_name[i];
+               *p++ = '\0';
+
+               /* get and check current object's header */
+               if (fread((char *)&exec_head, sizeof(exec_head),
+                   (size_t)1, fp) != 1) {
+                       (void)fprintf(stderr, "nm: %s: premature EOF.\n", name);
+                       (void)free(name);
+                       return(1);
                }
 
                }
 
-               /* seek to and read symbols */
-               (void)fseek(fi, (long)(N_SYMOFF(mag_un.mag_exp) - sizeof(struct exec)), L_INCR);
-               if (!symp || nsyms > lastnsyms) {
-                       if (!symp) {
-                               /*NOSTRICT*/
-                               symp = (NLIST *)malloc((u_int)(nsyms * sizeof(NLIST)));
-                               /*NOSTRICT*/
-                               list = (NLIST **)malloc((u_int)(nsyms * sizeof(NLIST *)));
-                       }
-                       else {
-                               /*NOSTRICT*/
-                               symp = (NLIST *)realloc((char *)symp, (u_int)(nsyms * sizeof(NLIST)));
-                               /*NOSTRICT*/
-                               list = (NLIST **)realloc((char *)list, (u_int)(nsyms * sizeof(NLIST *)));
+               if (N_BADMAG(exec_head)) {
+                       if (!ignore_bad_archive_entries) {
+                               (void)fprintf(stderr,
+                                   "nm: %s: bad format.\n", name);
+                               rval = 1;
                        }
                        }
-                       if (!symp || !list)
-                               error(YES, "out of memory");
-                       lastnsyms = nsyms;
+               } else {
+                       (void)fseek(fp, (long)-sizeof(exec_head),
+                           SEEK_CUR);
+                       if (!print_file_each_line)
+                               (void)printf("\n%s:\n", name);
+                       rval |= show_objfile(name, fp);
                }
                }
-               /*NOSTRICT*/
-               if (fread((char *)symp, sizeof(NLIST), nsyms, fi) != nsyms) {
-                       error(NO, "bad symbol table");
-                       continue;
+
+               /*
+                * skip to next archive object - it starts at the next
+                * even byte boundary
+                */
+#define even(x) (((x) + 1) & ~1)
+skip:          if (fseek(fp, last_ar_off + even(atol(ar_head.ar_size)),
+                   SEEK_SET)) {
+                       (void)fprintf(stderr,
+                           "nm: %s: %s\n", fname, strerror(errno));
+                       (void)free(name);
+                       return(1);
                }
                }
+       }
+       (void)free(name);
+       return(rval);
+}
+
+/*
+ * show_objfile()
+ *     show symbols from the object file pointed to by fp.  The current
+ *     file pointer for fp is expected to be at the beginning of an a.out
+ *     header.
+ */
+show_objfile(objname, fp)
+       char *objname;
+       FILE *fp;
+{
+       register struct nlist *names, *np;
+       register int i, nnames, nrawnames;
+       struct exec head;
+       long stabsize;
+       char *stab;
+
+       /* read a.out header */
+       if (fread((char *)&head, sizeof(head), (size_t)1, fp) != 1) {
+               (void)fprintf(stderr,
+                   "nm: %s: cannot read header.\n", objname);
+               return(1);
+       }
+
+       /*
+        * skip back to the header - the N_-macros return values relative
+        * to the beginning of the a.out header
+        */
+       if (fseek(fp, (long)-sizeof(head), SEEK_CUR)) {
+               (void)fprintf(stderr,
+                   "nm: %s: %s\n", objname, strerror(errno));
+               return(1);
+       }
+
+       /* stop if this is no valid object file */
+       if (N_BADMAG(head)) {
+               (void)fprintf(stderr,
+                   "nm: %s: bad format.\n", objname);
+               return(1);
+       }
+
+       /* stop if the object file contains no symbol table */
+       if (!head.a_syms) {
+               (void)fprintf(stderr,
+                   "nm: %s: no name list.\n", objname);
+               return(1);
+       }
+
+       if (fseek(fp, (long)N_SYMOFF(head), SEEK_CUR)) {
+               (void)fprintf(stderr,
+                   "nm: %s: %s\n", objname, strerror(errno));
+               return(1);
+       }
+
+       /* get memory for the symbol table */
+       names = emalloc((size_t)head.a_syms);
+       nrawnames = head.a_syms / sizeof(*names);
+       if (fread((char *)names, (size_t)head.a_syms, (size_t)1, fp) != 1) {
+               (void)fprintf(stderr,
+                   "nm: %s: cannot read symbol table.\n", objname);
+               (void)free((char *)names);
+               return(1);
+       }
+
+       /*
+        * Following the symbol table comes the string table.  The first
+        * 4-byte-integer gives the total size of the string table
+        * _including_ the size specification itself.
+        */
+       if (fread((char *)&stabsize, sizeof(stabsize), (size_t)1, fp) != 1) {
+               (void)fprintf(stderr,
+                   "nm: %s: cannot read stab size.\n", objname);
+               (void)free((char *)names);
+               return(1);
+       }
+       stab = emalloc((size_t)stabsize);
+
+       /*
+        * read the string table offset by 4 - all indices into the string
+        * table include the size specification.
+        */
+       stabsize -= 4;          /* we already have the size */
+       if (fread(stab + 4, (size_t)stabsize, (size_t)1, fp) != 1) {
+               (void)fprintf(stderr,
+                   "nm: %s: stab truncated..\n", objname);
+               (void)free((char *)names);
+               (void)free(stab);
+               return(1);
+       }
 
 
-               /* read number of strings, string table */
-               /*NOSTRICT*/
-               if (!fread((char *)&strsiz, sizeof(strsiz), 1, fi)) {
-                       error(NO, "no string table (old format .o?)");
+       /*
+        * fix up the symbol table and filter out unwanted entries
+        *
+        * common symbols are characterized by a n_type of N_UNDF and a
+        * non-zero n_value -- change n_type to N_COMM for all such
+        * symbols to make life easier later.
+        *
+        * filter out all entries which we don't want to print anyway
+        */
+       for (np = names, i = nnames = 0; i < nrawnames; np++, i++) {
+               if (SYMBOL_TYPE(np->n_type) == N_UNDF && np->n_value)
+                       np->n_type = N_COMM | (np->n_type & N_EXT);
+               if (!print_all_symbols && IS_DEBUGGER_SYMBOL(np->n_type))
                        continue;
                        continue;
-               }
-               if (!strp || strsiz > laststrsiz) {
-                       strp = strp ? realloc(strp, (u_int)strsiz) : malloc((u_int)strsiz);
-                       if (!strp)
-                               error(YES, "out of memory");
-                       laststrsiz = strsiz;
-               }
-               if (!fread(strp + sizeof(strsiz), 1, (int)(strsiz - sizeof(strsiz)), fi)) {
-                       error(NO, "no string table (old format .o?)");
+               if (print_only_external_symbols && !IS_EXTERNAL(np->n_type))
+                       continue;
+               if (print_only_undefined_symbols &&
+                   SYMBOL_TYPE(np->n_type) != N_UNDF)
                        continue;
                        continue;
-               }
 
 
-               for (symcount = nsyms, L = list, N = symp;--nsyms >= 0;++N)
-                       if (!(N->n_type & N_EXT) && gflg || N->n_type & N_STAB && (!aflg || gflg || uflg))
-                               --symcount;
-                       else {
-                               N->u_name = N->u_strx ? strp + N->u_strx : "";
-                               *L++ = N;
-                       }
+               /*
+                * make n_un.n_name a character pointer by adding the string
+                * table's base to n_un.n_strx
+                *
+                * don't mess with zero offsets
+                */
+               if (np->n_un.n_strx)
+                       np->n_un.n_name = stab + np->n_un.n_strx;
+               else
+                       np->n_un.n_name = "";
+               names[nnames++] = *np;
+       }
 
 
-               if (!pflg)
-                       qsort(list, symcount, sizeof(NLIST *), compare);
+       /* sort the symbol table if applicable */
+       if (sfunc)
+               qsort((char *)names, (size_t)nnames, sizeof(*names), sfunc);
 
 
-               if ((archive || narg > 1) && !oflg)
-                       printf("\n%s:\n", SELECT);
+       /* print out symbols */
+       for (np = names, i = 0; i < nnames; np++, i++)
+               print_symbol(objname, np);
 
 
-               psyms(list, symcount);
-       } while(archive && nextel());
+       (void)free((char *)names);
+       (void)free(stab);
+       return(0);
 }
 
 }
 
-psyms(list, nsyms)
-       NLIST   **list;
-       register int    nsyms;
+/*
+ * print_symbol()
+ *     show one symbol
+ */
+print_symbol(objname, sym)
+       char *objname;
+       register struct nlist *sym;
 {
 {
-       register NLIST  *L;
-       register u_char type;
-       char    *stab();
-
-       while (nsyms--) {
-               L = *list++;
-               type = L->n_type;
-               if (type & N_STAB) {
-                       if (oflg) {
-                               if (archive)
-                                       printf("%s:", *xargv);
-                               printf("%s:", SELECT);
-                       }
-                       printf("%08x - %02x %04x %5.5s %s\n", (int)L->n_value, L->n_other & 0xff, L->n_desc & 0xffff, stab(L->n_type), L->u_name);
-                       continue;
-               }
-               switch (type & N_TYPE) {
-               case N_UNDF:
-                       type = L->n_value ? 'c' : 'u';
-                       break;
-               case N_ABS:
-                       type = 'a';
-                       break;
-               case N_TEXT:
-                       type = 't';
-                       break;
-               case N_DATA:
-                       type = 'd';
-                       break;
-               case N_BSS:
-                       type = 'b';
-                       break;
-               case N_FN:
-                       type = 'f';
-                       break;
-               default:
-                       type = '?';
-                       break;
-               }
-               if (uflg && type != 'u')
-                       continue;
-               if (oflg) {
-                       if (archive)
-                               printf("%s:", *xargv);
-                       printf("%s:", SELECT);
-               }
-               if (L->n_type & N_EXT)
-                       type = toupper(type);
-               if (!uflg) {
-                       if (type == 'u' || type == 'U')
-                               fputs("        ", stdout);
-                       else
-                               printf(N_FORMAT, (int)L->n_value);
-                       printf(" %c ", (char)type);
-               }
-               puts(L->u_name);
+       char *typestring(), typeletter();
+
+       if (print_file_each_line)
+               (void)printf("%s:", objname);
+
+       /*
+        * handle undefined-only format seperately (no space is
+        * left for symbol values, no type field is printed)
+        */
+       if (print_only_undefined_symbols) {
+               (void)puts(sym->n_un.n_name);
+               return;
        }
        }
+
+       /* print symbol's value */
+       if (SYMBOL_TYPE(sym->n_type) == N_UNDF)
+               (void)printf("        ");
+       else
+               (void)printf("%08lx", sym->n_value);
+
+       /* print type information */
+       if (IS_DEBUGGER_SYMBOL(sym->n_type))
+               (void)printf(" - %02x %04x %5s ", sym->n_other,
+                   sym->n_desc&0xffff, typestring(sym->n_type));
+       else
+               (void)printf(" %c ", typeletter(sym->n_type));
+
+       /* print the symbol's name */
+       (void)puts(sym->n_un.n_name);
 }
 
 }
 
-compare(p1, p2)
-       NLIST   **p1, **p2;
+/*
+ * typestring()
+ *     return the a description string for an STAB entry
+ */
+char *
+typestring(type)
+       register u_char type;
 {
 {
-       if (nflg) {
-               if ((*p1)->n_value > (*p2)->n_value)
-                       return(rflg);
-               if ((*p1)->n_value < (*p2)->n_value)
-                       return(-rflg);
+       switch(type) {
+       case N_BCOMM:
+               return("BCOMM");
+       case N_ECOML:
+               return("ECOML");
+       case N_ECOMM:
+               return("ECOMM");
+       case N_ENTRY:
+               return("ENTRY");
+       case N_FNAME:
+               return("FNAME");
+       case N_FUN:
+               return("FUN");
+       case N_GSYM:
+               return("GSYM");
+       case N_LBRAC:
+               return("LBRAC");
+       case N_LCSYM:
+               return("LCSYM");
+       case N_LENG:
+               return("LENG");
+       case N_LSYM:
+               return("LSYM");
+       case N_PC:
+               return("PC");
+       case N_PSYM:
+               return("PSYM");
+       case N_RBRAC:
+               return("RBRAC");
+       case N_RSYM:
+               return("RSYM");
+       case N_SLINE:
+               return("SLINE");
+       case N_SO:
+               return("SO");
+       case N_SOL:
+               return("SOL");
+       case N_SSYM:
+               return("SSYM");
+       case N_STSYM:
+               return("STSYM");
        }
        }
-       return(rflg * strcmp((*p1)->u_name, (*p2)->u_name));
+       return("???");
 }
 
 }
 
-nextel()
+/*
+ * typeletter()
+ *     return a description letter for the given basic type code of an
+ *     symbol table entry.  The return value will be upper case for
+ *     external, lower case for internal symbols.
+ */
+char
+typeletter(type)
+       u_char type;
 {
 {
-       register char   *cp;
-       long    arsize,
-               lseek();
-
-       (void)fseek(fi, off, L_SET);
-       /*NOSTRICT*/
-       if (!fread((char *)&archdr, sizeof(struct ar_hdr), 1, fi))
-               return(0);
-       for (cp = archdr.ar_name; cp < &archdr.ar_name[sizeof(archdr.ar_name)]; ++cp)
-               if (*cp == ' ') {
-                       *cp = '\0';
-                       break;
-               }
-       arsize = atol(archdr.ar_size);
-       if (arsize & 1)
-               ++arsize;
-       off = ftell(fi) + arsize;       /* beginning of next element */
-       return(1);
+       switch(SYMBOL_TYPE(type)) {
+       case N_ABS:
+               return(IS_EXTERNAL(type) ? 'A' : 'a');
+       case N_BSS:
+               return(IS_EXTERNAL(type) ? 'B' : 'b');
+       case N_COMM:
+               return(IS_EXTERNAL(type) ? 'C' : 'c');
+       case N_DATA:
+               return(IS_EXTERNAL(type) ? 'D' : 'd');
+       case N_FN:
+               return(IS_EXTERNAL(type) ? 'F' : 'f');
+       case N_TEXT:
+               return(IS_EXTERNAL(type) ? 'T' : 't');
+       case N_UNDF:
+               return(IS_EXTERNAL(type) ? 'U' : 'u');
+       }
+       return('?');
 }
 
 }
 
-struct stabnames {
-       int     st_value;
-       char    *st_name;
-} stabnames[] ={
-       N_GSYM,         "GSYM",
-       N_FNAME,        "FNAME",
-       N_FUN,          "FUN",
-       N_STSYM,        "STSYM",
-       N_LCSYM,        "LCSYM",
-       N_RSYM,         "RSYM",
-       N_SLINE,        "SLINE",
-       N_SSYM,         "SSYM",
-       N_SO,           "SO",
-       N_LSYM,         "LSYM",
-       N_SOL,          "SOL",
-       N_PSYM,         "PSYM",
-       N_ENTRY,        "ENTRY",
-       N_LBRAC,        "LBRAC",
-       N_RBRAC,        "RBRAC",
-       N_BCOMM,        "BCOMM",
-       N_ECOMM,        "ECOMM",
-       N_ECOML,        "ECOML",
-       N_LENG,         "LENG",
-       N_PC,           "PC",
-       0,              0
-};
+fname(a0, b0)
+       void *a0, *b0;
+{
+       struct nlist *a = a0, *b = b0;
 
 
-char *
-stab(val)
-       register u_char val;
+       return(strcmp(a->n_un.n_name, b->n_un.n_name));
+}
+
+rname(a0, b0)
+       void *a0, *b0;
 {
 {
-       register struct stabnames       *sp;
-       static char     prbuf[5];
-
-       for (sp = stabnames; sp->st_value; ++sp)
-               if (sp->st_value == val)
-                       return(sp->st_name);
-       (void)sprintf(prbuf, "%02x", (int)val);
-       return(prbuf);
+       struct nlist *a = a0, *b = b0;
+
+       return(strcmp(b->n_un.n_name, a->n_un.n_name));
 }
 
 }
 
-error(doexit, msg)
-       int     doexit;
-       char    *msg;
+value(a0, b0)
+       void *a0, *b0;
 {
 {
-       fprintf(stderr, "nm: %s:", *xargv);
-       if (archive)
-               fprintf(stderr, "(%s): %s\n", archdr.ar_name, msg);
-       else
-               fprintf(stderr, " %s\n", msg);
-       if (doexit)
-               exit(2);
-       errs = 1;
+       register struct nlist *a = a0, *b = b0;
+
+       if (SYMBOL_TYPE(a->n_type) == N_UNDF)
+               if (SYMBOL_TYPE(b->n_type) == N_UNDF)
+                       return(0);
+               else
+                       return(-1);
+       else if (SYMBOL_TYPE(b->n_type) == N_UNDF)
+               return(1);
+       if (rev) {
+               if (a->n_value == b->n_value)
+                       return(rname(a0, b0));
+               return(b->n_value > a->n_value ? 1 : -1);
+       } else {
+               if (a->n_value == b->n_value)
+                       return(fname(a0, b0));
+               return(a->n_value > b->n_value ? 1 : -1);
+       }
+}
+
+void *
+emalloc(size)
+       size_t size;
+{
+       char *p;
+
+       /* NOSTRICT */
+       if (p = malloc(size))
+               return(p);
+       (void)fprintf(stderr, "nm: %s\n", strerror(errno));
+       exit(1);
+}
+
+usage()
+{
+       (void)fprintf(stderr, "usage: nm [-agnopruw] [file ...]\n");
+       exit(1);
 }
 }