summaryrefslogtreecommitdiff
path: root/libc/tests
diff options
context:
space:
mode:
Diffstat (limited to 'libc/tests')
-rw-r--r--libc/tests/Config2
-rw-r--r--libc/tests/Makefile20
-rw-r--r--libc/tests/README19
-rw-r--r--libc/tests/Real_make19
-rw-r--r--libc/tests/compr.c383
-rw-r--r--libc/tests/env.c82
-rw-r--r--libc/tests/ft.c1217
-rwxr-xr-xlibc/tests/grab.c80
-rw-r--r--libc/tests/hd.c90
-rw-r--r--libc/tests/line2.c15
-rw-r--r--libc/tests/lines.c36
-rwxr-xr-xlibc/tests/ls.c1049
-rw-r--r--libc/tests/ouch.c27
-rw-r--r--libc/tests/rand.c16
-rw-r--r--libc/tests/size.c51
-rw-r--r--libc/tests/sync.c1
-rw-r--r--libc/tests/ucomp.c108
-rw-r--r--libc/tests/wc.c133
18 files changed, 3348 insertions, 0 deletions
diff --git a/libc/tests/Config b/libc/tests/Config
new file mode 100644
index 0000000..4bdf884
--- /dev/null
+++ b/libc/tests/Config
@@ -0,0 +1,2 @@
+
+tools: These are tools to test libc - make directly
diff --git a/libc/tests/Makefile b/libc/tests/Makefile
new file mode 100644
index 0000000..37e57b8
--- /dev/null
+++ b/libc/tests/Makefile
@@ -0,0 +1,20 @@
+# Copyright (C) 1996 Robert de Bath <robert@mayday.compulink.co.uk>
+# This file is part of the Linux-8086 C library and is distributed
+# under the GNU Library General Public License.
+
+TOP=..
+include $(TOP)/Make.defs
+CFLAGS=$(CCFLAGS) -ansi
+
+default: all
+
+libc.a:
+ @echo -n
+
+include Real_make
+
+fetch_them:
+ cp -p $(SRC) Real_make $(TOPDIR)/tests/.
+
+clean:
+ rm -f $(OBJ) $(EXE) $(LINK_FILES)
diff --git a/libc/tests/README b/libc/tests/README
new file mode 100644
index 0000000..642e636
--- /dev/null
+++ b/libc/tests/README
@@ -0,0 +1,19 @@
+Copyright (C) 1996 Robert de Bath <robert@mayday.compulink.co.uk>
+This file is part of the Linux-8086 C library and is distributed
+under the GNU Library General Public License.
+
+These are user level tools, they're being used to test libc routines.
+
+env.c Prints the environment and arguments (Plus some junk)
+compr.c Mini compression program (rather slow at times)
+ucomp.c Mini uncompression program (Very fast)
+ft.c Multiple simple file tools.
+hd.c Hex dump.
+line2.c Print lines from /etc/passwd (stdio)
+lines.c Print lines from /etc/passwd
+ouch.c Signal test
+size.c Size of executables and object files.
+sync.c :-)
+wc.c Word count.
+
+-Robert
diff --git a/libc/tests/Real_make b/libc/tests/Real_make
new file mode 100644
index 0000000..38c4232
--- /dev/null
+++ b/libc/tests/Real_make
@@ -0,0 +1,19 @@
+# Copyright (C) 1996 Robert de Bath <robert@mayday.compulink.co.uk>
+# This file is part of the Linux-8086 C library and is distributed
+# under the GNU Library General Public License.
+
+SRC=env.c ft.c hd.c size.c sync.c compr.c ucomp.c ouch.c lines.c \
+ wc.c line2.c rand.c grab.c
+OBJ=
+EXE=env ft hd size sync compr ucomp ouch lines wc line2 rand grab
+
+LINK_FILES=cat chgrp chmod chown cp install ln mkdir mkfifo mknod mv rm
+
+all: $(EXE)
+
+links:
+ for i in $(LINK_FILES) ; do ln -s ft $$i ; done
+
+no_links:
+ rm -f $(LINK_FILES)
+
diff --git a/libc/tests/compr.c b/libc/tests/compr.c
new file mode 100644
index 0000000..8e53443
--- /dev/null
+++ b/libc/tests/compr.c
@@ -0,0 +1,383 @@
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <malloc.h>
+
+#define MAXNO 32767
+#define MAXLEN 127
+#define XXQSCAN /* Speed up scanning at the cost of not being optimal */
+
+unsigned char *fptr;
+unsigned short *vptr;
+FILE * fd;
+
+#define ITBSIZE 4096
+#define itbfunc() (ptr[mainscan]^(ptr[mainscan+1]<<4)^(ptr[mainscan+2]<<2))
+/*
+#define ITBSIZE 4001
+#define itbfunc() ((ptr[mainscan]+ptr[mainscan+1]*79+ptr[mainscan+2]*307)%4001)
+*/
+int * itb;
+
+int size;
+int maxno= 8000;
+long cnt=0;
+
+long icount = 0;
+long ocount = 0;
+
+unsigned char key;
+
+int fl;
+
+main(argc, argv)
+int argc;
+char ** argv;
+{
+ if( argc < 2 ) { fprintf(stderr, "Usage; ... \n"); exit(1); }
+
+ if( argc == 3 )
+ {
+ maxno = atoi(argv[2]);
+ if( maxno < 256 ) maxno = 256;
+ if( maxno > MAXNO) maxno = MAXNO;
+ }
+
+ if( strcmp(argv[1], "-") == 0 )
+ fd = stdin;
+ else
+ fd = fopen(argv[1], "r" );
+ if( fd == 0 ) { perror("Open failed\n"); exit(1); }
+
+ fptr = (unsigned char * ) malloc((unsigned)maxno*2);
+ itb = (int * ) malloc(ITBSIZE*sizeof(int));
+ if( itb )
+ vptr = (unsigned short * ) malloc((unsigned)maxno * sizeof(short)*2 );
+ else
+ vptr = 0;
+
+ if( fptr == 0 )
+ {
+ perror("Cannot allocate RAM");
+ exit(1);
+ }
+ if( vptr == 0 && itb ) free(itb);
+
+ fl = 0;
+ {
+ if( (size = fread(fptr, 1, (int)maxno, fd )) < 0 ) { fprintf(stderr, "\nRead failed\n"); exit(1); }
+
+ if( size )
+ {
+ icount += size;
+ if( fl == 0 )
+ {
+ key = scan_min();
+ putchar(key); ocount++;
+ fl = 1;
+ }
+ else
+ fprintf(stderr, "\rBlock %d \r", fl++ );
+ if( vptr) compress();
+ else slo_compress();
+ }
+ }
+
+ fprintf(stderr, "\n");
+ exit(0);
+}
+
+scan_min()
+{
+ long count[256];
+ long i;
+ int j, n;
+
+ for( j=0; j<256; j++ ) count[j] = 0;
+
+ for( i=0; i<size; i++) count[ fptr[i] & 0xFF ]++;
+
+ for( i= (((unsigned long) -1) >> 1), j=0; j<256; j++ )
+ if( count[j] < i )
+ {
+ i = count[j] ;
+ n = j;
+ }
+
+ fprintf(stderr, "Most unused in 0x%lx to 0x%lx is 0x%02x at %ld\n", cnt, cnt+size, n, i );
+ cnt+= size;
+
+ return n;
+}
+
+compress()
+{
+ register long mainscan;
+ register long secondscan;
+ register unsigned char * ptr = (unsigned char * ) fptr;
+ register int len;
+ register int matchlen;
+ long notepos;
+ long emark;
+#ifdef QSCAN
+ int count;
+#endif
+
+ for( mainscan=0; mainscan <ITBSIZE; itb[mainscan++] = -1 );
+
+ mainscan=0;
+ emark = size - 130 ;
+loopback:
+
+ for( ; mainscan < emark; )
+ {
+ matchlen = 3;
+ notepos = -1;
+#ifdef QSCAN
+ count = 0;
+#endif
+ for( secondscan=itb[itbfunc()];
+ secondscan >= 0 && mainscan - secondscan < maxno;
+ secondscan -= vptr[secondscan] )
+ {
+#ifdef DEBUG
+if( vptr[secondscan] == 0 )
+{
+ fprintf(stderr, "\nOh !!!!! mainsc %ld, sec-scan %ld\n", mainscan, secondscan);
+ vptr[secondscan] = secondscan+1;
+}
+#endif
+
+ for( len = 0; len < MAXLEN ; len++ )
+ if( mainscan+len >= size || ptr[mainscan+len] != ptr[secondscan+len] ) break;
+ if( len > matchlen && (len != 4 || mainscan - secondscan < 256 ) )
+ {
+ notepos = secondscan;
+ matchlen = len;
+ if( len == MAXLEN ) break;
+ }
+#ifdef QSCAN
+ if( matchlen > 20 && len > 3 && ++count > 5 )
+ break;
+#endif
+ }
+
+ if( notepos == -1 )
+ {
+ if( ptr[mainscan] == key )
+ {
+ ocount+=2;
+ putchar(key);
+ putchar(0);
+ }
+ else
+ {
+ ocount++;
+ putchar(ptr[mainscan]);
+ }
+ matchlen = 1;
+ }
+ else
+ {
+ long x = mainscan - notepos;
+ ocount+=3;
+ putchar(key);
+ if( x > 255 ) putchar(matchlen | 0x80);
+ else putchar(matchlen);
+ putchar((int)x);
+ if( x > 255 ) { putchar((int)x>>8); ocount++; }
+ }
+
+ while( matchlen-- )
+ {
+ len = itbfunc();
+ vptr[mainscan] = mainscan - itb[len];
+#if 1
+ if( vptr[mainscan] == 0 )
+ {
+ fprintf(stderr, "\nHumm.. ms=%ld, hash=%d, itb[hash]=%ld\n", mainscan, len, itb[len]);
+ vptr[mainscan] = mainscan+1;
+ }
+#endif
+ itb[len] = mainscan;
+ mainscan++;
+ }
+ }
+
+ fprintf(stderr, "\rBlock %d ..In:%ld Out:%ld \r", fl-1, icount, ocount );
+
+ if( emark < size-4 )
+ {
+ int cnt;
+ long l ;
+ if(mainscan > maxno )
+ {
+ for(cnt=0; cnt<ITBSIZE; cnt++)
+ {
+ if( itb[cnt] < maxno) itb[cnt] = -1;
+ else itb[cnt] -= maxno;
+ }
+ for(l=0; l<maxno; l++)
+ {
+ ptr[l] = ptr[l+maxno];
+ vptr[l] = vptr[l+maxno];
+ }
+ mainscan -= maxno;
+ size -= maxno;
+ }
+ if( size <= maxno )
+ {
+ if(( cnt = fread(ptr+size, 1, (int)maxno, fd)) < 0 )
+ { fprintf(stderr, "\nRead failed\n"); exit(1); }
+ size += cnt;
+ icount += cnt;
+ fprintf(stderr, "\rBlock %d \r", fl++ );
+ }
+ emark = size - 130;
+ if( mainscan >= emark )
+ emark = size -4;
+
+ goto loopback;
+ }
+
+ for( ; mainscan < size; )
+ {
+ if( ptr[mainscan] == key )
+ {
+ ocount+=2;
+ putchar(key);
+ putchar(0);
+ }
+ else
+ {
+ ocount++;
+ putchar(fptr[mainscan]);
+ }
+ mainscan++;
+ }
+ fprintf(stderr, "\rBlock %d ..In:%ld Out:%ld \r", fl-1, icount, ocount );
+ /* end */
+}
+
+slo_compress()
+{
+ register long mainscan;
+ register long secondscan;
+ register unsigned char * ptr = (unsigned char * ) fptr;
+ register int len;
+ register int matchlen;
+ long notepos;
+ long emark;
+#ifdef QSCAN
+ int count;
+#endif
+
+ mainscan=0;
+ emark = size - 130 ;
+loopback:
+
+ for( ; mainscan < emark; )
+ {
+ matchlen = 3;
+ notepos = -1;
+#ifdef QSCAN
+ count = 0;
+#endif
+ for( secondscan=mainscan-1;
+ secondscan >= 0 && mainscan - secondscan < maxno;
+ secondscan-- )
+ {
+ for( len = 0; len < MAXLEN ; len++ )
+ if( mainscan+len >= size || ptr[mainscan+len] != ptr[secondscan+len] ) break;
+ if( len > matchlen && (len != 4 || mainscan - secondscan < 256 ) )
+ {
+ notepos = secondscan;
+ matchlen = len;
+ if( len == MAXLEN ) break;
+ }
+#ifdef QSCAN
+ if( matchlen > 20 && len > 3 && ++count > 5 )
+ break;
+#endif
+ }
+
+ if( notepos == -1 )
+ {
+ if( ptr[mainscan] == key )
+ {
+ ocount+=2;
+ putchar(key);
+ putchar(0);
+ }
+ else
+ {
+ ocount++;
+ putchar(ptr[mainscan]);
+ }
+ matchlen = 1;
+ }
+ else
+ {
+ long x = mainscan - notepos;
+ ocount+=3;
+ putchar(key);
+ if( x > 255 ) putchar(matchlen | 0x80);
+ else putchar(matchlen);
+ putchar((int)x);
+ if( x > 255 ) { putchar((int)x>>8); ocount++; }
+ }
+
+ mainscan += matchlen;
+ }
+
+ fprintf(stderr, "\rBlock %d ..In:%ld Out:%ld \r", fl-1, icount, ocount );
+
+ if( emark < size-4 )
+ {
+ int cnt;
+ long l ;
+ if(mainscan > maxno )
+ {
+ for(l=0; l<maxno; l++)
+ {
+ ptr[l] = ptr[l+maxno];
+ }
+ mainscan -= maxno;
+ size -= maxno;
+ }
+ if( size <= maxno )
+ {
+ if(( cnt = fread(ptr+size, 1, (int)maxno, fd)) < 0 )
+ { fprintf(stderr, "\nRead failed\n"); exit(1); }
+ size += cnt;
+ icount += cnt;
+ fprintf(stderr, "\rBlock %d \r", fl++ );
+ }
+ emark = size - 130;
+ if( mainscan >= emark )
+ emark = size -4;
+
+ goto loopback;
+ }
+
+ for( ; mainscan < size; )
+ {
+ if( ptr[mainscan] == key )
+ {
+ ocount+=2;
+ putchar(key);
+ putchar(0);
+ }
+ else
+ {
+ ocount++;
+ putchar(fptr[mainscan]);
+ }
+ mainscan++;
+ }
+ fprintf(stderr, "\rBlock %d ..In:%ld Out:%ld \r", fl-1, icount, ocount );
+ /* end */
+}
+
diff --git a/libc/tests/env.c b/libc/tests/env.c
new file mode 100644
index 0000000..baeb8e9
--- /dev/null
+++ b/libc/tests/env.c
@@ -0,0 +1,82 @@
+
+char hex[] = "0123456789ABCDEF";
+
+char buf[20];
+main(argc, argv, envp)
+int argc;
+char ** argv;
+char ** envp;
+{
+ int i,j; char *p; char * str;
+ int * arg = &argc;
+
+ for(j=0; j<8; j++)
+ {
+ phex(arg);
+ putstr(":");
+ for(i=0; i<8; i++)
+ {
+ putstr(" ");
+ phex(*arg++);
+ }
+ putstr("\n");
+ }
+
+#if 0
+ str = alloca(sizeof(hex)+2);
+ putstr("Alloca = ");
+ phex(&str);
+ putstr(",");
+ phex(str);
+ putstr("\n");
+#endif
+
+ p = (char*) &argc;
+
+ putstr("ARGC="); phex(argc); putstr("\n");
+ for(i=0; i<argc; i++)
+ {
+ phex(argv[i]);
+ putstr(":");
+ putstr(argv[i]);
+ putstr("\n");
+ }
+ putstr("ENV=>\n");
+ for(; *envp; envp++)
+ {
+ phex(envp);
+ putstr(":");
+ phex(*envp);
+ putstr(":");
+ putstr(*envp);
+ putstr("\n");
+ }
+}
+
+phex(val)
+{
+ int i;
+ printf("%04x", val);
+}
+
+putstr(str)
+{
+ printf("%s", str);
+}
+
+#if 0
+int global_var_that_needs_init = 0x201;
+
+#asm
+ loc 1 ! Make sure the pointer is in the correct segment
+auto_func: ! Label for bcc -M to work.
+ .word _init_vars ! Pointer to the autorun function
+ .word no_op ! Space filler cause segs are padded to 4 bytes.
+ .text ! So the function after is also in the correct seg.
+#endasm
+
+static void init_vars()
+{
+ global_var_that_needs_init = getuid();
+}
+#endif
diff --git a/libc/tests/ft.c b/libc/tests/ft.c
new file mode 100644
index 0000000..6456d0b
--- /dev/null
+++ b/libc/tests/ft.c
@@ -0,0 +1,1217 @@
+/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
+ * This program is distributed under the GNU General Public License.
+ */
+
+/*
+ * File Tool, This program is a collection of basic file tools
+ * it includes cat, cp, ln, mkdir, mknod, chmod, chown, mv, rm
+ *
+ * Links may be used to call it under any of these names.
+ */
+#include <stdio.h>
+#ifdef __STDC__
+#include <unistd.h>
+#include <stdlib.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/param.h>
+#include <utime.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef __BCC__X
+#undef S_IFLNK
+#undef S_IFSOCK
+#endif
+
+#ifdef S_IFSOCK
+#include <sys/socket.h>
+#endif
+#ifndef S_IFLNK
+#define lstat stat
+#endif
+
+/* Ansi prototypes */
+#ifdef __STDC__
+#define PR(x) x
+#else
+#define PR(x) ()
+#endif
+
+void main PR((int argc, char ** argv));
+int select_command PR((char * argv));
+void do_prep PR((void));
+void do_post PR((void));
+void execute PR((char * dname, char * fname));
+int exec_for_subdir PR((char * dname));
+void exec_for_item PR((int when, char * fname));
+void parse_perms PR((char * prefix, char * ustring));
+int edit_mode PR((int mode, char * mode_str));
+int cmd_ft PR((char * fname));
+int cmd_mkfifo PR((char * fname));
+int cmd_mksock PR((char * fname));
+int cmd_rm PR((char * fname));
+void build_dest PR((char * dest, char * name, char * newpath));
+int strisdigit PR((char * str));
+int cmd_mv PR((char * fname));
+int cmd_ln PR((char * fname));
+int cmd_cp PR((char * fname));
+int copy_modes PR((char * file));
+int copy_file PR((char * source, char * dest));
+void Usage PR((void));
+int cmd_mkdir PR((char * dirname));
+int cmd_mknod PR((void));
+int warn_func PR((int enumber, char * estr, char * eobj));
+int error_func PR((int enumber, char * estr, char * eobj));
+
+#define warning(x,y,z) ( Line_no = __LINE__, warn_func(x,y,z))
+#define error(x,y,z) ( Line_no = __LINE__, error_func(x,y,z))
+int Line_no = -1;
+
+#define DO_BDIR 0x0010 /* Do Dir before contents */
+#define DO_ADIR 0x0020 /* Do Dir after contents */
+#define DO_MCOPY 0x0040 /* Preserve modes flag forced */
+#define OK_DIR 0x0080 /* Directorys OK even if no flg_recurse */
+#define IGN_LNK 0x0100 /* Not interested in symlinks */
+#define NO_SOURCE 0x0200 /* Named files created */
+#define OK_NO_SOURCE 0x0400 /* Don't need the source */
+
+#define CMD_FT (0+OK_DIR+DO_BDIR)
+#define CMD_CAT (1+IGN_LNK)
+#define CMD_CHGRP (2+OK_DIR+IGN_LNK+DO_ADIR)
+#define CMD_CHMOD (3+OK_DIR+IGN_LNK+DO_ADIR)
+#define CMD_CHOWN (4+OK_DIR+IGN_LNK+DO_ADIR)
+#define CMD_CP (5+IGN_LNK)
+#define CMD_EXTAR (6+DO_MCOPY+DO_BDIR)
+#define CMD_INSTALL (7+DO_MCOPY)
+#define CMD_LN (8+IGN_LNK+DO_BDIR)
+#define CMD_MKDIR (9+NO_SOURCE)
+#define CMD_MKFIFO (10+NO_SOURCE)
+#define CMD_MKSOCK (11+NO_SOURCE)
+#define CMD_MKNOD (12+NO_SOURCE)
+#define CMD_MV (13+DO_MCOPY+OK_DIR+DO_BDIR)
+#define CMD_RM (14+DO_ADIR)
+
+struct {
+ char * name;
+ int cmd;
+ int argpat;
+ char * opts;
+} command_list[] =
+{
+ { "ft", CMD_FT, 0, "-Rv" },
+ { "cat", CMD_CAT, 0, "uR" },
+ { "chgrp", CMD_CHGRP, 1, "vfR" },
+ { "chmod", CMD_CHMOD, 1, "vfR" },
+ { "chown", CMD_CHOWN, 1, "vfR" },
+ { "cp", CMD_CP, -1, "vifRrpsd" },
+ { "extar", CMD_EXTAR, 1, "" },
+ { "install", CMD_INSTALL, -1, "cdso:g:m:" },
+ { "ln", CMD_LN, -1, "vifs" },
+ { "mkdir", CMD_MKDIR, 0, "m:" },
+ { "mkfifo", CMD_MKFIFO, 0, "m:" },
+#ifdef S_IFSOCK
+ { "mksock", CMD_MKSOCK, 0, "m:" },
+#endif
+ { "mknod", CMD_MKNOD, 4, "m:" },
+ { "mv", CMD_MV, -1, "vif" },
+ { "rm", CMD_RM, 0, "vifr" },
+ { 0 }
+};
+
+int cmd_arg = 0;
+int cmd_tok = CMD_FT;
+char * cmd_opt = "-";
+char * cmd_string = 0; /* the first (or last) arg where special */
+char * prog_name = "";
+
+char ** flist = 0;
+int fcount = 0;
+int add_base=0;
+char * or_name = 0;
+int or_offset = 0;
+
+int flg_recurse = 0;
+int flg_verbose = 1;
+int flg_preserve= 0;
+int flg_mkpdir = 0;
+int flg_noderef = 0;
+int flg_symlink = 0;
+int flg_exestrip= 0;
+
+int flg_r, flg_force;
+char *str_o, *str_g, *str_m;
+
+/* Things to set on the new file */
+int set_user = -1;
+int set_group = -1;
+int set_mode = -1;
+time_t set_time = -1;
+char mode_str[32] = "";
+int u_mask = 0; /* 07777 altered by umask() */
+
+struct stat cur_file_stat;
+struct stat dest_item;
+struct stat access_stat;
+
+int done_something = 0;
+
+void
+main(argc, argv)
+int argc; char ** argv;
+{
+ int ar;
+ (void) select_command(argv[0]);
+
+ for(ar=1;
+ argv[ar] && argv[ar][0] == '-' && argv[ar][1];
+ ar++)
+ {
+ char * p = argv[ar]+1;
+ /* For symbolic changes of the form -rwx */
+ if( cmd_tok == CMD_CHMOD && strchr("rwx", *p) != 0 ) break;
+ while(*p)
+ {
+ char * ap=0, *av=0;
+ char ch;
+ /* Is it a valid opt for this cmd */
+ if(*p == ':' || (ap=strchr(cmd_opt, *p)) == 0) Usage();
+
+ /* Got an argument ? */
+ if(ap[1] == ':')
+ {
+ if(!argv[ar+1]) Usage();
+ av = argv[++ar];
+ }
+
+ if( (ch = *p) == '-' )
+ {
+ if( (ch=select_command(p)) < 0 ) Usage();
+ }
+ switch(ch)
+ {
+ case '\0': break;
+ case 'r':
+ case 'R': flg_recurse++; break;
+ case 'v': flg_verbose++; break;
+ case 'p': if(cmd_tok == CMD_MKDIR) flg_mkpdir++;
+ else flg_preserve++;
+ break;
+ case 'd': if(cmd_tok == CMD_INSTALL)
+ { flg_mkpdir++; cmd_arg=0; } /* Special mkdir */
+ else flg_noderef++; /* cmd_copy */
+ break;
+
+ case 'f': flg_force++; flg_verbose=0; break;
+ case 'o': str_o = av; break;
+ case 'g': str_g = av; break;
+ case 'm': str_m = av; break;
+
+ case 's': flg_symlink++;
+ if( cmd_tok == CMD_LN) cmd_tok |= OK_DIR+OK_NO_SOURCE;
+ break;
+ }
+ if(*p == '-') break;
+ p++;
+ }
+ }
+
+ switch(cmd_arg)
+ {
+ case 1:
+ if( ar >= argc ) Usage();
+ cmd_string = argv[ar++];
+ fcount = argc-ar;
+ flist = argv+ar;
+ break;
+ case 0:
+ fcount = argc-ar;
+ flist = argv+ar;
+ break;
+ case -1:
+ if( ar >= argc ) Usage();
+ cmd_string = argv[argc-1];
+ fcount = argc-ar-1;
+ flist = argv+ar;
+ break;
+ default:
+ if( ar != argc-cmd_arg ) Usage();
+ fcount = argc-ar;
+ flist = argv+ar;
+ break;
+ }
+
+ do_prep();
+
+ for(ar=0; ar<fcount; ar++)
+ {
+ done_something=1;
+ or_name = flist[ar]; or_offset = strlen(or_name)+1;
+ execute(flist[ar], (char*)0);
+ }
+
+ do_post();
+
+ if( !done_something )
+ {
+ if( cmd_tok == CMD_CAT )
+ execute("-", (char*)0);
+ else
+ Usage();
+ }
+ exit(0);
+}
+
+int select_command(argv)
+char * argv;
+{
+ int ar;
+ char *p, *s;
+ prog_name = argv;
+ for(ar=0; command_list[ar].name; ar++)
+ {
+ p = strrchr(argv, '-'); if(p) p++; else p=argv;
+ s = strrchr(p, '/'); if(s) s++; else s=p;
+ if( strcmp(s, command_list[ar].name) == 0 )
+ {
+ cmd_arg = command_list[ar].argpat;
+ cmd_tok = command_list[ar].cmd;
+ cmd_opt = command_list[ar].opts;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void do_prep()
+{
+ char * prefix = "::";
+
+ u_mask = umask(077);
+ umask(u_mask);
+ u_mask = (07777&(~u_mask));
+
+ if(cmd_tok&DO_MCOPY) flg_preserve++;
+ if(str_m) parse_perms(prefix, str_m);
+
+ switch(cmd_tok)
+ {
+ /* mknod is very different */
+ case CMD_MKNOD: cmd_mknod(); exit(0); break;
+
+ case CMD_CP:
+ if(strcmp(cmd_string, "-") == 0)
+ {
+ cmd_tok = CMD_CAT;
+ cmd_arg = 0;
+ break;
+ }
+ if(flg_symlink)
+ {
+ cmd_tok = CMD_LN+OK_DIR+OK_NO_SOURCE;
+ flg_preserve = 0;
+ }
+ break;
+
+ case CMD_CHOWN: prefix++;
+ case CMD_CHGRP: prefix++;
+ case CMD_CHMOD:
+ parse_perms(prefix, cmd_string);
+ set_time = 0;
+ break;
+ case CMD_INSTALL:
+ flg_exestrip = flg_symlink;
+ flg_symlink = 0;
+ if(str_o) parse_perms(prefix+2, str_o);
+ if(str_g) parse_perms(prefix+1, str_g);
+ if(flg_mkpdir) cmd_tok = CMD_MKDIR;
+ else
+ {
+ cmd_tok = CMD_CP;
+ flg_preserve = 1;
+ }
+ break;
+ }
+
+#ifndef S_IFLNK
+ if(flg_symlink)
+ {
+ error(0, "No support for symlinks available:", cmd_string);
+ exit(1);
+ }
+#endif
+
+ /* Are we transfering many to one ? Then it must be a directory */
+ if(cmd_arg == -1)
+ {
+ if( stat(cmd_string, &dest_item) == -1)
+ {
+ if( fcount > 1 )
+ {
+ if( cmd_mkdir(cmd_string) < 0 )
+ exit(1);
+ stat(cmd_string, &dest_item);
+ add_base = 1;
+ }
+ }
+ else
+ {
+ if( !S_ISDIR(dest_item.st_mode) )
+ {
+ if( fcount > 1 )
+ {
+ error(0, "Destination must be a directory:", cmd_string);
+ exit(1);
+ }
+ }
+ else add_base = 1;
+ }
+ }
+}
+
+void do_post()
+{
+ /* Oh! It seems there's nothing to do, ah well. */
+}
+
+void execute(dname, fname)
+char * dname; char * fname;
+{
+ char * buf;
+ if( strcmp(dname, "-") == 0 )
+ {
+ exec_for_item(0, dname);
+ return;
+ }
+ if( fname )
+ {
+ buf = alloca(strlen(dname) + strlen(fname) + 4);
+ if( buf == 0 )
+ {
+ error(errno, "Can't allocate memory for path beyond", dname);
+ return ;
+ }
+ strcpy(buf, dname);
+ if(strcmp(dname, "/")) strcat(buf, "/");
+ strcat(buf, fname);
+ }
+ else buf = dname;
+
+ if( lstat(buf, &cur_file_stat) == -1 )
+ {
+ if( cmd_tok&(NO_SOURCE|OK_NO_SOURCE) )
+ exec_for_item(0, buf);
+ else
+ warning(errno, "", buf);
+ return;
+ }
+ if( !flg_force && ( cmd_tok&NO_SOURCE ))
+ {
+ error(EEXIST, "", buf);
+ return;
+ }
+
+ if( S_ISDIR(cur_file_stat.st_mode))
+ {
+ if( (cmd_tok&OK_DIR) || flg_recurse )
+ (void) exec_for_subdir(buf);
+ else
+ error(EISDIR, "", buf);
+ return;
+ }
+
+#ifdef S_IFLNK
+ if( S_ISLNK(cur_file_stat.st_mode))
+ {
+ /* Links are special */
+ if( cmd_tok&IGN_LNK )
+ {
+ if( stat(buf, &cur_file_stat) == -1 )
+ {
+ warning(errno, "", buf);
+ return;
+ }
+ }
+ }
+#endif
+ exec_for_item(0, buf);
+}
+
+int exec_for_subdir(dname)
+char * dname;
+{
+ DIR * dfd;
+ struct dirent * ent;
+ int old_mode = -1;
+
+ if( cmd_tok&DO_BDIR ) exec_for_item(-1, dname);
+
+ if( flg_recurse )
+ {
+ dfd = opendir(dname);
+
+ if( dfd == 0 && errno == EACCES && flg_force )
+ {
+ old_mode = (cur_file_stat.st_mode & 07777);
+ if( chmod(dname, (0700|old_mode)) )
+ return error(errno, "Can't unlock", dname);
+
+ dfd = opendir(dname);
+ }
+ if( dfd == 0 ) return error(errno, "Can't open", dname);
+
+ while((ent=readdir(dfd)))
+ {
+ if( strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0 )
+ continue;
+
+ alloca(0); /* Free up if using fake version */
+ execute(dname, ent->d_name);
+ }
+ closedir(dfd);
+ if( old_mode != -1 )
+ chmod(dname, old_mode);
+ }
+
+ if( cmd_tok&DO_ADIR )
+ {
+ lstat(dname, &cur_file_stat);
+ exec_for_item(1, dname);
+ }
+ return 0;
+}
+
+void exec_for_item(when, fname)
+int when; char * fname;
+{
+ int rv = -1;
+ switch(cmd_tok)
+ {
+ case CMD_FT: rv = cmd_ft(fname); break;
+
+ case CMD_CAT: rv = copy_file(fname, "-"); break;
+
+ case CMD_CHGRP: /* And fall */
+ case CMD_CHMOD: /* And fall */
+ case CMD_CHOWN: rv = copy_modes(fname); break;
+
+ case CMD_CP: rv = cmd_cp(fname); break;
+ case CMD_MV: rv = cmd_mv(fname); break;
+ case CMD_RM: rv = cmd_rm(fname); break;
+
+ case CMD_EXTAR: error(ENOSYS, "", ""); exit(1);
+
+ case CMD_LN+OK_DIR+OK_NO_SOURCE:
+ case CMD_LN: rv = cmd_ln(fname); break;
+
+ case CMD_INSTALL: error(EINVAL, "", "Bad program"); exit(1);
+
+ case CMD_MKDIR: rv = cmd_mkdir(fname); break;
+ case CMD_MKFIFO: rv = cmd_mkfifo(fname); break;
+#ifdef S_IFSOCK
+ case CMD_MKSOCK: rv = cmd_mksock(fname); break;
+#endif
+ case CMD_MKNOD: break;
+ }
+}
+
+void parse_perms(prefix, ustring)
+char * prefix; char * ustring;
+{
+ char * userstr;
+ char * groupstr;
+ char * modestr;
+ char * cp;
+ struct passwd * pwd = 0;
+ struct group * grp;
+
+ userstr = alloca(strlen(prefix) + strlen(ustring) + 2);
+ strcpy(userstr, prefix);
+ strcat(userstr, ustring);
+
+ /* Select User */
+ cp = strchr(userstr, ':');
+ if(!cp) cp = strchr(userstr, '.');
+ if(cp) *cp = '\0';
+
+ /* If there's a user */
+ if( *userstr != 0 )
+ {
+ pwd = getpwnam(userstr);
+ if(pwd == NULL)
+ {
+ if(!strisdigit(userstr) )
+ {
+ error(EINVAL, "Unknown user", userstr);
+ exit(1);
+ }
+ set_user = atoi(userstr);
+ }
+ else set_user = pwd->pw_uid;
+ endpwent();
+ }
+ if(cp)
+ {
+ groupstr = cp+1;
+ cp = strchr(groupstr, ':');
+ if(!cp) cp = strchr(groupstr, '.');
+ if(cp) *cp = '\0';
+ if( *groupstr != '\0' )
+ {
+ grp = getgrnam(groupstr);
+ if(grp == NULL)
+ {
+ if(!strisdigit(groupstr) )
+ {
+ error(EINVAL, "Unknown group", groupstr);
+ exit(1);
+ }
+ set_group = atoi(groupstr);
+ }
+ else set_group = grp->gr_gid;
+ endgrent();
+ }
+ else if( pwd )
+ set_group = pwd->pw_gid;
+ }
+ if(cp)
+ {
+ modestr = cp+1;
+ if(strisdigit(modestr))
+ set_mode = strtol(modestr, NULL, 8);
+ else
+ {
+ strncpy(mode_str, modestr, sizeof(mode_str)-1);
+ /* This is the time that the mode change will fail on syn error */
+ (void) edit_mode(u_mask, mode_str);
+ }
+ }
+
+ if( set_user < 0 && set_group < 0 && set_mode < 0 && *mode_str == 0)
+ {
+ error(EINVAL, "", "Permission string has no changes");
+ exit(1);
+ }
+}
+
+int edit_mode(mode, mode_str)
+int mode; char * mode_str;
+{
+ char * str=mode_str;
+static mtab[] = {0, 0111, 0222, 0333, 0444, 0555, 0666, 0777 };
+
+ int done_change = 0;
+ int isdir = S_ISDIR(mode);
+ int change_op = 0;
+ int change_mask = u_mask;
+ int v=0, s=0, nm=0;
+
+ for(; *mode_str; mode_str++)
+ {
+ switch(*mode_str)
+ {
+ case ',': change_op = 0;
+ change_mask=u_mask; continue;
+ case '=': change_op = 1; if(0) {
+ case '+': change_op = 2; } if(0) {
+ case '-': change_op = 3; }
+ v=0; nm=0;
+ if(strchr(",=+-", mode_str[1]) == 0 ) continue;
+ break;
+ case 'a': if(change_op) goto ch_error;
+ nm |= 07777; if(0) {
+ case 'u': nm |= 04700; s= 6; } if(0) {
+ case 'g': nm |= 02070; s= 3; } if(0) {
+ case 'o': nm |= 01007; s= 0; }
+ if(change_op==0) { change_mask=nm; continue; }
+ v |= mtab[(mode>>s)&7];
+ break;
+ case 'r': v |= 0444; break;
+ case 'w': v |= 0222; break;
+ case 'x': v |= 0111; break;
+ case 's': v |=06000; break;
+ case 't': v |=01000; break;
+ case 'X': v |= mtab[isdir]; break;
+ default: goto ch_error;
+ }
+ switch(change_op)
+ {
+ case 0: goto ch_error;
+ case 1: mode= ((mode&(~change_mask)) | (v&change_mask));
+ break;
+ case 2: mode= ( mode | (v&change_mask));
+ break;
+ case 3: mode= ( mode & ~(v&change_mask));
+ break;
+ }
+ done_change=1;
+ }
+ if(!done_change)
+ {
+ch_error:
+ error(EINVAL, "Invalid mode string", str);
+ exit(1);
+ }
+ return mode;
+}
+
+int
+cmd_ft(fname)
+char * fname;
+{
+static char oldpath[2048] = "~";
+static int last_uid=-1, last_gid=-1, last_mode=-1;
+ struct passwd * pptr;
+ struct group * gptr;
+
+ if( flg_verbose>1 )
+ {
+ char *p = 0;
+ if( fname[1] ) p = strrchr(fname, '/');
+ if( p )
+ {
+ *p = '\0';
+ if( strcmp(fname, oldpath) != 0 )
+ {
+ strcpy(oldpath, fname);
+ printf("%s/\n", oldpath);
+ }
+ *p = '/';
+ }
+ else if( *oldpath )
+ *oldpath = '\0';
+ if(p) printf("%s", p+1);
+ else printf("%s", fname);
+
+#ifdef S_IFLNK
+ if( S_ISLNK(cur_file_stat.st_mode))
+ {
+ char linkbuf[1024];
+ int v;
+ *linkbuf='\0';
+ v = readlink(fname, linkbuf, sizeof(linkbuf));
+ if(v>0) linkbuf[v] = '\0';
+ printf("\t+%s", linkbuf);
+ }
+ else
+#endif
+ if( cur_file_stat.st_mode != last_mode
+ || cur_file_stat.st_uid != last_uid
+ || cur_file_stat.st_gid != last_gid)
+ {
+ printf("\t");
+ if( cur_file_stat.st_uid != last_uid )
+ {
+ pptr = getpwuid(cur_file_stat.st_uid);
+ if( pptr )
+ printf("%s", pptr->pw_name);
+ else
+ printf("%d", cur_file_stat.st_uid);
+ }
+ printf(":");
+ if( cur_file_stat.st_gid != last_gid )
+ {
+ gptr = getgrgid(cur_file_stat.st_gid);
+ if( gptr )
+ printf("%s", gptr->gr_name);
+ else
+ printf("%d", cur_file_stat.st_gid);
+ }
+ if( (cur_file_stat.st_mode&07777) != (last_mode&07777) )
+ printf(":%03o", cur_file_stat.st_mode & 07777);
+
+ switch(cur_file_stat.st_mode & S_IFMT)
+ {
+ case S_IFDIR: printf("\td"); break;
+ case S_IFIFO: printf("\tp"); break;
+#ifdef S_IFSOCK
+ case S_IFSOCK: printf("\ts"); break;
+#endif
+ case S_IFBLK: printf("\tb,%d,%d", cur_file_stat.st_rdev>>8,
+ cur_file_stat.st_rdev&0xFF);
+ break;
+ case S_IFCHR: printf("\tc,%d,%d", cur_file_stat.st_rdev>>8,
+ cur_file_stat.st_rdev&0xFF);
+ break;
+ }
+ last_mode = ((cur_file_stat.st_mode&07777)|S_IFREG);
+ if( (cur_file_stat.st_mode&07000) ) last_mode = -1;
+ last_uid = cur_file_stat.st_uid;
+ last_gid = cur_file_stat.st_gid;
+ }
+ printf("\n");
+ }
+ else printf("%s\n", fname);
+
+ return 0;
+}
+
+int
+cmd_mkfifo(fname)
+char * fname;
+{
+ int rv;
+ int mode=0666;
+ if( set_mode >= 0 ) mode=set_mode;
+ rv = mknod(fname, S_IFIFO|mode, 0);
+ if(rv<0)
+ warning(errno, "Cannot create fifo", fname);
+ return rv;
+}
+
+#ifdef S_IFSOCK
+int
+cmd_mksock(fname)
+char * fname;
+{
+ int rv, fd, len;
+ struct sockaddr *adr;
+
+ len = strlen(fname)+1 + sizeof(*adr) - sizeof(adr->sa_data);
+ if( len < sizeof(*adr) ) len = sizeof(*adr);
+
+ adr = alloca(len+2);
+ adr->sa_family = AF_UNIX;
+ strcpy(adr->sa_data, fname);
+
+ rv = fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if( fd>=0 ) rv = bind(fd, adr, len);
+ if( fd>=0 ) close(fd);
+ if(set_mode >= 0 && chmod(fname, set_mode&07777) < 0 )
+ warning(errno, "Chmod", fname);
+
+ if(rv<0)
+ warning(errno, "Cannot create socket", fname);
+ return rv;
+}
+#endif
+
+int
+cmd_rm(fname)
+char * fname;
+{
+ struct stat dirstat;
+ int rv;
+ char * buf, * p;
+
+ if( S_ISDIR(cur_file_stat.st_mode) )
+ if( !flg_recurse ) return error(EISDIR, "", fname);
+
+ if( S_ISDIR(cur_file_stat.st_mode) )
+ {
+ if( rmdir(fname) >= 0 ) return 0;
+ }
+ else
+ {
+ if( unlink(fname) >= 0 ) return 0;
+ }
+
+ if( !flg_force )
+ return error(errno, "", fname);
+
+ /* Try VERY hard */
+ buf = alloca(strlen(fname)+4);
+ strcpy(buf, fname);
+ p = strrchr(buf, '/');
+ if( p ) strcpy(p+1, "."); else strcpy(buf, ".");
+
+ if( stat(buf, &dirstat) < 0 ) return -1;
+ if( chmod(buf, dirstat.st_mode|0700) < 0 ) return -1;
+
+ if( S_ISDIR(cur_file_stat.st_mode) )
+ rv = rmdir(fname);
+ else
+ rv = unlink(fname);
+
+ chmod(buf, dirstat.st_mode);
+
+ return rv;
+}
+
+void
+build_dest(dest, name, newpath)
+char * dest; char * name; char * newpath;
+{
+ char * p;
+ strcpy(dest, newpath);
+ if( add_base )
+ {
+ strcat(dest, "/");
+ p = strrchr(or_name, '/');
+ if(p==0) strcat(dest, or_name);
+ else strcat(dest, p+1);
+ }
+ if(strlen(name) <= or_offset) return;
+ strcat(dest, name+or_offset);
+}
+
+int
+strisdigit(str)
+char * str;
+{
+ if( str==0 || *str == 0 ) return 0;
+
+ for(;*str; str++)
+ if(*str>'9'|| *str<'0') return 0;
+ return 1;
+}
+
+int
+cmd_mv(fname)
+char * fname;
+{
+ char * destfile;
+ destfile = alloca(strlen(fname)+strlen(cmd_string)+4);
+
+ build_dest(destfile, fname, cmd_string);
+
+ if( !flg_force && lstat(destfile, &access_stat) == 0 )
+ return error(EEXIST, "", destfile);
+
+ if( rename(fname, destfile) == 0 ) return 0;
+
+ if( errno != EXDEV )
+ return error(errno, "", fname);
+
+ if( S_ISDIR(cur_file_stat.st_mode) )
+ return error(EISDIR, "Can't rename across devices", fname);
+
+ if( copy_file(fname, destfile) != 0 ) return -1;
+ copy_modes(destfile);
+ return unlink(fname);
+}
+
+int
+cmd_ln(fname)
+char * fname;
+{
+ char * destfile;
+ destfile = alloca(strlen(fname)+strlen(cmd_string)+4);
+
+ build_dest(destfile, fname, cmd_string);
+
+ if( lstat(destfile, &access_stat) != -1 )
+ {
+ if( !flg_force ) return error(EEXIST, "", destfile);
+ cmd_rm(destfile);
+ }
+
+#ifdef S_IFLNK
+ if( flg_symlink )
+ {
+ if( symlink(fname, destfile) == 0 ) return 0;
+ }
+ else
+ {
+#endif
+ if( link(fname, destfile) == 0 ) return 0;
+#ifdef S_IFLNK
+ }
+#endif
+
+ return error(errno, "", destfile);
+}
+
+int
+cmd_cp(fname)
+char * fname;
+{
+ struct stat dest_stat;
+ char * destfile;
+ int no_dest = 0;
+
+ destfile = alloca(strlen(fname)+strlen(cmd_string)+4);
+
+ build_dest(destfile, fname, cmd_string);
+
+ if( stat(destfile, &dest_stat) >= 0 )
+ {
+ if( dest_stat.st_ino == cur_file_stat.st_ino
+ && dest_stat.st_dev == cur_file_stat.st_dev )
+ {
+ warning(EPERM, "Can't copy file to itself", fname);
+ return -1;
+ }
+ }
+ else no_dest = 1;
+
+ if( S_ISDIR(cur_file_stat.st_mode) )
+ {
+ if( !no_dest )
+ {
+ if( S_ISDIR(dest_stat.st_mode) ) return 0;
+ if( unlink(destfile) < 0 )
+ return error(errno, "Can't delete", destfile);
+ }
+ return cmd_mkdir(destfile);
+ }
+ else if( S_ISDIR(dest_stat.st_mode) )
+ return error(EPERM, "Can't copy non-directory to directory", destfile);
+ else if( S_ISREG(cur_file_stat.st_mode) )
+ {
+ /* Copy_ok - do we want to force a real file */;
+ if( flg_force && !no_dest && !S_ISREG(dest_stat.st_mode) )
+ cmd_rm(destfile);
+ }
+ else if( flg_recurse ) /* Don't copy other things while recursing */
+ {
+ return error(EPERM, "Can't copy", fname);
+ }
+
+ if( copy_file(fname, destfile) != 0 ) return -1;
+ if( flg_preserve ) copy_modes(destfile);
+ return 0;
+}
+
+int
+copy_modes(file)
+char * file;
+{
+ int user, group, mode;
+ /* chown turns off set[ug]id bits for non-root,
+ so do the chmod last. */
+
+ /* Try to copy the old file's modtime and access time. */
+ if(set_time)
+ {
+ struct utimbuf tv;
+
+ tv.actime = cur_file_stat.st_atime;
+ tv.modtime = cur_file_stat.st_mtime;
+ if( set_time != -1 )
+ tv.modtime = set_time;
+ if (utime (file, &tv) && !flg_force)
+ return error (errno, "", file);
+ }
+
+ /* Try to preserve ownership. For non-root it might fail, but that's ok.
+ But root probably wants to know, e.g. if NFS disallows it. */
+ user = cur_file_stat.st_uid; if(set_user>=0) user = set_user;
+ group = cur_file_stat.st_gid; if(set_group>=0) group = set_group;
+
+ if (chown (file, user, group)
+ && (errno != EPERM || geteuid() == 0 || (flg_preserve==0 && flg_force==0)))
+ error (errno, "Can't change perms for", file);
+
+ mode = cur_file_stat.st_mode;
+ if(set_mode>=0) mode=set_mode;
+ else if(*mode_str)
+ mode = edit_mode(mode, mode_str);
+
+ if (chmod (file, mode & 07777))
+ return error (errno, "", file);
+
+ return 0;
+}
+
+/* This copies from something to a file or stdout */
+/* If the source has zero blocks (possibly holes) the destination
+ * is built with holes (assuming it's a normal file) */
+
+int
+copy_file(source, dest)
+char * source; char * dest;
+{
+ char * buf;
+ int sfd, dfd;
+ struct stat st;
+ int blksz = BUFSIZ;
+ int cc;
+ char * ptr;
+ int hole_flag = 0;
+ int retv = 0;
+ int no_seek;
+ int mmode = 0666;
+
+ if(flg_verbose>1) printf("%s -> %s\n", source, dest);
+ if( strcmp(source, "-") == 0 )
+ sfd = 0;
+ else
+ {
+ sfd = open(source, O_RDONLY);
+ if(sfd<0) return error(errno, "", source);
+ mmode = (cur_file_stat.st_mode&0777);
+ }
+
+ if( strcmp(dest, "-") == 0 )
+ dfd = 1;
+ else
+ {
+ dfd = open(dest, O_WRONLY|O_TRUNC|O_CREAT, mmode);
+ if(dfd<0)
+ {
+ close(sfd);
+ return error(errno, "Cannot create", source);
+ }
+ }
+
+ if( fstat(dfd, &st) )
+ {
+ retv = error(errno, "", dest);
+ no_seek = 1;
+ }
+ else
+ {
+#ifndef __BCC__
+ blksz = st.st_blksize;
+#endif
+ no_seek = !S_ISREG(st.st_mode);
+ }
+ buf = alloca(blksz + sizeof(int));
+ if( buf == 0 ) return error(0, "Out of memory", "");
+
+ for(;;)
+ {
+ cc = read(sfd, buf, blksz);
+ if(cc<0)
+ {
+ retv = error(errno, "", source);
+ goto exit_now;
+ }
+ if(cc==0) break;
+ buf[cc] = 1;
+ for(ptr=buf; *ptr==0 ; ptr++) ;
+ if((hole_flag = (ptr == buf+cc)))
+ { /* Make a hole */
+ if( lseek(dfd, (off_t) cc, SEEK_CUR) < 0 )
+ {
+ retv = error(errno, "", dest);
+ goto exit_now;
+ }
+ }
+ else
+ {
+ if( cc != write(dfd, buf, cc))
+ {
+ retv = error(errno, "", dest);
+ goto exit_now;
+ }
+ }
+ }
+ if( hole_flag )
+ {
+ if( lseek(dfd, (off_t) -1, SEEK_CUR) < 0
+ || write(dfd, "", 1) != 1 )
+ {
+ retv = error(errno, "", dest);
+ goto exit_now;
+ }
+ }
+
+exit_now:
+ if(sfd>2) close(sfd);
+ if(dfd>2) close(dfd);
+ return retv;
+}
+
+void
+Usage()
+{
+ int i;
+
+ printf("FileTool Usage: %s%s", prog_name[0]=='-'?"ft -":"", prog_name);
+ if( cmd_tok == CMD_FT )
+ {
+ printf(" --[com_name] [-options] [files]\n");
+ printf("\nAvailable commands are:\n");
+ }
+
+ for(i=1; command_list[i].name; i++)
+ {
+ if( cmd_tok == CMD_FT )
+ printf(" %s --%s", prog_name, command_list[i].name);
+ else if( cmd_tok != command_list[i].cmd )
+ continue;
+
+ if( *command_list[i].opts )
+ printf(" [-%s]", command_list[i].opts);
+ switch(command_list[i].argpat)
+ {
+ case 1: printf(" <info> [files]"); break;
+ case -1: printf(" [files] [dest]"); break;
+ case 0: printf(" [files]"); break;
+ default: printf(" path [bcu] major minor"); break;
+ }
+ printf("\n");
+ }
+
+ exit(99);
+}
+
+int
+cmd_mkdir(dirname)
+char * dirname;
+{
+ int retv;
+ int mode = 0777;
+ if( set_mode >= 0 ) mode = set_mode;
+
+ retv = mkdir(dirname, mode);
+ if(retv<0)
+ {
+ if(flg_mkpdir && errno == ENOENT)
+ {
+ /* Create parents */
+ }
+ }
+ if( retv>=0 && cmd_tok == CMD_MKDIR )
+ {
+ if( set_user > 0 || set_group > 0 )
+ {
+ if( chown(dirname, set_user, set_group) < 0)
+ warning(errno, "Cannot change directory owner", dirname);
+ else if( chmod (dirname, mode & 07777) )
+ warning(errno, "", dirname);
+ }
+ }
+
+ if(retv<0) error(errno, "Cannot create directory", dirname);
+ return retv;
+}
+
+int
+cmd_mknod()
+{
+ int device;
+ int rv = -1;
+ int mode=0666;
+ if( set_mode >= 0 ) mode=set_mode;
+
+ device = (atoi(flist[2])<<8) + atoi(flist[3]);
+
+ if(flist[1][0] == 'b')
+ rv = mknod(flist[0], S_IFBLK|mode, device);
+ else if(flist[1][0] == 'c' || flist[1][0] == 'u')
+ rv = mknod(flist[0], S_IFCHR|mode, device);
+ else Usage();
+
+ if(rv<0)
+ {
+ error(errno, "", flist[0]);
+ exit(1);
+ }
+ return rv;
+}
+
+int
+warn_func(enumber, estr, eobj)
+int enumber; char * estr; char * eobj;
+{
+ if(flg_verbose)
+ return error_func(enumber, estr, eobj);
+ return 0;
+}
+
+int
+error_func(enumber, estr, eobj)
+int enumber; char * estr; char * eobj;
+{
+ fprintf(stderr, "%s%s(%d): ", prog_name[0]=='-'?"ft":"", prog_name, Line_no);
+ fprintf(stderr, "%s%s%s: %s\n", estr, (*estr?" ":""), eobj, strerror(enumber));
+ return -1;
+}
diff --git a/libc/tests/grab.c b/libc/tests/grab.c
new file mode 100755
index 0000000..bd62a0f
--- /dev/null
+++ b/libc/tests/grab.c
@@ -0,0 +1,80 @@
+
+#include <stdio.h>
+#include <malloc.h>
+
+struct s
+{
+ struct s * n;
+ char v[1];
+};
+
+#define M ((unsigned)-1>>1)
+#define V (M^(M>>1))
+
+main (argc,argv)
+int argc;
+char ** argv;
+{
+ struct s * ptr1 = 0;
+ struct s * ptr2;
+ struct s * ptr3;
+ int i,sz;
+ long total = 0;
+
+ for(i=0, sz=256 ; i<32; i++, sz = ((sz << 1) | (sz & V)) & M)
+ {
+ ptr2 = (struct s *) malloc(sz-sizeof(int));
+ printf("%2d(%8u)..%08lx..%ld\n",i,sz,(long)ptr2,(long)ptr2);
+ if(ptr2==0) break;
+ total+=sz;
+ if(ptr1==0)
+ {
+ ptr1 = ptr3 = ptr2;
+ ptr3->n = 0;
+ }
+ else
+ {
+ ptr3->n = ptr2;
+ ptr3 = ptr2;
+ ptr3->n = 0;
+ }
+ }
+ for(sz>>=1; sz>255; )
+ {
+ ptr2 = (struct s *) malloc(sz-sizeof(int));
+ if(ptr2==0) { sz >>=1; continue; }
+ printf("%2d(%8u)..%08lx..%ld\n",i++,sz,(long)ptr2,(long)ptr2);
+ total+=sz;
+ if(ptr1==0)
+ {
+ ptr1 = ptr3 = ptr2;
+ ptr3->n = 0;
+ }
+ else
+ {
+ ptr3->n = ptr2;
+ ptr3 = ptr2;
+ ptr3->n = 0;
+ }
+ }
+ printf("Free all - total was %ldK bytes\n", total/1024);
+ while( ptr1 )
+ {
+ ptr3 = ptr1->n;
+ free(ptr1);
+ ptr1 = ptr3;
+ }
+ ptr2 = (struct s *) malloc(200);
+ printf("%2d(%8u)..%08lx..%ld\n",i++,200,(long)ptr2,(long)ptr2);
+ ptr2 = (struct s *) malloc(30000);
+ printf("%2d(%8u)..%08lx..%ld\n",i++,30000,(long)ptr2,(long)ptr2);
+ ptr2 = (struct s *) malloc(20000);
+ printf("%2d(%8u)..%08lx..%ld\n",i++,20000,(long)ptr2,(long)ptr2);
+ sz = (256<<sizeof(int));
+ do
+ {
+ ptr2 = (struct s *) malloc(sz-sizeof(int));
+ printf("%2d(%8u)..%08lx..%ld\n",i++,sz,(long)ptr2,(long)ptr2);
+ }
+ while(ptr2 && i < 100);
+}
diff --git a/libc/tests/hd.c b/libc/tests/hd.c
new file mode 100644
index 0000000..f6af1f9
--- /dev/null
+++ b/libc/tests/hd.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <ctype.h>
+
+int lastnum[16] = { -1 };
+long lastaddr = -1;
+
+main(argc, argv)
+int argc;
+char ** argv;
+{
+ FILE * fd;
+ int j, ch;
+ char buf[20];
+ int num[16];
+ long offset = 0;
+
+#ifndef MSDOS
+ if( argc == 1 )
+ {
+ fd = stdin;
+ }
+ else
+#endif
+ {
+ if( argc == 3 ) offset = strtol(argv[2], (char*)0, 16);
+ else if( argc != 2 )
+ {
+ fprintf(stderr, "Usage: hd file [hexoffset]\n");
+ exit(1);
+ }
+ fd = fopen(argv[1], "rb");
+ if( fd == 0 )
+ {
+ fprintf(stderr, "Cannot open file '%s'\n", argv[1]);
+ exit(1);
+ }
+ }
+
+ /* if( offset ) fseek(fd, offset, 0); */
+
+ for(ch=0; ch!=EOF; offset+=16)
+ {
+ memset(buf, '\0', 16);
+ for(j=0; j<16; j++) num[j] = -1;
+ for(j=0; j<16; j++)
+ {
+ ch = fgetc(fd);
+ if( ch == EOF ) break;
+
+ num[j] = ch;
+ if( isascii(ch) && isprint(ch) ) buf[j] = ch;
+ else buf[j] = '.';
+ }
+ printline(offset, num, buf, ch==EOF);
+ }
+ fclose(fd);
+}
+
+printline(address, num, chr, eofflag)
+long address;
+int * num;
+char * chr;
+int eofflag;
+{
+ int j;
+
+ if( lastaddr >= 0 )
+ {
+ for(j=0; j<16; j++)
+ if( num[j] != lastnum[j] )
+ break;
+ if( j == 16 && !eofflag ) return;
+ if( lastaddr+16 != address )
+ printf("*\n");
+ }
+
+ lastaddr = address;
+ printf("%06lx:", address);
+ for(j=0; j<16; j++)
+ {
+ if( num[j] >= 0 )
+ printf(" %02x", num[j]);
+ else
+ printf(" ");
+ lastnum[j] = num[j];
+ num[j] = -1;
+ }
+
+ printf(" %.16s\n", chr);
+}
diff --git a/libc/tests/line2.c b/libc/tests/line2.c
new file mode 100644
index 0000000..6cc11ff
--- /dev/null
+++ b/libc/tests/line2.c
@@ -0,0 +1,15 @@
+
+#include <stdio.h>
+
+char buf[256];
+
+main()
+{
+ FILE * fd;
+ fd = fopen("/etc/passwd", "r");
+
+ while(fgets(buf, sizeof(buf), fd) != NULL)
+ {
+ printf(">>%s", buf);
+ }
+}
diff --git a/libc/tests/lines.c b/libc/tests/lines.c
new file mode 100644
index 0000000..6f3afb0
--- /dev/null
+++ b/libc/tests/lines.c
@@ -0,0 +1,36 @@
+
+#include <string.h>
+#include <fcntl.h>
+
+char *
+readline(fd)
+{
+static char linebuf[256];
+ int cc;
+ char * p;
+
+ cc = read(fd, linebuf, sizeof(linebuf)-1);
+ if( cc <= 0 ) return 0;
+ p = strchr(linebuf, '\n');
+ if( p == 0 ) p = linebuf+sizeof(linebuf)-1;
+ else
+ {
+ p++; lseek(fd, (long)(p-linebuf)-cc, 1);
+ }
+ *p = 0;
+ return linebuf;
+}
+
+main()
+{
+ int fd = open("/etc/passwd", O_RDONLY);
+ char * p;
+
+ if(fd<0) exit(1);
+
+ while( p=readline(fd) )
+ {
+ write(1, ">>", 2);
+ write(1, p, strlen(p));
+ }
+}
diff --git a/libc/tests/ls.c b/libc/tests/ls.c
new file mode 100755
index 0000000..8cae4d0
--- /dev/null
+++ b/libc/tests/ls.c
@@ -0,0 +1,1049 @@
+/* ls 3.2 - List files. Author: Kees J. Bot
+ *
+ * About the amount of bytes for heap + stack under Minix:
+ * Ls needs a average amount of 42 bytes per unserviced directory entry, so
+ * scanning 10 directory levels deep in an ls -R with 100 entries per directory
+ * takes 42000 bytes of heap. So giving ls 10000 bytes is tight, 20000 is
+ * usually enough, 40000 is pessimistic.
+ */
+
+/* Compile with the proper -D flag for your system:
+ *
+ * _MINIX Minix (1.5 or later)
+ * BSD BSD derived (has st_blocks)
+ * AMOEBA Amoeba's emulation of UNIX
+ */
+
+/* The array _ifmt[] is used in an 'ls -l' to map the type of a file to a
+ * letter. This is done so that ls can list any future file or device type
+ * other than symlinks, without recompilation. (Yes it's dirty.)
+ */
+char _ifmt[] = "0pcCd?bB-?l?s???";
+
+#define ifmt(mode) _ifmt[((mode) >> 12) & 0xF]
+
+#define nil 0
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef AMOEBA
+#undef S_IFLNK /* Liars */
+#endif
+#include <dirent.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <fcntl.h>
+#if BSD || __minix_vmd
+#include <termios.h>
+#endif
+#if __minix_vmd
+#include <sys/ioctl.h>
+#endif
+
+#ifndef major
+#define major(dev) ((int) (((dev) >> 8) & 0xFF))
+#define minor(dev) ((int) (((dev) >> 0) & 0xFF))
+#endif
+
+#if !_MINIX
+#define SUPER_ID uid /* Let -A flag be default for SUPER_ID == 0. */
+#else
+#define SUPER_ID gid
+#endif
+
+#ifdef S_IFLNK
+int (*status)(const char *file, struct stat *stp);
+#else
+#define status stat
+#endif
+
+/* Basic disk block size is 512 except for one niche O.S. */
+#if _MINIX
+#define BLOCK 1024
+#else
+#define BLOCK 512
+#endif
+
+/* Some terminals ignore more than 80 characters on a line. Dumb ones wrap
+ * when the cursor hits the side. Nice terminals don't wrap until they have
+ * to print the 81st character. Wether we like it or not, no column 80.
+ */
+#ifdef TIOCGWINSZ
+int ncols= 79;
+#else
+#define ncols 79
+#endif
+
+#define NSEP 2 /* # spaces between columns. */
+
+#ifdef TIOCGWINSZ
+#define MAXCOLS 150
+#else
+#define MAXCOLS (1 + (ncols / (1+NSEP))) /* Max # of files per line. */
+#endif
+
+char *arg0; /* Last component of argv[0]. */
+int uid, gid; /* callers id. */
+int ex= 0; /* Exit status to be. */
+int istty; /* Output is on a terminal. */
+
+/* Safer versions of malloc and realloc: */
+
+void heaperr(void)
+{
+ fprintf(stderr, "%s: Out of memory\n", arg0);
+ exit(-1);
+}
+
+void *allocate(size_t n)
+/* Deliver or die. */
+{
+ void *a;
+
+ if ((a= malloc(n)) == nil) heaperr();
+ return a;
+}
+
+#define reallocate rllct /* Same as realloc under some compilers. */
+
+void *reallocate(void *a, size_t n)
+{
+ if ((a= realloc(a, n)) == nil) heaperr();
+ return a;
+}
+
+char allowed[] = "acdfgilnqrstu1ACFLMRTX";
+char flags[sizeof(allowed)];
+
+char arg0flag[] = "cfmrtx"; /* These in argv[0] go to upper case. */
+
+void setflags(char *flgs)
+{
+ int c;
+
+ while ((c= *flgs++) != 0) {
+ if (strchr(allowed, c) == nil) {
+ fprintf(stderr, "Usage: %s -[%s] [file ...]\n",
+ arg0, allowed);
+ exit(1);
+ } else
+ if (strchr(flags, c) == nil)
+ flags[strlen(flags)] = c;
+ }
+}
+
+int present(int f)
+{
+ return f == 0 || strchr(flags, f) != nil;
+}
+
+void report(char *f)
+/* Like perror(3), but in the style: "ls: junk: No such file or directory. */
+{
+ fprintf(stderr, "%s: %s: %s\n", arg0, f, strerror(errno));
+ ex= 1;
+}
+
+/* Two functions, uidname and gidname, translate id's to readable names.
+ * All names are remembered to avoid searching the password file.
+ */
+#define NNAMES (1 << (sizeof(int) + sizeof(char *)))
+enum whatmap { PASSWD, GROUP };
+
+struct idname { /* Hash list of names. */
+ struct idname *next;
+ char *name;
+ uid_t id;
+} *uids[NNAMES], *gids[NNAMES];
+
+char *idname(unsigned id, enum whatmap map)
+/* Return name for a given user/group id. */
+{
+ struct idname *i;
+ struct idname **ids= &(map == PASSWD ? uids : gids)[id % NNAMES];
+
+ while ((i= *ids) != nil && id < i->id) ids= &i->next;
+
+ if (i == nil || id != i->id) {
+ /* Not found, go look in the password or group map. */
+ char *name= nil;
+ char noname[3 * sizeof(uid_t)];
+
+ if (!present('n')) {
+ if (map == PASSWD) {
+ struct passwd *pw= getpwuid(id);
+
+ if (pw != nil) name= pw->pw_name;
+ } else {
+ struct group *gr= getgrgid(id);
+
+ if (gr != nil) name= gr->gr_name;
+ }
+ }
+ if (name == nil) {
+ /* Can't find it, weird. Use numerical "name." */
+ sprintf(noname, "%u", id);
+ name= noname;
+ }
+
+ /* Add a new id-to-name cell. */
+ i= allocate(sizeof(*i));
+ i->id= id;
+ i->name= allocate(strlen(name) + 1);
+ strcpy(i->name, name);
+ i->next= *ids;
+ *ids= i;
+ }
+ return i->name;
+}
+
+#define uidname(uid) idname((uid), PASSWD)
+#define gidname(gid) idname((gid), GROUP)
+
+/* Path name construction, addpath adds a component, delpath removes it.
+ * The string path is used throughout the program as the file under examination.
+ */
+
+char *path; /* Path name constructed in path[]. */
+int plen= 0, pidx= 0; /* Lenght/index for path[]. */
+
+void addpath(int *didx, char *name)
+/* Add a component to path. (name may also be a full path at the first call)
+ * The index where the current path ends is stored in *pdi.
+ */
+{
+ if (plen == 0) path= (char *) allocate((plen= 32) * sizeof(path[0]));
+
+ if (pidx == 1 && path[0] == '.') pidx= 0; /* Remove "." */
+
+ *didx= pidx; /* Record point to go back to for delpath. */
+
+ if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/';
+
+ do {
+ if (*name != '/' || pidx == 0 || path[pidx-1] != '/') {
+ if (pidx == plen)
+ path= (char *) reallocate((void *) path,
+ (plen*= 2) * sizeof(path[0]));
+ path[pidx++]= *name;
+ }
+ } while (*name++ != 0);
+
+ --pidx; /* Put pidx back at the null. The path[pidx++]= '/'
+ * statement will overwrite it at the next call.
+ */
+}
+
+#define delpath(didx) (path[pidx= didx]= 0) /* Remove component. */
+
+int field = 0; /* (used to be) Fields that must be printed. */
+ /* (now) Effects triggered by certain flags. */
+
+#define F_INODE 0x001 /* -i */
+#define F_BLOCKS 0x002 /* -s */
+#define F_EXTRA 0x004 /* -X */
+#define F_MODE 0x008 /* -lMX */
+#define F_LONG 0x010 /* -l */
+#define F_GROUP 0x020 /* -g */
+#define F_BYTIME 0x040 /* -tuc */
+#define F_ATIME 0x080 /* -u */
+#define F_CTIME 0x100 /* -c */
+#define F_MARK 0x200 /* -F */
+#define F_TYPE 0x400 /* -T */
+#define F_DIR 0x800 /* -d */
+
+struct file { /* A file plus stat(2) information. */
+ struct file *next; /* Lists are made of them. */
+ char *name; /* Null terminated name. */
+ ino_t ino;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ nlink_t nlink;
+ dev_t rdev;
+ off_t size;
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+#if BSD
+ long blocks;
+#endif
+};
+
+void setstat(struct file *f, struct stat *stp)
+{
+ f->ino= stp->st_ino;
+ f->mode= stp->st_mode;
+ f->nlink= stp->st_nlink;
+ f->uid= stp->st_uid;
+ f->gid= stp->st_gid;
+ f->rdev= stp->st_rdev;
+ f->size= stp->st_size;
+ f->mtime= stp->st_mtime;
+ f->atime= stp->st_atime;
+ f->ctime= stp->st_ctime;
+#if BSD
+ f->blocks= stp->st_blocks;
+#endif
+}
+
+#define PAST (26*7*24*3600L) /* Half a year ago. */
+/* Between PAST and FUTURE from now a time is printed, otherwise a year. */
+#define FUTURE (15*60L) /* Fifteen minutes. */
+
+static char *timestamp(struct file *f)
+/* Transform the right time field into something readable. */
+{
+ struct tm *tm;
+ time_t t;
+ static time_t now;
+ static int drift= 0;
+ static char date[] = "Jan 19 2038";
+ static char month[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+
+ t= f->mtime;
+ if (field & F_ATIME) t= f->atime;
+ if (field & F_CTIME) t= f->ctime;
+
+ tm= localtime(&t);
+ if (--drift < 0) { time(&now); drift= 50; } /* limit time() calls */
+
+ if (t < now - PAST || t > now + FUTURE) {
+ sprintf(date, "%.3s %2d %4d",
+ month + 3*tm->tm_mon,
+ tm->tm_mday,
+ 1900 + tm->tm_year);
+ } else {
+ sprintf(date, "%.3s %2d %02d:%02d",
+ month + 3*tm->tm_mon,
+ tm->tm_mday,
+ tm->tm_hour, tm->tm_min);
+ }
+ return date;
+}
+
+char *permissions(struct file *f)
+/* Compute long or short rwx bits. */
+{
+ static char rwx[] = "drwxr-x--x";
+
+ rwx[0] = ifmt(f->mode);
+ /* Note that rwx[0] is a guess for the more alien file types. It is
+ * correct for BSD4.3 and derived systems. I just don't know how
+ * "standardized" these numbers are.
+ */
+
+ if (field & F_EXTRA) { /* Short style */
+ int mode = f->mode, ucase= 0;
+
+ if (uid == f->uid) /* What group of bits to use. */
+ /* mode<<= 0, */
+ ucase= (mode<<3) | (mode<<6);
+ /* Remember if group or others have permissions. */
+ else
+ if (gid == f->gid)
+ mode<<= 3;
+ else
+ mode<<= 6;
+
+ rwx[1]= mode&S_IRUSR ? (ucase&S_IRUSR ? 'R' : 'r') : '-';
+ rwx[2]= mode&S_IWUSR ? (ucase&S_IWUSR ? 'W' : 'w') : '-';
+
+ if (mode&S_IXUSR) {
+ static char sbit[]= { 'x', 'g', 'u', 's' };
+
+ rwx[3]= sbit[(f->mode&(S_ISUID|S_ISGID))>>10];
+ if (ucase&S_IXUSR) rwx[3] += 'A'-'a';
+ } else
+ rwx[3]= f->mode&(S_ISUID|S_ISGID) ? '=' : '-';
+ rwx[4]= 0;
+ } else { /* Long form. */
+ char *p= rwx+1;
+ int mode= f->mode;
+
+ do {
+ p[0] = (mode & S_IRUSR) ? 'r' : '-';
+ p[1] = (mode & S_IWUSR) ? 'w' : '-';
+ p[2] = (mode & S_IXUSR) ? 'x' : '-';
+ mode<<= 3;
+ } while ((p+=3) <= rwx+7);
+
+ if (f->mode&S_ISUID) rwx[3]= f->mode&(S_IXUSR>>0) ? 's' : '=';
+ if (f->mode&S_ISGID) rwx[6]= f->mode&(S_IXUSR>>3) ? 's' : '=';
+ if (f->mode&S_ISVTX) rwx[9]= f->mode&(S_IXUSR>>6) ? 't' : '=';
+ }
+ return rwx;
+}
+
+void numeral(int i, char **pp)
+{
+ char itoa[3*sizeof(int)], *a=itoa;
+
+ do *a++ = i%10 + '0'; while ((i/=10) > 0);
+
+ do *(*pp)++ = *--a; while (a>itoa);
+}
+
+#define K 1024L /* A kilobyte counts in multiples of K */
+#define T 1000L /* A megabyte in T*K, a gigabyte in T*T*K */
+
+char *cxsize(struct file *f)
+/* Try and fail to turn a 32 bit size into 4 readable characters. */
+{
+ static char siz[] = "1.2m";
+ char *p= siz;
+ off_t z;
+
+ siz[1]= siz[2]= siz[3]= 0;
+
+ if (f->size <= 5*K) { /* <= 5K prints as is. */
+ numeral((int) f->size, &p);
+ return siz;
+ }
+ z= (f->size + K-1) / K;
+
+ if (z <= 999) { /* Print as 123k. */
+ numeral((int) z, &p);
+ *p = 'k'; /* Can't use 'K', looks bad */
+ } else
+ if (z*10 <= 99*T) { /* 1.2m (Try ls -X /dev/at0) */
+ z= (z*10 + T-1) / T; /* Force roundup */
+ numeral((int) z / 10, &p);
+ *p++ = '.';
+ numeral((int) z % 10, &p);
+ *p = 'm';
+ } else
+ if (z <= 999*T) { /* 123m */
+ numeral((int) ((z + T-1) / T), &p);
+ *p = 'm';
+ } else { /* 1.2g */
+ z= (z*10 + T*T-1) / (T*T);
+ numeral((int) z / 10, &p);
+ *p++ = '.';
+ numeral((int) z % 10, &p);
+ *p = 'g';
+ }
+ return siz;
+}
+
+/* Transform size of file to number of blocks. This was once a function that
+ * guessed the number of indirect blocks, but that nonsense has been removed.
+ */
+#if BSD
+#define nblocks(f) ((f)->blocks)
+#else
+#define nblocks(f) (((f)->size + BLOCK-1) / BLOCK)
+#endif
+
+/* From number of blocks to kilobytes. */
+#if BLOCK < 1024
+#define nblk2k(nb) (((nb) + (1024 / BLOCK - 1)) / (1024 / BLOCK))
+#else
+#define nblk2k(nb) ((nb) * (BLOCK / 1024))
+#endif
+
+static int (*CMP)(struct file *f1, struct file *f2);
+static int (*rCMP)(struct file *f1, struct file *f2);
+
+static void mergesort(struct file **al)
+/* This is either a stable mergesort, or thermal noise, I'm no longer sure.
+ * It must be called like this: if (L != nil && L->next != nil) mergesort(&L);
+ */
+{
+ /* static */ struct file *l1, **mid; /* Need not be local */
+ struct file *l2;
+
+ l1= *(mid= &(*al)->next);
+ do {
+ if ((l1= l1->next) == nil) break;
+ mid= &(*mid)->next;
+ } while ((l1= l1->next) != nil);
+
+ l2= *mid;
+ *mid= nil;
+
+ if ((*al)->next != nil) mergesort(al);
+ if (l2->next != nil) mergesort(&l2);
+
+ l1= *al;
+ for (;;) {
+ if ((*CMP)(l1, l2) <= 0) {
+ if ((l1= *(al= &l1->next)) == nil) {
+ *al= l2;
+ break;
+ }
+ } else {
+ *al= l2;
+ l2= *(al= &l2->next);
+ *al= l1;
+ if (l2 == nil) break;
+ }
+ }
+}
+
+int namecmp(struct file *f1, struct file *f2)
+{
+ return strcmp(f1->name, f2->name);
+}
+
+int mtimecmp(struct file *f1, struct file *f2)
+{
+ return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1;
+}
+
+int atimecmp(struct file *f1, struct file *f2)
+{
+ return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1;
+}
+
+int ctimecmp(struct file *f1, struct file *f2)
+{
+ return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1;
+}
+
+int typecmp(struct file *f1, struct file *f2)
+{
+ return ifmt(f1->mode) - ifmt(f2->mode);
+}
+
+int revcmp(struct file *f1, struct file *f2) { return (*rCMP)(f2, f1); }
+
+static void sort(struct file **al)
+/* Sort the files according to the flags. */
+{
+ if (!present('f') && *al != nil && (*al)->next != nil) {
+ CMP= namecmp;
+
+ if (!(field & F_BYTIME)) {
+ /* Sort on name */
+
+ if (present('r')) { rCMP= CMP; CMP= revcmp; }
+ mergesort(al);
+ } else {
+ /* Sort on name first, then sort on time. */
+
+ mergesort(al);
+ if (field & F_CTIME)
+ CMP= ctimecmp;
+ else
+ if (field & F_ATIME)
+ CMP= atimecmp;
+ else
+ CMP= mtimecmp;
+
+ if (present('r')) { rCMP= CMP; CMP= revcmp; }
+ mergesort(al);
+ }
+ /* Separate by file type if so desired. */
+
+ if (field & F_TYPE) {
+ CMP= typecmp;
+ mergesort(al);
+ }
+ }
+}
+
+struct file *newfile(char *name)
+/* Create file structure for given name. */
+{
+ struct file *new;
+
+ new= (struct file *) allocate(sizeof(*new));
+ new->name= strcpy((char *) allocate(strlen(name)+1), name);
+ return new;
+}
+
+void pushfile(struct file **flist, struct file *new)
+/* Add file to the head of a list. */
+{
+ new->next= *flist;
+ *flist= new;
+}
+
+void delfile(struct file *old)
+/* Release old file structure. */
+{
+ free((void *) old->name);
+ free((void *) old);
+}
+
+struct file *popfile(struct file **flist)
+/* Pop file off top of file list. */
+{
+ struct file *f;
+
+ f= *flist;
+ *flist= f->next;
+ return f;
+}
+
+int dotflag(char *name)
+/* Return flag that would make ls list this name: -a or -A. */
+{
+ if (*name++ != '.') return 0;
+
+ switch (*name++) {
+ case 0: return 'a'; /* "." */
+ case '.': if (*name == 0) return 'a'; /* ".." */
+ default: return 'A'; /* ".*" */
+ }
+}
+
+int adddir(struct file **aflist, char *name)
+/* Add directory entries of directory name to a file list. */
+{
+ DIR *d;
+ struct dirent *e;
+
+ if (access(name, 0) < 0) {
+ report(name);
+ return 0;
+ }
+
+ if ((d= opendir(name)) == nil) {
+ report(name);
+ return 0;
+ }
+ while ((e= readdir(d)) != nil) {
+ if (e->d_ino != 0 && present(dotflag(e->d_name))) {
+ pushfile(aflist, newfile(e->d_name));
+ aflist= &(*aflist)->next;
+ }
+ }
+ closedir(d);
+ return 1;
+}
+
+off_t countblocks(struct file *flist)
+/* Compute total block count for a list of files. */
+{
+ off_t cb = 0;
+
+ while (flist != nil) {
+ switch (flist->mode & S_IFMT) {
+ case S_IFDIR:
+ case S_IFREG:
+#ifdef S_IFLNK
+ case S_IFLNK:
+#endif
+ cb += nblocks(flist);
+ }
+ flist= flist->next;
+ }
+ return cb;
+}
+
+void printname(char *name)
+/* Print a name with control characters as '?' (unless -q). The terminal is
+ * assumed to be eight bit clean.
+ */
+{
+ int c, q= present('q');
+
+ while ((c= *name++) != 0) {
+ if (q && (c <= ' ' || c == 0177)) c= '?';
+ putchar(c);
+ }
+}
+
+int mark(struct file *f, int doit)
+{
+ int c;
+
+ if (!(field & F_MARK)) return 0;
+
+ switch (f->mode & S_IFMT) {
+ case S_IFDIR: c= '/'; break;
+#ifdef S_IFIFO
+ case S_IFIFO: c= '|'; break;
+#endif
+#ifdef S_IFLNK
+ case S_IFLNK: c= '@'; break;
+#endif
+#ifdef S_IFSOCK
+ case S_IFSOCK: c= '='; break;
+#endif
+ case S_IFREG:
+ if (f->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ c= '*';
+ break;
+ }
+ default:
+ c= 0;
+ }
+ if (doit && c != 0) putchar(c);
+ return c;
+}
+
+int colwidth[MAXCOLS]; /* Need colwidth[i] spaces to print column i. */
+int sizwidth[MAXCOLS]; /* Spaces for the size field in a -X print. */
+int namwidth[MAXCOLS]; /* Name field. */
+
+int maxise(int *aw, int w)
+/* Set *aw to the larger of it and w. Then return it. */
+{
+ if (w > *aw) *aw= w;
+ return *aw;
+}
+
+static int nsp= 0; /* This many spaces have not been printed yet. */
+#define spaces(n) (nsp= (n))
+#define terpri() (nsp= 0, putchar('\n')) /* No trailing spaces */
+
+void print1(struct file *f, int col, int doit)
+/* Either compute the number of spaces needed to print file f (doit == 0) or
+ * really print it (doit == 1).
+ */
+{
+ int width= 0, n;
+ char *p;
+
+ while (nsp>0) { putchar(' '); nsp--; }/* Fill gap between two columns */
+
+ if (field & F_INODE) {
+ if (doit) printf("%5d ", f->ino); else width+= 6;
+ }
+ if (field & F_BLOCKS) {
+ if (doit) printf("%4ld ", nblk2k(nblocks(f))); else width+= 5;
+ }
+ if (field & F_MODE) {
+ if (doit)
+ printf("%s ", permissions(f));
+ else
+ width+= (field & F_EXTRA) ? 5 : 11;
+ }
+ if (field & F_EXTRA) {
+ p= cxsize(f);
+ n= strlen(p)+1;
+
+ if (doit) {
+ n= sizwidth[col] - n;
+ while (n > 0) { putchar(' '); --n; }
+ printf("%s ", p);
+ } else
+ width+= maxise(&sizwidth[col], n);
+ }
+ if (field & F_LONG) {
+ if (doit) {
+ printf("%2d %-8s ", f->nlink, uidname(f->uid));
+ if (field & F_GROUP) printf("%-8s ", gidname(f->gid));
+
+ switch (f->mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+#ifdef S_IFMPB
+ case S_IFMPB:
+#endif
+#ifdef S_IFMPC
+ case S_IFMPC:
+#endif
+ printf("%3d, %3d ",
+ major(f->rdev), minor(f->rdev));
+ break;
+ default:
+ printf("%8ld ", (long) f->size);
+ }
+ printf("%s ", timestamp(f));
+ } else
+ width += (field & F_GROUP) ? 43 : 34;
+ }
+ n= strlen(f->name);
+ if (doit) {
+ printname(f->name);
+ if (mark(f, 1) != 0) n++;
+#ifdef S_IFLNK
+ if ((field & F_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
+ char *buf;
+ int r, didx;
+
+ buf= (char *) allocate(((size_t) f->size + 1)
+ * sizeof(buf[0]));
+ addpath(&didx, f->name);
+ r= readlink(path, buf, (int) f->size);
+ delpath(didx);
+ if (r > 0) buf[r] = 0; else r=1, strcpy(buf, "?");
+ printf(" -> ");
+ printname(buf);
+ free((void *) buf);
+ n+= 4 + r;
+ }
+#endif
+ spaces(namwidth[col] - n);
+ } else {
+ if (mark(f, 0) != 0) n++;
+#ifdef S_IFLNK
+ if ((field & F_LONG) && (f->mode & S_IFMT) == S_IFLNK) {
+ n+= 4 + (int) f->size;
+ }
+#endif
+ width+= maxise(&namwidth[col], n + NSEP);
+ maxise(&colwidth[col], width);
+ }
+}
+
+int countfiles(struct file *flist)
+/* Return number of files in the list. */
+{
+ int n= 0;
+
+ while (flist != nil) { n++; flist= flist->next; }
+
+ return n;
+}
+
+struct file *filecol[MAXCOLS]; /* filecol[i] is list of files for column i. */
+int nfiles, nlines; /* # files to print, # of lines needed. */
+
+int columnise(struct file *flist, int nplin)
+/* Chop list of files up in columns. Note that 3 columns are used for 5 files
+ * even though nplin may be 4, filecol[3] will simply be nil.
+ */
+{
+ int i, j;
+
+ nlines= (nfiles + nplin - 1) / nplin; /* nlines needed for nfiles */
+
+ filecol[0]= flist;
+
+ for (i=1; i<nplin; i++) { /* Give nlines files to each column. */
+ for (j=0; j<nlines && flist != nil; j++) flist= flist->next;
+
+ filecol[i]= flist;
+ }
+}
+
+int print(struct file *flist, int nplin, int doit)
+/* Try (doit == 0), or really print the list of files over nplin columns.
+ * Return true if it can be done in nplin columns or if nplin == 1.
+ */
+{
+ register struct file *f;
+ register int i, totlen;
+
+ columnise(flist, nplin);
+
+ if (!doit) {
+ if (nplin==1 && !(field & F_EXTRA))
+ return 1; /* No need to try 1 column. */
+
+ for (i=0; i<nplin; i++)
+ colwidth[i]= sizwidth[i]= namwidth[i]= 0;
+ }
+ while (--nlines >= 0) {
+ totlen=0;
+
+ for (i=0; i<nplin; i++) {
+ if ((f= filecol[i]) != nil) {
+ filecol[i]= f->next;
+ print1(f, i, doit);
+ }
+ if (!doit && nplin>1) {
+ /* See if this line is not too long. */
+ totlen+= colwidth[i];
+ if (totlen > ncols+NSEP) return 0;
+ }
+ }
+ if (doit) terpri();
+ }
+ return 1;
+}
+
+enum depth { SURFACE, SURFACE1, SUBMERGED };
+enum state { BOTTOM, SINKING, FLOATING };
+
+void listfiles(struct file *flist, enum depth depth, enum state state)
+/* Main workhorse of ls, it sorts and prints the list of files. Flags:
+ * depth: working with the command line / just one file / listing dir.
+ * state: How "recursive" do we have to be.
+ */
+{
+ struct file *dlist= nil, **afl= &flist, **adl= &dlist;
+ int nplin;
+ static int white = 1; /* Nothing printed yet. */
+
+ /* Flush everything previously printed, so new error output will
+ * not intermix with files listed earlier.
+ */
+ fflush(stdout);
+
+ if (field != 0 || state != BOTTOM) { /* Need stat(2) info. */
+ while (*afl != nil) {
+ static struct stat st;
+ int r, didx;
+
+ addpath(&didx, (*afl)->name);
+
+ if ((r= status(path, &st)) < 0
+#ifdef S_IFLNK
+ && (status == lstat || lstat(path, &st) < 0)
+#endif
+ ) {
+ if (depth != SUBMERGED || errno != ENOENT)
+ report((*afl)->name);
+ delfile(popfile(afl));
+ } else {
+ setstat(*afl, &st);
+ afl= &(*afl)->next;
+ }
+ delpath(didx);
+ }
+ }
+ sort(&flist);
+
+ if (depth == SUBMERGED && (field & (F_BLOCKS | F_LONG)))
+ printf("total %ld\n", nblk2k(countblocks(flist)));
+
+ if (state == SINKING || depth == SURFACE1) {
+ /* Don't list directories themselves, list their contents later. */
+ afl= &flist;
+ while (*afl != nil) {
+ if (((*afl)->mode & S_IFMT) == S_IFDIR) {
+ pushfile(adl, popfile(afl));
+ adl= &(*adl)->next;
+ } else
+ afl= &(*afl)->next;
+ }
+ }
+
+ if ((nfiles= countfiles(flist)) > 0) {
+ /* Print files in how many columns? */
+ nplin= !present('C') ? 1 : nfiles < MAXCOLS ? nfiles : MAXCOLS;
+
+ while (!print(flist, nplin, 0)) nplin--; /* Try first */
+
+ print(flist, nplin, 1); /* Then do it! */
+ white = 0;
+ }
+
+ while (flist != nil) { /* Destroy file list */
+ if (state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR) {
+ /* But keep these directories for ls -R. */
+ pushfile(adl, popfile(&flist));
+ adl= &(*adl)->next;
+ } else
+ delfile(popfile(&flist));
+ }
+
+ while (dlist != nil) { /* List directories */
+ if (dotflag(dlist->name) != 'a' || depth != SUBMERGED) {
+ int didx;
+
+ addpath(&didx, dlist->name);
+
+ flist= nil;
+ if (adddir(&flist, path)) {
+ if (depth != SURFACE1) {
+ if (!white) putchar('\n');
+ printf("%s:\n", path);
+ white = 0;
+ }
+ listfiles(flist, SUBMERGED,
+ state == FLOATING ? FLOATING : BOTTOM);
+ }
+ delpath(didx);
+ }
+ delfile(popfile(&dlist));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct file *flist= nil, **aflist= &flist;
+ enum depth depth;
+ char *lsflags;
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+#endif
+
+ uid= geteuid();
+ gid= getegid();
+
+ if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++;
+ argv++;
+
+ if (strcmp(arg0, "ls") != 0) {
+ char *p= arg0+1;
+
+ while (*p != 0) {
+ if (strchr(arg0flag, *p) != nil) *p += 'A' - 'a';
+ p++;
+ }
+ setflags(arg0+1);
+ }
+ while (*argv != nil && (*argv)[0] == '-') {
+ if ((*argv)[1] == '-' && (*argv)[2] == 0) {
+ argv++;
+ break;
+ }
+ setflags(*argv++ + 1);
+ }
+
+ istty= isatty(1);
+
+ if (istty && (lsflags= getenv("LSOPTS")) != nil) {
+ if (*lsflags == '-') lsflags++;
+ setflags(lsflags);
+ }
+
+ if (!present('1') && !present('C') && !present('l')
+ && (istty || present('M') || present('X') || present('F'))
+ ) setflags("C");
+
+ if (istty) setflags("q");
+
+ if (SUPER_ID == 0 || present('a')) setflags("A");
+
+ if (present('i')) field|= F_INODE;
+ if (present('s')) field|= F_BLOCKS;
+ if (present('M')) field|= F_MODE;
+ if (present('X')) field|= F_EXTRA|F_MODE;
+ if (present('t')) field|= F_BYTIME;
+ if (present('u')) field|= F_ATIME;
+ if (present('c')) field|= F_CTIME;
+ if (present('l')) {
+ field= (field | F_MODE | F_LONG) & ~F_EXTRA;
+ if (present('g')) field|= F_GROUP;
+ }
+ if (present('F')) field|= F_MARK;
+ if (present('T')) field|= F_TYPE;
+ if (present('d')) field|= F_DIR;
+
+#ifdef S_IFLNK
+ status= present('L') ? stat : lstat;
+#endif
+
+#ifdef TIOCGWINSZ
+ if (present('C')) {
+ int t= istty ? 1 : open("/dev/tty", O_WRONLY);
+
+ if (t >= 0 && ioctl(t, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
+ ncols= ws.ws_col - 1;
+
+ if (t != 1) close(t);
+ }
+#endif
+
+ depth= SURFACE;
+
+ if (*argv == nil) {
+ if (!(field & F_DIR)) depth= SURFACE1;
+ pushfile(aflist, newfile("."));
+ } else {
+ if (argv[1] == nil && !(field & F_DIR)) depth= SURFACE1;
+
+ do {
+ pushfile(aflist, newfile(*argv++));
+ aflist= &(*aflist)->next;
+ } while (*argv!=nil);
+ }
+ listfiles(flist, depth,
+ (field & F_DIR) ? BOTTOM : present('R') ? FLOATING : SINKING);
+ exit(ex);
+}
+/* Kees J. Bot 25-4-89. */
diff --git a/libc/tests/ouch.c b/libc/tests/ouch.c
new file mode 100644
index 0000000..c2925c6
--- /dev/null
+++ b/libc/tests/ouch.c
@@ -0,0 +1,27 @@
+
+#include <signal.h>
+#include <stdio.h>
+#include <errno.h>
+
+void trap()
+{
+ write(1, "Ouch!!\n", 7);
+}
+
+main()
+{
+ char buf[2];
+ int cc;
+
+ signal(SIGINT, trap);
+ while( (cc=read(0, buf, 1)) > 0 || (cc == -1 && errno == EINTR) )
+ {
+ if( cc < 0 )
+ fprintf(stderr, "INTR\n");
+ else
+ fprintf(stderr, "%x\n", buf[0]);
+ }
+
+
+ write(1, "\nExit!\n", 7);
+}
diff --git a/libc/tests/rand.c b/libc/tests/rand.c
new file mode 100644
index 0000000..c4fc6d2
--- /dev/null
+++ b/libc/tests/rand.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+
+FILE * popen();
+
+main()
+{
+ FILE * fd = popen("./hd", "w");
+ int ch;
+
+ srand(time((void*)0));
+
+ for(ch=0; ch<256; ch++)
+ putc(rand(), fd);
+
+ pclose(fd);
+}
diff --git a/libc/tests/size.c b/libc/tests/size.c
new file mode 100644
index 0000000..2d6676b
--- /dev/null
+++ b/libc/tests/size.c
@@ -0,0 +1,51 @@
+#include <fcntl.h>
+#include <a.out.h>
+
+void size(filename)
+ char *filename;
+{
+ int f;
+ struct exec ex;
+ long total;
+ int cc;
+
+ if ((f = open(filename, O_RDONLY)) < 0 )
+ {
+ perror(filename);
+ return;
+ }
+ cc = read(f, &ex, sizeof(ex));
+
+ if (cc == sizeof(ex) && !BADMAG(ex))
+ {
+ total = ex.a_text + ex.a_data + ex.a_bss;
+ printf("%-ld\t%-ld\t%-ld\t%-ld\t%-lx\t%s\n",
+ ex.a_text, ex.a_data, ex.a_bss, total, total,
+ filename);
+ }
+ else if( cc > 16 && memcmp(&ex, "\243\206\001\000*", 5) == 0 )
+ { /* *.o file */
+ total = ((unsigned char*)&ex)[9] +
+ ((unsigned char*)&ex)[10] * 256;
+ printf("\t\t\t%-ld\t%-lx\t%s\n",
+ total, total, filename);
+ }
+ else
+ printf("%s: Not an a.out file\n", filename);
+ close(f);
+}
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (argc < 2)
+ {
+ printf("Usage: %s file\n", argv[0]);
+ exit(1);
+ }
+ printf("text\tdata\tbss\tdec\thex\tfilename\n");
+ for (--argc, ++argv; argc > 0; --argc, ++argv)
+ size(*argv);
+ exit(0);
+}
diff --git a/libc/tests/sync.c b/libc/tests/sync.c
new file mode 100644
index 0000000..03ca096
--- /dev/null
+++ b/libc/tests/sync.c
@@ -0,0 +1 @@
+int main() { return sync(); }
diff --git a/libc/tests/ucomp.c b/libc/tests/ucomp.c
new file mode 100644
index 0000000..cc3eef8
--- /dev/null
+++ b/libc/tests/ucomp.c
@@ -0,0 +1,108 @@
+
+/*
+ * Uncompress program this is very very fast
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <malloc.h>
+
+#define MAXLEN 255
+
+#define maxno 61000U
+#define USE_BSS
+
+#ifdef USE_BSS
+unsigned char fptr[maxno];
+#else
+unsigned char *fptr;
+#endif
+FILE * fd;
+int key;
+
+main(argc, argv)
+int argc;
+char ** argv;
+{
+#ifndef USE_BSS
+ fptr = (unsigned char * ) malloc(maxno);
+
+ if( fptr == 0 )
+ {
+ perror("Cannot allocate memory");
+ exit(1);
+ }
+#endif
+
+ if( argc < 2 )
+ {
+ fd = stdin;
+ key = getc(fd);
+ uncompress();
+ }
+ else
+ {
+ fd = fopen(argv[1], "r" );
+ if( fd == 0 ) { perror("Open failed"); exit(1); }
+
+ key = getc(fd);
+ uncompress();
+ }
+}
+
+/*
+
+ Uncompression routine -- v.v.fast
+*/
+
+uncompress()
+{
+ register unsigned char * mainscan;
+ register unsigned char * secondscan;
+ register unsigned char * ptr = (unsigned char * ) fptr;
+ register unsigned char * eptr = ptr+maxno;
+ register unsigned int len;
+ register int ch;
+
+ mainscan = ptr;
+
+ for(;;)
+ {
+ ch = getc(fd);
+ if(ch == EOF) break;
+ ch &= 0xFF;
+ if(ch == key)
+ {
+ ch = getc(fd);
+ if( ch == 0 )
+ *mainscan++ = key;
+ else
+ {
+ len = (unsigned char) getc(fd);
+ if( ch & 0x80 )
+ len += ((unsigned char) getc(fd)) << 8;
+ secondscan = mainscan - len;
+ if(len > mainscan - ptr) secondscan += maxno;
+ len = (unsigned char) ch & 0x7F;
+ for( ; len>0; len-- )
+ {
+ *mainscan++ = *secondscan++;
+ if( secondscan == eptr ) secondscan = ptr;
+ if( mainscan == eptr )
+ { write(1, ptr, (int)(mainscan-ptr)); mainscan = ptr; }
+ }
+ }
+ }
+ else
+ *mainscan++ = ch;
+
+ if( mainscan == eptr )
+ { write(1, ptr, (int)(mainscan-ptr)); mainscan = ptr; }
+ }
+ if( mainscan != ptr )
+ { write(1, ptr, (int)(mainscan-ptr)); mainscan = ptr; }
+}
+
diff --git a/libc/tests/wc.c b/libc/tests/wc.c
new file mode 100644
index 0000000..08b93ca
--- /dev/null
+++ b/libc/tests/wc.c
@@ -0,0 +1,133 @@
+
+#include <stdio.h>
+#include <ctype.h>
+
+int lflag; /* Want count lines */
+int wflag; /* Want count words */
+int cflag; /* Want count characters */
+
+long lcount; /* File count of lines */
+long wcount; /* File count of words */
+long ccount; /* File count of characters */
+
+long ltotal; /* Total count of lines */
+long wtotal; /* Total count of words */
+long ctotal; /* Total count of characters */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char *p;
+ int ar;
+
+ if (argc > 1 && argv[1][0] == '-')
+ {
+ for (p = argv[1] + 1; *p; p++)
+ {
+ switch (*p)
+ {
+ case 'l':
+ lflag++;
+ break;
+ case 'w':
+ wflag++;
+ break;
+ case 'c':
+ cflag++;
+ break;
+ default:
+ Usage();
+ }
+ }
+ argc--;
+ argv++;
+ }
+
+ /* If no flags are set, treat as wc -lwc. */
+ if (!lflag && !wflag && !cflag)
+ lflag = wflag = cflag = 1;
+
+ /* No filename, use stdin */
+ if (argc == 1)
+ {
+ count(stdin, "");
+ exit(0);
+ }
+
+ /* There is an explicit list of files. Loop on files. */
+ for (ar = 1; ar < argc; ar++)
+ {
+ FILE *f;
+
+ if ((f = fopen(argv[ar], "r")) == NULL)
+ fprintf(stderr, "wc: cannot open %s\n", argv[ar]);
+ else
+ {
+ count(f, argv[ar]);
+ fclose(f);
+ }
+ }
+
+ if (argc > 2)
+ {
+ if (lflag)
+ printf("%7ld ", ltotal);
+ if (wflag)
+ printf("%7ld ", wtotal);
+ if (cflag)
+ printf("%7ld ", ctotal);
+ printf("total\n");
+ }
+ exit(0);
+}
+
+count(f, fname)
+FILE *f;
+char *fname;
+{
+ register int c;
+ register int inword = 0;
+
+ lcount = 0;
+ wcount = 0;
+ ccount = 0;
+
+ while ((c = getc(f)) != EOF)
+ {
+ ccount++;
+
+ if (isspace(c))
+ {
+ if (inword)
+ wcount++;
+ inword = 0;
+ }
+ else
+ inword = 1;
+
+ if (c == '\n' || c == '\f')
+ lcount++;
+ }
+
+ ltotal += lcount;
+ wtotal += wcount;
+ ctotal += ccount;
+
+ if (lflag)
+ printf("%7ld ", lcount);
+ if (wflag)
+ printf("%7ld ", wcount);
+ if (cflag)
+ printf("%7ld ", ccount);
+ if (fname && *fname)
+ printf("%s", fname);
+ printf("\n");
+}
+
+Usage()
+{
+ fprintf(stderr, "Usage: wc [-lwc] [name ...]\n");
+ exit(1);
+}