Unofficial patch for Chew's ls 2.1

Piercarlo Grandi pcg at aber-cs.UUCP
Sat Jan 6 04:57:13 AEST 1990


Here are some patches for the free ls(1) clone (and its manual page)
by Earl Chew, as posted ato comp.os.minix, at patchlevel 2.1.

These patches add some BSD like behaviour, like option implicit command
name aliases, and displaying accessed and changed inode times, or sorting
by them, instead of modified. It will also automatically column if the
output is a tty. Some small bugs have also been corrected.

I have also added some of my own preferences for ls(1); they are two
distinct options, to use in *addition* or *alternative* to -l for
getting owner and group, printing owner and group back to back, and
removing the automatic assuming of option -A for the super user.

As this ls(1) clone stands now, it is as far as I can see a
virtually complete emulation of BSD and USG ls(1). It also
remains quite small and fast, which pleases me.

There remain a few improprieties; the sticky bit is displayed
in the long format listing as 's' instead of 't', and the
lockable property of a non executable file under System V is
not specially recognized at all. To me, these are very small
problems.

===================================================================
RCS file: ls.1,v
retrieving revision 2.1
retrieving revision 2.1.0.1
diff -c -r2.1 -r2.1.0.1
*** /tmp/,RCSt1a01715	Fri Jan  5 14:04:27 1990
--- /tmp/,RCSt2a01715	Fri Jan  5 14:04:27 1990
***************
*** 1,10 ****
! .TH LS 1 MINIX "" "Minix Programmer's Manual"
  .SH NAME
! ls - list contents of directory
  .SH SYNOPSIS
  .B ls
  [
! .B -abdfgilmnopqrstux1ACFLR
  ] [names]
  .SH DESCRIPTION
  For each directory argument,
--- 1,10 ----
! .TH LS 1
  .SH NAME
! ls, ll, lx, lf, lr, l - list contents of directory
  .SH SYNOPSIS
  .B ls
  [
! .B -abcdfgilmnopqrstux1ACFLR
  ] [names]
  .SH DESCRIPTION
  For each directory argument,
***************
*** 39,44 ****
--- 39,47 ----
  \-b
  Force printing of nongraphic characters to be in the octal \\ddd notation.
  .TP 4
+ \-c
+ Display the inode changed time and sort according to it, if requested.
+ .TP 4
  \-d
  If an argument is a directory, list only its name (not its contents).
  .TP 4
***************
*** 49,61 ****
  in the directory.
  .TP 4
  \-g
! The same as \-l, except that the group is printed but not the owner.
  .TP 4
  \-i
  For each file, print the inode number in the first column of the report.
  .TP 4
  \-l
! List in long format giving mode, number of links, owner, group, size in
  bytes, and time of last modification for each file. If the file is a special
  file, the size field will instead contain the major and minor device numbers
  rather than a size. If the file is a symbolic link, the link is printed
--- 52,64 ----
  in the directory.
  .TP 4
  \-g
! Print the group of the file.
  .TP 4
  \-i
  For each file, print the inode number in the first column of the report.
  .TP 4
  \-l
! List in long format giving mode, number of links, size in
  bytes, and time of last modification for each file. If the file is a special
  file, the size field will instead contain the major and minor device numbers
  rather than a size. If the file is a symbolic link, the link is printed
***************
*** 66,76 ****
  attempt to format it into even columns.
  .TP 4
  \-n
! The same as \-l, except that the owner's uid and group's gid numbers are
! printed, rather than the associated character strings.
  .TP 4
  \-o
! The same as \-l, except that the owner is not printed but not the group.
  .TP 4
  \-p
  Put a slash after each filename if that file is a directory.
--- 69,80 ----
  attempt to format it into even columns.
  .TP 4
  \-n
! The owner's uid and group's gid numbers are printed, rather
! than the associated character strings, if options \-o or \-g
! are specified.
  .TP 4
  \-o
! Print the owner of the file.
  .TP 4
  \-p
  Put a slash after each filename if that file is a directory.
***************
*** 83,93 ****
  oldest first as appropriate.
  .TP 4
  \-s
! Give the size in kilobytes, including indirect blocks, for each entry.
  .TP 4
  \-t
  Sort by time stamp (latest first) instead of by name. The time indicated
