[comp.os.minix] About the Minix C compiler

croes@uva.UUCP (Felix A. Croes) (06/08/89)

Mr. Tanenbaum designed Minix as an teaching operating system. For this reason
Minix includes the source of the OS and of most of the programs.
However, from my point of view Minix has one major drawback: not everything
is in source. In Minix-ST, the compiler, the assembler, the loader and the
archiver are just binaries. The whole idea of Minix is that you can look at
the source, maybe modify it, and hopefully learn from it. Strangely, this
doesn't seem to apply to the compiler.

This results in another problem: for the rest of the programs, enhancements
and bugfixes find there way through the Usenet. But not for the compiler,
that seems to be one of the most buggy programs present in Minix. For
example, do you think the following program works?

        # define qwerty(a, b)   (a-b)
        # define asd            qwerty

        main()
        {
                printf("%d\n", asd (1, 2) < 0x1);
        }

No it doesn't. First, the compiler treats octal and hexadecimal constants as
unsigned ints. Following the C conversion rules, asd(1, 2) == -1 will be
converted to an unsigned before comparision, thus becomes (unsigned) 0xFFFF
and printf prints a 0. This is not K&R C and certainly not ANSI C. The -O
option doesn't affect this. By the way, the -O option doesn't seem to affect
anything. Second, this will not even make it through the C preprocessor which
generates garbage on the macro "asd" (cem does the same). These are only
minor bugs, but typical for the state the compiler is in.

The above problems apply to Minix-ST but considering the many desparate
attempts to use a Turbo C cross compiler on the PC, the compiler there cannot
be much either. I would be prepared to overlook the fact that the compiler is
slow and produces poor code, if at least it WORKED. On the ST, cem-opt-cg are
full of bugs, the assembler changes "move.l #2, d0" in "moveq #2, d0" without
warning, ld seems to work but cv produced incorrect code in the old version,
and doesn't accept correct (well, assembler output anyway) input in the new
version. Besides, cv should have been merged with ld in the first place.

What can be done about it?
- Wait until patches are send. This still leaves us without the source. And
  afterwards we have to wait again for the next patches.
- Wait until Mr. Tanenbaum writes a book called "Compiler construction with
  Minix", sold together with full sources for a compiler (for an incredably
  low price of cource). Probably just wishful thinking. (?)
- Buy the sources. But then you still won't be able to distribute them.
- Write, or port, your own (public domain) compiler.

To me, the last approach looks most promising. Several compilers have been
ported to Minix, most notably GNU CC. However, GCC is not really an
alternative as it is not compatible with ACK and so enormously large that,
with 1 Mb of memory, it hardly fits and certainly cannot compile itself.
Besides, this is no solution for those poor PC guys.

A good compiler should
- be available for both PC and ST Minix
- be almost identical on those two machines - except for the code generator
  and the assembler
- be ACK compatible (at least use the same object files)
- be small
- be reasonably fast
- produce correct, maybe even good, code.

I understand that the Minix-PC compiler can only produce programs with a
maximum size of 64K (text+data). An option to use 64K text and 64K data
on the PC should be included. As for the ST, how about this:

It IS possible to create real position independent code on the 68000. Make
jsr/jmp relative to a4 (or pc), replace 

        rts
by
        move.l  (sp)+, d1
        jmp     0(a4, d1.l)

Also make all data references relative to a5. If you consider the frame
pointer as pointing to a linked list of frame pointers, it is easy to see
that all these frame pointer values can have the right offset added when
the data space is moved in memory, so even link and unlk present no problem.
The disadvantages are slower and larger code, no more memory faults on
reference of NULL pointers, and some modifications to the kernel are needed.
The advantages include
- PIC (you can even use swapping)
- less registers left, but all registers can hold pointers now
- as an extra option, 2 bytes pointers are possible: this would make sure that
  all references would be limited to a 64K (+ 2 bytes) data segment, so there is
  no way to crash the operating system by destroying important data outside a
  process' address space. Of course this also means that the text segment must
  be limited to 64K
- better fork(): no shadowing.

I am not saying that the above solution should replace the present one, but
it would be a nice alternative and it would mean more PC compatibility.
Perhaps SEPARATE_ID programs could be in this format?



SUMMARY: if comp.os.minix is to be split up, it should become comp.os.minix
         and comp.os.minix.compiler.



As soon as I have time, I will write a ld that replaces the old ld and cv for
the ST - I will try to keep it PC-compatible.

			Felix Croes

+---------------------------------------|--------------------------------------+
| "GEM is dead -			|	croes@uva.uucp		       |
|		 long live Minix!"	|	...!mcvax!uva!croes	       |
+---------------------------------------|--------------------------------------+

jimh1@.UUCP (jimh1 is ACK alter ego) (06/09/89)

In the above messages, references are made to the infamous "ACK"
package.  Is there another group that regularly posts information
about this package. Its front ends? back ends?  I have to field 
questions daily about ACK around here, and I would sure like to
know if there others who know more than I.

Incidentally, the ACK back-end for Minix contains run-time routines
that are very different from the ones that are supplied with Minix.
Do I have radically different versions of the two? or are the
differences more than cosmetic?

