diff options
Diffstat (limited to 'src/lib/libcmd')
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; +} + |