! is the last modification time.
  .TP 4
  \-x
  Multicolumn output with entries sorted across rather than down the page.
--- 87,100 ----
  oldest first as appropriate.
  .TP 4
  \-s
! Give the size in 512 byte sectors, for each entry.
  .TP 4
  \-t
  Sort by time stamp (latest first) instead of by name. The time indicated
! is the last modification time by default.
! .TP 4
! \-u
! Display the inode last accessed time and sort according to it, if requested.
  .TP 4
  \-x
  Multicolumn output with entries sorted across rather than down the page.
***************
*** 98,108 ****
  .TP 4
  \-A
  List all entries including those beginning with a period, except
! for . and .. which are not listed. This option is automatically turned on
! when root invokes ls.
  .TP 4
  \-C
! Multicolumn output with entries sorted down the columns.
  .TP 4
  \-F
  Cause directories to be marked with a trailing /, symbolic links
--- 105,117 ----
  .TP 4
  \-A
  List all entries including those beginning with a period, except
! for . and .. which are not listed. This option is
! .B not
! automatically turned on when root invokes ls.
  .TP 4
  \-C
! Multicolumn output with entries sorted down the columns. This option is
! automatically turned on if the output stream is a terminal.
  .TP 4
  \-F
  Cause directories to be marked with a trailing /, symbolic links
***************
*** 120,139 ****
  interpreted as follows:
  .PP
  The first character is:
! .br
  	d	if the entry is a directory
- .br
  	b	if the entry is a block special file
- .br
  	c	if the entry is a character special file
- .br
  	p	if the entry is a FIFO special file
- .br
  	l	if the entry is a symbolic link
- .br
  	s	if the entry is a socket
- .br
  	-	if the entry is an ordinary file
  .PP
  The next 9 characters are interpreted as three sets of three bits each. The
  first set refers to the owner's permissions; the next to permissions of
--- 129,144 ----
  interpreted as follows:
  .PP
  The first character is:
! .PP
! .nf
  	d	if the entry is a directory
  	b	if the entry is a block special file
  	c	if the entry is a character special file
  	p	if the entry is a FIFO special file
  	l	if the entry is a symbolic link
  	s	if the entry is a socket
  	-	if the entry is an ordinary file
+ .fi
  .PP
  The next 9 characters are interpreted as three sets of three bits each. The
  first set refers to the owner's permissions; the next to permissions of
***************
*** 144,164 ****
  specified file.
  .PP
  The permissions within each section are indicated as follows:
! .br
  	r	the file is readable
- .br
  	w	the file is writable
- .br
  	x	the file is executable
- .br
  	-	the indicated permission is not granted
! .br
! 	s	the set-uid or set-gid and the execute bit is on
! .br
  	S	as with s but the execute bit is off
  .PP
  When the sizes of the files in a directory are listed, a total count of
! kilobytes, including indirect blocks is printed.
  .SH ENVIRONMENT
  LSOPTS		default options for output to a terminal
  .SH FILES
--- 149,178 ----
  specified file.
  .PP
  The permissions within each section are indicated as follows:
! .PP
! .nf
  	r	the file is readable
  	w	the file is writable
  	x	the file is executable
  	-	the indicated permission is not granted
! 	s	the set-uid or set-gid (or sticky) and executable
  	S	as with s but the execute bit is off
+ .fi
  .PP
  When the sizes of the files in a directory are listed, a total count of
! sectors is printed.
! .PP
! This command may be invoked under various names; each name implies a default
! option, as follows:
! .PP
! .nf
! 	ll	\-l
! 	lx	\-x
! 	lc	\-C
! 	lf	\-F
! 	lr	\-R
! 	l	\-m
! .fi
  .SH ENVIRONMENT
  LSOPTS		default options for output to a terminal
  .SH FILES
===================================================================
RCS file: ls.c,v
retrieving revision 2.1
retrieving revision 2.1.0.2
diff -c -r2.1 -r2.1.0.2
*** /tmp/,RCSt1a01715	Fri Jan  5 14:04:29 1990
--- /tmp/,RCSt2a01715	Fri Jan  5 14:04:31 1990
***************
*** 43,49 ****
  #ifndef		_SYSV
  # ifndef	_BSD
  #  ifndef	_MINIX
! #   define 	_MINIX
  #  endif
  # endif
  #endif