I remain, Your most humble pupil in these and other matters,


James A. Hinds
Jimh1!veritoo!aloha1!uhccux!humu!nosc!ucsd.edu ...
Verifone, Inc.

croes@uva.UUCP (Felix A. Croes) (06/14/89)

Here is the source of a public domain re-implementation of ar. It can do
anything the old (Minix-ST) ar can, has some extras and is significantly
faster (and shorter, but it uses more memory).

Notes:

- I wrote this program in a hurry, while trying to figure out the ar & ranlib
  format. It shows: the code is not as clean as could be and comments are
  sparse. Feel free to improve it.

- ar has the following new options:
	[r]u   : only replace if file is newer than the archive version
	[x]o   : extract with old date, only works for owner and superuser
	[ard]R : when the archive is updated, create not only a __.SYMDEF
		 header but also a __.SYMREF header which contains unresolved
		 links. This is completely non-standard but the new loader
		 I will write works faster that way. The old loader can use
		 new libraries and the new loader will be able to use old
		 libraries, so there are no compatibility problems.

- The Book says that the archive magic number is 0177545 and so did ar.h.
  However, the old ar (and thus ld) does NOT use this magic number, but
  0177454 instead (ST). I suppose someone made a typo somewhere. Which magic
  number is used on the PC? I would like to know the right magic number before
  I write my loader. Could perhaps one of The Makers of Minix clarify this?
  For the time being, I will stick to the one now used on the ST. I have
  enclosed a new ar.h in this posting.

- The archiver is written for Minix-ST but I see no reason why it shouldn't
  work on Minix-PC, unless Minix-PC uses a different .a/.o format.

- bcopy() is not in the official Minix library. strncpy() is but is wrong.
  Both are in this posting.

- You will need the file out.h that was distributed in one of the official
  Minix patches. It was also in the mdb posting.


'sum ar.c' gives 45715	47.


				Felix Croes.


