Bourne | Ash |  #!  | find | ARG_MAX | Shells | portability | permissions | UUOC | ancient | - | ../Various | HOME
"$@" | echo/printf | set -e | test | tty defs | tty chars | $() vs ) | IFS | using siginfo | nanosleep | line charset | locale


Back to the main page.

I am glad to have found the following in the CSRG Archive CD-ROMs.

The source code was released under the licenses of BSD and Caldera.

Here are extracts from 4.0BSD /usr/src/sys/newsys/sys1.c, which is headed by a mail from Dennis M. Ritchie (some arbitrary emphasis in bold face)
(and for comparison see 4.0BSD /usr/src/sys/sys/sys1.c

Note: the mail mentions the original limit of 16 characters in research unix, but in 4.0BSD sizeof(struct u_exdata) was already 32.
(It was also 32 then in 4.2BSD, now explicitly defined as SHSIZE in sys/h/user.h.)


From uucp Thu Jan 10 01:37:58 1980
>From dmr Thu Jan 10 04:25:49 1980 remote from research
The system has been changed so that if a file being executed
begins with the magic characters #! , the rest of the line is understood
to be the name of an interpreter for the executed file.
Previously (and in fact still) the shell did much of this job;
it automatically executed itself on a text file with executable mode
when the text file's name was typed as a command.
Putting the facility into the system gives the following
benefits.

1) It makes shell scripts more like real executable files,
because they can be the subject of 'exec.'

2) If you do a 'ps' while such a command is running, its real
name appears instead of 'sh'.
Likewise, accounting is done on the basis of the real name.

3) Shell scripts can be set-user-ID.

4) It is simpler to have alternate shells available;
e.g. if you like the Berkeley csh there is no question about
which shell is to interpret a file.

5) It will allow other interpreters to fit in more smoothly.

To take advantage of this wonderful opportunity,
put

	#! /bin/sh

at the left margin of the first line of your shell scripts.
Blanks after ! are OK.  Use a complete pathname (no search is done).
At the moment the whole line is restricted to 16 characters but
this limit will be raised.


From uucp Thu Jan 10 01:37:49 1980
>From dmr Thu Jan 10 04:23:53 1980 remote from research

[...]


exece()
{
	register nc;
	register char *cp;
	register struct buf *bp;
	register struct execa *uap;
	int na, ne, bno, ucp, ap, c, indir, uid, gid;
	struct inode *ip;

        bno = 0;
        bp = 0;
        indir = 0;
        if ((ip = namei(uchar, 0)) == NULL)
                return;
        uid = u.u_uid;
        gid = u.u_gid;
        if (ip->i_mode&ISUID)
                uid = ip->i_uid;
        if (ip->i_mode&ISGID)
                gid = ip->i_gid;
again:
	if(access(ip, IEXEC))
		goto bad;
	if((ip->i_mode & IFMT) != IFREG ||
	   (ip->i_mode & (IEXEC|(IEXEC>>3)|(IEXEC>>6))) == 0) {
		u.u_error = EACCES;
		goto bad;
	}
	/*
	 * read in first few bytes
	 * of file for segment
	 * types and sizes:
	 * ux_mag = 407/410/411/405
	 *  407 is plain executable
	 *  410 is RO text
	 *  411 is separated ID
	 *  405 is overlaid text
	 *
	 * Also, an ascii line beginning
	 * with '#!' is the file name of a shell.
	 */
	u.u_base = (caddr_t)&u.u_exdata;
	u.u_count = sizeof(u.u_exdata);
	u.u_offset = 0;
	u.u_segflg = 1;
	readi(ip);
	u.u_segflg = 0;
	if(u.u_error)
		goto bad;
	if (u.u_count > sizeof(u.u_exdata) - sizeof(u.u_exdata.A)
	  && u.u_exdata.S[0] != '#') {
		u.u_error = ENOEXEC;
		goto bad;
	}
	if(u.u_exdata.A.ux_mag == 0407)
		;
	else if (u.u_exdata.A.ux_mag == 0411)
		;
	else if (u.u_exdata.A.ux_mag == 0405)
		;
	else if (u.u_exdata.A.ux_mag == 0410)
		;
	else if (u.u_exdata.S[0]=='#' && u.u_exdata.S[1]=='!' && indir==0) {
		cp = &u.u_exdata.S[2];
		while (*cp==' ' && cp<&u.u_exdata.S[SHSIZ])
			cp++;
		u.u_dirp = cp;
		while (cp < &u.u_exdata.S[SHSIZ-1]  && *cp != '\n')
			cp++;
		*cp = '\0';
		indir++;
		iput(ip);
		ip = namei(schar, 0);
		if (ip==NULL)
			return;
		goto again;
	} else {
		u.u_error = ENOEXEC;
		goto bad;
	}
[...]