--- 43,49 ----
  #ifndef		_SYSV
  # ifndef	_BSD
  #  ifndef	_MINIX
! #   define 	_SYSV
  #  endif
  # endif
  #endif
***************
*** 51,56 ****
--- 51,58 ----
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <errno.h>
+ # define strerror(n)	((n) < 1 || (n) > sys_nerr \
+ 			 ? "Unknown" : sys_errlist[n])
  #include <stdio.h>
  #ifdef		_BSD
  # include <sys/dir.h>
***************
*** 60,67 ****
  # define PATH_MAX	MAXPATHLEN
  # include <strings.h>
  # define strchr		index
- # define strerror(n)	((n) < 1 || (n) > sys_nerr \
- 			 ? "Unknown" : sys_errlist[n])
  # include <sys/time.h>
  #else
  # include <limits.h>
--- 62,67 ----
***************
*** 102,108 ****
  #define PARENT		".."		/* cd back to parent */
  #define ENVNAME		"LSOPTS"	/* ls options */
  #define COLUMNS		80		/* columns on terminal */
! #define INTERSPACE	2		/* spacing between columns */
  #define INODEWIDTH	5		/* width of field for inode */
  #define BLOCKWIDTH	4		/* width of field for blocks */
  
--- 102,108 ----
  #define PARENT		".."		/* cd back to parent */
  #define ENVNAME		"LSOPTS"	/* ls options */
  #define COLUMNS		80		/* columns on terminal */
! #define INTERSPACE	1		/* spacing between columns */
  #define INODEWIDTH	5		/* width of field for inode */
  #define BLOCKWIDTH	4		/* width of field for blocks */
  
***************
*** 224,229 ****
--- 224,231 ----
  void freeentry P((DIRENTRY *));		/* free an entry */
  int alphacmp P((DIRENTRY *, DIRENTRY *)); /* alphabetic comparison */
  int mtimecmp P((DIRENTRY *, DIRENTRY *)); /* compare by modification time */