# --------------------------CUT HERE-------------------------
echo x - ar.c
gres '^X' '' > ar.c << '/'
X/*
X * ar.c - a public domain re-implementation of the Minix ar command
X *
X * Author:	Felix A. Croes (croes@fwi.uva.nl)
X * Version:	1.0
X * Date:	09/06/89
X *
X * You may copy or modify this source anyway you like, but please leave this
X * header intact.
X */
X
X/*==========================================================================*
X *			#include's, #define's				    *
X *==========================================================================*/
X
X# include <stdio.h>
X# include <ar.h>	/* NOTE: ARMAG should be 0177454!!! */
X# include <out.h>
X# include <sys/types.h>
X# include <sys/stat.h>
X
X# define TRUE		1
X# define FALSE		0
X# define FNAMELEN	14
X# define MEMSIZ		(8*1024)
X
X# define RANLIBSZ	1024
X# define RLXDEFSZ	(8*RANLIBSZ)
X# define RLXREFSZ	(4*RANLIBSZ)
X
X# define align(x)	(((x)+1)&-2)
X# define get1032	get3210
X# define put1032	put3210
X
X/*==========================================================================*
X *				global stuff				    *
X *==========================================================================*/
X
Xextern char *malloc(), *strcpy(), *strncpy();
X
Xchar *command, *archive, *tmpfile;
Xlong archive_size;
X
Xtypedef char bool;
X
Xbool	addflag, creatflag, deleteflag, replaceflag,
X	listflag, extractflag, oldtimeflag, updateflag,
X	verboseflag, localflag, xrefflag, readflag;
X
X/*
X * mem is used by copy() as a buffer for copying between files, and by
X * addranlib() to load the symbol table in. If the object file is small enough,
X * addranlib() loads it completely in mem and sets in_mem to show that copy
X * doesn't have to load it again.
X */
Xchar mem[MEMSIZ];
Xint in_mem;		/* no. of valid bytes in mem */
X
Xvoid addranlib();	/* forward */
X
Xchar SYMDEF[] = "__.SYMDEF";
Xchar SYMREF[] = "__.SYMREF";
Xchar HEAD[] = SF_HEAD;
Xchar NAME[] = SF_NAME;
Xchar ARCH[] = "2211222";	/* really "41124" */
Xchar MAGIC[] = "2";
Xchar LONG[] = "4";
Xchar XDEF[] = "44";	/* xref also uses this */
X
X
X/*
X * a fatal error has occurred: output message, cleanup & exit
X */
Xvoid fatal(format, arg)
Xchar *format, *arg;
X{
X/*	fprintf(stderr, "%s: ", command);	*/
X	fprintf(stderr, format, arg);
X	putc('\n', stderr);
X	if (tmpfile)
X		unlink(tmpfile);
X	exit(1);
X}
X
Xvoid mem_error()
X{
X	fatal("out of memory");
X}
X
X/*==========================================================================*
X *				io package				    *
X *==========================================================================*/
X
X/*
X * the normal fwrite etc. stuff is too slow. the functions needed have been
X * rewritten.
X */
X
X/* the MFILE structure */
X
Xtypedef struct {
X	int _fd;
X	int _count;
X	long _posn;
X	char *_buf;
X	char *_ptr;
X	/*
X	 * mcreat() and mopen() are always called with a pointer to some
X	 * data that is not destroyed before closing the file. Therefore the
X	 * pointer can be kept in this structure, for better error messages.
X	 */
X	char *_file;
X} MFILE;
X
Xvoid read_error(fp)
XMFILE *fp;
X{
X	fatal("%s: read error", fp->_file);
X}
X
Xvoid write_error(fp)
XMFILE *fp;
X{
X	fatal("%s: write error", fp->_file);
X}
X
X# define mtell(fp)	(fp->_posn)
X
X/*
X * create a file. return NULL if could not create
X */
XMFILE *mcreat(file)
Xchar *file;
X{
X	register MFILE *fp;
X	int fd;
X
X	/* create file */
X	if ((fd = creat(file, 0644)) < 0)
X		return NULL;
X
X	/* allocate memory */
X	fp = (MFILE*) malloc(sizeof(MFILE) + BUFSIZ);
X	if (fp == NULL) mem_error();
X
X	/* fill in MFILE structure */
X	fp->_fd = fd;
X	fp->_count = 0;
X	fp->_posn = 0L;
X	fp->_ptr = fp->_buf = (char*) (fp+1);
X	fp->_file = file;
X
X	return fp;
X}
X
X/*
X * open a file. return NULL if could not open
X */
XMFILE *mopen(file)
Xchar *file;
X{
X	register MFILE *fp;
X	int fd;
X
X	/* open file */
X	if ((fd = open(file, 0)) < 0)
X		return NULL;
X
X	/* allocate memory */
X	fp = (MFILE*) malloc(sizeof(MFILE) + BUFSIZ);
X	if (fp == NULL) mem_error();
X
X	/* fill in MFILE structure */
X	fp->_fd = fd;
X	fp->_count = 0;
X	fp->_posn = 0L;
X	fp->_buf = (char*) (fp+1);	/* no need to set fp->_ptr here */
X	fp->_file = file;
X
X	return fp;
X}
X
X/*
X * read bytes. return 0 if no bytes could be read (EOF).
X */
Xint mread(fp, buf, n)
Xregister MFILE *fp;
Xregister char *buf;
Xregister int n;
X{
X	register int needed;
X
X	do {
X	    /* first read from buffer */
X	    if (fp->_count) {
X		needed = (fp->_count > n) ? n : fp->_count;
X		bcopy(fp->_ptr, buf, needed);
X		buf += needed;
X		n -= needed;
X		fp->_count -= needed;
X		fp->_ptr += needed;
X		fp->_posn += needed;
X	    }
X	    /* then read directly */
X	    if (n) {
X		needed = n - (n % BUFSIZ);
X		if (needed) {
X		    if (read(fp->_fd, buf, needed) != needed)
X			read_error(fp);
X		    buf += needed;
X		    n -= needed;
X		    fp->_posn += needed;
X		}
X
X		/* then fill buffer and try again */
X		if (n) {
X		    if ((needed = read(fp->_fd, fp->_buf, BUFSIZ)) == 0)
X			/* only at this point EOF is allowed */
X			return 0;	/* EOF */
X		    if (needed < n) read_error(fp);
X		    fp->_ptr = fp->_buf;
X		    fp->_count = needed;
X		}
X	    }
X	} while (n);
X
X	return 1;	/* success */
X}
X
X/*
X * write bytes to fp. No errors allowed
X */
Xvoid mwrite(fp, buf, n)
Xregister MFILE *fp;
Xregister char *buf;
Xregister int n;
X{
X	register int left;
X
X	do {
X		/* first fill buffer */
X		left = BUFSIZ - fp->_count;
X		if (left > n) left = n;
X		bcopy(buf, fp->_ptr, left);
X		buf += left;
X		n -= left;
X		fp->_count += left;
X		fp->_posn += left;
X		fp->_ptr += left;
X
X		/* empty the buffer */
X		if (fp->_count >= BUFSIZ) {
X			if (write(fp->_fd, fp->_buf, fp->_count) < 0)
X				write_error(fp);
X			fp->_count = 0;
X			fp->_ptr = fp->_buf;
X
X			/* then write directly */
X			left = n - (n % BUFSIZ);
X			if (left) {
X				if (write(fp->_fd, buf, left) < 0)
X					write_error(fp);
X				buf += left;
X				n -= left;
X				fp->_posn += left;
X			}
X		}
X	} while (n);
X}
X
X/*
X * mseek. how can only be 0 or 1, fp must be a file opened for reading
X */
Xvoid mseek(fp, offset, how)
Xregister MFILE *fp;
Xregister long offset;
Xint how;
X{
X	long lseek();
X
X	if (how == 0) offset -= fp->_posn;
X	fp->_posn += offset;
X	if (offset >= fp->_buf - fp->_ptr  &&  offset < fp->_count) {
X		fp->_count -= offset;
X		fp->_ptr += offset;
X	} else {
X		if (lseek(fp->_fd, fp->_posn, 0) < 0)
X			read_error(fp);
X		fp->_count = 0;
X	}
X}
X
X/*
X * close a file opened for reading (or writing, when called from mwclose)
X */
Xvoid mrclose(fp)
XMFILE *fp;
X{
X	close(fp->_fd);
X	free(fp);
X}
X
X/*
X * flush buffers and close a file (opened for writing).
X */
Xvoid mwclose(fp)
Xregister MFILE *fp;
X{
X	if (fp->_count && write(fp->_fd, fp->_buf, fp->_count) < 0)
X		write_error(fp);
X	mrclose(fp);
X}
X
X
X/*==========================================================================*
X *			8086 byte order conversion			    *
X *==========================================================================*/
X
X/*
X * read structure in reverse byte order from memory
X */
Xvoid get3210mem(ad, buf, sizes)
Xregister char *ad, *buf, *sizes;
X{
X	register int size;
X
X	while (size = *sizes++) {
X		size -= '0';
X		buf += size;
X		switch (size) {
X		case 4:
X			*--buf = *ad++;
X			*--buf = *ad++;
X		case 2:
X			*--buf = *ad++;
X		case 1:
X			*--buf = *ad++;
X		}
X		buf += size;
X	}
X}
X
X/*
X * write structure in reverse byte order to memory
X */
Xvoid put3210mem(buf, ad, sizes)
Xregister char *buf, *ad, *sizes;
X{
X	register int size;
X
X	while (size = *sizes++) {
X		size -= '0';
X		buf += size;
X		switch (size) {
X		case 4:
X			*ad++ = *--buf;
X			*ad++ = *--buf;
X		case 2:
X			*ad++ = *--buf;
X		case 1:
X			*ad++ = *--buf;
X		}
X		buf += size;
X	}
X}
X
X
X/*
X * read structure in reverse byte order from file
X */
Xvoid get3210(fp, buf, size, sizes)
XMFILE *fp;
Xchar *buf, *sizes;
Xint size;
X{
X	char convbuf[ sizeof(struct ar_hdr) ];	/* adjust to largest structure size */
X
X	if (mread(fp, convbuf, size) == 0) read_error(fp);
X	get3210mem(convbuf, buf, sizes);
X}
X
X/*
X * write structure in reverse byte order to file
X */
Xvoid put3210(fp, buf, size, sizes)
XMFILE *fp;
Xchar *buf, *sizes;
Xint size;
X{
X	char convbuf[ sizeof(struct ar_hdr) ];	/* adjust to largest structure size */
X
X	put3210mem(buf, convbuf, sizes);
X	mwrite(fp, convbuf, size);
X}
X
X
X/* some utilities */
X
X/*
X * return non-directory part of filename
X */
Xchar *nodir(file)
Xchar *file;
X{
X	register char *p;
X	char *rindex();
X
X	p = rindex(file, '/');
X	return (p)? p+1 : file;
X}
X
X/*
X * copy (part of) a file from sfp to dfp
X */
Xvoid copy(sfp, dfp, size)
Xregister MFILE *sfp, *dfp;
Xregister long size;
X{
X	/*
X	 * if in_mem is non-zero, then the object file is already loaded
X	 * there - no need to load it again. Otherwise, read it from sfp
X	 * and write it to dfp.
X	 * NOTE: depending on in_mem, the seek file posn is directly after
X	 * the header (in_mem == 0) or at the end (in_mem != 0) on entry
X	 * of this function. Afterwards it is always at the end.
X	 */
X	if (!in_mem) {		/* nothing in mem */
X		while (size > MEMSIZ) {
X			if (mread(sfp, mem, MEMSIZ) == 0) read_error(sfp);
X			mwrite(dfp, mem, MEMSIZ);
X			size -= MEMSIZ;
X		}
X		if (mread(sfp, mem, (int)size) == 0) read_error(sfp);
X	} else
X		in_mem = 0;	/* mem contains file */
X
X	mwrite(dfp, mem, (int)size);
X}
X
X/*==========================================================================*
X *			archive manipulating functions			    *
X *==========================================================================*/
X
X/*
X * All these different byte orders are very confusing... In object files, the
X * order is 3210 for longs. In archive headers, the order is 1032!
X * Why can't it just be 0123? (Sigh)
X */
X
X/*
X * read an archive header
X */
Xbool readheader(hdr, fp)
Xstruct ar_hdr *hdr;
Xregister MFILE *fp;
X{
X	if (mread(fp, hdr->ar_name, FNAMELEN) == 0)
X		return FALSE;	/* EOF */
X	get1032(fp, &hdr->ar_date, sizeof(*hdr)-FNAMELEN, ARCH);
X	return TRUE;
X}
X
X/*
X * write an archive header
X */
Xvoid writeheader(hdr, fp)
Xstruct ar_hdr *hdr;
Xregister MFILE *fp;
X{
X	mwrite(fp, hdr->ar_name, FNAMELEN);
X	put1032(fp, &hdr->ar_date, sizeof(*hdr)-FNAMELEN, ARCH);
X}
X
X/*
X * create an archive
X */
XMFILE *acreat(file)
Xchar *file;
X{
X	register MFILE *fp;
X	int magic = ARMAG;
X
X	fp = mcreat(file);
X	if (fp == NULL)
X		fatal("cannot create %s\n", file);
X	put3210(fp, &magic, sizeof(magic), MAGIC); /* insert magic number */
X	return fp;
X}
X
X/*
X * open an archive for reading. if is does not exist and (create == TRUE),
X * issue a 'creating ...' message if (message == TRUE).
X * if (create == FALSE) => error
X */
XMFILE *aopen(file, create, message)
Xchar *file;
Xbool create, message;
X{
X	register MFILE *fp;
X	int magic;
X	struct ar_hdr hdr;
X
X	fp = mopen(file);
X	if (fp == NULL)
X		if (create) {
X			if (message)
X				printf("%s: creating %s\n", command, file);
X		} else fatal("cannot open %s", file);
X	else {
X		get3210(fp, &magic, sizeof(magic), MAGIC);
X		if (magic != ARMAG)
X			fatal("%s: not in ar format", file);
X
X		/* skip __.SYMDEF header */
X		if (readheader(&hdr, fp))
X			mseek(fp, (!strncmp(SYMDEF, hdr.ar_name, FNAMELEN))
X					? hdr.ar_size	/* always even */
X					: (long) -sizeof(hdr),
X				1);
X
X		/* skip __.SYMREF header */
X		if (readheader(&hdr, fp))
X			mseek(fp, (!strncmp(SYMREF, hdr.ar_name, FNAMELEN))
X					? hdr.ar_size	/* always even */
X					: (long) -sizeof(hdr),
X				1);
X	}
X	return fp;
X}
X
X/*
X * copy a file from (archive or file) sfp to (archive) dfp
X */
Xvoid copyfile(hdr, sfp, dfp)
Xregister struct ar_hdr *hdr;
Xregister MFILE *sfp, *dfp;
X{
X	if (archive_size & 1) {		/* align */
X		archive_size++;
X		mwrite(dfp, "", 1);	/* there is a \0 hiding in there */
X	}
X	writeheader(hdr, dfp);
X	addranlib(hdr, mtell(dfp), sfp);
X	copy(sfp, dfp, hdr->ar_size);
X	archive_size += sizeof(*hdr) + hdr->ar_size;
X}
X
X/*
X * skip an archive entry
X */
Xvoid skip(hdr, fp)
Xstruct ar_hdr *hdr;
XMFILE *fp;
X{
X	mseek(fp, hdr->ar_size, 1);
X}
X
X/*
X * add a file to an archive. return TRUE if it was added, FALSE otherwise
X */
Xbool addfile(file, date, afp)
Xchar *file;
Xlong date;	/* only add if more recent than date */
XMFILE *afp;
X{
X	struct ar_hdr hdr;
X	struct stat buf;
X	register MFILE *fp;
X
X	if (stat(file, &buf) >= 0 &&
X	    (unsigned long)buf.st_mtime > (unsigned long)date) {
X
X		/* it exists and is up-to-date: copy info to hdr */
X		strncpy(hdr.ar_name, nodir(file), FNAMELEN);
X		hdr.ar_date = buf.st_mtime;
X		hdr.ar_uid = buf.st_uid;
X		hdr.ar_gid = buf.st_gid;
X		hdr.ar_mode = buf.st_mode;
X		hdr.ar_size = buf.st_size;
X		fp = mopen(file);
X		if (fp) {
X			/* can be read: put it in the archive */
X			copyfile(&hdr, fp, afp);
X			mwclose(fp);
X			return TRUE;
X		}
X	}
X	/* not a fatal error */
X	return FALSE;
X}
X
X/*
X * extract a file. return (file = extracted)
X */
Xbool extract(hdr, file, oldtime, afp)
Xregister struct ar_hdr *hdr;
Xregister char *file;
Xbool oldtime;
XMFILE *afp;
X{
X	register MFILE *fp;
X
X	fp = mcreat(file);
X	if (fp == NULL) {
X		/* not a fatal error */
X		fprintf(stderr, "cannot extract %s\n", file);
X		skip(hdr, afp);
X		return FALSE;
X	} else {
X		int uid;
X
X		copy(afp, fp, hdr->ar_size);
X		mwclose(fp);
X		chmod(file, hdr->ar_mode);
X		if (oldtime && ((uid = getuid()) == 0 || uid == hdr->ar_uid)) {
X			time_t timep[2];
X
X			timep[1] = timep[0] = hdr->ar_date;
X			utime(file, timep);
X		}
X		return TRUE;
X	}
X}
X
X/*
X * list an archive entry
X */
Xvoid list(hdr, verbose)
Xregister struct ar_hdr *hdr;
Xbool verbose;
X{
X	char modestring[10], *ctime();
X	register char *p;
X	register int shift;
X
X	if (verbose) {
X		/* construct modestring */
X		strcpy(modestring, "rwxrwxrwx");
X		for (p = modestring, shift = 0400; shift; p++, shift >>= 1) {
X			if (!(hdr->ar_mode & shift)) *p = '-';
X		}
X		printf("%s %3d/%-3d %7ld %.20s %.14s\n",
X			modestring,
X			hdr->ar_uid, hdr->ar_gid,
X			hdr->ar_size,
X			ctime(&hdr->ar_date) + 4,
X			hdr->ar_name);
X	} else {
X		printf("%.14s\n", hdr->ar_name);
X	}
X}
X
X/*==========================================================================*
X *				ranlib functions			    *
X *==========================================================================*/
X
Xstruct xdef {
X	long x_name;
X	long x_foff;
X} xdef[RANLIBSZ];			/* externally defined symbols */
Xlong xdsize;
Xchar xdstrings[RLXDEFSZ], *xdp = xdstrings;	/* xdef string table */
X
X/*
X * NOTE: the same structure is used for xdef and xref definitions
X */
Xstruct xdef xref[RANLIBSZ];		/* unresolved links */
Xlong xrsize;
Xchar xrstrings[RLXREFSZ], *xrp = xrstrings;	/* xref symbol table */
X
X/*
X * add external symbols from object file to ranlib header
X */
Xvoid addranlib(hdr, offset, fp)
Xstruct ar_hdr *hdr;
Xlong offset;
XMFILE *fp;
X{
X	struct outhead header;
X	struct outname name, *names;
X	char *strings;
X	long posn;
X
X	/* could check here if name ends in .o */
X
X	if (hdr->ar_size < sizeof(header)) return;
X
X	posn = mtell(fp);
X	if (hdr->ar_size <= MEMSIZ) {
X		/*
X		 * load whole object file in memory
X		 */
X		in_mem = hdr->ar_size;	/* set in_mem to show mem holds something */
X		mread(fp, mem, in_mem);
X
X		get3210mem(mem, &header, HEAD);
X		/*
X		 * return immediately if not object file
X		 */
X		if (header.oh_magic != O_MAGIC) return;
X
X		names = (struct outname*)(mem + OFF_NAME(header));
X	} else {
X		/*
X		 * load only header and symbol table in memory
X		 */
X		get3210(fp, &header, SZ_HEAD, HEAD);
X
X		/*
X		 * return immediately if not object file
X		 */
X		if (header.oh_magic != O_MAGIC) {
X			mseek(fp, posn, 0);
X			return;
X		}
X
X		if (header.oh_nname*SZ_NAME + header.oh_nchar > MEMSIZ)
X			mem_error();
X		mseek(fp, OFF_NAME(header) - SZ_HEAD, 1);
X		if (mread(fp, mem, (int)(header.oh_nname*SZ_NAME + header.oh_nchar)) == 0)
X			read_error(fp);
X		names = (struct outname*)mem;
X		mseek(fp, posn, 0);
X	}
X	strings = (char *)(names + header.oh_nname) - OFF_CHAR(header);
X
X	/*
X	 * Process xdefs and xrefs.
X	 * Offset is the offset of the object module in the library, the
X	 * __.SYMDEF (and maybe __.SYMREF) header not taken into account.
X	 */
X	while (header.oh_nname-- > 0) {
X	    get3210mem(names++, &name, NAME);
X	    if ((name.on_type & S_EXT)) {
X		register char *p = strings + name.on_foff;
X
X		if (name.on_type & S_TYP) {	/* XDEF */
X		    if (xdp + strlen(p) + 1 >= &xdstrings[RLXDEFSZ])
X			mem_error();
X
X		    strcpy(xdp, p);
X		    xdef[xdsize].x_name = xdp - xdstrings;
X		    xdef[xdsize++].x_foff = offset;
X		    xdp += strlen(xdp) + 1;
X
X		} else if (xrefflag) {		/* XREF */
X		    /*
X		     * usually, many object files in a library will refer
X		     * to the same symbols. To save space, don't put
X		     * identical strings in the xrstrings table.
X		     * You can do this for xdstrings also but it probably
X		     * isn't worth the trouble.
X		     */
X		    register char *txrp = xrstrings;
X
X		    for (txrp = xrstrings; ; txrp += strlen(txrp) + 1) {
X
X			if (!strcmp(p, txrp)) break;	/* old string */
X
X			if (txrp == xrp) {		/* new string */
X			    if ((xrp += strlen(p) + 1) >= &xrstrings[RLXREFSZ])
X				mem_error();
X			    strcpy(txrp, p);
X			    break;
X			}
X		    }
X
X		    xref[xrsize].x_name = txrp - xrstrings;
X		    xref[xrsize++].x_foff = offset;
X		}
X	    }
X	}
X}
X
X/*
X * put the ranlib header to fp
X */
Xvoid putranlib(fp)
Xregister MFILE *fp;
X{
X	long dsize, rsize, time();
X	register long tdsize, trsize, i;
X	register struct xdef *x;
X	struct xdef tx;
X	struct ar_hdr hdr;
X
X	/* dsize = size of xdstring table */
X	dsize = align(xdp - xdstrings);
X	/* tdsize = size of SYMDEF entry */
X	tdsize = 2*sizeof(long) + xdsize*sizeof(struct xdef) + dsize;
X
X	/* output header */
X	strncpy(hdr.ar_name, SYMDEF, FNAMELEN);
X	hdr.ar_date = time((long*)0)+30*60L;	/* 30 minutes ahead */
X	hdr.ar_uid = getuid();
X	hdr.ar_gid = getgid();
X	hdr.ar_mode = 0444;	/* r--r--r-- */
X	hdr.ar_size = tdsize;
X	writeheader(&hdr, fp);
X
X	if (xrefflag) {	/* XREF */
X		rsize = align(xrp - xrstrings);
X		trsize = 2*sizeof(long) + xrsize*sizeof(struct xdef) + rsize;
X		strncpy(hdr.ar_name, SYMREF, FNAMELEN);
X		hdr.ar_size = trsize;
X		trsize += sizeof(hdr);
X	} else {
X		trsize = 0;
X	}
X
X	/* output no. of xdefs */
X	put3210(fp, &xdsize, sizeof(long), LONG);
X
X	/* output xdefs */
X	x = xdef;
X	tdsize += trsize;
X	for (i = xdsize; i > 0; --i) {	/* convert to 8086 byte order */
X		tx = *x;
X		tx.x_foff += tdsize;
X		put3210mem(&tx, x++, XDEF);
X	}
X	mwrite(fp, xdef, (int)xdsize*sizeof(struct xdef));
X
X	/* output string table size */
X	put3210(fp, &dsize, sizeof(long), LONG);
X
X	/* output string table */
X	mwrite(fp, xdstrings, (int)dsize);
X
X	if (xrefflag) {
X		/* output header */
X		writeheader(&hdr, fp);
X
X		/* output no. of xrefs */
X		put3210(fp, &xrsize, sizeof(long), LONG);
X
X		/* output xrefs */
X		x = xref;
X		for (i = xrsize; i > 0; --i) {	/* convert to 8086 byte order */
X			tx = *x;
X			tx.x_foff += tdsize;
X			put3210mem(&tx, x++, XDEF);
X		}
X		mwrite(fp, xref, (int)xrsize*sizeof(struct xdef));
X
X		/* output string table size */
X		put3210(fp, &rsize, sizeof(long), LONG);
X
X		/* output string table */
X		mwrite(fp, xrstrings, (int)rsize);
X	}
X}
X
X/*==========================================================================*
X *				utilities				    *
X *==========================================================================*/
X
X/*
X * check if a filename is in a list.
X * return filename found, or zero
X */
Xchar *match(file, filelist)
Xregister char *file, *filelist[];
X{
X	while (*filelist) {
X		if (**filelist && !strncmp(file, nodir(*filelist), FNAMELEN)) {
X			return *filelist;
X		}
X		filelist++;
X	}
X	return NULL;
X}
X
X/*
X * copy one archive to another
X */
Xvoid copyfiles(sfp, dfp)
XMFILE *sfp, *dfp;
X{
X	copy(sfp, dfp, archive_size);
X}
X
X/*
X * show usage of ar command
X */
Xvoid usage()
X{
X	fprintf(stderr, "usage: %s [adrtxRclouv] archive [file] ...\n",
X		command);
X	exit(2);
X}
X
X/*==========================================================================*
X *				MAIN program				    *
X *==========================================================================*/
X
Xmain(argc, argv)
Xregister int argc;
Xregister char *argv[];
X{
X	char *options, *file;
X	register MFILE *afp, *tfp;
X	struct ar_hdr hdr;
X
X
X	command = *argv++;
X
X	/* process options */
X	if (argc < 3) usage();
X	options = *argv++;
X	if (*options == '-') options++;		/* skip '-' */
X	switch (*options++) {
X	case 'a':
X		addflag = TRUE;		/* add file to archive */
X		break;
X	case 'd':
X		deleteflag = TRUE;	/* delete file */
X		break;
X	case 'r':
X		replaceflag = TRUE;	/* replace file */
X		break;
X	case 't':
X		listflag = TRUE;	/* list archive */
X		readflag = TRUE;	/* just reading */
X		break;
X	case 'x':
X		extractflag = TRUE;	/* extract file */
X		readflag = TRUE;	/* just reading */
X		break;
X	default:
X		usage();
X	}
X	while (*options) switch (*options++) {
X	/* NEVER MIND about these flags given more than once */
X	case 'c':
X		creatflag = TRUE;	/* suppress 'creating ...' message */
X		break;
X	case 'o':
X		oldtimeflag = TRUE;	/* restore time on extraction */
X		break;
X	case 'u':
X		updateflag = TRUE;	/* update - used with r */
X		break;
X	case 'v':
X		verboseflag = TRUE;	/* verbose */
X		break;
X	case 'l':
X		localflag = TRUE;	/* tmpfile in current directory */
X		break;
X	case 'R':
X		xrefflag = TRUE;	/* add __.SYMREF header */
X		break;
X	default:
X		usage();
X	}
X
X	archive = *argv++;
X
X	/* safety check */
X	if (addflag || replaceflag || extractflag) {
X		register char **ap = argv;
X
X		/*
X		 * cannot add or extract this archive to/from itself
X		 */
X		while (*ap) {
X			if (!strcmp(archive, *ap)) {
X				**ap = '\0';
X				--argc;
X			}
X			ap++;
X		}
X	}
X
X	/* sanity check
X		- delayed until after safety check (which modifies argc) */
X
X	if (((creatflag | localflag | xrefflag) && readflag) ||
X	    (updateflag > replaceflag) ||
X	    (oldtimeflag > extractflag) ||
X	    (!readflag && argc == 3))
X		usage();
X
X	/* open archive */
X	afp = aopen(archive, addflag | replaceflag, !creatflag);
X
X	/* do the job */
X	if (!readflag) {
X		/* CHANGING THE ARCHIVE */
X		extern char *mktemp();
X
X		/* make tmparchive */
X		tmpfile = mktemp("/tmp/arXXXXXX" + ((localflag) ? 5 : 0));
X		tfp = acreat(tmpfile);
X
X		/* so far, no changes */
X		readflag = TRUE;
X
X		/* main loop */
X		if (afp) {
X		  while (readheader(&hdr, afp)) {
X		    if ( (file=match(hdr.ar_name, argv)) ) {
X
X			if (replaceflag && addfile(file,
X				(updateflag) ? hdr.ar_date : 0L,
X				tfp)) {
X			    readflag = FALSE;
X			    if (verboseflag) printf("r - %s\n", file);
X			}
X
X			else if (deleteflag) {
X			    readflag = FALSE;
X			    if (verboseflag) printf("d - %s\n", file);
X			}
X
X			if (addflag) {	/* double */
X			    copyfile(&hdr, afp, tfp);
X			    printf("%s: already in archive\n", file);
X			}
X			else skip(&hdr, afp);
X
X			*file = '\0';	/* mark as used */
X
X		    } else {
X			/* copy from archive to tmpfile */
X			copyfile(&hdr, afp, tfp);
X		    }
X		    if (hdr.ar_size & 1)	/* align */
X			mread(afp, hdr.ar_name, 1);
X		  }
X		  mrclose(afp);
X		}
X
X		/* add files to tmpfile - if nesseccary */
X		if (addflag || replaceflag) {
X		    register char **ap = argv;
X
X		    do {
X			if (**ap && addfile(*ap, 0L, tfp)) {
X			    readflag = FALSE;
X			    if (verboseflag)
X				printf("a - %s\n", *ap);
X			    **ap = '\0';	/* mark as used */
X			}
X		    } while (*++ap);
X		}
X
X		if (!readflag) {	/* changes were made */
X			/* close and reopen the tmpfile */
X			mwclose(tfp);
X			tfp = aopen(tmpfile, FALSE, FALSE);
X
X			/* creat new archive */
X			afp = acreat(archive);
X
X			/* output new archive */
X			putranlib(afp);
X			copyfiles(tfp, afp);
X			mwclose(afp);
X		}
X		mrclose(tfp);
X		unlink(tmpfile);
X
X	} else {
X		/* READING ARCHIVE */
X
X		file = NULL;
X		/* main loop */
X		while (readheader(&hdr, afp)) {
X
X		    if (argc == 3 || (file=match(hdr.ar_name, argv))) {
X			if (extractflag) {
X			    static char *ff, buf[FNAMELEN+1];
X			    /*
X			     * the file name must be \0-terminated, so copy
X			     * it to a buffer where this can be done
X			     */
X			    ff = (file)?
X				file :
X				strncpy(buf, hdr.ar_name, FNAMELEN);
X			    if (extract(&hdr, ff, oldtimeflag, afp) &&
X				verboseflag)
X			    	printf("x - %s\n", ff);
X			} else {
X			    list(&hdr, verboseflag);
X			    skip(&hdr, afp);
X			}
X		    } else skip(&hdr, afp);
X
X		    if (file) *file = '\0';	/* mark as used */
X		    if (hdr.ar_size & 1)	/* align */
X			mread(afp, hdr.ar_name, 1);
X		}
X		mrclose(afp);
X	}
X	/* ready */
X
X	/* warnings */
X	while (*argv) {
X		if (**argv) printf("%s: not found\n", *argv);
X		argv++;
X	}
X
X	exit(0);
X}
/
echo x - ar.h
gres '^X' '' > ar.h << '/'
X#define	ARMAG	0177454		/* or is it 0177545? */
X
Xstruct ar_hdr {
X	char	ar_name[14];
X	long	ar_date;
X	char	ar_uid;
X	char	ar_gid;
X	int	ar_mode;
X	long	ar_size;
X};
/
echo x - bcopy.c
gres '^X' '' > bcopy.c << '/'
Xbcopy(old, new, n)
Xregister char *old, *new;
Xint n;
X{
X/* Copy a block of data. */
X
X  while (n--) *new++ = *old++;
X}
/
echo x - strncpy.c
gres '^X' '' > strncpy.c << '/'
Xchar *strncpy(s1, s2, n)
Xregister char *s1, *s2;
Xint n;
X{
X/* Copy s2 to s1, but at most n characters. */
X
X  char *original = s1;
X
X  while (*s2 != 0) {
X	*s1++ = *s2++;
X	if (--n <= 0) break;
X  }
X  while (--n >= 0)
X	*s1++ = 0;
X  return(original);
X}
/
exit
--

			Felix Croes

+---------------------------------------|--------------------------------------+
| "GEM is dead -			|	     croes@fwi.uva.nl	       |
|		 long live Minix!"	|    croes%fwi.uva.nl@hp4nl.nluug.nl   |
+---------------------------------------|--------------------------------------+