[unix-pc.sources] ld front end

andy@cit-vax.Caltech.Edu (Andy Fyfe) (03/17/89)

Here are the sources to my front end to /bin/ld, as described in
unix-pc.general.

Andy Fyfe
            andy@csvax.caltech.edu
            wjafyfe@caltech.bitnet
            andy@cit-vax.UUCP	(...!ames!elroy!cit-vax!andy)

Of all the things I've lost I miss my mind the most.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile ld.c ld.h load.c munge.c
# Wrapped by andy@marmot on Thu Mar 16 12:45:59 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2921 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is a front end to the loader, /bin/ld, to make "better" use of
Xthe shared library.
X
XIf the string "/lib/shlib.ifile" does not appear as one of the arguments
Xto ld, then it will simply exec /bin/ld with the same arguments.
X
XIf the string does appear, then ld will make two calls to /bin/ld.  The
Xfirst will be with all the specified object files and libraries, but
Xnot the shared library.  The object file will then be scanned for
Xunresolved symbols and an "ifile" will be built from those symbols.  Then
X/bin/ld will be run again to complete the load.
X
XIf this program is installed with GNU C (as /usr/local/lib/gcc-ld, or
Xwhatever), then the "-shlib" option to gcc will cause the two pass load
Xas described above.  Without "-shlib", it will just exec /bin/ld with
Xthe same arguments.  This means that, for example, you can build gnu
Xcpp with "-lmalloc" and still use "-shlib" (and the malloc library makes
Xfor a quicker cpp).
X
XIf this program is installed as "ld" (in the search path before /bin/ld,
Xor my renaming /bin/ld (and updating the Makefile to use the new name))
Xthen explicit "ld" commands in Makefiles to use the shared library will
Xalso get the two pass behaviour.  To prevent this, it's only necessary
Xto refer to the system ifile as "//lib/shlib.ifile".
X
XINSTALLATION
X
XFirst you need to make a library.  The shared library contains most of
Xlibc but not all of it.  The missing parts of libc are listed in the
XMakefile for both Unix 3.5 and 3.51.  Select the right one for you and
Xexecute "make lib".  This will make "libminic.a".  You don't need to
Xcall this library "minic" if you don't want to, and you can install it
Xwherever you like.  The Makefile assumes it will be installed as
X"/usr/local/lib/libminic.a".  If you install it in "/lib" or "/usr/lib"
Xyou can change that string to be simply "-lminic".  In any case, you'll
Xhave to copy it wherever you want it yourself.
X
XMake sure that the ifile file, library file and location of the system ld
Xare all correct in the Makefile, and run "make".  Part of the process will
Xbe running the program "munge" on the system "ifile" and compiling a
Xsorted list of symbols contained in it into "ld".  (The file "ifile.c"
Xshould be 5 lines longer than "/lib/shlib.ifile".)  If you upgrade your
Xsystem you will have to remake "ld" if the ifile changed.  (Ld will
Xcheck to see if it is older than the system ifile, and if it is it will
Xrefuse to do the load.)  Ld will be made as "ld.temp" and then that will
Xbe used to relink "ld".  "Ld" can then be installed wherever you like.
X
X"Ld" can be compiled with either gcc or cc, though gcc will get you a
Xsomewhat smaller binary (and will put all the "ifile" stuff that is
Xcompiled in into the .text section).
X
XACKNOWLEDGEMENTS
X
XThis is based on John MacMillan's shcc.  Since it only deals with the
Xloader, it's much simpler.  This seemed like the easiest way to bring
Xshcc's functionality to the GNU C compiler.
END_OF_FILE
if test 2921 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(2689 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# This makefile is based largely on the one John MacMillan provided
X# with shcc
X#
X
X# What you want it called, and where to put it:
X
XNAME = ld
XREAL_LD = /bin/ld
XTARGET = /usr/local/lib/gcc-ld
X
X# LIBMISSING can is the name of the archive created here.  LIBMINIC
X# is where it will be installed.  If you install it in /lib or /usr/lib,
X# then LIBMINIC can be of the form "-lminic".  The name "minic" isn't
X# important, and can be whatever you want.
X
XLIBMISSING = libminic.a
XLIBMINIC = /usr/local/lib/libminic.a
X
X# SHLIB is where the system ifile is
X
XSHLIB = /lib/shlib.ifile
X
X# Things missing from the shared version of libc.  I used the same list
X# as is in Jeffery Small's ccc, who did the hard part and figured out
X# what was missing (and he credits Jim Apedaile for the 3.5 stuff).
X# Thanks Jeffery and Jim!
X
XMISSING35 = acct.o assert.o aulrem.o biglitpow.o bsearch.o clrerr.o \
X	    doprnt.o doscan.o dtop.o fakcu.o findiop.o hsearch.o \
X	    lfind.o lockf.o lsearch.o ltostr.o mcount.o mon.o ptod.o \
X	    setvbuf.o sigrte.o strtod.o sigtrap.o tell.o tfind.o \
X	    tsearch.o vfprintf.o vprintf.o vsprintf.o
XMISSING351 = acct.o aulrem.o biglitpow.o clrerr.o doprnt.o doscan.o \
X	     dtop.o fakcu.o findiop.o lfind.o lockf.o lsearch.o ltostr.o \
X	     mcount.o ptod.o setvbuf.o sigrte.o strtod.o tell.o tfind.o \
X	     vfprintf.o vprintf.o vsprintf.o
X
X# Now pick MISSING35 if you're using Version 3.5 of the OS, MISSING351 if
X# you're using version 3.51:
X
XMISSING = $(MISSING35)
X#MISSING = $(MISSING351)
X
X# Compile time options
X#
X# ld will compile with both gcc and cc
X
XCC = gcc
XDEFINES = -DTARGET=\"$(TARGET)\" -DREAL_LD=\"$(REAL_LD)\" \
X	-DSHLIB=\"$(SHLIB)\" -DLIBMINIC=\"$(LIBMINIC)\"
XCFLAGS = -O $(DEFINES)
XLDFLAGS = -s
X
X# You shouldn't have to touch these
X
XSRCS = ld.c load.c
XMISC = README Makefile munge.c
XINCS = ld.h
XOBJS = ld.o load.o
XLIBS = -lld
XLIBC = /lib/libc.a
X
Xall: $(NAME)
X
Xlib: $(LIBMISSING)
X
X$(NAME): $(OBJS)
X	$(CC) $(LDFLAGS) -o ld.temp $(OBJS) $(LIBS)
X	./ld.temp -s -o $@ /lib/crt0s.o $(SHLIB) $(OBJS) $(LIBS)
X	rm ld.temp
X
Xinstall: $(NAME)
X	cp $(NAME) $(TARGET)
X
Xload.o: ifile.c ld.h
Xld.o: ld.h
X
Xifile.c: munge $(SHLIB)
X	sed -e '/"/s/"/\\"/g' $(SHLIB) | ./munge > $@
X
X$(LIBMISSING): $(LIBC) Makefile
X	-rm -f $(LIBMISSING)
X	ar x $(LIBC) $(MISSING)
X	ar cr $@ `lorder $(MISSING) | tsort`
X	grep daylight $(SHLIB) > /dev/null || ( \
X		echo 'int daylight = 1;' > daylight.c ; \
X		$(CC) -c daylight.c ; \
X		ar cr $@ daylight.o \
X	)
X	rm -f $(MISSING) daylight.[co]
X
Xlint: $(SRCS)
X	lint $(SRCS)
X
Xshar $(NAME).shar:
X	shar $(MISC) $(SRCS) $(INCS) > $(NAME).shar
X
Xclean:
X	rm -f $(NAME) ld.temp munge munge.o ifile.c $(OBJS) $(MISSING) \
X	$(LIBMISSING) daylight.[co] *.out core
END_OF_FILE
if test 2689 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'ld.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ld.c'\"
else
echo shar: Extracting \"'ld.c'\" \(4516 characters\)
sed "s/^X//" >'ld.c' <<'END_OF_FILE'
X/*
X *	ld -- the main program
X *
X *	front in to /bin/ld to do a two-pass load when the shared library
X *	is used.  this is designed to be used with the gnu c compiler.
X *
X */
X
X#include <stdio.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/wait.h>
X#include <sys/stat.h>
X
X#include "ld.h"
X
Xchar *cmd_name;
X
X#ifdef __STDC__
Xextern void *malloc(unsigned);
Xstatic void parse_args(int argc, char *argv[], int skip);
Xstatic int execute(char **argv);
Xstatic void catch(void);
Xstatic void check_dates(const char *);
X#else
Xextern void *malloc();
Xstatic void parse_args();
Xstatic int execute();
Xstatic void catch();
Xstatic void check_dates();
X#endif
X
Xstatic char a_out[30], ifile[30];
Xstatic char **ld1, **ld2;
Xstatic int ac1, ac2;
X
X#ifdef __STDC__
Xmain(int argc, char *argv[])
X#else
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X#endif
X{
X    int shlib = 0, i, ret;
X
X    cmd_name = argv[0];
X
X    for (i=1; i<argc; ++i)
X        if (strcmp(argv[i], SHLIB) == 0) {
X	    shlib = i;
X	    break;
X	}
X
X    if (shlib == 0) {
X	argv[0] = "ld";
X	exit(execute(argv));
X    }
X
X    check_dates(argv[0]);
X
X    strcpy(a_out, "/tmp/lda.out-XXXXXX");
X    strcpy(ifile, "/tmp/ldifile-XXXXXX");
X    mktemp(a_out);
X    mktemp(ifile);
X
X    parse_args(argc, argv, shlib);
X
X    if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X	signal(SIGINT, catch);
X    if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X	signal(SIGQUIT, catch);
X
X    ret = execute(ld1);
X    if (ret != 0) {
X	unlink(a_out);
X	exit(ret);
X    }
X
X    if (make_ifile(a_out, ifile) < 0) {
X	unlink(a_out);
X	unlink(ifile);
X	exit(1);
X    }
X
X    ret = execute(ld2);
X    unlink(a_out);
X    unlink(ifile);
X    exit(ret);
X}
X
X#ifdef __STDC__
Xstatic void
Xparse_args(int argc, char *argv[], int skip)
X#else
Xstatic void
Xparse_args(argc, argv, skip)
Xint argc;
Xchar *argv[];
Xint skip;
X#endif
X{
X    int i;
X
X    ld1 = (char **)malloc((argc + 5) * sizeof(char *));
X    ld2 = (char **)malloc((argc + 3) * sizeof(char *));
X    if (ld1 == 0 || ld2 == 0) {
X	fprintf(stderr, "%s: can't malloc: ", cmd_name);
X	perror("");
X	exit(1);
X    }
X    ac1 = 0;
X    ac2 = 0;
X    ld1[ac1++] = "ld";
X    ld1[ac1++] = "-r";
X    ld1[ac1++] = "-o";
X    ld1[ac1++] = a_out;
X    ld2[ac2++] = "ld";
X    for (i=1; i<argc; ++i) {
X	if (i == skip)
X	    continue;
X	if (argv[i][0] == '-') {
X	    switch(argv[i][1]) {
X	    case 't':
X		ld1[ac1++] = argv[i];
X		break;
X	    case 'F':
X	    case 'm':
X	    case 'n':
X	    case 'N':
X	    case 'r':
X	    case 's':
X	    case 'x':
X	    case 'z':
X		ld2[ac2++] = argv[i];
X		break;
X	    case 'l':
X	    case 'L':
X	    case 'u':
X		ld1[ac1++] = argv[i];
X		if (argv[i][2] == '\0')
X		    ld1[ac1++] = argv[++i];
X		break;
X	    case 'e':
X	    case 'f':
X	    case 'o':
X		ld2[ac2++] = argv[i];
X		if (argv[i][2] == '\0')
X		    ld2[ac2++] = argv[++i];
X		break;
X	    case 'V':
X		if (argv[i][2] != 'S')
X		    ld1[ac1++] = argv[i];
X		else {
X		    ld2[ac2++] = argv[i];
X		    if (argv[i][3] == '\0')
X			ld2[ac2++] = argv[++i];
X		}
X		break;
X	    }
X	}
X	else
X	    ld1[ac1++] = argv[i];
X    }
X    ld1[ac1++] = LIBMINIC;
X    ld2[ac2++] = a_out;
X    ld2[ac2++] = ifile;
X
X    ld1[ac1] = 0;
X    ld2[ac2] = 0;
X
X    return;
X}
X
X#ifdef __STDC__
Xstatic int
Xexecute(char *argv[])
X#else
Xstatic int
Xexecute(argv)
Xchar *argv[];
X#endif
X{
X    int pid;
X    union wait status;
X
X    pid = fork();
X    if (pid < 0) {
X	fprintf(stderr, "%s: can't fork: ", cmd_name);
X	perror("");
X	return 1;
X    }
X
X    if (pid == 0) {
X	execv(REAL_LD, argv);
X	fprintf(stderr, "%s: can't exec %s: ", cmd_name, REAL_LD);
X	perror("");
X	exit(1);
X    }
X    else {
X	while (pid != wait(&status))
X	    ;
X	if (status.w_termsig == 0)
X	    return status.w_retcode;
X	else {
X	    fprintf(stderr, "%s: caught signal %d%s\n",
X		cmd_name, status.w_termsig,
X		(status.w_coredump ? " (core dumped)" : ""));
X	    return 1;
X	}
X    }
X}
X
X#ifdef __STDC__
Xstatic void
Xcatch(void)
X#else
Xstatic void
Xcatch()
X#endif
X{
X    unlink(a_out);
X    unlink(ifile);
X    exit(1);
X}
X
X#ifdef __STDC__
Xstatic void
Xcheck_dates(const char *name)
X#else
Xstatic void
Xcheck_dates(name)
Xchar *name;
X#endif
X{
X    struct stat buf1, buf2;
X    char *p, *strrchr();
X
X    p = strrchr(TARGET, '/');
X    p = (p == NULL || strrchr(name, '/') != NULL) ? TARGET : p+1;
X
X    if (strcmp(p, name) != 0)
X	return;
X
X    if (stat(TARGET, &buf1) < 0)
X        return;
X    if (stat(SHLIB, &buf2) < 0) {
X	fprintf(stderr, "%s: can't stat %s: ", cmd_name, SHLIB);
X	perror("");
X	exit(1);
X    }
X    if (buf1.st_mtime < buf2.st_mtime) {
X	fprintf(stderr, "%s: %s has changed -- %s must be remade\n",
X	    cmd_name, SHLIB, TARGET);
X	exit(1);
X    }
X}
END_OF_FILE
if test 4516 -ne `wc -c <'ld.c'`; then
    echo shar: \"'ld.c'\" unpacked with wrong size!
fi
# end of 'ld.c'
fi
if test -f 'ld.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ld.h'\"
else
echo shar: Extracting \"'ld.h'\" \(548 characters\)
sed "s/^X//" >'ld.h' <<'END_OF_FILE'
X/*
X *	ld.h -- configuration stuff
X *
X *	most of which is handled by the makefile
X */
X
X#ifndef SHLIB
X#define SHLIB		"/lib/shlib.ifile"
X#endif
X
X#ifndef LIBMINIC
X#define LIBMINIC	"/usr/local/lib/libminic.a"
X#endif
X
X#ifndef TARGET
X#define TARGET		"/usr/local/lib/gcc-ld"
X#endif
X
X#ifndef REAL_LD
X#define REAL_LD		"/bin/ld"
X#endif
X
X#ifdef __STDC__
Xextern int make_ifile(const char *a_out, const char *ifile);
X#else
Xextern int make_ifile();
X#endif
X
Xextern char *cmd_name;
X
X#ifdef __STDC__
X#ifndef __GNUC__
X#define inline
X#endif
X#else
X#define const
X#endif
END_OF_FILE
if test 548 -ne `wc -c <'ld.h'`; then
    echo shar: \"'ld.h'\" unpacked with wrong size!
fi
# end of 'ld.h'
fi
if test -f 'load.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'load.c'\"
else
echo shar: Extracting \"'load.c'\" \(2889 characters\)
sed "s/^X//" >'load.c' <<'END_OF_FILE'
X/*
X *	load.c -- find the unresolved symbols and build an ifile
X *
X *	after the first load pass, the object is scanned for unsresolved
X *	symbols.  each one is looked up in the compiled-in copy of the
X *	system ifile, and if found is added to a temporary ifile used in
X *	the second load pass.
X *
X */
X
X#include <stdio.h>
X#include <filehdr.h>
X#include <syms.h>
X#include <ldfcn.h>
X
X#include "ld.h"
X
Xstruct symbolentry {
X    char *name;
X    long addr;
X};
X
X#include "ifile.c"
X
Xextern void *malloc();
X
X#ifdef __STDC__
Xstatic inline void
Xlookup(const char *name, FILE *f)
X#else
Xstatic void
Xlookup(name, f)
Xchar *name;
XFILE *f;
X#endif
X{
X    int l, m, h, d;
X
X    l = 0;
X    h = sizeof(symtable) / sizeof(struct symbolentry);
X    while (h >= l) {
X	m = (l + h) / 2;
X	d = strcmp(name, symtable[m].name);
X	if (d == 0) {
X	    fprintf(f, "%s = %#.8x;\n", name, symtable[m].addr);
X	    return;
X	}
X	if (d > 0)
X	    l = m + 1;
X	else
X	    h = m - 1;
X    }
X}
X
X#ifdef __STDC__
Xint
Xmake_ifile(const char *a_out, const char *ifile)
X#else
Xint
Xmake_ifile(a_out, ifile)
Xchar *a_out, *ifile;
X#endif
X{
X    int i;
X    FILE *f;
X    LDFILE *ld;
X    SYMENT sym;
X    char *strtab = 0, *name;
X    static char sname[SYMNMLEN + 1];
X
X    ld = ldopen(a_out, (LDFILE *)0);
X    if (ld == NULL) {
X	fprintf(stderr, "%s: can't ldopen %s: ", cmd_name, a_out);
X	perror("");
X	return -1;
X    }
X    f = fopen(ifile, "w");
X    if (f == NULL) {
X	fprintf(stderr, "%s: can't fopen %s: ", cmd_name, ifile);
X	perror("");
X	return -1;
X    }
X
X    for (i=0; i<sizeof(header)/sizeof(char *); ++i)
X	fprintf(f, "%s\n", header[i]);
X
X    for (i=0; i<HEADER(ld).f_nsyms; ++i) {
X	if (ldtbread(ld, i, &sym) != SUCCESS) {
X	    fprintf(stderr, "%s: ldtbread failed: ", cmd_name);
X	    perror("");
X	    return -1;
X	}
X	i += sym.n_numaux;
X	if (sym.n_value != 0 || sym.n_scnum != 0)
X	    continue;
X	if (sym._n._n_n._n_zeroes == 0) {
X	    if (strtab == 0) {
X		long size, count;
X		FSEEK(ld, HEADER(ld).f_symptr + HEADER(ld).f_nsyms * SYMESZ, 0);
X		if (FREAD(&size, sizeof(long), 1, ld) != 1) {
X		    fprintf(stderr, "%s: read string table size: ", cmd_name);
X		    perror("");
X		    return -1;
X		}
X		size -= sizeof(long);
X		strtab = (char *)malloc(size);
X		if (strtab == 0) {
X		    fprintf(stderr, "%s: can't malloc: ", cmd_name);
X		    perror("");
X		    return -1;
X		}
X		if ((count = FREAD(strtab, sizeof(char), size, ld)) != size) {
X		    fprintf(stderr,
X			"%s: read string table: wanted %d, got %d: ",
X			cmd_name, size, count);
X		    perror("");
X		    return -1;
X		}
X	    }
X	    name = &strtab[sym._n._n_n._n_offset - sizeof(long)];
X	}
X	else {
X	    strncpy(sname, sym._n._n_name, SYMNMLEN);
X	    sname[SYMNMLEN] = '\0';
X	    name = sname;
X	}
X	lookup(name, f);
X    }
X
X    ldclose(ld);
X    if (fclose(f) == EOF) {
X	fprintf(stderr, "%s: error closing %s: ", cmd_name, ifile);
X	perror("");
X	return -1;
X    }
X
X    if (strtab != 0)
X	free(strtab);
X
X    return 0;
X}
END_OF_FILE
if test 2889 -ne `wc -c <'load.c'`; then
    echo shar: \"'load.c'\" unpacked with wrong size!
fi
# end of 'load.c'
fi
if test -f 'munge.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'munge.c'\"
else
echo shar: Extracting \"'munge.c'\" \(1871 characters\)
sed "s/^X//" >'munge.c' <<'END_OF_FILE'
X/*
X * Munge the /lib/shlib.ifile to produce ifile.c.
X *
X * This is basically taken from John MacMillan's shcc.
X */
X
X#include <stdio.h>
X
X#define INIT_SIZE	500
X
Xstruct sym {
X    char *name;
X    long addr;
X};
X
Xmain()
X{
X    struct sym *symp;
X    char line[256], *p, *base;
X    int inheader = 1;
X    int size, count, i, cmp();
X    long baseval;
X    long value();
X    extern char	*strchr();
X    extern void *malloc(), *realloc();
X
X    printf("const char * const header[] = {\n");
X    while (inheader && gets(line)) {
X	if (line[0] != ' ' && line[0] != '\t' && strchr(line, '='))
X	    inheader = 0;
X	else
X	    printf("    \"%s\",\n", line);
X    }
X    printf("};\n\nconst struct symbolentry symtable[] = {\n");
X
X    size = INIT_SIZE;
X    count = 0;
X    symp = (struct sym *)malloc(size * sizeof(struct sym));
X    do {
X	if (count == size) {
X	    size *= 2;
X	    free(symp);
X	    symp = (struct sym *)realloc(size * sizeof(struct sym));
X	}
X	p = strchr(line, ' ');
X	*p = '\0';
X	symp[count].name = (char *)malloc(p - line + 1);
X	(void) strcpy(symp[count].name, line);
X	p = strchr(++p, '=');
X	p += 2;
X	if (*p == '0') {
X	    p += 2;
X	    sscanf(p, "%lx", &symp[count].addr);
X	}
X	else {
X	    base = p;
X	    p = strchr(p, ' ');
X	    *p = '\0';
X	    baseval = value(symp, base, count);
X	    p = strchr(++p, '+');
X	    p += 4;
X	    sscanf(p, "%lx", &symp[count].addr);
X	    symp[count].addr += baseval;
X	}
X	++count;
X    } while (gets(line));
X
X    qsort(symp, count, sizeof(struct sym), cmp);
X    for (i=0; i<count; ++i)
X	printf("    {\"%s\", %#lx},\n", symp[i].name, symp[i].addr);
X    printf("};\n");
X    exit(0);
X}
X
Xlong
Xvalue(symp, sym, count)
Xstruct sym *symp;
Xchar *sym;
Xint count;
X{
X    int i;
X
X    for (i=0; i<count; ++i) {
X	if (strcmp(symp[i].name, sym) == 0)
X		return(symp[i].addr);
X    }
X    return 0L;
X}
X
Xint cmp(a, b)
Xstruct sym *a, *b;
X{
X    return strcmp(a->name, b->name);
X}
END_OF_FILE
if test 1871 -ne `wc -c <'munge.c'`; then
    echo shar: \"'munge.c'\" unpacked with wrong size!
fi
# end of 'munge.c'
fi
echo shar: End of shell archive.
exit 0