+ int ctimecmp P((DIRENTRY *, DIRENTRY *)); /* compare by change time */
+ int atimecmp P((DIRENTRY *, DIRENTRY *)); /* compare by visit time */
  void list P((DIRENTRY *, DIRLIST, int, int)); /* file listing routine */
  void suffix P((DIRENTRY *));		/* do suffixes */
  int filestat P((int,
***************
*** 258,264 ****
  int (*status)() = STAT;			/* opaque status */
  int extrawidth = INTERSPACE;		/* implied extra space to add */
  char *incompatible[] = {		/* mutually exclusive flags */
!   "aA", "mx1C", "glno", "bq", "pF", (char *) NIL
  };
  
  int main(argc, argv)
--- 260,266 ----
  int (*status)() = STAT;			/* opaque status */
  int extrawidth = INTERSPACE;		/* implied extra space to add */
  char *incompatible[] = {		/* mutually exclusive flags */
!   "aA", "mx1C", "bq", "pF", (char *) NIL
  };
  
  int main(argc, argv)
***************
*** 277,283 ****
--- 279,289 ----
  /* Initialise by emptying the directory lists */
    EMPTY_dl(dlist), EMPTY_dl(nlist);
  
+   if (isatty(1))
+     SET('C');
+ 
  /* Parse the command line */
+   docmdname(argv[0]);
    dolsopts();
    parse(argc, argv);
    init();
***************
*** 296,308 ****
  
        dp = newentry(argv[optind]);
  
!       if (EVAL(dostat, TEST('t') || TEST('g') ||
! 		       TEST('o') || TEST('s') ||
  		       TEST('F') || TEST('p') ||
! 		       TEST('R') || TEST('i') ||
! 		       ! TEST('f') && ! TEST('d')) &&
! 	  dp->status == (struct stat *) NIL        &&
! 	  filestat(0, stat, dp->name,
  		   dp->status =
  		     (struct stat *) safemalloc(sizeof(*dp->status)))) {
  	freeentry(dp);
--- 302,312 ----
  
        dp = newentry(argv[optind]);
  
!       if (EVAL(dostat, TEST('l') ||
! 		       TEST('t') || TEST('s') ||
  		       TEST('F') || TEST('p') ||
! 		       TEST('R') || TEST('i') || !TEST('d')) &&
! 	  filestat(0, status, dp->name,
  		   dp->status =
  		     (struct stat *) safemalloc(sizeof(*dp->status)))) {
  	freeentry(dp);
***************
*** 309,318 ****
  	continue;
        }
  
!       if (EVAL(dodir, TEST('f') || ! TEST('d')) &&
! 	  (dp->status->st_mode & S_IFMT) == S_IFDIR) {
  
! 	free(dp->status);
  	dp->status = (struct stat *) NIL;
        
  	if (HEAD_dl(dlist))
--- 313,324 ----
  	continue;
        }
  
!       if (EVAL(dodir, !TEST('d'))
! 	    && dp->status
! 	    && (dp->status->st_mode & S_IFMT) == S_IFDIR) {
  
! 	if (dp->status)
! 	    free(dp->status);
  	dp->status = (struct stat *) NIL;
        
  	if (HEAD_dl(dlist))
***************
*** 353,359 ****
  
      HEAD_dl(nlist) = makelist(pathname, &width, &number, &kb);
  
!     if (EVAL(dototal, TEST('g') || TEST('o') || TEST('s')))
        (void) printf("total %lu\n", kb);
  
      list(dp, nlist, width, number);
--- 359,365 ----
  
      HEAD_dl(nlist) = makelist(pathname, &width, &number, &kb);
  
!     if (EVAL(dototal, TEST('l') || TEST('s')))
        (void) printf("total %lu\n", kb);
  
      list(dp, nlist, width, number);
***************
*** 362,367 ****
--- 368,403 ----
    return 0;
  }
  
+ 
+ /*
+  * Look at the last char of the command name. This may
+  * imply one of several options.
+  */
+ 
+ docmdname(name)
+   register char *name;
+ {
+   register char *cp;
+ 
+   for (cp = name; *cp != '\0'; cp++);
+   for (--cp; *cp != '/' && cp > name; --cp);
+   cp = (*cp == '/') ? cp+1 : cp;
+ 
+   /*
+   	Certain kinds of links (l, ll, lr, lf, lx) cause some
+   	various options to be turned on.
+   */
+   switch (cp[1])
+   {
+   case '\0': /* l -> -m */ SET('m'); break;
+   case 'l': /* ll -> -l */ SET('l'); break;
+   case 'x': /* lx => -x */ SET('x'); break;
+   case 'c': /* ll -> -l */ SET('C'); break;
+   case 'f': /* lf => -F */ SET('F'); break;
+   case 'r': /* lr => -R */ SET('R'); break;
+   }
+ }
+ 
  /*
   * Scan the options from the environment variable. This is
   * done in a similar fashion to the command line option
***************
*** 396,402 ****
  {
    int swch;				/* switch character */
    
!   while ((swch = getopt(argc, argv, "abdfgilmnopqrstx1ACFLR")) != EOF) {
      if (swch != '?')
        setflag(swch);
      else {
--- 432,438 ----
  {
    int swch;				/* switch character */
    
!   while ((swch = getopt(argc, argv, "abcdfgilmnopqrstux1ACFLR")) != EOF) {
      if (swch != '?')
        setflag(swch);
      else {
***************
*** 457,465 ****
--- 493,503 ----
  /* Get today's date */
    today = time((long *) 0);
  
+ #ifdef undef
  /* Turn on -A if we're the super user */
    if (! TEST('a') && getuid() == SUPER_USER)
      SET('A');
+ #endif
  
  /* Force raw directory listing */
    if (TEST('f')) {
***************
*** 480,495 ****
  
  /* Sort criterion */
    if (TEST('t'))
!     compare = mtimecmp;
    
  /* Open the password and group files if required */
    if (TEST('l') || TEST('n')) {
      SET('o');
      SET('g');
    }
  
  /* Long format forces output to be single column */
!   if (TEST('g') || TEST('o')) {
      CLEAR('m');
      CLEAR('x');
      CLEAR('C');
--- 518,538 ----
  
  /* Sort criterion */
    if (TEST('t'))
!     compare = TEST('u') ? atimecmp : TEST('c') ? ctimecmp : mtimecmp;
    
+ #ifdef undef
  /* Open the password and group files if required */
    if (TEST('l') || TEST('n')) {
      SET('o');
      SET('g');
    }
+ #endif
+ 
+   if (TEST('g') || TEST('o'))
+     SET('l');
  
  /* Long format forces output to be single column */
!   if (TEST('l')) {
      CLEAR('m');
      CLEAR('x');
      CLEAR('C');
***************
*** 545,552 ****
  
        p = newentry(cp->d_name);
  
!       if (EVAL(dostat, TEST('t') || TEST('g') ||
! 		       TEST('o') || TEST('s') ||
  		       TEST('F') || TEST('p') ||
  		       TEST('R') || TEST('i')) &&
  	  filestat(0, status, p->name,
--- 588,595 ----
  
        p = newentry(cp->d_name);
  
!       if (EVAL(dostat, TEST('l') ||
! 		       TEST('t') || TEST('s') ||
  		       TEST('F') || TEST('p') ||
  		       TEST('R') || TEST('i')) &&
  	  filestat(0, status, p->name,
***************
*** 562,568 ****
          *width = p->length;
        (*number)++;
  
!       if (EVAL(doblock, TEST('g') || TEST('o') || TEST('s')))
  	*kb += bytestokb(p->status);
        
        APPEND_dl(nlist, p);
--- 605,611 ----
          *width = p->length;
        (*number)++;
  
!       if (EVAL(doblock, TEST('l') || TEST('s')))
  	*kb += bytestokb(p->status);
        
        APPEND_dl(nlist, p);
***************
*** 809,815 ****
    if (TEST('s'))
      (void) printf("%*lu ", w ? BLOCKWIDTH : 0, bytestokb(p->status));
  
!   if (EVAL(dolong, TEST('o') || TEST('g')))
      longprint(p->status);
  
  /* Print the name of this file */
--- 852,858 ----
    if (TEST('s'))
      (void) printf("%*lu ", w ? BLOCKWIDTH : 0, bytestokb(p->status));
  
!   if (EVAL(dolong, TEST('l')))
      longprint(p->status);
  
  /* Print the name of this file */
***************
*** 848,879 ****
    char *prot[3];			/* protection ogu */
    static struct passwd *pwent = (struct passwd *) NIL; /* password entry */
    static struct group *grent  = (struct group *)  NIL; /* group entry */
!   static char *protmode[16] = {		/* protection modes */
      "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx",
!     "--S", "--s", "-wS", "-ws", "r-S", "r-s", "rwS", "rws"
    };
    static unsigned int setbits[3] = {	/* set on execution bits */
!     0, S_ISGID, S_ISUID
    };
    int i;				/* general counter */
  
!   filetype = sp->st_mode & S_IFMT;
!   if      (filetype == S_IFDIR)  filecode = 'd';
!   else if (filetype == S_IFBLK)  filecode = 'b';
!   else if (filetype == S_IFCHR)  filecode = 'c';
  #ifdef		S_IFIFO
!   else if (filetype == S_IFIFO)  filecode = 'p';
  #endif
  #ifdef		S_IFLNK
!   else if (filetype == S_IFLNK)  filecode = 'l';
  #endif
  #ifdef		S_IFSOCK
!   else if (filetype == S_IFSOCK) filecode = 's';
  #endif
!   else                           filecode = '-';
! 
!   for (i = 0, mode = sp->st_mode; i < 3; mode >>= 3, i++)
!     prot[i] = protmode[(mode & 07) + (sp->st_mode & setbits[i] ? 8 : 0)];
  
    (void) printf("%c%s%s%s %2d ",
                  filecode, prot[2], prot[1], prot[0], sp->st_nlink);
--- 891,927 ----
    char *prot[3];			/* protection ogu */
    static struct passwd *pwent = (struct passwd *) NIL; /* password entry */
    static struct group *grent  = (struct group *)  NIL; /* group entry */
!   static char *protmode[24] = {		/* protection modes */
      "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx",
!     "--S", "--s", "-wS", "-ws", "r-S", "r-s", "rwS", "rws",
    };
    static unsigned int setbits[3] = {	/* set on execution bits */
!     S_ISVTX, S_ISGID, S_ISUID
    };
    int i;				/* general counter */
  
!   for (i = 0, mode = sp->st_mode; i < 3; mode >>= 3, i++)
!     prot[i] = protmode[(mode & 07) + (sp->st_mode & setbits[i] ? 8 : 0)];
! 
!   switch (filetype = sp->st_mode & S_IFMT)
!   {
!   case S_IFDIR:		filecode = 'd';		break;
!   case S_IFBLK:		filecode = 'b';		break;
!   case S_IFCHR:		filecode = 'c';		break;
  #ifdef		S_IFIFO
!   case S_IFIFO:		filecode = 'p';		break;
  #endif
  #ifdef		S_IFLNK
!   case S_IFLNK:		filecode = 'l';		break;
  #endif
  #ifdef		S_IFSOCK
!   case S_IFSOCK:	filecode = 's';		break;
  #endif
! #ifdef		S_IFNAM
!   case S_IFNAM:		filecode = 'x';		break;
! #endif
!   default:		filecode = '-';		break;
!   }
  
    (void) printf("%c%s%s%s %2d ",
                  filecode, prot[2], prot[1], prot[0], sp->st_nlink);
***************
*** 881,897 ****
    if (TEST('o')) {
      if (! TEST('n') && ((pwent && pwent->pw_uid == sp->st_uid) ||
                          (pwent = getpwuid(sp->st_uid)) != (struct passwd *) NIL))
!       (void) printf("%-8.8s ", pwent->pw_name);
      else
!       (void) printf("%-8d ", sp->st_uid);
    }
  
    if (TEST('g')) {
      if (! TEST('n') && ((grent && grent->gr_gid == sp->st_gid) ||
                          (grent = getgrgid(sp->st_gid)) != (struct group *) NIL))
!       (void) printf("%-9.9s ", grent->gr_name);
      else
!       (void) printf("%-9d ", sp->st_gid);
    }
  
  /* Now show how big the file is */
--- 929,945 ----
    if (TEST('o')) {
      if (! TEST('n') && ((pwent && pwent->pw_uid == sp->st_uid) ||
                          (pwent = getpwuid(sp->st_uid)) != (struct passwd *) NIL))
!       (void) printf("%8.8s ", pwent->pw_name);
      else
!       (void) printf("%8d ", sp->st_uid);
    }
  
    if (TEST('g')) {
      if (! TEST('n') && ((grent && grent->gr_gid == sp->st_gid) ||
                          (grent = getgrgid(sp->st_gid)) != (struct group *) NIL))
!       (void) printf("%-8.8s ", grent->gr_name);
      else
!       (void) printf("%-8d ", sp->st_gid);
    }
  
  /* Now show how big the file is */
***************
*** 901,907 ****
      (void) printf("%2d,%3d ", (sp->st_rdev >> 8) & 0377, sp->st_rdev & 0377);
    
  /* Now the date */
!   date(sp->st_mtime);
  }
  
  /*
--- 949,955 ----
      (void) printf("%2d,%3d ", (sp->st_rdev >> 8) & 0377, sp->st_rdev & 0377);
    
  /* Now the date */
!   date(TEST('u') ? sp->st_atime : TEST('c') ? sp->st_ctime : sp->st_mtime);
  }
  
  /*
***************
*** 943,948 ****
--- 991,1000 ----
    return sp->st_blocks * STD_BLK / 1024;
  #endif
  
+ #ifdef		_SYSV
+   return (sp->st_size + 511) / 512;
+ #endif
+ 
  #ifdef		_MINIX
    unsigned long blocks;			/* accumulated blocks */
    unsigned long fileb;			/* size of file in blocks */
***************
*** 1127,1133 ****
  
  #ifdef		S_IFLNK
    link = (char *) NIL;
!   if (EVAL(dolink, TEST('o') || TEST('g')) &&
        (p->status->st_mode & S_IFMT) == S_IFLNK) {
      fstat = filestat(1, stat, link = followlink(p->name), &lsb);
      p->length += 4 + stringlength(link);
--- 1179,1185 ----
  
  #ifdef		S_IFLNK
    link = (char *) NIL;
!   if (EVAL(dolink, TEST('l')) &&
        (p->status->st_mode & S_IFMT) == S_IFLNK) {
      fstat = filestat(1, stat, link = followlink(p->name), &lsb);
      p->length += 4 + stringlength(link);

-- 
Piercarlo "Peter" Grandi           | ARPA: pcg%cs.aber.ac.uk at nsfnet-relay.ac.uk
Dept of CS, UCW Aberystwyth        | UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg at cs.aber.ac.uk



More information about the Comp.sources.bugs mailing list