summaryrefslogtreecommitdiff
path: root/src/lib/libcmd
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libcmd')
-rw-r--r--src/lib/libcmd/Makefile90
-rw-r--r--src/lib/libcmd/Mamfile866
-rw-r--r--src/lib/libcmd/RELEASE328
-rw-r--r--src/lib/libcmd/basename.c139
-rw-r--r--src/lib/libcmd/cat.c557
-rw-r--r--src/lib/libcmd/chgrp.c501
-rw-r--r--src/lib/libcmd/chmod.c325
-rw-r--r--src/lib/libcmd/chown.c39
-rw-r--r--src/lib/libcmd/cksum.c632
-rw-r--r--src/lib/libcmd/cmd.h163
-rw-r--r--src/lib/libcmd/cmdinit.c75
-rw-r--r--src/lib/libcmd/cmp.c383
-rw-r--r--src/lib/libcmd/comm.c203
-rw-r--r--src/lib/libcmd/cp.c1009
-rw-r--r--src/lib/libcmd/cut.c702
-rw-r--r--src/lib/libcmd/date.c515
-rw-r--r--src/lib/libcmd/dirname.c139
-rw-r--r--src/lib/libcmd/expr.c535
-rw-r--r--src/lib/libcmd/fds.c360
-rw-r--r--src/lib/libcmd/features/ids9
-rw-r--r--src/lib/libcmd/features/sockets3
-rw-r--r--src/lib/libcmd/features/symlink23
-rw-r--r--src/lib/libcmd/features/utsname15
-rw-r--r--src/lib/libcmd/fmt.c635
-rw-r--r--src/lib/libcmd/fold.c240
-rw-r--r--src/lib/libcmd/fts_fix.c57
-rw-r--r--src/lib/libcmd/fts_fix.h49
-rw-r--r--src/lib/libcmd/getconf.c396
-rw-r--r--src/lib/libcmd/head.c150
-rw-r--r--src/lib/libcmd/id.c472
-rw-r--r--src/lib/libcmd/join.c984
-rw-r--r--src/lib/libcmd/lib.c25
-rw-r--r--src/lib/libcmd/ln.c35
-rw-r--r--src/lib/libcmd/logname.c78
-rw-r--r--src/lib/libcmd/md5sum.c35
-rw-r--r--src/lib/libcmd/mkdir.c189
-rw-r--r--src/lib/libcmd/mkfifo.c96
-rw-r--r--src/lib/libcmd/mktemp.c169
-rw-r--r--src/lib/libcmd/mv.c35
-rw-r--r--src/lib/libcmd/paste.c288
-rw-r--r--src/lib/libcmd/pathchk.c265
-rw-r--r--src/lib/libcmd/pids.c124
-rw-r--r--src/lib/libcmd/rev.c168
-rw-r--r--src/lib/libcmd/rev.h34
-rw-r--r--src/lib/libcmd/revlib.c112
-rw-r--r--src/lib/libcmd/rm.c417
-rw-r--r--src/lib/libcmd/rmdir.c126
-rw-r--r--src/lib/libcmd/stty.c971
-rw-r--r--src/lib/libcmd/sum.c35
-rw-r--r--src/lib/libcmd/sync.c79
-rw-r--r--src/lib/libcmd/tail.c773
-rw-r--r--src/lib/libcmd/tee.c204
-rw-r--r--src/lib/libcmd/tty.c105
-rw-r--r--src/lib/libcmd/uname.c514
-rw-r--r--src/lib/libcmd/uniq.c342
-rw-r--r--src/lib/libcmd/vmstate.c197
-rw-r--r--src/lib/libcmd/wc.c188
-rw-r--r--src/lib/libcmd/wc.h59
-rw-r--r--src/lib/libcmd/wclib.c505
59 files changed, 16762 insertions, 0 deletions
diff --git a/src/lib/libcmd/Makefile b/src/lib/libcmd/Makefile
new file mode 100644
index 0000000..3cecffd
--- /dev/null
+++ b/src/lib/libcmd/Makefile
@@ -0,0 +1,90 @@
+/*
+ * command library -- commands small enough to be builtins
+ */
+
+:PACKAGE: ast
+
+LICENSE = since=1992,author=gsf+dgk
+
+CCFLAGS = $(CC.SUFFIX.DEBUG:+$(CC.DEBUG)) $(CC.OPTIMIZE) $(CC.DLL)
+
+/* 2009-04-15 ld.so experiment -- may become a default for all plugins */
+CC.DLL.ORIGIN = $(CC.LD.ORIGIN:C,/.*,,)
+LDFLAGS = $(CC.DLL.ORIGIN)
+
+CP = $(STDCP|"cp")
+CHMOD = $(STDCHMOD|"chmod")
+
+HOSTTYPE == "$(CC.HOSTTYPE)"
+
+cmd 1.2 :LIBRARY: RELEASE cmdinit.c \
+ cmd.h rev.h wc.h \
+ basename.c cat.c chgrp.c chmod.c chown.c cksum.c cmp.c \
+ comm.c cp.c cut.c dirname.c date.c expr.c fds.c fmt.c fold.c \
+ getconf.c head.c id.c join.c ln.c logname.c md5sum.c mkdir.c \
+ mkfifo.c mktemp.c mv.c paste.c pathchk.c pids.c rev.c rm.c \
+ rmdir.c stty.c sum.c sync.c tail.c tee.c tty.c uname.c uniq.c \
+ vmstate.c wc.c revlib.c wclib.c sumlib.o \
+ fts_fix.c lib.c \
+ -lfsg -lmd
+
+sumlib.o : +lsum
+ $(AR) x $(*:O=1) $(<)
+
+$(INCLUDEDIR) :INSTALLPROTO: cmd.h cmdext.h cmdlist.h
+
+"win32*" :NOOPTIMIZE: id.c
+
+src = $(*$(*$(*cmd)):N=*.c:T=F)
+
+cmdext.h : (src) $$(-mam:+$$(src))
+ {
+ cat <<!
+ $("#")pragma prototyped
+ $("/")*
+ $(" ")* -lcmd extern function prototypes
+ $(" ")*/
+
+ $("#")include <shcmd.h>
+
+ !
+ $(SED) \
+ -e '/^b_[a-z_][a-z_0-9]*(/!d' \
+ -e 's/^b_//' \
+ -e 's/(.*//' \
+ -e 's/.*/extern int b_&(int, char**, Shbltin_t*);/' \
+ $($(~:O=1)) |
+ $(SORT) -u
+ } > 1.$(tmp).h
+ if $(CMP) $(CMPFLAGS) 1.$(tmp).h $(<)
+ then $(RM) $(RMFLAGS) 1.$(tmp).h
+ else $(MV) 1.$(tmp).h $(<)
+ fi
+
+cmdlist.h : (src)
+ {
+ cat <<!
+ $("#")pragma prototyped
+ $("/")*
+ $(" ")* -lcmd function list -- define your own CMDLIST()
+ $(" ")*/
+
+ !
+ $(SED) \
+ -e '/^b_[a-z_][a-z_0-9]*(/!d' \
+ -e 's/^b_//' \
+ -e 's/(.*//' \
+ -e 's/.*/CMDLIST(&)/' \
+ $($(~):T=F) |
+ $(SORT) -u
+ } > 1.$(tmp).h
+ if $(CMP) $(CMPFLAGS) 1.$(tmp).h $(<)
+ then $(RM) $(RMFLAGS) 1.$(tmp).h
+ else $(MV) 1.$(tmp).h $(<)
+ fi
+
+if CC.HOSTTYPE == "sun4"
+ pathsetlink == symlink /* ld.so workaround */
+end
+
+ignore dlldefs.h /* prevents bootstrap double build */
diff --git a/src/lib/libcmd/Mamfile b/src/lib/libcmd/Mamfile
new file mode 100644
index 0000000..a523fa4
--- /dev/null
+++ b/src/lib/libcmd/Mamfile
@@ -0,0 +1,866 @@
+info mam static 00000 1994-07-17 make (AT&T Research) 5.7 2012-02-29
+setv INSTALLROOT ../../..
+setv PACKAGE_ast_INCLUDE ${INSTALLROOT}/include/ast
+setv PACKAGE_ast_LIB ${INSTALLROOT}/lib
+setv PACKAGEROOT ../../../../..
+setv AR ${mam_cc_AR} ${mam_cc_AR_ARFLAGS}
+setv ARFLAGS rc
+setv AS as
+setv ASFLAGS
+setv CC cc
+setv mam_cc_FLAGS ${mam_cc_DLL}
+setv CCFLAGS ${mam_cc_DEBUG}
+setv CCLDFLAGS ${-strip-symbols?1?${mam_cc_LD_STRIP}??}
+setv COTEMP $$
+setv CPIO cpio
+setv CPIOFLAGS
+setv CPP "${CC} -E"
+setv F77 f77
+setv HOSTCC ${CC}
+setv IGNORE
+setv LD ld
+setv LDFLAGS ""
+setv LEX lex
+setv LEXFLAGS
+setv LPR lpr
+setv LPRFLAGS
+setv M4FLAGS
+setv NMAKE nmake
+setv NMAKEFLAGS
+setv PR pr
+setv PRFLAGS
+setv SHELL /bin/sh
+setv SILENT
+setv TAR tar
+setv YACC yacc
+setv YACCFLAGS -d
+make ${PACKAGEROOT}/lib/package/ast.lic
+done ${PACKAGEROOT}/lib/package/ast.lic
+make install
+make cmd
+make libcmd.a archive
+make cmd.req
+exec - set -
+exec - echo 'int main(){return 0;}' > 1.${COTEMP}.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -c 1.${COTEMP}.c &&
+exec - x=`${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} -o 1.${COTEMP}.x 1.${COTEMP}.o -l'*' 2>&1 | sed -e 's/[][()+@?]/#/g' || :` &&
+exec - {
+exec - case "" in
+exec - *?) echo " " ;;
+exec - esac
+exec - for i in cmd fsg md ast
+exec - do case $i in
+exec - "cmd"|cmd)
+exec - ;;
+exec - *) if test -f ${INSTALLROOT}/lib/lib/$i
+exec - then y=`cat ${INSTALLROOT}/lib/lib/$i`
+exec - case $y in
+exec - *-?*) echo "" $y ;;
+exec - esac
+exec - continue
+exec - elif test ! -f ${INSTALLROOT}/lib/lib$i.a
+exec - then case `{ ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -L${INSTALLROOT}/lib ${LDFLAGS} -o 1.${COTEMP}.x 1.${COTEMP}.o -l$i 2>&1 || echo '' $x ;} | sed -e 's/[][()+@?]/#/g' || :` in
+exec - *$x*) case `{ ${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} -o 1.${COTEMP}.x 1.${COTEMP}.o -l$i 2>&1 || echo '' $x ;} | sed -e 's/[][()+@?]/#/g' || :` in
+exec - *$x*) continue ;;
+exec - esac
+exec - ;;
+exec - esac
+exec - fi
+exec - ;;
+exec - esac
+exec - echo " -l$i"
+exec - done
+exec - } > cmd.req
+exec - rm -f 1.${COTEMP}.*
+done cmd.req generated
+make cmdinit.o
+make cmdinit.c
+make ${PACKAGE_ast_INCLUDE}/shcmd.h implicit
+make ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+make ${INSTALLROOT}/include/prototyped.h implicit
+done ${INSTALLROOT}/include/prototyped.h dontcare
+done ${PACKAGE_ast_INCLUDE}/prototyped.h dontcare
+done ${PACKAGE_ast_INCLUDE}/shcmd.h
+make cmd.h implicit
+make ${PACKAGE_ast_INCLUDE}/dlldefs.h implicit
+done ${PACKAGE_ast_INCLUDE}/dlldefs.h dontcare ignore
+make cmdext.h implicit
+prev cmdinit.c
+make basename.c
+prev cmd.h implicit
+done basename.c
+make cat.c
+make ${PACKAGE_ast_INCLUDE}/endian.h implicit
+make ${PACKAGE_ast_INCLUDE}/bytesex.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_map.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_map.h dontcare
+done ${PACKAGE_ast_INCLUDE}/ast_common.h dontcare
+done ${PACKAGE_ast_INCLUDE}/bytesex.h dontcare
+done ${PACKAGE_ast_INCLUDE}/endian.h dontcare
+prev cmd.h implicit
+done cat.c
+make chgrp.c
+make FEATURE/symlink implicit
+meta FEATURE/symlink features/%>FEATURE/% features/symlink symlink
+make features/symlink
+done features/symlink
+bind -last
+exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libast} : run features/symlink
+done FEATURE/symlink generated
+make fts_fix.h implicit
+make ${PACKAGE_ast_INCLUDE}/fts.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_mode.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_mode.h dontcare
+make ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_fs.h dontcare
+make ${PACKAGE_ast_INCLUDE}/ast_std.h implicit
+make ${PACKAGE_ast_INCLUDE}/regex.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_wchar.h implicit
+make ${PACKAGE_ast_INCLUDE}/wctype.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_wctype.h implicit
+prev ${PACKAGE_ast_INCLUDE}/endian.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_wctype.h dontcare
+done ${PACKAGE_ast_INCLUDE}/wctype.h dontcare
+make ${PACKAGE_ast_INCLUDE}/stdio.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_stdio.h implicit
+make ${PACKAGE_ast_INCLUDE}/sfio_s.h implicit
+done ${PACKAGE_ast_INCLUDE}/sfio_s.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_stdio.h dontcare
+done ${PACKAGE_ast_INCLUDE}/stdio.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/stdio.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_wchar.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/regex.h dontcare
+make ${PACKAGE_ast_INCLUDE}/getopt.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_getopt.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/getopt.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_map.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_botch.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_botch.h dontcare
+make ${PACKAGE_ast_INCLUDE}/ast_limits.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_limits.h dontcare
+make ${PACKAGE_ast_INCLUDE}/ast_fcntl.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_fcntl.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_sys.h implicit
+prev ${PACKAGE_ast_INCLUDE}/getopt.h implicit
+prev ${PACKAGE_ast_INCLUDE}/endian.h implicit
+prev ${PACKAGE_ast_INCLUDE}/endian.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_sys.h dontcare
+make ${PACKAGE_ast_INCLUDE}/ast_lib.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_lib.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_std.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/fts.h dontcare
+done fts_fix.h
+prev ${PACKAGE_ast_INCLUDE}/endian.h implicit
+make ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_mode.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/ls.h
+make ${PACKAGE_ast_INCLUDE}/cdt.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit
+done ${PACKAGE_ast_INCLUDE}/cdt.h
+prev cmd.h implicit
+done chgrp.c
+make chmod.c
+prev FEATURE/symlink implicit
+prev fts_fix.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done chmod.c
+make chown.c
+prev cmd.h implicit
+done chown.c
+make cksum.c
+make ${PACKAGE_ast_INCLUDE}/error.h implicit
+make ${PACKAGE_ast_INCLUDE}/option.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_api.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_api.h dontcare
+make ${PACKAGE_ast_INCLUDE}/vmalloc.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit
+done ${PACKAGE_ast_INCLUDE}/vmalloc.h dontcare
+make ${PACKAGE_ast_INCLUDE}/sfio.h implicit
+prev ${PACKAGE_ast_INCLUDE}/sfio_s.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit
+done ${PACKAGE_ast_INCLUDE}/sfio.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/option.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/error.h
+prev fts_fix.h implicit
+make ${PACKAGE_ast_INCLUDE}/modex.h implicit
+make ${PACKAGE_ast_INCLUDE}/modecanon.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/modecanon.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/modex.h
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+make ${PACKAGE_ast_INCLUDE}/sum.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/sum.h
+prev cmd.h implicit
+done cksum.c
+make cmp.c
+make ${PACKAGE_ast_INCLUDE}/ccode.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_ccode.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_ccode.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/ccode.h
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done cmp.c
+make comm.c
+prev cmd.h implicit
+done comm.c
+make cp.c
+make ${PACKAGE_ast_INCLUDE}/tmx.h implicit
+make ${PACKAGE_ast_INCLUDE}/tv.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+done ${PACKAGE_ast_INCLUDE}/tv.h dontcare
+make ${PACKAGE_ast_INCLUDE}/tm.h implicit
+make ${PACKAGE_ast_INCLUDE}/times.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_time.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_time.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/times.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/tm.h dontcare
+done ${PACKAGE_ast_INCLUDE}/tmx.h
+make ${PACKAGE_ast_INCLUDE}/stk.h implicit
+prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/stk.h
+make ${PACKAGE_ast_INCLUDE}/hashkey.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/hashkey.h
+make ${PACKAGE_ast_INCLUDE}/fs3d.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/fs3d.h
+prev fts_fix.h implicit
+prev ${PACKAGE_ast_INCLUDE}/times.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done cp.c
+make cut.c
+prev cmd.h implicit
+done cut.c
+make dirname.c
+prev cmd.h implicit
+done dirname.c
+make date.c
+prev ${PACKAGE_ast_INCLUDE}/times.h implicit
+prev ${PACKAGE_ast_INCLUDE}/tmx.h implicit
+make ${PACKAGE_ast_INCLUDE}/proc.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/proc.h
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done date.c
+make expr.c
+prev ${PACKAGE_ast_INCLUDE}/regex.h implicit
+prev cmd.h implicit
+done expr.c
+make fds.c
+prev ${PACKAGE_ast_INCLUDE}/endian.h implicit
+make FEATURE/sockets implicit
+meta FEATURE/sockets features/%>FEATURE/% features/sockets sockets
+make features/sockets
+done features/sockets
+exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libast} : run features/sockets
+done FEATURE/sockets generated
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done fds.c
+make fmt.c
+prev cmd.h implicit
+done fmt.c
+make fold.c
+prev cmd.h implicit
+done fold.c
+make getconf.c
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev ${PACKAGE_ast_INCLUDE}/proc.h implicit
+prev cmd.h implicit
+done getconf.c
+make head.c
+prev cmd.h implicit
+done head.c
+make id.c
+make fsg.h implicit
+done fsg.h dontcare virtual
+prev ${PACKAGE_ast_INCLUDE}/stdio.h implicit
+prev ${PACKAGE_ast_INCLUDE}/stdio.h implicit
+make FEATURE/ids implicit
+meta FEATURE/ids features/%>FEATURE/% features/ids ids
+make features/ids
+done features/ids
+exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libast} : run features/ids
+done FEATURE/ids generated
+prev cmd.h implicit
+done id.c
+make join.c
+prev ${PACKAGE_ast_INCLUDE}/wctype.h implicit
+make ${PACKAGE_ast_INCLUDE}/wchar.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast_wchar.h implicit
+done ${PACKAGE_ast_INCLUDE}/wchar.h dontcare
+make ${PACKAGE_ast_INCLUDE}/sfdisc.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/sfdisc.h
+prev cmd.h implicit
+done join.c
+make ln.c
+prev cmd.h implicit
+done ln.c
+make logname.c
+prev cmd.h implicit
+done logname.c
+make md5sum.c
+prev cmd.h implicit
+done md5sum.c
+make mkdir.c
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done mkdir.c
+make mkfifo.c
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done mkfifo.c
+make mktemp.c
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done mktemp.c
+make mv.c
+prev cmd.h implicit
+done mv.c
+make paste.c
+prev cmd.h implicit
+done paste.c
+make pathchk.c
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done pathchk.c
+make pids.c
+prev ${PACKAGE_ast_INCLUDE}/sfdisc.h implicit
+make ${PACKAGE_ast_INCLUDE}/ast_tty.h implicit
+done ${PACKAGE_ast_INCLUDE}/ast_tty.h
+prev cmd.h implicit
+done pids.c
+make rev.c
+make rev.h implicit
+done rev.h
+prev cmd.h implicit
+done rev.c
+make rm.c
+prev ${PACKAGE_ast_INCLUDE}/fs3d.h implicit
+prev fts_fix.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done rm.c
+make rmdir.c
+prev cmd.h implicit
+done rmdir.c
+make stty.c
+prev ${PACKAGE_ast_INCLUDE}/ast_tty.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ccode.h implicit
+prev cmd.h implicit
+done stty.c
+make sum.c
+prev cmd.h implicit
+done sum.c
+make sync.c
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done sync.c
+make tail.c
+prev rev.h implicit
+prev ${PACKAGE_ast_INCLUDE}/tm.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done tail.c
+make tee.c
+make ${PACKAGE_ast_INCLUDE}/sig.h implicit
+done ${PACKAGE_ast_INCLUDE}/sig.h
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+prev cmd.h implicit
+done tee.c
+make tty.c
+prev cmd.h implicit
+done tty.c
+make uname.c
+make FEATURE/utsname implicit
+meta FEATURE/utsname features/%>FEATURE/% features/utsname utsname
+make features/utsname
+done features/utsname
+exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libast} : run features/utsname
+done FEATURE/utsname generated
+prev ${PACKAGE_ast_INCLUDE}/proc.h implicit
+prev cmd.h implicit
+done uname.c
+make uniq.c
+prev cmd.h implicit
+done uniq.c
+make vmstate.c
+prev ${PACKAGE_ast_INCLUDE}/vmalloc.h implicit
+prev cmd.h implicit
+done vmstate.c
+make wc.c
+prev ${PACKAGE_ast_INCLUDE}/ls.h implicit
+make wc.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+done wc.h
+prev cmd.h implicit
+done wc.c
+make revlib.c
+prev rev.h implicit
+prev cmd.h implicit
+done revlib.c
+make wclib.c
+make ${PACKAGE_ast_INCLUDE}/lc.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/lc.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/wctype.h implicit
+prev ${PACKAGE_ast_INCLUDE}/wchar.h implicit
+prev wc.h implicit
+prev cmd.h implicit
+done wclib.c
+make fts_fix.c
+prev fts_fix.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+done fts_fix.c
+make lib.c
+prev cmd.h implicit
+done lib.c
+exec - {
+exec - cat <<!
+exec - #pragma prototyped
+exec - /*
+exec - * -lcmd extern function prototypes
+exec - */
+exec -
+exec - #include <shcmd.h>
+exec -
+exec - !
+exec - sed \
+exec - -e '/^b_[a-z_][a-z_0-9]*(/!d' \
+exec - -e 's/^b_//' \
+exec - -e 's/(.*//' \
+exec - -e 's/.*/extern int b_&(int, char**, Shbltin_t*);/' \
+exec - cmdinit.c basename.c cat.c chgrp.c chmod.c chown.c cksum.c cmp.c comm.c cp.c cut.c dirname.c date.c expr.c fds.c fmt.c fold.c getconf.c head.c id.c join.c ln.c logname.c md5sum.c mkdir.c mkfifo.c mktemp.c mv.c paste.c pathchk.c pids.c rev.c rm.c rmdir.c stty.c sum.c sync.c tail.c tee.c tty.c uname.c uniq.c vmstate.c wc.c revlib.c wclib.c fts_fix.c lib.c |
+exec - sort -u
+exec - } > 1.${COTEMP}.h
+exec - if cmp 2>/dev/null -s 1.${COTEMP}.h cmdext.h
+exec - then rm -f 1.${COTEMP}.h
+exec - else mv 1.${COTEMP}.h cmdext.h
+exec - fi
+prev ${PACKAGE_ast_INCLUDE}/shcmd.h implicit
+done cmdext.h dontcare generated
+prev ${PACKAGE_ast_INCLUDE}/shcmd.h implicit
+make ${PACKAGE_ast_INCLUDE}/stak.h implicit
+prev ${PACKAGE_ast_INCLUDE}/stk.h implicit
+prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit
+done ${PACKAGE_ast_INCLUDE}/stak.h dontcare
+prev ${PACKAGE_ast_INCLUDE}/error.h implicit
+prev ${PACKAGE_ast_INCLUDE}/ast.h implicit
+done cmd.h
+done cmdinit.c
+meta cmdinit.o %.c>%.o cmdinit.c cmdinit
+prev cmdinit.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c cmdinit.c
+done cmdinit.o generated
+make basename.o
+prev basename.c
+meta basename.o %.c>%.o basename.c basename
+prev basename.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c basename.c
+done basename.o generated
+make cat.o
+prev cat.c
+meta cat.o %.c>%.o cat.c cat
+prev cat.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c cat.c
+done cat.o generated
+make chgrp.o
+prev chgrp.c
+meta chgrp.o %.c>%.o chgrp.c chgrp
+prev chgrp.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_cmd -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c chgrp.c
+done chgrp.o generated
+make chmod.o
+prev chmod.c
+meta chmod.o %.c>%.o chmod.c chmod
+prev chmod.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c chmod.c
+done chmod.o generated
+make chown.o
+prev chown.c
+meta chown.o %.c>%.o chown.c chown
+prev chown.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c chown.c
+done chown.o generated
+make cksum.o
+prev cksum.c
+meta cksum.o %.c>%.o cksum.c cksum
+prev cksum.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c cksum.c
+done cksum.o generated
+make cmp.o
+prev cmp.c
+meta cmp.o %.c>%.o cmp.c cmp
+prev cmp.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c cmp.c
+done cmp.o generated
+make comm.o
+prev comm.c
+meta comm.o %.c>%.o comm.c comm
+prev comm.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c comm.c
+done comm.o generated
+make cp.o
+prev cp.c
+meta cp.o %.c>%.o cp.c cp
+prev cp.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c cp.c
+done cp.o generated
+make cut.o
+prev cut.c
+meta cut.o %.c>%.o cut.c cut
+prev cut.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c cut.c
+done cut.o generated
+make dirname.o
+prev dirname.c
+meta dirname.o %.c>%.o dirname.c dirname
+prev dirname.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c dirname.c
+done dirname.o generated
+make date.o
+prev date.c
+meta date.o %.c>%.o date.c date
+prev date.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c date.c
+done date.o generated
+make expr.o
+prev expr.c
+meta expr.o %.c>%.o expr.c expr
+prev expr.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c expr.c
+done expr.o generated
+make fds.o
+prev fds.c
+meta fds.o %.c>%.o fds.c fds
+prev fds.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c fds.c
+done fds.o generated
+make fmt.o
+prev fmt.c
+meta fmt.o %.c>%.o fmt.c fmt
+prev fmt.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c fmt.c
+done fmt.o generated
+make fold.o
+prev fold.c
+meta fold.o %.c>%.o fold.c fold
+prev fold.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c fold.c
+done fold.o generated
+make getconf.o
+prev getconf.c
+meta getconf.o %.c>%.o getconf.c getconf
+prev getconf.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c getconf.c
+done getconf.o generated
+make head.o
+prev head.c
+meta head.o %.c>%.o head.c head
+prev head.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c head.c
+done head.o generated
+make id.o
+prev id.c
+meta id.o %.c>%.o id.c id
+prev id.c
+exec - ${CC} ${mam_cc_FLAGS} ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${CCFLAGS.FORCE}?} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c id.c
+done id.o generated
+make join.o
+prev join.c
+meta join.o %.c>%.o join.c join
+prev join.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c join.c
+done join.o generated
+make ln.o
+prev ln.c
+meta ln.o %.c>%.o ln.c ln
+prev ln.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c ln.c
+done ln.o generated
+make logname.o
+prev logname.c
+meta logname.o %.c>%.o logname.c logname
+prev logname.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c logname.c
+done logname.o generated
+make md5sum.o
+prev md5sum.c
+meta md5sum.o %.c>%.o md5sum.c md5sum
+prev md5sum.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c md5sum.c
+done md5sum.o generated
+make mkdir.o
+prev mkdir.c
+meta mkdir.o %.c>%.o mkdir.c mkdir
+prev mkdir.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c mkdir.c
+done mkdir.o generated
+make mkfifo.o
+prev mkfifo.c
+meta mkfifo.o %.c>%.o mkfifo.c mkfifo
+prev mkfifo.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c mkfifo.c
+done mkfifo.o generated
+make mktemp.o
+prev mktemp.c
+meta mktemp.o %.c>%.o mktemp.c mktemp
+prev mktemp.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c mktemp.c
+done mktemp.o generated
+make mv.o
+prev mv.c
+meta mv.o %.c>%.o mv.c mv
+prev mv.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c mv.c
+done mv.o generated
+make paste.o
+prev paste.c
+meta paste.o %.c>%.o paste.c paste
+prev paste.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c paste.c
+done paste.o generated
+make pathchk.o
+prev pathchk.c
+meta pathchk.o %.c>%.o pathchk.c pathchk
+prev pathchk.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c pathchk.c
+done pathchk.o generated
+make pids.o
+prev pids.c
+meta pids.o %.c>%.o pids.c pids
+prev pids.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c pids.c
+done pids.o generated
+make rev.o
+prev rev.c
+meta rev.o %.c>%.o rev.c rev
+prev rev.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c rev.c
+done rev.o generated
+make rm.o
+prev rm.c
+meta rm.o %.c>%.o rm.c rm
+prev rm.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c rm.c
+done rm.o generated
+make rmdir.o
+prev rmdir.c
+meta rmdir.o %.c>%.o rmdir.c rmdir
+prev rmdir.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c rmdir.c
+done rmdir.o generated
+make stty.o
+prev stty.c
+meta stty.o %.c>%.o stty.c stty
+prev stty.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c stty.c
+done stty.o generated
+make sum.o
+prev sum.c
+meta sum.o %.c>%.o sum.c sum
+prev sum.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c sum.c
+done sum.o generated
+make sync.o
+prev sync.c
+meta sync.o %.c>%.o sync.c sync
+prev sync.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c sync.c
+done sync.o generated
+make tail.o
+prev tail.c
+meta tail.o %.c>%.o tail.c tail
+prev tail.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c tail.c
+done tail.o generated
+make tee.o
+prev tee.c
+meta tee.o %.c>%.o tee.c tee
+prev tee.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c tee.c
+done tee.o generated
+make tty.o
+prev tty.c
+meta tty.o %.c>%.o tty.c tty
+prev tty.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c tty.c
+done tty.o generated
+make uname.o
+prev uname.c
+meta uname.o %.c>%.o uname.c uname
+prev uname.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -DHOSTTYPE=\""${mam_cc_HOSTTYPE}"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c uname.c
+done uname.o generated
+make uniq.o
+prev uniq.c
+meta uniq.o %.c>%.o uniq.c uniq
+prev uniq.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c uniq.c
+done uniq.o generated
+make vmstate.o
+prev vmstate.c
+meta vmstate.o %.c>%.o vmstate.c vmstate
+prev vmstate.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_cmd -D_BLD_DEBUG -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c vmstate.c
+done vmstate.o generated
+make wc.o
+prev wc.c
+meta wc.o %.c>%.o wc.c wc
+prev wc.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -DERROR_CATALOG=\""libcmd"\" -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -DUSAGE_LICENSE=\""[-author?Glenn Fowler <gsf@research.att.com>][-author?David Korn <dgk@research.att.com>][-copyright?Copyright (c) 1992-2012 AT&T Intellectual Property][-license?http://www.eclipse.org/org/documents/epl-v10.html][--catalog?libcmd]"\" -c wc.c
+done wc.o generated
+make revlib.o
+prev revlib.c
+meta revlib.o %.c>%.o revlib.c revlib
+prev revlib.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c revlib.c
+done revlib.o generated
+make wclib.o
+prev wclib.c
+meta wclib.o %.c>%.o wclib.c wclib
+prev wclib.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_DEBUG -D_BLD_cmd -c wclib.c
+done wclib.o generated
+make sumlib.o
+bind -lsum
+exec - ${AR} x ${mam_libsum} sumlib.o
+done sumlib.o generated
+make fts_fix.o
+prev fts_fix.c
+meta fts_fix.o %.c>%.o fts_fix.c fts_fix
+prev fts_fix.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_DEBUG -c fts_fix.c
+done fts_fix.o generated
+make lib.o
+prev lib.c
+meta lib.o %.c>%.o lib.c lib
+prev lib.c
+exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -I${PACKAGE_ast_INCLUDE} -D_BLD_cmd -D_PACKAGE_ast -D_BLD_DEBUG -c lib.c
+done lib.o generated
+exec - ${AR} rc libcmd.a cmdinit.o basename.o cat.o chgrp.o chmod.o chown.o cksum.o cmp.o comm.o cp.o cut.o dirname.o date.o expr.o fds.o fmt.o fold.o getconf.o head.o id.o join.o ln.o logname.o md5sum.o mkdir.o mkfifo.o mktemp.o mv.o paste.o pathchk.o
+exec - ${AR} rc libcmd.a pids.o rev.o rm.o rmdir.o stty.o sum.o sync.o tail.o tee.o tty.o uname.o uniq.o vmstate.o wc.o revlib.o wclib.o sumlib.o fts_fix.o lib.o
+exec - (ranlib libcmd.a) >/dev/null 2>&1 || true
+done libcmd.a generated
+done cmd virtual
+prev libcmd.a archive
+make ${INSTALLROOT}/lib
+exec - if silent test ! -d ${INSTALLROOT}/lib
+exec - then mkdir -p ${INSTALLROOT}/lib
+exec - fi
+done ${INSTALLROOT}/lib generated
+make ${INSTALLROOT}/lib/libcmd.a archive
+prev ${INSTALLROOT}/lib
+prev libcmd.a archive
+exec - test '' = 'libcmd.a' || ${STDCMP} 2>/dev/null -s libcmd.a ${INSTALLROOT}/lib/libcmd.a || { ${STDMV} ${INSTALLROOT}/lib/libcmd.a ${INSTALLROOT}/lib/libcmd.a.old 2>/dev/null || true; ${STDCP} libcmd.a ${INSTALLROOT}/lib/libcmd.a ;}
+exec - (ranlib ${INSTALLROOT}/lib/libcmd.a) >/dev/null 2>&1 || true
+done ${INSTALLROOT}/lib/libcmd.a generated
+make ${INSTALLROOT}/lib/lib
+exec - if silent test ! -d ${INSTALLROOT}/lib/lib
+exec - then mkdir -p ${INSTALLROOT}/lib/lib
+exec - fi
+done ${INSTALLROOT}/lib/lib generated
+make ${INSTALLROOT}/lib/lib/cmd
+prev ${INSTALLROOT}/lib/lib
+prev cmd.req
+exec - test '' = 'cmd.req' || ${STDCMP} 2>/dev/null -s cmd.req ${INSTALLROOT}/lib/lib/cmd || { ${STDMV} ${INSTALLROOT}/lib/lib/cmd ${INSTALLROOT}/lib/lib/cmd.old 2>/dev/null || true; ${STDCP} cmd.req ${INSTALLROOT}/lib/lib/cmd ;}
+done ${INSTALLROOT}/lib/lib/cmd generated
+make ${PACKAGE_ast_INCLUDE}
+exec - if silent test ! -d ${PACKAGE_ast_INCLUDE}
+exec - then mkdir -p ${PACKAGE_ast_INCLUDE}
+exec - fi
+done ${PACKAGE_ast_INCLUDE} generated
+make ${PACKAGE_ast_INCLUDE}/cmd.h
+prev ${PACKAGE_ast_INCLUDE}
+prev cmd.h
+exec - proto -p -s -l ${PACKAGEROOT}/lib/package/ast.lic '-o since=1992,author=gsf+dgk' cmd.h > 1.${COTEMP}.x
+exec - if cmp 2>/dev/null -s ${PACKAGE_ast_INCLUDE}/cmd.h 1.${COTEMP}.x
+exec - then rm -f 1.${COTEMP}.x
+exec - else mv 1.${COTEMP}.x ${PACKAGE_ast_INCLUDE}/cmd.h
+exec - fi
+done ${PACKAGE_ast_INCLUDE}/cmd.h generated
+make ${PACKAGE_ast_INCLUDE}/cmdext.h
+prev cmdext.h
+exec - proto -p -s -l ${PACKAGEROOT}/lib/package/ast.lic '-o since=1992,author=gsf+dgk' cmdext.h > 1.${COTEMP}.x
+exec - if cmp 2>/dev/null -s ${PACKAGE_ast_INCLUDE}/cmdext.h 1.${COTEMP}.x
+exec - then rm -f 1.${COTEMP}.x
+exec - else mv 1.${COTEMP}.x ${PACKAGE_ast_INCLUDE}/cmdext.h
+exec - fi
+done ${PACKAGE_ast_INCLUDE}/cmdext.h generated
+make ${PACKAGE_ast_INCLUDE}/cmdlist.h
+make cmdlist.h
+exec - {
+exec - cat <<!
+exec - #pragma prototyped
+exec - /*
+exec - * -lcmd function list -- define your own CMDLIST()
+exec - */
+exec -
+exec - !
+exec - sed \
+exec - -e '/^b_[a-z_][a-z_0-9]*(/!d' \
+exec - -e 's/^b_//' \
+exec - -e 's/(.*//' \
+exec - -e 's/.*/CMDLIST(&)/' \
+exec - cmdinit.c basename.c cat.c chgrp.c chmod.c chown.c cksum.c cmp.c comm.c cp.c cut.c dirname.c date.c expr.c fds.c fmt.c fold.c getconf.c head.c id.c join.c ln.c logname.c md5sum.c mkdir.c mkfifo.c mktemp.c mv.c paste.c pathchk.c pids.c rev.c rm.c rmdir.c stty.c sum.c sync.c tail.c tee.c tty.c uname.c uniq.c vmstate.c wc.c revlib.c wclib.c fts_fix.c lib.c |
+exec - sort -u
+exec - } > 1.${COTEMP}.h
+exec - if cmp 2>/dev/null -s 1.${COTEMP}.h cmdlist.h
+exec - then rm -f 1.${COTEMP}.h
+exec - else mv 1.${COTEMP}.h cmdlist.h
+exec - fi
+done cmdlist.h generated
+exec - proto -p -s -l ${PACKAGEROOT}/lib/package/ast.lic '-o since=1992,author=gsf+dgk' cmdlist.h > 1.${COTEMP}.x
+exec - if cmp 2>/dev/null -s ${PACKAGE_ast_INCLUDE}/cmdlist.h 1.${COTEMP}.x
+exec - then rm -f 1.${COTEMP}.x
+exec - else mv 1.${COTEMP}.x ${PACKAGE_ast_INCLUDE}/cmdlist.h
+exec - fi
+done ${PACKAGE_ast_INCLUDE}/cmdlist.h generated
+done install virtual
+make test
+done test dontcare virtual
diff --git a/src/lib/libcmd/RELEASE b/src/lib/libcmd/RELEASE
new file mode 100644
index 0000000..436cfbe
--- /dev/null
+++ b/src/lib/libcmd/RELEASE
@@ -0,0 +1,328 @@
+12-02-14 rm.c: --force ignores no file operands specified
+12-01-10 b_* (int, char**, void*) => (int, char**, Shbltin_t*)
+11-08-27 pids.c: add getsid() iffe test
+10-08-16 chmod.c: add -l alias for { -h --symlink }
+11-08-16 chgrp.c: change lchmod() ref to lchown()
+11-05-03 cp.c: do not delete src if mv to dest fails -- doh
+11-03-28 chmod.c,chgrp.c: fix --symlink logic
+11-03-26 rm.c: don't eaccess() check symlinks!
+11-01-27 date: add { -R, --rfc-2822, -T, --rfc-3339=type }
+11-01-03 chgrp.c: --symlink => --physical
+10-12-10 rm.c: fix not-writable logic
+10-12-01 tee.c: add iterrupt logic for slow open(1) -- needs to be generalized
+10-11-30 chgrp.c: add -N,--numeric to bypass name lookup
+10-10-20 cp: add --timestamps (preserv timestamps and permissions)
+10-10-20 ln: fix 'cannot replace existing file' logic
+10-10-10 cp,mv: add --remove-destination
+10-08-11 cp.c,expr.c: use conformance("standard",0) test
+10-08-11 cut.c: use mbnsize() instead of mblen() (for ast C.UTF-8)
+10-07-28 chgrp.c,chmod.c,cksum.c: fts_path for diagnostics, not fts_accpath!
+10-06-14 rm.c: fix -rfu logic
+10-06-12 paste.c: repeat after me: do not modify argv[i]
+10-06-01 sync with ast api 20100601
+10-05-09 tail.c: fix -0f bug that inially listed the entire file
+10-05-06 basename.c: add { -a,--all -s,--suffux=suffix } from BSD
+10-04-12 cat.c: fix -v bug that dumped core and make consistent with cmp --print-chars
+10-04-11 cmp.c: add --print-bytes, --count=n, --differences=n
+10-04-08 vmstate.c: add { method flags } vars for Vmstat_t.mode
+10-04-08 mkdir.c: fix check for { S_ISUID S_ISGID S_ISVTX } after successful mkdir(2)
+10-04-01 stty.c: add --fd=fd option
+10-03-23 tail.c: fix -f large initial offset bug that didn't copy all data
+10-03-07 tail.c: sfsync(sfstdout) after all -f done, fix -f partial line
+10-03-05 mktemp.c: add --regress=seed for testing
+10-03-05 vmstate.c: add
+10-01-26 tail.c: -f sleep(1) only if no progress from last round of checks
+10-01-20 fts_fix.[ch]: use <fts_fix.h> instead of <fts.h> (see fts_fix.c)
+10-01-20 cp.c: free(state) if called from old shell
+09-12-10 join.c: <wctype.h> for iswspace()!
+09-12-04 cmd.h: fix CMD_DYNAMIC logic
+09-12-04 cut.c: handle -d mb
+09-12-03 mkdir.c: add --verbose
+09-11-30 cat.c,date.c,cksum.c: drop setlocale() call already done by optget()
+09-11-30 join.c: handle -t mb
+09-11-28 wclib.c: { -w -L } mb independent of -m
+09-11-28 paste.c: handle -d mb
+09-11-28 uniq.c: handle -s mb
+09-11-28 cksum.c: FTS_SEEDOTDIR by default
+09-09-09 fds.c: add --unit=fd
+09-08-25 tail.c: initialize Tail_t.fifo=0 !!
+09-08-15 tail.c: fix fifo logic
+09-08-11 wc.c: add setlocale(LC_CTYPE,"C") cleanup, add utf8 optimzations
+09-08-10 uniq.c: replace -c 1..9999 sfsprintf() with inline conversion
+09-08-01 join.c: fix empty field null pointer deref
+09-07-23 pathchk.c: add -P,--path and -a,--all
+09-07-02 chgrp.c,chmod.c,cksum.c: fts_flags() default only if not --recursive
+09-06-19 cmd.h,cmdinit.c: add ERROR_CALLBACK for ERROR_NOTIFY main() callback
+09-06-19 mktemp.c: --unsafe now checks and prints path but does create
+09-06-19 tee.c: add ERROR_CALLBACK for tee_cleanup() sfio discipline pop
+09-06-18 rm.c: handle interrupts during interactive query
+09-06-18 cp.c: handle interrupts during interactive query
+09-05-25 tail.c: fix old style option logic to handle --invalid-long-option
+09-05-24 tail.c: -r == +1r
+09-05-01 mktemp.c: handle foo/prefix, add -p dir and -u
+09-03-31 cat.c: handle --no* options
+09-03-15 tail.c: fix --timeout termination logic
+09-03-03 tee.c: clean up sfio disciplines on error
+09-03-03 cat.c: fix -v|-e|-n|-B interaction bugs
+09-02-14 tail.c: fix VSC failures
+09-02-14 join.c: fix VSC failure
+09-02-02 uniq.c: document -number == -fnumber, +number == -snumber
+09-02-02 tail.c: fix usage[] for negative offsets, add sun -b
+09-02-02 mktemp.c: add
+09-02-02 features/utsname: UWIN _UNAME_os_DEFAULT => UWIN
+09-01-31 dirname.c: add experimental { -f -r -x } for pathpath(3)
+09-01-05 cmp.c: fix EOF diagnostic to conform to posix
+09-01-03 mkfifo.c: fix --mode=mode logic
+08-12-07 date.c: add %[_][EO]K for [space pad] [full|long] iso docs
+08-11-10 stty.c: check for -t grouping so -tostop != -t -ostop
+08-10-15 rm.c: handle 'rm -f x x' => exit 0
+08-09-08 stty.c: #ifdef guard TAB[012] -- freebsd: damn the posix, full speed ahead
+08-06-17 shcmd.h: move to libast
+08-04-24 uniq.c: add optget() 'n' option for -1 => -f1
+08-04-24 getconf.c: clarify diffs between "name - value" and "name = value"
+08-04-01 cut.c: add write error check
+08-04-01 paste.c: fix --noserial stream vector access bug
+08-04-01 pids.c: add ls/ps style --format=format
+08-04-01 stty.c: fix off2 unitialized reference
+08-03-28 chgrp.c: add --before=file
+08-03-14 pids.c: add
+08-03-11 chgrp.c: fix -m to use uid:gid as lookup key
+08-02-11 Makefile: add -lmd possibly required by sumlib.o -- hack alert
+08-01-30 expr.c: fix <=0 type that broke substr * 1 * -- wow
+07-12-13 cp.c: fix builtin state reinitialization
+07-11-29 rev.c: honor multibyte locales
+07-11-27 cp.c: open non-existent destination with O_EXCL
+07-11-27 stty.c: add -t,--terminal-group to list tty pgrp
+07-11-27 cksum.c: --silent -s => -S, -s == -x sys5 for gnu compatibility
+07-11-11 tee.c: drop ancient bsd compatibility "-" operand => SIGINT
+07-10-29 cksum.c: add SUM_LEGACY for -r
+07-10-12 cp.c: plug usage string memory leak by using per-builtin state
+07-09-21 cksum.c: add sumprint() default scale arg, --scale, --bsd for solaris
+07-09-10 chmod.c: add --show,-n
+07-07-27 wclib.c: bias <wchar.h> checks for modern unix
+07-07-17 cat.c: fix --squeeze-blank to reduce multiple blank lines to *one*
+07-05-20 cmd.h: handle msvc's balk at if(0)0=0;
+07-05-20 cksum.c: #include <modex.h>
+07-05-11 cmd.h: add _CMD_CONTEXT_OK() to verify >= 20070511 context
+07-05-09 fds.c: handle ipv6 sockets
+07-05-09 cmd.h: <shbltin.h> : cmdquit() => sh_checksig(context)
+07-04-25 mkdir.c: force (S_ISVTX|S_ISUID|S_ISGID) after mkdir(2)
+07-04-24 procrun.c: add -last intercept => sh_run() and whence -q
+07-04-19 uname.c: name operands first checked for CS_NAME, then NAME
+07-03-28 date.c: add --unelapsed=scale, -U: fmtelapsed() => strelapsed()
+07-03-25 wclib.h: iswspace() requires <wctype.h>!
+07-03-11 tty.c: add sysV --line-number, -l
+07-02-26 Makefile: sumlib.o: direct extract from +lsum (vcodex someday)
+07-02-24 Makefile: tweak cmdext.h action for --mam bootstrap
+07-02-09 Makefile: { cmdext.h cmdlist.h } depend on *.c list!
+07-02-09 Makefile: +lsum to bring in static -lsum (no dynamic right now)
+07-02-07 cksum.c: move from src/cmd/std with ftwalk => fts
+07-02-07 getconf.c: handle /bin == /usr/bin in defer logic
+07-01-26 chmod.c: don't FTS_FOLLOW if !FTS_PHYSICAL
+07-01-23 cut.c: Cut_t variable dimension list[] must be last member
+07-01-22 uname.c: fix -h typo that clobbered astconf() state -- ouch
+07-01-02 fmt.c: fix buffer splice off by one bug -- what else
+06-11-23 cmd.h: because of proto cmdinit cannot be a function like macro
+06-11-21 cp.c: fix 06-10-31 const dot[] readonly assignment
+06-11-15 cp.c: fix 06-10-31 ln -s enoent bug
+06-11-11 getconf.c: let astconf() handle "undefined" vs. ""
+06-11-11 getconf.c: fix deferred getconf path search
+06-11-11 fmt.c: handle two char { \t \n } in --usage ouput
+06-10-31 global edit to eliminate most non-const static data0
+06-10-31 use <cmd.h> for all b_*() implementations; drop <cmdlib.h>
+06-10-31 cmd.h: add CMD_ prefix to { BUILTIN DYNAMIC STANDALONE }
+06-10-31 join.c: tone down /tmp usage vi SFSK_DISCARD
+06-10-31 cp.c,rm.c: update to <fts.h> to accomodate non-static data
+06-10-29 date.c: "...%H%..." => "...%H" "%..." to avoid SCCS conflict
+06-10-26 fds.c: handle sctp
+06-10-18 tail.c: fix invalid suffix infinite loop
+06-10-11 chgrp.c,cp.c: add sfstruse() error checks
+06-10-10 tee.c: add --linebuffer, -l
+06-10-06 getconf.c: preserve native getconf(1) known variable behavior
+06-10-04 sync.c: add (thanks to Roland Mainz)
+06-10-04 getconf.c: add -v specification => run native getconf(1)
+06-09-28 stty.c: static setmode() => set() for darwin.i386
+06-09-27 head.c: handle -1c => -c1
+06-09-19 pathchk.c: pathconf() => astconf()
+06-09-11 tail.c: handle compatibility corner cases
+06-09-08 date.c: add output write error diagnostic
+06-09-04 tail.c: fix initial position for -n0, no args => no -f
+06-08-28 uniq.c: add -D,--all-repeated
+06-08-25 wc.c,wclib.c: add -L,--longest-line,WC_LONGEST
+06-08-24 wc.c,wclib.c: implement -m and WC_MBYTE
+06-08-24 rmdir.c: -sp applies to every message, add gnu -e
+06-08-23 rmdir.c: add solaris --suppress, -s
+06-08-23 mkdir.c: don't add 0300 to -p final dir mode
+06-07-17 cut.c: handle last line with no newline
+06-07-17 cut.c: --output-delimiter == --line-delimiter
+06-06-25 chmod.c: mask -c output with S_IPERM
+06-05-09 uname.c: add -o; change -a to match linux
+06-05-03 date.c: add --last -L to list last of multiple time args
+06-02-14 tail.c: fix -f bug that lost fast stream data
+06-02-11 getconf.c: exit 1 if name invalid -- duh
+06-01-28 cp.c,rm.c: fix astquery() 'q' to return and not exit()
+05-08-11 fmt.c: fix -o to handle raw --usage strings
+05-05-17 cat.c,head.c: disable EPIPE error messages
+05-04-14 chgrp.c: -f means all non-syntax error messages
+05-04-11 fds.c: add from old internal open(1)
+05-04-09 cmdext.h,cmdlist.h: generate from source -- about time
+05-03-24 features/symlink: verify { lchmod lchown } implementations
+05-03-07 date.c: add --listzones to list the time zone table
+05-02-14 chmod.c: add --reference=file
+05-01-11 cat.c: restore output stream to binary mode on exit
+04-12-15 cp.c: add --preserve high resolution time support
+04-12-08 date.c: add high resolution time support
+04-12-01 cmp.c: fix %6I*ld => %6I*d -- doh
+ fmt.c: handle "\n\n operands \n\n"
+ head.c: handle -cN -nN, N > 4Gb
+04-11-22 cmp.c: handle >2G chars/lines
+04-11-18 fold.c: add --prepend=text, --append=text
+04-10-31 tail.c: use SF_LOCKR macro
+04-10-28 tail.c: use strtol() for old stype [+-]number[suffix] -- doh
+04-10-22 cp.c: check rename() errno==ENOENT to retain destination
+04-10-11 fmt.c: fix -o,--optget sublist bugs
+ tail.c: use strton() for number conversion
+04-10-08 pathchk.c: add empty path and -p - first component char
+04-10-01 fmt.c: add -o,--optget concatenated usage string format
+ stty.c: context is ERROR_INTERCATIVE
+ rm.c: restore 3d before exit
+04-09-24 pathchk.c: fix docs
+04-09-14 date.c: add %| and %& --parse docs
+04-08-27 cp.c: add FTW_DC check -- duh
+04-08-01 fmt.c: handle last char != '\n'
+04-07-22 date.c,uname.c: access() => eaccess()
+04-07-01 fmt.c: handle large input lines -- ouch
+04-06-11 id.c: fix -r to output something!
+04-05-27 expr.c: fix `:' op subexpression output
+04-04-15 chmod.c: follow symlink for relative mode
+04-04-12 Makefile: add STDCHMOD (for osf.alpha)
+04-03-19 tail.c: handle -f sfreserve() large chunk failure
+04-02-29 cp.c: decouple -f and -i for standard CONFORMANCE
+ cp.c: mv now attempts rename() before remove()+rename()
+ date.c: -f format or +format disables system clock set
+04-02-14 cp.c: add -F --fsync to call fsync(2) for each copied file
+04-01-05 head.c: -s now uses opt_info.number for >2Gb skip
+03-09-18 tail.c: add --log
+03-09-11 rm.c: add --unconditional
+03-08-11 fold.c: add --delimiter=c to break at c
+03-07-28 features/time: change settimeofday() test to 2nd arg of (void*)0
+ expr.c: add {match,substr,index,length,quote}
+03-07-15 fmt.c: fix trailing space bug
+03-06-20 uname.c: fix -p constant string overwrite
+03-06-04 stty.c: add undef to control assignment docs
+03-05-31 uname.c: add -f and sysinfo()/confstr() compatibility via astconf()
+03-05-27 rm.c: fix inappropriate "/.." append path overflow
+ cut.c: snarf from dgk
+03-05-18 rm.c: check st_nlink to verify progress w.r.t. ftwalk/fts
+03-05-15 join.c: fix stealth -v2 bug (thanks ahs)
+03-05-04 wc.c: drop trailing space for `wc -l < file'
+03-03-21 date.c: add %Q/recent/distant/ docs
+03-02-19 date.c: fix %+|!flag docs
+02-11-14 update for cmdinit() 4th arg and ERROR_NOTIFY for interrupt cleanup
+02-10-02 date.c: tmform() => tmfmt()
+02-09-30 date.c,uname.c: change execv() calls to procrun(): exec|exit => bad
+02-09-06 wclib.c: fix 1 char-at-a-time miscount bug
+02-08-19 chgrp.c: convert to use <cdt.h>
+02-07-23 join.c: fix comm snarf typo
+02-04-05 date.c: add %u
+02-01-24 stty.c: ifdef a few more macros for uts (yes, its still running)
+01-12-14 date.c: clarify %z doc
+01-10-31 mkdir.c: mkdir() on existing dir could fail with errno!=EEXIST
+ uname.c: add execve() loop check for unknown options
+01-10-29 tail.c: SF_SHARE on only if not reading through EOF
+01-10-11 getconf.c: fix usage typos
+01-09-11 cp.c,cmd.h: handle . in argv[0]
+ cp.c: add O_BINARY to all open() calls
+01-09-06 tail: input streams must be SF_SHARE -- duh
+01-07-16 stty: fix cntl() macro for CC_NATIVE!=CC_ASCII
+01-05-31 date: fix /bin/date fallback logic
+ stty: fix a few mismatched flags, -a and -g option logic
+ stty: tone down sane to modify current settings rather than from zero
+01-05-01 uname: -i => -h, add sol.sun4 -i, add sgi -R, punt to /usr/bin/uname
+01-04-17 date,rm: add
+01-03-07 cp: fix readonly string mod on "."
+01-01-23 cp: `cp foo' => `cp foo .' only for CONFORMANCE!=standard
+00-12-01 cut: multibyte support
+00-10-31 mkdir: handle races by checking EEXIST
+00-09-20 cp: copy argv to stack before modifying in place
+00-05-18 add setlocale(LC_ALL,"")
+00-04-30 join: drop weird opt_info.argv reference
+00-03-17 expr: add == operator -- duh
+ cp,ln,mv: delay pathcanon() on destination to verify `cp a b/.'
+ getconf: use astgetconf for proper message control
+ ERROR_translate: dictionary update
+00-03-08 tail: handle multiple -f files
+00-03-07 fmt: add
+00-03-07 dirname: handle PATH_LEADING_SLASHES as documented
+ tail: accept + options
+00-02-14 chmod: --ignore-umask to ignore umask(2) in symbolic expressions
+ chmod,chgrp,cp: use FTS_NOSEEDOTDIR for correct path construction
+ cat: fix -n (was ignored, wow)
+00-01-27 getconf: add "-a" and "-v spec" for sol7.* compatibility
+99-09-09 join: fix -j1 vs. -j 1, add --ignorecase
+99-06-22 paste: defualt delim in writable string
+99-06-16 cat: fix --dos-ouput typo
+99-06-11 cp: tighten chown() diagnostics
+99-06-08 expr: nothing for NULL string bug fix
+99-05-21 paste: fix missing newline columnize bug
+99-05-20 mv: do not check for `mv foo foo' since rename() handles it
+99-05-01 cmp,comm,cp/mv/ln,expr,fold,getconf,head: long options
+ join,logname,paste,pathchk,tail,tee: long options
+99-04-10 uname: long options, stdize -a
+ chmod,head,tail,rev: long options
+ cut: long options, pass regression test 02
+99-04-07 cat: long options, fix O_TEXT modes
+99-01-11 tail: fix +n
+ join: another ggs/psm bug
+ join: all 1 and/or 2 to be unseekable
+99-01-01 cp: fix -p
+ chmod: drop -l option because of clash with l (lock) mode
+98-12-25 cat: add -T to sfopen(,,"rt")
+98-11-11 chgrp,chmod: cannot open file stream => not found
+ join: fix another ggs/psm bug; thanks guys
+98-10-20 cp: fix cp -rp to update dir times too
+98-09-22 join: fix ggs null field bug
+98-08-11 join: fix last regression test bug
+98-05-29 join: add jp->common to handle boundary conditions
+98-03-11 cat,cp,rev,tee: fix sfmove() error checks
+98-03-01 join: fix bug that emitted records more than once after eof
+ cp: fix sfmove() error check
+98-02-14 cp: -R physical, -[HLP], -r getconf(PATH_RESOLVE)
+98-01-11 cp: check sfclose() return value
+98-01-07 chown,chgrp,chmod: use fts for -R
+ mkdir: fix -p default mode
+97-12-07 mkdir: fix umask() reset
+97-11-11 chown,chgrp: proper interpretation of -h,-l for lchown()
+ chown,chgrp: only chown() if uid or gid change
+97-10-31 mkdir: do umask right
+97-08-11 cmdinit: clear opt_info.index to allow multiple calls
+ cp,ln,mv: add
+97-07-17 join: fix a few more -a bugs
+97-05-31 expr: optget() only if CONFORMANCE==standard
+97-04-01 join: fix a few bugs that make it work!
+96-12-25 head: sfset(sfstdin,SF_SHARE,1)
+ Makefile: add -last to cmd lib list
+ drop function __IMPORT__
+96-08-11 tail: check for truncated file and rewind for -f
+96-04-08 update <cmd.h>
+96-02-29 uname: -a like std, -l for everything
+ id: add -a (default)
+96-02-14 wc: speed up inner loop newline breakout
+96-01-30 unused var cleanup
+96-01-01 AT&T Research now
+ pathchk: handle getcwd(0,0) error
+ expr: switch to <regex.h>
+95-11-11 add expr.c
+ fix cut exit code and -s optimization
+95-10-11 add extern b_* to cmd.h
+ add void* context 3rd arg to b_main()
+95-05-09 add getconf
+ cat -u avoids mmap
+ add chown|chgrp -m uid|gid map file
+ add chown|chgrp -P for systems with lchown(2)
+ chown|chgrp -P => lstat() too!
+ chmod|chown|chgrp -HLP
+95-04-01 version 1.2
+ add rmdir
+95-02-14 fix mkdir -p symlink bug
+ fix mkdir '/' skip bug that went one too far
diff --git a/src/lib/libcmd/basename.c b/src/lib/libcmd/basename.c
new file mode 100644
index 0000000..9461183
--- /dev/null
+++ b/src/lib/libcmd/basename.c
@@ -0,0 +1,139 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * namebase pathname [suffix]
+ *
+ * print the namebase of a pathname
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: basename (AT&T Research) 2010-05-06 $\n]"
+USAGE_LICENSE
+"[+NAME?basename - strip directory and suffix from filenames]"
+"[+DESCRIPTION?\bbasename\b removes all leading directory components "
+ "from the file name defined by \astring\a. If the file name defined by "
+ "\astring\a has a suffix that ends in \asuffix\a, it is removed as "
+ "well.]"
+"[+?If \astring\a consists solely of \b/\b characters the output will be "
+ "a single \b/\b unless \bPATH_LEADING_SLASHES\b returned by "
+ "\bgetconf\b(1) is \b1\b and \astring\a consists of multiple \b/\b "
+ "characters in which case \b//\b will be output. Otherwise, trailing "
+ "\b/\b characters are removed, and if there are any remaining \b/\b "
+ "characters in \astring\a, all characters up to and including the last "
+ "\b/\b are removed. Finally, if \asuffix\a is specified, and is "
+ "identical the end of \astring\a, these characters are removed. The "
+ "characters not removed from \astring\a will be written on a single line "
+ "to the standard output.]"
+"[a:all?All operands are treated as \astring\a and each modified "
+ "pathname is printed on a separate line on the standard output.]"
+"[s:suffix?All operands are treated as \astring\a and each modified "
+ "pathname, with \asuffix\a removed if it exists, is printed on a "
+ "separate line on the standard output.]:[suffix]"
+"\n"
+"\n string [suffix]\n"
+"string ...\n"
+"\n"
+"[+EXIT STATUS?]"
+ "{"
+ "[+0?Successful Completion.]"
+ "[+>0?An error occurred.]"
+ "}"
+"[+SEE ALSO?\bdirname\b(1), \bgetconf\b(1), \bbasename\b(3)]"
+;
+
+
+#include <cmd.h>
+
+static void namebase(Sfio_t *outfile, register char *pathname, char *suffix)
+{
+ register char *first, *last;
+ register int n=0;
+ for(first=last=pathname; *last; last++);
+ /* back over trailing '/' */
+ if(last>first)
+ while(*--last=='/' && last > first);
+ if(last==first && *last=='/')
+ {
+ /* all '/' or "" */
+ if(*first=='/')
+ if(*++last=='/') /* keep leading // */
+ last++;
+ }
+ else
+ {
+ for(first=last++;first>pathname && *first!='/';first--);
+ if(*first=='/')
+ first++;
+ /* check for trailing suffix */
+ if(suffix && (n=strlen(suffix)) && n<(last-first))
+ {
+ if(memcmp(last-n,suffix,n)==0)
+ last -=n;
+ }
+ }
+ if(last>first)
+ sfwrite(outfile,first,last-first);
+ sfputc(outfile,'\n');
+}
+
+int
+b_basename(int argc, register char** argv, Shbltin_t* context)
+{
+ char* string;
+ char* suffix = 0;
+ int all = 0;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ all = 1;
+ continue;
+ case 's':
+ all = 1;
+ suffix = opt_info.arg;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if (error_info.errors || argc < 1 || !all && argc > 2)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (!all)
+ namebase(sfstdout, argv[0], argv[1]);
+ else
+ while (string = *argv++)
+ namebase(sfstdout, string, suffix);
+ return 0;
+}
diff --git a/src/lib/libcmd/cat.c b/src/lib/libcmd/cat.c
new file mode 100644
index 0000000..dae2a72
--- /dev/null
+++ b/src/lib/libcmd/cat.c
@@ -0,0 +1,557 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Bell Laboratories
+ *
+ * cat
+ */
+
+#include <cmd.h>
+#include <fcntl.h>
+
+static const char usage[] =
+"[-?\n@(#)$Id: cat (AT&T Research) 2012-01-01 $\n]"
+USAGE_LICENSE
+"[+NAME?cat - concatenate files]"
+"[+DESCRIPTION?\bcat\b copies each \afile\a in sequence to the standard"
+" output. If no \afile\a is given, or if the \afile\a is \b-\b,"
+" \bcat\b copies from standard input starting at the current location.]"
+
+"[b:number-nonblank?Number lines as with \b-n\b but omit line numbers from"
+" blank lines.]"
+"[d:dos-input?Input files are opened in \atext\amode which removes carriage"
+" returns in front of new-lines on some systems.]"
+"[e?Equivalent to \b-vE\b.]"
+"[n:number?Causes a line number to be inserted at the beginning of each line.]"
+"[s?Equivalent to \b-S\b for \aatt\a universe and \b-B\b otherwise.]"
+"[t?Equivalent to \b-vT\b.]"
+"[u:unbuffer?The output is not delayed by buffering.]"
+"[v:show-nonprinting|print-chars?Print characters as follows: space and "
+ "printable characters as themselves; control characters as \b^\b "
+ "followed by a letter of the alphabet; and characters with the high bit "
+ "set as the lower 7 bit character prefixed by \bM^\b for 7 bit "
+ "non-printable characters and \bM-\b for all other characters. If the 7 "
+ "bit character encoding is not ASCII then the characters are converted "
+ "to ASCII to determine \ahigh bit set\a, and if set it is cleared and "
+ "converted back to the native encoding. Multibyte characters in the "
+ "current locale are treated as printable characters.]"
+"[A:show-all?Equivalent to \b-vET\b.]"
+"[B:squeeze-blank?Multiple adjacent new-line characters are replace by one"
+" new-line.]"
+"[D:dos-output?Output files are opened in \atext\amode which inserts carriage"
+" returns in front of new-lines on some systems.]"
+"[E:show-ends?Causes a \b$\b to be inserted before each new-line.]"
+"[R:regress?Regression test defaults: \b-v\b buffer size 4.]"
+"[S:silent?\bcat\b is silent about non-existent files.]"
+"[T:show-blank?Causes tabs to be copied as \b^I\b and formfeeds as \b^L\b.]"
+
+"\n"
+"\n[file ...]\n"
+"\n"
+
+"[+SEE ALSO?\bcp\b(1), \bgetconf\b(1), \bpr\b(1)]"
+;
+
+#define RUBOUT 0177
+
+/* control flags */
+#define B_FLAG (1<<0)
+#define E_FLAG (1<<1)
+#define F_FLAG (1<<2)
+#define N_FLAG (1<<3)
+#define S_FLAG (1<<4)
+#define T_FLAG (1<<5)
+#define U_FLAG (1<<6)
+#define V_FLAG (1<<7)
+#define D_FLAG (1<<8)
+#define d_FLAG (1<<9)
+
+/* character types */
+#define T_ERROR 1
+#define T_EOF 2
+#define T_ENDBUF 3
+#define T_NEWLINE 4
+#define T_CONTROL 5
+#define T_EIGHTBIT 6
+#define T_CNTL8BIT 7
+
+#define printof(c) ((c)^0100)
+
+typedef void* (*Reserve_f)(Sfio_t*, ssize_t, int);
+
+#ifndef sfvalue
+#define sfvalue(f) ((f)->_val)
+#endif
+
+static void*
+regress(Sfio_t* sp, ssize_t n, int f)
+{
+ void* r;
+
+ if (!(r = sfreserve(sp, 4, f)))
+ r = sfreserve(sp, n, f);
+ else if (sfvalue(sp) > 4)
+ sfvalue(sp) = 4;
+ return r;
+}
+
+/*
+ * called for any special output processing
+ */
+
+static int
+vcat(register char* states, Sfio_t* ip, Sfio_t* op, Reserve_f reserve, int flags)
+{
+ register unsigned char* cp;
+ register unsigned char* pp;
+ unsigned char* cur;
+ unsigned char* end;
+ unsigned char* buf;
+ unsigned char* nxt;
+ register int n;
+ register int line;
+ register int raw;
+ int last;
+ int c;
+ int m;
+ int any;
+ int header;
+
+ unsigned char meta[3];
+ unsigned char tmp[32];
+
+ meta[0] = 'M';
+ last = -1;
+ *(cp = buf = end = tmp) = 0;
+ any = 0;
+ header = flags & (B_FLAG|N_FLAG);
+ line = 1;
+ states[0] = T_ENDBUF;
+ raw = !mbwide();
+ for (;;)
+ {
+ cur = cp;
+ if (raw)
+ while (!(n = states[*cp++]));
+ else
+ for (;;)
+ {
+ while (!(n = states[*cp++]));
+ if (n < T_CONTROL)
+ break;
+ if ((m = mbsize(pp = cp - 1)) > 1)
+ cp += m - 1;
+ else
+ {
+ if (m <= 0)
+ {
+ if (cur == pp)
+ {
+ if (last > 0)
+ {
+ *end = last;
+ last = -1;
+ c = end - pp + 1;
+ if ((m = mbsize(pp)) == c)
+ {
+ any = 1;
+ if (header)
+ {
+ header = 0;
+ sfprintf(op, "%6d\t", line);
+ }
+ sfwrite(op, cur, m);
+ *(cp = cur = end) = 0;
+ }
+ else
+ {
+ memcpy(tmp, pp, c);
+ if (!(nxt = (unsigned char*)(*reserve)(ip, SF_UNBOUND, 0)))
+ {
+ states[0] = sfvalue(ip) ? T_ERROR : T_EOF;
+ *(cp = end = tmp + sizeof(tmp) - 1) = 0;
+ last = -1;
+ }
+ else if ((n = sfvalue(ip)) <= 0)
+ {
+ states[0] = n ? T_ERROR : T_EOF;
+ *(cp = end = tmp + sizeof(tmp) - 1) = 0;
+ last = -1;
+ }
+ else
+ {
+ cp = buf = nxt;
+ end = buf + n - 1;
+ last = *end;
+ *end = 0;
+ }
+ mb:
+ if ((n = end - cp + 1) >= (sizeof(tmp) - c))
+ n = sizeof(tmp) - c - 1;
+ memcpy(tmp + c, cp, n);
+ if ((m = mbsize(tmp)) >= c)
+ {
+ any = 1;
+ if (header)
+ {
+ header = 0;
+ sfprintf(op, "%6d\t", line);
+ }
+ sfwrite(op, tmp, m);
+ cur = cp += m - c;
+ }
+ }
+ continue;
+ }
+ }
+ else
+ {
+ cp = pp + 1;
+ n = 0;
+ }
+ }
+ break;
+ }
+ }
+ c = *--cp;
+ if ((m = cp - cur) || n >= T_CONTROL)
+ {
+ flush:
+ any = 1;
+ if (header)
+ {
+ header = 0;
+ sfprintf(op, "%6d\t", line);
+ }
+ if (m)
+ sfwrite(op, cur, m);
+ }
+ special:
+ switch (n)
+ {
+ case T_ERROR:
+ if (cp < end)
+ {
+ n = T_CONTROL;
+ goto flush;
+ }
+ return -1;
+ case T_EOF:
+ if (cp < end)
+ {
+ n = T_CONTROL;
+ goto flush;
+ }
+ return 0;
+ case T_ENDBUF:
+ if (cp < end)
+ {
+ n = T_CONTROL;
+ goto flush;
+ }
+ c = last;
+ if (!(nxt = (unsigned char*)(*reserve)(ip, SF_UNBOUND, 0)))
+ {
+ *(cp = end = tmp + sizeof(tmp) - 1) = 0;
+ states[0] = (m = sfvalue(ip)) ? T_ERROR : T_EOF;
+ last = -1;
+ }
+ else if ((m = sfvalue(ip)) <= 0)
+ {
+ *(cp = end = tmp + sizeof(tmp) - 1) = 0;
+ states[0] = m ? T_ERROR : T_EOF;
+ last = -1;
+ }
+ else
+ {
+ buf = nxt;
+ end = buf + m - 1;
+ last = *end;
+ *end = 0;
+ cp = buf;
+ }
+ if (c >= 0)
+ {
+ if (!(n = states[c]))
+ {
+ *(cur = tmp) = c;
+ m = 1;
+ goto flush;
+ }
+ if (raw || n < T_CONTROL)
+ {
+ cp--;
+ goto special;
+ }
+ tmp[0] = c;
+ c = 1;
+ goto mb;
+ }
+ break;
+ case T_CONTROL:
+ do
+ {
+ sfputc(op, '^');
+ sfputc(op, printof(c));
+ } while (states[c = *++cp] == T_CONTROL);
+ break;
+ case T_CNTL8BIT:
+ meta[1] = '^';
+ do
+ {
+ n = c & ~0200;
+ meta[2] = printof(n);
+ sfwrite(op, (char*)meta, 3);
+ } while (states[c = *++cp] == T_CNTL8BIT && raw);
+ break;
+ case T_EIGHTBIT:
+ meta[1] = '-';
+ do
+ {
+ meta[2] = c & ~0200;
+ sfwrite(op, (char*)meta, 3);
+ } while (states[c = *++cp] == T_EIGHTBIT && raw);
+ break;
+ case T_NEWLINE:
+ if (header && !(flags & B_FLAG))
+ sfprintf(op, "%6d\t", line);
+ if (flags & E_FLAG)
+ sfputc(op, '$');
+ sfputc(op, '\n');
+ if (!header || !(flags & B_FLAG))
+ line++;
+ header = !(flags & S_FLAG);
+ for (;;)
+ {
+ if ((n = states[*++cp]) == T_ENDBUF)
+ {
+ if (cp < end || last != '\n')
+ break;
+ if (!(nxt = (unsigned char*)(*reserve)(ip, SF_UNBOUND, 0)))
+ {
+ states[0] = sfvalue(ip) ? T_ERROR : T_EOF;
+ cp = end = tmp;
+ *cp-- = 0;
+ last = -1;
+ }
+ else if ((n = sfvalue(ip)) <= 0)
+ {
+ states[0] = n ? T_ERROR : T_EOF;
+ cp = end = tmp;
+ *cp-- = 0;
+ last = -1;
+ }
+ else
+ {
+ buf = nxt;
+ end = buf + n - 1;
+ last = *end;
+ *end = 0;
+ cp = buf - 1;
+ }
+ }
+ else if (n != T_NEWLINE)
+ break;
+ if (!(flags & S_FLAG) || any || header)
+ {
+ any = 0;
+ header = 0;
+ if ((flags & (B_FLAG|N_FLAG)) == N_FLAG)
+ sfprintf(op, "%6d\t", line);
+ if (flags & E_FLAG)
+ sfputc(op, '$');
+ sfputc(op, '\n');
+ }
+ if (!(flags & B_FLAG))
+ line++;
+ }
+ header = flags & (B_FLAG|N_FLAG);
+ break;
+ }
+ }
+}
+
+int
+b_cat(int argc, char** argv, Shbltin_t* context)
+{
+ register int n;
+ register int flags = 0;
+ register char* cp;
+ register Sfio_t* fp;
+ char* mode;
+ Reserve_f reserve = sfreserve;
+ int att;
+ int dovcat = 0;
+ char states[UCHAR_MAX+1];
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ att = !strcmp(astconf("UNIVERSE", NiL, NiL), "att");
+ mode = "r";
+ for (;;)
+ {
+ n = 0;
+ switch (optget(argv, usage))
+ {
+ case 'A':
+ n = T_FLAG|E_FLAG|V_FLAG;
+ break;
+ case 'B':
+ n = S_FLAG;
+ break;
+ case 'b':
+ n = B_FLAG;
+ break;
+ case 'd':
+ mode = opt_info.num ? "rt" : "r";
+ continue;
+ case 'D':
+ n = d_FLAG;
+ break;
+ case 'E':
+ n = E_FLAG;
+ break;
+ case 'e':
+ n = E_FLAG|V_FLAG;
+ break;
+ case 'n':
+ n = N_FLAG;
+ break;
+ case 'R':
+ reserve = opt_info.num ? regress : sfreserve;
+ continue;
+ case 's':
+ n = att ? F_FLAG : S_FLAG;
+ break;
+ case 'S':
+ n = F_FLAG;
+ break;
+ case 'T':
+ n = T_FLAG;
+ break;
+ case 't':
+ n = T_FLAG|V_FLAG;
+ break;
+ case 'u':
+ n = U_FLAG;
+ break;
+ case 'v':
+ n = V_FLAG;
+ break;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ if (!n)
+ break;
+ if (opt_info.num)
+ flags |= n;
+ else
+ flags &= ~n;
+ }
+ argv += opt_info.index;
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ memset(states, 0, sizeof(states));
+ if (flags&V_FLAG)
+ {
+ memset(states, T_CONTROL, ' ');
+ states[RUBOUT] = T_CONTROL;
+ memset(states+0200, T_EIGHTBIT, 0200);
+ memset(states+0200, T_CNTL8BIT, ' ');
+ states[RUBOUT|0200] = T_CNTL8BIT;
+ states['\n'] = 0;
+ }
+ if (flags&T_FLAG)
+ states['\t'] = T_CONTROL;
+ states[0] = T_ENDBUF;
+ if (att)
+ {
+ if (flags&V_FLAG)
+ {
+ states['\n'|0200] = T_EIGHTBIT;
+ if (!(flags&T_FLAG))
+ {
+ states['\t'] = states['\f'] = 0;
+ states['\t'|0200] = states['\f'|0200] = T_EIGHTBIT;
+ }
+ }
+ }
+ else if (flags)
+ {
+ if (!(flags&T_FLAG))
+ states['\t'] = 0;
+ }
+ if (flags&(V_FLAG|T_FLAG|N_FLAG|E_FLAG|B_FLAG|S_FLAG))
+ {
+ states['\n'] = T_NEWLINE;
+ dovcat = 1;
+ }
+ if (flags&d_FLAG)
+ sfopen(sfstdout, NiL, "wt");
+ if (cp = *argv)
+ argv++;
+ do
+ {
+ if (!cp || streq(cp, "-"))
+ {
+ fp = sfstdin;
+ if (flags&D_FLAG)
+ sfopen(fp, NiL, mode);
+ }
+ else if (!(fp = sfopen(NiL, cp, mode)))
+ {
+ if (!(flags&F_FLAG))
+ error(ERROR_system(0), "%s: cannot open", cp);
+ error_info.errors = 1;
+ continue;
+ }
+ if (flags&U_FLAG)
+ sfsetbuf(fp, (void*)fp, -1);
+ if (dovcat)
+ n = vcat(states, fp, sfstdout, reserve, flags);
+ else if (sfmove(fp, sfstdout, SF_UNBOUND, -1) >= 0 && sfeof(fp))
+ n = 0;
+ else
+ n = -1;
+ if (fp != sfstdin)
+ sfclose(fp);
+ if (n < 0 && errno != EPIPE && errno != EINTR)
+ {
+ if (cp)
+ error(ERROR_system(0), "%s: read error", cp);
+ else
+ error(ERROR_system(0), "read error");
+ }
+ if (sferror(sfstdout))
+ break;
+ } while (cp = *argv++);
+ if (sfsync(sfstdout))
+ error(ERROR_system(0), "write error");
+ if (flags&d_FLAG)
+ sfopen(sfstdout, NiL, "w");
+ return error_info.errors;
+}
diff --git a/src/lib/libcmd/chgrp.c b/src/lib/libcmd/chgrp.c
new file mode 100644
index 0000000..3c53528
--- /dev/null
+++ b/src/lib/libcmd/chgrp.c
@@ -0,0 +1,501 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * chgrp+chown
+ */
+
+static const char usage_1[] =
+"[-?@(#)$Id: chgrp (AT&T Research) 2011-04-28 $\n]"
+USAGE_LICENSE
+;
+
+static const char usage_grp_1[] =
+"[+NAME?chgrp - change the group ownership of files]"
+"[+DESCRIPTION?\bchgrp\b changes the group ownership of each file"
+" to \agroup\a, which can be either a group name or a numeric"
+" group id. The user ownership of each file may also be changed to"
+" \auser\a by prepending \auser\a\b:\b to the group name.]"
+;
+
+static const char usage_own_1[] =
+"[+NAME?chown - change the ownership of files]"
+"[+DESCRIPTION?\bchown\b changes the ownership of each file"
+" to \auser\a, which can be either a user name or a numeric"
+" user id. The group ownership of each file may also be changed to"
+" \auser\a by appending \b:\b\agroup\a to the user name.]"
+;
+
+static const char usage_2[] =
+"[b:before?Only change files with \bctime\b before (less than) the "
+ "\bmtime\b of \afile\a.]:[file]"
+"[c:changes?Describe only files whose ownership actually changes.]"
+"[f:quiet|silent?Do not report files whose ownership fails to change.]"
+"[h|l:symlink?Change the ownership of symbolic links on systems that "
+ "support \blchown\b(2). Implies \b--physical\b.]"
+"[m:map?The first operand is interpreted as a file that contains a map "
+ "of space separated \afrom_uid:from_gid to_uid:to_gid\a pairs. The "
+ "\auid\a or \agid\a part of each pair may be omitted to mean any \auid\a "
+ "or \agid\a. Ownership of files matching the \afrom\a part of any pair "
+ "is changed to the corresponding \ato\a part of the pair. The matching "
+ "for each file operand is in the order \auid\a:\agid\a, \auid\a:, "
+ ":\agid\a. For a given file, once a \auid\a or \agid\a mapping is "
+ "determined it is not overridden by any subsequent match. Unmatched "
+ "files are silently ignored.]"
+"[n:show?Show actions but don't execute.]"
+"[N:numeric?By default numeric user and group id operands are first "
+ "interpreted as names; if no name exists then they are interpreted as "
+ "explicit numeric ids. \b--numeric\b interprets numeric id operands as "
+ "numeric ids.]"
+"[r:reference?Omit the explicit ownership operand and use the ownership "
+ "of \afile\a instead.]:[file]"
+"[u:unmapped?Print a diagnostic for each file for which either the "
+ "\auid\a or \agid\a or both were not mapped.]"
+"[v:verbose?Describe changed permissions of all files.]"
+"[H:metaphysical?Follow symbolic links for command arguments; otherwise "
+ "don't follow symbolic links when traversing directories.]"
+"[L:logical|follow?Follow symbolic links when traversing directories.]"
+"[P:physical|nofollow?Don't follow symbolic links when traversing "
+ "directories.]"
+"[R:recursive?Recursively change ownership of directories and their "
+ "contents.]"
+"[X:test?Canonicalize output for testing.]"
+
+"\n"
+"\n"
+;
+
+static const char usage_3[] =
+" file ...\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files changed successfully.]"
+ "[+>0?Unable to change ownership of one or more files.]"
+"}"
+"[+SEE ALSO?\bchmod\b(1), \bchown\b(2), \btw\b(1), \bgetconf\b(1), \bls\b(1)]"
+;
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:hide lchown
+#else
+#define lchown ______lchown
+#endif
+
+#include <cmd.h>
+#include <cdt.h>
+#include <ls.h>
+#include <ctype.h>
+#include <fts_fix.h>
+
+#ifndef ENOSYS
+#define ENOSYS EINVAL
+#endif
+
+#include "FEATURE/symlink"
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:nohide lchown
+#else
+#undef lchown
+#endif
+
+typedef struct Key_s /* uid/gid key */
+{
+ int uid; /* uid */
+ int gid; /* gid */
+} Key_t;
+
+typedef struct Map_s /* uid/gid map */
+{
+ Dtlink_t link; /* dictionary link */
+ Key_t key; /* key */
+ Key_t to; /* map to these */
+} Map_t;
+
+#define NOID (-1)
+
+#define OPT_CHOWN 0x0001 /* chown */
+#define OPT_FORCE 0x0002 /* ignore errors */
+#define OPT_GID 0x0004 /* have gid */
+#define OPT_LCHOWN 0x0008 /* lchown */
+#define OPT_NUMERIC 0x0010 /* favor numeric ids */
+#define OPT_SHOW 0x0020 /* show but don't do */
+#define OPT_TEST 0x0040 /* canonicalize output */
+#define OPT_UID 0x0080 /* have uid */
+#define OPT_UNMAPPED 0x0100 /* unmapped file diagnostic */
+#define OPT_VERBOSE 0x0200 /* have uid */
+
+extern int lchown(const char*, uid_t, gid_t);
+
+/*
+ * parse uid and gid from s
+ */
+
+static void
+getids(register char* s, char** e, Key_t* key, int options)
+{
+ register char* t;
+ register int n;
+ register int m;
+ char* z;
+ char buf[64];
+
+ key->uid = key->gid = NOID;
+ while (isspace(*s))
+ s++;
+ for (t = s; (n = *t) && n != ':' && n != '.' && !isspace(n); t++);
+ if (n)
+ {
+ options |= OPT_CHOWN;
+ if ((n = t++ - s) >= sizeof(buf))
+ n = sizeof(buf) - 1;
+ *((s = (char*)memcpy(buf, s, n)) + n) = 0;
+ }
+ if (options & OPT_CHOWN)
+ {
+ if (*s)
+ {
+ n = (int)strtol(s, &z, 0);
+ if (*z || !(options & OPT_NUMERIC))
+ {
+ if ((m = struid(s)) != NOID)
+ n = m;
+ else if (*z)
+ error(ERROR_exit(1), "%s: unknown user", s);
+ }
+ key->uid = n;
+ }
+ for (s = t; (n = *t) && !isspace(n); t++);
+ if (n)
+ {
+ if ((n = t++ - s) >= sizeof(buf))
+ n = sizeof(buf) - 1;
+ *((s = (char*)memcpy(buf, s, n)) + n) = 0;
+ }
+ }
+ if (*s)
+ {
+ n = (int)strtol(s, &z, 0);
+ if (*z || !(options & OPT_NUMERIC))
+ {
+ if ((m = strgid(s)) != NOID)
+ n = m;
+ else if (*z)
+ error(ERROR_exit(1), "%s: unknown group", s);
+ }
+ key->gid = n;
+ }
+ if (e)
+ *e = t;
+}
+
+/*
+ * NOTE: we only use the native lchown() on symlinks just in case
+ * the implementation is a feckless stub
+ */
+
+int
+b_chgrp(int argc, char** argv, Shbltin_t* context)
+{
+ register int options = 0;
+ register char* s;
+ register Map_t* m;
+ register FTS* fts;
+ register FTSENT*ent;
+ register int i;
+ Dt_t* map = 0;
+ int logical = 1;
+ int flags;
+ int uid;
+ int gid;
+ char* op;
+ char* usage;
+ char* t;
+ Sfio_t* sp;
+ unsigned long before;
+ Dtdisc_t mapdisc;
+ Key_t keys[3];
+ Key_t key;
+ struct stat st;
+ int (*chownf)(const char*, uid_t, gid_t);
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+ flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
+ before = ~0;
+ if (!(sp = sfstropen()))
+ error(ERROR_SYSTEM|3, "out of space");
+ sfputr(sp, usage_1, -1);
+ if (error_info.id[2] == 'g')
+ sfputr(sp, usage_grp_1, -1);
+ else
+ {
+ sfputr(sp, usage_own_1, -1);
+ options |= OPT_CHOWN;
+ }
+ sfputr(sp, usage_2, -1);
+ if (options & OPT_CHOWN)
+ sfputr(sp, ERROR_translate(0, 0, 0, "[owner[:group]]"), -1);
+ else
+ sfputr(sp, ERROR_translate(0, 0, 0, "[[owner:]group]"), -1);
+ sfputr(sp, usage_3, -1);
+ if (!(usage = sfstruse(sp)))
+ error(ERROR_SYSTEM|3, "out of space");
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'b':
+ if (stat(opt_info.arg, &st))
+ error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
+ before = st.st_mtime;
+ continue;
+ case 'c':
+ case 'v':
+ options |= OPT_VERBOSE;
+ continue;
+ case 'f':
+ options |= OPT_FORCE;
+ continue;
+ case 'h':
+ options |= OPT_LCHOWN;
+ continue;
+ case 'm':
+ memset(&mapdisc, 0, sizeof(mapdisc));
+ mapdisc.key = offsetof(Map_t, key);
+ mapdisc.size = sizeof(Key_t);
+ if (!(map = dtopen(&mapdisc, Dtset)))
+ error(ERROR_exit(1), "out of space [id map]");
+ continue;
+ case 'n':
+ options |= OPT_SHOW;
+ continue;
+ case 'N':
+ options |= OPT_NUMERIC;
+ continue;
+ case 'r':
+ if (stat(opt_info.arg, &st))
+ error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
+ uid = st.st_uid;
+ gid = st.st_gid;
+ options |= OPT_UID|OPT_GID;
+ continue;
+ case 'u':
+ options |= OPT_UNMAPPED;
+ continue;
+ case 'H':
+ flags |= FTS_META|FTS_PHYSICAL;
+ logical = 0;
+ continue;
+ case 'L':
+ flags &= ~(FTS_META|FTS_PHYSICAL);
+ logical = 0;
+ continue;
+ case 'P':
+ flags &= ~FTS_META;
+ flags |= FTS_PHYSICAL;
+ logical = 0;
+ continue;
+ case 'R':
+ flags &= ~FTS_TOP;
+ logical = 0;
+ continue;
+ case 'X':
+ options |= OPT_TEST;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ continue;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if (error_info.errors || argc < 2)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ s = *argv;
+ if (options & OPT_LCHOWN)
+ {
+ flags &= ~FTS_META;
+ flags |= FTS_PHYSICAL;
+ logical = 0;
+ }
+ if (logical)
+ flags &= ~(FTS_META|FTS_PHYSICAL);
+ if (map)
+ {
+ if (streq(s, "-"))
+ sp = sfstdin;
+ else if (!(sp = sfopen(NiL, s, "r")))
+ error(ERROR_exit(1), "%s: cannot read", s);
+ while (s = sfgetr(sp, '\n', 1))
+ {
+ getids(s, &t, &key, options);
+ if (!(m = (Map_t*)dtmatch(map, &key)))
+ {
+ if (!(m = (Map_t*)stakalloc(sizeof(Map_t))))
+ error(ERROR_exit(1), "out of space [id dictionary]");
+ m->key = key;
+ m->to.uid = m->to.gid = NOID;
+ dtinsert(map, m);
+ }
+ getids(t, NiL, &m->to, options);
+ }
+ if (sp != sfstdin)
+ sfclose(sp);
+ keys[1].gid = keys[2].uid = NOID;
+ }
+ else if (!(options & (OPT_UID|OPT_GID)))
+ {
+ getids(s, NiL, &key, options);
+ if ((uid = key.uid) != NOID)
+ options |= OPT_UID;
+ if ((gid = key.gid) != NOID)
+ options |= OPT_GID;
+ }
+ switch (options & (OPT_UID|OPT_GID))
+ {
+ case OPT_UID:
+ s = ERROR_translate(0, 0, 0, " owner");
+ break;
+ case OPT_GID:
+ s = ERROR_translate(0, 0, 0, " group");
+ break;
+ case OPT_UID|OPT_GID:
+ s = ERROR_translate(0, 0, 0, " owner and group");
+ break;
+ default:
+ s = "";
+ break;
+ }
+ if (!(fts = fts_open(argv + 1, flags, NiL)))
+ error(ERROR_system(1), "%s: not found", argv[1]);
+ while (!sh_checksig(context) && (ent = fts_read(fts)))
+ switch (ent->fts_info)
+ {
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (options & OPT_LCHOWN)
+ {
+#if _lib_lchown
+ chownf = lchown;
+ op = "lchown";
+ goto commit;
+#else
+ if (!(options & OPT_FORCE))
+ {
+ errno = ENOSYS;
+ error(ERROR_system(0), "%s: cannot change symlink owner/group", ent->fts_path);
+ }
+#endif
+ }
+ break;
+ case FTS_F:
+ case FTS_D:
+ anyway:
+ chownf = chown;
+ op = "chown";
+ commit:
+ if ((unsigned long)ent->fts_statp->st_ctime >= before)
+ break;
+ if (map)
+ {
+ options &= ~(OPT_UID|OPT_GID);
+ uid = gid = NOID;
+ keys[0].uid = keys[1].uid = ent->fts_statp->st_uid;
+ keys[0].gid = keys[2].gid = ent->fts_statp->st_gid;
+ i = 0;
+ do
+ {
+ if (m = (Map_t*)dtmatch(map, &keys[i]))
+ {
+ if (uid == NOID && m->to.uid != NOID)
+ {
+ uid = m->to.uid;
+ options |= OPT_UID;
+ }
+ if (gid == NOID && m->to.gid != NOID)
+ {
+ gid = m->to.gid;
+ options |= OPT_GID;
+ }
+ }
+ } while (++i < elementsof(keys) && (uid == NOID || gid == NOID));
+ }
+ else
+ {
+ if (!(options & OPT_UID))
+ uid = ent->fts_statp->st_uid;
+ if (!(options & OPT_GID))
+ gid = ent->fts_statp->st_gid;
+ }
+ if ((options & OPT_UNMAPPED) && (uid == NOID || gid == NOID))
+ {
+ if (uid == NOID && gid == NOID)
+ error(ERROR_warn(0), "%s: uid and gid not mapped", ent->fts_path);
+ else if (uid == NOID)
+ error(ERROR_warn(0), "%s: uid not mapped", ent->fts_path);
+ else
+ error(ERROR_warn(0), "%s: gid not mapped", ent->fts_path);
+ }
+ if (uid != ent->fts_statp->st_uid && uid != NOID || gid != ent->fts_statp->st_gid && gid != NOID)
+ {
+ if (options & (OPT_SHOW|OPT_VERBOSE))
+ {
+ if (options & OPT_TEST)
+ {
+ ent->fts_statp->st_uid = 0;
+ ent->fts_statp->st_gid = 0;
+ }
+ sfprintf(sfstdout, "%s uid:%05d->%05d gid:%05d->%05d %s\n", op, ent->fts_statp->st_uid, uid, ent->fts_statp->st_gid, gid, ent->fts_path);
+ }
+ if (!(options & OPT_SHOW) && (*chownf)(ent->fts_accpath, uid, gid) && !(options & OPT_FORCE))
+ error(ERROR_system(0), "%s: cannot change%s", ent->fts_path, s);
+ }
+ break;
+ case FTS_DC:
+ if (!(options & OPT_FORCE))
+ error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
+ break;
+ case FTS_DNR:
+ if (!(options & OPT_FORCE))
+ error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
+ goto anyway;
+ case FTS_DNX:
+ if (!(options & OPT_FORCE))
+ error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
+ goto anyway;
+ case FTS_NS:
+ if (!(options & OPT_FORCE))
+ error(ERROR_system(0), "%s: not found", ent->fts_path);
+ break;
+ }
+ fts_close(fts);
+ if (map)
+ dtclose(map);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/chmod.c b/src/lib/libcmd/chmod.c
new file mode 100644
index 0000000..fcb974e
--- /dev/null
+++ b/src/lib/libcmd/chmod.c
@@ -0,0 +1,325 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * chmod
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: chmod (AT&T Research) 2011-03-28 $\n]"
+USAGE_LICENSE
+"[+NAME?chmod - change the access permissions of files]"
+"[+DESCRIPTION?\bchmod\b changes the permission of each file "
+ "according to mode, which can be either a symbolic representation "
+ "of changes to make, or an octal number representing the bit "
+ "pattern for the new permissions.]"
+"[+?Symbolic mode strings consist of one or more comma separated list "
+ "of operations that can be perfomed on the mode. Each operation is of "
+ "the form \auser\a \aop\a \aperm\a where \auser\a is zero or more of "
+ "the following letters:]{"
+ "[+u?User permission bits.]"
+ "[+g?Group permission bits.]"
+ "[+o?Other permission bits.]"
+ "[+a?All permission bits. This is the default if none are specified.]"
+ "}"
+"[+?The \aperm\a portion consists of zero or more of the following letters:]{"
+ "[+r?Read permission.]"
+ "[+s?Setuid when \bu\b is selected for \awho\a and setgid when \bg\b "
+ "is selected for \awho\a.]"
+ "[+w?Write permission.]"
+ "[+x?Execute permission for files, search permission for directories.]"
+ "[+X?Same as \bx\b except that it is ignored for files that do not "
+ "already have at least one \bx\b bit set.]"
+ "[+l?Exclusive lock bit on systems that support it. Group execute "
+ "must be off.]"
+ "[+t?Sticky bit on systems that support it.]"
+ "}"
+"[+?The \aop\a portion consists of one or more of the following characters:]{"
+ "[++?Cause the permission selected to be added to the existing "
+ "permissions. | is equivalent to +.]"
+ "[+-?Cause the permission selected to be removed to the existing "
+ "permissions.]"
+ "[+=?Cause the permission to be set to the given permissions.]"
+ "[+&?Cause the permission selected to be \aand\aed with the existing "
+ "permissions.]"
+ "[+^?Cause the permission selected to be propagated to more "
+ "restrictive groups.]"
+ "}"
+"[+?Symbolic modes with the \auser\a portion omitted are subject to "
+ "\bumask\b(2) settings unless the \b=\b \aop\a or the "
+ "\b--ignore-umask\b option is specified.]"
+"[+?A numeric mode is from one to four octal digits (0-7), "
+ "derived by adding up the bits with values 4, 2, and 1. "
+ "Any omitted digits are assumed to be leading zeros. The "
+ "first digit selects the set user ID (4) and set group ID "
+ "(2) and save text image (1) attributes. The second digit "
+ "selects permissions for the user who owns the file: read "
+ "(4), write (2), and execute (1); the third selects permissions"
+ "for other users in the file's group, with the same values; "
+ "and the fourth for other users not in the file's group, with "
+ "the same values.]"
+
+"[+?For symbolic links, by default, \bchmod\b changes the mode on the file "
+ "referenced by the symbolic link, not on the symbolic link itself. "
+ "The \b-h\b options can be specified to change the mode of the link. "
+ "When traversing directories with \b-R\b, \bchmod\b either follows "
+ "symbolic links or does not follow symbolic links, based on the "
+ "options \b-H\b, \b-L\b, and \b-P\b. The configuration parameter "
+ "\bPATH_RESOLVE\b determines the default behavior if none of these "
+ "options is specified.]"
+
+"[+?When the \b-c\b or \b-v\b options are specified, change notifications "
+ "are written to standard output using the format, "
+ "\b%s: mode changed to %0.4o (%s)\b, with arguments of the "
+ "pathname, the numeric mode, and the resulting permission bits as "
+ "would be displayed by the \bls\b command.]"
+
+"[+?For backwards compatibility, if an invalid option is given that is a valid "
+ "symbolic mode specification, \bchmod\b treats this as a mode "
+ "specification rather than as an option specification.]"
+
+"[H:metaphysical?Follow symbolic links for command arguments; otherwise don't "
+ "follow symbolic links when traversing directories.]"
+"[L:logical|follow?Follow symbolic links when traversing directories.]"
+"[P:physical|nofollow?Don't follow symbolic links when traversing directories.]"
+"[R:recursive?Change the mode for files in subdirectories recursively.]"
+"[c:changes?Describe only files whose permission actually change.]"
+"[f:quiet|silent?Do not report files whose permissioins fail to change.]"
+"[h|l:symlink?Change the mode of symbolic links on systems that "
+ "support \blchmod\b(2). Implies \b--physical\b.]"
+"[i:ignore-umask?Ignore the \bumask\b(2) value in symbolic mode "
+ "expressions. This is probably how you expect \bchmod\b to work.]"
+"[n:show?Show actions but do not change any file modes.]"
+"[F:reference?Omit the \amode\a operand and use the mode of \afile\a "
+ "instead.]:[file]"
+"[v:verbose?Describe changed permissions of all files.]"
+"\n"
+"\nmode file ...\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files changed successfully.]"
+ "[+>0?Unable to change mode of one or more files.]"
+"}"
+"[+SEE ALSO?\bchgrp\b(1), \bchown\b(1), \blchmod\b(1), \btw\b(1), \bgetconf\b(1), "
+ "\bls\b(1), \bumask\b(2)]"
+;
+
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:hide lchmod
+#else
+#define lchmod ______lchmod
+#endif
+
+#include <cmd.h>
+#include <ls.h>
+#include <fts_fix.h>
+
+#ifndef ENOSYS
+#define ENOSYS EINVAL
+#endif
+
+#include "FEATURE/symlink"
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:nohide lchmod
+#else
+#undef lchmod
+#endif
+
+extern int lchmod(const char*, mode_t);
+
+/*
+ * NOTE: we only use the native lchmod() on symlinks just in case
+ * the implementation is a feckless stub
+ */
+
+int
+b_chmod(int argc, char** argv, Shbltin_t* context)
+{
+ register int mode;
+ register int force = 0;
+ register int flags;
+ register char* amode = 0;
+ register FTS* fts;
+ register FTSENT*ent;
+ char* last;
+ int (*chmodf)(const char*, mode_t);
+ int logical = 1;
+ int notify = 0;
+ int ignore = 0;
+ int show = 0;
+ int chlink = 0;
+ struct stat st;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+ flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR;
+
+ /*
+ * NOTE: we diverge from the normal optget boilerplate
+ * to allow `chmod -x etc' to fall through
+ */
+
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'c':
+ notify = 1;
+ continue;
+ case 'f':
+ force = 1;
+ continue;
+ case 'h':
+ chlink = 1;
+ continue;
+ case 'i':
+ ignore = 1;
+ continue;
+ case 'n':
+ show = 1;
+ continue;
+ case 'v':
+ notify = 2;
+ continue;
+ case 'F':
+ if (stat(opt_info.arg, &st))
+ error(ERROR_exit(1), "%s: cannot stat", opt_info.arg);
+ mode = st.st_mode;
+ amode = "";
+ continue;
+ case 'H':
+ flags |= FTS_META|FTS_PHYSICAL;
+ logical = 0;
+ continue;
+ case 'L':
+ flags &= ~(FTS_META|FTS_PHYSICAL);
+ logical = 0;
+ continue;
+ case 'P':
+ flags &= ~FTS_META;
+ flags |= FTS_PHYSICAL;
+ logical = 0;
+ continue;
+ case 'R':
+ flags &= ~FTS_TOP;
+ logical = 0;
+ continue;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || !*argv || !amode && !*(argv + 1))
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (chlink)
+ {
+ flags &= ~FTS_META;
+ flags |= FTS_PHYSICAL;
+ logical = 0;
+ }
+ if (logical)
+ flags &= ~(FTS_META|FTS_PHYSICAL);
+ if (ignore)
+ ignore = umask(0);
+ if (amode)
+ amode = 0;
+ else
+ {
+ amode = *argv++;
+ mode = strperm(amode, &last, 0);
+ if (*last)
+ {
+ if (ignore)
+ umask(ignore);
+ error(ERROR_exit(1), "%s: invalid mode", amode);
+ }
+ }
+ if (!(fts = fts_open(argv, flags, NiL)))
+ {
+ if (ignore)
+ umask(ignore);
+ error(ERROR_system(1), "%s: not found", *argv);
+ }
+ while (!sh_checksig(context) && (ent = fts_read(fts)))
+ switch (ent->fts_info)
+ {
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (chlink)
+ {
+#if _lib_lchmod
+ chmodf = lchmod;
+ goto commit;
+#else
+ if (!force)
+ {
+ errno = ENOSYS;
+ error(ERROR_system(0), "%s: cannot change symlink mode", ent->fts_path);
+ }
+#endif
+ }
+ break;
+ case FTS_F:
+ case FTS_D:
+ anyway:
+ chmodf = chmod;
+#if _lib_lchmod
+ commit:
+#endif
+ if (amode)
+ mode = strperm(amode, &last, ent->fts_statp->st_mode);
+ if (show || (*chmodf)(ent->fts_accpath, mode) >= 0)
+ {
+ if (notify == 2 || notify == 1 && (mode&S_IPERM) != (ent->fts_statp->st_mode&S_IPERM))
+ sfprintf(sfstdout, "%s: mode changed to %0.4o (%s)\n", ent->fts_path, mode, fmtmode(mode, 1)+1);
+ }
+ else if (!force)
+ error(ERROR_system(0), "%s: cannot change mode", ent->fts_path);
+ break;
+ case FTS_DC:
+ if (!force)
+ error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
+ break;
+ case FTS_DNR:
+ if (!force)
+ error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
+ goto anyway;
+ case FTS_DNX:
+ if (!force)
+ error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
+ goto anyway;
+ case FTS_NS:
+ if (!force)
+ error(ERROR_system(0), "%s: not found", ent->fts_path);
+ break;
+ }
+ fts_close(fts);
+ if (ignore)
+ umask(ignore);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/chown.c b/src/lib/libcmd/chown.c
new file mode 100644
index 0000000..3896635
--- /dev/null
+++ b/src/lib/libcmd/chown.c
@@ -0,0 +1,39 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * chown
+ */
+
+static const char id[] = "\n@(#)$Id: chown (AT&T Research) 1997-11-11 $\0\n";
+
+#include <cmd.h>
+
+int
+b_chown(int argc, char** argv, Shbltin_t* context)
+{
+ NoP(id[0]);
+ return b_chgrp(argc, argv, context);
+}
diff --git a/src/lib/libcmd/cksum.c b/src/lib/libcmd/cksum.c
new file mode 100644
index 0000000..ca43e17
--- /dev/null
+++ b/src/lib/libcmd/cksum.c
@@ -0,0 +1,632 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * sum -- list file checksum and size
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: sum (AT&T Research) 2010-07-28 $\n]"
+USAGE_LICENSE
+"[+NAME?cksum,md5sum,sum - print file checksum and block count]"
+"[+DESCRIPTION?\bsum\b lists the checksum, and for most methods the block"
+" count, for each file argument. The standard input is read if there are"
+" no \afile\a arguments. \bgetconf UNIVERSE\b determines the default"
+" \bsum\b method: \batt\b for the \batt\b universe, \bbsd\b otherwise."
+" The default for the other commands is the command name itself. The"
+" \batt\b method is a true sum, all others are order dependent.]"
+"[+?Method names consist of a leading identifier and 0 or more options"
+" separated by -.]"
+"[+?\bgetconf PATH_RESOLVE\b determines how symbolic links are handled. This"
+" can be explicitly overridden by the \b--logical\b, \b--metaphysical\b,"
+" and \b--physical\b options below. \bPATH_RESOLVE\b can be one of:]{"
+" [+logical?Follow all symbolic links.]"
+" [+metaphysical?Follow command argument symbolic links,"
+" otherwise don't follow.]"
+" [+physical?Don't follow symbolic links.]"
+"}"
+
+"[a:all?List the checksum for all files. Use with \b--total\b to list both"
+" individual and total checksums and block counts.]"
+"[b:binary?Read files in binary mode. This is the default.]"
+"[B:scale?Block count scale (bytes per block) override for methods that"
+" include size in the output. The default is method specific.]#[scale]"
+"[c:check?Each \afile\a is interpreted as the output from a previous \bsum\b."
+" If \b--header\b or \b--permissions\b was specified in the previous"
+" \bsum\b then the checksum method is automatically determined,"
+" otherwise \b--method\b must be specified. The listed checksum is"
+" compared with the current value and a warning is issued for each file"
+" that does not match. If \afile\a was generated by \b--permissions\b"
+" then the file mode, user and group are also checked. Empty lines,"
+" lines starting with \b#<space>\b, or the line \b#\b are ignored. Lines"
+" containing no blanks are interpreted as [no]]\aname\a[=\avalue\a]]"
+" options:]{"
+" [+method=name?Checksum method to apply to subsequent lines.]"
+" [+permissions?Subsequent lines were generated with"
+" \b--permissions\b.]"
+"}"
+"[h:header?Print the checksum method as the first output line. Used with"
+" \b--check\b and \b--permissions\b.]"
+"[l:list?Each \afile\a is interpreted as a list of files, one per line,"
+" that is checksummed.]"
+"[p:permissions?If \b--check\b is not specified then list the file"
+" mode, user and group between the checksum and path. User and group"
+" matching the caller are output as \b-\b. If \b--check\b is"
+" specified then the mode, user and group for each path in \afile\a"
+" are updated if necessary to match those in \afile\a. A warning is"
+" printed on the standard error for each changed file.]"
+"[R:recursive?Recursively checksum the contents of directories.]"
+"[S:silent|status?No output for \b--check\b; 0 exit status means all sums"
+" matched, non-0 means at least one sum failed to match. Ignored for"
+" \b--permissions\b.]"
+"[t:total?List only the total checksum and block count of all files."
+" \b--all\b \b--total\b lists each checksum and the total. The"
+" total checksum and block count may be different from the checksum"
+" and block count of the catenation of all files due to partial"
+" blocks that may occur when the files are treated separately.]"
+"[T:text?Read files in text mode (i.e., treat \b\\r\\n\b as \b\\n\b).]"
+"[w!:warn?Warn about invalid \b--check\b lines.]"
+"[x:method|algorithm?Specifies the checksum \amethod\a to"
+" apply. Parenthesized method options are readonly implementation"
+" details.]:[method]{\fmethods\f}"
+"[L:logical|follow?Follow symbolic links when traversing directories. The"
+" default is determined by \bgetconf PATH_RESOLVE\b.]"
+"[H:metaphysical?Follow command argument symbolic links, otherwise don't"
+" follow symbolic links when traversing directories. The default is"
+" determined by \bgetconf PATH_RESOLVE\b.]"
+"[P:physical?Don't follow symbolic links when traversing directories. The"
+" default is determined by \bgetconf PATH_RESOLVE\b.]"
+"[r:bsd?Equivalent to \b--method=bsd --scale=512\b for compatibility with"
+" other \bsum\b(1) implementations.]"
+"[s:sysv?Equivalent to \b--method=sys5\b for compatibility with other"
+" \bsum\b(1) implementations.]"
+
+"\n"
+"\n[ file ... ]\n"
+"\n"
+
+"[+SEE ALSO?\bgetconf\b(1), \btw\b(1), \buuencode\b(1)]"
+;
+
+#include <cmd.h>
+#include <sum.h>
+#include <ls.h>
+#include <modex.h>
+#include <fts_fix.h>
+#include <error.h>
+
+typedef struct State_s /* program state */
+{
+ int all; /* list all items */
+ Sfio_t* check; /* check previous output */
+ int flags; /* sumprint() SUM_* flags */
+ gid_t gid; /* caller gid */
+ int header; /* list method on output */
+ int list; /* list file name too */
+ Sum_t* oldsum; /* previous sum method */
+ int permissions; /* include mode,uer,group */
+ int haveperm; /* permissions in the input */
+ int recursive; /* recursively descend dirs */
+ size_t scale; /* scale override */
+ unsigned long size; /* combined size of all files */
+ int silent; /* silent check, 0 exit if ok */
+ int (*sort)(FTSENT* const*, FTSENT* const*);
+ Sum_t* sum; /* sum method */
+ int text; /* \r\n == \n */
+ int total; /* list totals only */
+ uid_t uid; /* caller uid */
+ int warn; /* invalid check line warnings */
+} State_t;
+
+static void verify(State_t*, char*, char*, Sfio_t*);
+
+/*
+ * open path for read mode
+ */
+
+static Sfio_t*
+openfile(const char* path, const char* mode)
+{
+ Sfio_t* sp;
+
+ if (!path || streq(path, "-") || streq(path, "/dev/stdin") || streq(path, "/dev/fd/0"))
+ {
+ sp = sfstdin;
+ sfopen(sp, NiL, mode);
+ }
+ else if (!(sp = sfopen(NiL, path, mode)))
+ error(ERROR_SYSTEM|2, "%s: cannot read", path);
+ return sp;
+}
+
+/*
+ * close an openfile() stream
+ */
+
+static int
+closefile(Sfio_t* sp)
+{
+ return sp == sfstdin ? 0 : sfclose(sp);
+}
+
+/*
+ * compute and print sum on an open file
+ */
+
+static void
+pr(State_t* state, Sfio_t* op, Sfio_t* ip, char* file, int perm, struct stat* st, Sfio_t* check)
+{
+ register char* p;
+ register char* r;
+ register char* e;
+ register int peek;
+ struct stat ss;
+
+ if (check)
+ {
+ state->oldsum = state->sum;
+ while (p = sfgetr(ip, '\n', 1))
+ verify(state, p, file, check);
+ state->sum = state->oldsum;
+ if (state->warn && !sfeof(ip))
+ error(2, "%s: last line incomplete", file);
+ return;
+ }
+ suminit(state->sum);
+ if (state->text)
+ {
+ peek = 0;
+ while (p = sfreserve(ip, SF_UNBOUND, 0))
+ {
+ e = p + sfvalue(ip);
+ if (peek)
+ {
+ peek = 0;
+ if (*p != '\n')
+ sumblock(state->sum, "\r", 1);
+ }
+ while (r = memchr(p, '\r', e - p))
+ {
+ if (++r >= e)
+ {
+ e--;
+ peek = 1;
+ break;
+ }
+ sumblock(state->sum, p, r - p - (*r == '\n'));
+ p = r;
+ }
+ sumblock(state->sum, p, e - p);
+ }
+ if (peek)
+ sumblock(state->sum, "\r", 1);
+ }
+ else
+ while (p = sfreserve(ip, SF_UNBOUND, 0))
+ sumblock(state->sum, p, sfvalue(ip));
+ if (sfvalue(ip))
+ error(ERROR_SYSTEM|2, "%s: read error", file);
+ sumdone(state->sum);
+ if (!state->total || state->all)
+ {
+ sumprint(state->sum, op, state->flags|SUM_SCALE, state->scale);
+ if (perm >= 0)
+ {
+ if (perm)
+ {
+ if (!st && fstat(sffileno(ip), st = &ss))
+ error(ERROR_SYSTEM|2, "%s: cannot stat", file);
+ else
+ sfprintf(sfstdout, " %04o %s %s",
+ modex(st->st_mode & S_IPERM),
+ (st->st_uid != state->uid && ((st->st_mode & S_ISUID) || (st->st_mode & S_IRUSR) && !(st->st_mode & (S_IRGRP|S_IROTH)) || (st->st_mode & S_IXUSR) && !(st->st_mode & (S_IXGRP|S_IXOTH)))) ? fmtuid(st->st_uid) : "-",
+ (st->st_gid != state->gid && ((st->st_mode & S_ISGID) || (st->st_mode & S_IRGRP) && !(st->st_mode & S_IROTH) || (st->st_mode & S_IXGRP) && !(st->st_mode & S_IXOTH))) ? fmtgid(st->st_gid) : "-");
+ }
+ if (ip != sfstdin)
+ sfprintf(op, " %s", file);
+ sfputc(op, '\n');
+ }
+ }
+}
+
+/*
+ * verify previous sum output
+ */
+
+static void
+verify(State_t* state, register char* s, char* check, Sfio_t* rp)
+{
+ register char* t;
+ char* e;
+ char* file;
+ int attr;
+ int mode;
+ int uid;
+ int gid;
+ Sfio_t* sp;
+ struct stat st;
+
+ if (!*s || *s == '#' && (!*(s + 1) || *(s + 1) == ' ' || *(s + 1) == '\t'))
+ return;
+ if (t = strchr(s, ' '))
+ {
+ if ((t - s) > 10 || !(file = strchr(t + 1, ' ')))
+ file = t;
+ *file++ = 0;
+ attr = 0;
+ if ((mode = strtol(file, &e, 8)) && *e == ' ' && (e - file) == 4)
+ {
+ mode = modei(mode);
+ if (t = strchr(++e, ' '))
+ {
+ if (*e == '-' && (t - e) == 1)
+ uid = -1;
+ else
+ {
+ *t = 0;
+ uid = struid(e);
+ *t = ' ';
+ }
+ if (e = strchr(++t, ' '))
+ {
+ if (*t == '-' && (e - t) == 1)
+ gid = -1;
+ else
+ {
+ *e = 0;
+ gid = struid(t);
+ *e = ' ';
+ }
+ file = e + 1;
+ attr = 1;
+ }
+ }
+ }
+ if (sp = openfile(file, "rb"))
+ {
+ pr(state, rp, sp, file, -1, NiL, NiL);
+ if (!(t = sfstruse(rp)))
+ error(ERROR_SYSTEM|3, "out of space");
+ if (!streq(s, t))
+ {
+ if (state->silent)
+ error_info.errors++;
+ else
+ error(2, "%s: checksum changed", file);
+ }
+ else if (attr)
+ {
+ if (fstat(sffileno(sp), &st))
+ {
+ if (state->silent)
+ error_info.errors++;
+ else
+ error(ERROR_SYSTEM|2, "%s: cannot stat", file);
+ }
+ else
+ {
+ if (uid < 0 || uid == st.st_uid)
+ uid = -1;
+ else if (!state->permissions)
+ {
+ if (state->silent)
+ error_info.errors++;
+ else
+ error(2, "%s: uid should be %s", file, fmtuid(uid));
+ }
+ if (gid < 0 || gid == st.st_gid)
+ gid = -1;
+ else if (!state->permissions)
+ {
+ if (state->silent)
+ error_info.errors++;
+ else
+ error(2, "%s: gid should be %s", file, fmtgid(gid));
+ }
+ if (state->permissions && (uid >= 0 || gid >= 0))
+ {
+ if (chown(file, uid, gid) < 0)
+ {
+ if (uid < 0)
+ error(ERROR_SYSTEM|2, "%s: cannot change group to %s", file, fmtgid(gid));
+ else if (gid < 0)
+ error(ERROR_SYSTEM|2, "%s: cannot change user to %s", file, fmtuid(uid));
+ else
+ error(ERROR_SYSTEM|2, "%s: cannot change user to %s and group to %s", file, fmtuid(uid), fmtgid(gid));
+ }
+ else
+ {
+ if (uid < 0)
+ error(1, "%s: changed group to %s", file, fmtgid(gid));
+ else if (gid < 0)
+ error(1, "%s: changed user to %s", file, fmtuid(uid));
+ else
+ error(1, "%s: changed user to %s and group to %s", file, fmtuid(uid), fmtgid(gid));
+ }
+ }
+ if ((st.st_mode & S_IPERM) ^ mode)
+ {
+ if (state->permissions)
+ {
+ if (chmod(file, mode) < 0)
+ error(ERROR_SYSTEM|2, "%s: cannot change mode to %s", file, fmtmode(mode, 0));
+ else
+ error(ERROR_SYSTEM|1, "%s: changed mode to %s", file, fmtmode(mode, 0));
+ }
+ else if (state->silent)
+ error_info.errors++;
+ else
+ error(2, "%s: mode should be %s", file, fmtmode(mode, 0));
+ }
+ }
+ }
+ closefile(sp);
+ }
+ }
+ else if (strneq(s, "method=", 7))
+ {
+ s += 7;
+ if (state->sum != state->oldsum)
+ sumclose(state->sum);
+ if (!(state->sum = sumopen(s)))
+ error(3, "%s: %s: unknown checksum method", check, s);
+ }
+ else if (streq(s, "permissions"))
+ state->haveperm = 1;
+ else
+ error(1, "%s: %s: unknown option", check, s);
+}
+
+/*
+ * sum the list of files in lp
+ */
+
+static void
+list(State_t* state, register Sfio_t* lp)
+{
+ register char* file;
+ register Sfio_t* sp;
+
+ while (file = sfgetr(lp, '\n', 1))
+ if (sp = openfile(file, state->check ? "rt" : "rb"))
+ {
+ pr(state, sfstdout, sp, file, state->permissions, NiL, state->check);
+ closefile(sp);
+ }
+}
+
+/*
+ * order child entries
+ */
+
+static int
+order(FTSENT* const* f1, FTSENT* const* f2)
+{
+ return strcoll((*f1)->fts_name, (*f2)->fts_name);
+}
+
+/*
+ * optget() info discipline function
+ */
+
+static int
+optinfo(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
+{
+ if (streq(s, "methods"))
+ return sumusage(sp);
+ return 0;
+}
+
+int
+b_cksum(int argc, register char** argv, Shbltin_t* context)
+{
+ register int flags;
+ char* file;
+ char* method;
+ Sfio_t* sp;
+ FTS* fts;
+ FTSENT* ent;
+ int logical;
+ Optdisc_t optdisc;
+ State_t state;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+ memset(&state, 0, sizeof(state));
+ flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER;
+ state.flags = SUM_SIZE;
+ state.warn = 1;
+ logical = 1;
+ method = 0;
+ optinit(&optdisc, optinfo);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ state.all = 1;
+ continue;
+ case 'b':
+ state.text = 0;
+ continue;
+ case 'B':
+ state.scale = opt_info.num;
+ continue;
+ case 'c':
+ if (!(state.check = sfstropen()))
+ error(3, "out of space [check]");
+ continue;
+ case 'h':
+ state.header = 1;
+ continue;
+ case 'l':
+ state.list = 1;
+ continue;
+ case 'p':
+ state.permissions = 1;
+ continue;
+ case 'r':
+ method = "bsd";
+ state.scale = 512;
+ state.flags |= SUM_LEGACY;
+ continue;
+ case 'R':
+ flags &= ~FTS_TOP;
+ state.recursive = 1;
+ state.sort = order;
+ logical = 0;
+ continue;
+ case 's':
+ method = "sys5";
+ continue;
+ case 'S':
+ state.silent = opt_info.num;
+ continue;
+ case 't':
+ state.total = 1;
+ continue;
+ case 'w':
+ state.warn = opt_info.num;
+ continue;
+ case 'x':
+ method = opt_info.arg;
+ continue;
+ case 'H':
+ flags |= FTS_META|FTS_PHYSICAL;
+ logical = 0;
+ continue;
+ case 'L':
+ flags &= ~(FTS_META|FTS_PHYSICAL);
+ logical = 0;
+ continue;
+ case 'P':
+ flags &= ~FTS_META;
+ flags |= FTS_PHYSICAL;
+ logical = 0;
+ continue;
+ case 'T':
+ state.text = 1;
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ break;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+
+ /*
+ * check the method
+ */
+
+ if (method && !(state.sum = sumopen(method)))
+ error(3, "%s: unknown checksum method", method);
+ if (!state.sum && !(state.sum = sumopen(error_info.id)) && !(state.sum = sumopen(astconf("UNIVERSE", NiL, NiL))))
+ state.sum = sumopen(NiL);
+
+ /*
+ * do it
+ */
+
+ if (logical)
+ {
+ flags &= ~(FTS_META|FTS_PHYSICAL);
+ flags |= FTS_SEEDOTDIR;
+ }
+ if (state.permissions)
+ {
+ state.uid = geteuid();
+ state.gid = getegid();
+ state.silent = 0;
+ }
+ if (!state.check && (state.header || state.permissions))
+ {
+ sfprintf(sfstdout, "method=%s\n", state.sum->name);
+ if (state.permissions)
+ sfprintf(sfstdout, "permissions\n");
+ }
+ if (state.list)
+ {
+ if (*argv)
+ {
+ while (file = *argv++)
+ if (sp = openfile(file, "rt"))
+ {
+ list(&state, sp);
+ closefile(sp);
+ }
+ }
+ else if (sp = openfile(NiL, "rt"))
+ {
+ list(&state, sp);
+ closefile(sp);
+ }
+ }
+ else if (!*argv && !state.recursive)
+ pr(&state, sfstdout, sfstdin, "/dev/stdin", state.permissions, NiL, state.check);
+ else if (!(fts = fts_open(argv, flags, state.sort)))
+ error(ERROR_system(1), "%s: not found", *argv);
+ else
+ {
+ while (!sh_checksig(context) && (ent = fts_read(fts)))
+ switch (ent->fts_info)
+ {
+ case FTS_SL:
+ if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1)
+ fts_set(NiL, ent, FTS_FOLLOW);
+ break;
+ case FTS_F:
+ if (sp = openfile(ent->fts_accpath, "rb"))
+ {
+ pr(&state, sfstdout, sp, ent->fts_path, state.permissions, ent->fts_statp, state.check);
+ closefile(sp);
+ }
+ break;
+ case FTS_DC:
+ error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
+ break;
+ case FTS_DNR:
+ error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
+ break;
+ case FTS_DNX:
+ error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
+ break;
+ case FTS_NS:
+ error(ERROR_system(0), "%s: not found", ent->fts_path);
+ break;
+ }
+ fts_close(fts);
+ }
+ if (state.total)
+ {
+ sumprint(state.sum, sfstdout, state.flags|SUM_TOTAL|SUM_SCALE, state.scale);
+ sfputc(sfstdout, '\n');
+ }
+ sumclose(state.sum);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/cmd.h b/src/lib/libcmd/cmd.h
new file mode 100644
index 0000000..2146e74
--- /dev/null
+++ b/src/lib/libcmd/cmd.h
@@ -0,0 +1,163 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * AT&T Research
+ *
+ * builtin cmd definitions
+ */
+
+#ifndef _CMD_H
+#define _CMD_H
+
+#include <ast.h>
+#include <error.h>
+#include <stak.h>
+#include <shcmd.h>
+
+#define cmdinit _cmd_init
+
+#define ERROR_CALLBACK ERROR_SET
+
+#if _BLD_cmd && defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+#include <cmdext.h>
+
+#undef extern
+
+#if defined(CMD_BUILTIN) && !defined(CMD_STANDALONE)
+#define CMD_STANDALONE CMD_BUILTIN
+#endif
+
+#ifdef CMD_STANDALONE
+
+#define CMD_CONTEXT(c) ((Shbltin_t*)0)
+
+#if CMD_DYNAMIC
+
+#include <dlldefs.h>
+
+#else
+
+extern int CMD_STANDALONE(int, char**, Shbltin_t*);
+
+#endif
+
+#ifndef CMD_BUILTIN
+
+/*
+ * command initialization
+ */
+
+static int
+cmdinit(int argc, register char** argv, Shbltin_t* context, const char* catalog, int flags)
+{
+ register char* cp;
+ register char* pp;
+
+ if (cp = strrchr(argv[0], '/'))
+ cp++;
+ else
+ cp = argv[0];
+ if (pp = strrchr(cp, '_'))
+ cp = pp + 1;
+ error_info.id = cp;
+ if (!error_info.catalog)
+ error_info.catalog = (char*)catalog;
+ opt_info.index = 0;
+ if (context)
+ error_info.flags |= flags & ~(ERROR_CALLBACK|ERROR_NOTIFY);
+ return 0;
+}
+
+#endif
+
+int
+main(int argc, char** argv)
+{
+#if CMD_DYNAMIC
+ register char* s;
+ register char* t;
+ void* dll;
+ Shbltin_f fun;
+ char buf[64];
+
+ if (s = strrchr(argv[0], '/'))
+ s++;
+ else if (!(s = argv[0]))
+ return 127;
+ if ((t = strrchr(s, '_')) && *++t)
+ s = t;
+ buf[0] = '_';
+ buf[1] = 'b';
+ buf[2] = '_';
+ strncpy(buf + 3, s, sizeof(buf) - 4);
+ buf[sizeof(buf) - 1] = 0;
+ if (t = strchr(buf, '.'))
+ *t = 0;
+ for (;;)
+ {
+ if (dll = dlopen(NiL, RTLD_LAZY))
+ {
+ if (fun = (Shbltin_f)dlsym(dll, buf + 1))
+ break;
+ if (fun = (Shbltin_f)dlsym(dll, buf))
+ break;
+ }
+ if (dll = dllplug(NiL, "cmd", NiL, RTLD_LAZY, NiL, 0))
+ {
+ if (fun = (Shbltin_f)dlsym(dll, buf + 1))
+ break;
+ if (fun = (Shbltin_f)dlsym(dll, buf))
+ break;
+ }
+ return 127;
+ }
+ return (*fun)(argc, argv, NiL);
+#else
+ return CMD_STANDALONE(argc, argv, NiL);
+#endif
+}
+
+#else
+
+#undef cmdinit
+#ifdef _MSC_VER
+#define CMD_CONTEXT(p) ((Shbltin_t*)(p))
+#define cmdinit(a,b,c,d,e) do{if(_cmd_init(a,b,c,d,e))return -1;}while(0)
+#else
+#define CMD_CONTEXT(p) (((p)&&((Shbltin_t*)(p))->version>=20071012&&((Shbltin_t*)(p))->version<20350101)?((Shbltin_t*)(p)):0)
+#define cmdinit(a,b,c,d,e) do{if((c)&&!CMD_CONTEXT(c))c=0;if(_cmd_init(a,b,c,d,e))return -1;}while(0)
+#endif
+
+#if _BLD_cmd && defined(__EXPORT__)
+#define extern extern __EXPORT__
+#endif
+
+extern int _cmd_init(int, char**, Shbltin_t*, const char*, int);
+
+#undef extern
+
+#endif
+
+#endif
diff --git a/src/lib/libcmd/cmdinit.c b/src/lib/libcmd/cmdinit.c
new file mode 100644
index 0000000..a25a51e
--- /dev/null
+++ b/src/lib/libcmd/cmdinit.c
@@ -0,0 +1,75 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * command initialization
+ */
+
+#include <cmd.h>
+#include <shcmd.h>
+
+int
+_cmd_init(int argc, char** argv, Shbltin_t* context, const char* catalog, int flags)
+{
+ register char* cp;
+
+ if (argc <= 0)
+ return -1;
+ if (context)
+ {
+ if (flags & ERROR_CALLBACK)
+ {
+ flags &= ~ERROR_CALLBACK;
+ flags |= ERROR_NOTIFY;
+ }
+ else if (flags & ERROR_NOTIFY)
+ {
+ context->notify = 1;
+ flags &= ~ERROR_NOTIFY;
+ }
+ error_info.flags |= flags;
+ }
+ if (cp = strrchr(argv[0], '/'))
+ cp++;
+ else
+ cp = argv[0];
+ error_info.id = cp;
+ if (!error_info.catalog)
+ error_info.catalog = catalog;
+ opt_info.index = 0;
+ return 0;
+}
+
+#if __OBSOLETE__ < 20080101
+
+#if defined(__EXPORT__)
+#define extern __EXPORT__
+#endif
+
+#undef cmdinit
+
+extern void
+cmdinit(char** argv, Shbltin_t* context, const char* catalog, int flags)
+{
+ _cmd_init(0, argv, context, catalog, flags);
+}
+
+#endif
diff --git a/src/lib/libcmd/cmp.c b/src/lib/libcmd/cmp.c
new file mode 100644
index 0000000..7433020
--- /dev/null
+++ b/src/lib/libcmd/cmp.c
@@ -0,0 +1,383 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Bell Laboratories
+ *
+ * cmp
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: cmp (AT&T Research) 2010-04-11 $\n]"
+USAGE_LICENSE
+"[+NAME?cmp - compare two files]"
+"[+DESCRIPTION?\bcmp\b compares two files \afile1\a and \afile2\a. "
+ "\bcmp\b writes no output if the files are the same. By default, if the "
+ "files differ, the byte and line number at which the first difference "
+ "occurred are written to standard output. Bytes and lines are numbered "
+ "beginning with 1.]"
+"[+?If \askip1\a or \askip2\a are specified, or the \b-i\b option is "
+ "specified, initial bytes of the corresponding file are skipped before "
+ "beginning the compare. The skip values are in bytes or can have a "
+ "suffix of \bk\b for kilobytes or \bm\b for megabytes.]"
+"[+?If either \afile1\a or \afiles2\a is \b-\b, \bcmp\b uses standard "
+ "input starting at the current location.]"
+"[b:print-bytes?Print differing bytes as 3 digit octal values.]"
+"[c:print-chars?Print differing bytes as follows: non-space printable "
+ "characters as themselves; space and control characters as \b^\b "
+ "followed by a letter of the alphabet; and characters with the high bit "
+ "set as the lower 7 bit character prefixed by \bM^\b for 7 bit space and "
+ "non-printable characters and \bM-\b for all other characters. If the 7 "
+ "bit character encoding is not ASCII then the characters are converted "
+ "to ASCII to determine \ahigh bit set\a, and if set it is cleared and "
+ "converted back to the native encoding. Multibyte characters in the "
+ "current locale are treated as printable characters.]"
+"[d:differences?Print at most \adifferences\a differences using "
+ "\b--verbose\b output format. \b--differences=0\b is equivalent to "
+ "\b--silent\b.]#[differences]"
+"[i:ignore-initial|skip?Skip the the first \askip1\a bytes in \afile1\a "
+ "and the first \askip2\a bytes in \afile2\a. If \askip2\a is omitted "
+ "then \askip1\a is used.]:[skip1[::skip2]]:=0::0]"
+"[l:verbose?Write the decimal byte number and the differing bytes (in "
+ "octal) for each difference.]"
+"[n:count|bytes?Compare at most \acount\a bytes.]#[count]"
+"[s:quiet|silent?Write nothing for differing files; return non-zero exit "
+ "status only.]"
+"\n"
+"\nfile1 file2 [skip1 [skip2]]\n"
+"\n"
+"[+EXIT STATUS?]"
+ "{"
+ "[+0?The files or portions compared are identical.]"
+ "[+1?The files are different.]"
+ "[+>1?An error occurred.]"
+ "}"
+"[+SEE ALSO?\bcomm\b(1), \bdiff\b(1), \bcat\b(1)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+#include <ctype.h>
+#include <ccode.h>
+
+#define CMP_VERBOSE 0x01
+#define CMP_SILENT 0x02
+#define CMP_CHARS 0x04
+#define CMP_BYTES 0x08
+
+static void
+pretty(Sfio_t *out, int o, int delim, int flags)
+{
+ int c;
+ int m;
+ char* s;
+ char buf[10];
+
+ s = buf;
+ if ((flags & CMP_BYTES) || !(flags & CMP_CHARS))
+ {
+ *s++ = ' ';
+ if ((flags & CMP_CHARS) && delim != -1)
+ *s++ = ' ';
+ *s++ = '0' + ((o >> 6) & 07);
+ *s++ = '0' + ((o >> 3) & 07);
+ *s++ = '0' + (o & 07);
+ }
+ if (flags & CMP_CHARS)
+ {
+ *s++ = ' ';
+ c = ccmapc(o, CC_NATIVE, CC_ASCII);
+ if (c & 0x80)
+ {
+ m = 1;
+ *s++ = 'M';
+ c &= 0x7f;
+ o = ccmapc(c, CC_ASCII, CC_NATIVE);
+ }
+ else
+ m = 0;
+ if (isspace(o) || !isprint(o))
+ {
+ if (!m)
+ *s++ = ' ';
+ *s++ = '^';
+ c ^= 0x40;
+ o = ccmapc(c, CC_ASCII, CC_NATIVE);
+ }
+ else if (m)
+ *s++ = '-';
+ else
+ {
+ *s++ = ' ';
+ *s++ = ' ';
+ }
+ *s++ = o;
+ }
+ *s = 0;
+ sfputr(out, buf, delim);
+}
+
+/*
+ * compare two files
+ */
+
+static int
+cmp(const char* file1, Sfio_t* f1, const char* file2, Sfio_t* f2, int flags, Sfoff_t count, Sfoff_t differences)
+{
+ register int c1;
+ register int c2;
+ register unsigned char* p1 = 0;
+ register unsigned char* p2 = 0;
+ register Sfoff_t lines = 1;
+ register unsigned char* e1 = 0;
+ register unsigned char* e2 = 0;
+ Sfoff_t pos = 0;
+ int n1 = 0;
+ int ret = 0;
+ unsigned char* last;
+
+ for (;;)
+ {
+ if ((c1 = e1 - p1) <= 0)
+ {
+ if (count > 0 && !(count -= n1))
+ return ret;
+ if (!(p1 = (unsigned char*)sfreserve(f1, SF_UNBOUND, 0)) || (c1 = sfvalue(f1)) <= 0)
+ {
+ if ((e2 - p2) > 0 || sfreserve(f2, SF_UNBOUND, 0) && sfvalue(f2) > 0)
+ {
+ ret = 1;
+ if (!(flags & CMP_SILENT))
+ error(ERROR_exit(1), "EOF on %s", file1);
+ }
+ return ret;
+ }
+ if (count > 0 && c1 > count)
+ c1 = (int)count;
+ e1 = p1 + c1;
+ n1 = c1;
+ }
+ if ((c2 = e2 - p2) <= 0)
+ {
+ if (!(p2 = (unsigned char*)sfreserve(f2, SF_UNBOUND, 0)) || (c2 = sfvalue(f2)) <= 0)
+ {
+ if (!(flags & CMP_SILENT))
+ error(ERROR_exit(1), "EOF on %s", file2);
+ return 1;
+ }
+ e2 = p2 + c2;
+ }
+ if (c1 > c2)
+ c1 = c2;
+ pos += c1;
+ if (flags & CMP_SILENT)
+ {
+ if (memcmp(p1, p2, c1))
+ return 1;
+ p1 += c1;
+ p2 += c1;
+ }
+ else
+ {
+ last = p1 + c1;
+ while (p1 < last)
+ {
+ if ((c1 = *p1++) != *p2++)
+ {
+ if (differences >= 0)
+ {
+ if (!differences)
+ return 1;
+ differences--;
+ }
+#if 0
+ if (!flags)
+ sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u\n", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines);
+ else
+ {
+ sfprintf(sfstdout, "%6I*d", sizeof(pos), pos - (last - p1));
+ pretty(sfstdout, c1, -1, flags);
+ pretty(sfstdout, *(p2-1), '\n', flags);
+ }
+#else
+ if (flags & CMP_VERBOSE)
+ sfprintf(sfstdout, "%6I*d", sizeof(pos), pos - (last - p1));
+ else
+ sfprintf(sfstdout, "%s %s differ: char %I*d, line %I*u", file1, file2, sizeof(pos), pos - (last - p1), sizeof(lines), lines);
+ if (flags & (CMP_BYTES|CMP_CHARS|CMP_VERBOSE))
+ {
+ sfputc(sfstdout, (flags & CMP_VERBOSE) ? ' ' : ',');
+ pretty(sfstdout, c1, -1, flags);
+ pretty(sfstdout, *(p2-1), '\n', flags);
+ }
+ else
+ sfputc(sfstdout, '\n');
+#endif
+ if (!differences || differences < 0 && !(flags & CMP_VERBOSE))
+ return 1;
+ ret = 1;
+ }
+ if (c1 == '\n')
+ lines++;
+ }
+ }
+ }
+}
+
+int
+b_cmp(int argc, register char** argv, Shbltin_t* context)
+{
+ char* s;
+ char* e;
+ char* file1;
+ char* file2;
+ int n;
+ struct stat s1;
+ struct stat s2;
+
+ Sfio_t* f1 = 0;
+ Sfio_t* f2 = 0;
+ Sfoff_t o1 = 0;
+ Sfoff_t o2 = 0;
+ Sfoff_t count = -1;
+ Sfoff_t differences = -1;
+ int flags = 0;
+
+ NoP(argc);
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'b':
+ flags |= CMP_BYTES;
+ continue;
+ case 'c':
+ flags |= CMP_CHARS;
+ continue;
+ case 'd':
+ flags |= CMP_VERBOSE;
+ differences = opt_info.number;
+ continue;
+ case 'i':
+ o1 = strtoll(opt_info.arg, &e, 0);
+ if (*e == ':')
+ o2 = strtoll(e + 1, &e, 0);
+ else
+ o2 = o1;
+ if (*e)
+ {
+ error(2, "%s: skip1:skip2 expected", opt_info.arg);
+ break;
+ }
+ continue;
+ case 'l':
+ flags |= CMP_VERBOSE;
+ continue;
+ case 'n':
+ count = opt_info.number;
+ continue;
+ case 's':
+ flags |= CMP_SILENT;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || !(file1 = *argv++) || !(file2 = *argv++))
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ n = 2;
+ if (streq(file1, "-"))
+ f1 = sfstdin;
+ else if (!(f1 = sfopen(NiL, file1, "r")))
+ {
+ if (!(flags & CMP_SILENT))
+ error(ERROR_system(0), "%s: cannot open", file1);
+ goto done;
+ }
+ if (streq(file2, "-"))
+ f2 = sfstdin;
+ else if (!(f2 = sfopen(NiL, file2, "r")))
+ {
+ if (!(flags & CMP_SILENT))
+ error(ERROR_system(0), "%s: cannot open", file2);
+ goto done;
+ }
+ if (s = *argv++)
+ {
+ o1 = strtoll(s, &e, 0);
+ if (*e)
+ {
+ error(ERROR_exit(0), "%s: %s: invalid skip", file1, s);
+ goto done;
+ }
+ if (s = *argv++)
+ {
+ o2 = strtoll(s, &e, 0);
+ if (*e)
+ {
+ error(ERROR_exit(0), "%s: %s: invalid skip", file2, s);
+ goto done;
+ }
+ }
+ if (*argv)
+ {
+ error(ERROR_usage(0), "%s", optusage(NiL));
+ goto done;
+ }
+ }
+ if (o1 && sfseek(f1, o1, SEEK_SET) != o1)
+ {
+ if (!(flags & CMP_SILENT))
+ error(ERROR_exit(0), "EOF on %s", file1);
+ n = 1;
+ goto done;
+ }
+ if (o2 && sfseek(f2, o2, SEEK_SET) != o2)
+ {
+ if (!(flags & CMP_SILENT))
+ error(ERROR_exit(0), "EOF on %s", file2);
+ n = 1;
+ goto done;
+ }
+ if (fstat(sffileno(f1), &s1))
+ error(ERROR_system(0), "%s: cannot stat", file1);
+ else if (fstat(sffileno(f2), &s2))
+ error(ERROR_system(0), "%s: cannot stat", file1);
+ else if (s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev && o1 == o2)
+ n = 0;
+ else
+ n = ((flags & CMP_SILENT) && S_ISREG(s1.st_mode) && S_ISREG(s2.st_mode) && (s1.st_size - o1) != (s2.st_size - o2)) ? 1 : cmp(file1, f1, file2, f2, flags, count, differences);
+ done:
+ if (f1 && f1 != sfstdin)
+ sfclose(f1);
+ if (f2 && f2 != sfstdin)
+ sfclose(f2);
+ return n;
+}
diff --git a/src/lib/libcmd/comm.c b/src/lib/libcmd/comm.c
new file mode 100644
index 0000000..8080001
--- /dev/null
+++ b/src/lib/libcmd/comm.c
@@ -0,0 +1,203 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * comm
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: comm (AT&T Research) 1999-04-28 $\n]"
+USAGE_LICENSE
+"[+NAME?comm - select or reject lines common to two files]"
+"[+DESCRIPTION?\bcomm\b reads two files \afile1\a and \afile2\a "
+ "which should be ordered in the collating sequence of the "
+ "current locale, and produces three text columns as output:]{"
+ "[+1?Lines only in \afile1\a.]"
+ "[+2?Lines only in \afile2\a.]"
+ "[+3?Lines in both files.]"
+ "}"
+"[+?If lines in either file are not ordered according to the collating "
+ "sequence of the current locale, the results are not specified.]"
+"[+?If either \afile1\a or \afile2\a is \b-\b, \bcomm\b "
+ "uses standard input starting at the current location.]"
+
+"[1?Suppress the output column of lines unique to \afile1\a.]"
+"[2?Suppress the output column of lines unique to \afile2\a.]"
+"[3?Suppress the output column of lines duplicate in \afile1\a and \afile2\a.]"
+"\n"
+"\nfile1 file2\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Both files processed successfully.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bcmp\b(1), \bdiff\b(1)]"
+;
+
+
+#include <cmd.h>
+
+#define C_FILE1 1
+#define C_FILE2 2
+#define C_COMMON 4
+#define C_ALL (C_FILE1|C_FILE2|C_COMMON)
+
+static int comm(Sfio_t *in1, Sfio_t *in2, register Sfio_t *out,register int mode)
+{
+ register char *cp1, *cp2;
+ register int n1, n2, n, comp;
+ if(cp1 = sfgetr(in1,'\n',0))
+ n1 = sfvalue(in1);
+ if(cp2 = sfgetr(in2,'\n',0))
+ n2 = sfvalue(in2);
+ while(cp1 && cp2)
+ {
+ n=(n1<n2?n1:n2);
+ if((comp=memcmp(cp1,cp2,n-1))==0 && (comp=n1-n2)==0)
+ {
+ if(mode&C_COMMON)
+ {
+ if(mode!=C_COMMON)
+ {
+ sfputc(out,'\t');
+ if(mode==C_ALL)
+ sfputc(out,'\t');
+ }
+ if(sfwrite(out,cp1,n) < 0)
+ return(-1);
+ }
+ if(cp1 = sfgetr(in1,'\n',0))
+ n1 = sfvalue(in1);
+ if(cp2 = sfgetr(in2,'\n',0))
+ n2 = sfvalue(in2);
+ }
+ else if(comp > 0)
+ {
+ if(mode&C_FILE2)
+ {
+ if(mode&C_FILE1)
+ sfputc(out,'\t');
+ if(sfwrite(out,cp2,n2) < 0)
+ return(-1);
+ }
+ if(cp2 = sfgetr(in2,'\n',0))
+ n2 = sfvalue(in2);
+ }
+ else
+ {
+ if((mode&C_FILE1) && sfwrite(out,cp1,n1) < 0)
+ return(-1);
+ if(cp1 = sfgetr(in1,'\n',0))
+ n1 = sfvalue(in1);
+ }
+ }
+ n = 0;
+ if(cp2)
+ {
+ cp1 = cp2;
+ in1 = in2;
+ n1 = n2;
+ if(mode&C_FILE1)
+ n = 1;
+ mode &= C_FILE2;
+ }
+ else
+ mode &= C_FILE1;
+ if(!mode || !cp1)
+ {
+ if(cp1 && in1==sfstdin)
+ sfseek(in1,(Sfoff_t)0,SEEK_END);
+ return(0);
+ }
+ /* process the remaining stream */
+ while(1)
+ {
+ if(n)
+ sfputc(out,'\t');
+ if(sfwrite(out,cp1,n1) < 0)
+ return(-1);
+ if(!(cp1 = sfgetr(in1,'\n',0)))
+ return(0);
+ n1 = sfvalue(in1);
+ }
+ /* NOT REACHED */
+}
+
+int
+b_comm(int argc, char *argv[], Shbltin_t* context)
+{
+ register int mode = C_FILE1|C_FILE2|C_COMMON;
+ register char *cp;
+ Sfio_t *f1, *f2;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case '1':
+ mode &= ~C_FILE1;
+ continue;
+ case '2':
+ mode &= ~C_FILE2;
+ continue;
+ case '3':
+ mode &= ~C_COMMON;
+ continue;
+ case ':':
+ error(2, "%s",opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s",opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if(error_info.errors || argc!=2)
+ error(ERROR_usage(2),"%s",optusage(NiL));
+ cp = *argv++;
+ if(streq(cp,"-"))
+ f1 = sfstdin;
+ else if(!(f1 = sfopen(NiL, cp,"r")))
+ error(ERROR_system(1),"%s: cannot open",cp);
+ cp = *argv;
+ if(streq(cp,"-"))
+ f2 = sfstdin;
+ else if(!(f2 = sfopen(NiL, cp,"r")))
+ error(ERROR_system(1),"%s: cannot open",cp);
+ if(mode)
+ {
+ if(comm(f1,f2,sfstdout,mode) < 0)
+ error(ERROR_system(1)," write error");
+ }
+ else if(f1==sfstdin || f2==sfstdin)
+ sfseek(sfstdin,(Sfoff_t)0,SEEK_END);
+ if(f1!=sfstdin)
+ sfclose(f1);
+ if(f2!=sfstdin)
+ sfclose(f2);
+ return error_info.errors;
+}
diff --git a/src/lib/libcmd/cp.c b/src/lib/libcmd/cp.c
new file mode 100644
index 0000000..3d1eddb
--- /dev/null
+++ b/src/lib/libcmd/cp.c
@@ -0,0 +1,1009 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * cp/ln/mv -- copy/link/move files
+ */
+
+static const char usage_head[] =
+"[-?@(#)$Id: cp (AT&T Research) 2011-05-03 $\n]"
+USAGE_LICENSE
+;
+
+static const char usage_cp[] =
+"[+NAME?cp - copy files]"
+"[+DESCRIPTION?If the last argument names an existing directory, \bcp\b "
+ "copies each \afile\a into a file with the same name in that directory. "
+ "Otherwise, if only two files are given, \bcp\b copies the first onto "
+ "the second. It is an error if the last argument is not a directory and "
+ "more than two files are given. By default directories are not copied.]"
+
+"[a:archive?Preserve as much as possible of the structure and attributes "
+ "of the original files in the copy. Equivalent to \b--physical\b "
+ "\b--preserve\b \b--recursive\b.]"
+"[A:attributes?Preserve selected file attributes:]:[eipt]"
+ "{"
+ "[+e?Everything permissible.]"
+ "[+i?Owner uid and gid.]"
+ "[+p?Permissions.]"
+ "[+t?Access and modify times.]"
+ "}"
+"[p:preserve?Preserve file owner, group, permissions and timestamps.]"
+"[h:hierarchy|parents?Form the name of each destination file by "
+ "appending to the target directory a slash and the specified source file "
+ "name. The last argument must be an existing directory. Missing "
+ "destination directories are created.]"
+"[H:metaphysical?Follow command argument symbolic links, otherwise don't "
+ "follow.]"
+"[l:link?Make hard links to destination files instead of copies.]"
+"[U:remove-destination?Remove existing destination files before copying.]"
+"[L:logical|dereference?Follow symbolic links and copy the files they "
+ "point to.]"
+"[P|d:physical|nodereference?Don't follow symbolic links; copy symbolic "
+ "rather than the files they point to.]"
+;
+
+static const char usage_ln[] =
+"[+NAME?ln - link files]"
+"[+DESCRIPTION?If the last argument names an existing directory, \bln\b "
+ "links each \afile\a into a file with the same name in that directory. "
+ "Otherwise, if only two files are given, \bln\b links the first onto the "
+ "second. It is an error if the last argument is not a directory and more "
+ "than two files are given. By default directories are not linked.]"
+;
+
+static const char usage_mv[] =
+"[+NAME?mv - rename files]"
+"[+DESCRIPTION?If the last argument names an existing directory, \bmv\b "
+ "renames each \afile\a into a file with the same name in that directory. "
+ "Otherwise, if only two files are given, \bmv\b renames the first onto "
+ "the second. It is an error if the last argument is not a directory and "
+ "more than two files are given. If a source and destination file reside "
+ "on different filesystems then \bmv\b copies the file contents to the "
+ "destination and then deletes the source file.]"
+
+"[U:remove-destination?Remove existing destination files before moving.]"
+;
+
+static const char usage_tail[] =
+"[f:force?Replace existing destination files.]"
+"[i:interactive|prompt?Prompt whether to replace existing destination "
+ "files. An affirmative response (\by\b or \bY\b) replaces the file, a "
+ "quit response (\bq\b or \bQ\b) exits immediately, and all other "
+ "responses skip the file.]"
+"[r|R:recursive?Operate on the contents of directories recursively.]"
+"[s:symlink|symbolic-link?Make symbolic links to destination files.]"
+"[u:update?Replace a destination file only if its modification time is "
+ "older than the corresponding source file modification time.]"
+"[v:verbose?Print the name of each file before operating on it.]"
+"[F:fsync|sync?\bfsync\b(2) each file after it is copied.]"
+"[B:backup?Make backups of files that are about to be replaced. "
+ "\b--suffix\b sets the backup suffix. The backup type is determined in "
+ "this order: this option, the \bVERSION_CONTROL\b environment variable, "
+ "or the default value \bexisting\b. \atype\a may be one of:]:?[type]"
+ "{"
+ "[+numbered|t?Always make numbered backups. The numbered backup "
+ "suffix is \b.\aSNS\a, where \aS\a is the \bbackup-suffix\b and "
+ "\aN\a is the version number, starting at 1, incremented with "
+ "each version.]"
+ "[+existing|nil?Make numbered backups of files that already have "
+ "them, otherwise simple backups.]"
+ "[+simple|never?Always make simple backups.]"
+ "[+none|off?Disable backups.]"
+ "}"
+"[S:suffix?A backup file is made by renaming the file to the same name "
+ "with the backup suffix appended. The backup suffix is determined in "
+ "this order: this option, the \bSIMPLE_BACKUP_SUFFIX\b, environment "
+ "variable, or the default value \b~\b.]:[suffix]"
+"[b?\b--backup\b using the type in the \bVERSION_CONTROL\b environment "
+ "variable.]"
+"[x|X:xdev|local|mount|one-file-system?Do not descend into directories "
+ "in different filesystems than their parents.]"
+
+"\n"
+"\nsource destination\n"
+"file ... directory\n"
+"\n"
+
+"[+SEE ALSO?\bpax\b(1), \bfsync\b(2), \brename\b(2), \bunlink\b(2),"
+" \bremove\b(3)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+#include <times.h>
+#include <fts_fix.h>
+#include <fs3d.h>
+#include <hashkey.h>
+#include <stk.h>
+#include <tmx.h>
+
+#define PATH_CHUNK 256
+
+#define CP 1
+#define LN 2
+#define MV 3
+
+#define PRESERVE_IDS 0x1 /* preserve uid gid */
+#define PRESERVE_PERM 0x2 /* preserve permissions */
+#define PRESERVE_TIME 0x4 /* preserve times */
+
+#define BAK_replace 0 /* no backup -- just replace */
+#define BAK_existing 1 /* number if already else simple*/
+#define BAK_number 2 /* append .suffix number suffix */
+#define BAK_simple 3 /* append suffix */
+
+typedef struct State_s /* program state */
+{
+ Shbltin_t* context; /* builtin context */
+ int backup; /* BAK_* type */
+ int directory; /* destination is directory */
+ int flags; /* FTS_* flags */
+ int force; /* force approval */
+ int fs3d; /* 3d fs enabled */
+ int hierarchy; /* preserve hierarchy */
+ int interactive; /* prompt for approval */
+ int missmode; /* default missing dir mode */
+ int official; /* move to next view */
+ int op; /* {CP,LN,MV} */
+ int perm; /* permissions to preserve */
+ int postsiz; /* state.path post index */
+ int presiz; /* state.path pre index */
+ int preserve; /* preserve { ids perms times } */
+ int recursive; /* subtrees too */
+ int remove; /* remove destination before op */
+ int suflen; /* strlen(state.suffix) */
+ int sync; /* fsync() each file after copy */
+ int uid; /* caller uid */
+ int update; /* replace only if newer */
+ int verbose; /* list each file before op */
+ int wflags; /* open() for write flags */
+
+ int (*link)(const char*, const char*); /* link */
+ int (*stat)(const char*, struct stat*); /* stat */
+
+#define INITSTATE pathsiz /* (re)init state before this */
+ int pathsiz; /* state.path buffer size */
+
+
+ char* path; /* to pathname buffer */
+ char* opname; /* state.op message string */
+ char* suffix; /* backup suffix */
+
+ Sfio_t* tmp; /* tmp string stream */
+
+ char text[PATH_MAX]; /* link text buffer */
+} State_t;
+
+static const char dot[2] = { '.' };
+
+/*
+ * preserve support
+ */
+
+static void
+preserve(State_t* state, const char* path, struct stat* ns, struct stat* os)
+{
+ int n;
+
+ if ((state->preserve & PRESERVE_TIME) && tmxtouch(path, tmxgetatime(os), tmxgetmtime(os), TMX_NOTIME, 0))
+ error(ERROR_SYSTEM|2, "%s: cannot reset access and modify times", path);
+ if (state->preserve & PRESERVE_IDS)
+ {
+ n = ((ns->st_uid != os->st_uid) << 1) | (ns->st_gid != os->st_gid);
+ if (n && chown(state->path, os->st_uid, os->st_gid))
+ switch (n)
+ {
+ case 01:
+ error(ERROR_SYSTEM|2, "%s: cannot reset group to %s", path, fmtgid(os->st_gid));
+ break;
+ case 02:
+ error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s", path, fmtuid(os->st_uid));
+ break;
+ case 03:
+ error(ERROR_SYSTEM|2, "%s: cannot reset owner to %s and group to %s", path, fmtuid(os->st_uid), fmtgid(os->st_gid));
+ break;
+ }
+ }
+}
+
+/*
+ * visit a single file and state.op to the destination
+ */
+
+static int
+visit(State_t* state, register FTSENT* ent)
+{
+ register char* base;
+ register int n;
+ register int len;
+ int rm;
+ int rfd;
+ int wfd;
+ int m;
+ int v;
+ char* s;
+ char* e;
+ char* protection;
+ Sfio_t* ip;
+ Sfio_t* op;
+ FTS* fts;
+ FTSENT* sub;
+ struct stat st;
+
+ if (ent->fts_info == FTS_DC)
+ {
+ error(2, "%s: directory causes cycle", ent->fts_path);
+ fts_set(NiL, ent, FTS_SKIP);
+ return 0;
+ }
+ if (ent->fts_level == 0)
+ {
+ base = ent->fts_name;
+ len = ent->fts_namelen;
+ if (state->hierarchy)
+ state->presiz = -1;
+ else
+ {
+ state->presiz = ent->fts_pathlen;
+ while (*base == '.' && *(base + 1) == '/')
+ for (base += 2; *base == '/'; base++);
+ if (*base == '.' && !*(base + 1))
+ state->presiz--;
+ else if (*base)
+ state->presiz -= base - ent->fts_name;
+ base = ent->fts_name + len;
+ while (base > ent->fts_name && *(base - 1) == '/')
+ base--;
+ while (base > ent->fts_name && *(base - 1) != '/')
+ base--;
+ len -= base - ent->fts_name;
+ if (state->directory)
+ state->presiz -= len + 1;
+ }
+ }
+ else
+ {
+ base = ent->fts_path + state->presiz + 1;
+ len = ent->fts_pathlen - state->presiz - 1;
+ }
+ len++;
+ if (state->directory)
+ {
+ if ((state->postsiz + len) > state->pathsiz && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + len, PATH_CHUNK), 0)))
+ error(ERROR_SYSTEM|3, "out of space");
+ if (state->hierarchy && ent->fts_level == 0 && strchr(base, '/'))
+ {
+ s = state->path + state->postsiz;
+ memcpy(s, base, len);
+ while (e = strchr(s, '/'))
+ {
+ *e = 0;
+ if (access(state->path, F_OK))
+ {
+ st.st_mode = state->missmode;
+ if (s = strrchr(s, '/'))
+ {
+ *s = 0;
+ stat(state->path, &st);
+ *s = '/';
+ }
+ if (mkdir(state->path, st.st_mode & S_IPERM))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
+ fts_set(NiL, ent, FTS_SKIP);
+ return 0;
+ }
+ }
+ *e++ = '/';
+ s = e;
+ }
+ }
+ }
+ switch (ent->fts_info)
+ {
+ case FTS_DP:
+ if (state->preserve && state->op != LN || ent->fts_level > 0 && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
+ {
+ if (len && ent->fts_level > 0)
+ memcpy(state->path + state->postsiz, base, len);
+ else
+ state->path[state->postsiz] = 0;
+ if (stat(state->path, &st))
+ error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
+ else
+ {
+ if ((ent->fts_statp->st_mode & S_IPERM) != (st.st_mode & S_IPERM) && chmod(state->path, ent->fts_statp->st_mode & S_IPERM))
+ error(ERROR_SYSTEM|2, "%s: cannot reset directory mode to %s", state->path, fmtmode(st.st_mode & S_IPERM, 0) + 1);
+ if (state->preserve & (PRESERVE_IDS|PRESERVE_TIME))
+ preserve(state, state->path, &st, ent->fts_statp);
+ }
+ }
+ return 0;
+ case FTS_DNR:
+ case FTS_DNX:
+ case FTS_D:
+ if (!state->recursive)
+ {
+ fts_set(NiL, ent, FTS_SKIP);
+ if (state->op == CP)
+ error(1, "%s: directory -- copying as plain file", ent->fts_path);
+ else if (state->link == link && !state->force)
+ {
+ error(2, "%s: cannot link directory", ent->fts_path);
+ return 0;
+ }
+ }
+ else switch (ent->fts_info)
+ {
+ case FTS_DNR:
+ error(2, "%s: cannot read directory", ent->fts_path);
+ return 0;
+ case FTS_DNX:
+ error(2, "%s: cannot search directory", ent->fts_path);
+ fts_set(NiL, ent, FTS_SKIP);
+
+ /*FALLTHROUGH*/
+ case FTS_D:
+ if (state->directory)
+ memcpy(state->path + state->postsiz, base, len);
+ if (!(*state->stat)(state->path, &st))
+ {
+ if (!S_ISDIR(st.st_mode))
+ {
+ error(2, "%s: not a directory -- %s ignored", state->path, ent->fts_path);
+ return 0;
+ }
+ }
+ else if (mkdir(state->path, (ent->fts_statp->st_mode & S_IPERM)|(ent->fts_info == FTS_D ? S_IRWXU : 0)))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot create directory -- %s ignored", state->path, ent->fts_path);
+ fts_set(NiL, ent, FTS_SKIP);
+ }
+ if (!state->directory)
+ {
+ state->directory = 1;
+ state->path[state->postsiz++] = '/';
+ state->presiz--;
+ }
+ return 0;
+ }
+ break;
+ case FTS_ERR:
+ case FTS_NS:
+ case FTS_SLNONE:
+ if (state->link != pathsetlink)
+ {
+ error(2, "%s: not found", ent->fts_path);
+ return 0;
+ }
+ break;
+#if 0
+ case FTS_SL:
+ if (state->op == CP)
+ {
+ error(2, "%s: cannot copy non-terminal symbolic link", ent->fts_path);
+ return 0;
+ }
+ break;
+#endif
+ }
+ if (state->directory)
+ memcpy(state->path + state->postsiz, base, len);
+ if ((*state->stat)(state->path, &st))
+ st.st_mode = 0;
+ else if (state->update && !S_ISDIR(st.st_mode) && (unsigned long)ent->fts_statp->st_mtime < (unsigned long)st.st_mtime)
+ {
+ fts_set(NiL, ent, FTS_SKIP);
+ return 0;
+ }
+ else if (!state->fs3d || !iview(&st))
+ {
+ /*
+ * target is in top 3d view
+ */
+
+ if (state->op != LN && st.st_dev == ent->fts_statp->st_dev && st.st_ino == ent->fts_statp->st_ino)
+ {
+ if (state->op == MV)
+ {
+ /*
+ * let rename() handle it
+ */
+
+ if (state->verbose)
+ sfputr(sfstdout, state->path, '\n');
+ goto operate;
+ }
+ if (!state->official)
+ error(2, "%s: identical to %s", state->path, ent->fts_path);
+ return 0;
+ }
+ if (S_ISDIR(st.st_mode))
+ {
+ error(2, "%s: cannot %s existing directory", state->path, state->opname);
+ return 0;
+ }
+ if (state->verbose)
+ sfputr(sfstdout, state->path, '\n');
+ rm = state->remove || ent->fts_info == FTS_SL;
+ if (!rm || !state->force)
+ {
+ if (S_ISLNK(st.st_mode) && (n = -1) || (n = open(state->path, O_RDWR|O_BINARY)) >= 0)
+ {
+ if (n >= 0)
+ close(n);
+ if (state->force)
+ /* ok */;
+ else if (state->interactive)
+ {
+ if (astquery(-1, "%s %s? ", state->opname, state->path) < 0 || sh_checksig(state->context))
+ return 0;
+ }
+ else if (state->op == LN)
+ {
+ error(2, "%s: cannot %s existing file", state->path, state->opname);
+ return 0;
+ }
+ }
+ else if (state->force)
+ rm = 1;
+ else
+ {
+ protection =
+#ifdef ETXTBSY
+ errno == ETXTBSY ? "``running program''" :
+#endif
+ st.st_uid != state->uid ? "``not owner''" :
+ fmtmode(st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO), 0) + 1;
+ if (state->interactive)
+ {
+ if (astquery(-1, "override protection %s for %s? ", protection, state->path) < 0 || sh_checksig(state->context))
+ return 0;
+ rm = 1;
+ }
+ else if (!rm)
+ {
+ error(2, "%s: cannot %s %s protection", state->path, state->opname, protection);
+ return 0;
+ }
+ }
+ }
+ switch (state->backup)
+ {
+ case BAK_existing:
+ case BAK_number:
+ v = 0;
+ if (s = strrchr(state->path, '/'))
+ {
+ e = state->path;
+ *s++ = 0;
+ }
+ else
+ {
+ e = (char*)dot;
+ s = state->path;
+ }
+ n = strlen(s);
+ if (fts = fts_open((char**)e, FTS_NOCHDIR|FTS_ONEPATH|FTS_PHYSICAL|FTS_NOPOSTORDER|FTS_NOSTAT|FTS_NOSEEDOTDIR, NiL))
+ {
+ while (sub = fts_read(fts))
+ {
+ if (strneq(s, sub->fts_name, n) && sub->fts_name[n] == '.' && strneq(sub->fts_name + n + 1, state->suffix, state->suflen) && (m = strtol(sub->fts_name + n + state->suflen + 1, &e, 10)) && streq(e, state->suffix) && m > v)
+ v = m;
+ if (sub->fts_level)
+ fts_set(NiL, sub, FTS_SKIP);
+ }
+ fts_close(fts);
+ }
+ if (s != state->path)
+ *--s = '/';
+ if (v || state->backup == BAK_number)
+ {
+ sfprintf(state->tmp, "%s.%s%d%s", state->path, state->suffix, v + 1, state->suffix);
+ goto backup;
+ }
+ /*FALLTHROUGH*/
+ case BAK_simple:
+ sfprintf(state->tmp, "%s%s", state->path, state->suffix);
+ backup:
+ if (!(s = sfstruse(state->tmp)))
+ error(ERROR_SYSTEM|3, "%s: out of space", state->path);
+ if (rename(state->path, s))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot backup to %s", state->path, s);
+ return 0;
+ }
+ break;
+ default:
+ if (rm && remove(state->path))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot remove", state->path);
+ return 0;
+ }
+ break;
+ }
+ }
+ operate:
+ switch (state->op)
+ {
+ case MV:
+ for (;;)
+ {
+ if (!rename(ent->fts_path, state->path))
+ return 0;
+ if (errno == ENOENT)
+ rm = 1;
+ else if (!rm && st.st_mode && !remove(state->path))
+ {
+ rm = 1;
+ continue;
+ }
+ if (errno != EXDEV && (rm || S_ISDIR(ent->fts_statp->st_mode)))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot rename to %s", ent->fts_path, state->path);
+ return 0;
+ }
+ else
+ break;
+ }
+ /*FALLTHROUGH*/
+ case CP:
+ if (S_ISLNK(ent->fts_statp->st_mode))
+ {
+ if ((n = pathgetlink(ent->fts_path, state->text, sizeof(state->text) - 1)) < 0)
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot read symbolic link text", ent->fts_path);
+ return 0;
+ }
+ state->text[n] = 0;
+ if (pathsetlink(state->text, state->path))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot copy symbolic link to %s", ent->fts_path, state->path);
+ return 0;
+ }
+ }
+ else if (state->op == CP || S_ISREG(ent->fts_statp->st_mode) || S_ISDIR(ent->fts_statp->st_mode))
+ {
+ if (ent->fts_statp->st_size > 0 && (rfd = open(ent->fts_path, O_RDONLY|O_BINARY)) < 0)
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot read", ent->fts_path);
+ return 0;
+ }
+ else if ((wfd = open(state->path, st.st_mode ? (state->wflags & ~O_EXCL) : state->wflags, ent->fts_statp->st_mode & state->perm)) < 0)
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot write", state->path);
+ if (ent->fts_statp->st_size > 0)
+ close(rfd);
+ return 0;
+ }
+ else if (ent->fts_statp->st_size > 0)
+ {
+ if (!(ip = sfnew(NiL, NiL, SF_UNBOUND, rfd, SF_READ)))
+ {
+ error(ERROR_SYSTEM|2, "%s: %s read stream error", ent->fts_path, state->path);
+ close(rfd);
+ close(wfd);
+ return 0;
+ }
+ if (!(op = sfnew(NiL, NiL, SF_UNBOUND, wfd, SF_WRITE)))
+ {
+ error(ERROR_SYSTEM|2, "%s: %s write stream error", ent->fts_path, state->path);
+ close(wfd);
+ sfclose(ip);
+ return 0;
+ }
+ n = 0;
+ if (sfmove(ip, op, (Sfoff_t)SF_UNBOUND, -1) < 0)
+ n |= 3;
+ if (!sfeof(ip))
+ n |= 1;
+ if (sfsync(op) || state->sync && fsync(wfd) || sfclose(op))
+ n |= 2;
+ if (sfclose(ip))
+ n |= 1;
+ if (n)
+ {
+ error(ERROR_SYSTEM|2, "%s: %s %s error", ent->fts_path, state->path, n == 1 ? ERROR_translate(0, 0, 0, "read") : n == 2 ? ERROR_translate(0, 0, 0, "write") : ERROR_translate(0, 0, 0, "io"));
+ return 0;
+ }
+ }
+ else
+ close(wfd);
+ }
+ else if (S_ISBLK(ent->fts_statp->st_mode) || S_ISCHR(ent->fts_statp->st_mode) || S_ISFIFO(ent->fts_statp->st_mode))
+ {
+ if (mknod(state->path, ent->fts_statp->st_mode, idevice(ent->fts_statp)))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot copy special file to %s", ent->fts_path, state->path);
+ return 0;
+ }
+ }
+ else
+ {
+ error(2, "%s: cannot copy -- unknown file type 0%o", ent->fts_path, S_ITYPE(ent->fts_statp->st_mode));
+ return 0;
+ }
+ if (state->preserve)
+ {
+ if (ent->fts_info != FTS_SL)
+ {
+ if (stat(state->path, &st))
+ error(ERROR_SYSTEM|2, "%s: cannot stat", state->path);
+ else
+ {
+ if ((state->preserve & PRESERVE_PERM) && (ent->fts_statp->st_mode & state->perm) != (st.st_mode & state->perm) && chmod(state->path, ent->fts_statp->st_mode & state->perm))
+ error(ERROR_SYSTEM|2, "%s: cannot reset mode to %s", state->path, fmtmode(st.st_mode & state->perm, 0) + 1);
+ if (state->preserve & (PRESERVE_IDS|PRESERVE_TIME))
+ preserve(state, state->path, &st, ent->fts_statp);
+ }
+ }
+ if (state->op == MV && remove(ent->fts_path))
+ error(ERROR_SYSTEM|1, "%s: cannot remove", ent->fts_path);
+ }
+ break;
+ case LN:
+ if ((*state->link)(ent->fts_path, state->path))
+ error(ERROR_SYSTEM|2, "%s: cannot link to %s", ent->fts_path, state->path);
+ break;
+ }
+ return 0;
+}
+
+int
+b_cp(int argc, register char** argv, Shbltin_t* context)
+{
+ register char* file;
+ register char* s;
+ char** v;
+ char* backup_type;
+ FTS* fts;
+ FTSENT* ent;
+ const char* usage;
+ int path_resolve;
+ int standard;
+ struct stat st;
+ State_t* state;
+ Shbltin_t* sh;
+ Shbltin_t* cleanup = context;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+ if (!(sh = CMD_CONTEXT(context)) || !(state = (State_t*)sh->ptr))
+ {
+ if (!(state = newof(0, State_t, 1, 0)))
+ error(ERROR_SYSTEM|3, "out of space");
+ if (sh)
+ sh->ptr = state;
+ }
+ else
+ memset(state, 0, offsetof(State_t, INITSTATE));
+ state->context = context;
+ state->presiz = -1;
+ backup_type = 0;
+ state->flags = FTS_NOCHDIR|FTS_NOSEEDOTDIR;
+ state->uid = geteuid();
+ state->wflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
+ if (!state->tmp && !(state->tmp = sfstropen()))
+ error(ERROR_SYSTEM|3, "out of space [tmp string]");
+ sfputr(state->tmp, usage_head, -1);
+ standard = !!conformance(0, 0);
+ switch (error_info.id[0])
+ {
+ case 'c':
+ case 'C':
+ sfputr(state->tmp, usage_cp, -1);
+ state->op = CP;
+ state->stat = stat;
+ path_resolve = -1;
+ break;
+ case 'l':
+ case 'L':
+ sfputr(state->tmp, usage_ln, -1);
+ state->op = LN;
+ state->flags |= FTS_PHYSICAL;
+ state->link = link;
+ state->remove = 1;
+ state->stat = lstat;
+ path_resolve = 1;
+ break;
+ case 'm':
+ case 'M':
+ sfputr(state->tmp, usage_mv, -1);
+ state->op = MV;
+ state->flags |= FTS_PHYSICAL;
+ state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
+ state->stat = lstat;
+ path_resolve = 1;
+ break;
+ default:
+ error(3, "not implemented");
+ break;
+ }
+ sfputr(state->tmp, usage_tail, -1);
+ if (!(usage = sfstruse(state->tmp)))
+ error(ERROR_SYSTEM|3, "%s: out of space", state->path);
+ state->opname = state->op == CP ? ERROR_translate(0, 0, 0, "overwrite") : ERROR_translate(0, 0, 0, "replace");
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ state->flags |= FTS_PHYSICAL;
+ state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
+ state->recursive = 1;
+ path_resolve = 1;
+ continue;
+ case 'A':
+ s = opt_info.arg;
+ for (;;)
+ {
+ switch (*s++)
+ {
+ case 0:
+ break;
+ case 'e':
+ state->preserve |= PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
+ continue;
+ case 'i':
+ state->preserve |= PRESERVE_IDS;
+ continue;
+ case 'p':
+ state->preserve |= PRESERVE_PERM;
+ continue;
+ case 't':
+ state->preserve |= PRESERVE_TIME;
+ continue;
+ default:
+ error(1, "%s=%c: unknown attribute flag", opt_info.option, *(s - 1));
+ continue;
+ }
+ break;
+ }
+ continue;
+ case 'b':
+ state->backup = 1;
+ continue;
+ case 'f':
+ state->force = 1;
+ if (state->op != CP || !standard)
+ state->interactive = 0;
+ continue;
+ case 'h':
+ state->hierarchy = 1;
+ continue;
+ case 'i':
+ state->interactive = 1;
+ if (state->op != CP || !standard)
+ state->force = 0;
+ continue;
+ case 'l':
+ state->op = LN;
+ state->link = link;
+ state->stat = lstat;
+ continue;
+ case 'p':
+ state->preserve = PRESERVE_IDS|PRESERVE_PERM|PRESERVE_TIME;
+ continue;
+ case 'r':
+ state->recursive = 1;
+ if (path_resolve < 0)
+ path_resolve = 0;
+ continue;
+ case 's':
+ state->op = LN;
+ state->link = pathsetlink;
+ state->stat = lstat;
+ continue;
+ case 'u':
+ state->update = 1;
+ continue;
+ case 'v':
+ state->verbose = 1;
+ continue;
+ case 'x':
+ state->flags |= FTS_XDEV;
+ continue;
+ case 'B':
+ backup_type = opt_info.arg;
+ state->backup = 1;
+ continue;
+ case 'F':
+#if _lib_fsync
+ state->sync = 1;
+#else
+ error(1, "%s not implemented on this system", opt_info.name);
+#endif
+ continue;
+ case 'H':
+ state->flags |= FTS_META|FTS_PHYSICAL;
+ path_resolve = 1;
+ continue;
+ case 'L':
+ state->flags &= ~FTS_PHYSICAL;
+ path_resolve = 1;
+ continue;
+ case 'P':
+ state->flags &= ~FTS_META;
+ state->flags |= FTS_PHYSICAL;
+ path_resolve = 1;
+ continue;
+ case 'R':
+ state->recursive = 1;
+ state->flags &= ~FTS_META;
+ state->flags |= FTS_PHYSICAL;
+ path_resolve = 1;
+ continue;
+ case 'S':
+ state->suffix = opt_info.arg;
+ continue;
+ case 'U':
+ state->remove = 1;
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ continue;
+ }
+ break;
+ }
+ argc -= opt_info.index + 1;
+ argv += opt_info.index;
+ if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
+ {
+ argc--;
+ argv++;
+ }
+ if (!(v = (char**)stkalloc(stkstd, (argc + 2) * sizeof(char*))))
+ error(ERROR_SYSTEM|3, "out of space");
+ memcpy(v, argv, (argc + 1) * sizeof(char*));
+ argv = v;
+ if (!standard)
+ {
+ state->wflags |= O_EXCL;
+ if (!argc)
+ {
+ argc++;
+ argv[1] = (char*)dot;
+ }
+ }
+ if (state->backup)
+ {
+ if (!(file = backup_type) && !(backup_type = getenv("VERSION_CONTROL")))
+ state->backup = 0;
+ else
+ switch (strkey(backup_type))
+ {
+ case HASHKEY6('e','x','i','s','t','i'):
+ case HASHKEY5('e','x','i','s','t'):
+ case HASHKEY4('e','x','i','s'):
+ case HASHKEY3('e','x','i'):
+ case HASHKEY2('e','x'):
+ case HASHKEY1('e'):
+ case HASHKEY3('n','i','l'):
+ case HASHKEY2('n','i'):
+ state->backup = BAK_existing;
+ break;
+ case HASHKEY5('n','e','v','e','r'):
+ case HASHKEY4('n','e','v','e'):
+ case HASHKEY3('n','e','v'):
+ case HASHKEY2('n','e'):
+ case HASHKEY6('s','i','m','p','l','e'):
+ case HASHKEY5('s','i','m','p','l'):
+ case HASHKEY4('s','i','m','p'):
+ case HASHKEY3('s','i','m'):
+ case HASHKEY2('s','i'):
+ case HASHKEY1('s'):
+ state->backup = BAK_simple;
+ break;
+ case HASHKEY4('n','o','n','e'):
+ case HASHKEY3('n','o','n'):
+ case HASHKEY2('n','o'):
+ case HASHKEY3('o','f','f'):
+ case HASHKEY2('o','f'):
+ case HASHKEY1('o'):
+ state->backup = 0;
+ break;
+ case HASHKEY6('n','u','m','b','e','r'):
+ case HASHKEY5('n','u','m','b','e'):
+ case HASHKEY4('n','u','m','b'):
+ case HASHKEY3('n','u','m'):
+ case HASHKEY2('n','u'):
+ case HASHKEY1('t'):
+ state->backup = BAK_number;
+ break;
+ default:
+ if (file)
+ error(2, "%s: unknown backup type", backup_type);
+ break;
+ }
+ if (!state->suffix && !(state->suffix = getenv("SIMPLE_BACKUP_SUFFIX")))
+ state->suffix = "~";
+ state->suflen = strlen(state->suffix);
+ }
+ if (argc <= 0 || error_info.errors)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ if (!path_resolve)
+ state->flags |= fts_flags();
+ file = argv[argc];
+ argv[argc] = 0;
+ if (s = strrchr(file, '/'))
+ {
+ while (*s == '/')
+ s++;
+ if (!(!*s || *s == '.' && (!*++s || *s == '.' && !*++s)))
+ s = 0;
+ }
+ if (file != (char*)dot)
+ pathcanon(file, 0, 0);
+ if (!(state->directory = !stat(file, &st) && S_ISDIR(st.st_mode)) && argc > 1)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ if (s && !state->directory)
+ error(3, "%s: not a directory", file);
+ if ((state->fs3d = fs3d(FS3D_TEST)) && strmatch(file, "...|*/...|.../*"))
+ state->official = 1;
+ state->postsiz = strlen(file);
+ if (state->pathsiz < roundof(state->postsiz + 2, PATH_CHUNK) && !(state->path = newof(state->path, char, state->pathsiz = roundof(state->postsiz + 2, PATH_CHUNK), 0)))
+ error(ERROR_SYSTEM|3, "out of space");
+ memcpy(state->path, file, state->postsiz + 1);
+ if (state->directory && state->path[state->postsiz - 1] != '/')
+ state->path[state->postsiz++] = '/';
+ if (state->hierarchy)
+ {
+ if (!state->directory)
+ error(3, "%s: last argument must be a directory", file);
+ state->missmode = st.st_mode;
+ }
+ state->perm = state->uid ? S_IPERM : (S_IPERM & ~S_ISVTX);
+ if (!state->recursive)
+ state->flags |= FTS_TOP;
+ if (fts = fts_open(argv, state->flags, NiL))
+ {
+ while (!sh_checksig(context) && (ent = fts_read(fts)) && !visit(state, ent));
+ fts_close(fts);
+ }
+ else if (state->link != pathsetlink)
+ switch (state->op)
+ {
+ case CP:
+ error(ERROR_SYSTEM|2, "%s: cannot copy", argv[0]);
+ break;
+ case LN:
+ error(ERROR_SYSTEM|2, "%s: cannot link", argv[0]);
+ break;
+ case MV:
+ error(ERROR_SYSTEM|2, "%s: cannot move", argv[0]);
+ break;
+ }
+ else if ((*state->link)(*argv, state->path))
+ error(ERROR_SYSTEM|2, "%s: cannot link to %s", *argv, state->path);
+ if (cleanup && !sh)
+ {
+ if (state->path)
+ free(state->path);
+ free(state);
+ }
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/cut.c b/src/lib/libcmd/cut.c
new file mode 100644
index 0000000..985b789
--- /dev/null
+++ b/src/lib/libcmd/cut.c
@@ -0,0 +1,702 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * cut fields or columns from fields from a file
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: cut (AT&T Research) 2010-08-11 $\n]"
+USAGE_LICENSE
+"[+NAME?cut - cut out selected columns or fields of each line of a file]"
+"[+DESCRIPTION?\bcut\b bytes, characters, or character-delimited fields "
+ "from one or more files, contatenating them on standard output.]"
+"[+?The option argument \alist\a is a comma-separated or blank-separated "
+ "list of positive numbers and ranges. Ranges can be of three "
+ "forms. The first is two positive integers separated by a hyphen "
+ "(\alow\a\b-\b\ahigh\a), which represents all fields from \alow\a to "
+ "\ahigh\a. The second is a positive number preceded by a hyphen "
+ "(\b-\b\ahigh\a), which represents all fields from field \b1\b to "
+ "\ahigh\a. The last is a positive number followed by a hyphen "
+ "(\alow\a\b-\b), which represents all fields from \alow\a to the "
+ "last field, inclusive. Elements in the \alist\a can be repeated, "
+ "can overlap, and can appear in any order. The order of the "
+ "output is that of the input.]"
+"[+?One and only one of \b-b\b, \b-c\b, or \b-f\b must be specified.]"
+"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bcut\b "
+ "cuts from standard input. The start of the file is defined "
+ "as the current offset.]"
+"[b:bytes]:[list?\bcut\b based on a list of byte counts.]"
+"[c:characters]:[list?\bcut\b based on a list of character counts.]"
+"[d:delimiter]:[delim?The field character for the \b-f\b option is set "
+ "to \adelim\a. The default is the \btab\b character.]"
+"[f:fields]:[list?\bcut\b based on fields separated by the delimiter "
+ "character specified with the \b-d\b optiion.]"
+"[n!:split?Split multibyte characters selected by the \b-b\b option.]"
+"[R|r:reclen]#[reclen?If \areclen\a > 0, the input will be read as fixed length "
+ "records of length \areclen\a when used with the \b-b\b or \b-c\b "
+ "option.]"
+"[s:suppress|only-delimited?Suppress lines with no delimiter characters, "
+ "when used with the \b-f\b option. By default, lines with no "
+ "delimiters will be passsed in untouched.]"
+"[D:line-delimeter|output-delimiter]:[ldelim?The line delimiter character for "
+ "the \b-f\b option is set to \aldelim\a. The default is the "
+ "\bnewline\b character.]"
+"[N!:newline?Output new-lines at end of each record when used "
+ "with the \b-b\b or \b-c\b option.]"
+"\n"
+"\n[file ...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files processed successfully.]"
+ "[+>0?One or more files failed to open or could not be read.]"
+"}"
+"[+SEE ALSO?\bpaste\b(1), \bgrep\b(1)]"
+;
+
+#include <cmd.h>
+#include <ctype.h>
+
+typedef struct Delim_s
+{
+ char* str;
+ int len;
+ int chr;
+} Delim_t;
+
+typedef struct Cut_s
+{
+ int mb;
+ int eob;
+ int cflag;
+ int nosplit;
+ int sflag;
+ int nlflag;
+ int reclen;
+ Delim_t wdelim;
+ Delim_t ldelim;
+ unsigned char space[UCHAR_MAX+1];
+ int list[2]; /* NOTE: must be last member */
+} Cut_t;
+
+#define HUGE INT_MAX
+#define BLOCK 8*1024
+#define C_BYTES 1
+#define C_CHARS 2
+#define C_FIELDS 4
+#define C_SUPRESS 8
+#define C_NOSPLIT 16
+#define C_NONEWLINE 32
+
+#define SP_LINE 1
+#define SP_WORD 2
+#define SP_WIDE 3
+
+/*
+ * compare the first of an array of integers
+ */
+
+static int
+mycomp(register const void* a, register const void* b)
+{
+ if (*((int*)a) < *((int*)b))
+ return -1;
+ if (*((int*)a) > *((int*)b))
+ return 1;
+ return 0;
+}
+
+static Cut_t*
+cutinit(int mode, char* str, Delim_t* wdelim, Delim_t* ldelim, size_t reclen)
+{
+ register int* lp;
+ register int c;
+ register int n = 0;
+ register int range = 0;
+ register char* cp = str;
+ Cut_t* cut;
+
+ if (!(cut = (Cut_t*)stakalloc(sizeof(Cut_t) + strlen(cp) * sizeof(int))))
+ error(ERROR_exit(1), "out of space");
+ if (cut->mb = mbwide())
+ {
+ memset(cut->space, 0, sizeof(cut->space) / 2);
+ memset(cut->space + sizeof(cut->space) / 2, SP_WIDE, sizeof(cut->space) / 2);
+ }
+ else
+ memset(cut->space, 0, sizeof(cut->space));
+ cut->wdelim = *wdelim;
+ if (wdelim->len == 1)
+ cut->space[wdelim->chr] = SP_WORD;
+ cut->ldelim = *ldelim;
+ cut->eob = (ldelim->len == 1) ? ldelim->chr : 0;
+ cut->space[cut->eob] = SP_LINE;
+ cut->cflag = (mode&C_CHARS) && cut->mb;
+ cut->nosplit = (mode&(C_BYTES|C_NOSPLIT)) == (C_BYTES|C_NOSPLIT) && cut->mb;
+ cut->sflag = (mode&C_SUPRESS) != 0;
+ cut->nlflag = (mode&C_NONEWLINE) != 0;
+ cut->reclen = reclen;
+ lp = cut->list;
+ for (;;)
+ switch(c = *cp++)
+ {
+ case ' ':
+ case '\t':
+ while(*cp==' ' || *cp=='\t')
+ cp++;
+ /*FALLTHROUGH*/
+ case 0:
+ case ',':
+ if(range)
+ {
+ --range;
+ if((n = (n ? (n-range) : (HUGE-1))) < 0)
+ error(ERROR_exit(1),"invalid range for c/f option");
+ *lp++ = range;
+ *lp++ = n;
+ }
+ else
+ {
+ *lp++ = --n;
+ *lp++ = 1;
+ }
+ if(c==0)
+ {
+ register int *dp;
+ *lp = HUGE;
+ n = 1 + (lp-cut->list)/2;
+ qsort(lp=cut->list,n,2*sizeof(*lp),mycomp);
+ /* eliminate overlapping regions */
+ for(n=0,range= -2,dp=lp; *lp!=HUGE; lp+=2)
+ {
+ if(lp[0] <= range)
+ {
+ if(lp[1]==HUGE)
+ {
+ dp[-1] = HUGE;
+ break;
+ }
+ if((c = lp[0]+lp[1]-range)>0)
+ {
+ range += c;
+ dp[-1] += c;
+ }
+ }
+ else
+ {
+ range = *dp++ = lp[0];
+ if(lp[1]==HUGE)
+ {
+ *dp++ = HUGE;
+ break;
+ }
+ range += (*dp++ = lp[1]);
+ }
+ }
+ *dp = HUGE;
+ lp = cut->list;
+ /* convert ranges into gaps */
+ for(n=0; *lp!=HUGE; lp+=2)
+ {
+ c = *lp;
+ *lp -= n;
+ n = c+lp[1];
+ }
+ return cut;
+ }
+ n = range = 0;
+ break;
+
+ case '-':
+ if(range)
+ error(ERROR_exit(1),"bad list for c/f option");
+ range = n?n:1;
+ n = 0;
+ break;
+
+ default:
+ if(!isdigit(c))
+ error(ERROR_exit(1),"bad list for c/f option");
+ n = 10*n + (c-'0');
+ break;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * cut each line of file <fdin> and put results to <fdout> using list <list>
+ */
+
+static void
+cutcols(Cut_t* cut, Sfio_t* fdin, Sfio_t* fdout)
+{
+ register int c;
+ register int len;
+ register int ncol = 0;
+ register const int* lp = cut->list;
+ register char* bp;
+ register int skip; /* non-zero for don't copy */
+ int must;
+ char* ep;
+ const char* xx;
+
+ for (;;)
+ {
+ if (len = cut->reclen)
+ bp = sfreserve(fdin, len, -1);
+ else
+ bp = sfgetr(fdin, '\n', 0);
+ if (!bp && !(bp = sfgetr(fdin, 0, SF_LASTR)))
+ break;
+ len = sfvalue(fdin);
+ ep = bp + len;
+ xx = 0;
+ if (!(ncol = skip = *(lp = cut->list)))
+ ncol = *++lp;
+ must = 1;
+ do
+ {
+ if (cut->nosplit)
+ {
+ register const char* s = bp;
+ register int w = len < ncol ? len : ncol;
+ register int z;
+
+ while (w > 0)
+ {
+ if (!(*s & 0x80))
+ z = 1;
+ else if ((z = mbnsize(s, w)) <= 0)
+ {
+ if (s == bp && xx)
+ {
+ w += s - xx;
+ bp = (char*)(s = xx);
+ xx = 0;
+ continue;
+ }
+ xx = s;
+ if (skip)
+ s += w;
+ w = 0;
+ break;
+ }
+ s += z;
+ w -= z;
+ }
+ c = s - bp;
+ ncol = !w && ncol >= len;
+ }
+ else if (cut->cflag)
+ {
+ register const char* s = bp;
+ register int w = len;
+ register int z;
+
+ while (w > 0 && ncol > 0)
+ {
+ ncol--;
+ if (!(*s & 0x80) || (z = mbnsize(s, w)) <= 0)
+ z = 1;
+ s += z;
+ w -= z;
+
+ }
+ c = s - bp;
+ ncol = !w && (ncol || !skip);
+ }
+ else
+ {
+ if ((c = ncol) > len)
+ c = len;
+ else if (c == len && !skip)
+ ncol++;
+ ncol -= c;
+ }
+ if (!skip && c)
+ {
+ if (sfwrite(fdout, (char*)bp, c) < 0)
+ return;
+ must = 0;
+ }
+ bp += c;
+ if (ncol)
+ break;
+ len -= c;
+ ncol = *++lp;
+ skip = !skip;
+ } while (ncol != HUGE);
+ if (!cut->nlflag && (skip || must || cut->reclen))
+ {
+ if (cut->ldelim.len > 1)
+ sfwrite(fdout, cut->ldelim.str, cut->ldelim.len);
+ else
+ sfputc(fdout, cut->ldelim.chr);
+ }
+ }
+}
+
+/*
+ * cut each line of file <fdin> and put results to <fdout> using list <list>
+ * stream <fdin> must be line buffered
+ */
+
+static void
+cutfields(Cut_t* cut, Sfio_t* fdin, Sfio_t* fdout)
+{
+ register unsigned char *sp = cut->space;
+ register unsigned char *cp;
+ register unsigned char *wp;
+ register int c, nfields;
+ register const int *lp = cut->list;
+ register unsigned char *copy;
+ register int nodelim, empty, inword=0;
+ register unsigned char *ep;
+ unsigned char *bp, *first;
+ int lastchar;
+ wchar_t w;
+ Sfio_t *fdtmp = 0;
+ long offset = 0;
+ unsigned char mb[8];
+ /* process each buffer */
+ while ((bp = (unsigned char*)sfreserve(fdin, SF_UNBOUND, -1)) && (c = sfvalue(fdin)) > 0)
+ {
+ cp = bp;
+ ep = cp + --c;
+ if((lastchar = cp[c]) != cut->eob)
+ *ep = cut->eob;
+ /* process each line in the buffer */
+ while (cp <= ep)
+ {
+ first = cp;
+ if (!inword)
+ {
+ nodelim = empty = 1;
+ copy = cp;
+ if (nfields = *(lp = cut->list))
+ copy = 0;
+ else
+ nfields = *++lp;
+ }
+ else if (copy)
+ copy = cp;
+ inword = 0;
+ do
+ {
+ /* skip over non-delimiter characters */
+ if (cut->mb)
+ for (;;)
+ {
+ switch (c = sp[*(unsigned char*)cp++])
+ {
+ case 0:
+ continue;
+ case SP_WIDE:
+ wp = --cp;
+ while ((c = mb2wc(w, cp, ep - cp)) <= 0)
+ {
+ /* mb char possibly spanning buffer boundary -- fun stuff */
+ if ((ep - cp) < mbmax())
+ {
+ int i;
+ int j;
+ int k;
+
+ if (lastchar != cut->eob)
+ {
+ *ep = lastchar;
+ if ((c = mb2wc(w, cp, ep - cp)) > 0)
+ break;
+ }
+ if (copy)
+ {
+ empty = 0;
+ if ((c = cp - copy) > 0 && sfwrite(fdout, (char*)copy, c) < 0)
+ goto failed;
+ }
+ for (i = 0; i <= (ep - cp); i++)
+ mb[i] = cp[i];
+ if (!(bp = (unsigned char*)sfreserve(fdin, SF_UNBOUND, -1)) || (c = sfvalue(fdin)) <= 0)
+ goto failed;
+ cp = bp;
+ ep = cp + --c;
+ if ((lastchar = cp[c]) != cut->eob)
+ *ep = cut->eob;
+ j = i;
+ k = 0;
+ while (j < mbmax())
+ mb[j++] = cp[k++];
+ if ((c = mb2wc(w, (char*)mb, j)) <= 0)
+ {
+ c = i;
+ w = 0;
+ }
+ first = bp = cp += c - i;
+ if (copy)
+ {
+ copy = bp;
+ if (w == cut->ldelim.chr)
+ lastchar = cut->ldelim.chr;
+ else if (w != cut->wdelim.chr)
+ {
+ empty = 0;
+ if (sfwrite(fdout, (char*)mb, c) < 0)
+ goto failed;
+ }
+ }
+ c = 0;
+ }
+ else
+ {
+ w = *cp;
+ c = 1;
+ }
+ break;
+ }
+ cp += c;
+ c = w;
+ if (c == cut->wdelim.chr)
+ {
+ c = SP_WORD;
+ break;
+ }
+ if (c == cut->ldelim.chr)
+ {
+ c = SP_LINE;
+ break;
+ }
+ continue;
+ default:
+ wp = cp - 1;
+ break;
+ }
+ break;
+ }
+ else
+ {
+ while (!(c = sp[*cp++]));
+ wp = cp - 1;
+ }
+ /* check for end-of-line */
+ if (c == SP_LINE)
+ {
+ if (cp <= ep)
+ break;
+ if (lastchar == cut->ldelim.chr)
+ break;
+ /* restore cut->last character */
+ if (lastchar != cut->eob)
+ *ep = lastchar;
+ inword++;
+ if (!sp[lastchar])
+ break;
+ }
+ nodelim = 0;
+ if (--nfields > 0)
+ continue;
+ nfields = *++lp;
+ if (copy)
+ {
+ empty = 0;
+ if ((c = wp - copy) > 0 && sfwrite(fdout, (char*)copy, c) < 0)
+ goto failed;
+ copy = 0;
+ }
+ else
+ /* set to delimiter unless the first field */
+ copy = empty ? cp : wp;
+ } while (!inword);
+ if (!inword)
+ {
+ if (!copy)
+ {
+ if (nodelim)
+ {
+ if (!cut->sflag)
+ {
+ if (offset)
+ {
+ sfseek(fdtmp,(Sfoff_t)0,SEEK_SET);
+ sfmove(fdtmp,fdout,offset,-1);
+ }
+ copy = first;
+ }
+ }
+ else
+ sfputc(fdout,'\n');
+ }
+ if (offset)
+ sfseek(fdtmp,offset=0,SEEK_SET);
+ }
+ if (copy && (c=cp-copy)>0 && (!nodelim || !cut->sflag) && sfwrite(fdout,(char*)copy,c)< 0)
+ goto failed;
+ }
+ /* see whether to save in tmp file */
+ if(inword && nodelim && !cut->sflag && (c=cp-first)>0)
+ {
+ /* copy line to tmpfile in case no fields */
+ if(!fdtmp)
+ fdtmp = sftmp(BLOCK);
+ sfwrite(fdtmp,(char*)first,c);
+ offset +=c;
+ }
+ }
+ failed:
+ if(fdtmp)
+ sfclose(fdtmp);
+}
+
+int
+b_cut(int argc, char** argv, Shbltin_t* context)
+{
+ register char* cp = 0;
+ register Sfio_t* fp;
+ char* s;
+ int n;
+ Cut_t* cut;
+ int mode = 0;
+ Delim_t wdelim;
+ Delim_t ldelim;
+ size_t reclen = 0;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ wdelim.chr = '\t';
+ ldelim.chr = '\n';
+ wdelim.len = ldelim.len = 1;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 0:
+ break;
+ case 'b':
+ case 'c':
+ if(mode&C_FIELDS)
+ {
+ error(2, "f option already specified");
+ continue;
+ }
+ cp = opt_info.arg;
+ if(opt_info.option[1]=='b')
+ mode |= C_BYTES;
+ else
+ mode |= C_CHARS;
+ continue;
+ case 'D':
+ ldelim.str = opt_info.arg;
+ if (mbwide())
+ {
+ s = opt_info.arg;
+ ldelim.chr = mbchar(s);
+ if ((n = s - opt_info.arg) > 1)
+ {
+ ldelim.len = n;
+ continue;
+ }
+ }
+ ldelim.chr = *(unsigned char*)opt_info.arg;
+ ldelim.len = 1;
+ continue;
+ case 'd':
+ wdelim.str = opt_info.arg;
+ if (mbwide())
+ {
+ s = opt_info.arg;
+ wdelim.chr = mbchar(s);
+ if ((n = s - opt_info.arg) > 1)
+ {
+ wdelim.len = n;
+ continue;
+ }
+ }
+ wdelim.chr = *(unsigned char*)opt_info.arg;
+ wdelim.len = 1;
+ continue;
+ case 'f':
+ if(mode&(C_CHARS|C_BYTES))
+ {
+ error(2, "c option already specified");
+ continue;
+ }
+ cp = opt_info.arg;
+ mode |= C_FIELDS;
+ continue;
+ case 'n':
+ mode |= C_NOSPLIT;
+ continue;
+ case 'N':
+ mode |= C_NONEWLINE;
+ continue;
+ case 'R':
+ case 'r':
+ if(opt_info.num>0)
+ reclen = opt_info.num;
+ continue;
+ case 's':
+ mode |= C_SUPRESS;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s",optusage(NiL));
+ if(!cp)
+ {
+ error(2, "b, c or f option must be specified");
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ }
+ if(!*cp)
+ error(3, "non-empty b, c or f option must be specified");
+ if((mode & (C_FIELDS|C_SUPRESS)) == C_SUPRESS)
+ error(3, "s option requires f option");
+ cut = cutinit(mode, cp, &wdelim, &ldelim, reclen);
+ if(cp = *argv)
+ argv++;
+ do
+ {
+ if(!cp || streq(cp,"-"))
+ fp = sfstdin;
+ else if(!(fp = sfopen(NiL,cp,"r")))
+ {
+ error(ERROR_system(0),"%s: cannot open",cp);
+ continue;
+ }
+ if(mode&C_FIELDS)
+ cutfields(cut,fp,sfstdout);
+ else
+ cutcols(cut,fp,sfstdout);
+ if(fp!=sfstdin)
+ sfclose(fp);
+ } while(cp = *argv++);
+ if (sfsync(sfstdout))
+ error(ERROR_system(0), "write error");
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/date.c b/src/lib/libcmd/date.c
new file mode 100644
index 0000000..3f9a303
--- /dev/null
+++ b/src/lib/libcmd/date.c
@@ -0,0 +1,515 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * date -- set/display date
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: date (AT&T Research) 2011-01-27 $\n]"
+USAGE_LICENSE
+"[+NAME?date - set/list/convert dates]"
+"[+DESCRIPTION?\bdate\b sets the current date and time (with appropriate"
+" privilege), lists the current date or file dates, or converts"
+" dates.]"
+"[+?Most common \adate\a forms are recognized, including those for"
+" \bcrontab\b(1), \bls\b(1), \btouch\b(1), and the default"
+" output from \bdate\b itself.]"
+"[+?If the \adate\a operand consists of 4, 6, 8, 10 or 12 digits followed"
+" by an optional \b.\b and two digits then it is interpreted as:"
+" \aHHMM.SS\a, \addHHMM.SS\a, \ammddHHMM.SS\a, \ammddHHMMyy.SS\a or"
+" \ayymmddHHMM.SS\a, or \ammddHHMMccyy.SS\a or \accyymmddHHMM.SS\a."
+" Conflicting standards and practice allow a leading or trailing"
+" 2 or 4 digit year for the 10 and 12 digit forms; the X/Open trailing"
+" form is used to disambiguate (\btouch\b(1) uses the leading form.)"
+" Avoid the 10 digit form to avoid confusion. The digit fields are:]{"
+" [+cc?Century - 1, 19-20.]"
+" [+yy?Year in century, 00-99.]"
+" [+mm?Month, 01-12.]"
+" [+dd?Day of month, 01-31.]"
+" [+HH?Hour, 00-23.]"
+" [+MM?Minute, 00-59.]"
+" [+SS?Seconds, 00-60.]"
+"}"
+"[+?If more than one \adate\a operand is specified then:]{"
+" [+1.?Each operand sets the reference date for the next"
+" operand.]"
+" [+2.?The date is listed for each operand.]"
+" [+3.?The system date is not set.]"
+"}"
+
+"[a:access-time|atime?List file argument access times.]"
+"[c:change-time|ctime?List file argument change times.]"
+"[d:date?Use \adate\a as the current date and do not set the system"
+" clock.]:[date]"
+"[e:epoch?Output the date in seconds since the epoch."
+" Equivalent to \b--format=%s\b.]"
+"[E:elapsed?Interpret pairs of arguments as start and stop dates, sum the"
+" differences between all pairs, and list the result as a"
+" \bfmtelapsed\b(3) elapsed time on the standard output. If there are"
+" an odd number of arguments then the last time argument is differenced"
+" with the current time.]"
+"[f:format?Output the date according to the \bstrftime\b(3) \aformat\a."
+" For backwards compatibility, a first argument of the form"
+" \b+\b\aformat\a is equivalent to \b-f\b format."
+" \aformat\a is in \bprintf\b(3) style, where %\afield\a names"
+" a fixed size field, zero padded if necessary,"
+" and \\\ac\a and \\\annn\a sequences are as in C. Invalid"
+" %\afield\a specifications and all other characters are copied"
+" without change. \afield\a may be preceded by \b%-\b to turn off"
+" padding or \b%_\b to pad with space, otherwise numeric fields"
+" are padded with \b0\b and string fields are padded with space."
+" \afield\a may also be preceded by \bE\b for alternate era"
+" representation or \bO\b for alternate digit representation (if"
+" supported by the current locale.) Finally, an integral \awidth\a"
+" preceding \afield\a truncates the field to \awidth\a characters."
+" The fields are:]:[format]{"
+" [+%?% character]"
+" [+a?abbreviated weekday name]"
+" [+A?full weekday name]"
+" [+b?abbreviated month name]"
+" [+B?full month name]"
+" [+c?\bctime\b(3) style date without the trailing newline]"
+" [+C?2-digit century]"
+" [+d?day of month number]"
+" [+D?date as \amm/dd/yy\a]"
+" [+e?blank padded day of month number]"
+" [+f?locale default override date format]"
+" [+F?%ISO 8601:2000 standard date format; equivalent to Y-%m-%d]"
+" [+g?\bls\b(1) \b-l\b recent date with \ahh:mm\a]"
+" [+G?\bls\b(1) \b-l\b distant date with \ayyyy\a]"
+" [+h?abbreviated month name]"
+" [+H?24-hour clock hour]"
+" [+i?international \bdate\b(1) date with time zone type name]"
+" [+I?12-hour clock hour]"
+" [+j?1-offset Julian date]"
+" [+J?0-offset Julian date]"
+" [+k?\bdate\b(1) style date]"
+" [+K?all numeric date; equivalent to \b%Y-%m-%d+%H:%M:%S\b; \b%_[EO]]K\b for space separator, %OK adds \b.%N\b, \b%EK\b adds \b%.N%z\b, \b%_EK\b adds \b.%N %z\b]"
+" [+l?\bls\b(1) \b-l\b date; equivalent to \b%Q/%g/%G/\b]"
+" [+L?locale default date format]"
+" [+m?month number]"
+" [+M?minutes]"
+" [+n?newline character]"
+" [+N?nanoseconds 000000000-999999999]"
+" [+p?meridian (e.g., \bAM\b or \bPM\b)]"
+" [+q?time zone type name (nation code)]"
+" [+Q?\a<del>recent<del>distant<del>\a: \a<del>\a is a unique"
+" delimter character; \arecent\a format for recent"
+" dates, \adistant\a format otherwise]"
+" [+r?12-hour time as \ahh:mm:ss meridian\a]"
+" [+R?24-hour time as \ahh:mm\a]"
+" [+s?number of seconds since the epoch; \a.prec\a preceding"
+" \bs\b appends \aprec\a nanosecond digits, \b9\b if"
+" \aprec\a is omitted]"
+" [+S?seconds 00-60]"
+" [+t?tab character]"
+" [+T?24-hour time as \ahh:mm:ss\a]"
+" [+u?weekday number 1(Monday)-7]"
+" [+U?week number with Sunday as the first day]"
+" [+V?ISO week number (i18n is \afun\a)]"
+" [+w?weekday number 0(Sunday)-6]"
+" [+W?week number with Monday as the first day]"
+" [+x?locale date style that includes month, day and year]"
+" [+X?locale time style that includes hours and minutes]"
+" [+y?2-digit year (you'll be sorry)]"
+" [+Y?4-digit year]"
+" [+z?time zone \aSHHMM\a west of GMT offset where S is"
+" \b+\b or \b-\b, use pad _ for \aSHH:MM\a]"
+" [+Z?time zone name]"
+" [+=[=]][-+]]flag?set (default or +) or clear (-) \aflag\a"
+" for the remainder of \aformat\a, or for the remainder"
+" of the process if \b==\b is specified. \aflag\a may be:]{"
+" [+l?enable leap second adjustments]"
+" [+n?convert \b%S\b as \b%S.%N\b]"
+" [+u?UTC time zone]"
+" }"
+" [+#?equivalent to %s]"
+" [+??alternate?use \aalternate\a format if a default format"
+" override has not been specified, e.g., \bls\b(1) uses"
+" \"%?%l\"; export TM_OPTIONS=\"format='\aoverride\a'\""
+" to override the default]"
+"}"
+"[i:incremental|adjust?Set the system time in incrementatl adjustments to"
+" avoid complete time shift shock. Negative adjustments still maintain"
+" monotonic increasing time. Not available on all systems.]"
+"[L:last?List only the last time for multiple \adate\a operands.]"
+"[l:leap-seconds?Include leap seconds in time calculations. Leap seconds"
+" after the ast library release date are not accounted for.]"
+"[m:modify-time|mtime?List file argument modify times.]"
+"[n!:network?Set network time.]"
+"[p:parse?Add \aformat\a to the list of \bstrptime\b(3) parse conversion"
+" formats. \aformat\a follows the same conventions as the"
+" \b--format\b option, with the addition of these format"
+" fields:]:[format]{"
+" [+|?If the format failed before this point then restart"
+" the parse with the remaining format.]"
+" [+&?Call the \btmdate\b(3) heuristic parser. This is"
+" is the default when \b--parse\b is omitted.]"
+"}"
+"[R:rfc-2822?List date and time in RFC 2822 format "
+ "(%a, %-e %h %Y %H:%M:%S %z).]"
+"[T:rfc-3339?List date and time in RFC 3339 format according to "
+ "\atype\a:]:[type]"
+ "{"
+ "[d:date?(%Y-%m-%d)]"
+ "[s:seconds?(%Y-%m-%d %H:%M:%S%_z)]"
+ "[n:ns|nanoseconds?(%Y-%m-%d %H:%M:%S.%N%_z)]"
+ "}"
+"[s:show?Show the date without setting the system time.]"
+"[u:utc|gmt|zulu|universal?Output dates in \acoordinated universal time\a (UTC).]"
+"[U:unelapsed?Interpret each argument as \bfmtelapsed\b(3) elapsed"
+" time and list the \bstrelapsed\b(3) 1/\ascale\a seconds.]#[scale]"
+"[z:list-zones?List the known time zone table and exit. The table columns"
+" are: country code, standard zone name, savings time zone name,"
+" minutes west of \bUTC\b, and savings time minutes offset. Blank"
+" or empty entries are listed as \b-\b.]"
+
+"\n"
+"\n[ +format | date ... | file ... ]\n"
+"\n"
+
+"[+SEE ALSO?\bcrontab\b(1), \bls\b(1), \btouch\b(1), \bfmtelapsed\b(3),"
+" \bstrftime\b(3), \bstrptime\b(3), \btm\b(3)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+#include <proc.h>
+#include <tmx.h>
+#include <times.h>
+
+typedef struct Fmt
+{
+ struct Fmt* next;
+ char* format;
+} Fmt_t;
+
+#ifndef ENOSYS
+#define ENOSYS EINVAL
+#endif
+
+/*
+ * set the system clock
+ * the standards wimped out here
+ */
+
+static int
+settime(Shbltin_t* context, const char* cmd, Time_t now, int adjust, int network)
+{
+ char* s;
+ char** argv;
+ char* args[5];
+ char buf[1024];
+
+ if (!adjust && !network)
+ return tmxsettime(now);
+ argv = args;
+ s = "/usr/bin/date";
+ if (!streq(cmd, s) && (!eaccess(s, X_OK) || !eaccess(s+=4, X_OK)))
+ {
+ *argv++ = s;
+ if (streq(astconf("UNIVERSE", NiL, NiL), "att"))
+ {
+ tmxfmt(buf, sizeof(buf), "%m%d%H" "%M%Y.%S", now);
+ if (adjust)
+ *argv++ = "-a";
+ }
+ else
+ {
+ tmxfmt(buf, sizeof(buf), "%Y%m%d%H" "%M.%S", now);
+ if (network)
+ *argv++ = "-n";
+ if (tm_info.flags & TM_UTC)
+ *argv++ = "-u";
+ }
+ *argv++ = buf;
+ *argv = 0;
+ if (!sh_run(context, argv - args, args))
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * convert s to Time_t with error checking
+ */
+
+static Time_t
+convert(register Fmt_t* f, char* s, Time_t now)
+{
+ char* t;
+ char* u;
+
+ do
+ {
+ now = tmxscan(s, &t, f->format, &u, now, 0);
+ if (!*t && (!f->format || !*u))
+ break;
+ } while (f = f->next);
+ if (!f || *t)
+ error(3, "%s: invalid date specification", f ? t : s);
+ return now;
+}
+
+int
+b_date(int argc, register char** argv, Shbltin_t* context)
+{
+ register int n;
+ register char* s;
+ register Fmt_t* f;
+ char* t;
+ unsigned long u;
+ Time_t now;
+ Time_t ts;
+ Time_t te;
+ Time_t e;
+ char buf[1024];
+ Fmt_t* fmts;
+ Fmt_t fmt;
+ struct stat st;
+
+ char* cmd = argv[0]; /* original command path */
+ char* format = 0; /* tmxfmt() format */
+ char* string = 0; /* date string */
+ int elapsed = 0; /* args are start/stop pairs */
+ int filetime = 0; /* use this st_ time field */
+ int increment = 0; /* incrementally adjust time */
+ int last = 0; /* display the last time arg */
+ Tm_zone_t* listzones = 0; /* known time zone table */
+ int network = 0; /* don't set network time */
+ int show = 0; /* show date and don't set */
+ int unelapsed = 0; /* fmtelapsed() => strelapsed */
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ tm_info.flags = TM_DATESTYLE;
+ fmts = &fmt;
+ fmt.format = "";
+ fmt.next = 0;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ case 'c':
+ case 'm':
+ filetime = opt_info.option[1];
+ continue;
+ case 'd':
+ string = opt_info.arg;
+ show = 1;
+ continue;
+ case 'e':
+ format = "%s";
+ continue;
+ case 'E':
+ elapsed = 1;
+ continue;
+ case 'f':
+ format = opt_info.arg;
+ continue;
+ case 'i':
+ increment = 1;
+ continue;
+ case 'l':
+ tm_info.flags |= TM_LEAP;
+ continue;
+ case 'L':
+ last = 1;
+ continue;
+ case 'n':
+ network = 1;
+ continue;
+ case 'p':
+ if (!(f = newof(0, Fmt_t, 1, 0)))
+ error(ERROR_SYSTEM|3, "out of space [format]");
+ f->next = fmts;
+ f->format = opt_info.arg;
+ fmts = f;
+ continue;
+ case 'R':
+ format = "%a, %-e %h %Y %H:%M:%S %z";
+ continue;
+ case 's':
+ show = 1;
+ continue;
+ case 'T':
+ switch (opt_info.num)
+ {
+ case 'd':
+ format = "%Y-%m-%d";
+ continue;
+ case 'n':
+ format = "%Y-%m-%d %H:%M:%S.%N%_z";
+ continue;
+ case 's':
+ format = "%Y-%m-%d %H:%M:%S%_z";
+ continue;
+ }
+ continue;
+ case 'u':
+ tm_info.flags |= TM_UTC;
+ continue;
+ case 'U':
+ unelapsed = (int)opt_info.num;
+ continue;
+ case 'z':
+ listzones = tm_data.zone;
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ continue;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ now = tmxgettime();
+ if (listzones)
+ {
+ s = "-";
+ while (listzones->standard)
+ {
+ if (listzones->type)
+ s = listzones->type;
+ sfprintf(sfstdout, "%3s %4s %4s %4d %4d\n", s, *listzones->standard ? listzones->standard : "-", listzones->daylight ? listzones->daylight : "-", listzones->west, listzones->dst);
+ listzones++;
+ show = 1;
+ }
+ }
+ else if (elapsed)
+ {
+ e = 0;
+ while (s = *argv++)
+ {
+ if (!(t = *argv++))
+ {
+ argv--;
+ t = "now";
+ }
+ ts = convert(fmts, s, now);
+ te = convert(fmts, t, now);
+ if (te > ts)
+ e += te - ts;
+ else
+ e += ts - te;
+ }
+ sfputr(sfstdout, fmtelapsed((unsigned long)tmxsec(e), 1), '\n');
+ show = 1;
+ }
+ else if (unelapsed)
+ {
+ while (s = *argv++)
+ {
+ u = strelapsed(s, &t, unelapsed);
+ if (*t)
+ error(3, "%s: invalid elapsed time", s);
+ sfprintf(sfstdout, "%lu\n", u);
+ }
+ show = 1;
+ }
+ else if (filetime)
+ {
+ if (!*argv)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ n = argv[1] != 0;
+ while (s = *argv++)
+ {
+ if (stat(s, &st))
+ error(2, "%s: not found", s);
+ else
+ {
+ switch (filetime)
+ {
+ case 'a':
+ now = tmxgetatime(&st);
+ break;
+ case 'c':
+ now = tmxgetctime(&st);
+ break;
+ default:
+ now = tmxgetmtime(&st);
+ break;
+ }
+ tmxfmt(buf, sizeof(buf), format, now);
+ if (n)
+ sfprintf(sfstdout, "%s: %s\n", s, buf);
+ else
+ sfprintf(sfstdout, "%s\n", buf);
+ show = 1;
+ }
+ }
+ }
+ else
+ {
+ if ((s = *argv) && !format && *s == '+')
+ {
+ format = s + 1;
+ argv++;
+ s = *argv;
+ }
+ if (s || (s = string))
+ {
+ if (*argv && string)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ now = convert(fmts, s, now);
+ if (*argv && (s = *++argv))
+ {
+ show = 1;
+ do
+ {
+ if (!last)
+ {
+ tmxfmt(buf, sizeof(buf), format, now);
+ sfprintf(sfstdout, "%s\n", buf);
+ }
+ now = convert(fmts, s, now);
+ } while (s = *++argv);
+ }
+ }
+ else
+ show = 1;
+ if (format || show)
+ {
+ tmxfmt(buf, sizeof(buf), format, now);
+ sfprintf(sfstdout, "%s\n", buf);
+ }
+ else if (settime(context, cmd, now, increment, network))
+ error(ERROR_SYSTEM|3, "cannot set system time");
+ }
+ while (fmts != &fmt)
+ {
+ f = fmts;
+ fmts = fmts->next;
+ free(f);
+ }
+ tm_info.flags = 0;
+ if (show && sfsync(sfstdout))
+ error(ERROR_system(0), "write error");
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/dirname.c b/src/lib/libcmd/dirname.c
new file mode 100644
index 0000000..a0704d5
--- /dev/null
+++ b/src/lib/libcmd/dirname.c
@@ -0,0 +1,139 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * dirname path [suffix]
+ *
+ * print the dirname of a pathname
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: dirname (AT&T Research) 2009-01-31 $\n]"
+USAGE_LICENSE
+"[+NAME?dirname - return directory portion of file name]"
+"[+DESCRIPTION?\bdirname\b treats \astring\a as a file name and returns "
+ "the name of the directory containing the file name by deleting "
+ "the last component from \astring\a.]"
+"[+?If \astring\a consists solely of \b/\b characters the output will "
+ "be a single \b/\b unless \bPATH_LEADING_SLASHES\b returned by "
+ "\bgetconf\b(1) is \b1\b and \astring\a consists of multiple "
+ "\b/\b characters in which case \b//\b will be output. "
+ "Otherwise, trailing \b/\b characters are removed, and if "
+ "there are no remaining \b/\b characters in \astring\a, "
+ "the string \b.\b will be written to standard output. "
+ "Otherwise, all characters following the last \b/\b are removed. "
+ "If the remaining string consists solely of \b/\b characters, "
+ "the output will be as if the original string had consisted solely "
+ "as \b/\b characters as described above. Otherwise, all "
+ "trailing slashes are removed and the output will be this string "
+ "unless this string is empty. If empty the output will be \b.\b.]"
+"[f:file?Print the \b$PATH\b relative regular file path for \astring\a.]"
+"[r:relative?Print the \b$PATH\b relative readable file path for \astring\a.]"
+"[x:executable?Print the \b$PATH\b relative executable file path for \astring\a.]"
+"\n"
+"\nstring\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Successful Completion.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bbasename\b(1), \bgetconf\b(1), \bdirname\b(3), \bpathname\b(3)]"
+;
+
+#include <cmd.h>
+
+static void l_dirname(register Sfio_t *outfile, register const char *pathname)
+{
+ register const char *last;
+ /* go to end of path */
+ for(last=pathname; *last; last++);
+ /* back over trailing '/' */
+ while(last>pathname && *--last=='/');
+ /* back over non-slash chars */
+ for(;last>pathname && *last!='/';last--);
+ if(last==pathname)
+ {
+ /* all '/' or "" */
+ if(*pathname!='/')
+ last = pathname = ".";
+ }
+ else
+ {
+ /* back over trailing '/' */
+ for(;*last=='/' && last > pathname; last--);
+ }
+ /* preserve // */
+ if(last!=pathname && pathname[0]=='/' && pathname[1]=='/')
+ {
+ while(pathname[2]=='/' && pathname<last)
+ pathname++;
+ if(last!=pathname && pathname[0]=='/' && pathname[1]=='/' && *astconf("PATH_LEADING_SLASHES",NiL,NiL)!='1')
+ pathname++;
+ }
+ sfwrite(outfile,pathname,last+1-pathname);
+ sfputc(outfile,'\n');
+}
+
+int
+b_dirname(int argc, char** argv, Shbltin_t* context)
+{
+ int mode = 0;
+ char buf[PATH_MAX];
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'f':
+ mode |= PATH_REGULAR;
+ continue;
+ case 'r':
+ mode &= ~PATH_REGULAR;
+ mode |= PATH_READ;
+ continue;
+ case 'x':
+ mode |= PATH_EXECUTE;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if(error_info.errors || argc != 1)
+ error(ERROR_usage(2),"%s", optusage(NiL));
+ if(!mode)
+ l_dirname(sfstdout,argv[0]);
+ else if(pathpath(argv[0], "", mode, buf, sizeof(buf)))
+ sfputr(sfstdout, buf, '\n');
+ else
+ error(1|ERROR_WARNING, "%s: relative path not found", argv[0]);
+ return 0;
+}
diff --git a/src/lib/libcmd/expr.c b/src/lib/libcmd/expr.c
new file mode 100644
index 0000000..a19b042
--- /dev/null
+++ b/src/lib/libcmd/expr.c
@@ -0,0 +1,535 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+/*
+ * expr.c
+ * Written by David Korn
+ * Tue Oct 31 08:48:11 EST 1995
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: expr (AT&T Research) 2010-08-11 $\n]"
+USAGE_LICENSE
+"[+NAME?expr - evaluate arguments as an expression]"
+"[+DESCRIPTION?\bexpr\b evaluates an expression given as arguments and writes "
+ "the result to standard output. The character \b0\b will be written "
+ "to indicate a zero value and nothing will be written to indicate an "
+ "empty string.]"
+"[+?Most of the functionality of \bexpr\b is provided in a more natural "
+ "way by the shell, \bsh\b(1), and \bexpr\b is provided primarily "
+ "for backward compatibility.]"
+"[+?Terms of the expression must be separate arguments. A string argument is "
+ "one that can not be identified as an integer. Integer-valued "
+ "arguments may be preceded by a unary plus or minus sign. Because "
+ "many of the operators use characters that have special meaning to "
+ "the shell, they must be quoted when entered from the shell.]"
+
+"[+?Expressions are formed from the operators listed below in order "
+ "of increasing precedence within groups. All of the operators are "
+ "left associative. The symbols \aexpr1\a and \aexpr2\a represent "
+ "expressions formed from strings and integers and the following "
+ "operators:]{"
+ "[+\aexpr1\a \b|\b \aexpr2\a?Returns the evaluation of \aexpr1\a if "
+ "it is neither null nor 0, otherwise returns the evaluation of expr2.]"
+
+ "[+\aexpr1\a \b&\b \aexpr2\a?Returns the evaluation of \aexpr1\a if "
+ "neither expression evaluates to null or 0, otherwise returns 0.]"
+
+ "[+\aexpr1\a \aop\a \aexpr2\a?Returns the result of a decimal integer "
+ "comparison if both arguments are integers; otherwise, returns the "
+ "result of a string comparison using the locale-specific collation "
+ "sequence. The result of each comparison will be 1 if the specified "
+ "relationship is true, or 0 if the relationship is false. \aop\a "
+ "can be one of the following:]{"
+ "[+=?Equal.]"
+ "[+==?Equal.]"
+ "[+>?Greater than.]"
+ "[+>=?Greater than or equal to.]"
+ "[+<?Less than.]"
+ "[+<=?Less than or equal to.]"
+ "[+!=?Not equal to.]"
+ "}"
+
+ "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b+\b or \b-\b; "
+ "addition or subtraction of decimal integer-valued arguments.]"
+ "[+\aexpr1\a \aop\a \aexpr2\a?Where \aop\a is \b*\b, \b/\b or \b%\b; "
+ "multiplication, division, or remainder of the decimal "
+ "integer-valued arguments.]"
+ "[+\aexpr1\a \b::\b \aexpr2\a?The matching operator : compares "
+ "\aexpr1\a with \aexpr2\a, which must be a BRE. Normally, "
+ "the matching operator returns the number of bytes matched "
+ "and 0 on failure. However, if the pattern contains at "
+ "least one sub-expression [\\( . . .\\)]], the string "
+ "corresponding to \\1 will be returned.]"
+ "[+( \aexpr1\a )?Grouping symbols. An expression can "
+ "be placed within parenthesis to change precedence.]"
+ "[+match\b \astring\a \aexpr\a?Equivalent to \astring\a \b:\b "
+ "\aexpr\a.]"
+ "[+substr\b \astring\a \apos\a \alength\a?\alength\a character "
+ "substring of \astring\a starting at \apos\a "
+ "(counting from 1).]"
+ "[+index\b \astring\a \achars\a?The position in \astring\a "
+ "(counting from 1) of the leftmost occurrence of any "
+ "character in \achars\a.]"
+ "[+length\b \astring\a?The number of characters in \astring\a.]"
+ "[+quote\b \atoken\a?Treat \atoken\a as a string operand.]"
+ "}"
+"[+?For backwards compatibility, unrecognized options beginning with "
+ "a \b-\b will be treated as operands. Portable applications "
+ "should use \b--\b to indicate end of options.]"
+
+"\n"
+"\n operand ...\n"
+"\n"
+
+"[+EXIT STATUS?]{"
+ "[+0?The expression is neither null nor 0.]"
+ "[+1?The expression is null or 0.]"
+ "[+2?Invalid expressions.]"
+ "[+>2?An error occurred.]"
+ "}"
+"[+SEE ALSO?\bregcomp\b(5), \bgrep\b(1), \bsh\b(1)]"
+;
+
+#include <cmd.h>
+#include <regex.h>
+
+#define T_ADD 0x100
+#define T_MULT 0x200
+#define T_CMP 0x400
+#define T_FUN 0x800
+#define T_OP 7
+#define T_NUM 1
+#define T_STR 2
+
+#define OP_EQ (T_CMP|0)
+#define OP_GT (T_CMP|1)
+#define OP_LT (T_CMP|2)
+#define OP_GE (T_CMP|3)
+#define OP_LE (T_CMP|4)
+#define OP_NE (T_CMP|5)
+#define OP_PLUS (T_ADD|0)
+#define OP_MINUS (T_ADD|1)
+#define OP_MULT (T_MULT|0)
+#define OP_DIV (T_MULT|1)
+#define OP_MOD (T_MULT|2)
+#define OP_INDEX (T_FUN|0)
+#define OP_LENGTH (T_FUN|1)
+#define OP_MATCH (T_FUN|2)
+#define OP_QUOTE (T_FUN|3)
+#define OP_SUBSTR (T_FUN|4)
+
+#define numeric(np) ((np)->type&T_NUM)
+
+static const struct Optable_s
+{
+ const char opname[3];
+ int op;
+}
+optable[] =
+{
+ "|", '|',
+ "&", '&',
+ "=", OP_EQ,
+ "==", OP_EQ,
+ ">", OP_GT,
+ "<", OP_LT,
+ ">=", OP_GE,
+ "<=", OP_LE,
+ "!=", OP_NE,
+ "+", OP_PLUS,
+ "-", OP_MINUS,
+ "*", OP_MULT,
+ "/", OP_DIV,
+ "%", OP_MOD,
+ ":", ':',
+ "(", '(',
+ ")", ')'
+};
+
+typedef struct Node_s
+{
+ int type;
+ long num;
+ char *str;
+} Node_t;
+
+typedef struct State_s
+{
+ int standard;
+ char** arglist;
+ char buf[36];
+} State_t;
+
+static int expr_or(State_t*, Node_t*);
+
+static int getnode(State_t* state, Node_t *np)
+{
+ register char* sp;
+ register char* cp;
+ register int i;
+ register int j;
+ register int k;
+ register int tok;
+ char* ep;
+
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "argument expected");
+ if (!state->standard)
+ switch (cp[0])
+ {
+ case 'i':
+ if (cp[1] == 'n' && !strcmp(cp, "index"))
+ {
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ if (!(ep = *state->arglist++))
+ error(ERROR_exit(2), "chars argument expected");
+ np->num = (ep = strpbrk(cp, ep)) ? (ep - cp + 1) : 0;
+ np->type = T_NUM;
+ goto next;
+ }
+ break;
+ case 'l':
+ if (cp[1] == 'e' && !strcmp(cp, "length"))
+ {
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ np->num = strlen(cp);
+ np->type = T_NUM;
+ goto next;
+ }
+ break;
+ case 'm':
+ if (cp[1] == 'a' && !strcmp(cp, "match"))
+ {
+ if (!(np->str = *state->arglist++))
+ error(ERROR_exit(2), "pattern argument expected");
+ np->type = T_STR;
+ return ':';
+ }
+ break;
+ case 'q':
+ if (cp[1] == 'u' && !strcmp(cp, "quote") && !(cp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ break;
+ case 's':
+ if (cp[1] == 'u' && !strcmp(cp, "substr"))
+ {
+ if (!(sp = *state->arglist++))
+ error(ERROR_exit(2), "string argument expected");
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "position argument expected");
+ i = strtol(cp, &ep, 10);
+ if (*ep || --i < 0)
+ i = -1;
+ if (!(cp = *state->arglist++))
+ error(ERROR_exit(2), "length argument expected");
+ j = strtol(cp, &ep, 10);
+ if (*ep)
+ j = -1;
+ k = strlen(sp);
+ if (i < 0 || i >= k || j < 0)
+ sp = "";
+ else
+ {
+ sp += i;
+ k -= i;
+ if (j < k)
+ sp[j] = 0;
+ }
+ np->type = T_STR;
+ np->str = sp;
+ goto next;
+ }
+ break;
+ }
+ if (*cp=='(' && cp[1]==0)
+ {
+ tok = expr_or(state, np);
+ if (tok != ')')
+ error(ERROR_exit(2),"closing parenthesis missing");
+ }
+ else
+ {
+ np->type = T_STR;
+ np->str = cp;
+ if (*cp)
+ {
+ np->num = strtol(np->str,&ep,10);
+ if (!*ep)
+ np->type |= T_NUM;
+ }
+ }
+ next:
+ if (!(cp = *state->arglist))
+ return 0;
+ state->arglist++;
+ for (i=0; i < sizeof(optable)/sizeof(*optable); i++)
+ if (*cp==optable[i].opname[0] && cp[1]==optable[i].opname[1])
+ return optable[i].op;
+ error(ERROR_exit(2),"%s: unknown operator argument",cp);
+ return 0;
+}
+
+static int expr_cond(State_t* state, Node_t *np)
+{
+ register int tok = getnode(state, np);
+
+ while (tok==':')
+ {
+ regex_t re;
+ regmatch_t match[2];
+ int n;
+ Node_t rp;
+ char *cp;
+ tok = getnode(state, &rp);
+ if (np->type&T_STR)
+ cp = np->str;
+ else
+ sfsprintf(cp=state->buf,sizeof(state->buf),"%d",np->num);
+ np->num = 0;
+ np->type = T_NUM;
+ if (n = regcomp(&re, rp.str, REG_LEFT|REG_LENIENT))
+ regfatal(&re, ERROR_exit(2), n);
+ if (!(n = regexec(&re, cp, elementsof(match), match, 0)))
+ {
+ if (re.re_nsub > 0)
+ {
+ np->type = T_STR;
+ if (match[1].rm_so >= 0)
+ {
+ np->str = cp + match[1].rm_so;
+ np->str[match[1].rm_eo - match[1].rm_so] = 0;
+ np->num = strtol(np->str,&cp,10);
+ if (cp!=np->str && *cp==0)
+ np->type |= T_NUM;
+ }
+ else
+ np->str = "";
+ }
+ else
+ np->num = match[0].rm_eo - match[0].rm_so;
+ }
+ else if (n != REG_NOMATCH)
+ regfatal(&re, ERROR_exit(2), n);
+ else if (re.re_nsub)
+ {
+ np->str = "";
+ np->type = T_STR;
+ }
+ regfree(&re);
+ }
+ return tok;
+}
+
+static int expr_mult(State_t* state, Node_t *np)
+{
+ register int tok = expr_cond(state, np);
+
+ while ((tok&~T_OP)==T_MULT)
+ {
+ Node_t rp;
+ int op = (tok&T_OP);
+ tok = expr_cond(state, &rp);
+ if (!numeric(np) || !numeric(&rp))
+ error(ERROR_exit(2),"non-numeric argument");
+ if (op && rp.num==0)
+ error(ERROR_exit(2),"division by zero");
+ switch(op)
+ {
+ case 0:
+ np->num *= rp.num;
+ break;
+ case 1:
+ np->num /= rp.num;
+ break;
+ case 2:
+ np->num %= rp.num;
+ }
+ np->type = T_NUM;
+ }
+ return tok;
+}
+
+static int expr_add(State_t* state, Node_t *np)
+{
+ register int tok = expr_mult(state, np);
+
+ while ((tok&~T_OP)==T_ADD)
+ {
+ Node_t rp;
+ int op = (tok&T_OP);
+ tok = expr_mult(state, &rp);
+ if (!numeric(np) || !numeric(&rp))
+ error(ERROR_exit(2),"non-numeric argument");
+ if (op)
+ np->num -= rp.num;
+ else
+ np->num += rp.num;
+ np->type = T_NUM;
+ }
+ return tok;
+}
+
+static int expr_cmp(State_t* state, Node_t *np)
+{
+ register int tok = expr_add(state, np);
+
+ while ((tok&~T_OP)==T_CMP)
+ {
+ Node_t rp;
+ register char *left,*right;
+ char buff1[36],buff2[36];
+ int op = (tok&T_OP);
+ tok = expr_add(state, &rp);
+ if (numeric(&rp) && numeric(np))
+ op |= 010;
+ else
+ {
+ if (np->type&T_STR)
+ left = np->str;
+ else
+ sfsprintf(left=buff1,sizeof(buff1),"%d",np->num);
+ if (rp.type&T_STR)
+ right = rp.str;
+ else
+ sfsprintf(right=buff2,sizeof(buff2),"%d",rp.num);
+ }
+ switch(op)
+ {
+ case 0:
+ np->num = streq(left,right);
+ break;
+ case 1:
+ np->num = (strcoll(left,right)>0);
+ break;
+ case 2:
+ np->num = (strcoll(left,right)<0);
+ break;
+ case 3:
+ np->num = (strcoll(left,right)>=0);
+ break;
+ case 4:
+ np->num = (strcoll(left,right)<=0);
+ break;
+ case 5:
+ np->num = !streq(left,right);
+ break;
+ case 010:
+ np->num = (np->num==rp.num);
+ break;
+ case 011:
+ np->num = (np->num>rp.num);
+ break;
+ case 012:
+ np->num = (np->num<rp.num);
+ break;
+ case 013:
+ np->num = (np->num>=rp.num);
+ break;
+ case 014:
+ np->num = (np->num<=rp.num);
+ break;
+ case 015:
+ np->num = (np->num!=rp.num);
+ break;
+ }
+ np->type = T_NUM;
+ }
+ return tok;
+}
+
+static int expr_and(State_t* state, Node_t *np)
+{
+ register int tok = expr_cmp(state, np);
+ while (tok=='&')
+ {
+ Node_t rp;
+ tok = expr_cmp(state, &rp);
+ if ((numeric(&rp) && rp.num==0) || *rp.str==0)
+ {
+ np->num = 0;
+ np->type=T_NUM;
+ }
+ }
+ return tok;
+}
+
+static int expr_or(State_t* state, Node_t *np)
+{
+ register int tok = expr_and(state, np);
+ while (tok=='|')
+ {
+ Node_t rp;
+ tok = expr_and(state, &rp);
+ if ((numeric(np) && np->num==0) || *np->str==0)
+ *np = rp;
+ }
+ return tok;
+}
+
+int
+b_expr(int argc, char** argv, Shbltin_t* context)
+{
+ State_t state;
+ Node_t node;
+ int n;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ state.standard = !!conformance(0, 0);
+#if 0
+ if (state.standard)
+ state.arglist = argv+1;
+ else
+#endif
+ {
+ while (n=optget(argv, usage))
+ {
+ /*
+ * NOTE: this loop ignores all but literal -- and -?
+ * out of kindness for obsolescent usage
+ * (and is ok with the standard) but strict
+ * getopt conformance would give usage for all
+ * unknown - options
+ */
+ if(n=='?')
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ if (opt_info.option[1] != '?')
+ break;
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ }
+ if (error_info.errors)
+ error(ERROR_usage(2),"%s",optusage((char*)0));
+ state.arglist = argv+opt_info.index;
+ }
+ if (expr_or(&state, &node))
+ error(ERROR_exit(2),"syntax error");
+ if (node.type&T_STR)
+ {
+ if (*node.str)
+ sfprintf(sfstdout,"%s\n",node.str);
+ }
+ else
+ sfprintf(sfstdout,"%d\n",node.num);
+ return numeric(&node)?node.num==0:*node.str==0;
+}
diff --git a/src/lib/libcmd/fds.c b/src/lib/libcmd/fds.c
new file mode 100644
index 0000000..4dc934f
--- /dev/null
+++ b/src/lib/libcmd/fds.c
@@ -0,0 +1,360 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+static const char usage[] =
+"[-?\n@(#)$Id: fds (AT&T Research) 2009-09-09 $\n]"
+USAGE_LICENSE
+"[+NAME?fds - list open file descriptor status]"
+"[+DESCRIPTION?\bfds\b lists the status for each open file descriptor. "
+ "When invoked as a shell builtin it accesses the file descriptors of the "
+ "calling shell, otherwise it lists the file descriptors passed across "
+ "\bexec\b(2).]"
+"[l:long?List file descriptor details.]"
+"[u:unit?Write output to \afd\a.]#[fd]"
+"[+SEE ALSO?\blogname\b(1), \bwho\b(1), \bgetgroups\b(2), \bgetsockname\b(2), \bgetsockopts\b(2)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+
+#include "FEATURE/sockets"
+
+#if defined(S_IFSOCK) && _sys_socket && _hdr_arpa_inet && _hdr_netinet_in && _lib_getsockname && _lib_getsockopt && _lib_inet_ntoa
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#else
+#undef S_IFSOCK
+#endif
+
+#ifndef minor
+#define minor(x) (int)((x)&0xff)
+#endif
+#ifndef major
+#define major(x) (int)(((unsigned int)(x)>>8)&0xff)
+#endif
+
+#undef getconf
+#define getconf(x) strtol(astconf(x,NiL,NiL),NiL,0)
+
+#ifdef S_IFSOCK
+
+typedef struct NV_s
+{
+ const char* name;
+ int value;
+} NV_t;
+
+static const NV_t family[] =
+{
+#ifdef AF_LOCAL
+ "pipe", AF_LOCAL,
+#endif
+#ifdef AF_UNIX
+ "pipe", AF_UNIX,
+#endif
+#ifdef AF_FILE
+ "FILE", AF_FILE,
+#endif
+#ifdef AF_INET
+ "INET", AF_INET,
+#endif
+#ifdef AF_AX25
+ "AX25", AF_AX25,
+#endif
+#ifdef AF_IPX
+ "IPX", AF_IPX,
+#endif
+#ifdef AF_APPLETALK
+ "APPLETALK", AF_APPLETALK,
+#endif
+#ifdef AF_NETROM
+ "NETROM", AF_NETROM,
+#endif
+#ifdef AF_BRIDGE
+ "BRIDGE", AF_BRIDGE,
+#endif
+#ifdef AF_ATMPVC
+ "ATMPVC", AF_ATMPVC,
+#endif
+#ifdef AF_X25
+ "X25", AF_X25,
+#endif
+#ifdef AF_INET6
+ "INET6", AF_INET6,
+#endif
+#ifdef AF_ROSE
+ "ROSE", AF_ROSE,
+#endif
+#ifdef AF_DECnet
+ "DECnet", AF_DECnet,
+#endif
+#ifdef AF_NETBEUI
+ "NETBEUI", AF_NETBEUI,
+#endif
+#ifdef AF_SECURITY
+ "SECURITY", AF_SECURITY,
+#endif
+#ifdef AF_KEY
+ "KEY", AF_KEY,
+#endif
+#ifdef AF_NETLINK
+ "NETLINK", AF_NETLINK,
+#endif
+#ifdef AF_ROUTE
+ "ROUTE", AF_ROUTE,
+#endif
+#ifdef AF_PACKET
+ "PACKET", AF_PACKET,
+#endif
+#ifdef AF_ASH
+ "ASH", AF_ASH,
+#endif
+#ifdef AF_ECONET
+ "ECONET", AF_ECONET,
+#endif
+#ifdef AF_ATMSVC
+ "ATMSVC", AF_ATMSVC,
+#endif
+#ifdef AF_SNA
+ "SNA", AF_SNA,
+#endif
+#ifdef AF_IRDA
+ "IRDA", AF_IRDA,
+#endif
+#ifdef AF_PPPOX
+ "PPPOX", AF_PPPOX,
+#endif
+#ifdef AF_WANPIPE
+ "WANPIPE", AF_WANPIPE,
+#endif
+#ifdef AF_BLUETOOTH
+ "BLUETOOTH", AF_BLUETOOTH,
+#endif
+ 0
+};
+
+#endif
+
+int
+b_fds(int argc, char** argv, Shbltin_t* context)
+{
+ register char* s;
+ register int i;
+ register char* m;
+ register char* x;
+ int flags;
+ int details;
+ int open_max;
+ int unit;
+ Sfio_t* sp;
+ struct stat st;
+#ifdef S_IFSOCK
+ struct sockaddr_in addr;
+ char* a;
+ unsigned char* b;
+ unsigned char* e;
+ socklen_t addrlen;
+ socklen_t len;
+ int type;
+ int port;
+ int prot;
+ char num[64];
+ char fam[64];
+#ifdef INET6_ADDRSTRLEN
+ char nam[256];
+#endif
+#endif
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ details = 0;
+ unit = 1;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'l':
+ details = opt_info.num;
+ continue;
+ case 'u':
+ unit = opt_info.num;
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ break;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || *argv)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ if ((open_max = getconf("OPEN_MAX")) <= 0)
+ open_max = OPEN_MAX;
+ if (unit == 1)
+ sp = sfstdout;
+ else if (fstat(unit, &st) || !(sp = sfnew(NiL, NiL, SF_UNBOUND, unit, SF_WRITE)))
+ error(ERROR_SYSTEM|3, "%d: cannot write to file descriptor");
+ for (i = 0; i <= open_max; i++)
+ {
+ if (fstat(i, &st))
+ {
+ /* not open */
+ continue;
+ }
+ if (!details)
+ {
+ sfprintf(sp, "%d\n", i);
+ continue;
+ }
+ if ((flags = fcntl(i, F_GETFL, (char*)0)) == -1)
+ m = "--";
+ else
+ switch (flags & (O_RDONLY|O_WRONLY|O_RDWR))
+ {
+ case O_RDONLY:
+ m = "r-";
+ break;
+ case O_WRONLY:
+ m = "-w";
+ break;
+ case O_RDWR:
+ m = "rw";
+ break;
+ default:
+ m = "??";
+ break;
+ }
+ x = (fcntl(i, F_GETFD, (char*)0) > 0) ? "x" : "-";
+ if (isatty(i) && (s = ttyname(i)))
+ {
+ sfprintf(sp, "%02d %s%s %s %s\n", i, m, x, fmtmode(st.st_mode, 0), s);
+ continue;
+ }
+#ifdef S_IFSOCK
+ addrlen = sizeof(addr);
+ memset(&addr, 0, addrlen);
+ if (!getsockname(i, (struct sockaddr*)&addr, (void*)&addrlen))
+ {
+ type = 0;
+ prot = 0;
+#ifdef SO_TYPE
+ len = sizeof(type);
+ if (getsockopt(i, SOL_SOCKET, SO_TYPE, (void*)&type, (void*)&len))
+ type = -1;
+#endif
+#ifdef SO_PROTOTYPE
+ len = sizeof(prot);
+ if (getsockopt(i, SOL_SOCKET, SO_PROTOTYPE, (void*)&prot, (void*)&len))
+ prot = -1;
+#endif
+ if (!st.st_mode)
+ st.st_mode = S_IFSOCK|S_IRUSR|S_IWUSR;
+ s = 0;
+ switch (type)
+ {
+ case SOCK_DGRAM:
+ switch (addr.sin_family)
+ {
+ case AF_INET:
+#ifdef AF_INET6
+ case AF_INET6:
+#endif
+ s = "udp";
+ break;
+ }
+ break;
+ case SOCK_STREAM:
+ switch (addr.sin_family)
+ {
+ case AF_INET:
+#ifdef AF_INET6
+ case AF_INET6:
+#endif
+#ifdef IPPROTO_SCTP
+ if (prot == IPPROTO_SCTP)
+ s = "sctp";
+ else
+#endif
+ s = "tcp";
+ break;
+ }
+ break;
+#ifdef SOCK_RAW
+ case SOCK_RAW:
+ s = "raw";
+ break;
+#endif
+#ifdef SOCK_RDM
+ case SOCK_RDM:
+ s = "rdm";
+ break;
+#endif
+#ifdef SOCK_SEQPACKET
+ case SOCK_SEQPACKET:
+ s = "seqpacket";
+ break;
+#endif
+ }
+ if (!s)
+ {
+ for (type = 0; family[type].name && family[type].value != addr.sin_family; type++);
+ if (!(s = (char*)family[type].name))
+ sfsprintf(s = num, sizeof(num), "family.%d", addr.sin_family);
+ }
+ port = 0;
+#ifdef INET6_ADDRSTRLEN
+ if (a = (char*)inet_ntop(addr.sin_family, &addr.sin_addr, nam, sizeof(nam)))
+ port = ntohs(addr.sin_port);
+ else
+#endif
+ if (addr.sin_family == AF_INET)
+ {
+ a = inet_ntoa(addr.sin_addr);
+ port = ntohs(addr.sin_port);
+ }
+ else
+ {
+ a = fam;
+ e = (b = (unsigned char*)&addr) + addrlen;
+ while (b < e && a < &fam[sizeof(fam)-1])
+ a += sfsprintf(a, &fam[sizeof(fam)] - a - 1, ".%d", *b++);
+ a = a == fam ? "0" : fam + 1;
+ }
+ if (port)
+ sfprintf(sp, "%02d %s%s %s /dev/%s/%s/%d\n", i, m, x, fmtmode(st.st_mode, 0), s, a, port);
+ else
+ sfprintf(sp, "%02d %s%s %s /dev/%s/%s\n", i, m, x, fmtmode(st.st_mode, 0), s, a);
+ continue;
+ }
+#endif
+ sfprintf(sp, "%02d %s%s %s /dev/inode/%u/%u\n", i, m, x, fmtmode(st.st_mode, 0), st.st_dev, st.st_ino);
+ }
+ if (sp != sfstdout)
+ {
+ sfsetfd(sp, -1);
+ sfclose(sp);
+ }
+ return 0;
+}
diff --git a/src/lib/libcmd/features/ids b/src/lib/libcmd/features/ids
new file mode 100644
index 0000000..c8f7faa
--- /dev/null
+++ b/src/lib/libcmd/features/ids
@@ -0,0 +1,9 @@
+lib endgrent,getgrent,setgrent
+lib fsid,getfsgid,isfsg sys/types.h sys/fss.h fsg.h -lfsg
+lib getsid unistd.h
+mac fsid sys/types.h sys/fss.h fsg.h
+cat{
+ #if !_lib_fsid && _mac_fsid
+ #define _lib_fsid 1
+ #endif
+}end
diff --git a/src/lib/libcmd/features/sockets b/src/lib/libcmd/features/sockets
new file mode 100644
index 0000000..2660abb
--- /dev/null
+++ b/src/lib/libcmd/features/sockets
@@ -0,0 +1,3 @@
+sys socket
+hdr arpa.inet,netinet.in
+lib getsockname,getsockopt,inet_ntoa
diff --git a/src/lib/libcmd/features/symlink b/src/lib/libcmd/features/symlink
new file mode 100644
index 0000000..789db0b
--- /dev/null
+++ b/src/lib/libcmd/features/symlink
@@ -0,0 +1,23 @@
+lib lchmod note{ lchmod implemented }end execute{
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <errno.h>
+ int
+ main()
+ {
+ lchmod("No-FiLe", 0);
+ return errno != ENOENT;
+ }
+}end
+
+lib lchown note{ lchown implemented }end execute{
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <errno.h>
+ int
+ main()
+ {
+ lchown("No-FiLe", 0, 0);
+ return errno != ENOENT;
+ }
+}end
diff --git a/src/lib/libcmd/features/utsname b/src/lib/libcmd/features/utsname
new file mode 100644
index 0000000..7254867
--- /dev/null
+++ b/src/lib/libcmd/features/utsname
@@ -0,0 +1,15 @@
+lib getdomainname,gethostid,gethostname,sethostname,syscall,systeminfo,uname
+lib syssgi
+mem utsname.base_rel,utsname.idnumber,utsname.m_type,utsname.nodeext sys/utsname.h
+sys syscall,systeminfo,syssgi
+
+tst cross{
+ u=`/bin/uname -o 2>/dev/null`
+ case $u in
+ UWIN-*) u=UWIN ;;
+ esac
+ case $u in
+ '') ;;
+ *) echo "#define _UNAME_os_DEFAULT \"$u\" /* default os name */" ;;
+ esac
+}end
diff --git a/src/lib/libcmd/fmt.c b/src/lib/libcmd/fmt.c
new file mode 100644
index 0000000..e320767
--- /dev/null
+++ b/src/lib/libcmd/fmt.c
@@ -0,0 +1,635 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+static const char usage[] =
+"[-?\n@(#)$Id: fmt (AT&T Research) 2007-01-02 $\n]"
+USAGE_LICENSE
+"[+NAME?fmt - simple text formatter]"
+"[+DESCRIPTION?\bfmt\b reads the input files and left justifies space "
+ "separated words into lines \awidth\a characters or less in length and "
+ "writes the lines to the standard output. The standard input is read if "
+ "\b-\b or no files are specified. Blank lines and interword spacing are "
+ "preserved in the output. Indentation is preserved, and lines with "
+ "identical indentation are joined and justified.]"
+"[+?\bfmt\b is meant to format mail messages prior to sending, but may "
+ "also be useful for other simple tasks. For example, in \bvi\b(1) the "
+ "command \b:!}fmt\b will justify the lines in the current paragraph.]"
+"[c:crown-margin?Preserve the indentation of the first two lines within "
+ "a paragraph, and align the left margin of each subsequent line with "
+ "that of the second line.]"
+"[o:optget?Format concatenated \boptget\b(3) usage strings.]"
+"[s:split-only?Split lines only; do not join short lines to form longer "
+ "ones.]"
+"[u:uniform-spacing?One space between words, two after sentences.]"
+"[w:width?Set the output line width to \acolumns\a.]#[columns:=72]"
+ "\n\n"
+"[ file ... ]"
+ "\n\n"
+"[+SEE ALSO?\bmailx\b(1), \bnroff\b(1), \btroff\b(1), \bvi\b(1), "
+ "\boptget\b(3)]"
+;
+
+#include <cmd.h>
+#include <ctype.h>
+
+typedef struct Fmt_s
+{
+ long flags;
+ char* outp;
+ char* outbuf;
+ char* endbuf;
+ Sfio_t* in;
+ Sfio_t* out;
+ int indent;
+ int nextdent;
+ int nwords;
+ int prefix;
+ int quote;
+ int retain;
+ int section;
+} Fmt_t;
+
+#define INDENT 4
+#define TABSZ 8
+
+#define isoption(fp,c) ((fp)->flags&(1L<<((c)-'a')))
+#define setoption(fp,c) ((fp)->flags|=(1L<<((c)-'a')))
+#define clroption(fp,c) ((fp)->flags&=~(1L<<((c)-'a')))
+
+static void
+outline(Fmt_t* fp)
+{
+ register char* cp = fp->outbuf;
+ int n = 0;
+ int c;
+ int d;
+
+ if (!fp->outp)
+ return;
+ while (fp->outp[-1] == ' ')
+ fp->outp--;
+ *fp->outp = 0;
+ while (*cp++ == ' ')
+ n++;
+ if (n >= TABSZ)
+ {
+ n /= TABSZ;
+ cp = &fp->outbuf[TABSZ*n];
+ while (n--)
+ *--cp = '\t';
+ }
+ else
+ cp = fp->outbuf;
+ fp->nwords = 0;
+ if (!isoption(fp, 'o'))
+ sfputr(fp->out, cp, '\n');
+ else if (*cp)
+ {
+ n = fp->indent;
+ if (*cp != '[')
+ {
+ if (*cp == ' ')
+ cp++;
+ n += INDENT;
+ }
+ while (n--)
+ sfputc(fp->out, ' ');
+ if (fp->quote)
+ {
+ if ((d = (fp->outp - cp)) <= 0)
+ c = 0;
+ else if ((c = fp->outp[-1]) == 'n' && d > 1 && fp->outp[-2] == '\\')
+ c = '}';
+ sfprintf(fp->out, "\"%s%s\"\n", cp, c == ']' || c == '{' || c == '}' ? "" : " ");
+ }
+ else
+ sfputr(fp->out, cp, '\n');
+ if (fp->nextdent)
+ {
+ fp->indent += fp->nextdent;
+ fp->endbuf -= fp->nextdent;
+ fp->nextdent = 0;
+ }
+ }
+ fp->outp = 0;
+}
+
+static void
+split(Fmt_t* fp, char* buf, int splice)
+{
+ register char* cp;
+ register char* ep;
+ register char* qp;
+ register int c = 1;
+ register int q = 0;
+ register int n;
+ int prefix;
+
+ for (ep = buf; *ep == ' '; ep++);
+ prefix = ep - buf;
+
+ /*
+ * preserve blank lines
+ */
+
+ if ((*ep == 0 || *buf == '.') && !isoption(fp, 'o'))
+ {
+ if (*ep)
+ prefix = strlen(buf);
+ outline(fp);
+ strcpy(fp->outbuf, buf);
+ fp->outp = fp->outbuf+prefix;
+ outline(fp);
+ return;
+ }
+ if (fp->prefix < prefix && !isoption(fp, 'c'))
+ outline(fp);
+ if (!fp->outp || prefix < fp->prefix)
+ fp->prefix = prefix;
+ while (c)
+ {
+ cp = ep;
+ while (*ep == ' ')
+ ep++;
+ if (cp != ep && isoption(fp, 'u'))
+ cp = ep-1;
+ while (c = *ep)
+ {
+ if (c == ' ')
+ break;
+ ep++;
+
+ /*
+ * skip over \space
+ */
+
+ if (c == '\\' && *ep)
+ ep++;
+ }
+ n = (ep-cp);
+ if (n && isoption(fp, 'o'))
+ {
+ for (qp = cp; qp < ep; qp++)
+ if (*qp == '\\')
+ qp++;
+ else if (*qp == '"')
+ q = !q;
+ if (*(ep-1) == '"')
+ goto skip;
+ }
+ if (fp->nwords > 0 && &fp->outp[n] >= fp->endbuf && !fp->retain && !q)
+ outline(fp);
+ skip:
+ if (fp->nwords == 0)
+ {
+ if (fp->prefix)
+ memset(fp->outbuf, ' ', fp->prefix);
+ fp->outp = &fp->outbuf[fp->prefix];
+ while (*cp == ' ')
+ cp++;
+ n = (ep-cp);
+ }
+ memcpy(fp->outp, cp, n);
+ fp->outp += n;
+ fp->nwords++;
+ }
+ if (isoption(fp, 's') || *buf == 0)
+ outline(fp);
+ else if (fp->outp)
+ {
+ /*
+ * two spaces at ends of sentences
+ */
+
+ if (!isoption(fp, 'o') && strchr(".:!?", fp->outp[-1]))
+ *fp->outp++ = ' ';
+ if (!splice && !fp->retain && (!fp->quote || (fp->outp - fp->outbuf) < 2 || fp->outp[-2] != '\\' || fp->outp[-1] != 'n' && fp->outp[-1] != 't' && fp->outp[-1] != ' '))
+ *fp->outp++ = ' ';
+ }
+}
+
+static int
+dofmt(Fmt_t* fp)
+{
+ register int c;
+ int b;
+ int x;
+ int splice;
+ char* cp;
+ char* dp;
+ char* ep;
+ char* lp;
+ char* tp;
+ char buf[8192];
+
+ cp = 0;
+ while (cp || (cp = sfgetr(fp->in, '\n', 0)) && !(splice = 0) && (lp = cp + sfvalue(fp->in) - 1) || (cp = sfgetr(fp->in, '\n', SF_LASTR)) && (splice = 1) && (lp = cp + sfvalue(fp->in)))
+ {
+ if (isoption(fp, 'o'))
+ {
+ if (!isoption(fp, 'i'))
+ {
+ setoption(fp, 'i');
+ b = 0;
+ while (cp < lp)
+ {
+ if (*cp == ' ')
+ b += 1;
+ else if (*cp == '\t')
+ b += INDENT;
+ else
+ break;
+ cp++;
+ }
+ fp->indent = roundof(b, INDENT);
+ }
+ else
+ while (cp < lp && (*cp == ' ' || *cp == '\t'))
+ cp++;
+ if (!isoption(fp, 'q') && cp < lp)
+ {
+ setoption(fp, 'q');
+ if (*cp == '"')
+ {
+ ep = lp;
+ while (--ep > cp)
+ if (*ep == '"')
+ {
+ fp->quote = 1;
+ break;
+ }
+ else if (*ep != ' ' && *ep != '\t')
+ break;
+ }
+ }
+ }
+ again:
+ dp = buf;
+ ep = 0;
+ for (b = 1;; b = 0)
+ {
+ if (cp >= lp)
+ {
+ cp = 0;
+ break;
+ }
+ c = *cp++;
+ if (isoption(fp, 'o'))
+ {
+ if (c == '\\')
+ {
+ x = 0;
+ c = ' ';
+ cp--;
+ while (cp < lp)
+ {
+ if (*cp == '\\')
+ {
+ cp++;
+ if ((lp - cp) < 1)
+ {
+ c = '\\';
+ break;
+ }
+ if (*cp == 'n')
+ {
+ cp++;
+ c = '\n';
+ if ((lp - cp) > 2)
+ {
+ if (*cp == ']' || *cp == '@' && *(cp + 1) == '(')
+ {
+ *dp++ = '\\';
+ *dp++ = 'n';
+ c = *cp++;
+ break;
+ }
+ if (*cp == '\\' && *(cp + 1) == 'n')
+ {
+ cp += 2;
+ *dp++ = '\n';
+ break;
+ }
+ }
+ }
+ else if (*cp == 't' || *cp == ' ')
+ {
+ cp++;
+ x = 1;
+ c = ' ';
+ }
+ else
+ {
+ if (x && dp != buf && *(dp - 1) != ' ')
+ *dp++ = ' ';
+ *dp++ = '\\';
+ c = *cp++;
+ break;
+ }
+ }
+ else if (*cp == ' ' || *cp == '\t')
+ {
+ cp++;
+ c = ' ';
+ x = 1;
+ }
+ else
+ {
+ if (x && c != '\n' && dp != buf && *(dp - 1) != ' ')
+ *dp++ = ' ';
+ break;
+ }
+ }
+ if (c == '\n')
+ {
+ c = 0;
+ goto flush;
+ }
+ if (c == ' ' && (dp == buf || *(dp - 1) == ' '))
+ continue;
+ }
+ else if (c == '"')
+ {
+ if (b || cp >= lp)
+ {
+ if (fp->quote)
+ continue;
+ fp->section = 0;
+ }
+ }
+ else if (c == '\a')
+ {
+ *dp++ = '\\';
+ c = 'a';
+ }
+ else if (c == '\b')
+ {
+ *dp++ = '\\';
+ c = 'b';
+ }
+ else if (c == '\f')
+ {
+ *dp++ = '\\';
+ c = 'f';
+ }
+ else if (c == '\v')
+ {
+ *dp++ = '\\';
+ c = 'v';
+ }
+ else if (c == ']' && (cp >= lp || *cp != ':' && *cp != '#' && *cp != '!'))
+ {
+ if (cp < lp && *cp == ']')
+ {
+ cp++;
+ *dp++ = c;
+ }
+ else
+ {
+ fp->section = 1;
+ fp->retain = 0;
+ flush:
+ *dp++ = c;
+ *dp = 0;
+ split(fp, buf, 0);
+ outline(fp);
+ goto again;
+ }
+ }
+ else if (fp->section)
+ {
+ if (c == '[')
+ {
+ if (b)
+ fp->retain = 1;
+ else
+ {
+ cp--;
+ c = 0;
+ goto flush;
+ }
+ fp->section = 0;
+ }
+ else if (c == '{')
+ {
+ x = 1;
+ for (tp = cp; tp < lp; tp++)
+ {
+ if (*tp == '[' || *tp == '\n')
+ break;
+ if (*tp == ' ' || *tp == '\t' || *tp == '"')
+ continue;
+ if (*tp == '\\' && (lp - tp) > 1)
+ {
+ if (*++tp == 'n')
+ break;
+ if (*tp == 't' || *tp == '\n')
+ continue;
+ }
+ x = 0;
+ break;
+ }
+ if (x)
+ {
+ if (fp->endbuf > (fp->outbuf + fp->indent + 2*INDENT))
+ fp->nextdent = 2*INDENT;
+ goto flush;
+ }
+ else
+ fp->section = 0;
+ }
+ else if (c == '}')
+ {
+ if (fp->indent && (b || *(cp - 2) != 'f'))
+ {
+ if (b)
+ {
+ fp->indent -= 2*INDENT;
+ fp->endbuf += 2*INDENT;
+ }
+ else
+ {
+ cp--;
+ c = 0;
+ }
+ goto flush;
+ }
+ else
+ fp->section = 0;
+ }
+ else if (c == ' ' || c == '\t')
+ continue;
+ else
+ fp->section = 0;
+ }
+ else if (c == '?' && (cp >= lp || *cp != '?'))
+ {
+ if (fp->retain)
+ {
+ cp--;
+ while (cp < lp && *cp != ' ' && *cp != '\t' && *cp != ']' && dp < &buf[sizeof(buf)-3])
+ *dp++ = *cp++;
+ if (cp < lp && (*cp == ' ' || *cp == '\t'))
+ *dp++ = *cp++;
+ *dp = 0;
+ split(fp, buf, 0);
+ dp = buf;
+ ep = 0;
+ fp->retain = 0;
+ if (fp->outp >= fp->endbuf)
+ outline(fp);
+ continue;
+ }
+ }
+ else if (c == ' ' || c == '\t')
+ for (c = ' '; *cp == ' ' || *cp == '\t'; cp++);
+ }
+ else if (c == '\b')
+ {
+ if (dp > buf)
+ {
+ dp--;
+ if (ep)
+ ep--;
+ }
+ continue;
+ }
+ else if (c == '\t')
+ {
+ /*
+ * expand tabs
+ */
+
+ if (!ep)
+ ep = dp;
+ c = isoption(fp, 'o') ? 1 : TABSZ - (dp - buf) % TABSZ;
+ if (dp >= &buf[sizeof(buf) - c - 3])
+ {
+ cp--;
+ break;
+ }
+ while (c-- > 0)
+ *dp++ = ' ';
+ continue;
+ }
+ else if (!isprint(c))
+ continue;
+ if (dp >= &buf[sizeof(buf) - 3])
+ {
+ tp = dp;
+ while (--tp > buf)
+ if (isspace(*tp))
+ {
+ cp -= dp - tp;
+ dp = tp;
+ break;
+ }
+ ep = 0;
+ break;
+ }
+ if (c != ' ')
+ ep = 0;
+ else if (!ep)
+ ep = dp;
+ *dp++ = c;
+ }
+ if (ep)
+ *ep = 0;
+ else
+ *dp = 0;
+ split(fp, buf, splice);
+ }
+ return 0;
+}
+
+int
+b_fmt(int argc, char** argv, Shbltin_t* context)
+{
+ register int n;
+ char* cp;
+ Fmt_t fmt;
+ char outbuf[8 * 1024];
+
+ fmt.flags = 0;
+ fmt.out = sfstdout;
+ fmt.outbuf = outbuf;
+ fmt.outp = 0;
+ fmt.endbuf = &outbuf[72];
+ fmt.indent = 0;
+ fmt.nextdent = 0;
+ fmt.nwords = 0;
+ fmt.prefix = 0;
+ fmt.quote = 0;
+ fmt.retain = 0;
+ fmt.section = 1;
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (n = optget(argv, usage))
+ {
+ case 'c':
+ case 'o':
+ case 's':
+ case 'u':
+ setoption(&fmt, n);
+ continue;
+ case 'w':
+ if (opt_info.num < TABSZ || opt_info.num>= sizeof(outbuf))
+ error(2, "width out of range");
+ fmt.endbuf = &outbuf[opt_info.num];
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (isoption(&fmt, 'o'))
+ setoption(&fmt, 'c');
+ if (isoption(&fmt, 's'))
+ clroption(&fmt, 'u');
+ if (cp = *argv)
+ argv++;
+ do {
+ if (!cp || streq(cp, "-"))
+ fmt.in = sfstdin;
+ else if (!(fmt.in = sfopen(NiL, cp, "r")))
+ {
+ error(ERROR_system(0), "%s: cannot open", cp);
+ error_info.errors = 1;
+ continue;
+ }
+ dofmt(&fmt);
+ if (fmt.in != sfstdin)
+ sfclose(fmt.in);
+ } while (cp = *argv++);
+ outline(&fmt);
+ if (sfsync(sfstdout))
+ error(ERROR_system(0), "write error");
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/fold.c b/src/lib/libcmd/fold.c
new file mode 100644
index 0000000..7e94ffd
--- /dev/null
+++ b/src/lib/libcmd/fold.c
@@ -0,0 +1,240 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * fold
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: fold (AT&T Research) 2004-11-18 $\n]"
+USAGE_LICENSE
+"[+NAME?fold - fold lines]"
+"[+DESCRIPTION?\bfold\b is a filter that folds lines from its input, "
+ "breaking the lines to have a maximum of \awidth\a column "
+ "positions (or bytes if the \b-b\b option is specified). Lines "
+ "are broken by the insertion of a newline character such that "
+ "each output line is the maximum width possible that does not "
+ "exceed the specified number of column positions, (or bytes). A line "
+ "will not be broken in the middle of a character.] "
+"[+?Unless the \b-b\b option is specified, the following will be treated "
+ "specially:]{"
+ "[+carriage-return?The current count of line width will be set "
+ "to zero. \bfold\b will not insert a newline immediately "
+ "before or after a carriage-return.]"
+ "[+backspace?If positive, the current count of line width will be "
+ "decremented by one. \bfold\b will not insert a newline "
+ "immediately before or after a backspace.]"
+ "[+tab?Each tab character encountered will advance the column "
+ "position to the next tab stop. Tab stops are at each "
+ "column position \an\a, where \an\a modulo 8 equals 1.]"
+ "}"
+"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bfold\b "
+ "reads from standard input. The start of the file is defined "
+ "as the current offset.]"
+
+"[b:bytes?Count bytes rather than columns so that each carriage-return, "
+ "backspace, and tab counts as 1.]"
+"[c:continue?Emit \atext\a at line splits.]:[text:='\\n']"
+"[d:delimiter?Break at \adelim\a boundaries.]:[delim]"
+"[s:spaces?Break at word boundaries. If the line contains any blanks, "
+ "(spaces or tabs), within the first \awidth\a column positions or "
+ "bytes, the line is broken after the last blank meeting the "
+ "\awidth\a constraint.]"
+"[w:width]#[width:=80?Use a maximum line length of \awidth\a columns "
+ "instead of the default.]"
+"\n"
+"\n[file ...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files processed successfully.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bpaste\b(1)]"
+;
+
+
+#include <cmd.h>
+
+#define WIDTH 80
+#define TABSIZE 8
+
+#define T_EOF 1
+#define T_NL 2
+#define T_BS 3
+#define T_TAB 4
+#define T_SP 5
+#define T_RET 6
+
+static void fold(Sfio_t *in, Sfio_t *out, register int width, const char *cont, size_t contsize, char *cols)
+{
+ register char *cp, *first;
+ register int n, col=0, x=0;
+ register char *last_space=0;
+ cols[0] = 0;
+ for (;;)
+ {
+ if (!(cp = sfgetr(in,'\n',0)))
+ {
+ if (!(cp = sfgetr(in,'\n',-1)) || (n = sfvalue(in)) <= 0)
+ break;
+ x = cp[--n];
+ cp[n] = '\n';
+ }
+ /* special case -b since no column adjustment is needed */
+ if(cols['\b']==0 && (n=sfvalue(in))<=width)
+ {
+ sfwrite(out,cp,n);
+ continue;
+ }
+ first = cp;
+ col = 0;
+ last_space = 0;
+ for(;;)
+ {
+ while((n=cols[*(unsigned char*)cp++])==0);
+ while((cp-first) > (width-col))
+ {
+ if(last_space)
+ col = last_space - first;
+ else
+ col = width-col;
+ sfwrite(out,first,col);
+ first += col;
+ col = 0;
+ last_space = 0;
+ if(cp>first+1 || (n!=T_NL && n!=T_BS))
+ sfwrite(out, cont, contsize);
+ }
+ switch(n)
+ {
+ case T_NL:
+ if(x)
+ *(cp-1) = x;
+ break;
+ case T_RET:
+ col = 0;
+ continue;
+ case T_BS:
+ if((cp+(--col)-first)>0)
+ col--;
+ continue;
+ case T_TAB:
+ n = (TABSIZE-1) - (cp+col-1-first)&(TABSIZE-1);
+ col +=n;
+ if((cp-first) > (width-col))
+ {
+ sfwrite(out,first,(--cp)-first);
+ sfwrite(out, cont, contsize);
+ first = cp;
+ col = TABSIZE-1;
+ last_space = 0;
+ continue;
+ }
+ if(cols[' '])
+ last_space = cp;
+ continue;
+ case T_SP:
+ last_space = cp;
+ continue;
+ default:
+ continue;
+ }
+ break;
+ }
+ sfwrite(out,first,cp-first);
+ }
+}
+
+int
+b_fold(int argc, char** argv, Shbltin_t* context)
+{
+ register int n, width=WIDTH;
+ register Sfio_t *fp;
+ register char *cp;
+ char *cont="\n";
+ size_t contsize = 1;
+ char cols[1<<CHAR_BIT];
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ memset(cols, 0, sizeof(cols));
+ cols['\t'] = T_TAB;
+ cols['\b'] = T_BS;
+ cols['\n'] = T_NL;
+ cols['\r'] = T_RET;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'b':
+ cols['\r'] = cols['\b'] = 0;
+ cols['\t'] = cols[' '];
+ continue;
+ case 'c':
+ contsize = stresc(cont = strdup(opt_info.arg));
+ continue;
+ case 'd':
+ if (n = *opt_info.arg)
+ cols[n] = T_SP;
+ continue;
+ case 's':
+ cols[' '] = T_SP;
+ if(cols['\t']==0)
+ cols['\t'] = T_SP;
+ continue;
+ case 'w':
+ if ((width = opt_info.num) <= 0)
+ error(2, "%d: width must be positive", opt_info.num);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ continue;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ continue;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if(error_info.errors)
+ error(ERROR_usage(2),"%s", optusage(NiL));
+ if(cp = *argv)
+ argv++;
+ do
+ {
+ if(!cp || streq(cp,"-"))
+ fp = sfstdin;
+ else if(!(fp = sfopen(NiL,cp,"r")))
+ {
+ error(ERROR_system(0),"%s: cannot open",cp);
+ error_info.errors = 1;
+ continue;
+ }
+ fold(fp,sfstdout,width,cont,contsize,cols);
+ if(fp!=sfstdin)
+ sfclose(fp);
+ }
+ while(cp= *argv++);
+ return(error_info.errors);
+}
diff --git a/src/lib/libcmd/fts_fix.c b/src/lib/libcmd/fts_fix.c
new file mode 100644
index 0000000..61b61ba
--- /dev/null
+++ b/src/lib/libcmd/fts_fix.c
@@ -0,0 +1,57 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * -lcmd specific workaround to handle
+ * fts_namelen
+ * fts_pathlen
+ * fts_level
+ * changing from [unsigned] short bit to [s]size_t
+ *
+ * ksh (or any other main application) that pulls in -lcmd
+ * at runtime may result in old -last running with new -lcmd
+ * which is not a good situation (tm)
+ *
+ * probably safe to drop after 20150101
+ */
+
+#include <ast.h>
+#include <fts_fix.h>
+
+#undef fts_read
+
+FTSENT*
+_fts_read(FTS* fts)
+{
+ FTSENT* oe;
+
+ static FTSENT* ne;
+
+ if ((oe = _ast_fts_read(fts)) && ast.version < 20100102L && (ne || (ne = newof(0, FTSENT, 1, 0))))
+ {
+ *ne = *oe;
+ oe = ne;
+ ne->fts_namelen = ne->_fts_namelen;
+ ne->fts_pathlen = ne->_fts_pathlen;
+ ne->fts_level = ne->_fts_level;
+ }
+ return oe;
+}
diff --git a/src/lib/libcmd/fts_fix.h b/src/lib/libcmd/fts_fix.h
new file mode 100644
index 0000000..96f4448
--- /dev/null
+++ b/src/lib/libcmd/fts_fix.h
@@ -0,0 +1,49 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * -lcmd specific workaround to handle
+ * fts_namelen
+ * fts_pathlen
+ * fts_level
+ * changing from [unsigned] short bit to [s]size_t
+ *
+ * ksh (or any other main application) that pulls in -lcmd
+ * at runtime may result in old -last running with new -lcmd
+ * which is not a good situation (tm)
+ */
+
+#ifndef _FTS_FIX_H
+#define _FTS_FIX_H 1
+
+#include <fts.h>
+
+#ifdef fts_read
+#undef fts_read
+#else
+#define _ast_fts_read fts_read
+#endif
+
+#define fts_read _fts_read
+
+extern FTSENT* fts_read(FTS*);
+
+#endif
diff --git a/src/lib/libcmd/getconf.c b/src/lib/libcmd/getconf.c
new file mode 100644
index 0000000..e151f62
--- /dev/null
+++ b/src/lib/libcmd/getconf.c
@@ -0,0 +1,396 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * getconf - get configuration values
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: getconf (AT&T Research) 2008-04-24 $\n]"
+USAGE_LICENSE
+"[+NAME?getconf - get configuration values]"
+"[+DESCRIPTION?\bgetconf\b displays the system configuration value for"
+" \aname\a. If \aname\a is a filesystem specific variable then"
+" the value is determined relative to \apath\a or the current"
+" directory if \apath\a is omitted. If \avalue\a is specified then"
+" \bgetconf\b attempts to change the process local value to \avalue\a."
+" \b-\b may be used in place of \apath\a when it is not relevant."
+" If \apath\a is \b=\b then the the \avalue\a is cached and used"
+" for subsequent tests in the calling and all child processes."
+" Only \bwritable\b variables may be set; \breadonly\b variables"
+" cannot be changed.]"
+"[+?The current value for \aname\a is written to the standard output. If"
+" \aname\a is valid but undefined then \bundefined\b is written to"
+" the standard output. If \aname\a is invalid or an error occurs in"
+" determining its value, then a diagnostic written to the standard error"
+" and \bgetconf\b exits with a non-zero exit status.]"
+"[+?More than one variable may be set or queried by providing the \aname\a"
+" \apath\a \avalue\a 3-tuple for each variable, specifying \b-\b for"
+" \avalue\a when querying.]"
+"[+?If no operands are specified then all known variables are written in"
+" \aname\a=\avalue\a form to the standard output, one per line."
+" Only one of \b--call\b, \b--name\b or \b--standard\b may be specified.]"
+"[+?This implementation uses the \bastgetconf\b(3) string interface to the native"
+" \bsysconf\b(2), \bconfstr\b(2), \bpathconf\b(2), and \bsysinfo\b(2)"
+" system calls. If \bgetconf\b on \b$PATH\b is not the default native"
+" \bgetconf\b, named by \b$(getconf GETCONF)\b, then \bastgetconf\b(3)"
+" checks only \bast\b specific extensions and the native system calls;"
+" invalid options and/or names not supported by \bastgetconf\b(3) cause"
+" the \bgetconf\b on \b$PATH\b to be executed.]"
+
+"[a:all?Call the native \bgetconf\b(1) with option \b-a\b.]"
+"[b:base?List base variable name sans call and standard prefixes.]"
+"[c:call?Display variables with call prefix that matches \aRE\a. The call"
+" prefixes are:]:[RE]{"
+" [+CS?\bconfstr\b(2)]"
+" [+PC?\bpathconf\b(2)]"
+" [+SC?\bsysconf\b(2)]"
+" [+SI?\bsysinfo\b(2)]"
+" [+XX?Constant value.]"
+"}"
+"[d:defined?Only display defined values when no operands are specified.]"
+"[l:lowercase?List variable names in lower case.]"
+"[n:name?Display variables with name that match \aRE\a.]:[RE]"
+"[p:portable?Display the named \bwritable\b variables and values in a form that"
+" can be directly executed by \bsh\b(1) to set the values. If \aname\a"
+" is omitted then all \bwritable\b variables are listed.]"
+"[q:quote?\"...\" quote values.]"
+"[r:readonly?Display the named \breadonly\b variables in \aname\a=\avalue\a form."
+" If \aname\a is omitted then all \breadonly\b variables are listed.]"
+"[s:standard?Display variables with standard prefix that matches \aRE\a."
+" Use the \b--table\b option to view all standard prefixes, including"
+" local additions. The standard prefixes available on all systems"
+" are:]:[RE]{"
+" [+AES]"
+" [+AST]"
+" [+C]"
+" [+GNU]"
+" [+POSIX]"
+" [+SVID]"
+" [+XBS5]"
+" [+XOPEN]"
+" [+XPG]"
+"}"
+"[t:table?Display the internal table that contains the name, standard,"
+" standard section, and system call symbol prefix for each variable.]"
+"[w:writable?Display the named \bwritable\b variables in \aname\a=\avalue\a"
+" form. If \aname\a is omitted then all \bwritable\b variables are"
+" listed.]"
+"[v:specification?Call the native \bgetconf\b(1) with option"
+" \b-v\b \aname\a.]:[name]"
+
+"\n"
+"\n[ name [ path [ value ] ] ... ]\n"
+"\n"
+
+"[+ENVIRONMENT]{"
+" [+_AST_FEATURES?Process local writable values that are different from"
+" the default are stored in the \b_AST_FEATURES\b environment"
+" variable. The \b_AST_FEATURES\b value is a space-separated"
+" list of \aname\a \apath\a \avalue\a 3-tuples, where"
+" \aname\a is the system configuration name, \apath\a is the"
+" corresponding path, \b-\b if no path is applicable, and"
+" \avalue\a is the system configuration value.]"
+"}"
+"[+SEE ALSO?\bpathchk\b(1), \bconfstr\b(2), \bpathconf\b(2),"
+" \bsysconf\b(2), \bastgetconf\b(3)]"
+;
+
+#include <cmd.h>
+#include <proc.h>
+#include <ls.h>
+
+typedef struct Path_s
+{
+ const char* path;
+ int len;
+} Path_t;
+
+int
+b_getconf(int argc, char** argv, Shbltin_t* context)
+{
+ register char* name;
+ register char* path;
+ register char* value;
+ register const char* s;
+ register const char* t;
+ char* pattern;
+ char* native;
+ char* cmd;
+ Path_t* e;
+ Path_t* p;
+ int flags;
+ int n;
+ int i;
+ int m;
+ int q;
+ char** oargv;
+ char buf[PATH_MAX];
+ Path_t std[64];
+ struct stat st0;
+ struct stat st1;
+
+ static const char empty[] = "-";
+ static const Path_t equiv[] = { { "/bin", 4 }, { "/usr/bin", 8 } };
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ oargv = argv;
+ if (*(native = astconf("GETCONF", NiL, NiL)) != '/')
+ native = 0;
+ flags = 0;
+ name = 0;
+ pattern = 0;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ if (native)
+ goto defer;
+ continue;
+ case 'b':
+ flags |= ASTCONF_base;
+ continue;
+ case 'c':
+ flags |= ASTCONF_matchcall;
+ pattern = opt_info.arg;
+ continue;
+ case 'd':
+ flags |= ASTCONF_defined;
+ continue;
+ case 'l':
+ flags |= ASTCONF_lower;
+ continue;
+ case 'n':
+ flags |= ASTCONF_matchname;
+ pattern = opt_info.arg;
+ continue;
+ case 'p':
+ flags |= ASTCONF_parse;
+ continue;
+ case 'q':
+ flags |= ASTCONF_quote;
+ continue;
+ case 'r':
+ flags |= ASTCONF_read;
+ continue;
+ case 's':
+ flags |= ASTCONF_matchstandard;
+ pattern = opt_info.arg;
+ continue;
+ case 't':
+ flags |= ASTCONF_table;
+ continue;
+ case 'v':
+ if (native)
+ goto defer;
+ continue;
+ case 'w':
+ flags |= ASTCONF_write;
+ continue;
+ case ':':
+ if (native)
+ goto defer;
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (!(name = *argv))
+ path = 0;
+ else if (streq(name, empty))
+ {
+ name = 0;
+ if (path = *++argv)
+ {
+ argv++;
+ if (streq(path, empty))
+ path = 0;
+ }
+ }
+ if (error_info.errors || !name && *argv)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (!name)
+ astconflist(sfstdout, path, flags, pattern);
+ else
+ {
+ flags = native ? (ASTCONF_system|ASTCONF_error) : 0;
+ do
+ {
+ if (!(path = *++argv))
+ value = 0;
+ else
+ {
+ if (streq(path, empty))
+ {
+ path = 0;
+ flags = 0;
+ }
+ if ((value = *++argv) && (streq(value, empty)))
+ {
+ value = 0;
+ flags = 0;
+ }
+ }
+ s = astgetconf(name, path, value, flags, errorf);
+ if (error_info.errors)
+ break;
+ if (!s)
+ goto defer;
+ if (!value)
+ {
+ if (flags & ASTCONF_write)
+ {
+ sfputr(sfstdout, name, ' ');
+ sfputr(sfstdout, path ? path : empty, ' ');
+ }
+ sfputr(sfstdout, s, '\n');
+ }
+ } while (*argv && (name = *++argv));
+ }
+ return error_info.errors != 0;
+
+ defer:
+
+ /*
+ * defer to argv[0] if absolute and it exists
+ */
+
+ if ((cmd = oargv[0]) && *cmd == '/' && !access(cmd, X_OK))
+ goto found;
+
+ /*
+ * defer to the first getconf on $PATH that is also on the standard PATH
+ */
+
+ e = std;
+ s = astconf("PATH", NiL, NiL);
+ q = !stat(equiv[0].path, &st0) && !stat(equiv[1].path, &st1) && st0.st_ino == st1.st_ino && st0.st_dev == st1.st_dev;
+ m = 0;
+ do
+ {
+ for (t = s; *s && *s != ':'; s++);
+ if ((n = s - t) && *t == '/')
+ {
+ if (q)
+ for (i = 0; i < 2; i++)
+ if (n == equiv[i].len && !strncmp(t, equiv[i].path, n))
+ {
+ if (m & (i+1))
+ t = 0;
+ else
+ {
+ m |= (i+1);
+ if (!(m & (!i+1)))
+ {
+ m |= (!i+1);
+ e->path = t;
+ e->len = n;
+ e++;
+ if (e >= &std[elementsof(std)])
+ break;
+ t = equiv[!i].path;
+ n = equiv[!i].len;
+ }
+ }
+ }
+ if (t)
+ {
+ e->path = t;
+ e->len = n;
+ e++;
+ }
+ }
+ while (*s == ':')
+ s++;
+ } while (*s && e < &std[elementsof(std)]);
+ if (e < &std[elementsof(std)])
+ {
+ e->len = strlen(e->path = "/usr/sbin");
+ if (++e < &std[elementsof(std)])
+ {
+ e->len = strlen(e->path = "/sbin");
+ e++;
+ }
+ }
+ if (s = getenv("PATH"))
+ do
+ {
+ for (t = s; *s && *s != ':'; s++);
+ if ((n = s - t) && *t == '/')
+ {
+ for (p = std; p < e; p++)
+ if (p->len == n && !strncmp(t, p->path, n))
+ {
+ sfsprintf(buf, sizeof(buf), "%-*.*s/%s", n, n, t, error_info.id);
+ if (!access(buf, X_OK))
+ {
+ cmd = buf;
+ goto found;
+ }
+ }
+ }
+ while (*s == ':')
+ s++;
+ } while (*s);
+
+ /*
+ * defer to the first getconf on the standard PATH
+ */
+
+ for (p = std; p < e; p++)
+ {
+ sfsprintf(buf, sizeof(buf), "%-*.*s/%s", p->len, p->len, p->path, error_info.id);
+ if (!access(buf, X_OK))
+ {
+ cmd = buf;
+ goto found;
+ }
+ }
+
+ /*
+ * out of deferrals
+ */
+
+ if (name)
+ error(4, "%s: unknown name -- no native getconf(1) to defer to", name);
+ else
+ error(4, "no native getconf(1) to defer to");
+ return 2;
+
+ found:
+
+ /*
+ * don't blame us for crappy diagnostics
+ */
+
+ oargv[0] = cmd;
+ if ((n = sh_run(context, argc, oargv)) >= EXIT_NOEXEC)
+ error(ERROR_SYSTEM|2, "%s: exec error [%d]", cmd, n);
+ return n;
+}
diff --git a/src/lib/libcmd/head.c b/src/lib/libcmd/head.c
new file mode 100644
index 0000000..6f42b5a
--- /dev/null
+++ b/src/lib/libcmd/head.c
@@ -0,0 +1,150 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * output the beginning portion of one or more files
+ */
+
+static const char usage[] =
+"[-n?\n@(#)$Id: head (AT&T Research) 2012-01-01 $\n]"
+USAGE_LICENSE
+"[+NAME?head - output beginning portion of one or more files ]"
+"[+DESCRIPTION?\bhead\b copies one or more input files to standard "
+ "output stopping at a designated point for each file or to the end of "
+ "the file whichever comes first. Copying ends at the point indicated by "
+ "the options. By default a header of the form \b==> \b\afilename\a\b "
+ "<==\b is output before all but the first file but this can be changed "
+ "with the \b-q\b and \b-v\b options.]"
+"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bhead\b "
+ "copies from standard input starting at the current location.]"
+"[+?The option argument for \b-c\b, and \b-s\b can optionally be "
+ "followed by one of the following characters to specify a different unit "
+ "other than a single byte:]"
+ "{"
+ "[+b?512 bytes.]"
+ "[+k?1-killobyte.]"
+ "[+m?1-megabyte.]"
+ "}"
+"[+?For backwards compatibility, \b-\b\anumber\a is equivalent to \b-n\b "
+ "\anumber\a.]"
+"[n:lines?Copy \alines\a lines from each file.]#[lines:=10]"
+"[c:bytes?Copy \achars\a bytes from each file.]#[chars]"
+"[q:quiet|silent?Never ouput filename headers.]"
+"[s:skip?Skip \askip\a characters or lines from each file before "
+ "copying.]#[skip]"
+"[v:verbose?Always ouput filename headers.]"
+ "\n\n"
+"[ file ... ]"
+ "\n\n"
+"[+EXIT STATUS?]"
+ "{"
+ "[+0?All files copied successfully.]"
+ "[+>0?One or more files did not copy.]"
+ "}"
+"[+SEE ALSO?\bcat\b(1), \btail\b(1)]"
+;
+
+#include <cmd.h>
+
+int
+b_head(int argc, register char** argv, Shbltin_t* context)
+{
+ static const char header_fmt[] = "\n==> %s <==\n";
+
+ register Sfio_t* fp;
+ register char* cp;
+ register off_t keep = 10;
+ register off_t skip = 0;
+ register int delim = '\n';
+ int header = 1;
+ char* format = (char*)header_fmt+1;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'c':
+ delim = -1;
+ /*FALLTHROUGH*/
+ case 'n':
+ if (opt_info.offset && argv[opt_info.index][opt_info.offset] == 'c')
+ {
+ delim = -1;
+ opt_info.offset++;
+ }
+ if ((keep = opt_info.number) <=0)
+ error(2, "%s: %I*d: positive numeric option argument expected", opt_info.name, sizeof(keep), keep);
+ continue;
+ case 'q':
+ header = argc;
+ continue;
+ case 'v':
+ header = 0;
+ continue;
+ case 's':
+ skip = opt_info.number;
+ continue;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ continue;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (cp = *argv)
+ argv++;
+ do
+ {
+ if (!cp || streq(cp, "-"))
+ {
+ cp = "/dev/stdin";
+ fp = sfstdin;
+ sfset(fp, SF_SHARE, 1);
+ }
+ else if (!(fp = sfopen(NiL, cp, "r")))
+ {
+ error(ERROR_system(0), "%s: cannot open", cp);
+ continue;
+ }
+ if (argc > header)
+ sfprintf(sfstdout, format, cp);
+ format = (char*)header_fmt;
+ if (skip > 0)
+ sfmove(fp, NiL, skip, delim);
+ if (sfmove(fp, sfstdout, keep, delim) < 0 && errno != EPIPE && errno != EINTR)
+ error(ERROR_system(0), "%s: read error", cp);
+ if (fp != sfstdin)
+ sfclose(fp);
+ } while (cp = *argv++);
+ if (sfsync(sfstdout))
+ error(ERROR_system(0), "write error");
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/id.c b/src/lib/libcmd/id.c
new file mode 100644
index 0000000..aa0a65b
--- /dev/null
+++ b/src/lib/libcmd/id.c
@@ -0,0 +1,472 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * id
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: id (AT&T Research) 2004-06-11 $\n]"
+USAGE_LICENSE
+"[+NAME?id - return user identity]"
+"[+DESCRIPTION?If no \auser\a operand is specified \bid\b writes user and "
+ "group IDs and the corresponding user and group names of the "
+ "invoking process to standard output. If the effective and "
+ "real IDs do not match, both are written. Any supplementary "
+ "groups the current process belongs to will also be written.]"
+"[+?If a \auser\a operand is specified and the process has permission, "
+ "the user and group IDs and any supplementary group IDs of the "
+ "selected user will be written to standard output.]"
+"[+?If any options are specified, then only a portion of the information "
+ "is written.]"
+"[n:name?Write the name instead of the numeric ID.]"
+"[r:real?Writes real ID instead of the effective ID.]"
+"[[a?This option is ignored.]"
+"[g:group?Writes only the group ID.]"
+"[u:user?Writes only the user ID.]"
+"[G:groups?Writes only the supplementary group IDs.]"
+"[s:fair-share?Writes fair share scheduler IDs and groups on systems that "
+ "support fair share scheduling.]"
+"\n"
+"\n[user]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Successful completion.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\blogname\b(1), \bwho\b(1), \bgetgroups\b(2)]"
+;
+
+#include <cmd.h>
+
+#include "FEATURE/ids"
+
+#include <grp.h>
+#include <pwd.h>
+
+#if _lib_fsid
+#if _lib_getfsgid && ( _sys_fss || _hdr_fsg )
+#define fss_grp fs_grp
+#define fss_id fs_id
+#define fss_mem fs_mem
+#define fss_passwd fs_passwd
+#define fss_shares fs_shares
+#if _sys_fss
+#include <sys/fss.h>
+#endif
+#if _hdr_fsg
+#include <fsg.h>
+#endif
+#if !_lib_isfsg && !defined(isfsg)
+#define isfsg(p) (!(p)->fs_id&&!(p)->fs_shares&&(!(p)->fs_passwd||!*(p)->fs_passwd))
+#endif
+#else
+#undef _lib_fsid
+#endif
+#endif
+
+#define power2(n) (!((n)&((n)-1)))
+
+#define GG_FLAG (1<<0)
+#define G_FLAG (1<<1)
+#define N_FLAG (1<<2)
+#define R_FLAG (1<<3)
+#define U_FLAG (1<<4)
+#define S_FLAG (1<<5)
+#define O_FLAG (1<<6)
+#define X_FLAG (1<<7)
+
+#if _lib_fsid
+static void
+getfsids(Sfio_t* sp, const char* name, int flags, register int lastchar)
+{
+ register struct fsg* fs;
+ register char* s;
+ register char** p;
+ char** x;
+
+ if (lastchar)
+ {
+ if (flags & O_FLAG) flags = 1;
+ else flags = 0;
+ }
+ else if (flags & N_FLAG) flags = 1;
+ else flags = -1;
+ setfsgent();
+ while (fs = getfsgnam(name))
+ if (!isfsg(fs))
+ {
+ if (p = fs->fs_mem)
+ {
+ if (flags > 0) x = 0;
+ else
+ {
+ register char** q;
+ register char* t;
+ register int n;
+
+ n = 0;
+ q = p;
+ while (s = *q++)
+ n += strlen(s) + 1;
+ if (!(x = newof(0, char*, q - p, n)))
+ break;
+ s = (char*)(x + (q - p));
+ q = x;
+ while (t = *p++)
+ {
+ *q++ = s;
+ while (*s++ = *t++);
+ }
+ *q = 0;
+ p = x;
+ }
+ while (s = *p++)
+ {
+ if (lastchar == '=')
+ {
+ lastchar = ',';
+ sfputr(sp, " fsid=", -1);
+ }
+ else if (!lastchar) lastchar = ' ';
+ else sfputc(sp, lastchar);
+ if (flags > 0) sfprintf(sp, "%s", s);
+ else
+ {
+ setfsgent();
+ while (fs = getfsgnam(s))
+ if (isfsg(fs))
+ {
+ if (flags < 0) sfprintf(sp, "%u", fs->fs_id);
+ else sfprintf(sp, "%u(%s)", fs->fs_id, s);
+ break;
+ }
+ }
+ }
+ if (x) free(x);
+ }
+ break;
+ }
+ endfsgent();
+ if (lastchar == ' ') sfputc(sp, '\n');
+}
+#endif
+
+static void
+putid(Sfio_t* sp, int flags, const char* label, const char* name, long number)
+{
+ sfprintf(sp, "%s=", label);
+ if (flags & O_FLAG)
+ {
+ if (name) sfputr(sp, name, -1);
+ else sfprintf(sp, "%lu", number);
+ }
+ else
+ {
+ sfprintf(sp, "%u", number);
+ if (name) sfprintf(sp, "(%s)", name);
+ }
+}
+
+static int
+getids(Sfio_t* sp, const char* name, register int flags)
+{
+ register struct passwd* pw;
+ register struct group* grp;
+ register int i;
+ register int j;
+ register int k;
+#if _lib_fsid
+ register struct fsg* fs;
+ const char* fs_name;
+ int fs_id;
+#endif
+ char** p;
+ char* s;
+ int lastchar;
+ int ngroups = 0;
+ const char* gname;
+ uid_t user;
+ uid_t euid;
+ gid_t group;
+ gid_t egid;
+
+ static gid_t* groups;
+
+ if (flags & GG_FLAG)
+ {
+ static int maxgroups;
+
+ /*
+ * get supplemental groups if required
+ */
+
+ if (!maxgroups)
+ {
+ /*
+ * first time
+ */
+
+ if ((maxgroups = getgroups(0, groups)) <= 0)
+ maxgroups = NGROUPS_MAX;
+ if (!(groups = newof(0, gid_t, maxgroups + 1, 0)))
+ error(ERROR_exit(1), "out of space [group array]");
+ }
+ ngroups = getgroups(maxgroups, groups);
+ for (i = j = 0; i < ngroups; i++)
+ {
+ for (k = 0; k < j && groups[k] != groups[i]; k++);
+ if (k >= j) groups[j++] = groups[i];
+ }
+ ngroups = j;
+ }
+ if (name)
+ {
+ flags |= X_FLAG;
+ if (!(flags & N_FLAG) || (flags & (G_FLAG|GG_FLAG)))
+ {
+ if (!(pw = getpwnam(name)))
+ {
+ user = strtol(name, &s, 0);
+ if (*s || !(pw = getpwuid(user)))
+ error(ERROR_exit(1), "%s: name not found", name);
+ name = pw->pw_name;
+ }
+ user = pw->pw_uid;
+ group = pw->pw_gid;
+ }
+#if _lib_fsid
+ if (!(flags & N_FLAG) || (flags & S_FLAG))
+ {
+ setfsgent();
+ do
+ {
+ if (!(fs = getfsgnam(name)))
+ error(ERROR_exit(1), "%u: fss name not found", name);
+ } while (isfsg(fs));
+ fs_id = fs->fs_id;
+ }
+#endif
+ }
+ else
+ {
+ if (flags & G_FLAG)
+ group = (flags & R_FLAG) ? getgid() : getegid();
+ if (flags & (GG_FLAG|N_FLAG|U_FLAG))
+ user = (flags & R_FLAG) ? getuid() : geteuid();
+#if _lib_fsid
+ if (flags & S_FLAG)
+ fs_id = fsid(0);
+#endif
+ if (flags & N_FLAG)
+ name = (pw = getpwuid(user)) ? pw->pw_name : (char*)0;
+ }
+ if (ngroups == 1 && groups[0] == group)
+ ngroups = 0;
+ if ((flags & N_FLAG) && (flags & G_FLAG))
+ gname = (grp = getgrgid(group)) ? grp->gr_name : (char*)0;
+#if _lib_fsid
+ if ((flags & N_FLAG) && (flags & S_FLAG))
+ {
+ setfsgent();
+ fs_name = (fs = getfsgid(fs_id)) ? fs->fs_grp : (char*)0;
+ }
+#endif
+ if ((flags & (U_FLAG|G_FLAG|S_FLAG)) == (U_FLAG|G_FLAG|S_FLAG))
+ {
+ putid(sp, flags, "uid", name, user);
+ putid(sp, flags, " gid", gname, group);
+ if ((flags & X_FLAG) && name)
+ {
+#if _lib_getgrent
+#if _lib_setgrent
+ setgrent();
+#endif
+ lastchar = '=';
+ while (grp = getgrent())
+ if (p = grp->gr_mem)
+ while (s = *p++)
+ if (streq(s, name))
+ {
+ if (lastchar == '=')
+ sfputr(sp, " groups", -1);
+ sfputc(sp, lastchar);
+ lastchar = ',';
+ if (flags & O_FLAG)
+ sfprintf(sp, "%s", grp->gr_name);
+ else sfprintf(sp, "%u(%s)", grp->gr_gid, grp->gr_name);
+ }
+#if _lib_endgrent
+ endgrent();
+#endif
+#endif
+#if _lib_fsid
+ getfsids(sp, name, flags, '=');
+#endif
+ }
+ else
+ {
+ if ((euid = geteuid()) != user)
+ putid(sp, flags, " euid", (pw = getpwuid(euid)) ? pw->pw_name : (char*)0, euid);
+ if ((egid = getegid()) != group)
+ putid(sp, flags, " egid", (grp = getgrgid(egid)) ? grp->gr_name : (char*)0, egid);
+ if (ngroups > 0)
+ {
+ sfputr(sp, " groups", -1);
+ lastchar = '=';
+ for (i = 0; i < ngroups; i++)
+ {
+ group = groups[i];
+ sfputc(sp, lastchar);
+ if (grp = getgrgid(group))
+ {
+ if (flags & O_FLAG) sfprintf(sp, "%s", grp->gr_name);
+ else sfprintf(sp, "%u(%s)", group, grp->gr_name);
+ }
+ else sfprintf(sp, "%u", group);
+ lastchar = ',';
+ }
+ }
+#if _lib_fsid
+ putid(sp, flags, " fsid", fs_name, fs_id);
+#endif
+ }
+ sfputc(sp,'\n');
+ return(0);
+ }
+ if (flags & U_FLAG)
+ {
+ if ((flags & N_FLAG) && name) sfputr(sp, name, '\n');
+ else sfprintf(sp, "%u\n", user);
+ }
+ else if (flags & G_FLAG)
+ {
+ if ((flags & N_FLAG) && gname) sfputr(sp, gname, '\n');
+ else sfprintf(sp, "%u\n", group);
+ }
+ else if (flags & GG_FLAG)
+ {
+ if ((flags & X_FLAG) && name)
+ {
+#if _lib_getgrent
+#if _lib_setgrent
+ setgrent();
+#endif
+ i = 0;
+ while (grp = getgrent())
+ if (p = grp->gr_mem)
+ while (s = *p++)
+ if (streq(s, name))
+ {
+ if (i++) sfputc(sp, ' ');
+ if (flags & N_FLAG) sfprintf(sp, "%s", grp->gr_name);
+ else sfprintf(sp, "%u", grp->gr_gid);
+ }
+#if _lib_endgrent
+ endgrent();
+#endif
+ if (i) sfputc(sp, '\n');
+#endif
+ }
+ else if (ngroups > 0)
+ {
+ for (i = 0;;)
+ {
+ group = groups[i];
+ if ((flags & N_FLAG) && (grp = getgrgid(group)))
+ sfprintf(sp, "%s", grp->gr_name);
+ else sfprintf(sp, "%u", group);
+ if (++i >= ngroups) break;
+ sfputc(sp, ' ');
+ }
+ sfputc(sp, '\n');
+ }
+ }
+#if _lib_fsid
+ else if (flags & S_FLAG)
+ {
+ if ((flags & X_FLAG) && name) getfsids(sp, name, flags, 0);
+ else if ((flags & N_FLAG) && fs_name) sfputr(sp, fs_name, '\n');
+ else sfprintf(sp, "%u\n", fs_id);
+ }
+#endif
+ return(0);
+}
+
+int
+b_id(int argc, char** argv, Shbltin_t* context)
+{
+ register int flags = 0;
+ register int n;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ continue;
+ case 'G':
+ flags |= GG_FLAG;
+ continue;
+ case 'g':
+ flags |= G_FLAG;
+ continue;
+ case 'n':
+ flags |= N_FLAG;
+ continue;
+ case 'r':
+ flags |= R_FLAG;
+ continue;
+ case 's':
+ flags |= S_FLAG;
+ continue;
+ case 'u':
+ flags |= U_FLAG;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ n = (flags & (GG_FLAG|G_FLAG|S_FLAG|U_FLAG));
+ if (!power2(n))
+ error(2, "incompatible options selected");
+ if (error_info.errors || argc > 1)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (!(flags & ~(N_FLAG|R_FLAG)))
+ {
+ if (flags & N_FLAG) flags |= O_FLAG;
+ flags |= (U_FLAG|G_FLAG|N_FLAG|R_FLAG|S_FLAG|GG_FLAG);
+ }
+ error_info.errors = getids(sfstdout, *argv, flags);
+ return(error_info.errors);
+}
diff --git a/src/lib/libcmd/join.c b/src/lib/libcmd/join.c
new file mode 100644
index 0000000..675577c
--- /dev/null
+++ b/src/lib/libcmd/join.c
@@ -0,0 +1,984 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * join
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: join (AT&T Research) 2009-12-10 $\n]"
+USAGE_LICENSE
+"[+NAME?join - relational database operator]"
+"[+DESCRIPTION?\bjoin\b performs an \aequality join\a on the files \afile1\a "
+ "and \afile2\a and writes the resulting joined files to standard "
+ "output. By default, a field is delimited by one or more spaces "
+ "and tabs with leading spaces and/or tabs ignored. The \b-t\b option "
+ "can be used to change the field delimiter.]"
+"[+?The \ajoin field\a is a field in each file on which files are compared. "
+ "By default \bjoin\b writes one line in the output for each pair "
+ "of lines in \afiles1\a and \afiles2\a that have identical join "
+ "fields. The default output line consists of the join field, "
+ "then the remaining fields from \afile1\a, then the remaining "
+ "fields from \afile2\a, but this can be changed with the \b-o\b "
+ "option. The \b-a\b option can be used to add unmatched lines "
+ "to the output. The \b-v\b option can be used to output only "
+ "unmatched lines.]"
+"[+?The files \afile1\a and \afile2\a must be ordered in the collating "
+ "sequence of \bsort -b\b on the fields on which they are to be "
+ "joined otherwise the results are unspecified.]"
+"[+?If either \afile1\a or \afile2\a is \b-\b, \bjoin\b "
+ "uses standard input starting at the current location.]"
+
+"[e:empty]:[string?Replace empty output fields in the list selected with"
+" \b-o\b with \astring\a.]"
+"[o:output]:[list?Construct the output line to comprise the fields specified "
+ "in a blank or comma separated list \alist\a. Each element in "
+ "\alist\a consists of a file number (either 1 or 2), a period, "
+ "and a field number or \b0\b representing the join field. "
+ "As an obsolete feature multiple occurrences of \b-o\b can "
+ "be specified.]"
+"[t:separator|tabs]:[delim?Use \adelim\a as the field separator for both input"
+" and output.]"
+"[1:j1]#[field?Join on field \afield\a of \afile1\a. Fields start at 1.]"
+"[2:j2]#[field?Join on field \afield\a of \afile2\a. Fields start at 1.]"
+"[j:join]#[field?Equivalent to \b-1\b \afield\a \b-2\b \afield\a.]"
+"[a:unpairable]#[fileno?Write a line for each unpairable line in file"
+" \afileno\a, where \afileno\a is either 1 or 2, in addition to the"
+" normal output. If \b-a\b options appear for both 1 and 2, then "
+ "all unpairable lines will be output.]"
+"[v:suppress]#[fileno?Write a line for each unpairable line in file"
+" \afileno\a, where \afileno\a is either 1 or 2, instead of the normal "
+ "output. If \b-v\b options appear for both 1 and 2, then "
+ "all unpairable lines will be output.] ]"
+"[i:ignorecase?Ignore case in field comparisons.]"
+"[B!:mmap?Enable memory mapped reads instead of buffered.]"
+
+"[+?The following obsolete option forms are also recognized: \b-j\b \afield\a"
+" is equivalent to \b-1\b \afield\a \b-2\b \afield\a, \b-j1\b \afield\a"
+" is equivalent to \b-1\b \afield\a, and \b-j2\b \afield\a is"
+" equivalent to \b-2\b \afield\a.]"
+
+"\n"
+"\nfile1 file2\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Both files processed successfully.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bcut\b(1), \bcomm\b(1), \bpaste\b(1), \bsort\b(1), \buniq\b(1)]"
+;
+
+#include <cmd.h>
+#include <sfdisc.h>
+
+#if _hdr_wchar && _hdr_wctype && _lib_iswctype
+
+#include <wchar.h>
+#include <wctype.h>
+
+#else
+
+#include <ctype.h>
+
+#ifndef iswspace
+#define iswspace(x) isspace(x)
+#endif
+
+#endif
+
+#define C_FILE1 001
+#define C_FILE2 002
+#define C_COMMON 004
+#define C_ALL (C_FILE1|C_FILE2|C_COMMON)
+
+#define NFIELD 10
+#define JOINFIELD 2
+
+#define S_DELIM 1
+#define S_SPACE 2
+#define S_NL 3
+#define S_WIDE 4
+
+typedef struct Field_s
+{
+ char* beg;
+ char* end;
+} Field_t;
+
+typedef struct File_s
+{
+ Sfio_t* iop;
+ char* name;
+ char* recptr;
+ int reclen;
+ int field;
+ int fieldlen;
+ int nfields;
+ int maxfields;
+ int spaces;
+ int hit;
+ int discard;
+ Field_t* fields;
+} File_t;
+
+typedef struct Join_s
+{
+ unsigned char state[1<<CHAR_BIT];
+ Sfio_t* outfile;
+ int* outlist;
+ int outmode;
+ int ooutmode;
+ char* nullfield;
+ char* delimstr;
+ int delim;
+ int delimlen;
+ int buffered;
+ int ignorecase;
+ int mb;
+ char* same;
+ int samesize;
+ Shbltin_t* context;
+ File_t file[2];
+} Join_t;
+
+static void
+done(register Join_t* jp)
+{
+ if (jp->file[0].iop && jp->file[0].iop != sfstdin)
+ sfclose(jp->file[0].iop);
+ if (jp->file[1].iop && jp->file[1].iop != sfstdin)
+ sfclose(jp->file[1].iop);
+ if (jp->outlist)
+ free(jp->outlist);
+ if (jp->file[0].fields)
+ free(jp->file[0].fields);
+ if (jp->file[1].fields)
+ free(jp->file[1].fields);
+ if (jp->same)
+ free(jp->same);
+ free(jp);
+}
+
+static Join_t*
+init(void)
+{
+ register Join_t* jp;
+ register int i;
+
+ setlocale(LC_ALL, "");
+ if (jp = newof(0, Join_t, 1, 0))
+ {
+ if (jp->mb = mbwide())
+ for (i = 0x80; i <= 0xff; i++)
+ jp->state[i] = S_WIDE;
+ jp->state[' '] = jp->state['\t'] = S_SPACE;
+ jp->state['\n'] = S_NL;
+ jp->delim = -1;
+ jp->nullfield = 0;
+ if (!(jp->file[0].fields = newof(0, Field_t, NFIELD + 1, 0)) ||
+ !(jp->file[1].fields = newof(0, Field_t, NFIELD + 1, 0)))
+ {
+ done(jp);
+ return 0;
+ }
+ jp->file[0].maxfields = NFIELD;
+ jp->file[1].maxfields = NFIELD;
+ jp->outmode = C_COMMON;
+ }
+ return jp;
+}
+
+static int
+getolist(Join_t* jp, const char* first, char** arglist)
+{
+ register const char* cp = first;
+ char** argv = arglist;
+ register int c;
+ int* outptr;
+ int* outmax;
+ int nfield = NFIELD;
+ char* str;
+
+ outptr = jp->outlist = newof(0, int, NFIELD + 1, 0);
+ outmax = outptr + NFIELD;
+ while (c = *cp++)
+ {
+ if (c==' ' || c=='\t' || c==',')
+ continue;
+ str = (char*)--cp;
+ if (*cp=='0' && ((c=cp[1])==0 || c==' ' || c=='\t' || c==','))
+ {
+ str++;
+ c = JOINFIELD;
+ goto skip;
+ }
+ if (cp[1]!='.' || (*cp!='1' && *cp!='2') || (c=strtol(cp+2,&str,10)) <=0)
+ {
+ error(2,"%s: invalid field list",first);
+ break;
+ }
+ c--;
+ c <<=2;
+ if (*cp=='2')
+ c |=1;
+ skip:
+ if (outptr >= outmax)
+ {
+ jp->outlist = newof(jp->outlist, int, 2 * nfield + 1, 0);
+ outptr = jp->outlist + nfield;
+ nfield *= 2;
+ outmax = jp->outlist + nfield;
+ }
+ *outptr++ = c;
+ cp = str;
+ }
+ /* need to accept obsolescent command syntax */
+ while (cp = *argv)
+ {
+ if (cp[1]!='.' || (*cp!='1' && *cp!='2'))
+ {
+ if (*cp=='0' && cp[1]==0)
+ {
+ c = JOINFIELD;
+ goto skip2;
+ }
+ break;
+ }
+ str = (char*)cp;
+ c = strtol(cp+2, &str,10);
+ if (*str || --c<0)
+ break;
+ argv++;
+ c <<= 2;
+ if (*cp=='2')
+ c |=1;
+ skip2:
+ if (outptr >= outmax)
+ {
+ jp->outlist = newof(jp->outlist, int, 2 * nfield + 1, 0);
+ outptr = jp->outlist + nfield;
+ nfield *= 2;
+ outmax = jp->outlist + nfield;
+ }
+ *outptr++ = c;
+ }
+ *outptr = -1;
+ return argv-arglist;
+}
+
+/*
+ * read in a record from file <index> and split into fields
+ */
+static unsigned char*
+getrec(Join_t* jp, int index, int discard)
+{
+ register unsigned char* sp = jp->state;
+ register File_t* fp = &jp->file[index];
+ register Field_t* field = fp->fields;
+ register Field_t* fieldmax = field + fp->maxfields;
+ register char* cp;
+ register int n;
+ char* tp;
+
+ if (sh_checksig(jp->context))
+ return 0;
+ if (discard && fp->discard)
+ sfraise(fp->iop, SFSK_DISCARD, NiL);
+ fp->spaces = 0;
+ fp->hit = 0;
+ if (!(cp = sfgetr(fp->iop, '\n', 0)))
+ {
+ jp->outmode &= ~(1<<index);
+ return 0;
+ }
+ fp->recptr = cp;
+ fp->reclen = sfvalue(fp->iop);
+ if (jp->delim == '\n') /* handle new-line delimiter specially */
+ {
+ field->beg = cp;
+ cp += fp->reclen;
+ field->end = cp - 1;
+ field++;
+ }
+ else
+ do /* separate into fields */
+ {
+ if (field >= fieldmax)
+ {
+ n = 2 * fp->maxfields;
+ fp->fields = newof(fp->fields, Field_t, n + 1, 0);
+ field = fp->fields + fp->maxfields;
+ fp->maxfields = n;
+ fieldmax = fp->fields + n;
+ }
+ field->beg = cp;
+ if (jp->delim == -1)
+ {
+ switch (sp[*(unsigned char*)cp])
+ {
+ case S_SPACE:
+ cp++;
+ break;
+ case S_WIDE:
+ tp = cp;
+ if (iswspace(mbchar(tp)))
+ {
+ cp = tp;
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ goto next;
+ }
+ fp->spaces = 1;
+ if (jp->mb)
+ for (;;)
+ {
+ switch (sp[*(unsigned char*)cp++])
+ {
+ case S_SPACE:
+ continue;
+ case S_WIDE:
+ tp = cp - 1;
+ if (iswspace(mbchar(tp)))
+ {
+ cp = tp;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+ else
+ while (sp[*(unsigned char*)cp++]==S_SPACE);
+ cp--;
+ }
+ next:
+ if (jp->mb)
+ {
+ for (;;)
+ {
+ tp = cp;
+ switch (n = sp[*(unsigned char*)cp++])
+ {
+ case 0:
+ continue;
+ case S_WIDE:
+ cp--;
+ n = mbchar(cp);
+ if (n == jp->delim)
+ {
+ n = S_DELIM;
+ break;
+ }
+ if (jp->delim == -1 && iswspace(n))
+ {
+ n = S_SPACE;
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ field->end = tp;
+ }
+ else
+ {
+ while (!(n = sp[*(unsigned char*)cp++]));
+ field->end = cp - 1;
+ }
+ field++;
+ } while (n != S_NL);
+ fp->nfields = field - fp->fields;
+ if ((n = fp->field) < fp->nfields)
+ {
+ cp = fp->fields[n].beg;
+ /* eliminate leading spaces */
+ if (fp->spaces)
+ {
+ if (jp->mb)
+ for (;;)
+ {
+ switch (sp[*(unsigned char*)cp++])
+ {
+ case S_SPACE:
+ continue;
+ case S_WIDE:
+ tp = cp - 1;
+ if (iswspace(mbchar(tp)))
+ {
+ cp = tp;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+ else
+ while (sp[*(unsigned char*)cp++]==S_SPACE);
+ cp--;
+ }
+ fp->fieldlen = fp->fields[n].end - cp;
+ return (unsigned char*)cp;
+ }
+ fp->fieldlen = 0;
+ return (unsigned char*)"";
+}
+
+#if DEBUG_TRACE
+static unsigned char* u1;
+#define getrec(p,n,d) (u1 = getrec(p, n, d), sfprintf(sfstdout, "[G%d#%d@%I*d:%-.8s]", __LINE__, n, sizeof(Sfoff_t), sftell(p->file[n].iop), u1), u1)
+#endif
+
+/*
+ * print field <n> from file <index>
+ */
+static int
+outfield(Join_t* jp, int index, register int n, int last)
+{
+ register File_t* fp = &jp->file[index];
+ register char* cp;
+ register char* cpmax;
+ register int size;
+ register Sfio_t* iop = jp->outfile;
+ char* tp;
+
+ if (n < fp->nfields)
+ {
+ cp = fp->fields[n].beg;
+ cpmax = fp->fields[n].end + 1;
+ }
+ else
+ cp = 0;
+ if ((n = jp->delim) == -1)
+ {
+ if (cp && fp->spaces)
+ {
+ register unsigned char* sp = jp->state;
+
+ /*eliminate leading spaces */
+ if (jp->mb)
+ for (;;)
+ {
+ switch (sp[*(unsigned char*)cp++])
+ {
+ case S_SPACE:
+ continue;
+ case S_WIDE:
+ tp = cp - 1;
+ if (iswspace(mbchar(tp)))
+ {
+ cp = tp;
+ continue;
+ }
+ break;
+ }
+ break;
+ }
+ else
+ while (sp[*(unsigned char*)cp++]==S_SPACE);
+ cp--;
+ }
+ n = ' ';
+ }
+ else if (jp->delimstr)
+ n = -1;
+ if (last)
+ n = '\n';
+ if (cp)
+ size = cpmax - cp;
+ else
+ size = 0;
+ if (n == -1)
+ {
+ if (size<=1)
+ {
+ if (jp->nullfield && sfputr(iop, jp->nullfield, -1) < 0)
+ return -1;
+ }
+ else if (sfwrite(iop, cp, size) < 0)
+ return -1;
+ if (sfwrite(iop, jp->delimstr, jp->delimlen) < 0)
+ return -1;
+ }
+ else if (size <= 1)
+ {
+ if (!jp->nullfield)
+ sfputc(iop, n);
+ else if (sfputr(iop, jp->nullfield, n) < 0)
+ return -1;
+ }
+ else
+ {
+ last = cp[size-1];
+ cp[size-1] = n;
+ if (sfwrite(iop, cp, size) < 0)
+ return -1;
+ cp[size-1] = last;
+ }
+ return 0;
+}
+
+#if DEBUG_TRACE
+static int i1,i2,i3;
+#define outfield(p,i,n,f) (sfprintf(sfstdout, "[F%d#%d:%d,%d]", __LINE__, i1=i, i2=n, i3=f), outfield(p, i1, i2, i3))
+#endif
+
+static int
+outrec(register Join_t* jp, int mode)
+{
+ register File_t* fp;
+ register int i;
+ register int j;
+ register int k;
+ register int n;
+ int* out;
+
+ if (mode < 0 && jp->file[0].hit++)
+ return 0;
+ if (mode > 0 && jp->file[1].hit++)
+ return 0;
+ if (out = jp->outlist)
+ {
+ while ((n = *out++) >= 0)
+ {
+ if (n == JOINFIELD)
+ {
+ i = mode >= 0;
+ j = jp->file[i].field;
+ }
+ else
+ {
+ i = n & 1;
+ j = (mode<0 && i || mode>0 && !i) ?
+ jp->file[i].nfields :
+ n >> 2;
+ }
+ if (outfield(jp, i, j, *out < 0) < 0)
+ return -1;
+ }
+ return 0;
+ }
+ k = jp->file[0].nfields;
+ if (mode >= 0)
+ k += jp->file[1].nfields - 1;
+ for (i=0; i<2; i++)
+ {
+ fp = &jp->file[i];
+ if (mode>0 && i==0)
+ {
+ k -= (fp->nfields - 1);
+ continue;
+ }
+ n = fp->field;
+ if (mode||i==0)
+ {
+ /* output join field first */
+ if (outfield(jp,i,n,!--k) < 0)
+ return -1;
+ if (!k)
+ return 0;
+ for (j=0; j<n; j++)
+ {
+ if (outfield(jp,i,j,!--k) < 0)
+ return -1;
+ if (!k)
+ return 0;
+ }
+ j = n + 1;
+ }
+ else
+ j = 0;
+ for (;j<fp->nfields; j++)
+ {
+ if (j!=n && outfield(jp,i,j,!--k) < 0)
+ return -1;
+ if (!k)
+ return 0;
+ }
+ }
+ return 0;
+}
+
+#if DEBUG_TRACE
+#define outrec(p,n) (sfprintf(sfstdout, "[R#%d,%d,%lld,%lld:%-.*s{%d}:%-.*s{%d}]", __LINE__, i1=n, lo, hi, jp->file[0].fieldlen, cp1, jp->file[0].hit, jp->file[1].fieldlen, cp2, jp->file[1].hit), outrec(p, i1))
+#endif
+
+static int
+join(Join_t* jp)
+{
+ register unsigned char* cp1;
+ register unsigned char* cp2;
+ register int n1;
+ register int n2;
+ register int n;
+ register int cmp;
+ register int same;
+ int o2;
+ Sfoff_t lo = -1;
+ Sfoff_t hi = -1;
+
+ if ((cp1 = getrec(jp, 0, 0)) && (cp2 = getrec(jp, 1, 0)) || (cp2 = 0))
+ {
+ n1 = jp->file[0].fieldlen;
+ n2 = jp->file[1].fieldlen;
+ same = 0;
+ for (;;)
+ {
+ n = n1 < n2 ? n1 : n2;
+#if DEBUG_TRACE
+ if (!n && !(cmp = n1 < n2 ? -1 : (n1 > n2)) || n && !(cmp = (int)*cp1 - (int)*cp2) && !(cmp = jp->ignorecase ? strncasecmp((char*)cp1, (char*)cp2, n) : memcmp(cp1, cp2, n)))
+ cmp = n1 - n2;
+sfprintf(sfstdout, "[C#%d:%d(%c-%c),%d,%lld,%lld%s]", __LINE__, cmp, *cp1, *cp2, same, lo, hi, (jp->outmode & C_COMMON) ? ",COMMON" : "");
+ if (!cmp)
+#else
+ if (!n && !(cmp = n1 < n2 ? -1 : (n1 > n2)) || n && !(cmp = (int)*cp1 - (int)*cp2) && !(cmp = jp->ignorecase ? strncasecmp((char*)cp1, (char*)cp2, n) : memcmp(cp1, cp2, n)) && !(cmp = n1 - n2))
+#endif
+ {
+ if (!(jp->outmode & C_COMMON))
+ {
+ if (cp1 = getrec(jp, 0, 1))
+ {
+ n1 = jp->file[0].fieldlen;
+ same = 1;
+ continue;
+ }
+ if ((jp->ooutmode & (C_FILE1|C_FILE2)) != C_FILE2)
+ break;
+ if (sfseek(jp->file[0].iop, (Sfoff_t)-jp->file[0].reclen, SEEK_CUR) < 0 || !(cp1 = getrec(jp, 0, 0)))
+ {
+ error(ERROR_SYSTEM|2, "%s: seek error", jp->file[0].name);
+ return -1;
+ }
+ }
+ else if (outrec(jp, 0) < 0)
+ return -1;
+ else if (lo < 0 && (jp->outmode & C_COMMON))
+ {
+ if ((lo = sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR)) < 0)
+ {
+ error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
+ return -1;
+ }
+ lo -= jp->file[1].reclen;
+ }
+ if (cp2 = getrec(jp, 1, lo < 0))
+ {
+ n2 = jp->file[1].fieldlen;
+ continue;
+ }
+#if DEBUG_TRACE
+sfprintf(sfstdout, "[2#%d:0,%lld,%lld]", __LINE__, lo, hi);
+#endif
+ }
+ else if (cmp > 0)
+ {
+ if (same)
+ {
+ same = 0;
+ next:
+ if (n2 > jp->samesize)
+ {
+ jp->samesize = roundof(n2, 16);
+ if (!(jp->same = newof(jp->same, char, jp->samesize, 0)))
+ {
+ error(ERROR_SYSTEM|2, "out of space");
+ return -1;
+ }
+ }
+ memcpy(jp->same, cp2, o2 = n2);
+ if (!(cp2 = getrec(jp, 1, 0)))
+ break;
+ n2 = jp->file[1].fieldlen;
+ if (n2 == o2 && *cp2 == *jp->same && !memcmp(cp2, jp->same, n2))
+ goto next;
+ continue;
+ }
+ if (hi >= 0)
+ {
+ if (sfseek(jp->file[1].iop, hi, SEEK_SET) != hi)
+ {
+ error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
+ return -1;
+ }
+ hi = -1;
+ }
+ else if ((jp->outmode & C_FILE2) && outrec(jp, 1) < 0)
+ return -1;
+ lo = -1;
+ if (cp2 = getrec(jp, 1, 1))
+ {
+ n2 = jp->file[1].fieldlen;
+ continue;
+ }
+#if DEBUG_TRACE
+sfprintf(sfstdout, "[2#%d:0,%lld,%lld]", __LINE__, lo, hi);
+#endif
+ }
+ else if (same)
+ {
+ same = 0;
+ if (!(cp1 = getrec(jp, 0, 0)))
+ break;
+ n1 = jp->file[0].fieldlen;
+ continue;
+ }
+ if (lo >= 0)
+ {
+ if ((hi = sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR)) < 0 ||
+ (hi -= jp->file[1].reclen) < 0 ||
+ sfseek(jp->file[1].iop, lo, SEEK_SET) != lo ||
+ !(cp2 = getrec(jp, 1, 0)))
+ {
+ error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
+ return -1;
+ }
+ n2 = jp->file[1].fieldlen;
+ lo = -1;
+ if (jp->file[1].discard)
+ sfseek(jp->file[1].iop, (Sfoff_t)-1, SEEK_SET);
+ }
+ else if (!cp2)
+ break;
+ else if ((jp->outmode & C_FILE1) && outrec(jp, -1) < 0)
+ return -1;
+ if (!(cp1 = getrec(jp, 0, 1)))
+ break;
+ n1 = jp->file[0].fieldlen;
+ }
+ }
+#if DEBUG_TRACE
+sfprintf(sfstdout, "[X#%d:?,%p,%p,%d,%d,%d%s]", __LINE__, cp1, cp2, cmp, lo, hi, (jp->outmode & C_COMMON) ? ",COMMON" : "");
+#endif
+ if (cp2)
+ {
+ if (hi >= 0 &&
+ sfseek(jp->file[1].iop, (Sfoff_t)0, SEEK_CUR) < hi &&
+ sfseek(jp->file[1].iop, hi, SEEK_SET) != hi)
+ {
+ error(ERROR_SYSTEM|2, "%s: seek error", jp->file[1].name);
+ return -1;
+ }
+#if DEBUG_TRACE
+sfprintf(sfstdout, "[O#%d:%02o:%02o]", __LINE__, jp->ooutmode, jp->outmode);
+#endif
+ cp1 = (!cp1 && cmp && hi < 0 && !jp->file[1].hit && ((jp->ooutmode ^ C_ALL) <= 1 || jp->outmode == 2)) ? cp2 : getrec(jp, 1, 0);
+ cmp = 1;
+ n = 1;
+ }
+ else
+ {
+ cmp = -1;
+ n = 0;
+ }
+#if DEBUG_TRACE
+sfprintf(sfstdout, "[X#%d:%d,%p,%p,%d,%02o,%02o%s]", __LINE__, n, cp1, cp2, cmp, jp->ooutmode, jp->outmode, (jp->outmode & C_COMMON) ? ",COMMON" : "");
+#endif
+ if (!cp1 || !(jp->outmode & (1<<n)))
+ {
+ if (cp1 && jp->file[n].iop == sfstdin)
+ sfseek(sfstdin, (Sfoff_t)0, SEEK_END);
+ return 0;
+ }
+ if (outrec(jp, cmp) < 0)
+ return -1;
+ do
+ {
+ if (!getrec(jp, n, 1))
+ return 0;
+ } while (outrec(jp, cmp) >= 0);
+ return -1;
+}
+
+int
+b_join(int argc, char** argv, Shbltin_t* context)
+{
+ register int n;
+ register char* cp;
+ register Join_t* jp;
+ char* e;
+
+#if !DEBUG_TRACE
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+#endif
+ if (!(jp = init()))
+ error(ERROR_system(1),"out of space");
+ jp->context = context;
+ for (;;)
+ {
+ switch (n = optget(argv, usage))
+ {
+ case 'j':
+ /*
+ * check for obsolete "-j1 field" and "-j2 field"
+ */
+
+ if (opt_info.offset == 0)
+ {
+ cp = argv[opt_info.index - 1];
+ for (n = strlen(cp) - 1; n > 0 && cp[n] != 'j'; n--);
+ n = cp[n] == 'j';
+ }
+ else
+ n = 0;
+ if (n)
+ {
+ if (opt_info.num!=1 && opt_info.num!=2)
+ error(2,"-jfileno field: fileno must be 1 or 2");
+ n = '0' + opt_info.num;
+ if (!(cp = argv[opt_info.index]))
+ {
+ argc = 0;
+ break;
+ }
+ opt_info.num = strtol(cp, &e, 10);
+ if (*e)
+ {
+ argc = 0;
+ break;
+ }
+ opt_info.index++;
+ }
+ else
+ {
+ jp->file[0].field = (int)(opt_info.num-1);
+ n = '2';
+ }
+ /*FALLTHROUGH*/
+ case '1':
+ case '2':
+ if (opt_info.num <=0)
+ error(2,"field number must positive");
+ jp->file[n-'1'].field = (int)(opt_info.num-1);
+ continue;
+ case 'v':
+ jp->outmode &= ~C_COMMON;
+ /*FALLTHROUGH*/
+ case 'a':
+ if (opt_info.num!=1 && opt_info.num!=2)
+ error(2,"%s: file number must be 1 or 2", opt_info.name);
+ jp->outmode |= 1<<(opt_info.num-1);
+ continue;
+ case 'e':
+ jp->nullfield = opt_info.arg;
+ continue;
+ case 'o':
+ /* need to accept obsolescent command syntax */
+ n = getolist(jp, opt_info.arg, argv+opt_info.index);
+ opt_info.index += n;
+ continue;
+ case 't':
+ jp->state[' '] = jp->state['\t'] = 0;
+ if (jp->mb)
+ {
+ cp = opt_info.arg;
+ jp->delim = mbchar(cp);
+ if ((n = cp - opt_info.arg) > 1)
+ {
+ jp->delimlen = n;
+ jp->delimstr = opt_info.arg;
+ continue;
+ }
+ }
+ n = *(unsigned char*)opt_info.arg;
+ jp->state[n] = S_DELIM;
+ jp->delim = n;
+ continue;
+ case 'i':
+ jp->ignorecase = !opt_info.num;
+ continue;
+ case 'B':
+ jp->buffered = !opt_info.num;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ done(jp);
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ argc -= opt_info.index;
+ if (error_info.errors || argc!=2)
+ {
+ done(jp);
+ error(ERROR_usage(2),"%s", optusage(NiL));
+ }
+ jp->ooutmode = jp->outmode;
+ jp->file[0].name = cp = *argv++;
+ if (streq(cp,"-"))
+ {
+ if (sfseek(sfstdin,(Sfoff_t)0,SEEK_CUR) < 0)
+ {
+ if (sfdcseekable(sfstdin))
+ error(ERROR_warn(0),"%s: seek may fail",cp);
+ else
+ jp->file[0].discard = 1;
+ }
+ jp->file[0].iop = sfstdin;
+ }
+ else if (!(jp->file[0].iop = sfopen(NiL, cp, "r")))
+ {
+ done(jp);
+ error(ERROR_system(1),"%s: cannot open",cp);
+ }
+ jp->file[1].name = cp = *argv;
+ if (streq(cp,"-"))
+ {
+ if (sfseek(sfstdin,(Sfoff_t)0,SEEK_CUR) < 0)
+ {
+ if (sfdcseekable(sfstdin))
+ error(ERROR_warn(0),"%s: seek may fail",cp);
+ else
+ jp->file[1].discard = 1;
+ }
+ jp->file[1].iop = sfstdin;
+ }
+ else if (!(jp->file[1].iop = sfopen(NiL, cp, "r")))
+ {
+ done(jp);
+ error(ERROR_system(1),"%s: cannot open",cp);
+ }
+ if (jp->buffered)
+ {
+ sfsetbuf(jp->file[0].iop, jp->file[0].iop, SF_UNBOUND);
+ sfsetbuf(jp->file[1].iop, jp->file[1].iop, SF_UNBOUND);
+ }
+ jp->outfile = sfstdout;
+ if (!jp->outlist)
+ jp->nullfield = 0;
+ if (join(jp) < 0)
+ {
+ done(jp);
+ error(ERROR_system(1),"write error");
+ }
+ else if (jp->file[0].iop==sfstdin || jp->file[1].iop==sfstdin)
+ sfseek(sfstdin,(Sfoff_t)0,SEEK_END);
+ done(jp);
+ return error_info.errors;
+}
diff --git a/src/lib/libcmd/lib.c b/src/lib/libcmd/lib.c
new file mode 100644
index 0000000..2f0bb41
--- /dev/null
+++ b/src/lib/libcmd/lib.c
@@ -0,0 +1,25 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+#include <cmd.h>
+
+SHLIB(cmd)
diff --git a/src/lib/libcmd/ln.c b/src/lib/libcmd/ln.c
new file mode 100644
index 0000000..709b450
--- /dev/null
+++ b/src/lib/libcmd/ln.c
@@ -0,0 +1,35 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * cp/ln/mv -- copy/link/move files
+ */
+
+#include <cmd.h>
+
+int
+b_ln(int argc, register char** argv, Shbltin_t* context)
+{
+ return b_cp(argc, argv, context);
+}
diff --git a/src/lib/libcmd/logname.c b/src/lib/libcmd/logname.c
new file mode 100644
index 0000000..5dce0e2
--- /dev/null
+++ b/src/lib/libcmd/logname.c
@@ -0,0 +1,78 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Research
+ *
+ * logname
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: logname (AT&T Research) 1999-04-30 $\n]"
+USAGE_LICENSE
+"[+NAME?logname - return the user's login name]"
+"[+DESCRIPTION?\blogname\b writes the users's login name to standard "
+ "output. The login name is the string that is returned by the "
+ "\bgetlogin\b(2) function. If \bgetlogin\b(2) does not return "
+ "successfully, the corresponding to the real user id of the calling "
+ "process is used instead.]"
+
+"\n"
+"\n\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?Successful Completion.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bgetlogin\b(2)]"
+;
+
+
+#include <cmd.h>
+
+int
+b_logname(int argc, char** argv, Shbltin_t* context)
+{
+ register char* logname;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case ':':
+ error(2, "%s", opt_info.arg);
+ continue;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ continue;
+ }
+ break;
+ }
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (!(logname = getlogin()))
+ logname = fmtuid(getuid());
+ sfputr(sfstdout, logname, '\n');
+ return 0;
+}
+
diff --git a/src/lib/libcmd/md5sum.c b/src/lib/libcmd/md5sum.c
new file mode 100644
index 0000000..bd0b25f
--- /dev/null
+++ b/src/lib/libcmd/md5sum.c
@@ -0,0 +1,35 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * md5sum -- implemented by cksum
+ */
+
+#include <cmd.h>
+
+int
+b_md5sum(int argc, register char** argv, Shbltin_t* context)
+{
+ return b_cksum(argc, argv, context);
+}
diff --git a/src/lib/libcmd/mkdir.c b/src/lib/libcmd/mkdir.c
new file mode 100644
index 0000000..bdd6ecf
--- /dev/null
+++ b/src/lib/libcmd/mkdir.c
@@ -0,0 +1,189 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * mkdir
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: mkdir (AT&T Research) 2010-04-08 $\n]"
+USAGE_LICENSE
+"[+NAME?mkdir - make directories]"
+"[+DESCRIPTION?\bmkdir\b creates one or more directories. By "
+ "default, the mode of created directories is \ba=rwx\b minus the "
+ "bits set in the \bumask\b(1).]"
+"[m:mode]:[mode?Set the mode of created directories to \amode\a. "
+ "\amode\a is symbolic or octal mode as in \bchmod\b(1). Relative "
+ "modes assume an initial mode of \ba=rwx\b.]"
+"[p:parents?Create any missing intermediate pathname components. For "
+ "each dir operand that does not name an existing directory, effects "
+ "equivalent to those caused by the following command shall occur: "
+ "\vmkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode]] "
+ "dir\v where the \b-m\b mode option represents that option supplied to "
+ "the original invocation of \bmkdir\b, if any. Each dir operand that "
+ "names an existing directory shall be ignored without error.]"
+"[v:verbose?Print a message on the standard error for each created "
+ "directory.]"
+"\n"
+"\ndirectory ...\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All directories created successfully, or the \b-p\b option "
+ "was specified and all the specified directories now exist.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bchmod\b(1), \brmdir\b(1), \bumask\b(1)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+
+#define DIRMODE (S_IRWXU|S_IRWXG|S_IRWXO)
+
+int
+b_mkdir(int argc, char** argv, Shbltin_t* context)
+{
+ register char* path;
+ register int n;
+ register mode_t mode = DIRMODE;
+ register mode_t mask = 0;
+ register int mflag = 0;
+ register int pflag = 0;
+ register int vflag = 0;
+ int made;
+ char* part;
+ mode_t dmode;
+ struct stat st;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'm':
+ mflag = 1;
+ mode = strperm(opt_info.arg, &part, mode);
+ if (*part)
+ error(ERROR_exit(0), "%s: invalid mode", opt_info.arg);
+ continue;
+ case 'p':
+ pflag = 1;
+ continue;
+ case 'v':
+ vflag = 1;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || !*argv)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ mask = umask(0);
+ if (mflag || pflag)
+ {
+ dmode = DIRMODE & ~mask;
+ if (!mflag)
+ mode = dmode;
+ dmode |= S_IWUSR | S_IXUSR;
+ }
+ else
+ {
+ mode &= ~mask;
+ umask(mask);
+ mask = 0;
+ }
+ while (path = *argv++)
+ {
+ if (!mkdir(path, mode))
+ {
+ if (vflag)
+ error(0, "%s: directory created", path);
+ made = 1;
+ }
+ else if (!pflag || !(errno == ENOENT || errno == EEXIST || errno == ENOTDIR))
+ {
+ error(ERROR_system(0), "%s:", path);
+ continue;
+ }
+ else if (errno == EEXIST)
+ continue;
+ else
+ {
+ /*
+ * -p option, preserve intermediates
+ * first eliminate trailing /'s
+ */
+
+ made = 0;
+ n = strlen(path);
+ while (n > 0 && path[--n] == '/');
+ path[n + 1] = 0;
+ for (part = path, n = *part; n;)
+ {
+ /* skip over slashes */
+ while (*part == '/')
+ part++;
+ /* skip to next component */
+ while ((n = *part) && n != '/')
+ part++;
+ *part = 0;
+ if (mkdir(path, n ? dmode : mode) < 0 && errno != EEXIST && access(path, F_OK) < 0)
+ {
+ error(ERROR_system(0), "%s: cannot create intermediate directory", path);
+ *part = n;
+ break;
+ }
+ if (vflag)
+ error(0, "%s: directory created", path);
+ if (!(*part = n))
+ {
+ made = 1;
+ break;
+ }
+ }
+ }
+ if (made && (mode & (S_ISVTX|S_ISUID|S_ISGID)))
+ {
+ if (stat(path, &st))
+ {
+ error(ERROR_system(0), "%s: cannot stat", path);
+ break;
+ }
+ if ((st.st_mode & (S_ISVTX|S_ISUID|S_ISGID)) != (mode & (S_ISVTX|S_ISUID|S_ISGID)) && chmod(path, mode))
+ {
+ error(ERROR_system(0), "%s: cannot change mode from %s to %s", path, fmtperm(st.st_mode & (S_ISVTX|S_ISUID|S_ISGID)), fmtperm(mode));
+ break;
+ }
+ }
+ }
+ if (mask)
+ umask(mask);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/mkfifo.c b/src/lib/libcmd/mkfifo.c
new file mode 100644
index 0000000..25861a1
--- /dev/null
+++ b/src/lib/libcmd/mkfifo.c
@@ -0,0 +1,96 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * mkfifo
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: mkfifo (AT&T Research) 2009-01-02 $\n]"
+USAGE_LICENSE
+"[+NAME?mkfifo - make FIFOs (named pipes)]"
+"[+DESCRIPTION?\bmkfifo\b creates one or more FIFO's. By "
+ "default, the mode of created FIFO is \ba=rw\b minus the "
+ "bits set in the \bumask\b(1).]"
+"[m:mode]:[mode?Set the mode of created FIFO to \amode\a. "
+ "\amode\a is symbolic or octal mode as in \bchmod\b(1). Relative "
+ "modes assume an initial mode of \ba=rw\b.]"
+"\n"
+"\nfile ...\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All FIFO's created successfully.]"
+ "[+>0?One or more FIFO's could not be created.]"
+"}"
+"[+SEE ALSO?\bchmod\b(1), \bumask\b(1)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+
+int
+b_mkfifo(int argc, char** argv, Shbltin_t* context)
+{
+ register char* arg;
+ register mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ register mode_t mask = 0;
+ register int mflag = 0;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'm':
+ mflag = 1;
+ mode = strperm(arg = opt_info.arg, &opt_info.arg, mode);
+ if (*opt_info.arg)
+ error(ERROR_exit(0), "%s: invalid mode", arg);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || !*argv)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ mask = umask(0);
+ if (!mflag)
+ {
+ mode &= ~mask;
+ umask(mask);
+ mask = 0;
+ }
+ while (arg = *argv++)
+ if (mkfifo(arg, mode) < 0)
+ error(ERROR_system(0), "%s:", arg);
+ if (mask)
+ umask(mask);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/mktemp.c b/src/lib/libcmd/mktemp.c
new file mode 100644
index 0000000..d95f864
--- /dev/null
+++ b/src/lib/libcmd/mktemp.c
@@ -0,0 +1,169 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+static const char usage[] =
+"[-?\n@(#)$Id: mktemp (AT&T Research) 2010-03-05 $\n]"
+USAGE_LICENSE
+"[+NAME?mktemp - make temporary file or directory]"
+"[+DESCRIPTION?\bmktemp\b creates a temporary file with optional base "
+ "name prefix \aprefix\a. If \aprefix\a is omitted then \btmp_\b is used "
+ "and \b--tmp\b is implied. If \aprefix\a contains a directory prefix "
+ "then that directory overrides any of the directories described below. A "
+ "temporary file will have mode \brw-------\b and a temporary directory "
+ "will have mode \brwx------\b, subject to \bumask\b(1). Generated paths "
+ "have these attributes:]"
+ "{"
+ "[+*?Lower case to avoid clashes on case ignorant filesystems.]"
+ "[+*?Pseudo-random part to deter denial of service attacks.]"
+ "[+*?Default pseudo-random part (no specific \bX...\b template) "
+ "formatted to accomodate 8.3 filesystems.]"
+ "}"
+"[+?A consecutive trailing sequence of \bX\b's in \aprefix\a is replaced "
+ "by the pseudo-random part. If there are no \bX\b's then the "
+ "pseudo-random part is appended to the prefix.]"
+"[d:directory?Create a directory instead of a regular file.]"
+"[m:mode]:[mode?Set the mode of the created temporary to \amode\a. "
+ "\amode\a is symbolic or octal mode as in \bchmod\b(1). Relative modes "
+ "assume an initial mode of \bu=rwx\b.]"
+"[p:default?Use \adirectory\a if the \bTMPDIR\b environment variable is "
+ "not defined. Implies \b--tmp\b.]:[directory]"
+"[q:quiet?Suppress file and directory error diagnostics.]"
+"[R:regress?The pseudo random generator is seeded with \aseed\a instead "
+ "of process/system specific transient data. Use for testing "
+ "only. A seed of \b0\b is silently changed to \b1\b.]#[seed]"
+"[t:tmp|temporary-directory?Create a path rooted in a temporary "
+ "directory.]"
+"[u:unsafe|dry-run?Check for file/directory existence but do not create. "
+ "Use this for testing only.]"
+"\n"
+"\n[ prefix ]\n"
+"\n"
+"[+SEE ALSO?\bmkdir\b(1), \bpathtemp\b(3), \bmktemp\b(3)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+
+int
+b_mktemp(int argc, char** argv, Shbltin_t* context)
+{
+ mode_t mode = 0;
+ mode_t mask;
+ int fd;
+ int i;
+ int quiet = 0;
+ int unsafe = 0;
+ int* fdp = &fd;
+ char* dir = "";
+ char* pfx;
+ char* t;
+ char path[PATH_MAX];
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'd':
+ fdp = 0;
+ continue;
+ case 'm':
+ mode = strperm(pfx = opt_info.arg, &opt_info.arg, S_IRWXU);
+ if (*opt_info.arg)
+ error(ERROR_exit(0), "%s: invalid mode", pfx);
+ continue;
+ case 'p':
+ if ((t = getenv("TMPDIR")) && *t)
+ dir = 0;
+ else
+ dir = opt_info.arg;
+ continue;
+ case 'q':
+ quiet = 1;
+ continue;
+ case 't':
+ dir = 0;
+ continue;
+ case 'u':
+ unsafe = 1;
+ fdp = 0;
+ continue;
+ case 'R':
+ if (!pathtemp(NiL, 0, opt_info.arg, "/seed", NiL))
+ error(2, "%s: regression test initializtion failed", opt_info.arg);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || (pfx = *argv++) && *argv)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ mask = umask(0);
+ if (!mode)
+ mode = (fdp ? (S_IRUSR|S_IWUSR) : S_IRWXU) & ~mask;
+ umask(~mode & (S_IRWXU|S_IRWXG|S_IRWXO));
+ if (!pfx)
+ {
+ pfx = "tmp_";
+ if (dir && !*dir)
+ dir = 0;
+ }
+ if (t = strrchr(pfx, '/'))
+ {
+ i = ++t - pfx;
+ dir = fmtbuf(i);
+ memcpy(dir, pfx, i);
+ dir[i] = 0;
+ pfx = t;
+ }
+ for (;;)
+ {
+ if (!pathtemp(path, sizeof(path), dir, pfx, fdp))
+ {
+ if (quiet)
+ error_info.errors++;
+ else
+ error(ERROR_SYSTEM|2, "cannot create temporary path");
+ break;
+ }
+ if (fdp || unsafe || !mkdir(path, mode))
+ {
+ if (fdp)
+ close(*fdp);
+ sfputr(sfstdout, path, '\n');
+ break;
+ }
+ if (sh_checksig(context))
+ {
+ error_info.errors++;
+ break;
+ }
+ }
+ umask(mask);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/mv.c b/src/lib/libcmd/mv.c
new file mode 100644
index 0000000..f282bdc
--- /dev/null
+++ b/src/lib/libcmd/mv.c
@@ -0,0 +1,35 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * cp/ln/mv -- copy/link/move files
+ */
+
+#include <cmd.h>
+
+int
+b_mv(int argc, register char** argv, Shbltin_t* context)
+{
+ return b_cp(argc, argv, context);
+}
diff --git a/src/lib/libcmd/paste.c b/src/lib/libcmd/paste.c
new file mode 100644
index 0000000..cb22e8e
--- /dev/null
+++ b/src/lib/libcmd/paste.c
@@ -0,0 +1,288 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * paste [-s] [-d delim] [file] ...
+ *
+ * paste lines from files together
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: paste (AT&T Research) 2010-06-12 $\n]"
+USAGE_LICENSE
+"[+NAME?paste - merge lines of files]"
+"[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a "
+ "given input file and writes the resulting lines to standard "
+ "output. By default \bpaste\b replaces the newline character of "
+ "every line other than the last input file with the TAB character.]"
+"[+?Unless the \b-s\b option is specified, if an end-of-file is encountered "
+ "on one or more input files, but not all input files, \bpaste\b "
+ "behaves as if empty lines were read from the file(s) on which "
+ "end-of-file was detected.]"
+"[+?Unless the \b-s\b option is specified, \bpaste\b is limited by "
+ "the underlying operating system on how many \afile\a operands "
+ "can be specified.]"
+"[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b "
+ "reads from standard input. The start of the file is defined as the "
+ "current offset.]"
+
+"[s:serial?Paste the lines of one file at a time rather than one line "
+ "from each file. In this case if the \b-d\b option is "
+ "specified the delimiter will be reset to the first in the "
+ "list at the beginning of each file.]"
+"[d:delimiters]:[list?\alist\a specifies a list of delimiters. These "
+ "delimiters are used circularly instead of TAB to replace "
+ "the newline character of the input lines. Unless the \b-s\b "
+ "option is specified, the delimiter will be reset to the first "
+ "element of \alist\a each time a line is processed from each file. "
+ "The delimiter characters corresponding to \alist\a will be found "
+ "by treating \alist\a as an ANSI-C string, except that the \b\\0\b "
+ "sequence will insert the empty string instead of the null character.]"
+"\n"
+"\n[file ...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files processed successfully.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]"
+;
+
+#include <cmd.h>
+
+typedef struct Delim_s
+{
+ const char* chr;
+ size_t len;
+} Delim_t;
+
+/*
+ * paste the lines of the <nstreams> defined in <streams> and put results
+ * to <out>
+ */
+
+static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim, int dsiz, int dlen, Delim_t* mp)
+{
+ register const char *cp;
+ register int d, n, i, z, more=1;
+ register Sfio_t *fp;
+ do
+ {
+ d = (dlen>0?0:-1);
+ for(n=more-1,more=0; n < nstream;)
+ {
+ if(fp=streams[n])
+ {
+ if(cp = sfgetr(fp,'\n',0))
+ {
+ if(n==0)
+ more = 1;
+ else if(!more) /* first stream with output */
+ {
+ if(dsiz == 1)
+ sfnputc(out, *delim, n);
+ else if(dlen>0)
+ {
+ for(d=n; d>dlen; d-=dlen)
+ sfwrite(out,delim,dsiz);
+ if(d)
+ {
+ if(mp)
+ for (i = z = 0; i < d; i++)
+ z += mp[i].len;
+ else
+ z = d;
+ sfwrite(out,delim,z);
+ }
+ }
+ more = n+1;
+ }
+ if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0)
+ return(-1);
+ }
+ else
+ streams[n] = 0;
+ }
+ if(++n<nstream && more && d>=0)
+ {
+ register int c;
+ if(d >= dlen)
+ d = 0;
+ if(mp)
+ sfwrite(out,mp[d].chr,mp[d].len);
+ else if(c=delim[d])
+ sfputc(out,c);
+ d++;
+ }
+ else if(n==nstream && !streams[n-1] && more)
+ sfputc(out,'\n');
+ }
+ } while(more);
+ return(0);
+}
+
+/*
+ * Handles paste -s, for file <in> to file <out> using delimiters <delim>
+ */
+static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dsiz,int dlen,Delim_t* mp)
+{
+ register const char *cp;
+ register int d=0;
+ if((cp = sfgetr(in,'\n',0)) && sfwrite(out,cp,sfvalue(in)-1) < 0)
+ return(-1);
+ while(cp=sfgetr(in, '\n',0))
+ {
+ if(dlen)
+ {
+ register int c;
+ if(d >= dlen)
+ d = 0;
+ if(mp)
+ sfwrite(out,mp[d].chr,mp[d].len);
+ else if(c=delim[d])
+ sfputc(out,c);
+ d++;
+ }
+ if(sfwrite(out,cp,sfvalue(in)-1) < 0)
+ return(-1);
+ }
+ sfputc(out,'\n');
+ return(0);
+}
+
+int
+b_paste(int argc, char** argv, Shbltin_t* context)
+{
+ register int n, sflag=0;
+ register Sfio_t *fp, **streams;
+ register char *cp, *delim;
+ char *ep;
+ Delim_t *mp;
+ int dlen, dsiz;
+ char defdelim[2];
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ delim = 0;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'd':
+ delim = opt_info.arg;
+ continue;
+ case 's':
+ sflag++;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if(error_info.errors)
+ error(ERROR_usage(2),"%s", optusage(NiL));
+ if(!delim || !*delim)
+ {
+ delim = defdelim;
+ delim[0] = '\t';
+ delim[1] = 0;
+ }
+ if (!(delim = strdup(delim)))
+ error(ERROR_system(1), "out of space");
+ dlen = dsiz = stresc(delim);
+ mp = 0;
+ if (mbwide())
+ {
+ cp = delim;
+ ep = delim + dlen;
+ dlen = 0;
+ while (cp < ep)
+ {
+ mbchar(cp);
+ dlen++;
+ }
+ if(dlen < dsiz)
+ {
+ if (!(mp = newof(0, Delim_t, dlen, 0)))
+ {
+ free(delim);
+ error(ERROR_system(1), "out of space");
+ }
+ cp = delim;
+ dlen = 0;
+ while (cp < ep)
+ {
+ mp[dlen].chr = cp;
+ mbchar(cp);
+ mp[dlen].len = cp - mp[dlen].chr;
+ dlen++;
+ }
+ }
+ }
+ if(cp = *argv)
+ {
+ n = argc - opt_info.index;
+ argv++;
+ }
+ else
+ n = 1;
+ if(!sflag)
+ {
+ if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*))))
+ error(ERROR_exit(1), "out of space");
+ n = 0;
+ }
+ do
+ {
+ if(!cp || streq(cp,"-"))
+ fp = sfstdin;
+ else if(!(fp = sfopen(NiL,cp,"r")))
+ error(ERROR_system(0),"%s: cannot open",cp);
+ if(fp && sflag)
+ {
+ if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0)
+ error(ERROR_system(0),"write failed");
+ if(fp!=sfstdin)
+ sfclose(fp);
+ }
+ else if(!sflag)
+ streams[n++] = fp;
+ } while(cp= *argv++);
+ if(!sflag)
+ {
+ if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0)
+ error(ERROR_system(0),"write failed");
+ while(--n>=0)
+ if((fp=streams[n]) && fp!=sfstdin)
+ sfclose(fp);
+ }
+ if (mp)
+ free(mp);
+ free(delim);
+ return(error_info.errors);
+}
diff --git a/src/lib/libcmd/pathchk.c b/src/lib/libcmd/pathchk.c
new file mode 100644
index 0000000..0cba4bd
--- /dev/null
+++ b/src/lib/libcmd/pathchk.c
@@ -0,0 +1,265 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * pathchk
+ *
+ * Written by David Korn
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: pathchk (AT&T Research) 2009-07-24 $\n]"
+USAGE_LICENSE
+"[+NAME?pathchk - check pathnames for portability]"
+"[+DESCRIPTION?\bpathchk\b checks each \apathname\a to see if it is "
+ "valid and/or portable. A \apathname\a is valid if it can be used to "
+ "access or create a file without causing syntax errors. A file is "
+ "portable if no truncation will result on any conforming POSIX.1 "
+ "implementation.]"
+"[+?By default \bpathchk\b checks each component of each \apathname\a "
+ "based on the underlying file system. A diagnostic is written to "
+ "standard error for each pathname that:]"
+ "{"
+ "[+-?Is longer than \b$(getconf PATH_MAX)\b bytes.]"
+ "[+-?Contains any component longer than \b$(getconf NAME_MAX)\b "
+ "bytes.]"
+ "[+-?Contains any directory component in a directory that is not "
+ "searchable.]"
+ "[+-?Contains any character in any component that is not valid "
+ "in its containing directory.]"
+ "[+-?Is empty.]"
+ "}"
+"[p:components?Instead of performing length checks on the underlying "
+ "file system, write a diagnostic for each pathname operand that:]"
+ "{"
+ "[+-?Is longer than \b$(getconf _POSIX_PATH_MAX)\b bytes.]"
+ "[+-?Contains any component longer than \b$(getconf "
+ "_POSIX_NAME_MAX)\b bytes.]"
+ "[+-?Contains any character in any component that is not in the "
+ "portable filename character set.]"
+ "}"
+"[P:path?Write a diagnostic for each pathname operand that:]"
+ "{"
+ "[+-?Contains any component with \b-\b as the first character.]"
+ "[+-?Is empty.]"
+ "}"
+"[a:all|portability?Equivalent to \b--components\b \b--path\b.]"
+"\n"
+"\npathname ...\n"
+"\n"
+"[+EXIT STATUS?]"
+ "{"
+ "[+0?All \apathname\a operands passed all of the checks.]"
+ "[+>0?An error occurred.]"
+ "}"
+"[+SEE ALSO?\bgetconf\b(1), \bcreat\b(2), \bpathchk\b(2)]"
+;
+
+
+#include <cmd.h>
+#include <ls.h>
+
+#define COMPONENTS 0x1
+#define PATH 0x2
+
+#define isport(c) (((c)>='a' && (c)<='z') || ((c)>='A' && (c)<='Z') || ((c)>='0' && (c)<='9') || (strchr("._-",(c))!=0) )
+
+/*
+ * call pathconf and handle unlimited sizes
+ */
+static long mypathconf(const char *path, int op)
+{
+ register long r;
+
+ static const char* const ops[] = { "NAME_MAX", "PATH_MAX" };
+
+ errno = 0;
+ if ((r = strtol(astconf(ops[op], path, NiL), NiL, 0)) < 0 && !errno)
+ return LONG_MAX;
+ return r;
+}
+
+/*
+ * returns 1 if <path> passes test
+ */
+static int pathchk(char* path, int mode)
+{
+ register char *cp=path, *cpold;
+ register int c;
+ register long r,name_max,path_max;
+ char buf[2];
+
+ if(!*path)
+ {
+ if (mode & PATH)
+ error(2,"path is empty");
+ return -1;
+ }
+ if(mode & COMPONENTS)
+ {
+ name_max = _POSIX_NAME_MAX;
+ path_max = _POSIX_PATH_MAX;
+ }
+ else
+ {
+ char tmp[2];
+ name_max = path_max = 0;
+ tmp[0] = (*cp=='/'? '/': '.');
+ tmp[1] = 0;
+ if((r=mypathconf(tmp, 0)) > _POSIX_NAME_MAX)
+ name_max = r;
+ if((r=mypathconf(tmp, 1)) > _POSIX_PATH_MAX)
+ path_max = r;
+ if(*cp!='/')
+ {
+ if(name_max==0||path_max==0)
+ {
+ if(!(cpold = getcwd((char*)0, 0)) && errno == EINVAL && (cpold = newof(0, char, PATH_MAX, 0)) && !getcwd(cpold, PATH_MAX))
+ {
+ free(cpold);
+ cpold = 0;
+ }
+ if(cpold)
+ {
+ cp = cpold + strlen(cpold);
+ while(name_max==0 || path_max==0)
+ {
+ if(cp>cpold)
+ while(--cp>cpold && *cp=='/');
+ *++cp = 0;
+ if(name_max==0 && (r=mypathconf(cpold, 0)) > _POSIX_NAME_MAX)
+ name_max = r;
+ if(path_max==0 && (r=mypathconf(cpold, 1)) > _POSIX_PATH_MAX)
+ path_max=r;
+ if(--cp==cpold)
+ {
+ free(cpold);
+ break;
+ }
+ while(*cp!='/')
+ cp--;
+ }
+ cp=path;
+ }
+ }
+ while(*cp=='/')
+ cp++;
+ }
+ if(name_max==0)
+ name_max=_POSIX_NAME_MAX;
+ if(path_max==0)
+ path_max=_POSIX_PATH_MAX;
+ while(*(cpold=cp))
+ {
+ while((c= *cp++) && c!='/');
+ if((cp-cpold) > name_max)
+ goto err;
+ errno=0;
+ cp[-1] = 0;
+ r = mypathconf(path, 0);
+ if((cp[-1]=c)==0)
+ cp--;
+ else while(*cp=='/')
+ cp++;
+ if(r>=0)
+ name_max=(r<_POSIX_NAME_MAX?_POSIX_NAME_MAX:r);
+ else if(errno==EINVAL)
+ continue;
+#ifdef ENAMETOOLONG
+ else if(errno==ENAMETOOLONG)
+ {
+ error(2,"%s: pathname too long",path);
+ return -1;
+ }
+#endif /*ENAMETOOLONG*/
+ else
+ break;
+ }
+ }
+ while(*(cpold=cp))
+ {
+ if((mode & PATH) && *cp == '-')
+ {
+ error(2,"%s: path component begins with '-'",path,fmtquote(buf, NiL, "'", 1, 0));
+ return -1;
+ }
+ while((c= *cp++) && c!='/')
+ if((mode & COMPONENTS) && !isport(c))
+ {
+ buf[0] = c;
+ buf[1] = 0;
+ error(2,"%s: '%s' not in portable character set",path,fmtquote(buf, NiL, "'", 1, 0));
+ return -1;
+ }
+ if((cp-cpold) > name_max)
+ goto err;
+ if(c==0)
+ break;
+ while(*cp=='/')
+ cp++;
+ }
+ if((cp-path) >= path_max)
+ {
+ error(2, "%s: pathname too long", path);
+ return -1;
+ }
+ return 0;
+ err:
+ error(2, "%s: component name %.*s too long", path, cp-cpold-1, cpold);
+ return -1;
+}
+
+int
+b_pathchk(int argc, char** argv, Shbltin_t* context)
+{
+ register int mode = 0;
+ register char* s;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ mode |= COMPONENTS|PATH;
+ continue;
+ case 'p':
+ mode |= COMPONENTS;
+ continue;
+ case 'P':
+ mode |= PATH;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (!*argv || error_info.errors)
+ error(ERROR_usage(2),"%s", optusage(NiL));
+ while (s = *argv++)
+ pathchk(s, mode);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/pids.c b/src/lib/libcmd/pids.c
new file mode 100644
index 0000000..638f3d3
--- /dev/null
+++ b/src/lib/libcmd/pids.c
@@ -0,0 +1,124 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+#define FORMAT "PID=%(pid)d PPID=%(ppid)d PGID=%(pgid)d TID=%(tid)d SID=%(sid)d"
+
+static const char usage[] =
+"[-?\n@(#)$Id: pids (AT&T Research) 2011-08-27 $\n]"
+USAGE_LICENSE
+"[+NAME?pids - list calling shell process ids]"
+"[+DESCRIPTION?When invoked as a shell builtin, \bpids\b lists one or "
+ "more of the calling process ids determined by \bgetpid\b(2), "
+ "\bgetppid\b(2), \bgetpgrp\b(2), \btcgetpgrp\b(2) and \bgetsid\b(2). "
+ "Unknown or invalid ids have the value \b-1\b.]"
+"[f:format?List the ids specified by \aformat\a. \aformat\a follows "
+ "\bprintf\b(3) conventions, except that \bsfio\b(3) inline ids are used "
+ "instead of arguments: "
+ "%[-+]][\awidth\a[.\aprecis\a[.\abase\a]]]]]](\aid\a)\achar\a. The "
+ "supported \aid\as are:]:[format:=" FORMAT "]"
+ "{"
+ "[+pid?The process id.]"
+ "[+pgid?The process group id.]"
+ "[+ppid?The parent process id.]"
+ "[+tid|tty?The controlling terminal id.]"
+ "[+sid?The session id.]"
+ "}"
+"[+SEE ALSO?\bgetpid\b(2), \bgetppid\b(2), \bgetpgrp\b(2), "
+ "\btcgetpgrp\b(2), \bgetsid\b(2)]"
+;
+
+#include <cmd.h>
+#include <ast_tty.h>
+#include <sfdisc.h>
+
+/*
+ * sfkeyprintf() lookup
+ * handle==0 for heading
+ */
+
+static int
+key(void* handle, Sffmt_t* fp, const char* arg, char** ps, Sflong_t* pn)
+{
+ register char* s;
+ int fd;
+ long tid;
+
+ if (!(s = fp->t_str) || streq(s, "pid"))
+ *pn = getpid();
+ else if (streq(s, "pgid"))
+ *pn = getpgrp();
+ else if (streq(s, "ppid"))
+ *pn = getppid();
+ else if (streq(s, "tid") || streq(s, "tty"))
+ {
+ for (fd = 0; fd < 3; fd++)
+ if ((tid = tcgetpgrp(fd)) >= 0)
+ break;
+ *pn = tid;
+ }
+ else if (streq(s, "sid"))
+#if _lib_getsid
+ *pn = getsid(0);
+#else
+ *pn = -1;
+#endif
+ else if (streq(s, "format"))
+ *ps = (char*)handle;
+ else
+ {
+ error(2, "%s: unknown format identifier", s);
+ return 0;
+ }
+ return 1;
+}
+
+int
+b_pids(int argc, char** argv, Shbltin_t* context)
+{
+ char* format = 0;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'f':
+ format = opt_info.arg;
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ break;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || *argv)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ if (!format)
+ format = FORMAT;
+ sfkeyprintf(sfstdout, format, format, key, NiL);
+ sfprintf(sfstdout, "\n");
+ return 0;
+}
diff --git a/src/lib/libcmd/rev.c b/src/lib/libcmd/rev.c
new file mode 100644
index 0000000..9841120
--- /dev/null
+++ b/src/lib/libcmd/rev.c
@@ -0,0 +1,168 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * rev [-l] [file ...]
+ *
+ * reverse the characters or lines of one or more files
+ *
+ * David Korn
+ * AT&T Laboratories
+ * dgk@research.att.com
+ *
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: rev (AT&T Research) 2007-11-29 $\n]"
+USAGE_LICENSE
+"[+NAME?rev - reverse the characters or lines of one or more files]"
+"[+DESCRIPTION?\brev\b copies one or more files to standard output "
+ "reversing the order of characters on every line of the file "
+ "or reversing the order of lines of the file if \b-l\b is specified.]"
+"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \brev\b "
+ "copies from standard input starting at the current offset.]"
+"[l:line?Reverse the lines of the file.]"
+
+"\n"
+"\n[file ...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files copied successfully.]"
+ "[+>0?One or more files did not copy.]"
+"}"
+"[+SEE ALSO?\bcat\b(1), \btail\b(1)]"
+;
+
+#include <cmd.h>
+#include <rev.h>
+
+/*
+ * reverse the characters within a line
+ */
+static int rev_char(Sfio_t *in, Sfio_t *out)
+{
+ register int c;
+ register char *ep, *bp, *cp;
+ register wchar_t *wp, *xp;
+ register size_t n;
+ register size_t w;
+ if (mbwide())
+ {
+ wp = 0;
+ w = 0;
+ while(cp = bp = sfgetr(in,'\n',0))
+ {
+ ep = bp + (n=sfvalue(in)) - 1;
+ if (n > w)
+ {
+ w = roundof(n + 1, 1024);
+ if (!(wp = newof(wp, wchar_t, w, 0)))
+ {
+ error(ERROR_SYSTEM|2, "out of space");
+ return 0;
+ }
+ }
+ xp = wp;
+ while (cp < ep)
+ *xp++ = mbchar(cp);
+ cp = bp;
+ while (xp > wp)
+ cp += mbconv(cp, *--xp);
+ *cp++ = '\n';
+ if (sfwrite(out, bp, cp - bp) < 0)
+ {
+ if (wp)
+ free(wp);
+ return -1;
+ }
+ }
+ if (wp)
+ free(wp);
+ }
+ else
+ while(cp = bp = sfgetr(in,'\n',0))
+ {
+ ep = bp + (n=sfvalue(in)) -1;
+ while(ep > bp)
+ {
+ c = *--ep;
+ *ep = *bp;
+ *bp++ = c;
+ }
+ if(sfwrite(out,cp,n)<0)
+ return(-1);
+ }
+ return(0);
+}
+
+int
+b_rev(int argc, register char** argv, Shbltin_t* context)
+{
+ register Sfio_t *fp;
+ register char *cp;
+ register int n, line=0;
+ NOT_USED(argc);
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'l':
+ line=1;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if(error_info.errors)
+ error(ERROR_usage(2),"%s",optusage((char*)0));
+ n=0;
+ if(cp = *argv)
+ argv++;
+ do
+ {
+ if(!cp || streq(cp,"-"))
+ fp = sfstdin;
+ else if(!(fp = sfopen((Sfio_t*)0,cp,"r")))
+ {
+ error(ERROR_system(0),"%s: cannot open",cp);
+ n=1;
+ continue;
+ }
+ if(line)
+ line = rev_line(fp,sfstdout,sftell(fp));
+ else
+ line = rev_char(fp,sfstdout);
+ if(fp!=sfstdin)
+ sfclose(fp);
+ if(line < 0)
+ error(ERROR_system(1),"write failed");
+ }
+ while(cp= *argv++);
+ return(n);
+}
diff --git a/src/lib/libcmd/rev.h b/src/lib/libcmd/rev.h
new file mode 100644
index 0000000..c71e689
--- /dev/null
+++ b/src/lib/libcmd/rev.h
@@ -0,0 +1,34 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+/*
+ * rev common definitions
+ */
+
+#ifndef _REVLIB_H
+#define _REVLIB_H
+
+#define rev_line _cmd_revline
+
+extern int rev_line(Sfio_t*, Sfio_t*, off_t);
+
+#endif
diff --git a/src/lib/libcmd/revlib.c b/src/lib/libcmd/revlib.c
new file mode 100644
index 0000000..afddbe9
--- /dev/null
+++ b/src/lib/libcmd/revlib.c
@@ -0,0 +1,112 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * common support for tail and rev
+ */
+
+#include <cmd.h>
+#include <rev.h>
+
+#define BUFSIZE SF_BUFSIZE
+#define rounddown(n,size) (((n)-1)&~((size)-1))
+
+/*
+ * copy the lines starting at offset <start> from in <in> to <out>
+ * in reverse order
+ */
+int rev_line(Sfio_t *in, Sfio_t *out, off_t start)
+{
+ register char *cp, *cpold;
+ register int n, nleft=0;
+ char buff[BUFSIZE];
+ off_t offset;
+ if(sfseek(in,(off_t)0,SEEK_CUR) < 0)
+ {
+ Sfio_t *tmp = sftmp(4*SF_BUFSIZE);
+ if(!tmp)
+ return(-1);
+ if(start>0 && sfmove(in, (Sfio_t*)0, start, -1) != start)
+ return(-1);
+ if(sfmove(in, tmp, SF_UNBOUND, -1) < 0 || !sfeof(in) || sferror(tmp))
+ return(-1);
+ in = tmp;
+ start=0;
+ }
+ if((offset = sfseek(in,(off_t)0,SEEK_END)) <= start)
+ return(0);
+ offset = rounddown(offset,BUFSIZE);
+ while(1)
+ {
+ n = BUFSIZE;
+ if(offset < start)
+ {
+ n -= (start-offset);
+ offset = start;
+ }
+ sfseek(in, offset, SEEK_SET);
+ if((n=sfread(in, buff, n)) <=0)
+ break;
+ cp = buff+n;
+ n = *buff;
+ *buff = '\n';
+ while(1)
+ {
+ cpold = cp;
+ if(nleft==0)
+ cp--;
+ if(cp==buff)
+ {
+ nleft= 1;
+ break;
+ }
+ while(*--cp != '\n');
+ if(cp==buff && n!='\n')
+ {
+ *cp = n;
+ nleft += cpold-cp;
+ break;
+ }
+ else
+ cp++;
+ if(sfwrite(out,cp,cpold-cp) < 0)
+ return(-1);
+ if(nleft)
+ {
+ if(nleft==1)
+ sfputc(out,'\n');
+ else if(sfmove(in,out,nleft,-1) != nleft)
+ return(-1);
+ nleft = 0;
+ }
+ }
+ if(offset <= start)
+ break;
+ offset -= BUFSIZE;
+ }
+ if(nleft)
+ {
+ sfseek(in, start, SEEK_SET);
+ if(sfmove(in,out,nleft,-1) != nleft)
+ return(-1);
+ }
+ return(0);
+}
diff --git a/src/lib/libcmd/rm.c b/src/lib/libcmd/rm.c
new file mode 100644
index 0000000..f9abab8
--- /dev/null
+++ b/src/lib/libcmd/rm.c
@@ -0,0 +1,417 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * rm [-fir] [file ...]
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: rm (AT&T Research) 2012-02-14 $\n]"
+USAGE_LICENSE
+"[+NAME?rm - remove files]"
+"[+DESCRIPTION?\brm\b removes the named \afile\a arguments. By default it"
+" does not remove directories. If a file is unwritable, the"
+" standard input is a terminal, and the \b--force\b option is not"
+" given, \brm\b prompts the user for whether to remove the file."
+" An affirmative response (\by\b or \bY\b) removes the file, a quit"
+" response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
+" all other responses skip the current file.]"
+
+"[c|F:clear|clobber?Clear the contents of each file before removing by"
+" writing a 0 filled buffer the same size as the file, executing"
+" \bfsync\b(2) and closing before attempting to remove. Implemented"
+" only on systems that support \bfsync\b(2).]"
+"[d:directory?\bremove\b(3) (or \bunlink\b(2)) directories rather than"
+" \brmdir\b(2), and don't require that they be empty before removal."
+" The caller requires sufficient privilege, not to mention a strong"
+" constitution, to use this option. Even though the directory must"
+" not be empty, \brm\b still attempts to empty it before removal.]"
+"[f:force?Ignore nonexistent files, ignore no file operands specified,"
+" and never prompt the user.]"
+"[i:interactive|prompt?Prompt whether to remove each file."
+" An affirmative response (\by\b or \bY\b) removes the file, a quit"
+" response (\bq\b or \bQ\b) causes \brm\b to exit immediately, and"
+" all other responses skip the current file.]"
+"[r|R:recursive?Remove the contents of directories recursively.]"
+"[u:unconditional?If \b--recursive\b and \b--force\b are also enabled then"
+" the owner read, write and execute modes are enabled (if not already"
+" enabled) for each directory before attempting to remove directory"
+" contents.]"
+"[v:verbose?Print the name of each file before removing it.]"
+
+"\n"
+"\nfile ...\n"
+"\n"
+
+"[+SEE ALSO?\bmv\b(1), \brmdir\b(2), \bunlink\b(2), \bremove\b(3)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+#include <fts_fix.h>
+#include <fs3d.h>
+
+#define RM_ENTRY 1
+
+#define beenhere(f) (((f)->fts_number>>1)==(f)->fts_statp->st_nlink)
+#define isempty(f) (!((f)->fts_number&RM_ENTRY))
+#define nonempty(f) ((f)->fts_parent->fts_number|=RM_ENTRY)
+#define pathchunk(n) roundof(n,1024)
+#define retry(f) ((f)->fts_number=((f)->fts_statp->st_nlink<<1))
+
+typedef struct State_s /* program state */
+{
+ Shbltin_t* context; /* builtin context */
+ int clobber; /* clear out file data first */
+ int directory; /* remove(dir) not rmdir(dir) */
+ int force; /* force actions */
+ int fs3d; /* 3d enabled */
+ int interactive; /* prompt for approval */
+ int recursive; /* remove subtrees too */
+ int terminal; /* attached to terminal */
+ int uid; /* caller uid */
+ int unconditional; /* enable dir rwx on preorder */
+ int verbose; /* display each file */
+#if _lib_fsync
+ char buf[SF_BUFSIZE];/* clobber buffer */
+#endif
+} State_t;
+
+/*
+ * remove a single file
+ */
+
+static int
+rm(State_t* state, register FTSENT* ent)
+{
+ register char* path;
+ register int n;
+ int v;
+ struct stat st;
+
+ if (ent->fts_info == FTS_NS || ent->fts_info == FTS_ERR || ent->fts_info == FTS_SLNONE)
+ {
+ if (!state->force)
+ error(2, "%s: not found", ent->fts_path);
+ }
+ else if (state->fs3d && iview(ent->fts_statp))
+ fts_set(NiL, ent, FTS_SKIP);
+ else switch (ent->fts_info)
+ {
+ case FTS_DNR:
+ case FTS_DNX:
+ if (state->unconditional)
+ {
+ if (!beenhere(ent))
+ break;
+ if (!chmod(ent->fts_name, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU))
+ {
+ fts_set(NiL, ent, FTS_AGAIN);
+ break;
+ }
+ error_info.errors++;
+ }
+ else if (!state->force)
+ error(2, "%s: cannot %s directory", ent->fts_path, (ent->fts_info & FTS_NR) ? "read" : "search");
+ else
+ error_info.errors++;
+ fts_set(NiL, ent, FTS_SKIP);
+ nonempty(ent);
+ break;
+ case FTS_D:
+ case FTS_DC:
+ path = ent->fts_name;
+ if (path[0] == '.' && (!path[1] || path[1] == '.' && !path[2]) && (ent->fts_level > 0 || path[1]))
+ {
+ fts_set(NiL, ent, FTS_SKIP);
+ if (!state->force)
+ error(2, "%s: cannot remove", ent->fts_path);
+ else
+ error_info.errors++;
+ break;
+ }
+ if (!state->recursive)
+ {
+ fts_set(NiL, ent, FTS_SKIP);
+ error(2, "%s: directory", ent->fts_path);
+ break;
+ }
+ if (!beenhere(ent))
+ {
+ if (state->unconditional && (ent->fts_statp->st_mode & S_IRWXU) != S_IRWXU)
+ chmod(path, (ent->fts_statp->st_mode & S_IPERM)|S_IRWXU);
+ if (ent->fts_level > 0)
+ {
+ char* s;
+
+ if (ent->fts_accpath == ent->fts_name || !(s = strrchr(ent->fts_accpath, '/')))
+ v = !stat(".", &st);
+ else
+ {
+ path = ent->fts_accpath;
+ *s = 0;
+ v = !stat(path, &st);
+ *s = '/';
+ }
+ if (v)
+ v = st.st_nlink <= 2 || st.st_ino == ent->fts_parent->fts_statp->st_ino && st.st_dev == ent->fts_parent->fts_statp->st_dev || strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'l');
+ }
+ else
+ v = 1;
+ if (v)
+ {
+ if (state->interactive)
+ {
+ if ((v = astquery(-1, "remove directory %s? ", ent->fts_path)) < 0 || sh_checksig(state->context))
+ return -1;
+ if (v > 0)
+ {
+ fts_set(NiL, ent, FTS_SKIP);
+ nonempty(ent);
+ }
+ }
+ if (ent->fts_info == FTS_D)
+ break;
+ }
+ else
+ {
+ ent->fts_info = FTS_DC;
+ error(1, "%s: hard link to directory", ent->fts_path);
+ }
+ }
+ else if (ent->fts_info == FTS_D)
+ break;
+ /*FALLTHROUGH*/
+ case FTS_DP:
+ if (isempty(ent) || state->directory)
+ {
+ path = ent->fts_name;
+ if (path[0] != '.' || path[1])
+ {
+ path = ent->fts_accpath;
+ if (state->verbose)
+ sfputr(sfstdout, ent->fts_path, '\n');
+ if ((ent->fts_info == FTS_DC || state->directory) ? remove(path) : rmdir(path))
+ switch (errno)
+ {
+ case ENOENT:
+ break;
+ case EEXIST:
+#if defined(ENOTEMPTY) && (ENOTEMPTY) != (EEXIST)
+ case ENOTEMPTY:
+#endif
+ if (ent->fts_info == FTS_DP && !beenhere(ent))
+ {
+ retry(ent);
+ fts_set(NiL, ent, FTS_AGAIN);
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ nonempty(ent);
+ if (!state->force)
+ error(ERROR_SYSTEM|2, "%s: directory not removed", ent->fts_path);
+ else
+ error_info.errors++;
+ break;
+ }
+ }
+ else if (!state->force)
+ error(2, "%s: cannot remove", ent->fts_path);
+ else
+ error_info.errors++;
+ }
+ else
+ {
+ nonempty(ent);
+ if (!state->force)
+ error(2, "%s: directory not removed", ent->fts_path);
+ else
+ error_info.errors++;
+ }
+ break;
+ default:
+ path = ent->fts_accpath;
+ if (state->verbose)
+ sfputr(sfstdout, ent->fts_path, '\n');
+ if (state->interactive)
+ {
+ if ((v = astquery(-1, "remove %s? ", ent->fts_path)) < 0 || sh_checksig(state->context))
+ return -1;
+ if (v > 0)
+ {
+ nonempty(ent);
+ break;
+ }
+ }
+ else if (!(ent->fts_info & FTS_SL) && !state->force && state->terminal && eaccess(path, W_OK))
+ {
+ if ((v = astquery(-1, "override protection %s for %s? ",
+#ifdef ETXTBSY
+ errno == ETXTBSY ? "``running program''" :
+#endif
+ ent->fts_statp->st_uid != state->uid ? "``not owner''" :
+ fmtmode(ent->fts_statp->st_mode & S_IPERM, 0) + 1, ent->fts_path)) < 0 ||
+ sh_checksig(state->context))
+ return -1;
+ if (v > 0)
+ {
+ nonempty(ent);
+ break;
+ }
+ }
+#if _lib_fsync
+ if (state->clobber && S_ISREG(ent->fts_statp->st_mode) && ent->fts_statp->st_size > 0)
+ {
+ if ((n = open(path, O_WRONLY)) < 0)
+ error(ERROR_SYSTEM|2, "%s: cannot clear data", ent->fts_path);
+ else
+ {
+ off_t c = ent->fts_statp->st_size;
+
+ for (;;)
+ {
+ if (write(n, state->buf, sizeof(state->buf)) != sizeof(state->buf))
+ {
+ error(ERROR_SYSTEM|2, "%s: data clear error", ent->fts_path);
+ break;
+ }
+ if (c <= sizeof(state->buf))
+ break;
+ c -= sizeof(state->buf);
+ }
+ fsync(n);
+ close(n);
+ }
+ }
+#endif
+ if (remove(path))
+ {
+ nonempty(ent);
+ switch (errno)
+ {
+ case ENOENT:
+ break;
+ default:
+ if (!state->force || state->interactive)
+ error(ERROR_SYSTEM|2, "%s: not removed", ent->fts_path);
+ else
+ error_info.errors++;
+ break;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+int
+b_rm(int argc, register char** argv, Shbltin_t* context)
+{
+ State_t state;
+ FTS* fts;
+ FTSENT* ent;
+ int set3d;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
+ memset(&state, 0, sizeof(state));
+ state.context = context;
+ state.fs3d = fs3d(FS3D_TEST);
+ state.terminal = isatty(0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'd':
+ state.directory = 1;
+ continue;
+ case 'f':
+ state.force = 1;
+ state.interactive = 0;
+ continue;
+ case 'i':
+ state.interactive = 1;
+ state.force = 0;
+ continue;
+ case 'r':
+ case 'R':
+ state.recursive = 1;
+ continue;
+ case 'F':
+#if _lib_fsync
+ state.clobber = 1;
+#else
+ error(1, "%s not implemented on this system", opt_info.name);
+#endif
+ continue;
+ case 'u':
+ state.unconditional = 1;
+ continue;
+ case 'v':
+ state.verbose = 1;
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ break;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (*argv && streq(*argv, "-") && !streq(*(argv - 1), "--"))
+ argv++;
+ if (error_info.errors || !*argv && !state.force)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ if (!*argv)
+ return 0;
+
+ /*
+ * do it
+ */
+
+ if (state.interactive)
+ state.verbose = 0;
+ state.uid = geteuid();
+ state.unconditional = state.unconditional && state.recursive && state.force;
+ if (state.recursive && state.fs3d)
+ {
+ set3d = state.fs3d;
+ state.fs3d = 0;
+ fs3d(0);
+ }
+ else
+ set3d = 0;
+ if (fts = fts_open(argv, FTS_PHYSICAL, NiL))
+ {
+ while (!sh_checksig(context) && (ent = fts_read(fts)) && !rm(&state, ent));
+ fts_close(fts);
+ }
+ else if (!state.force)
+ error(ERROR_SYSTEM|2, "%s: cannot remove", argv[0]);
+ if (set3d)
+ fs3d(set3d);
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/rmdir.c b/src/lib/libcmd/rmdir.c
new file mode 100644
index 0000000..f03b99b
--- /dev/null
+++ b/src/lib/libcmd/rmdir.c
@@ -0,0 +1,126 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * rmdir
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: rmdir (AT&T Research) 2006-08-24 $\n]"
+USAGE_LICENSE
+"[+NAME?rmdir - remove empty directories]"
+"[+DESCRIPTION?\brmdir\b deletes each given directory. The directory "
+ "must be empty; containing no entries other than \b.\b or \b..\b. "
+ "If a directory and a subdirectory of that directory are specified "
+ "as operands, the subdirectory must be specified before the parent "
+ "so that the parent directory will be empty when \brmdir\b attempts "
+ "to remove it.]"
+"[e:ignore-fail-on-non-empty?Ignore each non-empty directory failure.]"
+"[p:parents?Remove each explicit \adirectory\a argument directory that "
+ "becomes empty after its child directories are removed.]"
+"[s:suppress?Suppress the message printed on the standard error when "
+ "\b-p\b is in effect.]"
+"\n"
+"\ndirectory ...\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All directories deleted successfully.]"
+ "[+>0?One or more directories could not be deleted.]"
+"}"
+"[+SEE ALSO?\bmkdir\b(1), \brm\b(1), \brmdir\b(2), \bunlink\b(2)]"
+;
+
+#include <cmd.h>
+
+int
+b_rmdir(int argc, char** argv, Shbltin_t* context)
+{
+ register char* dir;
+ register char* end;
+ register int n;
+ int eflag = 0;
+ int pflag = 0;
+ int sflag = 0;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'e':
+ eflag = 1;
+ continue;
+ case 'p':
+ pflag = 1;
+ continue;
+ case 's':
+ sflag = 1;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || !*argv)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (!pflag)
+ sflag = 0;
+ while (dir = *argv++)
+ {
+ end = dir;
+ if (pflag) end += strlen(dir);
+ n = 0;
+ for (;;)
+ {
+ if (rmdir(dir) < 0)
+ {
+ if (!eflag || errno != EEXIST
+#ifdef ENOTEMPTY
+ && errno != ENOTEMPTY
+#endif
+ )
+ {
+ if (sflag)
+ error_info.errors++;
+ else
+ error(ERROR_system(0), "%s: cannot remove", dir);
+ }
+ break;
+ }
+ if (n) *end = '/';
+ else n = 1;
+ do if (end <= dir) goto next; while (*--end != '/');
+ do if (end <= dir) goto next; while (*(end - 1) == '/' && end--);
+ *end = 0;
+ }
+ next: ;
+ }
+ return(error_info.errors != 0);
+}
+
diff --git a/src/lib/libcmd/stty.c b/src/lib/libcmd/stty.c
new file mode 100644
index 0000000..9afcd58
--- /dev/null
+++ b/src/lib/libcmd/stty.c
@@ -0,0 +1,971 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * stty.c
+ * Written by David Korn
+ * Tue Apr 4 10:46:00 EDT 1995
+ */
+
+static const char usage[] =
+"[-?@(#)$Id: stty (AT&T Research) 2010-04-01 $\n]"
+USAGE_LICENSE
+"[+NAME?stty - set or get terminal modes]"
+"[+DESCRIPTION?\bstty\b sets certain terminal I/O modes for the device "
+ "that is the current standard input; without arguments, it writes the "
+ "settings of certain modes to standard output.]"
+"[a:all?Writes to standard output all of the mode settings.]"
+"[f|F:fd|file?Use \afd\a as the terminal fd.]#[fd:=0]"
+"[g:save?Writes the current settings to standard output in a form that "
+ "can be used as an argument to another \bstty\b command. The \brows\b "
+ "and \bcolumns\b values are not included.]"
+"[t:terminal-group?Print the terminal group id of the device, -1 if "
+ "unknown.]"
+"\n"
+"\n[mode ...]\n"
+"\n"
+"[+EXTENDED DESCRIPTION?Modes are specified either as a single name or "
+ "as a name followed by a value. As indicated below, many of the mode "
+ "names can be preceded by a \b-\b to negate its meaning. Modes are "
+ "listed by group corresponding to field in the \btermios\b structure "
+ "defined in \b<termios.h>\b. Modes in the last group are implemented "
+ "using options in the previous groups. Note that many combinations of "
+ "modes make no sense, but no sanity checking is performed. The modes are "
+ "selected from the following:]"
+ "{\fabc\f}"
+"[+EXIT STATUS?]"
+ "{"
+ "[+0?All modes reported or set successfully.]"
+ "[+>0?Standard input not a terminaol or one or more modes "
+ "failed.]"
+ "}"
+"[+SEE ALSO?\btegetattr\b(2), \btcsetattr\b(2), \bioctl\b(2)]"
+;
+
+#include <cmd.h>
+#include <ccode.h>
+#include <ctype.h>
+#include <ast_tty.h>
+#if _sys_ioctl
+#include <sys/ioctl.h>
+#endif
+
+#define C(x) ERROR_catalog(x)
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE 0
+#endif
+
+#ifndef NCCS
+# ifdef NCC
+# define NCCS NCC
+# else
+# define NCCS elementsof(((struct termio*)0)->c_cc)
+# endif
+#endif
+
+/* command options */
+#define A_FLAG 1
+#define G_FLAG 2
+#define T_FLAG 4
+
+/* termios fields */
+#define C_FLAG 1
+#define C_LINE 2
+#define C_SPEED 3
+#define I_FLAG 4
+#define O_FLAG 5
+#define L_FLAG 6
+#define T_CHAR 7
+#define W_SIZE 8
+
+#define BIT 1
+#define BITS 2
+#define NUM 3
+#define CHAR 4
+#define SPEED 5
+#define SIZE 6
+#define MIXED 7
+#define SANE 8
+#define COOKED 9
+#define CASE 10
+#define TABS 11
+#define WIND 12
+
+#undef SS /* who co-opted this namespace? */
+
+#define IG 0x0001 /* ignore display */
+#define NL 0x0002 /* entry ends line of display */
+#define SS 0x0004 /* set in sane mode */
+#define US 0x0010 /* unset in sane mode */
+
+typedef struct tty_s
+{
+ const char name[8];
+ unsigned char type;
+ unsigned char field;
+ short flags;
+ unsigned long mask;
+ unsigned long val;
+ const char description[76];
+} Tty_t;
+
+static const Tty_t Ttable[] =
+{
+#ifdef CBAUD
+{ "ispeed", NUM, C_SPEED,0, CBAUD, 0, C("\an\a is the input baud rate") },
+{ "ospeed", NUM, C_SPEED,0, CBAUD, 0, C("\an\a is the output baud rate") },
+{ "speed", NUM, C_SPEED,IG, CBAUD },
+#endif
+{ "0", SPEED, C_FLAG, 0, B0 },
+{ "50", SPEED, C_FLAG, 0, B50 },
+{ "75", SPEED, C_FLAG, 0, B75 },
+{ "110", SPEED, C_FLAG, 0, B110 },
+{ "134", SPEED, C_FLAG, 0, B134 },
+{ "150", SPEED, C_FLAG, 0, B150 },
+{ "200", SPEED, C_FLAG, 0, B200 },
+{ "300", SPEED, C_FLAG, 0, B300 },
+{ "600", SPEED, C_FLAG, 0, B600 },
+{ "1200", SPEED, C_FLAG, 0, B1200 },
+{ "1800", SPEED, C_FLAG, 0, B1800 },
+{ "2400", SPEED, C_FLAG, 0, B2400 },
+{ "4800", SPEED, C_FLAG, 0, B4800 },
+{ "9600", SPEED, C_FLAG, 0, B9600 },
+{ "19200", SPEED, C_FLAG, 0, B19200 },
+{ "38400", SPEED, C_FLAG, 0, B38400 },
+
+#ifdef TIOCSWINSZ
+{ "rows", WIND, W_SIZE, IG, 0, 24, C("\an\a is the number of lines for display") },
+{ "cols", WIND, W_SIZE, IG, 1, 80, C("\an\a is the number of columns for display") },
+{ "columns", WIND, W_SIZE, IG, 1, 80, C("Same as \bcols\b") },
+#endif
+{ "intr", CHAR, T_CHAR, SS, VINTR, 'C', C("Send an interrupt signal") },
+{ "quit", CHAR, T_CHAR, SS, VQUIT, '|', C("Send a quit signal") },
+{ "erase", CHAR, T_CHAR, SS, VERASE, 'H', C("Erase the last character entered") },
+{ "kill", CHAR, T_CHAR, NL|SS, VKILL, 'U', C("Erase the current line") },
+{ "eof", CHAR, T_CHAR, SS, VEOF, 'D', C("Send an end of file") },
+#ifdef VEOL2
+{ "eol2", CHAR, T_CHAR, US, VEOL2, _POSIX_VDISABLE, C("Alternate character to end the line") },
+#endif /* VEOL2 */
+#ifdef VSWTCH
+{ "swtch", CHAR, T_CHAR, US, VSWTCH, _POSIX_VDISABLE, C("Switch to a different shell layer") },
+#endif /* VSWTCH */
+{ "eol", CHAR, T_CHAR, NL|US, VEOL, _POSIX_VDISABLE, C("End the line") },
+#ifdef VSTART
+{ "start", CHAR, T_CHAR, SS, VSTART, 'Q', C("Restart the output after stopping it") },
+#endif /* VSTART */
+#ifdef VSTOP
+{ "stop", CHAR, T_CHAR, SS, VSTOP, 'S', C("Stop the output") },
+#endif /* VSTOP */
+#ifdef VDSUSP
+{ "dsusp", CHAR, T_CHAR, SS, VDSUSP, 'Y', C("Send a terminal stop signal after flushing the input") },
+#endif /* VDSUSP */
+#ifdef VSUSP
+{ "susp", CHAR, T_CHAR, NL|SS, VSUSP, 'Z', C("Send a terminal stop signal") },
+#endif /* VSUSP */
+#ifdef VREPRINT
+{ "rprnt", CHAR, T_CHAR, SS, VREPRINT, 'R', C("Redraw the current line") },
+#endif /* VREPRINT */
+#ifdef VDISCARD
+{ "flush", CHAR, T_CHAR, SS, VDISCARD, 'O', C("Discard output") },
+#endif /* VDISCARD */
+#ifdef VWERASE
+{ "werase", CHAR, T_CHAR, SS, VWERASE, 'W', C("Erase the last word entered") },
+#endif /* VWERASE */
+#ifdef VLNEXT
+{ "lnext", CHAR, T_CHAR, NL|SS, VLNEXT, 'V', C("Enter the next input character literally") },
+#endif /* VLNEXT */
+
+#if _mem_c_line_termios
+{ "line", NUM, C_LINE, 0, 0, 0, C("Line discipline number") },
+#endif
+{ "min", NUM, T_CHAR, 0, VMIN, 0, C("Mininmum number of characters to read in raw mode") },
+{ "time", NUM, T_CHAR, 0, VTIME, 0, C("Number of .1 second intervals with raw mode") },
+
+{ "parenb", BIT, C_FLAG, 0, PARENB, PARENB, C("Enable (disable) parity generation and detection") },
+{ "parodd", BIT, C_FLAG, 0, PARODD, PARODD, C("Use odd (even) parity") },
+#ifdef PAREXT
+{ "parext", BIT, C_FLAG, 0, PAREXT, PAREXT },
+#endif /* PAREXT */
+#ifdef CREAD
+{ "cread", BIT, C_FLAG, SS, CREAD, CREAD, C("Enable (disable) input") },
+#endif /* CREAD */
+{ "cs5", SIZE, C_FLAG, 0, CSIZE, CS5 , C("Char size 5") },
+{ "cs6", SIZE, C_FLAG, 0, CSIZE, CS6 , C("Char size 6") },
+{ "cs7", SIZE, C_FLAG, 0, CSIZE, CS7 , C("Char size 7") },
+{ "cs8", SIZE, C_FLAG, 0, CSIZE, CS8 , C("Char size 8") },
+{ "hupcl", BIT, C_FLAG, 0, HUPCL, HUPCL, C("Hangup (do not hangup) connection on last close") },
+{ "hup", BIT, C_FLAG, IG, HUPCL, HUPCL, C("Same as \bhupcl\b") },
+{ "cstopb", BIT, C_FLAG, 0, CSTOPB, CSTOPB, C("Use two (one) stop bits") },
+#ifdef CRTSCTS
+{ "crtscts", BIT, C_FLAG, 0, CRTSCTS, CRTSCTS, C("Enable (disable) RTS/CTS handshaking") },
+#endif /* CRTSCTS */
+{ "clocal", BIT, C_FLAG, NL, CLOCAL, CLOCAL, C("Disable (enable) modem control signals") },
+
+{ "ignbrk", BIT, I_FLAG, US, IGNBRK, IGNBRK, C("Ignore (do not ignore) break characters") },
+{ "brkint", BIT, I_FLAG, SS, BRKINT, BRKINT, C("Generate (do not generate) INTR signal on break") },
+{ "ignpar", BIT, I_FLAG, 0, IGNPAR, IGNPAR, C("Ignore (do not ignore) characters with parity errors") },
+{ "parmrk", BIT, I_FLAG, 0, PARMRK, PARMRK, C("Mark (do not mark) parity errors") },
+{ "inpck", BIT, I_FLAG, 0, INPCK, INPCK, C("Enable (disable) input parity checking") },
+{ "istrip", BIT, I_FLAG, 0, ISTRIP, ISTRIP, C("Clear (do not clear) high bit of input characters") },
+{ "inlcr", BIT, I_FLAG, US, INLCR, INLCR, C("Translate (do not translate) carriage return to newline") },
+{ "igncr", BIT, I_FLAG, US, IGNCR, IGNCR, C("Ignore (do not ignore) carriage return") },
+#ifdef IUCLC
+{ "iuclc", BIT, I_FLAG, US, IUCLC, IUCLC, C("Map (do not map) upper-case to lower case") },
+#endif /* IUCLC */
+{ "ixon", BIT, I_FLAG, 0, IXON, IXON, C("Enable (disable) XON/XOFF flow control. \bstop\b character stops output") },
+#ifdef IXANY
+{ "ixany", BIT, I_FLAG, US, IXANY, IXANY, C("Any character (only start character) can restart output.") },
+{ "decctlq", BIT, I_FLAG, IG, IXANY, 0, C("Same as \b-ixany\b") },
+#endif /* IXANY */
+{ "ixoff", BIT, I_FLAG, US, IXOFF, IXOFF, C("Disable (enable) XON/XOFF flow control") },
+#ifdef IMAXBEL
+{ "imaxbel", BIT, I_FLAG, SS, IMAXBEL, IMAXBEL, C("Beep (do not beep) if a character arrives with full input buffer") },
+#endif /* IMAXBEL */
+{ "icrnl", BIT, I_FLAG, NL|SS, ICRNL, ICRNL, C("Translate (do not translate) carriage return to newline") },
+
+{ "isig", BIT, L_FLAG, SS, ISIG, ISIG, C("Enable (disable) \bintr\b, \bquit\b, and \bsusp\b special characters") },
+{ "icanon", BIT, L_FLAG, SS, ICANON, ICANON, C("Enable (disable) \berase\b, \bkill\b, \bwerase\b, and \brprnt\b special characters") },
+{ "icannon", BIT, L_FLAG, SS, ICANON, ICANON },
+#ifdef IEXTEN
+{ "iexten", BIT, L_FLAG, SS, IEXTEN, IEXTEN, C("Enable (disable) non-POSIX special characters") },
+#endif /* IEXTEN */
+{ "echo", BIT, L_FLAG, SS, ECHO|ECHONL, ECHO|ECHONL, C("Echo (do not echo) input characters") },
+{ "echoe", BIT, L_FLAG, SS, ECHOE, ECHOE, C("Echo (do not echo) erase characters as backspace-space-backspace") },
+{ "echok", BIT, L_FLAG, SS, ECHOK, ECHOK, C("Echo (do not echo) a newline after a kill character") },
+#ifdef ECHOKE
+{ "echoke", BIT, L_FLAG, SS, ECHOKE, ECHOKE, C("Echo (do not echo) a newline after a kill character") },
+#endif
+{ "lfkc", BIT, L_FLAG, IG, ECHOK, ECHOK, C("Same as \bechok\b (\b-echok\b); obsolete") },
+{ "echonl", BIT, L_FLAG, SS, ECHONL, ECHONL,"Echo (do not echo) newline even if not echoing other character" },
+#ifdef ECHOCTL
+{ "echoctl", BIT, L_FLAG, SS, ECHOCTL, ECHOCTL, C("Echo (do not echo) control characters as \b^\b\ac\a") },
+#else
+#define ECHOCTL 0
+#endif /* ECHOCTL */
+#ifdef ECHOPRT
+{ "echoprt", BIT, L_FLAG, US, ECHOPRT, ECHOPRT, C("Echo (do not echo) erased characters backward, between '\\' and '/'") },
+#else
+#define ECHOPRT 0
+#endif /* ECHOPRT */
+#ifdef XCASE
+{ "xcase", BIT, L_FLAG, US, XCASE, XCASE, C("Enable (disable) \bicanon\b uppercase as lowercase with '\\' prefix") },
+#endif /* XCASE */
+#ifdef DEFECHO
+{ "defecho", BIT, L_FLAG, 0, DEFECHO, DEFECHO },
+#endif /* DEFECHO */
+#ifdef FLUSHO
+{ "flusho", BIT, L_FLAG, 0, FLUSHO, FLUSHO, C("Discard (do not discard) written data. Cleared by subsequent input") },
+#endif /* FLUSHO */
+#ifdef PENDIN
+{ "pendin", BIT, L_FLAG, 0, PENDIN, PENDIN, C("Redisplay pending input at next read and then automatically clear \bpendin\b") },
+#endif /* PENDIN */
+{ "noflsh", BIT, L_FLAG, US, NOFLSH, NOFLSH, C("Disable (enable) flushing after \bintr\b and \bquit\b special characters") },
+#ifdef TOSTOP
+{ "tostop", BIT, L_FLAG, NL|US, TOSTOP, TOSTOP, C("Stop (do not stop) background jobs that try to write to the terminal") },
+#endif /* TOSTOP */
+#ifdef OLCUC
+{ "olcuc", BIT, O_FLAG, US, OLCUC, OLCUC, C("Translate (do not translate) lowercase characters to uppercase") },
+#endif /* OLCUC */
+#ifdef ONLCR
+{ "onlcr", BIT, O_FLAG, SS, ONLCR, ONLCR, C("Translate (do not translate) newline to carriage return-newline") },
+#endif /* ONLCR */
+#ifdef ONLRET
+{ "onlret", BIT, O_FLAG, US, ONLRET, ONLRET, C("Newline performs (does not perform) a carriage return") },
+#endif /* ONLRET */
+#ifdef OCRNL
+{ "ocrnl", BIT, O_FLAG, US, OCRNL, OCRNL, C("Translate (do not translate) carriage return to newline") },
+#endif /* OCRNL */
+#ifdef ONOCR
+{ "onocr", BIT, O_FLAG, US, ONOCR, ONOCR, C("Do not (do) print carriage returns in the first column") },
+#endif /* ONOCR */
+#ifdef OFILL
+{ "ofill", BIT, O_FLAG, US, OFILL, OFILL, C("Use fill characters (use timing) for delays") },
+#endif /* OFILL */
+#ifdef OFDEL
+{ "ofdel", BIT, O_FLAG, US, OFDEL, OFDEL, C("Use DEL (NUL) as fill characters for delays") },
+#endif /* OFDEL */
+{ "opost", BIT, O_FLAG, SS, OPOST, OPOST, C(" Postprocess (do not postprocess) output") },
+#ifdef CRDLY
+{ "cr0", BITS, O_FLAG, IG|SS, CRDLY, CR0 },
+{ "cr1", BITS, O_FLAG, US, CRDLY, CR1 },
+{ "cr2", BITS, O_FLAG, US, CRDLY, CR2 },
+{ "cr3", BITS, O_FLAG, US, CRDLY, CR3 },
+#endif
+#ifdef NLDLY
+{ "nl0", BITS, O_FLAG, IG|US, NLDLY, NL0 },
+{ "nl1", BITS, O_FLAG, US, NLDLY, NL1 },
+#endif
+#ifdef TABDLY
+{ "tabs", TABS, O_FLAG, IG, TABDLY, TAB3, C("Preserve (expand to spaces) tabs") },
+#ifdef TAB0
+{ "tab0", BITS, O_FLAG, IG|SS, TABDLY, TAB0 },
+#endif
+#ifdef TAB1
+{ "tab1", BITS, O_FLAG, US, TABDLY, TAB1 },
+#endif
+#ifdef TAB2
+{ "tab2", BITS, O_FLAG, US, TABDLY, TAB2 },
+#endif
+{ "tab3", BITS, O_FLAG, US, TABDLY, TAB3 },
+#endif
+#ifdef BSDLY
+{ "bs0", BITS, O_FLAG, IG|SS, BSDLY, BS0 },
+{ "bs1", BITS, O_FLAG, US, BSDLY, BS1 },
+#endif
+#ifdef VTDLY
+{ "vt0", BITS, O_FLAG, IG|SS, VTDLY, VT0 },
+{ "vt1", BITS, O_FLAG, US, VTDLY, VT1 },
+#endif
+#ifdef FFDLY
+{ "ff0", BITS, O_FLAG, IG|SS, FFDLY, FF0 },
+{ "ff1", BITS, O_FLAG, US, FFDLY, FF1 },
+#endif
+{ "", MIXED, O_FLAG, NL|IG },
+
+{ "evenp", MIXED, C_FLAG, IG, PARENB, 0, C("Same as \bparenb -parodd cs7\b") },
+{ "oddp", MIXED, C_FLAG, IG, PARODD, 0, C("Same as \bparenb parodd cs7\b") },
+{ "parity", MIXED, C_FLAG, IG, 0, 0, C("Same as parenb \b-parodd cs7\b") },
+{ "ek", MIXED, C_FLAG, IG, 0, 0, C("Reset the \berase\b and \bkill\b special characters to their default values") },
+{ "sane", SANE, C_FLAG, IG, 0, 0, C("Reset all modes to some reasonable values") },
+{ "cooked", COOKED, C_FLAG, IG, 0, 0, C("Disable raw input and output") },
+{ "raw", COOKED, C_FLAG, IG, 0, 0, C("Enable raw input and output") },
+{ "lcase", CASE, C_FLAG, IG, 0 , 0, C("Set \bxcase\b, \biuclc\b, and \bolcuc\b") },
+{ "LCASE", CASE, C_FLAG, IG, 0 , 0, C("Same as \blcase\b") }
+};
+
+#if CC_NATIVE == CC_ASCII
+#define cntl(x) (((x)=='?')?0177:((x)&037))
+#else
+#define cntl(x) (((x)=='?')?ccmapc(0177,CC_ASCII,CC_NATIVE):ccmapc(ccmapc(x,CC_NATIVE,CC_ASCII)&037,CC_ASCII,CC_NATIVE))
+#endif
+
+static void sane(register struct termios *sp)
+{
+ register const Tty_t* tp;
+
+ for (tp = Ttable; tp < &Ttable[elementsof(Ttable)]; tp++)
+ if (tp->flags & (SS|US))
+ switch (tp->type)
+ {
+ case BIT:
+ case BITS:
+ switch (tp->field)
+ {
+ case C_FLAG:
+ if (tp->flags & SS)
+ sp->c_cflag |= tp->mask;
+ else
+ sp->c_cflag &= ~tp->mask;
+ break;
+ case I_FLAG:
+ if (tp->flags & SS)
+ sp->c_iflag |= tp->mask;
+ else
+ sp->c_iflag &= ~tp->mask;
+ break;
+ case O_FLAG:
+ if (tp->flags & SS)
+ sp->c_oflag |= tp->mask;
+ else
+ sp->c_oflag &= ~tp->mask;
+ break;
+ case L_FLAG:
+ if (tp->flags & SS)
+ sp->c_lflag |= tp->mask;
+ else
+ sp->c_lflag &= ~tp->mask;
+ break;
+ }
+ break;
+ case CHAR:
+ sp->c_cc[tp->mask] = cntl(tp->val);
+ break;
+ }
+}
+
+static int gin(char *arg,struct termios *sp)
+{
+ register int i;
+ if(*arg++ != ':')
+ return(0);
+ sp->c_iflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ sp->c_oflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ sp->c_cflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ sp->c_lflag = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ for(i=0;i< NCCS; i++)
+ {
+ sp->c_cc[i] = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ }
+#if _mem_c_line_termios
+ sp->c_line =
+#endif
+ strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ i = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ cfsetispeed(sp, i);
+ i = strtol(arg,&arg,16);
+ if(*arg++ != ':')
+ return(0);
+ cfsetospeed(sp, i);
+ if(*arg)
+ return(0);
+ return(1);
+}
+
+static void gout(struct termios *sp)
+{
+ register int i;
+ sfprintf(sfstdout,":%x",sp->c_iflag);
+ sfprintf(sfstdout,":%x",sp->c_oflag);
+ sfprintf(sfstdout,":%x",sp->c_cflag);
+ sfprintf(sfstdout,":%x",sp->c_lflag);
+ for(i=0;i< NCCS; i++)
+ sfprintf(sfstdout,":%x",sp->c_cc[i]);
+#if _mem_c_line_termios
+ sfprintf(sfstdout,":%x", sp->c_line);
+#else
+ sfprintf(sfstdout,":%x", 0);
+#endif
+ sfprintf(sfstdout,":%x",cfgetispeed(sp));
+ sfprintf(sfstdout,":%x",cfgetospeed(sp));
+ sfprintf(sfstdout,":\n");
+}
+
+static void output(struct termios *sp, int flags)
+{
+ const Tty_t *tp;
+ struct termios tty;
+ register int delim = ' ';
+ register int i,off,off2;
+ char schar[2];
+ unsigned int ispeed = cfgetispeed(sp);
+ unsigned int ospeed = cfgetospeed(sp);
+ if(flags&G_FLAG)
+ {
+ gout(sp);
+ return;
+ }
+ tty = *sp;
+ sane(&tty);
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ tp= &Ttable[i];
+ if(tp->flags&IG)
+ {
+ if(tp->flags&NL)
+ sfputc(sfstdout,'\n');
+ continue;
+ }
+ switch(tp->type)
+ {
+ case BIT:
+ case BITS:
+ off = off2 = 1;
+ switch(tp->field)
+ {
+ case C_FLAG:
+ if(sp->c_cflag&tp->mask)
+ off = 0;
+ if(tty.c_cflag&tp->mask)
+ off2 = 0;
+ break;
+ case I_FLAG:
+ if(sp->c_iflag&tp->mask)
+ off = 0;
+ if(tty.c_iflag&tp->mask)
+ off2 = 0;
+ break;
+ case O_FLAG:
+ if((sp->c_oflag&tp->mask)==tp->val)
+ off = 0;
+ if(tty.c_oflag&tp->mask)
+ off2 = 0;
+ break;
+ case L_FLAG:
+ if(sp->c_lflag&tp->mask)
+ off = 0;
+ if(tty.c_lflag&tp->mask)
+ off2 = 0;
+ }
+ if(tp->flags&NL)
+ delim = '\n';
+ if(!flags && off==off2)
+ continue;
+ if(!off)
+ sfprintf(sfstdout,"%s%c",tp->name,delim);
+ else if(tp->type==BIT)
+ sfprintf(sfstdout,"-%s%c",tp->name,delim);
+ delim = ' ';
+ break;
+
+ case CHAR:
+ off = sp->c_cc[tp->mask];
+ if(tp->flags&NL)
+ delim = '\n';
+ if(!flags && off==(unsigned char)tty.c_cc[tp->mask])
+ continue;
+ if(off==_POSIX_VDISABLE)
+ sfprintf(sfstdout,"%s = <undef>;%c",tp->name,delim);
+ else if(isprint(off&0xff))
+ sfprintf(sfstdout,"%s = %c;%c",tp->name,off,delim);
+ else
+#if CC_NATIVE == CC_ASCII
+ sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':(off^0100),delim);
+#else
+ {
+ off = ccmapc(off, CC_NATIVE, CC_ASCII);
+ sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':ccmapc(off^0100,CC_ASCII,CC_NATIVE),delim);
+ }
+#endif
+ delim = ' ';
+ break;
+ case SIZE:
+ if((sp->c_cflag&CSIZE)!=tp->mask)
+ continue;
+ if(flags || (sp->c_cflag&CSIZE) != (tty.c_cflag&CSIZE))
+ sfprintf(sfstdout,"%s ",tp->name);
+ break;
+ case SPEED:
+ if(tp->mask==ispeed)
+ {
+ if(ispeed!=ospeed)
+ schar[0]='i';
+ else
+ schar[0]=0;
+ }
+ else if(tp->mask==ospeed)
+ schar[0]='o';
+ else
+ continue;
+ schar[1] = 0;
+#ifdef TIOCSWINSZ
+ {
+ struct winsize win;
+ off = ioctl(0,TIOCGWINSZ,&win);
+ if(off>=0)
+ sfprintf(sfstdout,"%sspeed %s baud; rows %d; columns %d;\n",schar,tp->name,win.ws_row,win.ws_col);
+ }
+ if(off<0)
+#endif
+ sfprintf(sfstdout,"%sspeed %s baud;\n",schar,tp->name);
+ }
+ }
+ if(delim=='\n')
+ sfputc(sfstdout,'\n');
+}
+
+static const Tty_t *lookup(const char *name)
+{
+ register int i;
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(strcmp(Ttable[i].name,name)==0)
+ return(&Ttable[i]);
+ }
+ return(0);
+
+}
+
+static const Tty_t *getspeed(unsigned long val)
+{
+ register int i;
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].type==SPEED && Ttable[i].mask==val)
+ return(&Ttable[i]);
+ }
+ return(0);
+}
+
+static int gettchar(register const char *cp)
+{
+ if(*cp==0)
+ return(-1);
+ if(cp[1]==0)
+ return((unsigned)cp[0]);
+ if(*cp=='^' && cp[1] && cp[2]==0)
+ {
+ switch(cp[1])
+ {
+ case '-':
+ return(-1);
+ default:
+ return(cntl(cp[1]));
+ }
+ }
+ if(streq(cp,"undef") || streq(cp,"<undef>"))
+ return(-1);
+ return(*((unsigned char*)cp));
+}
+
+static void set(char *argv[], struct termios *sp)
+{
+ const Tty_t *tp;
+ register int c,off;
+ char *cp;
+ char *ep;
+ while(cp = *argv++)
+ {
+ off = 0;
+ if(*cp=='-')
+ {
+ cp++;
+ off=1;
+ }
+ if(!(tp=lookup(cp)) || (off && (tp->type!=BIT) && (tp->type!=TABS)))
+ error(ERROR_exit(1),"%s: unknown mode",cp);
+ switch(tp->type)
+ {
+ case CHAR:
+ if(off)
+ error(ERROR_exit(1),"%s: unknown mode",cp);
+ if(!*argv)
+ error(ERROR_exit(1),"missing argument to %s",cp);
+ c = gettchar(*argv++);
+ if(c>=0)
+ sp->c_cc[tp->mask] = c;
+ else
+ sp->c_cc[tp->mask] = _POSIX_VDISABLE;
+ break;
+ case BIT: case BITS:
+ switch(tp->field)
+ {
+ case C_FLAG:
+ if(off)
+ sp->c_cflag &= ~tp->mask;
+ else
+ sp->c_cflag |= tp->mask;
+ break;
+ case I_FLAG:
+ if(off)
+ sp->c_iflag &= ~tp->mask;
+ else
+ sp->c_iflag |= tp->mask;
+ break;
+ case O_FLAG:
+ sp->c_oflag &= ~tp->mask;
+ sp->c_oflag |= tp->val;
+ break;
+ case L_FLAG:
+ if(off)
+ sp->c_lflag &= ~tp->mask;
+ else
+ sp->c_lflag |= tp->mask;
+ break;
+ }
+ break;
+ case TABS:
+ sp->c_oflag &= ~tp->mask;
+ if(off)
+ sp->c_oflag |= tp->val;
+ break;
+#ifdef TIOCSWINSZ
+ case WIND:
+ {
+ struct winsize win;
+ int n;
+ if(ioctl(0,TIOCGWINSZ,&win)<0)
+ error(ERROR_system(1),"cannot set %s",tp->name);
+ if(!(cp= *argv))
+ {
+ sfprintf(sfstdout,"%d\n",tp->mask?win.ws_col:win.ws_row);
+ break;
+ }
+ argv++;
+ n=strtol(cp,&cp,10);
+ if(*cp)
+ error(ERROR_system(1),"%d: invalid number of %s",argv[-1],tp->name);
+ if(tp->mask)
+ win.ws_col = n;
+ else
+ win.ws_row = n;
+ if(ioctl(0,TIOCSWINSZ,&win)<0)
+ error(ERROR_system(1),"cannot set %s",tp->name);
+ break;
+ }
+#endif
+ case NUM:
+ cp = *argv;
+ if (!cp)
+ {
+ if (tp->field == C_SPEED)
+ {
+ if (tp = getspeed(*tp->name == 'i' ? cfgetispeed(sp) : cfgetospeed(sp)))
+ sfprintf(sfstdout, "%s\n", tp->name);
+ break;
+ }
+ error(ERROR_exit(1), "%s: missing numeric argument", tp->name);
+ }
+ argv++;
+ c = (int)strtol(cp, &ep, 10);
+ if (*ep)
+ error(ERROR_exit(1), "%s: %s: numeric argument expected", tp->name, cp);
+ switch (tp->field)
+ {
+#if _mem_c_line_termios
+ case C_LINE:
+ sp->c_line = c;
+ break;
+#endif
+ case C_SPEED:
+ if(getspeed(c))
+ {
+ if (*tp->name != 'o')
+ cfsetispeed(sp, c);
+ if (*tp->name != 'i')
+ cfsetospeed(sp, c);
+ }
+ else
+ error(ERROR_exit(1), "%s: %s: invalid speed", tp->name, cp);
+ break;
+ case T_CHAR:
+ sp->c_cc[tp->mask] = c;
+ break;
+ }
+ break;
+ case SPEED:
+ cfsetospeed(sp, tp->mask);
+ cfsetispeed(sp, tp->mask);
+ break;
+ case SIZE:
+ sp->c_cflag &= ~CSIZE;
+ sp->c_cflag |= tp->mask;
+ break;
+ case SANE:
+ sane(sp);
+ break;
+#if defined(OLCUC) && defined(IUCLC)
+ case CASE:
+ if(off)
+ {
+ sp->c_iflag |= IUCLC;
+ sp->c_oflag |= OLCUC;
+ }
+ else
+ {
+ sp->c_iflag &= ~IUCLC;
+ sp->c_oflag &= ~OLCUC;
+ }
+ break;
+#endif /* OLCUC && IUCLC */
+ }
+ }
+}
+
+
+static void listchars(Sfio_t *sp,int type)
+{
+ int i,c;
+ c = (type==CHAR?'c':'n');
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].type==type && *Ttable[i].description)
+ sfprintf(sp,"[+%s \a%c\a?%s.]",Ttable[i].name,c,Ttable[i].description);
+ }
+}
+
+static void listgroup(Sfio_t *sp,int type, const char *description)
+{
+ int i;
+ sfprintf(sp,"[+");
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].type==type)
+ sfprintf(sp,"%s ",Ttable[i].name);
+ }
+ sfprintf(sp,"?%s.]",description);
+}
+
+static void listmask(Sfio_t *sp,unsigned int mask,const char *description)
+{
+ int i;
+ sfprintf(sp,"[+");
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].mask==mask && Ttable[i].type==BITS)
+ sfprintf(sp,"%s ",Ttable[i].name);
+ }
+ sfprintf(sp,"?%s.]",description);
+}
+
+static void listfields(Sfio_t *sp,int field)
+{
+ int i;
+ for(i=0; i < elementsof(Ttable); i++)
+ {
+ if(Ttable[i].field==field && Ttable[i].type==BIT && *Ttable[i].description)
+ sfprintf(sp,"[+%s (-%s)?%s.]",Ttable[i].name,Ttable[i].name,Ttable[i].description);
+ }
+}
+
+static void listmode(Sfio_t *sp,const char *name)
+{
+ sfprintf(sp,"[+%s?%s.]",name,lookup(name)->description);
+}
+
+static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
+{
+ NoP(op);
+ NoP(s);
+ NoP(dp);
+ sfprintf(sp,"[+Control Modes.]{");
+ listfields(sp,C_FLAG);
+ listgroup(sp,SPEED,"Attempt to set input and output baud rate to number given. A value of \b0\b causes immediate hangup");
+ listchars(sp,NUM);
+ listgroup(sp,SIZE,"Number of bits in a character");
+ sfprintf(sp,"}[+Input Modes.]{");
+ listfields(sp,I_FLAG);
+ sfprintf(sp,"}[+Output Modes.]{");
+ listfields(sp,O_FLAG);
+#ifdef CRDLY
+ listmask(sp,CRDLY,"Carriage return delay style");
+#endif
+#ifdef NLDLY
+ listmask(sp,NLDLY,"Newline delay style");
+#endif
+#ifdef TABDLY
+ listmask(sp,TABDLY,"Horizontal tab delay style");
+#endif
+#ifdef BSDLY
+ listmask(sp,BSDLY,"Backspace delay style");
+#endif
+#ifdef FFDLY
+ listmask(sp,FFDLY,"Form feed delay style");
+#endif
+#ifdef VTDLY
+ listmask(sp,VTDLY,"Vertical tab delay style");
+#endif
+ sfprintf(sp,"}[+Local Modes.]{");
+ listfields(sp,L_FLAG);
+ sfprintf(sp,"}[+Control Assignments.?If \ac\a is \bundef\b or an empty "
+ "string then the control assignment is disabled.]{");
+ listchars(sp,WIND);
+ listchars(sp,CHAR);
+ sfprintf(sp,"}[+Combination Modes.]{");
+ listmode(sp,"ek");
+ listmode(sp,"evenp");
+ listmode(sp,"lcase");
+ listmode(sp,"oddp");
+ listmode(sp,"parity");
+ listmode(sp,"sane");
+ listmode(sp,"tabs");
+ listmode(sp,"LCASE");
+ sfputc(sp,'}');
+ return(1);
+}
+
+#ifndef _lib_tcgetpgrp
+# ifdef TIOCGPGRP
+ static int _i_;
+# define tcgetpgrp(a) (ioctl(a, TIOCGPGRP, &_i_)>=0?_i_:-1)
+# else
+# define tcgetpgrp(a) (-1)
+# endif /* TIOCGPGRP */
+#endif /* _lib_tcgetpgrp */
+
+int
+b_stty(int argc, char** argv, Shbltin_t* context)
+{
+ struct termios tty;
+ register int n;
+ register int flags = 0;
+ int fd = 0;
+ const Tty_t* tp;
+ Optdisc_t disc;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_INTERACTIVE);
+ memset(&disc, 0, sizeof(disc));
+ disc.version = OPT_VERSION;
+ disc.infof = infof;
+ opt_info.disc = &disc;
+ for (;;)
+ {
+ switch (n = optget(argv, usage))
+ {
+ case 'f':
+ fd = (int)opt_info.num;
+ continue;
+ case 'a':
+ case 'g':
+ case 't':
+ if (!opt_info.offset || !argv[opt_info.index][opt_info.offset])
+ {
+ switch (n)
+ {
+ case 'a':
+ flags |= A_FLAG;
+ break;
+ case 'g':
+ flags |= G_FLAG;
+ break;
+ case 't':
+ flags |= T_FLAG;
+ break;
+ }
+ continue;
+ }
+ /*FALLTHROUGH*/
+ case ':':
+ if (!opt_info.offset)
+ error(2, "%s", opt_info.arg);
+ else if (!(tp = lookup(argv[opt_info.index]+1)) || (tp->type != BIT && tp->type != TABS))
+ error(ERROR_exit(1), "%s: unknown mode", argv[opt_info.index]);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || (flags && *argv) || (flags&(flags-1)))
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (tcgetattr(fd, &tty) < 0)
+ error(ERROR_system(1), "not a tty");
+ if (flags & T_FLAG)
+ sfprintf(sfstdout, "%d\n", tcgetpgrp(0));
+ else if (*argv)
+ {
+ if (!argv[1] && **argv == ':')
+ gin(*argv, &tty);
+ else
+ set(argv, &tty);
+ if (tcsetattr(0, TCSANOW, &tty) < 0)
+ error(ERROR_system(1), "cannot set tty");
+ }
+ else
+ output(&tty, flags);
+ return error_info.errors;
+}
diff --git a/src/lib/libcmd/sum.c b/src/lib/libcmd/sum.c
new file mode 100644
index 0000000..64c6c9e
--- /dev/null
+++ b/src/lib/libcmd/sum.c
@@ -0,0 +1,35 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * sum -- implemented by cksum
+ */
+
+#include <cmd.h>
+
+int
+b_sum(int argc, register char** argv, Shbltin_t* context)
+{
+ return b_cksum(argc, argv, context);
+}
diff --git a/src/lib/libcmd/sync.c b/src/lib/libcmd/sync.c
new file mode 100644
index 0000000..0531725
--- /dev/null
+++ b/src/lib/libcmd/sync.c
@@ -0,0 +1,79 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: sync (AT&T Research) 2006-10-04 $\n]"
+USAGE_LICENSE
+"[+NAME?sync - schedule file system updates]"
+"[+DESCRIPTION?\bsync\b calls \bsync\b(2), which causes all information "
+ "in memory that updates file systems to be scheduled for writing out to "
+ "all file systems. The writing, although scheduled, is not necessarily "
+ "complete upon return from \bsync\b.]"
+"[+?Since \bsync\b(2) has no failure indication, \bsync\b only fails for "
+ "option/operand syntax errors, or when \bsync\b(2) does not return, in "
+ "which case \bsync\b also does not return.]"
+"[+?At minimum \bsync\b should be called before halting the system. Most "
+ "systems provide graceful shutdown procedures that include \bsync\b -- "
+ "use them if possible.]"
+"[+EXIT STATUS?]"
+ "{"
+ "[+0?\bsync\b(2) returned.]"
+ "[+>0?Option/operand syntax error.]"
+ "}"
+"[+SEE ALSO?\bsync\b(2), \bshutdown\b(8)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+
+int
+b_sync(int argc, char** argv, Shbltin_t* context)
+{
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || *argv)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+#if _lib_sync
+ sync();
+#else
+ error(ERROR_usage(2), "failed -- the native system does not provide a sync(2) call");
+#endif
+ return 0;
+}
diff --git a/src/lib/libcmd/tail.c b/src/lib/libcmd/tail.c
new file mode 100644
index 0000000..18aa270
--- /dev/null
+++ b/src/lib/libcmd/tail.c
@@ -0,0 +1,773 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+/*
+ * print the tail of one or more files
+ *
+ * David Korn
+ * Glenn Fowler
+ */
+
+static const char usage[] =
+"+[-?\n@(#)$Id: tail (AT&T Research) 2010-05-09 $\n]"
+USAGE_LICENSE
+"[+NAME?tail - output trailing portion of one or more files ]"
+"[+DESCRIPTION?\btail\b copies one or more input files to standard output "
+ "starting at a designated point for each file. Copying starts "
+ "at the point indicated by the options and is unlimited in size.]"
+"[+?By default a header of the form \b==> \b\afilename\a\b <==\b "
+ "is output before all but the first file but this can be changed "
+ "with the \b-q\b and \b-v\b options.]"
+"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \btail\b "
+ "copies from standard input. The start of the file is defined "
+ "as the current offset.]"
+"[+?The option argument for \b-c\b can optionally be "
+ "followed by one of the following characters to specify a different "
+ "unit other than a single byte:]{"
+ "[+b?512 bytes.]"
+ "[+k?1 KiB.]"
+ "[+m?1 MiB.]"
+ "[+g?1 GiB.]"
+ "}"
+"[+?For backwards compatibility, \b-\b\anumber\a is equivalent to "
+ "\b-n\b \anumber\a and \b+\b\anumber\a is equivalent to "
+ "\b-n -\b\anumber\a. \anumber\a may also have these option "
+ "suffixes: \bb c f g k l m r\b.]"
+
+"[n:lines]:[lines:=10?Copy \alines\a lines from each file. A negative value "
+ "for \alines\a indicates an offset from the end of the file.]"
+"[b:blocks?Copy units of 512 bytes.]"
+"[c:bytes]:?[chars?Copy \achars\a bytes from each file. A negative value "
+ "for \achars\a indicates an offset from the end of the file.]"
+"[f:forever|follow?Loop forever trying to read more characters as the "
+ "end of each file to copy new data. Ignored if reading from a pipe "
+ "or fifo.]"
+"[h!:headers?Output filename headers.]"
+"[l:lines?Copy units of lines. This is the default.]"
+"[L:log?When a \b--forever\b file times out via \b--timeout\b, verify that "
+ "the curent file has not been renamed and replaced by another file "
+ "of the same name (a common log file practice) before giving up on "
+ "the file.]"
+"[q:quiet?Don't output filename headers. For GNU compatibility.]"
+"[r:reverse?Output lines in reverse order.]"
+"[s:silent?Don't warn about timeout expiration and log file changes.]"
+"[t:timeout?Stop checking after \atimeout\a elapses with no additional "
+ "\b--forever\b output. A separate elapsed time is maintained for "
+ "each file operand. There is no timeout by default. The default "
+ "\atimeout\a unit is seconds. \atimeout\a may be a catenation of 1 "
+ "or more integers, each followed by a 1 character suffix. The suffix "
+ "may be omitted from the last integer, in which case it is "
+ "interpreted as seconds. The supported suffixes are:]:[timeout]{"
+ "[+s?seconds]"
+ "[+m?minutes]"
+ "[+h?hours]"
+ "[+d?days]"
+ "[+w?weeks]"
+ "[+M?months]"
+ "[+y?years]"
+ "[+S?scores]"
+ "}"
+"[v:verbose?Always ouput filename headers.]"
+
+"\n"
+"\n[file ...]\n"
+"\n"
+
+"[+EXIT STATUS?]{"
+ "[+0?All files copied successfully.]"
+ "[+>0?One or more files did not copy.]"
+"}"
+"[+SEE ALSO?\bcat\b(1), \bhead\b(1), \brev\b(1)]"
+;
+
+#include <cmd.h>
+#include <ctype.h>
+#include <ls.h>
+#include <tm.h>
+#include <rev.h>
+
+#define COUNT (1<<0)
+#define ERROR (1<<1)
+#define FOLLOW (1<<2)
+#define HEADERS (1<<3)
+#define LINES (1<<4)
+#define LOG (1<<5)
+#define NEGATIVE (1<<6)
+#define POSITIVE (1<<7)
+#define REVERSE (1<<8)
+#define SILENT (1<<9)
+#define TIMEOUT (1<<10)
+#define VERBOSE (1<<11)
+
+#define NOW (unsigned long)time(NiL)
+
+#define DEFAULT 10
+
+#ifdef S_ISSOCK
+#define FIFO(m) (S_ISFIFO(m)||S_ISSOCK(m))
+#else
+#define FIFO(m) S_ISFIFO(m)
+#endif
+
+struct Tail_s; typedef struct Tail_s Tail_t;
+
+struct Tail_s
+{
+ Tail_t* next;
+ char* name;
+ Sfio_t* sp;
+ Sfoff_t cur;
+ Sfoff_t end;
+ unsigned long expire;
+ long dev;
+ long ino;
+ int fifo;
+};
+
+static const char header_fmt[] = "\n==> %s <==\n";
+
+/*
+ * if file is seekable, position file to tail location and return offset
+ * otherwise, return -1
+ */
+
+static Sfoff_t
+tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
+{
+ register size_t n;
+ register Sfoff_t offset;
+ register Sfoff_t first;
+ register Sfoff_t last;
+ register char* s;
+ register char* t;
+ struct stat st;
+
+ last = sfsize(fp);
+ if ((first = sfseek(fp, (Sfoff_t)0, SEEK_CUR)) < 0)
+ return last || fstat(sffileno(fp), &st) || st.st_size || FIFO(st.st_mode) ? -1 : 0;
+ if (delim < 0)
+ {
+ if ((offset = last - number) < first)
+ return first;
+ return offset;
+ }
+ for (;;)
+ {
+ if ((offset = last - SF_BUFSIZE) < first)
+ offset = first;
+ sfseek(fp, offset, SEEK_SET);
+ n = last - offset;
+ if (!(s = sfreserve(fp, n, SF_LOCKR)))
+ return -1;
+ t = s + n;
+ while (t > s)
+ if (*--t == delim && number-- <= 0)
+ {
+ sfread(fp, s, 0);
+ return offset + (t - s) + 1;
+ }
+ sfread(fp, s, 0);
+ if (offset == first)
+ break;
+ last = offset;
+ }
+ return first;
+}
+
+/*
+ * this code handles tail from a pipe without any size limits
+ */
+
+static void
+pipetail(Sfio_t* infile, Sfio_t* outfile, Sfoff_t number, int delim)
+{
+ register Sfio_t* out;
+ register Sfoff_t n;
+ register Sfoff_t nleft = number;
+ register size_t a = 2 * SF_BUFSIZE;
+ register int fno = 0;
+ Sfoff_t offset[2];
+ Sfio_t* tmp[2];
+
+ if (delim < 0 && a > number)
+ a = number;
+ out = tmp[0] = sftmp(a);
+ tmp[1] = sftmp(a);
+ offset[0] = offset[1] = 0;
+ while ((n = sfmove(infile, out, number, delim)) > 0)
+ {
+ offset[fno] = sftell(out);
+ if ((nleft -= n) <= 0)
+ {
+ out = tmp[fno= !fno];
+ sfseek(out, (Sfoff_t)0, SEEK_SET);
+ nleft = number;
+ }
+ }
+ if (nleft == number)
+ {
+ offset[fno] = 0;
+ fno= !fno;
+ }
+ sfseek(tmp[0], (Sfoff_t)0, SEEK_SET);
+
+ /*
+ * see whether both files are needed
+ */
+
+ if (offset[fno])
+ {
+ sfseek(tmp[1], (Sfoff_t)0, SEEK_SET);
+ if ((n = number - nleft) > 0)
+ sfmove(tmp[!fno], NiL, n, delim);
+ if ((n = offset[!fno] - sftell(tmp[!fno])) > 0)
+ sfmove(tmp[!fno], outfile, n, -1);
+ }
+ else
+ fno = !fno;
+ sfmove(tmp[fno], outfile, offset[fno], -1);
+ sfclose(tmp[0]);
+ sfclose(tmp[1]);
+}
+
+/*
+ * (re)initialize a tail stream
+ */
+
+static int
+init(Tail_t* tp, Sfoff_t number, int delim, int flags, const char** format)
+{
+ Sfoff_t offset;
+ Sfio_t* op;
+ struct stat st;
+
+ tp->fifo = 0;
+ if (tp->sp)
+ {
+ offset = 0;
+ if (tp->sp == sfstdin)
+ tp->sp = 0;
+ }
+ else
+ offset = 1;
+ if (!tp->name || streq(tp->name, "-"))
+ {
+ tp->name = "/dev/stdin";
+ tp->sp = sfstdin;
+ }
+ else if (!(tp->sp = sfopen(tp->sp, tp->name, "r")))
+ {
+ error(ERROR_system(0), "%s: cannot open", tp->name);
+ return -1;
+ }
+ sfset(tp->sp, SF_SHARE, 0);
+ if (offset)
+ {
+ if (number < 0 || !number && (flags & POSITIVE))
+ {
+ sfset(tp->sp, SF_SHARE, !(flags & FOLLOW));
+ if (number < -1)
+ {
+ sfmove(tp->sp, NiL, -number - 1, delim);
+ offset = sfseek(tp->sp, (Sfoff_t)0, SEEK_CUR);
+ }
+ else
+ offset = 0;
+ }
+ else if ((offset = tailpos(tp->sp, number, delim)) >= 0)
+ sfseek(tp->sp, offset, SEEK_SET);
+ else if (fstat(sffileno(tp->sp), &st))
+ {
+ error(ERROR_system(0), "%s: cannot stat", tp->name);
+ goto bad;
+ }
+ else if (!FIFO(st.st_mode))
+ {
+ error(ERROR_SYSTEM|2, "%s: cannot position file to tail", tp->name);
+ goto bad;
+ }
+ else
+ {
+ tp->fifo = 1;
+ if (flags & (HEADERS|VERBOSE))
+ {
+ sfprintf(sfstdout, *format, tp->name);
+ *format = header_fmt;
+ }
+ op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout;
+ pipetail(tp->sp ? tp->sp : sfstdin, op, number, delim);
+ if (flags & REVERSE)
+ {
+ sfseek(op, (Sfoff_t)0, SEEK_SET);
+ rev_line(op, sfstdout, (Sfoff_t)0);
+ sfclose(op);
+ }
+ }
+ }
+ tp->cur = tp->end = offset;
+ if (flags & LOG)
+ {
+ if (fstat(sffileno(tp->sp), &st))
+ {
+ error(ERROR_system(0), "%s: cannot stat", tp->name);
+ goto bad;
+ }
+ tp->dev = st.st_dev;
+ tp->ino = st.st_ino;
+ }
+ return 0;
+ bad:
+ if (tp->sp != sfstdin)
+ sfclose(tp->sp);
+ tp->sp = 0;
+ return -1;
+}
+
+/*
+ * convert number with validity diagnostics
+ */
+
+static intmax_t
+num(register const char* s, char** e, int* f, int o)
+{
+ intmax_t number;
+ char* t;
+ int c;
+
+ *f &= ~(ERROR|NEGATIVE|POSITIVE);
+ if ((c = *s) == '-')
+ {
+ *f |= NEGATIVE;
+ s++;
+ }
+ else if (c == '+')
+ {
+ *f |= POSITIVE;
+ s++;
+ }
+ while (*s == '0' && isdigit(*(s + 1)))
+ s++;
+ errno = 0;
+ number = strtonll(s, &t, NiL, 0);
+ if (t == s)
+ number = DEFAULT;
+ if (o && *t)
+ {
+ number = 0;
+ *f |= ERROR;
+ error(2, "-%c: %s: invalid numeric argument -- unknown suffix", o, s);
+ }
+ else if (errno)
+ {
+ *f |= ERROR;
+ if (o)
+ error(2, "-%c: %s: invalid numeric argument -- out of range", o, s);
+ else
+ error(2, "%s: invalid numeric argument -- out of range", s);
+ }
+ else
+ {
+ *f |= COUNT;
+ if (t > s && isalpha(*(t - 1)))
+ *f &= ~LINES;
+ if (c == '-')
+ number = -number;
+ }
+ if (e)
+ *e = t;
+ return number;
+}
+
+int
+b_tail(int argc, char** argv, Shbltin_t* context)
+{
+ register Sfio_t* ip;
+ register int n;
+ register int i;
+ int delim;
+ int flags = HEADERS|LINES;
+ int blocks = 0;
+ char* s;
+ char* t;
+ char* r;
+ char* file;
+ Sfoff_t offset;
+ Sfoff_t number = DEFAULT;
+ unsigned long timeout = 0;
+ struct stat st;
+ const char* format = header_fmt+1;
+ ssize_t z;
+ ssize_t w;
+ Sfio_t* op;
+ register Tail_t* fp;
+ register Tail_t* pp;
+ register Tail_t* hp;
+ Tail_t* files;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (n = optget(argv, usage))
+ {
+ case 0:
+ if (!(flags & FOLLOW) && argv[opt_info.index] && (argv[opt_info.index][0] == '-' || argv[opt_info.index][0] == '+') && !argv[opt_info.index][1])
+ {
+ number = argv[opt_info.index][0] == '-' ? 10 : -10;
+ flags |= LINES;
+ opt_info.index++;
+ continue;
+ }
+ break;
+ case 'b':
+ blocks = 512;
+ flags &= ~LINES;
+ if (opt_info.option[0] == '+')
+ number = -number;
+ continue;
+ case 'c':
+ flags &= ~LINES;
+ if (opt_info.arg == argv[opt_info.index - 1])
+ {
+ strtol(opt_info.arg, &s, 10);
+ if (*s)
+ {
+ opt_info.index--;
+ t = "";
+ goto suffix;
+ }
+ }
+ else if (opt_info.arg && isalpha(*opt_info.arg))
+ {
+ t = opt_info.arg;
+ goto suffix;
+ }
+ /*FALLTHROUGH*/
+ case 'n':
+ flags |= COUNT;
+ if (s = opt_info.arg)
+ number = num(s, &s, &flags, n);
+ else
+ {
+ number = DEFAULT;
+ flags &= ~(ERROR|NEGATIVE|POSITIVE);
+ s = "";
+ }
+ if (n != 'n' && s && isalpha(*s))
+ {
+ t = s;
+ goto suffix;
+ }
+ if (flags & ERROR)
+ continue;
+ if (flags & (NEGATIVE|POSITIVE))
+ number = -number;
+ if (opt_info.option[0]=='+')
+ number = -number;
+ continue;
+ case 'f':
+ flags |= FOLLOW;
+ continue;
+ case 'h':
+ if (opt_info.num)
+ flags |= HEADERS;
+ else
+ flags &= ~HEADERS;
+ continue;
+ case 'l':
+ flags |= LINES;
+ if (opt_info.option[0] == '+')
+ number = -number;
+ continue;
+ case 'L':
+ flags |= LOG;
+ continue;
+ case 'q':
+ flags &= ~HEADERS;
+ continue;
+ case 'r':
+ flags |= REVERSE;
+ continue;
+ case 's':
+ flags |= SILENT;
+ continue;
+ case 't':
+ flags |= TIMEOUT;
+ timeout = strelapsed(opt_info.arg, &s, 1);
+ if (*s)
+ error(ERROR_exit(1), "%s: invalid elapsed time [%s]", opt_info.arg, s);
+ continue;
+ case 'v':
+ flags |= VERBOSE;
+ continue;
+ case ':':
+ /* handle old style arguments */
+ if (!(r = argv[opt_info.index]) || !opt_info.offset)
+ {
+ error(2, "%s", opt_info.arg);
+ break;
+ }
+ s = r + opt_info.offset - 1;
+ if (i = *(s - 1) == '-' || *(s - 1) == '+')
+ s--;
+ if ((number = num(s, &t, &flags, 0)) && i)
+ number = -number;
+ goto compatibility;
+ suffix:
+ r = 0;
+ if (opt_info.option[0] == '+')
+ number = -number;
+ compatibility:
+ for (;;)
+ {
+ switch (*t++)
+ {
+ case 0:
+ if (r)
+ opt_info.offset = t - r - 1;
+ break;
+ case 'c':
+ flags &= ~LINES;
+ continue;
+ case 'f':
+ flags |= FOLLOW;
+ continue;
+ case 'l':
+ flags |= LINES;
+ continue;
+ case 'r':
+ flags |= REVERSE;
+ continue;
+ default:
+ error(2, "%s: invalid suffix", t - 1);
+ if (r)
+ opt_info.offset = strlen(r);
+ break;
+ }
+ break;
+ }
+ continue;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (!*argv)
+ {
+ flags &= ~HEADERS;
+ if (fstat(0, &st))
+ error(ERROR_system(0), "/dev/stdin: cannot stat");
+ else if (FIFO(st.st_mode))
+ flags &= ~FOLLOW;
+ }
+ else if (!*(argv + 1))
+ flags &= ~HEADERS;
+ delim = (flags & LINES) ? '\n' : -1;
+ if (blocks)
+ number *= blocks;
+ if (flags & REVERSE)
+ {
+ if (delim < 0)
+ error(2, "--reverse requires line mode");
+ if (!(flags & COUNT))
+ number = -1;
+ flags &= ~FOLLOW;
+ }
+ if ((flags & (FOLLOW|TIMEOUT)) == TIMEOUT)
+ {
+ flags &= ~TIMEOUT;
+ timeout = 0;
+ error(ERROR_warn(0), "--timeout ignored for --noforever");
+ }
+ if ((flags & (LOG|TIMEOUT)) == LOG)
+ {
+ flags &= ~LOG;
+ error(ERROR_warn(0), "--log ignored for --notimeout");
+ }
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (flags & FOLLOW)
+ {
+ if (!(fp = (Tail_t*)stakalloc(argc * sizeof(Tail_t))))
+ error(ERROR_system(1), "out of space");
+ files = 0;
+ s = *argv;
+ do
+ {
+ fp->name = s;
+ fp->sp = 0;
+ if (!init(fp, number, delim, flags, &format))
+ {
+ fp->expire = timeout ? (NOW + timeout + 1) : 0;
+ if (files)
+ pp->next = fp;
+ else
+ files = fp;
+ pp = fp;
+ fp++;
+ }
+ } while (s && (s = *++argv));
+ if (!files)
+ return error_info.errors != 0;
+ pp->next = 0;
+ hp = 0;
+ n = 1;
+ while (fp = files)
+ {
+ if (n)
+ n = 0;
+ else
+ sleep(1);
+ pp = 0;
+ while (fp)
+ {
+ if (fstat(sffileno(fp->sp), &st))
+ error(ERROR_system(0), "%s: cannot stat", fp->name);
+ else if (fp->fifo || fp->end < st.st_size)
+ {
+ n = 1;
+ if (timeout)
+ fp->expire = NOW + timeout;
+ z = fp->fifo ? SF_UNBOUND : st.st_size - fp->cur;
+ i = 0;
+ if ((s = sfreserve(fp->sp, z, SF_LOCKR)) || (z = sfvalue(fp->sp)) && (s = sfreserve(fp->sp, z, SF_LOCKR)) && (i = 1))
+ {
+ z = sfvalue(fp->sp);
+ for (r = s + z; r > s && *(r - 1) != '\n'; r--);
+ if ((w = r - s) || i && (w = z))
+ {
+ if ((flags & (HEADERS|VERBOSE)) && hp != fp)
+ {
+ hp = fp;
+ sfprintf(sfstdout, format, fp->name);
+ format = header_fmt;
+ }
+ fp->cur += w;
+ sfwrite(sfstdout, s, w);
+ }
+ else
+ w = 0;
+ sfread(fp->sp, s, w);
+ fp->end += w;
+ }
+ goto next;
+ }
+ else if (!timeout || fp->expire > NOW)
+ goto next;
+ else
+ {
+ if (flags & LOG)
+ {
+ i = 3;
+ while (--i && stat(fp->name, &st))
+ sleep(1);
+ if (i && (fp->dev != st.st_dev || fp->ino != st.st_ino) && !init(fp, 0, 0, flags, &format))
+ {
+ if (!(flags & SILENT))
+ error(ERROR_warn(0), "%s: log file change", fp->name);
+ fp->expire = NOW + timeout;
+ goto next;
+ }
+ }
+ if (!(flags & SILENT))
+ error(ERROR_warn(0), "%s: %s timeout", fp->name, fmtelapsed(timeout, 1));
+ }
+ if (fp->sp && fp->sp != sfstdin)
+ sfclose(fp->sp);
+ if (pp)
+ pp = pp->next = fp->next;
+ else
+ files = files->next;
+ fp = fp->next;
+ continue;
+ next:
+ pp = fp;
+ fp = fp->next;
+ }
+ if (sfsync(sfstdout))
+ error(ERROR_system(1), "write error");
+ }
+ }
+ else
+ {
+ if (file = *argv)
+ argv++;
+ do
+ {
+ if (!file || streq(file, "-"))
+ {
+ file = "/dev/stdin";
+ ip = sfstdin;
+ }
+ else if (!(ip = sfopen(NiL, file, "r")))
+ {
+ error(ERROR_system(0), "%s: cannot open", file);
+ continue;
+ }
+ if (flags & (HEADERS|VERBOSE))
+ {
+ sfprintf(sfstdout, format, file);
+ format = header_fmt;
+ }
+ if (number < 0 || !number && (flags & POSITIVE))
+ {
+ sfset(ip, SF_SHARE, 1);
+ if (number < -1)
+ sfmove(ip, NiL, -number - 1, delim);
+ if (flags & REVERSE)
+ rev_line(ip, sfstdout, sfseek(ip, (Sfoff_t)0, SEEK_CUR));
+ else
+ sfmove(ip, sfstdout, SF_UNBOUND, -1);
+ }
+ else
+ {
+ sfset(ip, SF_SHARE, 0);
+ if ((offset = tailpos(ip, number, delim)) >= 0)
+ {
+ if (flags & REVERSE)
+ rev_line(ip, sfstdout, offset);
+ else
+ {
+ sfseek(ip, offset, SEEK_SET);
+ sfmove(ip, sfstdout, SF_UNBOUND, -1);
+ }
+ }
+ else
+ {
+ op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout;
+ pipetail(ip, op, number, delim);
+ if (flags & REVERSE)
+ {
+ sfseek(op, (Sfoff_t)0, SEEK_SET);
+ rev_line(op, sfstdout, (Sfoff_t)0);
+ sfclose(op);
+ }
+ flags = 0;
+ }
+ }
+ if (ip != sfstdin)
+ sfclose(ip);
+ } while (file = *argv++);
+ }
+ return error_info.errors != 0;
+}
diff --git a/src/lib/libcmd/tee.c b/src/lib/libcmd/tee.c
new file mode 100644
index 0000000..ce1c873
--- /dev/null
+++ b/src/lib/libcmd/tee.c
@@ -0,0 +1,204 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * tee
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: tee (AT&T Research) 2012-01-01 $\n]"
+USAGE_LICENSE
+"[+NAME?tee - duplicate standard input]"
+"[+DESCRIPTION?\btee\b copies standard input to standard output "
+ "and to zero or more files. The options determine whether "
+ "the specified files are overwritten or appended to. The "
+ "\btee\b utility does not buffer output. If writes to any "
+ "\afile\a fail, writes to other files continue although \btee\b "
+ "will exit with a non-zero exit status.]"
+"[+?The number of \afile\a operands that can be specified is limited "
+ "by the underlying operating system.]"
+"[a:append?Append the standard input to the given files rather "
+ "than overwriting them.]"
+"[i:ignore-interrupts?Ignore SIGINT signal.]"
+"[l:linebuffer?Set the standard output to be line buffered.]"
+"\n"
+"\n[file ...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files copies successfully.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bcat\b(1), \bsignal\b(3)]"
+;
+
+#include <cmd.h>
+#include <ls.h>
+#include <sig.h>
+
+typedef struct Tee_s
+{
+ Sfdisc_t disc;
+ int line;
+ int fd[1];
+} Tee_t;
+
+/*
+ * This discipline writes to each file in the list given in handle
+ */
+
+static ssize_t
+tee_write(Sfio_t* fp, const void* buf, size_t n, Sfdisc_t* handle)
+{
+ register const char* bp;
+ register const char* ep;
+ register int* hp = ((Tee_t*)handle)->fd;
+ register int fd = sffileno(fp);
+ register ssize_t r;
+
+ do
+ {
+ bp = (const char*)buf;
+ ep = bp + n;
+ while (bp < ep)
+ {
+ if ((r = write(fd, bp, ep - bp)) <= 0)
+ return -1;
+ bp += r;
+ }
+ } while ((fd = *hp++) >= 0);
+ return n;
+}
+
+static void
+tee_cleanup(register Tee_t* tp)
+{
+ register int* hp;
+ register int n;
+
+ if (tp)
+ {
+ sfdisc(sfstdout, NiL);
+ if (tp->line >= 0)
+ sfset(sfstdout, SF_LINE, tp->line);
+ for (hp = tp->fd; (n = *hp) >= 0; hp++)
+ close(n);
+ }
+}
+
+int
+b_tee(int argc, register char** argv, Shbltin_t* context)
+{
+ register Tee_t* tp = 0;
+ register int oflag = O_WRONLY|O_TRUNC|O_CREAT|O_BINARY;
+ register int* hp;
+ register char* cp;
+ int line;
+
+ if (argc <= 0)
+ {
+ if (context && (tp = (Tee_t*)sh_context(context)->data))
+ {
+ sh_context(context)->data = 0;
+ tee_cleanup(tp);
+ }
+ return 0;
+ }
+ cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_CALLBACK);
+ line = -1;
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ oflag &= ~O_TRUNC;
+ oflag |= O_APPEND;
+ continue;
+ case 'i':
+ signal(SIGINT, SIG_IGN);
+ continue;
+ case 'l':
+ line = sfset(sfstdout, 0, 0) & SF_LINE;
+ if ((line == 0) == (opt_info.num == 0))
+ line = -1;
+ else
+ sfset(sfstdout, SF_LINE, !!opt_info.num);
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ argv += opt_info.index;
+ argc -= opt_info.index;
+#if _ANCIENT_BSD_COMPATIBILITY
+ if (*argv && streq(*argv, "-"))
+ {
+ signal(SIGINT, SIG_IGN);
+ argv++;
+ argc--;
+ }
+#endif
+ if (argc > 0)
+ {
+ if (tp = (Tee_t*)stakalloc(sizeof(Tee_t) + argc * sizeof(int)))
+ {
+ memset(&tp->disc, 0, sizeof(tp->disc));
+ tp->disc.writef = tee_write;
+ if (context)
+ sh_context(context)->data = (void*)tp;
+ tp->line = line;
+ hp = tp->fd;
+ while (cp = *argv++)
+ {
+ while ((*hp = open(cp, oflag, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0 && errno == EINTR)
+ errno = 0;
+ if (*hp < 0)
+ error(ERROR_system(0), "%s: cannot create", cp);
+ else
+ hp++;
+ }
+ if (hp == tp->fd)
+ tp = 0;
+ else
+ {
+ *hp = -1;
+ sfdisc(sfstdout, &tp->disc);
+ }
+ }
+ else
+ error(ERROR_exit(0), "out of space");
+ }
+ if ((sfmove(sfstdin, sfstdout, SF_UNBOUND, -1) < 0 || !sfeof(sfstdin)) && errno != EPIPE && errno != EINTR)
+ error(ERROR_system(0), "read error");
+ if (sfsync(sfstdout))
+ error(ERROR_system(0), "write error");
+ tee_cleanup(tp);
+ return error_info.errors;
+}
diff --git a/src/lib/libcmd/tty.c b/src/lib/libcmd/tty.c
new file mode 100644
index 0000000..f5e6aca
--- /dev/null
+++ b/src/lib/libcmd/tty.c
@@ -0,0 +1,105 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * tty
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: tty (AT&T Research) 2008-03-13 $\n]"
+USAGE_LICENSE
+"[+NAME?tty - write the name of the terminal to standard output]"
+"[+DESCRIPTION?\btty\b writes the name of the terminal that is connected "
+ "to standard input onto standard output. If the standard input is not "
+ "a terminal, \"\bnot a tty\b\" will be written to standard output.]"
+"[l:line-number?Write the synchronous line number of the terminal on a "
+ "separate line following the terminal name line. If the standard "
+ "input is not a synchronous terminal then "
+ "\"\bnot on an active synchronous line\b\" is written.]"
+"[s:silent|quiet?Disable the terminal name line. Use \b[[ -t 0 ]]]]\b instead.]"
+"[+EXIT STATUS?]{"
+ "[+0?Standard input is a tty.]"
+ "[+1?Standard input is not a tty.]"
+ "[+2?Invalid arguments.]"
+ "[+3?A an error occurred.]"
+"}"
+;
+
+
+#include <cmd.h>
+
+#if _mac_STWLINE
+#include <sys/stermio.h>
+#endif
+
+int
+b_tty(int argc, char** argv, Shbltin_t* context)
+{
+ register int sflag = 0;
+ register int lflag = 0;
+ register char* tty;
+#if _mac_STWLINE
+ int n;
+#endif
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'l':
+ lflag++;
+ continue;
+ case 's':
+ sflag++;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ if(error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if(!(tty=ttyname(0)))
+ {
+ tty = ERROR_translate(0, 0, 0, "not a tty");
+ error_info.errors++;
+ }
+ if(!sflag)
+ sfputr(sfstdout,tty,'\n');
+ if(lflag)
+ {
+#if _mac_STWLINE
+ if ((n = ioctl(0, STWLINE, 0)) >= 0)
+ error(ERROR_OUTPUT, 1, "synchronous line %d", n);
+ else
+#endif
+ error(ERROR_OUTPUT, 1, "not on an active synchronous line");
+ }
+ return(error_info.errors);
+}
diff --git a/src/lib/libcmd/uname.c b/src/lib/libcmd/uname.c
new file mode 100644
index 0000000..cece94a
--- /dev/null
+++ b/src/lib/libcmd/uname.c
@@ -0,0 +1,514 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * Glenn Fowler
+ * AT&T Research
+ *
+ * uname
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: uname (AT&T Research) 2007-04-19 $\n]"
+USAGE_LICENSE
+"[+NAME?uname - identify the current system ]"
+"[+DESCRIPTION?By default \buname\b writes the operating system name to"
+" standard output. When options are specified, one or more"
+" system characteristics are written to standard output, space"
+" separated, on a single line. When more than one option is specified"
+" the output is in the order specfied by the \b-A\b option below."
+" Unsupported option values are listed as \a[option]]\a. If any unknown"
+" options are specified then the local \b/usr/bin/uname\b is called.]"
+"[+?If any \aname\a operands are specified then the \bsysinfo\b(2) values"
+" for each \aname\a are listed, separated by space, on one line."
+" \bgetconf\b(1), a pre-existing \astandard\a interface, provides"
+" access to the same information; vendors should spend more time"
+" using standards than inventing them.]"
+"[+?Selected information is printed in the same order as the options below.]"
+"[a:all?Equivalent to \b-snrvmpio\b.]"
+"[s:system|sysname|kernel-name?The detailed kernel name. This is the default.]"
+"[n:nodename?The hostname or nodename.]"
+"[r:release|kernel-release?The kernel release level.]"
+"[v:version|kernel-version?The kernel version level.]"
+"[m:machine?The name of the hardware type the system is running on.]"
+"[p:processor?The name of the processor instruction set architecture.]"
+"[i:implementation|platform|hardware-platform?The hardware implementation;"
+" this is \b--host-id\b on some systems.]"
+"[o:operating-system?The generic operating system name.]"
+"[h:host-id|id?The host id in hex.]"
+"[d:domain?The domain name returned by \agetdomainname\a(2).]"
+"[R:extended-release?The extended release name.]"
+"[A:everything?Equivalent to \b-snrvmpiohdR\b.]"
+"[f:list?List all \bsysinfo\b(2) names and values, one per line.]"
+"[S:sethost?Set the hostname or nodename to \aname\a. No output is"
+" written to standard output.]:[name]"
+"\n"
+"\n[ name ... ]\n"
+"\n"
+"[+SEE ALSO?\bhostname\b(1), \bgetconf\b(1), \buname\b(2),"
+" \bsysconf\b(2), \bsysinfo\b(2)]"
+;
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:hide getdomainname gethostid gethostname sethostname
+#else
+#define getdomainname ______getdomainname
+#define gethostid ______gethostid
+#define gethostname ______gethostname
+#define sethostname ______sethostname
+#endif
+
+#include <cmd.h>
+#include <ctype.h>
+#include <proc.h>
+
+#include "FEATURE/utsname"
+
+#define MAXHOSTNAME 64
+
+#if _lib_uname && _sys_utsname
+
+#include <sys/utsname.h>
+
+#endif
+
+#if defined(__STDPP__directive) && defined(__STDPP__hide)
+__STDPP__directive pragma pp:nohide getdomainname gethostid gethostname sethostname
+#else
+#undef getdomainname
+#undef gethostid
+#undef gethostname
+#undef sethostname
+#endif
+
+#if _lib_getdomainname
+extern int getdomainname(char*, size_t);
+#endif
+#if _lib_gethostid
+extern long gethostid(void);
+#endif
+#if _lib_gethostname
+extern int gethostname(char*, size_t);
+#endif
+#if _lib_sethostname
+extern int sethostname(const char*, size_t);
+#endif
+
+#ifndef HOSTTYPE
+#define HOSTTYPE "unknown"
+#endif
+
+static const char hosttype[] = HOSTTYPE;
+
+#if !_lib_uname || !_sys_utsname
+
+#if defined(__STDPP__)
+#define SYSNAME #(getprd machine)
+#define RELEASE #(getprd release)
+#define VERSION #(getprd version)
+#define MACHINE #(getprd architecture)
+#else
+#define SYSNAME ""
+#define RELEASE ""
+#define VERSION ""
+#define MACHINE ""
+#endif
+
+struct utsname
+{
+ char* sysname;
+ char nodename[MAXHOSTNAME];
+ char* release;
+ char* version;
+ char* machine;
+};
+
+int
+uname(register struct utsname* ut)
+{
+#ifdef HOSTTYPE
+ char* sys = 0;
+ char* arch = 0;
+
+ if (*hosttype)
+ {
+ static char buf[sizeof(hosttype)];
+
+ strcpy(buf, hosttype);
+ sys = buf;
+ if (arch = strchr(sys, '.'))
+ {
+ *arch++ = 0;
+ if (!*arch)
+ arch = 0;
+ }
+ if (!*sys)
+ sys = 0;
+ }
+#endif
+#ifdef _lib_gethostname
+ if (gethostname(ut->nodename, sizeof(ut->nodename) - 1))
+ return -1;
+#else
+ strncpy(ut->nodename, "local", sizeof(ut->nodename) - 1);
+#endif
+#ifdef HOSTTYPE
+ if (!(ut->sysname = sys))
+#endif
+ if (!*(ut->sysname = SYSNAME))
+ ut->sysname = ut->nodename;
+#ifdef HOSTTYPE
+ if (!(ut->machine = arch))
+#endif
+ ut->machine = MACHINE;
+ ut->release = RELEASE;
+ ut->version = VERSION;
+ return 0;
+}
+
+#endif
+
+#define OPT_system (1<<0)
+#define OPT_nodename (1<<1)
+#define OPT_release (1<<2)
+#define OPT_version (1<<3)
+#define OPT_machine (1<<4)
+#define OPT_processor (1<<5)
+
+#define OPT_STANDARD 6
+
+#define OPT_implementation (1<<6)
+#define OPT_operating_system (1<<7)
+
+#define OPT_ALL 8
+
+#define OPT_hostid (1<<8)
+#define OPT_vendor (1<<9)
+#define OPT_domain (1<<10)
+#define OPT_machine_type (1<<11)
+#define OPT_base (1<<12)
+#define OPT_extended_release (1<<13)
+#define OPT_extra (1<<14)
+
+#define OPT_TOTAL 15
+
+#define OPT_all (1L<<29)
+#define OPT_total (1L<<30)
+#define OPT_standard ((1<<OPT_STANDARD)-1)
+
+#ifndef MACHINE
+#if defined(__STDPP__)
+#define MACHINE #(getprd architecture)
+#else
+#define MACHINE ""
+#endif
+#endif
+
+#ifndef HOSTTYPE
+#define HOSTTYPE "unknown"
+#endif
+
+#define extra(m) do \
+ { \
+ if ((char*)&ut.m[sizeof(ut.m)] > last) \
+ last = (char*)&ut.m[sizeof(ut.m)]; \
+ } while(0)
+
+#define output(f,v,u) do \
+ { \
+ if ((flags&(f))&&(*(v)||(flags&(OPT_all|OPT_total))==OPT_all&&((f)&OPT_standard)||!(flags&(OPT_all|OPT_total)))) \
+ { \
+ if (sep) \
+ sfputc(sfstdout, ' '); \
+ else \
+ sep = 1; \
+ if (*(v)) \
+ sfputr(sfstdout, v, -1); \
+ else \
+ sfprintf(sfstdout, "[%s]", u); \
+ } \
+ } while (0)
+
+int
+b_uname(int argc, char** argv, Shbltin_t* context)
+{
+ register long flags = 0;
+ register int sep = 0;
+ register int n;
+ register char* s;
+ char* t;
+ char* e;
+ char* sethost = 0;
+ int list = 0;
+ struct utsname ut;
+ char buf[257];
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'a':
+ flags |= OPT_all|((1L<<OPT_ALL)-1);
+ continue;
+ case 'b':
+ flags |= OPT_base;
+ continue;
+ case 'c':
+ flags |= OPT_vendor;
+ continue;
+ case 'd':
+ flags |= OPT_domain;
+ continue;
+ case 'f':
+ list = 1;
+ continue;
+ case 'h':
+ flags |= OPT_hostid;
+ continue;
+ case 'i':
+ flags |= OPT_implementation;
+ continue;
+ case 'm':
+ flags |= OPT_machine;
+ continue;
+ case 'n':
+ flags |= OPT_nodename;
+ continue;
+ case 'o':
+ flags |= OPT_operating_system;
+ continue;
+ case 'p':
+ flags |= OPT_processor;
+ continue;
+ case 'r':
+ flags |= OPT_release;
+ continue;
+ case 's':
+ flags |= OPT_system;
+ continue;
+ case 't':
+ flags |= OPT_machine_type;
+ continue;
+ case 'v':
+ flags |= OPT_version;
+ continue;
+ case 'x':
+ flags |= OPT_extra;
+ continue;
+ case 'A':
+ flags |= OPT_total|((1L<<OPT_TOTAL)-1);
+ continue;
+ case 'R':
+ flags |= OPT_extended_release;
+ continue;
+ case 'S':
+ sethost = opt_info.arg;
+ continue;
+ case ':':
+ s = "/usr/bin/uname";
+ if (!streq(argv[0], s) && (!eaccess(s, X_OK) || !eaccess(s+=4, X_OK)))
+ {
+ argv[0] = s;
+ return sh_run(context, argc, argv);
+ }
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || *argv && (flags || sethost) || sethost && flags)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (sethost)
+ {
+#if _lib_sethostname
+ if (sethostname(sethost, strlen(sethost) + 1))
+#else
+#ifdef ENOSYS
+ errno = ENOSYS;
+#else
+ errno = EPERM;
+#endif
+#endif
+ error(ERROR_system(1), "%s: cannot set host name", sethost);
+ }
+ else if (list)
+ astconflist(sfstdout, NiL, ASTCONF_base|ASTCONF_defined|ASTCONF_lower|ASTCONF_quote|ASTCONF_matchcall, "CS|SI");
+ else if (*argv)
+ {
+ e = &buf[sizeof(buf)-1];
+ while (s = *argv++)
+ {
+ t = buf;
+ *t++ = 'C';
+ *t++ = 'S';
+ *t++ = '_';
+ while (t < e && (n = *s++))
+ *t++ = islower(n) ? toupper(n) : n;
+ *t = 0;
+ sfprintf(sfstdout, "%s%c", *(t = astconf(buf, NiL, NiL)) ? t : *(t = astconf(buf+3, NiL, NiL)) ? t : "unknown", *argv ? ' ' : '\n');
+ }
+ }
+ else
+ {
+ s = buf;
+ if (!flags)
+ flags = OPT_system;
+ memzero(&ut, sizeof(ut));
+ if (uname(&ut) < 0)
+ error(ERROR_usage(2), "information unavailable");
+ output(OPT_system, ut.sysname, "sysname");
+ if (flags & OPT_nodename)
+ {
+#if !_mem_nodeext_utsname && _lib_gethostname
+ if (sizeof(ut.nodename) > 9 || gethostname(s, sizeof(buf)))
+#endif
+ s = ut.nodename;
+ output(OPT_nodename, s, "nodename");
+ }
+ output(OPT_release, ut.release, "release");
+ output(OPT_version, ut.version, "version");
+ output(OPT_machine, ut.machine, "machine");
+ if (flags & OPT_processor)
+ {
+ if (!*(s = astconf("ARCHITECTURE", NiL, NiL)))
+ s = ut.machine;
+ output(OPT_processor, s, "processor");
+ }
+ if (flags & OPT_implementation)
+ {
+ if (!*(s = astconf("PLATFORM", NiL, NiL)) && !*(s = astconf("HW_NAME", NiL, NiL)))
+ {
+ if (t = strchr(hosttype, '.'))
+ t++;
+ else
+ t = (char*)hosttype;
+ strncpy(s = buf, t, sizeof(buf) - 1);
+ }
+ output(OPT_implementation, s, "implementation");
+ }
+ if (flags & OPT_operating_system)
+ {
+ s = astconf("OPERATING_SYSTEM", NiL, NiL);
+ if (!*s)
+#ifdef _UNAME_os_DEFAULT
+ s = _UNAME_os_DEFAULT;
+#else
+ s = ut.sysname;
+#endif
+ output(OPT_operating_system, s, "operating-system");
+ }
+ if (flags & OPT_extended_release)
+ {
+ s = astconf("RELEASE", NiL, NiL);
+ output(OPT_extended_release, s, "extended-release");
+ }
+#if _mem_idnumber_utsname
+ output(OPT_hostid, ut.idnumber, "hostid");
+#else
+ if (flags & OPT_hostid)
+ {
+ if (!*(s = astconf("HW_SERIAL", NiL, NiL)))
+#if _lib_gethostid
+ sfsprintf(s = buf, sizeof(buf), "%08x", gethostid());
+#else
+ /*NOP*/;
+#endif
+ output(OPT_hostid, s, "hostid");
+ }
+#endif
+ if (flags & OPT_vendor)
+ {
+ s = astconf("HW_PROVIDER", NiL, NiL);
+ output(OPT_vendor, s, "vendor");
+ }
+ if (flags & OPT_domain)
+ {
+ if (!*(s = astconf("SRPC_DOMAIN", NiL, NiL)))
+#if _lib_getdomainname
+ getdomainname(s, sizeof(buf));
+#else
+ /*NOP*/;
+#endif
+ output(OPT_domain, s, "domain");
+ }
+#if _mem_m_type_utsname
+ s = ut.m_type;
+#else
+ s = astconf("MACHINE", NiL, NiL);
+#endif
+ output(OPT_machine_type, s, "m_type");
+#if _mem_base_rel_utsname
+ s = ut.base_rel;
+#else
+ s = astconf("BASE", NiL, NiL);
+#endif
+ output(OPT_base, s, "base_rel");
+ if (flags & OPT_extra)
+ {
+ char* last = (char*)&ut;
+
+ extra(sysname);
+ extra(nodename);
+ extra(release);
+ extra(version);
+ extra(machine);
+#if _mem_idnumber_utsname
+ extra(idnumber);
+#endif
+#if _mem_m_type_utsname
+ extra(m_type);
+#endif
+#if _mem_base_rel_utsname
+ extra(base_rel);
+#endif
+ if (last < ((char*)(&ut + 1)))
+ {
+ s = t = last;
+ while (s < (char*)(&ut + 1))
+ {
+ if (!(n = *s++))
+ {
+ if ((s - t) > 1)
+ {
+ if (sep)
+ sfputc(sfstdout, ' ');
+ else
+ sep = 1;
+ sfputr(sfstdout, t, -1);
+ }
+ t = s;
+ }
+ else if (!isprint(n))
+ break;
+ }
+ }
+ }
+ if (sep)
+ sfputc(sfstdout, '\n');
+ }
+ return error_info.errors;
+}
diff --git a/src/lib/libcmd/uniq.c b/src/lib/libcmd/uniq.c
new file mode 100644
index 0000000..1336acd
--- /dev/null
+++ b/src/lib/libcmd/uniq.c
@@ -0,0 +1,342 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * uniq
+ *
+ * Written by David Korn
+ */
+
+static const char usage[] =
+"[-n?\n@(#)$Id: uniq (AT&T Research) 2009-11-28 $\n]"
+USAGE_LICENSE
+"[+NAME?uniq - Report or filter out repeated lines in a file]"
+"[+DESCRIPTION?\buniq\b reads the input, compares adjacent lines, and "
+ "writes one copy of each input line on the output. The second "
+ "and succeeding copies of the repeated adjacent lines are not "
+ "written.]"
+"[+?If the output file, \aoutfile\a, is not specified, \buniq\b writes "
+ "to standard output. If no \ainfile\a is given, or if the \ainfile\a "
+ "is \b-\b, \buniq\b reads from standard input with the start of "
+ "the file defined as the current offset.]"
+"[c:count?Output the number of times each line occurred along with "
+ "the line.]"
+"[d:repeated|duplicates?Output the first of each duplicate line.]"
+"[D:all-repeated?Output all duplicate lines as a group with an empty "
+ "line delimiter specified by \adelimit\a:]:?[delimit:=none]"
+ "{"
+ "[n:none?Do not delimit duplicate groups.]"
+ "[p:prepend?Prepend an empty line before each group.]"
+ "[s:separate?Separate each group with an empty line.]"
+ "}"
+"[f:skip-fields]#[fields?\afields\a is the number of fields to skip over "
+ "before checking for uniqueness. A field is the minimal string matching "
+ "the BRE \b[[:blank:]]]]*[^[:blank:]]]]*\b. -\anumber\a is equivalent to "
+ "\b--skip-fields\b=\anumber\a.]"
+"[i:ignore-case?Ignore case in comparisons.]"
+"[s:skip-chars]#[chars?\achars\a is the number of characters to skip over "
+ "before checking for uniqueness. If specified along with \b-f\b, "
+ "the first \achars\a after the first \afields\a are ignored. If "
+ "the \achars\a specifies more characters than are on the line, "
+ "an empty string will be used for comparison. +\anumber\a is "
+ "equivalent to \b--skip-chars\b=\anumber\a.]"
+"[u:unique?Output unique lines.]"
+"[w:check-chars]#[chars?\achars\a is the number of characters to compare "
+ "after skipping any specified fields and characters.]"
+"\n"
+"\n[infile [outfile]]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?The input file was successfully processed.]"
+ "[+>0?An error occurred.]"
+"}"
+"[+SEE ALSO?\bsort\b(1), \bgrep\b(1)]"
+;
+
+#include <cmd.h>
+
+#define C_FLAG 1
+#define D_FLAG 2
+#define U_FLAG 4
+
+#define CWIDTH 4
+#define MAXCNT 9999
+
+typedef int (*Compare_f)(const char*, const char*, size_t);
+
+static int uniq(Sfio_t *fdin, Sfio_t *fdout, int fields, int chars, int width, int mode, int* all, Compare_f compare)
+{
+ register int n, f, outsize=0, mb = mbwide();
+ register char *cp, *ep, *mp, *bufp, *outp;
+ char *orecp, *sbufp=0, *outbuff;
+ int reclen,oreclen= -1,count=0,cwidth=0,sep,next;
+ if(mode&C_FLAG)
+ cwidth = CWIDTH+1;
+ while(1)
+ {
+ if(bufp = sfgetr(fdin,'\n',0))
+ n = sfvalue(fdin);
+ else if(bufp = sfgetr(fdin,'\n',SF_LASTR))
+ {
+ n = sfvalue(fdin);
+ bufp = memcpy(fmtbuf(n + 1), bufp, n);
+ bufp[n++] = '\n';
+ }
+ else
+ n = 0;
+ if (n)
+ {
+ cp = bufp;
+ ep = cp + n;
+ if (f = fields)
+ while (f-->0 && cp<ep) /* skip over fields */
+ {
+ while (cp<ep && *cp==' ' || *cp=='\t')
+ cp++;
+ while (cp<ep && *cp!=' ' && *cp!='\t')
+ cp++;
+ }
+ if (chars)
+ {
+ if (mb)
+ for (f = chars; f; f--)
+ mbchar(cp);
+ else
+ cp += chars;
+ }
+ if ((reclen = n - (cp - bufp)) <= 0)
+ {
+ reclen = 1;
+ cp = bufp + n - 1;
+ }
+ else if (width >= 0 && width < reclen)
+ {
+ if (mb)
+ {
+ reclen = 0;
+ mp = cp;
+ while (reclen < width && mp < ep)
+ {
+ reclen++;
+ mbchar(mp);
+ }
+ reclen = mp - cp;
+ }
+ else
+ reclen = width;
+ }
+ }
+ else
+ reclen = -2;
+ if(reclen==oreclen && (!reclen || !(*compare)(cp,orecp,reclen)))
+ {
+ count++;
+ if (!all)
+ continue;
+ next = count;
+ }
+ else
+ {
+ next = 0;
+ if(outsize>0)
+ {
+ if(((mode&D_FLAG)&&count==0) || ((mode&U_FLAG)&&count))
+ {
+ if(outp!=sbufp)
+ sfwrite(fdout,outp,0);
+ }
+ else
+ {
+ if(cwidth)
+ {
+ if(count<9)
+ {
+ f = 0;
+ while(f < CWIDTH-1)
+ outp[f++] = ' ';
+ outp[f++] = '0' + count + 1;
+ outp[f] = ' ';
+ }
+ else if(count<MAXCNT)
+ {
+ count++;
+ f = CWIDTH;
+ outp[f--] = ' ';
+ do
+ {
+ outp[f--] = '0' + (count % 10);
+ } while (count /= 10);
+ while (f >= 0)
+ outp[f--] = ' ';
+ }
+ else
+ {
+ outsize -= (CWIDTH+1);
+ if(outp!=sbufp)
+ {
+ if(!(sbufp=fmtbuf(outsize)))
+ return(1);
+ memcpy(sbufp,outp+CWIDTH+1,outsize);
+ sfwrite(fdout,outp,0);
+ outp = sbufp;
+ }
+ else
+ outp += CWIDTH+1;
+ sfprintf(fdout,"%4d ",count+1);
+ }
+ }
+ if(sfwrite(fdout,outp,outsize) != outsize)
+ return(1);
+ }
+ }
+ }
+ if(n==0)
+ break;
+ if(count = next)
+ {
+ if(sfwrite(fdout,outp,outsize) != outsize)
+ return(1);
+ if(*all >= 0)
+ *all = 1;
+ sep = 0;
+ }
+ else
+ sep = all && *all > 0;
+ /* save current record */
+ if (!(outbuff = sfreserve(fdout, 0, 0)) || (outsize = sfvalue(fdout)) < 0)
+ return(1);
+ outp = outbuff;
+ if(outsize < n+cwidth+sep)
+ {
+ /* no room in outp, clear lock and use side buffer */
+ sfwrite(fdout,outp,0);
+ if(!(sbufp = outp=fmtbuf(outsize=n+cwidth+sep)))
+ return(1);
+ }
+ else
+ outsize = n+cwidth+sep;
+ memcpy(outp+cwidth+sep,bufp,n);
+ if(sep)
+ outp[cwidth] = '\n';
+ oreclen = reclen;
+ orecp = outp+cwidth+sep + (cp-bufp);
+ }
+ return(0);
+}
+
+int
+b_uniq(int argc, char** argv, Shbltin_t* context)
+{
+ register int mode=0;
+ register char *cp;
+ int fields=0, chars=0, width=-1;
+ Sfio_t *fpin, *fpout;
+ int* all = 0;
+ int sep;
+ Compare_f compare = (Compare_f)memcmp;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'c':
+ mode |= C_FLAG;
+ continue;
+ case 'd':
+ mode |= D_FLAG;
+ continue;
+ case 'D':
+ mode |= D_FLAG;
+ switch ((int)opt_info.num)
+ {
+ case 'p':
+ sep = 1;
+ break;
+ case 's':
+ sep = 0;
+ break;
+ default:
+ sep = -1;
+ break;
+ }
+ all = &sep;
+ continue;
+ case 'i':
+ compare = (Compare_f)strncasecmp;
+ continue;
+ case 'u':
+ mode |= U_FLAG;
+ continue;
+ case 'f':
+ if(*opt_info.option=='-')
+ fields = opt_info.num;
+ else
+ chars = opt_info.num;
+ continue;
+ case 's':
+ chars = opt_info.num;
+ continue;
+ case 'w':
+ width = opt_info.num;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if(all && (mode&C_FLAG))
+ error(2, "-c and -D are mutually exclusive");
+ if(error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if((cp = *argv) && (argv++,!streq(cp,"-")))
+ {
+ if(!(fpin = sfopen(NiL,cp,"r")))
+ error(ERROR_system(1),"%s: cannot open",cp);
+ }
+ else
+ fpin = sfstdin;
+ if(cp = *argv)
+ {
+ argv++;
+ if(!(fpout = sfopen(NiL,cp,"w")))
+ error(ERROR_system(1),"%s: cannot create",cp);
+ }
+ else
+ fpout = sfstdout;
+ if(*argv)
+ {
+ error(2, "too many arguments");
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ }
+ error_info.errors = uniq(fpin,fpout,fields,chars,width,mode,all,compare);
+ if(fpin!=sfstdin)
+ sfclose(fpin);
+ if(fpout!=sfstdout)
+ sfclose(fpout);
+ return(error_info.errors);
+}
+
diff --git a/src/lib/libcmd/vmstate.c b/src/lib/libcmd/vmstate.c
new file mode 100644
index 0000000..6367819
--- /dev/null
+++ b/src/lib/libcmd/vmstate.c
@@ -0,0 +1,197 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+
+#define FORMAT "region=%(region)p method=%(method)s flags=%(flags)s size=%(size)d segments=%(segments)d busy=(%(busy_size)d,%(busy_blocks)d,%(busy_max)d) free=(%(free_size)d,%(free_blocks)d,%(free_max)d)"
+
+static const char usage[] =
+"[-?\n@(#)$Id: vmstate (AT&T Research) 2010-04-08 $\n]"
+USAGE_LICENSE
+"[+NAME?vmstate - list the calling process vmalloc region state]"
+"[+DESCRIPTION?When invoked as a shell builtin, \bvmstate\b lists the "
+ "calling process \bvmalloc\b(3) state for all regions.]"
+"[f:format?List the ids specified by \aformat\a. \aformat\a follows "
+ "\bprintf\b(3) conventions, except that \bsfio\b(3) inline ids are used "
+ "instead of arguments: "
+ "%[-+]][\awidth\a[.\aprecis\a[.\abase\a]]]]]](\aid\a)\achar\a. The "
+ "supported \aid\as are:]:[format:=" FORMAT "]"
+ "{"
+ "[+method?The vmalloc method name.]"
+ "[+flags?The vmalloc method flags.]"
+ "[+size?The total region size.]"
+ "[+segments?The number of segments in the region.]"
+ "[+busy_size?The total busy block size.]"
+ "[+busy_blocks?The number of busy blocks.]"
+ "[+busy_max?The maximum busy block size.]"
+ "[+free_size?The total free block size.]"
+ "[+free_blocks?The number of free blocks.]"
+ "[+free_max?The maximum free block size.]"
+ "}"
+"[+SEE ALSO?\bvmalloc\b(3)]"
+;
+
+#include <cmd.h>
+#include <vmalloc.h>
+
+typedef struct State_s
+{
+ char* format;
+ Vmalloc_t* vm;
+ Vmstat_t vs;
+ unsigned int regions;
+ Vmalloc_t* region[256];
+} State_t;
+
+/*
+ * sfkeyprintf() lookup
+ * handle==0 for heading
+ */
+
+static int
+key(void* handle, Sffmt_t* fp, const char* arg, char** ps, Sflong_t* pn)
+{
+ register State_t* state = (State_t*)handle;
+ register char* s;
+
+ if (!(s = fp->t_str) || streq(s, "size"))
+ *pn = state->vs.extent;
+ else if (streq(s, "region"))
+ *pn = integralof(state->vm);
+ else if (streq(s, "segments"))
+ *pn = state->vs.n_seg;
+ else if (streq(s, "busy_size"))
+ *pn = state->vs.s_busy;
+ else if (streq(s, "busy_blocks"))
+ *pn = state->vs.n_busy;
+ else if (streq(s, "busy_max"))
+ *pn = state->vs.m_busy;
+ else if (streq(s, "flags"))
+ {
+ *ps = s = fmtbuf(32);
+#ifdef VM_TRUST
+ if (state->vs.mode & VM_TRUST)
+ s = strcopy(s, "TRUST|");
+#endif
+ if (state->vs.mode & VM_TRACE)
+ s = strcopy(s, "TRACE|");
+ if (state->vs.mode & VM_DBCHECK)
+ s = strcopy(s, "DBCHECK|");
+ if (state->vs.mode & VM_DBABORT)
+ s = strcopy(s, "DBABORT|");
+ if (s > *ps)
+ *(s - 1) = 0;
+ else
+ strcpy(s, "0");
+ }
+ else if (streq(s, "free_size"))
+ *pn = state->vs.s_free;
+ else if (streq(s, "free_blocks"))
+ *pn = state->vs.n_free;
+ else if (streq(s, "free_max"))
+ *pn = state->vs.m_free;
+ else if (streq(s, "format"))
+ *ps = (char*)state->format;
+ else if (streq(s, "method"))
+ {
+ if (state->vs.mode & VM_MTBEST)
+ *ps = "best";
+ else if (state->vs.mode & VM_MTPOOL)
+ *ps = "pool";
+ else if (state->vs.mode & VM_MTLAST)
+ *ps = "last";
+ else if (state->vs.mode & VM_MTDEBUG)
+ *ps = "debug";
+ else if (state->vs.mode & VM_MTPROFILE)
+ *ps = "profile";
+ else
+ *ps = "UNKNOWN";
+ }
+ else
+ {
+ error(2, "%s: unknown format identifier", s);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+visit(Vmalloc_t* vm, void* addr, size_t size, Vmdisc_t* disc, void* handle)
+{
+ State_t* state = (State_t*)handle;
+
+ if (vm != state->vm)
+ {
+ state->vm = vm;
+ if (state->regions < elementsof(state->region))
+ state->region[state->regions++] = vm;
+ }
+ return 0;
+}
+
+int
+b_vmstate(int argc, char** argv, Shbltin_t* context)
+{
+ register int i;
+ State_t state;
+
+ memset(&state, 0, sizeof(state));
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'f':
+ state.format = opt_info.arg;
+ continue;
+ case '?':
+ error(ERROR_USAGE|4, "%s", opt_info.arg);
+ break;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors || *argv)
+ error(ERROR_USAGE|4, "%s", optusage(NiL));
+ if (!state.format)
+ state.format = FORMAT;
+
+ /*
+ * the walk must do no allocations because it locks the regions
+ */
+
+ vmwalk(NiL, visit, &state);
+
+ /*
+ * now we can compute and list the state of each region
+ */
+
+ for (i = 0; i < state.regions; i++)
+ {
+ state.vm = state.region[i];
+ vmstat(state.vm, &state.vs);
+ sfkeyprintf(sfstdout, &state, state.format, key, NiL);
+ sfprintf(sfstdout, "\n");
+ }
+ return 0;
+}
diff --git a/src/lib/libcmd/wc.c b/src/lib/libcmd/wc.c
new file mode 100644
index 0000000..42956a0
--- /dev/null
+++ b/src/lib/libcmd/wc.c
@@ -0,0 +1,188 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * count the number of bytes, words, and lines in a file
+ */
+
+static const char usage[] =
+"[-?\n@(#)$Id: wc (AT&T Research) 2009-11-28 $\n]"
+USAGE_LICENSE
+"[+NAME?wc - print the number of bytes, words, and lines in files]"
+"[+DESCRIPTION?\bwc\b reads one or more input files and, by default, "
+ "for each file writes a line containing the number of newlines, "
+ "\aword\as, and bytes contained in each file followed by the "
+ "file name to standard output in that order. A \aword\a is "
+ "defined to be a non-zero length string delimited by \bisspace\b(3) "
+ "characters.]"
+"[+?If more than one file is specified, \bwc\b writes a total count "
+ "for all of the named files with \btotal\b written instead "
+ "of the file name.]"
+"[+?By default, \bwc\b writes all three counts. Options can specified "
+ "so that only certain counts are written. The options \b-c\b "
+ "and \b-m\b are mutually exclusive.]"
+"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \bwc\b "
+ "reads from standard input and no filename is written to standard "
+ "output. The start of the file is defined as the current offset.]"
+"[l:lines?List the line counts.]"
+"[w:words?List the word counts.]"
+"[c:bytes|chars:chars?List the byte counts.]"
+"[m|C:multibyte-chars?List the character counts.]"
+"[q:quiet?Suppress invalid multibyte character warnings.]"
+"[L:longest-line|max-line-length?List the longest line length; the newline,"
+ "if any, is not counted in the length.]"
+"[N!:utf8?For \bUTF-8\b locales \b--noutf8\b disables \bUTF-8\b "
+ "optimzations and relies on the native \bmbtowc\b(3).]"
+"\n"
+"\n[file ...]\n"
+"\n"
+"[+EXIT STATUS?]{"
+ "[+0?All files processed successfully.]"
+ "[+>0?One or more files failed to open or could not be read.]"
+"}"
+"[+SEE ALSO?\bcat\b(1), \bisspace\b(3)]"
+;
+
+
+#include <cmd.h>
+#include <wc.h>
+#include <ls.h>
+
+#define ERRORMAX 125
+
+static void printout(register Wc_t *wp, register char *name,register int mode)
+{
+ if (mode&WC_LINES)
+ sfprintf(sfstdout," %7I*d",sizeof(wp->lines),wp->lines);
+ if (mode&WC_WORDS)
+ sfprintf(sfstdout," %7I*d",sizeof(wp->words),wp->words);
+ if (mode&WC_CHARS)
+ sfprintf(sfstdout," %7I*d",sizeof(wp->chars),wp->chars);
+ if (mode&WC_LONGEST)
+ sfprintf(sfstdout," %7I*d",sizeof(wp->chars),wp->longest);
+ if (name)
+ sfprintf(sfstdout," %s",name);
+ sfputc(sfstdout,'\n');
+}
+
+int
+b_wc(int argc,register char **argv, Shbltin_t* context)
+{
+ register char *cp;
+ register int mode=0, n;
+ register Wc_t *wp;
+ Sfio_t *fp;
+ Sfoff_t tlines=0, twords=0, tchars=0;
+ struct stat statb;
+
+ cmdinit(argc, argv, context, ERROR_CATALOG, 0);
+ for (;;)
+ {
+ switch (optget(argv, usage))
+ {
+ case 'c':
+ mode |= WC_CHARS;
+ continue;
+ case 'l':
+ mode |= WC_LINES;
+ continue;
+ case 'L':
+ mode |= WC_LONGEST;
+ continue;
+ case 'N':
+ if (!opt_info.num)
+ mode |= WC_NOUTF8;
+ continue;
+ case 'm':
+ case 'C':
+ mode |= WC_MBYTE;
+ continue;
+ case 'q':
+ mode |= WC_QUIET;
+ continue;
+ case 'w':
+ mode |= WC_WORDS;
+ continue;
+ case ':':
+ error(2, "%s", opt_info.arg);
+ break;
+ case '?':
+ error(ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ break;
+ }
+ argv += opt_info.index;
+ if (error_info.errors)
+ error(ERROR_usage(2), "%s", optusage(NiL));
+ if (mode&WC_MBYTE)
+ {
+ if (mode&WC_CHARS)
+ error(2, "-c and -C are mutually exclusive");
+ if (!mbwide())
+ mode &= ~WC_MBYTE;
+ mode |= WC_CHARS;
+ }
+ if (!(mode&(WC_WORDS|WC_CHARS|WC_LINES|WC_MBYTE|WC_LONGEST)))
+ mode |= (WC_WORDS|WC_CHARS|WC_LINES);
+ if (!(wp = wc_init(mode)))
+ error(3,"internal error");
+ if (cp = *argv)
+ argv++;
+ n = 0;
+ do
+ {
+ if (!cp || streq(cp,"-"))
+ fp = sfstdin;
+ else if (!(fp = sfopen(NiL,cp,"r")))
+ {
+ error(ERROR_system(0),"%s: cannot open",cp);
+ continue;
+ }
+ if (cp)
+ n++;
+ if (!(mode&(WC_WORDS|WC_LINES|WC_MBYTE|WC_LONGEST)) && fstat(sffileno(fp),&statb)>=0
+ && S_ISREG(statb.st_mode))
+ {
+ wp->chars = statb.st_size - lseek(sffileno(fp),0L,1);
+ lseek(sffileno(fp),0L,2);
+ }
+ else
+ wc_count(wp, fp, cp);
+ if (fp!=sfstdin)
+ sfclose(fp);
+ tchars += wp->chars;
+ twords += wp->words;
+ tlines += wp->lines;
+ printout(wp,cp,mode);
+ } while (cp= *argv++);
+ if (n > 1)
+ {
+ wp->lines = tlines;
+ wp->chars = tchars;
+ wp->words = twords;
+ printout(wp,"total",mode);
+ }
+ return error_info.errors<ERRORMAX?error_info.errors:ERRORMAX;
+}
diff --git a/src/lib/libcmd/wc.h b/src/lib/libcmd/wc.h
new file mode 100644
index 0000000..972767f
--- /dev/null
+++ b/src/lib/libcmd/wc.h
@@ -0,0 +1,59 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * header for wc library interface
+ */
+
+#ifndef _WC_H
+#define _WC_H
+
+#include <ast.h>
+
+#define WC_LINES 0x01
+#define WC_WORDS 0x02
+#define WC_CHARS 0x04
+#define WC_MBYTE 0x08
+#define WC_LONGEST 0x10
+#define WC_QUIET 0x20
+#define WC_NOUTF8 0x40
+
+typedef struct
+{
+ char type[1<<CHAR_BIT];
+ Sfoff_t words;
+ Sfoff_t lines;
+ Sfoff_t chars;
+ Sfoff_t longest;
+ int mode;
+ int mb;
+} Wc_t;
+
+#define wc_count _cmd_wccount
+#define wc_init _cmd_wcinit
+
+extern Wc_t* wc_init(int);
+extern int wc_count(Wc_t*, Sfio_t*, const char*);
+
+#endif /* _WC_H */
diff --git a/src/lib/libcmd/wclib.c b/src/lib/libcmd/wclib.c
new file mode 100644
index 0000000..0d52fda
--- /dev/null
+++ b/src/lib/libcmd/wclib.c
@@ -0,0 +1,505 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1992-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * David Korn
+ * AT&T Bell Laboratories
+ *
+ * library interface for word count
+ */
+
+#include <cmd.h>
+#include <wc.h>
+#include <ctype.h>
+
+#if _hdr_wchar && _hdr_wctype && _lib_iswctype
+
+#include <wchar.h>
+#include <wctype.h>
+#include <lc.h>
+
+#else
+
+#ifndef iswspace
+#define iswspace(x) isspace(x)
+#endif
+
+#endif
+
+#define WC_SP 0x08
+#define WC_NL 0x10
+#define WC_MB 0x20
+#define WC_ERR 0x40
+
+#define eol(c) ((c)&WC_NL)
+#define mbc(c) ((c)&WC_MB)
+#define spc(c) ((c)&WC_SP)
+#define mb2wc(w,p,n) (*ast.mb_towc)(&w,(char*)p,n)
+
+Wc_t* wc_init(int mode)
+{
+ register int n;
+ register int w;
+ Wc_t* wp;
+
+ if (!(wp = (Wc_t*)stakalloc(sizeof(Wc_t))))
+ return 0;
+ if (!mbwide())
+ wp->mb = 0;
+#if _hdr_wchar && _hdr_wctype && _lib_iswctype
+ else if (!(mode & WC_NOUTF8) && (lcinfo(LC_CTYPE)->lc->flags & LC_utf8))
+ wp->mb = 1;
+#endif
+ else
+ wp->mb = -1;
+ w = mode & WC_WORDS;
+ for (n = (1<<CHAR_BIT); --n >= 0;)
+ wp->type[n] = (w && isspace(n)) ? WC_SP : 0;
+ wp->type['\n'] = WC_SP|WC_NL;
+ if ((mode & (WC_MBYTE|WC_WORDS)) && wp->mb > 0)
+ {
+ for (n = 0; n < 64; n++)
+ {
+ wp->type[0x80+n] |= WC_MB;
+ if (n<32)
+ wp->type[0xc0+n] |= WC_MB+1;
+ else if (n<48)
+ wp->type[0xc0+n] |= WC_MB+2;
+ else if (n<56)
+ wp->type[0xc0+n] |= WC_MB+3;
+ else if (n<60)
+ wp->type[0xc0+n] |= WC_MB+4;
+ else if (n<62)
+ wp->type[0xc0+n] |= WC_MB+5;
+ }
+ wp->type[0xc0] = WC_MB|WC_ERR;
+ wp->type[0xc1] = WC_MB|WC_ERR;
+ wp->type[0xfe] = WC_MB|WC_ERR;
+ wp->type[0xff] = WC_MB|WC_ERR;
+ }
+ wp->mode = mode;
+ return wp;
+}
+
+static int invalid(const char *file, int nlines)
+{
+ error_info.file = (char*)file;
+ error_info.line = nlines;
+ error(ERROR_SYSTEM|1, "invalid multibyte character");
+ error_info.file = 0;
+ error_info.line = 0;
+ return nlines;
+}
+
+/*
+ * handle utf space characters
+ */
+
+static int chkstate(int state, register unsigned int c)
+{
+ switch(state)
+ {
+ case 1:
+ state = (c==0x9a?4:0);
+ break;
+ case 2:
+ state = ((c==0x80||c==0x81)?6+(c&1):0);
+ break;
+ case 3:
+ state = (c==0x80?5:0);
+ break;
+ case 4:
+ state = (c==0x80?10:0);
+ break;
+ case 5:
+ state = (c==0x80?10:0);
+ break;
+ case 6:
+ state = 0;
+ if(c==0xa0 || c==0xa1)
+ return(10);
+ else if((c&0xf0)== 0x80)
+ {
+ if((c&=0xf)==7)
+ return(iswspace(0x2007)?10:0);
+ if(c<=0xb)
+ return(10);
+ }
+ else if(c==0xaf && iswspace(0x202f))
+ return(10);
+ break;
+ case 7:
+ state = (c==0x9f?10:0);
+ break;
+ case 8:
+ return (iswspace(c)?10:0);
+ }
+ return state;
+}
+
+/*
+ * compute the line, word, and character count for file <fd>
+ */
+
+int wc_count(Wc_t *wp, Sfio_t *fd, const char* file)
+{
+ register char* type = wp->type;
+ register unsigned char* cp;
+ register Sfoff_t nbytes;
+ register Sfoff_t nchars;
+ register Sfoff_t nwords;
+ register Sfoff_t nlines;
+ register Sfoff_t eline = -1;
+ register Sfoff_t longest = 0;
+ register ssize_t c;
+ register unsigned char* endbuff;
+ register int lasttype = WC_SP;
+ unsigned int lastchar;
+ ssize_t n;
+ ssize_t o;
+ unsigned char* buff;
+ wchar_t x;
+ unsigned char side[32];
+
+ sfset(fd,SF_WRITE,1);
+ nlines = nwords = nchars = nbytes = 0;
+ wp->longest = 0;
+ if (wp->mb < 0 && (wp->mode & (WC_MBYTE|WC_WORDS)))
+ {
+ cp = buff = endbuff = 0;
+ for (;;)
+ {
+ if (cp >= endbuff || (n = mb2wc(x, cp, endbuff-cp)) < 0)
+ {
+ if ((o = endbuff-cp) < sizeof(side))
+ {
+ if (buff)
+ {
+ if (o)
+ memcpy(side, cp, o);
+ mbinit();
+ }
+ else
+ o = 0;
+ cp = side + o;
+ if (!(buff = (unsigned char*)sfreserve(fd, SF_UNBOUND, 0)) || (n = sfvalue(fd)) <= 0)
+ {
+ if ((nchars - longest) > wp->longest)
+ wp->longest = nchars - longest;
+ break;
+ }
+ nbytes += n;
+ if ((c = sizeof(side) - o) > n)
+ c = n;
+ if (c)
+ memcpy(cp, buff, c);
+ endbuff = buff + n;
+ cp = side;
+ x = mbchar(cp);
+ if ((cp-side) < o)
+ {
+ cp = buff;
+ nchars += (cp-side) - 1;
+ }
+ else
+ cp = buff + (cp-side) - o;
+ }
+ else
+ {
+ cp++;
+ x = -1;
+ }
+ if (x == -1 && eline != nlines && !(wp->mode & WC_QUIET))
+ eline = invalid(file, nlines);
+ }
+ else
+ cp += n ? n : 1;
+ if (x == '\n')
+ {
+ if ((nchars - longest) > wp->longest)
+ wp->longest = nchars - longest;
+ longest = nchars + 1;
+ nlines++;
+ lasttype = 1;
+ }
+ else if (iswspace(x))
+ lasttype = 1;
+ else if (lasttype)
+ {
+ lasttype = 0;
+ nwords++;
+ }
+ nchars++;
+ }
+ if (!(wp->mode & WC_MBYTE))
+ nchars = nbytes;
+ }
+ else if (!wp->mb && !(wp->mode & WC_LONGEST) || wp->mb > 0 && !(wp->mode & (WC_MBYTE|WC_WORDS|WC_LONGEST)))
+ {
+ if (!(wp->mode & (WC_MBYTE|WC_WORDS|WC_LONGEST)))
+ {
+ while ((cp = (unsigned char*)sfreserve(fd, SF_UNBOUND, 0)) && (c = sfvalue(fd)) > 0)
+ {
+ nchars += c;
+ endbuff = cp + c;
+ if (*--endbuff == '\n')
+ nlines++;
+ else
+ *endbuff = '\n';
+ for (;;)
+ if (*cp++ == '\n')
+ {
+ if (cp > endbuff)
+ break;
+ nlines++;
+ }
+ }
+ }
+ else
+ {
+ while ((cp = buff = (unsigned char*)sfreserve(fd, SF_UNBOUND, 0)) && (c = sfvalue(fd)) > 0)
+ {
+ nchars += c;
+ /* check to see whether first character terminates word */
+ if (c==1)
+ {
+ if (eol(lasttype))
+ nlines++;
+ if ((c = type[*cp]) && !lasttype)
+ nwords++;
+ lasttype = c;
+ continue;
+ }
+ if (!lasttype && type[*cp])
+ nwords++;
+ lastchar = cp[--c];
+ *(endbuff = cp+c) = '\n';
+ c = lasttype;
+ /* process each buffer */
+ for (;;)
+ {
+ /* process spaces and new-lines */
+ do
+ {
+ if (eol(c))
+ for (;;)
+ {
+ /* check for end of buffer */
+ if (cp > endbuff)
+ goto beob;
+ nlines++;
+ if (*cp != '\n')
+ break;
+ cp++;
+ }
+ } while (c = type[*cp++]);
+ /* skip over word characters */
+ while (!(c = type[*cp++]));
+ nwords++;
+ }
+ beob:
+ if ((cp -= 2) >= buff)
+ c = type[*cp];
+ else
+ c = lasttype;
+ lasttype = type[lastchar];
+ /* see if was in word */
+ if (!c && !lasttype)
+ nwords--;
+ }
+ if (eol(lasttype))
+ nlines++;
+ else if (!lasttype)
+ nwords++;
+ }
+ }
+ else
+ {
+ int lineoff=0;
+ int skip=0;
+ int adjust=0;
+ int state=0;
+ int oldc;
+ int xspace;
+ int wasspace = 1;
+ unsigned char* start;
+
+ lastchar = 0;
+ start = (endbuff = side) + 1;
+ xspace = iswspace(0xa0) || iswspace(0x85);
+ while ((cp = buff = (unsigned char*)sfreserve(fd, SF_UNBOUND, 0)) && (c = sfvalue(fd)) > 0)
+ {
+ nbytes += c;
+ nchars += c;
+ start = cp-lineoff;
+ /* check to see whether first character terminates word */
+ if(c==1)
+ {
+ if(eol(lasttype))
+ nlines++;
+ if((c = type[*cp]) && !lasttype)
+ nwords++;
+ lasttype = c;
+ endbuff = start;
+ continue;
+ }
+ lastchar = cp[--c];
+ endbuff = cp+c;
+ cp[c] = '\n';
+ if(mbc(lasttype))
+ {
+ c = lasttype;
+ goto mbyte;
+ }
+ if(!lasttype && spc(type[*cp]))
+ nwords++;
+ c = lasttype;
+ /* process each buffer */
+ for (;;)
+ {
+ /* process spaces and new-lines */
+ spaces:
+ do
+ {
+ if (eol(c))
+ {
+ /* check for end of buffer */
+ if (cp > endbuff)
+ goto eob;
+ if(wp->mode&WC_LONGEST)
+ {
+ if((cp-start)-adjust > longest)
+ longest = (cp-start)-adjust-1;
+ start = cp;
+ }
+ nlines++;
+ nchars -= adjust;
+ adjust = 0;
+ }
+ } while (spc(c = type[*cp++]));
+ wasspace=1;
+ if(mbc(c))
+ {
+ mbyte:
+ do
+ {
+ if(c&WC_ERR)
+ goto err;
+ if(skip && (c&7))
+ break;
+ if(!skip)
+ {
+ if(!(c&7))
+ {
+ skip=1;
+ break;
+ }
+ skip = (c&7);
+ adjust += skip;
+ state = 0;
+ if(skip==2 && (cp[-1]&0xc)==0 && (state=(cp[-1]&0x3)))
+ oldc = *cp;
+ else if(xspace && cp[-1]==0xc2)
+ {
+ state = 8;
+ oldc = *cp;
+ }
+ }
+ else
+ {
+ skip--;
+ if(state && (state=chkstate(state,oldc)))
+ {
+ if(state==10)
+ {
+ if(!wasspace)
+ nwords++;
+ wasspace = 1;
+ state=0;
+ goto spaces;
+ }
+ oldc = *cp;
+ }
+ }
+ } while (mbc(c = type[*cp++]));
+ wasspace = 0;
+ if(skip)
+ {
+ if(eol(c) && (cp > endbuff))
+ goto eob;
+ err:
+ skip = 0;
+ state = 0;
+ if(eline!=nlines && !(wp->mode & WC_QUIET))
+ eline = invalid(file, nlines);
+ while(mbc(c) && ((c|WC_ERR) || (c&7)==0))
+ c=type[*cp++];
+ if(eol(c) && (cp > endbuff))
+ {
+ c = WC_MB|WC_ERR;
+ goto eob;
+ }
+ if(mbc(c))
+ goto mbyte;
+ else if(c&WC_SP)
+ goto spaces;
+ }
+ if(spc(c))
+ {
+ nwords++;
+ continue;
+ }
+ }
+ /* skip over word characters */
+ while(!(c = type[*cp++]));
+ if(mbc(c))
+ goto mbyte;
+ nwords++;
+ }
+ eob:
+ lineoff = cp-start;
+ if((cp -= 2) >= buff)
+ c = type[*cp];
+ else
+ c = lasttype;
+ lasttype = type[lastchar];
+ /* see if was in word */
+ if(!c && !lasttype)
+ nwords--;
+ }
+ if ((wp->mode&WC_LONGEST) && ((endbuff + 1 - start) - adjust - (lastchar == '\n')) > longest)
+ longest = (endbuff + 1 - start) - adjust - (lastchar == '\n');
+ wp->longest = longest;
+ if (eol(lasttype))
+ nlines++;
+ else if (!lasttype)
+ nwords++;
+ if (wp->mode & WC_MBYTE)
+ nchars -= adjust;
+ else
+ nchars = nbytes;
+ }
+ wp->chars = nchars;
+ wp->words = nwords;
+ wp->lines = nlines;
+ return 0;
+}
+