Index: src/Makefile.lint =================================================================== --- src/Makefile.lint (revision 676) +++ src/Makefile.lint (revision 740) @@ -23,7 +23,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)Makefile.lint 1.119 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # include global definitions @@ -305,7 +305,6 @@ lib/libc \ lib/libc_db \ lib/libcfgadm \ - lib/libcmd \ lib/libcmdutils \ lib/libcontract \ lib/libcryptoutil \ Index: src/pkgdefs/SUNWarc/prototype_com =================================================================== --- src/pkgdefs/SUNWarc/prototype_com (revision 676) +++ src/pkgdefs/SUNWarc/prototype_com (revision 740) @@ -24,7 +24,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_com 1.135 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -80,8 +80,6 @@ s none usr/lib/llib-lbsm.ln=../../lib/llib-lbsm.ln s none usr/lib/llib-lc=../../lib/llib-lc s none usr/lib/llib-lc.ln=../../lib/llib-lc.ln -f none usr/lib/llib-lcmd 644 root bin -f none usr/lib/llib-lcmd.ln 644 root bin f none usr/lib/llib-lcrypt 644 root bin f none usr/lib/llib-lcrypt.ln 644 root bin f none usr/lib/llib-lcfgadm 644 root bin Index: src/pkgdefs/SUNWarc/prototype_sparc =================================================================== --- src/pkgdefs/SUNWarc/prototype_sparc (revision 676) +++ src/pkgdefs/SUNWarc/prototype_sparc (revision 740) @@ -24,7 +24,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_sparc 1.23 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -68,7 +68,6 @@ s none usr/lib/sparcv9/llib-lpam.ln=../../../lib/sparcv9/llib-lpam.ln s none usr/lib/sparcv9/llib-lc.ln=../../../lib/sparcv9/llib-lc.ln f none usr/lib/sparcv9/llib-lcfgadm.ln 644 root bin -f none usr/lib/sparcv9/llib-lcmd.ln 644 root bin s none usr/lib/sparcv9/llib-lcontract.ln=../../../lib/sparcv9/llib-lcontract.ln s none usr/lib/sparcv9/llib-lctf.ln=../../../lib/sparcv9/llib-lctf.ln f none usr/lib/sparcv9/llib-lcrypt.ln 644 root bin Index: src/pkgdefs/SUNWarc/prototype_i386 =================================================================== --- src/pkgdefs/SUNWarc/prototype_i386 (revision 676) +++ src/pkgdefs/SUNWarc/prototype_i386 (revision 740) @@ -24,7 +24,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_i386 1.12 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -72,7 +72,6 @@ s none usr/lib/amd64/llib-lpam.ln=../../../lib/amd64/llib-lpam.ln s none usr/lib/amd64/llib-lc.ln=../../../lib/amd64/llib-lc.ln f none usr/lib/amd64/llib-lcfgadm.ln 644 root bin -f none usr/lib/amd64/llib-lcmd.ln 644 root bin s none usr/lib/amd64/llib-lcontract.ln=../../../lib/amd64/llib-lcontract.ln s none usr/lib/amd64/llib-lctf.ln=../../../lib/amd64/llib-lctf.ln f none usr/lib/amd64/llib-lcrypt.ln 644 root bin Index: src/pkgdefs/SUNWhea/prototype_com =================================================================== --- src/pkgdefs/SUNWhea/prototype_com (revision 676) +++ src/pkgdefs/SUNWhea/prototype_com (revision 740) @@ -23,7 +23,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_com 1.460 07/03/10 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -63,6 +63,107 @@ f none usr/include/asm/sunddi.h 644 root bin f none usr/include/asm/thread.h 644 root bin f none usr/include/assert.h 644 root bin +d none usr/include/ast 755 root bin +f none usr/include/ast/align.h 644 root bin +f none usr/include/ast/ast.h 644 root bin +f none usr/include/ast/ast_botch.h 644 root bin +f none usr/include/ast/ast_ccode.h 644 root bin +f none usr/include/ast/ast_common.h 644 root bin +f none usr/include/ast/ast_dir.h 644 root bin +f none usr/include/ast/ast_dirent.h 644 root bin +f none usr/include/ast/ast_fcntl.h 644 root bin +f none usr/include/ast/ast_float.h 644 root bin +f none usr/include/ast/ast_fs.h 644 root bin +f none usr/include/ast/ast_getopt.h 644 root bin +f none usr/include/ast/ast_iconv.h 644 root bin +f none usr/include/ast/ast_lib.h 644 root bin +f none usr/include/ast/ast_limits.h 644 root bin +f none usr/include/ast/ast_map.h 644 root bin +f none usr/include/ast/ast_mmap.h 644 root bin +f none usr/include/ast/ast_mode.h 644 root bin +f none usr/include/ast/ast_nl_types.h 644 root bin +f none usr/include/ast/ast_param.h 644 root bin +f none usr/include/ast/ast_standards.h 644 root bin +f none usr/include/ast/ast_std.h 644 root bin +f none usr/include/ast/ast_stdio.h 644 root bin +f none usr/include/ast/ast_sys.h 644 root bin +f none usr/include/ast/ast_time.h 644 root bin +f none usr/include/ast/ast_tty.h 644 root bin +f none usr/include/ast/ast_types.h 644 root bin +f none usr/include/ast/ast_version.h 644 root bin +f none usr/include/ast/ast_vfork.h 644 root bin +f none usr/include/ast/ast_wait.h 644 root bin +f none usr/include/ast/ast_wchar.h 644 root bin +f none usr/include/ast/ast_windows.h 644 root bin +f none usr/include/ast/bytesex.h 644 root bin +f none usr/include/ast/ccode.h 644 root bin +f none usr/include/ast/cdt.h 644 root bin +f none usr/include/ast/cmd.h 644 root bin +f none usr/include/ast/cmdext.h 644 root bin +f none usr/include/ast/debug.h 644 root bin +f none usr/include/ast/dirent.h 644 root bin +f none usr/include/ast/dlldefs.h 644 root bin +f none usr/include/ast/dt.h 644 root bin +f none usr/include/ast/endian.h 644 root bin +f none usr/include/ast/error.h 644 root bin +f none usr/include/ast/find.h 644 root bin +f none usr/include/ast/fnmatch.h 644 root bin +f none usr/include/ast/fnv.h 644 root bin +f none usr/include/ast/fs3d.h 644 root bin +f none usr/include/ast/fts.h 644 root bin +f none usr/include/ast/ftw.h 644 root bin +f none usr/include/ast/ftwalk.h 644 root bin +f none usr/include/ast/getopt.h 644 root bin +f none usr/include/ast/glob.h 644 root bin +f none usr/include/ast/hash.h 644 root bin +f none usr/include/ast/hashkey.h 644 root bin +f none usr/include/ast/hashpart.h 644 root bin +f none usr/include/ast/history.h 644 root bin +f none usr/include/ast/iconv.h 644 root bin +f none usr/include/ast/lc.h 644 root bin +f none usr/include/ast/ls.h 644 root bin +f none usr/include/ast/magic.h 644 root bin +f none usr/include/ast/magicid.h 644 root bin +f none usr/include/ast/mc.h 644 root bin +f none usr/include/ast/mime.h 644 root bin +f none usr/include/ast/mnt.h 644 root bin +f none usr/include/ast/modecanon.h 644 root bin +f none usr/include/ast/modex.h 644 root bin +f none usr/include/ast/namval.h 644 root bin +f none usr/include/ast/nl_types.h 644 root bin +f none usr/include/ast/nval.h 644 root bin +f none usr/include/ast/option.h 644 root bin +f none usr/include/ast/preroot.h 644 root bin +f none usr/include/ast/proc.h 644 root bin +f none usr/include/ast/prototyped.h 644 root bin +f none usr/include/ast/re_comp.h 644 root bin +f none usr/include/ast/recfmt.h 644 root bin +f none usr/include/ast/regex.h 644 root bin +f none usr/include/ast/regexp.h 644 root bin +f none usr/include/ast/sfdisc.h 644 root bin +f none usr/include/ast/sfio.h 644 root bin +f none usr/include/ast/sfio_s.h 644 root bin +f none usr/include/ast/sfio_t.h 644 root bin +f none usr/include/ast/shell.h 644 root bin +f none usr/include/ast/sig.h 644 root bin +f none usr/include/ast/stack.h 644 root bin +f none usr/include/ast/stak.h 644 root bin +f none usr/include/ast/stdio.h 644 root bin +f none usr/include/ast/stk.h 644 root bin +f none usr/include/ast/swap.h 644 root bin +f none usr/include/ast/tar.h 644 root bin +f none usr/include/ast/times.h 644 root bin +f none usr/include/ast/tm.h 644 root bin +f none usr/include/ast/tmx.h 644 root bin +f none usr/include/ast/tok.h 644 root bin +f none usr/include/ast/tv.h 644 root bin +f none usr/include/ast/usage.h 644 root bin +f none usr/include/ast/vdb.h 644 root bin +f none usr/include/ast/vecargs.h 644 root bin +f none usr/include/ast/vmalloc.h 644 root bin +f none usr/include/ast/wait.h 644 root bin +f none usr/include/ast/wchar.h 644 root bin +f none usr/include/ast/wordexp.h 644 root bin f none usr/include/atomic.h 644 root bin f none usr/include/auth_attr.h 644 root bin d none usr/include/bsm 755 root bin Index: src/pkgdefs/Makefile =================================================================== --- src/pkgdefs/Makefile (revision 676) +++ src/pkgdefs/Makefile (revision 740) @@ -23,7 +23,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)Makefile 1.345 07/03/19 SMI" +# ident "%Z%%M% %I% %E% SMI" # include $(SRC)/Makefile.master @@ -161,6 +161,7 @@ SUNWatfsu \ SUNWarc \ SUNWarcr \ + SUNWastdev \ SUNWav1394 \ SUNWbart \ SUNWbge \ Index: src/pkgdefs/SUNWcsl/prototype_com =================================================================== --- src/pkgdefs/SUNWcsl/prototype_com (revision 676) +++ src/pkgdefs/SUNWcsl/prototype_com (revision 740) @@ -24,7 +24,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_com 1.106 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -73,6 +73,7 @@ s none usr/lib/libadm.so.1=../../lib/libadm.so.1 s none usr/lib/libaio.so=../../lib/libaio.so.1 s none usr/lib/libaio.so.1=../../lib/libaio.so.1 +f none usr/lib/libast.so.1 755 root bin s none usr/lib/libavl.so.1=../../lib/libavl.so.1 s none usr/lib/libbsdmalloc.so=./libbsdmalloc.so.1 f none usr/lib/libbsdmalloc.so.1 755 root bin @@ -115,6 +116,7 @@ s none usr/lib/libdisasm.so=./libdisasm.so.1 s none usr/lib/libdl.so=../../lib/libdl.so.1 s none usr/lib/libdl.so.1=../../lib/libdl.so.1 +f none usr/lib/libdll.so.1 755 root bin s none usr/lib/libdoor.so=../../lib/libdoor.so.1 s none usr/lib/libdoor.so.1=../../lib/libdoor.so.1 s none usr/lib/libefi.so=../../lib/libefi.so.1 @@ -239,6 +241,7 @@ s none usr/lib/libsecdb.so.1=../../lib/libsecdb.so.1 s none usr/lib/libsendfile.so=../../lib/libsendfile.so.1 s none usr/lib/libsendfile.so.1=../../lib/libsendfile.so.1 +f none usr/lib/libshell.so.1 755 root bin s none usr/lib/libsip.so=./libsip.so.1 f none usr/lib/libsip.so.1 755 root bin s none usr/lib/libsldap.so=libsldap.so.1 Index: src/pkgdefs/SUNWcsl/prototype_sparc =================================================================== --- src/pkgdefs/SUNWcsl/prototype_sparc (revision 676) +++ src/pkgdefs/SUNWcsl/prototype_sparc (revision 740) @@ -24,7 +24,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_sparc 1.31 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -213,6 +213,7 @@ s none usr/lib/sparcv9/libadm.so=../../../lib/sparcv9/libadm.so.1 s none usr/lib/sparcv9/libaio.so.1=../../../lib/sparcv9/libaio.so.1 s none usr/lib/sparcv9/libaio.so=../../../lib/sparcv9/libaio.so.1 +f none usr/lib/sparcv9/libast.so.1 755 root bin s none usr/lib/sparcv9/libavl.so.1=../../../lib/sparcv9/libavl.so.1 s none usr/lib/sparcv9/libc.so.1=../../../lib/sparcv9/libc.so.1 s none usr/lib/sparcv9/libc.so=../../../lib/sparcv9/libc.so.1 @@ -228,6 +229,7 @@ s none usr/lib/sparcv9/libdevinfo.so=../../../lib/sparcv9/libdevinfo.so.1 s none usr/lib/sparcv9/libdl.so.1=../../../lib/sparcv9/libdl.so.1 s none usr/lib/sparcv9/libdl.so=../../../lib/sparcv9/libdl.so.1 +f none usr/lib/sparcv9/libdll.so.1 755 root bin s none usr/lib/sparcv9/libdoor.so.1=../../../lib/sparcv9/libdoor.so.1 s none usr/lib/sparcv9/libdoor.so=../../../lib/sparcv9/libdoor.so.1 s none usr/lib/sparcv9/libefi.so.1=../../../lib/sparcv9/libefi.so.1 @@ -290,6 +292,7 @@ s none usr/lib/sparcv9/libsecdb.so=../../../lib/sparcv9/libsecdb.so.1 s none usr/lib/sparcv9/libsendfile.so.1=../../../lib/sparcv9/libsendfile.so.1 s none usr/lib/sparcv9/libsendfile.so=../../../lib/sparcv9/libsendfile.so.1 +f none usr/lib/sparcv9/libshell.so.1 755 root bin f none usr/lib/sparcv9/libsip.so.1 755 root bin s none usr/lib/sparcv9/libsip.so=./libsip.so.1 f none usr/lib/sparcv9/libsldap.so.1 755 root bin Index: src/pkgdefs/SUNWcsl/prototype_i386 =================================================================== --- src/pkgdefs/SUNWcsl/prototype_i386 (revision 676) +++ src/pkgdefs/SUNWcsl/prototype_i386 (revision 740) @@ -23,7 +23,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_i386 1.29 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -224,6 +224,7 @@ s none usr/lib/amd64/libadm.so=../../../lib/amd64/libadm.so.1 s none usr/lib/amd64/libaio.so.1=../../../lib/amd64/libaio.so.1 s none usr/lib/amd64/libaio.so=../../../lib/amd64/libaio.so.1 +f none usr/lib/amd64/libast.so.1 755 root bin s none usr/lib/amd64/libavl.so.1=../../../lib/amd64/libavl.so.1 s none usr/lib/amd64/libc.so.1=../../../lib/amd64/libc.so.1 s none usr/lib/amd64/libc.so=../../../lib/amd64/libc.so.1 @@ -239,6 +240,7 @@ s none usr/lib/amd64/libdisasm.so=libdisasm.so.1 s none usr/lib/amd64/libdl.so.1=../../../lib/amd64/libdl.so.1 s none usr/lib/amd64/libdl.so=../../../lib/amd64/libdl.so.1 +f none usr/lib/amd64/libdll.so.1 755 root bin s none usr/lib/amd64/libdoor.so.1=../../../lib/amd64/libdoor.so.1 s none usr/lib/amd64/libdoor.so=../../../lib/amd64/libdoor.so.1 s none usr/lib/amd64/libefi.so.1=../../../lib/amd64/libefi.so.1 @@ -302,6 +304,7 @@ s none usr/lib/amd64/libsecdb.so=../../../lib/amd64/libsecdb.so.1 s none usr/lib/amd64/libsendfile.so.1=../../../lib/amd64/libsendfile.so.1 s none usr/lib/amd64/libsendfile.so=../../../lib/amd64/libsendfile.so.1 +f none usr/lib/amd64/libshell.so.1 755 root bin f none usr/lib/amd64/libsip.so.1 755 root bin s none usr/lib/amd64/libsip.so=./libsip.so.1 s none usr/lib/amd64/libsldap.so=libsldap.so.1 Index: src/pkgdefs/SUNWcsr/prototype_com =================================================================== --- src/pkgdefs/SUNWcsr/prototype_com (revision 676) +++ src/pkgdefs/SUNWcsr/prototype_com (revision 740) @@ -23,7 +23,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_com 1.372 07/01/18 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -186,6 +186,7 @@ s none etc/install=../usr/sbin/install e preserve etc/ioctl.syscon 644 root sys s none etc/killall=../usr/sbin/killall +e renameold etc/ksh.kshrc 644 root sys s none etc/labelit=../usr/sbin/labelit d none etc/lib 755 root sys s none etc/lib/ld.so.1=../../lib/ld.so.1 Index: src/pkgdefs/SUNWcsu/prototype_com =================================================================== --- src/pkgdefs/SUNWcsu/prototype_com (revision 676) +++ src/pkgdefs/SUNWcsu/prototype_com (revision 740) @@ -22,7 +22,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_com 1.542 07/01/22 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -165,6 +165,7 @@ l none usr/bin/kill=../../usr/bin/alias f none usr/bin/kmfcfg 555 root bin f none usr/bin/ksh 555 root bin +l none usr/bin/ksh93=../../usr/lib/isaexec f none usr/bin/line 555 root bin f none usr/bin/listdgrp 555 root bin f none usr/bin/listusers 555 root bin @@ -232,6 +233,7 @@ l none usr/bin/red=../../usr/bin/ed f none usr/bin/renice 555 root bin l none usr/bin/rksh=../../usr/bin/ksh +l none usr/bin/rksh93=../../usr/lib/isaexec f none usr/bin/rm 555 root bin s none usr/bin/rmail=./mail f none usr/bin/rmdir 555 root bin Index: src/pkgdefs/SUNWcsu/prototype_sparc =================================================================== --- src/pkgdefs/SUNWcsu/prototype_sparc (revision 676) +++ src/pkgdefs/SUNWcsu/prototype_sparc (revision 740) @@ -22,7 +22,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)prototype_sparc 1.129 07/01/10 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -47,10 +47,13 @@ # SUNWcsu # d none usr/bin/sparcv7 755 root bin +f none usr/bin/sparcv7/ksh93 555 root bin +l none usr/bin/sparcv7/rksh93=ksh93 f none usr/bin/sparcv7/savecore 555 root bin d none usr/bin/sparcv9 755 root bin f none usr/bin/sparcv9/amt 555 root bin f none usr/bin/sparcv9/crle 555 root bin +f none usr/bin/sparcv9/ksh93 555 root bin f none usr/bin/sparcv9/ls 555 root bin f none usr/bin/sparcv9/moe 555 root bin f none usr/bin/sparcv9/newtask 4555 root sys @@ -58,6 +61,7 @@ f none usr/bin/sparcv9/prctl 555 root bin f none usr/bin/sparcv9/prstat 555 root bin f none usr/bin/sparcv9/ps 555 root bin +l none usr/bin/sparcv9/rksh93=ksh93 f none usr/bin/sparcv9/savecore 555 root bin f none usr/bin/sparcv9/setuname 555 root bin f none usr/bin/sparcv9/uptime 4555 root bin Index: src/pkgdefs/SUNWcsu/prototype_i386 =================================================================== --- src/pkgdefs/SUNWcsu/prototype_i386 (revision 676) +++ src/pkgdefs/SUNWcsu/prototype_i386 (revision 740) @@ -22,7 +22,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "@(#)prototype_i386 1.87 07/01/18 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -50,11 +50,13 @@ f none usr/bin/i86/amt 555 root bin f none usr/bin/diskscan 555 root bin d none usr/bin/i86 755 root bin +f none usr/bin/i86/ksh93 555 root bin f none usr/bin/i86/newtask 4555 root sys f none usr/bin/i86/nohup 555 root bin f none usr/bin/i86/prctl 555 root bin f none usr/bin/i86/prstat 555 root bin f none usr/bin/i86/ps 555 root bin +l none usr/bin/i86/rksh93=ksh93 f none usr/bin/i86/savecore 555 root bin f none usr/bin/i86/setuname 555 root bin f none usr/bin/i86/uptime 4555 root bin @@ -101,6 +103,7 @@ d none usr/bin/amd64 755 root bin f none usr/bin/amd64/amt 555 root bin f none usr/bin/amd64/crle 555 root bin +f none usr/bin/amd64/ksh93 555 root bin f none usr/bin/amd64/ls 555 root bin f none usr/bin/amd64/moe 555 root bin f none usr/bin/amd64/newtask 4555 root sys @@ -108,6 +111,7 @@ f none usr/bin/amd64/prctl 555 root bin f none usr/bin/amd64/prstat 555 root bin f none usr/bin/amd64/ps 555 root bin +l none usr/bin/amd64/rksh93=ksh93 f none usr/bin/amd64/savecore 555 root bin f none usr/bin/amd64/setuname 555 root bin f none usr/bin/amd64/uptime 4555 root bin Index: src/pkgdefs/SUNWastdev/prototype_com =================================================================== --- src/pkgdefs/SUNWastdev/prototype_com (revision 0) +++ src/pkgdefs/SUNWastdev/prototype_com (revision 740) @@ -0,0 +1,59 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # puts parameter in pkg environment + +# packaging files +i pkginfo +i copyright +i depend +# +# source locations relative to the prototype file +# +# SUNWastdev +# +d none usr 755 root sys +d none usr/ast 0755 root sys +d none usr/ast/bin 0755 root bin +f none usr/ast/bin/msgcc 0555 root bin +f none usr/ast/bin/msgcpp 0555 root bin +f none usr/ast/bin/msgcvt 0555 root bin +f none usr/ast/bin/msggen 0555 root bin +f none usr/ast/bin/msgget 0555 root bin +d none usr/include 755 root bin +d none usr/include/ast 755 root bin +f none usr/include/ast/pp.h 644 root bin +f none usr/include/ast/ppkey.h 644 root bin +d none usr/lib 755 root bin +f none usr/lib/libpp.so.1 755 root bin Index: src/pkgdefs/SUNWastdev/pkginfo.tmpl =================================================================== --- src/pkgdefs/SUNWastdev/pkginfo.tmpl (revision 0) +++ src/pkgdefs/SUNWastdev/pkginfo.tmpl (revision 740) @@ -0,0 +1,58 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# This required package information file describes characteristics of the +# package, such as package abbreviation, full package name, package version, +# and package architecture. +# +PKG="SUNWastdev" +NAME="AT&T AST development utilities" +ARCH="ISA" +VERSION="ONVERS,REV=0.0.0" +SUNW_PRODNAME="SunOS" +SUNW_PRODVERS="RELEASE/VERSION" +SUNW_PKGTYPE="usr" +MAXINST="1000" +CATEGORY="system" +DESC="AT&T AST development utilities" +VENDOR="Sun Microsystems, Inc." +HOTLINE="Please contact your local service provider" +EMAIL="" +CLASSES="none" +BASEDIR=/ +SUNW_PKGVERS="1.0" +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="false" +SUNW_PKG_THISZONE="false" +#VSTOCK="" +#ISTATES="" +#RSTATES='' +#ULIMIT="" +#ORDER="" +#PSTAMP="" +#INTONLY="" Index: src/pkgdefs/SUNWastdev/Makefile =================================================================== --- src/pkgdefs/SUNWastdev/Makefile (revision 0) +++ src/pkgdefs/SUNWastdev/Makefile (revision 740) @@ -0,0 +1,37 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com + +DATAFILES += depend + +.KEEP_STATE: + +all: $(FILES) +install: all pkg + +include ../Makefile.targ Index: src/pkgdefs/SUNWastdev/prototype_sparc =================================================================== --- src/pkgdefs/SUNWastdev/prototype_sparc (revision 0) +++ src/pkgdefs/SUNWastdev/prototype_sparc (revision 740) @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# +# +# List files which are SPARC specific here +# +# source locations relative to the prototype file +# +# +# SUNWastdev +# Index: src/pkgdefs/SUNWastdev/prototype_i386 =================================================================== --- src/pkgdefs/SUNWastdev/prototype_i386 (revision 0) +++ src/pkgdefs/SUNWastdev/prototype_i386 (revision 740) @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # puts parameter in pkg environment + +# +# Include ISA independent files (prototype_com) +# +!include prototype_com +# +# +# +# List files which are I386 specific here +# +# source locations relative to the prototype file +# +# +# SUNWastdev +# Index: src/pkgdefs/etc/exception_list_sparc =================================================================== --- src/pkgdefs/etc/exception_list_sparc (revision 676) +++ src/pkgdefs/etc/exception_list_sparc (revision 740) @@ -22,7 +22,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)exception_list_sparc 1.240 07/03/09 SMI" +# ident "%Z%%M% %I% %E% SMI" # # Exception List for protocmp # @@ -869,6 +869,36 @@ # usr/include/nss.h sparc # +# AT&T AST (ksh93) files which are currently needed only to build OS/Net +# (msgcc&co.) +# libast +usr/lib/libast.so sparc +usr/lib/llib-last sparc +usr/lib/llib-last.ln sparc +usr/lib/sparcv9/libast.so sparc +usr/lib/sparcv9/llib-last.ln sparc +# libcmd +usr/lib/llib-lcmd sparc +usr/lib/llib-lcmd.ln sparc +usr/lib/sparcv9/llib-lcmd.ln sparc +# libdll +usr/lib/libdll.so sparc +usr/lib/llib-ldll sparc +usr/lib/llib-ldll.ln sparc +usr/lib/sparcv9/libdll.so sparc +usr/lib/sparcv9/llib-ldll.ln sparc +# libpp (a helper library needed by AST's msgcc) +usr/lib/libpp.so sparc +usr/lib/llib-lpp sparc +usr/lib/llib-lpp.ln sparc +usr/lib/locale/C/LC_MESSAGES/libpp sparc +# libshell +usr/lib/libshell.so sparc +usr/lib/llib-lshell sparc +usr/lib/llib-lshell.ln sparc +usr/lib/sparcv9/libshell.so sparc +usr/lib/sparcv9/llib-lshell.ln sparc +# # bmc (IPMI) interfaces shared within ON. # usr/include/sys/bmc_intf.h sparc Index: src/pkgdefs/etc/exception_list_i386 =================================================================== --- src/pkgdefs/etc/exception_list_i386 (revision 676) +++ src/pkgdefs/etc/exception_list_i386 (revision 740) @@ -22,7 +22,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)exception_list_i386 1.222 07/02/17 SMI" +# ident "%Z%%M% %I% %E% SMI" # # Exception List for protocmp # @@ -805,6 +805,36 @@ # usr/include/nss.h i386 # +# AT&T AST (ksh93) files which are currently needed only to build OS/Net +# (msgcc&co.) +# libast +usr/lib/libast.so i386 +usr/lib/llib-last i386 +usr/lib/llib-last.ln i386 +usr/lib/amd64/libast.so i386 +usr/lib/amd64/llib-last.ln i386 +# libcmd +usr/lib/llib-lcmd i386 +usr/lib/llib-lcmd.ln i386 +usr/lib/amd64/llib-lcmd.ln i386 +# libdll +usr/lib/libdll.so i386 +usr/lib/llib-ldll i386 +usr/lib/llib-ldll.ln i386 +usr/lib/amd64/libdll.so i386 +usr/lib/amd64/llib-ldll.ln i386 +# libpp (a helper library needed by AST's msgcc) +usr/lib/libpp.so i386 +usr/lib/llib-lpp i386 +usr/lib/llib-lpp.ln i386 +usr/lib/locale/C/LC_MESSAGES/libpp i386 +# libshell +usr/lib/libshell.so i386 +usr/lib/llib-lshell i386 +usr/lib/llib-lshell.ln i386 +usr/lib/amd64/libshell.so i386 +usr/lib/amd64/llib-lshell.ln i386 +# # bmc (IPMI) interfaces shared within ON. # usr/include/sys/bmc_intf.h i386 Index: src/pkgdefs/SUNW0on/prototype_com =================================================================== --- src/pkgdefs/SUNW0on/prototype_com (revision 676) +++ src/pkgdefs/SUNW0on/prototype_com (revision 740) @@ -22,7 +22,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "@(#)prototype_com 1.62 07/01/03 SMI" +# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -72,6 +72,16 @@ f none usr/lib/locale/C/LC_TIME/SUNW_OST_OSCMD.po 644 root sys f none usr/lib/locale/C/LC_TIME/SUNW_OST_OSLIB.po 644 root sys # +# AT&T AST locale files +# (file names and locations must be in sync with upstream definitions +# to ensure compatibility between our version of AST/ksh93 and the +# native one) +# +f none usr/lib/locale/C/LC_MESSAGES/libast 644 root sys +f none usr/lib/locale/C/LC_MESSAGES/libcmd 644 root sys +f none usr/lib/locale/C/LC_MESSAGES/libdll 644 root sys +f none usr/lib/locale/C/LC_MESSAGES/libshell 644 root sys +# # Java locale directories # d none usr/share 755 root sys Index: src/pkgdefs/SUNWosdem/prototype_com =================================================================== --- src/pkgdefs/SUNWosdem/prototype_com (revision 676) +++ src/pkgdefs/SUNWosdem/prototype_com (revision 740) @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,10 +19,10 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "@(#)prototype_com 1.13 05/06/08 SMI" +#ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -54,6 +53,47 @@ f none usr/demo/ELF/pcom.c 644 root bin f none usr/demo/ELF/dcom.c 644 root bin f none usr/demo/ELF/tpcom.c 644 root bin +d none usr/demo/ksh 755 root bin +d none usr/demo/ksh/fun 755 root bin +f none usr/demo/ksh/fun/dirs 644 root bin +f none usr/demo/ksh/fun/mandelbrotset1 644 root bin +f none usr/demo/ksh/fun/gnaw 644 root bin +f none usr/demo/ksh/fun/rssread 644 root bin +f none usr/demo/ksh/fun/popd 644 root bin +f none usr/demo/ksh/fun/pushd 644 root bin +f none usr/demo/ksh/fun/termclock 644 root bin +f none usr/demo/ksh/fun/title 644 root bin +d none usr/demo/ksh/tests 755 root bin +f none usr/demo/ksh/tests/alias.sh 644 root bin +f none usr/demo/ksh/tests/append.sh 644 root bin +f none usr/demo/ksh/tests/arith.sh 644 root bin +f none usr/demo/ksh/tests/arrays.sh 644 root bin +f none usr/demo/ksh/tests/attributes.sh 644 root bin +f none usr/demo/ksh/tests/basic.sh 644 root bin +f none usr/demo/ksh/tests/bracket.sh 644 root bin +f none usr/demo/ksh/tests/builtins.sh 644 root bin +f none usr/demo/ksh/tests/case.sh 644 root bin +f none usr/demo/ksh/tests/comvar.sh 644 root bin +f none usr/demo/ksh/tests/coprocess.sh 644 root bin +f none usr/demo/ksh/tests/exit.sh 644 root bin +f none usr/demo/ksh/tests/expand.sh 644 root bin +f none usr/demo/ksh/tests/functions.sh 644 root bin +f none usr/demo/ksh/tests/glob.sh 644 root bin +f none usr/demo/ksh/tests/grep.sh 644 root bin +f none usr/demo/ksh/tests/heredoc.sh 644 root bin +f none usr/demo/ksh/tests/io.sh 644 root bin +f none usr/demo/ksh/tests/nameref.sh 644 root bin +f none usr/demo/ksh/tests/options.sh 644 root bin +f none usr/demo/ksh/tests/path.sh 644 root bin +f none usr/demo/ksh/tests/quoting.sh 644 root bin +f none usr/demo/ksh/tests/quoting2.sh 644 root bin +f none usr/demo/ksh/tests/return.sh 644 root bin +f none usr/demo/ksh/tests/select.sh 644 root bin +f none usr/demo/ksh/tests/shtests 644 root bin +f none usr/demo/ksh/tests/substring.sh 644 root bin +f none usr/demo/ksh/tests/sun_solaris_getconf.sh 644 root bin +f none usr/demo/ksh/tests/tilde.sh 644 root bin +f none usr/demo/ksh/tests/variables.sh 644 root bin d none usr/demo/libexacct 755 root bin f none usr/demo/libexacct/README 644 root bin f none usr/demo/libexacct/Makefile 644 root bin Index: src/Makefile.ksh93switch =================================================================== --- src/Makefile.ksh93switch (revision 0) +++ src/Makefile.ksh93switch (revision 740) @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Should we build ksh93 as /bin/ksh ? +# This can be overridden at build time via: +# $ export ON_BUILD_KSH93_AS_BINKSH=1 +ON_BUILD_KSH93_AS_BINKSH=0 + +BINKSH_IS_KSH93_1= $(ON_BUILD_KSH93_AS_BINKSH:0=$(POUND_SIGN)) +BINKSH_ISNOT_KSH93_1=$(ON_BUILD_KSH93_AS_BINKSH:1=$(POUND_SIGN)) +BINKSH_IS_KSH93= $(BINKSH_IS_KSH93_1:1=) +BINKSH_ISNOT_KSH93= $(BINKSH_ISNOT_KSH93_1:0=) Index: src/tools/findunref/exception_list =================================================================== --- src/tools/findunref/exception_list (revision 676) +++ src/tools/findunref/exception_list (revision 740) @@ -23,7 +23,7 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)exception_list 1.80 07/02/01 SMI" +# ident "%Z%%M% %I% %E% SMI" # # Exception list for the findunref tool. Files in this list are by definition # intentionally never used during a standard nightly build. Since very few @@ -34,14 +34,24 @@ # # Ignore text files. # -./src/OPENSOLARIS.LICENSE +*.[1-9] +*.memo +*.mm *.txt */*[Rr][Ee][Aa][Dd][Mm][Ee]* +*/BUGS +*/COMPATIBILITY +*/COPYRIGHT */ChangeLog +*/DESIGN +*/HISTORY +*/LICENSE +*/NOTES +*/OBSOLETE */READ_ME +*/RELEASE* */TODO -*/COPYRIGHT -*/LICENSE +./src/OPENSOLARIS.LICENSE # # Ignore oddly-named text files scattered about -- someday these should be @@ -63,6 +73,53 @@ ./src/uts/intel/sys/acpi # +# Ignore ksh93/ast-related files that are only used to resync our build +# configuration with upstream. +# +./src/lib/libast/*/src/lib/libast/FEATURE/* +./src/lib/libast/common/comp/conf.* +./src/lib/libast/common/features/* +./src/lib/libast/common/include/ast_windows.h +./src/lib/libast/common/port/lc.tab +./src/lib/libast/common/port/lcgen.c +./src/lib/libcmd/*/src/lib/libcmd/FEATURE/* +./src/lib/libcmd/common/features/* +./src/lib/libdll/*/src/lib/libdll/FEATURE/* +./src/lib/libdll/common/features/* +./src/lib/libpp/*/pp.* +./src/lib/libpp/common/gentab.sh +./src/lib/libpp/common/ppsym.c +./src/lib/libpp/i386/ppdebug.h +./src/lib/libpp/sparc/ppdebug.h +./src/lib/libshell/*/src/cmd/ksh93/FEATURE/* +./src/lib/libshell/common/data/math.tab +./src/lib/libshell/common/features/* +./src/lib/libshell/misc/buildksh93.ksh +./src/lib/libshell/misc/buildksh93.readme + +# +# Ignore ksh93/ast-related test programs. +# +./src/cmd/ast/msgcc/msgcc.tst +./src/lib/libast/common/port/astmath.c + +# +# Ignore ksh93/ast-related source components that are not currently +# used but may be useful later. +# +./src/lib/libcmd/common/cksum.c +./src/lib/libcmd/common/md5sum.c +./src/lib/libcmd/common/sum.c +./src/lib/libshell/common/bltins/mkservice.c +./src/lib/libshell/common/bltins/shopen.c +./src/lib/libshell/common/data/bash_pre_rc.sh +./src/lib/libshell/common/include/env.h +./src/lib/libshell/common/sh/bash.c +./src/lib/libshell/common/sh/env.c +./src/lib/libshell/common/sh/shcomp.c +./src/lib/libshell/common/sh/suid_exec.c + +# # Ignore any files built as part of the nightly program itself. # # ISUSED - let checkpaths know that the next entry is good. Index: src/tools/scripts/bfu.sh =================================================================== --- src/tools/scripts/bfu.sh (revision 676) +++ src/tools/scripts/bfu.sh (revision 740) @@ -106,6 +106,7 @@ etc/krb5/kpropd.acl etc/krb5/krb5.conf etc/krb5/warn.conf + etc/ksh.kshrc etc/logadm.conf etc/logindevperm etc/lp/Systems Index: src/tools/opensolaris/license-list =================================================================== --- src/tools/opensolaris/license-list (revision 0) +++ src/tools/opensolaris/license-list (revision 740) @@ -0,0 +1,123 @@ +usr/closed/cmd/man/src/util/solbookv1/THIRDPARTYLICENSE +usr/closed/lib/smartcard/ocfserv/opencard/core/event/THIRDPARTYLICENSE +usr/src/cmd/agents/snmp/THIRDPARTYLICENSE +usr/src/cmd/ast/THIRDPARTYLICENSE +usr/src/cmd/backup/dump/THIRDPARTYLICENSE +usr/src/cmd/bnu/THIRDPARTYLICENSE +usr/src/cmd/checkeq/THIRDPARTYLICENSE +usr/src/cmd/checknr/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/THIRDPARTYLICENSE.kcmd +usr/src/cmd/cmd-inet/sbin/ifparse/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.bin/THIRDPARTYLICENSE.rcp +usr/src/cmd/cmd-inet/usr.bin/THIRDPARTYLICENSE.rsh +usr/src/cmd/cmd-inet/usr.bin/ftp/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.bin/pppd/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/THIRDPARTYLICENSE.minconnect +usr/src/cmd/cmd-inet/usr.bin/pppd/plugins/THIRDPARTYLICENSE.passwd +usr/src/cmd/cmd-inet/usr.bin/pppdump/LICENSE.top +usr/src/cmd/cmd-inet/usr.bin/rdist/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.bin/telnet/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.lib/in.mpathd/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.lib/mipagent/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.sbin/THIRDPARTYLICENSE.arp +usr/src/cmd/cmd-inet/usr.sbin/THIRDPARTYLICENSE.comsat +usr/src/cmd/cmd-inet/usr.sbin/THIRDPARTYLICENSE.rlogind +usr/src/cmd/cmd-inet/usr.sbin/THIRDPARTYLICENSE.route +usr/src/cmd/cmd-inet/usr.sbin/ifconfig/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/LICENSE +usr/src/cmd/cmd-inet/usr.sbin/in.rdisc/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.sbin/in.routed/THIRDPARTYLICENSE +usr/src/cmd/cmd-inet/usr.sbin/traceroute/THIRDPARTYLICENSE +usr/src/cmd/compress/THIRDPARTYLICENSE +usr/src/cmd/csh/THIRDPARTYLICENSE +usr/src/cmd/eeprom/THIRDPARTYLICENSE +usr/src/cmd/eqn/THIRDPARTYLICENSE +usr/src/cmd/fs.d/udfs/fsck/THIRDPARTYLICENSE +usr/src/cmd/fs.d/ufs/THIRDPARTYLICENSE +usr/src/cmd/ipf/tools/IPFILTER.LICENCE +usr/src/cmd/lastcomm/THIRDPARTYLICENSE +usr/src/cmd/ldap/THIRDPARTYLICENSE +usr/src/cmd/look/THIRDPARTYLICENSE +usr/src/cmd/lp/cmd/lptest/THIRDPARTYLICENSE +usr/src/cmd/man/src/THIRDPARTYLICENSE +usr/src/cmd/man/src/util/THIRDPARTYLICENSE +usr/src/cmd/man/src/util/instant.src/THIRDPARTYLICENSE +usr/src/cmd/man/src/util/nsgmls.src/COPYING +usr/src/cmd/man/src/util/solbookv2/THIRDPARTYLICENSE +usr/src/cmd/mdb/common/libstand/THIRDPARTYLICENSE +usr/src/cmd/mt/THIRDPARTYLICENSE +usr/src/cmd/perl/5.8.4/distrib/ext/Cwd/THIRDPARTYLICENSE +usr/src/cmd/perl/THIRDPARTYLICENSE +usr/src/cmd/refer/THIRDPARTYLICENSE +usr/src/cmd/script/THIRDPARTYLICENSE +usr/src/cmd/sendmail/THIRDPARTYLICENSE +usr/src/cmd/soelim/THIRDPARTYLICENSE +usr/src/cmd/ssh/THIRDPARTYLICENSE +usr/src/cmd/stat/vmstat/THIRDPARTYLICENSE +usr/src/cmd/tbl/THIRDPARTYLICENSE +usr/src/cmd/tcpd/THIRDPARTYLICENSE +usr/src/cmd/terminfo/THIRDPARTYLICENSE +usr/src/cmd/tip/THIRDPARTYLICENSE +usr/src/cmd/ul/THIRDPARTYLICENSE +usr/src/cmd/units/THIRDPARTYLICENSE +usr/src/cmd/vgrind/THIRDPARTYLICENSE +usr/src/cmd/vi/THIRDPARTYLICENSE +usr/src/cmd/which/THIRDPARTYLICENSE +usr/src/cmd/xntpd/THIRDPARTYLICENSE +usr/src/cmd/xstr/THIRDPARTYLICENSE +usr/src/common/openssl/LICENSE +usr/src/grub/grub-0.95/COPYING +usr/src/lib/gss_mechs/mech_krb5/THIRDPARTYLICENSE +usr/src/lib/krb5/THIRDPARTYLICENSE +usr/src/lib/libast/THIRDPARTYLICENSE +usr/src/lib/libbc/THIRDPARTYLICENSE +usr/src/lib/libbsdmalloc/THIRDPARTYLICENSE +usr/src/lib/libcmd/THIRDPARTYLICENSE +usr/src/lib/libdll/THIRDPARTYLICENSE +usr/src/lib/libgss/THIRDPARTYLICENSE +usr/src/lib/libinetutil/common/THIRDPARTYLICENSE +usr/src/lib/libkmf/THIRDPARTYLICENSE +usr/src/lib/libldap5/THIRDPARTYLICENSE +usr/src/lib/libmp/common/THIRDPARTYLICENSE +usr/src/lib/libpp/THIRDPARTYLICENSE +usr/src/lib/libresolv/THIRDPARTYLICENSE +usr/src/lib/libresolv2/THIRDPARTYLICENSE +usr/src/lib/libsasl/THIRDPARTYLICENSE +usr/src/lib/libshell/THIRDPARTYLICENSE +usr/src/lib/libtecla/THIRDPARTYLICENSE +usr/src/lib/pam_modules/authtok_check/THIRDPARTYLICENSE +usr/src/lib/passwdutil/THIRDPARTYLICENSE +usr/src/lib/pkcs11/include/THIRDPARTYLICENSE +usr/src/stand/lib/tcp/THIRDPARTYLICENSE +usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE +usr/src/ucbcmd/basename/THIRDPARTYLICENSE +usr/src/ucbcmd/echo/THIRDPARTYLICENSE +usr/src/ucbcmd/from/THIRDPARTYLICENSE +usr/src/ucbcmd/groups/THIRDPARTYLICENSE +usr/src/ucbcmd/ln/THIRDPARTYLICENSE +usr/src/ucbcmd/ls/THIRDPARTYLICENSE +usr/src/ucbcmd/plot/THIRDPARTYLICENSE +usr/src/ucbcmd/sum/THIRDPARTYLICENSE +usr/src/ucbcmd/test/THIRDPARTYLICENSE +usr/src/ucbcmd/tset/THIRDPARTYLICENSE +usr/src/ucbcmd/users/THIRDPARTYLICENSE +usr/src/ucbcmd/whereis/THIRDPARTYLICENSE +usr/src/ucbcmd/whoami/THIRDPARTYLICENSE +usr/src/ucblib/libcurses/THIRDPARTYLICENSE +usr/src/ucblib/libtermcap/THIRDPARTYLICENSE +usr/src/ucblib/libucb/THIRDPARTYLICENSE +usr/src/uts/common/gssapi/mechs/krb5/THIRDPARTYLICENSE +usr/src/uts/common/inet/ip/THIRDPARTYLICENSE.rts +usr/src/uts/common/inet/tcp/THIRDPARTYLICENSE +usr/src/uts/common/io/THIRDPARTYLICENSE.etheraddr +usr/src/uts/common/io/chxge/com/THIRDPARTYLICENSE +usr/src/uts/common/io/ib/clients/rds/THIRDPARTYLICENSE +usr/src/uts/common/io/wpi/fw-wpi/LICENSE +usr/src/uts/common/sys/THIRDPARTYLICENSE.agpgart +usr/src/uts/common/sys/THIRDPARTYLICENSE.unicode +usr/src/uts/common/sys/i2o/THIRDPARTYLICENSE +usr/src/uts/common/zmod/THIRDPARTYLICENSE +usr/src/uts/intel/THIRDPARTYLICENSE +usr/src/uts/intel/io/aac/THIRDPARTYLICENSE +usr/src/uts/intel/io/acpica/THIRDPARTYLICENSE +usr/src/uts/intel/io/amr/THIRDPARTYLICENSE Index: src/cmd/ksh/Makefile.testshell =================================================================== --- src/cmd/ksh/Makefile.testshell (revision 0) +++ src/cmd/ksh/Makefile.testshell (revision 740) @@ -0,0 +1,160 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# run the ksh93 minimum set of tests +# +# Notes: +# - "builtins.sh" may fail in some locales like this: +# -- snip -- +# ## Running ksh test: LANG='zh_TW.EUC' script='builtins.sh' +# builtins.sh[274]: printf "%T" now +# -- snip -- +# +# - "options.sh" may currently fail in some locales with: +# -- snip -- +# options.sh[145]: -G ** failed -- expected 'bam.c bar bar.c bar/bam.c bar/foo.c foo foo/bam.c', got 'bam.c bar bar/bam.c bar.c bar/foo.c foo foo/bam.c' +# options.sh[149]: -G **/*.c failed -- expected 'bam.c bar.c bar/bam.c bar/foo.c foo/bam.c', got 'bam.c bar/bam.c bar.c bar/foo.c foo/bam.c' +# -- snip -- +# This may be simply a different sort order or a bug in the test suite. +# Currently under investigation. +# +# - "io.sh" may fail due a subtle bug in ksh93 or the test suite which +# only happens when ksh93 is NOT called "ksh"; to work around the +# problem the test sequence currently uses $(SRC)/cmd/ksh/$(CMDTRANSMACH)/ksh +# instead of $(SRC)/cmd/ksh/$(CMDTRANSMACH)/$(PROG) until we+upstream figure +# out what exactly is going wrong in this case. +# -- snip -- +# ./close0[2]: ./close1: cannot execute [Exec format error] +# io.sh[81]: picked up file descriptor zero for opening script file +# -- snip -- +# +# - These tests need a working system clock, otherwise they'll bite you. +# +# - The test frontend in this Makefile should be rewritten in ksh93 +# instead of the current /usr/bin/ksh (=ksh88i). This would be far less +# complicated. +# +# - More locales should be tested here (via ON_KSH_TEST_LOCALES below). +# Locales like "ru_RU.KOI8-R","de_DE.UTF-8", "is_IS.ISO8859-1", +# "is_IS.UTF-8" and "nl_BE.ISO8859-15" are on our wishlist - but +# that is getting little bit more compliciated because these locales use +# ',' as decimal delimter. The best solution may be to wait for ksh93 +# being integrated into OS/Net and then change the test sequence to +# use ksh93's associate/compound variables (this may require a flag +# day... ;-( ). +# The current list was mainly composed to cover various encodings and +# all important markets based on suggestions by Sun's i18n team. +# + +TESTSRC= $(LIBSHELLSRC)/../tests + +# ON_KSH_TEST_LOCALES can be overridden via +# $ export ON_KSH_TEST_LOCALES= # before $ make install # +ON_KSH_TEST_LOCALES = \ + C \ + en_US en_US.UTF-8 \ + he_IL.UTF-8 \ + hi_IN.UTF-8 \ + ja_JP.PCK ja_JP.UTF-8 ja_JP.eucJP \ + ko_KR.EUC \ + th_TH.TIS620 \ + zh_CN.EUC zh_CN.GBK zh_CN.GB18030 zh_CN.UTF-8 \ + zh_HK.BIG5HK \ + zh_TW.BIG5 zh_TW.EUC zh_TW.UTF-8 + +# ON_KSH_TEST_LIST can be overridden via +# $ export ON_KSH_TEST_LIST= # before $ make install # +ON_KSH_TEST_LIST = $(TESTSRC)/*.sh + +# Flag to control whether we should make test failures non-fatal +ON_KSH_TEST_IGNORE_TESTFAILURE=false + +# We must wait for other things in this subdir to finish before running +# the test suite, otherwise we may run into trouble that this activity +# may disturb the test suite run (resulting in weird "heisenbug"-like +# test failures). +testshell: $(PROG) + @ \ + print '# NOTE: Make sure your binaries in ROOT match your kernel!' ; \ + ( \ + set +o errexit ; \ + export PATH="$(SRC)/cmd/ksh/$(CMDTRANSMACH):/bin:/usr/bin" ; \ + printf "# which ksh='%s', ksh93='%s'\n" \ + "$$(which ksh)" "$$(which ksh93)" ; \ + ) ; \ + if [[ "$$(isalist | fgrep "$(CMDTRANSMACH)")" = "" ]] ; then \ + printf \ + "# ISA='%s' not available on this system, skipping tests...\n" \ + "$(CMDTRANSMACH)" ; \ + exit 0 ; \ + fi ; \ + exec 2>&1 ; \ + (supported_locales="$$(/usr/bin/locale -a)" ; \ + for test_lang in $(ON_KSH_TEST_LOCALES) ; do \ + if [[ "$$(print "$${supported_locales}" | \ + egrep "^$${test_lang}\$$")" = "" ]] ; then \ + printf \ + "# Locale '%s' not supported, skipping tests...\n" \ + "$${test_lang}" ; \ + continue ; \ + fi ; \ + (for test_item in $(ON_KSH_TEST_LIST) ; do \ + [[ "$${test_item}" = "$(TESTSRC)/builtins.sh" || \ + "$${test_item}" = "$(TESTSRC)/options.sh" ]] || \ + $(ON_KSH_TEST_IGNORE_TESTFAILURE) && \ + set +o errexit ; \ + printf \ + "## Running %s test: LANG='%s' script='%s'\n" \ + "$(CMDTRANSMACH)/ksh" \ + "$${test_lang}" \ + "$$(basename "$${test_item}")"; \ + ( \ + test_output="$$( ( \ + export \ + SHELL="$(SRC)/cmd/ksh/$(CMDTRANSMACH)/ksh" \ + LD_LIBRARY_PATH_64="$(ROOTLIB64)/" \ + LD_LIBRARY_PATH_32="$(ROOTLIB)/" ; \ + LD_LIBRARY_PATH="$(ROOTLIB64)/:$(ROOTLIB)/" ; \ + "$${SHELL}" "$(TESTSRC)/shtests" -t \ + LD_LIBRARY_PATH_64="$${LD_LIBRARY_PATH_64}" \ + LD_LIBRARY_PATH_32="$${LD_LIBRARY_PATH_32}" \ + LD_LIBRARY_PATH="$${LD_LIBRARY_PATH}" \ + SHELL="$${SHELL}" \ + LANG="$${test_lang}" \ + LC_ALL="$${test_lang}" \ + "$${test_item}" \ + ) 2>&1 | while read ; do \ + printf "#\t%s\n" "$${REPLY}" ; \ + done | tee /dev/stderr)" ; \ + [[ "$$(print "$${test_output}" | \ + egrep 'passed \[ .* tests 0 errors \]')" != "" ]] || \ + (print "##> test failed" ; exit 1) \ + ) ; \ + set -o errexit ; \ + done) ; \ + done) Index: src/cmd/ksh/sparcv9/Makefile =================================================================== --- src/cmd/ksh/sparcv9/Makefile (revision 0) +++ src/cmd/ksh/sparcv9/Makefile (revision 740) @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Specify the MACH we currently use to build and test ksh +CMDTRANSMACH= $(MACH64) + +include ../../Makefile.cmd +include ../../Makefile.cmd.64 + +include ../Makefile.com + +install: all $(ROOTPROG64) + @ \ + (print "# Installing 64bit $(PROG) aliases $(USRKSH_ALIAS_LIST)" ; \ + set -o xtrace ; \ + for i in $(USRKSH_ALIAS_LIST) ; do \ + [[ "$$i" = "$(PROG)" ]] && continue ; \ + $(RM) "$(ROOTBIN64)/$$i" ; \ + $(LN) "$(ROOTBIN64)/$(PROG)" "$(ROOTBIN64)/$$i" ; \ + done \ + ) + +include ../../Makefile.targ Index: src/cmd/ksh/sparc/Makefile =================================================================== --- src/cmd/ksh/sparc/Makefile (revision 0) +++ src/cmd/ksh/sparc/Makefile (revision 740) @@ -0,0 +1,46 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Specify the MACH we currently use to build and test ksh +CMDTRANSMACH= $(MACH) + +include ../../Makefile.cmd + +include ../Makefile.com + +install: all $(ROOTPROG32) + @ \ + (print "# Installing 32bit $(PROG) aliases $(USRKSH_ALIAS_LIST)" ; \ + set -o xtrace ; \ + for i in $(USRKSH_ALIAS_LIST) ; do \ + [[ "$$i" = "$(PROG)" ]] && continue ; \ + $(RM) "$(ROOTBIN32)/$$i" ; \ + $(LN) "$(ROOTBIN32)/$(PROG)" "$(ROOTBIN32)/$$i" ; \ + done \ + ) + +include ../../Makefile.targ Index: src/cmd/ksh/i386/Makefile =================================================================== --- src/cmd/ksh/i386/Makefile (revision 0) +++ src/cmd/ksh/i386/Makefile (revision 740) @@ -0,0 +1,46 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Specify the MACH we currently use to build and test ksh +CMDTRANSMACH= $(MACH) + +include ../../Makefile.cmd + +include ../Makefile.com + +install: all $(ROOTPROG32) + @ \ + (print "# Installing 32bit $(PROG) aliases $(USRKSH_ALIAS_LIST)" ; \ + set -o xtrace ; \ + for i in $(USRKSH_ALIAS_LIST) ; do \ + [[ "$$i" = "$(PROG)" ]] && continue ; \ + $(RM) "$(ROOTBIN32)/$$i" ; \ + $(LN) "$(ROOTBIN32)/$(PROG)" "$(ROOTBIN32)/$$i" ; \ + done \ + ) + +include ../../Makefile.targ Index: src/cmd/ksh/Makefile.com =================================================================== --- src/cmd/ksh/Makefile.com (revision 0) +++ src/cmd/ksh/Makefile.com (revision 740) @@ -0,0 +1,110 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SHELL=/usr/bin/ksh + +include ../../../Makefile.ksh93switch + +$(BINKSH_IS_KSH93)PROG= ksh +$(BINKSH_IS_KSH93)USRKSH_ALIAS_LIST=ksh ksh93 rksh rksh93 pfksh + +$(BINKSH_ISNOT_KSH93)PROG= ksh93 +$(BINKSH_ISNOT_KSH93)USRKSH_ALIAS_LIST=ksh93 rksh93 + +OBJECTS= \ + pmain.o + +LIBSHELLSRC=../../../lib/libshell/common/sh + +SRCS= $(OBJECTS:%.o=$(LIBSHELLSRC)/%.c) + +GROUP= bin +LDLIBS += -lshell + +# 1. Make sure that the -D/-U defines in CFLAGS below are in sync +# with usr/src/lib/libshell/Makefile.com +# 2. We use "=" here since using $(CPPFLAGS.master) is very tricky in our +# case - it MUST come as the last element but future changes in -D options +# may then cause silent breakage in the AST sources because the last -D +# option specified overrides previous -D options so we prefer the current +# way to expliclity list each single flag. +CPPFLAGS = \ + $(DTEXTDOM) $(DTS_ERRNO) \ + $(LIBSHELLCPPFLAGS) + +CFLAGS += \ + $(CCVERBOSE) \ + -xstrconst +CFLAGS64 += \ + $(CCVERBOSE) \ + -xstrconst + +# Set common AST build flags (e.g., needed to support the math stuff). +include ../../../Makefile.ast + +.KEEP_STATE: + +%.o: $(LIBSHELLSRC)/%.c + $(COMPILE.c) -c -o $@ $< + $(POST_PROCESS_O) + +all: $(PROG) + +# We explicitly delete "ksh" and "ksh93" to catch changes in +# BUILD_KSH93_AS_BINKSH (see Makefile.ksh93switch) +# and soft-link $(PROG) to ksh/ksh93 below because ksh93 test +# suite seems to require that ksh93 is available as "ksh" in +# ${PATH} (see comment about "io.sh" in Makefile.testshell). +$(PROG): $(OBJECTS) + $(RM) ksh ksh93 + $(LINK.c) $(OBJECTS) -o $@ $(LDLIBS) + $(POST_PROCESS) + (set +o errexit ; \ + [[ ! -x ksh93 ]] && ln $(PROG) ksh93 ; \ + [[ ! -x ksh ]] && ln $(PROG) ksh ; \ + true \ + ) + +clean: + $(RM) $(OBJECTS) + +# We explicitly delete "ksh" and "ksh93" to catch changes in +# BUILD_KSH93_AS_BINKSH (see Makefile.ksh93switch) +CLOBBERFILES += \ + ksh \ + ksh93 + +# +# ksh is not lint-clean yet; fake up a target. (You can use +# "make lintcheck" to actually run lint; please send all lint fixes +# upstream (to AT&T) so the next update will pull them into ON.) +# +lint: + @ print "usr/src/cmd/ksh is not lint-clean: skipping" + @ $(TRUE) + +include ../Makefile.testshell Index: src/cmd/ksh/amd64/Makefile =================================================================== --- src/cmd/ksh/amd64/Makefile (revision 0) +++ src/cmd/ksh/amd64/Makefile (revision 740) @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Specify the MACH we currently use to build and test ksh +CMDTRANSMACH= $(MACH64) + +include ../../Makefile.cmd +include ../../Makefile.cmd.64 + +include ../Makefile.com + +install: all $(ROOTPROG64) + @ \ + (print "# Installing 64bit $(PROG) aliases $(USRKSH_ALIAS_LIST)" ; \ + set -o xtrace ; \ + for i in $(USRKSH_ALIAS_LIST) ; do \ + [[ "$$i" = "$(PROG)" ]] && continue ; \ + $(RM) "$(ROOTBIN64)/$$i" ; \ + $(LN) "$(ROOTBIN64)/$(PROG)" "$(ROOTBIN64)/$$i" ; \ + done \ + ) + +include ../../Makefile.targ Index: src/cmd/ksh/Makefile =================================================================== --- src/cmd/ksh/Makefile (revision 0) +++ src/cmd/ksh/Makefile (revision 740) @@ -0,0 +1,81 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SHELL=/usr/bin/ksh + +include ../../Makefile.ksh93switch + +$(BINKSH_IS_KSH93)PROG= ksh +$(BINKSH_IS_KSH93)USRKSH_ALIAS_LIST=ksh ksh93 rksh rksh93 pfksh + +$(BINKSH_ISNOT_KSH93)PROG= ksh93 +$(BINKSH_ISNOT_KSH93)USRKSH_ALIAS_LIST=ksh93 rksh93 + +include ../Makefile.cmd + +SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +# Serialise the build to avoid that we run the test suite for 32bit +# and 64bit in parallel +.NO_PARALLEL: $(SUBDIRS) + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint +testshell := TARGET = testshell + +.KEEP_STATE: + +all clean clobber lint testshell: $(SUBDIRS) + +# dummy file since AST/ksh doesn't use *.po files +# (and "ksh" is just a frontend which calls directly into libshell, +# e.g. there are no l10n strings here) +$(PROG).po: + $(RM) ksh.po ksh93.po + touch $(PROG).po + +install: $(ISAEXEC) $(SUBDIRS) + $(RM) $(ROOTPROG) + $(LN) $(ISAEXEC) $(ROOTPROG) + @(set -o xtrace ; \ + for i in $(USRKSH_ALIAS_LIST) ; do \ + [[ "$$i" = "$(PROG)" ]] && continue ; \ + $(RM) "$(ROOTBIN)/$$i" ; \ + $(LN) "$(ROOTBIN)/$(PROG)" "$(ROOTBIN)/$$i" ; \ + done \ + ) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ Index: src/cmd/Makefile =================================================================== --- src/cmd/Makefile (revision 676) +++ src/cmd/Makefile (revision 740) @@ -24,9 +24,10 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)Makefile 1.366 07/01/18 SMI" +# ident "%Z%%M% %I% %E% SMI" include ../Makefile.master +include ../Makefile.ksh93switch # # Note that the commands 'agents', 'lp', 'perl', and 'man' are first in @@ -61,6 +62,7 @@ acctadm \ arch \ asa \ + ast \ audio \ auths \ autopush \ @@ -209,6 +211,7 @@ keyserv \ killall \ krb5 \ + ksh \ kstat \ last \ lastcomm \ @@ -570,6 +573,7 @@ join \ kbd \ krb5 \ + ksh \ kstat \ last \ ldap \ Index: src/cmd/sgs/libelf/Makefile.targ =================================================================== --- src/cmd/sgs/libelf/Makefile.targ (revision 676) +++ src/cmd/sgs/libelf/Makefile.targ (revision 740) @@ -19,10 +19,10 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)Makefile.targ 1.43 06/08/01 SMI" +# ident "%Z%%M% %I% %E% SMI" # $(VAR_POUND_2)LIBS += $(LIBRARY) @@ -49,9 +49,10 @@ all: $(BLTSRCS) .WAIT $(LIBS) $(LIBLINKS) -install: all .WAIT $(ROOTFS_LIBS) $(ROOTFS_LINKS) $(ROOTDEMODIR) \ +install: all .WAIT $(ROOTFS_LIBS) $(ROOTFS_LINKS) \ $(ROOTFS_LINTLIB) \ - .WAIT $(ROOTDEMOFILES) $(ROOTFS_LIBDIR)/$(LINTLIBSRC) + .WAIT $(ROOTDEMODIRS) .WAIT $(ROOTDEMOFILES) \ + $(ROOTFS_LIBDIR)/$(LINTLIBSRC) $(VAR_POUND_1)$(ROOTFS_LIBDIR)/$(LINTLIBSRC): ../common/$(LINTLIBSRC) $(VAR_POUND_1) $(INS.file) ../common/$(LINTLIBSRC) @@ -74,13 +75,6 @@ include $(SRC)/lib/Makefile.targ include $(SRC)/cmd/sgs/Makefile.targ - -$(ROOTDEMODIR): - $(INS.dir) - -$(ROOTDEMODIR)/%: ../demo/% - $(INS.file) - xlate.c: ../common/xlate.m4 $(M4) < ../common/xlate.m4 > xlate.c Index: src/cmd/sgs/libelf/Makefile.com =================================================================== --- src/cmd/sgs/libelf/Makefile.com (revision 676) +++ src/cmd/sgs/libelf/Makefile.com (revision 740) @@ -19,10 +19,10 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "@(#)Makefile.com 1.54 06/08/03 SMI" +# ident "%Z%%M% %I% %E% SMI" # LIBRARY= libelf.a @@ -50,10 +50,14 @@ OBJECTS= $(BLTOBJS) $(MACHOBJS) $(COMOBJS) $(CLASSOBJS) $(MISCOBJS) +include $(SRC)/lib/Makefile.lib + DEMOFILES= Makefile README acom.c dcom.c \ pcom.c tpcom.c dispsyms.c +DEMOFILESRCDIR= ../demo +ROOTDEMODIRBASE=$(ROOT)/usr/demo/ELF +ROOTDEMODIRS= $(ROOTDEMODIRBASE) -include $(SRC)/lib/Makefile.lib include $(SRC)/cmd/sgs/Makefile.com # @@ -107,15 +111,8 @@ $(ROOTFS_DYNLIB) := FILEMODE= 755 $(ROOTFS_DYNLIB64) := FILEMODE= 755 -ROOTDEMODIR= $(ROOT)/usr/demo/ELF -ROOTDEMOFILES= $(DEMOFILES:%=$(ROOTDEMODIR)/%) - LIBS = $(DYNLIB) $(LINTLIB) CLEANFILES += $(LINTOUTS) $(BLTSRCS) $(BLTFILES) $(WARLOCKFILES) -$(ROOTDEMODIR) := OWNER = root -$(ROOTDEMODIR) := GROUP = bin -$(ROOTDEMODIR) := DIRMODE = $(VAR_LIBELF_ROOTDEMODIR_DIRMODE) - -.PARALLEL: $(LIBS) $(ROOTDEMOFILES) +.PARALLEL: $(LIBS) Index: src/cmd/nsadmin/profile =================================================================== --- src/cmd/nsadmin/profile (revision 676) +++ src/cmd/nsadmin/profile (revision 740) @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -19,8 +18,12 @@ # # CDDL HEADER END # -#ident "@(#)profile 1.20 05/06/10 SMI" /* SVr4.0 1.3 */ +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + # The profile that all logins get before using their own .profile. trap "" 2 3 @@ -41,7 +44,7 @@ # -rsh is given its environment in its .profile. case "$0" in --sh | -ksh | -jsh | -bash) +-sh | -ksh | -ksh93 | -jsh | -bash) if [ ! -f .hushlogin ] then Index: src/cmd/nsadmin/Makefile =================================================================== --- src/cmd/nsadmin/Makefile (revision 676) +++ src/cmd/nsadmin/Makefile (revision 740) @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. @@ -20,12 +19,12 @@ # CDDL HEADER END # # -#ident "@(#)Makefile 1.9 05/06/08 SMI" +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. # -# Copyright (c) 1989, 2001 by Sun Microsystems, Inc. -# All rights reserved. +# ident "%Z%%M% %I% %E% SMI" -PROG= profile .login system +PROG= profile .login ksh.kshrc system PROGSKEL= local.login local.profile local.cshrc include ../Makefile.cmd @@ -57,4 +56,3 @@ $(RM) .login lint: - Index: src/cmd/nsadmin/ksh.kshrc =================================================================== --- src/cmd/nsadmin/ksh.kshrc (revision 0) +++ src/cmd/nsadmin/ksh.kshrc (revision 740) @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# This file is sourced by interactive ksh93 shells before ${HOME}/.kshrc +# + +# Enable "gmacs" editor mode if the user did not set an input mode yet +# (for example via ${EDITOR}, ${VISUAL} or any "set -o" flag) +if [[ "$(set +o)" != ~(E)--(gmacs|emacs|vi)( |$) ]] ; then + set -o gmacs +fi + + +# enable multiline input mode +#set -o multiline + Index: src/cmd/ast/THIRDPARTYLICENSE.descrip =================================================================== --- src/cmd/ast/THIRDPARTYLICENSE.descrip (revision 0) +++ src/cmd/ast/THIRDPARTYLICENSE.descrip (revision 740) @@ -0,0 +1 @@ +AT&T ADVANCED SOFTWARE TECHNOLOGY UTILITIES Index: src/cmd/ast/msgcc/msggen.c =================================================================== --- src/cmd/ast/msgcc/msggen.c (revision 0) +++ src/cmd/ast/msgcc/msggen.c (revision 740) @@ -0,0 +1,522 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 2000-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + */ + +static const char usage[] = +"[-?\n@(#)$Id: msggen (AT&T Research) 2002-03-11 $\n]" +USAGE_LICENSE +"[+NAME?msggen - generate a machine independent formatted message catalog]" +"[+DESCRIPTION?\bmsggen\b merges the message text source files \amsgfile\a" +" into a machine independent formatted message catalog \acatfile\a." +" The file \acatfile\a will be created if it does not already exist." +" If \acatfile\a does exist, its messages will be included in the new" +" \acatfile\a. If set and message numbers collide, the new message" +" text defined in \amsgfile\a will replace the old message text" +" currently contained in \acatfile\a. Non-ASCII characters must be" +" UTF-8 encoded. \biconv\b(1) can be used to convert to/from UTF-8.]" +"[f:format?List the \bprintf\b(3) format signature for each message in" +" \acatfile\a. A format signature is one line containing one character" +" per format specification:]{" +" [c?char]" +" [d?double]" +" [D?long double]" +" [f?float]" +" [h?short]" +" [i?int]" +" [j?long long]" +" [l?long]" +" [p?void*]" +" [s?string]" +" [t?ptrdiff_t]" +" [z?size_t]" +" [???unknown]" +"}" +"[l:list?List \acatfile\a in UTF-8 \amsgfile\a form.]" +"[s:set?Convert the \acatfile\a operand to a message set number and" +" print the number on the standard output.]" +"[+EXTENDED DESCRIPTION?Message text source files are in \bgencat\b(1)" +" format, defined as follows. Note that the fields of a message text" +" source line are separated by a single blank character. Any other" +" blank characters are considered as being part of the subsequent" +" field. The \bNL_*\b constants are defined in one or both of" +" \b\b and \b\b.]{" +" [+$ \acomment\a?A line beginning with \b$\b followed by a" +" blank character is treated as a comment.]" +" [+$delset \an\a \acomment\a?This line deletes message set" +" \an\a from an existing message catalog. \an\a" +" denotes the set number [1, \bNL_SETMAX\b]]. Any" +" text following the set number is treated as a" +" comment.]" +" [+$quote \ac\a?This line specifies an optional quote" +" character \ac\a, which can be used to surround" +" \amessage-text\a so that trailing spaces or" +" empty messages are visible in a message source" +" line. By default, or if an empty \b$quote\b" +" directive is supplied, no quoting of \amessage-text\a" +" will be recognized.]" +" [+$set \an\a \acomment\a?This line specifies the set" +" identifier of the following messages until the next" +" \b$set\b or end-of-file appears. \an\a denotes the set" +" identifier, which is defined as a number in the range" +" [1, \bNL_SETMAX\b]]. Set numbers need not be" +" contiguous. Any text following the set identifier is" +" treated as a comment. If no \b$set\b directive is" +" specified in a message text source file, all messages" +" will be located in message set \b1\b.]" +" [+$translation \aidentification\a \aYYYY-MM-DD\a[,...]]?Append" +" translation info to the message catalog header. Only" +" the newest date for a given \aidentification\a" +" is retained in the catalog. Multiple translation lines" +" are combined into a single \b,\b separated list.]" +" [+\am\a \amessage-text\a?\am\a denotes the message identifier," +" which is defined as a number in the range" +" [1, \bNL_MSGMAX\b]]. The message-text is stored in the" +" message catalogue with the set identifier specified by" +" the last \b$set\b directive, and with message" +" identifier \am\a. If the \amessage-text\a is empty," +" and a blank character field separator is present, an" +" empty string is stored in the message catalogue. If a" +" message source line has a message number, but neither" +" a field separator nor \amessage-text\a, the existing" +" message with that number (if any) is deleted from the" +" catalogue. Message identifiers need not be contiguous." +" There are no \amessage-text\a length restrictions.]" +"}" + +"\n" +"\ncatfile [ msgfile ]\n" +"\n" + +"[+SEE ALSO?\bgencat\b(1), \biconv\b(1), \bmsgcc\b(1), \btranslate\b(1)," +" \bfmtfmt\b(3)]" +; + +#include +#include +#include +#include +#include + +typedef struct Xl_s +{ + struct Xl_s* next; + char* date; + char name[1]; +} Xl_t; + +/* + * append s to the translation list + */ + +static Xl_t* +translation(Xl_t* xp, register char* s) +{ + register Xl_t* px; + register char* t; + char* d; + char* e; + + do + { + for (; isspace(*s); s++); + for (d = e = 0, t = s; *t; t++) + if (*t == ',') + { + e = t; + *e++ = 0; + break; + } + else if (isspace(*t)) + d = t; + if (d) + { + *d++ = 0; + for (px = xp; px; px = px->next) + if (streq(px->name, s)) + { + if (strcoll(px->date, d) < 0) + { + free(px->date); + if (!(px->date = strdup(d))) + error(ERROR_SYSTEM|3, "out of space [translation]"); + } + break; + } + if (!px) + { + if (!(px = newof(0, Xl_t, 1, strlen(s))) || !(px->date = strdup(d))) + error(ERROR_SYSTEM|3, "out of space [translation]"); + strcpy(px->name, s); + px->next = xp; + xp = px; + } + } + } while (s = e); + return xp; +} + +/* + * sfprintf() with ccmaps(from,to) + */ + +static int +ccsfprintf(int from, int to, Sfio_t* sp, const char* format, ...) +{ + va_list ap; + Sfio_t* tp; + char* s; + int n; + + va_start(ap, format); + if (from == to) + n = sfvprintf(sp, format, ap); + else if (tp = sfstropen()) + { + n = sfvprintf(tp, format, ap); + s = sfstrbase(tp); + ccmaps(s, n, from, to); + n = sfwrite(sp, s, n); + sfstrclose(tp); + } + else + n = -1; + return n; +} + +int +main(int argc, char** argv) +{ + register Mc_t* mc; + register char* s; + register char* t; + register int c; + register int q; + register int i; + int num; + char* b; + char* e; + char* catfile; + char* msgfile; + Sfio_t* sp; + Sfio_t* mp; + Sfio_t* tp; + Xl_t* px; + Xl_t* bp; + + Xl_t* xp = 0; + int format = 0; + int list = 0; + int set = 0; + + NoP(argc); + error_info.id = "msggen"; + for (;;) + { + switch (optget(argv, usage)) + { + case 'f': + format = list = 1; + continue; + case 'l': + list = 1; + continue; + case 's': + set = 1; + 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 || !(catfile = *argv++)) + error(ERROR_USAGE|4, "%s", optusage(NiL)); + + /* + * set and list only need catfile + */ + + if (set) + { + sfprintf(sfstdout, "%d\n", mcindex(catfile, NiL, NiL, NiL)); + return error_info.errors != 0; + } + else if (list) + { + if (!(sp = sfopen(NiL, catfile, "r"))) + error(ERROR_SYSTEM|3, "%s: cannot read catalog", catfile); + if (!(mc = mcopen(sp))) + error(ERROR_SYSTEM|3, "%s: catalog content error", catfile); + sfclose(sp); + if (format) + { + for (set = 1; set <= mc->num; set++) + if (mc->set[set].num) + { + sfprintf(sfstdout, "$set %d\n", set); + for (num = 1; num <= mc->set[set].num; num++) + if (s = mc->set[set].msg[num]) + sfprintf(sfstdout, "%d \"%s\"\n", num, fmtfmt(s)); + } + } + else + { + if (*mc->translation) + { + ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$translation "); + sfprintf(sfstdout, "%s", mc->translation); + ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\n"); + } + ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$quote \"\n"); + for (set = 1; set <= mc->num; set++) + if (mc->set[set].num) + { + ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "$set %d\n", set); + for (num = 1; num <= mc->set[set].num; num++) + if (s = mc->set[set].msg[num]) + { + ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "%d \"", num); + while (c = *s++) + { + /*INDENT...*/ + + switch (c) + { + case 0x22: /* " */ + case 0x5C: /* \ */ + sfputc(sfstdout, 0x5C); + break; + case 0x07: /* \a */ + c = 0x61; + sfputc(sfstdout, 0x5C); + break; + case 0x08: /* \b */ + c = 0x62; + sfputc(sfstdout, 0x5C); + break; + case 0x0A: /* \n */ + c = 0x6E; + sfputc(sfstdout, 0x5C); + break; + case 0x0B: /* \v */ + c = 0x76; + sfputc(sfstdout, 0x5C); + break; + case 0x0C: /* \f */ + c = 0x66; + sfputc(sfstdout, 0x5C); + break; + case 0x0D: /* \r */ + c = 0x72; + sfputc(sfstdout, 0x5C); + break; + } + + /*...UNDENT*/ + sfputc(sfstdout, c); + } + ccsfprintf(CC_NATIVE, CC_ASCII, sfstdout, "\"\n"); + } + } + } + mcclose(mc); + return error_info.errors != 0; + } + else if (!(msgfile = *argv++) || *argv) + error(3, "exactly one message file must be specified"); + + /* + * open the files and handles + */ + + if (!(tp = sfstropen())) + error(ERROR_SYSTEM|3, "out of space [string stream]"); + if (!(mp = sfopen(NiL, msgfile, "r"))) + error(ERROR_SYSTEM|3, "%s: cannot read message file", msgfile); + sp = sfopen(NiL, catfile, "r"); + if (!(mc = mcopen(sp))) + error(ERROR_SYSTEM|3, "%s: catalog content error", catfile); + if (sp) + sfclose(sp); + xp = translation(xp, mc->translation); + + /* + * read the message file + */ + + q = 0; + set = 1; + error_info.file = msgfile; + while (s = sfgetr(mp, '\n', 1)) + { + error_info.line++; + if (!*s) + continue; + if (*s == '$') + { + if (!*++s || isspace(*s)) + continue; + for (t = s; *s && !isspace(*s); s++); + if (*s) + *s++ = 0; + if (streq(t, "delset")) + { + while (isspace(*s)) + s++; + num = (int)strtol(s, NiL, 0); + if (num < mc->num && mc->set[num].num) + for (i = 1; i <= mc->set[num].num; i++) + mcput(mc, num, i, NiL); + } + else if (streq(t, "quote")) + q = *s ? *s : 0; + else if (streq(t, "set")) + { + while (isspace(*s)) + s++; + num = (int)strtol(s, &e, 0); + if (e != s) + set = num; + else + error(2, "set number expected"); + } + else if (streq(t, "translation")) + xp = translation(xp, s); + } + else + { + t = s + sfvalue(mp); + num = (int)strtol(s, &e, 0); + if (e != s) + { + s = e; + if (!*s) + { + if (mcput(mc, set, num, NiL)) + error(2, "(%d,%d): cannot delete message", set, num); + } + else if (isspace(*s++)) + { + if (t > (s + 1) && *(t -= 2) == '\\') + { + sfwrite(tp, s, t - s); + while (s = sfgetr(mp, '\n', 0)) + { + error_info.line++; + t = s + sfvalue(mp); + if (t <= (s + 1) || *(t -= 2) != '\\') + break; + sfwrite(tp, s, t - s); + } + if (!(s = sfstruse(tp))) + error(ERROR_SYSTEM|3, "out of space"); + } + if (q) + { + if (*s++ != q) + { + error(2, "(%d,%d): %c quote expected", set, num, q); + continue; + } + b = t = s; + while (c = *s++) + { + if (c == '\\') + { + c = chresc(s - 1, &e); + s = e; + if (c) + *t++ = c; + else + error(1, "nul character ignored"); + } + else if (c == q) + break; + else + *t++ = c; + } + if (*s) + { + error(2, "(%d,%d): characters after quote not expected", set, num); + continue; + } + *t = 0; + s = b; + } + if (mcput(mc, set, num, s)) + error(2, "(%d,%d): cannot add message", set, num); + } + else + error(2, "message text expected"); + } + else + error(2, "message number expected"); + } + } + error_info.file = 0; + error_info.line = 0; + + /* + * fix up the translation record + */ + + if (xp) + { + t = ""; + for (;;) + { + for (bp = 0, px = xp; px; px = px->next) + if (px->date && (!bp || strcoll(bp->date, px->date) < 0)) + bp = px; + if (!bp) + break; + sfprintf(tp, "%s%s %s", t, bp->name, bp->date); + t = ", "; + bp->date = 0; + } + if (!(mc->translation = sfstruse(tp))) + error(ERROR_SYSTEM|3, "out of space"); + } + + /* + * dump the catalog to a local temporary + * rename if no errors + */ + + if (!(s = pathtemp(NiL, 0, "", error_info.id, NiL)) || !(sp = sfopen(NiL, s, "w"))) + error(ERROR_SYSTEM|3, "%s: cannot write catalog file", catfile); + if (mcdump(mc, sp) || mcclose(mc) || sfclose(sp)) + { + remove(s); + error(ERROR_SYSTEM|3, "%s: temporary catalog file write error", s); + } + remove(catfile); + if (rename(s, catfile)) + error(ERROR_SYSTEM|3, "%s: cannot rename from temporary catalog file %s", catfile, s); + return error_info.errors != 0; +} Index: src/cmd/ast/msgcc/PROMO.mm =================================================================== --- src/cmd/ast/msgcc/PROMO.mm (revision 0) +++ src/cmd/ast/msgcc/PROMO.mm (revision 740) @@ -0,0 +1,22 @@ +.H 1 msgcc +.B msgcc +and +.B msgcpp +extract message text from C source for +.BR gencat (1) +message catalogs. +.BR msggen (1) +is a +.BR gencat (1) +replacement that generates machine independent binary message +catalogs that are compatible with the +.B ast +.BR catgets (3) +implementation. +.B catgets +also supports native message catalogs where available. +.BR msgcvt (1) +and +.BR msgadmin (1) +are administrative commands that support machine translation +of C locale message catalogs. Index: src/cmd/ast/msgcc/msgcvt.c =================================================================== --- src/cmd/ast/msgcc/msgcvt.c (revision 0) +++ src/cmd/ast/msgcc/msgcvt.c (revision 740) @@ -0,0 +1,691 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 2000-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + */ + +static const char usage[] = +"[-?\n@(#)$Id: msgcvt (AT&T Research) 2000-05-01 $\n]" +USAGE_LICENSE +"[+NAME?msgcvt - convert message file to/from html]" +"[+DESCRIPTION?\bmsgcvt\b reads a \bgencat\b(1) format file on the standard" +" input and converts it to \bhtml\b on the standard output. The input" +" file must contain the control statement \b$quote \"\b and use the \"" +" character to quote message text. The output is in a form suitable for" +" automatic translation by web sites like" +" \bhttp://babelfish.altavista.com/\b or filters like" +" \btranslate\b(1).]" +"[h:html?Generate \bhtml\b from \bgencat\b(1) input. This is the default.]" +"[m:msg?Generate a \bgencat\b(1) message file from (presumably translated)" +" \bhtml\b. Wide characters are UTF-8 encoded.]" +"[r:raw?The message file is raw message text, one message per line, with no" +" quoting or line numbering.]" +"[+SEE ALSO?\bgencat\b(1), \bmsgcc\b(1), \bmsggen\b(1), \btranslate\b(1)]" +; + +#include +#include +#include + +#define MSG_RAW (1<<0) +#define MSG_SPLICE (1<<1) + +#define SPACE(s) (isspace(*s)&&(s+=1)||*s=='\\'&&(*(s+1)=='n'||*(s+1)=='t')&&(s+=2)) + +typedef void (*Convert_f)(Sfio_t*, Sfio_t*, int); + +typedef struct +{ + const char* name; + int code; +} Code_t; + +static const Code_t codes[] = +{ + "aacute", 225, + "Aacute", 193, + "acirc", 226, + "Acirc", 194, + "aelig", 230, + "AElig", 198, + "agrave", 224, + "Agrave", 192, + "amp", '&', + "aring", 229, + "Aring", 197, + "atilde", 227, + "Atilde", 195, + "auml", 228, + "Auml", 196, + "ccedil", 231, + "Ccedil", 199, + "copy", 169, + "eacute", 233, + "Eacute", 201, + "ecirc", 234, + "Ecirc", 202, + "egrave", 232, + "Egrave", 200, + "euml", 235, + "Euml", 203, + "gt", '>', + "iacute", 237, + "Iacute", 205, + "icirc", 238, + "Icirc", 206, + "igrave", 236, + "Igrave", 204, + "iuml", 239, + "Iuml", 207, + "lt", '<', + "nbsp", ' ', + "ntilde", 241, + "Ntilde", 209, + "oacute", 243, + "Oacute", 211, + "ocirc", 244, + "Ocirc", 212, + "ograve", 242, + "Ograve", 210, + "oslash", 248, + "Oslash", 216, + "otilde", 245, + "Otilde", 213, + "ouml", 246, + "Ouml", 214, + "quot", '"', + "reg", 174, + "szlig", 223, + "uacute", 250, + "Uacute", 218, + "ucirc", 251, + "Ucirc", 219, + "ugrave", 249, + "Ugrave", 217, + "uuml", 252, + "Uuml", 220, + "yuml", 255, +}; + +static int +decode(Sfio_t* ip) +{ + register int c; + register int i; + char name[32]; + + if ((c = sfgetc(ip)) == EOF) + return '&'; + name[0] = c; + i = 1; + if (c != '#' && !isalpha(c)) + goto bad; + while ((c = sfgetc(ip)) != EOF && c != ';') + { + if (c == '&') + i = 0; + else + { + name[i++] = c; + if (!isalnum(c) && (i > 1 || c != '#') || i >= (elementsof(name) - 1)) + goto bad; + } + } + name[i] = 0; + if (name[0] == '#') + { + switch (c = strtol(name + 1, NiL, 10)) + { + case 91: + c = '['; + break; + case 93: + c = ']'; + break; + } + } + else + { + for (i = 0; i < elementsof(codes); i++) + if (streq(codes[i].name, name)) + { + c = codes[i].code; + break; + } + if (i >= elementsof(codes)) + goto bad; + } + return c; + bad: + name[i] = 0; + if (c == ';') + error(1, "&%s: unknown HTML special character -- & assumed", name); + else + error(1, "&%s: invalid HTML special character -- & assumed", name); + while (i--) + sfungetc(ip, name[i]); + return '&'; +} + +static int +sfpututf(Sfio_t* op, register int w) +{ + if (!(w & ~0x7F)) + return sfputc(op, w); + else if (!(w & ~0x7FF)) + sfputc(op, 0xC0 + (w >> 6)); + else if (!(w & ~0xFFFF)) + { + sfputc(op, 0xE0 + (w >> 12)); + sfputc(op, 0x80 + (w >> 6 ) & 0x3F); + } + else + return sfputc(op, '?'); + return sfputc(op, 0x80 + (w & 0x3F)); +} + +static int +sfnext(Sfio_t* ip) +{ + register int c; + + while (isspace(c = sfgetc(ip))); + return c; +} + +static void +html2msg(register Sfio_t* ip, register Sfio_t* op, int flags) +{ + register int c; + register int q; + + again: + while ((c = sfgetc(ip)) != EOF) + if (c == '<') + { + if ((c = sfnext(ip)) == 'O' && + (c = sfnext(ip)) == 'L' && + isspace(c = sfgetc(ip)) && + (c = sfnext(ip)) == 'S' && + (c = sfnext(ip)) == 'T' && + (c = sfnext(ip)) == 'A' && + (c = sfnext(ip)) == 'R' && + (c = sfnext(ip)) == 'T' && + (c = sfnext(ip)) == '=' && + (c = sfnext(ip)) == '"' && + (c = sfnext(ip)) == '5' && + (c = sfnext(ip)) == '5' && + (c = sfnext(ip)) == '0' && + (c = sfnext(ip)) == '7' && + (c = sfnext(ip)) == '1' && + (c = sfnext(ip)) == '7' && + (c = sfnext(ip)) == '"' && + (c = sfnext(ip)) == '>') + break; + while (c != EOF && c != '>') + c = sfgetc(ip); + } + if ((c = sfnext(ip)) != EOF) + sfungetc(ip, c); + q = 0; + for (;;) + { + switch (c = sfgetc(ip)) + { + case EOF: + break; + case '&': + c = decode(ip); + sfpututf(op, c); + if (isspace(c)) + { + while (isspace(c = sfgetc(ip))); + if (c == EOF) + break; + sfungetc(ip, c); + } + continue; + case '<': + switch (c = sfnext(ip)) + { + case '/': + if ((c = sfnext(ip)) == 'O' && + (c = sfgetc(ip)) == 'L' && + (c = sfnext(ip)) == '>') + { + if (q) + { + sfputc(op, q); + q = '"'; + } + goto again; + } + break; + case 'B': + if ((c = sfgetc(ip)) == 'R' && + (c = sfnext(ip)) == '>') + sfputc(op, ' '); + break; + case 'L': + if ((c = sfgetc(ip)) == 'I' && + (c = sfnext(ip)) == '>' && + isdigit(c = sfnext(ip))) + { + if (q) + sfputc(op, q); + else + q = '"'; + sfputc(op, '\n'); + do + { + sfputc(op, c); + } while (isdigit(c = sfgetc(ip))); + if (c == EOF) + break; + sfputc(op, ' '); + sfputc(op, '"'); + if (isspace(c)) + c = sfnext(ip); + if (c == '<' && + (c = sfnext(ip)) == 'L' && + (c = sfgetc(ip)) == 'I' && + (c = sfnext(ip)) == '>') + /* great */; + continue; + } + break; + case 'P': + if ((c = sfnext(ip)) == '>') + sfputc(op, '\n'); + else if (c == 'C' && + (c = sfgetc(ip)) == 'L' && + (c = sfgetc(ip)) == 'A' && + (c = sfgetc(ip)) == 'S' && + (c = sfgetc(ip)) == 'S' && + (c = sfnext(ip)) == '=' && + (c = sfnext(ip)) == '"') + for (;;) + { + switch (c = sfgetc(ip)) + { + case EOF: + case '"': + break; + case '&': + c = decode(ip); + sfpututf(op, c); + continue; + default: + sfpututf(op, c); + continue; + } + break; + } + break; + } + while (c != EOF && c != '>') + c = sfgetc(ip); + if (c == EOF || (c = sfgetc(ip)) == EOF) + break; + sfungetc(ip, c); + continue; + case '"': + if (!flags) + sfputc(op, '\\'); + sfputc(op, c); + continue; + case '\n': + if (flags) + { + sfputc(op, c); + continue; + } + /*FALLTHROUGH*/ + case ' ': + case '\t': + while ((c = sfgetc(ip)) != EOF) + if (c == '&') + { + c = decode(ip); + if (!isspace(c)) + sfputc(op, ' '); + sfpututf(op, c); + break; + } + else if (!isspace(c)) + { + if (c == '<') + { + c = sfgetc(ip); + if (c == EOF) + break; + sfungetc(ip, c); + sfungetc(ip, '<'); + if (c != 'L' && c != '/') + sfputc(op, ' '); + } + else + { + if (c != EOF) + sfungetc(ip, c); + sfputc(op, ' '); + } + break; + } + continue; + case '\r': + case '[': + case ']': + continue; + default: + sfpututf(op, c); + continue; + } + break; + } + if (q) + sfputc(op, q); + sfputc(op, '\n'); +} + +static void +encode(Sfio_t* op, register int c) +{ + if (c == '<') + sfprintf(op, "<"); + else if (c == '>') + sfprintf(op, ">"); + else if (c == '"') + sfprintf(op, """); + else if (c == '&') + sfprintf(op, "&"); + else if (c == '[') + sfprintf(op, "["); + else if (c == ']') + sfprintf(op, "]"); + else + sfputc(op, c); +} + +static void +msg2html(register Sfio_t* ip, register Sfio_t* op, register int flags) +{ + register char* s; + register int c; + register int q; + register int p; + + sfprintf(op, "\n"); + sfprintf(op, "
    \n"); + p = q = 0; + while (s = sfgetr(ip, '\n', 1)) + { + error_info.line++; + if (flags) + sfprintf(op, "

    "); + else + { + if (*s == '$') + { + if (p) + sfprintf(op, "

    "); + else + p = 1; + sfprintf(op, "

    \n"); + continue; + } + p = 0; + if (!isdigit(*s)) + continue; + sfprintf(op, "

  1. "); + while (isdigit(c = *s++)) + sfputc(op, c); + sfprintf(op, "
  2. "); + while (c && c != '"') + c = *s++; + if (!c) + s--; + else if (isspace(*s)) + { + s++; + sfprintf(op, "
    "); + } + } + for (;;) + { + switch (c = *s++) + { + case 0: + flags &= ~MSG_SPLICE; + if (q) + { + q = 0; + sfprintf(op, "\">"); + } + sfputc(op, '\n'); + break; + case '<': + sfprintf(op, "<"); + continue; + case '>': + sfprintf(op, ">"); + continue; + case '&': + sfprintf(op, "&"); + continue; + case '[': + sfprintf(op, "["); + continue; + case ']': + sfprintf(op, "]"); + continue; + case '$': + if (!q) + { + q = 1; + sfprintf(op, "

    "); + } + else + sfprintf(op, "
    "); + continue; + } + c = ' '; + /*FALLTHROUGH*/ + default: + if (q) + { + q = 0; + sfprintf(op, "\">"); + } + sfputc(op, c); + continue; + } + break; + } + } + sfprintf(op, "

\n"); + sfprintf(op, "\n"); + error_info.line = 0; +} + +int +main(int argc, char** argv) +{ + int flags = 0; + Convert_f convert = msg2html; + + NoP(argc); + error_info.id = "msgcvt"; + for (;;) + { + switch (optget(argv, usage)) + { + case 'h': + convert = msg2html; + continue; + case 'm': + convert = html2msg; + continue; + case 'r': + flags |= MSG_RAW; + 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)); + (*convert)(sfstdin, sfstdout, flags); + return error_info.errors != 0; +} Index: src/cmd/ast/msgcc/msgcc.sh =================================================================== --- src/cmd/ast/msgcc/msgcc.sh (revision 0) +++ src/cmd/ast/msgcc/msgcc.sh (revision 740) @@ -0,0 +1,405 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 2000-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# Glenn Fowler # +# # +######################################################################## +: C language message catalog compiler + +# NOTE: all variable names match __*__ to avoid clash with msgcpp def vars + +__command__=msgcc +integer __similar__=30 + +case `(getopts '[-][123:xyz]' opt --xyz; echo 0$opt) 2>/dev/null` in +0123) ARGV0="-a $__command__" + USAGE=$' +[-? +@(#)$Id: msgcc (AT&T Labs Research) 2002-09-15 $ +] +'$USAGE_LICENSE$' +[+NAME?msgcc - C language message catalog compiler] +[+DESCRIPTION?\bmsgcc\b is a C language message catalog compiler. It accepts + \bcc\b(1) style options and arguments. A \bmsgcpp\b(1) \b.mso\b file + is generated for each input \b.c\b file. If the \b-c\b option is not + specified then a \bgencat\b(1) format \b.msg\b file is generated from + the input \b.mso\b and \b.msg\b files. If \b-c\b is not specified then + a \b.msg\b suffix is appended to the \b-o\b \afile\a if it doesn\'t + already have a suffix. The default output is \ba.out.msg\b if \b-c\b + and \b-o\b are not specified.] +[+?If \b-M-new\b is not specified then messages are merged with those in the + pre-existing \b-o\b file.] +[M?Set a \bmsgcc\b specific \aoption\a. \aoption\a may be:]:[-option]{ + [+mkmsgs?The \b-o\b file is assumed to be in \bmkmsgs\b(1) format.] + [+new?Create a new \b-o\b file.] + [+preserve?Messages in the \b-o\b file that are not in new + \b.msg\b file arguments are preserved. The default is to + either reuse the message numbers with new message text that + is similar to the old or to delete the message text, leaving + an unused message number.] + [+set=\anumber\a?Set the message set number to \anumber\a. The default + is \b1\b.] + [+similar=\anumber\a?The message text similarity measure thresshold. + The similarity measure between \aold\a and \anew\a message + text is 100*(2*gzip(\aold\a+\anew\a)/(gzip(\aold\a)+gzip(\anew\a))-1), + where gzip(\ax\a) is the size of text \ax\a when compressed by + \bgzip\b(1). The default threshhold is '$__similar__$'. A + threshhold of \b0\b turns off message replacement, but unused + old messages are still deleted. Use \b-M-preserve\b to preserve + all old messages.] + [+verbose?Trace similar message replacements on the standard error.] +} + +file ... + +[+SEE ALSO?\bcc\b(1), \bcpp\b(1), \bgencat\b(1), \bmsggen\b(1), + \bmsgcpp\b(1), \bmsgcvt\b(1)] +' + ;; +*) ARGV0="" + USAGE="M:[-option] [ cc-options ] file ..." + ;; +esac + +usage() +{ + OPTIND=0 + getopts $ARGV0 "$USAGE" OPT '-?' + exit 2 +} + +keys() +{ + $1 --??keys -- 2>&1 | grep '^".*"$' +} + +typeset -A __index__ +typeset __keep__ __text__ __drop__ __oz__ __nz__ __z__ __hit__ __hit_i__ +typeset __compile__ __debug__ __mkmsgs__ __preprocess__ +typeset __merge__=1 __preserve__ __verbose__ +integer __i__=0 __args__=0 __code__=0 __files__=0 __max__=0 __num__=0 __skip__=0 +integer __set__=1 __sources__=0 __cmds__=0 __ndrop__=0 __new__=0 __old__=0 +__out__=a.out.msg +__OUT__= + +case " $* " in +*" --"*|*" -?"*) + while getopts $ARGV0 "$USAGE" OPT + do case $OPT in + *) break ;; + esac + done + ;; +esac +while : +do case $# in + 0) break ;; + esac + __arg__=$1 + case $__arg__ in + -c) __compile__=1 + ;; + -[DIU]*)__argv__[__args__]=$__arg__ + (( __args__++ )) + ;; + -E) __preprocess__=1 + ;; + -M-debug) + __debug__=1 + ;; + -M-mkmsgs) + __mkmsgs__=1 + ;; + -M-new) __merge__= + ;; + -M-perserve) + __preserve__=1 + ;; + -M-set=*) + __set__=$(msggen -s ${__arg__#*=}.1) + ;; + -M-similar=*) + __similar__=${__arg__#*=} + ;; + -M-verbose) + __verbose__=1 + ;; + -o) case $# in + 1) print -u2 $"$__command__: output argument expected" + exit 1 + ;; + esac + shift + __out__=${1%.*}.msg + __OUT__=$1 + ;; + [-+]*|*.[aAlLsS]*) + ;; + *.[cCiI]*|*.[oO]*) + case $__arg__ in + *.[oO]*);; + *) __srcv__[__files__]=$__arg__ + (( __sources__++ )) + ;; + esac + __arg__=${__arg__##*/} + __arg__=${__arg__%.*}.mso + __objv__[__files__]=$__arg__ + (( __files__++ )) + ;; + *.ms[go]) + __objv__[__files__]=$__arg__ + (( __files__++ )) + ;; + *) __cmdv__[__cmds__]=$__arg__ + (( __cmds__++ )) + ;; + esac + shift +done +__cmdv__[__cmds__]=${__out__%.msg} +(( __cmds__++ )) + +# generate the .mso files + +if [[ $__OUT__ && $__compile__ ]] +then __objv__[0]=$__OUT__ +fi + +if (( __sources__ )) +then for (( __i__=0; __i__<=__files__; __i__++ )) + do if [[ ${__srcv__[__i__]} ]] + then if (( __sources__ > 1 )) + then print "${__srcv__[__i__]}:" + fi + if [[ $__preprocess__ ]] + then msgcpp "${__argv__[@]}" "${__srcv__[__i__]}" + else msgcpp "${__argv__[@]}" "${__srcv__[__i__]}" > "${__objv__[__i__]}" + fi + fi + done +fi + +# combine the .mso and .msg files + +if [[ ! $__compile__ && ! $__preprocess__ ]] +then if [[ $__merge__ && -r $__out__ ]] + then __tmp__=$__out__.tmp + trap '__code__=$?; rm -f ${__tmp__}*; exit $__code__' 0 1 2 + while read -r __line__ + do if (( $__skip__ )) + then if [[ $__line__ == '%}'* ]] + then __skip__=0 + fi + continue + fi + if [[ $__mkmsgs__ && $__line__ == '%{'* ]] + then __skip__=1 + continue + fi + if [[ $__mkmsgs__ ]] + then if [[ $__line__ == '%#'*';;'* ]] + then __line__=${__line__#'%#'} + __num__=${__line__%';;'*} + read -r __line__ + elif [[ $__line__ == %* ]] + then continue + else print -u2 $"$__command__: unrecognized line=$__line__" + __code__=1 + fi + else case $__line__ in + +([0-9])' '*) + __num__=${__line__%%' '*} + __line__=${__line__#*'"'} + __line__=${__line__%'"'} + ;; + *) continue + ;; + esac + fi + __index__["$__line__"]=$__num__ + __text__[$__num__]=$__line__ + if (( __max__ < __num__ )) + then (( __max__=__num__ )) + fi + done < $__out__ + (( __new__=__max__+1 )) + else __tmp__=$__out__ + (( __new__=1 )) + fi + if (( __code__ )) + then exit $__code__ + fi + exec 1>$__tmp__ 9>&1 + print -r -- '$'" ${__out__%.msg} message catalog" + print -r -- '$translation'" $__command__ $(date +%Y-%m-%d)" + print -r -- '$set'" $__set__" + print -r -- '$quote "' + sort -u "${__objv__[@]}" | { + while read -r __line__ + do __op__=${__line__%% *} + __line__=${__line__#* } + case $__op__ in + cmd) __a1__=${__line__%% *} + case $__a1__ in + dot_cmd) __a1__=. ;; + esac + keys $__a1__ + ;; + def) __a1__=${__line__%% *} + __a2__=${__line__#* } + eval $__a1__='$'__a2__ + ;; + str) print -r -- "$__line__" + ;; + var) __a1__=${__line__%% *} + __a2__=${__line__#* } + case $__a1__ in + [[:digit:]]*) + eval __v__='$'$__a2__ + __v__='"'${__v__:__a1__+1} + ;; + *) eval __v__='$'$__a1__ + ;; + esac + if [[ $__v__ == '"'*'"' ]] + then print -r -- "$__v__" + fi + ;; + [[:digit:]]*) + [[ $__preserve__ ]] && print -r -- "$__line__" + ;; + '$') print -r -u9 $__op__ include $__line__ + ;; + esac + done + for (( __i__=0; __i__ < __cmds__; __i__++ )) + do keys ${__cmdv__[__i__]} + done + } | { + __num__=1 + while read -r __line__ + do case $__line__ in + '$'[\ \ ]*) + print -r -- "$__line__" + continue + ;; + '$'*|*"@(#)"*|*"<"*([[:word:] .-])"@"*([[:word:] .-])">"*([ ])'"'|"http://"*) + continue + ;; + *[[:alpha:]][[:alpha:]]*) + __line__=${__line__#*'"'} + __line__=${__line__%'"'} + if [[ $__line__ ]] + then if [[ ${__index__["$__line__"]} ]] + then if [[ ! $__preserve__ ]] + then __num__=${__index__["$__line__"]} + __keep__[$__num__]=1 + fi + else while [[ ${__text__[$__num__]} ]] + do (( __num__++ )) + done + if (( __max__ < __num__ )) + then (( __max__=__num__ )) + fi + if [[ ! $__preserve__ ]] + then __keep__[$__num__]=1 + fi + __text__[$__num__]=$__line__ + __index__["$__line__"]=$__num__ + (( __num__++ )) + fi + fi + ;; + esac + done + if (( __max__ < __num__ )) + then (( __max__=__num__ )) + fi + if [[ $__debug__ ]] + then for (( __num__=1; __num__<=__max__; __num__++ )) + do if [[ ${__text__[$__num__]} ]] + then if (( __num__ > __new__ )) + then if [[ ! ${__keep__[$__num__]} ]] + then print -r -u2 -- $__num__ HUH '"'"${__text__[$__num__]}"'"' + else print -r -u2 -- $__num__ NEW '"'"${__text__[$__num__]}"'"' + fi + elif [[ ${__keep__[$__num__]} ]] + then print -r -u2 -- $__num__ OLD '"'"${__text__[$__num__]}"'"' + else print -r -u2 -- $__num__ XXX '"'"${__text__[$__num__]}"'"' + fi + fi + done + exit 0 + fi + # check for replacements + if [[ ! $__preserve__ ]] + then for (( __num__=1; __num__<__new__; __num__++ )) + do if [[ ${__text__[$__num__]} && ! ${__keep__[$__num__]} ]] + then (( __ndrop__++ )) + __drop__[__ndrop__]=$__num__ + fi + done + [[ $__verbose__ ]] && print -u2 $__command__: old:1-$((__new__-1)) new:$__new__-$__max__ drop $__ndrop__ add $((__max__-__new__+1)) + if (( __ndrop__ )) + then for (( __i__=1; __i__<=__ndrop__; __i__++ )) + do (( __old__=${__drop__[$__i__]} )) + __oz__[__i__]=$(print -r -- "\"${__text__[$__old__]}\"" | gzip | wc -c) + done + for (( __num__=__new__; __num__<=__max__; __num__++ )) + do [[ ${__text__[$__num__]} ]] || continue + __nz__=$(print -r -- "\"${__text__[$__num__]}\"" | gzip | wc -c) + __hit__=0 + (( __bz__=__similar__ )) + for (( __i__=1; __i__<=__ndrop__; __i__++ )) + do if (( __old__=${__drop__[$__i__]} )) + then __z__=$(print -r -- "\"${__text__[$__old__]}\"""\"${__text__[$__num__]}\"" | gzip | wc -c) + (( __z__ = (__z__ * 200 / (${__oz__[__i__]} + $__nz__)) - 100 )) + if (( __z__ < __bz__ )) + then (( __bz__=__z__ )) + (( __hit__=__old__ )) + (( __hit_i__=__i__ )) + fi + fi + done + if (( __hit__ )) + then [[ $__verbose__ ]] && print -u2 $__command__: $__hit__ $__num__ $__bz__ + __text__[$__hit__]=${__text__[$__num__]} + __keep__[$__hit__]=1 + __drop__[$__hit_i__]=0 + __text__[$__num__]= + __keep__[$__num__]= + fi + done + fi + fi + # final output + for (( __num__=1; __num__<=__max__; __num__++ )) + do if [[ ${__text__[$__num__]} && ( $__preserve__ || ${__keep__[$__num__]} ) ]] + then print -r -- $__num__ "\"${__text__[$__num__]}\"" + fi + done + } + if [[ $__tmp__ != $__out__ ]] + then grep -v '^\$' $__tmp__ > ${__tmp__}n + [[ -f $__out__ ]] && grep -v '^\$' $__out__ > ${__tmp__}o + cmp -s ${__tmp__}n ${__tmp__}o || { + [[ -f $__out__ ]] && mv $__out__ $__out__.old + mv $__tmp__ $__out__ + } + fi +fi +exit $__code__ Index: src/cmd/ast/msgcc/msgcc.tst =================================================================== --- src/cmd/ast/msgcc/msgcc.tst (revision 0) +++ src/cmd/ast/msgcc/msgcc.tst (revision 740) @@ -0,0 +1,28 @@ +# regression tests for the msgcc utility + +TEST 01 'basics' + EXEC -c t.c + NOTE 'pp:allpossible' + INPUT t.c $' + #include + void f(void) + { + #if 0 + error(1, "foo bar"); + #else + errormsg(locale, 2, "%s: bar foo"); + #endif + } + ' + OUTPUT t.mso $'str "foo bar"\nstr "%s: bar foo"' + OUTPUT - + EXEC -Dfprintf=_STDIO_ -c t.c + NOTE 'ignore readonly redefinitions' + INPUT t.c $' + #define stderr foo + void f(void) + { + fprintf(stderr, "foo bar"); + } + ' + OUTPUT t.mso $'str "foo bar"' Index: src/cmd/ast/msgcc/msgget.c =================================================================== --- src/cmd/ast/msgcc/msgget.c (revision 0) +++ src/cmd/ast/msgcc/msgget.c (revision 740) @@ -0,0 +1,109 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 2000-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + */ + +static const char usage[] = +"[-?\n@(#)$Id: msgget (AT&T Research) 2001-04-21 $\n]" +USAGE_LICENSE +"[+NAME?msgget - get a message from a message catalog]" +"[+DESCRIPTION?\bmsgget\b gets the message corresponding to the parameters." +" If \alocale\a is \b-\b then the current locale is used. \acommand\a" +" may be specified for command specific messages. \acatalog\a specifies" +" the message catalog name. [\aset\a.]]\anumber\a identifies the message" +" by message \anumber\a and an optional message \aset\a; if specified as" +" \b-\b then the message set and number are determined by looking up" +" \atext\a in the corresponding \bC\b locale message catalog.]" + +"\n" +"\nlocale [command:]catalog [set.]number [ text ]\n" +"\n" + +"[+SEE ALSO?\biconv\b(1), \bmsgcc\b(1), \bmsggen\b(1)]" +; + +#include +#include +#include + +int +main(int argc, char** argv) +{ + register Mc_t* mc; + register char* s; + char* loc; + char* cmd; + char* cat; + char* msg; + int set; + int num; + Sfio_t* sp; + char path[PATH_MAX]; + + NoP(argc); + error_info.id = "msgget"; + for (;;) + { + switch (optget(argv, usage)) + { + 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 || !(loc = *argv++) || !(cmd = *argv++) || !(s = *argv++)) + error(ERROR_USAGE|4, "%s", optusage(NiL)); + if (streq(s, "-")) + set = num = 0; + else + mcindex(s, NiL, &set, &num); + if (!(msg = *argv++)) + msg = ""; + else if (*argv) + error(ERROR_USAGE|4, "%s", optusage(NiL)); + if (streq(loc, "-")) + loc = 0; + if (cat = strchr(cmd, ':')) + *cat++ = 0; + if (!mcfind(path, loc, cmd, LC_MESSAGES, 0) && (!cat || !mcfind(path, loc, cat, LC_MESSAGES, 0))) + { + if (cat) + *--cat = ':'; + error(3, "%s: cannot locate message catalog", cmd); + } + if (!(sp = sfopen(NiL, path, "r"))) + error(ERROR_SYSTEM|3, "%s: cannot read message catalog", path); + if (!(mc = mcopen(sp))) + error(3, "%s: invalid message catalog", path); + if (set) + s = mcget(mc, set, num, msg); + else + s = errorx(loc, cmd, cat, msg); + sfputr(sfstdout, s, '\n'); + return error_info.errors != 0; +} Index: src/cmd/ast/msgcc/NOTES =================================================================== --- src/cmd/ast/msgcc/NOTES (revision 0) +++ src/cmd/ast/msgcc/NOTES (revision 740) @@ -0,0 +1,65 @@ +2000-04-01 ast message catalog plan + +(1) error_info.dictionary should be error_info.catalog + to match xopen and the internal naming in our implementation + and also to more closely match the webster definition + (catalog == enumerated list) + + DONE + +(2) nmake by default will + CATALOG = $(ID:N=+([A-Za-z0-9_]):?$(PWD:N=*/lib/*:Y,lib,,)$(ID)?$(PWD:B)?) + ERROR_CATALOG == "$(CATALOG)" + e.g., commands in src/cmd/std will use the "std" catalog; all of the + commands in src/lib/libcmd will use the "libcmd" catalog + + nmake will add "[--catalog?$(CATALOG)]" to USAGE_LICENSE + optget() will set error_info.catalog if not defined on the first call + commands that don't emit messages before optget() need not change + otherwise the command should + + error_info.id = "foo"; + error_info.catalog = ERROR_CATALOG; + + undefined references to { USAGE_LICENSE ERROR_CATALOG } are hard + compile time errors + + DONE + +(3) add catalog argument to libcmd cmdinit(argv, context, catalog) + + DONE + +(4) msgcat global target build msgs/*.mso and $(CATALOG).msg + each Makefile will generate one catalog $(CATALOG).msg where + + the *.msg files are weird -- we need to build them viewed over an + architecture specific tree, even though they will be eventually used + as architecture independent source + + $(CATALOG).msg will be the "C" locale + + debug will be a debugging locale that will translate each message to + (CATALOG-NAME:MESSAGE-INDEX)\n + this will make it easy to locate text that escaped translation (in what + should be translated output); it will also be a way for us to do + regression tests in the face of typo fixes -- presumably typos can be + fixed without changing the message index + + see msgadmin(1) + + DONE + +(5) once all this is working I'll do catopen(3) and msggen(1) + + DONE + +(6) the makerules "all" action will + catgen $(CATALOG).cat + catgen $(CATALOG)-*.cat + and the makerules "install" action will copy the catgen output to + $(LOCALEDIR)/$(LOCALE)/LC_MESSAGES/$(CATALOG)* + where + LOCALEDIR = $(INSTALLROOT)/lib/locale + + NOTE: still under consideration Index: src/cmd/ast/msgcc/Mamfile =================================================================== --- src/cmd/ast/msgcc/Mamfile (revision 0) +++ src/cmd/ast/msgcc/Mamfile (revision 740) @@ -0,0 +1,291 @@ +info mam static 00000 1994-07-17 make (AT&T Research) 5.2 2007-01-11 +setv INSTALLROOT ../../.. +setv PACKAGE_ast_INCLUDE ${INSTALLROOT}/include/ast +setv PACKAGE_ast_LIB ${INSTALLROOT}/lib +setv PACKAGEROOT ../../../../.. +setv AR ar +setv ARFLAGS cr +setv AS as +setv ASFLAGS +setv CC cc +setv mam_cc_FLAGS +setv CCFLAGS ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${mam_cc_OPTIMIZE}?} +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 msgadmin +make msgadmin.sh +done msgadmin.sh +meta msgadmin %.sh>% msgadmin.sh msgadmin +prev msgadmin.sh +exec - case static,port:$OPTIND:$RANDOM in +exec - ?*:*:*|*::*|*:*:$RANDOM) +exec - ;; +exec - *) if ENV= x= $SHELL -nc ': ${list[level]} !(pattern)' 2>/dev/null +exec - then ENV= $SHELL -n msgadmin.sh +exec - fi +exec - ;; +exec - esac +exec - case '${mam_cc_SHELLMAGIC}' in +exec - "") case 194 in +exec - 0) cp msgadmin.sh msgadmin +exec - ;; +exec - *) { +exec - i=`(read x; echo $x) < msgadmin.sh` +exec - case $i in +exec - '#!'*|*'||'*|':'*|'":"'*|"':'"*) echo "$i" ;; +exec - esac +exec - cat - msgadmin.sh <<'!' +exec - USAGE_LICENSE="[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]" +exec - ! +exec - } > msgadmin +exec - ;; +exec - esac +exec - ;; +exec - *) cat - msgadmin.sh > msgadmin <<'!' +exec - ${mam_cc_SHELLMAGIC} +exec - USAGE_LICENSE="[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]" +exec - ! +exec - ;; +exec - esac +exec - silent test -w msgadmin -a -x msgadmin || chmod u+w,+x msgadmin +done msgadmin generated +make msgcpp +make msgcpp.o +make msgcpp.c +make ${PACKAGE_ast_INCLUDE}/ppkey.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}/ppkey.h +make ${PACKAGE_ast_INCLUDE}/pp.h implicit +make ${PACKAGE_ast_INCLUDE}/ccode.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_ccode.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_ccode.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast.h implicit +make ${PACKAGE_ast_INCLUDE}/vmalloc.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 +make ${PACKAGE_ast_INCLUDE}/endian.h implicit +make ${PACKAGE_ast_INCLUDE}/bytesex.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +done ${PACKAGE_ast_INCLUDE}/bytesex.h dontcare +done ${PACKAGE_ast_INCLUDE}/endian.h dontcare +done ${PACKAGE_ast_INCLUDE}/ast_common.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +make ${PACKAGE_ast_INCLUDE}/regex.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/regex.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 +make ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_fs.h dontcare +done ${PACKAGE_ast_INCLUDE}/ast_fcntl.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_getopt.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_sys.h implicit +make ${PACKAGE_ast_INCLUDE}/getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/getopt.h dontcare +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 +done ${PACKAGE_ast_INCLUDE}/vmalloc.h dontcare +make ${PACKAGE_ast_INCLUDE}/sfio.h implicit +make ${PACKAGE_ast_INCLUDE}/sfio_s.h implicit +done ${PACKAGE_ast_INCLUDE}/sfio_s.h dontcare +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 +make ${PACKAGE_ast_INCLUDE}/ast_version.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_version.h dontcare +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}/ccode.h dontcare +make ${PACKAGE_ast_INCLUDE}/error.h implicit +make ${PACKAGE_ast_INCLUDE}/option.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +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 dontcare +make ${PACKAGE_ast_INCLUDE}/hash.h implicit +make ${PACKAGE_ast_INCLUDE}/hashpart.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/hashpart.h dontcare +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/hash.h dontcare +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/pp.h +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done msgcpp.c +meta msgcpp.o %.c>%.o msgcpp.c msgcpp +prev msgcpp.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DUSAGE_LICENSE=\""[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]"\" -c msgcpp.c +done msgcpp.o generated +bind -lpp +bind -last +exec - ${CC} ${CCLDFLAGS} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ${mam_cc_L+-L${INSTALLROOT}/lib} -o msgcpp msgcpp.o ${mam_libpp} ${mam_libast} +done msgcpp generated +make msgcc +make msgcc.sh +done msgcc.sh +meta msgcc %.sh>% msgcc.sh msgcc +prev msgcc.sh +exec - case static,port:$OPTIND:$RANDOM in +exec - ?*:*:*|*::*|*:*:$RANDOM) +exec - ;; +exec - *) if ENV= x= $SHELL -nc ': ${list[level]} !(pattern)' 2>/dev/null +exec - then ENV= $SHELL -n msgcc.sh +exec - fi +exec - ;; +exec - esac +exec - case '${mam_cc_SHELLMAGIC}' in +exec - "") case 194 in +exec - 0) cp msgcc.sh msgcc +exec - ;; +exec - *) { +exec - i=`(read x; echo $x) < msgcc.sh` +exec - case $i in +exec - '#!'*|*'||'*|':'*|'":"'*|"':'"*) echo "$i" ;; +exec - esac +exec - cat - msgcc.sh <<'!' +exec - USAGE_LICENSE="[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]" +exec - ! +exec - } > msgcc +exec - ;; +exec - esac +exec - ;; +exec - *) cat - msgcc.sh > msgcc <<'!' +exec - ${mam_cc_SHELLMAGIC} +exec - USAGE_LICENSE="[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]" +exec - ! +exec - ;; +exec - esac +exec - silent test -w msgcc -a -x msgcc || chmod u+w,+x msgcc +done msgcc generated +make msgcvt +make msgcvt.o +make msgcvt.c +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done msgcvt.c +meta msgcvt.o %.c>%.o msgcvt.c msgcvt +prev msgcvt.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DUSAGE_LICENSE=\""[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]"\" -c msgcvt.c +done msgcvt.o generated +exec - ${CC} ${CCLDFLAGS} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ${mam_cc_L+-L${INSTALLROOT}/lib} -o msgcvt msgcvt.o ${mam_libast} +done msgcvt generated +make msggen +make msggen.o +make msggen.c +make ${PACKAGE_ast_INCLUDE}/mc.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/mc.h +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/ccode.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done msggen.c +meta msggen.o %.c>%.o msggen.c msggen +prev msggen.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DUSAGE_LICENSE=\""[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]"\" -c msggen.c +done msggen.o generated +exec - ${CC} ${CCLDFLAGS} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ${mam_cc_L+-L${INSTALLROOT}/lib} -o msggen msggen.o ${mam_libast} +done msggen generated +make msgget +make msgget.o +make msgget.c +prev ${PACKAGE_ast_INCLUDE}/mc.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done msgget.c +meta msgget.o %.c>%.o msgget.c msgget +prev msgget.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DUSAGE_LICENSE=\""[-author?Glenn Fowler ][-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?msgcc]"\" -c msgget.c +done msgget.o generated +exec - ${CC} ${CCLDFLAGS} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ${mam_cc_L+-L${INSTALLROOT}/lib} -o msgget msgget.o ${mam_libast} +done msgget generated +make ${INSTALLROOT}/bin +exec - if silent test ! -d ${INSTALLROOT}/bin +exec - then mkdir -p ${INSTALLROOT}/bin +exec - fi +done ${INSTALLROOT}/bin generated +make ${INSTALLROOT}/bin/msgadmin +prev ${INSTALLROOT}/bin +prev msgadmin +exec - test '' = 'msgadmin' || ${STDCMP} 2>/dev/null -s msgadmin ${INSTALLROOT}/bin/msgadmin || { ${STDMV} ${INSTALLROOT}/bin/msgadmin ${INSTALLROOT}/bin/msgadmin.old 2>/dev/null || true; ${STDCP} msgadmin ${INSTALLROOT}/bin/msgadmin ;} +done ${INSTALLROOT}/bin/msgadmin generated +make ${INSTALLROOT}/bin/msgcpp +prev msgcpp +exec - test '' = 'msgcpp' || ${STDCMP} 2>/dev/null -s msgcpp ${INSTALLROOT}/bin/msgcpp || { ${STDMV} ${INSTALLROOT}/bin/msgcpp ${INSTALLROOT}/bin/msgcpp.old 2>/dev/null || true; ${STDCP} msgcpp ${INSTALLROOT}/bin/msgcpp ;} +done ${INSTALLROOT}/bin/msgcpp generated +make ${INSTALLROOT}/bin/msgcc +prev msgcc +exec - test '' = 'msgcc' || ${STDCMP} 2>/dev/null -s msgcc ${INSTALLROOT}/bin/msgcc || { ${STDMV} ${INSTALLROOT}/bin/msgcc ${INSTALLROOT}/bin/msgcc.old 2>/dev/null || true; ${STDCP} msgcc ${INSTALLROOT}/bin/msgcc ;} +done ${INSTALLROOT}/bin/msgcc generated +make ${INSTALLROOT}/bin/msgcvt +prev msgcvt +exec - test '' = 'msgcvt' || ${STDCMP} 2>/dev/null -s msgcvt ${INSTALLROOT}/bin/msgcvt || { ${STDMV} ${INSTALLROOT}/bin/msgcvt ${INSTALLROOT}/bin/msgcvt.old 2>/dev/null || true; ${STDCP} msgcvt ${INSTALLROOT}/bin/msgcvt ;} +done ${INSTALLROOT}/bin/msgcvt generated +make ${INSTALLROOT}/bin/msggen +prev msggen +exec - test '' = 'msggen' || ${STDCMP} 2>/dev/null -s msggen ${INSTALLROOT}/bin/msggen || { ${STDMV} ${INSTALLROOT}/bin/msggen ${INSTALLROOT}/bin/msggen.old 2>/dev/null || true; ${STDCP} msggen ${INSTALLROOT}/bin/msggen ;} +done ${INSTALLROOT}/bin/msggen generated +make ${INSTALLROOT}/bin/msgget +prev msgget +exec - test '' = 'msgget' || ${STDCMP} 2>/dev/null -s msgget ${INSTALLROOT}/bin/msgget || { ${STDMV} ${INSTALLROOT}/bin/msgget ${INSTALLROOT}/bin/msgget.old 2>/dev/null || true; ${STDCP} msgget ${INSTALLROOT}/bin/msgget ;} +done ${INSTALLROOT}/bin/msgget generated +done install virtual +make test +make test.msgcc +make msgcc.tst +done msgcc.tst +prev msgcc +exec - regress msgcc.tst msgcc +done test.msgcc virtual +done test dontcare virtual Index: src/cmd/ast/msgcc/msgcpp.c =================================================================== --- src/cmd/ast/msgcc/msgcpp.c (revision 0) +++ src/cmd/ast/msgcc/msgcpp.c (revision 740) @@ -0,0 +1,289 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 2000-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * C message catalog preprocessor + */ + +static const char usage[] = +"[-?\n@(#)$Id: msgcpp (AT&T Research) 2002-03-11 $\n]" +USAGE_LICENSE +"[+NAME?msgcpp - C language message catalog preprocessor]" +"[+DESCRIPTION?\bmsgcpp\b is a C language message catalog preprocessor." +" It accepts \bcpp\b(1) style options and arguments. \bmsgcpp\b" +" preprocesses an input C source file and emits keyed lines to the" +" output, usually for further processing by \bmsgcc\b(1). \bmsgcc\b" +" output is in the \bgencat\b(1) syntax. Candidate message text is" +" determined by arguments to the \bast\b \b\b and" +" \b\b functions. The \bmsgcpp\b keyed output lines are:]{" +" [+cmd \acommand\a?\acommand\a is a candidate for \b--??keys\b" +" option string generation. Triggered by" +" \bb_\b\acommand\a\b(int argc,\b in the input.]" +" [+def \aname\a \astring\a?\aname\a is a candidate variable with" +" string value \astring\a.]" +" [+str \astring\a?\astring\a should be entered into the catalog.]" +" [+var \aname\a?If \bdef\b \aname\a occurs then its \astring\a value" +" should be entered into the catalog.]" +" }" +"[+?The input source file is preprocessed with the \bpp:allpossible\b" +" option on. This enables non-C semantics; all source should first" +" be compiled error-free with a real compiler before running \bmsgcpp\b." +" The following changes are enabled for the top level files (i.e.," +" included file behavior is not affected):]{" +" [+(1)?All \b#if\b, \b#ifdef\b and \b#ifndef\b branches" +" are enabled.]" +" [+(2)?The first definition for a macro is retained, even when" +" subsequent \b#define\b statements would normally" +" redefine the macro. \b#undef\b must be used to" +" redefine a macro.]" +" [+(3)?Macro calls with an improper number of arguments are" +" silently ignored.]" +" [+(4)?\b#include\b on non-existent headers are silently" +" ignored.]" +" [+(5)?Invalid C source characters are silently ignored.]" +" }" +"[+?\b\"msgcat.h\"\b is included if it exists. This file may contain macro" +" definitions for functions that translate string arguments. If \afoo\a" +" is a function that translates its string arguments then include the" +" line \b#define \b\afoo\a\b _TRANSLATE_\b in \bmsgcat.h\b or specify" +" the option \b-D\b\afoo\a\b=_TRANSLATE_\b. If \abar\a is a function" +" that translates string arguments if the first argument is \bstderr\b" +" then use either \b#define \b\abar\a\b _STDIO_\b or" +" \b-D\b\abar\a\b=_STDIO_\b.]" +"[+?The macro \b_BLD_msgcat\b is defined to be \b1\b. As an alternative to" +" \bmsgcat.h\b, \b_TRANSLATE_\b definitions could be placed inside" +" \b#ifdef _BLD_msgcat\b ... \b#endif\b.]" + +"\n" +"\n[ input [ output ] ]\n" +"\n" + +"[+SEE ALSO?\bcc\b(1), \bcpp\b(1), \bgencat\b(1), \bmsggen\b(1)," +" \bmsgcc\b(1), \bmsgcvt\b(1)]" +; + +#include +#include + +#include "pp.h" +#include "ppkey.h" + +#define T_STDERR (T_KEYWORD+1) +#define T_STDIO (T_KEYWORD+2) +#define T_TRANSLATE (T_KEYWORD+3) + +#define OMIT "*@(\\[[-+]*\\?*\\]|\\@\\(#\\)|Copyright \\(c\\)|\\\\000|\\\\00[!0-9]|\\\\0[!0-9])*" + +static struct ppkeyword keys[] = +{ + "char", T_CHAR, + "int", T_INT, + "sfstderr", T_STDERR, + "stderr", T_STDERR, + "_STDIO_", T_STDIO, + "_TRANSLATE_", T_TRANSLATE, + 0, 0 +}; + +static int +msgppargs(char** argv, int last) +{ + for (;;) + { + switch (optget(argv, usage)) + { + case 0: + break; + case '?': + if (!last) + { + opt_info.again = 1; + return 1; + } + error(ERROR_USAGE|4, "%s", opt_info.arg); + break; + case ':': + if (!last) + { + opt_info.again = 1; + return 1; + } + error(2, "%s", opt_info.arg); + continue; + default: + if (!last) + { + opt_info.again = 1; + return 1; + } + continue; + } + break; + } + return argv[opt_info.index] != 0; +} + +int +main(int argc, char** argv) +{ + register char* s; + register int x; + register int c; + Sfio_t* tmp; + + NoP(argc); + if (s = strrchr(*argv, '/')) + s++; + else + s = *argv; + error_info.id = s; + ppop(PP_DEFAULT, PPDEFAULT); + optjoin(argv, msgppargs, ppargs, NiL); + if (strlen(s) >= 5 && *(s + 3) != 'c') + { + ppop(PP_PLUSPLUS, 1); + ppop(PP_NOHASH, 1); + ppop(PP_PROBE, "CC"); + } + ppop(PP_SPACEOUT, 0); + ppop(PP_COMPILE, keys); + ppop(PP_OPTION, "allpossible"); + ppop(PP_OPTION, "catliteral"); + ppop(PP_OPTION, "modern"); + ppop(PP_OPTION, "readonly"); + ppop(PP_DEFINE, "_BLD_msgcat=1"); + ppop(PP_DEFINE, "const="); + ppop(PP_DEFINE, "errorf=_TRANSLATE_"); + ppop(PP_DEFINE, "register="); + ppop(PP_DEFINE, "sfstderr=sfstderr"); + ppop(PP_DEFINE, "stderr=stderr"); + ppop(PP_DEFINE, "_(m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "__(m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "gettxt(i,m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "gettext(m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "dgettext(d,m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "dcgettext(d,m,c)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "ERROR_catalog(m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "ERROR_dictionary(m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "ERROR_translate(l,i,c,m)=_TRANSLATE_(m)"); + ppop(PP_DEFINE, "error(l,f,...)=_TRANSLATE_(f)"); + ppop(PP_DEFINE, "errormsg(t,l,f,...)=_TRANSLATE_(f)"); + ppop(PP_DIRECTIVE, "include \"msgcat.h\""); + ppop(PP_OPTION, "noreadonly"); + ppop(PP_INIT); + if (!(tmp = sfstropen())) + error(ERROR_SYSTEM|3, "out of space"); + x = 0; + for (;;) + { + c = pplex(); + again: + switch (c) + { + case 0: + break; + case T_TRANSLATE: + switch (c = pplex()) + { + case '(': + x = 1; + break; + case ')': + if ((c = pplex()) != '(') + { + x = 0; + goto again; + } + x = 1; + break; + default: + x = 0; + goto again; + } + continue; + case '(': + if (x > 0) + x++; + continue; + case ')': + if (x > 0) + x--; + continue; + case T_STDIO: + if ((c = pplex()) != '(' || (c = pplex()) != T_STDERR || (c = pplex()) != ',') + { + x = 0; + goto again; + } + x = 1; + continue; + case T_STRING: + if (x > 0 && !strmatch(pp.token, OMIT)) + sfprintf(sfstdout, "str \"%s\"\n", pp.token); + continue; + case T_ID: + s = pp.symbol->name; + if (x > 0) + { + if ((c = pplex()) == '+' && ppisinteger(c = pplex())) + sfprintf(sfstdout, "var %s %s\n", pp.token, s); + else + sfprintf(sfstdout, "var %s\n", s); + } + else if (s[0] == 'b' && s[1] == '_' && s[2]) + { + if ((c = pplex()) == '(' && (c = pplex()) == T_INT && (c = pplex()) == T_ID && (c = pplex()) == ',' && (c = pplex()) == T_CHAR && (c = pplex()) == '*') + sfprintf(sfstdout, "cmd %s\n", s + 2); + else + goto again; + } + else + { + if ((c = pplex()) == '[') + { + if (ppisinteger(c = pplex())) + c = pplex(); + if (c != ']') + goto again; + c = pplex(); + } + if (c == '=' && (c = pplex()) == T_STRING && !strmatch(pp.token, OMIT)) + { + sfprintf(sfstdout, "def %s \"%s\"\n", s, pp.token); + sfprintf(tmp, "#define %s \"%s\"\n", s, pp.token); + if (!(s = sfstruse(tmp))) + error(ERROR_SYSTEM|3, "out of space"); + ppinput(s, "string", 0); + } + else + goto again; + } + continue; + default: + continue; + } + break; + } + ppop(PP_DONE); + return error_info.errors != 0; +} Index: src/cmd/ast/msgcc/Makefile =================================================================== --- src/cmd/ast/msgcc/Makefile (revision 0) +++ src/cmd/ast/msgcc/Makefile (revision 740) @@ -0,0 +1,75 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SHELL=/usr/bin/ksh + +include ../../Makefile.cmd + +.KEEP_STATE: + +# Set common AST build flags (e.g., needed to support the math stuff). +include ../../../Makefile.ast + +# build rules +CPPFLAGS = \ + $(DTEXTDOM) $(DTS_ERRNO) \ + -I$(ROOT)/usr/include/ast \ + -D_PACKAGE_ast \ + '-DUSAGE_LICENSE=\ + "[-author?Glenn Fowler ]"\ + "[-copyright?Copyright (c) 2000-2007 AT&T Knowledge Ventures]"\ + "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"\ + "[--catalog?msgcc]"' + +CFLAGS += \ + $(CCVERBOSE) \ + -xstrconst + +LDLIBS += -last +msgcpp := LDLIBS += -lpp + +msgcc: msgcc.sh + rm -f msgcc ; \ + ( \ + print "#!/usr/bin/ksh93" ; \ + print "export PATH=/usr/ast/bin:/usr/xpg6/bin:/usr/xpg4/bin:/usr/bin:\$${PATH}" ; \ + print "builtin date" ; \ + cat "msgcc.sh" ; \ + ) >msgcc ; \ + chmod a+rx msgcc + +ROOTCMDDIR=$(ROOT)/usr/ast/bin + +PROG= msgcvt msggen msgget msgcpp msgcc + +all: $(PROG) + +install: all $(ROOTCMD) + +clean lint: + +include ../../Makefile.targ Index: src/cmd/ast/Makefile =================================================================== --- src/cmd/ast/Makefile (revision 0) +++ src/cmd/ast/Makefile (revision 740) @@ -0,0 +1,50 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +SHELL=/usr/bin/ksh + +include ../Makefile.cmd + +SUBDIRS= \ + msgcc + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint + +.KEEP_STATE: + +all install clean clobber lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ Index: src/cmd/ast/THIRDPARTYLICENSE =================================================================== --- src/cmd/ast/THIRDPARTYLICENSE (revision 0) +++ src/cmd/ast/THIRDPARTYLICENSE (revision 740) @@ -0,0 +1,245 @@ ++------------------------------------------------------------------------------+ +| This license covers all software that refers to the URL | +| http://www.opensource.org/licenses/cpl1.0.txt | ++------------------------------------------------------------------------------+ + +Common Public License Version 1.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF + THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + + 1. DEFINITIONS + + "Contribution" means: + + a) in the case of the initial Contributor, the initial code and + documentation distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from + and are distributed by that particular Contributor. A Contribution + 'originates' from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's + behalf. Contributions do not include additions to the Program + which: (i) are separate modules of software distributed in + conjunction with the Program under their own license agreement, and + (ii) are not derivative works of the Program. + + "Contributor" means any person or entity that distributes the Program. + + "Licensed Patents " mean patent claims licensable by a Contributor + which are necessarily infringed by the use or sale of its Contribution + alone or when combined with the Program. + + "Program" means the Contributions distributed in accordance with this + Agreement. + + "Recipient" means anyone who receives the Program under this + Agreement, including all Contributors. + + 2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare derivative works of, publicly + display, publicly perform, distribute and sublicense the + Contribution of such Contributor, if any, and such derivative + works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in source code and object code form. This patent license + shall apply to the combination of the Contribution and the Program + if, at the time the Contribution is added by the Contributor, such + addition of the Contribution causes such combination to be covered + by the Licensed Patents. The patent license shall not apply to any + other combinations which include the Contribution. No hardware per + se is licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + 3. REQUIREMENTS + + A Contributor may choose to distribute the Program in object code form + under its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and implied + warranties or conditions of merchantability and fitness for a + particular purpose; + + ii) effectively excludes on behalf of all Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement + are offered by that Contributor alone and not by any other party; + and + + iv) states that source code for the Program is available from such + Contributor, and informs licensees how to obtain it in a reasonable + manner on or through a medium customarily used for software + exchange. + + When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the + Program. + + Contributors may not remove or alter any copyright notices contained + within the Program. + + Each Contributor must identify itself as the originator of its + Contribution, if any, in a manner that reasonably allows subsequent + Recipients to identify the originator of the Contribution. + + 4. COMMERCIAL DISTRIBUTION + + Commercial distributors of software may accept certain + responsibilities with respect to end users, business partners and the + like. While this license is intended to facilitate the commercial use + of the Program, the Contributor who includes the Program in a + commercial product offering should do so in a manner which does not + create potential liability for other Contributors. Therefore, if a + Contributor includes the Program in a commercial product offering, + such Contributor ("Commercial Contributor") hereby agrees to defend + and indemnify every other Contributor ("Indemnified Contributor") + against any losses, damages and costs (collectively "Losses") arising + from claims, lawsuits and other legal actions brought by a third party + against the Indemnified Contributor to the extent caused by the acts + or omissions of such Commercial Contributor in connection with its + distribution of the Program in a commercial product offering. The + obligations in this section do not apply to any claims or Losses + relating to any actual or alleged intellectual property infringement. + In order to qualify, an Indemnified Contributor must: a) promptly + notify the Commercial Contributor in writing of such claim, and b) + allow the Commercial Contributor to control, and cooperate with the + Commercial Contributor in, the defense and any related settlement + negotiations. The Indemnified Contributor may participate in any such + claim at its own expense. + + For example, a Contributor might include the Program in a commercial + product offering, Product X. That Contributor is then a Commercial + Contributor. If that Commercial Contributor then makes performance + claims, or offers warranties related to Product X, those performance + claims and warranties are such Commercial Contributor's responsibility + alone. Under this section, the Commercial Contributor would have to + defend claims against the other Contributors related to those + performance claims and warranties, and if a court requires any other + Contributor to pay any damages as a result, the Commercial Contributor + must pay those damages. + + 5. NO WARRANTY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS + PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY + OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely + responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, compliance with applicable + laws, damage to or loss of data, programs or equipment, and + unavailability or interruption of operations. + + 6. DISCLAIMER OF LIABILITY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR + ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING + WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR + DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + 7. GENERAL + + If any provision of this Agreement is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this Agreement, and without further + action by the parties hereto, such provision shall be reformed to the + minimum extent necessary to make such provision valid and enforceable. + + If Recipient institutes patent litigation against a Contributor with + respect to a patent applicable to software (including a cross-claim or + counterclaim in a lawsuit), then any patent licenses granted by that + Contributor to such Recipient under this Agreement shall terminate as + of the date such litigation is filed. In addition, if Recipient + institutes patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Program + itself (excluding combinations of the Program with other software or + hardware) infringes such Recipient's patent(s), then such Recipient's + rights granted under Section 2(b) shall terminate as of the date such + litigation is filed. + + All Recipient's rights under this Agreement shall terminate if it + fails to comply with any of the material terms or conditions of this + Agreement and does not cure such failure in a reasonable period of + time after becoming aware of such noncompliance. If all Recipient's + rights under this Agreement terminate, Recipient agrees to cease use + and distribution of the Program as soon as reasonably practicable. + However, Recipient's obligations under this Agreement and any licenses + granted by Recipient relating to the Program shall continue and + survive. + + Everyone is permitted to copy and distribute copies of this Agreement, + but in order to avoid inconsistency the Agreement is copyrighted and + may only be modified in the following manner. The Agreement Steward + reserves the right to publish new versions (including revisions) of + this Agreement from time to time. No one other than the Agreement + Steward has the right to modify this Agreement. IBM is the initial + Agreement Steward. IBM may assign the responsibility to serve as the + Agreement Steward to a suitable separate entity. Each new version of + the Agreement will be given a distinguishing version number. The + Program (including Contributions) may always be distributed subject to + the version of the Agreement under which it was received. In addition, + after a new version of the Agreement is published, Contributor may + elect to distribute the Program (including its Contributions) under + the new version. Except as expressly stated in Sections 2(a) and 2(b) + above, Recipient receives no rights or licenses to the intellectual + property of any Contributor under this Agreement, whether expressly, + by implication, estoppel or otherwise. All rights in the Program not + expressly granted under this Agreement are reserved. + + This Agreement is governed by the laws of the State of New York and + the intellectual property laws of the United States of America. No + party to this Agreement will bring a legal action under this Agreement + more than one year after the cause of action arose. Each party waives + its rights to a jury trial in any resulting litigation. + +Copyright (c) 2004 by the Open Source Initiative +This is a copy of the license posted on 2004-10-06 at: + http://www.opensource.org/licenses/cpl Index: src/Makefile.ast =================================================================== --- src/Makefile.ast (revision 0) +++ src/Makefile.ast (revision 740) @@ -0,0 +1,67 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Override this top level flag so the compiler builds in its native +# C99 mode. This has been enabled to support the math stuff in the +# AST tools (including ksh93). +C99MODE= $(C99_ENABLE) -D_XOPEN_SOURCE=600 -D__EXTENSIONS__=1 + +# silence common AST&co. warnings... +# ... about |#pragma prototyped| ... +CERRWARN += -erroff=E_UNRECOGNIZED_PRAGMA_IGNORED + +# common CPP flags for libshell consumers (ksh etc.) +LIBSHELLCPPFLAGS = \ + -I$(ROOT)/usr/include/ast \ + -DKSHELL \ + -DSHOPT_BRACEPAT \ + -DSHOPT_CMDLIB_BLTIN=0 \ + '-DSH_CMDLIB_DIR="/usr/ast/bin"' \ + '-DSHOPT_CMDLIB_HDR="solaris_cmdlist.h"' \ + -DSHOPT_DYNAMIC \ + -DSHOPT_ESH \ + -DSHOPT_FILESCAN \ + -DSHOPT_HISTEXPAND \ + -DSHOPT_KIA \ + -DSHOPT_MULTIBYTE \ + -DSHOPT_NAMESPACE \ + -DSHOPT_OPTIMIZE \ + -DSHOPT_PFSH \ + -DSHOPT_RAWONLY \ + -DSHOPT_SUID_EXEC \ + -DSHOPT_SYSRC \ + -DSHOPT_VSH \ + -D_BLD_shell \ + -D_PACKAGE_ast \ + -DERROR_CONTEXT_T=Error_context_t \ + '-DUSAGE_LICENSE=\ + "[-author?David Korn ]"\ + "[-copyright?Copyright (c) 1982-2007 AT&T Knowledge Ventures]"\ + "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"\ + "[--catalog?libshell]"' + + Index: src/lib/libshell/Makefile.demo =================================================================== --- src/lib/libshell/Makefile.demo (revision 0) +++ src/lib/libshell/Makefile.demo (revision 740) @@ -0,0 +1,74 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +ROOTDEMODIRBASE= $(ROOT)/usr/demo/ksh + +DEMOFILES= \ + fun/dirs \ + fun/gnaw \ + fun/mandelbrotset1 \ + fun/rssread \ + fun/popd \ + fun/pushd \ + fun/termclock \ + fun/title \ + tests/shtests \ + tests/alias.sh \ + tests/append.sh \ + tests/arith.sh \ + tests/arrays.sh \ + tests/attributes.sh \ + tests/basic.sh \ + tests/bracket.sh \ + tests/builtins.sh \ + tests/case.sh \ + tests/comvar.sh \ + tests/coprocess.sh \ + tests/exit.sh \ + tests/expand.sh \ + tests/functions.sh \ + tests/glob.sh \ + tests/grep.sh \ + tests/heredoc.sh \ + tests/io.sh \ + tests/nameref.sh \ + tests/options.sh \ + tests/path.sh \ + tests/quoting.sh \ + tests/quoting2.sh \ + tests/return.sh \ + tests/select.sh \ + tests/substring.sh \ + tests/sun_solaris_getconf.sh \ + tests/tilde.sh \ + tests/variables.sh + +ROOTDEMODIRS= $(ROOTDEMODIRBASE) .WAIT \ + $(ROOTDEMODIRBASE)/fun \ + $(ROOTDEMODIRBASE)/tests + +install: $(ROOTDEMODIRS) .WAIT $(ROOTDEMOFILES) Index: src/lib/libshell/THIRDPARTYLICENSE.descrip =================================================================== --- src/lib/libshell/THIRDPARTYLICENSE.descrip (revision 0) +++ src/lib/libshell/THIRDPARTYLICENSE.descrip (revision 740) @@ -0,0 +1 @@ +PORTIONS OF AT&T KSH93 SOFTWARE (LIBSHELL) Index: src/lib/libshell/sparcv9/include/ast/nval.h =================================================================== --- src/lib/libshell/sparcv9/include/ast/nval.h (revision 0) +++ src/lib/libshell/sparcv9/include/ast/nval.h (revision 740) @@ -0,0 +1,317 @@ + +/* : : generated by proto : : */ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ + +#ifndef NV_DEFAULT +#if !defined(__PROTO__) +#include +#endif +#if !defined(__LINKAGE__) +#define __LINKAGE__ /* 2004-08-11 transition */ +#endif + +/* + * David Korn + * AT&T Labs + * + * Interface definitions of structures for name-value pairs + * These structures are used for named variables, functions and aliases + * + */ + + +#include +#include + +/* for compatibility with old hash library */ +#define Hashtab_t Dt_t +#define HASH_BUCKET 1 +#define HASH_NOSCOPE 2 +#define HASH_SCOPE 4 +#define hashscope(x) dtvnext(x) + +typedef struct Namval Namval_t; +typedef struct Namfun Namfun_t; +typedef struct Namdisc Namdisc_t; +typedef struct Nambfun Nambfun_t; +typedef struct Namarray Namarr_t; +typedef struct Nambltin Nambltin_t; +typedef struct Namtype Namtype_t; + +/* + * This defines the template for nodes that have their own assignment + * and or lookup functions + */ +struct Namdisc +{ + size_t dsize; + void (*putval) __PROTO__((Namval_t*, const char*, int, Namfun_t*)); + char *(*getval) __PROTO__((Namval_t*, Namfun_t*)); + Sfdouble_t (*getnum) __PROTO__((Namval_t*, Namfun_t*)); + char *(*setdisc) __PROTO__((Namval_t*, const char*, Namval_t*, Namfun_t*)); + Namval_t *(*createf) __PROTO__((Namval_t*, const char*, int, Namfun_t*)); + Namfun_t *(*clonef) __PROTO__((Namval_t*, Namval_t*, int, Namfun_t*)); + char *(*namef) __PROTO__((Namval_t*, Namfun_t*)); + Namval_t *(*nextf) __PROTO__((Namval_t*, Dt_t*, Namfun_t*)); + Namval_t *(*typef) __PROTO__((Namval_t*, Namfun_t*)); + int (*readf) __PROTO__((Namval_t*, Sfio_t*, int, Namfun_t*)); +}; + +struct Namfun +{ + const Namdisc_t *disc; + char nofree; + char funs; + unsigned short dsize; + Namfun_t *next; + char *last; + Namval_t *type; +}; + +struct Nambfun +{ + Namfun_t fun; + int num; + const char **bnames; + Namval_t *bltins[1]; +}; + +/* This is an array template header */ +struct Namarray +{ + Namfun_t hdr; + long nelem; /* number of elements */ + __V_ *(*fun) __PROTO__((Namval_t*,const char*,int)); /* associative arrays */ + Namval_t *parent; /* for multi-dimensional */ +}; + +/* Passed as third argument to a builtin when NV_BLTINOPT is set on node */ +struct Nambltin +{ + __V_ *shp; + Namval_t *np; + __V_ *ptr; + __V_ *data; + int flags; +}; + +struct Namtype +{ + __V_ *shp; + Namval_t *np; + const char *optstring; + __V_ *optinfof; +}; + +/* attributes of name-value node attribute flags */ + +#define NV_DEFAULT 0 +/* This defines the attributes for an attributed name-value pair node */ +struct Namval +{ + Dtlink_t nvlink; /* space for cdt links */ + char *nvname; /* pointer to name of the node */ + unsigned short nvflag; /* attributes */ + unsigned short nvsize; /* size or base */ +#ifdef _NV_PRIVATE + _NV_PRIVATE +#else + Namfun_t *nvfun; + char *nvalue; + char *nvprivate; +#endif /* _NV_PRIVATE */ +}; + +#define NV_CLASS ".sh.type" +#define NV_MINSZ (sizeof(struct Namval)-sizeof(Dtlink_t)-sizeof(char*)) +#define nv_namptr(p,n) ((Namval_t*)((char*)(p)+(n)*NV_MINSZ-sizeof(Dtlink_t))) + +/* The following attributes are for internal use */ +#define NV_NOFREE 0x200 /* don't free the space when releasing value */ +#define NV_ARRAY 0x400 /* node is an array */ +#define NV_REF 0x4000 /* reference bit */ +#define NV_TABLE 0x800 /* node is a dictionary table */ +#define NV_IMPORT 0x1000 /* value imported from environment */ +#define NV_MINIMAL NV_IMPORT /* node does not contain all fields */ + +#define NV_INTEGER 0x2 /* integer attribute */ +/* The following attributes are valid only when NV_INTEGER is off */ +#define NV_LTOU 0x4 /* convert to uppercase */ +#define NV_UTOL 0x8 /* convert to lowercase */ +#define NV_ZFILL 0x10 /* right justify and fill with leading zeros */ +#define NV_RJUST 0x20 /* right justify and blank fill */ +#define NV_LJUST 0x40 /* left justify and blank fill */ +#define NV_BINARY 0x100 /* fixed size data buffer */ +#define NV_RAW NV_LJUST /* used only with NV_BINARY */ +#define NV_HOST (NV_RJUST|NV_LJUST) /* map to host filename */ + +/* The following attributes do not effect the value */ +#define NV_RDONLY 0x1 /* readonly bit */ +#define NV_EXPORT 0x2000 /* export bit */ +#define NV_TAGGED 0x8000 /* user define tag bit */ + +/* The following are used with NV_INTEGER */ +#define NV_SHORT (NV_RJUST) /* when integers are not long */ +#define NV_LONG (NV_UTOL) /* for long long and long double */ +#define NV_UNSIGN (NV_LTOU) /* for unsigned quantities */ +#define NV_DOUBLE (NV_ZFILL) /* for floating point */ +#define NV_EXPNOTE (NV_LJUST) /* for scientific notation */ + +/* options for nv_open */ + +#define NV_APPEND 0x10000 /* append value */ +#define NV_MOVE 0x20000 /* for use with nv_clone */ +#define NV_ADD 8 + /* add node if not found */ +#define NV_ASSIGN NV_NOFREE /* assignment is possible */ +#define NV_NOASSIGN 0 /* backward compatibility */ +#define NV_NOARRAY 0x200000 /* array name not possible */ +#define NV_IARRAY 0x400000 /* for indexed array */ +#define NV_NOREF NV_REF /* don't follow reference */ +#define NV_IDENT 0x80 /* name must be identifier */ +#define NV_VARNAME 0x20000 /* name must be ?(.)id*(.id) */ +#define NV_NOADD 0x40000 /* do not add node */ +#define NV_NOSCOPE 0x80000 /* look only in current scope */ +#define NV_NOFAIL 0x100000 /* return 0 on failure, no msg */ +#define NV_NODISC NV_IDENT /* ignore disciplines */ + +#define NV_FUNCT NV_IDENT /* option for nv_create */ +#define NV_BLTINOPT NV_ZFILL /* save state for optimization*/ + +#define NV_PUBLIC (~(NV_NOSCOPE|NV_ASSIGN|NV_IDENT|NV_VARNAME|NV_NOADD)) + +/* numeric types */ +#define NV_INT16 (NV_SHORT|NV_INTEGER) +#define NV_UINT16 (NV_UNSIGN|NV_SHORT|NV_INTEGER) +#define NV_INT32 (NV_INTEGER) +#define NV_UNT32 (NV_UNSIGN|NV_INTEGER) +#define NV_INT64 (NV_LONG|NV_INTEGER) +#define NV_UINT64 (NV_UNSIGN|NV_LONG|NV_INTEGER) +#define NV_FLOAT (NV_SHORT|NV_DOUBLE|NV_INTEGER) +#define NV_LDOUBLE (NV_LONG|NV_DOUBLE|NV_INTEGER) + +/* name-value pair macros */ +#define nv_isattr(np,f) ((np)->nvflag & (f)) +#define nv_onattr(n,f) ((n)->nvflag |= (f)) +#define nv_offattr(n,f) ((n)->nvflag &= ~(f)) +#define nv_isarray(np) (nv_isattr((np),NV_ARRAY)) + +/* The following are operations for associative arrays */ +#define NV_AINIT 1 /* initialize */ +#define NV_AFREE 2 /* free array */ +#define NV_ANEXT 3 /* advance to next subscript */ +#define NV_ANAME 4 /* return subscript name */ +#define NV_ADELETE 5 /* delete current subscript */ +#define NV_AADD 6 /* add subscript if not found */ +#define NV_ACURRENT 7 /* return current subscript Namval_t* */ + +/* The following are for nv_disc */ +#define NV_FIRST 1 +#define NV_LAST 2 +#define NV_POP 3 +#define NV_CLONE 4 + +/* The following are operations for nv_putsub() */ +#define ARRAY_BITS 24 +#define ARRAY_ADD (1L< * +* * +***********************************************************************/ + +#ifndef HIST_VERSION +#if !defined(__PROTO__) +#include +#endif +#if !defined(__LINKAGE__) +#define __LINKAGE__ /* 2004-08-11 transition */ +#endif + +/* + * Interface for history mechanism + * written by David Korn + * + */ + +#include + +#define HIST_CHAR '!' +#define HIST_VERSION 1 /* history file format version no. */ + +typedef struct +{ + Sfdisc_t histdisc; /* discipline for history */ + Sfio_t *histfp; /* history file stream pointer */ + char *histname; /* name of history file */ + int32_t histind; /* current command number index */ + int histsize; /* number of accessible history lines */ +#ifdef _HIST_PRIVATE + _HIST_PRIVATE +#endif /* _HIST_PRIVATE */ +} History_t; + +typedef struct +{ + int hist_command; + int hist_line; + int hist_char; +} Histloc_t; + +/* the following are readonly */ +extern __MANGLE__ const char hist_fname[]; + +extern __MANGLE__ int _Hist; +#define hist_min(hp) ((_Hist=((int)((hp)->histind-(hp)->histsize)))>=0?_Hist:0) +#define hist_max(hp) ((int)((hp)->histind)) +/* these are the history interface routines */ +extern __MANGLE__ int sh_histinit __PROTO__((void)); +extern __MANGLE__ void hist_cancel __PROTO__((History_t*)); +extern __MANGLE__ void hist_close __PROTO__((History_t*)); +extern __MANGLE__ int hist_copy __PROTO__((char*, int, int, int)); +extern __MANGLE__ void hist_eof __PROTO__((History_t*)); +extern __MANGLE__ Histloc_t hist_find __PROTO__((History_t*,char*,int, int, int)); +extern __MANGLE__ void hist_flush __PROTO__((History_t*)); +extern __MANGLE__ void hist_list __PROTO__((History_t*,Sfio_t*, off_t, int, char*)); +extern __MANGLE__ int hist_match __PROTO__((History_t*,off_t, char*, int*)); +extern __MANGLE__ off_t hist_tell __PROTO__((History_t*,int)); +extern __MANGLE__ off_t hist_seek __PROTO__((History_t*,int)); +extern __MANGLE__ char *hist_word __PROTO__((char*, int, int)); +#if SHOPT_ESH + extern __MANGLE__ Histloc_t hist_locate __PROTO__((History_t*,int, int, int)); +#endif /* SHOPT_ESH */ + +#endif /* HIST_VERSION */ Index: src/lib/libshell/sparcv9/include/ast/shell.h =================================================================== --- src/lib/libshell/sparcv9/include/ast/shell.h (revision 0) +++ src/lib/libshell/sparcv9/include/ast/shell.h (revision 740) @@ -0,0 +1,256 @@ + +/* : : generated by proto : : */ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ + +#ifndef SH_INTERACTIVE +#if !defined(__PROTO__) +#include +#endif +#if !defined(__LINKAGE__) +#define __LINKAGE__ /* 2004-08-11 transition */ +#endif + +/* + * David Korn + * AT&T Labs + * + * Interface definitions for shell command language + * + */ + +#include +#include +#ifdef _SH_PRIVATE +# include "name.h" +#else +# include +#endif /* _SH_PRIVATE */ + +#define SH_VERSION 20060510 + +#undef NOT_USED +#define NOT_USED(x) (&x,1) + +/* options */ +typedef struct +{ + unsigned long v[4]; +} +Shopt_t; + +typedef void (*Shinit_f) __PROTO__((int)); +typedef int (*Shbltin_f) __PROTO__((int, char*[], __V_*)); +typedef int (*Shwait_f) __PROTO__((int, long, int)); + +union Shnode_u; +typedef union Shnode_u Shnode_t; + +#define SH_CFLAG 0 +#define SH_HISTORY 1 /* used also as a state */ +#define SH_ERREXIT 2 /* used also as a state */ +#define SH_VERBOSE 3 /* used also as a state */ +#define SH_MONITOR 4 /* used also as a state */ +#define SH_INTERACTIVE 5 /* used also as a state */ +#define SH_RESTRICTED 6 +#define SH_XTRACE 7 +#define SH_KEYWORD 8 +#define SH_NOUNSET 9 +#define SH_NOGLOB 10 +#define SH_ALLEXPORT 11 +#define SH_PFSH 12 +#define SH_IGNOREEOF 13 +#define SH_NOCLOBBER 14 +#define SH_MARKDIRS 15 +#define SH_BGNICE 16 +#define SH_VI 17 +#define SH_VIRAW 18 +#define SH_TFLAG 19 +#define SH_TRACKALL 20 +#define SH_SFLAG 21 +#define SH_NOEXEC 22 +#define SH_GMACS 24 +#define SH_EMACS 25 +#define SH_PRIVILEGED 26 +#define SH_SUBSHARE 27 /* subshell shares state with parent */ +#define SH_NOLOG 28 +#define SH_NOTIFY 29 +#define SH_DICTIONARY 30 +#define SH_PIPEFAIL 32 +#define SH_GLOBSTARS 33 +#define SH_XARGS 34 +#define SH_RC 35 +#define SH_SHOWME 36 + +/* + * passed as flags to builtins in Nambltin_t struct when BLT_OPTIM is on + */ +#define SH_BEGIN_OPTIM 0x1 +#define SH_END_OPTIM 0x2 + +/* The following type is used for error messages */ + +/* error messages */ +extern __MANGLE__ const char e_defpath[]; +extern __MANGLE__ const char e_found[]; +extern __MANGLE__ const char e_nospace[]; +extern __MANGLE__ const char e_format[]; +extern __MANGLE__ const char e_number[]; +extern __MANGLE__ const char e_restricted[]; +extern __MANGLE__ const char e_recursive[]; +extern __MANGLE__ char e_version[]; + +typedef struct sh_scope +{ + struct sh_scope *par_scope; + int argc; + char **argv; + char *cmdname; + char *filename; + int lineno; + Dt_t *var_tree; + struct sh_scope *self; +} Shscope_t; + +/* + * Saves the state of the shell + */ + +typedef struct sh_static +{ + Shopt_t options; /* set -o options */ + Dt_t *var_tree; /* for shell variables */ + Dt_t *fun_tree; /* for shell functions */ + Dt_t *alias_tree; /* for alias names */ + Dt_t *bltin_tree; /* for builtin commands */ + Shscope_t *topscope; /* pointer to top-level scope */ + int inlineno; /* line number of current input file */ + int exitval; /* most recent exit value */ + unsigned char trapnote; /* set when trap/signal is pending */ + char subshell; /* set for virtual subshell */ +#ifdef _SH_PRIVATE + _SH_PRIVATE +#endif /* _SH_PRIVATE */ +} Shell_t; + +/* flags for sh_parse */ +#define SH_NL 1 /* Treat new-lines as ; */ +#define SH_EOF 2 /* EOF causes syntax error */ + +/* symbolic values for sh_iogetiop */ +#define SH_IOCOPROCESS (-2) +#define SH_IOHISTFILE (-3) + +/* symbolic value for sh_fdnotify */ +#define SH_FDCLOSE (-1) + +#if defined(__EXPORT__) && defined(_DLL) +# ifdef _BLD_shell +#undef __MANGLE__ +#define __MANGLE__ __LINKAGE__ __EXPORT__ +# endif /* _BLD_shell */ +#endif /* _DLL */ + +extern __MANGLE__ Dt_t *sh_bltin_tree __PROTO__((void)); +extern __MANGLE__ void sh_subfork __PROTO__((void)); +extern __MANGLE__ Shell_t *sh_init __PROTO__((int,char*[],Shinit_f)); +extern __MANGLE__ int sh_reinit __PROTO__((char*[])); +extern __MANGLE__ int sh_eval __PROTO__((Sfio_t*,int)); +extern __MANGLE__ void sh_delay __PROTO__((double)); +extern __MANGLE__ __V_ *sh_parse __PROTO__((Shell_t*, Sfio_t*,int)); +extern __MANGLE__ int sh_trap __PROTO__((const char*,int)); +extern __MANGLE__ int sh_fun __PROTO__((Namval_t*,Namval_t*, char*[])); +extern __MANGLE__ int sh_funscope __PROTO__((int,char*[],int(*)(__V_*),__V_*,int)); +extern __MANGLE__ Sfio_t *sh_iogetiop __PROTO__((int,int)); +extern __MANGLE__ int sh_main __PROTO__((int, char*[], void(*)(int))); +extern __MANGLE__ void sh_menu __PROTO__((Sfio_t*, int, char*[])); +extern __MANGLE__ Namval_t *sh_addbuiltin __PROTO__((const char*, int(*)(int, char*[],__V_*), __V_*)); +extern __MANGLE__ char *sh_fmtq __PROTO__((const char*)); +extern __MANGLE__ char *sh_fmtqf __PROTO__((const char*, int, int)); +extern __MANGLE__ Sfdouble_t sh_strnum __PROTO__((const char*, char**, int)); +extern __MANGLE__ int sh_access __PROTO__((const char*,int)); +extern __MANGLE__ int sh_close __PROTO__((int)); +extern __MANGLE__ int sh_dup __PROTO__((int)); +extern __MANGLE__ void sh_exit __PROTO__((int)); +extern __MANGLE__ int sh_fcntl __PROTO__((int, int, ...)); +extern __MANGLE__ Sfio_t *sh_fd2sfio __PROTO__((int)); +extern __MANGLE__ int (*sh_fdnotify __PROTO__((int(*)(int,int)))) __PROTO__((int,int)); +extern __MANGLE__ Shell_t *sh_getinterp __PROTO__((void)); +extern __MANGLE__ int sh_open __PROTO__((const char*, int, ...)); +extern __MANGLE__ int sh_openmax __PROTO__((void)); +extern __MANGLE__ Sfio_t *sh_pathopen __PROTO__((const char*)); +extern __MANGLE__ ssize_t sh_read __PROTO__((int, __V_*, size_t)); +extern __MANGLE__ ssize_t sh_write __PROTO__((int, const __V_*, size_t)); +extern __MANGLE__ off_t sh_seek __PROTO__((int, off_t, int)); +extern __MANGLE__ int sh_pipe __PROTO__((int[])); +extern __MANGLE__ mode_t sh_umask __PROTO__((mode_t)); +extern __MANGLE__ __V_ *sh_waitnotify __PROTO__((Shwait_f)); +extern __MANGLE__ Shscope_t *sh_getscope __PROTO__((int,int)); +extern __MANGLE__ Shscope_t *sh_setscope __PROTO__((Shscope_t*)); +extern __MANGLE__ void sh_sigcheck __PROTO__((void)); +extern __MANGLE__ unsigned long sh_isoption __PROTO__((int)); +extern __MANGLE__ unsigned long sh_onoption __PROTO__((int)); +extern __MANGLE__ unsigned long sh_offoption __PROTO__((int)); +extern __MANGLE__ int sh_waitsafe __PROTO__((void)); +extern __MANGLE__ int sh_exec __PROTO__((const Shnode_t*,int)); + +#if SHOPT_DYNAMIC + extern __MANGLE__ __V_ **sh_getliblist __PROTO__((void)); +#endif /* SHOPT_DYNAMIC */ + +/* + * direct access to sh is obsolete, use sh_getinterp() instead + */ +#if !defined(_SH_PRIVATE) && defined(__IMPORT__) && !defined(_BLD_shell) + extern __MANGLE__ __IMPORT__ Shell_t sh; +#else + extern __MANGLE__ Shell_t sh; +#endif + +#ifdef _DLL +#undef __MANGLE__ +#define __MANGLE__ __LINKAGE__ +#endif /* _DLL */ + +#ifndef _SH_PRIVATE +# define access(a,b) sh_access(a,b) +# define close(a) sh_close(a) +# define exit(a) sh_exit(a) +# define fcntl(a,b,c) sh_fcntl(a,b,c) +# define pipe(a) sh_pipe(a) +# define read(a,b,c) sh_read(a,b,c) +# define write(a,b,c) sh_write(a,b,c) +# define umask(a) sh_umask(a) +# define dup sh_dup +# if _lib_lseek64 +# define open64 sh_open +# define lseek64 sh_seek +# else +# define open sh_open +# define lseek sh_seek +# endif +#endif /* !_SH_PRIVATE */ + +#define SH_SIGSET 4 +#define SH_EXITSIG 0400 /* signal exit bit */ +#define SH_EXITMASK (SH_EXITSIG-1) /* normal exit status bits */ +#define SH_RUNPROG -1022 /* needs to be negative and < 256 */ + +#endif /* SH_INTERACTIVE */ Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/locale =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/locale (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/locale (revision 740) @@ -0,0 +1,38 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/locale by iffe version 2007-04-04 : : */ +#ifndef _def_locale_ksh93 +#define _def_locale_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _hdr_locale 1 /* #include ok */ +#define _hdr_wchar 1 /* #include ok */ +#define _lib_localeconv 1 /* localeconv() in default lib(s) */ +#define _lib_wctype 1 /* wctype() in default lib(s) */ +#define _lib_iswctype 1 /* iswctype() in default lib(s) */ +#define _lib_iswblank 1 /* iswblank() in default lib(s) */ +#if _PACKAGE_ast +# undef _hdr_locale +# define _hdr_locale 1 +#else +# ifdef _hdr_locale +# include +# ifndef LC_MESSAGES +# define LC_MESSAGES LC_ALL +# endif /* LC_MESSAGES */ +# endif /* _hdr_locale */ +#endif /* _PACKAGE_ast */ +#ifdef _hdr_locale +# ifdef _lib_localeconv + static struct lconv *lp; +# define GETDECIMAL(x) (((lp=localeconv()) && lp->decimal_point && *lp->decimal_point) ? *lp->decimal_point : '.' ) +# else +# define GETDECIMAL(x) ('.') +# endif /* _lib_localeconv */ +#else +# define GETDECIMAL(x) ('.') +#endif /* _hdr_locale */ + +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/execargs =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/execargs (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/execargs (revision 740) @@ -0,0 +1,10 @@ +/* : : generated by iffe version 2007-04-04 : : */ +#ifndef _def_execargs_ksh93 +#define _def_execargs_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/sigfeatures =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/sigfeatures (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/sigfeatures (revision 740) @@ -0,0 +1,54 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/sigfeatures by iffe version 2007-04-04 : : */ +#ifndef _def_sigfeatures_ksh93 +#define _def_sigfeatures_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _lib_sigrelse 1 /* sigrelse() in default lib(s) */ +#define _lib_sigprocmask 1 /* sigprocmask() in default lib(s) */ +#define _hdr_time 1 /* #include ok */ +#define _sys_time 1 /* #include ok */ +#define _sys_times 1 /* #include ok */ +#define _hdr_stddef 1 /* #include ok */ +#define _hdr_stdlib 1 /* #include ok */ +#define _hdr_ast 1 /* #include ok */ +#define _hdr_signal 1 /* #include ok */ +#define _typ_sigset_t 1 /* sigset_t is a type */ +#ifndef _mem_sigvec_sv_mask +# undef _lib_sigvec +#endif +#ifdef _lib_sigprocmask +# define sh_sigaction(s,action) do { sigset_t ss;\ + sigemptyset(&ss);\ + sigaddset(&ss,(s));\ + sigprocmask(action,&ss,0); \ + }while(0) +# define sigrelease(s) sh_sigaction(s,SIG_UNBLOCK) +# define sigblock(s) sh_sigaction(s,SIG_BLOCK) +# define sig_begin() sh_sigaction(0,SIG_SETMASK) +#else +# ifndef _lib_sigblock +# define sigblock(s) +# endif +# ifdef _lib_sigsetmask +# define sigrelease(s) sigsetmask(0) +# define sig_begin() sigsetmask(0) +# else +# ifdef _lib_sigrelse +# define sigrelease sigrelse +# define sig_begin() +# else +# define sig_begin() (0) +# define sigrelease(s) (0) +# endif /* _lib_sigrelse */ +# endif /* _lib_sigsetmask */ +#endif /* _lib_sigprocmask */ + +#undef _SIGRTMIN +#define _SIGRTMIN 41 +#undef _SIGRTMAX +#define _SIGRTMAX 48 +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/setjmp =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/setjmp (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/setjmp (revision 740) @@ -0,0 +1,35 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/setjmp by iffe version 2007-04-04 : : */ +#ifndef _def_setjmp_ksh93 +#define _def_setjmp_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _lib_sigsetjmp 1 /* sigsetjmp() in default lib(s) */ +#define _lib__setjmp 1 /* _setjmp() in default lib(s) */ +#define _lib__longjmp 1 /* _longjmp() in default lib(s) */ +#define _hdr_time 1 /* #include ok */ +#define _sys_time 1 /* #include ok */ +#define _sys_times 1 /* #include ok */ +#define _hdr_stddef 1 /* #include ok */ +#define _hdr_stdlib 1 /* #include ok */ +#define _hdr_setjmp 1 /* #include ok */ +#define _typ_sigjmp_buf 1 /* sigjmp_buf is a type */ +#undef sigsetjmp +#undef siglongjmp +#undef sigjmp_buf +#define sigjmp_buf jmp_buf +#ifdef _lib__setjmp +# define sigsetjmp(a,b) _setjmp(a) +#else +# define sigsetjmp(a,b) setjmp(a) +#endif /* _lib__setjmp */ +#ifdef _lib__longjmp +# define siglongjmp(a,b) _longjmp(a,b) +#else +# define siglongjmp(a,b) longjmp(a,b) +#endif /* _lib__longjmp */ + +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/rlimits =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/rlimits (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/rlimits (revision 740) @@ -0,0 +1,19 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/rlimits by iffe version 2007-04-04 : : */ +#ifndef _def_rlimits_ksh93 +#define _def_rlimits_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _sys_resource 1 /* #include ok */ +#define _lib_getrlimit 1 /* getrlimit() in default lib(s) */ +#define _lib_ulimit 1 /* ulimit() in default lib(s) */ +#define _hdr_time 1 /* #include ok */ +#define _sys_time 1 /* #include ok */ +#define _sys_times 1 /* #include ok */ +#define _hdr_stddef 1 /* #include ok */ +#define _hdr_stdlib 1 /* #include ok */ +#define _typ_rlim_t 1 /* rlim_t is a type */ +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/ttys =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/ttys (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/ttys (revision 740) @@ -0,0 +1,19 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/ttys by iffe version 2007-04-04 : : */ +#ifndef _def_ttys_ksh93 +#define _def_ttys_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _hdr_termios 1 /* #include ok */ +#define _hdr_termio 1 /* #include ok */ +#define _hdr_sgtty 1 /* #include ok */ +#define _sys_termios 1 /* #include ok */ +#define _sys_termio 1 /* #include ok */ +#define _sys_ioctl 1 /* #include ok */ +#define _sys_filio 1 /* #include ok */ +#define _lib_tcgetattr 1 /* tcgetattr() in default lib(s) */ +#define _lib_tcgetpgrp 1 /* tcgetpgrp() in default lib(s) */ +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/dynamic =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/dynamic (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/dynamic (revision 740) @@ -0,0 +1,23 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/dynamic by iffe version 2007-04-04 : : */ +#ifndef _def_dynamic_ksh93 +#define _def_dynamic_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _hdr_dlfcn 1 /* #include ok */ +#define _sys_dl 1 /* #include ok */ +#define _hdr_dlldefs 1 /* #include ok */ +#define _lib_dlopen 1 /* dlopen() in default lib(s) */ +#define _lib_dllfind 1 /* dllfind() in default lib(s) */ +#if !defined(SHOPT_FS_3D) && ( _lib_dllfind || _lib_dlopen || _lib_shl_load || _lib_loadbind ) +# define SHOPT_FS_3D 1 +#endif /* !SHOPT_FS_3D */ +#if SHOPT_FS_3D +# undef mount +# include +#endif /* SHOPT_FS_3D */ + +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/cmds =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/cmds (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/cmds (revision 740) @@ -0,0 +1,36 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/cmds by iffe version 2007-04-04 : : */ +#ifndef _def_cmds_ksh93 +#define _def_cmds_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _cmd_newgrp 1 /* newgrp in ?(/usr)/(bin|etc|ucb) */ +#define _bin_newgrp 1 /* /bin/newgrp found */ +#define _usr_bin_newgrp 1 /* /usr/bin/newgrp found */ +#define _cmd_test 1 /* test in ?(/usr)/(bin|etc|ucb) */ +#define _bin_test 1 /* /bin/test found */ +#define _usr_bin_test 1 /* /usr/bin/test found */ +#define _usr_ucb_test 1 /* /usr/ucb/test found */ +#define _cmd_id 1 /* id in ?(/usr)/(bin|etc|ucb) */ +#define _bin_id 1 /* /bin/id found */ +#define _usr_bin_id 1 /* /usr/bin/id found */ +#define _cmd_wc 1 /* wc in ?(/usr)/(bin|etc|ucb) */ +#define _bin_wc 1 /* /bin/wc found */ +#define _usr_bin_wc 1 /* /usr/bin/wc found */ +#define _usr_ucb_wc 1 /* /usr/ucb/wc found */ +#define _cmd_cut 1 /* cut in ?(/usr)/(bin|etc|ucb) */ +#define _bin_cut 1 /* /bin/cut found */ +#define _usr_bin_cut 1 /* /usr/bin/cut found */ +#define _cmd_logname 1 /* logname in ?(/usr)/(bin|etc|ucb) */ +#define _bin_logname 1 /* /bin/logname found */ +#define _usr_bin_logname 1 /* /usr/bin/logname found */ +#define _cmd_pfexec 1 /* pfexec in ?(/usr)/(bin|etc|ucb) */ +#define _bin_pfexec 1 /* /bin/pfexec found */ +#define _usr_bin_pfexec 1 /* /usr/bin/pfexec found */ +#define _cmd_tput 1 /* tput in ?(/usr)/(bin|etc|ucb) */ +#define _bin_tput 1 /* /bin/tput found */ +#define _usr_bin_tput 1 /* /usr/bin/tput found */ +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/poll =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/poll (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/poll (revision 740) @@ -0,0 +1,119 @@ + +/* : : generated by proto : : */ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/poll by iffe version 2007-04-04 : : */ +#ifndef _def_poll_ksh93 +#if !defined(__PROTO__) +# if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus) +# if defined(__cplusplus) +# define __LINKAGE__ "C" +# else +# define __LINKAGE__ +# endif +# define __STDARG__ +# define __PROTO__(x) x +# define __OTORP__(x) +# define __PARAM__(n,o) n +# if !defined(__STDC__) && !defined(__cplusplus) +# if !defined(c_plusplus) +# define const +# endif +# define signed +# define void int +# define volatile +# define __V_ char +# else +# define __V_ void +# endif +# else +# define __PROTO__(x) () +# define __OTORP__(x) x +# define __PARAM__(n,o) o +# define __LINKAGE__ +# define __V_ char +# define const +# define signed +# define void int +# define volatile +# endif +# define __MANGLE__ __LINKAGE__ +# if defined(__cplusplus) || defined(c_plusplus) +# define __VARARG__ ... +# else +# define __VARARG__ +# endif +# if defined(__STDARG__) +# define __VA_START__(p,a) va_start(p,a) +# else +# define __VA_START__(p,a) va_start(p) +# endif +# if !defined(__INLINE__) +# if defined(__cplusplus) +# define __INLINE__ extern __MANGLE__ inline +# else +# if defined(_WIN32) && !defined(__GNUC__) +# define __INLINE__ __inline +# endif +# endif +# endif +#endif +#if !defined(__LINKAGE__) +#define __LINKAGE__ /* 2004-08-11 transition */ +#endif + +#define _def_poll_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _hdr_poll 1 /* #include ok */ +#define _hdr_netinet_in 1 /* #include ok */ +#define _sys_poll 1 /* #include ok */ +#define _sys_socket 1 /* #include ok */ +#define _lib_select 1 /* select() in default lib(s) */ +#define _lib_poll 1 /* poll() in default lib(s) */ +#define _lib_socket 1 /* socket() in default lib(s) */ +#define _lib_htons 1 /* htons() in default lib(s) */ +#define _lib_htonl 1 /* htonl() in default lib(s) */ +#define _hdr_netdb 1 /* #include ok */ +#define _lib_getaddrinfo 1 /* getaddrinfo() in default lib(s) */ +#define _hdr_time 1 /* #include ok */ +#define _sys_time 1 /* #include ok */ +#define _sys_times 1 /* #include ok */ +#define _hdr_stddef 1 /* #include ok */ +#define _hdr_stdlib 1 /* #include ok */ +#define _sys_select 1 /* #include ok */ +#define _typ_fd_set 1 /* fd_set is a type */ +#define _socketpair_devfd 1 /* /dev/fd/N handles socketpair() */ + +#ifdef _lib_poll +# define poll _SYS_poll +#else +# undef _hdr_poll +# undef _sys_poll +#endif /* _lib_poll */ +#ifdef _hdr_poll +# include +#else +# ifdef _sys_poll +# include +# endif /* _sys_poll */ +#endif /* _hdr_poll */ +#ifdef _lib_poll +# undef poll + extern __MANGLE__ int poll __PROTO__((struct pollfd*,unsigned long,int)); +#endif /* _lib_poll */ +#ifdef _lib_select +# ifndef FD_ZERO +# define FD_ZERO(x) (*(x)=0) +# endif /* FD_ZERO */ +# ifndef FD_SET +# define FD_SET(n,x) (*(x)|=(1L<<(n))) +# endif /* FD_SET */ +# ifndef _typ_fd_set + typedef long fd_set; +# endif /*_typ_fd_set */ +#endif /* _lib_select */ + +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/externs =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/externs (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/externs (revision 740) @@ -0,0 +1,81 @@ + +/* : : generated by proto : : */ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/externs by iffe version 2007-04-04 : : */ + +#ifndef _def_externs_ksh93 +#if !defined(__PROTO__) +# if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus) +# if defined(__cplusplus) +# define __LINKAGE__ "C" +# else +# define __LINKAGE__ +# endif +# define __STDARG__ +# define __PROTO__(x) x +# define __OTORP__(x) +# define __PARAM__(n,o) n +# if !defined(__STDC__) && !defined(__cplusplus) +# if !defined(c_plusplus) +# define const +# endif +# define signed +# define void int +# define volatile +# define __V_ char +# else +# define __V_ void +# endif +# else +# define __PROTO__(x) () +# define __OTORP__(x) x +# define __PARAM__(n,o) o +# define __LINKAGE__ +# define __V_ char +# define const +# define signed +# define void int +# define volatile +# endif +# define __MANGLE__ __LINKAGE__ +# if defined(__cplusplus) || defined(c_plusplus) +# define __VARARG__ ... +# else +# define __VARARG__ +# endif +# if defined(__STDARG__) +# define __VA_START__(p,a) va_start(p,a) +# else +# define __VA_START__(p,a) va_start(p) +# endif +# if !defined(__INLINE__) +# if defined(__cplusplus) +# define __INLINE__ extern __MANGLE__ inline +# else +# if defined(_WIN32) && !defined(__GNUC__) +# define __INLINE__ __inline +# endif +# endif +# endif +#endif +#if !defined(__LINKAGE__) +#define __LINKAGE__ /* 2004-08-11 transition */ +#endif + +#define _def_externs_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _hdr_exec_attr 1 /* #include ok */ +#define _hdr_math 1 /* #include ok */ +#define _mem_name_exception 1 /* name is a member of struct exception */ +#define _lib_setreuid 1 /* setreuid() in default lib(s) */ +#define _lib_setregid 1 /* setregid() in default lib(s) */ +#define _lib_nice 1 /* nice() in default lib(s) */ +#define _lib_sigflag 1 /* sigflag() in default lib(s) */ +#define _lib_fork 1 /* fork() in default lib(s) */ +#define _lib_spawnveg 1 /* spawnveg() in default lib(s) */ +#define _lib_fchdir 1 /* fchdir() in default lib(s) */ +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/math =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/math (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/math (revision 740) @@ -0,0 +1,162 @@ + +/* : : generated by proto : : */ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/math.sh by iffe version 2007-04-04 : : */ +#ifndef _def_math_ksh93 +#if !defined(__PROTO__) +# if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus) +# if defined(__cplusplus) +# define __LINKAGE__ "C" +# else +# define __LINKAGE__ +# endif +# define __STDARG__ +# define __PROTO__(x) x +# define __OTORP__(x) +# define __PARAM__(n,o) n +# if !defined(__STDC__) && !defined(__cplusplus) +# if !defined(c_plusplus) +# define const +# endif +# define signed +# define void int +# define volatile +# define __V_ char +# else +# define __V_ void +# endif +# else +# define __PROTO__(x) () +# define __OTORP__(x) x +# define __PARAM__(n,o) o +# define __LINKAGE__ +# define __V_ char +# define const +# define signed +# define void int +# define volatile +# endif +# define __MANGLE__ __LINKAGE__ +# if defined(__cplusplus) || defined(c_plusplus) +# define __VARARG__ ... +# else +# define __VARARG__ +# endif +# if defined(__STDARG__) +# define __VA_START__(p,a) va_start(p,a) +# else +# define __VA_START__(p,a) va_start(p) +# endif +# if !defined(__INLINE__) +# if defined(__cplusplus) +# define __INLINE__ extern __MANGLE__ inline +# else +# if defined(_WIN32) && !defined(__GNUC__) +# define __INLINE__ __inline +# endif +# endif +# endif +#endif +#if !defined(__LINKAGE__) +#define __LINKAGE__ /* 2004-08-11 transition */ +#endif + +#define _def_math_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ + + +/* : : generated by iffe from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/data/math.tab : : */ + +typedef Sfdouble_t (*Math_f) __PROTO__((Sfdouble_t,...)); + +#include +#include +#include + +static Sfdouble_t local_finite __PARAM__((Sfdouble_t a1), (a1)) __OTORP__(Sfdouble_t a1;){return finite(a1);} +static int local_fpclassify __PARAM__((Sfdouble_t a1), (a1)) __OTORP__(Sfdouble_t a1;){return fpclassify(a1);} +static int local_isfinite __PARAM__((Sfdouble_t a1), (a1)) __OTORP__(Sfdouble_t a1;){return isfinite(a1);} +static int local_isgreater __PARAM__((Sfdouble_t a1,Sfdouble_t a2), (a1, a2)) __OTORP__(Sfdouble_t a1;Sfdouble_t a2;){return isgreater(a1,a2);} +static int local_isgreaterequal __PARAM__((Sfdouble_t a1,Sfdouble_t a2), (a1, a2)) __OTORP__(Sfdouble_t a1;Sfdouble_t a2;){return isgreaterequal(a1,a2);} +static int local_isinf __PARAM__((Sfdouble_t a1), (a1)) __OTORP__(Sfdouble_t a1;){return isinf(a1);} +static int local_isless __PARAM__((Sfdouble_t a1,Sfdouble_t a2), (a1, a2)) __OTORP__(Sfdouble_t a1;Sfdouble_t a2;){return isless(a1,a2);} +static int local_islessequal __PARAM__((Sfdouble_t a1,Sfdouble_t a2), (a1, a2)) __OTORP__(Sfdouble_t a1;Sfdouble_t a2;){return islessequal(a1,a2);} +static int local_islessgreater __PARAM__((Sfdouble_t a1,Sfdouble_t a2), (a1, a2)) __OTORP__(Sfdouble_t a1;Sfdouble_t a2;){return islessgreater(a1,a2);} +static int local_isnormal __PARAM__((Sfdouble_t a1), (a1)) __OTORP__(Sfdouble_t a1;){return isnormal(a1);} +static int local_isunordered __PARAM__((Sfdouble_t a1,Sfdouble_t a2), (a1, a2)) __OTORP__(Sfdouble_t a1;Sfdouble_t a2;){return isunordered(a1,a2);} +static int local_signbit __PARAM__((Sfdouble_t a1), (a1)) __OTORP__(Sfdouble_t a1;){return signbit(a1);} + +/* + * first byte is two-digit octal number. Last digit is number of args + * first digit is 0 if return value is double, 1 for integer + */ +const struct mathtab shtab_math[] = +{ + "\001acos", (Math_f)acosl, + "\001acosh", (Math_f)acoshl, + "\001asin", (Math_f)asinl, + "\001asinh", (Math_f)asinhl, + "\001atan", (Math_f)atanl, + "\002atan2", (Math_f)atan2l, + "\001atanh", (Math_f)atanhl, + "\001cbrt", (Math_f)cbrtl, + "\002copysign", (Math_f)copysignl, + "\001cos", (Math_f)cosl, + "\001cosh", (Math_f)coshl, + "\001erf", (Math_f)erfl, + "\001erfc", (Math_f)erfcl, + "\001exp", (Math_f)expl, + "\001exp2", (Math_f)exp2l, + "\001expm1", (Math_f)expm1l, + "\001fabs", (Math_f)fabsl, + "\001abs", (Math_f)fabsl, + "\002fdim", (Math_f)fdiml, + "\001finite", (Math_f)local_finite, + "\001floor", (Math_f)floorl, + "\001int", (Math_f)floorl, + "\003fma", (Math_f)fmal, + "\002fmax", (Math_f)fmaxl, + "\002fmin", (Math_f)fminl, + "\002fmod", (Math_f)fmodl, + "\011fpclassify", (Math_f)local_fpclassify, + "\002hypot", (Math_f)hypotl, + "\011ilogb", (Math_f)ilogbl, + "\011isfinite", (Math_f)local_isfinite, + "\012isgreater", (Math_f)local_isgreater, + "\012isgreaterequal", (Math_f)local_isgreaterequal, + "\011isinf", (Math_f)local_isinf, + "\012isless", (Math_f)local_isless, + "\012islessequal", (Math_f)local_islessequal, + "\012islessgreater", (Math_f)local_islessgreater, + "\011isnan", (Math_f)isnanl, + "\011isnormal", (Math_f)local_isnormal, + "\012isunordered", (Math_f)local_isunordered, + "\001lgamma", (Math_f)lgammal, + "\001log", (Math_f)logl, + "\001log1p", (Math_f)log1pl, + "\001log2", (Math_f)log2l, + "\001logb", (Math_f)logbl, + "\001nearbyint", (Math_f)nearbyintl, + "\002nextafter", (Math_f)nextafterl, + "\002nexttoward", (Math_f)nexttowardl, + "\002pow", (Math_f)powl, + "\002remainder", (Math_f)remainderl, + "\001rint", (Math_f)rintl, + "\001round", (Math_f)roundl, + "\002scalb", (Math_f)scalbl, + "\002scalbn", (Math_f)scalbnl, + "\011signbit", (Math_f)local_signbit, + "\001sin", (Math_f)sinl, + "\001sinh", (Math_f)sinhl, + "\001sqrt", (Math_f)sqrtl, + "\001tan", (Math_f)tanl, + "\001tanh", (Math_f)tanhl, + "\001tgamma", (Math_f)tgammal, + "\001trunc", (Math_f)truncl, + "", (Math_f)0 +}; +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/acct =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/acct (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/acct (revision 740) @@ -0,0 +1,12 @@ +/* : : generated by iffe version 2007-04-04 : : */ +#ifndef _def_acct_ksh93 +#define _def_acct_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _lib_acct 1 /* acct() in default lib(s) */ +#define _sys_acct 1 /* #include ok */ +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/options =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/options (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/options (revision 740) @@ -0,0 +1,26 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/options by iffe version 2007-04-04 : : */ +#ifndef _def_options_ksh93 +#define _def_options_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define SHELLMAGIC 1 +#ifndef SHOPT_DEVFD +# define SHOPT_DEVFD 1 +#endif +#ifndef SHOPT_PFSH +# define SHOPT_PFSH 1 +#endif +#undef SHOPT_TEST_L +#ifndef SHOPT_SYSRC +# define SHOPT_SYSRC 1 +#endif +#undef SHOPT_UCB +#if !_PACKAGE_ast && ( (MB_LEN_MAX-1)<=0 || !defined(_lib_mbtowc) ) +# undef SHOPT_MULTIBYTE +#endif + +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/pstat =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/pstat (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/pstat (revision 740) @@ -0,0 +1,10 @@ +/* : : generated by iffe version 2007-04-04 : : */ +#ifndef _def_pstat_ksh93 +#define _def_pstat_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#endif Index: src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/time =================================================================== --- src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/time (revision 0) +++ src/lib/libshell/sparcv9/src/cmd/ksh93/FEATURE/time (revision 740) @@ -0,0 +1,28 @@ +/* : : generated from /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/src/cmd/ksh93/features/time by iffe version 2007-04-04 : : */ +#ifndef _def_time_ksh93 +#define _def_time_ksh93 1 +#define _sys_types 1 /* #include ok */ +#define _LIB_dll 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libdll.a is a library */ +#define _LIB_ast 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libast.a is a library */ +#define _LIB_m 1 /* -lm is a library */ +#define _LIB_cmd 1 /* /home/gisburn/ksh93/ast_ksh_20070418/build_sparc_64bit/arch/sol11.sun4/lib/libcmd.a is a library */ +#define _LIB_nsl 1 /* -lnsl is a library */ +#define _hdr_utime 1 /* #include ok */ +#define _lib_gettimeofday 1 /* gettimeofday() in default lib(s) */ +#define _lib_setitimer 1 /* setitimer() in default lib(s) */ +#define _sys_time 1 /* #include ok */ +#define _mem_tv_usec_timeval 1 /* tv_usec is a member of struct timeval */ +#define _lib_2_timeofday 1 /* 2 arg gettimeofday() */ +#undef _def_time +#include +#define _def_time 1 +#undef timeofday +#if _lib_2_timeofday +#define timeofday(p) gettimeofday(p,(struct timezone*)0) +#else +#if _lib_1_timeofday +#define timeofday(p) gettimeofday(p) +#endif +#endif + +#endif Index: src/lib/libshell/sparcv9/Makefile =================================================================== --- src/lib/libshell/sparcv9/Makefile (revision 0) +++ src/lib/libshell/sparcv9/Makefile (revision 740) @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) Index: src/lib/libshell/common/mamexec =================================================================== --- src/lib/libshell/common/mamexec (revision 0) +++ src/lib/libshell/common/mamexec (revision 740) @@ -0,0 +1,307 @@ +: +# +# Glenn Fowler +# AT&T Bell Laboratories +# +# make abstract machine executor with state +# +# @(#)mamexec (gsf@research.att.com) 07/17/94 +# +# mamexec [ -dfins ] [ target ... ] < mam-file +# +# NOTE: variables defined in this script may conflict with +# mam-file variables +# + +_command_=mamexec +_debug_=: +_diff_=. +_error_='exit 1' +_exec_=eval +_force_= +_list_= +_same_=. +_set_= +_silent_= +_state_list_=MAMlist +_state_time_=MAMtime +_tmp_=/tmp/mam.$$.mam + +trap 'rm -f $_tmp_' 0 +trap 'exit 2' 1 2 +while : +do case $# in + 0) break ;; + esac + case $1 in + --) shift + break + ;; + -*) case $1 in + -*[abceghjklmopqrtuvwxyz_A-Z0-9]*) # [!...] not portable + echo "Usage: $_command_ [ -dfins ] [ target ... ] < mam-file" >&2; exit 2 + ;; + *) case $1 in + *d*) _debug_="eval echo $_command_: debug: >&2" ;; + esac + case $1 in + *f*) _force_=1 ;; + esac + case $1 in + *i*) _error_=: ;; + esac + case $1 in + *n*) _exec_=echo ;; + esac + case $1 in + *s*) _silent_=1 ;; + esac + ;; + esac + ;; + *) break + ;; + esac + shift +done +_select_= +while : +do case $# in + 0) break ;; + esac + case $1 in + *=*) eval $1 + ;; + *) case $_select_ in + "") _select_=$1 ;; + *) _select_="$_select_|$1" ;; + esac + ;; + esac + shift +done +case $_select_ in +"") _select_="*" ;; +esac +(set -e; false || true) && _set_=e || echo $_command_: command errors ignored because of shell botch >&2 +case $_silent_ in +"") _set_=x$_set_ + ;; +*) case $_exec_ in + "echo") _exec_=: ;; + esac + ;; +esac +case $_exec_ in +"eval") _begin_="(" + _end_=") $_tmp_ + for _i_ in `comm -12 $_state_time_ $_tmp_ | sed 's/ .*//'` + do case $_same_ in + .) _same_=$_i_ ;; + *) _same_="$_same_|$_i_" ;; + esac + done +fi + +_index_=_ +_match_= +case `(echo ok | (read -r a; echo $a) 2>/dev/null)` in +ok) _read_='read -r' + ;; +*) # read strips \ -- thanks a lot + # tmp file avoids char at a time read + _read_=read + sed 's/\\/\\\\/g' > $_tmp_ + exec < $_tmp_ + rm -f $_tmp_ + ;; +esac +_old_=1 +_ifs_=$IFS +while IFS=' '; $_read_ _op_ _arg_ _val_ +do IFS=$_ifs_ + case $_op_ in + "note") continue + ;; + "info") case $_arg_ in + "mam") _old_= ;; + esac + continue + ;; + "setv") eval _data_='$'$_arg_ + case $_index_:$_data_ in + __*:*|*:) + case $_exec_ in + "eval") ;; + *) echo "$_arg_=$_val_" ;; + esac + eval $_arg_="$_val_" " main.c + code=1 + if $CC -c main.c 2>/dev/null + then if $CC -L. main.o -lc 2>/dev/null + then $CC -L. main.o -lc > libc.a 2>/dev/null || code=0 + fi + fi + cd /tmp + rm -rf /tmp/mam$$ + exit $code + ) &2; continue ;; + esac + case $_op_ in + "attr") case $_val_ in + "meta"|"suff") _attr_=m$_attr_ ;; + esac + ;; + "exec"|"....") + case $_old_ in + "") _arg_=$_val_ + ;; + *) case $_val_ in + ?*) _arg_="$_arg_ $_val_" ;; + esac + ;; + esac + case $_cmds_ in + "") _cmds_=$_arg_ + ;; + *) _cmds_="$_cmds_ +$_arg_" + ;; + esac + ;; + "done") eval _arg_=$_arg_ + _prop_= + case $_arg_ in + $_name_)case $_attr_ in + *m*) ;; + *x*u*|*u*x*) + case $_cmds_ in + "") case $_attr_ in + *u*) _prop_=u ;; + esac + ;; + *) $_exec_ "$_begin_$_set_$_cmds_$_end_" || + { + _code_=$? + case $_set_ in + *-*e*) ;; + *) case $_cmds_ in + *if*then*fi*|"||") _code_=0 ;; + esac + ;; + esac + case $_code_ in + 0) ;; + *) echo "*** exit code $_code_ making $_name_" >&2 + $_error_ + ;; + esac + } + _prop_=u + ;; + esac + ;; + esac + _index_=$_prev_ + eval _name_='$'_name_$_index_ + eval _prev_='$'_prev_$_index_ + eval _cmds_='$'_cmds_$_index_ + eval _attr_=$_prop_'$'_attr_$_index_ + ;; + *) echo $_val_: $_op_ $_name_ expected >&2 + ;; + esac + ;; + esac +done +IFS=$_ifs_ +case $_match_ in +"") echo "$_command_: don't know how to make $_select_" >&2; $_error_ ;; +esac +case $_exec_ in +"eval") echo "$_list_" > $_state_list_ + mamstate $_state_list_ < $_state_list_ | sort > $_state_time_ + ;; +esac Index: src/lib/libshell/common/RELEASE =================================================================== --- src/lib/libshell/common/RELEASE (revision 0) +++ src/lib/libshell/common/RELEASE (revision 740) @@ -0,0 +1,1455 @@ +07-04-18 --- Release ksh93s+ --- +07-04-18 A small memory leak with each redirection of a non-builtin has + been fixed. +07-03-08 A bug in which set +o output command line options has been fixed. +07-03-08 A bug in which an error in read (for example, an invalid variable + name), could leave the terminal in raw mode has been fixed. +07-03-06 A bug in which read could core dump when specified with an array + variable with a subscript that is an arithmetic expression has + been fixed. +07-03-06 Several serious bugs with the restricted shell were reported and + fixed. +07-03-02 If a job is stopped, and subsequently restarted with a CONT + signal and exits normally, ksh93 was incorrectly exiting with + the exit status of the stop signal number. +07-02-26 M-^L added to emacs mode to clear the screen. +07-02-26 A bug in which setting a variable readonly in a subshell would + cause an unset error when the subshell completed has been fixed. +07-02-19 The format with printf uses the new = flag to center the output. +07-02-19 A bug in which ksh93 did not allow multibyte characters in + identifier names has been fixed. +07-02-19 A bug introduced in ksh93 that causes global compound variable + definitions inside functions to exit with "no parent" has been fixed. +07-02-19 A bug in which using compound commands in process redirection + arguments would give syntax errors <(...) and >(...) has been fixed. +07-01-29 A bug which caused the shell to core dump which can occur when a + built-in exits without closing files that it opens has been fixed. +07-01-26 A bug in which ~(E) in patterns containing \ that are not inside () + has been fixed. + +06-12-29 --- Release ksh93s --- +06-12-29 A bug in which the value of IFS could be changed after a command + substitution has been fixed. +06-12-22 /dev/(tcp|udp|sctp)/HOST/SEVRICE now handles IPv6 addresses on + systems that provide getaddrinfo(3). +06-12-19 A -v option was added to read. With this option the value of + the first variable name argument will become the default value + when read from a terminal device. +06-11-20 A bug in which "${foo[@]:1}}" expands a null argument (instead of + no argument), when foo[0] is not empty has been fixed. +06-11-16 The discipline functions have been modified to allow each subscript + to act independently. Currently the discipline function will not + be called when called from a discipline function of the same variable. +06-11-14 A bug which could cause a core dump if a file descriptor for + an internal file was closed from with a subshell has been fixed. +06-10-30 The redirections <# pattern, and <## pattern have been added. + Both seek forward to the beginning of the next line that contains + the pattern. The <## form copies the skipped portion to standard + output. +06-10-26 On systems that support stream control transport, the virtual file + name /dev/sctp/host/port can now be used to establish connections. +06-10-26 The printf modifier # when used with d produces units in thousands + with a single letter suffix added. The modifier # when used with + the i specification provides units of 1024 with a two letter suffix. +06-10-24 The value of $! is now set to the process id of a job put + into the background with the bg command as required by POSIX. +06-10-23 A bug in which the value of $! was affected by a background + job started from a subshell has been fixed. +06-10-23 A bug in ${var:offset:len} in multibyte locales has been fixed. +06-10-15 The remaining math functions from C99 were added for any system + that supports them. +06-10-13 The klockwork.com software detected a few coding errors that + have been fixed. +06-10-12 A bug when skipping over `...` with ${x:=`...`} when x is set + has been fixed. +06-10-11 A bug in process floating constants produced by the %a format + of printf has been fixed. +06-10-06 A bug in which IFS was not being restored correctly in some + cases after a subshell has been fixed. +06-10-06 A bug in which pipefail was not detecting some failures in + pipelines with 3 or more states has been fixed. +06-10-03 A bug in the processing of >(...) with builtins which could + cause the builtin to hang has been fixed. +06-10-03 A bug in the for loop optimizer which causes >(...) process + substitution to be ignored has been fixed. +06-09-17 The -a option was added to typeset for indexed arrays. This + is only needed when using the ([subscript]=value ...) form. +06-09-06 The showme option was added. Each simple command not beginning + with a redirection and not occurring with in the while, until, if, + select condition can be preceded by a semi-colon which will + be ignored when showme is off. When showme is on, any command + preceded by a colon will be traced but not executed. +06-08-16 As a new feature, a leading ~(N) on a pattern has no effect + except when used for file expansion. In this case if not + matches are found, the pattern is replaced by nothing rather + than itself. +06-08-11 A bug in the expansion of ${.sh.match[i]:${#.shmatch[i]}} has + been fixed. +06-08-10 The read builtin options -n and -N have been modified to treat + the size as characters rather than bytes unless storing into a + binary (typeset -B) variable. +06-07-27 When the here document operator << is followed directly by a # + rather than a -, the first line of the here-document determines + how much whitespace is removed for each line. +06-07-26 A bug in the C-shell history (enabled with set -H) in which the + history event !$ was not processed has been fixed. +06-07-21 A bug on some systems in which assigning PATH on a command line + would not take effect has been fixed. +06-07-20 Add ksh93 and rksh93 as allowable names for ksh binaries. +06-07-20 Removed the SHOPT_OO compilation option which was only partially + implemented. +06-07-20 The ability to use egrep, grep, and fgrep expressions within + shell patterns has been documented. +06-07-17 A bug with arithmetic command expressions for locales in which + the comma is a thousands separator has been fixed. +06-07-13 The default HISTSIZE was increased from 128 to 512. +06-07-13 A multibyte problem with locales that use shift codes has been fixed. +06-06-23 A number of bug fixes for command, file, and variable completion + have been mode. +06-06-20 Floating point division by zero now yields the constant Inf or -Inf + and floating functions with invalid arguments yield NaN. +06-06-20 The floating point constants Inf and NaN can be used in arithmetic + expressions. +06-06-20 The functions isinf(), isnan(), tanhl() have been added for + arithmetic expressions. +06-06-13 Internal change to use ordering for variables instead of hashing + to speed up prefix matching. +06-06-13 A window between fork/exec in which a signal could get lost + and cause a program to hang has been eliminated +06-06-13 A bug in edit completion with quoted strings has been fixed. +06-06-07 The restricted options can now be enabled by set as well as on + the command line. Once set, it can not be disabled. +06-06-04 Modified built-in binding so that for systems for which /bin + and /usr/bin are the same, a builtin bound to /bin will get + selected when either /bin or /usr/bin is scanned. +06-06-04 Added literal-next character processing for emacs/gmacs mode. + This change is not compatible with earlier versions of ksh93 + and ksh88 when the stty lnext is control-v. The sequence + escape-control-v will display the shell version. +06-05-31 Modified emacs and vi mode so that entering a TAB after a partial + TAB completion, generates a listing of possible completions. + After the second TAB, a number followed by a TAB will perform + the completion with the corresponding item. +06-05-19 Modified arithmetic so that conversions to strings default to + the maximum number of precision digits. +06-05-16 Bug fixes for multibyte locales. +06-05-10 The =~ operator was added to [[...]] and [[ string ~= ERE ]] + is equivalent to [[ string == ~(E)ERE ]]. +06-04-25 A bug in the vi edit mode which could cause the shell to core dump + when switching from emacs mode. +06-04-17 A bug in which using LANG or LC_ in assignment lists with builtins + did not restore the localed correctly has been fixed. +06-04-04 A bug in which discipline functions could not be added to variables + whose names started with .sh has been fixed. +06-03-28 The -s option to typeset was added to modify -i to indicate short + integers. +06-03-28 A bug in which variables assignment lists before functions + defined with function name were not passed on the functions + invoked by this function has been fixed. +06-03-28 A bug in which name references defined within a function defined + with function name could not be used with compound variables has + been fixed. +06-03-27 A bug in which read <&p (print >&p) would cause the coprocess input + (output) pipe to close before reading from (after writing to) + it has been fixed. +06-02-28 A bug in which stopping a job created with the hist builtin command + would create a job that could not be restarted has been fixed. + +06-01-24 --- Release ksh93r --- +06-01-24 A bug in which running commands with standard output closed would + not work as expected has been fixed. +06-01-23 A bug in which print -u could fail when file descriptor was + open for writing has been fixed. +06-01-19 The ?: arithmetic operator fixed to work when the operation after + the colon was an assignment. +05-12-24 A bug which could lead to a core dump when elements of a compound + variable were array elements, i.e. foo=(bar=(1 2)), has been fixed. +05-12-13 An arithmetic bug in which x+=y+=z was not working has been fixed. +05-12-13 An arithmetic bug in which x||y was returning x when x was non-zero + rather than 1 has been fixed. +05-12-07 The aliases for integer and float have been changed to use attributes + -li and -lE to handle long long and long double types. +05-12-07 The histexpand (-H) option has been added which allows C-shell + style history expansions using the history character !. +05-12-07 The multiline option was added which changes that way the edit + modes handle lines longer than the window width. Instead of + horizontal scrolling, multiple lines on the screen are used. +05-12-05 The whence builtin now returns an absolute pathname when the + command is found in the current directory. +05-11-29 A bug which caused ksh -c '[[ ! ((' to core dump rather than + report a syntax error has been fixed. +05-11-29 A bug when reading fixed length records into typeset -b variables + which caused a zero byte to terminate the value has been fixed. +05-11-22 The ability to seek to an offset within a file has been added + with the new I/O redirection operators, <# and >#. Currently, + these redirection operators must be followed by ((expr)) + but in a future release, it should be able to used to seek forward + to the specified shell pattern. In addition $(n<#) expands to the + current byte offset for file descriptor n. +05-11-22 The .sh.match array variable is now set after each [[ ... ]] + pattern match. Previously it was only set for substring matches. +05-10-17 A bug in which the library path variable could be prefixed + with a directory when a .path file was not encountered in + the directory of the executable has been fixed. +05-09-15 A for/while loop optimizer bug in which $OPTIND was not + correctly expanded has been fixed. +05-09-05 A bug in which a history command that invoked a history + command could go into an infinite loop has been fixed. +05-08-31 In the case that IFS contains to adjacent new-lines so that + new-line is not treated as a space delimiter, only a single + new-line is deleted at the end of a command substitution. +05-08-19 When a tilde substitution expands to the / directory and is + followed by a /, it is replaced by the empty string. +05-08-16 A bug in which n<&m did not synchronize m has been fixed. +05-08-16 A bug in which process substitution ( <() and >() ) was not + working within for and while loops has been fixed. +05-07-24 A bug in which the pattern ~(E)(foo|bar) was treated as a syntax + error has been fixed. +05-07-24 A bug in completion with =, where n was the one of the + previous selection choices has been fixed. +05-07-21 A bug with multibyte input when no edit mode was specified which + caused the input line to shift left/right has been fixed. +05-06-24 A race condition which could cause the exit status to get lost + on some fast systems has been fixed. +05-06-21 A bug in which nested patterns of the form {m,n}(pat) would cause + syntax errors has been fixed. +05-06-21 A bug in the macro expander has been fixed which could cause a + syntax error for an expansion of the form ${x-$(...)} when + x is set and the command substitution contained certain strings. +05-06-08 On systems for which echo does not do System V style \ expansions, + the -e option was added to enable these expansion. +05-06-08 A bug in which ${var op pattern} to not work when inside an + arithmetic expression has been fixed. +05-05-23 An extension to shell patterns that allows matching of nested + groups while skipping over quoted strings has been added. +05-05-18 A bug in which the line number for errors was not correct for + functions loaded from FPATH has been fixed. +05-04-18 A bug in which the exit status $? is not set when a trap triggered + by the [[...]] command is executed has been fixed. +05-04-08 Redirection operators can be directly preceded with {varname} + with no intervening space, where varname is a variable name which + allows the shell to select a file descriptor > 10 and store it + into varname. +05-04-08 SHOPT_CMDLIB_BLTIN=1 now includes generated table. +05-04-07 [[ -o ?option ]] is true if "option" is a supported option. +05-04-05 A bug in handling file completion with spaces in the names + has been fixed. +05-03-25 The SIGWINCH signal is caught by default to keeps the LINES and + COLUMNS variables in sync with the actual window size. +05-03-25 Building ksh with SHOPT_REMOTE=1 causes ksh to set --rc if stdin is + a socket (presumably part of a remote shell invocation.) +05-03-25 Building ksh with SHOPT_SYSRC=1 causes interactive ksh to source + /etc/ksh.kshrc (if it exists) before sourcing the $ENV file. +05-03-25 {first..last[..incr][%fmt]} sequences added to brace expansions + when braceexpand is enabled. +05-03-03 A bug where a SIGCHLD interrupt could cause a fifo open to fail has + been fixed. +05-02-25 A bug in which a builtin command run in the background could + keep a file descriptor open which could cause a foreground + process to hang has been fixed. +05-02-24 A bug where builtin library commands (e.g., date and TZ) failed to + detect environment variable changes has been fixed. +05-02-22 The read builtin and word splitting are now consistent with respect + to IFS -- both treat IFS as field delimiters. +05-02-22 The read builtin no longer strips off trailing delimiters that + are not space characters when there are fewer variables than fields. +05-02-17 A builtin bug on systems where dlsym(libcmd) returns link-time + bindings has been fixed. +05-02-12 A bug in which the lib_init() function for .paths BUILTIN_LIB + libraries was not called has been fixed. +05-02-06 A bug on some systems in which moving the write end of a co-process + to a numbered file descriptor could cause it to close has been fixed. +05-02-06 A bug in the vi-edit mode in which the character under the cursor + was not deleted in some cases with the d% directive has been fixed. +05-02-06 A bug where external builtin stdout/stderr redirection corrupted + stdout has been fixed. +05-02-04 A bug where times formatting assumed CLK_TCK==60 has been fixed. + +05-01-11 --- Release ksh93q --- +05-01-11 A bug in the integral divide by zero check has been fixed. +05-01-11 The -l option has been added to read /etc/profile and + $HOME/.profile, if they exist, before the first command. +05-01-11 An argument parsing bug that caused `kill -s x -- n' to fail has + been fixed. +05-01-11 The .paths file, introduced in ksh93m, which can appear in + any directory in PATH, now allows a line of the form 'BUILTIN_LIB=.' + When a command is searched for this directory, and the full path + matches the path of the built-in version of the command (listed + by the 'builtin' command) then the built-in version of the command + is used. When ksh is built with SHOPT_CMDLIB_DIR=1 then all libcmd + functions become builtins with the '/opt/ast/bin/' directory prefix. +05-01-10 A bug in which a nameref to a compound name caused a core dump has + been fixed. +05-01-09 A bug in which some SIGCHLD interrupts (from child processes exiting) + caused a fatal print/echo error diagnostic has been fixed. +04-12-24 A bug in which some SIGCHLD interrupts (from child processes exiting) + corrupted the internal process/job list, sometimes causing the shell + to hang, has been fixed. +04-12-01 A bug in which typeset -Fn truncated less than n digits for large + numbers has been fixed. +04-11-25 A bug in which standard error could be closed after a redirection + to /dev/stderr has been fixed. +04-11-17 A bug in which an expansion of the form ${array[@]:3} could expand + to ${array[0]} when ${array[3]} was not set has been fixed. +04-10-22 The -E or -orc command line option reads ${ENV-$HOME/.kshrc} file. +04-10-22 `-o foo' equivalent to `+o nofoo', `-o nobar' equivalent to `+o bar'. + `--foo' equivalent to `-o foo', `--nofoo' equivalent to `+o foo' +04-10-05 The .paths file, introduced in ksh93m, which can appear in + any directory in PATH, now allows a line of the form + 'BUILTIN_LIB=libname'. When a command is searched for this directory, + the shared library named by libname will first be searched for a + built-in version of the command. +04-09-03 <<< here documents now handle quotes in the word token correctly. +04-08-08 The maximum size for read -n and and read -N was increased from + 4095 to 32M. +04-08-04 printf %q was modified so that if an no operand was supplied, no + no output would be generated rather than a quoted empty string. +04-08-01 The -n and -N options of the read builtin has been modified + when reading variables with the binary attribute so that the + data is stored directly rather than through assignment. +04-08-01 The shcomp command has been modified to process alias commands + under some conditions. +04-07-31 The .sh.match variable added in ksh93l, now works like other + indexed arrays. +04-07-08 A loop optimizer bug which occurs when typeset is used in + a for or while loop inside a function has been fixed. +04-06-24 The number of subexpressions in a pattern was increased to 64 + from the current number of 20. +04-06-17 The -t option to read was modified to allow seconds to be + specified as any arithmetic expression rather than just + an integral number of seconds, for example even -t 'sin(.5)' + is now valid. +04-06-16 Two small memory leak problems were fixed. +04-06-15 A bug in ${var/pattern/"string"} which occurred when string + contained pattern matching characters has been fixed. +04-05-08 printf $'%d\n' produced an erroneous error message and has + been fixed. +04-05-24 A bug in which an associative array without any elements could + cause a core dump when a script with an associative array with + the same name was declared in a script invoked by name has + been fixed. +04-05-11 A bug in which an exec statement could close the script that + is being processed in a script that is run by name causing + a failure has been fixed. +04-04-28 If the first character of assignment to an integer variable was 0, + the variable had been treated as unsigned. This behavior was + undocumented and has been removed. +04-04-05 A bug in which the positioning of standard input could be incorrect + after reading from standard input from a subshell has been fixed. +04-03-30 A bug in the for loop optimizer which in rare cases could cause + memory corruption has been fixed. +04-03-29 The preset alias source='command .' has been added. +04-03-29 A bug introduced in ksh93p on some systems in which invoked by + name with #! on the first line would not get the signals handling + initialized correctly has been fixed. +04-03-29 A bug introduced in ksh93p in which a HUP signal received by + a shell that is a session group leader was not passed down to + its children has been fixed. + +04-02-28 --- Release ksh93p --- +04-02-28 The ability to apply an append discipline to any variable has + been added. +04-02-14 A bug in which the exportall option (set -a) would cause incorrect + results for arrays has been fixed. +04-02-02 A bug in which an exported array would pass more than + the first element to a script invoked by name has been fixed. +04-02-02 A bug on some systems in which name=value pairs preceding a script + invoked by name was not getting passed to the script has been fixed. +04-01-20 A bug in which an unset discipline function could cause a core + dump on some systems has been fixed. +04-01-12 A bug in which a continue or break called outside a loop from + inside a function defined with name() syntax could affect + the invoking function has been fixed. +04-01-08 If a command name begins with ~, only filename completion will be + attempted rather than pathname completion using the builtin editors. +04-01-08 A bug in the vi edit mode in which the wrong repeat count on + multiple word replacements with the . directive has been fixed. +04-01-06 Backspace characters are now handled correctly in prompt strings. +04-01-06 The getopts builtin has been modified to accept numerical + arguments of size long long on systems that support this. +04-01-06 A bug in which unsetting all elements of an associative array + would cause it to be treated as an indexed array has been fixed. +03-12-15 A bug in which a quoted string ending with an unescaped $ would + delete the ending $ in certain cases has been fixed. +03-12-05 A bug in which the shell could hang when set -x tracing a command + when an invalid multibyte character is encountered has been fixed. +03-12-05 On some systems, if the KEYBD trap is set, then commands that use + the meta key were not processed until return was hit. This + has been fixed. +03-12-05 A problem which occurred when the login shell was not a group + leader that could cause it to fail has been fixed. +03-12-05 A problem in which a shell could core dump after receiving a signal + that should cause it to terminate while it was in the process + of acquiring more space has been fixed. +03-12-05 If ENV is not specified, the shell will default to $HOME/.kshrc + for interactive shells. +03-11-21 A bug introduced in ksh93o in which the DEBUG trap could get + disabled after it triggered has been fixed. +03-11-04 A bug in which using arithmetic prefix operators ++ or -- on a + non-lvalue could cause a core dump has been fixed. +03-11-04 A bug in which leading zeros were stripped from variable + expansions within arithmetic computation to avoid being treated + as octal constants when they should not have, has been fixed. +03-10-08 A bug introduced in ksh93o in which a large here document inside + a function definition could get corrupted has been fixed. +03-09-22 A bug in which the .get discipline function was not being + called when a string variable was implicitly referenced from + within a numerical expression has been fixed. +03-09-22 A bug in which a script without a leading #! could get executed + by /bin/sh rather than the current shell on some systems has + been fixed. +03-09-12 To improve conformance with ksh88, leading zeros will be ignored + when getting the numerical value of a string variable so that + they will not be treated as octal constants. +03-09-03 The builtin kill command now processes obsolete invocations + such as kill -1 -pid. +03-09-02 The restriction on modifying FPATH in a restricted shell (sh -r) + has been documented. +03-09-02 The restricted shell (sh -r) has been modified to disallow + executing command -p. +03-08-07 A bug in which the KEYBD trap was not being invoked when + characters with the 8th bit set has been fixed. +03-08-02 A parser bug introduced in ksh93o which caused the character + after () in a Posix function definition to be skipped + when reading from standard input has been fixed. +03-08-01 A bug in which "${foo#pattern}(x)" treated (x) as if it were + part of the pattern has been fixed. +03-08-01 The command -x option has been modified so that any trailing + arguments that do expand to a single word will be included + on each invocation, so that commands like command -x mv * dir + work as expected. + +03-07-20 --- Release ksh93o+ --- +03-07-20 A bug in which could cause memory corruption when a posix + function invoked another one has been fixed. +03-07-15 A bug in which a file descriptor>2 could be closed before + executing a script has been fixed. +03-07-15 A parsing error for <() and >() process substitutions inside + command substitution has been fixed. +03-07-15 A parsing error for patterns of the form {...}(...) when + used inside ${...} has been fixed. +03-07-15 An error in which expanding an indexed array inside a compound + variable could cause a core dump has been fixed. +03-07-15 A bug in which under on rare occassions a job completion interrupt + could cause to core dump has been fixed. +03-06-26 A bug in which process substitution embedded within command + substitution would generate a syntax error has been fixed. +03-96-23 A bug in which ${@:offset:len} could core dump when there + were no arguments has been fixed. +03-96-23 A bug in which ${X[@]:offset:len} could core dump when X + was unset has been fixed. +03-06-22 The -x option was added to the command builtin. If this + option is on, and the number of arguments would exceed ARG_MAX, + the command will be invoked multiple times with a subset of + the arguments. For example, with alias grep='command -x grep, + any number of arguments can be specified. +03-06-14 A bug in which could cause a core dump on some systems with + vi and emacs editors with the MULTIBYTE option has been fixed. +03-06-06 A bug in which the shell could core dump when a script was + run from its directory, and the script name a symlink to a file + beginning with .., has been fixed. +03-06-05 A bug in which the shell could core dump when a child process + that it is unaware of terminates while it is calling malloc() + has been fixed. +03-06-02 An option named globstar (set -G) has been added. When enabled, + during pathname expansion, any component that consists only of ** is + matches all files and any number of directory levels. +03-05-30 A bug in which the PATH search could give incorrect results when + run from directory foo and PATH contained .:foo:xxx has been fixed. +03-05-29 Some changes were made to the code that displays the prompt in edit + mode to better handle escape sequences in the prompt. +03-05-27 I added = to the list of characters that mark the beginning of + a word for edit completion so that filenames in assignments + can be completed. +03-05-20 A bug in which read -N could hang on some systems when reading + from a terminal or a pipe has been fixed. +03-05-19 A bug in which the output of uname from a command substitution + would go to the standard output of the invoking command when + uname was invoked with a non-standard option has been fixed. +03-05-19 A job control bug which would cause the shell to exit because + it hadn't take back the terminal has been fixed. The bug + could occur when running a function that contained a pipeline + whose last element was a function. +03-05-19 A job control timing bug introduced in ksh93o on some systems + which could cause a pipeline to hang if the first component + completed quickly has been fixed. +03-05-13 The read builtin has been modified so that the builtin editors + will not overwrite output from a previous incomplete line. +03-05-13 A bug in which the name of an identifier could have the string + .sh. prefixed to it after expanding a variable whose name begins + with .sh. has been fixed. +03-05-13 A bug in the expansion of $var for compound variables in which + some elements would not be output when the name was a prefix + of another name in the compound variable has been fixed. +03-05-08 The last item in the ksh93o release on 03-01-02 has been + altered slightly to preserve the leading 0's when the + preceding character is a digit. Thus, with typeset -LZ3 x=10, + $(( 1$x)) will be 1010 whereas $(( $x) will be 10. +03-04-25 A bug in which if x is a name reference, then nameref y=x.foo + did not follow x has been fixed. + +03-03-18 --- Release ksh93o --- +03-03-18 A -N unary operator was added to test and [[...]] which returns + true if the file exists and the file has been modified since it + was last read. +03-03-18 The TIMEFORMAT variable was added to control the format for + the time compound command. The formatting description is + described in the man page. +03-03-06 A -N n option was added to read which causes exactly n bytes + to be read unlike -n n which causes at most n bytes to be read. +03-03-03 Three new shell variables were added. The variable .sh.file + stores the full pathname of the file that the current command + was found in. The variable .sh.fun names the current function + that is running. The variable .sh.subshell contains the depth + of the current subshell or command substitution. +03-03-03 When the DEBUG trap is executed, the current command line after + expansions is placed in the variable .sh.command. The trap + is also now triggered before each iteration of a for, select, + and case command and before each assignment and redirection. +03-02-28 Function definitions are no longer stored in the history file so + that set -o nolog no longer has any meaning. +03-02-28 All function definitions can be displayed with typeset -f not + just those stored in the history file. In addition, typeset +f + displays the function name followed by a comment containing the + line number and the path name for the file that defined this function. +03-02-28 A bug in which the value of $LINENO was not correct when executing + command contained inside mult-line command substitutions has been + fixed. +03-02-19 Since some existing ksh88 scripts use the undocumented and + unintended ability to insert a : in front of the % and # parameter + expansion operators, ksh93 was modified to accept :% as equivalent + to % and :# as equivalent to # with ${name op word}. +03-02-14 A bug which could cause a core dump when reading from standard + error when standard error was a pty has been fixed. +03-02-14 The shell arithmetic was modified to use long double on systems + that provide this data type. +03-02-09 A bug in which a function located in the first directory in FPATH + would not be found when the last component of PATH was . and the + current directory was one of the directories in PATH has been fixed. +03-02-07 The trap and kill builtin commands now accept a leading SIG prefix + on the signal names as documented. +03-02-05 A bug in the expansion of ${var/$pattern}, when pattern contained + \[ has been fixed. +03-02-05 A bug in which .sh.match[n], n>0, was not being set for substring + matches with % and %% has been fixed. +03-01-15 A bug in which getopts did not work for numerical arguments specified + as n#var in the getopts string has been fixed. +03-01-09 A bug in which using ${.sh.match} multiple times could lead to + a memory exception has been fixed. +03-01-06 A bug in the expansion of ${var/pattern/$string} in the case that + $string contains \digit has been fixed. +03-01-02 A -P option was added for systems such as Solaris 8 that support + profile shell. +03-01-02 For backward compatibility with ksh88, arithmetic expansion + with ((...)) and let has been modified so that if x is a zero-filled + variable, $x will not be treated as an octal constant. + +02-12-05 --- Release ksh93n+ --- +02-11-30 A bug that can show up in evaluating arithmetic statements that + are in an autoloaded function when the function is autoload from + another function has been fixed. +02-11-30 An optimization bug in which an expansion of the form ${!name.@}, + which occurred inside a for or a while loop, when name is a name + reference, has been fixed. +02-11-18 A bug in which modifying array variables in a subshell could leave + side effects in the parent shell environment has been fixed. +02-11-18 A memory leak when unsetting an associative array has been fixed. +02-11-14 The code to display compound objects was rewritten to make + it easier for runtime extensions to reuse this code. +02-11-14 A change was made to allow runtime builtins to be notified when + a signal is received so that cleanup can be performed. +02-10-31 User applications can now trap the ALRM signal. Previously, + the ALRM signal was used internally and could not be used + by applications. +02-10-31 A bug in which signals received while reading from a coprocess + for which traps were set was not handled correctly has been fixed. +02-10-31 A bug in which a file opened with exec inside a subshell could + be closed before the subshell completed has been fixed. +02-10-21 A bug in which setting PATH or FPATH inside a function might not + take effect has been fixed. +02-10-21 A bug which could cause a core dump when a local SECONDS variable + is defined in a function has been fixed. +02-10-15 A bug in which the associate array name operator ${!array[@]} + could return the same name multiple times has been fixed. +02-10-15 A bug in which the zero'th element of an associative array was + not getting set when an assignment was made without a subscript + specified has been fixed. + +02-09-30 --- Release ksh93n --- +02-09-30 The maximum indexed array size was increased to 16Megs. +02-09-30 A bug which could cause a core dump when changing attributes + of associative array has been fixed. +02-09-30 A bug in which exporting an array variable would not export the + 0-th element has been fixed. +02-09-30 A bug in which an array assignment of the form a=($a ...) would unset + 'a' before the right hand side was evaluated has been fixed. +02-09-27 A bug in which the error message for ${var?message} when var was + null or unset did not contain the variable name var has been fixed. +02-09-27 A bug in which closing file descriptors 0 through 2 could + cause a subsequent here document to fail has been fixed. +02-09-14 A bug in whence which occurs when the specified name contained + a / has been fixed. +02-09-14 A bug in the parser for strings of the form name$((expr))=value + has been fixed. +02-09-14 A for loop optimization bug in which the number of elements in + an array was treated as an invariant has been fixed. +02-09-09 A bug in which redirection or closing of a file descriptor between + 3 and 9 could cause a subsequent here document to fail has been + fixed. +02-09-09 A bug in which a background job was not removed from the job list + when a subshell completed has been fixed, for example (prog&). +02-09-03 A bug in which an assignment of the form name=(integer x=3) + could be interpretted as an array assignment rather than a + compound variable assignment has been fixed. +02-08-19 A command completion bug which occurred on file systems that + are case insensitive has been fixed. +02-08-19 A bug which could lead to an exception on some systems (for + example FREEBSD) which occurred when setting PATH has been fixed. +02-08-11 A bug in arithmetic rounding in which a value input as a decimal + string would output as a rounded version of the string has + been fixed. +02-08-11 A bug in which the last character could be deleted from shell + traces and from whence when called from a multibyte locale + has been fixed. +02-08-01 A bug which could cause a core dump to occur when a shell script + is executed while a coprocess is running that has closed the + output pipe has been fixed. +02-08-01 A bug in which command completion in multibyte mode could + corrupt memory for long command lines has been fixed. + +02-06-17 --- Release ksh93n- --- +02-06-17 A bug in which user defined macros could cause a core dump in + with MULTIBYE mode has been fixed. +02-06-17 A bug in which printf format specifiers of the form %2$s were causing + a core dump has been fixed. +02-06-17 A bug in which setting stty to noecho mode did not prevent the + echoing of characters by ksh when emacs or viraw mode + was enabled has been fixed. +02-06-17 A bug in which background job completion could cause the sleep + builtin to terminate prematurely has been fixed. +02-06-17 A bug in which the shell could core dump if getopts was called + when the OPTIND variable contained a negative value has been fixed. +02-06-10 The edit mode prompt has been modified to handle escape sequences. +02-06-10 A bug which occurred for interactive shells in which the builtin + cat command was used in command substitution on a file whose + size was larger than PIPE_BUF has been fixed. +02-06-10 A bug in which the trap on ERR was not being processed when + set inside a function has been fixed. +02-06-07 A bug in which function definitions could cause the history count + to be decremented by one (and even become negative) has been fixed. +02-06-05 A bug in read in which share mode could be enabled has been fixed. +02-05-28 A bug which could occur when the last command of a script was + a case statement and the action selected ended in ;& instead of ;; + has been fixed. +02-05-23 A bug with unary + introduced in ksh93k has been fixed. +02-05-07 A bug in substitutions of the form ${var/pattern/string} in which + a backslash was inserted in the replacement string when it contained + a special pattern character has been fixed. +02-05-01 A bug in the emacs edit mode which occurred in versions compiled + for multibyte character sets which occurred when a repeated search + was requested after a long line had been returned for the previous + search has been fixed. +02-04-02 vi and emacs edit modes were modified so that tab completion is + disabled when invoked from the read built-in. + +02-03-26 --- Release ksh93m+ --- +02-03-26 A bug in which \ was not handled correctly when used in file + expansion has been fixed. +02-02-18 A bug in which lines beginning with a # were deleted from here + documents when the here-document delimiter was followed by + a comment has been fixed. +02-12-06 An optimization bug in which ${!x[@]) was treated as invariant in + a for loop has been fixed. +02-02-06 A bug in which the ERR trap is not cleared for a script invoked + by name from within a function has been fixed. +02-01-08 A bug in which a shell script executed from within a subshell + could cause this script to have an invalid pointer leading + to a memory fault has been fixed. +02-01-07 Added here documents of the form <<< word (as per zsh) which + is equivalent to << delim\nword\ndelim. +02-01-07 A bug in which the first word of a compound assignment, + x=(word ...), was treated as a reserved word has been fixed. +02-01-07 A bug in the handling of \ when noglob was enabled and a + substitution of the form ${word op pattern} occurred in the + same word has been fixed. +02-01-07 A compilation option, CMDLIB_BLTIN in the file OPTION, has + been added. When this options is set, all commands implemented + in libcmd become shell builtin commands by default. +02-01-07 A bug in which builtin foo, where foo is already a builtin + would result in the builtin foo getting removed has been fixed. +02-01-07 A bug which the shell executed a command found in the current + directory when PATH have no valid directories has been fixed. +01-11-28 The value of $? was not being set when called with exit. +01-11-28 If the last command was of the form (...) and a trap on EXIT or + ERR was set, and the command inside () modified the trap, then + the original trap wasn't executed. +01-11-26 The value for 0 is now preceded by the base number when + the base was not 10. +01-11-26 The default has compilation mode has been changes so that + viraw mode will always be on. + +01-10-31 --- Release ksh93m --- +01-10-31 A for loop optimizer bug for subshells contained withing for + loops has been fixed. +01-10-16 typeset without arguments no longer outputs variable names + that do not have any attributes that are set. +01-10-16 A bug introduced in ksh93l in which assignments specified with + the exec built-in were not being expanded properly has been + fixed. +01-10-11 An optimization bug in which ${!x) was treated as invariant in + a for loop has been fixed. +01-10-11 Unsigned integer variables in bases other than 10 are printed now + expand in that base with the base prefix. +01-10-10 A number of typos in the self generating man pages for shell + built-ins have been fixed. +01-10-04 The self generated man pages for hist and fc were not working + correctly and have been fixed. +01-10-03 Yet another optimizer bug in which shell patterns were + treated as invariants has been fixed. +01-09-27 Two bugs relating to multibyte history searches and to find + have been fixed. +01-09-27 A bug introduced in ksh93k in which the PATH searching was + not restored after running a command with an assignment list + has been fixed. +01-09-26 A bug in which a zero filled field was treated as octal when + converted to integer has been fixed. +01-09-26 Yet another bug in the optimization of for loops related to + recursive functions with break or continue statements has been fixed. +01-09-25 The exponentiation operator ** was added to the shell arithmetic + evaluation. It has higher precedence than * and is left + associative. +01-09-25 The code was modified to use the ast multibyte macros + and functions for handing multibyte locales. +01-09-25 The expansion ${parameter:offset:length} now handles negative + offsets which cause offsets to be measured from the end. +01-09-25 Some spelling errors in the documentation were corrected. +01-09-24 The /dev/tcp/host/port and /dev/udp/host/port now allow + the ports to be specified by service name. +01-09-24 The change staring with ksh93g in which the the appropriate + library path variable is prepended with a corresponding library + directory has been modified. With the new method, only the + library path defined in the file named .paths in the directory + where the executable is found will be modified. See the + man page for more details. +01-09-23 The .fpath file (see ksh93h) is no longer looked for in each + directory on the path to locate function directories. The + file named .paths is used instead. +01-09-23 A bug in which IFS was not being restored after being changed in + a subshell has been fixed. +01-09-16 With the vi and emacs edit modes, after a list of command + or functions is generated with = or M-= respectively, + any element from the list can be pasted on the command line + by preceding the = or M-= with a numeric parameter specifying + the position on the list. +01-09-16 A bug in ksh93l caused command completion not to find aliases + and functions. Command listing from the edit mode was presented + in reverse order. This has been fixed. +01-09-13 Another bug in the optimization of for loops related to subshells + when traps were set has been fixed. +01-09-07 A change in ksh93l caused brace expansion to stop working + and this has been fixed. +01-09-04 A bug introduced in ksh93k in which an arithmetic statement + within a function that used name references did not follow the + reference has been fixed. +01-09-04 A bug introduced in ksh93l in which export -p did not prefix + each export with the word export has been fixed. +01-08-29 A bug in multibyte input which occurred when a partial multibyte + character was received has been fixed. +01-08-29 A bug introduced in ksh93l which could cause a core dump + when an assignment list containing PATH is specified inside + command substitution has been fixed. +01-08-09 Another bug in the optimization of for loops in ksh93l caused + errors in recursive functions using local variables that + contained for loops has been fixed. +01-07-27 A bug in which IFS would be unset after a command substitution + inside a here document has been fixed. +01-07-26 To conform to the POSIX standard, if you invoked ksh name, + and name does not contain a /, it will first try to run + one in the current directory whether it is executable or not + before doing a path search for an executable script. Earlier + versions first checked for an executable script using the + PATH variable. +01-07-23 A bug in which unset -f invoked in a subshell could unset a + function defined in the parent has been fixed. +01-07-16 A bug in the optimization of for loops in ksh93l caused + name references to be treated as invariants has been fixed. +01-07-09 A bug in which a discipline function applied to a local variable + could cause a shell exception has been fixed. Discipline + functions can only be specified for global variables. + +01-06-18 --- Release ksh93l --- +01-06-18 A bug in assigning integers larger than can be represented as + long integers to floating point variables has been fixed. +01-06-18 A bug in the handling of unsigned integers (typeset -ui) has + been fixed. +01-06-04 The evaluation of the PS1 prompt no longer effects the value + of the $? variable. +01-06-01 A small memory leak from subshells has been fixed. +01-05-22 A bug in which attributes for variables that did not have + values would be lost after a subshell has been fixed. +01-05-22 The %R format has been added to convert a shell pattern into + an extended regular expression. +01-05-22 The escape sequences \e, \cX, \C[.collating-element.], and + \x{hex} have been added to ASCII-C strings and to printf format + strings. +01-05-20 Patterns of the form {n}(pattern) and {m,n}(pattern) are now + recognized. The first form matches exactly n of pattern whereas, + the second form matches from m to n instances of pattern. +01-05-20 The shell allows *-(pattern), +-(pattern), ?-(pattern), + {m,n}-(pattern}, and @-(pattern) to cause the minimal + match of pattern to be selected whenever possible rather + than the maximal (greedy) match. +01-05-20 The character class [:word:] has been added to patterns. + The word class is the union of [:alnum:] and the character _. +01-05-20 Inside (...) pattern groups, the \ character is now treated + specially even when in an enclosing character class. The + sequences, \w, \d, \s are equivalent to the character classes + word, digit, and space respectively. The sequences \W, \D, + and \S are their complement sets. +01-05-20 The shell now recognizes pattern groups of the form + ~(options:pattern) where options or :pattern can be omitted. + Options use the letters + and - to enable and disable options + respectively. The option letters g (greedy), i (ignore case) + are used to cause maximal matching and to cause case + insensitive matching respectively. If :pattern is also + specified, these options are only in effect while this + pattern is being processed. Otherwise, these options remain + in effect until the end of the pattern group that they are contained + in or until another ~(...) is encountered. These pattern groups + are not counted with respect to group numbering. +01-05-14 When edit completion, expansion, or listing occurs in the + middle of a quoted string, the leading quote is ignored when + performing the completion, expansion, or listing. +01-05-14 A small memory leak from subshells has been fixed. +01-05-10 A bug in which open files were not restored after a subshell + that had used exec to replace a file has been fixed. +01-05-10 Redirection to a null file name now generates an error message. +01-05-09 The shell now rejects some invalid parameter substitutions that + were previously processed in undefined ways. +01-05-09 A bug in which the output of select was not flushed before the + read when input did not come from the terminal has been fixed. +01-05-08 A bug in which job ids would not be freed for interactive shells + when subshells ran built-ins in the background has been fixed. +01-05-08 The FPATH variable now requires an explicit . to cause the + current directory to be treated as a function directory. +01-05-08 A bug in read -n when echo mode was disabled has been fixed. +01-05-07 A bug in which function definitions could be listed as part + of the history has been fixed. +01-04-30 This release uses a new and often much faster pattern matcher than + earlier releases. +01-04-30 An optimizer now eliminates invariant parameter expansions from + for while and until loops. +01-04-30 The variable .sh.match is set after each pattern match (# % or /) + in a variable substitution. The variable .sh.match is an + indexed array with element 0 being the complete match. + The array is only valid until the next subsequent pattern + match or until the value of the variable changes which ever + comes first. +01-04-30 A self generating man page has been added to shcomp. Also, + shcomp now stops compiling when it finds an exit or exec + command and copies the remainder so that it can be used + for standard input. +01-04-30 The shcomp command was modified so that it can work in an + EBCIDIC environment and that binary scripts are portable + across environments. +01-04-30 A bug in the handling of a trailing : in PATH has been fixed. +01-04-30 A bug in which the builtin version of a command would get invoked + even though the full pathname for the command was specified + has been fixed. +01-04-30 A bug in which read would loose the last character when + reading the last line of a file that did not contain a new-line + character has been fixed. +01-04-23 A bug on some systems in which in vi mode the end of file + character and end of line character could be swapped has + been fixed. +01-04-23 A bug on some systems in which invoking a shell script that + did not have execute permission could set the exit value to + 127 rather than 126 has been fixed. +01-04-20 A bug in which read -n from a pipe would block if fewer than + n characters was received has been fixed. +01-04-09 A bug in which invalid patterns, for example, ) by itself, + was not treated as a string has been fixed so that if i=')', + then [[ $i == $i ]] is true. +01-04-09 The shell arithmetic now interprets C character constants. +01-04-09 A bug in which a non-zero return from a function defined + with the function reserved word did not trigger the ERR + trap or exit with set -e has been fixed. +01-04-02 A bug on some systems, in which characters above 127 were + not displayed correctly in vi or emacs edit mode has been fixed. +01-04-02 A bug on some systems, introduced in the 'k' point release, in + which the erase character in viraw mode was moving the cursor + to the left without erasing the character has been fixed. +01-04-02 On some systems the wcwith() function was returning a wrong + value for characters and caused characters to be displayed + incorrectly from the shell edit modes. A work around for + this problem has been added. +01-03-26 A bug in which valid scripts could produce syntax errors + when run with locales that considered characters such as "'" + to be space characters has been fixed. +01-03-20 A bug in which an syntax error in an arithmetic expression + entered interactively could cause the shell to go into + an infinite loop outputting the error message has been fixed. +01-03-10 ksh93 accepts -l as a synonym for -L in test on systems for + which /bin/test -l tests for symbolic links. +01-03-10 A bug in parsing scripts in which { and } are used in place of + in and esac in case statements embedded in compound commands + has been fixed. Use of { and } for in and esac is obsolete. +01-03-06 A bug in which an argument of the form foo=bar was not + being passed correctly to a traced function whose name + was foo has been fixed. +01-03-02 Using $(trap -p name) did not print the name of the current + trap setting for trap name. +01-02-26 Exported floating point variables gave incorrect results + when passing them to ksh88. This has been fixed. +01-02-25 A race condition in which a coprocess which completed too quickly + would not allow subsequent coprocesses to start has been fixed. +01-02-25 The 'g' format specifier is now handled by printf. It had + inadvertently been omitted. +01-02-20 The + was not being displayed during an execution trace + with the += assignment operator. +01-02-19 The error message which occurs when the interpreter name + defined on the #! line does not exist is more informative. +01-02-19 A bug in which $0 would not be set correctly when a + script with #! was invoked by full pathname from the + directory of the script has been fixed. +01-02-19 A shell script did not always pick up tty mode changes + made by external commands such as stty which could + effect the behavior of read. +01-02-19 The -u, -g, and -k unary tests did not give the correct + results when used with negation and this has been fixed. + +01-02-05 --- Release ksh93k+ --- +01-02-05 The sequence \ inside $'...' was not incrementing + the line count and this has been fixed. +01-02-05 Modified expansion of "${@-}" so that if no arguments are set + it results in null string rather than nothing. +01-02-02 memory leak problem with local variables in functions fixed. +01-01-25 allow arithmetic expressions with float%int and treat them + as ((int)float)%int rather than as an error. +01-01-19 read -n1 was not working and has been fixed. +01-01-17 ksh now handles the case in which a here document in command + substitution $() is terminated by the trailing ). Previously, + a new-line was needed at the end of the delimiter word. +01-01-02 A bug in which a KEYBD trap would cause a multi-line token + to be processed incorrectly has been fixed. +00-12-10 Arithmetic integer constants can now have L and U suffices. +00-12-10 A bug in the processing of arithmetic expressions with compound + variables when the -n option is on has been fixed. +00-12-08 A bug in M-f and M-b from emacs mode has been fixed. This + bug only occurs when ksh93 is compiled without MULTIBYTE enabled. +00-11-29 A bug in which jobs -p would yield 0 for background + jobs run in a script has been fixed. +00-11-21 A bug in integer arrays in which the number of elements is + incorrect when the ++ operator is applied to a non-existing + element has been fixed. For example, integer x; ((x[3]++)). +00-11-20 A timing bug in which the shell could reset the terminal + group to the wrong value in the case that the a new process + changes the terminal group during startup has been fixed. + +00-10-27 --- Release ksh93k --- +00-10-27 Using tab for completion now works only when applied + after a non-blank character at the end of the current line. + In other case a tab is inserted. +00-10-27 A bug in the emacs edit mode for ^X^E has been fixed. + The ^X^E sequence is supposed to invoke the full editor + on the current command. +00-10-18 A bug in which expansions of the form ${var//pattern/string} + did not work correctly when pattern was '/' or "/" has + been fixed. +00-10-18 The output format for indexed arrays in compound variables + has been modified so that it can be used as input. +00-10-18 Assignments with name references (typeset -n) will now + implicitly unreference an existing name reference. +00-10-17 A bug the += append operator when a single array element + is appended to a variable that is not an array has been fixed. +00-10-16 A bug in which the SIGCONT signal was being sent to + each process will kill -0 or kill -n 0 has been fixed. +00-10-12 The arithmetic evaluation portion has been rewritten to + perform a number of optimizations. +00-10-10 A bug in which name prefix matching ${!name.*} was not + checking name to see if it was a name reference has been fixed. +00-09-26 A bug in the multibyte version in which the width of for + non-printing characters was not correct has been fixed. +00-09-12 Made changes to get multibyte editing work on UWIN for windows +00-09-12 A bug in which multibyte characters would be displayed incorrectly + has been fixed. +00-08-08 Removed build dependency on iswprint() and iswalph(). +00-07-20 In some cases the read builtin would read more than a single + line from a pipe on standard input and therefore leave the seek + position in the wrong location. +00-07-05 If the directory / is on the path, a / will not be inserted + between the directory and the file name during path searching + to avoid searching // for systems that treat this specially. +00-06-26 A bug in which on rare occasions wait could return before all + jobs have completed has been fixed. +00-06-21 A bug in which backspace did not work correctly during the + R replace directive in vi-mode has been fixed. +00-06-12 Added variable name completion/expansion/listing to the set of + completions. Variable name completions begin with $ or "$ followed + by a letter. +00-05-09 --- Release ksh93j --- +00-05-09 Modified command substitution to avoid using /tmp files when + run on read-only file systems. +00-04-17 Modified printf to handle '%..Xc' and '%..Xs' options where X + is not an alpha character. Previous versions core dumped with this. +00-04-10 Changes to multibyte editing code were made to use standard + ISO C functions rather than methods devised before the standard. +00-04-09 Add %H options to printf to output strings with <"'&\t> properly + converted for use in HTML and XML documents. +00-04-07 Modified getopts builtin to handle \f...\f in usage string + by invoking specified function. +00-04-04 Added self generating man pages for bg, fc, fg, disown, jobs, + hist, let, ., and ulimit. +00-03-30 The append operator += has been added and can be used + for all assignments, strings, arrays, and compound variables. +00-03-30 Code was modified in several places to support automatic + generation of C locale dictionaries. +00-03-28 A bug in which the set and trap commands invoked with --name + type arguments would terminate the invoking script has + been fixed. +00-03-27 A bug in which the library path variable was not updated + correctly on some systems as described in the 'g' point + release has been fixed. +00-03-07 printf now returns a non-zero exit status when one of + its arguments cannot be converted to the given type. +00-03-05 The return value and error message for a command that + was found on the path but was not executable was set + incorrectly. +00-03-05 A prototype for ioctl() was removed from the vi edit mode. + +00-01-28 --- Release ksh93i --- +00-01-28 Most of the built-in commands and ksh itself are now + self documenting. Running command --man will produce + screen output. Running command --html produces the + man page in html format. +00-01-28 The getopts builtin can process command description + strings to produce man pages. +00-01-28 A bug in which a script could terminate when getopts + encountered an error when invoked inside a function + has been fixed. +00-01-28 When a symbolic link was specified as the name of + the script to invoke by name, the value of $0 was + set to the real file name rather than the link name + in some cases and this has been fixed. +00-01-28 A bug in which the precision given as an argument + to printf was not working has been fixed. + +99-03-31 --- Release ksh93h --- +99-03-31 The PATH search algorithm has been modified to look + for a file named .fpath in each bin directory and if + found, to search for functions in this directory if + it cannot find the command in that directory. +99-03-31 When performing pathname expansion, the shell checks + to see whether each directory it reads is case sensitive + or not, and performs the matching accordingly. +99-03-31 The %T format for printing formatted date/time. +99-03-31 The emacs and vi modes now handle arrow keys when + they use standard ANSI escape sequences. +99-03-31 The TAB key can be used for completion in emacs and viraw mode. +99-03-31 A bug in setting .sh.editchar during the KEYBD trap + for the MULTIBYTE option was fixed in release ksh93h. +99-03-31 A bug in shcomp for compilation of unary operators with [[...]] + has been fixed. +99-03-31 A bug in which the value of $? was changed when executing + a keyboard trap has been fixed. +99-03-31 The handling of SIGCHLD has been changed so that the + trap is not triggered while executing trap commands + to avoid recursive trap calls. +99-03-31 A bug in which a local variable in a function declared readonly + would generated an error when the function went out of + scope has been fixed. +99-03-31 A bug in which \ entered from the keyboard + with the KEYBD trap enabled has been fixed. +99-03-31 The error message for a misplaced ((, for example print ((3), + was often garbled and has been fixed. +99-03-31 A bug in the KEYBD trap in which escape sequences of the form + [#~ were not being handled as a unit has been fixed. +99-03-31 A bug in which ksh would consider expressions like [[ (a) ]] + as syntax errors has been fixed. +99-03-31 A function defined as foo() without a function body + was not reported as a syntax error. +99-03-31 A bug in which ksh could run out of file descriptors when + a stream was repeatedly opened with exec and read from + has been fixed. + +98-04-30 --- Release ksh93g --- +98-04-30 The pipefail option has been added. With pipefail + enabled, a pipeline will not complete until all + commands are complete, and the return value will + be that of the last command to fail, or zero if + all complete successfully. +98-04-30 The name-value pair library uses the cdt library rather + than the hash library. This change should be transparent + to applications. +98-04-30 On the U/WIN version for Window 95 and Windows NT, + when a directory beginning with a letter followed by + a colon is given to cd, it is assumed to be an absolute + directory +98-04-30 When an executable is found on a given path, + the appropriate library path variable is prepended + with a corresponding library directory. +98-04-30 A bug in which a name reference could be created to + itself and later cause the shell to get into an infinite + loop has been fixed. +98-04-30 A bug in shcomp relating to compound variables was fixed. +98-04-30 A bug introduced in ksh93e in which leading 0's in -Z + fields caused the value to be treated as octal for arithmetic + evaluation has been fixed. +98-04-30 A bug when a name reference with a shorter name than + the variable it references was the subject of a compound + assignment has been fixed. +98-04-30 A bug which in which assignment to array variables in + a subshell could effect the parent shell has been + fixed. +98-04-30 read name?prompt was putting a 0 byte at the end of the + prompt on standard error. +98-04-30 A bug in [[ string1 > string2 ]] when ksh was run with -x + has been fixed. +98-04-30 A bug in which the escape character was not processed + correctly inside {...} when brace expansion is enabled + has been fixed, for example {\$foo}. +98-04-30 A bug in line continuation in here-documents has been + fixed. +98-04-30 The default base when not specified with typeset -i is + 10 in accordance with the documentation. Previously, + the value was determined by the first assignment. +98-04-30 A parsing bug in which a # preceded alphanumeric + characters inside a command substitution caused + a syntax error to be reported has been fixed. +98-04-30 A bug in which a decimal constant represented as 10#ddd + where ddd was more than five digits generated a syntax + error has been fixed. +98-04-30 A bug in here document expansion in which ${...} expansions + were split across buffer boundaries has been fixed. +98-04-30 The sh_fun() function now takes third argument which + is an argument list for the invoked discipline function + or built-in. +98-04-30 A callback function can be installed which will give + notification of file duplications and file closes. +98-04-30 When ksh is compiled on systems that do not use fork() + current option settings where not propagated to sub-shells. + +97-06-30 --- Release ksh93f --- +97-06-30 Hostnames in addition to host addresses can be given in + /dev/tcp/host/port virtual file names. +97-06-30 File name completion and expansion now quotes special + characters in file names from both emacs and vi edit modes. +97-06-30 An empty for list behave like a for list with null expansions. + It produces a warning message with sh -n. +97-06-30 The code has been modified to work with EBCDIC as well as ASCII. +97-06-30 A bug which would cause the secondary prompt to be + displayed when a user entered a literal carriage + return has been fixed. +97-06-30 A bug which caused ksh read -s name to core dump was + fixed. +97-06-30 A bug with the expansion of \} and \] inside double + quoted strings that also contained variable expansions + has been fixed +97-06-30 Changes in the ksh93e point release caused autoload + functions invoked from within command substitution + to fail. This has been fixed. +97-06-30 A bug in the processing of here-documents that could + prevent variable substitution to occur after $(...) command + substitution for long here documents has been fixed. +97-06-30 A bug caused by a race condition that could cause SIGTERM + to be ignored by a child process has been fixed. +97-06-30 A bug which prevented the startup of a coprocess immediately + after killing a running coprocess has been fixed. +97-06-30 ulimit foobar, where foobar is not an arithmetic + expression, now gives an error message as it did with ksh88 + instead of setting the file size limit to 0. +97-06-30 A bug which could cause an interactive shell to terminate when + the last process of a pipeline was a POSIX function was fixed. +97-06-30 A bug which could cause command substitution of a shell script + to core dump has been fixed. +97-06-30 A security hole was fixed in suid_exec. +97-06-30 Arithmetic functions such as pow() that take more than + one argument, did not work if arguments other than the + first contained parenthesized sub-expression. +97-06-30 The error message from a script containing an incomplete + arithmetic expression has been corrected. +97-06-30 A bug which caused a core dump on some machines when + the value of a name reference contained a positional + parameter and the name reference was not defined inside + a function has been fixed. +97-06-30 Arithmetic expressions now correctly handle hexadecimal + constants. +97-06-30 A bug in which integer variables could be expanded + with a leading 10# when declared with typeset -i + multiple times has been corrected. +97-06-30 A bug in which IFS wasn't correctly restored when + set within command substitution has been fixed. +97-06-30 The _ character is now considered as part of a word + with the M-f and M-b emacs directives as it was in ksh88. +97-06-30 A bug in brace pattern expansions that caused expressions + such as {foo\,bar,bam} to expand incorrectly have been fixed. + + +96-07-31 --- Release ksh93e --- +96-07-31 The math functions, atan2, hypot, fmod, and pow were added. +96-07-31 When a shared library is loaded, if the function lib_init() + is defined in the library, it is invoked the first time that + the library is loaded with builtin -f library. +96-07-31 The k-shell information abstraction database option, KIA, + has been revamped. +96-07-31 Empty command substitutions of the form $() now work. + whence -v foo now gives the correct result after calling + builtin -d foo. +96-07-31 A bug in right to left arithmetic assignment for which + the arithmetic expression (( y = x = 1.5 )) did not + yield 1 for y when x was declared typeset -i was fixed. +96-07-31 printf has been fixed to handle format containing \0 + and/or \0145 correctly. In addition, characters following + %b in the format string are no longer displayed when + the operand contains \c. +96-07-31 A bug in printf that could cause the %E format to + produce unnormalized results has been fixed. +96-07-31 A bug which causes some arithmetic expressions to be + incorrectly evaluated as integer expressions rather + that floating point has been fixed. +96-07-31 Functions defined inside a subshell no longer remain + defined when the subshell completes. +96-07-31 The error message from sh -c ';echo foo' has been + corrected. +96-07-31 The format for umask -S has been changed to agree + with the specification in the POSIX standard. +96-07-31 A bug that caused side effects in subscript evaluation + when tracing was enabled for subscripts using ++ or -- + has been fixed. +96-07-31 To conform to the Posix standard getopts has been changed + so that the option char is set to ? when it returns with + a non-zero exit status. +96-07-31 The handling of \} inside ${name...} has been fixed so + that the \ quotes the }. +96-07-31 A bug that caused the read builtin to resume execution + after processing a trap has been fixed. +96-07-31 [[ -s file ]] has been fixed so that if file is open + by ksh, it is flushed first. +96-07-31 In some cases attributes and sizes for non exported + variables weren't being reset before running a script. +96-07-31 The value of TMOUT was affected by changes make to + it in a subshell. +96-07-31 The jobs command did not reflect changes make by + sending the CONT signal to a command. +96-07-31 The error message for ksh -o unknown was incorrect. +96-07-31 Functions invoked as name=value name, did not use + values from the calling scope when evaluating value. +96-07-31 A bug in which the shell would reexecute previously + executed code when a shell script or coprocess was + run in the background has been fixed. +96-07-31 A bug in which an empty here-document would leave + a file descriptor open has been fixed. +96-07-31 A bug in which $(set -A array ...) would leave a + side effect has been fixed. +96-07-31 A discipline function for a global variable defined + within a function defined with the function keyword, + incorrectly created a local variable of the same name + and applied the discipline to it. + +95-08-28 --- Release ksh93d --- +95-08-28 The \ character was not handled correctly in replacement + patterns with ${x/pattern/replace}. +95-08-28 A bug with read in which the line did not end with + a new-line has been fixed. +95-08-28 A bug in file name generation which sometimes + appended a . for filenames that ended in / has + been fixed. +95-08-28 If a process is waited for after a status has + been returned by a previous wait, wait now + returns 127. +95-08-28 A bug with hist (fc) -e which prevented a command + to re-executed after it had been edited has been fixed. +95-08-28 A bug which prevented quoting from removing the meaning + of unary test operators has been fixed. +95-08-28 A bug with typeahead and KEYBOARD traps with the + MULTIBYTE option set has been fixed. +95-08-28 Builtin functions can take a third argument which is + a void*. +95-08-28 The nv_scan() function can restrict the scope of a walk + to the top scope. + +95-04-31 --- Release ksh93c --- +95-04-31 The expansion of "$@" was incorrect when $1 was the null + string. +95-04-31 A bug which could incorrectly report a syntax error in + a backquoted expression when a $ was preceded by \\ + has been fixed. +95-04-31 A bug which prevented the shell from exiting after + reporting an error when failing to open a script + has been fixed. +95-04-31 A bug that could lead to memory corruption when a + large here document that required parameter or command + substitution was expanded has been fixed. +95-04-31 A bug that could cause a core dump on some systems + after ksh detected an error when reading a function + has been fixed. +95-04-31 A bug which could cause a coprocess to hang when + reading from a process that has terminated has been fixed. +95-04-31 A bug which caused a script to terminate when set -e + was on and the first command of and && or || list + failed has been fixed. +95-04-31 A bug with here documents inside $(...) when the delimiter + word is an identifier has been fixed. +95-04-31 A bug which caused $0 to display the wrong value when + a script was invoked as an argument to the . command + and the eval command has been fixed. +95-04-31 A bug that could cause the built-in sleep to hang + has been fixed. +95-04-31 A bug introduces in 12/28/93b which caused the backslash + to be removed when it was followed by digit inside double + quotes in some instances has been fixed. +95-04-31 A bug which could cause a core dump if ksh was invoked with + standard input closed has been fixed. +95-04-31 A bug which could cause a core dump if typeset -A was + specified for an existing variable has been fixed. +95-04-31 Variables that were unset but had attributes such as readonly + and export were not listed with readonly, export and typeset. +95-04-31 Several problems with signals have been fixed. +95-04-31 A bug which prevented ulimit -t from working has been fixed. + Also, a bug in which failed ulimits could cause a core dump + has also been fixed. +95-04-31 A bug in expansion of the form ${name/#pattern/string} and + ${name/%pattern/string} has been fixed. +95-04-31 A bug which caused read -r on a line that contained only + blanks to get a non-null value has been fixed. +95-04-31 A bug introduced in the 'a' point release in which + ${x='\\'} expanded to \ when x was unset has been fixed. +95-04-31 A bug which prevented a trap on EXIT from being executed + when the last command in a script was a function invocation + has been fixed. +95-04-31 A bug which caused an interactive shell ignore input when + standard error was redirected to a file with exec, + and then restored with exec 2>&1 has been fixed. +95-04-31 An interactive shell turns on monitor mode even when + standard error has been redirected to a file. +95-04-31 A bug which could cause standard input to be incorrectly + positioned for the last command of a script has been fixed. +95-04-31 A bug in the edit modes which allowed walking back in + the history file for more than HISTSIZE commands has + beed fixed. +95-04-31 A bug which could cause a core dump if variable TMPDIR was + changed between two command substitutions has been fixed. +95-04-31. A bug which prevented a trap on EXIT from being cleared + has been fixed. +95-04-31 A bug fixed for the v directive in vi MULTIBYTE has been + fixed. +95-04-31 Code to for IFS handling of multibyte characters has + been added. +95-04-31 The displaying of multibyte strings in export, readonly, + typeset, and execution traces has been fixed. +95-04-31 Variables inside functions are now statically scoped. + The previous behavior was never documented. +95-04-31 Variables inside functions are now statically scoped. + The previous behavior was never documented. +95-04-31 A few changes have been made to the name-value library + that affect built-ins that use disciplines. The + changes allow disciplines to be shared by variables + and should make it possible to add new disciplines + without recompilation. +95-04-31 The name-value library interface has undergone significant + change for this revision. See the new nval.3 man page. + +94-12-31 --- Release ksh93b --- +94-12-31 Variables inside functions are now statically scoped. + The previous behavior was never documented. +94-12-31 If IFS contains two consecutive identical characters belonging + to the [:space:] class, then this character is treated as + a non-space delimiter so that each instance will delimit + a field. For example, IFS=$'\t\t' will cause two consecutive + tabs to delimit a null field. +94-12-31 The getopts command has a -a name option that specifies a + name that will be used for usage messages. +94-12-31 A bug which caused unset RANDOM to dump core has been + fixed. +94-12-31 A bug which prevented return for terminating a profile + or ENV file has been fixed. +94-12-31 A bug which prevented standard input from being + directed to /dev/null for background jobs when + monitor mode was turned off has been fixed. +94-12-31 Statements of the form typeset -options var[expr]=value + did not perform substitutions on expr as expected. +94-12-31 A bug which prevented the shell from sending a HUP + signal to some background jobs that were not disowned + has been fixed. +94-12-31 A bug which allowed a script to trap signals that are + ignored at the time that the shell was invoked by exec + has been fixed. +94-12-31 A bug which could cause a core dump when a discipline + function was unset within a discipline was fixed. +94-12-31 The typeset builtin now accepts a first argument of + + or - for compatibility with ksh88. +94-12-31 For compatibility with ksh88, the results of expansions + of command arguments will treat the extended character + match characters ()|& as ordinary characters. +94-12-31 A bug which caused read to fail on a file that was + open for read/write with <> when the first operation + was print or printf has been fixed. +94-12-31 When a job is suspended, it is put on the top of + the job list as required by the POSIX standard. +94-12-31 The value of OPTARG when an option that required + an argument but didn't have one was incorrect in the + case the the option string began with a :. +94-12-31 A bug which caused the terminal to get into a bad + state with some KEYBD traps in vi-mode has been fixed. +94-12-31 A bug which caused an invalid trap to cause a script + to terminate, rather than just return an error, has + been fixed. +94-12-31 Backreferencing sub-expressions in patterns and replacement + strings now works. +94-12-31 A bug in chmod which caused the -R option to fail has + been fixed. +94-12-31 More signal names have been added for Solaris + +94-06-30 --- Release ksh93a --- +94-06-30 An expansion bug which causes portions of a word after + a $((...)) expansion that contains a nested $var expansion + to be lost has been fixed. +94-06-30 A bug that caused a core dump when a script that did not + have PWD set and did a cd inside command substitution + has been fixed. +94-06-30 A bug which caused a core dump on some machines when + the LANG variable was assigned to has been fixed. +94-06-30 A bug which incorrectly handled set disciplines that + performed arithmetic evaluation when the discipline + was called from the arithmetic evaluator has been fixed. +94-06-30 A bug caused by an EXIT trap inside a function that + was executed in a subshell was fixed. +94-06-30 If foo is a function, and not a program, then command foo + now reports that foo isn't found rather than invoking foo. +94-06-30 The previous version incorrectly listed -A as an + invocation option. The -A option is only for set. +94-06-30 A bug was fixed which caused ksh to loop when execution trace + was enabled and the PS4 prompt required command substitution. +94-06-30 A bug which could cause the job control switch character + to be disabled when a script that enabled monitor mode + terminated was fixed. +94-06-30 A bug in the macro expansion global replacement operator //, + when the pattern began with a [ or +( has been fixed. +94-06-30 A bug which prevented ~ expansion from occurring when + it was terminated with a colon inside an assignment + has been fixed. +94-06-30 A bug in the dot command which prevented autoload functions + from working has been fixed. +94-06-30 A bug which caused a variable to be unset if the + its value were expanded inside a set discipline has + been fixed. +94-06-30 Whence -a now longer reports that a defined function + is undefined. +94-06-30 A bug on some systems in which $0 would be incorrect + in scripts invoked by name has been fixed. +94-06-30 Here documents with an empty body now work. +94-06-30 A bug which disabled argument passing and resetting + of options for a script invoked by name inside a + function has been fixed. +94-06-30 A bug in which an EXIT trap set the caller of a function + would be executed if a command called inside a function + was not found has been fixed. +94-06-30 A bug which allowed a script to trap signals that are + ignored at the time that the shell was invoked has + been fixed. +94-06-30 A bug which caused 2<&1- when applied to a shell built-in + to leave standard input closed has been fixed. +94-06-30 A bug which caused the shell to incorrectly parse + $() command substitutions with nested case statements + has been fixed. + Index: src/lib/libshell/common/include/builtins.h =================================================================== --- src/lib/libshell/common/include/builtins.h (revision 0) +++ src/lib/libshell/common/include/builtins.h (revision 740) @@ -0,0 +1,200 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#ifndef SYSDECLARE + +#include +#include "FEATURE/options" +#include "FEATURE/dynamic" +#include "shtable.h" + +#define SYSLOGIN (sh.bltin_cmds) +#define SYSEXEC (sh.bltin_cmds+1) +#define SYSSET (sh.bltin_cmds+2) +#define SYSTRUE (sh.bltin_cmds+4) +#define SYSCOMMAND (sh.bltin_cmds+5) +#define SYSCD (sh.bltin_cmds+6) +#define SYSBREAK (sh.bltin_cmds+7) +#define SYSCONT (sh.bltin_cmds+8) +#define SYSTYPESET (sh.bltin_cmds+9) +#define SYSTEST (sh.bltin_cmds+10) +#define SYSBRACKET (sh.bltin_cmds+11) +#define SYSLET (sh.bltin_cmds+12) +#define SYSEXPORT (sh.bltin_cmds+13) +#if SHOPT_BASH +# define SYSLOCAL (sh.bltin_cmds+14) +#else +# define SYSLOCAL 0 +#endif + +/* entry point for shell special builtins */ + +#if _BLD_shell && defined(__EXPORT__) +# define extern __EXPORT__ +#endif + +extern int b_alias(int, char*[],void*); +extern int b_break(int, char*[],void*); +extern int b_dot_cmd(int, char*[],void*); +extern int b_exec(int, char*[],void*); +extern int b_eval(int, char*[],void*); +extern int b_return(int, char*[],void*); +extern int B_login(int, char*[],void*); +extern int b_true(int, char*[],void*); +extern int b_false(int, char*[],void*); +extern int b_readonly(int, char*[],void*); +extern int b_set(int, char*[],void*); +extern int b_shift(int, char*[],void*); +extern int b_trap(int, char*[],void*); +extern int b_typeset(int, char*[],void*); +extern int b_unset(int, char*[],void*); +extern int b_unalias(int, char*[],void*); + +/* The following are for job control */ +#if defined(SIGCLD) || defined(SIGCHLD) + extern int b_jobs(int, char*[],void*); + extern int b_kill(int, char*[],void*); +# ifdef SIGTSTP + extern int b_bg(int, char*[],void*); +# endif /* SIGTSTP */ +#endif + +/* The following utilities are built-in because of side-effects */ +extern int b_builtin(int, char*[],void*); +extern int b_cd(int, char*[],void*); +extern int b_command(int, char*[],void*); +extern int b_getopts(int, char*[],void*); +extern int b_hist(int, char*[],void*); +extern int b_let(int, char*[],void*); +extern int b_read(int, char*[],void*); +extern int b_ulimit(int, char*[],void*); +extern int b_umask(int, char*[],void*); +#ifdef _cmd_universe + extern int b_universe(int, char*[],void*); +#endif /* _cmd_universe */ +#if SHOPT_FS_3D + extern int b_vpath(int, char*[],void*); +#endif /* SHOPT_FS_3D */ +extern int b_wait(int, char*[],void*); +extern int b_whence(int, char*[],void*); + +extern int b_alarm(int, char*[],void*); +extern int b_print(int, char*[],void*); +extern int b_printf(int, char*[],void*); +extern int b_pwd(int, char*[],void*); +extern int b_sleep(int, char*[],void*); +extern int b_test(int, char*[],void*); +#if !SHOPT_ECHOPRINT + extern int B_echo(int, char*[],void*); +#endif /* SHOPT_ECHOPRINT */ + +#undef extern + +extern const char e_alrm1[]; +extern const char e_alrm2[]; +extern const char e_badfun[]; +extern const char e_baddisc[]; +extern const char e_nofork[]; +extern const char e_nosignal[]; +extern const char e_nolabels[]; +extern const char e_notimp[]; +extern const char e_nosupport[]; +extern const char e_badbase[]; +extern const char e_overlimit[]; + +extern const char e_eneedsarg[]; +extern const char e_toodeep[]; +extern const char e_badname[]; +extern const char e_badwrite[]; +extern const char e_badsyntax[]; +#ifdef _cmd_universe + extern const char e_nouniverse[]; +#endif /* _cmd_universe */ +extern const char e_histopen[]; +extern const char e_condition[]; +extern const char e_badrange[]; +extern const char e_numeric[]; +extern const char e_trap[]; +extern const char e_direct[]; +extern const char e_defedit[]; +extern const char e_cneedsarg[]; +extern const char e_defined[]; +#if SHOPT_FS_3D + extern const char e_cantset[]; + extern const char e_cantget[]; + extern const char e_mapping[]; + extern const char e_versions[]; +#endif /* SHOPT_FS_3D */ + +/* for option parsing */ +extern const char sh_set[]; +extern const char sh_optalarm[]; +extern const char sh_optalias[]; +extern const char sh_optbreak[]; +extern const char sh_optbuiltin[]; +extern const char sh_optcd[]; +extern const char sh_optcommand[]; +extern const char sh_optcont[]; +extern const char sh_optdot[]; +#ifndef ECHOPRINT + extern const char sh_optecho[]; +#endif /* !ECHOPRINT */ +extern const char sh_opteval[]; +extern const char sh_optexec[]; +extern const char sh_optexit[]; +extern const char sh_optexport[]; +extern const char sh_optgetopts[]; +extern const char sh_optbg[]; +extern const char sh_optdisown[]; +extern const char sh_optfg[]; +extern const char sh_opthist[]; +extern const char sh_optjobs[]; +extern const char sh_optkill[]; +extern const char sh_optksh[]; +extern const char sh_optlet[]; +extern const char sh_optprint[]; +extern const char sh_optprintf[]; +extern const char sh_optpwd[]; +extern const char sh_optread[]; +extern const char sh_optreadonly[]; +extern const char sh_optreturn[]; +extern const char sh_optset[]; +extern const char sh_optshift[]; +extern const char sh_optsleep[]; +extern const char sh_opttrap[]; +extern const char sh_opttypeset[]; +extern const char sh_optulimit[]; +extern const char sh_optumask[]; +extern const char sh_optunalias[]; +extern const char sh_optwait[]; +#ifdef _cmd_universe + extern const char sh_optuniverse[]; +#endif /* _cmd_universe */ +extern const char sh_optunset[]; +#if SHOPT_FS_3D + extern const char sh_optvpath[]; + extern const char sh_optvmap[]; +#endif /* SHOPT_FS_3D */ +extern const char sh_optwhence[]; +#endif /* SYSDECLARE */ + +extern const char e_dict[]; + Index: src/lib/libshell/common/include/path.h =================================================================== --- src/lib/libshell/common/include/path.h (revision 0) +++ src/lib/libshell/common/include/path.h (revision 740) @@ -0,0 +1,140 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef PATH_OFFSET + +/* + * UNIX shell path handling interface + * Written by David Korn + * These are the definitions for the lexical analyzer + */ + +#include "FEATURE/options" +#include + +#if !defined(SHOPT_SPAWN) +# if _UWIN || _use_spawnveg || !_lib_fork +# define SHOPT_SPAWN 1 +# endif +#endif /* !SHOPT_SPAWN */ + +#define PATH_PATH 0001 +#define PATH_FPATH 0002 +#define PATH_CDPATH 0004 +#define PATH_BFPATH 0010 +#define PATH_SKIP 0020 +#define PATH_BUILTIN_LIB 0040 +#define PATH_STD_DIR 0100 /* directory is on $(getconf PATH) */ + +#define PATH_OFFSET 2 /* path offset for path_join */ +#define MAXDEPTH (sizeof(char*)==2?64:4096) /* maximum recursion depth*/ + +/* + * path component structure for path searching + */ +typedef struct pathcomp +{ + struct pathcomp *next; + int refcount; + dev_t dev; + ino_t ino; + char *name; + char *lib; + char *blib; + void *bltin_lib; + unsigned short len; + unsigned short flags; + Shell_t *shp; +} Pathcomp_t; + +#ifndef ARG_RAW + struct argnod; +#endif /* !ARG_RAW */ + +/* pathname handling routines */ +extern void path_newdir(Pathcomp_t*); +extern Pathcomp_t *path_dirfind(Pathcomp_t*,const char*,int); +extern Pathcomp_t *path_unsetfpath(Pathcomp_t*); +extern Pathcomp_t *path_addpath(Pathcomp_t*,const char*,int); +extern Pathcomp_t *path_dup(Pathcomp_t*); +extern void path_delete(Pathcomp_t*); +extern void path_alias(Namval_t*,Pathcomp_t*); +extern Pathcomp_t *path_absolute(const char*, Pathcomp_t*); +extern char *path_basename(const char*); +extern char *path_fullname(const char*); +extern int path_expand(const char*, struct argnod**); +extern void path_exec(const char*,char*[],struct argnod*); +extern pid_t path_spawn(const char*,char*[],char*[],Pathcomp_t*,int); +#if defined(__EXPORT__) && defined(_BLD_DLL) && defined(_BLD_shell) +# define extern __EXPORT__ +#endif +extern int path_open(const char*,Pathcomp_t*); +extern Pathcomp_t *path_get(const char*); +#undef extern +extern char *path_pwd(int); +extern Pathcomp_t *path_nextcomp(Pathcomp_t*,const char*,Pathcomp_t*); +extern int path_search(const char*,Pathcomp_t*,int); +extern char *path_relative(const char*); +extern int path_complete(const char*, const char*,struct argnod**); +#if SHOPT_BRACEPAT + extern int path_generate(struct argnod*,struct argnod**); +#endif /* SHOPT_BRACEPAT */ + +/* constant strings needed for whence */ +extern const char e_timeformat[]; +extern const char e_badtformat[]; +extern const char e_dot[]; +extern const char e_pfsh[]; +extern const char e_pwd[]; +extern const char e_logout[]; +extern const char e_alphanum[]; +extern const char e_mailmsg[]; +extern const char e_suidprofile[]; +extern const char e_sysprofile[]; +extern const char e_traceprompt[]; +extern const char e_crondir[]; +#if SHOPT_SUID_EXEC + extern const char e_suidexec[]; +#endif /* SHOPT_SUID_EXEC */ +extern const char is_alias[]; +extern const char is_builtin[]; +extern const char is_builtver[]; +extern const char is_reserved[]; +extern const char is_talias[]; +extern const char is_xalias[]; +extern const char is_function[]; +extern const char is_ufunction[]; +#ifdef SHELLMAGIC + extern const char e_prohibited[]; +#endif /* SHELLMAGIC */ + +#if SHOPT_ACCT +# include "FEATURE/acct" +# ifdef _sys_acct + extern void sh_accinit(void); + extern void sh_accbegin(const char*); + extern void sh_accend(void); + extern void sh_accsusp(void); +# else +# undef SHOPT_ACCT +# endif /* _sys_acct */ +#endif /* SHOPT_ACCT */ + +#endif /*! PATH_OFFSET */ Index: src/lib/libshell/common/include/jobs.h =================================================================== --- src/lib/libshell/common/include/jobs.h (revision 0) +++ src/lib/libshell/common/include/jobs.h (revision 740) @@ -0,0 +1,159 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef JOB_NFLAG +/* + * Interface to job control for shell + * written by David Korn + * + */ + +#define JOBTTY 2 + +#include +#include +#ifndef SIGINT +# include +#endif /* !SIGINT */ +#include "FEATURE/options" + +#undef JOBS +#if defined(SIGCLD) && !defined(SIGCHLD) +# define SIGCHLD SIGCLD +#endif +#ifdef SIGCHLD +# define JOBS 1 +# include "terminal.h" +# ifdef FIOLOOKLD + /* Ninth edition */ + extern int tty_ld, ntty_ld; +# define OTTYDISC tty_ld +# define NTTYDISC ntty_ld +# endif /* FIOLOOKLD */ +#else +# undef SIGTSTP +# undef SH_MONITOR +# define SH_MONITOR 0 +# define job_set(x) +# define job_reset(x) +#endif + +struct process +{ + struct process *p_nxtjob; /* next job structure */ + struct process *p_nxtproc; /* next process in current job */ + pid_t p_pid; /* process id */ + pid_t p_pgrp; /* process group */ + pid_t p_fgrp; /* process group when stopped */ + short p_job; /* job number of process */ + unsigned short p_exit; /* exit value or signal number */ + unsigned short p_flag; /* flags - see below */ + int p_env; /* subshell environment number */ +#ifdef JOBS + off_t p_name; /* history file offset for command */ + struct termios p_stty; /* terminal state for job */ +#endif /* JOBS */ +}; + +struct jobs +{ + struct process *pwlist; /* head of process list */ + pid_t curpgid; /* current process gid id */ + pid_t parent; /* set by fork() */ + pid_t mypid; /* process id of shell */ + pid_t mypgid; /* process group id of shell */ + pid_t mytgid; /* terminal group id of shell */ + unsigned int in_critical; /* >0 => in critical region */ + int savesig; /* active signal */ + int numpost; /* number of posted jobs */ + short fd; /* tty descriptor number */ +#ifdef JOBS + int suspend; /* suspend character */ + int linedisc; /* line dicipline */ +#endif /* JOBS */ + char jobcontrol; /* turned on for real job control */ + char waitsafe; /* wait will not block */ + char waitall; /* wait for all jobs in pipe */ + char toclear; /* job table needs clearing */ + unsigned char *freejobs; /* free jobs numbers */ +}; + +/* flags for joblist */ +#define JOB_LFLAG 1 +#define JOB_NFLAG 2 +#define JOB_PFLAG 4 +#define JOB_NLFLAG 8 + +extern struct jobs job; + +#ifdef JOBS + +#define job_lock() (job.in_critical++) +#define job_unlock() do{if(!--job.in_critical&&job.savesig)job_reap(job.savesig);}while(0) + +extern const char e_jobusage[]; +extern const char e_done[]; +extern const char e_running[]; +extern const char e_coredump[]; +extern const char e_no_proc[]; +extern const char e_no_job[]; +extern const char e_jobsrunning[]; +extern const char e_nlspace[]; +extern const char e_access[]; +extern const char e_terminate[]; +extern const char e_no_jctl[]; +extern const char e_signo[]; +#ifdef SIGTSTP + extern const char e_no_start[]; +#endif /* SIGTSTP */ +#ifdef NTTYDISC + extern const char e_newtty[]; + extern const char e_oldtty[]; +#endif /* NTTYDISC */ +#endif /* JOBS */ + +/* + * The following are defined in jobs.c + */ + +extern void job_clear(void); +extern void job_bwait(char**); +extern int job_walk(Sfio_t*,int(*)(struct process*,int),int,char*[]); +extern int job_kill(struct process*,int); +extern void job_wait(pid_t); +extern int job_post(pid_t,pid_t); +extern void *job_subsave(void); +extern void job_subrestore(void*); +#ifdef JOBS + extern void job_init(int); + extern int job_close(void); + extern int job_list(struct process*,int); + extern int job_terminate(struct process*,int); + extern int job_switch(struct process*,int); + extern void job_fork(pid_t); + extern int job_reap(int); +#else +# define job_init(flag) +# define job_close() (0) +# define job_fork(p) +#endif /* JOBS */ + + +#endif /* !JOB_NFLAG */ Index: src/lib/libshell/common/include/nval.h =================================================================== --- src/lib/libshell/common/include/nval.h (revision 0) +++ src/lib/libshell/common/include/nval.h (revision 740) @@ -0,0 +1,305 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef NV_DEFAULT +/* + * David Korn + * AT&T Labs + * + * Interface definitions of structures for name-value pairs + * These structures are used for named variables, functions and aliases + * + */ + + +#include +#include + +/* for compatibility with old hash library */ +#define Hashtab_t Dt_t +#define HASH_BUCKET 1 +#define HASH_NOSCOPE 2 +#define HASH_SCOPE 4 +#define hashscope(x) dtvnext(x) + +typedef struct Namval Namval_t; +typedef struct Namfun Namfun_t; +typedef struct Namdisc Namdisc_t; +typedef struct Nambfun Nambfun_t; +typedef struct Namarray Namarr_t; +typedef struct Nambltin Nambltin_t; +typedef struct Namtype Namtype_t; + +/* + * This defines the template for nodes that have their own assignment + * and or lookup functions + */ +struct Namdisc +{ + size_t dsize; + void (*putval)(Namval_t*, const char*, int, Namfun_t*); + char *(*getval)(Namval_t*, Namfun_t*); + Sfdouble_t (*getnum)(Namval_t*, Namfun_t*); + char *(*setdisc)(Namval_t*, const char*, Namval_t*, Namfun_t*); + Namval_t *(*createf)(Namval_t*, const char*, int, Namfun_t*); + Namfun_t *(*clonef)(Namval_t*, Namval_t*, int, Namfun_t*); + char *(*namef)(Namval_t*, Namfun_t*); + Namval_t *(*nextf)(Namval_t*, Dt_t*, Namfun_t*); + Namval_t *(*typef)(Namval_t*, Namfun_t*); + int (*readf)(Namval_t*, Sfio_t*, int, Namfun_t*); +}; + +struct Namfun +{ + const Namdisc_t *disc; + char nofree; + char funs; + unsigned short dsize; + Namfun_t *next; + char *last; + Namval_t *type; +}; + +struct Nambfun +{ + Namfun_t fun; + int num; + const char **bnames; + Namval_t *bltins[1]; +}; + +/* This is an array template header */ +struct Namarray +{ + Namfun_t hdr; + long nelem; /* number of elements */ + void *(*fun)(Namval_t*,const char*,int); /* associative arrays */ + Namval_t *parent; /* for multi-dimensional */ +}; + +/* Passed as third argument to a builtin when NV_BLTINOPT is set on node */ +struct Nambltin +{ + void *shp; + Namval_t *np; + void *ptr; + void *data; + int flags; +}; + +struct Namtype +{ + void *shp; + Namval_t *np; + const char *optstring; + void *optinfof; +}; + +/* attributes of name-value node attribute flags */ + +#define NV_DEFAULT 0 +/* This defines the attributes for an attributed name-value pair node */ +struct Namval +{ + Dtlink_t nvlink; /* space for cdt links */ + char *nvname; /* pointer to name of the node */ + unsigned short nvflag; /* attributes */ + unsigned short nvsize; /* size or base */ +#ifdef _NV_PRIVATE + _NV_PRIVATE +#else + Namfun_t *nvfun; + char *nvalue; + char *nvprivate; +#endif /* _NV_PRIVATE */ +}; + +#define NV_CLASS ".sh.type" +#define NV_MINSZ (sizeof(struct Namval)-sizeof(Dtlink_t)-sizeof(char*)) +#define nv_namptr(p,n) ((Namval_t*)((char*)(p)+(n)*NV_MINSZ-sizeof(Dtlink_t))) + +/* The following attributes are for internal use */ +#define NV_NOFREE 0x200 /* don't free the space when releasing value */ +#define NV_ARRAY 0x400 /* node is an array */ +#define NV_REF 0x4000 /* reference bit */ +#define NV_TABLE 0x800 /* node is a dictionary table */ +#define NV_IMPORT 0x1000 /* value imported from environment */ +#define NV_MINIMAL NV_IMPORT /* node does not contain all fields */ + +#define NV_INTEGER 0x2 /* integer attribute */ +/* The following attributes are valid only when NV_INTEGER is off */ +#define NV_LTOU 0x4 /* convert to uppercase */ +#define NV_UTOL 0x8 /* convert to lowercase */ +#define NV_ZFILL 0x10 /* right justify and fill with leading zeros */ +#define NV_RJUST 0x20 /* right justify and blank fill */ +#define NV_LJUST 0x40 /* left justify and blank fill */ +#define NV_BINARY 0x100 /* fixed size data buffer */ +#define NV_RAW NV_LJUST /* used only with NV_BINARY */ +#define NV_HOST (NV_RJUST|NV_LJUST) /* map to host filename */ + +/* The following attributes do not effect the value */ +#define NV_RDONLY 0x1 /* readonly bit */ +#define NV_EXPORT 0x2000 /* export bit */ +#define NV_TAGGED 0x8000 /* user define tag bit */ + +/* The following are used with NV_INTEGER */ +#define NV_SHORT (NV_RJUST) /* when integers are not long */ +#define NV_LONG (NV_UTOL) /* for long long and long double */ +#define NV_UNSIGN (NV_LTOU) /* for unsigned quantities */ +#define NV_DOUBLE (NV_ZFILL) /* for floating point */ +#define NV_EXPNOTE (NV_LJUST) /* for scientific notation */ + +/* options for nv_open */ + +#define NV_APPEND 0x10000 /* append value */ +#define NV_MOVE 0x20000 /* for use with nv_clone */ +#define NV_ADD 8 + /* add node if not found */ +#define NV_ASSIGN NV_NOFREE /* assignment is possible */ +#define NV_NOASSIGN 0 /* backward compatibility */ +#define NV_NOARRAY 0x200000 /* array name not possible */ +#define NV_IARRAY 0x400000 /* for indexed array */ +#define NV_NOREF NV_REF /* don't follow reference */ +#define NV_IDENT 0x80 /* name must be identifier */ +#define NV_VARNAME 0x20000 /* name must be ?(.)id*(.id) */ +#define NV_NOADD 0x40000 /* do not add node */ +#define NV_NOSCOPE 0x80000 /* look only in current scope */ +#define NV_NOFAIL 0x100000 /* return 0 on failure, no msg */ +#define NV_NODISC NV_IDENT /* ignore disciplines */ + +#define NV_FUNCT NV_IDENT /* option for nv_create */ +#define NV_BLTINOPT NV_ZFILL /* save state for optimization*/ + +#define NV_PUBLIC (~(NV_NOSCOPE|NV_ASSIGN|NV_IDENT|NV_VARNAME|NV_NOADD)) + +/* numeric types */ +#define NV_INT16 (NV_SHORT|NV_INTEGER) +#define NV_UINT16 (NV_UNSIGN|NV_SHORT|NV_INTEGER) +#define NV_INT32 (NV_INTEGER) +#define NV_UNT32 (NV_UNSIGN|NV_INTEGER) +#define NV_INT64 (NV_LONG|NV_INTEGER) +#define NV_UINT64 (NV_UNSIGN|NV_LONG|NV_INTEGER) +#define NV_FLOAT (NV_SHORT|NV_DOUBLE|NV_INTEGER) +#define NV_LDOUBLE (NV_LONG|NV_DOUBLE|NV_INTEGER) + +/* name-value pair macros */ +#define nv_isattr(np,f) ((np)->nvflag & (f)) +#define nv_onattr(n,f) ((n)->nvflag |= (f)) +#define nv_offattr(n,f) ((n)->nvflag &= ~(f)) +#define nv_isarray(np) (nv_isattr((np),NV_ARRAY)) + +/* The following are operations for associative arrays */ +#define NV_AINIT 1 /* initialize */ +#define NV_AFREE 2 /* free array */ +#define NV_ANEXT 3 /* advance to next subscript */ +#define NV_ANAME 4 /* return subscript name */ +#define NV_ADELETE 5 /* delete current subscript */ +#define NV_AADD 6 /* add subscript if not found */ +#define NV_ACURRENT 7 /* return current subscript Namval_t* */ + +/* The following are for nv_disc */ +#define NV_FIRST 1 +#define NV_LAST 2 +#define NV_POP 3 +#define NV_CLONE 4 + +/* The following are operations for nv_putsub() */ +#define ARRAY_BITS 24 +#define ARRAY_ADD (1L< * +* * +***********************************************************************/ +#pragma prototyped +#ifndef HIST_VERSION +/* + * Interface for history mechanism + * written by David Korn + * + */ + +#include + +#define HIST_CHAR '!' +#define HIST_VERSION 1 /* history file format version no. */ + +typedef struct +{ + Sfdisc_t histdisc; /* discipline for history */ + Sfio_t *histfp; /* history file stream pointer */ + char *histname; /* name of history file */ + int32_t histind; /* current command number index */ + int histsize; /* number of accessible history lines */ +#ifdef _HIST_PRIVATE + _HIST_PRIVATE +#endif /* _HIST_PRIVATE */ +} History_t; + +typedef struct +{ + int hist_command; + int hist_line; + int hist_char; +} Histloc_t; + +/* the following are readonly */ +extern const char hist_fname[]; + +extern int _Hist; +#define hist_min(hp) ((_Hist=((int)((hp)->histind-(hp)->histsize)))>=0?_Hist:0) +#define hist_max(hp) ((int)((hp)->histind)) +/* these are the history interface routines */ +extern int sh_histinit(void); +extern void hist_cancel(History_t*); +extern void hist_close(History_t*); +extern int hist_copy(char*, int, int, int); +extern void hist_eof(History_t*); +extern Histloc_t hist_find(History_t*,char*,int, int, int); +extern void hist_flush(History_t*); +extern void hist_list(History_t*,Sfio_t*, off_t, int, char*); +extern int hist_match(History_t*,off_t, char*, int*); +extern off_t hist_tell(History_t*,int); +extern off_t hist_seek(History_t*,int); +extern char *hist_word(char*, int, int); +#if SHOPT_ESH + extern Histloc_t hist_locate(History_t*,int, int, int); +#endif /* SHOPT_ESH */ + +#endif /* HIST_VERSION */ Index: src/lib/libshell/common/include/shnodes.h =================================================================== --- src/lib/libshell/common/include/shnodes.h (revision 0) +++ src/lib/libshell/common/include/shnodes.h (revision 740) @@ -0,0 +1,222 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef _SHNODES_H +#define _SHNODES_H 1 +/* + * UNIX shell + * Written by David Korn + * + */ + + +#include +#include "argnod.h" + +/* command tree for tretyp */ +#define FINT (02< redirection operator */ +#define IOAPP 0x80 /* >> redirection operator */ +#define IODOC 0x100 /* << redirection operator */ +#define IOMOV 0x200 /* <& or >& operators */ +#define IOCLOB 0x400 /* noclobber bit */ +#define IORDW 0x800 /* <> redirection operator */ +#define IORAW 0x1000 /* no expansion needed for filename */ +#define IOSTRG 0x2000 /* here-document stored as incore string */ +#define IOSTRIP 0x4000 /* strip leading tabs for here-document */ +#define IOQUOTE 0x8000 /* here-document delimiter was quoted */ +#define IOVNM 0x10000 /* iovname field is non-zero */ +#define IOLSEEK 0x20000 /* seek operators <# or ># */ +#define IOARITH 0x40000 /* arithmetic seek <# ((expr)) */ +#define IOCOPY IOCLOB /* copy skipped lines onto standard output */ + +union Shnode_u +{ + struct argnod arg; + struct ionod io; + struct whnod wh; + struct swnod sw; + struct ifnod if_; + struct dolnod dol; + struct comnod com; + struct trenod tre; + struct forknod fork; + struct fornod for_; + struct regnod reg; + struct parnod par; + struct lstnod lst; + struct tstnod tst; + struct functnod funct; + struct arithnod ar; +}; + +extern void sh_freeup(void); +extern void sh_funstaks(struct slnod*,int); +extern Sfio_t *sh_subshell(Shnode_t*, int, int); +#if defined(__EXPORT__) && defined(_BLD_DLL) && defined(_BLD_shell) + __EXPORT__ +#endif +extern int sh_tdump(Sfio_t*, const Shnode_t*); +extern Shnode_t *sh_dolparen(void); +extern Shnode_t *sh_trestore(Sfio_t*); +#if SHOPT_KIA + extern int kiaclose(void); + extern unsigned long kiaentity(const char*,int,int,int,int,unsigned long,int,int,const char*); +#endif /* SHOPT_KIA */ + +#endif /* _SHNODES_H */ Index: src/lib/libshell/common/include/ulimit.h =================================================================== --- src/lib/libshell/common/include/ulimit.h (revision 0) +++ src/lib/libshell/common/include/ulimit.h (revision 740) @@ -0,0 +1,175 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef _ULIMIT_H +#define _ULIMIT_H 1 +/* + * This is for the ulimit built-in command + */ + +#include "FEATURE/time" +#include "FEATURE/rlimits" +#if defined(_sys_resource) && defined(_lib_getrlimit) +# include +# if !defined(RLIMIT_FSIZE) && defined(_sys_vlimit) + /* This handles hp/ux problem */ +# include +# define RLIMIT_FSIZE (LIM_FSIZE-1) +# define RLIMIT_DATA (LIM_DATA-1) +# define RLIMIT_STACK (LIM_STACK-1) +# define RLIMIT_CORE (LIM_CORE-1) +# define RLIMIT_CPU (LIM_CPU-1) +# ifdef LIM_MAXRSS +# define RLIMIT_RSS (LIM_MAXRSS-1) +# endif /* LIM_MAXRSS */ +# endif +# undef _lib_ulimit +#else +# ifdef _sys_vlimit +# include +# undef _lib_ulimit +# define RLIMIT_FSIZE LIM_FSIZE +# define RLIMIT_DATA LIM_DATA +# define RLIMIT_STACK LIM_STACK +# define RLIMIT_CORE LIM_CORE +# define RLIMIT_CPU LIM_CPU +# ifdef LIM_MAXRSS +# define RLIMIT_RSS LIM_MAXRSS +# endif /* LIM_MAXRSS */ +# else +# ifdef _lib_ulimit +# define vlimit ulimit +# endif /* _lib_ulimit */ +# endif /* _lib_vlimit */ +#endif + +#ifdef RLIM_INFINITY +# define INFINITY RLIM_INFINITY +#else +# ifndef INFINITY +# define INFINITY ((rlim_t)-1L) +# endif /* INFINITY */ +#endif /* RLIM_INFINITY */ + +#if defined(_lib_getrlimit) || defined(_lib_vlimit) || defined(_lib_ulimit) +# ifndef RLIMIT_CPU +# define RLIMIT_CPU 0 +# endif /* !RLIMIT_CPU */ +# ifndef RLIMIT_DATA +# define RLIMIT_DATA 0 +# endif /* !RLIMIT_DATA */ +# ifndef RLIMIT_RSS +# define RLIMIT_RSS 0 +# endif /* !RLIMIT_RSS */ +# ifndef RLIMIT_STACK +# define RLIMIT_STACK 0 +# endif /* !RLIMIT_STACK */ +# ifndef RLIMIT_CORE +# define RLIMIT_CORE 0 +# endif /* !RLIMIT_CORE */ +# ifndef RLIMIT_VMEM +# define RLIMIT_VMEM 0 +# endif /* !RLIMIT_VMEM */ +# ifndef RLIMIT_NOFILE +# define RLIMIT_NOFILE 0 +# endif /* !RLIMIT_NOFILE */ +#else +# define _no_ulimit +#endif +#ifndef _typ_rlim_t + typedef long rlim_t; +#endif + +#if !defined(RLIMIT_NOFILE) && defined(RLIMIT_OFILE) +#define RLIMIT_NOFILE RLIMIT_OFILE +#endif + +#ifndef RLIMIT_UNKNOWN +#define RLIMIT_UNKNOWN (-9999) +#endif +#ifndef RLIMIT_AS +#define RLIMIT_AS RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_CORE +#define RLIMIT_CORE RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_CPU +#define RLIMIT_CPU RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_DATA +#define RLIMIT_DATA RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_FSIZE +#define RLIMIT_FSIZE RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_LOCKS +#define RLIMIT_LOCKS RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_MEMLOCK +#define RLIMIT_MEMLOCK RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_NOFILE +#define RLIMIT_NOFILE RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_NPROC +#define RLIMIT_NPROC RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_PIPE +#define RLIMIT_PIPE RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_RSS +#define RLIMIT_RSS RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_SBSIZE +#define RLIMIT_SBSIZE RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_STACK +#define RLIMIT_STACK RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_PTHREAD +#define RLIMIT_PTHREAD RLIMIT_UNKNOWN +#endif +#ifndef RLIMIT_VMEM +#define RLIMIT_VMEM RLIMIT_UNKNOWN +#endif + +#define LIM_COUNT 0 +#define LIM_BLOCK 1 +#define LIM_BYTE 2 +#define LIM_KBYTE 3 +#define LIM_SECOND 4 + +typedef struct Limit_s +{ + const char name[8]; + const char* description; + int index; + const char* conf; + unsigned char option; + unsigned char type; +} Limit_t; + +extern const Limit_t shtab_limits[]; +extern const int shtab_units[]; + +extern const char e_unlimited[]; +extern const char* e_units[]; + +#endif /* _ULIMIT_H */ Index: src/lib/libshell/common/include/national.h =================================================================== --- src/lib/libshell/common/include/national.h (revision 0) +++ src/lib/libshell/common/include/national.h (revision 740) @@ -0,0 +1,37 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * national.h - definitions for multibyte character sets + * + * David Korn + * AT&T Labs + * + */ + +#if SHOPT_MULTIBYTE + +# ifndef MARKER +# define MARKER 0xdfff /* Must be invalid character */ +# endif + + extern int sh_strchr(const char*,const char*); + +#endif /* SHOPT_MULTIBYTE */ Index: src/lib/libshell/common/include/shell.h =================================================================== --- src/lib/libshell/common/include/shell.h (revision 0) +++ src/lib/libshell/common/include/shell.h (revision 740) @@ -0,0 +1,245 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef SH_INTERACTIVE +/* + * David Korn + * AT&T Labs + * + * Interface definitions for shell command language + * + */ + +#include +#include +#ifdef _SH_PRIVATE +# include "name.h" +#else +# include +#endif /* _SH_PRIVATE */ + +#define SH_VERSION 20060510 + +#undef NOT_USED +#define NOT_USED(x) (&x,1) + +/* options */ +typedef struct +{ + unsigned long v[4]; +} +Shopt_t; + +typedef void (*Shinit_f)(int); +typedef int (*Shbltin_f)(int, char*[], void*); +typedef int (*Shwait_f)(int, long, int); + +union Shnode_u; +typedef union Shnode_u Shnode_t; + +#define SH_CFLAG 0 +#define SH_HISTORY 1 /* used also as a state */ +#define SH_ERREXIT 2 /* used also as a state */ +#define SH_VERBOSE 3 /* used also as a state */ +#define SH_MONITOR 4 /* used also as a state */ +#define SH_INTERACTIVE 5 /* used also as a state */ +#define SH_RESTRICTED 6 +#define SH_XTRACE 7 +#define SH_KEYWORD 8 +#define SH_NOUNSET 9 +#define SH_NOGLOB 10 +#define SH_ALLEXPORT 11 +#define SH_PFSH 12 +#define SH_IGNOREEOF 13 +#define SH_NOCLOBBER 14 +#define SH_MARKDIRS 15 +#define SH_BGNICE 16 +#define SH_VI 17 +#define SH_VIRAW 18 +#define SH_TFLAG 19 +#define SH_TRACKALL 20 +#define SH_SFLAG 21 +#define SH_NOEXEC 22 +#define SH_GMACS 24 +#define SH_EMACS 25 +#define SH_PRIVILEGED 26 +#define SH_SUBSHARE 27 /* subshell shares state with parent */ +#define SH_NOLOG 28 +#define SH_NOTIFY 29 +#define SH_DICTIONARY 30 +#define SH_PIPEFAIL 32 +#define SH_GLOBSTARS 33 +#define SH_XARGS 34 +#define SH_RC 35 +#define SH_SHOWME 36 + +/* + * passed as flags to builtins in Nambltin_t struct when BLT_OPTIM is on + */ +#define SH_BEGIN_OPTIM 0x1 +#define SH_END_OPTIM 0x2 + +/* The following type is used for error messages */ + +/* error messages */ +extern const char e_defpath[]; +extern const char e_found[]; +extern const char e_nospace[]; +extern const char e_format[]; +extern const char e_number[]; +extern const char e_restricted[]; +extern const char e_recursive[]; +extern char e_version[]; + +typedef struct sh_scope +{ + struct sh_scope *par_scope; + int argc; + char **argv; + char *cmdname; + char *filename; + int lineno; + Dt_t *var_tree; + struct sh_scope *self; +} Shscope_t; + +/* + * Saves the state of the shell + */ + +typedef struct sh_static +{ + Shopt_t options; /* set -o options */ + Dt_t *var_tree; /* for shell variables */ + Dt_t *fun_tree; /* for shell functions */ + Dt_t *alias_tree; /* for alias names */ + Dt_t *bltin_tree; /* for builtin commands */ + Shscope_t *topscope; /* pointer to top-level scope */ + int inlineno; /* line number of current input file */ + int exitval; /* most recent exit value */ + unsigned char trapnote; /* set when trap/signal is pending */ + char subshell; /* set for virtual subshell */ +#ifdef _SH_PRIVATE + _SH_PRIVATE +#endif /* _SH_PRIVATE */ +} Shell_t; + +/* flags for sh_parse */ +#define SH_NL 1 /* Treat new-lines as ; */ +#define SH_EOF 2 /* EOF causes syntax error */ + +/* symbolic values for sh_iogetiop */ +#define SH_IOCOPROCESS (-2) +#define SH_IOHISTFILE (-3) + +/* symbolic value for sh_fdnotify */ +#define SH_FDCLOSE (-1) + +#if defined(__EXPORT__) && defined(_DLL) +# ifdef _BLD_shell +# define extern __EXPORT__ +# endif /* _BLD_shell */ +#endif /* _DLL */ + +extern Dt_t *sh_bltin_tree(void); +extern void sh_subfork(void); +extern Shell_t *sh_init(int,char*[],Shinit_f); +extern int sh_reinit(char*[]); +extern int sh_eval(Sfio_t*,int); +extern void sh_delay(double); +extern void *sh_parse(Shell_t*, Sfio_t*,int); +extern int sh_trap(const char*,int); +extern int sh_fun(Namval_t*,Namval_t*, char*[]); +extern int sh_funscope(int,char*[],int(*)(void*),void*,int); +extern Sfio_t *sh_iogetiop(int,int); +extern int sh_main(int, char*[], void(*)(int)); +extern void sh_menu(Sfio_t*, int, char*[]); +extern Namval_t *sh_addbuiltin(const char*, int(*)(int, char*[],void*), void*); +extern char *sh_fmtq(const char*); +extern char *sh_fmtqf(const char*, int, int); +extern Sfdouble_t sh_strnum(const char*, char**, int); +extern int sh_access(const char*,int); +extern int sh_close(int); +extern int sh_dup(int); +extern void sh_exit(int); +extern int sh_fcntl(int, int, ...); +extern Sfio_t *sh_fd2sfio(int); +extern int (*sh_fdnotify(int(*)(int,int)))(int,int); +extern Shell_t *sh_getinterp(void); +extern int sh_open(const char*, int, ...); +extern int sh_openmax(void); +extern Sfio_t *sh_pathopen(const char*); +extern ssize_t sh_read(int, void*, size_t); +extern ssize_t sh_write(int, const void*, size_t); +extern off_t sh_seek(int, off_t, int); +extern int sh_pipe(int[]); +extern mode_t sh_umask(mode_t); +extern void *sh_waitnotify(Shwait_f); +extern Shscope_t *sh_getscope(int,int); +extern Shscope_t *sh_setscope(Shscope_t*); +extern void sh_sigcheck(void); +extern unsigned long sh_isoption(int); +extern unsigned long sh_onoption(int); +extern unsigned long sh_offoption(int); +extern int sh_waitsafe(void); +extern int sh_exec(const Shnode_t*,int); + +#if SHOPT_DYNAMIC + extern void **sh_getliblist(void); +#endif /* SHOPT_DYNAMIC */ + +/* + * direct access to sh is obsolete, use sh_getinterp() instead + */ +#if !defined(_SH_PRIVATE) && defined(__IMPORT__) && !defined(_BLD_shell) + extern __IMPORT__ Shell_t sh; +#else + extern Shell_t sh; +#endif + +#ifdef _DLL +# undef extern +#endif /* _DLL */ + +#ifndef _SH_PRIVATE +# define access(a,b) sh_access(a,b) +# define close(a) sh_close(a) +# define exit(a) sh_exit(a) +# define fcntl(a,b,c) sh_fcntl(a,b,c) +# define pipe(a) sh_pipe(a) +# define read(a,b,c) sh_read(a,b,c) +# define write(a,b,c) sh_write(a,b,c) +# define umask(a) sh_umask(a) +# define dup sh_dup +# if _lib_lseek64 +# define open64 sh_open +# define lseek64 sh_seek +# else +# define open sh_open +# define lseek sh_seek +# endif +#endif /* !_SH_PRIVATE */ + +#define SH_SIGSET 4 +#define SH_EXITSIG 0400 /* signal exit bit */ +#define SH_EXITMASK (SH_EXITSIG-1) /* normal exit status bits */ +#define SH_RUNPROG -1022 /* needs to be negative and < 256 */ + +#endif /* SH_INTERACTIVE */ Index: src/lib/libshell/common/include/io.h =================================================================== --- src/lib/libshell/common/include/io.h (revision 0) +++ src/lib/libshell/common/include/io.h (revision 740) @@ -0,0 +1,123 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * UNIX shell + * David Korn + * + */ + +#include +#include + +#ifndef IOBSIZE +# define IOBSIZE SF_BUFSIZE +#endif /* IOBSIZE */ +#define IOMAXTRY 20 + +#ifndef SF_CLOSING +#define SF_CLOSING SF_CLOSE +#endif +#ifndef SF_APPENDWR +#define SF_APPENDWR SF_APPEND +#endif + +/* used for output of shell errors */ +#define ERRIO 2 + +#define IOREAD 001 +#define IOWRITE 002 +#define IODUP 004 +#define IOSEEK 010 +#define IONOSEEK 020 +#define IOTTY 040 +#define IOCLEX 0100 +#define IOCLOSE (IOSEEK|IONOSEEK) + +#define IOSUBSHELL 0x8000 /* must be larger than any file descriptor */ + +/* + * The remainder of this file is only used when compiled with shell + */ + +#if KSHELL + +#ifndef ARG_RAW + struct ionod; +#endif /* !ARG_RAW */ + +#define sh_inuse(f2) (sh.fdptrs[f2]) + +extern int sh_iocheckfd(int); +extern void sh_ioinit(void); +extern int sh_iomovefd(int); +extern int sh_iorenumber(int,int); +extern void sh_pclose(int[]); +extern void sh_iorestore(int,int); +#if defined(__EXPORT__) && defined(_BLD_DLL) && defined(_BLD_shell) + __EXPORT__ +#endif +extern Sfio_t *sh_iostream(int); +extern int sh_redirect(struct ionod*,int); +extern void sh_iosave(int,int); +extern void sh_iounsave(void); +extern int sh_chkopen(const char*); +extern int sh_ioaccess(int,int); +extern int sh_devtofd(const char*); +extern int sh_source(Shell_t*, Sfio_t*, const char*); + +/* the following are readonly */ +extern const char e_pexists[]; +extern const char e_query[]; +extern const char e_history[]; +extern const char e_argtype[]; +extern const char e_create[]; +extern const char e_tmpcreate[]; +extern const char e_exists[]; +extern const char e_file[]; +extern const char e_formspec[]; +extern const char e_badregexp[]; +extern const char e_open[]; +extern const char e_notseek[]; +extern const char e_noread[]; +extern const char e_badseek[]; +extern const char e_badpattern[]; +extern const char e_toomany[]; +extern const char e_pipe[]; +extern const char e_unknown[]; +extern const char e_devnull[]; +extern const char e_profile[]; +extern const char e_sysprofile[]; +#if SHOPT_SYSRC +extern const char e_sysrc[]; +#endif +#if SHOPT_BASH +#if SHOPT_SYSRC +extern const char e_bash_sysrc[]; +#endif +extern const char e_bash_rc[]; +extern const char e_bash_login[]; +extern const char e_bash_logout[]; +extern const char e_bash_profile[]; +#endif +extern const char e_stdprompt[]; +extern const char e_supprompt[]; +extern const char e_ambiguous[]; +#endif /* KSHELL */ Index: src/lib/libshell/common/include/variables.h =================================================================== --- src/lib/libshell/common/include/variables.h (revision 0) +++ src/lib/libshell/common/include/variables.h (revision 740) @@ -0,0 +1,104 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#ifndef SH_VALNOD + +#include +#include "FEATURE/options" +#include "FEATURE/dynamic" + +/* The following defines are coordinated with data in data/variables.c */ + +#define PATHNOD (sh.bltin_nodes) +#define PS1NOD (sh.bltin_nodes+1) +#define PS2NOD (sh.bltin_nodes+2) +#define IFSNOD (sh.bltin_nodes+3) +#define PWDNOD (sh.bltin_nodes+4) +#define HOME (sh.bltin_nodes+5) +#define MAILNOD (sh.bltin_nodes+6) +#define REPLYNOD (sh.bltin_nodes+7) +#define SHELLNOD (sh.bltin_nodes+8) +#define EDITNOD (sh.bltin_nodes+9) +#define MCHKNOD (sh.bltin_nodes+10) +#define RANDNOD (sh.bltin_nodes+11) +#define ENVNOD (sh.bltin_nodes+12) +#define HISTFILE (sh.bltin_nodes+13) +#define HISTSIZE (sh.bltin_nodes+14) +#define HISTEDIT (sh.bltin_nodes+15) +#define HISTCUR (sh.bltin_nodes+16) +#define FCEDNOD (sh.bltin_nodes+17) +#define CDPNOD (sh.bltin_nodes+18) +#define MAILPNOD (sh.bltin_nodes+19) +#define PS3NOD (sh.bltin_nodes+20) +#define OLDPWDNOD (sh.bltin_nodes+21) +#define VISINOD (sh.bltin_nodes+22) +#define COLUMNS (sh.bltin_nodes+23) +#define LINES (sh.bltin_nodes+24) +#define PPIDNOD (sh.bltin_nodes+25) +#define L_ARGNOD (sh.bltin_nodes+26) +#define TMOUTNOD (sh.bltin_nodes+27) +#define SECONDS (sh.bltin_nodes+28) +#define LINENO (sh.bltin_nodes+29) +#define OPTARGNOD (sh.bltin_nodes+30) +#define OPTINDNOD (sh.bltin_nodes+31) +#define PS4NOD (sh.bltin_nodes+32) +#define FPATHNOD (sh.bltin_nodes+33) +#define LANGNOD (sh.bltin_nodes+34) +#define LCALLNOD (sh.bltin_nodes+35) +#define LCCOLLNOD (sh.bltin_nodes+36) +#define LCTYPENOD (sh.bltin_nodes+37) +#define LCMSGNOD (sh.bltin_nodes+38) +#define LCNUMNOD (sh.bltin_nodes+39) +#define FIGNORENOD (sh.bltin_nodes+40) +#define DOTSHNOD (sh.bltin_nodes+41) +#define ED_CHRNOD (sh.bltin_nodes+42) +#define ED_COLNOD (sh.bltin_nodes+43) +#define ED_TXTNOD (sh.bltin_nodes+44) +#define ED_MODENOD (sh.bltin_nodes+45) +#define SH_NAMENOD (sh.bltin_nodes+46) +#define SH_SUBSCRNOD (sh.bltin_nodes+47) +#define SH_VALNOD (sh.bltin_nodes+48) +#define SH_VERSIONNOD (sh.bltin_nodes+49) +#define SH_DOLLARNOD (sh.bltin_nodes+50) +#define SH_MATCHNOD (sh.bltin_nodes+51) +#define SH_COMMANDNOD (sh.bltin_nodes+52) +#define SH_PATHNAMENOD (sh.bltin_nodes+53) +#define SH_FUNNAMENOD (sh.bltin_nodes+54) +#define SH_SUBSHELLNOD (sh.bltin_nodes+55) +#define SH_LEVELNOD (sh.bltin_nodes+56) +#if SHOPT_FS_3D +# define VPATHNOD (sh.bltin_nodes+57) +# define NFS_3D 1 +#else +# define NFS_3D 0 +#endif /* SHOPT_FS_3D */ +#if SHOPT_VPIX +# define DOSPATHNOD (sh.bltin_nodes+57+NFS_3D) +# define VPIXNOD (sh.bltin_nodes+58+NFS_3D) +# define NVPIX (NFS_3D+2) +#else +# define NVPIX NFS_3D +#endif /* SHOPT_VPIX */ +#ifdef apollo +# define SYSTYPENOD (sh.bltin_nodes+57+NVPIX) +#endif /* apollo */ + +#endif /* SH_VALNOD */ Index: src/lib/libshell/common/include/argnod.h =================================================================== --- src/lib/libshell/common/include/argnod.h (revision 0) +++ src/lib/libshell/common/include/argnod.h (revision 740) @@ -0,0 +1,147 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef ARG_RAW +/* + * struct to hold a word argument + * Written by David Korn + * + */ + +#include + +struct ionod +{ + unsigned iofile; + char *ioname; + struct ionod *ionxt; + struct ionod *iolst; + char *iodelim; + off_t iooffset; + long iosize; + char *iovname; +}; + +struct comnod +{ + int comtyp; + struct ionod *comio; + struct argnod *comarg; + struct argnod *comset; + void *comnamp; + void *comnamq; + void *comstate; + int comline; +}; + +#define COMBITS 4 +#define COMMSK ((1<argflag&ARG_RAW?sh_fmtq((ap)->argval):(ap)->argval) +#define ARG_SPARE 1 + + +/* legal argument flags */ +#define ARG_RAW 0x1 /* string needs no processing */ +#define ARG_MAKE 0x2 /* bit set during argument expansion */ +#define ARG_COMSUB 0x2 /* command sub */ +#define ARG_MAC 0x4 /* string needs macro expansion */ +#define ARG_EXP 0x8 /* string needs file expansion */ +#define ARG_ASSIGN 0x10 /* argument is an assignment */ +#define ARG_QUOTED 0x20 /* word contained quote characters */ +#define ARG_MESSAGE 0x40 /* contains international string */ +#define ARG_APPEND 0x80 /* for += assignment */ +/* The following can be passed as options to sh_macexpand() */ +#define ARG_ARITH 0x100 /* arithmetic expansion */ +#define ARG_OPTIMIZE 0x200 /* try to optimize */ +#define ARG_NOGLOB 0x400 /* no file name expansion */ +#define ARG_LET 0x800 /* processing let command arguments */ + +extern char **sh_argbuild(int*,const struct comnod*,int); +extern struct dolnod *sh_argcreate(char*[]); +extern char *sh_argdolminus(void); +extern struct dolnod *sh_argfree(struct dolnod*,int); +extern struct dolnod *sh_argnew(char*[],struct dolnod**); +extern int sh_argopts(int,char*[]); +extern void sh_argreset(struct dolnod*,struct dolnod*); +extern void sh_argset(char*[]); +extern struct dolnod *sh_arguse(void); + +extern const char e_heading[]; +extern const char e_off[]; +extern const char e_on[]; +extern const char e_sptbnl[]; +extern const char e_subst[]; +extern const char e_option[]; +extern const char e_exec[]; +extern const char e_devfdNN[]; +extern const char e_devfdstd[]; + +#endif /* ARG_RAW */ Index: src/lib/libshell/common/include/fault.h =================================================================== --- src/lib/libshell/common/include/fault.h (revision 0) +++ src/lib/libshell/common/include/fault.h (revision 740) @@ -0,0 +1,124 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef SH_SIGBITS +/* + * UNIX shell + * S. R. Bourne + * Rewritten by David Korn + * + */ + +#include +#include +#include +#include +#include "FEATURE/setjmp" +#include "FEATURE/sigfeatures" + +#ifndef SIGWINCH +# ifdef SIGWIND +# define SIGWINCH SIGWIND +# else +# ifdef SIGWINDOW +# define SIGWINCH SIGWINDOW +# endif +# endif +#endif + +typedef void (*SH_SIGTYPE)(int,void(*)(int)); + +#define SH_FORKLIM 16 /* fork timeout interval */ + +#define SH_TRAP 0200 /* bit for internal traps */ +#define SH_ERRTRAP 0 /* trap for non-zero exit status */ +#define SH_KEYTRAP 1 /* trap for keyboard event */ +#define SH_DEBUGTRAP 4 /* must be last internal trap */ + +#define SH_SIGBITS 8 +#define SH_SIGFAULT 1 /* signal handler is sh_fault */ +#define SH_SIGOFF 2 /* signal handler is SIG_IGN */ +#define SH_SIGSET 4 /* pending signal */ +#define SH_SIGTRAP 010 /* pending trap */ +#define SH_SIGDONE 020 /* default is exit */ +#define SH_SIGIGNORE 040 /* default is ingore signal */ +#define SH_SIGINTERACTIVE 0100 /* handle interactive specially */ +#define SH_SIGTSTP 0200 /* tstp signal received */ +#define SH_SIGALRM 0200 /* timer alarm received */ +#define SH_SIGTERM SH_SIGOFF /* term signal received */ + +/* + * These are longjmp values + */ + +#define SH_JMPDOT 2 +#define SH_JMPEVAL 3 +#define SH_JMPTRAP 4 +#define SH_JMPIO 5 +#define SH_JMPCMD 6 +#define SH_JMPFUN 7 +#define SH_JMPERRFN 8 +#define SH_JMPSUB 9 +#define SH_JMPERREXIT 10 +#define SH_JMPEXIT 11 +#define SH_JMPSCRIPT 12 + +struct openlist +{ + Sfio_t *strm; + struct openlist *next; +}; + +struct checkpt +{ + sigjmp_buf buff; + sigjmp_buf *prev; + int topfd; + int mode; + struct openlist *olist; +#if (ERROR_VERSION >= 20030214L) + Error_context_t err; +#else + struct errorcontext err; +#endif +}; + +#define sh_pushcontext(bp,n) ( (bp)->mode=(n) , (bp)->olist=0, \ + (bp)->topfd=sh.topfd, (bp)->prev=sh.jmplist, \ + (bp)->err = *ERROR_CONTEXT_BASE, \ + sh.jmplist = (sigjmp_buf*)(&(bp)->buff) \ + ) +#define sh_popcontext(bp) (sh.jmplist=(bp)->prev, errorpop(&((bp)->err))) + +extern void sh_fault(int); +extern void sh_done(int); +extern void sh_chktrap(void); +extern void sh_sigclear(int); +extern void sh_sigdone(void); +extern void sh_siginit(void); +extern void sh_sigtrap(int); +extern void sh_sigreset(int); +extern void sh_timetraps(void); +extern void *sh_timeradd(unsigned long,int ,void (*)(void*),void*); +extern void timerdel(void*); + +extern const char e_alarm[]; + +#endif /* !SH_SIGBITS */ Index: src/lib/libshell/common/include/terminal.h =================================================================== --- src/lib/libshell/common/include/terminal.h (revision 0) +++ src/lib/libshell/common/include/terminal.h (revision 740) @@ -0,0 +1,195 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#ifndef _terminal_ +#define _terminal_ 1 + +#include "FEATURE/ttys" +/* + * terminal interface + * complicated by the fact that there are so many variations + * This will use POSIX interface where available + */ + +#ifdef _hdr_termios +# include +# if __sgi__ || sgi /* special hack to eliminate ^M problem */ +# ifndef ECHOCTL +# define ECHOCTL ECHOE +# endif /* ECHOCTL */ +# ifndef CNSUSP +# define CNSUSP CNSWTCH +# endif /* CNSUSP */ +# endif /* sgi */ +# ifdef _NEXT_SOURCE +# define _lib_tcgetattr 1 +# define _lib_tcgetpgrp 1 +# endif /* _NEXT_SOURCE */ +#else +# if defined(_sys_termios) && defined(_lib_tcgetattr) +# include +# define _hdr_termios +# else +# undef _sys_termios +# endif /* _sys_termios */ +#endif /* _hdr_termios */ + +#ifdef _hdr_termios +# undef _hdr_sgtty +# undef tcgetattr +# undef tcsetattr +# undef tcgetpgrp +# undef tcsetpgrp +# undef cfgetospeed +# ifndef TCSANOW +# define TCSANOW TCSETS +# define TCSADRAIN TCSETSW +# define TCSAFLUSH TCSETSF +# endif /* TCSANOW */ + /* The following corrects bugs in some implementations */ +# if defined(TCSADFLUSH) && !defined(TCSAFLUSH) +# define TCSAFLUSH TCSADFLUSH +# endif /* TCSADFLUSH */ +# ifndef _lib_tcgetattr +# undef tcgetattr +# define tcgetattr(fd,tty) ioctl(fd, TCGETS, tty) +# undef tcsetattr +# define tcsetattr(fd,action,tty) ioctl(fd, action, tty) +# undef cfgetospeed +# define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) +# endif /* _lib_tcgetattr */ +# undef TIOCGETC +# if SHOPT_OLDTERMIO /* use both termios and termio */ +# ifdef _hdr_termio +# include +# else +# ifdef _sys_termio +# include +# define _hdr_termio 1 +# else +# undef SHOPT_OLDTERMIO +# endif /* _sys_termio */ +# endif /* _hdr_termio */ +# endif /* SHOPT_OLDTERMIO */ +#else +# define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) +# undef SHOPT_OLDTERMIO +# ifdef _hdr_termio +# include +# else +# ifdef _sys_termio +# include +# define _hdr_termio 1 +# endif /* _sys_termio */ +# endif /* _hdr_termio */ +# ifdef _hdr_termio +# define termios termio +# undef TIOCGETC +# define tcgetattr(fd,tty) ioctl(fd, TCGETA, tty) +# define tcsetattr(fd,action,tty) ioctl(fd, action, tty) + +# ifdef _sys_bsdtty +# include +# endif /* _sys_bsdtty */ +# else +# ifdef _hdr_sgtty +# include +# ifndef LPENDIN +# ifdef _sys_nttyio +# include +# endif /* _sys_nttyio */ +# endif /* LPENDIN */ +# define termios sgttyb +# ifdef TIOCSETN +# undef TCSETAW +# endif /* TIOCSETN */ +# ifdef TIOCGETP +# define tcgetattr(fd,tty) ioctl(fd, TIOCGETP, tty) +# define tcsetattr(fd,action,tty) ioctl(fd, action, tty) +# else +# define tcgetattr(fd,tty) gtty(fd, tty) +# define tcsetattr(fd,action,tty) stty(fd, tty) +# endif /* TIOCGETP */ +# endif /* _hdr_sgtty */ +# endif /* hdr_termio */ + +# ifndef TCSANOW +# ifdef TCSETAW +# define TCSANOW TCSETA +# ifdef u370 + /* delays are too long, don't wait for output to drain */ +# define TCSADRAIN TCSETA +# else +# define TCSADRAIN TCSETAW +# endif /* u370 */ +# define TCSAFLUSH TCSETAF +# else +# ifdef TIOCSETN +# define TCSANOW TIOCSETN +# define TCSADRAIN TIOCSETN +# define TCSAFLUSH TIOCSETP +# endif /* TIOCSETN */ +# endif /* TCSETAW */ +# endif /* TCSANOW */ +#endif /* _hdr_termios */ + +/* set ECHOCTL if driver can echo control charaters as ^c */ +#ifdef LCTLECH +# ifndef ECHOCTL +# define ECHOCTL LCTLECH +# endif /* !ECHOCTL */ +#endif /* LCTLECH */ +#ifdef LNEW_CTLECH +# ifndef ECHOCTL +# define ECHOCTL LNEW_CTLECH +# endif /* !ECHOCTL */ +#endif /* LNEW_CTLECH */ +#ifdef LNEW_PENDIN +# ifndef PENDIN +# define PENDIN LNEW_PENDIN +# endif /* !PENDIN */ +#endif /* LNEW_PENDIN */ +#ifndef ECHOCTL +# ifndef VEOL +# define RAWONLY 1 +# endif /* !VEOL */ +#endif /* !ECHOCTL */ + +#ifdef _sys_filio +# ifndef FIONREAD +# include +# endif /* FIONREAD */ +#endif /* _sys_filio */ +/* set FIORDCHK if you can check for characters in input queue */ +#ifdef FIONREAD +# ifndef FIORDCHK +# define FIORDCHK FIONREAD +# endif /* !FIORDCHK */ +#endif /* FIONREAD */ + +extern int tty_alt(int); +extern void tty_cooked(int); +extern int tty_get(int,struct termios*); +extern int tty_raw(int,int); +extern int tty_check(int); +extern int tty_set(int, int, struct termios*); + +#endif /* _terminal_ */ Index: src/lib/libshell/common/include/lexstates.h =================================================================== --- src/lib/libshell/common/include/lexstates.h (revision 0) +++ src/lib/libshell/common/include/lexstates.h (revision 740) @@ -0,0 +1,151 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef S_BREAK +#define S_BREAK 1 /* end of token */ +#define S_EOF 2 /* end of buffer */ +#define S_NL 3 /* new-line when not a token */ +#define S_RES 4 /* first character of reserved word */ +#define S_NAME 5 /* other identifier characters */ +#define S_REG 6 /* non-special characters */ +#define S_TILDE 7 /* first char is tilde */ +#define S_PUSH 8 +#define S_POP 9 +#define S_BRACT 10 +#define S_LIT 11 /* literal quote character */ +#define S_NLTOK 12 /* new-line token */ +#define S_OP 13 /* operator character */ +#define S_PAT 14 /* pattern characters * and ? */ +#define S_EPAT 15 /* pattern char when followed by ( */ +#define S_EQ 16 /* assignment character */ +#define S_COM 17 /* comment character */ +#define S_MOD1 18 /* ${...} modifier character - old quoting */ +#define S_MOD2 19 /* ${...} modifier character - new quoting */ +#define S_ERR 20 /* invalid character in ${...} */ +#define S_SPC1 21 /* special prefix characters after $ */ +#define S_SPC2 22 /* special characters after $ */ +#define S_DIG 23 /* digit character after $*/ +#define S_ALP 24 /* alpahbetic character after $ */ +#define S_LBRA 25 /* left brace after $ */ +#define S_RBRA 26 /* right brace after $ */ +#define S_PAR 27 /* set for $( */ +#define S_ENDCH 28 /* macro expansion terminator */ +#define S_SLASH 29 /* / character terminates ~ expansion */ +#define S_COLON 30 /* for character : */ +#define S_LABEL 31 /* for goto label */ +#define S_EDOL 32 /* ends $identifier */ +#define S_BRACE 33 /* left brace */ +#define S_DOT 34 /* . char */ +#define S_META 35 /* | & ; < > inside ${...} reserved for future use */ +#define S_SPACE S_BREAK /* IFS space characters */ +#define S_DELIM S_RES /* IFS delimter characters */ +#define S_MBYTE S_NAME /* IFS first byte of multi-byte char */ +#define S_BLNK 36 /* space or tab */ +/* The following must be the highest numbered states */ +#define S_QUOTE 37 /* double quote character */ +#define S_GRAVE 38 /* old comsub character */ +#define S_ESC 39 /* escape character */ +#define S_DOL 40 /* $ subsitution character */ +#define S_ESC2 41 /* escape character inside '...' */ + +/* These are the lexical state table names */ +#define ST_BEGIN 0 +#define ST_NAME 1 +#define ST_NORM 2 +#define ST_LIT 3 +#define ST_QUOTE 4 +#define ST_NESTED 5 +#define ST_DOL 6 +#define ST_BRACE 7 +#define ST_DOLNAME 8 +#define ST_MACRO 9 +#define ST_QNEST 10 +#define ST_NONE 11 + +#include "FEATURE/locale" + +#if _hdr_wchar +# include +# if _hdr_wctype +# include +# undef isalpha +# define isalpha(x) iswalpha(x) +# if defined(iswblank) || _lib_iswblank +# undef isblank +# define isblank(x) iswblank(x) +# else +# if _lib_wctype && _lib_iswctype +# define _lib_iswblank -1 +# undef isblank +# define isblank(x) local_iswblank(x) + extern int local_iswblank(wchar_t); +# endif +# endif +# endif +#endif +#ifndef isblank +# define isblank(x) ((x)==' '||(x)=='\t') +#endif + +#undef LEN +#if SHOPT_MULTIBYTE + static int NXT, LEN; +# define isaname(c) ((c)>0xff?isalpha(c): sh_lexstates[ST_NAME][(c)]==0) +# define isaletter(c) ((c)>0xff?isalpha(c): sh_lexstates[ST_DOL][(c)]==S_ALP && (c)!='.') +#else +# undef mbwide +# define mbwide() (0) +# define LEN 1 +# define isaname(c) (sh_lexstates[ST_NAME][c]==0) +# define isaletter(c) (sh_lexstates[ST_DOL][c]==S_ALP && (c)!='.') +#endif +#define STATE(s,c) (mbwide()?(c=fcmbstate(s,&NXT,&LEN),NXT):s[c=fcget()]) +#define isadigit(c) (sh_lexstates[ST_DOL][c]==S_DIG) +#define isastchar(c) ((c)=='@' || (c)=='*') +#define isexp(c) (sh_lexstates[ST_MACRO][c]==S_PAT||(c)=='$'||(c)=='`') +#define ismeta(c) (sh_lexstates[ST_NAME][c]==S_BREAK) + +extern char *sh_lexstates[ST_NONE]; +extern const char *sh_lexrstates[ST_NONE]; +extern const char e_lexversion[]; +extern const char e_lexspace[]; +extern const char e_lexslash[]; +extern const char e_lexlabignore[]; +extern const char e_lexlabunknown[]; +extern const char e_lexsyntax1[]; +extern const char e_lexsyntax2[]; +extern const char e_lexsyntax3[]; +extern const char e_lexobsolete1[]; +extern const char e_lexobsolete2[]; +extern const char e_lexobsolete3[]; +extern const char e_lexobsolete4[]; +extern const char e_lexobsolete5[]; +extern const char e_lexobsolete6[]; +extern const char e_lexusebrace[]; +extern const char e_lexusequote[]; +extern const char e_lexescape[]; +extern const char e_lexquote[]; +extern const char e_lexnested[]; +extern const char e_lexbadchar[]; +extern const char e_lexlongquote[]; +extern const char e_lexfuture[]; +extern const char e_lexzerobyte[]; +extern const char e_lexemptyfor[]; +#endif Index: src/lib/libshell/common/include/fcin.h =================================================================== --- src/lib/libshell/common/include/fcin.h (revision 0) +++ src/lib/libshell/common/include/fcin.h (revision 740) @@ -0,0 +1,62 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef fcgetc +/* + * David Korn + * AT&T Labs + * + * Fast character input with sfio text streams and strings + * + */ + +#include + +typedef struct _fcin +{ + Sfio_t *_fcfile; /* input file pointer */ + unsigned char *fcbuff; /* pointer to input buffer */ + unsigned char *fclast; /* pointer to end of input buffer */ + unsigned char *fcptr; /* pointer to next input char */ + unsigned char fcchar; /* saved character */ + void (*fcfun)(Sfio_t*,const char*,int); /* advance function */ + int fcleft; /* for multibyte boundary */ + Sfoff_t fcoff; /* offset for last read */ +} Fcin_t; + +#define fcfile() (_Fcin._fcfile) +#define fcgetc(c) (((c=fcget()) || (c=fcfill())), c) +#define fcget() ((int)(*_Fcin.fcptr++)) +#define fcpeek(n) ((int)_Fcin.fcptr[n]) +#define fcseek(n) ((char*)(_Fcin.fcptr+=(n))) +#define fcfirst() ((char*)_Fcin.fcbuff) +#define fcsopen(s) (_Fcin._fcfile=(Sfio_t*)0,_Fcin.fcbuff=_Fcin.fcptr=(unsigned char*)(s)) +#define fctell() (_Fcin.fcoff + (_Fcin.fcptr-_Fcin.fcbuff)) +#define fcsave(x) (*(x) = _Fcin) +#define fcrestore(x) (_Fcin = *(x)) +extern int fcfill(void); +extern int fcfopen(Sfio_t*); +extern int fcclose(void); +void fcnotify(void(*)(Sfio_t*,const char*,int)); +extern int fcmbstate(const char*,int*,int*); + +extern Fcin_t _Fcin; /* used by macros */ + +#endif /* fcgetc */ Index: src/lib/libshell/common/include/test.h =================================================================== --- src/lib/libshell/common/include/test.h (revision 0) +++ src/lib/libshell/common/include/test.h (revision 740) @@ -0,0 +1,71 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef TEST_ARITH +/* + * UNIX shell + * David Korn + * AT&T Labs + * + */ + +#include "FEATURE/options" +#include "shtable.h" +/* + * These are the valid test operators + */ + +#define TEST_ARITH 040 /* arithmetic operators */ +#define TEST_BINOP 0200 /* binary operator */ +#define TEST_PATTERN 0100 /* turn off bit for pattern compares */ + +#define TEST_NE (TEST_ARITH|9) +#define TEST_EQ (TEST_ARITH|4) +#define TEST_GE (TEST_ARITH|5) +#define TEST_GT (TEST_ARITH|6) +#define TEST_LE (TEST_ARITH|7) +#define TEST_LT (TEST_ARITH|8) +#define TEST_OR (TEST_BINOP|1) +#define TEST_AND (TEST_BINOP|2) +#define TEST_SNE (TEST_PATTERN|1) +#define TEST_SEQ (TEST_PATTERN|14) +#define TEST_PNE 1 +#define TEST_PEQ 14 +#define TEST_EF 3 +#define TEST_NT 10 +#define TEST_OT 12 +#define TEST_SLT 15 +#define TEST_SGT 16 +#define TEST_END 8 +#define TEST_REP 20 + +extern int test_unop(int, const char*); +extern int test_inode(const char*, const char*); +extern int test_binop(int, const char*, const char*); + +extern const char sh_opttest[]; +extern const char test_opchars[]; +extern const char e_argument[]; +extern const char e_missing[]; +extern const char e_badop[]; +extern const char e_tstbegin[]; +extern const char e_tstend[]; + +#endif /* TEST_ARITH */ Index: src/lib/libshell/common/include/name.h =================================================================== --- src/lib/libshell/common/include/name.h (revision 0) +++ src/lib/libshell/common/include/name.h (revision 740) @@ -0,0 +1,208 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef _NV_PRIVATE +/* + * This is the implementation header file for name-value pairs + */ + +#define _NV_PRIVATE \ + Namfun_t *nvfun; /* pointer to trap functions */ \ + union Value nvalue; /* value field */ \ + char *nvenv; /* pointer to environment name */ + +#include +#include +#include "shtable.h" + +/* Nodes can have all kinds of values */ +union Value +{ + const char *cp; + int *ip; + char c; + int i; + unsigned int u; + int32_t *lp; + Sflong_t *llp; /* for long long arithmetic */ + int16_t s; + double *dp; /* for floating point arithmetic */ + Sfdouble_t *ldp; /* for long floating point arithmetic */ + struct Namarray *array; /* for array node */ + struct Namval *np; /* for Namval_t node */ + union Value *up; /* for indirect node */ + struct Ufunction *rp; /* shell user defined functions */ + struct Namfun *funp; /* discipline pointer */ + struct Namref *nrp; /* name reference */ + int (*bfp)(int,char*[],void*);/* builtin entry point function pointer */ +}; + +#include "nval.h" + +/* used for arrays */ + +#define ARRAY_MAX (1L< 255 */ +#define BLT_DCL (NV_TAGGED) /* declaration command */ +#define nv_isref(n) (nv_isattr((n),NV_REF)==NV_REF) +#define nv_istable(n) (nv_isattr((n),NV_TABLE|NV_LJUST|NV_RJUST)==NV_TABLE) +#define is_abuiltin(n) (nv_isattr(n,NV_BLTIN)==NV_BLTIN) +#define is_afunction(n) (nv_isattr(n,NV_FUNCTION)==NV_FUNCTION) +#define nv_funtree(n) ((n)->nvalue.rp->ptree) +#define funptr(n) ((n)->nvalue.bfp) + +#define NV_SUBQUOTE (NV_ADD<<1) /* used with nv_endsubscript */ + +/* NAMNOD MACROS */ +/* ... for attributes */ + +#define nv_setattr(n,f) ((n)->nvflag = (f)) +#define nv_context(n) ((void*)(n)->nvfun) /* for builtins */ +/* The following are for name references */ +#define nv_refnode(n) ((n)->nvalue.nrp->np) +#define nv_reftree(n) ((n)->nvalue.nrp->root) +#define nv_reftable(n) ((n)->nvalue.nrp->table) +#define nv_refsub(n) ((n)->nvalue.nrp->sub) +#if SHOPT_OO +# define nv_class(np) (nv_isattr(np,NV_REF|NV_IMPORT)?0:(Namval_t*)((np)->nvenv)) +#endif /* SHOPT_OO */ + +/* ... etc */ + +#define nv_setsize(n,s) ((n)->nvsize = (s)) +#undef nv_size +#define nv_size(np) ((np)->nvsize) +#define nv_isnull(np) (!(np)->nvalue.cp && !(np)->nvfun && !nv_isattr(np,NV_SHORT)) + +/* ... for arrays */ + +#define array_elem(ap) ((ap)->nelem&ARRAY_MASK) +#define array_assoc(ap) ((ap)->fun) + +extern int array_maxindex(Namval_t*); +extern char *nv_endsubscript(Namval_t*, char*, int); +extern Namfun_t *nv_cover(Namval_t*); +extern Namarr_t *nv_arrayptr(Namval_t*); +extern int nv_setnotify(Namval_t*,char **); +extern int nv_unsetnotify(Namval_t*,char **); +extern void nv_setlist(struct argnod*, int); +extern void nv_optimize(Namval_t*); +extern void nv_outname(Sfio_t*,char*, int); +extern void nv_scope(struct argnod*); +extern void nv_unref(Namval_t*); +extern void _nv_unset(Namval_t*,int); +extern int nv_clone(Namval_t*, Namval_t*, int); +extern void *nv_diropen(const char*); +extern char *nv_dirnext(void*); +extern void nv_dirclose(void*); +extern char *nv_getvtree(Namval_t*, Namfun_t*); +extern void nv_attribute(Namval_t*, Sfio_t*, char*, int); +extern Namval_t *nv_bfsearch(const char*, Dt_t*, Namval_t**, char**); +extern Namval_t *nv_mkclone(Namval_t*); +extern Namval_t *nv_mktype(Namval_t**, int); +extern void nv_addnode(Namval_t*, int); +extern Namval_t *nv_parent(Namval_t*); +extern char *nv_getbuf(size_t); +extern Namval_t *nv_mount(Namval_t*, const char *name, Dt_t*); +extern Namval_t *nv_arraychild(Namval_t*, Namval_t*, int); +extern int nv_compare(Dt_t*, Void_t*, Void_t*, Dtdisc_t*); + +extern const Namdisc_t RESTRICTED_disc; +extern char nv_local; +extern Dtdisc_t _Nvdisc; +extern const char e_subscript[]; +extern const char e_nullset[]; +extern const char e_notset[]; +extern const char e_noparent[]; +extern const char e_readonly[]; +extern const char e_badfield[]; +extern const char e_restricted[]; +extern const char e_ident[]; +extern const char e_varname[]; +extern const char e_noalias[]; +extern const char e_noarray[]; +extern const char e_aliname[]; +extern const char e_badexport[]; +extern const char e_badref[]; +extern const char e_noref[]; +extern const char e_selfref[]; +extern const char e_envmarker[]; +extern const char e_badlocale[]; +extern const char e_loop[]; +extern const char e_redef[]; +#endif /* _NV_PRIVATE */ Index: src/lib/libshell/common/include/streval.h =================================================================== --- src/lib/libshell/common/include/streval.h (revision 0) +++ src/lib/libshell/common/include/streval.h (revision 740) @@ -0,0 +1,195 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef SEQPOINT +/* + * D. G. Korn + * + * arithmetic expression evaluator + */ + +/* The following only is needed for const */ +#include +#include +#if _AST_VERSION >= 20030127L +# include +#endif + +#if _ast_fltmax_double +#define LDBL_LLONG_MAX DBL_LLONG_MAX +#define LDBL_ULLONG_MAX DBL_ULLONG_MAX +#define LDBL_LLONG_MIN DBL_LLONG_MIN +#endif + +#ifndef LDBL_LLONG_MAX +# ifdef LLONG_MAX +# define LDBL_LLONG_MAX ((Sfdouble_t)LLONG_MAX) +# else +# ifdef LLONG_MAX +# define LDBL_LLONG_MAX ((Sfdouble_t)LLONG_MAX) +# else +# define LDBL_LLONG_MAX ((Sfdouble_t)((((Sflong_t)1) << (8*sizeof(Sflong_t)-1)) -1 )) +# endif +# endif +#endif +#ifndef LDBL_ULLONG_MAX +# ifdef ULLONG_MAX +# define LDBL_ULLONG_MAX ((Sfdouble_t)ULLONG_MAX) +# else +# define LDBL_ULLONG_MAX (2.*((Sfdouble_t)LDBL_LLONG_MAX)) +# endif +#endif +#ifndef LDBL_LLONG_MIN +# ifdef LLONG_MIN +# define LDBL_LLONG_MIN ((Sfdouble_t)LLONG_MIN) +# else +# define LDBL_LLONG_MIN (-LDBL_LLONG_MAX) +# endif +#endif +#ifndef LDBL_DIG +# define LDBL_DIG DBL_DIG +#endif + +struct lval +{ + char *value; + Sfdouble_t (*fun)(Sfdouble_t,...); + const char *expr; + short flag; + char isfloat; + char nargs; + short emode; + short level; + short elen; +}; + +struct mathtab +{ + char fname[16]; + Sfdouble_t (*fnptr)(Sfdouble_t,...); +}; + +typedef struct _arith_ +{ + unsigned char *code; + const char *expr; + Sfdouble_t (*fun)(const char**,struct lval*,int,Sfdouble_t); + short size; + short staksize; + short emode; + short elen; +} Arith_t; +#define ARITH_COMP 04 /* set when compile separate from execute */ + +#define MAXPREC 15 /* maximum precision level */ +#define SEQPOINT 0200 /* sequence point */ +#define NOASSIGN 0100 /* assignment legal with this operator */ +#define RASSOC 040 /* right associative */ +#define NOFLOAT 020 /* illegal with floating point */ +#define PRECMASK 017 /* precision bit mask */ + +#define A_EOF 1 +#define A_NEQ 2 +#define A_NOT 3 +#define A_MOD 4 +#define A_ANDAND 5 +#define A_AND 6 +#define A_LPAR 7 +#define A_RPAR 8 +#define A_POW 9 +#define A_TIMES 10 +#define A_PLUSPLUS 11 +#define A_PLUS 12 +#define A_COMMA 13 +#define A_MINUSMINUS 14 +#define A_MINUS 15 +#define A_DIV 16 +#define A_LSHIFT 17 +#define A_LE 18 +#define A_LT 19 +#define A_EQ 20 +#define A_ASSIGN 21 +#define A_COLON 22 +#define A_RSHIFT 23 +#define A_GE 24 +#define A_GT 25 +#define A_QCOLON 26 +#define A_QUEST 27 +#define A_XOR 28 +#define A_OROR 29 +#define A_OR 30 +#define A_TILDE 31 +#define A_REG 32 +#define A_DIG 33 +#define A_INCR 34 +#define A_DECR 35 +#define A_PUSHV 36 +#define A_PUSHL 37 +#define A_PUSHN 38 +#define A_PUSHF 39 +#define A_STORE 40 +#define A_POP 41 +#define A_SWAP 42 +#define A_UMINUS 43 +#define A_JMPZ 44 +#define A_JMPNZ 45 +#define A_JMP 46 +#define A_CALL0 47 +#define A_CALL1 48 +#define A_CALL2 49 +#define A_CALL3 50 +#define A_DOT 51 +#define A_LIT 52 +#define A_NOTNOT 53 + + +/* define error messages */ +extern const unsigned char strval_precedence[35]; +extern const char strval_states[64]; +extern const char e_moretokens[]; +extern const char e_argcount[]; +extern const char e_paren[]; +extern const char e_badnum[]; +extern const char e_badcolon[]; +extern const char e_recursive[]; +extern const char e_divzero[]; +extern const char e_synbad[]; +extern const char e_notlvalue[]; +extern const char e_function[]; +extern const char e_questcolon[]; +extern const char e_incompatible[]; +extern const char e_domain[]; +extern const char e_overflow[]; +extern const char e_singularity[]; +extern const char e_dict[]; +extern const char e_charconst[]; +extern const struct mathtab shtab_math[]; + +/* function code for the convert function */ + +#define LOOKUP 0 +#define ASSIGN 1 +#define VALUE 2 +#define MESSAGE 3 + +extern Sfdouble_t strval(const char*,char**,Sfdouble_t(*)(const char**,struct lval*,int,Sfdouble_t),int); +extern Arith_t *arith_compile(const char*,char**,Sfdouble_t(*)(const char**,struct lval*,int,Sfdouble_t),int); +extern Sfdouble_t arith_exec(Arith_t*); +#endif /* !SEQPOINT */ Index: src/lib/libshell/common/include/defs.h =================================================================== --- src/lib/libshell/common/include/defs.h (revision 0) +++ src/lib/libshell/common/include/defs.h (revision 740) @@ -0,0 +1,377 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * David Korn + * AT&T Labs + * + * Shell interface private definitions + * + */ + +#include +#include +#include +#include "FEATURE/options" +#include +#include +#include "fault.h" +#include "argnod.h" + +#ifndef pointerof +#define pointerof(x) ((void*)((char*)0+(x))) +#endif + +#define env_change() (++ast.env_serial) +#if SHOPT_ENV +# include +#else +# define Env_t void +# define sh_envput(e,p) env_change() +# define env_delete(e,p) env_change() +#endif + +/* + * note that the first few fields have to be the same as for + * Shscoped_t in + */ +struct sh_scoped +{ + struct sh_scoped *prevst; /* pointer to previous state */ + int dolc; + char **dolv; + char *cmdname; + char *filename; + int lineno; + Dt_t *save_tree; /* var_tree for calling function */ + struct sh_scoped *self; /* pointer to copy of this scope*/ + Dt_t *var_local; /* local level variables for name() */ + struct slnod *staklist; /* link list of function stacks */ + int states; + int breakcnt; + int execbrk; + int loopcnt; + int firstline; + int32_t optindex; + int32_t optnum; + int32_t tmout; /* value for TMOUT */ + short optchar; + short opterror; + int ioset; + unsigned short trapmax; + char *trap[SH_DEBUGTRAP+1]; + char **trapcom; + char **otrapcom; + void *timetrap; +}; + +struct limits +{ + long arg_max; /* max arg+env exec() size */ + int open_max; /* maximum number of file descriptors */ + int clk_tck; /* number of ticks per second */ + int child_max; /* maxumum number of children */ + int ngroups_max; /* maximum number of process groups */ + unsigned char posix_version; /* posix version number */ + unsigned char posix_jobcontrol;/* non-zero for job control systems */ + unsigned char fs3d; /* non-zero for 3-d file system */ +}; + +#define _SH_PRIVATE \ + struct sh_scoped st; /* scoped information */ \ + struct limits lim; /* run time limits */ \ + Sfio_t *heredocs; /* current here-doc temp file */ \ + Sfio_t *funlog; /* for logging function definitions */ \ + int **fdptrs; /* pointer to file numbers */ \ + int savexit; \ + char *lastarg; \ + char *lastpath; /* last alsolute path found */ \ + int path_err; /* last error on path search */ \ + Dt_t *track_tree; /* for tracked aliases*/ \ + Namval_t *bltin_nodes; /* pointer to built-in variables */ \ + Dt_t *var_base; /* global level variables */ \ + Namval_t *namespace; /* current active namespace*/ \ + Namval_t *last_table; /* last table used in last nv_open */ \ + Sfio_t *outpool; /* ouput stream pool */ \ + long timeout; /* read timeout */ \ + short curenv; /* current subshell number */ \ + short jobenv; /* subshell number for jobs */ \ + int nextprompt; /* next prompt is PS */ \ + Namval_t *bltin_cmds; /* pointer to built-in commands */ \ + Namval_t *posix_fun; /* points to last name() function */ \ + int infd; /* input file descriptor */ \ + char *outbuff; /* pointer to output buffer */ \ + char *errbuff; /* pointer to stderr buffer */ \ + char *prompt; /* pointer to prompt string */ \ + char *shname; /* shell name */ \ + char *shpath; /* path name of shell */ \ + char *user; /* name of real user for pfsh */ \ + char *comdiv; /* points to sh -c argument */ \ + char *prefix; /* prefix for compound assignment */ \ + sigjmp_buf *jmplist; /* longjmp return stack */ \ + char **sigmsg; /* points to signal messages */ \ + int oldexit; \ + uid_t userid,euserid; /* real and effective user id */ \ + gid_t groupid,egroupid;/* real and effective group id */ \ + pid_t pid; /* process id of shell */ \ + pid_t bckpid; /* background process id */ \ + pid_t cpid; \ + int32_t ppid; /* parent process id of shell */ \ + int topfd; \ + int sigmax; /* maximum number of signals */ \ + int savesig; \ + unsigned char *sigflag; /* pointer to signal states */ \ + char intrap; \ + char login_sh; \ + char lastbase; \ + char forked; \ + char binscript; \ + char deftype; \ + char used_pos; /* used postional parameter */\ + unsigned char lastsig; /* last signal received */ \ + char *readscript; /* set before reading a script */ \ + int *inpipe; /* input pipe pointer */ \ + int *outpipe; /* output pipe pointer */ \ + int cpipe[2]; \ + int coutpipe; \ + int inuse_bits; \ + struct argnod *envlist; \ + struct dolnod *arglist; \ + int fn_depth; \ + int dot_depth; \ + int hist_depth; \ + int xargmin; \ + int xargmax; \ + int xargexit; \ + mode_t mask; \ + long nforks; \ + Env_t *env; \ + void *init_context; \ + void *mac_context; \ + void *lex_context; \ + void *arg_context; \ + void *ed_context; \ + void *job_context; \ + void *pathlist; \ + void *defpathlist; \ + void *cdpathlist; \ + char **argaddr; \ + void *optlist; \ + int optcount ; \ + struct sh_scoped global; \ + struct checkpt checkbase; \ + Shinit_f userinit; \ + Shbltin_f bltinfun; \ + Shwait_f waitevent; \ + char *cur_line; \ + char *rcfile; \ + char **login_files; \ + short offsets[10]; \ + Sfio_t **sftable; \ + unsigned char *fdstatus; \ + const char *pwd; \ + History_t *hist_ptr; \ + char universe; \ + void *jmpbuffer; \ + void *mktype; \ + Sfio_t *strbuf; \ + Dt_t *last_root; \ + char ifstable[256]; \ + Shopt_t offoptions; + +#include + + +/* error exits from various parts of shell */ +#define NIL(type) ((type)0) + +#define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x))) + +#define exitset() (sh.savexit=sh.exitval) + +#ifndef SH_DICT +#define SH_DICT (void*)e_dict +#endif + +#ifndef SH_CMDLIB_DIR +#define SH_CMDLIB_DIR "/opt/ast/bin" +#endif + +/* states */ +/* low numbered states are same as options */ +#define SH_NOFORK 0 /* set when fork not necessary, not a state */ +#define SH_COMPLETE 0 /* set for command completion */ +#define SH_FORKED 7 /* set when process has been forked */ +#define SH_PROFILE 8 /* set when processing profiles */ +#define SH_NOALIAS 9 /* do not expand non-exported aliases */ +#define SH_NOTRACK 10 /* set to disable sftrack() function */ +#define SH_STOPOK 11 /* set for stopable builtins */ +#define SH_GRACE 12 /* set for timeout grace period */ +#define SH_TIMING 13 /* set while timing pipelines */ +#define SH_DEFPATH 14 /* set when using default path */ +#define SH_INIT 15 /* set when initializing the shell */ +#define SH_TTYWAIT 16 /* waiting for keyboard input */ +#define SH_FCOMPLETE 17 /* set for filename completion */ +#define SH_PREINIT 18 /* set with SH_INIT before parsing options */ + +#define SH_BASH 41 +#define SH_BRACEEXPAND 42 +#define SH_POSIX 46 +#define SH_MULTILINE 47 + +#define SH_NOPROFILE 78 +#define SH_NOUSRPROFILE 79 +#define SH_LOGIN_SHELL 67 +#define SH_COMMANDLINE 0x100 +#define SH_BASHEXTRA 0x200 +#define SH_BASHOPT 0x400 + +#define SH_ID "ksh" /* ksh id */ +#define SH_STD "sh" /* standard sh id */ + +/* defines for sh_type() */ + +#define SH_TYPE_SH 001 +#define SH_TYPE_KSH 002 +#define SH_TYPE_BASH 004 +#define SH_TYPE_LOGIN 010 +#define SH_TYPE_PROFILE 020 +#define SH_TYPE_RESTRICTED 040 + +#if SHOPT_BASH +# ifndef SHOPT_HISTEXPAND +# define SHOPT_HISTEXPAND 1 +# endif +/* + * define for all the bash options + */ +# define SH_CDABLE_VARS 51 +# define SH_CDSPELL 52 +# define SH_CHECKHASH 53 +# define SH_CHECKWINSIZE 54 +# define SH_CMDHIST 55 +# define SH_DOTGLOB 56 +# define SH_EXECFAIL 57 +# define SH_EXPAND_ALIASES 58 +# define SH_EXTGLOB 59 +# define SH_HOSTCOMPLETE 63 +# define SH_HUPONEXIT 64 +# define SH_INTERACTIVE_COMM 65 +# define SH_LITHIST 66 +# define SH_MAILWARN 68 +# define SH_NOEMPTYCMDCOMPL 69 +# define SH_NOCASEGLOB 70 +# define SH_NULLGLOB 71 +# define SH_PHYSICAL 45 +# define SH_PROGCOMP 72 +# define SH_PROMPTVARS 73 +# define SH_RESTRICTED2 74 +# define SH_SHIFT_VERBOSE 75 +# define SH_SOURCEPATH 76 +# define SH_XPG_ECHO 77 +#endif + +#if SHOPT_HISTEXPAND +# define SH_HISTAPPEND 60 +# define SH_HISTEXPAND 43 +# define SH_HISTORY2 44 +# define SH_HISTREEDIT 61 +# define SH_HISTVERIFY 62 +#endif + +#ifndef PIPE_BUF +# define PIPE_BUF 512 +#endif + +#define MATCH_MAX 64 + +extern int sh_addlib(void*); +extern void *sh_argopen(Shell_t*); +extern Namval_t *sh_assignok(Namval_t*,int); +extern char *sh_checkid(char*,char*); +extern int sh_debug(const char*,const char*,const char*,char *const[],int); +extern int sh_echolist(Sfio_t*, int, char**); +extern struct argnod *sh_endword(int); +extern char **sh_envgen(void); +#if SHOPT_ENV +extern void sh_envput(Env_t*, Namval_t*); +#endif +extern void sh_envnolocal(Namval_t*,void*); +extern Sfdouble_t sh_arith(const char*); +extern void *sh_arithcomp(char*); +extern pid_t sh_fork(int,int*); +extern pid_t _sh_fork(pid_t, int ,int*); +extern char *sh_mactrim(char*,int); +extern int sh_macexpand(struct argnod*,struct argnod**,int); +extern void sh_machere(Sfio_t*, Sfio_t*, char*); +extern void *sh_macopen(Shell_t*); +extern char *sh_macpat(struct argnod*,int); +extern char *sh_mactry(char*); +extern void sh_printopts(Shopt_t,int,Shopt_t*); +extern int sh_readline(Shell_t*,char**,int,int,long); +extern Sfio_t *sh_sfeval(char*[]); +extern void sh_setmatch(const char*,int,int,int[]); +extern Dt_t *sh_subaliastree(int); +extern Dt_t *sh_subfuntree(int); +extern int sh_subsavefd(int); +extern void sh_subtmpfile(void); +extern char *sh_substitute(const char*,const char*,char*); +extern const char *_sh_translate(const char*); +extern int sh_trace(char*[],int); +extern void sh_trim(char*); +extern int sh_type(const char*); +extern void sh_utol(const char*, char*); +extern int sh_whence(char**,int); + +#ifndef ERROR_dictionary +# define ERROR_dictionary(s) (s) +#endif +#define sh_translate(s) _sh_translate(ERROR_dictionary(s)) + +#define WBITS (sizeof(long)*8) +#define WMASK (0xff) + +#define is_option(s,x) ((s)->v[((x)&WMASK)/WBITS] & (1L << ((x) % WBITS))) +#define on_option(s,x) ((s)->v[((x)&WMASK)/WBITS] |= (1L << ((x) % WBITS))) +#define off_option(s,x) ((s)->v[((x)&WMASK)/WBITS] &= ~(1L << ((x) % WBITS))) +#define sh_isoption(x) is_option(&sh.options,x) +#define sh_onoption(x) on_option(&sh.options,x) +#define sh_offoption(x) off_option(&sh.options,x) + + +#define sh_state(x) ( 1<<(x)) +#define sh_isstate(x) (sh.st.states&sh_state(x)) +#define sh_onstate(x) (sh.st.states |= sh_state(x)) +#define sh_offstate(x) (sh.st.states &= ~sh_state(x)) +#define sh_getstate() (sh.st.states) +#define sh_setstate(x) (sh.st.states = (x)) + +#define sh_sigcheck() do{if(sh.trapnote&SH_SIGSET)sh_exit(SH_EXITSIG);} while(0) + +extern int32_t sh_mailchk; +extern const char e_dict[]; + +/* sh_printopts() mode flags -- set --[no]option by default */ + +#define PRINT_VERBOSE 0x01 /* option on|off list */ +#define PRINT_ALL 0x02 /* list unset iptions too */ +#define PRINT_NO_HEADER 0x04 /* omit listing header */ +#define PRINT_SHOPT 0x08 /* shopt -s|-u */ +#define PRINT_TABLE 0x10 /* table of all options */ Index: src/lib/libshell/common/include/shtable.h =================================================================== --- src/lib/libshell/common/include/shtable.h (revision 0) +++ src/lib/libshell/common/include/shtable.h (revision 740) @@ -0,0 +1,65 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef _SHTABLE_H + +/* + * David Korn + * AT&T Labs + * + * Interface definitions read-only data tables for shell + * + */ + +#define _SHTABLE_H 1 + +typedef struct shtable1 +{ + const char *sh_name; + unsigned sh_number; +} Shtable_t; + +struct shtable2 +{ + const char *sh_name; + unsigned sh_number; + const char *sh_value; +}; + +struct shtable3 +{ + const char *sh_name; + unsigned sh_number; + int (*sh_value)(int, char*[], void*); +}; + +#define sh_lookup(name,value) (sh_locate(name,(Shtable_t*)(value),sizeof(*(value)))->sh_number) +extern const Shtable_t shtab_testops[]; +extern const Shtable_t shtab_options[]; +extern const Shtable_t shtab_attributes[]; +extern const struct shtable2 shtab_variables[]; +extern const struct shtable2 shtab_aliases[]; +extern const struct shtable2 shtab_signals[]; +extern const struct shtable3 shtab_builtins[]; +extern const Shtable_t shtab_reserved[]; +extern const Shtable_t *sh_locate(const char*, const Shtable_t*, int); +extern int sh_lookopt(const char*, int*); + +#endif /* SH_TABLE_H */ Index: src/lib/libshell/common/include/shlex.h =================================================================== --- src/lib/libshell/common/include/shlex.h (revision 0) +++ src/lib/libshell/common/include/shlex.h (revision 740) @@ -0,0 +1,152 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef NOTSYM +/* + * UNIX shell + * Written by David Korn + * These are the definitions for the lexical analyzer + */ + +#include +#include "FEATURE/options" +#include "shnodes.h" +#include "shtable.h" +#include "lexstates.h" + +struct shlex_t +{ + Shell_t *sh; /* pointer to the interpreter */ + struct argnod *arg; /* current word */ + struct ionod *heredoc; /* pending here document list */ + int token; /* current token number */ + int lastline; /* last line number */ + int lasttok; /* previous token number */ + int digits; /* numerical value with word token */ + char aliasok; /* on when alias is legal */ + char assignok; /* on when name=value is legal */ + int inlineno; /* saved value of sh.inlineno */ + int firstline; /* saved value of sh.st.firstline */ + int comsub; /* parsing command substitution */ +#if SHOPT_KIA + Sfio_t *kiafile; /* kia output file */ + Sfio_t *kiatmp; /* kia reference file */ + unsigned long script; /* script entity number */ + unsigned long fscript; /* script file entity number */ + unsigned long current; /* current entity number */ + unsigned long unknown; /* entity number */ + off_t kiabegin; /* offset of first entry */ + char *scriptname; /* name of script file */ + Dt_t *entity_tree; /* for entity ids */ +#endif /* SHOPT_KIA */ +}; + +/* symbols for parsing */ +#define NL '\n' +#define NOTSYM '!' +#define SYMRES 0400 /* reserved word symbols */ +#define DOSYM (SYMRES|01) +#define FISYM (SYMRES|02) +#define ELIFSYM (SYMRES|03) +#define ELSESYM (SYMRES|04) +#define INSYM (SYMRES|05) +#define THENSYM (SYMRES|06) +#define DONESYM (SYMRES|07) +#define ESACSYM (SYMRES|010) +#define IFSYM (SYMRES|011) +#define FORSYM (SYMRES|012) +#define WHILESYM (SYMRES|013) +#define UNTILSYM (SYMRES|014) +#define CASESYM (SYMRES|015) +#define FUNCTSYM (SYMRES|016) +#define SELECTSYM (SYMRES|017) +#define TIMESYM (SYMRES|020) +#define NSPACESYM (SYMRES|021) + +#define SYMREP 01000 /* symbols for doubled characters */ +#define BREAKCASESYM (SYMREP|';') +#define ANDFSYM (SYMREP|'&') +#define ORFSYM (SYMREP|'|') +#define IOAPPSYM (SYMREP|'>') +#define IODOCSYM (SYMREP|'<') +#define EXPRSYM (SYMREP|'(') +#define BTESTSYM (SYMREP|'[') +#define ETESTSYM (SYMREP|']') + +#define SYMMASK 0170000 +#define SYMPIPE 010000 /* trailing '|' */ +#define SYMLPAR 020000 /* trailing LPAREN */ +#define SYMAMP 040000 /* trailing '&' */ +#define SYMGT 0100000 /* trailing '>' */ +#define SYMSEMI 0110000 /* trailing ';' */ +#define SYMSHARP 0120000 /* trailing '#' */ +#define IOSEEKSYM (SYMSHARP|'<') +#define IOMOV0SYM (SYMAMP|'<') +#define IOMOV1SYM (SYMAMP|'>') +#define FALLTHRUSYM (SYMAMP|';') +#define COOPSYM (SYMAMP|'|') +#define IORDWRSYM (SYMGT|'<') +#define IOCLOBSYM (SYMPIPE|'>') +#define IPROCSYM (SYMLPAR|'<') +#define OPROCSYM (SYMLPAR|'>') +#define EOFSYM 04000 /* end-of-file */ +#define TESTUNOP 04001 +#define TESTBINOP 04002 +#define LABLSYM 04003 +#define IOVNAME 04004 + +/* additional parser flag, others in */ +#define SH_EMPTY 04 +#define SH_NOIO 010 +#define SH_ASSIGN 020 +#define SH_FUNDEF 040 +#define SH_ARRAY 0100 +#define SH_SEMI 0200 /* semi-colon after NL ok */ + +#define SH_COMPASSIGN 010 /* allow compound assignments only */ + +typedef struct _shlex_ +{ + struct shlex_t _shlex; +#ifdef _SHLEX_PRIVATE + _SHLEX_PRIVATE +#endif +} Lex_t; + +#define shlex (((Lex_t*)(sh.lex_context))->_shlex) +extern const char e_unexpected[]; +extern const char e_unmatched[]; +extern const char e_endoffile[]; +extern const char e_newline[]; + +/* odd chars */ +#define LBRACE '{' +#define RBRACE '}' +#define LPAREN '(' +#define RPAREN ')' +#define LBRACT '[' +#define RBRACT ']' + +extern int sh_lex(); +extern Lex_t *sh_lexopen(Lex_t*, Shell_t*, int); +extern void sh_lexskip(int,int,int); +extern void sh_syntax(void); + +#endif /* !NOTSYM */ Index: src/lib/libshell/common/include/edit.h =================================================================== --- src/lib/libshell/common/include/edit.h (revision 0) +++ src/lib/libshell/common/include/edit.h (revision 740) @@ -0,0 +1,257 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef SEARCHSIZE +/* + * edit.h - common data structure for vi and emacs edit options + * + * David Korn + * AT&T Labs + * + */ + +#define SEARCHSIZE 80 + +#include "FEATURE/options" +#include "FEATURE/locale" +#if !SHOPT_VSH && !SHOPT_ESH +# define ed_winsize() (SEARCHSIZE) +#else + +#if !KSHELL +# include +# include +# include +#endif /* KSHELL */ + +#include "FEATURE/setjmp" +#include "terminal.h" + +#define STRIP 0377 +#define LOOKAHEAD 80 + +#if SHOPT_MULTIBYTE +# ifndef ESS_MAXCHAR +# include "national.h" +# endif /* ESS_MAXCHAR */ + typedef wchar_t genchar; +# define CHARSIZE (sizeof(wchar_t)<=2?3:sizeof(wchar_t)) +#else + typedef char genchar; +# define CHARSIZE 1 +#endif /* SHOPT_MULTIBYTE */ + +#define TABSIZE 8 +#define PRSIZE 160 +#define MAXLINE 1024 /* longest edit line permitted */ + +typedef struct _edit_pos +{ + unsigned short line; + unsigned short col; +} Edpos_t; + +typedef struct edit +{ + sigjmp_buf e_env; + int e_kill; + int e_erase; + int e_werase; + int e_eof; + int e_lnext; + int e_fchar; + int e_plen; /* length of prompt string */ + int e_crlf; /* zero if cannot return to beginning of line */ + int e_llimit; /* line length limit */ + int e_hline; /* current history line number */ + int e_hloff; /* line number offset for command */ + int e_hismin; /* minimum history line number */ + int e_hismax; /* maximum history line number */ + int e_raw; /* set when in raw mode or alt mode */ + int e_cur; /* current line position */ + int e_eol; /* end-of-line position */ + int e_pcur; /* current physical line position */ + int e_peol; /* end of physical line position */ + int e_mode; /* edit mode */ + int e_lookahead; /* index in look-ahead buffer */ + int e_repeat; + int e_saved; + int e_fcol; /* first column */ + int e_ucol; /* column for undo */ + int e_wsize; /* width of display window */ + char *e_outbase; /* pointer to start of output buffer */ + char *e_outptr; /* pointer to position in output buffer */ + char *e_outlast; /* pointer to end of output buffer */ + genchar *e_inbuf; /* pointer to input buffer */ + char *e_prompt; /* pointer to buffer containing the prompt */ + genchar *e_ubuf; /* pointer to the undo buffer */ + genchar *e_killbuf; /* pointer to delete buffer */ + char e_search[SEARCHSIZE]; /* search string */ + genchar *e_Ubuf; /* temporary workspace buffer */ + genchar *e_physbuf; /* temporary workspace buffer */ + int e_lbuf[LOOKAHEAD];/* pointer to look-ahead buffer */ + int e_fd; /* file descriptor */ + int e_ttyspeed; /* line speed, also indicates tty parms are valid */ + int e_tabcount; +#ifdef _hdr_utime + ino_t e_tty_ino; + dev_t e_tty_dev; + char *e_tty; +#endif +#if SHOPT_OLDTERMIO + char e_echoctl; + char e_tcgeta; + struct termio e_ott; +#endif +#if SHOPT_MULTIBYTE + int e_curchar; + int e_cursize; +#endif + int *e_globals; /* global variables */ + genchar *e_window; /* display window image */ + char e_inmacro; /* processing macro expansion */ +#if KSHELL + char e_vi_insert[2]; /* for sh_keytrap */ + int32_t e_col; /* for sh_keytrap */ +#else + char e_prbuff[PRSIZE]; /* prompt buffer */ +#endif /* KSHELL */ + struct termios e_ttyparm; /* initial tty parameters */ + struct termios e_nttyparm; /* raw tty parameters */ + struct termios e_savetty; /* saved terminal state */ + int e_savefd; /* file descriptor for saved terminal state */ + char e_macro[4]; /* macro buffer */ + void *e_vi; /* vi specific data */ + void *e_emacs; /* emacs specific data */ + Shell_t *sh; /* interpreter pointer */ + char *e_stkptr; /* saved stack pointer */ + int e_stkoff; /* saved stack offset */ + char **e_clist; /* completion list after = */ + int e_nlist; /* number of elements on completion list */ + int e_multiline; /* allow multiple lines for editing */ + int e_winsz; /* columns in window */ + Edpos_t e_curpos; /* cursor line and column */ + Namval_t *e_default; /* variable containing default value */ + Namval_t *e_term; /* TERM variable */ + char e_termname[80]; /* terminal name */ +} Edit_t; + +#undef MAXWINDOW +#define MAXWINDOW 300 /* maximum width window */ +#define FAST 2 +#define SLOW 1 +#define ESC cntl('[') +#define UEOF -2 /* user eof char synonym */ +#define UINTR -3 /* user intr char synonym */ +#define UERASE -4 /* user erase char synonym */ +#define UKILL -5 /* user kill char synonym */ +#define UWERASE -6 /* user word erase char synonym */ +#define ULNEXT -7 /* user next literal char synonym */ + +#if ( 'a' == 97) /* ASCII? */ +# define cntl(x) (x&037) +#else +# define cntl(c) (c=='D'?55:(c=='E'?45:(c=='F'?46:(c=='G'?'\a':(c=='H'?'\b': \ + (c=='I'?'\t':(c=='J'?'\n':(c=='T'?60:(c=='U'?61:(c=='V'?50: \ + (c=='W'?38:(c=='Z'?63:(c=='['?39:(c==']'?29: \ + (c<'J'?c+1-'A':(c+10-'J')))))))))))))))) +#endif + +#if !KSHELL +# define STRIP 0377 +# define GMACS 1 +# define EMACS 2 +# define VIRAW 4 +# define EDITVI 8 +# define NOHIST 16 +# define EDITMASK 15 +# define is_option(m) (opt_flag&(m)) + extern char opt_flag; +# ifdef SYSCALL +# define read(fd,buff,n) syscall(3,fd,buff,n) +# else +# define read(fd,buff,n) rEAd(fd,buff,n) +# endif /* SYSCALL */ +#endif /* KSHELL */ + +extern void ed_crlf(Edit_t*); +extern void ed_putchar(Edit_t*, int); +extern void ed_ringbell(void); +extern void ed_setup(Edit_t*,int, int); +extern void ed_flush(Edit_t*); +extern int ed_getchar(Edit_t*,int); +extern int ed_virt_to_phys(Edit_t*,genchar*,genchar*,int,int,int); +extern int ed_window(void); +extern void ed_ungetchar(Edit_t*,int); +extern int ed_viread(void*, int, char*, int, int); +extern int ed_read(void*, int, char*, int, int); +extern int ed_emacsread(void*, int, char*, int, int); +extern Edpos_t ed_curpos(Edit_t*, genchar*, int, int, Edpos_t); +extern int ed_setcursor(Edit_t*, genchar*, int, int, int); +#if KSHELL + extern int ed_macro(Edit_t*,int); + extern int ed_expand(Edit_t*, char[],int*,int*,int,int); + extern int ed_fulledit(Edit_t*); + extern void *ed_open(Shell_t*); +#endif /* KSHELL */ +# if SHOPT_MULTIBYTE + extern int ed_internal(const char*, genchar*); + extern int ed_external(const genchar*, char*); + extern void ed_gencpy(genchar*,const genchar*); + extern void ed_genncpy(genchar*,const genchar*,int); + extern int ed_genlen(const genchar*); + extern int ed_setwidth(const char*); +# endif /* SHOPT_MULTIBYTE */ + +extern const char e_runvi[]; +#if !KSHELL + extern const char e_version[]; +#endif /* KSHELL */ + +#if SHOPT_HISTEXPAND + +/* flags */ + +#define HIST_EVENT 0x1 /* event designator seen */ +#define HIST_QUESTION 0x2 /* question mark event designator */ +#define HIST_HASH 0x4 /* hash event designator */ +#define HIST_WORDDSGN 0x8 /* word designator seen */ +#define HIST_QUICKSUBST 0x10 /* quick substition designator seen */ +#define HIST_SUBSTITUTE 0x20 /* for substition loop */ +#define HIST_NEWLINE 0x40 /* newline in squashed white space */ + +/* modifier flags */ + +#define HIST_PRINT 0x100 /* print new command */ +#define HIST_QUOTE 0x200 /* quote resulting history line */ +#define HIST_QUOTE_BR 0x400 /* quote every word on space break */ +#define HIST_GLOBALSUBST 0x800 /* apply substition globally */ + +#define HIST_ERROR 0x1000 /* an error ocurred */ + +/* flags to be returned */ + +#define HIST_FLAG_RETURN_MASK (HIST_EVENT|HIST_PRINT|HIST_ERROR) + +extern int hist_expand(const char *, char **); +#endif + +#endif +#endif Index: src/lib/libshell/common/include/version.h =================================================================== --- src/lib/libshell/common/include/version.h (revision 0) +++ src/lib/libshell/common/include/version.h (revision 740) @@ -0,0 +1,20 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#define SH_RELEASE "1993-12-28 s+" Index: src/lib/libshell/common/include/timeout.h =================================================================== --- src/lib/libshell/common/include/timeout.h (revision 0) +++ src/lib/libshell/common/include/timeout.h (revision 740) @@ -0,0 +1,30 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +/* + * UNIX shell + * + * AT&T Labs + * + */ + +#define TGRACE 60 /* grace period before termination */ + /* The time_warn message contains this number */ +extern const char e_timeout[]; +extern const char e_timewarn[]; Index: src/lib/libshell/common/include/env.h =================================================================== --- src/lib/libshell/common/include/env.h (revision 0) +++ src/lib/libshell/common/include/env.h (revision 740) @@ -0,0 +1,50 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#ifndef _ENV_H +#define _ENV_H 1 + +#ifdef _BLD_env +# ifdef __EXPORT__ +# define export __EXPORT__ +# endif +#else + typedef void *Env_t; +#endif + +/* for use with env_open */ +#define ENV_STABLE (-1) + +/* for third agument to env_add */ +#define ENV_MALLOCED 1 +#define ENV_STRDUP 2 + +extern void env_close(Env_t*); +extern int env_add(Env_t*, const char*, int); +extern int env_delete(Env_t*, const char*); +extern char **env_get(Env_t*); +extern Env_t *env_open(char**,int); +extern Env_t *env_scope(Env_t*,int); + +#undef extern + +#endif + + Index: src/lib/libshell/common/edit/vi.c =================================================================== --- src/lib/libshell/common/edit/vi.c (revision 0) +++ src/lib/libshell/common/edit/vi.c (revision 740) @@ -0,0 +1,2635 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* Adapted for ksh by David Korn */ +/*+ VI.C P.D. Sullivan + * + * One line editor for the shell based on the vi editor. + * + * Questions to: + * P.D. Sullivan + * cbosgd!pds +-*/ + + +#if KSHELL +# include "defs.h" +#else +# include +# include "FEATURE/options" +#endif /* KSHELL */ +#include +#include "io.h" + +#include "history.h" +#include "edit.h" +#include "terminal.h" +#include "FEATURE/time" + +#if SHOPT_OLDTERMIO +# undef ECHOCTL +# define echoctl (vp->ed->e_echoctl) +#else +# ifdef ECHOCTL +# define echoctl ECHOCTL +# else +# define echoctl 0 +# endif /* ECHOCTL */ +#endif /*SHOPT_OLDTERMIO */ + +#ifndef FIORDCHK +# define NTICKS 5 /* number of ticks for typeahead */ +#endif /* FIORDCHK */ + +#define MAXCHAR MAXLINE-2 /* max char per line */ + +#if SHOPT_MULTIBYTE +# include "lexstates.h" +# define gencpy(a,b) ed_gencpy(a,b) +# define genncpy(a,b,n) ed_genncpy(a,b,n) +# define genlen(str) ed_genlen(str) +# define digit(c) ((c&~STRIP)==0 && isdigit(c)) +# define is_print(c) ((c&~STRIP) || isprint(c)) +# if !_lib_iswprint && !defined(iswprint) +# define iswprint(c) ((c&~0177) || isprint(c)) +# endif + static int _isalph(int); + static int _ismetach(int); + static int _isblank(int); +# undef isblank +# define isblank(v) _isblank(virtual[v]) +# define isalph(v) _isalph(virtual[v]) +# define ismetach(v) _ismetach(virtual[v]) +#else + static genchar _c; +# define gencpy(a,b) strcpy((char*)(a),(char*)(b)) +# define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n) +# define genlen(str) strlen(str) +# define isalph(v) ((_c=virtual[v])=='_'||isalnum(_c)) +# undef isblank +# define isblank(v) isspace(virtual[v]) +# define ismetach(v) ismeta(virtual[v]) +# define digit(c) isdigit(c) +# define is_print(c) isprint(c) +#endif /* SHOPT_MULTIBYTE */ + +#if ( 'a' == 97) /* ASCII? */ +# define fold(c) ((c)&~040) /* lower and uppercase equivalent */ +#else +# define fold(c) ((c)|0100) /* lower and uppercase equivalent */ +#endif + +#ifndef iswascii +#define iswascii(c) (!((c)&(~0177))) +#endif + +typedef struct _vi_ +{ + int direction; + int lastmacro; + char addnl; /* boolean - add newline flag */ + char last_find; /* last find command */ + char last_cmd; /* last command */ + char repeat_set; + char nonewline; + int findchar; /* last find char */ + genchar *lastline; + int first_wind; /* first column of window */ + int last_wind; /* last column in window */ + int lastmotion; /* last motion */ + int long_char; /* line bigger than window */ + int long_line; /* line bigger than window */ + int ocur_phys; /* old current physical position */ + int ocur_virt; /* old last virtual position */ + int ofirst_wind; /* old window first col */ + int o_v_char; /* prev virtual[ocur_virt] */ + int repeat; /* repeat count for motion cmds */ + int lastrepeat; /* last repeat count for motion cmds */ + int u_column; /* undo current column */ + int U_saved; /* original virtual saved */ + genchar *U_space; /* used for U command */ + genchar *u_space; /* used for u command */ +#ifdef FIORDCHK + clock_t typeahead; /* typeahead occurred */ +#else + int typeahead; /* typeahead occurred */ +#endif /* FIORDCHK */ +#if SHOPT_MULTIBYTE + int bigvi; +#endif + Edit_t *ed; /* pointer to edit data */ +} Vi_t; + +#define editb (*vp->ed) + +#undef putchar +#define putchar(c) ed_putchar(vp->ed,c) + +#define crallowed editb.e_crlf +#define cur_virt editb.e_cur /* current virtual column */ +#define cur_phys editb.e_pcur /* current phys column cursor is at */ +#define curhline editb.e_hline /* current history line */ +#define first_virt editb.e_fcol /* first allowable column */ +#define globals editb.e_globals /* local global variables */ +#define histmin editb.e_hismin +#define histmax editb.e_hismax +#define last_phys editb.e_peol /* last column in physical */ +#define last_virt editb.e_eol /* last column */ +#define lsearch editb.e_search /* last search string */ +#define lookahead editb.e_lookahead /* characters in buffer */ +#define previous editb.e_lbuf /* lookahead buffer */ +#define max_col editb.e_llimit /* maximum column */ +#define Prompt editb.e_prompt /* pointer to prompt */ +#define plen editb.e_plen /* length of prompt */ +#define physical editb.e_physbuf /* physical image */ +#define usreof editb.e_eof /* user defined eof char */ +#define usrerase editb.e_erase /* user defined erase char */ +#define usrlnext editb.e_lnext /* user defined next literal */ +#define usrkill editb.e_kill /* user defined kill char */ +#define virtual editb.e_inbuf /* pointer to virtual image buffer */ +#define window editb.e_window /* window buffer */ +#define w_size editb.e_wsize /* window size */ +#define inmacro editb.e_inmacro /* true when in macro */ +#define yankbuf editb.e_killbuf /* yank/delete buffer */ + + +#define ABORT -2 /* user abort */ +#define APPEND -10 /* append chars */ +#define BAD -1 /* failure flag */ +#define BIGVI -15 /* user wants real vi */ +#define CONTROL -20 /* control mode */ +#define ENTER -25 /* enter flag */ +#define GOOD 0 /* success flag */ +#define INPUT -30 /* input mode */ +#define INSERT -35 /* insert mode */ +#define REPLACE -40 /* replace chars */ +#define SEARCH -45 /* search flag */ +#define TRANSLATE -50 /* translate virt to phys only */ + +#define INVALID (-1) /* invalid column */ + +static const char paren_chars[] = "([{)]}"; /* for % command */ + +static void cursor(Vi_t*, int); +static void del_line(Vi_t*,int); +static int getcount(Vi_t*,int); +static void getline(Vi_t*,int); +static int getrchar(Vi_t*); +static int mvcursor(Vi_t*,int); +static void pr_string(Vi_t*,const char*); +static void putstring(Vi_t*,int, int); +static void refresh(Vi_t*,int); +static void replace(Vi_t*,int, int); +static void restore_v(Vi_t*); +static void save_last(Vi_t*); +static void save_v(Vi_t*); +static int search(Vi_t*,int); +static void sync_cursor(Vi_t*); +static int textmod(Vi_t*,int,int); + +/*+ VI_READ( fd, shbuf, nchar ) + * + * This routine implements a one line version of vi and is + * called by _filbuf.c + * +-*/ + +/* + * if reedit is non-zero, initialize edit buffer with reedit chars + */ +int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit) +{ + Edit_t *ed = (Edit_t*)context; + register int i; /* general variable */ + register int term_char; /* read() termination character */ + register Vi_t *vp = ed->e_vi; + char prompt[PRSIZE+2]; /* prompt */ + genchar Physical[2*MAXLINE]; /* physical image */ + genchar Ubuf[MAXLINE]; /* used for U command */ + genchar ubuf[MAXLINE]; /* used for u command */ + genchar Window[MAXLINE]; /* window image */ + int Globals[9]; /* local global variables */ + int esc_or_hang=0; /* or hangup */ + char cntl_char=0; /* TRUE if control character present */ +#if SHOPT_RAWONLY +# define viraw 1 +#else + int viraw = (sh_isoption(SH_VIRAW) || sh.st.trap[SH_KEYTRAP]); +# ifndef FIORDCHK + clock_t oldtime, newtime; + struct tms dummy; +# endif /* FIORDCHK */ +#endif /* SHOPT_RAWONLY */ + if(!vp) + { + ed->e_vi = vp = newof(0,Vi_t,1,0); + vp->lastline = (genchar*)malloc(MAXLINE*CHARSIZE); + vp->direction = -1; + vp->ed = ed; + } + + /*** setup prompt ***/ + + Prompt = prompt; + ed_setup(vp->ed,fd, reedit); + shbuf[reedit] = 0; + +#if !SHOPT_RAWONLY + if(!viraw) + { + /*** Change the eol characters to '\r' and eof ***/ + /* in addition to '\n' and make eof an ESC */ + if(tty_alt(ERRIO) < 0) + return(reexit?reedit:ed_read(context, fd, shbuf, nchar,0)); + +#ifdef FIORDCHK + ioctl(fd,FIORDCHK,&vp->typeahead); +#else + /* time the current line to determine typeahead */ + oldtime = times(&dummy); +#endif /* FIORDCHK */ +#if KSHELL + /* abort of interrupt has occurred */ + if(sh.trapnote&SH_SIGSET) + i = -1; + else +#endif /* KSHELL */ + /*** Read the line ***/ + i = ed_read(context, fd, shbuf, nchar, 0); +#ifndef FIORDCHK + newtime = times(&dummy); + vp->typeahead = ((newtime-oldtime) < NTICKS); +#endif /* FIORDCHK */ + if(echoctl) + { + if( i <= 0 ) + { + /*** read error or eof typed ***/ + tty_cooked(ERRIO); + return(i); + } + term_char = shbuf[--i]; + if( term_char == '\r' ) + term_char = '\n'; + if( term_char=='\n' || term_char==ESC ) + shbuf[i--] = '\0'; + else + shbuf[i+1] = '\0'; + } + else + { + register int c = shbuf[0]; + + /*** Save and remove the last character if its an eol, ***/ + /* changing '\r' to '\n' */ + + if( i == 0 ) + { + /*** ESC was typed as first char of line ***/ + esc_or_hang = 1; + term_char = ESC; + shbuf[i--] = '\0'; /* null terminate line */ + } + else if( i<0 || c==usreof ) + { + /*** read error or eof typed ***/ + tty_cooked(ERRIO); + if( c == usreof ) + i = 0; + return(i); + } + else + { + term_char = shbuf[--i]; + if( term_char == '\r' ) + term_char = '\n'; +#if !defined(VEOL2) && !defined(ECHOCTL) + if(term_char=='\n') + { + tty_cooked(ERRIO); + return(i+1); + } +#endif + if( term_char=='\n' || term_char==usreof ) + { + /*** remove terminator & null terminate ***/ + shbuf[i--] = '\0'; + } + else + { + /** terminator was ESC, which is not xmitted **/ + term_char = ESC; + shbuf[i+1] = '\0'; + } + } + } + } + else +#endif /* SHOPT_RAWONLY */ + { + /*** Set raw mode ***/ + +#if !SHOPT_RAWONLY + if( editb.e_ttyspeed == 0 ) + { + /*** never did TCGETA, so do it ***/ + /* avoids problem if user does 'sh -o viraw' */ + tty_alt(ERRIO); + } +#endif /* SHOPT_RAWONLY */ + if(tty_raw(ERRIO,0) < 0 ) + return(reedit?reedit:ed_read(context, fd, shbuf, nchar,0)); + i = last_virt-1; + } + + /*** Initialize some things ***/ + + virtual = (genchar*)shbuf; +#if SHOPT_MULTIBYTE + virtual = (genchar*)roundof((char*)virtual-(char*)0,sizeof(genchar)); + shbuf[i+1] = 0; + i = ed_internal(shbuf,virtual)-1; +#endif /* SHOPT_MULTIBYTE */ + globals = Globals; + cur_phys = i + 1; + cur_virt = i; + first_virt = 0; + vp->first_wind = 0; + last_virt = i; + last_phys = i; + vp->last_wind = i; + vp->long_line = ' '; + vp->long_char = ' '; + vp->o_v_char = '\0'; + vp->ocur_phys = 0; + vp->ocur_virt = MAXCHAR; + vp->ofirst_wind = 0; + physical = Physical; + vp->u_column = INVALID - 1; + vp->U_space = Ubuf; + vp->u_space = ubuf; + window = Window; + window[0] = '\0'; + + if(!yankbuf) + yankbuf = (genchar*)malloc(MAXLINE*CHARSIZE); + if( vp->last_cmd == '\0' ) + { + /*** first time for this shell ***/ + + vp->last_cmd = 'i'; + vp->findchar = INVALID; + vp->lastmotion = '\0'; + vp->lastrepeat = 1; + vp->repeat = 1; + *yankbuf = 0; + } + + /*** fiddle around with prompt length ***/ + if( nchar+plen > MAXCHAR ) + nchar = MAXCHAR - plen; + max_col = nchar - 2; + + if( !viraw ) + { + int kill_erase = 0; + for(i=(echoctl?last_virt:0); i 0) + last_phys = ed_virt_to_phys(vp->ed,virtual,physical,last_virt,0,0); + if( last_phys >= w_size ) + { + /*** line longer than window ***/ + vp->last_wind = w_size - 1; + } + else + vp->last_wind = last_phys; + genncpy(window, virtual, vp->last_wind+1); + + if( term_char!=ESC && (last_virt==INVALID + || virtual[last_virt]!=term_char) ) + { + /*** Line not terminated with ESC or escaped (^V) ***/ + /* eol, so return after doing a total update */ + /* if( (speed is greater or equal to 1200 */ + /* and something was typed) and */ + /* (control character present */ + /* or typeahead occurred) ) */ + + tty_cooked(ERRIO); + if( editb.e_ttyspeed==FAST && last_virt!=INVALID + && (vp->typeahead || cntl_char) ) + { + refresh(vp,TRANSLATE); + pr_string(vp,Prompt); + putstring(vp,0, last_phys+1); + if(echoctl) + ed_crlf(vp->ed); + else + while(kill_erase-- > 0) + putchar(' '); + } + + if( term_char=='\n' ) + { + if(!echoctl) + ed_crlf(vp->ed); + virtual[++last_virt] = '\n'; + } + vp->last_cmd = 'i'; + save_last(vp); +#if SHOPT_MULTIBYTE + virtual[last_virt+1] = 0; + last_virt = ed_external(virtual,shbuf); + return(last_virt); +#else + return(++last_virt); +#endif /* SHOPT_MULTIBYTE */ + } + + /*** Line terminated with escape, or escaped eol/eof, ***/ + /* so set raw mode */ + + if( tty_raw(ERRIO,0) < 0 ) + { + tty_cooked(ERRIO); + /* + * The following prevents drivers that return 0 on + * causing an infinite loop + */ + if(esc_or_hang) + return(-1); + virtual[++last_virt] = '\n'; +#if SHOPT_MULTIBYTE + virtual[last_virt+1] = 0; + last_virt = ed_external(virtual,shbuf); + return(last_virt); +#else + return(++last_virt); +#endif /* SHOPT_MULTIBYTE */ + } + + if(echoctl) /*** for cntl-echo erase the ^[ ***/ + pr_string(vp,"\b\b\b\b \b\b"); + + + if(crallowed) + { + /*** start over since there may be ***/ + /*** a control char, or cursor might not ***/ + /*** be at left margin (this lets us know ***/ + /*** where we are ***/ + cur_phys = 0; + window[0] = '\0'; + pr_string(vp,Prompt); + if( term_char==ESC && (last_virt<0 || virtual[last_virt]!=ESC)) + refresh(vp,CONTROL); + else + refresh(vp,INPUT); + } + else + { + /*** just update everything internally ***/ + refresh(vp,TRANSLATE); + } + } + + /*** Handle usrintr, usrquit, or EOF ***/ + + i = sigsetjmp(editb.e_env,0); + if( i != 0 ) + { + virtual[0] = '\0'; + tty_cooked(ERRIO); + + switch(i) + { + case UEOF: + /*** EOF ***/ + return(0); + + case UINTR: + /** interrupt **/ + return(-1); + } + return(-1); + } + + /*** Get a line from the terminal ***/ + + vp->U_saved = 0; + if(reedit) + refresh(vp,INPUT); + if(viraw) + getline(vp,APPEND); + else if(last_virt>=0 && virtual[last_virt]==term_char) + getline(vp,APPEND); + else + getline(vp,ESC); + if(vp->ed->e_multiline) + cursor(vp, last_phys); + /*** add a new line if user typed unescaped \n ***/ + /* to cause the shell to process the line */ + tty_cooked(ERRIO); + if(ed->e_nlist) + { + ed->e_nlist = 0; + stakset(ed->e_stkptr,ed->e_stkoff); + } + if( vp->addnl ) + { + virtual[++last_virt] = '\n'; + ed_crlf(vp->ed); + } + if( ++last_virt >= 0 ) + { +#if SHOPT_MULTIBYTE + if(vp->bigvi) + { + vp->bigvi = 0; + shbuf[last_virt-1] = '\n'; + } + else + { + virtual[last_virt] = 0; + last_virt = ed_external(virtual,shbuf); + } +#endif /* SHOPT_MULTIBYTE */ + return(last_virt); + } + else + return(-1); +} + + +/*{ APPEND( char, mode ) + * + * This routine will append char after cur_virt in the virtual image. + * mode = APPEND, shift chars right before appending + * REPLACE, replace char if possible + * +}*/ + +static void append(Vi_t *vp,int c, int mode) +{ + register int i,j; + + if( last_virt=0)) + { + j = (cur_virt>=0?cur_virt:0); + for(i = ++last_virt; i > j; --i) + virtual[i] = virtual[i-1]; + } + virtual[++cur_virt] = c; + } + else + ed_ringbell(); + return; +} + +/*{ BACKWORD( nwords, cmd ) + * + * This routine will position cur_virt at the nth previous word. + * +}*/ + +static void backword(Vi_t *vp,int nwords, register int cmd) +{ + register int tcur_virt = cur_virt; + while( nwords-- && tcur_virt > first_virt ) + { + if( !isblank(tcur_virt) && isblank(tcur_virt-1) + && tcur_virt>first_virt ) + --tcur_virt; + else if(cmd != 'B') + { + register int last = isalph(tcur_virt-1); + register int cur = isalph(tcur_virt); + if((!cur && last) || (cur && !last)) + --tcur_virt; + } + while( isblank(tcur_virt) && tcur_virt>=first_virt ) + --tcur_virt; + if( cmd == 'B' ) + { + while( !isblank(tcur_virt) && tcur_virt>=first_virt ) + --tcur_virt; + } + else + { + if(isalph(tcur_virt)) + while( isalph(tcur_virt) && tcur_virt>=first_virt ) + --tcur_virt; + else + while( !isalph(tcur_virt) && !isblank(tcur_virt) + && tcur_virt>=first_virt ) + --tcur_virt; + } + cur_virt = ++tcur_virt; + } + return; +} + +/*{ CNTLMODE() + * + * This routine implements the vi command subset. + * The cursor will always be positioned at the char of interest. + * +}*/ + +static int cntlmode(Vi_t *vp) +{ + register int c; + register int i; + genchar tmp_u_space[MAXLINE]; /* temporary u_space */ + genchar *real_u_space; /* points to real u_space */ + int tmp_u_column = INVALID; /* temporary u_column */ + int was_inmacro; + + if(!vp->U_saved) + { + /*** save virtual image if never done before ***/ + virtual[last_virt+1] = '\0'; + gencpy(vp->U_space, virtual); + vp->U_saved = 1; + } + + save_last(vp); + + real_u_space = vp->u_space; + curhline = histmax; + first_virt = 0; + vp->repeat = 1; + if( cur_virt > INVALID ) + { + /*** make sure cursor is at the last char ***/ + sync_cursor(vp); + } + + /*** Read control char until something happens to cause a ***/ + /* return to APPEND/REPLACE mode */ + + while( c=ed_getchar(vp->ed,-1) ) + { + vp->repeat_set = 0; + was_inmacro = inmacro; + if( c == '0' ) + { + /*** move to leftmost column ***/ + cur_virt = 0; + sync_cursor(vp); + continue; + } + + if( digit(c) ) + { + c = getcount(vp,c); + if( c == '.' ) + vp->lastrepeat = vp->repeat; + } + + /*** see if it's a move cursor command ***/ + + if(mvcursor(vp,c)) + { + sync_cursor(vp); + vp->repeat = 1; + continue; + } + + /*** see if it's a repeat of the last command ***/ + + if( c == '.' ) + { + c = vp->last_cmd; + vp->repeat = vp->lastrepeat; + i = textmod(vp,c, c); + } + else + { + i = textmod(vp,c, 0); + } + + /*** see if it's a text modification command ***/ + + switch(i) + { + case BAD: + break; + + default: /** input mode **/ + if(!was_inmacro) + { + vp->last_cmd = c; + vp->lastrepeat = vp->repeat; + } + vp->repeat = 1; + if( i == GOOD ) + continue; + return(i); + } + + switch( c ) + { + /***** Other stuff *****/ + + case cntl('L'): /** Redraw line **/ + /*** print the prompt and ***/ + /* force a total refresh */ + if(vp->nonewline==0) + putchar('\n'); + vp->nonewline = 0; + pr_string(vp,Prompt); + window[0] = '\0'; + cur_phys = vp->first_wind; + vp->ofirst_wind = INVALID; + vp->long_line = ' '; + break; + + case cntl('V'): + { + register const char *p = fmtident(e_version); + save_v(vp); + del_line(vp,BAD); + while(c = *p++) + append(vp,c,APPEND); + refresh(vp,CONTROL); + ed_getchar(vp->ed,-1); + restore_v(vp); + break; + } + + case '/': /** Search **/ + case '?': + case 'N': + case 'n': + save_v(vp); + switch( search(vp,c) ) + { + case GOOD: + /*** force a total refresh ***/ + window[0] = '\0'; + goto newhist; + + case BAD: + /*** no match ***/ + ed_ringbell(); + + default: + if( vp->u_column == INVALID ) + del_line(vp,BAD); + else + restore_v(vp); + break; + } + break; + + case 'j': /** get next command **/ + case '+': /** get next command **/ + curhline += vp->repeat; + if( curhline > histmax ) + { + curhline = histmax; + goto ringbell; + } + else if(curhline==histmax && tmp_u_column!=INVALID ) + { + vp->u_space = tmp_u_space; + vp->u_column = tmp_u_column; + restore_v(vp); + vp->u_space = real_u_space; + break; + } + save_v(vp); + cur_virt = INVALID; + goto newhist; + + case 'k': /** get previous command **/ + case '-': /** get previous command **/ + if( curhline == histmax ) + { + vp->u_space = tmp_u_space; + i = vp->u_column; + save_v(vp); + vp->u_space = real_u_space; + tmp_u_column = vp->u_column; + vp->u_column = i; + } + + curhline -= vp->repeat; + if( curhline <= histmin ) + { + curhline += vp->repeat; + goto ringbell; + } + save_v(vp); + cur_virt = INVALID; + newhist: + if(curhline!=histmax || cur_virt==INVALID) + hist_copy((char*)virtual, MAXLINE, curhline,-1); + else + { + strcpy((char*)virtual,(char*)vp->u_space); +#if SHOPT_MULTIBYTE + ed_internal((char*)vp->u_space,vp->u_space); +#endif /* SHOPT_MULTIBYTE */ + } +#if SHOPT_MULTIBYTE + ed_internal((char*)virtual,virtual); +#endif /* SHOPT_MULTIBYTE */ + if((last_virt=genlen(virtual)-1) >= 0 && cur_virt == INVALID) + cur_virt = 0; + break; + + + case 'u': /** undo the last thing done **/ + restore_v(vp); + break; + + case 'U': /** Undo everything **/ + save_v(vp); + if( virtual[0] == '\0' ) + goto ringbell; + else + { + gencpy(virtual, vp->U_space); + last_virt = genlen(vp->U_space) - 1; + cur_virt = 0; + } + break; + +#if KSHELL + case 'v': + if(vp->repeat_set==0) + goto vcommand; +#endif /* KSHELL */ + + case 'G': /** goto command repeat **/ + if(vp->repeat_set==0) + vp->repeat = histmin+1; + if( vp->repeat <= histmin || vp->repeat > histmax ) + { + goto ringbell; + } + curhline = vp->repeat; + save_v(vp); + if(c == 'G') + { + cur_virt = INVALID; + goto newhist; + } + +#if KSHELL + vcommand: + if(ed_fulledit(vp->ed)==GOOD) + return(BIGVI); + else + goto ringbell; +#endif /* KSHELL */ + + case '#': /** insert(delete) # to (no)comment command **/ + if( cur_virt != INVALID ) + { + register genchar *p = &virtual[last_virt+1]; + *p = 0; + /*** see whether first char is comment char ***/ + c = (virtual[0]=='#'); + while(p-- >= virtual) + { + if(*p=='\n' || p0) + ed_ungetchar(vp->ed,x); + } + if(lookahead) + { + ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1)); + if(c=='[') + { + vp->repeat = 1; + continue; + } + } + default: + ringbell: + ed_ringbell(); + vp->repeat = 1; + continue; + } + + refresh(vp,CONTROL); + vp->repeat = 1; + } +/* NOTREACHED */ + return(0); +} + +/*{ CURSOR( new_current_physical ) + * + * This routine will position the virtual cursor at + * physical column x in the window. + * +}*/ + +static void cursor(Vi_t *vp,register int x) +{ +#if SHOPT_MULTIBYTE + while(physical[x]==MARKER) + x++; +#endif /* SHOPT_MULTIBYTE */ + cur_phys = ed_setcursor(vp->ed, physical, cur_phys,x,vp->first_wind); +} + +/*{ DELETE( nchars, mode ) + * + * Delete nchars from the virtual space and leave cur_virt positioned + * at cur_virt-1. + * + * If mode = 'c', do not save the characters deleted + * = 'd', save them in yankbuf and delete. + * = 'y', save them in yankbuf but do not delete. + * +}*/ + +static void cdelete(Vi_t *vp,register int nchars, int mode) +{ + register int i; + register genchar *cp; + + if( cur_virt < first_virt ) + { + ed_ringbell(); + return; + } + if( nchars > 0 ) + { + cp = virtual+cur_virt; + vp->o_v_char = cp[0]; + if( (cur_virt-- + nchars) > last_virt ) + { + /*** set nchars to number actually deleted ***/ + nchars = last_virt - cur_virt; + } + + /*** save characters to be deleted ***/ + + if( mode != 'c' ) + { + i = cp[nchars]; + cp[nchars] = 0; + gencpy(yankbuf,cp); + cp[nchars] = i; + } + + /*** now delete these characters ***/ + + if( mode != 'y' ) + { + gencpy(cp,cp+nchars); + last_virt -= nchars; + } + } + return; +} + +/*{ DEL_LINE( mode ) + * + * This routine will delete the line. + * mode = GOOD, do a save_v() + * +}*/ +static void del_line(register Vi_t *vp, int mode) +{ + if( last_virt == INVALID ) + return; + + if( mode == GOOD ) + save_v(vp); + + cur_virt = 0; + first_virt = 0; + cdelete(vp,last_virt+1, BAD); + refresh(vp,CONTROL); + + cur_virt = INVALID; + cur_phys = 0; + vp->findchar = INVALID; + last_phys = INVALID; + last_virt = INVALID; + vp->last_wind = INVALID; + vp->first_wind = 0; + vp->o_v_char = '\0'; + vp->ocur_phys = 0; + vp->ocur_virt = MAXCHAR; + vp->ofirst_wind = 0; + window[0] = '\0'; + return; +} + +/*{ DELMOTION( motion, mode ) + * + * Delete thru motion. + * + * mode = 'd', save deleted characters, delete + * = 'c', do not save characters, change + * = 'y', save characters, yank + * + * Returns 1 if operation successful; else 0. + * +}*/ + +static int delmotion(Vi_t *vp,int motion, int mode) +{ + register int begin, end, delta; + /* the following saves a register */ + + if( cur_virt == INVALID ) + return(0); + if( mode != 'y' ) + save_v(vp); + begin = cur_virt; + + /*** fake out the motion routines by appending a blank ***/ + + virtual[++last_virt] = ' '; + end = mvcursor(vp,motion); + virtual[last_virt--] = 0; + if(!end) + return(0); + + end = cur_virt; + if( mode=='c' && end>begin && strchr("wW", motion) ) + { + /*** called by change operation, user really expects ***/ + /* the effect of the eE commands, so back up to end of word */ + while( end>begin && isblank(end-1) ) + --end; + if( end == begin ) + ++end; + } + + delta = end - begin; + if( delta >= 0 ) + { + cur_virt = begin; + if( strchr("eE;,TtFf%", motion) ) + ++delta; + } + else + { + delta = -delta + (motion=='%'); + } + + cdelete(vp,delta, mode); + if( mode == 'y' ) + cur_virt = begin; + return(1); +} + + +/*{ ENDWORD( nwords, cmd ) + * + * This routine will move cur_virt to the end of the nth word. + * +}*/ + +static void endword(Vi_t *vp, int nwords, register int cmd) +{ + register int tcur_virt = cur_virt; + while( nwords-- ) + { + if( !isblank(tcur_virt) && tcur_virt<=last_virt ) + ++tcur_virt; + while( isblank(tcur_virt) && tcur_virt<=last_virt ) + ++tcur_virt; + if( cmd == 'E' ) + { + while( !isblank(tcur_virt) && tcur_virt<=last_virt ) + ++tcur_virt; + } + else + { + if( isalph(tcur_virt) ) + while( isalph(tcur_virt) && tcur_virt<=last_virt ) + ++tcur_virt; + else + while( !isalph(tcur_virt) && !isblank(tcur_virt) + && tcur_virt<=last_virt ) + ++tcur_virt; + } + if( tcur_virt > first_virt ) + tcur_virt--; + } + cur_virt = tcur_virt; + return; +} + +/*{ FORWARD( nwords, cmd ) + * + * This routine will move cur_virt forward to the next nth word. + * +}*/ + +static void forward(Vi_t *vp,register int nwords, int cmd) +{ + register int tcur_virt = cur_virt; + while( nwords-- ) + { + if( cmd == 'W' ) + { + while( !isblank(tcur_virt) && tcur_virt < last_virt ) + ++tcur_virt; + } + else + { + if( isalph(tcur_virt) ) + { + while( isalph(tcur_virt) && tcur_virtrepeat_set++; + i = 0; + while( digit(c) ) + { + i = i*10 + c - '0'; + c = ed_getchar(vp->ed,-1); + } + + if( i > 0 ) + vp->repeat *= i; + return(c); +} + + +/*{ GETLINE( mode ) + * + * This routine will fetch a line. + * mode = APPEND, allow escape to cntlmode subroutine + * appending characters. + * = REPLACE, allow escape to cntlmode subroutine + * replacing characters. + * = SEARCH, no escape allowed + * = ESC, enter control mode immediately + * + * The cursor will always be positioned after the last + * char printed. + * + * This routine returns when cr, nl, or (eof in column 0) is + * received (column 0 is the first char position). + * +}*/ + +static void getline(register Vi_t* vp,register int mode) +{ + register int c; + register int tmp; + int max_virt=0, last_save=0; + genchar saveline[MAXLINE]; + + vp->addnl = 1; + + if( mode == ESC ) + { + /*** go directly to control mode ***/ + goto escape; + } + + for(;;) + { + if( (c=ed_getchar(vp->ed,mode==SEARCH?1:-2)) == usreof ) + c = UEOF; + else if( c == usrerase ) + c = UERASE; + else if( c == usrkill ) + c = UKILL; + else if( c == editb.e_werase ) + c = UWERASE; + else if( c == usrlnext ) + c = ULNEXT; + + if( c == ULNEXT) + { + /*** implement ^V to escape next char ***/ + c = ed_getchar(vp->ed,2); + append(vp,c, mode); + refresh(vp,INPUT); + continue; + } + + switch( c ) + { + case ESC: /** enter control mode **/ + if(!sh_isoption(SH_VI)) + { + append(vp,c, mode); + break; + } + if( mode == SEARCH ) + { + ed_ringbell(); + continue; + } + else + { + escape: + if( mode == REPLACE ) + { + c = max_virt-cur_virt; + if(c > 0 && last_save>=cur_virt) + { + genncpy((&virtual[cur_virt]),&saveline[cur_virt],c); + if(last_virt>=last_save) + last_virt=last_save-1; + refresh(vp,INPUT); + } + --cur_virt; + } + tmp = cntlmode(vp); + if( tmp == ENTER || tmp == BIGVI ) + { +#if SHOPT_MULTIBYTE + vp->bigvi = (tmp==BIGVI); +#endif /* SHOPT_MULTIBYTE */ + return; + } + if( tmp == INSERT ) + { + mode = APPEND; + continue; + } + mode = tmp; + if(mode==REPLACE) + { + c = last_save = last_virt+1; + if(c >= MAXLINE) + c = MAXLINE-1; + genncpy(saveline, virtual, c); + } + } + break; + + case UERASE: /** user erase char **/ + /*** treat as backspace ***/ + + case '\b': /** backspace **/ + if( virtual[cur_virt] == '\\' ) + { + cdelete(vp,1, BAD); + append(vp,usrerase, mode); + } + else + { + if( mode==SEARCH && cur_virt==0 ) + { + first_virt = 0; + cdelete(vp,1, BAD); + return; + } + if(mode==REPLACE || (last_save>0 && last_virt<=last_save)) + { + if(cur_virt<=first_virt) + ed_ringbell(); + else if(mode==REPLACE) + --cur_virt; + mode = REPLACE; + sync_cursor(vp); + continue; + } + else + cdelete(vp,1, BAD); + } + break; + + case UWERASE: /** delete back word **/ + if( cur_virt > first_virt && + !isblank(cur_virt) && + !ispunct(virtual[cur_virt]) && + isblank(cur_virt-1) ) + { + cdelete(vp,1, BAD); + } + else + { + tmp = cur_virt; + backword(vp,1, 'W'); + cdelete(vp,tmp - cur_virt + 1, BAD); + } + break; + + case UKILL: /** user kill line char **/ + if( virtual[cur_virt] == '\\' ) + { + cdelete(vp,1, BAD); + append(vp,usrkill, mode); + } + else + { + if( mode == SEARCH ) + { + cur_virt = 1; + delmotion(vp, '$', BAD); + } + else if(first_virt) + { + tmp = cur_virt; + cur_virt = first_virt; + cdelete(vp,tmp - cur_virt + 1, BAD); + } + else + del_line(vp,GOOD); + } + break; + + case UEOF: /** eof char **/ + if( cur_virt != INVALID ) + continue; + vp->addnl = 0; + + case '\n': /** newline or return **/ + if( mode != SEARCH ) + save_last(vp); + refresh(vp,INPUT); + return; + + case '\t': /** command completion **/ + if(mode!=SEARCH && last_virt>=0 && (vp->ed->e_tabcount|| !isblank(cur_virt)) && vp->ed->sh->nextprompt) + { + if(vp->ed->e_tabcount==0) + { + ed_ungetchar(vp->ed,'\\'); + vp->ed->e_tabcount=1; + goto escape; + } + else if(vp->ed->e_tabcount==1) + { + ed_ungetchar(vp->ed,'='); + goto escape; + } + vp->ed->e_tabcount = 0; + } + /* FALL THRU*/ + default: + if( mode == REPLACE ) + { + if( cur_virt < last_virt ) + { + replace(vp,c, 1); + if(cur_virt>max_virt) + max_virt = cur_virt; + continue; + } + cdelete(vp,1, BAD); + mode = APPEND; + max_virt = last_virt+3; + } + append(vp,c, mode); + break; + } + refresh(vp,INPUT); + + } +} + +/*{ MVCURSOR( motion ) + * + * This routine will move the virtual cursor according to motion + * for repeat times. + * + * It returns GOOD if successful; else BAD. + * +}*/ + +static int mvcursor(register Vi_t* vp,register int motion) +{ + register int count; + register int tcur_virt; + register int incr = -1; + register int bound = 0; + + switch(motion) + { + /***** Cursor move commands *****/ + + case '0': /** First column **/ + tcur_virt = 0; + break; + + case '^': /** First nonblank character **/ + tcur_virt = first_virt; + while( isblank(tcur_virt) && tcur_virt < last_virt ) + ++tcur_virt; + break; + + case '|': + tcur_virt = vp->repeat-1; + if(tcur_virt <= last_virt) + break; + /* fall through */ + + case '$': /** End of line **/ + tcur_virt = last_virt; + break; + + case '[': + switch(motion=getcount(vp,ed_getchar(vp->ed,-1))) + { + case 'A': + ed_ungetchar(vp->ed,'k'); + return(1); + case 'B': + ed_ungetchar(vp->ed,'j'); + return(1); + case 'C': + motion = last_virt; + incr = 1; + goto walk; + case 'D': + motion = first_virt; + goto walk; + case 'H': + tcur_virt = 0; + break; + case 'Y': + tcur_virt = last_virt; + break; + default: + ed_ungetchar(vp->ed,motion); + return(0); + } + break; + + case 'h': /** Left one **/ + case '\b': + motion = first_virt; + goto walk; + + case ' ': + case 'l': /** Right one **/ + motion = last_virt; + incr = 1; + walk: + tcur_virt = cur_virt; + if( incr*tcur_virt < motion) + { + tcur_virt += vp->repeat*incr; + if( incr*tcur_virt > motion) + tcur_virt = motion; + } + else + return(0); + break; + + case 'B': + case 'b': /** back word **/ + tcur_virt = cur_virt; + backword(vp,vp->repeat, motion); + if( cur_virt == tcur_virt ) + return(0); + return(1); + + case 'E': + case 'e': /** end of word **/ + tcur_virt = cur_virt; + if(tcur_virt >=0) + endword(vp, vp->repeat, motion); + if( cur_virt == tcur_virt ) + return(0); + return(1); + + case ',': /** reverse find old char **/ + case ';': /** find old char **/ + switch(vp->last_find) + { + case 't': + case 'f': + if(motion==';') + { + bound = last_virt; + incr = 1; + } + goto find_b; + + case 'T': + case 'F': + if(motion==',') + { + bound = last_virt; + incr = 1; + } + goto find_b; + + default: + return(0); + } + + + case 't': /** find up to new char forward **/ + case 'f': /** find new char forward **/ + bound = last_virt; + incr = 1; + + case 'T': /** find up to new char backward **/ + case 'F': /** find new char backward **/ + vp->last_find = motion; + if((vp->findchar=getrchar(vp))==ESC) + return(1); +find_b: + tcur_virt = cur_virt; + count = vp->repeat; + while( count-- ) + { + while( incr*(tcur_virt+=incr) <= bound + && virtual[tcur_virt] != vp->findchar ); + if( incr*tcur_virt > bound ) + { + return(0); + } + } + if( fold(vp->last_find) == 'T' ) + tcur_virt -= incr; + break; + + case '%': + { + int nextmotion; + int nextc; + tcur_virt = cur_virt; + while( tcur_virt <= last_virt + && strchr(paren_chars,virtual[tcur_virt])==(char*)0) + tcur_virt++; + if(tcur_virt > last_virt ) + return(0); + nextc = virtual[tcur_virt]; + count = strchr(paren_chars,nextc)-paren_chars; + if(count < 3) + { + incr = 1; + bound = last_virt; + nextmotion = paren_chars[count+3]; + } + else + nextmotion = paren_chars[count-3]; + count = 1; + while(count >0 && incr*(tcur_virt+=incr) <= bound) + { + if(virtual[tcur_virt] == nextmotion) + count--; + else if(virtual[tcur_virt]==nextc) + count++; + } + if(count) + return(0); + break; + } + + case 'W': + case 'w': /** forward word **/ + tcur_virt = cur_virt; + forward(vp,vp->repeat, motion); + if( tcur_virt == cur_virt ) + return(0); + return(1); + + default: + return(0); + } + cur_virt = tcur_virt; + + return(1); +} + +/* + * print a string + */ + +static void pr_string(register Vi_t *vp, register const char *sp) +{ + /*** copy string sp ***/ + register char *ptr = editb.e_outptr; + while(*sp) + *ptr++ = *sp++; + editb.e_outptr = ptr; + return; +} + +/*{ PUTSTRING( column, nchars ) + * + * Put nchars starting at column of physical into the workspace + * to be printed. + * +}*/ + +static void putstring(register Vi_t *vp,register int col, register int nchars) +{ + while( nchars-- ) + putchar(physical[col++]); + return; +} + +/*{ REFRESH( mode ) + * + * This routine will refresh the crt so the physical image matches + * the virtual image and display the proper window. + * + * mode = CONTROL, refresh in control mode, ie. leave cursor + * positioned at last char printed. + * = INPUT, refresh in input mode; leave cursor positioned + * after last char printed. + * = TRANSLATE, perform virtual to physical translation + * and adjust left margin only. + * + * +-------------------------------+ + * | | | virtual | | | + * +-------------------------------+ + * cur_virt last_virt + * + * +-----------------------------------------------+ + * | | | physical | | | + * +-----------------------------------------------+ + * cur_phys last_phys + * + * 0 w_size - 1 + * +-----------------------+ + * | | | window | + * +-----------------------+ + * cur_window = cur_phys - first_wind +}*/ + +static void refresh(register Vi_t* vp, int mode) +{ + register int p; + register int regb; + register int first_w = vp->first_wind; + int p_differ; + int new_lw; + int ncur_phys; + int opflag; /* search optimize flag */ + +# define w regb +# define v regb + + /*** find out if it's necessary to start translating at beginning ***/ + + if(lookahead>0) + { + p = previous[lookahead-1]; + if(p != ESC && p != '\n' && p != '\r') + mode = TRANSLATE; + } + v = cur_virt; + if( vocur_virt || vp->ocur_virt==INVALID + || ( v==vp->ocur_virt + && (!is_print(virtual[v]) || !is_print(vp->o_v_char))) ) + { + opflag = 0; + p = 0; + v = 0; + } + else + { + opflag = 1; + p = vp->ocur_phys; + v = vp->ocur_virt; + if( !is_print(virtual[v]) ) + { + /*** avoid double ^'s ***/ + ++p; + ++v; + } + } + virtual[last_virt+1] = 0; + ncur_phys = ed_virt_to_phys(vp->ed,virtual,physical,cur_virt,v,p); + p = genlen(physical); + if( --p < 0 ) + last_phys = 0; + else + last_phys = p; + + /*** see if this was a translate only ***/ + + if( mode == TRANSLATE ) + return; + + /*** adjust left margin if necessary ***/ + + if( ncur_phys=(first_w + w_size) ) + { + cursor(vp,first_w); + first_w = ncur_phys - (w_size>>1); + if( first_w < 0 ) + first_w = 0; + vp->first_wind = cur_phys = first_w; + } + + /*** attempt to optimize search somewhat to find ***/ + /*** out where physical and window images differ ***/ + + if( first_w==vp->ofirst_wind && ncur_phys>=vp->ocur_phys && opflag==1 ) + { + p = vp->ocur_phys; + w = p - first_w; + } + else + { + p = first_w; + w = 0; + } + + for(; (p<=last_phys && w<=vp->last_wind); ++p, ++w) + { + if( window[w] != physical[p] ) + break; + } + p_differ = p; + + if( (p>last_phys || p>=first_w+w_size) && w>vp->last_wind + && cur_virt==vp->ocur_virt ) + { + /*** images are identical ***/ + return; + } + + /*** copy the physical image to the window image ***/ + + if( last_virt != INVALID ) + { + while( p <= last_phys && w < w_size ) + window[w++] = physical[p++]; + } + new_lw = w; + + /*** erase trailing characters if needed ***/ + + while( w <= vp->last_wind ) + window[w++] = ' '; + vp->last_wind = --w; + + p = p_differ; + + /*** move cursor to start of difference ***/ + + cursor(vp,p); + + /*** and output difference ***/ + + w = p - first_w; + while( w <= vp->last_wind ) + putchar(window[w++]); + + cur_phys = w + first_w; + vp->last_wind = --new_lw; + + if( last_phys >= w_size ) + { + if( first_w == 0 ) + vp->long_char = '>'; + else if( last_phys < (first_w+w_size) ) + vp->long_char = '<'; + else + vp->long_char = '*'; + } + else + vp->long_char = ' '; + + if( vp->long_line != vp->long_char ) + { + /*** indicate lines longer than window ***/ + while( w++ < w_size ) + { + putchar(' '); + ++cur_phys; + } + putchar(vp->long_char); + ++cur_phys; + vp->long_line = vp->long_char; + } + + vp->ocur_phys = ncur_phys; + vp->ocur_virt = cur_virt; + vp->ofirst_wind = first_w; + + if( mode==INPUT && cur_virt>INVALID ) + ++ncur_phys; + + cursor(vp,ncur_phys); + ed_flush(vp->ed); + return; +} + +/*{ REPLACE( char, increment ) + * + * Replace the cur_virt character with char. This routine attempts + * to avoid using refresh(). + * + * increment = 1, increment cur_virt after replacement. + * = 0, leave cur_virt where it is. + * +}*/ + +static void replace(register Vi_t *vp, register int c, register int increment) +{ + register int cur_window; + + if( cur_virt == INVALID ) + { + /*** can't replace invalid cursor ***/ + ed_ringbell(); + return; + } + cur_window = cur_phys - vp->first_wind; + if( vp->ocur_virt == INVALID || !is_print(c) + || !is_print(virtual[cur_virt]) + || !is_print(vp->o_v_char) +#if SHOPT_MULTIBYTE + || !iswascii(c) || mbwidth(vp->o_v_char)>1 + || !iswascii(virtual[cur_virt]) +#endif /* SHOPT_MULTIBYTE */ + || (increment && (cur_window==w_size-1) + || !is_print(virtual[cur_virt+1])) ) + { + /*** must use standard refresh routine ***/ + + cdelete(vp,1, BAD); + append(vp,c, APPEND); + if( increment && cur_virto_v_char = c; + ed_flush(vp->ed); + } + return; +} + +/*{ RESTORE_V() + * + * Restore the contents of virtual space from u_space. + * +}*/ + +static void restore_v(register Vi_t *vp) +{ + register int tmpcol; + genchar tmpspace[MAXLINE]; + + if( vp->u_column == INVALID-1 ) + { + /*** never saved anything ***/ + ed_ringbell(); + return; + } + gencpy(tmpspace, vp->u_space); + tmpcol = vp->u_column; + save_v(vp); + gencpy(virtual, tmpspace); + cur_virt = tmpcol; + last_virt = genlen(tmpspace) - 1; + vp->ocur_virt = MAXCHAR; /** invalidate refresh optimization **/ + return; +} + +/*{ SAVE_LAST() + * + * If the user has typed something, save it in last line. + * +}*/ + +static void save_last(register Vi_t* vp) +{ + register int i; + + if( (i = cur_virt - first_virt + 1) > 0 ) + { + /*** save last thing user typed ***/ + if(i >= MAXLINE) + i = MAXLINE-1; + genncpy(vp->lastline, (&virtual[first_virt]), i); + vp->lastline[i] = '\0'; + } + return; +} + +/*{ SAVE_V() + * + * This routine will save the contents of virtual in u_space. + * +}*/ + +static void save_v(register Vi_t *vp) +{ + if(!inmacro) + { + virtual[last_virt + 1] = '\0'; + gencpy(vp->u_space, virtual); + vp->u_column = cur_virt; + } + return; +} + +/*{ SEARCH( mode ) + * + * Search history file for regular expression. + * + * mode = '/' require search string and search new to old + * mode = '?' require search string and search old to new + * mode = 'N' repeat last search in reverse direction + * mode = 'n' repeat last search + * +}*/ + +/* + * search for in the current command + */ +static int curline_search(Vi_t *vp, const char *string) +{ + register int len=strlen(string); + register const char *dp,*cp=string, *dpmax; +#if SHOPT_MULTIBYTE + ed_external(vp->u_space,(char*)vp->u_space); +#endif /* SHOPT_MULTIBYTE */ + for(dp=(char*)vp->u_space,dpmax=dp+strlen(dp)-len; dp<=dpmax; dp++) + { + if(*dp==*cp && memcmp(cp,dp,len)==0) + return(dp-(char*)vp->u_space); + } +#if SHOPT_MULTIBYTE + ed_internal((char*)vp->u_space,vp->u_space); +#endif /* SHOPT_MULTIBYTE */ + return(-1); +} + +static int search(register Vi_t* vp,register int mode) +{ + register int new_direction; + register int oldcurhline; + register int i; + Histloc_t location; + + if( mode == '/' || mode == '?') + { + /*** new search expression ***/ + del_line(vp,BAD); + append(vp,mode, APPEND); + refresh(vp,INPUT); + first_virt = 1; + getline(vp,SEARCH); + first_virt = 0; + virtual[last_virt + 1] = '\0'; /*** make null terminated ***/ + vp->direction = mode=='/' ? -1 : 1; + } + + if( cur_virt == INVALID ) + { + /*** no operation ***/ + return(ABORT); + } + + if( cur_virt==0 || fold(mode)=='N' ) + { + /*** user wants repeat of last search ***/ + del_line(vp,BAD); + strcpy( ((char*)virtual)+1, lsearch); +#if SHOPT_MULTIBYTE + *((char*)virtual) = '/'; + ed_internal((char*)virtual,virtual); +#endif /* SHOPT_MULTIBYTE */ + } + + if( mode == 'N' ) + new_direction = -vp->direction; + else + new_direction = vp->direction; + + + /*** now search ***/ + + oldcurhline = curhline; +#if SHOPT_MULTIBYTE + ed_external(virtual,(char*)virtual); +#endif /* SHOPT_MULTIBYTE */ + if(mode=='?' && (i=curline_search(vp,((char*)virtual)+1))>=0) + { + location.hist_command = curhline; + location.hist_char = i; + } + else + { + i = INVALID; + if( new_direction==1 && curhline >= histmax ) + curhline = histmin + 1; + location = hist_find(sh.hist_ptr,((char*)virtual)+1, curhline, 1, new_direction); + } + cur_virt = i; + strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE); + if( (curhline=location.hist_command) >=0 ) + { + vp->ocur_virt = INVALID; + return(GOOD); + } + + /*** could not find matching line ***/ + + curhline = oldcurhline; + return(BAD); +} + +/*{ SYNC_CURSOR() + * + * This routine will move the physical cursor to the same + * column as the virtual cursor. + * +}*/ + +static void sync_cursor(register Vi_t *vp) +{ + register int p; + register int v; + register int c; + int new_phys; + + if( cur_virt == INVALID ) + return; + + /*** find physical col that corresponds to virtual col ***/ + + new_phys = 0; + if(vp->first_wind==vp->ofirst_wind && cur_virt>vp->ocur_virt && vp->ocur_virt!=INVALID) + { + /*** try to optimize search a little ***/ + p = vp->ocur_phys + 1; +#if SHOPT_MULTIBYTE + while(physical[p]==MARKER) + p++; +#endif /* SHOPT_MULTIBYTE */ + v = vp->ocur_virt + 1; + } + else + { + p = 0; + v = 0; + } + for(; v <= last_virt; ++p, ++v) + { +#if SHOPT_MULTIBYTE + int d; + c = virtual[v]; + if((d = mbwidth(c)) > 1) + { + if( v != cur_virt ) + p += (d-1); + } + else if(!iswprint(c)) +#else + c = virtual[v]; + if(!isprint(c)) +#endif /* SHOPT_MULTIBYTE */ + { + if( c == '\t' ) + { + p -= ((p+editb.e_plen)%TABSIZE); + p += (TABSIZE-1); + } + else + { + ++p; + } + } + if( v == cur_virt ) + { + new_phys = p; + break; + } + } + + if( new_phys < vp->first_wind || new_phys >= vp->first_wind + w_size ) + { + /*** asked to move outside of window ***/ + + window[0] = '\0'; + refresh(vp,CONTROL); + return; + } + + cursor(vp,new_phys); + ed_flush(vp->ed); + vp->ocur_phys = cur_phys; + vp->ocur_virt = cur_virt; + vp->o_v_char = virtual[vp->ocur_virt]; + + return; +} + +/*{ TEXTMOD( command, mode ) + * + * Modify text operations. + * + * mode != 0, repeat previous operation + * +}*/ + +static int textmod(register Vi_t *vp,register int c, int mode) +{ + register int i; + register genchar *p = vp->lastline; + register int trepeat = vp->repeat; + genchar *savep; + + if(mode && (fold(vp->lastmotion)=='F' || fold(vp->lastmotion)=='T')) + vp->lastmotion = ';'; + + if( fold(c) == 'P' ) + { + /*** change p from lastline to yankbuf ***/ + p = yankbuf; + } + +addin: + switch( c ) + { + /***** Input commands *****/ + +#if KSHELL + case '\t': + if(vp->ed->e_tabcount!=1) + return(BAD); + c = '='; + case '*': /** do file name expansion in place **/ + case '\\': /** do file name completion in place **/ + if( cur_virt == INVALID ) + return(BAD); + case '=': /** list file name expansions **/ + save_v(vp); + i = last_virt; + ++last_virt; + mode = cur_virt-1; + virtual[last_virt] = 0; + if(ed_expand(vp->ed,(char*)virtual, &cur_virt, &last_virt, c, vp->repeat_set?vp->repeat:-1)<0) + { + if(vp->ed->e_tabcount) + { + vp->ed->e_tabcount=2; + ed_ungetchar(vp->ed,'\t'); + --last_virt; + return(APPEND); + } + last_virt = i; + ed_ringbell(); + } + else if(c == '=' && !vp->repeat_set) + { + last_virt = i; + vp->nonewline++; + ed_ungetchar(vp->ed,cntl('L')); + return(GOOD); + } + else + { + --cur_virt; + --last_virt; + vp->ocur_virt = MAXCHAR; + if(c=='=' || (modeed->e_tabcount = 0; + return(APPEND); + } + break; + + case '@': /** macro expansion **/ + if( mode ) + c = vp->lastmacro; + else + if((c=getrchar(vp))==ESC) + return(GOOD); + if(!inmacro) + vp->lastmacro = c; + if(ed_macro(vp->ed,c)) + { + save_v(vp); + inmacro++; + return(GOOD); + } + ed_ringbell(); + return(BAD); + +#endif /* KSHELL */ + case '_': /** append last argument of prev command **/ + save_v(vp); + { + genchar tmpbuf[MAXLINE]; + if(vp->repeat_set==0) + vp->repeat = -1; + p = (genchar*)hist_word((char*)tmpbuf,MAXLINE,vp->repeat); +#if !KSHELL + if(p==0) + { + ed_ringbell(); + break; + } +#endif /* KSHELL */ +#if SHOPT_MULTIBYTE + ed_internal((char*)p,tmpbuf); + p = tmpbuf; +#endif /* SHOPT_MULTIBYTE */ + i = ' '; + do + { + append(vp,i,APPEND); + } + while(i = *p++); + return(APPEND); + } + + case 'A': /** append to end of line **/ + cur_virt = last_virt; + sync_cursor(vp); + + case 'a': /** append **/ + if( fold(mode) == 'A' ) + { + c = 'p'; + goto addin; + } + save_v(vp); + if( cur_virt != INVALID ) + { + first_virt = cur_virt + 1; + cursor(vp,cur_phys + 1); + ed_flush(vp->ed); + } + return(APPEND); + + case 'I': /** insert at beginning of line **/ + cur_virt = first_virt; + sync_cursor(vp); + + case 'i': /** insert **/ + if( fold(mode) == 'I' ) + { + c = 'P'; + goto addin; + } + save_v(vp); + if( cur_virt != INVALID ) + { + vp->o_v_char = virtual[cur_virt]; + first_virt = cur_virt--; + } + return(INSERT); + + case 'C': /** change to eol **/ + c = '$'; + goto chgeol; + + case 'c': /** change **/ + if( mode ) + c = vp->lastmotion; + else + c = getcount(vp,ed_getchar(vp->ed,-1)); +chgeol: + vp->lastmotion = c; + if( c == 'c' ) + { + del_line(vp,GOOD); + return(APPEND); + } + + if(!delmotion(vp, c, 'c')) + return(BAD); + + if( mode == 'c' ) + { + c = 'p'; + trepeat = 1; + goto addin; + } + first_virt = cur_virt + 1; + return(APPEND); + + case 'D': /** delete to eol **/ + c = '$'; + goto deleol; + + case 'd': /** delete **/ + if( mode ) + c = vp->lastmotion; + else + c = getcount(vp,ed_getchar(vp->ed,-1)); +deleol: + vp->lastmotion = c; + if( c == 'd' ) + { + del_line(vp,GOOD); + break; + } + if(!delmotion(vp, c, 'd')) + return(BAD); + if( cur_virt < last_virt ) + ++cur_virt; + break; + + case 'P': + if( p[0] == '\0' ) + return(BAD); + if( cur_virt != INVALID ) + { + i = virtual[cur_virt]; + if(!is_print(i)) + vp->ocur_virt = INVALID; + --cur_virt; + } + + case 'p': /** print **/ + if( p[0] == '\0' ) + return(BAD); + + if( mode != 's' && mode != 'c' ) + { + save_v(vp); + if( c == 'P' ) + { + /*** fix stored cur_virt ***/ + ++vp->u_column; + } + } + if( mode == 'R' ) + mode = REPLACE; + else + mode = APPEND; + savep = p; + for(i=0; irepeat, BAD); + if( mode ) + { + c = 'p'; + trepeat = 1; + goto addin; + } + first_virt = cur_virt + 1; + return(APPEND); + + case 'Y': /** Yank to end of line **/ + c = '$'; + goto yankeol; + + case 'y': /** yank thru motion **/ + if( mode ) + c = vp->lastmotion; + else + c = getcount(vp,ed_getchar(vp->ed,-1)); +yankeol: + vp->lastmotion = c; + if( c == 'y' ) + { + gencpy(yankbuf, virtual); + } + else if(!delmotion(vp, c, 'y')) + { + return(BAD); + } + break; + + case 'x': /** delete repeat chars forward - dl **/ + c = 'l'; + goto deleol; + + case 'X': /** delete repeat chars backward - dh **/ + c = 'h'; + goto deleol; + + case '~': /** invert case and advance **/ + if( cur_virt != INVALID ) + { + save_v(vp); + i = INVALID; + while(trepeat-->0 && i!=cur_virt) + { + i = cur_virt; + c = virtual[cur_virt]; +#if SHOPT_MULTIBYTE + if((c&~STRIP)==0) +#endif /* SHOPT_MULTIBYTE */ + if( isupper(c) ) + c = tolower(c); + else if( islower(c) ) + c = toupper(c); + replace(vp,c, 1); + } + return(GOOD); + } + else + return(BAD); + + default: + return(BAD); + } + refresh(vp,CONTROL); + return(GOOD); +} + + +#if SHOPT_MULTIBYTE + static int _isalph(register int v) + { +#ifdef _lib_iswalnum + return(iswalnum(v) || v=='_'); +#else + return((v&~STRIP) || isalnum(v) || v=='_'); +#endif + } + + + static int _isblank(register int v) + { + return((v&~STRIP)==0 && isspace(v)); + } + + static int _ismetach(register int v) + { + return((v&~STRIP)==0 && ismeta(v)); + } + +#endif /* SHOPT_MULTIBYTE */ + +/* + * get a character, after ^V processing + */ +static int getrchar(register Vi_t *vp) +{ + register int c; + if((c=ed_getchar(vp->ed,1))== usrlnext) + c = ed_getchar(vp->ed,2); + return(c); +} Index: src/lib/libshell/common/edit/history.c =================================================================== --- src/lib/libshell/common/edit/history.c (revision 0) +++ src/lib/libshell/common/edit/history.c (revision 740) @@ -0,0 +1,1109 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * History file manipulation routines + * + * David Korn + * AT&T Labs + * + */ + +/* + * Each command in the history file starts on an even byte is null terminated. + * The first byte must contain the special character HIST_UNDO and the second + * byte is the version number. The sequence HIST_UNDO 0, following a command, + * nullifies the previous command. A six byte sequence starting with + * HIST_CMDNO is used to store the command number so that it is not necessary + * to read the file from beginning to end to get to the last block of + * commands. This format of this sequence is different in version 1 + * then in version 0. Version 1 allows commands to use the full 8 bit + * character set. It can understand version 0 format files. + */ + + +#define HIST_MAX (sizeof(int)*HIST_BSIZE) +#define HIST_BIG (0100000-1024) /* 1K less than maximum short */ +#define HIST_LINE 32 /* typical length for history line */ +#define HIST_MARKSZ 6 +#define HIST_RECENT 600 +#define HIST_UNDO 0201 /* invalidate previous command */ +#define HIST_CMDNO 0202 /* next 3 bytes give command number */ +#define HIST_BSIZE 4096 /* size of history file buffer */ +#define HIST_DFLT 512 /* default size of history list */ + +#define _HIST_PRIVATE \ + off_t histcnt; /* offset into history file */\ + off_t histmarker; /* offset of last command marker */ \ + int histflush; /* set if flushed outside of hflush() */\ + int histmask; /* power of two mask for histcnt */ \ + char histbuff[HIST_BSIZE+1]; /* history file buffer */ \ + int histwfail; \ + off_t histcmds[2]; /* offset for recent commands, must be last */ + +#define hist_ind(hp,c) ((int)((c)&(hp)->histmask)) + +#include +#include +#include "FEATURE/time" +#include +#include +#include +#if KSHELL +# include "defs.h" +# include "variables.h" +# include "path.h" +# include "builtins.h" +# include "io.h" +#endif /* KSHELL */ +#include "history.h" + +#if !KSHELL +# define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x))) +# define NIL(type) ((type)0) +# define path_relative(x) (x) +# ifdef __STDC__ +# define nv_getval(s) getenv(#s) +# else +# define nv_getval(s) getenv("s") +# endif /* __STDC__ */ +# define e_unknown "unknown" +# define sh_translate(x) (x) + char login_sh = 0; + char hist_fname[] = "/.history"; +#endif /* KSHELL */ + +#ifndef O_BINARY +# define O_BINARY 0 +#endif /* O_BINARY */ + +int _Hist = 0; +static void hist_marker(char*,long); +static void hist_trim(History_t*, int); +static int hist_nearend(History_t*,Sfio_t*, off_t); +static int hist_check(int); +static int hist_clean(int); +#ifdef SF_BUFCONST + static ssize_t hist_write(Sfio_t*, const void*, size_t, Sfdisc_t*); + static int hist_exceptf(Sfio_t*, int, void*, Sfdisc_t*); +#else + static int hist_write(Sfio_t*, const void*, int, Sfdisc_t*); + static int hist_exceptf(Sfio_t*, int, Sfdisc_t*); +#endif + + +static int histinit; +static mode_t histmode; +static History_t *wasopen; +static History_t *hist_ptr; + +#if SHOPT_ACCTFILE + static int acctfd; + static char *logname; +# include + + int acctinit(void) + { + register char *cp, *acctfile; + Namval_t *np = nv_search("ACCTFILE",sh.var_tree,0); + + if(!np || !(acctfile=nv_getval(np))) + return(0); + if(!(cp = getlogin())) + { + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) + cp = userinfo->pw_name; + else + cp = "unknown"; + } + logname = strdup(cp); + + if((acctfd=sh_open(acctfile, + O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && + (unsigned)acctfd < 10) + { + int n; + if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0) + { + close(acctfd); + acctfd = n; + } + } + if(acctfd < 0) + { + acctfd = 0; + return(0); + } + if(strmatch(acctfile,e_devfdNN)) + { + char newfile[16]; + sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd); + nv_putval(np,newfile,NV_RDONLY); + } + else + fcntl(acctfd,F_SETFD,FD_CLOEXEC); + return(1); + } +#endif /* SHOPT_ACCTFILE */ + +static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION }; +static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL}; + +static void hist_touch(void *handle) +{ + touch((char*)handle, (time_t)0, (time_t)0, 0); +} + +/* + * open the history file + * if HISTNAME is not given and userid==0 then no history file. + * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is + * cleaned up. + * hist_open() returns 1, if history file is open + */ +int sh_histinit(void) +{ + register int fd; + register History_t *hp; + register char *histname; + char *fname=0; + int histmask, maxlines, hist_start=0; + register char *cp; + register off_t hsize = 0; + + if(sh.hist_ptr=hist_ptr) + return(1); + if(!(histname = nv_getval(HISTFILE))) + { + int offset = staktell(); + if(cp=nv_getval(HOME)) + stakputs(cp); + stakputs(hist_fname); + stakputc(0); + stakseek(offset); + histname = stakptr(offset); + } +#ifdef future + if(hp=wasopen) + { + /* reuse history file if same name */ + wasopen = 0; + sh.hist_ptr = hist_ptr = hp; + if(strcmp(histname,hp->histname)==0) + return(1); + else + hist_free(); + } +#endif +retry: + cp = path_relative(histname); + if(!histinit) + histmode = S_IRUSR|S_IWUSR; + if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) + { + hsize=lseek(fd,(off_t)0,SEEK_END); + } + if((unsigned)fd <=2) + { + int n; + if((n=fcntl(fd,F_DUPFD,10))>=0) + { + close(fd); + fd=n; + } + } + /* make sure that file has history file format */ + if(hsize && hist_check(fd)) + { + close(fd); + hsize = 0; + if(unlink(cp)>=0) + goto retry; + fd = -1; + } + if(fd < 0) + { +#if KSHELL + /* don't allow root a history_file in /tmp */ + if(sh.userid) +#endif /* KSHELL */ + { + if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) + return(0); + fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); + } + } + if(fd<0) + return(0); + /* set the file to close-on-exec */ + fcntl(fd,F_SETFD,FD_CLOEXEC); + if(cp=nv_getval(HISTSIZE)) + maxlines = (unsigned)strtol(cp, (char**)0, 10); + else + maxlines = HIST_DFLT; + for(histmask=16;histmask <= maxlines; histmask <<=1 ); + if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) + { + close(fd); + return(0); + } + sh.hist_ptr = hist_ptr = hp; + hp->histsize = maxlines; + hp->histmask = histmask; + hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); + memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); + hp->histind = 1; + hp->histcmds[1] = 2; + hp->histcnt = 2; + hp->histname = strdup(histname); + hp->histdisc = hist_disc; + if(hsize==0) + { + /* put special characters at front of file */ + sfwrite(hp->histfp,(char*)hist_stamp,2); + sfsync(hp->histfp); + } + /* initialize history list */ + else + { + int first,last; + off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; + hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); + hist_eof(hp); /* this sets histind to last command */ + if((hist_start = (last=(int)hp->histind)-maxlines) <=0) + hist_start = 1; + mark = hp->histmarker; + while(first > hist_start) + { + size += size; + first = hist_nearend(hp,hp->histfp,hsize-size); + hp->histind = first; + } + histinit = hist_start; + hist_eof(hp); + if(!histinit) + { + sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); + hp->histind = last; + hp->histmarker = mark; + } + histinit = 0; + } + if(fname) + { + unlink(fname); + free((void*)fname); + } + if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) + { +#ifdef DEBUG + sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); + sfsync(sfstderr); +#endif /* DEBUG */ + hist_trim(hp,(int)hp->histind-maxlines); + } + sfdisc(hp->histfp,&hp->histdisc); +#if KSHELL + (HISTCUR)->nvalue.lp = (&hp->histind); +#endif /* KSHELL */ + sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); +#if SHOPT_ACCTFILE + if(sh_isstate(SH_INTERACTIVE)) + acctinit(); +#endif /* SHOPT_ACCTFILE */ + return(1); +} + +/* + * close the history file and free the space + */ + +void hist_close(register History_t *hp) +{ + sfclose(hp->histfp); + free((char*)hp); + hist_ptr = 0; + sh.hist_ptr = 0; +#if SHOPT_ACCTFILE + if(acctfd) + { + close(acctfd); + acctfd = 0; + } +#endif /* SHOPT_ACCTFILE */ +} + +/* + * check history file format to see if it begins with special byte + */ +static int hist_check(register int fd) +{ + unsigned char magic[2]; + lseek(fd,(off_t)0,SEEK_SET); + if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO)) + return(1); + return(0); +} + +/* + * clean out history file OK if not modified in HIST_RECENT seconds + */ +static int hist_clean(int fd) +{ + struct stat statb; + return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT); +} + +/* + * Copy the last commands to a new file and make this the history file + */ + +static void hist_trim(History_t *hp, int n) +{ + register char *cp; + register int incmd=1, c=0; + register History_t *hist_new, *hist_old = hp; + char *buff, *endbuff, *tmpname=0; + off_t oldp,newp; + struct stat statb; + unlink(hist_old->histname); + if(access(hist_old->histname,F_OK) >= 0) + { + /* The unlink can fail on windows 95 */ + int fd; + char *last, *name=hist_old->histname; + close(sffileno(hist_old->histfp)); + tmpname = (char*)malloc(strlen(name)+14); + if(last = strrchr(name,'/')) + { + *last = 0; + pathtmp(tmpname,name,"hist",NIL(int*)); + *last = '/'; + } + else + pathtmp(tmpname,".","hist",NIL(int*)); + if(rename(name,tmpname) < 0) + tmpname = name; + fd = open(tmpname,O_RDONLY); + sfsetfd(hist_old->histfp,fd); + if(tmpname==name) + tmpname = 0; + } + hp = hist_ptr = 0; + if(fstat(sffileno(hist_old->histfp),&statb)>=0) + { + histinit = 1; + histmode = statb.st_mode; + } + if(!sh_histinit()) + { + /* use the old history file */ + hist_ptr = hist_old; + return; + } + hist_new = hist_ptr; + hist_ptr = hist_old; + if(--n < 0) + n = 0; + newp = hist_seek(hist_old,++n); + while(1) + { + if(!incmd) + { + c = hist_ind(hist_new,++hist_new->histind); + hist_new->histcmds[c] = hist_new->histcnt; + if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2) + { + char locbuff[HIST_MARKSZ]; + hist_marker(locbuff,hist_new->histind); + sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ); + hist_new->histcnt += HIST_MARKSZ; + hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt; + } + oldp = newp; + newp = hist_seek(hist_old,++n); + if(newp <=oldp) + break; + } + if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0))) + break; + *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0; + /* copy to null byte */ + incmd = 0; + while(*cp++); + if(cp > endbuff) + incmd = 1; + else if(*cp==0) + cp++; + if(cp > endbuff) + cp = endbuff; + c = cp-buff; + hist_new->histcnt += c; + sfwrite(hist_new->histfp,buff,c); + } + hist_ptr = hist_new; + hist_cancel(hist_ptr); + sfclose(hist_old->histfp); + if(tmpname) + { + unlink(tmpname); + free(tmpname); + } + free((char*)hist_old); +} + +/* + * position history file at size and find next command number + */ +static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size) +{ + register unsigned char *cp, *endbuff; + register int n, incmd=1; + unsigned char *buff, marker[4]; + if(size <= 2L || sfseek(iop,size,SEEK_SET)<0) + goto begin; + /* skip to marker command and return the number */ + /* numbering commands occur after a null and begin with HIST_CMDNO */ + while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR)) + { + n = sfvalue(iop); + *(endbuff=cp+n) = 0; + while(1) + { + /* check for marker */ + if(!incmd && *cp++==HIST_CMDNO && *cp==0) + { + n = cp+1 - buff; + incmd = -1; + break; + } + incmd = 0; + while(*cp++); + if(cp>endbuff) + { + incmd = 1; + break; + } + if(*cp==0 && ++cp>endbuff) + break; + } + size += n; + sfread(iop,(char*)buff,n); + if(incmd < 0) + { + if((n=sfread(iop,(char*)marker,4))==4) + { + n = (marker[0]<<16)|(marker[1]<<8)|marker[2]; + if(n < size/2) + { + hp->histmarker = hp->histcnt = size+4; + return(n); + } + n=4; + } + if(n >0) + size += n; + incmd = 0; + } + } +begin: + sfseek(iop,(off_t)2,SEEK_SET); + hp->histmarker = hp->histcnt = 2L; + return(1); +} + +/* + * This routine reads the history file from the present position + * to the end-of-file and puts the information in the in-core + * history table + * Note that HIST_CMDNO is only recognized at the beginning of a command + * and that HIST_UNDO as the first character of a command is skipped + * unless it is followed by 0. If followed by 0 then it cancels + * the previous command. + */ + +void hist_eof(register History_t *hp) +{ + register char *cp,*first,*endbuff; + register int incmd = 0; + register off_t count = hp->histcnt; + int n,skip=0; + sfseek(hp->histfp,count,SEEK_SET); + while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0)) + { + n = sfvalue(hp->histfp); + *(endbuff = cp+n) = 0; + first = cp += skip; + while(1) + { + while(!incmd) + { + if(cp>first) + { + count += (cp-first); + n = hist_ind(hp, ++hp->histind); +#ifdef future + if(count==hp->histcmds[n]) + { + sfprintf(sfstderr,"count match n=%d\n",n); + if(histinit) + { + histinit = 0; + return; + } + } + else if(n>=histinit) +#endif + hp->histcmds[n] = count; + first = cp; + } + switch(*((unsigned char*)(cp++))) + { + case HIST_CMDNO: + if(*cp==0) + { + hp->histmarker=count+2; + cp += (HIST_MARKSZ-1); + hp->histind--; +#ifdef future + if(cp <= endbuff) + { + unsigned char *marker = (unsigned char*)(cp-4); + int n = ((marker[0]<<16) +|(marker[1]<<8)|marker[2]); + if((nhistind+1)) + errormsg(SH_DICT,2,"index=%d marker=%d", hp->histind, n); + } +#endif + } + break; + case HIST_UNDO: + if(*cp==0) + { + cp+=1; + hp->histind-=2; + } + break; + default: + cp--; + incmd = 1; + } + if(cp > endbuff) + { + cp++; + goto refill; + } + } + first = cp; + while(*cp++); + if(cp > endbuff) + break; + incmd = 0; + while(*cp==0) + { + if(++cp > endbuff) + goto refill; + } + } + refill: + count += (--cp-first); + skip = (cp-endbuff); + if(!incmd && !skip) + hp->histcmds[hist_ind(hp,++hp->histind)] = count; + } + hp->histcnt = count; +} + +/* + * This routine will cause the previous command to be cancelled + */ + +void hist_cancel(register History_t *hp) +{ + register int c; + if(!hp) + return; + sfputc(hp->histfp,HIST_UNDO); + sfputc(hp->histfp,0); + sfsync(hp->histfp); + hp->histcnt += 2; + c = hist_ind(hp,--hp->histind); + hp->histcmds[c] = hp->histcnt; +} + +/* + * flush the current history command + */ + +void hist_flush(register History_t *hp) +{ + register char *buff; + if(hp) + { + if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR)) + { + hp->histflush = sfvalue(hp->histfp)+1; + sfwrite(hp->histfp,buff,0); + } + else + hp->histflush=0; + if(sfsync(hp->histfp)<0) + { + hist_close(hp); + if(!sh_histinit()) + sh_offoption(SH_HISTORY); + } + hp->histflush = 0; + } +} + +/* + * This is the write discipline for the history file + * When called from hist_flush(), trailing newlines are deleted and + * a zero byte. Line sequencing is added as required + */ + +#ifdef SF_BUFCONST +static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle) +#else +static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle) +#endif +{ + register History_t *hp = (History_t*)handle; + register char *bufptr = ((char*)buff)+insize; + register int c,size = insize; + register off_t cur; + int saved=0; + char saveptr[HIST_MARKSZ]; + if(!hp->histflush) + return(write(sffileno(iop),(char*)buff,size)); + if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0) + { + errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno); + return(-1); + } + hp->histcnt = cur; + /* remove whitespace from end of commands */ + while(--bufptr >= (char*)buff) + { + c= *bufptr; + if(!isspace(c)) + { + if(c=='\\' && *(bufptr+1)!='\n') + bufptr++; + break; + } + } + /* don't count empty lines */ + if(++bufptr <= (char*)buff) + return(insize); + *bufptr++ = '\n'; + *bufptr++ = 0; + size = bufptr - (char*)buff; +#if SHOPT_ACCTFILE + if(acctfd) + { + int timechars, offset; + offset = staktell(); + stakputs(buff); + stakseek(staktell() - 1); + timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *))); + lseek(acctfd, (off_t)0, SEEK_END); + write(acctfd, stakptr(offset), size - 2 + timechars); + stakseek(offset); + + } +#endif /* SHOPT_ACCTFILE */ + if(size&01) + { + size++; + *bufptr++ = 0; + } + hp->histcnt += size; + c = hist_ind(hp,++hp->histind); + hp->histcmds[c] = hp->histcnt; + if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2) + { + memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ); + saved=1; + hp->histcnt += HIST_MARKSZ; + hist_marker(bufptr,hp->histind); + hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt; + size += HIST_MARKSZ; + } + errno = 0; + size = write(sffileno(iop),(char*)buff,size); + if(saved) + memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ); + if(size>=0) + { + hp->histwfail = 0; + return(insize); + } + return(-1); +} + +/* + * Put history sequence number into buffer + * The buffer must be large enough to hold HIST_MARKSZ chars + */ + +static void hist_marker(register char *buff,register long cmdno) +{ + *buff++ = HIST_CMDNO; + *buff++ = 0; + *buff++ = (cmdno>>16); + *buff++ = (cmdno>>8); + *buff++ = cmdno; + *buff++ = 0; +} + +/* + * return byte offset in history file for command + */ +off_t hist_tell(register History_t *hp, int n) +{ + return(hp->histcmds[hist_ind(hp,n)]); +} + +/* + * seek to the position of command + */ +off_t hist_seek(register History_t *hp, int n) +{ + return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET)); +} + +/* + * write the command starting at offset onto file . + * if character appears before newline it is deleted + * each new-line character is replaced with string . + */ + +void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl) +{ + register int oldc=0; + register int c; + if(offset<0 || !hp) + { + sfputr(outfile,sh_translate(e_unknown),'\n'); + return; + } + sfseek(hp->histfp,offset,SEEK_SET); + while((c = sfgetc(hp->histfp)) != EOF) + { + if(c && oldc=='\n') + sfputr(outfile,nl,-1); + else if(last && (c==0 || (c=='\n' && oldc==last))) + return; + else if(oldc) + sfputc(outfile,oldc); + oldc = c; + if(c==0) + return; + } + return; +} + +/* + * find index for last line with given string + * If flag==0 then line must begin with string + * direction < 1 for backwards search +*/ + +Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction) +{ + register int index2; + off_t offset; + int *coffset=0; + Histloc_t location; + location.hist_command = -1; + location.hist_char = 0; + location.hist_line = 0; + if(!hp) + return(location); + /* leading ^ means beginning of line unless escaped */ + if(flag) + { + index2 = *string; + if(index2=='\\') + string++; + else if(index2=='^') + { + flag=0; + string++; + } + } + if(flag) + coffset = &location.hist_char; + index2 = (int)hp->histind; + if(direction<0) + { + index2 -= hp->histsize; + if(index2<1) + index2 = 1; + if(index1 <= index2) + return(location); + } + else if(index1 >= index2) + return(location); + while(index1!=index2) + { + direction>0?++index1:--index1; + offset = hist_tell(hp,index1); + if((location.hist_line=hist_match(hp,offset,string,coffset))>=0) + { + location.hist_command = index1; + return(location); + } +#if KSHELL + /* allow a search to be aborted */ + if(sh.trapnote&SH_SIGSET) + break; +#endif /* KSHELL */ + } + return(location); +} + +/* + * search for in history file starting at location + * If coffset==0 then line must begin with string + * returns the line number of the match if successful, otherwise -1 + */ + +int hist_match(register History_t *hp,off_t offset,char *string,int *coffset) +{ + register unsigned char *first, *cp; + register int m,n,c=1,line=0; +#if SHOPT_MULTIBYTE + mbinit(); +#endif /* SHOPT_MULTIBYTE */ + sfseek(hp->histfp,offset,SEEK_SET); + if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0))) + return(-1); + m = sfvalue(hp->histfp); + n = strlen(string); + while(m > n) + { + if(*cp==*string && memcmp(cp,string,n)==0) + { + if(coffset) + *coffset = (cp-first); + return(line); + } + if(!coffset) + break; + if(*cp=='\n') + line++; +#if SHOPT_MULTIBYTE + if((c=mbsize(cp)) < 0) + c = 1; +#endif /* SHOPT_MULTIBYTE */ + cp += c; + m -= c; + } + return(-1); +} + + +#if SHOPT_ESH || SHOPT_VSH +/* + * copy command from history file to s1 + * at most characters copied + * if s1==0 the number of lines for the command is returned + * line=linenumber for emacs copy and only this line of command will be copied + * line < 0 for full command copy + * -1 returned if there is no history file + */ + +int hist_copy(char *s1,int size,int command,int line) +{ + register int c; + register History_t *hp = sh_getinterp()->hist_ptr; + register int count = 0; + register char *s1max = s1+size; + if(!hp) + return(-1); + hist_seek(hp,command); + while ((c = sfgetc(hp->histfp)) && c!=EOF) + { + if(c=='\n') + { + if(count++ ==line) + break; + else if(line >= 0) + continue; + } + if(s1 && (line<0 || line==count)) + { + if(s1 >= s1max) + { + *--s1 = 0; + break; + } + *s1++ = c; + } + + } + sfseek(hp->histfp,(off_t)0,SEEK_END); + if(s1==0) + return(count); + if(count && (c= *(s1-1)) == '\n') + s1--; + *s1 = '\0'; + return(count); +} + +/* + * return word number from command number + */ + +char *hist_word(char *string,int size,int word) +{ + register int c; + register char *s1 = string; + register unsigned char *cp = (unsigned char*)s1; + register int flag = 0; + History_t *hp = hist_ptr; + if(!hp) +#if KSHELL + { + strncpy(string,sh.lastarg,size); + return(string); + } +#else + return(NIL(char*)); +#endif /* KSHELL */ + hist_copy(string,size,(int)hp->histind-1,-1); + for(;c = *cp;cp++) + { + c = isspace(c); + if(c && flag) + { + *cp = 0; + if(--word==0) + break; + flag = 0; + } + else if(c==0 && flag==0) + { + s1 = (char*)cp; + flag++; + } + } + *cp = 0; + if(s1 != string) + strcpy(string,s1); + return(string); +} + +#endif /* SHOPT_ESH */ + +#if SHOPT_ESH +/* + * given the current command and line number, + * and number of lines back or foward, + * compute the new command and line number. + */ + +Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines) +{ + Histloc_t next; + line += lines; + if(!hp) + { + command = -1; + goto done; + } + if(lines > 0) + { + register int count; + while(command <= hp->histind) + { + count = hist_copy(NIL(char*),0, command,-1); + if(count > line) + goto done; + line -= count; + command++; + } + } + else + { + register int least = (int)hp->histind-hp->histsize; + while(1) + { + if(line >=0) + goto done; + if(--command < least) + break; + line += hist_copy(NIL(char*),0, command,-1); + } + command = -1; + } +done: + next.hist_line = line; + next.hist_command = command; + return(next); +} +#endif /* SHOPT_ESH */ + + +/* + * Handle history file exceptions + */ +#ifdef SF_BUFCONST +static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle) +#else +static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle) +#endif +{ + register int newfd,oldfd; + History_t *hp = (History_t*)handle; + if(type==SF_WRITE) + { + if(errno==ENOSPC || hp->histwfail++ >= 10) + return(0); + /* write failure could be NFS problem, try to re-open */ + close(oldfd=sffileno(fp)); + if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0) + { + if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd) + return(-1); + fcntl(oldfd,F_SETFD,FD_CLOEXEC); + close(newfd); + if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt) + { + register int index = hp->histind; + lseek(oldfd,(off_t)2,SEEK_SET); + hp->histcnt = 2; + hp->histind = 1; + hp->histcmds[1] = 2; + hist_eof(hp); + hp->histmarker = hp->histcnt; + hp->histind = index; + } + return(1); + } + errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname); + return(-1); + } + return(0); +} Index: src/lib/libshell/common/edit/edit.c =================================================================== --- src/lib/libshell/common/edit/edit.c (revision 0) +++ src/lib/libshell/common/edit/edit.c (revision 740) @@ -0,0 +1,1489 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * edit.c - common routines for vi and emacs one line editors in shell + * + * David Korn P.D. Sullivan + * AT&T Labs + * + * Coded April 1983. + */ + +#include +#include +#include +#include +#include "FEATURE/options" +#include "FEATURE/time" +#include "FEATURE/cmds" +#ifdef _hdr_utime +# include +# include +#endif + +#if KSHELL +# include "defs.h" +# include "variables.h" +#else + extern char ed_errbuf[]; + char e_version[] = "\n@(#)$Id: Editlib version 1993-12-28 r $\0\n"; +#endif /* KSHELL */ +#include "io.h" +#include "terminal.h" +#include "history.h" +#include "edit.h" + +static char CURSOR_UP[20] = { ESC, '[', 'A', 0 }; + +#if SHOPT_MULTIBYTE +# define is_cntrl(c) ((c<=STRIP) && iscntrl(c)) +# define is_print(c) ((c&~STRIP) || isprint(c)) +#else +# define is_cntrl(c) iscntrl(c) +# define is_print(c) isprint(c) +#endif + +#if (CC_NATIVE == CC_ASCII) +# define printchar(c) ((c) ^ ('A'-cntl('A'))) +#else + static int printchar(int c) + { + switch(c) + { + + case cntl('A'): return('A'); + case cntl('B'): return('B'); + case cntl('C'): return('C'); + case cntl('D'): return('D'); + case cntl('E'): return('E'); + case cntl('F'): return('F'); + case cntl('G'): return('G'); + case cntl('H'): return('H'); + case cntl('I'): return('I'); + case cntl('J'): return('J'); + case cntl('K'): return('K'); + case cntl('L'): return('L'); + case cntl('M'): return('M'); + case cntl('N'): return('N'); + case cntl('O'): return('O'); + case cntl('P'): return('P'); + case cntl('Q'): return('Q'); + case cntl('R'): return('R'); + case cntl('S'): return('S'); + case cntl('T'): return('T'); + case cntl('U'): return('U'); + case cntl('V'): return('V'); + case cntl('W'): return('W'); + case cntl('X'): return('X'); + case cntl('Y'): return('Y'); + case cntl('Z'): return('Z'); + case cntl(']'): return(']'); + case cntl('['): return('['); + } + return('?'); + } +#endif +#define MINWINDOW 15 /* minimum width window */ +#define DFLTWINDOW 80 /* default window width */ +#define RAWMODE 1 +#define ALTMODE 2 +#define ECHOMODE 3 +#define SYSERR -1 + +#if SHOPT_OLDTERMIO +# undef tcgetattr +# undef tcsetattr +#endif /* SHOPT_OLDTERMIO */ + +#ifdef RT +# define VENIX 1 +#endif /* RT */ + + +#ifdef _hdr_sgtty +# ifdef TIOCGETP + static int l_mask; + static struct tchars l_ttychars; + static struct ltchars l_chars; + static char l_changed; /* set if mode bits changed */ +# define L_CHARS 4 +# define T_CHARS 2 +# define L_MASK 1 +# endif /* TIOCGETP */ +#endif /* _hdr_sgtty */ + +#if KSHELL + static int keytrap(Edit_t *,char*, int, int, int); +#else + Edit_t editb; +#endif /* KSHELL */ + + +#ifndef _POSIX_DISABLE +# define _POSIX_DISABLE 0 +#endif + +#ifdef future + static int compare(const char*, const char*, int); +#endif /* future */ +#if SHOPT_VSH || SHOPT_ESH +# define ttyparm (ep->e_ttyparm) +# define nttyparm (ep->e_nttyparm) + static const char bellchr[] = "\a"; /* bell char */ +#endif /* SHOPT_VSH || SHOPT_ESH */ + + +/* + * This routine returns true if fd refers to a terminal + * This should be equivalent to isatty + */ +int tty_check(int fd) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + struct termios tty; + ep->e_savefd = -1; + return(tty_get(fd,&tty)==0); +} + +/* + * Get the current terminal attributes + * This routine remembers the attributes and just returns them if it + * is called again without an intervening tty_set() + */ + +int tty_get(register int fd, register struct termios *tty) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + if(fd == ep->e_savefd) + *tty = ep->e_savetty; + else + { + while(tcgetattr(fd,tty) == SYSERR) + { + if(errno !=EINTR) + return(SYSERR); + errno = 0; + } + /* save terminal settings if in cannonical state */ + if(ep->e_raw==0) + { + ep->e_savetty = *tty; + ep->e_savefd = fd; + } + } + return(0); +} + +/* + * Set the terminal attributes + * If fd<0, then current attributes are invalidated + */ + +int tty_set(int fd, int action, struct termios *tty) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + if(fd >=0) + { +#ifdef future + if(ep->e_savefd>=0 && compare(&ep->e_savetty,tty,sizeof(struct termios))) + return(0); +#endif + while(tcsetattr(fd, action, tty) == SYSERR) + { + if(errno !=EINTR) + return(SYSERR); + errno = 0; + } + ep->e_savetty = *tty; + } + ep->e_savefd = fd; + return(0); +} + +#if SHOPT_ESH || SHOPT_VSH +/*{ TTY_COOKED( fd ) + * + * This routine will set the tty in cooked mode. + * It is also called by error.done(). + * +}*/ + +void tty_cooked(register int fd) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + if(ep->e_raw==0) + return; + if(fd < 0) + fd = ep->e_savefd; +#ifdef L_MASK + /* restore flags */ + if(l_changed&L_MASK) + ioctl(fd,TIOCLSET,&l_mask); + if(l_changed&T_CHARS) + /* restore alternate break character */ + ioctl(fd,TIOCSETC,&l_ttychars); + if(l_changed&L_CHARS) + /* restore alternate break character */ + ioctl(fd,TIOCSLTC,&l_chars); + l_changed = 0; +#endif /* L_MASK */ + /*** don't do tty_set unless ttyparm has valid data ***/ + if(tty_set(fd, TCSANOW, &ttyparm) == SYSERR) + return; + ep->e_raw = 0; + return; +} + +/*{ TTY_RAW( fd ) + * + * This routine will set the tty in raw mode. + * +}*/ + +int tty_raw(register int fd, int echomode) +{ + int echo = echomode; +#ifdef L_MASK + struct ltchars lchars; +#endif /* L_MASK */ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + if(ep->e_raw==RAWMODE) + return(echo?-1:0); + else if(ep->e_raw==ECHOMODE) + return(echo?0:-1); +#if !SHOPT_RAWONLY + if(ep->e_raw != ALTMODE) +#endif /* SHOPT_RAWONLY */ + { + if(tty_get(fd,&ttyparm) == SYSERR) + return(-1); + } +#if L_MASK || VENIX + if(ttyparm.sg_flags&LCASE) + return(-1); + if(!(ttyparm.sg_flags&ECHO)) + { + if(!echomode) + return(-1); + echo = 0; + } + nttyparm = ttyparm; + if(!echo) + nttyparm.sg_flags &= ~(ECHO | TBDELAY); +# ifdef CBREAK + nttyparm.sg_flags |= CBREAK; +# else + nttyparm.sg_flags |= RAW; +# endif /* CBREAK */ + ep->e_erase = ttyparm.sg_erase; + ep->e_kill = ttyparm.sg_kill; + ep->e_eof = cntl('D'); + ep->e_werase = cntl('W'); + ep->e_lnext = cntl('V'); + if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR ) + return(-1); + ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW); +# ifdef TIOCGLTC + /* try to remove effect of ^V and ^Y and ^O */ + if(ioctl(fd,TIOCGLTC,&l_chars) != SYSERR) + { + lchars = l_chars; + lchars.t_lnextc = -1; + lchars.t_flushc = -1; + lchars.t_dsuspc = -1; /* no delayed stop process signal */ + if(ioctl(fd,TIOCSLTC,&lchars) != SYSERR) + l_changed |= L_CHARS; + } +# endif /* TIOCGLTC */ +#else + if (!(ttyparm.c_lflag & ECHO )) + { + if(!echomode) + return(-1); + echo = 0; + } +# ifdef FLUSHO + ttyparm.c_lflag &= ~FLUSHO; +# endif /* FLUSHO */ + nttyparm = ttyparm; +# ifndef u370 + nttyparm.c_iflag &= ~(IGNPAR|PARMRK|INLCR|IGNCR|ICRNL); + nttyparm.c_iflag |= BRKINT; +# else + nttyparm.c_iflag &= + ~(IGNBRK|PARMRK|INLCR|IGNCR|ICRNL|INPCK); + nttyparm.c_iflag |= (BRKINT|IGNPAR); +# endif /* u370 */ + if(echo) + nttyparm.c_lflag &= ~ICANON; + else + nttyparm.c_lflag &= ~(ICANON|ECHO|ECHOK); + nttyparm.c_cc[VTIME] = 0; + nttyparm.c_cc[VMIN] = 1; +# ifdef VREPRINT + nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE; +# endif /* VREPRINT */ +# ifdef VDISCARD + nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE; +# endif /* VDISCARD */ +# ifdef VDSUSP + nttyparm.c_cc[VDSUSP] = _POSIX_DISABLE; +# endif /* VDSUSP */ +# ifdef VWERASE + if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE) + ep->e_werase = cntl('W'); + else + ep->e_werase = nttyparm.c_cc[VWERASE]; + nttyparm.c_cc[VWERASE] = _POSIX_DISABLE; +# else + ep->e_werase = cntl('W'); +# endif /* VWERASE */ +# ifdef VLNEXT + if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE ) + ep->e_lnext = cntl('V'); + else + ep->e_lnext = nttyparm.c_cc[VLNEXT]; + nttyparm.c_cc[VLNEXT] = _POSIX_DISABLE; +# else + ep->e_lnext = cntl('V'); +# endif /* VLNEXT */ + ep->e_eof = ttyparm.c_cc[VEOF]; + ep->e_erase = ttyparm.c_cc[VERASE]; + ep->e_kill = ttyparm.c_cc[VKILL]; + if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR ) + return(-1); + ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW); +#endif + ep->e_raw = (echomode?ECHOMODE:RAWMODE); + return(0); +} + +#if !SHOPT_RAWONLY + +/* + * + * Get tty parameters and make ESC and '\r' wakeup characters. + * + */ + +# ifdef TIOCGETC +int tty_alt(register int fd) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + int mask; + struct tchars ttychars; + switch(ep->e_raw) + { + case ECHOMODE: + return(-1); + case ALTMODE: + return(0); + case RAWMODE: + tty_cooked(fd); + } + l_changed = 0; + if( ep->e_ttyspeed == 0) + { + if((tty_get(fd,&ttyparm) != SYSERR)) + ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW); + ep->e_raw = ALTMODE; + } + if(ioctl(fd,TIOCGETC,&l_ttychars) == SYSERR) + return(-1); + if(ioctl(fd,TIOCLGET,&l_mask)==SYSERR) + return(-1); + ttychars = l_ttychars; + mask = LCRTBS|LCRTERA|LCTLECH|LPENDIN|LCRTKIL; + if((l_mask|mask) != l_mask) + l_changed = L_MASK; + if(ioctl(fd,TIOCLBIS,&mask)==SYSERR) + return(-1); + if(ttychars.t_brkc!=ESC) + { + ttychars.t_brkc = ESC; + l_changed |= T_CHARS; + if(ioctl(fd,TIOCSETC,&ttychars) == SYSERR) + return(-1); + } + return(0); +} +# else +# ifndef PENDIN +# define PENDIN 0 +# endif /* PENDIN */ +# ifndef IEXTEN +# define IEXTEN 0 +# endif /* IEXTEN */ + +int tty_alt(register int fd) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + switch(ep->e_raw) + { + case ECHOMODE: + return(-1); + case ALTMODE: + return(0); + case RAWMODE: + tty_cooked(fd); + } + if((tty_get(fd, &ttyparm)==SYSERR) || (!(ttyparm.c_lflag&ECHO))) + return(-1); +# ifdef FLUSHO + ttyparm.c_lflag &= ~FLUSHO; +# endif /* FLUSHO */ + nttyparm = ttyparm; + ep->e_eof = ttyparm.c_cc[VEOF]; +# ifdef ECHOCTL + /* escape character echos as ^[ */ + nttyparm.c_lflag |= (ECHOE|ECHOK|ECHOCTL|PENDIN|IEXTEN); + nttyparm.c_cc[VEOL] = ESC; +# else + /* switch VEOL2 and EOF, since EOF isn't echo'd by driver */ + nttyparm.c_lflag |= (ECHOE|ECHOK); + nttyparm.c_cc[VEOF] = ESC; /* make ESC the eof char */ +# ifdef VEOL2 + nttyparm.c_iflag &= ~(IGNCR|ICRNL); + nttyparm.c_iflag |= INLCR; + nttyparm.c_cc[VEOL] = '\r'; /* make CR an eol char */ + nttyparm.c_cc[VEOL2] = ep->e_eof; /* make EOF an eol char */ +# else + nttyparm.c_cc[VEOL] = ep->e_eof; /* make EOF an eol char */ +# endif /* VEOL2 */ +# endif /* ECHOCTL */ +# ifdef VREPRINT + nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE; +# endif /* VREPRINT */ +# ifdef VDISCARD + nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE; +# endif /* VDISCARD */ +# ifdef VWERASE + if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE) + nttyparm.c_cc[VWERASE] = cntl('W'); + ep->e_werase = nttyparm.c_cc[VWERASE]; +# else + ep->e_werase = cntl('W'); +# endif /* VWERASE */ +# ifdef VLNEXT + if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE ) + nttyparm.c_cc[VLNEXT] = cntl('V'); + ep->e_lnext = nttyparm.c_cc[VLNEXT]; +# else + ep->e_lnext = cntl('V'); +# endif /* VLNEXT */ + ep->e_erase = ttyparm.c_cc[VERASE]; + ep->e_kill = ttyparm.c_cc[VKILL]; + if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR ) + return(-1); + ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW); + ep->e_raw = ALTMODE; + return(0); +} + +# endif /* TIOCGETC */ +#endif /* SHOPT_RAWONLY */ + +/* + * ED_WINDOW() + * + * return the window size + */ +int ed_window(void) +{ + int rows,cols; + register char *cp = nv_getval(COLUMNS); + if(cp) + cols = (int)strtol(cp, (char**)0, 10)-1; + else + { + astwinsize(2,&rows,&cols); + if(--cols <0) + cols = DFLTWINDOW-1; + } + if(cols < MINWINDOW) + cols = MINWINDOW; + else if(cols > MAXWINDOW) + cols = MAXWINDOW; + return(cols); +} + +/* E_FLUSH() + * + * Flush the output buffer. + * + */ + +void ed_flush(Edit_t *ep) +{ + register int n = ep->e_outptr-ep->e_outbase; + register int fd = ERRIO; + if(n<=0) + return; + write(fd,ep->e_outbase,(unsigned)n); + ep->e_outptr = ep->e_outbase; +} + +/* + * send the bell character ^G to the terminal + */ + +void ed_ringbell(void) +{ + write(ERRIO,bellchr,1); +} + +/* + * send a carriage return line feed to the terminal + */ + +void ed_crlf(register Edit_t *ep) +{ +#ifdef cray + ed_putchar(ep,'\r'); +#endif /* cray */ +#ifdef u370 + ed_putchar(ep,'\r'); +#endif /* u370 */ +#ifdef VENIX + ed_putchar(ep,'\r'); +#endif /* VENIX */ + ed_putchar(ep,'\n'); + ed_flush(ep); +} + +/* ED_SETUP( max_prompt_size ) + * + * This routine sets up the prompt string + * The following is an unadvertised feature. + * Escape sequences in the prompt can be excluded from the calculated + * prompt length. This is accomplished as follows: + * - if the prompt string starts with "%\r, or contains \r%\r", where % + * represents any char, then % is taken to be the quote character. + * - strings enclosed by this quote character, and the quote character, + * are not counted as part of the prompt length. + */ + +void ed_setup(register Edit_t *ep, int fd, int reedit) +{ + register char *pp; + register char *last; + char *ppmax; + int myquote = 0, n; + register int qlen = 1; + char inquote = 0; + ep->e_fd = fd; + ep->e_multiline = sh_isoption(SH_MULTILINE)!=0; +#ifdef SIGWINCH + if(!(sh.sigflag[SIGWINCH]&SH_SIGFAULT)) + { + signal(SIGWINCH,sh_fault); + sh.sigflag[SIGWINCH] |= SH_SIGFAULT; + } + sh_fault(SIGWINCH); +#endif +#if KSHELL + ep->e_stkptr = stakptr(0); + ep->e_stkoff = staktell(); + if(!(last = sh.prompt)) + last = ""; + sh.prompt = 0; +#else + last = ep->e_prbuff; +#endif /* KSHELL */ + if(sh.hist_ptr) + { + register History_t *hp = sh.hist_ptr; + ep->e_hismax = hist_max(hp); + ep->e_hismin = hist_min(hp); + } + else + { + ep->e_hismax = ep->e_hismin = ep->e_hloff = 0; + } + ep->e_hline = ep->e_hismax; + if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS)) + ep->e_wsize = MAXLINE; + else + ep->e_wsize = ed_window()-2; + ep->e_winsz = ep->e_wsize+2; + ep->e_crlf = 1; + ep->e_plen = 0; + pp = ep->e_prompt; + ppmax = pp+PRSIZE-1; + *pp++ = '\r'; + { + register int c; + while(c= *last++) switch(c) + { + case ESC: + { + int skip=0; + ep->e_crlf = 0; + *pp++ = c; + for(n=1; c = *last++; n++) + { + if(pp < ppmax) + *pp++ = c; + if(c=='\a') + break; + if(skip || (c>='0' && c<='9')) + continue; + if(n>1 && c==';') + skip = 1; + else if(n>2 || (c!= '[' && c!= ']')) + break; + } + qlen += (n+1); + break; + } + case '\b': + if(pp>ep->e_prompt+1) + pp--; + break; + case '\r': + if(pp == (ep->e_prompt+2)) /* quote char */ + myquote = *(pp-1); + /*FALLTHROUGH*/ + + case '\n': + /* start again */ + ep->e_crlf = 1; + qlen = 1; + inquote = 0; + pp = ep->e_prompt+1; + break; + + case '\t': + /* expand tabs */ + while((pp-ep->e_prompt)%TABSIZE) + { + if(pp >= ppmax) + break; + *pp++ = ' '; + } + break; + + case '\a': + /* cut out bells */ + break; + + default: + if(c==myquote) + { + qlen += inquote; + inquote ^= 1; + } + if(pp < ppmax) + { + qlen += inquote; + *pp++ = c; + if(!inquote && !is_print(c)) + ep->e_crlf = 0; + } + } + } + if(pp-ep->e_prompt > qlen) + ep->e_plen = pp - ep->e_prompt - qlen; + *pp = 0; + if((ep->e_wsize -= ep->e_plen) < 7) + { + register int shift = 7-ep->e_wsize; + ep->e_wsize = 7; + pp = ep->e_prompt+1; + strcpy(pp,pp+shift); + ep->e_plen -= shift; + last[-ep->e_plen-2] = '\r'; + } + sfsync(sfstderr); + if(fd == sffileno(sfstderr)) + { + /* can't use output buffer when reading from stderr */ + static char *buff; + if(!buff) + buff = (char*)malloc(MAXLINE); + ep->e_outbase = ep->e_outptr = buff; + ep->e_outlast = ep->e_outptr + MAXLINE; + return; + } + qlen = sfset(sfstderr,SF_READ,0); + /* make sure SF_READ not on */ + ep->e_outbase = ep->e_outptr = (char*)sfreserve(sfstderr,SF_UNBOUND,SF_LOCKR); + ep->e_outlast = ep->e_outptr + sfvalue(sfstderr); + if(qlen) + sfset(sfstderr,SF_READ,1); + sfwrite(sfstderr,ep->e_outptr,0); + ep->e_eol = reedit; + if(ep->e_multiline) + { +#ifdef _cmd_tput + char *term; + if(!ep->e_term) + ep->e_term = nv_search("TERM",sh.var_tree,0); + if(ep->e_term && (term=nv_getval(ep->e_term)) && strlen(term)e_termname) && strcmp(term,ep->e_termname)) + { + sh_trap(".sh.subscript=$(tput cuu1 2>/dev/null)",0); + if(pp=nv_getval(SH_SUBSCRNOD)) + strncpy(CURSOR_UP,pp,sizeof(CURSOR_UP)-1); + nv_unset(SH_SUBSCRNOD); + strcpy(ep->e_termname,term); + } +#endif + ep->e_wsize = MAXLINE - (ep->e_plen-2); + } + if(ep->e_default && (pp = nv_getval(ep->e_default))) + { + n = strlen(pp); + if(n > LOOKAHEAD) + n = LOOKAHEAD; + ep->e_lookahead = n; + while(n-- > 0) + ep->e_lbuf[n] = *pp++; + ep->e_default = 0; + } +} + +/* + * Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set + * Use sfpkrd() to poll() or select() to wait for input if possible + * Unfortunately, systems that get interrupted from slow reads update + * this access time for for the terminal (in violation of POSIX). + * The fixtime() macro, resets the time to the time at entry in + * this case. This is not necessary for systems that can handle + * sfpkrd() correctly (i,e., those that support poll() or select() + */ +int ed_read(void *context, int fd, char *buff, int size, int reedit) +{ + register Edit_t *ep = (Edit_t*)context; + register int rv= -1; + register int delim = (ep->e_raw==RAWMODE?'\r':'\n'); + int mode = -1; + int (*waitevent)(int,long,int) = sh.waitevent; + if(ep->e_raw==ALTMODE) + mode = 1; + if(size < 0) + { + mode = 1; + size = -size; + } + sh_onstate(SH_TTYWAIT); + errno = EINTR; + sh.waitevent = 0; + while(rv<0 && errno==EINTR) + { + if(sh.trapnote&(SH_SIGSET|SH_SIGTRAP)) + goto done; + /* an interrupt that should be ignored */ + errno = 0; + if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0) + rv = sfpkrd(fd,buff,size,delim,-1L,mode); + } + if(rv < 0) + { +#ifdef _hdr_utime +# define fixtime() if(isdevtty)utime(ep->e_tty,&utimes) + int isdevtty=0; + struct stat statb; + struct utimbuf utimes; + if(errno==0 && !ep->e_tty) + { + if((ep->e_tty=ttyname(fd)) && stat(ep->e_tty,&statb)>=0) + { + ep->e_tty_ino = statb.st_ino; + ep->e_tty_dev = statb.st_dev; + } + } + if(ep->e_tty_ino && fstat(fd,&statb)>=0 && statb.st_ino==ep->e_tty_ino && statb.st_dev==ep->e_tty_dev) + { + utimes.actime = statb.st_atime; + utimes.modtime = statb.st_mtime; + isdevtty=1; + } +#else +# define fixtime() +#endif /* _hdr_utime */ + while(1) + { + rv = read(fd,buff,size); + if(rv>=0 || errno!=EINTR) + break; + if(sh.trapnote&(SH_SIGSET|SH_SIGTRAP)) + goto done; + /* an interrupt that should be ignored */ + fixtime(); + } + } + else if(rv>=0 && mode>0) + rv = read(fd,buff,rv>0?rv:1); +done: + sh.waitevent = waitevent; + sh_offstate(SH_TTYWAIT); + return(rv); +} + + +/* + * put of length onto lookahead stack + * if is non-zero, the negation of the character is put + * onto the stack so that it can be checked for KEYTRAP + * putstack() returns 1 except when in the middle of a multi-byte char + */ +static int putstack(Edit_t *ep,char string[], register int nbyte, int type) +{ + register int c; +#if SHOPT_MULTIBYTE + char *endp, *p=string; + int size, offset = ep->e_lookahead + nbyte; + *(endp = &p[nbyte]) = 0; + endp = &p[nbyte]; + do + { + c = (int)((*p) & STRIP); + if(c< 0x80 && c!='<') + { + if (type) + c = -c; +# ifndef CBREAK + if(c == '\0') + { + /*** user break key ***/ + ep->e_lookahead = 0; +# if KSHELL + sh_fault(SIGINT); + siglongjmp(ep->e_env, UINTR); +# endif /* KSHELL */ + } +# endif /* CBREAK */ + + } + else + { + again: + if((c=mbchar(p)) >=0) + { + p--; /* incremented below */ + if(type) + c = -c; + } +#ifdef EILSEQ + else if(errno == EILSEQ) + errno = 0; +#endif + else if((endp-p) < mbmax()) + { + if ((c=ed_read(ep,ep->e_fd,endp, 1,0)) == 1) + { + *++endp = 0; + goto again; + } + return(c); + } + else + { + ed_ringbell(); + c = -(int)((*p) & STRIP); + offset += mbmax()-1; + } + } + ep->e_lbuf[--offset] = c; + p++; + } + while (p < endp); + /* shift lookahead buffer if necessary */ + if(offset -= ep->e_lookahead) + { + for(size=offset;size < nbyte;size++) + ep->e_lbuf[ep->e_lookahead+size-offset] = ep->e_lbuf[ep->e_lookahead+size]; + } + ep->e_lookahead += nbyte-offset; +#else + while (nbyte > 0) + { + c = string[--nbyte] & STRIP; + ep->e_lbuf[ep->e_lookahead++] = (type?-c:c); +# ifndef CBREAK + if( c == '\0' ) + { + /*** user break key ***/ + ep->e_lookahead = 0; +# if KSHELL + sh_fault(SIGINT); + siglongjmp(ep->e_env, UINTR); +# endif /* KSHELL */ + } +# endif /* CBREAK */ + } +#endif /* SHOPT_MULTIBYTE */ + return(1); +} + +/* + * routine to perform read from terminal for vi and emacs mode + * can be one of the following: + * -2 vi insert mode - key binding is in effect + * -1 vi control mode - key binding is in effect + * 0 normal command mode - key binding is in effect + * 1 edit keys not mapped + * 2 Next key is literal + */ +int ed_getchar(register Edit_t *ep,int mode) +{ + register int n, c; + char readin[LOOKAHEAD+1]; + if(!ep->e_lookahead) + { + ed_flush(ep); + ep->e_inmacro = 0; + /* The while is necessary for reads of partial multbyte chars */ + if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0) + n = putstack(ep,readin,n,1); + } + if(ep->e_lookahead) + { + /* check for possible key mapping */ + if((c = ep->e_lbuf[--ep->e_lookahead]) < 0) + { + if(mode<=0 && sh.st.trap[SH_KEYTRAP]) + { + n=1; + if((readin[0]= -c) == ESC) + { + while(1) + { + if(!ep->e_lookahead) + { + if((c=sfpkrd(ep->e_fd,readin+n,1,'\r',(mode?400L:-1L),0))>0) + putstack(ep,readin+n,c,1); + } + if(!ep->e_lookahead) + break; + if((c=ep->e_lbuf[--ep->e_lookahead])>=0) + { + ep->e_lookahead++; + break; + } + c = -c; + readin[n++] = c; + if(c>='0' && c<='9' && n>2) + continue; + if(n>2 || (c!= '[' && c!= 'O')) + break; + } + } + if(n=keytrap(ep,readin,n,LOOKAHEAD-n,mode)) + { + putstack(ep,readin,n,0); + c = ep->e_lbuf[--ep->e_lookahead]; + } + else + c = ed_getchar(ep,mode); + } + else + c = -c; + } + /*** map '\r' to '\n' ***/ + if(c == '\r' && mode!=2) + c = '\n'; + if(ep->e_tabcount && !(c=='\t'||c==ESC || c=='\\' || c=='=' || c==cntl('L') || isdigit(c))) + ep->e_tabcount = 0; + } + else + siglongjmp(ep->e_env,(n==0?UEOF:UINTR)); + return(c); +} + +void ed_ungetchar(Edit_t *ep,register int c) +{ + if (ep->e_lookahead < LOOKAHEAD) + ep->e_lbuf[ep->e_lookahead++] = c; + return; +} + +/* + * put a character into the output buffer + */ + +void ed_putchar(register Edit_t *ep,register int c) +{ + char buf[8]; + register char *dp = ep->e_outptr; + register int i,size=1; + buf[0] = c; +#if SHOPT_MULTIBYTE + /* check for place holder */ + if(c == MARKER) + return; + if((size = mbconv(buf, (wchar_t)c)) > 1) + { + for (i = 0; i < (size-1); i++) + *dp++ = buf[i]; + c = buf[i]; + } + else + { + buf[0] = c; + size = 1; + } +#endif /* SHOPT_MULTIBYTE */ + if (buf[0] == '_' && size==1) + { + *dp++ = ' '; + *dp++ = '\b'; + } + *dp++ = c; + *dp = '\0'; + if(dp >= ep->e_outlast) + ed_flush(ep); + else + ep->e_outptr = dp; +} + +/* + * returns the line and column corresponding to offset in the physical buffer + * if is non-zero and <= , then correspodning will start the search + */ +Edpos_t ed_curpos(Edit_t *ep,genchar *phys, int off, int cur, Edpos_t curpos) +{ + register genchar *sp=phys; + register int c=1, col=ep->e_plen; + Edpos_t pos; +#if SHOPT_MULTIBYTE + char p[16]; +#endif /* SHOPT_MULTIBYTE */ + if(cur && off>=cur) + { + sp += cur; + off -= cur; + pos = curpos; + col = pos.col; + } + else + pos.line = 0; + while(off-->0) + { + if(c) + c = *sp++; +#if SHOPT_MULTIBYTE + if(c && (mbconv(p, (wchar_t)c))==1 && p[0]=='\n') +#else + if(c=='\n') +#endif /* SHOPT_MULTIBYTE */ + col = 0; + else + col++; + if(col > ep->e_winsz) + col = 0; + if(col==0) + pos.line++; + } + pos.col = col; + return(pos); +} + +static void ed_putstring(register Edit_t *ep, const char *str) +{ + register int c; + while(c = *str++) + ed_putchar(ep,c); +} + +int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first) +{ + static int oldline; + register int delta; + Edpos_t newpos; + + delta = new - old; + if( delta == 0 ) + return(new); + if(ep->e_multiline) + { + ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos); + newpos = ed_curpos(ep, physical, new,old,ep->e_curpos); + if(ep->e_curpos.col==0 && ep->e_curpos.line>0 && oldlinee_curpos.line && delta<0) + ed_putstring(ep,"\r\n"); + oldline = newpos.line; + if(ep->e_curpos.line > newpos.line) + { + int n; + for(;ep->e_curpos.line > newpos.line; ep->e_curpos.line--) + ed_putstring(ep,CURSOR_UP); + if(newpos.line==0 && (n=ep->e_plen- ep->e_curpos.col)>0) + { + ep->e_curpos.col += n; + ed_putchar(ep,'\r'); + if(!ep->e_crlf) + ed_putstring(ep,ep->e_prompt); + else + { + int m = ep->e_winsz+1-ep->e_plen; + ed_putchar(ep,'\n'); + n = ep->e_plen; + if(m < ed_genlen(physical)) + { + while(physical[m] && n-->0) + ed_putchar(ep,physical[m++]); + } + while(n-->0) + ed_putchar(ep,' '); + ed_putstring(ep,CURSOR_UP); + } + } + } + else if(ep->e_curpos.line < newpos.line) + { + for(;ep->e_curpos.line < newpos.line;ep->e_curpos.line++) + ed_putchar(ep,'\n'); + ed_putchar(ep,'\r'); + ep->e_curpos.col = 0; + } + delta = newpos.col - ep->e_curpos.col; + old = new - delta; + } + else + newpos.line=0; + if(delta<0) + { + /*** move to left ***/ + delta = -delta; + /*** attempt to optimize cursor movement ***/ + if(!ep->e_crlf || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) ) + { + for( ; delta; delta-- ) + ed_putchar(ep,'\b'); + } + else + { + if(newpos.line==0) + ed_putstring(ep,ep->e_prompt); + old = first; + delta = new-first; + } + } + while(delta-->0) + ed_putchar(ep,physical[old++]); + return(new); +} + +/* + * copy virtual to physical and return the index for cursor in physical buffer + */ +int ed_virt_to_phys(Edit_t *ep,genchar *virt,genchar *phys,int cur,int voff,int poff) +{ + register genchar *sp = virt; + register genchar *dp = phys; + register int c; + genchar *curp = sp + cur; + genchar *dpmax = phys+MAXLINE; + int d, r; + sp += voff; + dp += poff; + for(r=poff;c= *sp;sp++) + { + if(curp == sp) + r = dp - phys; +#if SHOPT_MULTIBYTE + d = mbwidth((wchar_t)c); + if(d==1 && is_cntrl(c)) + d = -1; + if(d>1) + { + /* multiple width character put in place holders */ + *dp++ = c; + while(--d >0) + *dp++ = MARKER; + /* in vi mode the cursor is at the last character */ + if(dp>=dpmax) + break; + continue; + } + else +#else + d = (is_cntrl(c)?-1:1); +#endif /* SHOPT_MULTIBYTE */ + if(d<0) + { + if(c=='\t') + { + c = dp-phys; + if(sh_isoption(SH_VI)) + c += ep->e_plen; + c = TABSIZE - c%TABSIZE; + while(--c>0) + *dp++ = ' '; + c = ' '; + } + else + { + *dp++ = '^'; + c = printchar(c); + } + /* in vi mode the cursor is at the last character */ + if(curp == sp && sh_isoption(SH_VI)) + r = dp - phys; + } + *dp++ = c; + if(dp>=dpmax) + break; + } + *dp = 0; + return(r); +} + +#if SHOPT_MULTIBYTE +/* + * convert external representation to an array of genchars + * and can be the same + * returns number of chars in dest + */ + +int ed_internal(const char *src, genchar *dest) +{ + register const unsigned char *cp = (unsigned char *)src; + register int c; + register wchar_t *dp = (wchar_t*)dest; + if(dest == (genchar*)roundof(cp-(unsigned char*)0,sizeof(genchar))) + { + genchar buffer[MAXLINE]; + c = ed_internal(src,buffer); + ed_gencpy((genchar*)dp,buffer); + return(c); + } + while(*cp) + *dp++ = mbchar(cp); + *dp = 0; + return(dp-(wchar_t*)dest); +} + +/* + * convert internal representation into character array . + * The and may be the same. + * returns number of chars in dest. + */ + +int ed_external(const genchar *src, char *dest) +{ + register genchar wc; + register int c,size; + register char *dp = dest; + char *dpmax = dp+sizeof(genchar)*MAXLINE-2; + if((char*)src == dp) + { + char buffer[MAXLINE*sizeof(genchar)]; + c = ed_external(src,buffer); + +#ifdef _lib_wcscpy + wcscpy((wchar_t *)dest,(const wchar_t *)buffer); +#else + strcpy(dest,buffer); +#endif + return(c); + } + while((wc = *src++) && dp to + */ + +void ed_gencpy(genchar *dp,const genchar *sp) +{ + dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar)); + sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar)); + while(*dp++ = *sp++); +} + +/* + * copy at most items from to + */ + +void ed_genncpy(register genchar *dp,register const genchar *sp, int n) +{ + dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar)); + sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar)); + while(n-->0 && (*dp++ = *sp++)); +} + +/* + * find the string length of + */ + +int ed_genlen(register const genchar *str) +{ + register const genchar *sp = str; + sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar)); + while(*sp++); + return(sp-str-1); +} +#endif /* SHOPT_MULTIBYTE */ +#endif /* SHOPT_ESH || SHOPT_VSH */ + +#ifdef future +/* + * returns 1 when bytes starting at and are equal + */ +static int compare(register const char *a,register const char *b,register int n) +{ + while(n-->0) + { + if(*a++ != *b++) + return(0); + } + return(1); +} +#endif + +#if SHOPT_OLDTERMIO + +# include + +#ifndef ECHOCTL +# define ECHOCTL 0 +#endif /* !ECHOCTL */ +#define ott ep->e_ott + +/* + * For backward compatibility only + * This version will use termios when possible, otherwise termio + */ + + +tcgetattr(int fd, struct termios *tt) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + register int r,i; + ep->e_tcgeta = 0; + ep->e_echoctl = (ECHOCTL!=0); + if((r=ioctl(fd,TCGETS,tt))>=0 || errno!=EINVAL) + return(r); + if((r=ioctl(fd,TCGETA,&ott)) >= 0) + { + tt->c_lflag = ott.c_lflag; + tt->c_oflag = ott.c_oflag; + tt->c_iflag = ott.c_iflag; + tt->c_cflag = ott.c_cflag; + for(i=0; ic_cc[i] = ott.c_cc[i]; + ep->e_tcgeta++; + ep->e_echoctl = 0; + } + return(r); +} + +tcsetattr(int fd,int mode,struct termios *tt) +{ + register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); + register int r; + if(ep->e_tcgeta) + { + register int i; + ott.c_lflag = tt->c_lflag; + ott.c_oflag = tt->c_oflag; + ott.c_iflag = tt->c_iflag; + ott.c_cflag = tt->c_cflag; + for(i=0; ic_cc[i]; + if(tt->c_lflag&ECHOCTL) + { + ott.c_lflag &= ~(ECHOCTL|IEXTEN); + ott.c_iflag &= ~(IGNCR|ICRNL); + ott.c_iflag |= INLCR; + ott.c_cc[VEOF]= ESC; /* ESC -> eof char */ + ott.c_cc[VEOL] = '\r'; /* CR -> eol char */ + ott.c_cc[VEOL2] = tt->c_cc[VEOF]; /* EOF -> eol char */ + } + switch(mode) + { + case TCSANOW: + mode = TCSETA; + break; + case TCSADRAIN: + mode = TCSETAW; + break; + case TCSAFLUSH: + mode = TCSETAF; + } + return(ioctl(fd,mode,&ott)); + } + return(ioctl(fd,mode,tt)); +} +#endif /* SHOPT_OLDTERMIO */ + +#if KSHELL +/* + * Execute keyboard trap on given buffer of given size + * < 0 for vi insert mode + */ +static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int mode) +{ + register char *cp; + int savexit; +#if SHOPT_MULTIBYTE + char buff[MAXLINE]; + ed_external(ep->e_inbuf,cp=buff); +#else + cp = ep->e_inbuf; +#endif /* SHOPT_MULTIBYTE */ + inbuff[insize] = 0; + ep->e_col = ep->e_cur; + if(mode== -2) + { + ep->e_col++; + *ep->e_vi_insert = ESC; + } + else + *ep->e_vi_insert = 0; + nv_putval(ED_CHRNOD,inbuff,NV_NOFREE); + nv_putval(ED_COLNOD,(char*)&ep->e_col,NV_NOFREE|NV_INTEGER); + nv_putval(ED_TXTNOD,(char*)cp,NV_NOFREE); + nv_putval(ED_MODENOD,ep->e_vi_insert,NV_NOFREE); + savexit = sh.savexit; + sh_trap(sh.st.trap[SH_KEYTRAP],0); + sh.savexit = savexit; + if((cp = nv_getval(ED_CHRNOD)) == inbuff) + nv_unset(ED_CHRNOD); + else + { + strncpy(inbuff,cp,bufsize); + insize = strlen(inbuff); + } + nv_unset(ED_TXTNOD); + return(insize); +} +#endif /* KSHELL */ + +void *ed_open(Shell_t *shp) +{ + Edit_t *ed = newof(0,Edit_t,1,0); + ed->sh = shp; + strcpy(ed->e_macro,"_??"); + return((void*)ed); +} Index: src/lib/libshell/common/edit/hexpand.c =================================================================== --- src/lib/libshell/common/edit/hexpand.c (revision 0) +++ src/lib/libshell/common/edit/hexpand.c (revision 740) @@ -0,0 +1,736 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * bash style history expansion + * + * Author: + * Karsten Fleischer + * Omnium Software Engineering + * An der Luisenburg 7 + * D-51379 Leverkusen + * Germany + * + * + */ + + +#include "defs.h" +#include "edit.h" + +#if ! SHOPT_HISTEXPAND + +NoN(hexpand) + +#else + +#include + +static char *modifiers = "htrepqxs&"; +static int mod_flags[] = { 0, 0, 0, 0, HIST_PRINT, HIST_QUOTE, HIST_QUOTE|HIST_QUOTE_BR, 0, 0 }; + +#define DONE() {flag |= HIST_ERROR; cp = 0; stakseek(0); goto done;} + +struct subst +{ + char *str[2]; /* [0] is "old", [1] is "new" string */ +}; + + +/* + * parse an /old/new/ string, delimiter expected as first char. + * if "old" not specified, keep sb->str[0] + * if "new" not specified, set sb->str[1] to empty string + * read up to third delimeter char, \n or \0, whichever comes first. + * return adress is one past the last valid char in s: + * - the address containing \n or \0 or + * - one char beyond the third delimiter + */ + +static char *parse_subst(const char *s, struct subst *sb) +{ + char *cp,del; + int off,n = 0; + + /* build the strings on the stack, mainly for '&' substition in "new" */ + off = staktell(); + + /* init "new" with empty string */ + if(sb->str[1]) + free(sb->str[1]); + sb->str[1] = strdup(""); + + /* get delimiter */ + del = *s; + + cp = (char*) s + 1; + + while(n < 2) + { + if(*cp == del || *cp == '\n' || *cp == '\0') + { + /* delimiter or EOL */ + if(staktell() != off) + { + /* dupe string on stack and rewind stack */ + stakputc('\0'); + if(sb->str[n]) + free(sb->str[n]); + sb->str[n] = strdup(stakptr(off)); + stakseek(off); + } + n++; + + /* if not delimiter, we've reached EOL. Get outta here. */ + if(*cp != del) + break; + } + else if(*cp == '\\') + { + if(*(cp+1) == del) /* quote delimiter */ + { + stakputc(del); + cp++; + } + else if(*(cp+1) == '&' && n == 1) + { /* quote '&' only in "new" */ + stakputc('&'); + cp++; + } + else + stakputc('\\'); + } + else if(*cp == '&' && n == 1 && sb->str[0]) + /* substitute '&' with "old" in "new" */ + stakputs(sb->str[0]); + else + stakputc(*cp); + cp++; + } + + /* rewind stack */ + stakseek(off); + + return cp; +} + +/* + * history expansion main routine + */ + +int hist_expand(const char *ln, char **xp) +{ + int off, /* stack offset */ + q, /* quotation flags */ + p, /* flag */ + c, /* current char */ + flag=0; /* HIST_* flags */ + Sfoff_t n, /* history line number, counter, etc. */ + i, /* counter */ + w[2]; /* word range */ + char *sp, /* stack pointer */ + *cp, /* current char in ln */ + *str, /* search string */ + *evp, /* event/word designator string, for error msgs */ + *cc=0, /* copy of current line up to cp; temp ptr */ + hc[3], /* default histchars */ + *qc="\'\"`"; /* quote characters */ + Sfio_t *ref=0, /* line referenced by event designator */ + *tmp=0, /* temporary line buffer */ + *tmp2=0;/* temporary line buffer */ + Histloc_t hl; /* history location */ + static Namval_t *np = 0; /* histchars variable */ + static struct subst sb = {0,0}; /* substition strings */ + static Sfio_t *wm=0; /* word match from !?string? event designator */ + + if(!wm) + wm = sfopen(NULL, NULL, "swr"); + + hc[0] = '!'; + hc[1] = '^'; + hc[2] = 0; + if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np))) + { + if(cp[0]) + { + hc[0] = cp[0]; + if(cp[1]) + { + hc[1] = cp[1]; + if(cp[2]) + hc[2] = cp[2]; + } + } + } + + /* save shell stack */ + if(off = staktell()) + sp = stakfreeze(0); + + cp = (char*)ln; + + while(cp && *cp) + { + /* read until event/quick substitution/comment designator */ + if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2]) + || (*cp == hc[1] && cp != ln)) + { + if(*cp == '\\') /* skip escaped designators */ + stakputc(*cp++); + else if(*cp == '\'') /* skip quoted designators */ + { + do + stakputc(*cp); + while(*++cp && *cp != '\''); + } + stakputc(*cp++); + continue; + } + + if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */ + { + stakputc(*cp++); + stakputs(cp); + DONE(); + } + + n = -1; + str = 0; + flag &= HIST_EVENT; /* save event flag for returning later */ + evp = cp; + ref = 0; + + if(*cp == hc[1]) /* shortcut substitution */ + { + flag |= HIST_QUICKSUBST; + goto getline; + } + + if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */ + { + cp += 2; + goto getline; + } + + switch(c = *++cp) { + case ' ': + case '\t': + case '\n': + case '\0': + case '=': + case '(': + stakputc(hc[0]); + continue; + case '#': /* the line up to current position */ + flag |= HIST_HASH; + cp++; + n = staktell(); /* terminate string and dup */ + stakputc('\0'); + cc = strdup(stakptr(0)); + stakseek(n); /* remove null byte again */ + ref = sfopen(ref, cc, "s"); /* open as file */ + n = 0; /* skip history file referencing */ + break; + case '-': /* back reference by number */ + if(!isdigit(*(cp+1))) + goto string_event; + cp++; + case '0': /* reference by number */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n = 0; + while(isdigit(*cp)) + n = n * 10 + (*cp++) - '0'; + if(c == '-') + n = -n; + break; + case '$': + n = -1; + case ':': + break; + case '?': + cp++; + flag |= HIST_QUESTION; + string_event: + default: + /* read until end of string or word designator/modifier */ + str = cp; + while(*cp) + { + cp++; + if((!(flag&HIST_QUESTION) && + (*cp == ':' || isspace(*cp) + || *cp == '^' || *cp == '$' + || *cp == '*' || *cp == '-' + || *cp == '%') + ) + || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n'))) + { + c = *cp; + *cp = '\0'; + } + } + break; + } + +getline: + flag |= HIST_EVENT; + if(str) /* !string or !?string? event designator */ + { + + /* search history for string */ + hl = hist_find(sh.hist_ptr, str, + sh.hist_ptr->histind, + flag&HIST_QUESTION, -1); + if((n = hl.hist_command) == -1) + n = 0; /* not found */ + } + if(n) + { + if(n < 0) /* determine index for backref */ + n = sh.hist_ptr->histind + n; + /* search and use history file if found */ + if(n > 0 && hist_seek(sh.hist_ptr, n) != -1) + ref = sh.hist_ptr->histfp; + + } + if(!ref) + { + /* string not found or command # out of range */ + c = *cp; + *cp = '\0'; + errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp); + *cp = c; + DONE(); + } + + if(str) /* string search: restore orig. line */ + { + if(flag&HIST_QUESTION) + *cp++ = c; /* skip second question mark */ + else + *cp = c; + } + + /* colon introduces either word designators or modifiers */ + if(*(evp = cp) == ':') + cp++; + + w[0] = 0; /* -1 means last word, -2 means match from !?string? */ + w[1] = -1; /* -1 means last word, -2 means suppress last word */ + + if(flag & HIST_QUICKSUBST) /* shortcut substitution */ + goto getsel; + + n = 0; + while(n < 2) + { + switch(c = *cp++) { + case '^': /* first word */ + if(n == 0) + { + w[0] = w[1] = 1; + goto skip; + } + else + goto skip2; + case '$': /* last word */ + w[n] = -1; + goto skip; + case '%': /* match from !?string? event designator */ + if(n == 0) + { + if(!str) + { + w[0] = 0; + w[1] = -1; + ref = wm; + } + else + { + w[0] = -2; + w[1] = sftell(ref) + hl.hist_char; + } + sfseek(wm, 0, SEEK_SET); + goto skip; + } + default: + skip2: + cp--; + n = 2; + break; + case '*': /* until last word */ + if(n == 0) + w[0] = 1; + w[1] = -1; + skip: + flag |= HIST_WORDDSGN; + n = 2; + break; + case '-': /* until last word or specified index */ + w[1] = -2; + flag |= HIST_WORDDSGN; + n = 1; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': /* specify index */ + if((*evp == ':') || w[1] == -2) + { + w[n] = c - '0'; + while(isdigit(c=*cp++)) + w[n] = w[n] * 10 + c - '0'; + flag |= HIST_WORDDSGN; + if(n == 0) + w[1] = w[0]; + n++; + } + else + n = 2; + cp--; + break; + } + } + + if(w[0] != -2 && w[1] > 0 && w[0] > w[1]) + { + c = *cp; + *cp = '\0'; + errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); + *cp = c; + DONE(); + } + + /* no valid word designator after colon, rewind */ + if(!(flag & HIST_WORDDSGN) && (*evp == ':')) + cp = evp; + +getsel: + /* open temp buffer, let sfio do the (re)allocation */ + tmp = sfopen(NULL, NULL, "swr"); + + /* push selected words into buffer, squash + whitespace into single blank or a newline */ + n = i = q = 0; + + while((c = sfgetc(ref)) > 0) + { + if(isspace(c)) + { + flag |= (c == '\n' ? HIST_NEWLINE : 0); + continue; + } + + if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1)) + { + if(w[0] < 0) + sfseek(tmp, 0, SEEK_SET); + else + i = sftell(tmp); + + if(i > 0) + sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' '); + + flag &= ~HIST_NEWLINE; + p = 1; + } + else + p = 0; + + do + { + cc = strchr(qc, c); + q ^= cc ? 1<<(int)(cc - qc) : 0; + if(p) + sfputc(tmp, c); + } + while((c = sfgetc(ref)) > 0 && (!isspace(c) || q)); + + if(w[0] == -2 && sftell(ref) > w[1]) + break; + + flag |= (c == '\n' ? HIST_NEWLINE : 0); + n++; + } + if(w[0] != -2 && w[1] >= 0 && w[1] >= n) + { + c = *cp; + *cp = '\0'; + errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); + *cp = c; + DONE(); + } + else if(w[1] == -2) /* skip last word */ + sfseek(tmp, i, SEEK_SET); + + /* remove trailing newline */ + if(sftell(tmp)) + { + sfseek(tmp, -1, SEEK_CUR); + if(sfgetc(tmp) == '\n') + sfungetc(tmp, '\n'); + } + + sfputc(tmp, '\0'); + + if(str) + { + if(wm) + sfclose(wm); + wm = tmp; + } + + if(cc && (flag&HIST_HASH)) + { + /* close !# temp file */ + sfclose(ref); + flag &= ~HIST_HASH; + free(cc); + cc = 0; + } + + evp = cp; + + /* selected line/words are now in buffer, now go for the modifiers */ + while(*cp == ':' || (flag & HIST_QUICKSUBST)) + { + if(flag & HIST_QUICKSUBST) + { + flag &= ~HIST_QUICKSUBST; + c = 's'; + cp--; + } + else + c = *++cp; + + sfseek(tmp, 0, SEEK_SET); + tmp2 = sfopen(tmp2, NULL, "swr"); + + if(c == 'g') /* global substitution */ + { + flag |= HIST_GLOBALSUBST; + c = *++cp; + } + + if(cc = strchr(modifiers, c)) + flag |= mod_flags[cc - modifiers]; + else + { + errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c); + DONE(); + } + + if(c == 'h' || c == 'r') /* head or base */ + { + n = -1; + while((c = sfgetc(tmp)) > 0) + { /* remember position of / or . */ + if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r')) + n = sftell(tmp2); + sfputc(tmp2, c); + } + if(n > 0) + { /* rewind to last / or . */ + sfseek(tmp2, n, SEEK_SET); + /* end string there */ + sfputc(tmp2, '\0'); + } + } + else if(c == 't' || c == 'e') /* tail or suffix */ + { + n = 0; + while((c = sfgetc(tmp)) > 0) + { /* remember position of / or . */ + if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e')) + n = sftell(tmp); + } + /* rewind to last / or . */ + sfseek(tmp, n, SEEK_SET); + /* copy from there on */ + while((c = sfgetc(tmp)) > 0) + sfputc(tmp2, c); + } + else if(c == 's' || c == '&') + { + cp++; + + if(c == 's') + { + /* preset old with match from !?string? */ + if(!sb.str[0] && wm) + sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0)); + cp = parse_subst(cp, &sb); + } + + if(!sb.str[0] || !sb.str[1]) + { + c = *cp; + *cp = '\0'; + errormsg(SH_DICT, ERROR_ERROR, + "%s%s: no previous substitution", + (flag & HIST_QUICKSUBST) ? ":s" : "", + evp); + *cp = c; + DONE(); + } + + /* need pointer for strstr() */ + str = sfsetbuf(tmp, (Void_t*)1, 0); + + flag |= HIST_SUBSTITUTE; + while(flag & HIST_SUBSTITUTE) + { + /* find string */ + if(cc = strstr(str, sb.str[0])) + { /* replace it */ + c = *cc; + *cc = '\0'; + sfputr(tmp2, str, -1); + sfputr(tmp2, sb.str[1], -1); + *cc = c; + str = cc + strlen(sb.str[0]); + } + else if(!sftell(tmp2)) + { /* not successfull */ + c = *cp; + *cp = '\0'; + errormsg(SH_DICT, ERROR_ERROR, + "%s%s: substitution failed", + (flag & HIST_QUICKSUBST) ? ":s" : "", + evp); + *cp = c; + DONE(); + } + /* loop if g modifier specified */ + if(!cc || !(flag & HIST_GLOBALSUBST)) + flag &= ~HIST_SUBSTITUTE; + } + /* output rest of line */ + sfputr(tmp2, str, -1); + if(*cp) + cp--; + } + + if(sftell(tmp2)) + { /* if any substitions done, swap buffers */ + if(wm != tmp) + sfclose(tmp); + tmp = tmp2; + tmp2 = 0; + } + cc = 0; + if(*cp) + cp++; + } + + /* flush temporary buffer to stack */ + if(tmp) + { + sfseek(tmp, 0, SEEK_SET); + + if(flag & HIST_QUOTE) + stakputc('\''); + + while((c = sfgetc(tmp)) > 0) + { + if(isspace(c)) + { + flag = flag & ~HIST_NEWLINE; + + /* squash white space to either a + blank or a newline */ + do + flag |= (c == '\n' ? HIST_NEWLINE : 0); + while((c = sfgetc(tmp)) > 0 && isspace(c)); + + sfungetc(tmp, c); + + c = (flag & HIST_NEWLINE) ? '\n' : ' '; + + if(flag & HIST_QUOTE_BR) + { + stakputc('\''); + stakputc(c); + stakputc('\''); + } + else + stakputc(c); + } + else if((c == '\'') && (flag & HIST_QUOTE)) + { + stakputc('\''); + stakputc('\\'); + stakputc(c); + stakputc('\''); + } + else + stakputc(c); + } + if(flag & HIST_QUOTE) + stakputc('\''); + } + } + + stakputc('\0'); + +done: + if(cc && (flag&HIST_HASH)) + { + /* close !# temp file */ + sfclose(ref); + free(cc); + cc = 0; + } + + /* error? */ + if(staktell() && !(flag & HIST_ERROR)) + *xp = strdup(stakfreeze(1)); + + /* restore shell stack */ + if(off) + stakset(sp,off); + else + stakseek(0); + + /* drop temporary files */ + + if(tmp && tmp != wm) + sfclose(tmp); + if(tmp2) + sfclose(tmp2); + + return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK); +} + +#endif Index: src/lib/libshell/common/edit/emacs.c =================================================================== --- src/lib/libshell/common/edit/emacs.c (revision 0) +++ src/lib/libshell/common/edit/emacs.c (revision 740) @@ -0,0 +1,1444 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* Original version by Michael T. Veach + * Adapted for ksh by David Korn */ +/* EMACS_MODES: c tabstop=4 + +One line screen editor for any program + +*/ + + +/* The following is provided by: + * + * Matthijs N. Melchior + * AT&T Network Systems International + * APT Nederland + * HV BZ335 x2962 + * hvlpb!mmelchio + * + * These are now on by default + * + * ESH_NFIRST + * - A ^N as first history related command after the prompt will move + * to the next command relative to the last known history position. + * It will not start at the position where the last command was entered + * as is done by the ^P command. Every history related command will + * set both the current and last position. Executing a command will + * only set the current position. + * + * ESH_KAPPEND + * - Successive kill and delete commands will accumulate their data + * in the kill buffer, by appending or prepending as appropriate. + * This mode will be reset by any command not adding something to the + * kill buffer. + * + * ESH_BETTER + * - Some enhancements: + * - argument for a macro is passed to its replacement + * - ^X^H command to find out about history position (debugging) + * - ^X^D command to show any debugging info + * + * I do not pretend these for changes are completely independent, + * but you can use them to seperate features. + */ + +#include +#include +#include "FEATURE/cmds" +#if KSHELL +# include "defs.h" +#endif /* KSHELL */ +#include "io.h" + +#include "history.h" +#include "edit.h" +#include "terminal.h" + +#define ESH_NFIRST +#define ESH_KAPPEND +#define ESH_BETTER + +#undef putchar +#define putchar(ed,c) ed_putchar(ed,c) +#define beep() ed_ringbell() + + +#if SHOPT_MULTIBYTE +# define gencpy(a,b) ed_gencpy(a,b) +# define genncpy(a,b,n) ed_genncpy(a,b,n) +# define genlen(str) ed_genlen(str) + static int print(int); + static int _isword(int); +# define isword(c) _isword(out[c]) + +#else +# define gencpy(a,b) strcpy((char*)(a),(char*)(b)) +# define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n) +# define genlen(str) strlen(str) +# define print(c) isprint(c) +# define isword(c) (isalnum(out[c]) || (out[c]=='_')) +#endif /*SHOPT_MULTIBYTE */ + +typedef struct _emacs_ +{ + genchar *screen; /* pointer to window buffer */ + genchar *cursor; /* Cursor in real screen */ + int mark; + int in_mult; + char cr_ok; + char CntrlO; + char overflow; /* Screen overflow flag set */ + char scvalid; /* Screen is up to date */ + int offset; /* Screen offset */ + enum + { + CRT=0, /* Crt terminal */ + PAPER /* Paper terminal */ + } terminal; + Histloc_t _location; + int prevdirection; + Edit_t *ed; /* pointer to edit data */ +} Emacs_t; + +#define editb (*ep->ed) +#define eol editb.e_eol +#define cur editb.e_cur +#define hline editb.e_hline +#define hloff editb.e_hloff +#define hismin editb.e_hismin +#define usrkill editb.e_kill +#define usrlnext editb.e_lnext +#define usreof editb.e_eof +#define usrerase editb.e_erase +#define crallowed editb.e_crlf +#define Prompt editb.e_prompt +#define plen editb.e_plen +#define kstack editb.e_killbuf +#define lstring editb.e_search +#define lookahead editb.e_lookahead +#define env editb.e_env +#define raw editb.e_raw +#define histlines editb.e_hismax +#define w_size editb.e_wsize +#define drawbuff editb.e_inbuf +#define killing editb.e_mode +#define location ep->_location + +#define LBUF 100 +#define KILLCHAR UKILL +#define ERASECHAR UERASE +#define EOFCHAR UEOF +#define LNEXTCHAR ULNEXT +#define DELETE ('a'==97?0177:7) + +/********************** +A large lookahead helps when the user is inserting +characters in the middle of the line. +************************/ + + +typedef enum +{ + FIRST, /* First time thru for logical line, prompt on screen */ + REFRESH, /* Redraw entire screen */ + APPEND, /* Append char before cursor to screen */ + UPDATE, /* Update the screen as need be */ + FINAL /* Update screen even if pending look ahead */ +} Draw_t; + +static void draw(Emacs_t*,Draw_t); +static int escape(Emacs_t*,genchar*, int); +static void putstring(Emacs_t*,char*); +static void search(Emacs_t*,genchar*,int); +static void setcursor(Emacs_t*,int, int); +static void show_info(Emacs_t*,const char*); +static void xcommands(Emacs_t*,int); + +int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit) +{ + Edit_t *ed = (Edit_t*)context; + register int c; + register int i; + register genchar *out; + register int count; + register Emacs_t *ep = ed->e_emacs; + int adjust,oadjust; + char backslash; + genchar *kptr; + char prompt[PRSIZE]; + genchar Screen[MAXLINE]; + if(!ep) + { + ep = ed->e_emacs = newof(0,Emacs_t,1,0); + ep->ed = ed; + ep->prevdirection = 1; + location.hist_command = -5; + } + Prompt = prompt; + ep->screen = Screen; + if(tty_raw(ERRIO,0) < 0) + { + return(reedit?reedit:ed_read(context, fd,buff,scend,0)); + } + raw = 1; + /* This mess in case the read system call fails */ + + ed_setup(ep->ed,fd,reedit); + out = (genchar*)buff; +#if SHOPT_MULTIBYTE + out = (genchar*)roundof((char*)out-(char*)0,sizeof(genchar)); + ed_internal(buff,out); +#endif /* SHOPT_MULTIBYTE */ + if(!kstack) + { + kstack = (genchar*)malloc(CHARSIZE*MAXLINE); + kstack[0] = '\0'; + } + drawbuff = out; +#ifdef ESH_NFIRST + if (location.hist_command == -5) /* to be initialized */ + { + kstack[0] = '\0'; /* also clear kstack... */ + location.hist_command = hline; + location.hist_line = hloff; + } + if (location.hist_command <= hismin) /* don't start below minimum */ + { + location.hist_command = hismin + 1; + location.hist_line = 0; + } + ep->in_mult = hloff; /* save pos in last command */ +#endif /* ESH_NFIRST */ + i = sigsetjmp(env,0); + if (i !=0) + { + tty_cooked(ERRIO); + if (i == UEOF) + { + return(0); /* EOF */ + } + return(-1); /* some other error */ + } + out[reedit] = 0; + if(scend+plen > (MAXLINE-2)) + scend = (MAXLINE-2)-plen; + ep->mark = 0; + cur = eol; + draw(ep,reedit?REFRESH:FIRST); + adjust = -1; + backslash = 0; + if (ep->CntrlO) + { +#ifdef ESH_NFIRST + ed_ungetchar(ep->ed,cntl('N')); +#else + location = hist_locate(sh.hist_ptr,location.hist_command,location.hist_line,1); + if (location.hist_command < histlines) + { + hline = location.hist_command; + hloff = location.hist_line; + hist_copy((char*)kstack,MAXLINE, hline,hloff); +# if SHOPT_MULTIBYTE + ed_internal((char*)kstack,kstack); +# endif /* SHOPT_MULTIBYTE */ + ed_ungetchar(ep->ed,cntl('Y')); + } +#endif /* ESH_NFIRST */ + } + ep->CntrlO = 0; + while ((c = ed_getchar(ep->ed,0)) != (-1)) + { + if (backslash) + { + backslash = 0; + if (c==usrerase||c==usrkill||(!print(c) && + (c!='\r'&&c!='\n'))) + { + /* accept a backslashed character */ + cur--; + out[cur++] = c; + out[eol] = '\0'; + draw(ep,APPEND); + continue; + } + } + if (c == usrkill) + { + c = KILLCHAR ; + } + else if (c == usrerase) + { + c = ERASECHAR ; + } + else if (c == usrlnext) + { + c = LNEXTCHAR ; + } + else if ((c == usreof)&&(eol == 0)) + { + c = EOFCHAR; + } +#ifdef ESH_KAPPEND + if (--killing <= 0) /* reset killing flag */ + killing = 0; +#endif + oadjust = count = adjust; + if(count<0) + count = 1; + adjust = -1; + i = cur; + switch(c) + { + case LNEXTCHAR: + c = ed_getchar(ep->ed,2); + goto do_default_processing; + case cntl('V'): + show_info(ep,fmtident(e_version)); + continue; + case '\0': + ep->mark = i; + continue; + case cntl('X'): + xcommands(ep,count); + continue; + case EOFCHAR: + ed_flush(ep->ed); + tty_cooked(ERRIO); + return(0); +#ifdef u370 + case cntl('S') : + case cntl('Q') : + continue; +#endif /* u370 */ + case '\t': + if(cur>0 && ep->ed->sh->nextprompt) + { + if(ep->ed->e_tabcount==0) + { + ep->ed->e_tabcount=1; + ed_ungetchar(ep->ed,ESC); + goto do_escape; + } + else if(ep->ed->e_tabcount==1) + { + ed_ungetchar(ep->ed,'='); + goto do_escape; + } + ep->ed->e_tabcount = 0; + } + do_default_processing: + default: + + if ((eol+1) >= (scend)) /* will not fit on line */ + { + ed_ungetchar(ep->ed,c); /* save character for next line */ + goto process; + } + for(i= ++eol; i>cur; i--) + out[i] = out[i-1]; + backslash = (c == '\\'); + out[cur++] = c; + draw(ep,APPEND); + continue; + case cntl('Y') : + { + c = genlen(kstack); + if ((c + eol) > scend) + { + beep(); + continue; + } + ep->mark = i; + for(i=eol;i>=cur;i--) + out[c+i] = out[i]; + kptr=kstack; + while (i = *kptr++) + out[cur++] = i; + draw(ep,UPDATE); + eol = genlen(out); + continue; + } + case '\n': + case '\r': + c = '\n'; + goto process; + + case DELETE: /* delete char 0x7f */ + case '\b': /* backspace, ^h */ + case ERASECHAR : + if (count > i) + count = i; +#ifdef ESH_KAPPEND + kptr = &kstack[count]; /* move old contents here */ + if (killing) /* prepend to killbuf */ + { + c = genlen(kstack) + CHARSIZE; /* include '\0' */ + while(c--) /* copy stuff */ + kptr[c] = kstack[c]; + } + else + *kptr = 0; /* this is end of data */ + killing = 2; /* we are killing */ + i -= count; + eol -= count; + genncpy(kstack,out+i,cur-i); +#else + while ((count--)&&(i>0)) + { + i--; + eol--; + } + genncpy(kstack,out+i,cur-i); + kstack[cur-i] = 0; +#endif /* ESH_KAPPEND */ + gencpy(out+i,out+cur); + ep->mark = i; + goto update; + case cntl('W') : +#ifdef ESH_KAPPEND + ++killing; /* keep killing flag */ +#endif + if (ep->mark > eol ) + ep->mark = eol; + if (ep->mark == i) + continue; + if (ep->mark > i) + { + adjust = ep->mark - i; + ed_ungetchar(ep->ed,cntl('D')); + continue; + } + adjust = i - ep->mark; + ed_ungetchar(ep->ed,usrerase); + continue; + case cntl('D') : + ep->mark = i; +#ifdef ESH_KAPPEND + if (killing) + kptr = &kstack[genlen(kstack)]; /* append here */ + else + kptr = kstack; + killing = 2; /* we are now killing */ +#else + kptr = kstack; +#endif /* ESH_KAPPEND */ + while ((count--)&&(eol>0)&&(ii) + { + if (cntlC) + { + c = out[i]; +#if SHOPT_MULTIBYTE + if((c&~STRIP)==0 && islower(c)) +#else + if(islower(c)) +#endif /* SHOPT_MULTIBYTE */ + { + c += 'A' - 'a'; + out[i] = c; + } + } + i++; + } + goto update; + } + case cntl(']') : + c = ed_getchar(ep->ed,1); + if ((count == 0) || (count > eol)) + { + beep(); + continue; + } + if (out[i]) + i++; + while (i < eol) + { + if (out[i] == c && --count==0) + goto update; + i++; + } + i = 0; + while (i < cur) + { + if (out[i] == c && --count==0) + break; + i++; + }; + +update: + cur = i; + draw(ep,UPDATE); + continue; + + case cntl('B') : + if (count > i) + count = i; + i -= count; + goto update; + case cntl('T') : + if ((sh_isoption(SH_EMACS))&& (eol!=i)) + i++; + if (i >= 2) + { + c = out[i - 1]; + out[i-1] = out[i-2]; + out[i-2] = c; + } + else + { + if(sh_isoption(SH_EMACS)) + i--; + beep(); + continue; + } + goto update; + case cntl('A') : + i = 0; + goto update; + case cntl('E') : + i = eol; + goto update; + case cntl('U') : + adjust = 4*count; + continue; + case KILLCHAR : + cur = 0; + oadjust = -1; + case cntl('K') : + if(oadjust >= 0) + { +#ifdef ESH_KAPPEND + killing = 2; /* set killing signal */ +#endif + ep->mark = count; + ed_ungetchar(ep->ed,cntl('W')); + continue; + } + i = cur; + eol = i; + ep->mark = i; +#ifdef ESH_KAPPEND + if (killing) /* append to kill buffer */ + gencpy(&kstack[genlen(kstack)], &out[i]); + else + gencpy(kstack,&out[i]); + killing = 2; /* set killing signal */ +#else + gencpy(kstack,&out[i]); +#endif /* ESH_KAPPEND */ + out[i] = 0; + draw(ep,UPDATE); + if (c == KILLCHAR) + { + if (ep->terminal == PAPER) + { + putchar(ep->ed,'\n'); + putstring(ep,Prompt); + } + c = ed_getchar(ep->ed,0); + if (c != usrkill) + { + ed_ungetchar(ep->ed,c); + continue; + } + if (ep->terminal == PAPER) + ep->terminal = CRT; + else + { + ep->terminal = PAPER; + putchar(ep->ed,'\n'); + putstring(ep,Prompt); + } + } + continue; + case cntl('L'): + ed_crlf(ep->ed); + draw(ep,REFRESH); + continue; + case cntl('[') : + do_escape: + adjust = escape(ep,out,oadjust); + continue; + case cntl('R') : + search(ep,out,count); + goto drawline; + case cntl('P') : + if (count <= hloff) + hloff -= count; + else + { + hline -= count - hloff; + hloff = 0; + } +#ifdef ESH_NFIRST + if (hline <= hismin) +#else + if (hline < hismin) +#endif /* ESH_NFIRST */ + { + hline = hismin+1; + beep(); +#ifndef ESH_NFIRST + continue; +#endif + } + goto common; + + case cntl('O') : + location.hist_command = hline; + location.hist_line = hloff; + ep->CntrlO = 1; + c = '\n'; + goto process; + case cntl('N') : +#ifdef ESH_NFIRST + hline = location.hist_command; /* start at saved position */ + hloff = location.hist_line; +#endif /* ESH_NFIRST */ + location = hist_locate(sh.hist_ptr,hline,hloff,count); + if (location.hist_command > histlines) + { + beep(); +#ifdef ESH_NFIRST + location.hist_command = histlines; + location.hist_line = ep->in_mult; +#else + continue; +#endif /* ESH_NFIRST */ + } + hline = location.hist_command; + hloff = location.hist_line; + common: +#ifdef ESH_NFIRST + location.hist_command = hline; /* save current position */ + location.hist_line = hloff; +#endif + hist_copy((char*)out,MAXLINE, hline,hloff); +#if SHOPT_MULTIBYTE + ed_internal((char*)(out),out); +#endif /* SHOPT_MULTIBYTE */ + drawline: + eol = genlen(out); + cur = eol; + draw(ep,UPDATE); + continue; + } + + } + +process: + + if (c == (-1)) + { + lookahead = 0; + beep(); + *out = '\0'; + } + draw(ep,FINAL); + tty_cooked(ERRIO); + if(ed->e_nlist) + { + ed->e_nlist = 0; + stakset(ed->e_stkptr,ed->e_stkoff); + } + if(c == '\n') + { + out[eol++] = '\n'; + out[eol] = '\0'; + ed_crlf(ep->ed); + } +#if SHOPT_MULTIBYTE + ed_external(out,buff); +#endif /* SHOPT_MULTIBYTE */ + i = strlen(buff); + if (i) + return(i); + return(-1); +} + +static void show_info(Emacs_t *ep,const char *str) +{ + register genchar *out = drawbuff; + register int c; + genchar string[LBUF]; + int sav_cur = cur; + /* save current line */ + genncpy(string,out,sizeof(string)/sizeof(*string)); + *out = 0; + cur = 0; +#if SHOPT_MULTIBYTE + ed_internal(str,out); +#else + gencpy(out,str); +#endif /* SHOPT_MULTIBYTE */ + draw(ep,UPDATE); + c = ed_getchar(ep->ed,0); + if(c!=' ') + ed_ungetchar(ep->ed,c); + /* restore line */ + cur = sav_cur; + genncpy(out,string,sizeof(string)/sizeof(*string)); + draw(ep,UPDATE); +} + +static void putstring(Emacs_t* ep,register char *sp) +{ + register int c; + while (c= *sp++) + putchar(ep->ed,c); +} + + +static int escape(register Emacs_t* ep,register genchar *out,int count) +{ + register int i,value; + int digit,ch; + digit = 0; + value = 0; + while ((i=ed_getchar(ep->ed,0)),isdigit(i)) + { + value *= 10; + value += (i - '0'); + digit = 1; + } + if (digit) + { + ed_ungetchar(ep->ed,i) ; +#ifdef ESH_KAPPEND + ++killing; /* don't modify killing signal */ +#endif + return(value); + } + value = count; + if(value<0) + value = 1; + switch(ch=i) + { + case cntl('V'): + show_info(ep,fmtident(e_version)); + return(-1); + case ' ': + ep->mark = cur; + return(-1); + +#ifdef ESH_KAPPEND + case '+': /* M-+ = append next kill */ + killing = 2; + return -1; /* no argument for next command */ +#endif + + case 'p': /* M-p == ^W^Y (copy stack == kill & yank) */ + ed_ungetchar(ep->ed,cntl('Y')); + ed_ungetchar(ep->ed,cntl('W')); +#ifdef ESH_KAPPEND + killing = 0; /* start fresh */ +#endif + return(-1); + + case 'l': /* M-l == lower-case */ + case 'd': + case 'c': + case 'f': + { + i = cur; + while(value-- && i 0) + { + i = out[cur]; +#if SHOPT_MULTIBYTE + if((i&~STRIP)==0 && isupper(i)) +#else + if(isupper(i)) +#endif /* SHOPT_MULTIBYTE */ + { + i += 'a' - 'A'; + out[cur] = i; + } + cur++; + } + draw(ep,UPDATE); + return(-1); + } + + else if(ch=='f') + goto update; + else if(ch=='c') + { + ed_ungetchar(ep->ed,cntl('C')); + return(i-cur); + } + else + { + if (i-cur) + { + ed_ungetchar(ep->ed,cntl('D')); +#ifdef ESH_KAPPEND + ++killing; /* keep killing signal */ +#endif + return(i-cur); + } + beep(); + return(-1); + } + } + + + case 'b': + case DELETE : + case '\b': + case 'h': + { + i = cur; + while(value-- && i>0) + { + i--; + while ((i>0)&&(!isword(i))) + i--; + while ((i>0)&&(isword(i-1))) + i--; + } + if(ch=='b') + goto update; + else + { + ed_ungetchar(ep->ed,usrerase); +#ifdef ESH_KAPPEND + ++killing; +#endif + return(cur-i); + } + } + + case '>': + ed_ungetchar(ep->ed,cntl('N')); +#ifdef ESH_NFIRST + if (ep->in_mult) + { + location.hist_command = histlines; + location.hist_line = ep->in_mult - 1; + } + else + { + location.hist_command = histlines - 1; + location.hist_line = 0; + } +#else + hline = histlines-1; + hloff = 0; +#endif /* ESH_NFIRST */ + return(0); + + case '<': + ed_ungetchar(ep->ed,cntl('P')); + hloff = 0; +#ifdef ESH_NFIRST + hline = hismin + 1; + return 0; +#else + return(hline-hismin); +#endif /* ESH_NFIRST */ + + + case '#': + ed_ungetchar(ep->ed,'\n'); + ed_ungetchar(ep->ed,(out[0]=='#')?cntl('D'):'#'); + ed_ungetchar(ep->ed,cntl('A')); + return(-1); + case '_' : + case '.' : + { + genchar name[MAXLINE]; + char buf[MAXLINE]; + char *ptr; + ptr = hist_word(buf,MAXLINE,(count?count:-1)); +#if !KSHELL + if(ptr==0) + { + beep(); + break; + } +#endif /* KSHELL */ + if ((eol - cur) >= sizeof(name)) + { + beep(); + return(-1); + } + ep->mark = cur; + gencpy(name,&out[cur]); + while(*ptr) + { + out[cur++] = *ptr++; + eol++; + } + gencpy(&out[cur],name); + draw(ep,UPDATE); + return(-1); + } +#if KSHELL + + /* file name expansion */ + case cntl('[') : /* filename completion */ + i = '\\'; + case '*': /* filename expansion */ + case '=': /* escape = - list all matching file names */ + ep->mark = cur; + if(ed_expand(ep->ed,(char*)out,&cur,&eol,i,count) < 0) + { + if(ep->ed->e_tabcount==1) + { + ep->ed->e_tabcount=2; + ed_ungetchar(ep->ed,cntl('\t')); + return(-1); + } + beep(); + } + else if(i=='=') + { + draw(ep,REFRESH); + if(count>0) + ep->ed->e_tabcount=0; + else + { + i=ed_getchar(ep->ed,0); + ed_ungetchar(ep->ed,i); + if(isdigit(i)) + ed_ungetchar(ep->ed,ESC); + } + } + else + { + if(i=='\\' && cur>ep->mark && (out[cur-1]=='/' || out[cur-1]==' ')) + ep->ed->e_tabcount=0; + draw(ep,UPDATE); + } + return(-1); + + /* search back for character */ + case cntl(']'): /* feature not in book */ + { + int c = ed_getchar(ep->ed,1); + if ((value == 0) || (value > eol)) + { + beep(); + return(-1); + } + i = cur; + if (i > 0) + i--; + while (i >= 0) + { + if (out[i] == c && --value==0) + goto update; + i--; + } + i = eol; + while (i > cur) + { + if (out[i] == c && --value==0) + break; + i--; + }; + + } + update: + cur = i; + draw(ep,UPDATE); + return(-1); + +#ifdef _cmd_tput + case cntl('L'): /* clear screen */ + sh_trap("tput clear", 0); + draw(ep,REFRESH); + return(-1); +#endif + case '[': /* feature not in book */ + switch(i=ed_getchar(ep->ed,1)) + { + case 'A': + ed_ungetchar(ep->ed,cntl('P')); + return(-1); + case 'B': + ed_ungetchar(ep->ed,cntl('N')); + return(-1); + case 'C': + ed_ungetchar(ep->ed,cntl('F')); + return(-1); + case 'D': + ed_ungetchar(ep->ed,cntl('B')); + return(-1); + case 'H': + ed_ungetchar(ep->ed,cntl('A')); + return(-1); + case 'Y': + ed_ungetchar(ep->ed,cntl('E')); + return(-1); + default: + ed_ungetchar(ep->ed,i); + } + i = '_'; + + default: + /* look for user defined macro definitions */ + if(ed_macro(ep->ed,i)) +# ifdef ESH_BETTER + return(count); /* pass argument to macro */ +# else + return(-1); +# endif /* ESH_BETTER */ +#else + update: + cur = i; + draw(ep,UPDATE); + return(-1); + + default: +#endif /* KSHELL */ + beep(); + return(-1); + } +} + + +/* + * This routine process all commands starting with ^X + */ + +static void xcommands(register Emacs_t *ep,int count) +{ + register int i = ed_getchar(ep->ed,0); + NOT_USED(count); + switch(i) + { + case cntl('X'): /* exchange dot and mark */ + if (ep->mark > eol) + ep->mark = eol; + i = ep->mark; + ep->mark = cur; + cur = i; + draw(ep,UPDATE); + return; + +#if KSHELL +# ifdef ESH_BETTER + case cntl('E'): /* invoke emacs on current command */ + if(ed_fulledit(ep->ed)==-1) + beep(); + else + { +#if SHOPT_MULTIBYTE + ed_internal((char*)drawbuff,drawbuff); +#endif /* SHOPT_MULTIBYTE */ + ed_ungetchar(ep->ed,'\n'); + } + return; + +# define itos(i) fmtbase((long)(i),0,0)/* want signed conversion */ + + case cntl('H'): /* ^X^H show history info */ + { + char hbuf[MAXLINE]; + + strcpy(hbuf, "Current command "); + strcat(hbuf, itos(hline)); + if (hloff) + { + strcat(hbuf, " (line "); + strcat(hbuf, itos(hloff+1)); + strcat(hbuf, ")"); + } + if ((hline != location.hist_command) || + (hloff != location.hist_line)) + { + strcat(hbuf, "; Previous command "); + strcat(hbuf, itos(location.hist_command)); + if (location.hist_line) + { + strcat(hbuf, " (line "); + strcat(hbuf, itos(location.hist_line+1)); + strcat(hbuf, ")"); + } + } + show_info(ep,hbuf); + return; + } +# if 0 /* debugging, modify as required */ + case cntl('D'): /* ^X^D show debugging info */ + { + char debugbuf[MAXLINE]; + + strcpy(debugbuf, "count="); + strcat(debugbuf, itos(count)); + strcat(debugbuf, " eol="); + strcat(debugbuf, itos(eol)); + strcat(debugbuf, " cur="); + strcat(debugbuf, itos(cur)); + strcat(debugbuf, " crallowed="); + strcat(debugbuf, itos(crallowed)); + strcat(debugbuf, " plen="); + strcat(debugbuf, itos(plen)); + strcat(debugbuf, " w_size="); + strcat(debugbuf, itos(w_size)); + + show_info(ep,debugbuf); + return; + } +# endif /* debugging code */ +# endif /* ESH_BETTER */ +#endif /* KSHELL */ + + default: + beep(); + return; + } +} + +static void search(Emacs_t* ep,genchar *out,int direction) +{ +#ifndef ESH_NFIRST + Histloc_t location; +#endif + register int i,sl; + genchar str_buff[LBUF]; + register genchar *string = drawbuff; + /* save current line */ + int sav_cur = cur; + genncpy(str_buff,string,sizeof(str_buff)/sizeof(*str_buff)); + string[0] = '^'; + string[1] = 'R'; + string[2] = '\0'; + sl = 2; + cur = sl; + draw(ep,UPDATE); + while ((i = ed_getchar(ep->ed,1))&&(i != '\r')&&(i != '\n')) + { + if (i==usrerase || i==DELETE || i=='\b' || i==ERASECHAR) + { + if (sl > 2) + { + string[--sl] = '\0'; + cur = sl; + draw(ep,UPDATE); + } + else + beep(); + continue; + } + if (i==usrkill) + { + beep(); + goto restore; + } + if (i == '\\') + { + string[sl++] = '\\'; + string[sl] = '\0'; + cur = sl; + draw(ep,APPEND); + i = ed_getchar(ep->ed,1); + string[--sl] = '\0'; + } + string[sl++] = i; + string[sl] = '\0'; + cur = sl; + draw(ep,APPEND); + } + i = genlen(string); + + if (direction < 1) + { + ep->prevdirection = -ep->prevdirection; + direction = 1; + } + else + direction = -1; + if (i != 2) + { +#if SHOPT_MULTIBYTE + ed_external(string,(char*)string); +#endif /* SHOPT_MULTIBYTE */ + strncpy(lstring,((char*)string)+2,SEARCHSIZE); + ep->prevdirection = direction; + } + else + direction = ep->prevdirection ; + location = hist_find(sh.hist_ptr,(char*)lstring,hline,1,direction); + i = location.hist_command; + if(i>0) + { + hline = i; +#ifdef ESH_NFIRST + hloff = location.hist_line = 0; /* display first line of multi line command */ +#else + hloff = location.hist_line; +#endif /* ESH_NFIRST */ + hist_copy((char*)out,MAXLINE, hline,hloff); +#if SHOPT_MULTIBYTE + ed_internal((char*)out,out); +#endif /* SHOPT_MULTIBYTE */ + return; + } + if (i < 0) + { + beep(); +#ifdef ESH_NFIRST + location.hist_command = hline; + location.hist_line = hloff; +#else + hloff = 0; + hline = histlines; +#endif /* ESH_NFIRST */ + } +restore: + genncpy(string,str_buff,sizeof(str_buff)/sizeof(*str_buff)); + cur = sav_cur; + return; +} + + +/* Adjust screen to agree with inputs: logical line and cursor */ +/* If 'first' assume screen is blank */ +/* Prompt is always kept on the screen */ + +static void draw(register Emacs_t *ep,Draw_t option) +{ +#define NORMAL ' ' +#define LOWER '<' +#define BOTH '*' +#define UPPER '>' + + register genchar *sptr; /* Pointer within screen */ + genchar nscreen[2*MAXLINE]; /* New entire screen */ + genchar *ncursor; /* New cursor */ + register genchar *nptr; /* Pointer to New screen */ + char longline; /* Line overflow */ + genchar *logcursor; + genchar *nscend; /* end of logical screen */ + register int i; + + nptr = nscreen; + sptr = drawbuff; + logcursor = sptr + cur; + longline = NORMAL; + + if (option == FIRST || option == REFRESH) + { + ep->overflow = NORMAL; + ep->cursor = ep->screen; + ep->offset = 0; + ep->cr_ok = crallowed; + if (option == FIRST) + { + ep->scvalid = 1; + return; + } + *ep->cursor = '\0'; + putstring(ep,Prompt); /* start with prompt */ + } + + /********************* + Do not update screen if pending characters + **********************/ + + if ((lookahead)&&(option != FINAL)) + { + + ep->scvalid = 0; /* Screen is out of date, APPEND will not work */ + + return; + } + + /*************************************** + If in append mode, cursor at end of line, screen up to date, + the previous character was a 'normal' character, + and the window has room for another character. + Then output the character and adjust the screen only. + *****************************************/ + + + i = *(logcursor-1); /* last character inserted */ + + if ((option == APPEND)&&(ep->scvalid)&&(*logcursor == '\0')&& + print(i)&&((ep->cursor-ep->screen)<(w_size-1))) + { + putchar(ep->ed,i); + *ep->cursor++ = i; + *ep->cursor = '\0'; + return; + } + + /* copy the line */ + ncursor = nptr + ed_virt_to_phys(ep->ed,sptr,nptr,cur,0,0); + nptr += genlen(nptr); + sptr += genlen(sptr); + nscend = nptr - 1; + if(sptr == logcursor) + ncursor = nptr; + + /********************* + Does ncursor appear on the screen? + If not, adjust the screen offset so it does. + **********************/ + + i = ncursor - nscreen; + + if ((ep->offset && i<=ep->offset)||(i >= (ep->offset+w_size))) + { + /* Center the cursor on the screen */ + ep->offset = i - (w_size>>1); + if (--ep->offset < 0) + ep->offset = 0; + } + + /********************* + Is the range of screen[0] thru screen[w_size] up-to-date + with nscreen[offset] thru nscreen[offset+w_size] ? + If not, update as need be. + ***********************/ + + nptr = &nscreen[ep->offset]; + sptr = ep->screen; + + i = w_size; + + while (i-- > 0) + { + + if (*nptr == '\0') + { + *(nptr + 1) = '\0'; + *nptr = ' '; + } + if (*sptr == '\0') + { + *(sptr + 1) = '\0'; + *sptr = ' '; + } + if (*nptr == *sptr) + { + nptr++; + sptr++; + continue; + } + setcursor(ep,sptr-ep->screen,*nptr); + *sptr++ = *nptr++; +#if SHOPT_MULTIBYTE + while(*nptr==MARKER) + { + if(*sptr=='\0') + *(sptr + 1) = '\0'; + *sptr++ = *nptr++; + i--; + ep->cursor++; + } +#endif /* SHOPT_MULTIBYTE */ + } + + /****************** + + Screen overflow checks + + ********************/ + + if (nscend >= &nscreen[ep->offset+w_size]) + { + if (ep->offset > 0) + longline = BOTH; + else + longline = UPPER; + } + else + { + if (ep->offset > 0) + longline = LOWER; + } + + /* Update screen overflow indicator if need be */ + + if (longline != ep->overflow) + { + setcursor(ep,w_size,longline); + ep->overflow = longline; + } + i = (ncursor-nscreen) - ep->offset; + setcursor(ep,i,0); + if(option==FINAL && ep->ed->e_multiline) + setcursor(ep,nscend-nscreen,0); + ep->scvalid = 1; + return; +} + +/* + * put the cursor to the position within screen buffer + * if is non-zero then output this character + * cursor is set to reflect the change + */ + +static void setcursor(register Emacs_t *ep,register int newp,int c) +{ + register int oldp = ep->cursor - ep->screen; + newp = ed_setcursor(ep->ed, ep->screen, oldp, newp, 0); + if(c) + { + putchar(ep->ed,c); + newp++; + } + ep->cursor = ep->screen+newp; + return; +} + +#if SHOPT_MULTIBYTE +static int print(register int c) +{ + return((c&~STRIP)==0 && isprint(c)); +} + +static int _isword(register int c) +{ + return((c&~STRIP) || isalnum(c) || c=='_'); +} +#endif /* SHOPT_MULTIBYTE */ Index: src/lib/libshell/common/edit/completion.c =================================================================== --- src/lib/libshell/common/edit/completion.c (revision 0) +++ src/lib/libshell/common/edit/completion.c (revision 740) @@ -0,0 +1,522 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * completion.c - command and file completion for shell editors + * + */ + +#include "defs.h" +#include +#include +#include "lexstates.h" +#include "path.h" +#include "io.h" +#include "edit.h" +#include "history.h" + +static int charcmp(int a, int b, int nocase) +{ + if(nocase) + { + if(isupper(a)) + a = tolower(a); + if(isupper(b)) + b = tolower(b); + } + return(a==b); +} + +/* + * overwrites to common prefix of and + * if is equal to returns +strlen()+1 + * otherwise returns +strlen() + */ +static char *overlaid(register char *str,register const char *newstr,int nocase) +{ + register int c,d; + while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase))) + str++; + if(*str) + *str = 0; + else if(*newstr==0) + str++; + return(str); +} + + +/* + * returns pointer to beginning of expansion and sets type of expansion + */ +static char *find_begin(char outbuff[], char *last, int endchar, int *type) +{ + register char *cp=outbuff, *bp, *xp; + register int c,inquote = 0; + bp = outbuff; + *type = 0; + while(cp < last) + { + xp = cp; + switch(c= mbchar(cp)) + { + case '\'': case '"': + if(!inquote) + { + inquote = c; + bp = xp; + break; + } + if(inquote==c) + inquote = 0; + break; + case '\\': + if(inquote != '\'') + mbchar(cp); + break; + case '$': + if(inquote == '\'') + break; + c = *(unsigned char*)cp; + if(isaletter(c) || c=='{') + { + int dot = '.'; + if(c=='{') + { + xp = cp; + mbchar(cp); + c = *(unsigned char*)cp; + if(c!='.' && !isaletter(c)) + break; + } + else + dot = 'a'; + while(cp < last) + { + if((c= mbchar(cp)) , c!=dot && !isaname(c)) + break; + } + if(cp>=last) + { + *type='$'; + return(++xp); + } + } + else if(c=='(') + { + xp = find_begin(cp,last,')',type); + if(*(cp=xp)!=')') + bp = xp; + else + cp++; + } + break; + case '=': + if(!inquote) + bp = cp; + break; + case '~': + if(*cp=='(') + break; + /* fall through */ + default: + if(c && c==endchar) + return(xp); + if(!inquote && ismeta(c)) + bp = cp; + break; + } + } + if(inquote && *bp==inquote) + *type = *bp++; + return(bp); +} + +/* + * file name generation for edit modes + * non-zero exit for error, <0 ring bell + * don't search back past beginning of the buffer + * mode is '*' for inline expansion, + * mode is '\' for filename completion + * mode is '=' cause files to be listed in select format + */ + +int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) +{ + struct comnod *comptr; + struct argnod *ap; + register char *out; + char *av[2], *begin , *dir=0; + int addstar=0, rval=0, var=0, strip=1; + int nomarkdirs = !sh_isoption(SH_MARKDIRS); + sh_onstate(SH_FCOMPLETE); + if(ep->e_nlist) + { + if(mode=='=' && count>0) + { + if(count> ep->e_nlist) + return(-1); + mode = '*'; + av[0] = ep->e_clist[count-1]; + av[1] = 0; + } + else + { + stakset(ep->e_stkptr,ep->e_stkoff); + ep->e_nlist = 0; + } + } + comptr = (struct comnod*)stakalloc(sizeof(struct comnod)); + ap = (struct argnod*)stakseek(ARGVAL); +#if SHOPT_MULTIBYTE + { + register int c = *cur; + register genchar *cp; + /* adjust cur */ + cp = (genchar *)outbuff + *cur; + c = *cp; + *cp = 0; + *cur = ed_external((genchar*)outbuff,(char*)stakptr(0)); + *cp = c; + *eol = ed_external((genchar*)outbuff,outbuff); + } +#endif /* SHOPT_MULTIBYTE */ + out = outbuff + *cur + (sh_isoption(SH_VI)!=0); + comptr->comtyp = COMSCAN; + comptr->comarg = ap; + ap->argflag = (ARG_MAC|ARG_EXP); + ap->argnxt.ap = 0; + ap->argchn.cp = 0; + { + register int c; + char *last = out; + c = *(unsigned char*)out; + begin = out = find_begin(outbuff,last,0,&var); + /* addstar set to zero if * should not be added */ + if(var=='$') + { + stakputs("${!"); + stakwrite(out,last-out); + stakputs("@}"); + out = last; + } + else + { + addstar = '*'; + while(out < last) + { + c = *(unsigned char*)out; + if(isexp(c)) + addstar = 0; + if (c == '/') + { + if(addstar == 0) + strip = 0; + dir = out+1; + } + stakputc(c); + out++; + } + } + if(var!='$' && mode=='\\' && out[-1]!='*') + addstar = '*'; + if(*begin=='~' && !strchr(begin,'/')) + addstar = 0; + stakputc(addstar); + ap = (struct argnod*)stakfreeze(1); + } + if(mode!='*') + sh_onoption(SH_MARKDIRS); + { + register char **com; + char *cp=begin, *left=0, *saveout="."; + int nocase=0,narg,cmd_completion=0; + register int size='x'; + while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t')) + cp--; + if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&sh.nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' ))) + { + cmd_completion=1; + sh_onstate(SH_COMPLETE); + } + if(ep->e_nlist) + { + narg = 1; + com = av; + if(dir) + begin += (dir-begin); + } + else + { + com = sh_argbuild(&narg,comptr,0); + /* special handling for leading quotes */ + if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\'')) + begin--; + } + sh_offstate(SH_COMPLETE); + /* allow a search to be aborted */ + if(sh.trapnote&SH_SIGSET) + { + rval = -1; + goto done; + } + /* match? */ + if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*'))) + { + rval = -1; + goto done; + } + if(mode=='=') + { + if (strip && !cmd_completion) + { + register char **ptrcom; + for(ptrcom=com;*ptrcom;ptrcom++) + /* trim directory prefix */ + *ptrcom = path_basename(*ptrcom); + } + sfputc(sfstderr,'\n'); + sh_menu(sfstderr,narg,com); + sfsync(sfstderr); + ep->e_nlist = narg; + ep->e_clist = com; + goto done; + } + /* see if there is enough room */ + size = *eol - (out-begin); + if(mode=='\\') + { + int c; + if(dir) + { + c = *dir; + *dir = 0; + saveout = begin; + } + if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0)) + nocase = (strchr(saveout,'c')!=0); + if(dir) + *dir = c; + /* just expand until name is unique */ + size += strlen(*com); + } + else + { + size += narg; + { + char **savcom = com; + while (*com) + size += strlen(cp=sh_fmtq(*com++)); + com = savcom; + } + } + /* see if room for expansion */ + if(outbuff+size >= &outbuff[MAXLINE]) + { + com[0] = ap->argval; + com[1] = 0; + } + /* save remainder of the buffer */ + if(*out) + left=stakcopy(out); + if(cmd_completion && mode=='\\') + out = strcopy(begin,path_basename(cp= *com++)); + else if(mode=='*') + { + if(ep->e_nlist && dir && var) + { + if(*cp==var) + cp++; + else + *begin++ = var; + out = strcopy(begin,cp); + var = 0; + } + else + out = strcopy(begin,sh_fmtq(*com)); + com++; + } + else + out = strcopy(begin,*com++); + if(mode=='\\') + { + saveout= ++out; + while (*com && *begin) + { + if(cmd_completion) + out = overlaid(begin,path_basename(*com++),nocase); + else + out = overlaid(begin,*com++,nocase); + } + mode = (out==saveout); + if(out[-1]==0) + out--; + if(mode && out[-1]!='/') + { + if(cmd_completion) + { + Namval_t *np; + /* add as tracked alias */ +#ifdef PATH_BFPATH + Pathcomp_t *pp; + if(*cp=='/' && (pp=path_dirfind(sh.pathlist,cp,'/')) && (np=nv_search(begin,sh.track_tree,NV_ADD))) + path_alias(np,pp); +#else + if(*cp=='/' && (np=nv_search(begin,sh.track_tree,NV_ADD))) + path_alias(np,cp); +#endif + out = strcopy(begin,cp); + } + /* add quotes if necessary */ + if((cp=sh_fmtq(begin))!=begin) + out = strcopy(begin,cp); + if(var=='$' && begin[-1]=='{') + *out = '}'; + else + *out = ' '; + *++out = 0; + } + else if(out[-1]=='/' && (cp=sh_fmtq(begin))!=begin) + { + out = strcopy(begin,cp); + if(out[-1] =='"' || out[-1]=='\'') + *--out = 0;; + } + if(*begin==0) + ed_ringbell(); + } + else + { + while (*com) + { + *out++ = ' '; + out = strcopy(out,sh_fmtq(*com++)); + } + } + if(ep->e_nlist) + { + cp = com[-1]; + if(cp[strlen(cp)-1]!='/') + { + if(var=='$' && begin[-1]=='{') + *out = '}'; + else + *out = ' '; + out++; + } + else if(out[-1] =='"' || out[-1]=='\'') + out--; + *out = 0; + } + *cur = (out-outbuff); + /* restore rest of buffer */ + if(left) + out = strcopy(out,left); + *eol = (out-outbuff); + } + done: + sh_offstate(SH_FCOMPLETE); + if(!ep->e_nlist) + stakset(ep->e_stkptr,ep->e_stkoff); + if(nomarkdirs) + sh_offoption(SH_MARKDIRS); +#if SHOPT_MULTIBYTE + { + register int c,n=0; + /* first re-adjust cur */ + c = outbuff[*cur]; + outbuff[*cur] = 0; + for(out=outbuff; *out;n++) + mbchar(out); + outbuff[*cur] = c; + *cur = n; + outbuff[*eol+1] = 0; + *eol = ed_internal(outbuff,(genchar*)outbuff); + } +#endif /* SHOPT_MULTIBYTE */ + return(rval); +} + +/* + * look for edit macro named _i + * if found, puts the macro definition into lookahead buffer and returns 1 + */ +int ed_macro(Edit_t *ep, register int i) +{ + register char *out; + Namval_t *np; + genchar buff[LOOKAHEAD+1]; + if(i != '@') + ep->e_macro[1] = i; + /* undocumented feature, macros of the form [c evoke alias __c */ + if(i=='_') + ep->e_macro[2] = ed_getchar(ep,1); + else + ep->e_macro[2] = 0; + if (isalnum(i)&&(np=nv_search(ep->e_macro,sh.alias_tree,HASH_SCOPE))&&(out=nv_getval(np))) + { +#if SHOPT_MULTIBYTE + /* copy to buff in internal representation */ + int c = 0; + if( strlen(out) > LOOKAHEAD ) + { + c = out[LOOKAHEAD]; + out[LOOKAHEAD] = 0; + } + i = ed_internal(out,buff); + if(c) + out[LOOKAHEAD] = c; +#else + strncpy((char*)buff,out,LOOKAHEAD); + buff[LOOKAHEAD] = 0; + i = strlen((char*)buff); +#endif /* SHOPT_MULTIBYTE */ + while(i-- > 0) + ed_ungetchar(ep,buff[i]); + return(1); + } + return(0); +} + +/* + * Enter the fc command on the current history line + */ +int ed_fulledit(Edit_t *ep) +{ + register char *cp; + if(!sh.hist_ptr) + return(-1); + /* use EDITOR on current command */ + if(ep->e_hline == ep->e_hismax) + { + if(ep->e_eol<0) + return(-1); +#if SHOPT_MULTIBYTE + ep->e_inbuf[ep->e_eol+1] = 0; + ed_external(ep->e_inbuf, (char *)ep->e_inbuf); +#endif /* SHOPT_MULTIBYTE */ + sfwrite(sh.hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1); + sh_onstate(SH_HISTORY); + hist_flush(sh.hist_ptr); + } + cp = strcopy((char*)ep->e_inbuf,e_runvi); + cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0)); + ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0); + return(0); +} Index: src/lib/libshell/common/fun/dirs =================================================================== --- src/lib/libshell/common/fun/dirs (revision 0) +++ src/lib/libshell/common/fun/dirs (revision 740) @@ -0,0 +1,108 @@ +# +# DIRECTORY MANIPULATION FUNCTIONS, REPLACES CD +# +# Uses global parameters _push_max _push_top _push_stack +integer _push_max=${CDSTACK-32} _push_top=${CDSTACK-32} +unalias cd +alias cd=_cd +# Display directory stack -- $HOME displayed as ~ +function dirs +{ + typeset dir="${PWD#$HOME/}" + case $dir in + $HOME) + dir=\~ + ;; + /*) ;; + *) dir=\~/$dir + esac + PS3= + select i in "$dir" "${_push_stack[@]}" + do : + done < /dev/null +} + +# Change directory and put directory on front of stack +function _cd +{ + typeset dir= + integer n=0 type=4 + case $1 in + -|-1|2) # \cd - + n=_push_top type=1 + ;; + -[1-9]*([0-9])) # \cd -n + n=_push_top+${1#-}-1 type=2 + ;; + 1) # keep present directory + print -r - "$PWD" + return + ;; + [1-9]*([0-9])) # \cd n + n=_push_top+${1}-2 type=2 + ;; + *) if ((_push_top <= 0)) + then type=3 n=_push_max + fi + esac + if ((type<3)) + then if ((n >= _push_max+1)) + then print -u2 cd: Directory stack not that deep. + return 1 + else dir=${_push_stack[n]} + fi + fi + case $dir in + \~*) dir=$HOME${dir#\~} + esac + \cd "${dir:-$@}" >| /dev/null || return 1 + dir=${OLDPWD#$HOME/} + case $TERM in + 630) + print "\033[?${#PWD};2v$PWD\c" + ;; + esac + case $dir in + $HOME) + dir=\~ + ;; + /*) ;; + *) dir=\~/$dir + esac + case $type in + 1) # swap first two elements + _push_stack[_push_top]=$dir + ;; + 2|3) # put $dir on top and shift down by one until top + integer i=_push_top + for dir in "$dir" "${_push_stack[@]}" + do ((i > n)) && break + _push_stack[i]=$dir + i=i+1 + done + ;; + 4) # push name + _push_stack[_push_top=_push_top-1]=$dir + ;; + esac + print -r - "$PWD" +} + +# Menu driven change directory command +function mcd +{ + typeset dir="${PWD#$HOME/}" + case $dir in + $HOME) + dir=\~ + ;; + /*) ;; + *) dir=\~/$dir + esac + PS3='Select by number or enter a name: ' + select dir in "$dir" "${_push_stack[@]}" + do if _cd $REPLY + then return + fi + done +} Index: src/lib/libshell/common/fun/title =================================================================== --- src/lib/libshell/common/fun/title (revision 0) +++ src/lib/libshell/common/fun/title (revision 740) @@ -0,0 +1,57 @@ +# add to (+), delete from (-), set [=], or print (.) window title +# arguments are eval'd before printing +# title text string exported in TITLE_TEXT + +function title # [ + | - | = | . ] title ... +{ + typeset x t="$TITLE_TEXT" + + case $1 in + +) shift + case $# in + 0) ;; + *) for x + do case " $t " in + *" $x "*) ;; + " ") t=$x ;; + *) t="$t $x" ;; + esac + done + case $t in + $TITLE_TEXT) return 1 ;; + esac + ;; + esac + ;; + -) shift + case $# in + 0) ;; + *) for x + do case " $t " in + *" $x "*) t="${t%?( )$x*}${t##*$x?( )}" ;; + esac + done + case $t in + $TITLE_TEXT) return 1 ;; + esac + ;; + esac + ;; + .) print -r -- "$TITLE_TEXT" + return 0 + ;; + =) shift + t="$*" + ;; + *) t="$*" + ;; + esac + export TITLE_TEXT="$t" + eval x=\"$t\" + case $TERM in + 630*) print -nr -- $'\E[?'"${#x}"$';0v'"${x}" ;; + uwin*|*vt100|xterm*) print -nr -- $'\E]0;'"${x}"$'\a' ;; + *) return 1 ;; + esac + return 0 +} Index: src/lib/libshell/common/fun/popd =================================================================== --- src/lib/libshell/common/fun/popd (revision 0) +++ src/lib/libshell/common/fun/popd (revision 740) @@ -0,0 +1,111 @@ +# +# DIRECTORY MANIPULATION FUNCTIONS PUSHD, POPD AND DIRS +# +# Uses global parameters _push_max _push_top _push_stack +integer _push_max=100 _push_top=100 +# Display directory stack -- $HOME displayed as ~ +function dirs +{ + typeset dir="${PWD#$HOME/}" + case $dir in + $HOME) + dir=\~ + ;; + /*) ;; + *) dir=\~/$dir + esac + print -r - "$dir ${_push_stack[@]}" +} + +# Change directory and put directory on front of stack +function pushd +{ + typeset dir= type=0 + integer i + case $1 in + "") # pushd + if ((_push_top >= _push_max)) + then print pushd: No other directory. + return 1 + fi + type=1 dir=${_push_stack[_push_top]} + ;; + +[1-9]|+[1-9][0-9]) # pushd +n + integer i=_push_top$1-1 + if ((i >= _push_max)) + then print pushd: Directory stack not that deep. + return 1 + fi + type=2 dir=${_push_stack[i]} + ;; + *) if ((_push_top <= 0)) + then print pushd: Directory stack overflow. + return 1 + fi + esac + case $dir in + \~*) dir=$HOME${dir#\~} + esac + cd "${dir:-$1}" > /dev/null || return 1 + dir=${OLDPWD#$HOME/} + case $dir in + $HOME) + dir=\~ + ;; + /*) ;; + *) dir=\~/$dir + esac + case $type in + 0) # pushd name + _push_stack[_push_top=_push_top-1]=$dir + ;; + 1) # pushd + _push_stack[_push_top]=$dir + ;; + 2) # push +n + type=${1#+} i=_push_top-1 + set -- "${_push_stack[@]}" "$dir" "${_push_stack[@]}" + shift $type + for dir + do (((i=i+1) < _push_max)) || break + _push_stack[i]=$dir + done + esac + dirs +} + +# Pops the top directory +function popd +{ + typeset dir + if ((_push_top >= _push_max)) + then print popd: Nothing to pop. + return 1 + fi + case $1 in + "") + dir=${_push_stack[_push_top]} + case $dir in + \~*) dir=$HOME${dir#\~} + esac + cd "$dir" || return 1 + ;; + +[1-9]|+[1-9][0-9]) + typeset savedir + integer i=_push_top$1-1 + if ((i >= _push_max)) + then print pushd: Directory stack not that deep. + return 1 + fi + while ((i > _push_top)) + do _push_stack[i]=${_push_stack[i-1]} + i=i-1 + done + ;; + *) print pushd: Bad directory. + return 1 + esac + unset '_push_stack[_push_top]' + _push_top=_push_top+1 + dirs +} Index: src/lib/libshell/common/fun/pushd =================================================================== --- src/lib/libshell/common/fun/pushd (revision 0) +++ src/lib/libshell/common/fun/pushd (revision 740) @@ -0,0 +1,111 @@ +# +# DIRECTORY MANIPULATION FUNCTIONS PUSHD, POPD AND DIRS +# +# Uses global parameters _push_max _push_top _push_stack +integer _push_max=100 _push_top=100 +# Display directory stack -- $HOME displayed as ~ +function dirs +{ + typeset dir="${PWD#$HOME/}" + case $dir in + $HOME) + dir=\~ + ;; + /*) ;; + *) dir=\~/$dir + esac + print -r - "$dir ${_push_stack[@]}" +} + +# Change directory and put directory on front of stack +function pushd +{ + typeset dir= type=0 + integer i + case $1 in + "") # pushd + if ((_push_top >= _push_max)) + then print pushd: No other directory. + return 1 + fi + type=1 dir=${_push_stack[_push_top]} + ;; + +[1-9]|+[1-9][0-9]) # pushd +n + integer i=_push_top$1-1 + if ((i >= _push_max)) + then print pushd: Directory stack not that deep. + return 1 + fi + type=2 dir=${_push_stack[i]} + ;; + *) if ((_push_top <= 0)) + then print pushd: Directory stack overflow. + return 1 + fi + esac + case $dir in + \~*) dir=$HOME${dir#\~} + esac + cd "${dir:-$1}" > /dev/null || return 1 + dir=${OLDPWD#$HOME/} + case $dir in + $HOME) + dir=\~ + ;; + /*) ;; + *) dir=\~/$dir + esac + case $type in + 0) # pushd name + _push_stack[_push_top=_push_top-1]=$dir + ;; + 1) # pushd + _push_stack[_push_top]=$dir + ;; + 2) # push +n + type=${1#+} i=_push_top-1 + set -- "${_push_stack[@]}" "$dir" "${_push_stack[@]}" + shift $type + for dir + do (((i=i+1) < _push_max)) || break + _push_stack[i]=$dir + done + esac + dirs +} + +# Pops the top directory +function popd +{ + typeset dir + if ((_push_top >= _push_max)) + then print popd: Nothing to pop. + return 1 + fi + case $1 in + "") + dir=${_push_stack[_push_top]} + case $dir in + \~*) dir=$HOME${dir#\~} + esac + cd "$dir" || return 1 + ;; + +[1-9]|+[1-9][0-9]) + typeset savedir + integer i=_push_top$1-1 + if ((i >= _push_max)) + then print pushd: Directory stack not that deep. + return 1 + fi + while ((i > _push_top)) + do _push_stack[i]=${_push_stack[i-1]} + i=i-1 + done + ;; + *) print pushd: Bad directory. + return 1 + esac + unset '_push_stack[_push_top]' + _push_top=_push_top+1 + dirs +} Index: src/lib/libshell/common/fun/rssread =================================================================== --- src/lib/libshell/common/fun/rssread (revision 0) +++ src/lib/libshell/common/fun/rssread (revision 740) @@ -0,0 +1,414 @@ +#!/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# rssread - a simple RSS2.0 reader with RSS to XHTML to +# plaintext conversion. +# + +# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg4/bin:/bin:/usr/bin + +function printmsg +{ + print -u 2 "$@" +} + +function debugmsg +{ +# printmsg "$@" +true +} + +function fatal_error +{ + print -u 2 "${progname}: $@" + exit 1 +} + +function cat_http +{ +( + protocol="${1%://*}" + path1="${1#*://}" # "http://foo.bat.net/x/y.html" ----> "foo.bat.net/x/y.html" + + host="${path1%%/*}" + path="${path1#*/}" + port="${host##*:}" + + # If URL did not contain a port number in the host part then look at the + # protocol to get the port number + if [ "${port}" = "${host}" ] ; then + case "${protocol}" in + "http") port=80 ;; + *) port="$(getent services "${protocol}" | sed 's/[^0-9]*//;s/\/.*//')" ;; + esac + else + host="${host%:*}" + fi + + printmsg "protocol=${protocol} port=${port} host=${host} path=${path}" + + # prechecks + [ "${protocol}" = "" ] && fatal_error "protocol not set." + [ "${port}" = "" ] && fatal_error "port not set." + [ "${host}" = "" ] && fatal_error "host not set." + [ "${path}" = "" ] && fatal_error "path not set." + + # open TCP channel + exec 3<>"/dev/tcp/${host}/${port}" + + # send HTTP request + request="GET /${path} HTTP/1.0\n" + request+="Host: ${host}\n" + request+="User-Agent: ksh93/rssread (2007-01-16; $(uname -s -r -p))\n" + print "${request}\n" >&3 + + # collect response and send it to stdout + cat <&3 +) +} + +function html_entity_to_ascii +{ + typeset -A entity_cache + + # Todo: Add more HTML/MathML entities here + entity_cache["nbsp"]=" " + entity_cache["lt"]="<" + entity_cache["gt"]=">" + entity_cache["amp"]="&" + entity_cache["quot"]="\"" + entity_cache["apos"]="'" + + buf="" + while read -r -N 1 c ; do + if [ "$c" != "&" ] ; then + printf "%s" "${c}" + continue + fi + + entity="" + while read -r -N 1 c ; do + case "$c" in + ";") + break + ;; + ~(Eilr)[a-z0-9#]) + entity+="$c" + continue + ;; + *) + debugmsg "error &${entity}${c}#" + + print -n "${entity}${c}" + entity="" + continue 2 + ;; + esac + done + + value="" + if [ "${entity_cache["${entity}"]}" != "" ] ; then + debugmsg "match #${entity}# = #${entity_cache["${entity}"]}#" + value="${entity_cache["${entity}"]}" + else + if [ "${entity:0:1}" = "#" ] ; then + # decimal literal + value="$(printf "\u[$(printf "%x" "${entity:1:8}")]")" + elif [[ "${entity:0:7}" = ~(Eilr)[0-9a-f]* ]] ; then + # hexadecimal literal + value="$(printf "\u[${entity:0:7}]")" + else + # unknown literal - pass-through + value="" + fi + + entity_cache["${entity}"]="${value}" + + debugmsg "lookup #${entity}# = #${entity_cache["${entity}"]}#" + fi + + printf "%s" "$value" + done +} + +# dumb xhtml handler - no CSS, tables, images, iframes or nested +# structures are supported (and we assume that the input is correct +# xhtml). The code was written in a trial&&error manner and should be +# rewritten to parse xhtml correctly. +function handle_html +{ + # we can't use global variables here when multiple callbacks use the same + # callback function - but we can use the callback associative array for + # variable storage instead + nameref callbacks=${1} + tag_type="$2" + tag_value="$3" + + case "${tag_type}" in + tag_begin) + case "${tag_value}" in + br*) printf "\n" ;; + hr*) printf "\n-------------------------------------\n" ;; + pre*) callbacks["html_pre"]=1 ;; + p*) printf "\n" ;; + esac + ;; + + tag_end) + case "${tag_value}" in + pre*) callbacks["html_pre"]=0 ;; + esac + ;; + + tag_text) + if [ ${callbacks["html_pre"]} -eq 1 ] ; then + printf "%s" "${tag_value}" + else + # compress spaces/newlines/tabs/etc. + printf "%s" "${tag_value/+([\n\r\t\v[:space:][:blank:]])/ }" + fi + ;; + + document_start) + callbacks["html_pre"]=0 + ;; + document_end) ;; + esac +} + +function handle_rss +{ + # we can't use global variables here when multiple callbacks use the same + # callback function - but we can use the callback associative array for + # variable storage instead + nameref callbacks=${1} + tag_type="$2" + tag_value="$3" + + case "${tag_type}" in + tag_begin) + case "${tag_value}" in + item*) + item["title"]="" + item["link"]="" + item["tag"]="" + item["description"]="" + ;; + esac + callbacks["textbuf"]="" + ;; + tag_end) + case "${tag_value}" in + item*) + # note that each RSS item needs to be converted seperately from RSS to HTML to plain text + # to make sure that the state of one RSS item doesn't affect others + ( + printf $"
#### RSS item: title: %s ####" "${item["title"]}" + printf $"
## author: %s" "${item["author"]}" + printf $"
## link: %s" "${item["link"]}" + printf $"
## date: %s" "${item["pubDate"]}" + printf $"
## begin description:" + printf $"
%s
" "${item["description"]}" + printf $"
## end description
" + print # extra newline to make sure the sed pipeline gets flushed + ) | + html_entity_to_ascii | # convert XML entities (e.g. decode RSS content to HTML code) + xml_tok "xhtmltok_cb" | # convert HTML to plain text + html_entity_to_ascii # convert HTML entities + ;; + title*) item["title"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + link*) item["link"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + dc:creator* | author*) item["author"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + dc:date* | pubDate*) item["pubDate"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + description*) item["description"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + esac + callbacks["textbuf"]="" + ;; + tag_text) + callbacks["textbuf"]+="${tag_value}" + ;; + document_start) ;; + document_end) ;; + esac +} + +function xml_tok +{ + typeset buf="" + typeset c="" + + nameref callbacks=${1} + + [ ! -z "${callbacks["document_start"]}" ] && ${callbacks["document_start"]} "${1}" "document_start" + + while read -N 1 c -d '\0'; do + isendtag=false + + if [ "$c" = "<" ] ; then + if [ "$buf" != "" ] ; then + [ ! -z "${callbacks["tag_text"]}" ] && ${callbacks["tag_text"]} "${1}" "tag_text" "$buf" + buf="" + fi + + read -N 1 c -d '\0' + if [ "$c" = "/" ] ; then + isendtag=true + else + buf="$c" + fi + read -d '>' c + buf+="$c" + + if ${isendtag} ; then + [ ! -z "${callbacks["tag_end"]}" ] && ${callbacks["tag_end"]} "${1}" "tag_end" "$buf" + else + [ ! -z "${callbacks["tag_begin"]}" ] && ${callbacks["tag_begin"]} "${1}" "tag_begin" "$buf" + + # handle tags like
(which are start- and end-tag in one piece) + if [[ "${buf}" = ~(Er).*/ ]] ; then + [ ! -z "${callbacks["tag_end"]}" ] && ${callbacks["tag_end"]} "${1}" "tag_end" "$buf" + fi + fi + buf="" + else + buf+="$c" + fi + done + + [ ! -z "${callbacks["document_end"]}" ] && ${callbacks["document_start"]} "${1}" "document_end" "exit_success" + + print # final newline to make filters like "sed" happy +} + +# return the value of LC_MESSAGES needed for subprocesses which +# want to run in a different locale/encoding +function get_lc_messages +{ + [ "${LC_ALL}" != "" ] && { print "${LC_ALL}" ; return 0 ; } + [ "${LC_MESSAGES}" != "" ] && { print "${LC_MESSAGES}" ; return 0 ; } + [ "${LANG}" != "" ] && { print "${LANG}" ; return 0 ; } + print "C" ; return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${USAGE}" OPT '-?' + exit 2 +} + +# make sure we use the ksh93 builtin versions +builtin cat +builtin printf + +typeset -A rsstok_cb # callbacks for xml_tok +rsstok_cb["tag_begin"]="handle_rss" +rsstok_cb["tag_end"]="handle_rss" +rsstok_cb["tag_text"]="handle_rss" +rsstok_cb["textbuf"]="" + +typeset -A xhtmltok_cb # callbacks for xml_tok +xhtmltok_cb["tag_begin"]="handle_html" +xhtmltok_cb["tag_end"]="handle_html" +xhtmltok_cb["tag_text"]="handle_html" +xhtmltok_cb["textbuf"]="" +xhtmltok_cb["html_pre"]=0 + +typeset -A item + +typeset -A bookmark_urls + +# "ramdom" urls for testing +bookmark_urls=( + ["google_blogs_ksh"]="http://blogsearch.google.com/blogsearch_feeds?hl=en&scoring=d&q=ksh&ie=utf-8&num=100&output=rss" + ["ksh93_integration"]="http://www.opensolaris.org/rss/os/project/ksh93-integration/announcements/rss2.xml" + ["blogs_sun_com"]="http://blogs.sun.com/main/feed/entries/rss" + ["jmcp"]="http://www.jmcp.homeunix.com/roller/rss/jmcp" + ["katakai"]="http://blogs.sun.com/katakai/feed/entries/rss" + ["planetsun"]="http://www.planetsun.org/rss20.xml" + ["planetsolaris"]="http://www.planetsolaris.org/rss20.xml" + ["planetopensolaris"]="http://planet.opensolaris.org/rss20.xml" +) + +progname="${0}" + +USAGE=$' +[-? +@(#)\$Id: rssread (Roland Mainz) 2007-06-05 \$ +] +[+NAME?rssread - fetch RSS messages and convert them to plain text] +[+DESCRIPTION?\brssread\b RSS to plain text converter + which fetches RSS streams via HTTP and converts them from RSS to HTML to plain UTF-8 text.] + +[ url ] + +[+SEE ALSO?\bksh93\b(1)] +' + +while getopts -a "${progname}" "${USAGE}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift ${OPTIND}-1 + +url="$1" + +if [ "$url" = "" ] ; then + fatal_error $"No url given." +fi + +if [ "${bookmark_urls[${url}]}" != "" ] ; then + printmsg $"Using bookmark ${url} = ${bookmark_urls[${url}]}" + url="${bookmark_urls[${url}]}" +fi + +( + # set unicode locale since RSS is encoded in UTF-8 + # (and make sure $LC_MESSAGES is set to the parent + # process's locale that all error messages are using + # the callers locale/encoding) + export \ + LC_MESSAGES="$(get_lc_messages)" \ + LC_MONETARY="en_US.UTF-8" \ + LC_NUMERIC="en_US.UTF-8" \ + LC_COLLATE="en_US.UTF-8" \ + LC_CTYPE="en_US.UTF-8" \ + LC_TIME="en_US.UTF-8" \ + LANG="en_US.UTF-8" + + cat_http "$url" | + xml_tok "rsstok_cb" +) # | iconv -f "UTF-8" - - + +#EOF. Index: src/lib/libshell/common/fun/termclock =================================================================== --- src/lib/libshell/common/fun/termclock (revision 0) +++ src/lib/libshell/common/fun/termclock (revision 740) @@ -0,0 +1,267 @@ +#!/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# termclock - a simple analog clock for terminals +# + +# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg4/bin:/bin:/usr/bin + +function fatal_error +{ + print -u 2 "${progname}: $@" + exit 1 +} + +# cache tput values (to avoid |fork()|'ing a "tput" child every second) +function tput_cup +{ + integer y="$1" x="$2" + nameref c=tput_cup_cache["${y}_${x}"] + + if [ "$c" == "" ] ; then + # fast path for known terminal types + if [[ ${TERM} = ~(Elr)(vt100|vt220|xterm|xterm-color|dtterm) ]] ; then + c="$(printf "\E[%d;%dH" $((y+1)) $((x+1)))" + else + c="$(tput cup $y $x)" + fi + fi + + print -n "$c" +} + +function draw_clock +{ + float angle a + float x y + + for(( angle=0.0 ; angle < 360. ; angle+=6 )) ; do + (( a=angle/360.*(2*M_PI) )) + + (( x=clock.len_x*cos(a) )) + (( y=clock.len_y*sin(a) )) + tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x )) + + # add "mark" every 30 degrees + if (( int(angle)%30 == 0 )) ; then + print -n "0" + else + print -n "x" + fi + done +} + +function draw_hand +{ + float angle="$1" a + typeset ch="$2" + float length="$3" + float x y + + (( a=angle/360.*(2*M_PI) )) + + for(( s=0.0 ; s < 10. ; s+=0.5 )) ; do + (( x=(clock.len_x*(s/10.)*(length/100.))*cos(a) )) + (( y=(clock.len_y*(s/10.)*(length/100.))*sin(a) )) + + tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x )) + print -n "${ch}" + done +} + +function draw_clock_hand +{ + nameref hand=$1 + + draw_hand $(( 360.*(hand.val/hand.scale)-90. )) "${hand.ch}" ${hand.length} +} + +function clear_clock_hand +{ + nameref hand=$1 + + draw_hand $(( 360.*(hand.val/hand.scale)-90. )) " " ${hand.length} +} + +function main_loop +{ + typeset c + + # note: we can't use subshells when writing to the double-buffer file because this + # will render the tput value cache useless + while true ; do + if ${init_screen} ; then + init_screen="false" + + # "resize" is needed because older versions of ksh93 may have + # trouble with getting the right terminal size at startup + [ -x "/usr/X11/bin/resize" ] && eval "$(/usr/X11/bin/resize -u)" || + [ -x "/usr/X11R6/bin/resize" ] && eval "$(/usr/X11R6/bin/resize -u)" || + [ -x "/usr/openwin/bin/resize" ] && eval "$(/usr/openwin/bin/resize -u)" || + fatal_error "resize not found." + + (( clock.middle_x=COLUMNS/2.-.5 )) + (( clock.middle_y=LINES/2.-.5 )) + (( clock.len_x=COLUMNS/2-2 )) + (( clock.len_y=LINES/2-2 )) + + { + clear + draw_clock + } >&6 + fi + + { + (( $(date +"hours.val=%H , minutes.val=%M , seconds.val=%S") )) + + # small trick to get a smooth "analog" flair + (( hours.val+=minutes.val/60. )) + (( minutes.val+=seconds.val/60. )) + + draw_clock_hand seconds + draw_clock_hand minutes + draw_clock_hand hours + + # move cursor to home position + tput_cup 0 0 + } >&6 + + 6<#((0)) + cat <&6 + + 6<&- ; rm -f "${scratchfile}" ; exec 6<>"${scratchfile}" + + c="" ; read -t ${update_interval} -n 1 c + if [ "$c" != "" ] ; then + case "$c" in + ~(Ei)q | $'\E') return 0 ;; + esac + fi + + { + clear_clock_hand hours + clear_clock_hand minutes + clear_clock_hand seconds + } >&6 + done +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${USAGE}" OPT '-?' + exit 2 +} + +# program start +progname="${0}" + +builtin date +builtin rm +builtin printf + +typeset -A tput_cup_cache + +float -r M_PI=3.14159265358979323846 + +clock=( + float middle_x + float middle_y + integer len_x + integer len_y +) + +typeset init_screen="true" + +# set clock properties +seconds=( float val + typeset ch + float scale + integer length ) +minutes=( float val + typeset ch + float scale + integer length ) +hours=( float val + typeset ch + float scale + integer length ) + +seconds.length=90 seconds.scale=60 seconds.ch="s" +minutes.length=75 minutes.scale=60 minutes.ch="m" +hours.length=50 hours.scale=12 hours.ch="h" + +float update_interval=0.9 + +USAGE=$' +[-? +@(#)\$Id: termclock (Roland Mainz) 2007-06-05 \$ +] +[+NAME?termclock - analog clock for terminals] +[+DESCRIPTION?\btermclock\b is an analog clock for terminals. + The termclock program displays the time in analog or digital + form. The time is continuously updated at a frequency which + may be specified by the user.] +[u:update?Update interval (defaults to 0.9 seconds).]:[interval] +[+SEE ALSO?\bksh93\b(1), \bxclock\b(1)] +' + +while getopts -a "${progname}" "${USAGE}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + u) update_interval=${OPTARG} ;; + *) usage ;; + esac +done +shift ${OPTIND}-1 + +# prechecks +which tput >/dev/null || fatal_error "tput not found." +which mktemp >/dev/null || fatal_error "mktemp not found." +(( update_interval < 0. || update_interval > 7200. )) && fatal_error "invalid update_interval value." + +# create temporary file for double-buffering and register an EXIT trap +# to remove this file when the shell interpreter exits +scratchfile="$(mktemp /tmp/termclock.pid$$.XXXXXX)" +if [ -z "${scratchfile}" ]; then exit 1; fi +trap 'rm -f "${scratchfile}"' EXIT +rm -f "${scratchfile}" ; exec 6<>"${scratchfile}" + +# regiter trap to handle window size changes +trap 'init_screen="true"' WINCH + +main_loop + +# exiting - put cursor below clock +tput_cup $((LINES-2)) 0 + +# EOF. Index: src/lib/libshell/common/fun/mandelbrotset1 =================================================================== --- src/lib/libshell/common/fun/mandelbrotset1 (revision 0) +++ src/lib/libshell/common/fun/mandelbrotset1 (revision 740) @@ -0,0 +1,234 @@ +#!/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# mandelbrotset1 - a simple mandelbrot set generation and +# parallel execution demo +# + +# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg4/bin:/bin:/usr/bin:/usr/X11/bin:/usr/X11R6/bin:/usr/openwin/bin + +function printmsg +{ + print -u 2 "$@" +} + +function fatal_error +{ + print -u 2 "${progname}: $@" + exit 1 +} + +function print_color +{ + print -n "${symbollist:${1}:1}" +} + +function mandelbrot +{ + float x=$1 + float y=$2 + float xx + float yy + float x1=$3 + float y1=$4 + integer iteration=$5 + integer max_iteration=$6 + float mag + + for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do + (( xx=x*x )) + (( yy=y*y )) + (( mag=xx+yy )) + + (( y=x*y*2+y1 )) + (( x=xx-yy+x1 )) + done + + print ${iteration} + + return 0 +} + +function loop_serial +{ + for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do + for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do + print_color $(mandelbrot ${x} ${y} ${x} ${y} 1 ${symbollistlen}) + done + + print + done +} + +function loop_parallel +{ + integer numjobs=0 + # the following calculation suffers from rounding errors + integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) )) + + printmsg "# lines_per_job=${lines_per_job}" + printmsg "# numcpus=${numcpus}" + + # "renice" worker jobs + set -o bgnice + + if [ "${TMPDIR}" = "" ] ; then + TMPDIR="/tmp" + fi + + # try to generate a job identifer prefix which is unique across multiple hosts + jobident="job_host_$(uname -n)pid_$$_ppid${PPID}" + + printmsg $"## prepare..." + for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do + rm -f "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" + + let numjobs++ + done + + printmsg $"## running ${numjobs} children..." + for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do + ( + for (( ; y < y_max && lines_per_job-- > 0 ; y+=stepwidth )) ; do + for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do + print_color $(mandelbrot ${x} ${y} ${x} ${y} 1 ${symbollistlen}) + done + + print + done >"${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" + ) & + done + + printmsg $"## waiting for ${numjobs} children..." + wait + + printmsg $"## output:" + for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do + print "$(cat "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput")" + rm "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" + done +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${USAGE}" OPT '-?' + exit 2 +} + +# main +builtin printf +builtin cat +builtin rm +builtin sleep +builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names + +float x_max +float x_min +float y_max +float y_min +float m_width +float m_height +float max_mag +float stepwidth +integer numcpus + +# make sure ${COLUMN} and ${LINES} are set +eval $(resize -u) + +symbollist=' .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#' +symbollistlen=$(( ${#symbollist} - 1)) +mode="parallel" +progname="${0}" +max_mag=400 +stepwidth=0.1 +numcpus=16 + +let m_width=COLUMNS-1 m_height=LINES-2 + +progname="${0}" + +USAGE=$' +[-? +@(#)\$Id: mandelbrotset1 (Roland Mainz) 2007-06-05 \$ +] +[+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93] +[+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator + which runs either in serial or parallel mode (using multiple worker jobs).] +[w:width?Width of fractal.]:[width] +[h:height?Height of fractal.]:[height] +[s:symbols?Symbols to build the fractal from.]:[symbolstring] +[m:mag?Magnification level.]:[magnificationlevel] +[p:stepwidth?Width per step.]:[widthperstep] +[S:serial?Run in serial mode.] +[P:parallel?Run in parallel mode.] +[M:mode?Execution mode.]:[mode] +[C:numcpus?Number of processors used for parallel execution.]:[numcpus] +[+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)] +' + +while getopts -a "${progname}" "${USAGE}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + w) m_width="${OPTARG}" ;; + h) m_height="${OPTARG}" ;; + s) symbollist="${OPTARG}" ;; + m) max_mag="${OPTARG}" ;; + p) stepwidth="${OPTARG}" ;; + S) mode="serial" ;; + P) mode="parallel" ;; + M) mode="${OPTARG}" ;; + C) numcpus="${OPTARG}" ;; + *) usage ;; + esac +done +shift ${OPTIND}-1 + +printmsg "# width=${m_width}" +printmsg "# height=${m_height}" +printmsg "# max_mag=${max_mag}" +printmsg "# stepwidth=${stepwidth}" +printmsg "# symbollist='${symbollist}'" +printmsg "# mode=${mode}" + +symbollistlen=$(( ${#symbollist} - 1)) + +let x_max=m_width*stepwidth/2. x_min=-x_max +let y_max=m_height*stepwidth/2. y_min=-y_max + +case "${mode}" in + parallel) loop_parallel ;; + serial) loop_serial ;; + *) fatal_error $"Unknown mode \"${mode}\"." +esac + +# EOF. Index: src/lib/libshell/common/fun/gnaw =================================================================== --- src/lib/libshell/common/fun/gnaw (revision 0) +++ src/lib/libshell/common/fun/gnaw (revision 740) @@ -0,0 +1,1029 @@ +#!/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# +# gnaw - a simple ksh93 technology demo +# +# Note that this script has been written with the main idea to show +# many of ksh93's new features (comparing to ksh88/bash) and not +# as an example of efficient&&clean script code. +# + +# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg4/bin:/bin:/usr/bin + +function print_setcursorpos +{ + print -n "${vtcode[cup_${1}_${2}]}" +} + +function beep +{ + ${quiet} || print -n "${vtcode["bel"]}" +} + +function fatal_error +{ + print -u 2 "${progname}: $@" + exit 1 +} + +function print_levelmap +{ + integer screen_y_offset=$1 + integer start_y_pos=$2 # start at this line in the map + integer max_numlines=$3 # maximum lines we're allowed to render + integer x + integer y + line="" + + print_setcursorpos 0 ${screen_y_offset} + + for (( y=start_y_pos; (y-start_y_pos) < max_numlines && y < levelmap["max_y"] ; y++ )) ; do + line="" + for (( x=0 ; x < levelmap["max_x"] ; x++ )) ; do + line+="${levelmap["${x}_${y}"]}" + done + + print "${line} " + done + + # print lines filled with spaces for each line not filled + # by the level map + line="${vtcode["spaceline"]:0:${levelmap["max_x"]}}" + for (( ; (y-start_y_pos) < max_numlines ; y++ )) ; do + print "${line} " + done +} + +function level_completed +{ + render_buffer="$( + print -n "${vtcode["clear"]}" + cat < %s <--\n" "${player["score"]}" + printf " LIVES: --> %s <--\n" "${player["lives"]}" + )" + print "${render_buffer}" + + # wait five seconds and swallow any user input + for (( i=0 ; i < 50 ; i++ )) ; do + read -t 0.1 -n 1 dummy + done + + print "Press any key to continue..." + # wait five secs or for a key + read -t 5 -n 1 dummy +} + +function game_over +{ + render_buffer="$( + print -n "${vtcode["clear"]}" + cat < %s <--\n" "${player["score"]}" + )" + print "${render_buffer}" + + # wait five seconds and swallow any user input + for (( i=0 ; i < 50 ; i++ )) ; do + read -t 0.1 -n 1 dummy + done + + print "Press any key to continue..." + # wait five secs or for a key + read -t 5 -n 1 dummy +} + +function run_logo +{ + render_buffer="$( + cat <======================================\ +> /-\ .--. | +> | OO| / _.-' .-. .-. .-. .-. | +> | | \ '-. '-' '-' '-' '-' | +> ^^^^^ '--' | +>======\ /================\ .-. | +> | | | '-' | + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +ENDOFTEXT + print " GNAW - the ksh93 maze game" + print "\n\tMenu:" + + print "\t - [L]evels:" + for (( i=0 ; i < numlevels ; i++ )) ; do + printf "\t %s %s \n" "$([ $i -eq $selected_level ] && print -n "*" || print -n " ")" "${levellist[i]##levelmap_}" + done + + print "\t - Rendering options:" + printf "\t [%s] Use [U]nicode\n" "$([ $game_use_unicode -eq 1 ] && print -n "x" || print -n "_")" + printf "\t [%s] Use [C]olors\n" "$([ $game_use_colors -eq 1 ] && print -n "x" || print -n "_")" + + print "\t - [S]tart - [Q]uit" + + # wait 30 secs (before we switch to "attract mode") + c="" ; read -t 30 -n 1 c + case "$c" in + 'l') selected_level=$(((selected_level+numlevels+1) % numlevels)) ;; + 'L') selected_level=$(((selected_level+numlevels-1) % numlevels)) ;; + ~(Ei)s) + [ ${game_use_colors} -eq 1 ] && print "${vtcode["bg_black"]}" + case "${game_use_colors}${game_use_unicode}" in + "00") main_loop "${levellist[selected_level]}" ;; + "01") main_loop "${levellist[selected_level]}" | map_filter 0 1 ;; + "10") main_loop "${levellist[selected_level]}" | map_filter 1 0 ;; + "11") main_loop "${levellist[selected_level]}" | map_filter 1 1 ;; + esac + print "${vtcode["vtreset"]}" + ;; + ~(Ei)q|$'\E') + # make sure we do not exit on a cursor key (e.g. [A,B,C,D) + read -t 0.01 -n 1 c + if [ "$c" = "[" ] ; then + # this was a cursor key sequence, just eat the 3rd charcater + read -t 0.01 -n 1 c + else + exit 0 + fi + ;; + ~(Ei)u) game_use_unicode=$(((game_use_unicode+2+1) % 2)) ;; + ~(Ei)c) game_use_colors=$(((game_use_colors+2+1) % 2)) ;; + "") break ;; # timeout, switch to attract mode + *) beep ;; + esac + done + + print -n "${vtcode["clear"]}" + attract_mode + done +} + +function levelmap_stripes +{ +cat <= screen_y_offset && m_pos_y < render_num_lines )) ; then + print_setcursorpos ${m_pos_x} ${m_pos_y} + print -n "x" + fi + done + + # status block + print_setcursorpos 0 $((render_num_lines+screen_y_offset)) + emptyline=" " + print -n " >> ${player["message"]} <<${emptyline:0:${#emptyline}-${#player["message"]}}" + )" + print "${render_buffer}" +# print "renderbuffersize=$(print "${render_buffer}" | wc -c) " +} + +function main_loop +{ + float sleep_per_cycle=0.2 + float seconds_before_read + integer num_cycles=0 + float rs + + print -n "${vtcode["clear"]}" + + read_levelmap "$1" + + # player init + player["pos_x"]=${levelmap["playerstartpos_x"]} + player["pos_y"]=${levelmap["playerstartpos_y"]} + player["score"]=0 # player score + player["lives"]=5 # number of lives + player["invulnerable"]=10 # cycles how long the player remains invulnerable + player["message"]="Go..." + + monsterlist="maw claw jitterbug tentacle grendel" + + for currmonster in ${monsterlist} ; do + monster[${currmonster}_"pos_x"]=${levelmap["monsterstartpos_x"]} + monster[${currmonster}_"pos_y"]=${levelmap["monsterstartpos_y"]} + monster[${currmonster}_"xstep"]=0 + monster[${currmonster}_"ystep"]=0 + monster[${currmonster}_"homing"]=0 + done + + # main game cycle loop + while true ; do + num_cycles+=1 + seconds_before_read=${SECONDS} + c="" ; read -t ${sleep_per_cycle} -n 1 c + + if [ "$c" != "" ] ; then + # special case handling for cursor keys which are usually composed + # of three characters (e.g. "[D"). If only is hit we + # quicky exit + if [ "$c" = $'\E' ] ; then + read -t 0.1 -n 1 c + if [ "$c" != "[" ] ; then + return 0 + fi + + # we assume the user is using the cursor keys, this |read| + # should fetch the 3rd byte of the three-character sequence + # for the cursor keys + read -t 0.1 -n 1 c + fi + + # if the user hit a key the "read" above was interrupted + # and didn't wait exactly |sleep_per_cycle| seconds. + # We wait here some moments (|rs|="remaining seconds") to + # avoid that the game gets "faster" when more user input + # is given. + rs=$((sleep_per_cycle-(SECONDS-seconds_before_read))) + (( rs > 0.001 )) && sleep ${rs} + + player["message"]="" + + case "$c" in + j|D|4) let player["pos_x"]-=1 ;; + k|C|6) let player["pos_x"]+=1 ;; + i|A|8) let player["pos_y"]-=1 ;; + m|B|2) let player["pos_y"]+=1 ;; + + q) return 0 ;; + esac + + if [ "${levelmap["${player["pos_x"]}_${player["pos_y"]}"]}" = "." ] ; then + levelmap["${player["pos_x"]}_${player["pos_y"]}"]=" " + let levelmap["numdots"]-=1 + + let player["score"]+=10 + player["message"]='GNAW!!' + + if [ ${levelmap["numdots"]} -le 0 ] ; then + level_completed + return 0 + fi + fi + fi + + # generic player status change + if [ ${player["invulnerable"]} -gt 0 ] ; then + let player["invulnerable"]-=1 + fi + if [ ${player["lives"]} -le 0 ] ; then + game_over + return 0 + fi + + # move monsters + for currmonster in ${monsterlist} ; do + # make monster as half as slow then the others when it is following the user + if [ ${monster[${currmonster}_"homing"]} -gt 0 ] ; then + [ $((num_cycles % 2)) -gt 0 ] && continue + fi + + if [ ${monster[${currmonster}_"pos_x"]} = ${player["pos_x"]} ] ; then + if [ $((monster[${currmonster}_"pos_y"]-player["pos_y"])) -gt 0 ] ; then + let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=-1 + else + let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=+1 + fi + monster[${currmonster}_"homing"]=1 + if [ ${player["invulnerable"]} -le 0 ] ; then + player["message"]="Attention: ${currmonster} is chasing you" + fi + elif [ ${monster[${currmonster}_"pos_y"]} = ${player["pos_y"]} ] ; then + if [ $((monster[${currmonster}_"pos_x"]-player["pos_x"])) -gt 0 ] ; then + let monster[${currmonster}_"xstep"]=-1 monster[${currmonster}_"ystep"]=-0 + else + let monster[${currmonster}_"xstep"]=+1 monster[${currmonster}_"ystep"]=+0 + fi + monster[${currmonster}_"homing"]=1 + if [ ${player["invulnerable"]} -le 0 ] ; then + player["message"]="Attention: ${currmonster} is chasing you" + fi + else + if [ ${monster[${currmonster}_"homing"]} -eq 0 ] ; then + case $((SECONDS % 6 + RANDOM % 4)) in + 0) let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=+0 ;; + 2) let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=+1 ;; + 3) let monster[${currmonster}_"xstep"]=+1 monster[${currmonster}_"ystep"]=+0 ;; + 5) let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=-1 ;; + 6) let monster[${currmonster}_"xstep"]=-1 monster[${currmonster}_"ystep"]=+0 ;; + esac + fi + fi + + let monster[${currmonster}_"pos_x"]=monster[${currmonster}_"pos_x"]+monster[${currmonster}_"xstep"] + let monster[${currmonster}_"pos_y"]=monster[${currmonster}_"pos_y"]+monster[${currmonster}_"ystep"] + + # check if a monster hit the player + if [ ${player["invulnerable"]} -le 0 ] ; then + if [ ${monster[${currmonster}_"pos_x"]} -eq ${player["pos_x"]} -a \ + ${monster[${currmonster}_"pos_y"]} -eq ${player["pos_y"]} ] ; then + # if player was hit by a monster take one life and + # make him invulnerable for 10 cycles to avoid that + # the next cycle steals more lives + player["message"]="Ouuuchhhh" + player["invulnerable"]=10 + let player["lives"]-=1 + + beep ; beep ; sleep 0.3 ; beep ; beep + fi + fi + done + + render_game + done +} + +# program start +function map_filter +{ +# Choose between the old "sed"-based codepath and the new ksh93-native one +# The old codepath no longer used except for the unicode mode because +# we do not have control over the point where "sed" flushes it's buffer +# which completely defeats the doube-buffering code. Unfortunately the new +# codepath has problems in UTF-8 mode (bug in ksh93 ?) which forces us to +# use the old codepath in this case. +if [ $2 -eq 1 ] ; then +( + filter1="" + filter2="" + + # should we add the color map ? + if [ $1 -eq 1 ] ; then + filter1="s/#/${vtcode["fg_blue"]}#/g;\ + s/x/${vtcode["fg_red"]}x/g;\ + s/@/${vtcode["fg_yellow"]}@/g;\ + s/ /${vtcode["fg_grey"]} /g;\ + s/\./${vtcode["fg_lightred"]}./g;" + fi + + # should we add the unicode map ? + if [ $2 -eq 1 ] ; then + filter2="s/@/$(printf '\u[24d2]')/g;s/x/$(printf '\u[2605]')/g;s/#/$(printf '\u[25a6]')/g" + fi + + sed -e "${filter1}" -e "${filter2}" +) +else +( + if [ $1 -eq 1 ] ; then + ch_player="${vtcode["fg_yellow"]}" + ch_monster="${vtcode["fg_red"]}" + ch_wall="${vtcode["fg_blue"]}" + else + ch_player="" + ch_monster="" + ch_wall="" + fi + + if [ $2 -eq 1 ] ; then + # unicode map + ch_player+="$(printf '\u[24d2]')" + ch_monster+="$(printf '\u[2605]')" + ch_wall+="$(printf '\u[25a6]')" + else + # ascii map + ch_player+="@" + ch_monster+="x" + ch_wall+="#" + fi + + IFS="|" # make sure we don't swallow spaces/tabs + while read var ; do + var="${var// /${vtcode["fg_grey"]} }" + var="${var//\./${vtcode["fg_lightred"]}.}" + var="${var//@/${ch_player}}" + var="${var//x/${ch_monster}}" + var="${var//#/${ch_wall}}" + + print "${var}" + done +) +fi +} + +function exit_trap +{ + # restore stty settings + stty ${SAVED_STTY} + + print "bye." +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${USAGE}" OPT '-?' + exit 2 +} + +# program start +progname="${0}" +quiet=false + +# make sure we use the ksh93 "cat" builtin which supports the "-u" option +builtin cat +builtin wc +builtin printf # we need this for positional parameters ('printf "%2\$s %1\$s" hello world' = "world hello") +builtin sleep + +# global variables +typeset -A levelmap +typeset -A player +typeset -A monster +# global rendering options +integer game_use_colors=0 +integer game_use_unicode=0 + +USAGE=$' +[-? +@(#)\$Id: gnaw (Roland Mainz) 2007-06-05 \$ +] +[+NAME?gnaw - maze game written in ksh93] +[+DESCRIPTION?\bgnaw\b is a maze game. + The player maneuvers a yellow '@' sign to navigate a maze while eating + small dots. A level is finished when all the dots are eaten. Five monsters + (maw, claw, jitterbug, tentacle and grendel) also wander the maze in an attempt + to catch the '@'. Each level begins with all ghosts in their home, and '@' near + the bottom of the maze. The monsters are released from the home one by one at the + start of each level and start their rentless hunt after the player.] +[q:quiet?Disable use of terminal bell.] +[+SEE ALSO?\bksh93\b(1)] +' + +while getopts -a "${progname}" "${USAGE}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + q) quiet=true ;; + *) usage ;; + esac +done +shift ${OPTIND}-1 + +# save stty values and register the exit trap which restores these values on exit +SAVED_STTY="$(stty -g)" +trap exit_trap EXIT + +print "Loading..." + +# set stty values, "-icanon min 1 time 0 -inpck" should improve input latency, +# "-echo" turns the terminal echo off +stty -icanon min 1 time 0 -inpck -echo + +# "resize" cannot fetch the terminal width/height for some terminals +case ${TERM} in + sun | sun-color) + export COLUMNS=80 LINES=25 + ;; + vt52) + export COLUMNS=80 LINES=24 + ;; + *) + # get width/height from current terminal + [ -x "/usr/X11/bin/resize" ] && eval "$(/usr/X11/bin/resize -u)" || + [ -x "/usr/X11R6/bin/resize" ] && eval "$(/usr/X11R6/bin/resize -u)" || + [ -x "/usr/openwin/bin/resize" ] && eval "$(/usr/openwin/bin/resize -u)" || + fatal_error "resize not found." + ;; +esac + +# prechecks +(( COLUMNS < 60 )) && fatal_error "Terminal width must be larger than 60 columns (currently ${COLUMNS})." + +typeset -A vtcode +# color values taken from http://frexx.de/xterm-256-notes/, other +# codes from http://vt100.net/docs/vt100-tm/ +vtcode=( + ["bg_black"]="$(print -n "\E[40m")" + ["fg_black"]="$(print -n "\E[30m")" + ["fg_red"]="$(print -n "\E[31m")" + ["fg_lightred"]="$(print -n "\E[1;31m")" + ["fg_green"]="$(print -n "\E[32m")" + ["fg_lightgreen"]="$(print -n "\E[1;32m")" + ["fg_yellow"]="$(print -n "\E[33m")" + ["fg_lightyellow"]="$(print -n "\E[1;33m")" + ["fg_blue"]="$(print -n "\E[34m")" + ["fg_lightblue"]="$(print -n "\E[1;34m")" + ["fg_grey"]="$(print -n "\E[1;37m")" + ["fg_white"]="$(print -n "\E[37m")" + + # misc other vt stuff + ["vtreset"]="$(tput reset)" + ["clear"]="$(tput clear)" + ["bel"]="$(tput bel)" + ["spaceline"]="$(for (( i=0 ; i < COLUMNS ; i++ )) ; do print -n " " ; done)" +) + +# get terminal sequence to move cursor to position x,y +# (see http://vt100.net/docs/vt100-ug/chapter3.html#CPR) +case ${TERM} in + xterm | xterm-color | vt100 | vt220 | dtterm | sun | sun-color) + cup="$(infocmp -1 | \ + egrep '^[[:space:]]*cup=' | \ + sed -e 's/.*cup=//' \ + -e 's/%[%id]*p1[%id]*/%2\\\$d/g' \ + -e 's/%[%id]*p2[%id]*/%1\\\$d/g' \ + -e 's/,$//')" + for (( x=0 ; x < COLUMNS ; x++ )) ; do + for (( y=0 ; y < LINES ; y++ )) ; do + vtcode[cup_${x}_${y}]="$(printf "${cup}" $((x + 1)) $((y + 1)) )" + done + done + ;; + *) + printf "# Unrecognised terminal type '%s', fetching %dx%d items from terminfo database, please wait...\n" "${TERM}" "${COLUMNS}" "${LINES}" + for (( x=0 ; x < COLUMNS ; x++ )) ; do + for (( y=0 ; y < LINES ; y++ )) ; do + vtcode[cup_${x}_${y}]="$(tput cup ${y} ${x})" + done + done + ;; +esac + +print "${vtcode["vtreset"]}" + +run_logo +run_menu + +# EOF. + Index: src/lib/libshell/common/bltins/cd_pwd.c =================================================================== --- src/lib/libshell/common/bltins/cd_pwd.c (revision 0) +++ src/lib/libshell/common/bltins/cd_pwd.c (revision 740) @@ -0,0 +1,280 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * cd [-LP] [dirname] + * cd [-LP] [old] [new] + * pwd [-LP] + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include "defs.h" +#include +#include +#include "variables.h" +#include "path.h" +#include "name.h" +#include "builtins.h" +#include +#include + +#ifdef PATH_BFPATH +/* + * Invalidate path name bindings to relative paths + */ +static void rehash(register Namval_t *np,void *data) +{ + Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp; + NOT_USED(data); + if(pp && *pp->name!='/') + nv_unset(np); +} +#endif + +int b_cd(int argc, char *argv[],void *extra) +{ +#ifdef PATH_BFPATH + register char *dir; + Pathcomp_t *cdpath = 0; +#else + register char *dir, *cdpath=""; +#endif + register const char *dp; + register Shell_t *shp = (Shell_t*)extra; + int saverrno=0; + int rval,flag=0; + char *oldpwd; + Namval_t *opwdnod, *pwdnod; + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted+4); + while((rval = optget(argv,sh_optcd))) switch(rval) + { + case 'L': + flag = 0; + break; + case 'P': + flag = 1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + argc -= opt_info.index; + dir = argv[0]; + if(error_info.errors>0 || argc >2) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + oldpwd = (char*)shp->pwd; + opwdnod = (shp->subshell?sh_assignok(OLDPWDNOD,1):OLDPWDNOD); + pwdnod = (shp->subshell?sh_assignok(PWDNOD,1):PWDNOD); + if(argc==2) + dir = sh_substitute(oldpwd,dir,argv[1]); + else if(!dir || *dir==0) + dir = nv_getval(HOME); + else if(*dir == '-' && dir[1]==0) + dir = nv_getval(opwdnod); + if(!dir || *dir==0) + errormsg(SH_DICT,ERROR_exit(1),argc==2?e_subst+4:e_direct); +#if _WINIX + if(*dir != '/' && (dir[1]!=':')) +#else + if(*dir != '/') +#endif /* _WINIX */ + { +#ifdef PATH_BFPATH + if(!(cdpath = (Pathcomp_t*)shp->cdpathlist) && (dp=(CDPNOD)->nvalue.cp)) + { + if(cdpath=path_addpath((Pathcomp_t*)0,dp,PATH_CDPATH)) + { + shp->cdpathlist = (void*)cdpath; + cdpath->shp = shp; + } + } +#else + cdpath = nv_getval(nv_scoped(CDPNOD)); +#endif + if(!oldpwd) + oldpwd = path_pwd(1); + } +#ifndef PATH_BFPATH + if(!cdpath) + cdpath = ""; +#endif + if(*dir=='.') + { + /* test for pathname . ./ .. or ../ */ + if(*(dp=dir+1) == '.') + dp++; + if(*dp==0 || *dp=='/') +#ifdef PATH_BFPATH + cdpath = 0; +#else + cdpath = ""; +#endif + } + rval = -1; + do + { +#ifdef PATH_BFPATH + dp = cdpath?cdpath->name:""; + cdpath = path_nextcomp(cdpath,dir,0); +#else + dp = cdpath; + cdpath=path_join(cdpath,dir); +#endif +#if _WINIX + if(*stakptr(PATH_OFFSET+1)==':' && isalpha(*stakptr(PATH_OFFSET))) + { + *stakptr(PATH_OFFSET+1) = *stakptr(PATH_OFFSET); + *stakptr(PATH_OFFSET)='/'; + } +#endif /* _WINIX */ + if(*stakptr(PATH_OFFSET)!='/') + + { + char *last=(char*)stakfreeze(1); + stakseek(PATH_OFFSET); + stakputs(oldpwd); + /* don't add '/' of oldpwd is / itself */ + if(*oldpwd!='/' || oldpwd[1]) + stakputc('/'); + stakputs(last+PATH_OFFSET); + stakputc(0); + } + if(!flag) + { + register char *cp; + stakseek(PATH_MAX+PATH_OFFSET); +#if SHOPT_FS_3D + if(!(cp = pathcanon(stakptr(PATH_OFFSET),PATH_DOTDOT))) + continue; + /* eliminate trailing '/' */ + while(*--cp == '/' && cp>stakptr(PATH_OFFSET)) + *cp = 0; +#else + if(*(cp=stakptr(PATH_OFFSET))=='/') + if(!pathcanon(cp,PATH_DOTDOT)) + continue; +#endif /* SHOPT_FS_3D */ + } + if((rval=chdir(path_relative(stakptr(PATH_OFFSET)))) >= 0) + goto success; + if(errno!=ENOENT && saverrno==0) + saverrno=errno; + } + while(cdpath); + if(rval<0 && *dir=='/' && *(path_relative(stakptr(PATH_OFFSET)))!='/') + rval = chdir(dir); + /* use absolute chdir() if relative chdir() fails */ + if(rval<0) + { + if(saverrno) + errno = saverrno; + errormsg(SH_DICT,ERROR_system(1),"%s:",dir); + } +success: + if(dir == nv_getval(opwdnod) || argc==2) + dp = dir; /* print out directory for cd - */ + if(flag) + { + dir = stakptr(PATH_OFFSET); + if (!(dir=pathcanon(dir,PATH_PHYSICAL))) + { + dir = stakptr(PATH_OFFSET); + errormsg(SH_DICT,ERROR_system(1),"%s:",dir); + } + stakseek(dir-stakptr(0)); + } + dir = (char*)stakfreeze(1)+PATH_OFFSET; +#ifdef PATH_BFPATH + if(*dp && (*dp!='.'||dp[1]) && strchr(dir,'/')) +#else + if(*dp && *dp!= ':' && strchr(dir,'/')) +#endif + sfputr(sfstdout,dir,'\n'); + if(*dir != '/') + return(0); + nv_putval(opwdnod,oldpwd,NV_RDONLY); + if(oldpwd) + free(oldpwd); + flag = strlen(dir); + /* delete trailing '/' */ + while(--flag>0 && dir[flag]=='/') + dir[flag] = 0; + nv_putval(pwdnod,dir,NV_RDONLY); + nv_onattr(pwdnod,NV_NOFREE|NV_EXPORT); + shp->pwd = pwdnod->nvalue.cp; +#ifdef PATH_BFPATH + nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED); + path_newdir(shp->pathlist); + path_newdir(shp->cdpathlist); +#endif + return(0); +} + +int b_pwd(int argc, char *argv[],void *extra) +{ + register int n, flag = 0; + register char *cp; + register Shell_t *shp = (Shell_t*)extra; + NOT_USED(argc); + while((n = optget(argv,sh_optpwd))) switch(n) + { + case 'L': + flag = 0; + break; + case 'P': + flag = 1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(*(cp = path_pwd(0)) != '/') + errormsg(SH_DICT,ERROR_system(1), e_pwd); + if(flag) + { +#if SHOPT_FS_3D + if(shp->lim.fs3d && (flag = mount(e_dot,NIL(char*),FS3D_GET|FS3D_VIEW,0))>=0) + { + cp = (char*)stakseek(++flag+PATH_MAX); + mount(e_dot,cp,FS3D_GET|FS3D_VIEW|FS3D_SIZE(flag),0); + } + else +#endif /* SHOPT_FS_3D */ + cp = strcpy(stakseek(strlen(cp)+PATH_MAX),cp); + pathcanon(cp,PATH_PHYSICAL); + } + sfputr(sfstdout,cp,'\n'); + return(0); +} + Index: src/lib/libshell/common/bltins/ulimit.c =================================================================== --- src/lib/libshell/common/bltins/ulimit.c (revision 0) +++ src/lib/libshell/common/bltins/ulimit.c (revision 740) @@ -0,0 +1,211 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * ulimit [-HSacdfmnstuv] [limit] + * + * David Korn + * AT&T Labs + * + */ + +#include +#include +#include +#include +#include "builtins.h" +#include "name.h" +#include "ulimit.h" +#ifndef SH_DICT +# define SH_DICT "libshell" +#endif + +#ifdef _no_ulimit + int b_ulimit(int argc,char *argv[],void *extra) + { + NOT_USED(argc); + NOT_USED(argv); + NOT_USED(extra); + errormsg(SH_DICT,ERROR_exit(2),e_nosupport); + return(0); + } +#else + +static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) +{ + register const Limit_t* tp; + + for (tp = shtab_limits; tp->option; tp++) + { + sfprintf(sp, "[%c=%d:%s?The %s", tp->option, tp - shtab_limits + 1, tp->name, tp->description); + if(tp->type != LIM_COUNT) + sfprintf(sp, " in %ss", e_units[tp->type]); + sfprintf(sp, ".]"); + } + return(1); +} + +#define HARD 2 +#define SOFT 4 + +int b_ulimit(int argc,char *argv[],void *extra) +{ + register char *limit; + register int mode=0, n; + register unsigned long hit = 0; + Shell_t *shp = (Shell_t*)extra; +#ifdef _lib_getrlimit + struct rlimit rlp; +#endif /* _lib_getrlimit */ + const Limit_t* tp; + char* conf; + int label, unit, noargs, nosupport; + rlim_t i; + char tmp[32]; + Optdisc_t disc; + memset(&disc, 0, sizeof(disc)); + disc.version = OPT_VERSION; + disc.infof = infof; + opt_info.disc = &disc; + while((n = optget(argv,sh_optulimit))) switch(n) + { + case 'H': + mode |= HARD; + continue; + case 'S': + mode |= SOFT; + continue; + case 'a': + hit = ~0; + break; + default: + if(n < 0) + hit |= (1L<<(-(n+1))); + else + errormsg(SH_DICT,2, e_notimp, opt_info.name); + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + opt_info.disc = 0; + limit = argv[opt_info.index]; + /* default to -f */ + if(noargs=(hit==0)) + for(n=0; shtab_limits[n].option; n++) + if(shtab_limits[n].index == RLIMIT_FSIZE) + { + hit |= (1L<opt_info.index+1) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + if(mode==0) + mode = (HARD|SOFT); + for(tp = shtab_limits; tp->option && hit; tp++,hit>>=1) + { + if(!(hit&1)) + continue; + nosupport = (n = tp->index) == RLIMIT_UNKNOWN; + unit = shtab_units[tp->type]; + if(limit) + { + if(shp->subshell) + sh_subfork(); + if(strcmp(limit,e_unlimited)==0) + i = INFINITY; + else + { + char *last; + if((i=sh_strnum(limit,&last,2))==INFINITY || *last) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); + i *= unit; + } + if(nosupport) + errormsg(SH_DICT,ERROR_system(1),e_readonly,tp->name); + else + { +#ifdef _lib_getrlimit + if(getrlimit(n,&rlp) <0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); + if(mode&HARD) + rlp.rlim_max = i; + if(mode&SOFT) + rlp.rlim_cur = i; + if(setrlimit(n,&rlp) <0) + errormsg(SH_DICT,ERROR_system(1),e_overlimit,limit); +#else + if((i=vlimit(n,i)) < 0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); +#endif /* _lib_getrlimit */ + } + } + else + { + if(!nosupport) + { +#ifdef _lib_getrlimit + if(getrlimit(n,&rlp) <0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); + if(mode&HARD) + i = rlp.rlim_max; + if(mode&SOFT) + i = rlp.rlim_cur; +#else +# ifdef _lib_ulimit + n--; +# endif /* _lib_ulimit */ + i = -1; + if((i=vlimit(n,i)) < 0) + errormsg(SH_DICT,ERROR_system(1),e_number,limit); +#endif /* _lib_getrlimit */ + } + if(label) + { + if(tp->type != LIM_COUNT) + sfsprintf(tmp,sizeof(tmp),"%s (%ss)", tp->description, e_units[tp->type]); + else + sfsprintf(tmp,sizeof(tmp),"%s", tp->name); + sfprintf(sfstdout,"%-30s (-%c) ",tmp,tp->option); + } + if(nosupport) + { + if(!tp->conf || !*(conf = astconf(tp->conf, NiL, NiL))) + conf = (char*)e_nosupport; + sfputr(sfstdout,conf,'\n'); + } + else if(i!=INFINITY || noargs) + { + if(!noargs) + i += (unit-1); + sfprintf(sfstdout,"%I*d\n",sizeof(i),i/unit); + } + else + sfputr(sfstdout,e_unlimited,'\n'); + } + } + return(0); +} +#endif /* _no_ulimit */ Index: src/lib/libshell/common/bltins/trap.c =================================================================== --- src/lib/libshell/common/bltins/trap.c (revision 0) +++ src/lib/libshell/common/bltins/trap.c (revision 740) @@ -0,0 +1,347 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * trap [-p] action sig... + * kill [-l] [sig...] + * kill [-s sig] pid... + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include "defs.h" +#include +#include "jobs.h" +#include "builtins.h" + +#define L_FLAG 1 +#define S_FLAG 2 + +static const char trapfmt[] = "trap -- %s %s\n"; + +static int sig_number(const char*); +static void sig_list(Shell_t*,int); + +int b_trap(int argc,char *argv[],void *extra) +{ + register char *arg = argv[1]; + register int sig, pflag = 0; + register Shell_t *shp = (Shell_t*)extra; + NOT_USED(argc); + while (sig = optget(argv, sh_opttrap)) switch (sig) + { + case 'p': + pflag=1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); + if(arg = *argv) + { + register int clear; + char *action = arg; + if(!pflag) + { + /* first argument all digits or - means clear */ + while(isdigit(*arg)) + arg++; + clear = (arg!=action && *arg==0); + if(!clear) + { + ++argv; + if(*action=='-' && action[1]==0) + clear++; + } + while(!argv[0]) + errormsg(SH_DICT,ERROR_exit(1),e_condition); + } + while(arg = *argv++) + { + sig = sig_number(arg); + if(sig<0) + { + errormsg(SH_DICT,2,e_trap,arg); + return(1); + } + /* internal traps */ + if(sig&SH_TRAP) + { + sig &= ~SH_TRAP; + if(sig>SH_DEBUGTRAP) + { + errormsg(SH_DICT,2,e_trap,arg); + return(1); + } + if(pflag) + { + if(arg=shp->st.trap[sig]) + sfputr(sfstdout,sh_fmtq(arg),'\n'); + continue; + } + if(shp->st.trap[sig]) + free(shp->st.trap[sig]); + shp->st.trap[sig] = 0; + if(!clear && *action) + shp->st.trap[sig] = strdup(action); + if(sig == SH_DEBUGTRAP) + { + if(shp->st.trap[sig]) + shp->trapnote |= SH_SIGTRAP; + else + shp->trapnote = 0; + } + continue; + } + if(sig>shp->sigmax) + { + errormsg(SH_DICT,2,e_trap,arg); + return(1); + } + else if(pflag) + { + char **trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom); + if(arg=trapcom[sig]) + sfputr(sfstdout,arg,'\n'); + } + else if(clear) + sh_sigclear(sig); + else + { + if(sig >= shp->st.trapmax) + shp->st.trapmax = sig+1; + if(arg=shp->st.trapcom[sig]) + free(arg); + shp->st.trapcom[sig] = strdup(action); + sh_sigtrap(sig); + } + } + } + else /* print out current traps */ + sig_list(shp,-1); + return(0); +} + +int b_kill(int argc,char *argv[],void *extra) +{ + register char *signame; + register int sig=SIGTERM, flag=0, n; + register Shell_t *shp = (Shell_t*)extra; + NOT_USED(argc); + while((n = optget(argv,sh_optkill))) switch(n) + { + case ':': + if((signame=argv[opt_info.index++]) && (sig=sig_number(signame+1))>=0) + goto endopts; + opt_info.index--; + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case 'n': + sig = (int)opt_info.num; + goto endopts; + case 's': + flag |= S_FLAG; + signame = opt_info.arg; + goto endopts; + case 'l': + flag |= L_FLAG; + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } +endopts: + argv += opt_info.index; + if(*argv && strcmp(*argv,"--")==0 && strcmp(*(argv-1),"--")!=0) + argv++; + if(error_info.errors || flag==(L_FLAG|S_FLAG) || (!(*argv) && !(flag&L_FLAG))) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); + /* just in case we send a kill -9 $$ */ + sfsync(sfstderr); + if(flag&L_FLAG) + { + if(!(*argv)) + sig_list(shp,0); + else while(signame = *argv++) + { + if(isdigit(*signame)) + sig_list(shp,((int)strtol(signame, (char**)0, 10)&0177)+1); + else + { + if((sig=sig_number(signame))<0) + { + shp->exitval = 2; + errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame); + } + sfprintf(sfstdout,"%d\n",sig); + } + } + return(shp->exitval); + } + if(flag&S_FLAG) + { + if((sig=sig_number(signame)) < 0 || sig > shp->sigmax) + errormsg(SH_DICT,ERROR_exit(1),e_nosignal,signame); + } + if(job_walk(sfstdout,job_kill,sig,argv)) + shp->exitval = 1; + return(shp->exitval); +} + +/* + * Given the name or number of a signal return the signal number + */ + +static int sig_number(const char *string) +{ + const Shtable_t *tp; + register int n,sig=0; + char *last; + if(isdigit(*string)) + { + n = strtol(string,&last,10); + if(*last) + n = -1; + } + else + { + register int c; + n = staktell(); + do + { + c = *string++; + if(islower(c)) + c = toupper(c); + stakputc(c); + } + while(c); + stakseek(n); + if(memcmp(stakptr(n),"SIG",3)==0) + { + sig = 1; + n += 3; + } + tp = sh_locate(stakptr(n),(const Shtable_t*)shtab_signals,sizeof(*shtab_signals)); + n = tp->sh_number; + if(sig==1 && (n>=(SH_TRAP-1) && n < (1<sh_name)==0) + n = tp->sh_number; + } + n &= (1< is positive, then print signal name corresponding to + * if is zero, then print all signal names + * if is negative, then print all traps + */ +static void sig_list(register Shell_t *shp,register int flag) +{ + register const struct shtable2 *tp; + register int sig = shp->sigmax+1; + const char *names[SH_TRAP]; + const char *traps[SH_DEBUGTRAP+1]; + tp=shtab_signals; + if(flag==0) + { + /* not all signals may be defined, so initialize */ + while(--sig >= 0) + names[sig] = 0; + for(sig=SH_DEBUGTRAP; sig>=0; sig--) + traps[sig] = 0; + } + while(*tp->sh_name) + { + sig = tp->sh_number; + sig &= ((1<sh_name); + return; + } + else if(sig&SH_TRAP) + traps[sig&~SH_TRAP] = (char*)tp->sh_name; + else if(sig < sizeof(names)/sizeof(char*)) + names[sig] = (char*)tp->sh_name; + tp++; + } + if(flag > 0) + sfprintf(sfstdout,"%d\n",flag-1); + else if(flag<0) + { + /* print the traps */ + register char *trap,*sname,**trapcom; + char name[6]; + sig = shp->st.trapmax; + /* use parent traps if otrapcom is set (for $(trap) */ + trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom); + while(--sig >= 0) + { + if(!(trap=trapcom[sig])) + continue; + if(!(sname=(char*)names[sig+1])) + { + sname = name; + sname[0] = 'S'; + sname[1] = 'I'; + sname[2] = 'G'; + sname[3] = (sig/10)+'0'; + sname[4] = (sig%10)+'0'; + } + sfprintf(sfstdout,trapfmt,sh_fmtq(trap),sname); + } + for(sig=SH_DEBUGTRAP; sig>=0; sig--) + { + if(!(trap=shp->st.trap[sig])) + continue; + sfprintf(sfstdout,trapfmt,sh_fmtq(trap),traps[sig]); + } + } + else + { + /* print all the signal names */ + for(sig=2; sig <= shp->sigmax; sig++) + { + if(names[sig]) + sfputr(sfstdout,names[sig],'\n'); + else + sfprintf(sfstdout,"SIG%d\n",sig-1); + } + } +} + Index: src/lib/libshell/common/bltins/hist.c =================================================================== --- src/lib/libshell/common/bltins/hist.c (revision 0) +++ src/lib/libshell/common/bltins/hist.c (revision 740) @@ -0,0 +1,309 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#include "defs.h" +#include +#include +#include +#include +#include "variables.h" +#include "io.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#if SHOPT_HISTEXPAND +# include "edit.h" +#endif + +#define HIST_RECURSE 5 + +static void hist_subst(const char*, int fd, char*); + +#if 0 + /* for the benefit of the dictionary generator */ + int b_fc(int argc,char *argv[], void *extra){} +#endif +int b_hist(int argc,char *argv[], void *extra) +{ + register History_t *hp; + register char *arg; + register int flag,fdo; + register Shell_t *shp = (Shell_t*)extra; + Sfio_t *outfile; + char *fname; + int range[2], incr, index2, indx= -1; + char *edit = 0; /* name of editor */ + char *replace = 0; /* replace old=new */ + int lflag = 0, nflag = 0, rflag = 0; +#if SHOPT_HISTEXPAND + int pflag = 0; +#endif + Histloc_t location; + NOT_USED(argc); + if(!sh_histinit()) + errormsg(SH_DICT,ERROR_system(1),e_histopen); + hp = shp->hist_ptr; + while((flag = optget(argv,sh_opthist))) switch(flag) + { + case 'e': + edit = opt_info.arg; + break; + case 'n': + nflag++; + break; + case 'l': + lflag++; + break; + case 'r': + rflag++; + break; + case 's': + edit = "-"; + break; +#if SHOPT_HISTEXPAND + case 'p': + pflag++; + break; +#endif + case 'N': + if(indx<=0) + { + if((flag = hist_max(hp) - opt_info.num-1) < 0) + flag = 1; + range[++indx] = flag; + break; + } + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += (opt_info.index-1); +#if SHOPT_HISTEXPAND + if(pflag) + { + hist_cancel(hp); + pflag = 0; + while(arg=argv[1]) + { + flag = hist_expand(arg,&replace); + if(!(flag & HIST_ERROR)) + sfputr(sfstdout, replace, '\n'); + else + pflag = 1; + if(replace) + free(replace); + argv++; + } + return pflag; + } +#endif + flag = indx; + while(flag<1 && (arg=argv[1])) + { + /* look for old=new argument */ + if(!replace && strchr(arg+1,'=')) + { + replace = arg; + argv++; + continue; + } + else if(isdigit(*arg) || *arg == '-') + { + /* see if completely numeric */ + do arg++; + while(isdigit(*arg)); + if(*arg==0) + { + arg = argv[1]; + range[++flag] = (int)strtol(arg, (char**)0, 10); + if(*arg == '-') + range[flag] += (hist_max(hp)-1); + argv++; + continue; + } + } + /* search for last line starting with string */ + location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1); + if((range[++flag] = location.hist_command) < 0) + errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]); + argv++; + } + if(flag <0) + { + /* set default starting range */ + if(lflag) + { + flag = hist_max(hp)-16; + if(flag<1) + flag = 1; + } + else + flag = hist_max(hp)-2; + range[0] = flag; + flag = 0; + } + index2 = hist_min(hp); + if(range[0]=(flag=(hist_max(hp) - !lflag))) + range[1] = flag; + /* check for valid ranges */ + if(range[1]=flag) + errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]); + if(edit && *edit=='-' && range[0]!=range[1]) + errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg); + /* now list commands from range[rflag] to range[1-rflag] */ + incr = 1; + flag = rflag>0; + if(range[1-flag] < range[flag]) + incr = -1; + if(lflag) + { + outfile = sfstdout; + arg = "\n\t"; + } + else + { + if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*)))) + errormsg(SH_DICT,ERROR_exit(1),e_create,""); + if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0) + errormsg(SH_DICT,ERROR_system(1),e_create,fname); + outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE); + arg = "\n"; + nflag++; + } + while(1) + { + if(nflag==0) + sfprintf(outfile,"%d\t",range[flag]); + else if(lflag) + sfputc(outfile,'\t'); + hist_list(shp->hist_ptr,outfile,hist_tell(shp->hist_ptr,range[flag]),0,arg); + if(lflag) + sh_sigcheck(); + if(range[flag] == range[1-flag]) + break; + range[flag] += incr; + } + if(lflag) + return(0); + sfclose(outfile); + hist_eof(hp); + arg = edit; + if(!arg && !(arg=nv_getval(nv_scoped(HISTEDIT))) && !(arg=nv_getval(nv_scoped(FCEDNOD)))) + arg = (char*)e_defedit; +#ifdef apollo + /* + * Code to support the FC using the pad editor. + * Exampled of how to use: HISTEDIT=pad + */ + if (strcmp (arg, "pad") == 0) + { + extern int pad_create(char*); + sh_close(fdo); + fdo = pad_create(fname); + pad_wait(fdo); + unlink(fname); + strcat(fname, ".bak"); + unlink(fname); + lseek(fdo,(off_t)0,SEEK_SET); + } + else + { +#endif /* apollo */ + if(*arg != '-') + { + char *com[3]; + com[0] = arg; + com[1] = fname; + com[2] = 0; + error_info.errors = sh_eval(sh_sfeval(com),0); + } + fdo = sh_chkopen(fname); + unlink(fname); + free((void*)fname); +#ifdef apollo + } +#endif /* apollo */ + /* don't history fc itself unless forked */ + error_info.flags |= ERROR_SILENT; + if(!sh_isstate(SH_FORKED)) + hist_cancel(hp); + sh_onstate(SH_HISTORY); + sh_onstate(SH_VERBOSE); /* echo lines as read */ + if(replace) + hist_subst(error_info.id,fdo,replace); + else if(error_info.errors == 0) + { + char buff[IOBSIZE+1]; + Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ); + /* read in and run the command */ + if(shp->hist_depth++ > HIST_RECURSE) + errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history"); + sh_eval(iop,1); + shp->hist_depth--; + } + else + { + sh_close(fdo); + if(!sh_isoption(SH_VERBOSE)) + sh_offstate(SH_VERBOSE); + sh_offstate(SH_HISTORY); + } + return(shp->exitval); +} + + +/* + * given a file containing a command and a string of the form old=new, + * execute the command with the string old replaced by new + */ + +static void hist_subst(const char *command,int fd,char *replace) +{ + register char *newp=replace; + register char *sp; + register int c; + off_t size; + char *string; + while(*++newp != '='); /* skip to '=' */ + if((size = lseek(fd,(off_t)0,SEEK_END)) < 0) + return; + lseek(fd,(off_t)0,SEEK_SET); + c = (int)size; + string = stakalloc(c+1); + if(read(fd,string,c)!=c) + return; + string[c] = 0; + *newp++ = 0; + if((sp=sh_substitute(string,replace,newp))==0) + errormsg(SH_DICT,ERROR_exit(1),e_subst,command); + *(newp-1) = '='; + sh_eval(sfopen(NIL(Sfio_t*),sp,"s"),1); +} + Index: src/lib/libshell/common/bltins/sleep.c =================================================================== --- src/lib/libshell/common/bltins/sleep.c (revision 0) +++ src/lib/libshell/common/bltins/sleep.c (revision 740) @@ -0,0 +1,190 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * sleep delay + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#define sleep ______sleep +#include "defs.h" +#undef sleep +#include +#include +#include "builtins.h" +#include "FEATURE/time" +#include "FEATURE/poll" +#ifdef _NEXT_SOURCE +# define sleep _ast_sleep +#endif /* _NEXT_SOURCE */ +#ifdef _lib_poll_notimer +# undef _lib_poll +#endif /* _lib_poll_notimer */ + +int b_sleep(register int argc,char *argv[],void *extra) +{ + register char *cp; + register double d; + register Shell_t *shp = (Shell_t*)extra; + time_t tloc = 0; + while((argc = optget(argv,sh_optsleep))) switch(argc) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors || !(cp= *argv) || !(strmatch(cp,e_numeric))) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if((d=strtod(cp, (char**)0)) > .10) + { + sfsync(shp->outpool); + time(&tloc); + tloc += (time_t)(d+.5); + } + while(1) + { + time_t now; + errno = 0; + shp->lastsig=0; + sh_delay(d); + if(tloc==0 || errno!=EINTR || shp->lastsig) + break; + sh_sigcheck(); + if(tloc < (now=time(NIL(time_t*)))) + break; + d = (double)(tloc-now); + if(shp->sigflag[SIGALRM]&SH_SIGTRAP) + sh_timetraps(); + } + return(0); +} + +static void completed(void * handle) +{ + char *expired = (char*)handle; + *expired = 1; +} + +unsigned int sleep(unsigned int sec) +{ + pid_t newpid, curpid=getpid(); + void *tp; + char expired = 0; + sh.lastsig = 0; + tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired); + do + { + if(!sh.waitevent || (*sh.waitevent)(-1,-1L,0)==0) + pause(); + if(sh.sigflag[SIGALRM]&SH_SIGTRAP) + sh_timetraps(); + if((newpid=getpid()) != curpid) + { + curpid = newpid; + sh.lastsig = 0; + sh.trapnote &= ~SH_SIGSET; + if(expired) + expired = 0; + else + timerdel(tp); + tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired); + } + } + while(!expired && sh.lastsig==0); + if(!expired) + timerdel(tp); + sh_sigcheck(); + return(0); +} + +/* + * delay execution for time + */ + +void sh_delay(double t) +{ + register int n = (int)t; +#ifdef _lib_poll + struct pollfd fd; + if(t<=0) + return; + else if(n > 30) + { + sleep(n); + t -= n; + } + if(n=(int)(1000*t)) + { + if(!sh.waitevent || (*sh.waitevent)(-1,(long)n,0)==0) + poll(&fd,0,n); + } +#else +# if defined(_lib_select) && defined(_mem_tv_usec_timeval) + struct timeval timeloc; + if(t<=0) + return; + if(n=(int)(1000*t) && sh.waitevent && (*sh.waitevent)(-1,(long)n,0)) + return; + n = (int)t; + timeloc.tv_sec = n; + timeloc.tv_usec = 1000000*(t-(double)n); + select(0,(fd_set*)0,(fd_set*)0,(fd_set*)0,&timeloc); +# else +# ifdef _lib_select + /* for 9th edition machines */ + if(t<=0) + return; + if(n > 30) + { + sleep(n); + t -= n; + } + if(n=(int)(1000*t)) + { + if(!sh.waitevent || (*sh.waitevent)(-1,(long)n,0)==0) + select(0,(fd_set*)0,(fd_set*)0,n); + } +# else + struct tms tt; + if(t<=0) + return; + sleep(n); + t -= n; + if(t) + { + clock_t begin = times(&tt); + if(begin==0) + return; + t *= sh.lim.clk_tck; + n += (t+.5); + while((times(&tt)-begin) < n); + } +# endif +# endif +#endif /* _lib_poll */ +} Index: src/lib/libshell/common/bltins/whence.c =================================================================== --- src/lib/libshell/common/bltins/whence.c (revision 0) +++ src/lib/libshell/common/bltins/whence.c (revision 740) @@ -0,0 +1,273 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * command [-pvVx] name [arg...] + * whence [-afvp] name... + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include "shtable.h" +#include "name.h" +#include "path.h" +#include "shlex.h" +#include "builtins.h" + +#define P_FLAG 1 +#define V_FLAG 2 +#define A_FLAG 4 +#define F_FLAG 010 +#define X_FLAG 020 + +static int whence(Shell_t *,char**, int); + +/* + * command is called with argc==0 when checking for -V or -v option + * In this case return 0 when -v or -V or unknown option, otherwise + * the shift count to the command is returned + */ +int b_command(register int argc,char *argv[],void *extra) +{ + register int n, flags=0; + register Shell_t *shp = (Shell_t*)extra; + opt_info.index = opt_info.offset = 0; + while((n = optget(argv,sh_optcommand))) switch(n) + { + case 'p': + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,"-p"); + sh_onstate(SH_DEFPATH); + break; + case 'v': + flags |= X_FLAG; + break; + case 'V': + flags |= V_FLAG; + break; + case 'x': + shp->xargexit = 1; + break; + case ':': + if(argc==0) + return(0); + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + if(argc==0) + return(0); + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + if(argc==0) + return(flags?0:opt_info.index); + argv += opt_info.index; + if(error_info.errors || !*argv) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); + return(whence(shp,argv, flags)); +} + +/* + * for the whence command + */ +int b_whence(int argc,char *argv[],void *extra) +{ + register int flags=0, n; + register Shell_t *shp = (Shell_t*)extra; + NOT_USED(argc); + if(*argv[0]=='t') + flags = V_FLAG; + while((n = optget(argv,sh_optwhence))) switch(n) + { + case 'a': + flags |= A_FLAG; + /* FALL THRU */ + case 'v': + flags |= V_FLAG; + break; + case 'f': + flags |= F_FLAG; + break; + case 'p': + flags |= P_FLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors || !*argv) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + return(whence(shp, argv, flags)); +} + +static int whence(Shell_t *shp,char **argv, register int flags) +{ + register const char *name; + register Namval_t *np; + register const char *cp; + register int aflag,r=0; + register const char *msg; + int tofree; + Dt_t *root; + Namval_t *nq; + char *notused; +#ifdef PATH_BFPATH + Pathcomp_t *pp; +#endif + int notrack = 1; + while(name= *argv++) + { + tofree=0; + aflag = ((flags&A_FLAG)!=0); + cp = 0; + np = 0; +#ifdef PATH_BFPATH + pp = 0; +#endif + if(flags&P_FLAG) + goto search; + /* reserved words first */ + if(sh_lookup(name,shtab_reserved)) + { + sfprintf(sfstdout,"%s%s\n",name,(flags&V_FLAG)?sh_translate(is_reserved):""); + if(!aflag) + continue; + aflag++; + } + /* non-tracked aliases */ + if((np=nv_search(name,shp->alias_tree,0)) + && !nv_isnull(np) && !(notrack=nv_isattr(np,NV_TAGGED)) + && (cp=nv_getval(np))) + { + if(flags&V_FLAG) + { + if(nv_isattr(np,NV_EXPORT)) + msg = sh_translate(is_xalias); + else + msg = sh_translate(is_alias); + sfprintf(sfstdout,msg,name); + } + sfputr(sfstdout,sh_fmtq(cp),'\n'); + if(!aflag) + continue; + cp = 0; + aflag++; + } + /* built-ins and functions next */ + root = (flags&F_FLAG)?shp->bltin_tree:shp->fun_tree; + if(np= nv_bfsearch(name, root, &nq, ¬used)) + { + if(is_abuiltin(np) && nv_isnull(np)) + goto search; + cp = ""; + if(flags&V_FLAG) + { + if(nv_isnull(np)) + cp = sh_translate(is_ufunction); + else if(is_abuiltin(np)) + cp = sh_translate(is_builtin); + else + cp = sh_translate(is_function); + } + sfprintf(sfstdout,"%s%s\n",name,cp); + if(!aflag) + continue; + cp = 0; + aflag++; + } + search: + if(sh_isstate(SH_DEFPATH)) + { + cp=0; + notrack=1; + } +#ifdef PATH_BFPATH + if(path_search(name,pp,2)) + cp = name; + else + { + cp = stakptr(PATH_OFFSET); + if(*cp==0) + cp = 0; + else if(*cp!='/') + { + cp = path_fullname(cp); + tofree=1; + } + } +#else + if(path_search(name,cp,2)) + cp = name; + else + cp = shp->lastpath; + shp->lastpath = 0; +#endif + if(cp) + { + if(flags&V_FLAG) + { + if(*cp!= '/') + { +#ifdef PATH_BFPATH + if(!np && (np=nv_search(name,shp->track_tree,0))) + sfprintf(sfstdout,"%s %s %s/%s\n",name,sh_translate(is_talias),path_pwd(0),cp); + else if(!np || nv_isnull(np)) +#else + if(!np || nv_isnull(np)) +#endif + sfprintf(sfstdout,"%s%s\n",name,sh_translate(is_ufunction)); + continue; + } + sfputr(sfstdout,sh_fmtq(name),' '); + /* built-in version of program */ + if(*cp=='/' && (np=nv_search(cp,shp->bltin_tree,0))) + msg = sh_translate(is_builtver); + /* tracked aliases next */ + else if(!notrack || strchr(name,'/')) + msg = sh_translate("is"); + else + msg = sh_translate(is_talias); + sfputr(sfstdout,msg,' '); + } + sfputr(sfstdout,sh_fmtq(cp),'\n'); + if(tofree) + free((char*)cp); + } + else if(aflag<=1) + { + r |= 1; + if(flags&V_FLAG) + { + sfprintf(sfstdout,sh_translate(e_found),sh_fmtq(name)); + sfputc(sfstdout,'\n'); + } + } + } + return(r); +} + Index: src/lib/libshell/common/bltins/cflow.c =================================================================== --- src/lib/libshell/common/bltins/cflow.c (revision 0) +++ src/lib/libshell/common/bltins/cflow.c (revision 740) @@ -0,0 +1,118 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * break [n] + * continue [n] + * return [n] + * exit [n] + * + * David Korn + * AT&T Labs + * dgk@research.att.com + * + */ + +#include "defs.h" +#include +#include +#include +#include "shnodes.h" +#include "builtins.h" + +/* + * return and exit + */ +#if 0 + /* for the dictionary generator */ + int b_exit(int n, register char *argv[],void *extra){} +#endif +int b_return(register int n, register char *argv[],void *extra) +{ + register char *arg; + register Shell_t *shp = (Shell_t*)extra; + struct checkpt *pp = (struct checkpt*)shp->jmplist; + const char *options = (**argv=='r'?sh_optreturn:sh_optexit); + while((n = optget(argv,options))) switch(n) + { + case ':': + if(!strmatch(argv[opt_info.index],"[+-]+([0-9])")) + errormsg(SH_DICT,2, "%s", opt_info.arg); + goto done; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } +done: + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + pp->mode = (**argv=='e'?SH_JMPEXIT:SH_JMPFUN); + argv += opt_info.index; + n = (((arg= *argv)?(int)strtol(arg, (char**)0, 10):shp->oldexit)&SH_EXITMASK); + /* return outside of function, dotscript and profile is exit */ + if(shp->fn_depth==0 && shp->dot_depth==0 && !sh_isstate(SH_PROFILE)) + pp->mode = SH_JMPEXIT; + sh_exit(shp->savexit=n); + return(1); +} + + +/* + * break and continue + */ +#if 0 + /* for the dictionary generator */ + int b_continue(int n, register char *argv[],void *extra){} +#endif +int b_break(register int n, register char *argv[],void *extra) +{ + char *arg; + register int cont= **argv=='c'; + register Shell_t *shp = (Shell_t*)extra; + while((n = optget(argv,cont?sh_optcont:sh_optbreak))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + n=1; + if(arg= *argv) + { + n = strtol(arg,&arg,10); + if(n<=0 || *arg) + errormsg(SH_DICT,ERROR_exit(1),e_nolabels,*argv); + } + if(shp->st.loopcnt) + { + shp->st.execbrk = shp->st.breakcnt = n; + if(shp->st.breakcnt > shp->st.loopcnt) + shp->st.breakcnt = shp->st.loopcnt; + if(cont) + shp->st.breakcnt = -shp->st.breakcnt; + } + return(0); +} + Index: src/lib/libshell/common/bltins/read.c =================================================================== --- src/lib/libshell/common/bltins/read.c (revision 0) +++ src/lib/libshell/common/bltins/read.c (revision 740) @@ -0,0 +1,590 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * read [-Aprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...] + * + * David Korn + * AT&T Labs + * + */ + +#include +#include +#include +#include "defs.h" +#include "variables.h" +#include "lexstates.h" +#include "io.h" +#include "name.h" +#include "builtins.h" +#include "history.h" +#include "terminal.h" +#include "edit.h" + +#define R_FLAG 1 /* raw mode */ +#define S_FLAG 2 /* save in history file */ +#define A_FLAG 4 /* read into array */ +#define N_FLAG 8 /* fixed size read at most */ +#define NN_FLAG 0x10 /* fixed size read exact */ +#define V_FLAG 0x20 /* use default value */ +#define D_FLAG 8 /* must be number of bits for all flags */ + +int b_read(int argc,char *argv[], void *extra) +{ + Sfdouble_t sec; + register char *name; + register int r, flags=0, fd=0; + register Shell_t *shp = (Shell_t*)extra; + long timeout = 1000*shp->st.tmout; + int save_prompt; + static char default_prompt[3] = {ESC,ESC}; + NOT_USED(argc); + while((r = optget(argv,sh_optread))) switch(r) + { + case 'A': + flags |= A_FLAG; + break; + case 't': + sec = sh_strnum(opt_info.arg, (char**)0,1); + timeout = sec ? 1000*sec : 1; + break; + case 'd': + if(opt_info.arg && *opt_info.arg!='\n') + { + char *cp = opt_info.arg; + flags &= ~((1<cpipe[0])<=0) + errormsg(SH_DICT,ERROR_exit(1),e_query); + break; + case 'n': case 'N': + flags &= ~((1< (1<<((8*sizeof(int))-D_FLAG))-1) + errormsg(SH_DICT,ERROR_exit(1),e_overlimit,"n"); + flags |= (r<< D_FLAG); + break; + case 'r': + flags |= R_FLAG; + break; + case 's': + /* save in history file */ + flags |= S_FLAG; + break; + case 'u': + fd = (int)opt_info.num; + if(sh_inuse(fd)) + fd = -1; + break; + case 'v': + flags |= V_FLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); + if(!((r=shp->fdstatus[fd])&IOREAD) || !(r&(IOSEEK|IONOSEEK))) + r = sh_iocheckfd(fd); + if(fd<0 || !(r&IOREAD)) + errormsg(SH_DICT,ERROR_system(1),e_file+4); + /* look for prompt */ + shp->prompt = default_prompt; + if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY)) + { + r = strlen(++name)+1; + if(shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR)) + { + memcpy(shp->prompt,name,r); + sfwrite(sfstderr,shp->prompt,r-1); + } + } + shp->timeout = 0; + save_prompt = shp->nextprompt; + shp->nextprompt = 0; + r=sh_readline(shp,argv,fd,flags,timeout); + shp->nextprompt = save_prompt; + if(r==0 && (r=(sfeof(shp->sftable[fd])||sferror(shp->sftable[fd])))) + { + if(fd == shp->cpipe[0]) + { + sh_pclose(shp->cpipe); + return(1); + } + } + sfclrerr(shp->sftable[fd]); + return(r); +} + +/* + * here for read timeout + */ +static void timedout(void *handle) +{ + sfclrlock((Sfio_t*)handle); + sh_exit(1); +} + +/* + * This is the code to read a line and to split it into tokens + * is an array of variable names + * is the file descriptor + * is union of -A, -r, -s, and contains delimiter if not '\n' + * is number of milli-seconds until timeout + */ + +int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeout) +{ + register int c; + register unsigned char *cp; + register Namval_t *np; + register char *name, *val; + register Sfio_t *iop; + char *ifs; + unsigned char *cpmax; + unsigned char *del; + char was_escape = 0; + char use_stak = 0; + char was_write = 0; + char was_share = 1; + int rel, wrd; + long array_index = 0; + void *timeslot=0; + int delim = '\n'; + int jmpval=0; + int size = 0; + struct checkpt buff; + if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(fd))) + return(1); + if(names && (name = *names)) + { + if(val= strchr(name,'?')) + *val = 0; + np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME|NV_ARRAY); + if((flags&V_FLAG) && shp->ed_context) + ((struct edit*)shp->ed_context)->e_default = np; + if(flags&A_FLAG) + { + flags &= ~A_FLAG; + array_index = 1; + nv_unset(np); + nv_putsub(np,NIL(char*),0L); + } + else + name = *++names; + if(val) + *val = '?'; + } + else + { + name = 0; + if(dtvnext(shp->var_tree) || shp->namespace) + np = nv_open(nv_name(REPLYNOD),shp->var_tree,0); + else + np = REPLYNOD; + } + if(flags>>D_FLAG) /* delimiter not new-line or fixed size read */ + { + if(flags&(N_FLAG|NN_FLAG)) + size = ((unsigned)flags)>>D_FLAG; + else + delim = ((unsigned)flags)>>D_FLAG; + if(shp->fdstatus[fd]&IOTTY) + tty_raw(fd,1); + } + if(!(flags&(N_FLAG|NN_FLAG))) + { + Namval_t *mp; + /* set up state table based on IFS */ + ifs = nv_getval(mp=nv_scoped(IFSNOD)); + if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC) + shp->ifstable['\\'] = 0; + else if(!(flags&R_FLAG) && shp->ifstable['\\']==0) + shp->ifstable['\\'] = S_ESC; + shp->ifstable[delim] = S_NL; + if(delim!='\n') + { + shp->ifstable['\n'] = 0; + nv_putval(mp, ifs, NV_RDONLY); + } + shp->ifstable[0] = S_EOF; + } + sfclrerr(iop); + if(np->nvfun && np->nvfun->disc->readf) + return((* np->nvfun->disc->readf)(np,iop,delim,np->nvfun)); + was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0; + if(fd==0) + was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0; + if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) + { + sh_pushcontext(&buff,1); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval) + goto done; + if(timeout) + timeslot = (void*)sh_timeradd(timeout,0,timedout,(void*)iop); + } + if(flags&(N_FLAG|NN_FLAG)) + { + char buf[64],*var=buf; + /* reserved buffer */ + if((c=size)>=sizeof(buf)) + { + if(!(var = (char*)malloc(c+1))) + sh_exit(1); + } + if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0) + was_share = 1; + if(size==0) + { + cp = sfreserve(iop,0,0); + c = 0; + } + else + { + c= (shp->fdstatus[fd]&(IOTTY|IONOSEEK))?1:-1; + if(flags&NN_FLAG) + c = size; + if(cp = sfreserve(iop,c,!(flags&NN_FLAG))) + c = sfvalue(iop); + else + c = 0; + if(c>size) + c = size; + if(c>0) + { + memcpy((void*)var,cp,c); + if(flags&N_FLAG) + sfread(iop,cp,c); + } + var[c] = 0; + if(c>=size) + sfclrerr(iop); + } + if(timeslot) + timerdel(timeslot); + if(nv_isattr(np,NV_BINARY)) + { + if(c=sizeof(buf)) + free((void*)var); + } + goto done; + } + else if(cp = (unsigned char*)sfgetr(iop,delim,0)) + c = sfvalue(iop); + else if(cp = (unsigned char*)sfgetr(iop,delim,-1)) + c = sfvalue(iop)+1; + if(timeslot) + timerdel(timeslot); + if((flags&S_FLAG) && !shp->hist_ptr) + { + sh_histinit(); + if(!shp->hist_ptr) + flags &= ~S_FLAG; + } + if(cp) + { + cpmax = cp + c; +#if SHOPT_CRNL + if(delim=='\n' && c>=2 && cpmax[-2]=='\r') + cpmax--; +#endif /* SHOPT_CRNL */ + if(*(cpmax-1) != delim) + *(cpmax-1) = delim; + if(flags&S_FLAG) + sfwrite(shp->hist_ptr->histfp,(char*)cp,c); + c = shp->ifstable[*cp++]; +#if !SHOPT_MULTIBYTE + if(!name && (flags&R_FLAG)) /* special case single argument */ + { + /* skip over leading blanks */ + while(c==S_SPACE) + c = shp->ifstable[*cp++]; + /* strip trailing delimiters */ + if(cpmax[-1] == '\n') + cpmax--; + if(cpmax>cp) + { + while((c=shp->ifstable[*--cpmax])==S_DELIM || c==S_SPACE); + cpmax[1] = 0; + } + else + *cpmax =0; + if(nv_isattr(np, NV_RDONLY)) + { + errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); + jmpval = 1; + } + else + nv_putval(np,(char*)cp-1,0); + goto done; + } +#endif /* !SHOPT_MULTIBYTE */ + } + else + c = S_NL; + shp->nextprompt = 2; + rel= staktell(); + /* val==0 at the start of a field */ + val = 0; + del = 0; + while(1) + { + switch(c) + { +#if SHOPT_MULTIBYTE + case S_MBYTE: + if(val==0) + val = (char*)(cp-1); + if(sh_strchr(ifs,(char*)cp-1)>=0) + { + c = mbsize((char*)cp-1); + if(name) + cp[-1] = 0; + if(c>1) + cp += (c-1); + c = S_DELIM; + } + else + c = 0; + continue; +#endif /*SHOPT_MULTIBYTE */ + case S_ESC: + /* process escape character */ + if((c = shp->ifstable[*cp++]) == S_NL) + was_escape = 1; + else + c = 0; + if(val) + { + stakputs(val); + use_stak = 1; + was_escape = 1; + *val = 0; + } + continue; + + case S_EOF: + /* check for end of buffer */ + if(val && *val) + { + stakputs(val); + use_stak = 1; + } + val = 0; + if(cp>=cpmax) + { + c = S_NL; + break; + } + /* eliminate null bytes */ + c = shp->ifstable[*cp++]; + if(!name && val && (c==S_SPACE||c==S_DELIM||c==S_MBYTE)) + c = 0; + continue; + case S_NL: + if(was_escape) + { + was_escape = 0; + if(cp = (unsigned char*)sfgetr(iop,delim,0)) + c = sfvalue(iop); + else if(cp=(unsigned char*)sfgetr(iop,delim,-1)) + c = sfvalue(iop)+1; + if(cp) + { + if(flags&S_FLAG) + sfwrite(shp->hist_ptr->histfp,(char*)cp,c); + cpmax = cp + c; + c = shp->ifstable[*cp++]; + val=0; + if(!name && (c==S_SPACE || c==S_DELIM || c==S_MBYTE)) + c = 0; + continue; + } + } + c = S_NL; + break; + + case S_SPACE: + /* skip over blanks */ + while((c=shp->ifstable[*cp++])==S_SPACE); + if(!val) + continue; +#if SHOPT_MULTIBYTE + if(c==S_MBYTE) + { + if(sh_strchr(ifs,(char*)cp-1)>=0) + { + if((c = mbsize((char*)cp-1))>1) + cp += (c-1); + c = S_DELIM; + } + else + c = 0; + } +#endif /* SHOPT_MULTIBYTE */ + if(c!=S_DELIM) + break; + /* FALL THRU */ + + case S_DELIM: + if(!del) + del = cp - 1; + if(name) + { + /* skip over trailing blanks */ + while((c=shp->ifstable[*cp++])==S_SPACE); + break; + } + /* FALL THRU */ + + case 0: + if(val==0 || was_escape) + { + val = (char*)(cp-1); + was_escape = 0; + } + /* skip over word characters */ + wrd = -1; + while(1) + { + while((c=shp->ifstable[*cp++])==0) + if(!wrd) + wrd = 1; + if(!del&&c==S_DELIM) + del = cp - 1; + if(name || c==S_NL || c==S_ESC || c==S_EOF || c==S_MBYTE) + break; + if(wrd<0) + wrd = 0; + } + if(wrd>0) + del = (unsigned char*)""; + if(c!=S_MBYTE) + cp[-1] = 0; + continue; + } + /* assign value and advance to next variable */ + if(!val) + val = ""; + if(use_stak) + { + stakputs(val); + stakputc(0); + val = stakptr(rel); + } + if(!name && *val) + { + /* strip off trailing space delimiters */ + register unsigned char *vp = (unsigned char*)val + strlen(val); + while(shp->ifstable[*--vp]==S_SPACE); + if(vp==del) + { + if(vp==(unsigned char*)val) + vp--; + else + while(shp->ifstable[*--vp]==S_SPACE); + } + vp[1] = 0; + } + if(nv_isattr(np, NV_RDONLY)) + { + errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); + jmpval = 1; + } + else + nv_putval(np,val,0); + val = 0; + del = 0; + if(use_stak) + { + stakseek(rel); + use_stak = 0; + } + if(array_index) + { + nv_putsub(np, NIL(char*), array_index++); + if(c!=S_NL) + continue; + name = *++names; + } + while(1) + { + if(sh_isoption(SH_ALLEXPORT)&&!strchr(nv_name(np),'.') && !nv_isattr(np,NV_EXPORT)) + { + nv_onattr(np,NV_EXPORT); + sh_envput(sh.env,np); + } + if(name) + { + nv_close(np); + np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME); + name = *++names; + } + else + np = 0; + if(c!=S_NL) + break; + if(!np) + goto done; + if(nv_isattr(np, NV_RDONLY)) + { + errormsg(SH_DICT,ERROR_warn(0),e_readonly, nv_name(np)); + jmpval = 1; + } + else + nv_putval(np, "", 0); + } + } +done: + if(timeout || (shp->fdstatus[fd]&(IOTTY|IONOSEEK))) + sh_popcontext(&buff); + if(was_write) + sfset(iop,SF_WRITE,1); + if(!was_share) + sfset(iop,SF_SHARE,0); + nv_close(np); + if((flags>>D_FLAG) && (shp->fdstatus[fd]&IOTTY)) + tty_cooked(fd); + if(flags&S_FLAG) + hist_flush(shp->hist_ptr); + if(jmpval > 1) + siglongjmp(*shp->jmplist,jmpval); + return(jmpval); +} + Index: src/lib/libshell/common/bltins/test.c =================================================================== --- src/lib/libshell/common/bltins/test.c (revision 0) +++ src/lib/libshell/common/bltins/test.c (revision 740) @@ -0,0 +1,629 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * test expression + * [ expression ] + * + * David Korn + * AT&T Labs + * + */ + + +#include "defs.h" +#include +#include +#include +#include "io.h" +#include "terminal.h" +#include "test.h" +#include "builtins.h" +#include "FEATURE/externs" +#include "FEATURE/poll" +#include + +#if !_lib_setregid +# undef _lib_setreuid +#endif /* _lib_setregid */ + +#ifdef S_ISSOCK +# if _pipe_socketpair +# if _socketpair_shutdown_mode +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino&&((p)->st_mode&(S_IRUSR|S_IWUSR))!=(S_IRUSR|S_IWUSR)) +# else +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino) +# endif +# else +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino) +# endif +# define isasock(f,p) (test_stat(f,p)>=0&&S_ISSOCK((p)->st_mode)) +#else +# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)) +# define isasock(f,p) (0) +#endif + +#define permission(a,f) (sh_access(a,f)==0) +static time_t test_time(const char*, const char*); +static int test_stat(const char*, struct stat*); +static int test_mode(const char*); + +/* single char string compare */ +#define c_eq(a,c) (*a==c && *(a+1)==0) +/* two character string compare */ +#define c2_eq(a,c1,c2) (*a==c1 && *(a+1)==c2 && *(a+2)==0) + +struct test +{ + Shell_t *sh; + int ap; + int ac; + char **av; +}; + +static char *nxtarg(struct test*,int); +static int expr(struct test*,int); +static int e3(struct test*); + +static int test_strmatch(const char *str, const char *pat) +{ + int match[2*(MATCH_MAX+1)],n; + register int c, m=0; + register const char *cp=pat; + while(c = *cp++) + { + if(c=='(') + m++; + if(c=='\\' && *cp) + cp++; + } + if(m) + m++; + else + match[0] = 0; + if(m > elementsof(match)/2) + m = elementsof(match)/2; + n = strgrpmatch(str, pat, match, m, STR_MAXIMAL|STR_LEFT|STR_RIGHT); + if(m==0 && n==1) + match[1] = strlen(str); + if(n) + sh_setmatch(str, -1, n, match); + return(n); +} + +int b_test(int argc, char *argv[],void *extra) +{ + struct test tdata; + register char *cp = argv[0]; + register int not; + tdata.sh = (Shell_t*)extra; + tdata.av = argv; + tdata.ap = 1; + if(c_eq(cp,'[')) + { + cp = argv[--argc]; + if(!c_eq(cp, ']')) + errormsg(SH_DICT,ERROR_exit(2),e_missing,"']'"); + } + if(argc <= 1) + return(1); + cp = argv[1]; + not = c_eq(cp,'!'); + /* posix portion for test */ + switch(argc) + { + case 5: + if(!not) + break; + argv++; + /* fall through */ + case 4: + { + register int op = sh_lookup(cp=argv[2],shtab_testops); + if(op&TEST_BINOP) + break; + if(!op) + { + if(argc==5) + break; + if(not && cp[0]=='-' && cp[2]==0) + return(test_unop(cp[1],argv[3])!=0); + else if(argv[1][0]=='-' && argv[1][2]==0) + return(!test_unop(argv[1][1],cp)); + errormsg(SH_DICT,ERROR_exit(2),e_badop,cp); + } + return(test_binop(op,argv[1],argv[3])^(argc!=5)); + } + case 3: + if(not) + return(*argv[2]!=0); + if(cp[0] != '-' || cp[2] || cp[1]=='?') + { + if(cp[0]=='-' && (cp[1]=='-' || cp[1]=='?') && + strcmp(argv[2],"--")==0) + { + char *av[3]; + av[0] = argv[0]; + av[1] = argv[1]; + av[2] = 0; + optget(av,sh_opttest); + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + return(2); + } + break; + } + return(!test_unop(cp[1],argv[2])); + case 2: + return(*cp==0); + } + if(argc==5) + argv--; + tdata.ac = argc; + return(!expr(&tdata,0)); +} + +/* + * evaluate a test expression. + * flag is 0 on outer level + * flag is 1 when in parenthesis + * flag is 2 when evaluating -a + */ +static int expr(struct test *tp,register int flag) +{ + register int r; + register char *p; + r = e3(tp); + while(tp->ap < tp->ac) + { + p = nxtarg(tp,0); + /* check for -o and -a */ + if(flag && c_eq(p,')')) + { + tp->ap--; + break; + } + if(*p=='-' && *(p+2)==0) + { + if(*++p == 'o') + { + if(flag==2) + { + tp->ap--; + break; + } + r |= expr(tp,3); + continue; + } + else if(*p == 'a') + { + r &= expr(tp,2); + continue; + } + } + if(flag==0) + break; + errormsg(SH_DICT,ERROR_exit(2),e_badsyntax); + } + return(r); +} + +static char *nxtarg(struct test *tp,int mt) +{ + if(tp->ap >= tp->ac) + { + if(mt) + { + tp->ap++; + return(0); + } + errormsg(SH_DICT,ERROR_exit(2),e_argument); + } + return(tp->av[tp->ap++]); +} + + +static int e3(struct test *tp) +{ + register char *arg, *cp; + register int op; + char *binop; + arg=nxtarg(tp,0); + if(arg && c_eq(arg, '!')) + return(!e3(tp)); + if(c_eq(arg, '(')) + { + op = expr(tp,1); + cp = nxtarg(tp,0); + if(!cp || !c_eq(cp, ')')) + errormsg(SH_DICT,ERROR_exit(2),e_missing,"')'"); + return(op); + } + cp = nxtarg(tp,1); + if(cp!=0 && (c_eq(cp,'=') || c2_eq(cp,'!','='))) + goto skip; + if(c2_eq(arg,'-','t')) + { + if(cp && isdigit(*cp)) + return(*(cp+1)?0:tty_check(*cp-'0')); + else + { + /* test -t with no arguments */ + tp->ap--; + return(tty_check(1)); + } + } + if(*arg=='-' && arg[2]==0) + { + op = arg[1]; + if(!cp) + { + /* for backward compatibility with new flags */ + if(op==0 || !strchr(test_opchars+10,op)) + return(1); + errormsg(SH_DICT,ERROR_exit(2),e_argument); + } + if(strchr(test_opchars,op)) + return(test_unop(op,cp)); + } + if(!cp) + { + tp->ap--; + return(*arg!=0); + } +skip: + op = sh_lookup(binop=cp,shtab_testops); + if(!(op&TEST_BINOP)) + cp = nxtarg(tp,0); + if(!op) + errormsg(SH_DICT,ERROR_exit(2),e_badop,binop); + if(op==TEST_AND | op==TEST_OR) + tp->ap--; + return(test_binop(op,arg,cp)); +} + +int test_unop(register int op,register const char *arg) +{ + struct stat statb; + int f; + switch(op) + { + case 'r': + return(permission(arg, R_OK)); + case 'w': + return(permission(arg, W_OK)); + case 'x': + return(permission(arg, X_OK)); + case 'V': +#if SHOPT_FS_3D + { + register int offset = staktell(); + if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode)) + return(0); + /* add trailing / */ + stakputs(arg); + stakputc('/'); + stakputc(0); + arg = (const char*)stakptr(offset); + stakseek(offset); + /* FALL THRU */ + } +#else + return(0); +#endif /* SHOPT_FS_3D */ + case 'd': + return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode)); + case 'c': + return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode)); + case 'b': + return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode)); + case 'f': + return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode)); + case 'u': + return(test_mode(arg)&S_ISUID); + case 'g': + return(test_mode(arg)&S_ISGID); + case 'k': +#ifdef S_ISVTX + return(test_mode(arg)&S_ISVTX); +#else + return(0); +#endif /* S_ISVTX */ +#if SHOPT_TEST_L + case 'l': +#endif + case 'L': + case 'h': /* undocumented, and hopefully will disappear */ + if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0) + return(0); + return(S_ISLNK(statb.st_mode)); + + case 'C': +#ifdef S_ISCTG + return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode)); +#else + return(0); +#endif /* S_ISCTG */ + case 'H': +#ifdef S_ISCDF + { + register int offset = staktell(); + if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)) + return(1); + stakputs(arg); + stakputc('+'); + stakputc(0); + arg = (const char*)stakptr(offset); + stakseek(offset); + return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)); + } +#else + return(0); +#endif /* S_ISCDF */ + + case 'S': + return(isasock(arg,&statb)); + case 'N': + return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb)); + case 'p': + return(isapipe(arg,&statb)); + case 'n': + return(*arg != 0); + case 'z': + return(*arg == 0); + case 's': + sfsync(sfstdout); + case 'O': + case 'G': + if(*arg==0 || test_stat(arg,&statb)<0) + return(0); + if(op=='s') + return(statb.st_size>0); + else if(op=='O') + return(statb.st_uid==sh.userid); + return(statb.st_gid==sh.groupid); + case 'a': + case 'e': + return(permission(arg, F_OK)); + case 'o': + f=1; + if(*arg=='?') + return(sh_lookopt(arg+1,&f)>0); + op = sh_lookopt(arg,&f); + return(op && (f==(sh_isoption(op)!=0))); + case 't': + if(isdigit(*arg) && arg[1]==0) + return(tty_check(*arg-'0')); + return(0); + default: + { + static char a[3] = "-?"; + a[1]= op; + errormsg(SH_DICT,ERROR_exit(2),e_badop,a); + /* NOTREACHED */ + return(0); + } + } +} + +int test_binop(register int op,const char *left,const char *right) +{ + register double lnum,rnum; + if(op&TEST_ARITH) + { + while(*left=='0') + left++; + while(*right=='0') + right++; + lnum = sh_arith(left); + rnum = sh_arith(right); + } + switch(op) + { + /* op must be one of the following values */ + case TEST_AND: + case TEST_OR: + return(*left!=0); + case TEST_PEQ: + return(test_strmatch(left, right)); + case TEST_PNE: + return(!test_strmatch(left, right)); + case TEST_SGT: + return(strcoll(left, right)>0); + case TEST_SLT: + return(strcoll(left, right)<0); + case TEST_SEQ: + return(strcmp(left, right)==0); + case TEST_SNE: + return(strcmp(left, right)!=0); + case TEST_EF: + return(test_inode(left,right)); + case TEST_NT: + return(test_time(left,right)>0); + case TEST_OT: + return(test_time(left,right)<0); + case TEST_EQ: + return(lnum==rnum); + case TEST_NE: + return(lnum!=rnum); + case TEST_GT: + return(lnum>rnum); + case TEST_LT: + return(lnum=rnum); + case TEST_LE: + return(lnum<=rnum); + } + /* NOTREACHED */ + return(0); +} + +/* + * returns the modification time of f1 - modification time of f2 + */ + +static time_t test_time(const char *file1,const char *file2) +{ + Time_t t1, t2; + struct stat statb1,statb2; + int r=test_stat(file2,&statb2); + if(test_stat(file1,&statb1)<0) + return(r<0?0:-1); + if(r<0) + return(1); + t1 = tmxgetmtime(&statb1); + t2 = tmxgetmtime(&statb2); + if (t1 > t2) + return(1); + if (t1 < t2) + return(-1); + return(0); +} + +/* + * return true if inode of two files are the same + */ + +int test_inode(const char *file1,const char *file2) +{ + struct stat stat1,stat2; + if(test_stat(file1,&stat1)>=0 && test_stat(file2,&stat2)>=0) + if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino) + return(1); + return(0); +} + + +/* + * This version of access checks against effective uid/gid + * The static buffer statb is shared with test_mode. + */ + +int sh_access(register const char *name, register int mode) +{ + struct stat statb; + if(*name==0) + return(-1); + if(strmatch(name,(char*)e_devfdNN)) + return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode)); + /* can't use access function for execute permission with root */ + if(mode==X_OK && sh.euserid==0) + goto skip; + if(sh.userid==sh.euserid && sh.groupid==sh.egroupid) + return(access(name,mode)); +#ifdef _lib_setreuid + /* swap the real uid to effective, check access then restore */ + /* first swap real and effective gid, if different */ + if(sh.groupid==sh.euserid || setregid(sh.egroupid,sh.groupid)==0) + { + /* next swap real and effective uid, if needed */ + if(sh.userid==sh.euserid || setreuid(sh.euserid,sh.userid)==0) + { + mode = access(name,mode); + /* restore ids */ + if(sh.userid!=sh.euserid) + setreuid(sh.userid,sh.euserid); + if(sh.groupid!=sh.egroupid) + setregid(sh.groupid,sh.egroupid); + return(mode); + } + else if(sh.groupid!=sh.egroupid) + setregid(sh.groupid,sh.egroupid); + } +#endif /* _lib_setreuid */ +skip: + if(test_stat(name, &statb) == 0) + { + if(mode == F_OK) + return(mode); + else if(sh.euserid == 0) + { + if(!S_ISREG(statb.st_mode) || mode!=X_OK) + return(0); + /* root needs execute permission for someone */ + mode = (S_IXUSR|S_IXGRP|S_IXOTH); + } + else if(sh.euserid == statb.st_uid) + mode <<= 6; + else if(sh.egroupid == statb.st_gid) + mode <<= 3; +#ifdef _lib_getgroups + /* on some systems you can be in several groups */ + else + { + static int maxgroups; + gid_t *groups; + register int n; + if(maxgroups==0) + { + /* first time */ + if((maxgroups=getgroups(0,(gid_t*)0)) <= 0) + { + /* pre-POSIX system */ + maxgroups=NGROUPS_MAX; + } + } + groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t)); + n = getgroups(maxgroups,groups); + while(--n >= 0) + { + if(groups[n] == statb.st_gid) + { + mode <<= 3; + break; + } + } + } +# endif /* _lib_getgroups */ + if(statb.st_mode & mode) + return(0); + } + return(-1); +} + +/* + * Return the mode bits of file + * If is null, then the previous stat buffer is used. + * The mode bits are zero if the file doesn't exist. + */ + +static int test_mode(register const char *file) +{ + struct stat statb; + if(file && (*file==0 || test_stat(file,&statb)<0)) + return(0); + return(statb.st_mode); +} + +/* + * do an fstat() for /dev/fd/n, otherwise stat() + */ +static int test_stat(const char *name,struct stat *buff) +{ + if(*name==0) + { + errno = ENOENT; + return(-1); + } + if(strmatch(name,(char*)e_devfdNN)) + return(fstat((int)strtol(name+8, (char**)0, 10),buff)); + else + return(stat(name,buff)); +} Index: src/lib/libshell/common/bltins/umask.c =================================================================== --- src/lib/libshell/common/bltins/umask.c (revision 0) +++ src/lib/libshell/common/bltins/umask.c (revision 740) @@ -0,0 +1,98 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * umask [-S] [mask] + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include +#include +#include +#include +#include +#include +#include "builtins.h" +#ifndef SH_DICT +# define SH_DICT "libshell" +#endif + +int b_umask(int argc,char *argv[],void *extra) +{ + register char *mask; + register int flag = 0, sflag = 0; + NOT_USED(extra); + while((argc = optget(argv,sh_optumask))) switch(argc) + { + case 'S': + sflag++; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + if(mask = *argv) + { + register int c; + if(isdigit(*mask)) + { + while(c = *mask++) + { + if (c>='0' && c<='7') + flag = (flag<<3) + (c-'0'); + else + errormsg(SH_DICT,ERROR_exit(1),e_number,*argv); + } + } + else + { + char *cp = mask; + flag = umask(0); + c = strperm(cp,&cp,~flag); + if(*cp) + { + umask(flag); + errormsg(SH_DICT,ERROR_exit(1),e_format,mask); + } + flag = (~c&0777); + } + umask(flag); + } + else + { + umask(flag=umask(0)); + if(sflag) + sfprintf(sfstdout,"%s\n",fmtperm(~flag&0777)); + else + sfprintf(sfstdout,"%0#4o\n",flag); + } + return(0); +} + Index: src/lib/libshell/common/bltins/shiocmd_solaris.c =================================================================== --- src/lib/libshell/common/bltins/shiocmd_solaris.c (revision 0) +++ src/lib/libshell/common/bltins/shiocmd_solaris.c (revision 740) @@ -0,0 +1,1180 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include +#include +#include +#include +#include +#include "name.h" +#undef nv_isnull +#ifndef SH_DICT +# define SH_DICT "libshell" +#endif +#include + +/* + * time formatting related +*/ +struct dctime +{ + Namfun_t fun; + Namval_t *format; + char buff[256]; /* Must be large enougth for |tmfmt()| */ +}; + +static char *get_time(Namval_t* np, Namfun_t* nfp) +{ + struct dctime *dp = (struct dctime*)nfp; + time_t t = nv_getn(np,nfp); + char *format = nv_getval(dp->format); + tmfmt(dp->buff,sizeof(dp->buff),format,(time_t*)0); + return(dp->buff); +} + +static void put_time(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + struct dctime *dp = (struct dctime*)nfp; + char *last; + if(val) + { + int32_t t; + if(flag&NV_INTEGER) + { + if(flag&NV_LONG) + t = *(Sfdouble_t*)val; + else + t = *(double*)val; + } + else + { + t = tmdate(val, &last, (time_t*)0); + if(*last) + errormsg(SH_DICT, ERROR_exit(1),"%s: invalid date/time string", val); + } + nv_putv(np, (char*)&t,NV_INTEGER, nfp); + } + else + { + nv_unset(dp->format); + free((void*)dp->format); + nv_putv(np, val, flag, nfp); + } +} + +static Namval_t *create_time(Namval_t *np, const char *name, int flags, Namfun_t *nfp) +{ + struct dctime *dp = (struct dctime*)nfp; + if(strcmp(name, "format")) + return((Namval_t*)0); + return(dp->format); +} + +static const Namdisc_t timedisc = +{ + sizeof(struct dctime), + put_time, + get_time, + 0, + 0, + create_time, +}; + + +static Namval_t *make_time(Namval_t* np) +{ + int offset = stktell(stkstd); + char *name = nv_name(np); + struct dctime *dp = newof(NULL,struct dctime,1,0); + if(!dp) + return((Namval_t*)0); + sfprintf(stkstd,"%s.format\0",name); + sfputc(stkstd,0); + dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); + dp->fun.disc = &timedisc; + nv_stack(np,&dp->fun); + return(np); +} + +/* + * mode formatting related +*/ +static char *get_mode(Namval_t* np, Namfun_t* nfp) +{ + mode_t mode = nv_getn(np,nfp); + return(fmtperm(mode)); +} + +static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + if(val) + { + int32_t mode; + char *last; + if(flag&NV_INTEGER) + { + if(flag&NV_LONG) + mode = *(Sfdouble_t*)val; + else + mode = *(double*)val; + } + else + { + mode = strperm(val, &last,0); + if(*last) + errormsg(SH_DICT, ERROR_exit(1),"%s: invalid mode string", val); + } + nv_putv(np,(char*)&mode,NV_INTEGER,nfp); + } + else + nv_putv(np,val,flag,nfp); +} + +static const Namdisc_t modedisc = +{ + 0, + put_mode, + get_mode, +}; + +static Namval_t *make_mode(Namval_t* np) +{ + char *name = nv_name(np); + Namfun_t *nfp = newof(NULL,Namfun_t,1,0); + if(!nfp) + return((Namval_t*)0); + nfp->disc = &modedisc; + nv_stack(np,nfp); + return(np); +} + +/* + * field related typese and functions + */ +typedef struct _field_ +{ + char *name; /* field name */ + int flags; /* flags */ + short offset; /* offset of field into data */ + short size; /* size of field */ + Namval_t *(*make)(Namval_t*); /* discipline constructor */ +} Shfield_t; + +/* + * lookup field in field table + */ +static Shfield_t *sh_findfield(Shfield_t *ftable, int nelem, const char *name) +{ + Shfield_t *fp = ftable; + register int i,n; + register const char *cp; + for(cp=name; *cp; cp++) + { + if(*cp=='.') + break; + } + n = cp-name; + for(i=0; i < nelem; i++,fp++) + { + if(memcmp(fp->name,name,n)==0 && fp->name[n]==0) + return(fp); + } + return(0); +} + +/* + * class types and functions + */ + +typedef struct _class_ +{ + int nelem; /* number of elements */ + int dsize; /* size for data structure */ + Shfield_t *fields; /* field description table */ +} Shclass_t; + +struct dcclass +{ + Namfun_t fun; + Shclass_t sclass; +}; + +static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np) +{ + char *val = np->nvalue + fp->offset; + char *name = nv_name(np); + register Namval_t *nq; + int offset = stktell(stkstd); + sfprintf(stkstd,"%s.%s\0",name,fp->name); + sfputc(stkstd,0); + nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); + if(fp->size<0) + val = *(char**)val; + nv_putval(nq,val,fp->flags|NV_NOFREE); + if(fp->make) + (*fp->make)(nq); + return(nq); +} + +static Namval_t *fieldcreate(Namval_t *np, const char *name, int flags, Namfun_t *nfp) +{ + struct dcclass *dcp = (struct dcclass*)nfp; + Shclass_t *sp = &dcp->sclass; + Shfield_t *fp = sh_findfield(sp->fields,sp->nelem,name); + Namval_t *nq,**nodes = (Namval_t**)(dcp+1); + int n = fp-sp->fields; + int len = strlen(fp->name); + void *data = (void*)np->nvalue; + if(!(nq=nodes[n])) + { + nodes[n] = nq = sh_newnode(fp,np); + nfp->last = ""; + } + if(name[len]==0) + return(nq); + return(nq); +} + +static void genvalue(Sfio_t *out, Shclass_t *sp, int indent, Namval_t *npar) +{ + Shfield_t *fp = sp->fields; + Namval_t *np, **nodes= (Namval_t**)(sp+1); + register int i,isarray; + if(out) + { + sfwrite(out,"(\n",2); + indent++; + } + for(i=0; i < sp->nelem; i++,fp++) + { +#if 0 + /* handle recursive case */ +#endif + if(!(np=nodes[i]) && out) + np = sh_newnode(fp,npar); + if(np) + { + isarray=0; + if(nv_isattr(np,NV_ARRAY)) + { + isarray=1; + if(array_elem(nv_arrayptr(np))==0) + isarray=2; + else + nv_putsub(np,(char*)0,ARRAY_SCAN); + } + sfnputc(out,'\t',indent); + sfputr(out,fp->name,(isarray==2?'\n':'=')); + if(isarray) + { + if(isarray==2) + continue; + sfwrite(out,"(\n",2); + sfnputc(out,'\t',++indent); + } + while(1) + { + char *fmtq; + if(isarray) + { + sfprintf(out,"[%s]",sh_fmtq(nv_getsub(np))); + sfputc(out,'='); + } + if(!(fmtq=nv_getval(np)) || !(fmtq=sh_fmtq(fmtq))) + fmtq = ""; + sfputr(out,fmtq,'\n'); + if(!nv_nextsub(np)) + break; + sfnputc(out,'\t',indent); + } + if(isarray) + { + sfnputc(out,'\t',--indent); + sfwrite(out,")\n",2); + } + } + } + if(out) + { + if(indent>1) + sfnputc(out,'\t',indent-1); + sfputc(out,')'); + } +} + +static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp) +{ + static Sfio_t *out; + Sfio_t *outfile; + int savtop = stktell(stkstd); + char *savptr = stkfreeze(stkstd,0); + if(dlete) + outfile = 0; + else if(!(outfile=out)) + outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); + else + sfseek(outfile,0L,SEEK_SET); + genvalue(outfile,&dcp->sclass,0,np); + stkset(stkstd,savptr,savtop); + if(!outfile) + return((char*)0); + sfputc(out,0); + return((char*)out->_data); +} + +static char *get_classval(Namval_t* np, Namfun_t* nfp) +{ + return(walk_class(np,0,(struct dcclass *)nfp)); +} + +static void put_classval(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + walk_class(np,1,(struct dcclass *)nfp); + if(nfp = nv_stack(np,(Namfun_t*)0)) + { + free((void*)nfp); + if(np->nvalue && !nv_isattr(np,NV_NOFREE)) + free((void*)np->nvalue); + } + if(val) + nv_putval(np,val,flag); +} + +static const Namdisc_t classdisc = +{ + sizeof(struct dcclass), + put_classval, + get_classval, + 0, + 0, + fieldcreate +}; + +static int mkclass(Namval_t *np, Shclass_t *sp) +{ + struct dcclass *tcp = newof(NULL,struct dcclass,1,sp->nelem*sizeof(Namval_t*)); + if(!tcp) + return(0); + memset((void*)(tcp+1),0,sp->nelem*sizeof(Namval_t*)); + tcp->fun.disc = &classdisc; + tcp->sclass = *sp; + np->nvalue = (char*)calloc(sp->dsize,1); + nv_stack(np,&tcp->fun); + return(1); +} + +/* + * ====================from here down is file class specific + */ +static struct stat *Sp; + +struct filedata +{ + struct stat statb; + int fd; + char *name; +}; + +static Shfield_t filefield[] = +{ + { "atime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_atime), sizeof(Sp->st_atime), make_time}, + { "ctime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ctime), sizeof(Sp->st_ctime), make_time}, + { "dev", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_dev),sizeof(Sp->st_dev)}, + { "fd", NV_INTEGER|NV_RDONLY, offsetof(struct filedata,fd), sizeof(int)}, + { "gid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_gid), sizeof(Sp->st_gid)}, + { "ino", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ino), sizeof(Sp->st_ino)}, + { "mode", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mode), sizeof(Sp->st_mode), make_mode}, + { "mtime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mtime), sizeof(Sp->st_mtime), make_time}, + { "name", NV_RDONLY, offsetof(struct filedata,name), -1 }, + { "nlink", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_nlink), sizeof(Sp->st_nlink)}, + { "size", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_size), sizeof(Sp->st_size)}, + { "uid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_uid), sizeof(Sp->st_uid)} +}; + +static Shclass_t Fileclass = +{ + sizeof(filefield)/sizeof(*filefield), + sizeof(struct filedata), + filefield +}; + + +#define letterbit(bit) (1<<((bit)-'a')) + +static const char sh_optopen[] = +"[-?\n@(#)$Id: open (AT&T Labs Research) 2007-05-07 $\n]" +"[-author?David Korn ]" +"[-author?Roland Mainz ]" +"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" +"[+NAME? open - create a shell variable correspnding to a file]" +"[+DESCRIPTION?\bopen\b creates the compound variable \avar\a correspinding " + "to the file given by the pathname \afile\a. The elements of \avar\a " + "are the names of elements in the \astat\a structure with the \bst_\b " + "prefix removed.]" +"[+?\afile\a is opened (based on \b-r\b and/or \b-w\b) and the variable " + "\avar\a\b.fd\b is the file descriptor.]" +"[a:append?Open for append.]" +"[b:binary?Open in binary mode" +#ifndef O_BINARY + " (not supported/ignored on this platform)" +#endif + ".]" +"[t:text?Open in text mode" +#ifndef O_TEXT + " (not supported/ignored on this platform)" +#endif + ".]" +"[c:create?Open for create.]" +"[i:inherit?Open without the close-on-exec bit set.]" +"[I:noinherit?Open with the close-on-exec bit set.]" +"[r:read?Open with read access.]" +"[w:write?Open with write access.]" +"[m:mode]:[mode:=rwrwrw?Open with access mode \amode\a.]" +"[x:exclusive?Open exclusive.]" + +"[N:nofollow?If the path names a symbolic link, open fails with ELOOP " +#ifndef O_NOFOLLOW + " (not supported/ignored on this platform)" +#endif + ".]" +"[S:sync?Write I/O operations on the file descriptor complete as " + "defined by synchronized I/O file integrity completion" +#ifndef O_SYNC + " (not supported/ignored on this platform)" +#endif + ".]" +"[T:trunc?If the file exists and is a regular file, and the file " + "is successfully opened read/write or write-only, its length is " + "truncated to 0 and the mode and owner are unchanged. It " + "has no effect on FIFO special files or terminal device " + "files. Its effect on other file types is " + "implementation-dependent. The result of using -T " + "with read-only files is undefined" +#ifndef O_TRUNC + " (not supported/ignored on this platform)" +#endif + ".]" +"\n" +"\nvar file\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bpoll\b(1),\bstat\b(2)]" +; + + +extern int b_open(int argc, char *argv[], void *extra) +{ + register Namval_t *np; + register int n,oflag=0; + Shell_t *shp = (Shell_t*)extra; + struct filedata *fdp; + mode_t mode = 0666; + long flags = 0; + int fd = -1; + char *arg; + + while (n = optget(argv, sh_optopen)) switch (n) + { + case 'r': + case 'w': + case 'i': + flags |= letterbit(n); + break; + case 'I': + flags &= ~(letterbit('i')); + break; + case 'b': +#ifdef O_BINARY + oflag |= O_BINARY; +#endif + break; + case 't': +#ifdef O_TEXT + oflag |= O_TEXT; +#endif + break; + case 'N': +#ifdef O_NOFOLLOW + oflag |= O_NOFOLLOW; +#endif + break; + case 'T': +#ifdef O_TRUNC + oflag |= O_TRUNC; +#endif + break; + case 'x': + oflag |= O_EXCL; + break; + case 'c': + oflag |= O_CREAT; + break; + case 'a': + oflag |= O_APPEND; + break; + case 'S': +#ifdef O_SYNC + oflag |= O_SYNC; +#endif + break; + case 'm': + mode = strperm(arg = opt_info.arg, &opt_info.arg, mode); + if (*opt_info.arg) + errormsg(SH_DICT, ERROR_system(1), "%s: invalid mode", arg); + break; + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=2 || !(flags&(letterbit('r')|letterbit('w')))) + errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); + + if(flags&letterbit('r')) + { + if(flags&letterbit('w')) + oflag |= O_RDWR; + else + oflag |= O_RDONLY; + } + else if(flags&letterbit('w')) + oflag |= O_WRONLY; + + fd = sh_open(argv[1], oflag, mode); + if(fd<0) + errormsg(SH_DICT, ERROR_system(1), "%s: open failed", argv[1]); + + if(!(flags&letterbit('i'))) + fcntl(fd, F_SETFL, 0); + + np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN); + if(!nv_isnull(np)) + nv_unset(np); + mkclass(np, &Fileclass); + fdp = (struct filedata*)np->nvalue; + fstat(fd, &fdp->statb); + fdp->fd = fd; + fdp->name = strdup(argv[1]); + return(0); +} + +static const char sh_optclose[] = +"[-?\n@(#)$Id: close (AT&T Labs Research) 2007-04-21 $\n]" +"[-author?Roland Mainz ]" +"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" +"[+NAME? close - close a file descriptor]" +"[+DESCRIPTION?\bclose\b closes the file descriptor specified by fd.]" +"\n" +"\nfd\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bopen\b(1),\bdup\b(1),\btmpfile\b(1),\bpoll\b(1),\bstat\b(1)]" +; + +extern int b_close(int argc, char *argv[], void *extra) +{ + register int n=0; + int fd = -1; + + while (n = optget(argv, sh_optclose)) switch (n) + { + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=1) + errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); + + errno = 0; + fd = strtol(argv[0], (char **)NULL, 0); + if (errno != 0 || fd < 0) + errormsg(SH_DICT, ERROR_system(1), "%s: invalid descriptor", argv[0]); + + n = sh_close(fd); + + if (n < 0) + errormsg(SH_DICT, ERROR_system(1), "%s: close error", argv[0]); + + return(n==0?0:1); +} + + +static const char sh_opttmpfile[] = +"[-?\n@(#)$Id: tmpfile (AT&T Labs Research) 2007-05-07 $\n]" +"[-author?Roland Mainz ]" +"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" +"[+NAME? tmpfile - create a shell variable correspnding to a temporary file]" +"[+DESCRIPTION?\btmpfile\b creates the compound variable \avar\a correspinding " + "to a temporary file. The elements of \avar\a " + "are the names of elements in the \astat\a structure with the \bst_\b " + "prefix removed.]" +"[i:inherit?Open without the close-on-exec bit set.]" +"[I:noinherit?Open with the close-on-exec bit set.]" +"\n" +"\nvar\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bopen\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]" +; + + +extern int b_tmpfile(int argc, char *argv[], void *extra) +{ + register Namval_t *np; + register int n; + Shell_t *shp = (Shell_t*)extra; + struct filedata *fdp; + int inherit = 0; + FILE *file = NULL; + int ffd, fd = -1; + while (n = optget(argv, sh_opttmpfile)) switch (n) + { + case 'i': + inherit = 1; + break; + case 'I': + inherit = 0; + break; + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=1) + errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); + + file = tmpfile(); + if(!file) + errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]); + ffd = fileno(file); + fd = sh_dup(ffd); + if(fd<0) + errormsg(SH_DICT, ERROR_system(1), "%s: tmpfile failed", argv[1]); + fclose(file); + + if(!inherit) + fcntl(fd, F_SETFL, 0); + + np = nv_open(argv[0], shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN); + if(!nv_isnull(np)) + nv_unset(np); + mkclass(np,&Fileclass); + fdp = (struct filedata*)np->nvalue; + + fstat(fd, &fdp->statb); + fdp->fd = fd; + fdp->name = NULL; + return(0); +} + +static const char sh_optdup[] = +"[-?\n@(#)$Id: dup (AT&T Labs Research) 2007-05-07 $\n]" +"[-author?Roland Mainz ]" +"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" +"[+NAME? dup - duplicate an open file descriptor]" +"[+DESCRIPTION?The \bdup\b commands returns a new file descriptor having the " + "following in common with the original open file descriptor " + "fd: same open file (or pipe), same file pointer (that is, both file descriptors " + "share one file pointer) same access mode (read, write or read/write). " + "The file descriptor returned is the lowest one available.]" +"[i:inherit?Open without the close-on-exec bit set.]" +"[I:noinherit?Open with the close-on-exec bit set.]" +"\n" +"\nvar fd\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(1)]" +; + + +extern int b_dup(int argc, char *argv[], void *extra) +{ + register Namval_t *np; + register int n; + Shell_t *shp = (Shell_t*)extra; + struct filedata *fdp; + int inherit = 0; + int ffd, fd = -1; + while (n = optget(argv, sh_optdup)) switch (n) + { + case 'i': + inherit = 1; + break; + case 'I': + inherit = 0; + break; + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=2) + errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); + + errno = 0; + ffd = strtol(argv[1], (char **)NULL, 0); + if (errno != 0 || ffd < 0) + errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[1]); + + fd = sh_dup(ffd); + if(fd<0) + errormsg(SH_DICT, ERROR_system(1), "%s: dup failed", argv[1]); + + if(!inherit) + fcntl(fd,F_SETFL,0); + + np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); + if(!nv_isnull(np)) + nv_unset(np); + mkclass(np, &Fileclass); + fdp = (struct filedata*)np->nvalue; + + fstat(fd, &fdp->statb); + fdp->fd = fd; + fdp->name = NULL; + return(0); +} + +static const char sh_optstat[] = +"[-?\n@(#)$Id: stat (AT&T Labs Research) 2007-05-07 $\n]" +"[-author?David Korn ]" +"[-author?Roland Mainz ]" +"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" +"[+NAME? stat - get file status]" +"[+DESCRIPTION?\bstat\b creates the compound variable \avar\a correspinding " + "to the file given by the pathname \afile\a. The elements of \avar\a " + "are the names of elements in the \astat\a structure with the \bst_\b " + "prefix removed.]" +"[l:lstat?If the the named file is a symbolic link returns information about " + "the link itself.]" +"\n" +"\nvar file\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(1),\bstat\b(2),\blstat\b(2)]" +; + + +extern int b_stat(int argc, char *argv[], void *extra) +{ + register Namval_t *np; + register int n; + Shell_t *shp = (Shell_t*)extra; + struct filedata *fdp; + long flags = 0; + struct stat statb; + while (n = optget(argv, sh_optstat)) switch (n) + { + case 'l': + flags |= letterbit(n); + break; + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=2) + errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); + + if(flags&letterbit('l')) + { + if(lstat(argv[1], &statb) < 0) + errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]); + } + else + { + if(stat(argv[1], &statb) < 0) + errormsg(SH_DICT, ERROR_system(1), "%s: stat failed", argv[1]); + + } + + np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); + if(!nv_isnull(np)) + nv_unset(np); + mkclass(np,&Fileclass); + fdp = (struct filedata*)np->nvalue; + fdp->statb = statb; + fdp->fd = -1; + fdp->name = strdup(argv[1]); + return(0); +} + +static const char sh_optpoll[] = +"[-?\n@(#)$Id: poll (AT&T Labs Research) 2007-05-07 $\n]" +"[-author?Roland Mainz 0) may be " + "written. This event only examines bands " + "that have been written to at least once.]" + "[+POLLERR?An error has occurred on the device or " + "stream. This flag is only valid in the " + "revents bitmask; it is not used in the " + "events member.]" + "[+POLLHUP?A hangup has occurred on the stream. This " + "event and POLLOUT are mutually exclusive; a " + "stream can never be writable if a hangup has " + "occurred. However, this event and POLLIN, " + ", POLLRDBAND, or POLLPRI are not " + "mutually exclusive. This flag is only valid " + "in the revents bitmask; it is not used in " + "the events member.]" + "[+POLLNVAL?The specified fd value does not belong to an " + "open file. This flag is only valid in the " + "revents member; it is not used in the events " + "member.]" + "}" +"]" + +"[+?If the value fd is less than 0, events is ignored and " + "revents is set to 0 in that entry on return from poll.]" + +"[+?The results of the poll query are stored in the revents " + "member in the \bvar\b structure. POLL*-strings are set in the \brevents\b " + "variable to indicate which of the requested events are true. " + "If none are true, the \brevents\b will be an empty string when " + "the poll command returns. The event flags " + "POLLHUP, POLLERR, and POLLNVAL are always set in \brevents\b " + "if the conditions they indicate are true; this occurs even " + "though these flags were not present in events.]" + +"[+?If none of the defined events have occurred on any selected " + "file descriptor, poll waits at least timeout milliseconds " + "for an event to occur on any of the selected file descriptors. " + "On a computer where millisecond timing accuracy is not " + "available, timeout is rounded up to the nearest legal value " + "available on that system. If the value timeout is 0, poll " + "returns immediately. If the value of timeout is -1, poll " + "blocks until a requested event occurs or until the call is " + "interrupted.]" + +"[+?The poll function supports regular files, terminal and " + "pseudo-terminal devices, STREAMS-based files, FIFOs and " + "pipes. The behavior of poll on elements of fds that refer " + "to other types of file is unspecified.]" + +"[+?The poll function supports sockets.]" + +"[+?A file descriptor for a socket that is listening for connections " + "will indicate that it is ready for reading, once connections " + "are available. A file descriptor for a socket that " + "is connecting asynchronously will indicate that it is ready " + "for writing, once a connection has been established.]" + +"[+?Regular files always poll TRUE for reading and writing.]" + +"[t:timeout]:[milliseconds?Timeout in milliseconds. If the value timeout is 0, " + "poll returns immediately. If the value of timeout is -1, poll " + "blocks until a requested event occurs or until the call is " + "interrupted.]" +"\n" +"\nvar\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(2)]" +; + +/* + * |mystpcpy| - like |strcpy()| but returns the end of the buffer + * + * Copy string s2 to s1. s1 must be large enough. + * return s1-1 (position of string terminator ('\0') in destnation buffer). + */ +static +char *mystpcpy(char *s1, const char *s2) +{ + while (*s1++ = *s2++) + ; + return (s1-1); +} + +static +Namval_t *nv_open_fmt(Dt_t *dict, int flags, const char *namefmt, ...) +{ + char varnamebuff[PATH_MAX]; + va_list ap; + + va_start(ap, namefmt); + vsnprintf(varnamebuff, sizeof(varnamebuff), namefmt, ap); + va_end(ap); + + return nv_open(varnamebuff, dict, flags); +} + +static +int poll_strtoevents(const char *str) +{ + int events = 0; + + if (strstr(str, "POLLIN")) events |= POLLIN; + if (strstr(str, "POLLRDNORM")) events |= POLLRDNORM; + if (strstr(str, "POLLRDBAND")) events |= POLLRDBAND; + if (strstr(str, "POLLPRI")) events |= POLLPRI; + if (strstr(str, "POLLOUT")) events |= POLLOUT; + if (strstr(str, "POLLWRNORM")) events |= POLLWRNORM; + if (strstr(str, "POLLWRBAND")) events |= POLLWRBAND; + if (strstr(str, "POLLERR")) events |= POLLERR; + if (strstr(str, "POLLHUP")) events |= POLLHUP; + if (strstr(str, "POLLNVAL")) events |= POLLNVAL; + + return events; +} + + +static +void poll_eventstostr(char *s, int events) +{ + *s='\0'; + if (!events) + return; + + if (events & POLLIN) s=mystpcpy(s, "POLLIN|"); + if (events & POLLRDNORM) s=mystpcpy(s, "POLLRDNORM|"); + if (events & POLLRDBAND) s=mystpcpy(s, "POLLRDBAND|"); + if (events & POLLPRI) s=mystpcpy(s, "POLLPRI|"); + if (events & POLLOUT) s=mystpcpy(s, "POLLOUT|"); + if (events & POLLWRNORM) s=mystpcpy(s, "POLLWRNORM|"); + if (events & POLLWRBAND) s=mystpcpy(s, "POLLWRBAND|"); + if (events & POLLERR) s=mystpcpy(s, "POLLERR|"); + if (events & POLLHUP) s=mystpcpy(s, "POLLHUP|"); + if (events & POLLNVAL) s=mystpcpy(s, "POLLNVAL|"); + + /* Remove trailling '|' */ + s--; + if(*s=='|') + *s='\0'; +} + +extern int b_poll(int argc, char *argv[], void *extra) +{ + register Namval_t *np; + register int n; + Shell_t *shp = (Shell_t*)extra; + char *varname; + int fd; +/* |BPOLL_MAX| needs to be larger than |OPEN_MAX| to make sure we + * can listen to different sets of events per fd. + */ +#define BPOLL_MAX 512 + struct pollfd pollfd[BPOLL_MAX]; + unsigned int numpollfd = 0; + int i; + char *s; + long timeout = -1; + char buff[256]; + + while (n = optget(argv, sh_optpoll)) switch (n) + { + case 't': + errno = 0; + timeout = strtol(opt_info.arg, (char **)NULL, 0); + if (errno != 0) + errormsg(SH_DICT, ERROR_system(1), "%s: invalid timeout", opt_info.arg); + break; + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=1) + errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); + + varname = argv[0]; + + for(i=0 ; i < BPOLL_MAX ; i++) + { + np = nv_open_fmt(shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN|NV_NOFAIL|NV_NOADD, "%s[%d].fd", varname, i); + if (!np) + break; + fd = (int)nv_getnum(np); + if (fd < 0 || fd > OPEN_MAX) + errormsg(SH_DICT, ERROR_system(1), "poll: invalid pollfd fd"); + nv_close(np); + pollfd[i].fd = fd; + + np = nv_open_fmt(shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN|NV_NOFAIL|NV_NOADD, "%s[%d].events", varname, i); + if (!s) + errormsg(SH_DICT, ERROR_system(1), "poll: missing pollfd events"); + + s = nv_getval(np); + if (!s) + errormsg(SH_DICT, ERROR_system(1), "poll: missing pollfd events value"); + pollfd[i].events = poll_strtoevents(s); + nv_close(np); + + pollfd[i].revents = 0; + + numpollfd++; + } + + if (i == BPOLL_MAX) + errormsg(SH_DICT, ERROR_system(1), "poll: cannot handle more than %d entries.", BPOLL_MAX); + + n = poll(pollfd, numpollfd, timeout); + /* FixMe: EGAIN and EINTR may require extra handling */ + if (n < 0) + errormsg(SH_DICT, ERROR_system(1), "poll: failure"); + + for(i=0 ; i < numpollfd ; i++) + { + np = nv_open_fmt(shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN|NV_NOFAIL, "%s[%d].revents", varname, i); + if (!np) + errormsg(SH_DICT, ERROR_system(1), "poll: couldn't create pollfd %s[%d].revents", varname, i); + + poll_eventstostr(buff, pollfd[i].revents); + + nv_putval(np, buff, 0); + nv_close(np); + } + + return(0); +} + +static const char sh_optrewind[] = +"[-?\n@(#)$Id: rewind (AT&T Labs Research) 2007-05-07 $\n]" +"[-author?Roland Mainz ]" +"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" +"[+NAME? rewind - reset file position indicator in a stream]" +"[+DESCRIPTION?The \brewind\b command will move the file pointer of fd to position 0.]" +"\n" +"\nfd\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bstat\b(1),\bstat\b(2)]" +; + + +extern int b_rewind(int argc, char *argv[], void *extra) +{ + Shell_t *shp = (Shell_t*)extra; + int fd = -1; + register int n; + while (n = optget(argv, sh_optrewind)) switch (n) + { + case ':': + errormsg(SH_DICT, 2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=1) + errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); + + errno = 0; + fd = strtol(argv[0], (char **)NULL, 0); + if (errno != 0 || fd < 0) + errormsg(SH_DICT, ERROR_system(1), "%s: invalid fd", argv[0]); + + if (sh_seek(fd, 0, SEEK_SET) == (off_t)-1) + errormsg(SH_DICT, ERROR_system(1), "seek error"); + + return(0); +} + Index: src/lib/libshell/common/bltins/getopts.c =================================================================== --- src/lib/libshell/common/bltins/getopts.c (revision 0) +++ src/lib/libshell/common/bltins/getopts.c (revision 740) @@ -0,0 +1,185 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * getopts optstring name [arg...] + * + * David Korn + * AT&T Labs + * research!dgk + * + */ + +#include "defs.h" +#include "variables.h" +#include +#include +#include "builtins.h" + +static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) +{ + if(nv_search(s,sh.fun_tree,0)) + { + int savtop = staktell(); + char *savptr = stakfreeze(0); + stakputc('$'); + stakputc('('); + stakputs(s); + stakputc(')'); + sfputr(sp,sh_mactry(stakfreeze(1)),-1); + stakset(savptr,savtop); + } + return(1); +} + +int b_getopts(int argc,char *argv[],void *extra) +{ + register char *options=error_info.context->id; + register Namval_t *np; + register int flag, mode, r=0; + register Shell_t *shp = (Shell_t*)extra; + char value[2], key[2]; + int jmpval; + struct checkpt buff, *pp; + Optdisc_t disc; + memset(&disc, 0, sizeof(disc)); + disc.version = OPT_VERSION; + disc.infof = infof; + value[1] = 0; + key[1] = 0; + while((flag = optget(argv,sh_optgetopts))) switch(flag) + { + case 'a': + options = opt_info.arg; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + argc -= opt_info.index; + if(error_info.errors || argc<2) + errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); + error_info.context->flags |= ERROR_SILENT; + error_info.id = options; + options = argv[0]; + np = nv_open(argv[1],shp->var_tree,NV_NOASSIGN|NV_VARNAME); + if(argc>2) + { + argv +=1; + argc -=1; + } + else + { + argv = shp->st.dolv; + argc = shp->st.dolc; + } + opt_info.index = shp->st.optindex; + opt_info.offset = shp->st.optchar; + if(mode= (*options==':')) + options++; + sh_pushcontext(&buff,1); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval) + { + sh_popcontext(&buff); + pp = (struct checkpt*)shp->jmplist; + pp->mode = SH_JMPERREXIT; + sh_exit(2); + } + opt_info.disc = &disc; + switch(opt_info.index>=0 && opt_info.index<=argc?(opt_info.num= LONG_MIN,flag=optget(argv,options)):0) + { + case '?': + if(mode==0) + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + opt_info.option[1] = '?'; + /* FALL THRU */ + case ':': + key[0] = opt_info.option[1]; + if(strmatch(opt_info.arg,"*unknown*")) + flag = '?'; + if(mode) + opt_info.arg = key; + else + { + errormsg(SH_DICT,2, "%s", opt_info.arg); + opt_info.arg = 0; + flag = '?'; + } + *(options = value) = flag; + shp->st.opterror = 1; + if (opt_info.offset != 0 && !argv[opt_info.index][opt_info.offset]) + { + opt_info.offset = 0; + opt_info.index++; + } + break; + case 0: + if(shp->st.opterror) + { + char *com[2]; + com[0] = "-?"; + com[1] = 0; + flag = opt_info.index; + opt_info.index = 0; + optget(com,options); + opt_info.index = flag; + if(!mode && strchr(options,' ')) + errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); + } + opt_info.arg = 0; + options = value; + *options = '?'; + r=1; + opt_info.offset = 0; + break; + default: + options = opt_info.option + (*opt_info.option!='+'); + } + error_info.context->flags &= ~ERROR_SILENT; + shp->st.optindex = opt_info.index; + shp->st.optchar = opt_info.offset; + nv_putval(np, options, 0); + nv_close(np); + np = nv_open(nv_name(OPTARGNOD),shp->var_tree,NV_NOSCOPE); + if(opt_info.num == LONG_MIN) + nv_putval(np, opt_info.arg, NV_RDONLY); + else if (opt_info.num > 0 && opt_info.arg && opt_info.arg[0] == (char)opt_info.num) + { + key[0] = (char)opt_info.num; + key[1] = 0; + nv_putval(np, key, NV_RDONLY); + } + else + { + Sfdouble_t d; + d = opt_info.number; + nv_putval(np, (char*)&d, NV_LDOUBLE|NV_RDONLY); + } + nv_close(np); + sh_popcontext(&buff); + opt_info.disc = 0; + return(r); +} + Index: src/lib/libshell/common/bltins/mkservice.c =================================================================== --- src/lib/libshell/common/bltins/mkservice.c (revision 0) +++ src/lib/libshell/common/bltins/mkservice.c (revision 740) @@ -0,0 +1,494 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * mkservice varname pathname + * eloop [-t timeout] + * Written by David Korn + * AT&T Labs + */ + +static const char mkservice_usage[] = +"[-?\n@(#)$Id: mkservice (AT&T Research) 2001-06-13 $\n]" +USAGE_LICENSE +"[+NAME? mkservice - create a shell server ]" +"[+DESCRIPTION?\bmkservice\b creates a tcp or udp server that is " + "implemented by shell functions.]" +"[+?The \aservice_path\a must be of the form \b/dev/tcp/localhost/\b\aportno\a " + "or \b/dev/udp/localhost/\b\aportno\a depending on whether the " + "\btcp\b or \budp\b protocol is used. \aportno\a is the port " + "number that the service will use.]" +"[+?The shell variable \avarname\a is associated with the service. This " + "variable can have subvariables that keeps the state of all " + "active connections. The functions \avarname\a\b.accept\b, " + "\avarname\a\b.action\b and \avarname\a\b.close\b implement the " + "service as follows:]{" + "[+accept?This function is invoked when a client tries to connect " + "to the service. It is called with an argument which " + "is the file descriptor number associated with the " + "accepted connection. If the function returns a non-zero " + "value, this connection will be closed.]" + "[+action?This function is invoked when there is data waiting " + "to be read from one of the active connections. It is " + "called with the file descriptor number that has data " + "to be read. If the function returns a non-zero " + "value, this connection will be closed.]" + "[+close?This function is invoked when the connection is closed.]" + "}" +"[+?If \avarname\a is unset, then all active connection, and the service " + "itself will be closed.]" +"" +"\n" +"\nvarname service_path\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\beloop\b(1)]" +; + + +static const char eloop_usage[] = +"[-?\n@(#)$Id: eloop (AT&T Research) 2001-06-13 $\n]" +USAGE_LICENSE +"[+NAME? eloop - process event loop]" +"[+DESCRIPTION?\beloop\b causes the shell to block waiting for events " + "to process. By default, \beloop\b does not return.]" +"[t]#[timeout?\atimeout\a is the number of milliseconds to wait " + "without receiving any events to process.]" +"\n" +"\n\n" +"\n" +"[+EXIT STATUS?If no timeout is specified, \beloop\b will not return " + "unless interrupted. Otherwise]{" + "[+0?The specified timeout interval occurred.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bmkservice\b(1)]" +; + + +#include "defs.h" + +#include +#include +#include +#include +#include + +#define ACCEPT 0 +#define ACTION 1 +#define CLOSE 2 + +#ifndef O_SERVICE +# define O_SERVICE O_NOCTTY +#endif + +static const char* disctab[] = +{ + "accept", + "action", + "close", + 0 +}; + +typedef struct Service_s Service_t; + +struct Service_s +{ + Namfun_t fun; + short fd; + int refcount; + int (*acceptf)(Service_t*,int); + int (*actionf)(Service_t*,int,int); + int (*errorf)(Service_t*,int,const char*, ...); + void *context; + Namval_t* node; + Namval_t* disc[elementsof(disctab)-1]; +}; + +static short *file_list; +static Sfio_t **poll_list; +static Service_t **service_list; +static int npoll; +static int nready; +static int ready; +static int (*covered_fdnotify)(int, int); + +static int fdclose(Service_t *sp, register int fd) +{ + register int i; + service_list[fd] = 0; + if(sp->fd==fd) + sp->fd = -1; + for(i=0; i < npoll; i++) + { + if(file_list[i]==fd) + { + file_list[i] = file_list[npoll--]; + if(sp->actionf) + (*sp->actionf)(sp, fd, 1); + return(1); + } + } + return(0); +} + +static int fdnotify(int fd1, int fd2) +{ + Service_t *sp; + if (covered_fdnotify) + (*covered_fdnotify)(fd1, fd2); + if(fd2!=SH_FDCLOSE) + { + register int i; + service_list[fd2] = service_list[fd1]; + service_list[fd1] = 0; + for(i=0; i < npoll; i++) + { + if(file_list[i]==fd1) + { + file_list[i] = fd2; + return(0); + } + } + } + else if(sp = service_list[fd1]) + { + fdclose(sp,fd1); + if(--sp->refcount==0) + nv_unset(sp->node); + } + return(0); +} + +static void process_stream(Sfio_t* iop) +{ + int r=0, fd = sffileno(iop); + Service_t * sp = service_list[fd]; + if(fd==sp->fd) /* connection socket */ + { + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + fd = accept(fd, &addr, &addrlen); + service_list[fd] = sp; + sp->refcount++; + file_list[npoll++] = fd; + if(fd>=0) + { + if(sp->acceptf) + r = (*sp->acceptf)(sp,fd); + } + } + else if(sp->actionf) + { + service_list[fd] = 0; + r = (*sp->actionf)(sp, fd, 0); + service_list[fd] = sp; + if(r<0) + close(fd); + } +} + +static int waitnotify(int fd, long timeout, int rw) +{ + Sfio_t *special=0, **pstream; + register int i; + + if (fd >= 0) + special = sh_fd2sfio(fd); + while(1) + { + pstream = poll_list; + while(ready < nready) + process_stream(pstream[ready++]); + if(special) + *pstream++ = special; + for(i=0; i < npoll; i++) + { + if(service_list[file_list[i]]) + *pstream++ = sh_fd2sfio(file_list[i]); + } +#if 1 + for(i=0; i < pstream-poll_list; i++) + sfset(poll_list[i],SF_WRITE,0); +#endif + nready = ready = 0; + errno = 0; +#ifdef DEBUG + sfprintf(sfstderr,"before poll npoll=%d",pstream-poll_list); + for(i=0; i < pstream-poll_list; i++) + sfprintf(sfstderr," %d",sffileno(poll_list[i])); + sfputc(sfstderr,'\n'); +#endif + nready = sfpoll(poll_list,pstream-poll_list,timeout); +#ifdef DEBUG + sfprintf(sfstderr,"after poll nready=%d",nready); + for(i=0; i < nready; i++) + sfprintf(sfstderr," %d",sffileno(poll_list[i])); + sfputc(sfstderr,'\n'); +#endif +#if 1 + for(i=0; i < pstream-poll_list; i++) + sfset(poll_list[i],SF_WRITE,1); +#endif + if(nready<=0) + return(errno? -1: 0); + if(special && poll_list[0]==special) + { + ready = 1; + return(fd); + } + } +} + +static int service_init(void) +{ + file_list = newof(NULL,short,n,0); + poll_list = newof(NULL,Sfio_t*,n,0); + service_list = newof(NULL,Service_t*,n,0); + covered_fdnotify = sh_fdnotify(fdnotify); + sh_waitnotify(waitnotify); + return(1); +} + +void service_add(Service_t *sp) +{ + static int init; + if (!init) + init = service_init(); + service_list[sp->fd] = sp; + file_list[npoll++] = sp->fd; +} + +static int Accept(register Service_t *sp, int accept_fd) +{ + register Namval_t* nq = sp->disc[ACCEPT]; + int fd; + + fd = fcntl(accept_fd, F_DUPFD, 10); + if (fd >= 0) + { + close(accept_fd); + if (nq) + { + char* av[3]; + char buff[20]; + + av[1] = buff; + av[2] = 0; + sfsprintf(buff, sizeof(buff), "%d", fd); + if (sh_fun(nq, sp->node, av)) + { + close(fd); + return -1; + } + } + } + sfsync(NiL); + return fd; +} + +static int Action(Service_t *sp, int fd, int close) +{ + register Namval_t* nq; + int r=0; + + if(close) + nq = sp->disc[CLOSE]; + else + nq = sp->disc[ACTION]; + if (nq) + { + char* av[3]; + char buff[20]; + + av[1] = buff; + av[2] = 0; + sfsprintf(buff, sizeof(buff), "%d", fd); + r=sh_fun(nq, sp->node, av); + } + sfsync(NiL); + return r > 0 ? -1 : 1; +} + +static int Error(Service_t *sp, int level, const char* arg, ...) +{ + va_list ap; + + va_start(ap, arg); + if(sp->node) + nv_unset(sp->node); + free((void*)sp); + errorv(NiL, ERROR_exit(1), ap); + va_end(ap); + return 0; +} + +static char* setdisc(Namval_t* np, const char* event, Namval_t* action, Namfun_t* fp) +{ + register Service_t* sp = (Service_t*)fp; + register const char* cp; + register int i; + register int n = strlen(event) - 1; + register Namval_t* nq; + + for (i = 0; cp = disctab[i]; i++) + { + if (memcmp(event, cp, n)) + continue; + if (action == np) + action = sp->disc[i]; + else + { + if (nq = sp->disc[i]) + free((void*)nq); + if (action) + sp->disc[i] = action; + else + sp->disc[i] = 0; + } + return action ? (char*)action : ""; + } + /* try the next level */ + return nv_setdisc(np, event, action, fp); +} + +static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp) +{ + register Service_t* sp = (Service_t*)fp; + if (!val) + fp = nv_stack(np, NiL); + nv_putv(np, val, flag, fp); + if (!val) + { + register int i; + for(i=0; i< sh.lim.open_max; i++) + { + if(service_list[i]==sp) + { + close(i); + if(--sp->refcount<=0) + break; + } + } + free((void*)fp); + return; + } +} + +static const Namdisc_t servdisc = +{ + sizeof(Service_t), + putval, + 0, + 0, + setdisc +}; + +int b_mkservice(int argc, char** argv, void* extra) +{ + register char* var; + register char* path; + register Namval_t* np; + register Service_t* sp; + register int fd; + + NOT_USED(argc); + NOT_USED(extra); + for (;;) + { + switch (optget(argv, mkservice_usage)) + { + case 0: + break; + case ':': + error(2, opt_info.arg); + continue; + case '?': + error(ERROR_usage(2), opt_info.arg); + continue; + } + break; + } + argv += opt_info.index; + if (error_info.errors || !(var = *argv++) || !(path = *argv++) || *argv) + error(ERROR_usage(2), optusage(NiL)); + if (!(sp = newof(0, Service_t, 1, 0))) + error(ERROR_exit(1), "out of space"); + sp->acceptf = Accept; + sp->actionf = Action; + sp->errorf = Error; + sp->refcount = 1; + sp->context = extra; + sp->node = 0; + sp->fun.disc = &servdisc; + if((fd = sh_open(path, O_SERVICE|O_RDWR))<=0) + { + free((void*)sp); + error(ERROR_exit(1), "%s: cannot start service", path); + } + if((sp->fd = fcntl(fd, F_DUPFD, 10))>=10) + close(fd); + else + sp->fd = fd; + np = nv_open(var,sh.var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); + sp->node = np; + nv_putval(np, path, 0); + nv_stack(np, (Namfun_t*)sp); + service_add(sp); + return(0); +} + +int b_eloop(int argc, char** argv, void* extra) +{ + register long timeout = -1; + NOT_USED(argc); + NOT_USED(extra); + for (;;) + { + switch (optget(argv, eloop_usage)) + { + case 0: + break; + case 't': + timeout = opt_info.num; + continue; + case ':': + error(2, opt_info.arg); + continue; + case '?': + error(ERROR_usage(2), opt_info.arg); + continue; + } + break; + } + argv += opt_info.index; + if (error_info.errors || *argv) + error(ERROR_usage(2), optusage(NiL)); + while(1) + { + if(waitnotify(-1, timeout, 0)==0) + break; + sfprintf(sfstderr,"interrupted\n"); + } + return(errno != 0); +} Index: src/lib/libshell/common/bltins/misc.c =================================================================== --- src/lib/libshell/common/bltins/misc.c (revision 0) +++ src/lib/libshell/common/bltins/misc.c (revision 740) @@ -0,0 +1,589 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * exec [arg...] + * eval [arg...] + * jobs [-lnp] [job...] + * login [arg...] + * let expr... + * . file [arg...] + * :, true, false + * vpath [top] [base] + * vmap [top] [base] + * wait [job...] + * shift [n] + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include "variables.h" +#include "shnodes.h" +#include "path.h" +#include "io.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#include "jobs.h" + +#define DOTMAX MAXDEPTH /* maximum level of . nesting */ + +static void noexport(Namval_t*,void*); + +struct login +{ + Shell_t *sh; + int clear; + char *arg0; +}; + +int b_exec(int argc,char *argv[], void *extra) +{ + struct login logdata; + register int n; + logdata.clear = 0; + logdata.arg0 = 0; + logdata.sh = (Shell_t*)extra; + logdata.sh->st.ioset = 0; + while (n = optget(argv, sh_optexec)) switch (n) + { + case 'a': + logdata.arg0 = opt_info.arg; + argc = 0; + break; + case 'c': + logdata.clear=1; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(*argv) + B_login(0,argv,(void*)&logdata); + return(0); +} + +static void noexport(register Namval_t* np, void *data) +{ + NOT_USED(data); + nv_offattr(np,NV_EXPORT); +} + +int B_login(int argc,char *argv[],void *extra) +{ + struct checkpt *pp; + register struct login *logp=0; + register Shell_t *shp; + const char *pname; + if(argc) + shp = (Shell_t*)extra; + else + { + logp = (struct login*)extra; + shp = logp->sh; + } + pp = (struct checkpt*)shp->jmplist; + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[0]); + else + { + register struct argnod *arg=shp->envlist; + register Namval_t* np; + register char *cp; + if(shp->subshell) + sh_subfork(); + if(logp && logp->clear) + { +#ifdef _ENV_H + env_close(shp->env); + shp->env = env_open((char**)0,3); +#else + nv_scan(shp->var_tree,noexport,0,NV_EXPORT,NV_EXPORT); +#endif + } + while(arg) + { + if((cp=strchr(arg->argval,'=')) && + (*cp=0,np=nv_search(arg->argval,shp->var_tree,0))) + { + nv_onattr(np,NV_EXPORT); + sh_envput(shp->env,np); + } + if(cp) + *cp = '='; + arg=arg->argnxt.ap; + } + pname = argv[0]; + if(logp && logp->arg0) + argv[0] = logp->arg0; +#ifdef JOBS + if(job_close() < 0) + return(1); +#endif /* JOBS */ + /* force bad exec to terminate shell */ + pp->mode = SH_JMPEXIT; + sh_sigreset(2); + sh_freeup(); + path_exec(pname,argv,NIL(struct argnod*)); + sh_done(0); + } + return(1); +} + +int b_let(int argc,char *argv[],void *extra) +{ + register int r; + register char *arg; + NOT_USED(argc); + NOT_USED(extra); + while (r = optget(argv,sh_optlet)) switch (r) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors || !*argv) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + while(arg= *argv++) + r = !sh_arith(arg); + return(r); +} + +int b_eval(int argc,char *argv[], void *extra) +{ + register int r; + register Shell_t *shp = (Shell_t*)extra; + NOT_USED(argc); + while (r = optget(argv,sh_opteval)) switch (r) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + if(*argv && **argv) + { + sh_offstate(SH_MONITOR); + sh_eval(sh_sfeval(argv),0); + } + return(shp->exitval); +} + +int b_dot_cmd(register int n,char *argv[],void* extra) +{ + register char *script; + register Namval_t *np; + register int jmpval; + register Shell_t *shp = (Shell_t*)extra; + struct sh_scoped savst, *prevscope = shp->st.self; + char *filename=0; + int fd; + struct dolnod *argsave=0, *saveargfor; + struct checkpt buff; + Sfio_t *iop=0; + NOT_USED(extra); + while (n = optget(argv,sh_optdot)) switch (n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg); + return(2); + } + argv += opt_info.index; + script = *argv; + if(error_info.errors || !script) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(shp->dot_depth++ > DOTMAX) + errormsg(SH_DICT,ERROR_exit(1),e_toodeep,script); + shp->st.lineno = error_info.line; + if(!(np=shp->posix_fun)) + { + /* check for KornShell style function first */ + np = nv_search(script,shp->fun_tree,0); + if(np && is_afunction(np) && !nv_isattr(np,NV_FPOSIX)) + { + if(!np->nvalue.ip) + { +#ifdef PATH_BFPATH + path_search(script,NIL(Pathcomp_t*),0); +#else + path_search(script,NIL(char*),0); +#endif + if(np->nvalue.ip) + { + if(nv_isattr(np,NV_FPOSIX)) + np = 0; + } + else + errormsg(SH_DICT,ERROR_exit(1),e_found,script); + } + } + else + np = 0; + if(!np) + { + if((fd=path_open(script,path_get(script))) < 0) + errormsg(SH_DICT,ERROR_system(1),e_open,script); + filename = path_fullname(stakptr(PATH_OFFSET)); + } + } + *prevscope = shp->st; + if(filename) + shp->st.filename = filename; + shp->st.prevst = prevscope; + shp->st.self = &savst; + shp->topscope = (Shscope_t*)shp->st.self; + prevscope->save_tree = shp->var_tree; + shp->st.cmdname = argv[0]; + if(np) + shp->st.filename = np->nvalue.rp->fname; + nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE); + shp->posix_fun = 0; + if(np || argv[1]) + argsave = sh_argnew(argv,&saveargfor); + sh_pushcontext(&buff,SH_JMPDOT); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval == 0) + { + if(np) + sh_exec((Shnode_t*)(nv_funtree(np)),sh_isstate(SH_ERREXIT)); + else + { + char buff[IOBSIZE+1]; + iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fd,SF_READ); + sh_eval(iop,0); + } + } + sh_popcontext(&buff); + if(!np) + free((void*)shp->st.filename); + shp->dot_depth--; + if((np || argv[1]) && jmpval!=SH_JMPSCRIPT) + sh_argreset(argsave,saveargfor); + else + { + prevscope->dolc = shp->st.dolc; + prevscope->dolv = shp->st.dolv; + } + if (shp->st.self != &savst) + *shp->st.self = shp->st; + /* only restore the top Shscope_t portion for posix functions */ + memcpy((void*)&shp->st, (void*)prevscope, sizeof(Shscope_t)); + shp->topscope = (Shscope_t*)prevscope; + nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE); + if(shp->exitval > SH_EXITSIG) + sh_fault(shp->exitval&SH_EXITMASK); + if(jmpval && jmpval!=SH_JMPFUN) + siglongjmp(*shp->jmplist,jmpval); + return(shp->exitval); +} + +/* + * null, true command + */ +int b_true(int argc,register char *argv[],void *extra) +{ + NOT_USED(argc); + NOT_USED(argv[0]); + NOT_USED(extra); + return(0); +} + +/* + * false command + */ +int b_false(int argc,register char *argv[], void *extra) +{ + NOT_USED(argc); + NOT_USED(argv[0]); + NOT_USED(extra); + return(1); +} + +int b_shift(register int n, register char *argv[], void *extra) +{ + register char *arg; + register Shell_t *shp = (Shell_t*)extra; + while((n = optget(argv,sh_optshift))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s",opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + n = ((arg= *argv)?(int)sh_arith(arg):1); + if(n<0 || shp->st.dolcst.dolv += n; + shp->st.dolc -= n; + } + return(0); +} + +int b_wait(int n,register char *argv[],void *extra) +{ + register Shell_t *shp = (Shell_t*)extra; + while((n = optget(argv,sh_optwait))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + job_bwait(argv); + return(shp->exitval); +} + +#ifdef JOBS +# if 0 + /* for the dictionary generator */ + int b_fg(int n,char *argv[],void *extra){} + int b_disown(int n,char *argv[],void *extra){} +# endif +int b_bg(register int n,register char *argv[],void *extra) +{ + register int flag = **argv; + register Shell_t *shp = (Shell_t*)extra; + register const char *optstr = sh_optbg; + if(*argv[0]=='f') + optstr = sh_optfg; + else if(*argv[0]=='d') + optstr = sh_optdisown; + while((n = optget(argv,optstr))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + argv += opt_info.index; + if(!sh_isoption(SH_MONITOR) || !job.jobcontrol) + { + if(sh_isstate(SH_INTERACTIVE)) + errormsg(SH_DICT,ERROR_exit(1),e_no_jctl); + return(1); + } + if(flag=='d' && *argv==0) + argv = (char**)0; + if(job_walk(sfstdout,job_switch,flag,argv)) + errormsg(SH_DICT,ERROR_exit(1),e_no_job); + return(shp->exitval); +} + +int b_jobs(register int n,char *argv[],void *extra) +{ + register int flag = 0; + register Shell_t *shp = (Shell_t*)extra; + while((n = optget(argv,sh_optjobs))) switch(n) + { + case 'l': + flag = JOB_LFLAG; + break; + case 'n': + flag = JOB_NFLAG; + break; + case 'p': + flag = JOB_PFLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(*argv==0) + argv = (char**)0; + if(job_walk(sfstdout,job_list,flag,argv)) + errormsg(SH_DICT,ERROR_exit(1),e_no_job); + job_wait((pid_t)0); + return(shp->exitval); +} +#endif + +#ifdef _cmd_universe +/* + * There are several universe styles that are masked by the getuniv(), + * setuniv() calls. + */ +int b_universe(int argc, char *argv[],void *extra) +{ + register char *arg; + register int n; + NOT_USED(extra); + while((n = optget(argv,sh_optuniverse))) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + argv += opt_info.index; + argc -= opt_info.index; + if(error_info.errors || argc>1) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(arg = argv[0]) + { + if(!astconf("UNIVERSE",0,arg)) + errormsg(SH_DICT,ERROR_exit(1), e_badname,arg); + } + else + { + if(!(arg=astconf("UNIVERSE",0,0))) + errormsg(SH_DICT,ERROR_exit(1),e_nouniverse); + else + sfputr(sfstdout,arg,'\n'); + } + return(0); +} +#endif /* cmd_universe */ + +#if SHOPT_FS_3D +# if 0 + /* for the dictionary generator */ + int b_vmap(int argc,char *argv[], void *extra){} +# endif + int b_vpath(register int argc,char *argv[], void *extra) + { + register int flag, n; + register const char *optstr; + register char *vend; + register Shell_t *shp = (Shell_t*)extra; + if(argv[0][1]=='p') + { + optstr = sh_optvpath; + flag = FS3D_VIEW; + } + else + { + optstr = sh_optvmap; + flag = FS3D_VERSION; + } + while(n = optget(argv, optstr)) switch(n) + { + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg); + break; + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(!shp->lim.fs3d) + goto failed; + argv += opt_info.index; + argc -= opt_info.index; + switch(argc) + { + case 0: + case 1: + flag |= FS3D_GET; + if((n = mount(*argv,(char*)0,flag,0)) >= 0) + { + vend = stakalloc(++n); + n = mount(*argv,vend,flag|FS3D_SIZE(n),0); + } + if(n < 0) + goto failed; + if(argc==1) + { + sfprintf(sfstdout,"%s\n",vend); + break; + } + n = 0; + while(flag = *vend++) + { + if(flag==' ') + { + flag = e_sptbnl[n+1]; + n = !n; + } + sfputc(sfstdout,flag); + } + if(n) + sfputc(sfstdout,'\n'); + break; + default: + if((argc&1)) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + /*FALLTHROUGH*/ + case 2: + if(!shp->lim.fs3d) + goto failed; + if(shp->subshell) + sh_subfork(); + for(n=0;n1) + errormsg(SH_DICT,ERROR_exit(1),e_cantset,flag==2?e_mapping:e_versions); + else + errormsg(SH_DICT,ERROR_exit(1),e_cantget,flag==2?e_mapping:e_versions); + return(1); + } +#endif /* SHOPT_FS_3D */ + Index: src/lib/libshell/common/bltins/shopen.c =================================================================== --- src/lib/libshell/common/bltins/shopen.c (revision 0) +++ src/lib/libshell/common/bltins/shopen.c (revision 740) @@ -0,0 +1,533 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +static const char id[] = "\n@(#)$Id: open (AT&T Research) 1998-07-07 $\0\n"; + +#include +#include +#include +#include +#ifndef SH_DICT +# define SH_DICT "libshell" +#endif + +/* + * time formatting related +*/ +struct dctime +{ + Namfun_t fun; + Namval_t *format; +}; + +static char *get_time(Namval_t* np, Namfun_t* nfp) +{ + static char buff[256]; + struct dctime *dp = (struct dctime*)nfp; + time_t t = nv_getn(np,nfp); + char *format = nv_getval(dp->format); + tmfmt(buff,sizeof(buff),format,(time_t*)0); + return(buff); +} + +static void put_time(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + struct dctime *dp = (struct dctime*)nfp; + char *last; + if(val) + { + int32_t t; + if(flag&NV_INTEGER) + { + if(flag&NV_LONG) + t = *(Sfdouble_t*)val; + else + t = *(double*)val; + } + else + { + t = tmdate(val, &last, (time_t*)0); + if(*last) + errormsg(SH_DICT,ERROR_exit(1),"%s: invalid date/time string",val); + } + nv_putv(np,(char*)&t,NV_INTEGER,nfp); + } + else + { + nv_unset(dp->format); + free((void*)dp->format); + nv_putv(np,val,flag,nfp); + } +} + +static Namval_t *create_time(Namval_t *np, const char *name, int flags, Namfun_t *nfp) +{ + struct dctime *dp = (struct dctime*)nfp; + if(strcmp(name,"format")) + return((Namval_t*)0); + return(dp->format); +} + +static const Namdisc_t timedisc = +{ + sizeof(struct dctime), + put_time, + get_time, + 0, + 0, + create_time, +}; + + +static Namval_t *make_time(Namval_t* np) +{ + int offset = stktell(stkstd); + char *name = nv_name(np); + struct dctime *dp = newof(NULL,struct dctime,1,0); + if(!dp) + return((Namval_t*)0); + sfprintf(stkstd,"%s.format\0",name); + sfputc(stkstd,0); + dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); + dp->fun.disc = &timedisc; + nv_stack(np,&dp->fun); + return(np); +} + +/* + * mode formatting related +*/ +static char *get_mode(Namval_t* np, Namfun_t* nfp) +{ + mode_t mode = nv_getn(np,nfp); + return(fmtperm(mode)); +} + +static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + if(val) + { + int32_t mode; + char *last; + if(flag&NV_INTEGER) + { + if(flag&NV_LONG) + mode = *(Sfdouble_t*)val; + else + mode = *(double*)val; + } + else + { + mode = strperm(val, &last,0); + if(*last) + errormsg(SH_DICT,ERROR_exit(1),"%s: invalid mode string",val); + } + nv_putv(np,(char*)&mode,NV_INTEGER,nfp); + } + else + nv_putv(np,val,flag,nfp); +} + +static const Namdisc_t modedisc = +{ + 0, + put_mode, + get_mode, +}; + +static Namval_t *make_mode(Namval_t* np) +{ + char *name = nv_name(np); + Namfun_t *nfp = newof(NULL,Namfun_t,1,0); + if(!nfp) + return((Namval_t*)0); + nfp->disc = &modedisc; + nv_stack(np,nfp); + return(np); +} + +/* + * field related typese and functions + */ +typedef struct _field_ +{ + char *name; /* field name */ + int flags; /* flags */ + short offset; /* offset of field into data */ + short size; /* size of field */ + Namval_t *(*make)(Namval_t*); /* discipline constructor */ +} Shfield_t; + +/* + * lookup field in field table + */ +static Shfield_t *sh_findfield(Shfield_t *ftable, int nelem, const char *name) +{ + Shfield_t *fp = ftable; + register int i,n; + register const char *cp; + for(cp=name; *cp; cp++) + { + if(*cp=='.') + break; + } + n = cp-name; + for(i=0; i < nelem; i++,fp++) + { + if(memcmp(fp->name,name,n)==0 && fp->name[n]==0) + return(fp); + } + return(0); +} + +/* + * class types and functions + */ + +typedef struct _class_ +{ + int nelem; /* number of elements */ + int dsize; /* size for data structure */ + Shfield_t *fields; /* field description table */ +} Shclass_t; + +struct dcclass +{ + Namfun_t fun; + Shclass_t sclass; +}; + +static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np) +{ + char *val = np->nvalue + fp->offset; + char *name = nv_name(np); + register Namval_t *nq; + int offset = stktell(stkstd); + sfprintf(stkstd,"%s.%s\0",name,fp->name); + sfputc(stkstd,0); + nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); + if(fp->size<0) + val = *(char**)val; + nv_putval(nq,val,fp->flags|NV_NOFREE); + if(fp->make) + (*fp->make)(nq); + return(nq); +} + +static Namval_t *fieldcreate(Namval_t *np, const char *name, int flags, Namfun_t *nfp) +{ + struct dcclass *dcp = (struct dcclass*)nfp; + Shclass_t *sp = &dcp->sclass; + Shfield_t *fp = sh_findfield(sp->fields,sp->nelem,name); + Namval_t *nq,**nodes = (Namval_t**)(dcp+1); + int n = fp-sp->fields; + int len = strlen(fp->name); + void *data = (void*)np->nvalue; + if(!(nq=nodes[n])) + { + nodes[n] = nq = sh_newnode(fp,np); + nfp->last = ""; + } + if(name[len]==0) + return(nq); + return(nq); +} + +static void genvalue(Sfio_t *out, Shclass_t *sp, int indent, Namval_t *npar) +{ + Shfield_t *fp = sp->fields; + Namval_t *np, **nodes= (Namval_t**)(sp+1); + register int i,isarray; + if(out) + { + sfwrite(out,"(\n",2); + indent++; + } + for(i=0; i < sp->nelem; i++,fp++) + { +#if 0 + /* handle recursive case */ +#endif + if(!(np=nodes[i]) && out) + np = sh_newnode(fp,npar); + if(np) + { + isarray=0; + if(nv_isattr(np,NV_ARRAY)) + { + isarray=1; + if(array_elem(nv_arrayptr(np))==0) + isarray=2; + else + nv_putsub(np,(char*)0,ARRAY_SCAN); + } + sfnputc(out,'\t',indent); + sfputr(out,fp->name,(isarray==2?'\n':'=')); + if(isarray) + { + if(isarray==2) + continue; + sfwrite(out,"(\n",2); + sfnputc(out,'\t',++indent); + } + while(1) + { + char *fmtq; + if(isarray) + { + sfprintf(out,"[%s]",sh_fmtq(nv_getsub(np))); + sfputc(out,'='); + } + if(!(fmtq=nv_getval(np)) || !(fmtq=sh_fmtq(fmtq))) + fmtq = ""; + sfputr(out,fmtq,'\n'); + if(!nv_nextsub(np)) + break; + sfnputc(out,'\t',indent); + } + if(isarray) + { + sfnputc(out,'\t',--indent); + sfwrite(out,")\n",2); + } + } + } + if(out) + { + if(indent>1) + sfnputc(out,'\t',indent-1); + sfputc(out,')'); + } +} + +static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp) +{ + static Sfio_t *out; + Sfio_t *outfile; + int savtop = stktell(stkstd); + char *savptr = stkfreeze(stkstd,0); + if(dlete) + outfile = 0; + else if(!(outfile=out)) + outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); + else + sfseek(outfile,0L,SEEK_SET); + genvalue(outfile,&dcp->sclass,0,np); + stkset(stkstd,savptr,savtop); + if(!outfile) + return((char*)0); + sfputc(out,0); + return((char*)out->_data); +} + +static char *get_classval(Namval_t* np, Namfun_t* nfp) +{ + return(walk_class(np,0,(struct dcclass *)nfp)); +} + +static void put_classval(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + walk_class(np,1,(struct dcclass *)nfp); + if(nfp = nv_stack(np,(Namfun_t*)0)) + { + free((void*)nfp); + if(np->nvalue && !nv_isattr(np,NV_NOFREE)) + free((void*)np->nvalue); + } + if(val) + nv_putval(np,val,flag); +} + +static const Namdisc_t classdisc = +{ + sizeof(struct dcclass), + put_classval, + get_classval, + 0, + 0, + fieldcreate +}; + +static int mkclass(Namval_t *np, Shclass_t *sp) +{ + struct dcclass *tcp = newof(NULL,struct dcclass,1,sp->nelem*sizeof(Namval_t*)); + if(!tcp) + return(0); + memset((void*)(tcp+1),0,sp->nelem*sizeof(Namval_t*)); + tcp->fun.disc = &classdisc; + tcp->sclass = *sp; + np->nvalue = (char*)calloc(sp->dsize,1); + nv_stack(np,&tcp->fun); + return(1); +} + +/* + * ====================from here down is file class specific + */ +static struct stat *Sp; + +struct filedata +{ + struct stat statb; + int fd; + char *name; +}; + +static Shfield_t filefield[] = +{ + { "atime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_atime), sizeof(Sp->st_atime), make_time}, + { "ctime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ctime), sizeof(Sp->st_ctime), make_time}, + { "dev", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_dev),sizeof(Sp->st_dev)}, + { "fd", NV_INTEGER|NV_RDONLY, offsetof(struct filedata,fd), sizeof(int)}, + { "gid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_gid), sizeof(Sp->st_gid)}, + { "ino", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ino), sizeof(Sp->st_ino)}, + { "mode", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mode), sizeof(Sp->st_mode), make_mode}, + { "mtime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mtime), sizeof(Sp->st_mtime), make_time}, + { "name", NV_RDONLY, offsetof(struct filedata,name), -1 }, + { "nlink", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_nlink), sizeof(Sp->st_nlink)}, + { "size", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_size), sizeof(Sp->st_size)}, + { "uid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_uid), sizeof(Sp->st_uid)} +}; + +static Shclass_t Fileclass = +{ + sizeof(filefield)/sizeof(*filefield), + sizeof(struct filedata), + filefield +}; + + +#define letterbit(bit) (1<<((bit)-'a')) + +static const char sh_optopen[] = +"[-?\n@(#)$Id: open (AT&T Labs Research) 2007-03-11 $\n]" +"[-author?David Korn ]" +"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" +"[+NAME? open - create a shell variable correspnding to a file]" +"[+DESCRIPTION?\bopen\b creates the compound variable \avar\a correspinding " + "to the file given by the pathname \afile\a. The elements of \avar\a " + "are the names of elements in the \astat\a structure with the \bst_\b " + "prefix removed.]" +"[+?If the \b-r\b and/or \b-w\b mode is specified, then \afile\a is opened and " + "the variable \avar\a\b.fd\b is the file descriptor.]" +"[a:append?Open for append.]" +"[b:binary?Open in binary mode.]" +"[c:create?Open for create.]" +"[i:inherit?Open without the close-on-exec bit set.]" +"[r:read?Open with read access.]" +"[w:write?Open with write access.]" +"[m:mode]:[mode:=rwrwrw?Open with access mode \amode\a.]" +"[x:exclusive?Open exclusive.]" +"\n" +"\nvar file\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Success.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bstat\b(2)]" +; + + +extern int b_open(int argc, char *argv[], void *extra) +{ + register Namval_t *np; + register int n,oflag=0; + Shell_t *shp = (Shell_t*)extra; + struct filedata *fdp; + struct stat statb; + mode_t mode = 0666; + long flags = 0; + int fd = -1; + while (n = optget(argv, sh_optopen)) switch (n) + { + case 'r': + case 'i': + case 'w': + flags |= letterbit(n); + break; + case 'b': +#ifdef O_BINARY + oflag |= O_BINARY; +#endif + break; + case 't': +#ifdef O_TEXT + oflag |= O_TEXT; +#endif + break; + case 'x': + oflag |= O_EXCL; + break; + case 'c': + oflag |= O_CREAT; + break; + case 'a': + oflag |= O_APPEND; + break; + case 'm': + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(argc!=2) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + if(!(flags&(letterbit('r')|letterbit('w')))) + { + if(stat(argv[1],&statb)<0) + errormsg(SH_DICT,ERROR_system(1),"%s: open failed",argv[1]); + } + else + { + if(flags&letterbit('r')) + { + if(flags&letterbit('w')) + oflag |= O_RDWR; + else + oflag |= O_RDONLY; + } + else if(flags&letterbit('w')) + oflag |= O_WRONLY; + fd = open(argv[1],oflag,mode); + if(fd<0) + errormsg(SH_DICT,ERROR_system(1),"%s: open failed",argv[1]); + } + if(!(flags&letterbit('i'))) + fcntl(fd,F_SETFL,0); + np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); + if(!nv_isnull(np)) + nv_unset(np); + mkclass(np,&Fileclass); + fdp = (struct filedata*)np->nvalue; + if(!(flags&(letterbit('r')|letterbit('w')))) + fdp->statb = statb; + else + fstat(fd,&fdp->statb); + fdp->fd = fd; + fdp->name = strdup(argv[1]); + return(0); +} Index: src/lib/libshell/common/bltins/print.c =================================================================== --- src/lib/libshell/common/bltins/print.c (revision 0) +++ src/lib/libshell/common/bltins/print.c (revision 740) @@ -0,0 +1,897 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * echo [arg...] + * print [-nrps] [-f format] [-u filenum] [arg...] + * printf format [arg...] + * + * David Korn + * AT&T Labs + */ + +#include "defs.h" +#include +#include +#include "io.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#include "streval.h" +#include +#include +#include + +union types_t +{ + unsigned char c; + short h; + int i; + long l; + Sflong_t ll; + Sfdouble_t ld; + double d; + float f; + char *s; + int *ip; + char **p; +}; + +struct printf +{ + Sffmt_t hdr; + int argsize; + int intvar; + char **nextarg; + char cescape; + char err; + Shell_t *sh; +}; + +static int extend(Sfio_t*,void*, Sffmt_t*); +static const char preformat[] = ""; +static char *genformat(char*); +static int fmtvecho(const char*, struct printf*); + +struct print +{ + Shell_t *sh; + const char *options; + char raw; + char echon; +}; + +static char* nullarg[] = { 0, 0 }; + +/* + * Need to handle write failures to avoid locking output pool + */ +static int outexceptf(Sfio_t* iop, int mode, void* data, Sfdisc_t* dp) +{ + if(mode==SF_DPOP || mode==SF_FINAL) + free((void*)dp); + else if(mode==SF_WRITE && (errno!= EINTR || sh.trapnote)) + { + int save = errno; + sfpurge(iop); + sfpool(iop,NIL(Sfio_t*),SF_WRITE); + errno = save; + errormsg(SH_DICT,ERROR_system(1),e_badwrite,sffileno(iop)); + } + return(0); +} + +#if !SHOPT_ECHOPRINT + int B_echo(int argc, char *argv[],void *extra) + { + static char bsd_univ; + struct print prdata; + prdata.options = sh_optecho+5; + prdata.raw = prdata.echon = 0; + prdata.sh = (Shell_t*)extra; + NOT_USED(argc); + /* This mess is because /bin/echo on BSD is different */ + if(!prdata.sh->universe) + { + register char *universe; + if(universe=astconf("UNIVERSE",0,0)) + bsd_univ = (strcmp(universe,"ucb")==0); + prdata.sh->universe = 1; + } + if(!bsd_univ) + return(b_print(0,argv,&prdata)); + prdata.options = sh_optecho; + prdata.raw = 1; + while(argv[1] && *argv[1]=='-') + { + if(strcmp(argv[1],"-n")==0) + prdata.echon = 1; +#if !SHOPT_ECHOE + else if(strcmp(argv[1],"-e")==0) + prdata.raw = 0; + else if(strcmp(argv[1],"-ne")==0 || strcmp(argv[1],"-en")==0) + { + prdata.raw = 0; + prdata.echon = 1; + } +#endif /* SHOPT_ECHOE */ + else + break; + argv++; + } + return(b_print(0,argv,&prdata)); + } +#endif /* SHOPT_ECHOPRINT */ + +int b_printf(int argc, char *argv[],void *extra) +{ + struct print prdata; + NOT_USED(argc); + memset(&prdata,0,sizeof(prdata)); + prdata.sh = (Shell_t*)extra; + prdata.options = sh_optprintf; + return(b_print(-1,argv,&prdata)); +} + +/* + * argc==0 when called from echo + * argc==-1 when called from printf + */ + +int b_print(int argc, char *argv[], void *extra) +{ + register Sfio_t *outfile; + register int exitval=0,n, fd = 1; + register Shell_t *shp = (Shell_t*)extra; + const char *options, *msg = e_file+4; + char *format = 0; + int sflag = 0, nflag=0, rflag=0; + if(argc>0) + { + options = sh_optprint; + nflag = rflag = 0; + format = 0; + } + else + { + struct print *pp = (struct print*)extra; + shp = pp->sh; + options = pp->options; + if(argc==0) + { + nflag = pp->echon; + rflag = pp->raw; + argv++; + goto skip; + } + } + while((n = optget(argv,options))) switch(n) + { + case 'n': + nflag++; + break; + case 'p': + fd = shp->coutpipe; + msg = e_query; + break; + case 'f': + format = opt_info.arg; + break; + case 's': + /* print to history file */ + if(!sh_histinit()) + errormsg(SH_DICT,ERROR_system(1),e_history); + fd = sffileno(shp->hist_ptr->histfp); + sh_onstate(SH_HISTORY); + sflag++; + break; + case 'e': + rflag = 0; + break; + case 'r': + rflag = 1; + break; + case 'u': + fd = (int)strtol(opt_info.arg,&opt_info.arg,10); + if(*opt_info.arg) + fd = -1; + else if(fd<0 || fd >= shp->lim.open_max) + fd = -1; + else if(!(sh.inuse_bits&(1<hist_ptr && fd==sffileno(shp->hist_ptr->histfp)))) + + fd = -1; + break; + case ':': + /* The following is for backward compatibility */ +#if OPT_VERSION >= 19990123 + if(strcmp(opt_info.name,"-R")==0) +#else + if(strcmp(opt_info.option,"-R")==0) +#endif + { + rflag = 1; + if(error_info.errors==0) + { + argv += opt_info.index+1; + /* special case test for -Rn */ + if(strchr(argv[-1],'n')) + nflag++; + if(*argv && strcmp(*argv,"-n")==0) + { + + nflag++; + argv++; + } + goto skip2; + } + } + else + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors || (argc<0 && !(format = *argv++))) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); +skip: + if(format) + format = genformat(format); + /* handle special case of '-' operand for print */ + if(argc>0 && *argv && strcmp(*argv,"-")==0 && strcmp(argv[-1],"--")) + argv++; +skip2: + if(fd < 0) + { + errno = EBADF; + n = 0; + } + else if(!(n=shp->fdstatus[fd])) + n = sh_iocheckfd(fd); + if(!(n&IOWRITE)) + { + /* don't print error message for stdout for compatibility */ + if(fd==1) + return(1); + errormsg(SH_DICT,ERROR_system(1),msg); + } + if(!(outfile=shp->sftable[fd])) + { + Sfdisc_t *dp; + sh_onstate(SH_NOTRACK); + n = SF_WRITE|((n&IOREAD)?SF_READ:0); + shp->sftable[fd] = outfile = sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fd,n); + sh_offstate(SH_NOTRACK); + sfpool(outfile,shp->outpool,SF_WRITE); + if(dp = new_of(Sfdisc_t,0)) + { + dp->exceptf = outexceptf; + dp->seekf = 0; + dp->writef = 0; + dp->readf = 0; + sfdisc(outfile,dp); + } + } + /* turn off share to guarantee atomic writes for printf */ + n = sfset(outfile,SF_SHARE|SF_PUBLIC,0); + if(format) + { + /* printf style print */ + Sfio_t *pool; + struct printf pdata; + memset(&pdata, 0, sizeof(pdata)); + pdata.sh = shp; + pdata.hdr.version = SFIO_VERSION; + pdata.hdr.extf = extend; + pdata.nextarg = argv; + sh_offstate(SH_STOPOK); + pool=sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE); + do + { + if(shp->trapnote&SH_SIGSET) + break; + pdata.hdr.form = format; + sfprintf(outfile,"%!",&pdata); + } while(*pdata.nextarg && pdata.nextarg!=argv); + if(pdata.nextarg == nullarg && pdata.argsize>0) + sfwrite(outfile,stakptr(staktell()),pdata.argsize); + sfpool(sfstderr,pool,SF_WRITE); + exitval = pdata.err; + } + else + { + /* echo style print */ + if(sh_echolist(outfile,rflag,argv) && !nflag) + sfputc(outfile,'\n'); + } + if(sflag) + { + hist_flush(shp->hist_ptr); + sh_offstate(SH_HISTORY); + } + else if(n&SF_SHARE) + { + sfset(outfile,SF_SHARE|SF_PUBLIC,1); + sfsync(outfile); + } + return(exitval); +} + +/* + * echo the argument list onto + * if is non-zero then \ is not a special character. + * returns 0 for \c otherwise 1. + */ + +int sh_echolist(Sfio_t *outfile, int raw, char *argv[]) +{ + register char *cp; + register int n; + struct printf pdata; + pdata.cescape = 0; + pdata.err = 0; + while(!pdata.cescape && (cp= *argv++)) + { + if(!raw && (n=fmtvecho(cp,&pdata))>=0) + { + if(n) + sfwrite(outfile,stakptr(staktell()),n); + } + else + sfputr(outfile,cp,-1); + if(*argv) + sfputc(outfile,' '); + sh_sigcheck(); + } + return(!pdata.cescape); +} + +/* + * modified version of stresc for generating formats + */ +static char strformat(char *s) +{ + register char* t; + register int c; + char* b; + char* p; + + b = t = s; + for (;;) + { + switch (c = *s++) + { + case '\\': + if(*s==0) + break; + c = chresc(s - 1, &p); + s = p; +#if SHOPT_MULTIBYTE + if(c>UCHAR_MAX && mbwide()) + { + t += wctomb(t, c); + continue; + } +#endif /* SHOPT_MULTIBYTE */ + if(c=='%') + *t++ = '%'; + else if(c==0) + { + *t++ = '%'; + c = 'Z'; + } + break; + case 0: + *t = 0; + return(t - b); + } + *t++ = c; + } +} + + +static char *genformat(char *format) +{ + register char *fp; + stakseek(0); + stakputs(preformat); + stakputs(format); + fp = (char*)stakfreeze(1); + strformat(fp+sizeof(preformat)-1); + return(fp); +} + +static char *fmthtml(const char *string) +{ + register const char *cp = string; + register int c, offset = staktell(); + while(c= *(unsigned char*)cp++) + { +#if SHOPT_MULTIBYTE + register int s; + if((s=mbsize(cp-1)) > 1) + { + cp += (s-1); + continue; + } +#endif /* SHOPT_MULTIBYTE */ + if(c=='<') + stakputs("<"); + else if(c=='>') + stakputs(">"); + else if(c=='&') + stakputs("&"); + else if(c=='"') + stakputs("""); + else if(c=='\'') + stakputs("'"); + else if(c==' ') + stakputs(" "); + else if(!isprint(c) && c!='\n' && c!='\r') + sfprintf(stkstd,"&#%X;",CCMAPC(c,CC_NATIVE,CC_ASCII)); + else + stakputc(c); + } + stakputc(0); + return(stakptr(offset)); +} + +static void *fmtbase64(char *string, ssize_t *sz) +{ + char *cp; + Sfdouble_t d; + size_t size; + Namval_t *np = nv_open(string, NiL, NV_VARNAME|NV_NOASSIGN|NV_NOADD); + static union types_t number; + if(!np) + return(""); + if(nv_isattr(np,NV_INTEGER)) + { + d = nv_getnum(np); + if(nv_isattr(np,NV_DOUBLE)) + { + if(nv_isattr(np,NV_LONG)) + { + size = sizeof(Sfdouble_t); + number.ld = d; + } + else if(nv_isattr(np,NV_SHORT)) + { + size = sizeof(float); + number.f = (float)d; + } + else + { + size = sizeof(double); + number.d = (double)d; + } + } + else + { + if(nv_isattr(np,NV_LONG)) + { + size = sizeof(Sflong_t); + number.ll = (Sflong_t)d; + } + else if(nv_isattr(np,NV_SHORT)) + { + size = sizeof(short); + number.h = (short)d; + } + else + { + size = sizeof(short); + number.i = (int)d; + } + } + if(sz) + *sz = size; + return((void*)&number); + } + if(nv_isattr(np,NV_BINARY)) + nv_onattr(np,NV_RAW); + cp = nv_getval(np); + if(nv_isattr(np,NV_BINARY)) + nv_offattr(np,NV_RAW); + if((size = nv_size(np))==0) + size = strlen(cp); + if(sz) + *sz = size; + return((void*)cp); +} + +static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) +{ + char* lastchar = ""; + register int neg = 0; + Sfdouble_t d; + Sfdouble_t longmin = LDBL_LLONG_MIN; + Sfdouble_t longmax = LDBL_LLONG_MAX; + int format = fe->fmt; + int n; + int fold = fe->base; + union types_t* value = (union types_t*)v; + struct printf* pp = (struct printf*)fe; + register char* argp = *pp->nextarg; + + fe->flags |= SFFMT_VALUE; + if(!argp || format=='Z') + { + switch(format) + { + case 'c': + value->c = 0; + fe->flags &= ~SFFMT_LONG; + break; + case 'q': + format = 's'; + /* FALL THROUGH */ + case 's': + case 'H': + case 'B': + case 'P': + case 'R': + case 'Z': + case 'b': + fe->fmt = 's'; + fe->size = -1; + fe->base = -1; + value->s = ""; + fe->flags &= ~SFFMT_LONG; + break; + case 'a': + case 'e': + case 'f': + case 'g': + case 'A': + case 'E': + case 'F': + case 'G': + if(SFFMT_LDOUBLE) + value->ld = 0.; + else + value->d = 0.; + break; + case 'n': + value->ip = &pp->intvar; + break; + case 'Q': + value->ll = 0; + break; + case 'T': + fe->fmt = 'd'; + value->ll = tmxgettime(); + break; + default: + if(!strchr("DdXxoUu",format)) + errormsg(SH_DICT,ERROR_exit(1),e_formspec,format); + fe->fmt = 'd'; + value->ll = 0; + break; + } + } + else + { + switch(format) + { + case 'p': + value->p = (char**)strtol(argp,&lastchar,10); + break; + case 'n': + { + Namval_t *np; + np = nv_open(argp,sh.var_tree,NV_VARNAME|NV_NOASSIGN|NV_NOARRAY); + nv_unset(np); + nv_onattr(np,NV_INTEGER); + if (np->nvalue.lp = new_of(int32_t,0)) + *np->nvalue.lp = 0; + nv_setsize(np,10); + if(sizeof(int)==sizeof(int32_t)) + value->ip = (int*)np->nvalue.lp; + else + { + int32_t sl = 1; + value->ip = (int*)(((char*)np->nvalue.lp) + (*((char*)&sl) ? 0 : sizeof(int))); + } + nv_close(np); + break; + } + case 'q': + case 'b': + case 's': + case 'B': + case 'H': + case 'P': + case 'R': + fe->fmt = 's'; + fe->size = -1; + if(format=='s' && fe->base>=0) + { + value->p = pp->nextarg; + pp->nextarg = nullarg; + } + else + { + fe->base = -1; + value->s = argp; + } + fe->flags &= ~SFFMT_LONG; + break; + case 'c': + if(fe->base >=0) + value->s = argp; + else + value->c = *argp; + fe->flags &= ~SFFMT_LONG; + break; + case 'o': + case 'x': + case 'X': + case 'u': + case 'U': + longmax = LDBL_ULLONG_MAX; + case '.': + if(fe->size==2 && strchr("bcsqHPRQTZ",*fe->form)) + { + value->ll = ((unsigned char*)argp)[0]; + break; + } + case 'd': + case 'D': + case 'i': + switch(*argp) + { + case '\'': + case '"': + value->ll = ((unsigned char*)argp)[1]; + break; + default: + d = sh_strnum(argp,&lastchar,0); + if(derr = 1; + d = longmin; + } + else if(d>longmax) + { + errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp); + pp->err = 1; + d = longmax; + } + value->ll = (Sflong_t)d; + if(lastchar == *pp->nextarg) + { + value->ll = *argp; + lastchar = ""; + } + break; + } + if(neg) + value->ll = -value->ll; + fe->size = sizeof(value->ll); + break; + case 'a': + case 'e': + case 'f': + case 'g': + case 'A': + case 'E': + case 'F': + case 'G': + d = sh_strnum(*pp->nextarg,&lastchar,0); + if(SFFMT_LDOUBLE) + { + value->ld = d; + fe->size = sizeof(value->ld); + } + else + { + value->d = d; + fe->size = sizeof(value->d); + } + break; + case 'Q': + value->ll = (Sflong_t)strelapsed(*pp->nextarg,&lastchar,1); + break; + case 'T': + value->ll = (Sflong_t)tmxdate(*pp->nextarg,&lastchar,TMX_NOW); + break; + default: + value->ll = 0; + fe->fmt = 'd'; + fe->size = sizeof(value->ll); + errormsg(SH_DICT,ERROR_exit(1),e_formspec,format); + break; + } + if (format == '.') + value->i = value->ll; + if(*lastchar) + { + errormsg(SH_DICT,ERROR_warn(0),e_argtype,format); + pp->err = 1; + } + pp->nextarg++; + } + switch(format) + { + case 'Z': + fe->fmt = 'c'; + fe->base = -1; + value->c = 0; + break; + case 'b': + if((n=fmtvecho(value->s,pp))>=0) + { + if(pp->nextarg == nullarg) + { + pp->argsize = n; + return -1; + } + value->s = stakptr(staktell()); + } + break; + case 'B': + value->s = (char*)fmtbase64(value->s, &fe->size); + break; + case 'H': + value->s = fmthtml(value->s); + break; + case 'q': + value->s = sh_fmtqf(value->s, !!(fe->flags & SFFMT_ALTER), fold); + break; + case 'P': + { + char *s = fmtmatch(value->s); + if(!s || *s==0) + errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); + value->s = s; + break; + } + case 'R': + value->s = fmtre(value->s); + if(*value->s==0) + errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s); + break; + case 'Q': + if (fe->n_str>0) + { + fe->fmt = 'd'; + fe->size = sizeof(value->ll); + } + else + { + value->s = fmtelapsed(value->ll, 1); + fe->fmt = 's'; + fe->size = -1; + } + break; + case 'T': + if(fe->n_str>0) + { + n = fe->t_str[fe->n_str]; + fe->t_str[fe->n_str] = 0; + value->s = fmttmx(fe->t_str, value->ll); + fe->t_str[fe->n_str] = n; + } + else value->s = fmttmx(NIL(char*), value->ll); + fe->fmt = 's'; + fe->size = -1; + break; + } + return 0; +} + +/* + * construct System V echo string out of + * If there are not escape sequences, returns -1 + * Otherwise, puts null terminated result on stack, but doesn't freeze it + * returns length of output. + */ + +static int fmtvecho(const char *string, struct printf *pp) +{ + register const char *cp = string, *cpmax; + register int c; + register int offset = staktell(); +#if SHOPT_MULTIBYTE + int chlen; + if(mbwide()) + { + while(1) + { + if ((chlen = mbsize(cp)) > 1) + /* Skip over multibyte characters */ + cp += chlen; + else if((c= *cp++)==0 || c == '\\') + break; + } + } + else +#endif /* SHOPT_MULTIBYTE */ + while((c= *cp++) && (c!='\\')); + if(c==0) + return(-1); + c = --cp - string; + if(c>0) + stakwrite((void*)string,c); + for(; c= *cp; cp++) + { +#if SHOPT_MULTIBYTE + if (mbwide() && ((chlen = mbsize(cp)) > 1)) + { + stakwrite(cp,chlen); + cp += (chlen-1); + continue; + } +#endif /* SHOPT_MULTIBYTE */ + if( c=='\\') switch(*++cp) + { + case 'E': + c = ('a'==97?'\033':39); /* ASCII/EBCDIC */ + break; + case 'a': + c = '\a'; + break; + case 'b': + c = '\b'; + break; + case 'c': + pp->cescape++; + pp->nextarg = nullarg; + goto done; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 'v': + c = '\v'; + break; + case 't': + c = '\t'; + break; + case '\\': + c = '\\'; + break; + case '0': + c = 0; + cpmax = cp + 4; + while(++cp='0' && *cp<='7') + { + c <<= 3; + c |= (*cp-'0'); + } + default: + cp--; + } + stakputc(c); + } +done: + c = staktell()-offset; + stakputc(0); + stakseek(offset); + return(c); +} Index: src/lib/libshell/common/bltins/alarm.c =================================================================== --- src/lib/libshell/common/bltins/alarm.c (revision 0) +++ src/lib/libshell/common/bltins/alarm.c (revision 740) @@ -0,0 +1,276 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * alarm [-r] [varname [+]when] + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include +#include "builtins.h" +#include "FEATURE/time" + +#define R_FLAG 1 +#define L_FLAG 2 + +struct tevent +{ + Namfun_t fun; + Namval_t *node; + Namval_t *action; + struct tevent *next; + long milli; + int flags; + void *timeout; + Shell_t *sh; +}; + +static const char ALARM[] = "alarm"; + +static void trap_timeout(void*); + +/* + * insert timeout item on current given list in sorted order + */ +static void *time_add(struct tevent *item, void *list) +{ + register struct tevent *tp = (struct tevent*)list; + if(!tp || item->milli < tp->milli) + { + item->next = tp; + list = (void*)item; + } + else + { + while(tp->next && item->milli > tp->next->milli) + tp = tp->next; + item->next = tp->next; + tp->next = item; + } + tp = item; + tp->timeout = (void*)sh_timeradd(tp->milli,tp->flags&R_FLAG,trap_timeout,(void*)tp); + return(list); +} + +/* + * delete timeout item from current given list, delete timer + */ +static void *time_delete(register struct tevent *item, void *list) +{ + register struct tevent *tp = (struct tevent*)list; + if(item==tp) + list = (void*)tp->next; + else + { + while(tp && tp->next != item) + tp = tp->next; + if(tp) + tp->next = item->next; + } + if(item->timeout) + timerdel((void*)item->timeout); + return(list); +} + +static void print_alarms(void *list) +{ + register struct tevent *tp = (struct tevent*)list; + while(tp) + { + if(tp->timeout) + { + register char *name = nv_name(tp->node); + if(tp->flags&R_FLAG) + { + double d = tp->milli; + sfprintf(sfstdout,e_alrm1,name,d/1000.); + } + else + sfprintf(sfstdout,e_alrm2,name,nv_getnum(tp->node)); + } + tp = tp->next; + } +} + +static void trap_timeout(void* handle) +{ + register struct tevent *tp = (struct tevent*)handle; + tp->sh->trapnote |= SH_SIGALRM; + if(!(tp->flags&R_FLAG)) + tp->timeout = 0; + tp->flags |= L_FLAG; + tp->sh->sigflag[SIGALRM] |= SH_SIGALRM; + if(sh_isstate(SH_TTYWAIT)) + sh_timetraps(); +} + +void sh_timetraps(void) +{ + register struct tevent *tp, *tpnext; + register struct tevent *tptop; + while(1) + { + sh.sigflag[SIGALRM] &= ~SH_SIGALRM; + tptop= (struct tevent*)sh.st.timetrap; + for(tp=tptop;tp;tp=tpnext) + { + tpnext = tp->next; + if(tp->flags&L_FLAG) + { + tp->flags &= ~L_FLAG; + if(tp->action) + sh_fun(tp->action,tp->node,(char**)0); + tp->flags &= ~L_FLAG; + if(!tp->flags) + { + nv_unset(tp->node); + nv_close(tp->node); + } + } + } + if(!(sh.sigflag[SIGALRM]&SH_SIGALRM)) + break; + } +} + + +/* + * This trap function catches "alarm" actions only + */ +static char *setdisc(Namval_t *np, const char *event, Namval_t* action, Namfun_t + *fp) +{ + register struct tevent *tp = (struct tevent*)fp; + if(!event) + return(action?"":(char*)ALARM); + if(strcmp(event,ALARM)!=0) + { + /* try the next level */ + return(nv_setdisc(np, event, action, fp)); + } + if(action==np) + action = tp->action; + else + tp->action = action; + return(action?(char*)action:""); +} + +/* + * catch assignments and set alarm traps + */ +static void putval(Namval_t* np, const char* val, int flag, Namfun_t* fp) +{ + register struct tevent *tp; + register double d; + if(val) + { + double now; +#ifdef timeofday + struct timeval tmp; + timeofday(&tmp); + now = tmp.tv_sec + 1.e-6*tmp.tv_usec; +#else + now = (double)time(NIL(time_t*)); +#endif /* timeofday */ + nv_putv(np,val,flag,fp); + d = nv_getnum(np); + tp = (struct tevent*)fp; + if(*val=='+') + { + double x = d + now; + nv_putv(np,(char*)&x,NV_INTEGER,fp); + } + else + d -= now; + tp->milli = 1000*(d+.0005); + if(tp->timeout) + sh.st.timetrap = time_delete(tp,sh.st.timetrap); + if(tp->milli > 0) + sh.st.timetrap = time_add(tp,sh.st.timetrap); + } + else + { + tp = (struct tevent*)nv_stack(np, (Namfun_t*)0); + sh.st.timetrap = time_delete(tp,sh.st.timetrap); + if(tp->action) + nv_close(tp->action); + nv_unset(np); + free((void*)fp); + } +} + +static const Namdisc_t alarmdisc = +{ + sizeof(struct tevent), + putval, + 0, + 0, + setdisc, +}; + +int b_alarm(int argc,char *argv[],void *extra) +{ + register int n,rflag=0; + register Namval_t *np; + register struct tevent *tp; + register Shell_t *shp = (Shell_t*)extra; + while (n = optget(argv, sh_optalarm)) switch (n) + { + case 'r': + rflag = R_FLAG; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argc -= opt_info.index; + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + if(argc==0) + { + print_alarms(shp->st.timetrap); + return(0); + } + if(argc!=2) + errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); + np = nv_open(argv[0],shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOASSIGN); + if(!nv_isnull(np)) + nv_unset(np); + nv_setattr(np, NV_INTEGER|NV_DOUBLE); + if(!(tp = newof(NIL(struct tevent*),struct tevent,1,0))) + errormsg(SH_DICT,ERROR_exit(1),e_nospace); + tp->fun.disc = &alarmdisc; + tp->flags = rflag; + tp->node = np; + tp->sh = shp; + nv_stack(np,(Namfun_t*)tp); + nv_putval(np, argv[1], 0); + return(0); +} + Index: src/lib/libshell/common/bltins/typeset.c =================================================================== --- src/lib/libshell/common/bltins/typeset.c (revision 0) +++ src/lib/libshell/common/bltins/typeset.c (revision 740) @@ -0,0 +1,979 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * export [-p] [arg...] + * readonly [-p] [arg...] + * typeset [options] [arg...] + * alias [-ptx] [arg...] + * unalias [arg...] + * builtin [-sd] [-f file] [name...] + * set [options] [name...] + * unset [-fnv] [name...] + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include "path.h" +#include "name.h" +#include "history.h" +#include "builtins.h" +#include "variables.h" +#include + +struct tdata +{ + Shell_t *sh; + Namval_t *tp; + Sfio_t *outfile; + char *prefix; + int aflag; + int argnum; + int scanmask; + Dt_t *scanroot; + char **argnam; +}; + + +static int print_namval(Sfio_t*, Namval_t*, int, struct tdata*); +static void print_attribute(Namval_t*,void*); +static void print_all(Sfio_t*, Dt_t*, struct tdata*); +static void print_scan(Sfio_t*, int, Dt_t*, int, struct tdata*t); +static int b_unall(int, char**, Dt_t*, Shell_t*); +static int b_common(char**, int, Dt_t*, struct tdata*); +static void pushname(Namval_t*,void*); +static void(*nullscan)(Namval_t*,void*); + +static Namval_t *load_class(const char *name) +{ + errormsg(SH_DICT,ERROR_exit(1),"%s: type not loadable",name); + return(0); +} + +/* + * Note export and readonly are the same + */ +#if 0 + /* for the dictionary generator */ + int b_export(int argc,char *argv[],void *extra){} +#endif +int b_readonly(int argc,char *argv[],void *extra) +{ + register int flag; + char *command = argv[0]; + struct tdata tdata; + NOT_USED(argc); + memset((void*)&tdata,0,sizeof(tdata)); + tdata.sh = (Shell_t*)extra; + tdata.aflag = '-'; + while((flag = optget(argv,*command=='e'?sh_optexport:sh_optreadonly))) switch(flag) + { + case 'p': + tdata.prefix = command; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*))); + argv += (opt_info.index-1); + if(*command=='r') + flag = (NV_ASSIGN|NV_RDONLY|NV_VARNAME); +#ifdef _ENV_H + else if(!argv[1]) + { + char *cp,**env=env_get(tdata.sh->env); + while(cp = *env++) + { + if(tdata.prefix) + sfputr(sfstdout,tdata.prefix,' '); + sfprintf(sfstdout,"%s\n",sh_fmtq(cp)); + } + return(0); + } +#endif + else + { + flag = (NV_ASSIGN|NV_EXPORT|NV_IDENT); + if(!sh.prefix) + sh.prefix = ""; + } + return(b_common(argv,flag,tdata.sh->var_tree, &tdata)); +} + + +int b_alias(int argc,register char *argv[],void *extra) +{ + register unsigned flag = NV_NOARRAY|NV_NOSCOPE|NV_ASSIGN; + register Dt_t *troot; + register int n; + struct tdata tdata; + NOT_USED(argc); + memset((void*)&tdata,0,sizeof(tdata)); + tdata.sh = (Shell_t*)extra; + troot = tdata.sh->alias_tree; + if(*argv[0]=='h') + flag = NV_TAGGED; + if(argv[1]) + { + opt_info.offset = 0; + opt_info.index = 1; + *opt_info.option = 0; + tdata.argnum = 0; + tdata.aflag = *argv[1]; + while((n = optget(argv,sh_optalias))) switch(n) + { + case 'p': + tdata.prefix = argv[0]; + break; + case 't': + flag |= NV_TAGGED; + break; + case 'x': + flag |= NV_EXPORT; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); + argv += (opt_info.index-1); + if(flag&NV_TAGGED) + { + if(argv[1] && strcmp(argv[1],"-r")==0) + { + /* hack to handle hash -r */ + nv_putval(PATHNOD,nv_getval(PATHNOD),NV_RDONLY); + return(0); + } + troot = tdata.sh->track_tree; + } + } + return(b_common(argv,flag,troot,&tdata)); +} + + +#if 0 + /* for the dictionary generator */ + int b_local(int argc,char *argv[],void *extra){} +#endif +int b_typeset(int argc,register char *argv[],void *extra) +{ + register int flag = NV_VARNAME|NV_ASSIGN; + register int n; + struct tdata tdata; + Namtype_t *ntp = (Namtype_t*)extra; + Dt_t *troot; + int isfloat=0, shortint=0; + NOT_USED(argc); + memset((void*)&tdata,0,sizeof(tdata)); + tdata.sh = ntp->shp; + tdata.tp = ntp->np; + troot = tdata.sh->var_tree; + opt_info.disc = (Optdisc_t*)ntp->optinfof; + while((n = optget(argv,ntp->optstring))) + { + switch(n) + { + case 'a': + flag |= NV_IARRAY; + break; + case 'A': + flag |= NV_ARRAY; + break; + case 'E': + /* The following is for ksh88 compatibility */ + if(opt_info.offset && !strchr(argv[opt_info.index],'E')) + { + tdata.argnum = (int)opt_info.num; + break; + } + case 'F': + if(!opt_info.arg || (tdata.argnum = opt_info.num) <0) + tdata.argnum = 10; + isfloat = 1; + if(n=='E') + flag |= NV_EXPNOTE; + break; + case 'b': + flag |= NV_BINARY; + break; + case 'n': + flag &= ~NV_VARNAME; + flag |= (NV_REF|NV_IDENT); + break; + case 'H': + flag |= NV_HOST; + break; + case 'T': + flag |= NV_TYPE; + tdata.prefix = opt_info.arg; + break; + case 'L': + if(tdata.argnum==0) + tdata.argnum = (int)opt_info.num; + if(tdata.argnum < 0) + errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum); + flag &= ~NV_RJUST; + flag |= NV_LJUST; + break; + case 'Z': + flag |= NV_ZFILL; + /* FALL THRU*/ + case 'R': + if(tdata.argnum==0) + tdata.argnum = (int)opt_info.num; + if(tdata.argnum < 0) + errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum); + flag &= ~NV_LJUST; + flag |= NV_RJUST; + break; + case 'f': + flag &= ~(NV_VARNAME|NV_ASSIGN); + troot = tdata.sh->fun_tree; + break; + case 'i': + if(!opt_info.arg || (tdata.argnum = opt_info.num) <0) + tdata.argnum = 10; + flag |= NV_INTEGER; + break; + case 'l': + flag |= NV_UTOL; + break; + case 'p': + tdata.prefix = argv[0]; + continue; + case 'r': + flag |= NV_RDONLY; + break; + case 's': + shortint=1; + break; + case 't': + flag |= NV_TAGGED; + break; + case 'u': + flag |= NV_LTOU; + break; + case 'x': + flag &= ~NV_VARNAME; + flag |= (NV_EXPORT|NV_IDENT); + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + opt_info.disc = 0; + return(2); + } + if(tdata.aflag==0) + tdata.aflag = *opt_info.option; + } + argv += opt_info.index; + opt_info.disc = 0; + /* handle argument of + and - specially */ + if(*argv && argv[0][1]==0 && (*argv[0]=='+' || *argv[0]=='-')) + tdata.aflag = *argv[0]; + else + argv--; + if((flag&NV_INTEGER) && (flag&(NV_LJUST|NV_RJUST|NV_ZFILL))) + error_info.errors++; + if((flag&NV_BINARY) && (flag&(NV_LJUST|NV_UTOL|NV_LTOU))) + error_info.errors++; + if(troot==tdata.sh->fun_tree && ((isfloat || flag&~(NV_FUNCT|NV_TAGGED|NV_EXPORT|NV_LTOU)))) + error_info.errors++; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*))); + if(isfloat) + flag |= NV_INTEGER|NV_DOUBLE; + if(shortint) + flag |= NV_SHORT|NV_INTEGER; + if(tdata.sh->fn_depth) + flag |= NV_NOSCOPE; + if(flag&NV_TYPE) + { + int offset = staktell(); + stakputs(NV_CLASS); + if(NV_CLASS[sizeof(NV_CLASS)-2]!='.') + stakputc('.'); + stakputs(tdata.prefix); + stakputc(0); + tdata.tp = nv_open(stakptr(offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN); + stakseek(offset); + if(!tdata.tp) + errormsg(SH_DICT,ERROR_exit(1),"%s: unknown type",tdata.prefix); + flag &= ~NV_TYPE; + } + else if(tdata.aflag==0 && ntp->np) + tdata.aflag = '-'; + return(b_common(argv,flag,troot,&tdata)); +} + +static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata *tp) +{ + register char *name; + char *last = 0; + int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN)); + int r=0, ref=0; + Shell_t *shp =tp->sh; + if(!sh.prefix) + nvflags |= NV_NOSCOPE; + else if(*sh.prefix==0) + sh.prefix = 0; + flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT); + if(argv[1]) + { + if(flag&NV_REF) + { + flag &= ~NV_REF; + ref=1; + if(tp->aflag!='-') + nvflags |= NV_NOREF; + } + while(name = *++argv) + { + register unsigned newflag; + register Namval_t *np; + unsigned curflag; + if(troot == shp->fun_tree) + { + /* + *functions can be exported or + * traced but not set + */ + flag &= ~NV_ASSIGN; + if(flag&NV_LTOU) + { + /* Function names cannot be special builtin */ + if((np=nv_search(name,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC)) + errormsg(SH_DICT,ERROR_exit(1),e_badfun,name); + np = nv_open(name,shp->fun_tree,NV_NOARRAY|NV_IDENT|NV_NOSCOPE); + } + else + np = nv_search(name,shp->fun_tree,HASH_NOSCOPE); + if(np && ((flag&NV_LTOU) || !nv_isnull(np) || nv_isattr(np,NV_LTOU))) + { + if(flag==0) + { + print_namval(sfstdout,np,tp->aflag=='+',tp); + continue; + } + if(shp->subshell) + sh_subfork(); + if(tp->aflag=='-') + nv_onattr(np,flag|NV_FUNCTION); + else if(tp->aflag=='+') + nv_offattr(np,flag); + } + else + r++; + continue; + } + np = nv_open(name,troot,nvflags); + /* tracked alias */ + if(troot==shp->track_tree && tp->aflag=='-') + { +#ifdef PATH_BFPATH + path_alias(np,path_absolute(nv_name(np),NIL(Pathcomp_t*))); +#else + nv_onattr(np,NV_NOALIAS); + path_alias(np,path_absolute(nv_name(np),NIL(char*))); +#endif + continue; + } + if(flag==NV_ASSIGN && !ref && tp->aflag!='-' && !strchr(name,'=')) + { + if(troot!=shp->var_tree && (nv_isnull(np) || !print_namval(sfstdout,np,0,tp))) + { + sfprintf(sfstderr,sh_translate(e_noalias),name); + r++; + } + continue; + } + if(tp->tp) + { + nv_settype(np,tp->tp,tp->aflag=='-'?0:NV_APPEND); + flag = (np->nvflag&NV_NOCHANGE); + } + if(troot==shp->var_tree && (flag&NV_IARRAY)) + { + flag &= ~NV_IARRAY; + if(nv_isnull(np)) + nv_onattr(np,NV_ARRAY); + else + nv_putsub(np, (char*)0, 0); + } + if(troot==shp->var_tree && (nvflags&NV_ARRAY)) + nv_setarray(np,nv_associative); + curflag = np->nvflag; + flag &= ~NV_ASSIGN; + if(last=strchr(name,'=')) + *last = 0; + if (tp->aflag == '-') + { + if((flag&NV_EXPORT) && strchr(name,'.')) + errormsg(SH_DICT,ERROR_exit(1),e_badexport,name); +#if SHOPT_BSH + if(flag&NV_EXPORT) + nv_offattr(np,NV_IMPORT); +#endif /* SHOPT_BSH */ + newflag = curflag; + if(flag&~NV_NOCHANGE) + newflag &= NV_NOCHANGE; + newflag |= flag; + if (flag & (NV_LJUST|NV_RJUST)) + { + if(!(flag&NV_RJUST)) + newflag &= ~NV_RJUST; + + else if(!(flag&NV_LJUST)) + newflag &= ~NV_LJUST; + } + if (flag & NV_UTOL) + newflag &= ~NV_LTOU; + else if (flag & NV_LTOU) + newflag &= ~NV_UTOL; + } + else + { + if((flag&NV_RDONLY) && (curflag&NV_RDONLY)) + errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np)); + newflag = curflag & ~flag; + } + if (tp->aflag && (tp->argnum>0 || (curflag!=newflag))) + { + if(shp->subshell) + sh_assignok(np,1); + if(troot!=shp->var_tree) + nv_setattr(np,newflag&~NV_ASSIGN); + else + { + char *oldname=0; + if(tp->argnum==1 && newflag==NV_INTEGER && nv_isattr(np,NV_INTEGER)) + tp->argnum = 10; + /* use reference name for export */ + if((newflag^curflag)&NV_EXPORT) + { + oldname = np->nvname; + np->nvname = name; + } + nv_newattr (np, newflag&~NV_ASSIGN,tp->argnum); + if(oldname) + np->nvname = oldname; + } + } + if(last) + *last = '='; + /* set or unset references */ + if(ref) + { + if(tp->aflag=='-') + { + Dt_t *hp=0; + if(nv_isattr(np,NV_PARAM) && shp->st.prevst) + { + if(!(hp=(Dt_t*)shp->st.prevst->save_tree)) + hp = dtvnext(shp->var_tree); + } + nv_setref(np,hp,NV_VARNAME); + } + else + nv_unref(np); + } + nv_close(np); + } + } + else if(!sh.envlist) + { + if(tp->aflag) + { + if(troot==shp->fun_tree) + { + flag |= NV_FUNCTION; + tp->prefix = 0; + } + else if(troot==shp->var_tree) + flag |= (nvflags&NV_ARRAY); + print_scan(sfstdout,flag,troot,tp->aflag=='+',tp); + } + else if(troot==shp->alias_tree) + print_scan(sfstdout,0,troot,0,tp); + else + print_all(sfstdout,troot,tp); + sfsync(sfstdout); + } + return(r); +} + +typedef void (*Iptr_t)(int); +typedef int (*Fptr_t)(int, char*[], void*); + +#define GROWLIB 4 + +static void** liblist; +static int nlib; +static int maxlib; + +/* + * This allows external routines to load from the same library */ +void **sh_getliblist(void) +{ + return(liblist); +} + +/* + * add library to loaded list + * call (*lib_init)() on first load if defined + * always move to head of search list + * return: 0: already loaded 1: first load + */ +int sh_addlib(void* library) +{ + register int n; + register int r; + Iptr_t initfn; + + for (n = r = 0; n < nlib; n++) + { + if (r) + liblist[n-1] = liblist[n]; + else if (liblist[n] == library) + r++; + } + if (r) + nlib--; + else if ((initfn = (Iptr_t)dlllook(library, "lib_init"))) + (*initfn)(0); + if (nlib >= maxlib) + { + maxlib += GROWLIB; + if (liblist) + liblist = (void**)realloc((void*)liblist, (maxlib+1)*sizeof(void**)); + else + liblist = (void**)malloc((maxlib+1)*sizeof(void**)); + } + liblist[nlib++] = library; + liblist[nlib] = 0; + return !r; +} + +/* + * add change or list built-ins + * adding builtins requires dlopen() interface + */ +int b_builtin(int argc,char *argv[],void *extra) +{ + register char *arg=0, *name; + register int n, r=0, flag=0; + register Namval_t *np; + long dlete=0; + struct tdata tdata; + Fptr_t addr; + void *library=0; + char *errmsg; + NOT_USED(argc); + tdata.sh = (Shell_t*)extra; + while (n = optget(argv,sh_optbuiltin)) switch (n) + { + case 's': + flag = BLT_SPC; + break; + case 'd': + dlete=1; + break; + case 'f': +#if SHOPT_DYNAMIC + arg = opt_info.arg; +#else + errormsg(SH_DICT,2, "adding built-ins not supported"); + error_info.errors++; +#endif /* SHOPT_DYNAMIC */ + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); + break; + } + argv += opt_info.index; + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*))); + if(arg || *argv) + { + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,argv[-opt_info.index]); + if(sh_isoption(SH_PFSH)) + errormsg(SH_DICT,ERROR_exit(1),e_pfsh,argv[-opt_info.index]); + if(tdata.sh->subshell) + sh_subfork(); + } + if(arg) + { +#ifdef _hdr_dlldefs +#if (_AST_VERSION>=20040404) + if(!(library = dllplug(SH_ID,arg,NIL(char*),RTLD_LAZY,NIL(char*),0))) +#else + if(!(library = dllfind(arg,NIL(char*),RTLD_LAZY,NIL(char*),0))) +#endif +#else + if(!(library = dlopen(arg,DL_MODE))) +#endif + { + errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dlerror()); + return(1); + } + sh_addlib(library); + } + else if(*argv==0 && !dlete) + { + print_scan(sfstdout, flag, tdata.sh->bltin_tree, 1, &tdata); + return(0); + } + r = 0; + flag = staktell(); + while(arg = *argv) + { + name = path_basename(arg); + stakputs("b_"); + stakputs(name); + errmsg = 0; + addr = 0; + for(n=(nlib?nlib:dlete); --n>=0;) + { + /* (char*) added for some sgi-mips compilers */ + if(dlete || (addr = (Fptr_t)dlllook(liblist[n],stakptr(flag)))) + { + if(np = sh_addbuiltin(arg, addr,pointerof(dlete))) + { + if(dlete || nv_isattr(np,BLT_SPC)) + errmsg = "restricted name"; + } + break; + } + } + if(!dlete && !addr) + { + np = sh_addbuiltin(arg, 0 ,0); + if(np && nv_isattr(np,BLT_SPC)) + errmsg = "restricted name"; + else if(!np) + errmsg = "not found"; + } + if(errmsg) + { + errormsg(SH_DICT,ERROR_exit(0),"%s: %s",*argv,errmsg); + r = 1; + } + stakseek(flag); + argv++; + } + return(r); +} + +int b_set(int argc,register char *argv[],void *extra) +{ + struct tdata tdata; + memset(&tdata,0,sizeof(tdata)); + tdata.sh = (Shell_t*)extra; + tdata.prefix=0; + if(argv[1]) + { + if(sh_argopts(argc,argv) < 0) + return(2); + if(sh_isoption(SH_VERBOSE)) + sh_onstate(SH_VERBOSE); + else + sh_offstate(SH_VERBOSE); + if(sh_isoption(SH_MONITOR)) + sh_onstate(SH_MONITOR); + else + sh_offstate(SH_MONITOR); + } + else + /*scan name chain and print*/ + print_scan(sfstdout,0,tdata.sh->var_tree,0,&tdata); + return(0); +} + +/* + * The removing of Shell variable names, aliases, and functions + * is performed here. + * Unset functions with unset -f + * Non-existent items being deleted give non-zero exit status + */ + +int b_unalias(int argc,register char *argv[],void *extra) +{ + Shell_t *shp = (Shell_t*)extra; + return(b_unall(argc,argv,shp->alias_tree,shp)); +} + +int b_unset(int argc,register char *argv[],void *extra) +{ + Shell_t *shp = (Shell_t*)extra; + return(b_unall(argc,argv,shp->var_tree,shp)); +} + +static int b_unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp) +{ + register Namval_t *np; + register const char *name; + register int r; + int nflag=0,all=0,isfun; + NOT_USED(argc); + if(troot==shp->alias_tree) + { + name = sh_optunalias; + if(shp->subshell) + troot = sh_subaliastree(0); + } + else + name = sh_optunset; + while(r = optget(argv,name)) switch(r) + { + case 'f': + troot = sh_subfuntree(0); + break; + case 'a': + all=1; + break; + case 'n': + nflag = NV_NOREF; + case 'v': + troot = shp->var_tree; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + break; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(2); + } + argv += opt_info.index; + if(error_info.errors || (*argv==0 &&!all)) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); + if(!troot) + return(1); + r = 0; + if(troot==shp->var_tree) + nflag |= NV_VARNAME; + else + nflag = NV_NOSCOPE; + if(all) + dtclear(troot); + else while(name = *argv++) + { + if(np=nv_open(name,troot,NV_NOADD|nflag)) + { + if(is_abuiltin(np)) + { + r = 1; + continue; + } + isfun = is_afunction(np); + if(shp->subshell && troot==shp->var_tree) + np=sh_assignok(np,0); + nv_unset(np); + nv_close(np); + if(isfun) + dtdelete(troot,np); + } + else + r = 1; + } + return(r); +} + +/* + * print out the name and value of a name-value pair + */ + +static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, struct tdata *tp) +{ + register char *cp; + sh_sigcheck(); + if(flag) + flag = '\n'; + if(nv_isattr(np,NV_NOPRINT)==NV_NOPRINT) + { + if(is_abuiltin(np)) + sfputr(file,nv_name(np),'\n'); + return(0); + } + if(tp->prefix) + sfputr(file,tp->prefix,' '); + if(is_afunction(np)) + { + Sfio_t *iop=0; + char *fname=0; + if(!flag && !np->nvalue.ip) + sfputr(file,"typeset -fu",' '); + else if(!flag && !nv_isattr(np,NV_FPOSIX)) + sfputr(file,"function",' '); + sfputr(file,nv_name(np),-1); + if(nv_isattr(np,NV_FPOSIX)) + sfwrite(file,"()",2); + if(np->nvalue.ip && np->nvalue.rp->hoffset>=0) + fname = np->nvalue.rp->fname; + else + flag = '\n'; + if(flag) + { + if(np->nvalue.ip && np->nvalue.rp->hoffset>=0) + sfprintf(file," #line %d %s\n",np->nvalue.rp->lineno,fname?sh_fmtq(fname):""); + else + sfputc(file, '\n'); + } + else + { + if(nv_isattr(np,NV_FTMP)) + { + fname = 0; + iop = tp->sh->heredocs; + } + else if(fname) + iop = sfopen(iop,fname,"r"); + else if(tp->sh->hist_ptr) + iop = (tp->sh->hist_ptr)->histfp; + if(iop && sfseek(iop,(Sfoff_t)np->nvalue.rp->hoffset,SEEK_SET)>=0) + sfmove(iop,file, nv_size(np), -1); + else + flag = '\n'; + if(fname) + sfclose(iop); + } + return(nv_size(np)+1); + } + if(cp=nv_getval(np)) + { + sfputr(file,nv_name(np),-1); + if(!flag) + { + flag = '='; + if(nv_arrayptr(np)) + sfprintf(file,"[%s]", sh_fmtq(nv_getsub(np))); + } + sfputc(file,flag); + if(flag != '\n') + { + if(nv_isref(np) && nv_refsub(np)) + { + sfputr(file,sh_fmtq(cp),-1); + sfprintf(file,"[%s]\n", sh_fmtq(nv_refsub(np))); + } + else + sfputr(file,sh_fmtq(cp),'\n'); + } + return(1); + } + else if(tp->scanmask && tp->scanroot==tp->sh->var_tree) + sfputr(file,nv_name(np),'\n'); + return(0); +} + +/* + * print attributes at all nodes + */ +static void print_all(Sfio_t *file,Dt_t *root, struct tdata *tp) +{ + tp->outfile = file; + nv_scan(root, print_attribute, (void*)tp, 0, 0); +} + +/* + * print the attributes of name value pair give by + */ +static void print_attribute(register Namval_t *np,void *data) +{ + register struct tdata *dp = (struct tdata*)data; + nv_attribute(np,dp->outfile,dp->prefix,dp->aflag); +} + +/* + * print the nodes in tree which have attributes set + * of
' a* X* +test_glob $LINENO ' ' \a* + +if ( set --nullglob ) 2>/dev/null +then + set --nullglob + + test_glob $LINENO ' ' a* X* + + set --nonullglob +fi + +if ( set --failglob ) 2>/dev/null +then + set --failglob + mkdir tmp + touch tmp/l1 tmp/l2 tmp/l3 + + test_glob $LINENO '' tmp/l[12] tmp/*4 tmp/*3 + test_glob $LINENO '' tmp/l[12] tmp/*4 tmp/*3 + + rm -r tmp + set --nofailglob +fi + +test_glob $LINENO '' b*/ +test_glob $LINENO '<*>' \* +test_glob $LINENO '' 'a*' +test_glob $LINENO '' a\* +test_glob $LINENO ' <*q*>' c* a\* *q* +test_glob $LINENO '<**>' "*"* +test_glob $LINENO '<**>' \** +test_glob $LINENO '<\.\./*/>' "\.\./*/" +test_glob $LINENO '' 's/\..*//' +test_glob $LINENO '' "/^root:/{s/^[!:]*:[!:]*:\([!:]*\).*"'$'"/\1/" +test_glob $LINENO ' ' [a-c]b* +test_glob ++Beware $LINENO '
' [a-y]*[!c] +test_glob $LINENO ' ' a*[!c] + +touch a-b aXb + +test_glob $LINENO ' ' a[X-]b + +touch .x .y + +test_glob --Beware $LINENO '
' [!a-c]* + +if mkdir a\*b 2>/dev/null +then + touch a\*b/ooo + + test_glob $LINENO '' a\*b/* + test_glob $LINENO '' a\*?/* + test_case $LINENO '' '!7' '*\!*' + test_case $LINENO '' 'r.*' '*.\*' + test_glob $LINENO '' a[b]c + test_glob $LINENO '' a["b"]c + test_glob $LINENO '' a[\b]c + test_glob $LINENO '' a?c + test_case $LINENO '' 'abc' 'a"b"c' + test_case $LINENO '' 'abc' 'a*c' + test_case $LINENO '' 'abc' '"a?c"' + test_case $LINENO '' 'abc' 'a\*c' + test_case $LINENO '' 'abc' 'a\[b]c' + test_case $LINENO '' '"$undefined"' '""' + test_case $LINENO '' 'abc' 'a["\b"]c' + + rm -rf mkdir a\*b +fi + +mkdir man +mkdir man/man1 +touch man/man1/sh.1 + +test_glob $LINENO '' */man*/sh.* +test_glob $LINENO '' $(echo */man*/sh.*) +test_glob $LINENO '' "$(echo */man*/sh.*)" + +test_case $LINENO '' 'abc' 'a***c' +test_case $LINENO '' 'abc' 'a*****?c' +test_case $LINENO '' 'abc' '?*****??' +test_case $LINENO '' 'abc' '*****??' +test_case $LINENO '' 'abc' '*****??c' +test_case $LINENO '' 'abc' '?*****?c' +test_case $LINENO '' 'abc' '?***?****c' +test_case $LINENO '' 'abc' '?***?****?' +test_case $LINENO '' 'abc' '?***?****' +test_case $LINENO '' 'abc' '*******c' +test_case $LINENO '' 'abc' '*******?' +test_case $LINENO '' 'abcdecdhjk' 'a*cd**?**??k' +test_case $LINENO '' 'abcdecdhjk' 'a**?**cd**?**??k' +test_case $LINENO '' 'abcdecdhjk' 'a**?**cd**?**??k***' +test_case $LINENO '' 'abcdecdhjk' 'a**?**cd**?**??***k' +test_case $LINENO '' 'abcdecdhjk' 'a**?**cd**?**??***k**' +test_case $LINENO '' 'abcdecdhjk' 'a****c**?**??*****' +test_case $LINENO '' "'-'" '[-abc]' +test_case $LINENO '' "'-'" '[abc-]' +test_case $LINENO '' "'\\'" '\\' +test_case $LINENO '' "'\\'" '[\\]' +test_case $LINENO '' "'\\'" "'\\'" +test_case $LINENO '' "'['" '[[]' +test_case $LINENO '' '[' '[[]' +test_case $LINENO '' "'['" '[' +test_case $LINENO '' '[' '[' +test_case $LINENO '' "'[abc'" "'['*" +test_case $LINENO '' "'[abc'" '[*' +test_case $LINENO '' '[abc' "'['*" +test_case $LINENO '' '[abc' '[*' +test_case $LINENO '' 'abd' "a[b/c]d" +test_case $LINENO '' 'a/d' "a[b/c]d" +test_case $LINENO '' 'acd' "a[b/c]d" +test_case $LINENO '' "']'" '[]]' +test_case $LINENO '' "'-'" '[]-]' +test_case $LINENO '' 'p' '[a-\z]' +test_case $LINENO '' '"/tmp"' '[/\\]*' +test_case $LINENO '' 'abc' '??**********?****?' +test_case $LINENO '' 'abc' '??**********?****c' +test_case $LINENO '' 'abc' '?************c****?****' +test_case $LINENO '' 'abc' '*c*?**' +test_case $LINENO '' 'abc' 'a*****c*?**' +test_case $LINENO '' 'abc' 'a********???*******' +test_case $LINENO '' "'a'" '[]' +test_case $LINENO '' 'a' '[]' +test_case $LINENO '' "'['" '[abc' +test_case $LINENO '' '[' '[abc' + +test_glob ++Beware $LINENO ' ' b* +test_glob $LINENO ' ' [bB]* + +if ( set --nocaseglob ) 2>/dev/null +then + set --nocaseglob + + test_glob $LINENO ' ' b* + test_glob $LINENO ' ' [b]* + test_glob $LINENO ' ' [bB]* + + set --nonocaseglob +fi + +if ( set -f ) 2>/dev/null +then + set -f + + test_glob $LINENO '<*>' * + + set +f +fi + +if ( set --noglob ) 2>/dev/null +then + set --noglob + + test_glob $LINENO '<*>' * + + set --glob +fi + +FIGNORE='.*|*' +test_glob $LINENO '<*>' * + +FIGNORE='.*|*c|*e|?' +test_glob $LINENO '
' * + +FIGNORE='.*|*b|*d|?' +test_glob $LINENO ' ' * + +FIGNORE= +test_glob $LINENO '' */man*/sh.* + +unset FIGNORE +test_glob $LINENO '
' ?? +test_glob $LINENO '' */man*/sh.* + +GLOBIGNORE='.*:*' +set -- * +if [[ $1 == '*' ]] +then + GLOBIGNORE='.*:*c:*e:?' + test_glob $LINENO '<>' * + + GLOBIGNORE='.*:*b:*d:?' + test_glob $LINENO '<>' * + + unset GLOBIGNORE + test_glob $LINENO '<>' * + test_glob $LINENO '' */man*/sh.* + + GLOBIGNORE= + test_glob $LINENO '' */man*/sh.* +fi + +exit $errors Index: src/lib/libshell/common/tests/shtests =================================================================== --- src/lib/libshell/common/tests/shtests (revision 0) +++ src/lib/libshell/common/tests/shtests (revision 740) @@ -0,0 +1,63 @@ +# This program runs ksh regression tests +# shtests [ name=value ... ] a.sh b.sh ... + +unset DISPLAY ENV FIGNORE +LANG=C +LC_ALL=C +time=1 +while : +do case $1 in + *=*) n=${1%%=*} + v=${1#*=} + eval $n=\'$v\' + export $n + ;; + -t|--t*)time= + ;; + *) break + ;; + esac + shift +done +export LANG LC_ALL PATH PWD SHELL +PWD=`pwd` +SHELL=${SHELL-ksh} +case $0 in +/*) d=`dirname $0`;; +*/*) d=$PWD/`dirname $0`;; +*) d=$PWD;; +esac +case $SHELL in +/*) ;; +*/*) SHELL=$d/$SHELL;; +*) SHELL=$(whence $SHELL);; +esac +PATH=/bin:/usr/bin +if [[ -d /usr/ucb ]] +then PATH=$PATH:/usr/ucb +fi +PATH=$PATH:$d +if [[ $INSTALLROOT && -r $INSTALLROOT/bin/.paths ]] +then PATH=$INSTALLROOT/bin:$PATH +fi +for i in ${*-*.sh} +do echo test $i begins ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} + t=$(grep -c err_exit $i) + if (( $t )) + then (( t = $t - 1 )) + fi + T=test + if (( $t != 1 )) + then T=${T}s + fi + E=error + if $SHELL $i + then echo test $i passed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} "[ $t $T 0 ${E}s ]" + else e=$? + E=error + if (( $e != 1 )) + then E=${E}s + fi + echo test $i failed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ $t $T $e $E ]" + fi +done Index: src/lib/libshell/common/tests/attributes.sh =================================================================== --- src/lib/libshell/common/tests/attributes.sh (revision 0) +++ src/lib/libshell/common/tests/attributes.sh (revision 740) @@ -0,0 +1,216 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +r=readonly u=Uppercase l=Lowercase i=22 i8=10 L=abc L5=def uL5=abcdef xi=20 +x=export t=tagged H=hostname LZ5=026 RZ5=026 Z5=123 lR5=ABcdef R5=def n=l +for option in u l i i8 L L5 LZ5 RZ5 Z5 r x H t R5 uL5 lR5 xi n +do typeset -$option $option +done +(r=newval) 2> /dev/null && err_exit readonly attribute fails +i=i+5 +if ((i != 27)) +then err_exit integer attributes fails +fi +if [[ $i8 != 8#12 ]] +then err_exit integer base 8 fails +fi +if [[ $u != UPPERCASE ]] +then err_exit uppercase fails +fi +if [[ $l != lowercase ]] +then err_exit lowercase fails +fi +if [[ $n != lowercase ]] +then err_exit reference variables fail +fi +if [[ t=tagged != $(typeset -t) ]] +then err_exit tagged fails +fi +if [[ t != $(typeset +t) ]] +then err_exit tagged fails +fi +if [[ $Z5 != 00123 ]] +then err_exit zerofill fails +fi +if [[ $RZ5 != 00026 ]] +then err_exit right zerofill fails +fi +L=12345 +if [[ $L != 123 ]] +then err_exit leftjust fails +fi +if [[ $L5 != "def " ]] +then err_exit leftjust fails +fi +if [[ $uL5 != ABCDE ]] +then err_exit leftjust uppercase fails +fi +if [[ $lR5 != bcdef ]] +then err_exit rightjust fails +fi +if [[ $R5 != " def" ]] +then err_exit rightjust fails +fi +if [[ $($SHELL -c 'echo $x') != export ]] +then err_exit export fails +fi +if [[ $($SHELL -c 'xi=xi+4;echo $xi') != 24 ]] +then err_exit export attributes fails +fi +x=$(foo=abc $SHELL < /dev/null 2>&1 || err_exit 'typeset + not working' +(typeset -L-5 buf="A" 2>/dev/null) +if [[ $? == 0 ]] +then err_exit 'typeset allows negative field for left/right adjust' +fi +a=b +readonly $a=foo +if [[ $b != foo ]] +then err_exit 'readonly $a=b not working' +fi +if [[ $(export | grep '^PATH=') != PATH=* ]] +then err_exit 'export not working' +fi +picture=( + bitmap=/fruit + size=(typeset -E x=2.5) +) +string="$(print $picture)" +if [[ "${string}" != *'size=( typeset -E'* ]] +then err_exit 'print of compound exponential variable not working' +fi +sz=(typeset -E y=2.2) +string="$(print $sz)" +if [[ "${sz}" == *'typeset -E -F'* ]] +then err_exit 'print of exponential shows both -E and -F attributes' +fi +print 'typeset -i m=48/4+1;print -- $m' > /tmp/ksh$$ +chmod +x /tmp/ksh$$ +typeset -Z2 m +if [[ $(/tmp/ksh$$) != 13 ]] +then err_exit 'attributes not cleared for script execution' +fi +typeset -Z LAST=00 +unset -f foo +function foo +{ + if [[ $1 ]] + then LAST=$1 + else ((LAST++)) + fi +} +foo 1 +if (( ${#LAST} != 2 )) +then err_exit 'LAST!=2' +fi +foo +if (( ${#LAST} != 2 )) +then err_exit 'LAST!=2' +fi +rm -rf /tmp/ksh$$ +set -a +unset foo +foo=bar +if [[ $(export | grep ^foo=) != 'foo=bar' ]] +then err_exit 'all export not working' +fi +unset foo +read foo </dev/null) == ok ]] || err_exit 'malformed environment terminates shell' +unset var +typeset -b var +printf '12%Z34' | read -r -N 5 var +[[ $var == MTIAMzQ= ]] || err_exit 'binary files with zeros not working' +unset var +if command typeset -usi var=0xfffff 2> /dev/null +then (( $var == 0xffff )) || err_exit 'unsigned short integers not working' +else err_exit 'typeset -usi cannot be used for unsigned short' +fi +[[ $($SHELL -c 'unset foo;typeset -Z2 foo; print ${foo:-3}' 2> /dev/null) == 3 ]] || err_exit '${foo:-3} not 3 when typeset -Z2 field undefined' +[[ $($SHELL -c 'unset foo;typeset -Z2 foo; print ${foo:=3}' 2> /dev/null) == 03 ]] || err_exit '${foo:=-3} not 3 when typeset -Z2 foo undefined' +unset foo bar +unset -f fun +function fun +{ + export foo=hello + typeset -x bar=world + [[ $foo == hello ]] || err_exit 'export scoping problem in function' +} +fun +[[ $(export | grep foo) == 'foo=hello' ]] || err_exit 'export not working in functions' +[[ $(export | grep bar) ]] && err_exit 'typeset -x not local' +exit $((Errors)) Index: src/lib/libshell/common/tests/quoting.sh =================================================================== --- src/lib/libshell/common/tests/quoting.sh (revision 0) +++ src/lib/libshell/common/tests/quoting.sh (revision 740) @@ -0,0 +1,331 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +if [[ 'hi there' != "hi there" ]] +then err_exit "single quotes not the same as double quotes" +fi +x='hi there' +if [[ $x != 'hi there' ]] +then err_exit "$x not the same as 'hi there'" +fi +if [[ $x != "hi there" ]] +then err_exit "$x not the same as \"hi there \"" +fi +if [[ \a\b\c\*\|\"\ \\ != 'abc*|" \' ]] +then err_exit " \\ differs from '' " +fi +if [[ "ab\'\"\$(" != 'ab\'\''"$(' ]] +then err_exit " \"\" differs from '' " +fi +if [[ $(print -r - 'abc*|" \') != 'abc*|" \' ]] +then err_exit "\$(print -r - '') differs from ''" +fi +if [[ $(print -r - "abc*|\" \\") != 'abc*|" \' ]] +then err_exit "\$(print -r - '') differs from ''" +fi +if [[ "$(print -r - 'abc*|" \')" != 'abc*|" \' ]] +then err_exit "\"\$(print -r - '')\" differs from ''" +fi +if [[ "$(print -r - "abc*|\" \\")" != 'abc*|" \' ]] +then err_exit "\"\$(print -r - "")\" differs from ''" +fi +if [[ $(print -r - $(print -r - 'abc*|" \')) != 'abc*|" \' ]] +then err_exit "nested \$(print -r - '') differs from ''" +fi +if [[ "$(print -r - $(print -r - 'abc*|" \'))" != 'abc*|" \' ]] +then err_exit "\"nested \$(print -r - '')\" differs from ''" +fi +if [[ $(print -r - "$(print -r - 'abc*|" \')") != 'abc*|" \' ]] +then err_exit "nested \"\$(print -r - '')\" differs from ''" +fi +unset x +if [[ ${x-$(print -r - "abc*|\" \\")} != 'abc*|" \' ]] +then err_exit "\${x-\$(print -r - '')} differs from ''" +fi +if [[ ${x-$(print -r - "a}c*|\" \\")} != 'a}c*|" \' ]] +then err_exit "\${x-\$(print -r - '}')} differs from ''" +fi +x=$((echo foo)|(cat)) +if [[ $x != foo ]] +then err_exit "((cmd)|(cmd)) failed" +fi +x=$(print -r -- "\"$HOME\"") +if [[ $x != '"'$HOME'"' ]] +then err_exit "nested double quotes failed" +fi +: ${z="a{b}c"} +if [[ $z != 'a{b}c' ]] +then err_exit '${z="a{b}c"} not correct' +fi +unset z +: "${z="a{b}c"}" +if [[ $z != 'a{b}c' ]] +then err_exit '"${z="a{b}c"}" not correct' +fi +if [[ $(print -r -- "a\*b") != 'a\*b' ]] +then err_exit '$(print -r -- "a\*b") differs from a\*b' +fi +unset x +if [[ $(print -r -- "a\*b$x") != 'a\*b' ]] +then err_exit '$(print -r -- "a\*b$x") differs from a\*b' +fi +x=hello +set -- ${x+foo bar bam} +if (( $# !=3 )) +then err_exit '${x+foo bar bam} does not yield three arguments' +fi +set -- ${x+foo "bar bam"} +if (( $# !=2 )) +then err_exit '${x+foo "bar bam"} does not yield two arguments' +fi +set -- ${x+foo 'bar bam'} +if (( $# !=2 )) +then err_exit '${x+foo '\''bar bam'\''} does not yield two arguments' +fi +set -- ${x+foo $x bam} +if (( $# !=3 )) +then err_exit '${x+foo $x bam} does not yield three arguments' +fi +set -- ${x+foo "$x" bam} +if (( $# !=3 )) +then err_exit '${x+foo "$x" bam} does not yield three arguments' +fi +set -- ${x+"foo $x bam"} +if (( $# !=1 )) +then err_exit '${x+"foo $x bam"} does not yield one argument' +fi +set -- "${x+foo $x bam}" +if (( $# !=1 )) +then err_exit '"${x+foo $x bam}" does not yield one argument' +fi +set -- ${x+foo "$x "bam} +if (( $# !=2 )) +then err_exit '${x+foo "$x "bam} does not yield two arguments' +fi +x="ab$'cd" +if [[ $x != 'ab$'"'cd" ]] +then err_exit '$'"' inside double quotes not working" +fi +x=`print 'ab$'` +if [[ $x != 'ab$' ]] +then err_exit '$'"' inside `` quotes not working" +fi +unset a +x=$(print -r -- "'\ +\ +") +if [[ $x != "'" ]] +then err_exit 'line continuation in double strings not working' +fi +x=$(print -r -- "'\ +$a\ +") +if [[ $x != "'" ]] +then err_exit 'line continuation in expanded double strings not working' +fi +x='\*' +if [[ $(print -r -- $x) != '\*' ]] +then err_exit 'x="\\*";$x != \*' +fi +x=' hello world ' +set -- $x +if (( $# != 2 )) +then err_exit 'field splitting error' +fi +x=$(print -r -- '1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890 \ +1234567890123456789012345678901234567890123456789012345678901234567890') +if (( ${#x} != (15*73-3) )) +then err_exit "length of x, ${#x}, is incorrect should be $((15*73-3))" +fi +x='$hi' +if [[ $x\$ != '$hi$' ]] +then err_exit ' $x\$, with x=$hi, does not expand to $hi$' +fi +if [[ $x$ != '$hi$' ]] +then err_exit ' $x$, with x=$hi, does not expand to $hi$' +fi +set -- $(/bin/echo foo;sleep 1;/bin/echo bar) +if [[ $# != 2 ]] +then err_exit 'word splitting after command substitution not working' +fi +unset q +if [[ "${q:+'}q${q:+'}" != q ]] +then err_exit 'expansion of "{q:+'\''}" not correct when q unset' +fi +q=1 +if [[ "${q:+'}q${q:+'}" != "'q'" ]] +then err_exit 'expansion of "{q:+'\''}" not correct when q set' +fi +x=$'x\' #y' +if [[ $x != "x' #y" ]] +then err_exit "$'x\' #y'" not working +fi +x=$q$'x\' #y' +if [[ $x != "1x' #y" ]] +then err_exit "$q$'x\' #y'" not working +fi +IFS=, +x='a,b\,c,d' +set -- $x +if [[ $2 != 'b\' ]] +then err_exit "field splitting of $x with IFS=$IFS not working" +fi +foo=bar +bar=$(print -r -- ${foo+\\n\ }) +if [[ $bar != '\n ' ]] +then err_exit '${foo+\\n\ } expansion error' +fi +unset bar +bar=$(print -r -- ${foo+\\n\ $bar}) +if [[ $bar != '\n ' ]] +then err_exit '${foo+\\n\ $bar} expansion error with bar unset' +fi +x='\\(..\\)|&\|\|\\&\\|' +if [[ $(print -r -- $x) != "$x" ]] +then err_exit '$x, where x=\\(..\\)|&\|\|\\&\\| not working' +fi +x='\\(' +if [[ $(print -r -- a${x}b) != a"${x}"b ]] +then err_exit 'a${x}b, where x=\\( not working' +fi +x= +if [[ $(print -r -- $x'\\1') != '\\1' ]] +then err_exit 'backreference inside single quotes broken' +fi +set -- '' +set -- "$@" +if (( $# != 1 )) +then err_exit '"$@" not preserving nulls' +fi +x= +if [[ $(print -r s"!\2${x}\1\a!") != 's!\2\1\a!' ]] +then err_exit 'print -r s"!\2${x}\1\a!" not equal s!\2\1\a!' +fi +if [[ $(print -r $'foo\n\n\n') != foo ]] +then err_exit 'trailing newlines on comsubstitution not removed' +fi +unset x +if [[ ${x:='//'} != '//' ]] +then err_exit '${x:='//'} != "//"' +fi +if [[ $(print -r "\"hi$\"") != '"hi$"' ]] +then err_exit '$\ not correct inside ""' +fi +unset x +if [[ "${x-a\}b}" != 'a}b' ]] +then err_exit '"${x-a\}b}" != "a}b"' +fi +if [[ "\}\]$x\*\{\[\\" != '\}\]\*\{\[\' ]] +then err_exit '"\}\]$x\*\{\[\\" != "\}\]\*\{\[\"' +fi +foo=yes +if [[ $(print -r -- {\$foo}) != '{$foo}' ]] +then err_exit '{\$foo}' not expanded correctly +fi +[[ foo == $( +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +########################################################### +print foo) ]] || err_exit "command subsitution with long comments broken" +subject='some/other/words' +re='(?*)/(?*)/(?*)' +[[ ${subject/${re}/\3} != words ]] && err_exit 'string replacement with \3 not working' +[[ ${subject/${re}/'\3'} != '\3' ]] && err_exit 'string replacement with '"'\3'"' not working' +[[ ${subject/${re}/"\\3"} != '\3' ]] && err_exit 'string replacement with "\\3" not working' +[[ ${subject/${re}/"\3"} != '\3' ]] && err_exit 'string replacement with "\3" not working' +string='\3' +[[ ${subject/${re}/${string}} != words ]] && err_exit 'string replacement with $string not working with string=\3' +[[ $(print -r "${subject/${re}/${string}}") != words ]] && err_exit 'string replacement with $string not working with string=\3 using print' +[[ ${subject/${re}/"${string}"} != '\3' ]] && err_exit 'string replacement with "$string" not working with string=\3' +[[ $(print -r "${subject/${re}/"${string}"}") != '\3' ]] && err_exit 'string replacement with "$string" not working with string=\3 using print' +string='\\3' +[[ ${subject/${re}/${string}} != '\3' ]] && err_exit 'string replacement with $string not working with string=\\3' +[[ ${subject/${re}/"${string}"} != '\\3' ]] && err_exit 'string replacement with "$string" not working with string=\\3' +[[ ${subject/${re}/\4} != '\4' ]] && err_exit 'string replacement with \4 not working' +[[ ${subject/${re}/'\4'} != '\4' ]] && err_exit 'string replacement with '\4' not working' +string='\4' +[[ ${subject/${re}/${string}} != '\4' ]] && err_exit 'string replacement with $string not working with string=\4' +[[ ${subject/${re}/"${string}"} != '\4' ]] && err_exit 'string replacement with "$string" not working with string=\4' +string='&foo' +[[ ${subject/${re}/${string}} != '&foo' ]] && err_exit 'string replacement with $string not working with string=&foo' +[[ ${subject/${re}/"${string}"} != '&foo' ]] && err_exit 'string replacement with "$string" not working with string=&foo' +{ +x=x +x=${x:-`id | sed 's/^[^(]*(\([^)]*\)).*/\1/'`} +} 2> /dev/null || err_exit 'skipping over `` failed' +[[ $x == x ]] || err_exit 'assignment ${x:=`...`} failed' +exit $((Errors)) Index: src/lib/libshell/common/tests/comvar.sh =================================================================== --- src/lib/libshell/common/tests/comvar.sh (revision 0) +++ src/lib/libshell/common/tests/comvar.sh (revision 740) @@ -0,0 +1,197 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +#test for compound variables +Command=${0##*/} +integer Errors=0 +Point=( + float x=1. y=0. +) +eval p="$Point" +if (( (p.x*p.x + p.y*p.y) > 1.01 )) +then err_exit 'compound variable not working' +fi +nameref foo=p +if [[ ${foo.x} != ${Point.x} ]] +then err_exit 'reference to compound object not working' +fi +unset foo +rec=( + name='Joe Blow' + born=( + month=jan + integer day=16 + year=1980 + ) +) +eval newrec="$rec" +if [[ ${newrec.name} != "${rec.name}" ]] +then err_exit 'copying a compound object not working' +fi +if (( newrec.born.day != 16 )) +then err_exit 'copying integer field of compound object not working' +fi +p_t=( + integer z=0 + typeset -A tokens +) +unset x +typeset -A x +x=( [foo]=bar ) +if [[ ${x[@]} != bar ]] +then err_exit 'compound assignemnt of associative arrays not working' +fi +unset -n foo x +unset foo x +foo=( x=3) +nameref x=foo +if [[ ${!x.@} != foo.x ]] +then err_exit 'name references not expanded on prefix matching' +fi +unset x +( + x=() + x.foo.bar=7 + [[ ${x.foo.bar} == 7 ]] || err_exit '[[ ${x.foo.bar} != 7 ]]' + (( x.foo.bar == 7 ))|| err_exit '(( x.foo.bar != 7 ))' + [[ ${x.foo} == *bar=7* ]] || err_exit '[[ ${x.foo} != *bar=7* ]]' +) +foo=(integer x=3) +if [[ ${foo} != *x=3* ]] +then err_exit "compound variable with integer subvariable not working" +fi +$SHELL -c $'x=(foo=bar)\n[[ x == x ]]' 2> /dev/null || + err_exit '[[ ... ]] not working after compound assignment' +unset foo +[[ ${!foo.@} ]] && err_exit 'unset compound variable leaves subvariables' +suitable=( + label="Table Viewer" + langs="ksh" + uselang=ksh + launch=no + groups="default" + default=( + label="Table Viewer Preferences" + entrylist=" \ + vieworigin viewsize viewcolor viewfontname viewfontsize \ + showheader header showfooter footer showtitle title showlegends \ + class_td_lg1_style class_tr_tr1_style \ + class_th_th1_style class_td_td1_style \ + fields fieldorder \ + " + entries=( + vieworigin=( + type=coord var=vieworigin val="0 0" label="Window Position" + ) + viewsize=( + type=coord var=viewsize val="400 400" label="Window Size" + ) + viewcolor=( + type=2colors var=viewcolor val="gray black" + label="Window Colors" + ) + viewfontname=( + type=fontname var=viewfontname val="Times-Roman" + label="Window Font Name" + ) + viewfontsize=( + type=fontsize var=viewfontsize val=14 label="Window Font Size" + ) + + showheader=( + type=yesno var=showheader val=no label="Show Header" + ) + header=( + type=text var=header val="" label="Header" + ) + + showfooter=( + type=yesno var=showfooter val=no label="Show Footer" + ) + footer=( + type=text var=footer val="" label="Footer" + ) + + showtitle=( + type=yesno var=showtitle val=yes label="Show Title" + ) + title=( + type=text var=title val="SWIFTUI - Table View" label="Title" + ) + + showlegends=( + type=yesno var=showlegends val=yes label="Show Legends" + ) + + class_td_lg1_style=( + type=style var=class_td_lg1_style + val="color: black; font-family: Times-Roman; font-size: 14pt" + label="Legend 1 Style" + ) + + class_tr_tr1_style=( + type=style var=class_tr_tr1_style val="background: black" + label="Table Row 1 Style" + ) + + class_th_th1_style=( + type=style var=class_th_th1_style + val="color: black; font-family: Times-Roman; font-size: 14pt; text-align: left" + label="Table Header 1 Style" + ) + + class_td_td1_style=( + type=style var=class_td_td1_style + val="color: black; font-family: Times-Roman; font-size: 14pt; text-align: left" + label="Table Cell 1 Style" + ) + + fields=( + type=text var=fields val= label="List of Fields" + ) + fieldorder=( + type=text var=fieldorder val= label="Order of Fields" + ) + ) + ) +) +[[ "${suitable}" == *entrylist=* ]] || err_exit 'compound variable expansion omitting fields' +foo=( bar=foo barbar=bar) +[[ $foo == *bar=foo* ]] || err_exit 'no prefix elements in compound variable output' +function localvar +{ + typeset point=(typeset -i x=3 y=4) + (( (point.x*point.x + point.y*point.y) == 25 )) || err_exit "local compound variable not working" +} +point=(integer x=6 y=8) +localvar + (( (point.x*point.x + point.y*point.y) == 100 )) || err_exit "global compound variable not preserved" +[[ $($SHELL -c 'foo=();foo.[x]=(y z); print ${foo.x[@]}') == 'y z' ]] 2> /dev/null || err_exit 'foo=( [x]=(y z) not working' +unset z +( [[ ${z.foo.bar:-abc} == abc ]] 2> /dev/null) || err_exit ':- not working with compound variables' +exit $((Errors)) + Index: src/lib/libshell/common/tests/builtins.sh =================================================================== --- src/lib/libshell/common/tests/builtins.sh (revision 0) +++ src/lib/libshell/common/tests/builtins.sh (revision 740) @@ -0,0 +1,451 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +# test shell builtin commands +Command=${0##*/} +integer Errors=0 +builtin getconf +: ${foo=bar} || err_exit ": failed" +[[ $foo = bar ]] || err_exit ": side effects failed" +set -- - foobar +[[ $# = 2 && $1 = - && $2 = foobar ]] || err_exit "set -- - foobar failed" +set -- -x foobar +[[ $# = 2 && $1 = -x && $2 = foobar ]] || err_exit "set -- -x foobar failed" +getopts :x: foo || err_exit "getopts :x: returns false" +[[ $foo = x && $OPTARG = foobar ]] || err_exit "getopts :x: failed" +OPTIND=1 +getopts :r:s var -r +if [[ $var != : || $OPTARG != r ]] +then err_exit "'getopts :r:s var -r' not working" +fi +OPTIND=1 +getopts :d#u var -d 100 +if [[ $var != d || $OPTARG != 100 ]] +then err_exit "'getopts :d#u var -d 100' not working var=$var" +fi +OPTIND=1 +while getopts 'ab' option -a -b +do [[ $OPTIND == $((OPTIND)) ]] || err_exit "OPTIND optimization bug" +done + +USAGE=$'[-][S:server?Operate on the specified \asubservice\a:]:[subservice:=pmserver] + { + [p:pmserver] + [r:repserver] + [11:notifyd] + }' +set pmser p rep r notifyd -11 +while (( $# > 1 )) +do OPTIND=1 + getopts "$USAGE" OPT -S $1 + [[ $OPT == S && $OPTARG == $2 ]] || err_exit "OPT=$OPT OPTARG=$OPTARG -- expected OPT=S OPTARG=$2" + shift 2 +done + +false ${foo=bar} && err_exit "false failed" +read < /dev/null + read line +} +read <<\! +\ +a\ +\ +\ +b +! +if [[ $REPLY != ab ]] +then err_exit "read multiple continuation failed" +fi +if [[ $line != two ]] +then err_exit "read from pipeline failed" +fi +line=two +read line < /dev/null +if [[ $line != "" ]] +then err_exit "read from /dev/null failed" +fi +if [[ $(print -R -) != - ]] +then err_exit "print -R not working correctly" +fi +if [[ $(print -- -) != - ]] +then err_exit "print -- not working correctly" +fi +print -f "hello%nbar\n" size > /dev/null +if (( size != 5 )) +then err_exit "%n format of printf not working" +fi +print -n -u2 2>&1- +[[ -w /dev/fd/1 ]] || err_exit "2<&1- with built-ins has side effects" +x=$0 +if [[ $(eval 'print $0') != $x ]] +then err_exit '$0 not correct for eval' +fi +unset x +readonly x +set -- $(readonly) +if [[ " $@ " != *" x "* ]] +then err_exit 'unset readonly variables are not displayed' +fi +if [[ $( for i in foo bar + do print $i + continue 10 + done + ) != $'foo\nbar' ]] +then err_exit 'continue breaks out of loop' +fi +(continue bad 2>/dev/null && err_exit 'continue bad should return an error') +(break bad 2>/dev/null && err_exit 'break bad should return an error') +(continue 0 2>/dev/null && err_exit 'continue 0 should return an error') +(break 0 2>/dev/null && err_exit 'break 0 should return an error') +breakfun() { break;} +continuefun() { continue;} +for fun in break continue +do if [[ $( for i in foo + do ${fun}fun + print $i + done + ) != foo ]] + then err_exit "$fun call in ${fun}fun breaks out of for loop" + fi +done +if [[ $(print -f "%b" "\a\n\v\b\r\f\E\03\\oo") != $'\a\n\v\b\r\f\E\03\\oo' ]] +then err_exit 'print -f "%b" not working' +fi +if [[ $(print -f "%P" "[^x].*b$") != '*[!x]*b' ]] +then err_exit 'print -f "%P" not working' +fi +if [[ $(abc: for i in foo bar;do print $i;break abc;done) != foo ]] +then err_exit 'break labels not working' +fi +if [[ $(command -v if) != if ]] +then err_exit 'command -v not working' +fi +read -r var <<\! + +! +if [[ $var != "" ]] +then err_exit "read -r of blank line not working" +fi +mkdir -p /tmp/ksh$$/a/b/c 2>/dev/null || err_exit "mkdir -p failed" +$SHELL -c "cd /tmp/ksh$$/a/b; cd c" 2>/dev/null || err_exit "initial script relative cd fails" +rm -r /tmp/ksh$$ || err_exit "rm -r /tmp/ksh$$ failed" +trap 'print HUP' HUP +if [[ $(trap) != "trap -- 'print HUP' HUP" ]] +then err_exit '$(trap) not working' +fi +if [[ $(trap -p HUP) != 'print HUP' ]] +then err_exit '$(trap -p HUP) not working' +fi +[[ $($SHELL -c 'trap "print ok" SIGTERM; kill -s SIGTERM $$' 2> /dev/null) == ok + ]] || err_exit 'SIGTERM not recognized' +[[ $($SHELL -c 'trap "print ok" sigterm; kill -s sigterm $$' 2> /dev/null) == ok + ]] || err_exit 'SIGTERM not recognized' +${SHELL} -c 'kill -1 -$$' 2> /dev/null +[[ $(kill -l $?) == HUP ]] || err_exit 'kill -1 -pid not working' +${SHELL} -c 'kill -1 -$$' 2> /dev/null +[[ $(kill -l $?) == HUP ]] || err_exit 'kill -n1 -pid not working' +${SHELL} -c 'kill -s HUP -$$' 2> /dev/null +[[ $(kill -l $?) == HUP ]] || err_exit 'kill -HUP -pid not working' +n=123 +typeset -A base +base[o]=8# +base[x]=16# +base[X]=16# +for i in d i o u x X +do if (( $(( ${base[$i]}$(printf "%$i" $n) )) != n )) + then err_exit "printf %$i not working" + fi +done +if [[ $( trap 'print done' EXIT) != done ]] +then err_exit 'trap on EXIT not working' +fi +if [[ $( trap 'print done' EXIT; trap - EXIT) == done ]] +then err_exit 'trap on EXIT not being cleared' +fi +if [[ $(type test) != 'test is a shell builtin' ]] +then err_exit 'whence -v test not a builtin' +fi +builtin -d test +if [[ $(type test) == *builtin* ]] +then err_exit 'whence -v test after builtin -d incorrect' +fi +typeset -Z3 percent=$(printf '%o\n' "'%'") +forrmat=\\${percent}s +if [[ $(printf "$forrmat") != %s ]] +then err_exit "printf $forrmat not working" +fi +if (( $(printf 'x\0y' | wc -c) != 3 )) +then err_exit 'printf \0 not working' +fi +if [[ $(printf "%bx%s\n" 'f\to\cbar') != $'f\to' ]] +then err_exit 'printf %bx%s\n not working' +fi +alpha=abcdefghijklmnop +if [[ $(printf "%10.*s\n" 5 $alpha) != ' abcde' ]] +then err_exit 'printf %10.%s\n not working' +fi +float x2=.0000625 +if [[ $(printf "%10.5E\n" x2) != 6.25000E-05 ]] +then err_exit 'printf "%10.5E" not normalizing correctly' +fi +x2=.000000001 +if [[ $(printf "%g\n" x2 2>/dev/null) != 1e-09 ]] +then err_exit 'printf "%g" not working correctly' +fi +#FIXME#($SHELL read -s foobar <<\! +#FIXME#testing +#FIXME#! +#FIXME#) 2> /dev/null || err_exit ksh read -s var fails +if [[ $(printf +3 2>/dev/null) != +3 ]] +then err_exit 'printf is not processing formats beginning with + correctly' +fi +if printf "%d %d\n" 123bad 78 >/dev/null 2>/dev/null +then err_exit "printf not exiting non-zero with conversion errors" +fi +if [[ $(trap --version 2> /dev/null;print done) != done ]] +then err_exit 'trap builtin terminating after --version' +fi +if [[ $(set --version 2> /dev/null;print done) != done ]] +then err_exit 'set builtin terminating after --veresion' +fi +unset -f foobar +function foobar +{ + print 'hello world' +} +OPTIND=1 +if [[ $(getopts $'[+?X\ffoobar\fX]' v --man 2>&1) != *'Xhello world'X* ]] +then err_exit '\f...\f not working in getopts usage strings' +fi +if [[ $(printf '%H\n' $'<>"& \'\tabc') != '<>"& ' abc' ]] +then err_exit 'printf %H not working' +fi +if [[ $(printf '%R %R %R %R\n' 'a.b' '*.c' '^' '!(*.*)') != '^a\.b$ \.c$ ^\^$ ^(.*\..*)!$' ]] +then err_exit 'printf %R not working' +fi +if [[ $(printf '%..:c\n' abc) != a:b:c ]] +then err_exit "printf '%..:c' not working" +fi +if [[ $(printf '%..*c\n' : abc) != a:b:c ]] +then err_exit "printf '%..*c' not working" +fi +if [[ $(printf '%..:s\n' abc def ) != abc:def ]] +then err_exit "printf '%..:s' not working" +fi +if [[ $(printf '%..*s\n' : abc def) != abc:def ]] +then err_exit "printf '%..*s' not working" +fi +[[ $(printf '%q\n') == '' ]] || err_exit 'printf "%q" with missing arguments' +# we won't get hit by the one second boundary twice, right? +[[ $(printf '%T\n' now) == "$(date)" ]] || +[[ $(printf '%T\n' now) == "$(date)" ]] || +err_exit 'printf "%T" now' +behead() +{ + read line + left=$(cat) +} +print $'line1\nline2' | behead +if [[ $left != line2 ]] +then err_exit "read reading ahead on a pipe" +fi +read -n1 y < /tmp/ksh$$ +chmod 755 /tmp/ksh$$ +trap 'rm -rf /tmp/ksh$$' EXIT +if [[ $($SHELL < /tmp/ksh$$) != hello ]] +then err_exit 'read of incomplete line not working correctly' +fi +set -f +set -- * +if [[ $1 != '*' ]] +then err_exit 'set -f not working' +fi +unset pid1 pid2 +false & +pid1=$! +pid2=$( + wait $pid1 + (( $? == 127 )) || err_exit "job known to subshell" + print $! +) +wait $pid1 +(( $? == 1 )) || err_exit "wait not saving exit value" +wait $pid2 +(( $? == 127 )) || err_exit "subshell job known to parent" +set --noglob +ifs=$IFS +IFS=, +set -- $(getconf LIBPATH) +IFS=$ifs +env= +for v +do IFS=: + set -- $v + IFS=$ifs + eval [[ \$$2 ]] && env="$env $2=\"\$$2\"" +done +set --glob +if [[ $(foo=bar; eval foo=\$foo $env exec -c \$SHELL -c \'print \$foo\') != bar ]] +then err_exit '"name=value exec -c ..." not working' +fi +$SHELL -c 'OPTIND=-1000000; getopts a opt -a' 2> /dev/null +[[ $? == 1 ]] || err_exit 'getopts with negative OPTIND not working' +getopts 'n#num' opt -n 3 +[[ $OPTARG == 3 ]] || err_exit 'getopts with numerical arguments failed' +if [[ $($SHELL -c $'printf \'%2$s %1$s\n\' world hello') != 'hello world' ]] +then err_exit 'printf %2$s %1$s not working' +fi +((n=0)) +((n++)); ARGC[$n]=1 ARGV[$n]="" +((n++)); ARGC[$n]=2 ARGV[$n]="-a" +((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2" +((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2 x" +((n++)); ARGC[$n]=4 ARGV[$n]="-a -v 2 x y" +for ((i=1; i<=n; i++)) +do set -- ${ARGV[$i]} + OPTIND=0 + while getopts -a tst "av:" OPT + do : + done + if [[ $OPTIND != ${ARGC[$i]} ]] + then err_exit "\$OPTIND after getopts loop incorrect -- got $OPTIND, expected ${ARGC[$i]}" + fi +done +unset a +{ read -N3 a; read -N1 b;} < /dev/null +then (print -n a; sleep 1;print -n bcde) > /tmp/fifo$$ & + { + read -u5 -n3 -t2 a || err_exit 'read -n3 from fifo timedout' + read -u5 -n1 -t2 b || err_exit 'read -n1 from fifo timedout' + } 5< /tmp/fifo$$ + [[ $a == a ]] || err_exit 'read -n3 from fifo not working' + rm -f /tmp/fifo$$ + mkfifo /tmp/fifo$$ 2> /dev/null + (print -n a; sleep 1;print -n bcde) > /tmp/fifo$$ & + { + read -u5 -N3 -t2 a || err_exit 'read -N3 from fifo timed out' + read -u5 -N1 -t2 b || err_exit 'read -N1 from fifo timedout' + } 5< /tmp/fifo$$ + [[ $a == abc ]] || err_exit 'read -N3 from fifo not working' + [[ $b == d ]] || err_exit 'read -N1 from fifo not working' +fi +rm -f /tmp/fifo$$ +function longline +{ + integer i + for((i=0; i < $1; i++)) + do print argument$i + done +} +# test command -x option +integer sum=0 n=10000 +if ! ${SHELL:-ksh} -c 'print $#' count $(longline $n) > /dev/null 2>&1 +then for i in $(command command -x ${SHELL:-ksh} -c 'print $#;[[ $1 != argument0 ]]' count $(longline $n) 2> /dev/null) + do ((sum += $i)) + done + (( sum == n )) || err_exit "command -x processed only $sum arguments" + command -p command -x ${SHELL:-ksh} -c 'print $#;[[ $1 == argument0 ]]' count $(longline $n) > /dev/null 2>&1 + [[ $? != 1 ]] && err_exit 'incorrect exit status for command -x' +fi +# test command -x option with extra arguments +integer sum=0 n=10000 +if ! ${SHELL:-ksh} -c 'print $#' count $(longline $n) > /dev/null 2>&1 +then for i in $(command command -x ${SHELL:-ksh} -c 'print $#;[[ $1 != argument0 ]]' count $(longline $n) one two three) #2> /dev/null) + do ((sum += $i)) + done + (( sum > n )) || err_exit "command -x processed only $sum arguments" + (( (sum-n)%3==0 )) || err_exit "command -x processed only $sum arguments" + (( sum == n+3)) && err_exit "command -x processed only $sum arguments" + command -p command -x ${SHELL:-ksh} -c 'print $#;[[ $1 == argument0 ]]' count $(longline $n) > /dev/null 2>&1 + [[ $? != 1 ]] && err_exit 'incorrect exit status for command -x' +fi +# test for debug trap +[[ $(typeset -i i=0 + trap 'print $i' DEBUG + while (( i <2)) + do (( i++)) + done) == $'0\n0\n1\n1\n2' ]] || err_exit "DEBUG trap not working" +getconf UNIVERSE - ucb +[[ $($SHELL -c 'echo -3') == -3 ]] || err_exit "echo -3 not working in ucb universe" +typeset -F3 start_x=SECONDS total_t delay=0.02 +typeset reps=50 leeway=5 +sleep $(( 2 * leeway * reps * delay )) | +for (( i=0 ; i < reps ; i++ )) +do read -N1 -t $delay +done +(( total_t = SECONDS - start_x )) +if (( total_t > leeway * reps * delay )) +then err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimum - too long" +elif (( total_t < reps * delay )) +then err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimum - too fast" +fi +exit $((Errors)) Index: src/lib/libshell/common/tests/alias.sh =================================================================== --- src/lib/libshell/common/tests/alias.sh (revision 0) +++ src/lib/libshell/common/tests/alias.sh (revision 740) @@ -0,0 +1,83 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +alias foo='print hello' +if [[ $(foo) != hello ]] +then err_exit 'foo, where foo is alias for "print hello" failed' +fi +if [[ $(foo world) != 'hello world' ]] +then err_exit 'foo world, where foo is alias for "print hello" failed' +fi +alias foo='print hello ' +alias bar=world +if [[ $(foo bar) != 'hello world' ]] +then err_exit 'foo bar, where foo is alias for "print hello " failed' +fi +if [[ $(foo \bar) != 'hello bar' ]] +then err_exit 'foo \bar, where foo is alias for "print hello " failed' +fi +alias bar='foo world' +if [[ $(bar) != 'hello world' ]] +then err_exit 'bar, where bar is alias for "foo world" failed' +fi +if [[ $(alias bar) != "bar='foo world'" ]] +then err_exit 'alias bar, where bar is alias for "foo world" failed' +fi +unalias foo || err_exit "unalias foo failed" +alias foo 2> /dev/null && err_exit "alias for non-existent alias foo returns true" +unset bar +alias bar="print foo$bar" +bar=bar +if [[ $(bar) != foo ]] +then err_exit 'alias bar, where bar is alias for "print foo$bar" failed' +fi +unset bar +alias bar='print hello' +if [[ $bar != '' ]] +then err_exit 'alias bar cause variable bar to be set' +fi +alias !!=print +if [[ $(!! hello 2>/dev/null) != hello ]] +then err_exit 'alias for !!=print not working' +fi +alias foo=echo +if [[ $(print "$(foo bar)" ) != bar ]] +then err_exit 'alias in command substitution not working' +fi +( unalias foo) +if [[ $(foo bar 2> /dev/null) != bar ]] +then err_exit 'alias not working after unalias in subshell' +fi +builtin -d rm 2> /dev/null +if whence rm > /dev/null +then [[ ! $(alias -t | grep rm= ) ]] && err_exit 'tracked alias not set' + PATH=$PATH + [[ $(alias -t | grep rm= ) ]] && err_exit 'tracked alias not cleared' +fi +exit $((Errors)) Index: src/lib/libshell/common/tests/options.sh =================================================================== --- src/lib/libshell/common/tests/options.sh (revision 0) +++ src/lib/libshell/common/tests/options.sh (revision 740) @@ -0,0 +1,313 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +if [[ $( ${SHELL-ksh} -s hello<<-\! + print $1 + ! + ) != hello ]] +then err_exit "${SHELL-ksh} -s not working" +fi +x=$( + set -e + false && print bad + print good +) +if [[ $x != good ]] +then err_exit 'sh -e not workuing' +fi +[[ $($SHELL -D -c 'print hi; print $"hello"') == '"hello"' ]] || err_exit 'ksh -D not working' + +tmp=/tmp/ksh$$ +mkdir $tmp +rc=$tmp/.kshrc +print $'function env_hit\n{\n\tprint OK\n}' > $rc + +export ENV=$rc +if [[ -o privileged ]] +then + [[ $(print env_hit | $SHELL 2>&1) == "OK" ]] && + err_exit 'privileged nointeractive shell reads $ENV file' + [[ $(print env_hit | $SHELL -E 2>&1) == "OK" ]] && + err_exit 'privileged -E reads $ENV file' + [[ $(print env_hit | $SHELL +E 2>&1) == "OK" ]] && + err_exit 'privileged +E reads $ENV file' + [[ $(print env_hit | $SHELL --rc 2>&1) == "OK" ]] && + err_exit 'privileged --rc reads $ENV file' + [[ $(print env_hit | $SHELL --norc 2>&1) == "OK" ]] && + err_exit 'privileged --norc reads $ENV file' +else + [[ $(print env_hit | $SHELL 2>&1) == "OK" ]] && + err_exit 'nointeractive shell reads $ENV file' + [[ $(print env_hit | $SHELL -E 2>&1) == "OK" ]] || + err_exit '-E ignores $ENV file' + [[ $(print env_hit | $SHELL +E 2>&1) == "OK" ]] && + err_exit '+E reads $ENV file' + [[ $(print env_hit | $SHELL --rc 2>&1) == "OK" ]] || + err_exit '--rc ignores $ENV file' + [[ $(print env_hit | $SHELL --norc 2>&1) == "OK" ]] && + err_exit '--norc reads $ENV file' +fi + +export ENV= +if [[ -o privileged ]] +then + [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + err_exit 'privileged nointeractive shell reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] && + err_exit 'privileged -E ignores empty $ENV' + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + err_exit 'privileged +E reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] && + err_exit 'privileged --rc ignores empty $ENV' + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + err_exit 'privileged --norc reads $HOME/.kshrc file' +else + [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + err_exit 'nointeractive shell reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] && + err_exit '-E ignores empty $ENV' + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + err_exit '+E reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] && + err_exit '--rc ignores empty $ENV' + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + err_exit '--norc reads $HOME/.kshrc file' +fi + +unset ENV +if [[ -o privileged ]] +then + [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + err_exit 'privileged nointeractive shell reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] && + err_exit 'privileged -E reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + err_exit 'privileged +E reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] && + err_exit 'privileged --rc reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + err_exit 'privileged --norc reads $HOME/.kshrc file' +else + [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + err_exit 'nointeractive shell reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] || + err_exit '-E ignores $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + err_exit '+E reads $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] || + err_exit '--rc ignores $HOME/.kshrc file' + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + err_exit '--norc reads $HOME/.kshrc file' +fi + +rm -rf $tmp + +if command set -G 2> /dev/null +then mkdir /tmp/ksh$$ + cd /tmp/ksh$$ + mkdir bar foo + > bar.c > bam.c + > bar/foo.c > bar/bam.c + > foo/bam.c + set -- **.c + expected='bam.c bar.c' + [[ $* == $expected ]] || + err_exit "-G **.c failed -- expected '$expected', got '$*'" + set -- ** + expected='bam.c bar bar.c bar/bam.c bar/foo.c foo foo/bam.c' + [[ $* == $expected ]] || + err_exit "-G ** failed -- expected '$expected', got '$*'" + set -- **/*.c + expected='bam.c bar.c bar/bam.c bar/foo.c foo/bam.c' + [[ $* == $expected ]] || + err_exit "-G **/*.c failed -- expected '$expected', got '$*'" + set -- **/bam.c + expected='bam.c bar/bam.c foo/bam.c' + [[ $* == $expected ]] || + err_exit "-G **/bam.c failed -- expected '$expected', got '$*'" + cd ~- + rm -rf /tmp/ksh$$ +fi + +mkdir /tmp/ksh$$ +cd /tmp/ksh$$ +t="<$$>.profile.<$$>" +echo "echo '$t'" > .profile +cp $SHELL ./-ksh +if [[ -o privileged ]] +then + [[ $(HOME=$PWD $SHELL -l &1) == *$t* ]] && + err_exit 'privileged -l reads .profile' + [[ $(HOME=$PWD $SHELL --login &1) == *$t* ]] && + err_exit 'privileged --login reads .profile' + [[ $(HOME=$PWD $SHELL --login-shell &1) == *$t* ]] && + err_exit 'privileged --login-shell reads .profile' + [[ $(HOME=$PWD $SHELL --login_shell &1) == *$t* ]] && + err_exit 'privileged --login_shell reads .profile' + [[ $(HOME=$PWD exec -a -ksh $SHELL &1) == *$t* ]] && + err_exit 'privileged exec -a -ksh ksh reads .profile' + [[ $(HOME=$PWD ./-ksh -i &1) == *$t* ]] && + err_exit 'privileged ./-ksh reads .profile' + [[ $(HOME=$PWD ./-ksh -ip &1) == *$t* ]] && + err_exit 'privileged ./-ksh -p reads .profile' +else + [[ $(HOME=$PWD $SHELL -l &1) == *$t* ]] || + err_exit '-l ignores .profile' + [[ $(HOME=$PWD $SHELL --login &1) == *$t* ]] || + err_exit '--login ignores .profile' + [[ $(HOME=$PWD $SHELL --login-shell &1) == *$t* ]] || + err_exit '--login-shell ignores .profile' + [[ $(HOME=$PWD $SHELL --login_shell &1) == *$t* ]] || + err_exit '--login_shell ignores .profile' + [[ $(HOME=$PWD exec -a -ksh $SHELL &1) == *$t* ]] || + err_exit 'exec -a -ksh ksh ignores .profile' + [[ $(HOME=$PWD ./-ksh -i &1) == *$t* ]] || + err_exit './-ksh ignores .profile' + [[ $(HOME=$PWD ./-ksh -ip &1) == *$t* ]] && + err_exit './-ksh -p does not ignore .profile' +fi +cd ~- +rm -rf /tmp/ksh$$ + + +# { exec interactive login_shell restricted xtrace } in the following test + +for opt in \ + allexport all-export all_export \ + bgnice bg-nice bg_nice \ + clobber emacs \ + errexit err-exit err_exit \ + glob \ + globstar glob-star glob_star \ + gmacs \ + ignoreeof ignore-eof ignore_eof \ + keyword log markdirs monitor notify \ + pipefail pipe-fail pipe_fail \ + trackall track-all track_all \ + unset verbose vi \ + viraw vi-raw vi_raw +do old=$opt + if [[ ! -o $opt ]] + then old=no$opt + fi + + set --$opt || err_exit "set --$opt failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + [[ -o no$opt ]] && err_exit "[[ -o no$opt ]] failed" + [[ -o no-$opt ]] && err_exit "[[ -o no-$opt ]] failed" + [[ -o no_$opt ]] && err_exit "[[ -o no_$opt ]] failed" + [[ -o ?$opt ]] || err_exit "[[ -o ?$opt ]] failed" + [[ -o ?no$opt ]] || err_exit "[[ -o ?no$opt ]] failed" + [[ -o ?no-$opt ]] || err_exit "[[ -o ?no-$opt ]] failed" + [[ -o ?no_$opt ]] || err_exit "[[ -o ?no_$opt ]] failed" + + set --no$opt || err_exit "set --no$opt failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + [[ -o $opt ]] && err_exit "[[ -o $opt ]] failed" + + set --no-$opt || err_exit "set --no-$opt failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + [[ -o $opt ]] && err_exit "[[ -o $opt ]] failed" + + set --no_$opt || err_exit "set --no_$opt failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + [[ -o $opt ]] && err_exit "[[ -o $opt ]] failed" + + set -o $opt || err_exit "set -o $opt failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + set -o $opt=1 || err_exit "set -o $opt=1 failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + set -o no$opt=0 || err_exit "set -o no$opt=0 failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + set --$opt=1 || err_exit "set --$opt=1 failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + set --no$opt=0 || err_exit "set --no$opt=0 failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + + set -o no$opt || err_exit "set -o no$opt failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + set -o $opt=0 || err_exit "set -o $opt=0 failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + set -o no$opt=1 || err_exit "set -o no$opt=1 failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + set --$opt=0 || err_exit "set --$opt=0 failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + set --no$opt=1 || err_exit "set --no$opt=1 failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + + set -o no-$opt || err_exit "set -o no-$opt failed" + [[ -o no-$opt ]] || err_exit "[[ -o no-$opt ]] failed" + + set -o no_$opt || err_exit "set -o no_$opt failed" + [[ -o no_$opt ]] || err_exit "[[ -o no_$opt ]] failed" + + set +o $opt || err_exit "set +o $opt failed" + [[ -o no$opt ]] || err_exit "[[ -o no$opt ]] failed" + + set +o no$opt || err_exit "set +o no$opt failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + + set +o no-$opt || err_exit "set +o no-$opt failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + + set +o no_$opt || err_exit "set +o no_$opt failed" + [[ -o $opt ]] || err_exit "[[ -o $opt ]] failed" + + set --$old +done + +for opt in \ + exec interactive login_shell login-shell logi privileged \ + rc restricted xtrace +do [[ -o $opt ]] + y=$? + [[ -o no$opt ]] + n=$? + case $y$n in + 10|01) ;; + *) err_exit "[[ -o $opt ]] == [[ -o no$opt ]]" ;; + esac +done + +for opt in \ + foo foo-bar foo_bar +do if [[ -o ?$opt ]] + then err_exit "[[ -o ?$opt ]] should fail" + fi + if [[ -o ?no$opt ]] + then err_exit "[[ -o ?no$opt ]] should fail" + fi +done +false | true | true || err_exit 'pipe not exiting exit value of last element' +true | true | false && err_exit 'pipe not exiting false' +set -o pipefail +false | true | true && err_exit 'pipe with first not failing with pipefail' +true | false | true && err_exit 'pipe middle not failing with pipefail' +true | true | false && err_exit 'pipe last not failing with pipefail' +exit $((Errors)) Index: src/lib/libshell/common/tests/path.sh =================================================================== --- src/lib/libshell/common/tests/path.sh (revision 0) +++ src/lib/libshell/common/tests/path.sh (revision 740) @@ -0,0 +1,186 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +mkdir /tmp/ksh$$ +cd /tmp/ksh$$ +trap "PATH=$PATH; cd /; rm -rf /tmp/ksh$$" EXIT +(PATH="/bin") +[[ $($SHELL -c 'print -r -- "$PATH"') == "$PATH" ]] || err_exit 'export PATH lost in subshell' +cat > bug1 <<- \EOF + print print ok > /tmp/ok$$ + /bin/chmod 755 /tmp/ok$$ + trap 'cd /; rm -f /tmp/ok$$' EXIT + function a + { + typeset -x PATH=/tmp + ok$$ + } + path=$PATH + unset PATH + a + PATH=$path +} +EOF +[[ $($SHELL ./bug1 2> /dev/null) == ok ]] || err_exit "PATH in function not working" +cat > bug1 <<- \EOF + function lock_unlock + { + typeset PATH=/usr/bin + typeset -x PATH='' + } + + PATH=/usr/bin + : $(PATH=/usr/bin getconf PATH) + typeset -ft lock_unlock + lock_unlock +EOF +($SHELL ./bug1) 2> /dev/null || err_exit "path_delete bug" +mkdir tdir$$ +if $SHELL tdir$$ > /dev/null 2>&1 +then err_exit 'not an error to run ksh on a directory' +fi + +print 'print hi' > ls +if [[ $($SHELL ls 2> /dev/null) != hi ]] +then err_exit "$SHELL name not executing version in current directory" +fi +if [[ $(ls -d . 2>/dev/null) == . && $(PATH=/bin:/usr/bin:$PATH ls -d . 2>/dev/null) != . ]] +then err_exit 'PATH export in command substitution not working' +fi +pwd=$PWD +# get rid of leading and trailing : and trailing :. +PATH=${PATH%.} +PATH=${PATH%:} +PATH=${PATH#.} +PATH=${PATH#:} +path=$PATH +var=$(whence date) +dir=$(basename "$var") +for i in 1 2 3 4 5 6 7 8 9 0 +do if ! whence notfound$i 2> /dev/null + then cmd=notfound$i + break + fi +done +print 'print hello' > date +chmod +x date +print 'print notfound' > $cmd +chmod +x "$cmd" +> foo +chmod 755 foo +for PATH in $path :$path $path: .:$path $path: $path:. $PWD::$path $PWD:.:$path $path:$PWD $path:.:$PWD +do +# print path=$PATH $(whence date) +# print path=$PATH $(whence "$cmd") + date + "$cmd" +done > /dev/null 2>&1 +builtin -d date 2> /dev/null +if [[ $(PATH=:/usr/bin; date) != 'hello' ]] +then err_exit "leading : in path not working" +fi +( + PATH=$PWD: + builtin chmod + print 'print cannot execute' > noexec + chmod 644 noexec + if [[ ! -x noexec ]] + then noexec > /dev/null 2>&1 + else exit 126 + fi +) +status=$? +[[ $status == 126 ]] || err_exit "exit status of non-executable is $status -- 126 expected" +builtin -d rm 2> /dev/null +rm=$(whence rm) +d=$(dirname "$rm") +unset FPATH +PATH=/dev/null +if date > /dev/null 2>&1 +then err_exit 'programs in . should not be found' +fi +[[ $(whence ./foo) != "$PWD/"./foo ]] && err_exit 'whence ./foo not working' +[[ $(whence "$PWD/foo") != "$PWD/foo" ]] && err_exit 'whence $PWD/foo not working' +[[ $(whence ./xxxxx) ]] && err_exit 'whence ./xxxx not working' +PATH=$d: +cp "$rm" kshrm$$ +if [[ $(whence kshrm$$) != $PWD/kshrm$$ ]] +then err_exit 'trailing : in pathname not working' +fi +cp "$rm" rm +PATH=:$d +if [[ $(whence rm) != $PWD/rm ]] +then err_exit 'leading : in pathname not working' +fi +PATH=$d: whence rm > /dev/null +if [[ $(whence rm) != $PWD/rm ]] +then err_exit 'pathname not restored after scoping' +fi +mkdir bin +print 'print ok' > bin/tst +chmod +x bin/tst +if [[ $(PATH=$PWD/bin tst 2>/dev/null) != ok ]] +then err_exit '(PATH=$PWD/bin foo) does not find $PWD/bin/foo' +fi +cd / +if whence ls > /dev/null +then PATH= + if [[ $(whence rm) ]] + then err_exit 'setting PATH to Null not working' + fi + unset PATH + if [[ $(whence rm) != /*rm ]] + then err_exit 'unsetting path not working' + fi +fi +PATH=/dev:/tmp/ksh$$ +x=$(whence rm) +typeset foo=$(PATH=/xyz:/abc :) +y=$(whence rm) +[[ $x != "$y" ]] && err_exit 'PATH not restored after command substitution' +whence getconf > /dev/null && err_exit 'getconf should not be found' +builtin /bin/getconf +PATH=/bin +PATH=$(getconf PATH) +x=$(whence ls) +PATH=.:$PWD:${x%/ls} +[[ $(whence ls) == "$x" ]] || err_exit 'PATH search bug when .:$PWD in path' +PATH=$PWD:.:${x%/ls} +[[ $(whence ls) == "$x" ]] || err_exit 'PATH search bug when :$PWD:. in path' +cd "${x%/ls}" +[[ $(whence ls) == /* ]] || err_exit 'whence not generating absolute pathname' +status=$($SHELL -c $'trap \'print $?\' EXIT;/a/b/c/d/e 2> /dev/null') +[[ $status == 127 ]] || err_exit "not found command exit status $status -- expected 127" +status=$($SHELL -c $'trap \'print $?\' EXIT;/dev/null 2> /dev/null') +[[ $status == 126 ]] || err_exit "non executable command exit status $status -- expected 126" +status=$($SHELL -c $'trap \'print $?\' ERR;/a/b/c/d/e 2> /dev/null') +[[ $status == 127 ]] || err_exit "not found command with ERR trap exit status $status -- expected 127" +status=$($SHELL -c $'trap \'print $?\' ERR;/dev/null 2> /dev/null') +[[ $status == 126 ]] || err_exit "non executable command ERR trap exit status $status -- expected 126" +exit $((Errors)) Index: src/lib/libshell/common/tests/grep.sh =================================================================== --- src/lib/libshell/common/tests/grep.sh (revision 0) +++ src/lib/libshell/common/tests/grep.sh (revision 740) @@ -0,0 +1,102 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +function grep +{ + # + # SHELL VERSION OF GREP + # + vflag= xflag= cflag= lflag= nflag= + set -f + while ((1)) # look for grep options + do case "$1" in + -v*) vflag=1;; + -x*) xflag=1;; + -c*) cflag=1;; + -l*) lflag=1;; + -n*) nflag=1;; + -b*) print 'b option not supported';; + -e*) shift;expr="$1";; + -f*) shift;expr=$(< $1);; + -*) print $0: 'unknown flag';return 2;; + *) + if test "$expr" = '' + then expr="$1";shift + fi + test "$xflag" || expr="*${expr}*" + break;; + esac + shift # next argument + done + noprint=$vflag$cflag$lflag # don't print if these flags are set + integer n=0 c=0 tc=0 nargs=$# # initialize counters + for i in "$@" # go thru the files + do if ((nargs<=1)) + then fname='' + else fname="$i": + fi + test "$i" && exec 0< $i # open file if necessary + while read -r line # read in a line + do let n=n+1 + case "$line" in + $expr) # line matches pattern + test "$noprint" || print -r -- "$fname${nflag:+$n:}$line" + let c=c+1 ;; + *) # not a match + if test "$vflag" + then print -r -- "$fname${nflag:+$n:}$line" + fi;; + esac + done + if test "$lflag" && ((c)) + then print -r -- "$i" + fi + let tc=tc+c n=0 c=0 + done + test "$cflag" && print $tc # print count if cflag is set + let tc # set the return value +} + +trap 'rm -f /tmp/grep$$' EXIT +cat > /tmp/grep$$ <<\! +this is a food bar test +to see how many lines find both foo and bar. +Some line contain foo only, +and some lines contain bar only. +However, many lines contain both foo and also bar. +A line containing foobar should also be counted. +There should be six lines with foo and bar. +There are only two line with out foo but with bar. +! + +if (( $(grep -c 'foo*bar' /tmp/grep$$ ) != 6)) +then err_exit +fi +exit $((Errors)) Index: src/lib/libshell/common/tests/coprocess.sh =================================================================== --- src/lib/libshell/common/tests/coprocess.sh (revision 0) +++ src/lib/libshell/common/tests/coprocess.sh (revision 740) @@ -0,0 +1,218 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +# test the behavior of co-processes +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +if [[ -d /cygdrive ]] +then err_exit cygwin detected - coprocess tests disabled - enable at the risk of wedging your system + exit $((Errors)) +fi + +function ping # id +{ + integer x=0 + while ((x < 5)) + do read -r + print -r "$1 $REPLY" + done +} + +cat |& +print -p "hello" +read -p line +[[ $line == hello ]] || err_exit 'coprocessing fails' +exec 5>&p 6<&p +print -u5 'hello again' || err_exit 'write on u5 fails' +read -u6 line +[[ $line == 'hello again' ]] || err_exit 'coprocess after moving fds fails' +exec 5<&- 6<&- + +ping three |& +exec 3>&p +ping four |& +exec 4>&p +ping pipe |& + +integer count +for i in three four pipe four pipe four three pipe pipe three pipe +do case $i in + three) to=-u3;; + four) to=-u4;; + pipe) to=-p;; + esac + count=count+1 + print $to $i $count +done + +while ((count > 0)) +do count=count-1 + read -p +# print -r - "$REPLY" + set -- $REPLY + if [[ $1 != $2 ]] + then err_exit "$1 does not match 2" + fi + case $1 in + three);; + four) ;; + pipe) ;; + *) err_exit "unknown message +|$REPLY|+" + esac +done + +file=/tmp/regress$$ +trap "rm -f $file" EXIT +cat > $file <<\! +/bin/cat |& +! +chmod +x $file +$file 2> /dev/null || err_exit "parent coprocess prevents script coprocess" +exec 5<&p 6>&p +exec 5<&- 6>&- +${SHELL-ksh} |& +print -p $'print hello | cat\nprint Done' +read -t 5 -p +read -t 5 -p +if [[ $REPLY != Done ]] +then err_exit "${SHELL-ksh} coprocess not working" +fi +exec 5<&p 6>&p +exec 5<&- 6>&- +count=0 +{ +echo line1 | grep 'line2' +echo line2 | grep 'line1' +} |& +SECONDS=0 +while + read -p -t 10 line +do + ((count = count + 1)) + echo "Line $count: $line" +done +if (( SECONDS > 8 )) +then err_exit 'read -p hanging' +fi +( sleep 3 |& sleep 1 && kill $!; sleep 1; sleep 3 |& sleep 1 && kill $! ) || + err_exit "coprocess cleanup not working correctly" +unset line +( + integer n=0 + while read line + do echo $line |& + if cat <&p + then ((n++)) + wait $! + fi + done > /dev/null 2>&1 <<- ! + line1 + line2 + line3 + line4 + line5 + line6 + line7 + ! + (( n==7 )) && print ok +) | read -t 10 line +if [[ $line != ok ]] +then err_exit 'coprocess timing bug' +fi +( + /bin/cat |& + exec 6>&p + print -u6 ok + exec 6>&- + sleep 1 + kill $! 2> /dev/null +) && err_exit 'coprocess with subshell would hang' +for sig in IOT ABRT +do if ( trap - $sig ) 2> /dev/null + then if [[ $( + cat |& + pid=$! + trap "print TRAP" $sig + ( + sleep 2 + kill -$sig $$ + sleep 2 + kill -$sig $$ + kill $pid + ) 2> /dev/null & + read -p + ) != $'TRAP\nTRAP' ]] + then err_exit 'traps when reading from coprocess not working' + fi + break + fi +done + +trap 'sleep_pid=; kill $pid; err_exit "coprocess 1 hung"' TERM +{ sleep 5; kill $$; } & +sleep_pid=$! +builtin cat +cat |& +pid=$! +exec 5<&p 6>&p +print -u6 hi; read -u5 +[[ $REPLY == hi ]] || err_exit 'REPLY is $REPLY not hi' +exec 6>&- +wait $pid +trap - TERM +[[ $sleep_pid ]] && kill $sleep_pid + +trap 'sleep_pid=; kill $pid; err_exit "coprocess 2 hung"' TERM +{ sleep 5; kill $$; } & +sleep_pid=$! +cat |& +pid=$! +print foo >&p 2> /dev/null || err_exit 'first write of foo to coprocess failed' +print foo >&p 2> /dev/null || err_exit 'second write of foo to coprocess failed' +kill $pid +wait $pid 2> /dev/null +trap - TERM +[[ $sleep_pid ]] && kill $sleep_pid + +trap 'sleep_pid=; kill $pid; err_exit "coprocess 3 hung"' TERM +{ sleep 5; kill $$; } & +sleep_pid=$! +cat |& +pid=$! +print -p foo +print -p bar +read <&p || err_exit 'first read from coprocess failed' +[[ $REPLY == foo ]] || err_exit "first REPLY is $REPLY not foo" +read <&p || err_exit 'second read from coprocess failed' +[[ $REPLY == bar ]] || err_exit "second REPLY is $REPLY not bar" +kill $pid +wait $pid 2> /dev/null +trap - TERM +[[ $sleep_pid ]] && kill $sleep_pid + +exit $((Errors)) Index: src/lib/libshell/common/tests/arrays.sh =================================================================== --- src/lib/libshell/common/tests/arrays.sh (revision 0) +++ src/lib/libshell/common/tests/arrays.sh (revision 740) @@ -0,0 +1,381 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +function fun +{ + integer i + unset xxx + for i in 0 1 + do xxx[$i]=$i + done +} + +Command=${0##*/} +integer Errors=0 +set -A x zero one two three four 'five six' +if [[ $x != zero ]] +then err_exit '$x is not element 0' +fi +if [[ ${x[0]} != zero ]] +then err_exit '${x[0] is not element 0' +fi +if (( ${#x[0]} != 4 )) +then err_exit "length of ${x[0]} is not 4" +fi +if (( ${#x[@]} != 6 )) +then err_exit 'number of elements of x is not 6' +fi +if [[ ${x[2]} != two ]] +then err_exit ' element two is not 2' +fi +if [[ ${x[@]:2:1} != two ]] +then err_exit ' ${x[@]:2:1} is not two' +fi +set -A y -- ${x[*]} +if [[ $y != zero ]] +then err_exit '$x is not element 0' +fi +if [[ ${y[0]} != zero ]] +then err_exit '${y[0] is not element 0' +fi +if (( ${#y[@]} != 7 )) +then err_exit 'number of elements of y is not 7' +fi +if [[ ${y[2]} != two ]] +then err_exit ' element two is not 2' +fi +set +A y nine ten +if [[ ${y[2]} != two ]] +then err_exit ' element two is not 2' +fi +if [[ ${y[0]} != nine ]] +then err_exit '${y[0] is not nine' +fi +unset y[4] +if (( ${#y[@]} != 6 )) +then err_exit 'number of elements of y is not 6' +fi +if (( ${#y[4]} != 0 )) +then err_exit 'string length of unset element is not 0' +fi +unset foo +if (( ${#foo[@]} != 0 )) +then err_exit 'number of elements of unset variable foo is not 0' +fi +foo='' +if (( ${#foo[0]} != 0 )) +then err_exit 'string length of null element is not 0' +fi +if (( ${#foo[@]} != 1 )) +then err_exit 'number of elements of null variable foo is not 1' +fi +unset foo +foo[0]=foo +foo[3]=bar +unset foo[0] +unset foo[3] +if (( ${#foo[@]} != 0 )) +then err_exit 'number of elements of left in variable foo is not 0' +fi +unset foo +foo[3]=bar +foo[0]=foo +unset foo[3] +unset foo[0] +if (( ${#foo[@]} != 0 )) +then err_exit 'number of elements of left in variable foo again is not 0' +fi +fun +if (( ${#xxx[@]} != 2 )) +then err_exit 'number of elements of left in variable xxx is not 2' +fi +fun +if (( ${#xxx[@]} != 2 )) +then err_exit 'number of elements of left in variable xxx again is not 2' +fi +set -A foo -- "${x[@]}" +if (( ${#foo[@]} != 6 )) +then err_exit 'number of elements of foo is not 6' +fi +if (( ${#PWD[@]} != 1 )) +then err_exit 'number of elements of PWD is not 1' +fi +unset x +x[2]=foo x[4]=bar +if (( ${#x[@]} != 2 )) +then err_exit 'number of elements of x is not 2' +fi +s[1]=1 c[1]=foo +if [[ ${c[s[1]]} != foo ]] +then err_exit 'c[1]=foo s[1]=1; ${c[s[1]]} != foo' +fi +unset s +typeset -Ai s +y=* z=[ +s[$y]=1 +s[$z]=2 +if (( ${#s[@]} != 2 )) +then err_exit 'number of elements of is not 2' +fi +(( s[$z] = s[$z] + ${s[$y]} )) +if [[ ${s[$z]} != 3 ]] +then err_exit '[[ ${s[$z]} != 3 ]]' +fi +if (( s[$z] != 3 )) +then err_exit '(( s[$z] != 3 ))' +fi +(( s[$y] = s[$y] + ${s[$z]} )) +if [[ ${s[$y]} != 4 ]] +then err_exit '[[ ${s[$y]} != 4 ]]' +fi +if (( s[$y] != 4 )) +then err_exit '(( s[$y] != 4 ))' +fi +unset y +set -A y 2 4 6 +typeset -i y +z=${y[@]} +typeset -R12 y +typeset -i y +if [[ ${y[@]} != "$z" ]] +then err_exit 'error in array conversion from int to R12' +fi +if (( ${#y[@]} != 3 )) +then err_exit 'error in count of array conversion from int to R12' +fi +unset abcdefg +: ${abcdefg[1]} +set | grep '^abcdefg$' >/dev/null && err_exit 'empty array variable in set list' +unset x y +x=1 +typeset -i y[$x]=4 +if [[ ${y[1]} != 4 ]] +then err_exit 'arithmetic expressions in typeset not working' +fi +unset foo +typeset foo=bar +typeset -A foo +if [[ ${foo[0]} != bar ]] +then err_exit 'initial value not preserved when typecast to associative' +fi +unset foo +foo=(one two) +typeset -A foo +foo[two]=3 +if [[ ${#foo[*]} != 3 ]] +then err_exit 'conversion of indexed to associative array failed' +fi +set a b c d e f g h i j k l m +if [[ ${#} != 13 ]] +then err_exit '${#} not 13' +fi +unset xxx +xxx=foo +if [[ ${!xxx[@]} != 0 ]] +then err_exit '${!xxx[@]} for scalar not 0' +fi +if [[ ${11} != k ]] +then err_exit '${11} not working' +fi +if [[ ${@:4:1} != d ]] +then err_exit '${@:4:1} not working' +fi +foovar1=abc +foovar2=def +if [[ ${!foovar@} != +(foovar[[:alnum:]]?([ ])) ]] +then err_exit '${!foovar@} does not expand correctly' +fi +if [[ ${!foovar1} != foovar1 ]] +then err_exit '${!foovar1} != foovar1' +fi +unset xxx +: ${xxx[3]} +if [[ ${!xxx[@]} ]] +then err_exit '${!xxx[@]} should be null' +fi +integer i=0 +{ + set -x + xxx[++i]=1 + set +x +} 2> /dev/null +if (( i != 1)) +then err_exit 'execution trace side effects with array subscripts' +fi +unset list +: $(set -A list foo bar) +if (( ${#list[@]} != 0)) +then err_exit '$(set -A list ...) leaves side effects' +fi +unset list +list= (foo bar bam) +( set -A list one two three four) +if [[ ${list[1]} != bar ]] +then err_exit 'array not restored after subshell' +fi +XPATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:.:/sbin:/usr/sbin +xpath=( $( IFS=: ; echo $XPATH ) ) +if [[ $(print -r "${xpath[@]##*/}") != 'bin bin ucb bin . sbin sbin' ]] +then err_exit '${xpath[@]##*/} not applied to each element' +fi +foo=( zero one '' three four '' six) +integer n=-1 +if [[ ${foo[@]:n} != six ]] +then err_exit 'array offset of -1 not working' +fi +if [[ ${foo[@]: -3:1} != four ]] +then err_exit 'array offset of -3:1 not working' +fi +$SHELL -c 'x=(if then else fi)' 2> /dev/null || err_exit 'reserved words in x=() assignment not working' +unset foo +foo=one +foo=( $foo two) +if [[ ${#foo[@]} != 2 ]] +then err_exit 'array getting unset before right hand side evaluation' +fi +foo=(143 3643 38732) +export foo +typeset -i foo +if [[ $($SHELL -c 'print $foo') != 143 ]] +then err_exit 'exporting indexed array not exporting 0-th element' +fi +( $SHELL -c ' + unset foo + typeset -A foo=([0]=143 [1]=3643 [2]=38732) + export foo + typeset -i foo + [[ $($SHELL -c "print $foo") == 143 ]]' +) 2> /dev/null || + err_exit 'exporting associative array not exporting 0-th element' +unset foo +typeset -A foo +foo[$((10))]=ok 2> /dev/null || err_exit 'arithmetic expression as subscript not working' +unset foo +typeset -A foo +integer foo=0 +[[ $foo == 0 ]] || err_exit 'zero element of associative array not being set' +unset foo +typeset -A foo=( [two]=1) +for i in one three four five +do : ${foo[$i]} +done +if [[ ${!foo[@]} != two ]] +then err_exit 'Error in subscript names' +fi +unset x +x=( 1 2 3) +(x[1]=8) +[[ ${x[1]} == 2 ]] || err_exit 'index array produce side effects in subshells' +x=( 1 2 3) +( + x+=(8) + [[ ${#x[@]} == 4 ]] || err_exit 'index array append in subshell error' +) +[[ ${#x[@]} == 3 ]] || err_exit 'index array append in subshell effects parent' +x=( [one]=1 [two]=2 [three]=3) +(x[two]=8) +[[ ${x[two]} == 2 ]] || err_exit 'associative array produce side effects in subshells' +unset x +x=( [one]=1 [two]=2 [three]=3) +( + x+=( [four]=4 ) + [[ ${#x[@]} == 4 ]] || err_exit 'associative array append in subshell error' +) +[[ ${#x[@]} == 3 ]] || err_exit 'associative array append in subshell effects parent' +unset x +integer i +for ((i=0; i < 40; i++)) +do x[i]=$i +done +[[ ${#x[@]} == 40 ]] || err_exit 'index arrays loosing values' +[[ $( ($SHELL -c 'typeset -A var; (IFS=: ; set -A var a:b:c ;print ${var[@]});:' )2>/dev/null) == 'a b c' ]] || err_exit 'change associative to index failed' +unset foo +[[ $(foo=good +for ((i=0; i < 2; i++)) +do [[ ${foo[i]} ]] && print ok +done) == ok ]] || err_exit 'invalid optimization for subscripted variables' +( +x=([foo]=bar) +set +A x bam +) 2> /dev/null && err_exit 'set +A with associative array should be an error' +unset bam foo +foo=0 +typeset -A bam +unset bam[foo] +bam[foo]=value +[[ $bam == value ]] && err_exit 'unset associative array element error' +: only first element of an array can be exported +unset bam +trap 'rm -f /tmp/sharr$$' EXIT +print 'print ${var[0]} ${var[1]}' > /tmp/sharr$$ +chmod +x /tmp/sharr$$ +[[ $($SHELL -c "var=(foo bar);export var;/tmp/sharr$$") == foo ]] || err_exit 'export array not exporting just first element' +unset foo +set -o allexport +foo=one +foo[1]=two +foo[0]=three +[[ $foo == three ]] || err_exit 'export all not working with arrays' +cat > /tmp/sharr$$ <<- \! + typeset -A foo + print foo${foo[abc]} +! +# 04-05-24 bug fix +unset foo +[[ $($SHELL -c "typeset -A foo;/tmp/sharr$$") == foo ]] 2> /dev/null || err_exit 'empty associative arrays not being cleared correctly before scripts' +[[ $($SHELL -c "typeset -A foo;foo[abc]=abc;/tmp/sharr$$") == foo ]] 2> /dev/null || err_exit 'associative arrays not being cleared correctly before scripts' +unset foo +foo=(one two) +[[ ${foo[@]:1} == two ]] || err_exit '${foo[@]:1} == two' +[[ ! ${foo[@]:2} ]] || err_exit '${foo[@]:2} not null' +unset foo +foo=one +[[ ! ${foo[@]:1} ]] || err_exit '${foo[@]:1} not null' +function EMPTY +{ + typeset i + typeset -n ARRAY=$1 + for i in ${!ARRAY[@]} + do unset ARRAY[$i] + done +} +unset foo +typeset -A foo +foo[bar]=bam +foo[x]=y +EMPTY foo +[[ $(typeset | grep foo$) == *associative* ]] || err_exit 'array lost associative attribute' +[[ ! ${foo[@]} ]] || err_exit 'array not empty' +[[ ! ${!foo[@]} ]] || err_exit 'array names not empty' +unset foo +foo=bar +set -- "${foo[@]:1}" +(( $# == 0 )) || err_exit '${foo[@]:1} should not have any values' +unset bar +: ${_foo[bar=4]} +(( bar == 4 )) || err_exit 'subscript of unset variable not evaluated' +exit $((Errors)) Index: src/lib/libshell/common/tests/tilde.sh =================================================================== --- src/lib/libshell/common/tests/tilde.sh (revision 0) +++ src/lib/libshell/common/tests/tilde.sh (revision 740) @@ -0,0 +1,87 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r $Command: "$@" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +function home # id +{ + typeset IFS=: pwd=/etc/passwd + set -o noglob + if [[ -f $pwd ]] && grep -c "^$1:" $pwd > /dev/null + then set -- $(grep "^$1:" $pwd) + print -r -- "$6" + else print . + fi +} + +Command=${0##*/} +integer Errors=0 +OLDPWD=/bin +if [[ ~ != $HOME ]] +then err_exit '~' not $HOME +fi +x=~ +if [[ $x != $HOME ]] +then err_exit x=~ not $HOME +fi +x=x:~ +if [[ $x != x:$HOME ]] +then err_exit x=x:~ not x:$HOME +fi +if [[ ~+ != $PWD ]] +then err_exit '~' not $PWD +fi +x=~+ +if [[ $x != $PWD ]] +then err_exit x=~+ not $PWD +fi +if [[ ~- != $OLDPWD ]] +then err_exit '~' not $PWD +fi +x=~- +if [[ $x != $OLDPWD ]] +then err_exit x=~- not $OLDPWD +fi +for u in root Administrator +do h=$(home $u) + if [[ $h != . ]] + then [[ ~$u -ef $h ]] || err_exit "~$u not $h" + x=~$u + [[ $x -ef $h ]] || "x=~$u not $h" + break + fi +done +x=~%% +if [[ $x != '~%%' ]] +then err_exit 'x='~%%' not '~%% +fi +x=~:~ +if [[ $x != "$HOME:$HOME" ]] +then err_exit x=~:~ not $HOME:$HOME +fi +HOME=/ +[[ ~ == / ]] || err_exit '~ should be /' +[[ ~/foo == /foo ]] || err_exit '~/foo should be /foo when ~==/' +exit $((Errors)) Index: src/lib/libshell/common/llib-lshell =================================================================== --- src/lib/libshell/common/llib-lshell (revision 0) +++ src/lib/libshell/common/llib-lshell (revision 740) @@ -0,0 +1,138 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * lib/libshell/common/llib-lshell + * + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include +#include + +/* automatically generated data start here */ +extern const char e_defpath[]; +extern const char e_found[]; +extern const char e_nospace[]; +extern const char e_format[]; +extern const char e_number[]; +extern const char e_restricted[]; +extern const char e_recursive[]; +extern char e_version[]; +extern Dt_t *sh_bltin_tree (void); +extern void sh_subfork (void); +extern Shell_t *sh_init (int,char*[],Shinit_f); +extern int sh_reinit (char*[]); +extern int sh_eval (Sfio_t*,int); +extern void sh_delay (double); +extern void *sh_parse (Shell_t*, Sfio_t*,int); +extern int sh_trap (const char*,int); +extern int sh_fun (Namval_t*,Namval_t*, char*[]); +extern int sh_funscope (int,char*[],int(*)(void*),void*,int); +extern Sfio_t *sh_iogetiop (int,int); +extern int sh_main (int, char*[], void(*)(int)); +extern void sh_menu (Sfio_t*, int, char*[]); +extern Namval_t *sh_addbuiltin (const char*, int(*)(int, char*[],void*), void*); +extern char *sh_fmtq (const char*); +extern char *sh_fmtqf (const char*, int, int); +extern Sfdouble_t sh_strnum (const char*, char**, int); +extern int sh_access (const char*,int); +extern int sh_close (int); +extern int sh_dup (int); +extern void sh_exit (int); +extern int sh_fcntl (int, int, ...); +extern Sfio_t *sh_fd2sfio (int); +extern Shell_t *sh_getinterp (void); +extern int sh_open (const char*, int, ...); +extern int sh_openmax (void); +extern Sfio_t *sh_pathopen (const char*); +extern ssize_t sh_read (int, void*, size_t); +extern ssize_t sh_write (int, const void*, size_t); +extern off_t sh_seek (int, off_t, int); +extern int sh_pipe (int[]); +extern mode_t sh_umask (mode_t); +extern void *sh_waitnotify (Shwait_f); +extern Shscope_t *sh_getscope (int,int); +extern Shscope_t *sh_setscope (Shscope_t*); +extern void sh_sigcheck (void); +extern unsigned long sh_isoption (int); +extern unsigned long sh_onoption (int); +extern unsigned long sh_offoption (int); +extern int sh_waitsafe (void); +extern int sh_exec (const Shnode_t*,int); +extern int sh_waitsafe(void); +extern int sh_exec(const Shnode_t*,int); +extern void **sh_getliblist(void); +extern Shell_t sh; +extern Namarr_t *nv_setarray (Namval_t*,void*(*)(Namval_t*,const char*,int)); +extern void *nv_associative (Namval_t*,const char*,int); +extern int nv_aindex (Namval_t*); +extern int nv_nextsub (Namval_t*); +extern char *nv_getsub (Namval_t*); +extern Namval_t *nv_putsub (Namval_t*, char*, long); +extern Namval_t *nv_opensub (Namval_t*); +extern int nv_adddisc (Namval_t*, const char**, Namval_t**); +extern int nv_clone (Namval_t*, Namval_t*, int); +extern void nv_close (Namval_t*); +extern void *nv_context (Namval_t*); +extern Namval_t *nv_create (const char*, Dt_t*, int,Namfun_t*); +extern Dt_t *nv_dict (Namval_t*); +extern Sfdouble_t nv_getn (Namval_t*, Namfun_t*); +extern Sfdouble_t nv_getnum (Namval_t*); +extern char *nv_getv (Namval_t*, Namfun_t*); +extern char *nv_getval (Namval_t*); +extern Namfun_t *nv_hasdisc (Namval_t*, const Namdisc_t*); +extern int nv_isnull (Namval_t*); +extern Namval_t *nv_lastdict (void); +extern void nv_newattr (Namval_t*,unsigned,int); +extern Namval_t *nv_open (const char*,Dt_t*,int); +extern void nv_putval (Namval_t*,const char*,int); +extern void nv_putv (Namval_t*,const char*,int,Namfun_t*); +extern int nv_scan (Dt_t*,void(*)(Namval_t*,void*),void*,int,int); +extern Namval_t *nv_scoped (Namval_t*); +extern char *nv_setdisc (Namval_t*,const char*,Namval_t*,Namfun_t*); +extern void nv_setref (Namval_t*, Dt_t*,int); +extern int nv_settype (Namval_t*, Namval_t*, int); +extern void nv_setvec (Namval_t*,int,int,char*[]); +extern void nv_setvtree (Namval_t*); +extern int nv_setsize (Namval_t*,int); +extern Namfun_t *nv_disc (Namval_t*,Namfun_t*,int); +extern void nv_unset (Namval_t*); +extern Namval_t *nv_search (const char *, Dt_t*, int); +extern void nv_unscope (void); +extern char *nv_name (Namval_t*); +extern Namval_t *nv_type (Namval_t*); +extern const Namdisc_t *nv_discfun (int); +/* end of automatically generated data */ + +/* Manually added based on libshell/common/include/builtins.h */ +extern int b_printf(int, char*[],void*); +extern int B_echo(int, char*[],void*); +extern int b_print(int, char*[],void*); +extern int b_pwd(int, char*[],void*); +extern int b_sleep(int, char*[],void*); +extern int b_test(int, char*[],void*); Index: src/lib/libshell/common/PROMO.mm =================================================================== --- src/lib/libshell/common/PROMO.mm (revision 0) +++ src/lib/libshell/common/PROMO.mm (revision 740) @@ -0,0 +1,141 @@ +.H 1 ksh93 +KSH-93 is the most recent version of the KornShell Language +described in +"The KornShell Command and Programming Language," +by Morris Bolsky and David Korn of AT&T Bell Laboratories, ISBN 0-13-182700-6. +The KornShell is a shell programming language, +which is upward compatible with "sh" (the Bourne Shell), +and is intended to conform to the IEEE P1003.2/ISO 9945.2 Shell and +Utilities standard. +KSH-93 provides an enhanced programming environment in +addition to the major command-entry features of the BSD +shell "csh". With KSH-93, medium-sized programming tasks can be +performed at shell-level without a significant loss in performance. +In addition, "sh" scripts can be run on KSH-93 without modification. +.P +The code should conform to the IEEE POSIX 1003.1 standard and to the +proposed ANSI-C standard so that it should be portable to all +such systems. Like the previous version, KSH-88, +it is designed to accept eight bit character sets +transparently, thereby making it internationally compatible. +It can support multi-byte characters sets with some characteristics +of the character set given at run time. +.P +KSH-93 provides the following features, many of which were also inherent +in KSH-88: +.BL +.LI +Enhanced Command Re-entry Capability: The KSH-93 history +function records commands entered at any shell level and stores +them, up to a user-specified limit, even after you log off. +This allows you to re-enter long commands with a few keystrokes +- even those commands you entered yesterday. +The history file allows for eight bit characters in +commands and supports essentially unlimited size histories. +.LI +In-line Editing: In "sh", the only way to fix mistyped +commands is to backspace or retype the line. KSH-93 allows you +to edit a command line using a choice of EMACS-TC or "vi" +functions. +You can use the in-line editors to complete filenames as +you type them. +You may also use this editing feature when entering +command lines from your history file. +A user can capture keystrokes and rebind keys to customize the +editing interface. +.LI +Extended I/O Capabilities: KSH-93 provides several I/O +capabilities not available in "sh", including the ability to: +.BL +.LI +specify a file descriptor for input and output +.LI +start up and run co-processes +.LI +produce a prompt at the terminal before a read +.LI +easily format and interpret responses to a menu +.LI +echo lines exactly as output without escape processing +.LI +format output using printf formats. +.LI +read and echo lines ending in "\e". +.LE +.LI +Improved performance: KSH-93 executes many scripts faster +than the System V Bourne shell. A major reason for this is +that many of the standard utilities are built-in. +To reduce the time to initiate a command, KSH-93 allows +commands to be added as built-ins at run time +on systems that support dynamic loading such as System V Release 4. +.LI +Arithmetic: KSH-93 allows you to do integer arithmetic in any +base from two to sixty-four. You can also do double +precision floating point arithmetic. +Almost the complete set of C language operators are available +with the same syntax and precedence. +Arithmetic expressions can be used to as an argument expansion +or as a separate command. +In addition there is an arithmetic for command that works +like the for statement in C. +.LI +Arrays: KSH-93 supports both indexed and associative arrays. +The subscript for an indexed array is an arithmetic expression, +whereas, the subscript for an associative array is a string. +.LI +Shell Functions and Aliases: Two mechanisms - functions and +aliases - can be used to assign a user-selected identifier to +an existing command or shell script. +Functions allow local variables and provide scoping +for exception handling. +Functions can be searched for and loaded on first reference the +way scripts are. +.LI +Substring Capabilities: KSH-93 allows you to create a +substring of any given string either by specifying the starting +offset and length, or by stripping off leading +or trailing substrings during parameter substitution. +You can also specify attributes, such as upper and lower case, +field width, and justification to shell variables. +.LI +More pattern matching capabilities: KSH-93 allows you to specify +extended regular expressions for file and string matches. +.LI +KSH-93 uses a hierarchal name space for variables. +Compound variables can be defined and variables can +be passed by reference. In addition, each variable +can have one or more disciplines associated with +it to intercept assignments and references. +.LI +Improved debugging: KSH-93 can generate line numbers on execution +traces. Also, I/O redirections are now traced. +There is a DEBUG trap that gets evaluated after each command +so that errors can be localized. +.LI +Job Control: On systems that support job control, including +System V Release 4, KSH-93 +provides a job-control mechanism almost identical to that of +the BSD "csh", version 4.1. +This feature allows you +to stop and restart programs, and to move programs between the +foreground and the background. +.LI +Added security: +KSH-93 can execute scripts which do not have read permission +and scripts which have the setuid and/or setgid set when +invoked by name, rather than as an argument to the shell. +It is possible to log or control the execution of setuid and/or +setgid scripts. +The noclobber option prevents you from accidentally erasing +a file by redirecting to an existing file. +.LI +KSH-93 can be extended by adding built-in commands at run time. +In addition, KSH-93 can be used as a library that can +be embedded into an application to allow scripting. +.LE +Documentation for KSH-93 consists of an "Introduction to KSH-93", +"Compatibility with the Bourne Shell" and a manual page and a +README file. In addition, the "New KornShell Command and Programming +Language," book is available from Prentice Hall. + Index: src/lib/libshell/common/sh.memo =================================================================== --- src/lib/libshell/common/sh.memo (revision 0) +++ src/lib/libshell/common/sh.memo (revision 740) @@ -0,0 +1,3248 @@ +. \" use troff -mm +.nr C 3 +.nr N 2 +.SA 1 \" right justified +.ND "December 21, 1993" +.TL "311466-6713" "61175" \" charging case filing case +Introduction to \f5ksh-93\fP +.AU "David G. Korn" DGK MH 11267 7975 3C-526B "(research!dgk)" +.TM 11267-931221-26 \" technical memo + TM numbers +.MT 1 \" memo type +.OK Shell "Command interpreter" Language UNIX \" keyword +.AS 2 \" abstract start for TM +\f5ksh-93\fP is a +major rewrite of \f5ksh\fP, +a program that serves as a command language +(shell) for the UNIX* +.FS * +UNIX is a registered trademark of Novell. +.FE +operating system. +As with \f5ksh\fP, \f5ksh-93\fP +is essentially compatible with the System V version of the Bourne shell\*(Rf, +.RS +S. R. Bourne, +.I "An Introduction to the UNIX +Shell," +BSTJ - Vol. 57, No. 6 part 2, pages 1947-1972, 1978. +.RF +and compatible with previous versions of \f5ksh\fP. +\f5ksh-93\fP is intended to comply with the IEEE POSIX 1003.2 +and ISO 9945-2\*(Rf +.RS +.I "POSIX \- Part 2: Shell and Utilities," +IEEE Std 1003.2-1992, ISO/IEC 9945-2, IEEE, 1993. +.RF +shell standard. +In addition to changes in the language required +by these standards, the primary focus of \f5ksh-93\fP +is related to shell programming. +\f5ksh-93\fP provides the programming power of several +other interpretive languages such as \f5awk\fP\*(Rf, +.RS +Al Aho, +Brian Kernighan, +and +Peter Weinberger, +.I "The AWK Programming Language," +Addison Wesley, 1988. +.RF +\f5FIT\fP\*(Rf, +.RS +Lloyd H. Nakatani and Laurence W. Ruedisueli, +.I "The FIT Programming Language Primer", +TM 1126-920301-03, 1992. +.RF +\f5PERL\fP\*(Rf, +.RS +Larry Wall and Randal Schwartz, +.I "Programming perl," +O'Reilly & Assoc, 1990. +.RF +and +\f5tcl\fP\*(Rf. +.RS +John K. Ousterhout, +.I "Tcl: An Embeddable Command Language", +Proceedings of the Washington USENIX meeting, pp. 133-146, 1990. +.RF +.P +This memo +assumes that the reader is already familiar with the Bourne shell. +It introduces most of the features of \f5ksh-93\fP +relative to the Bourne shell; both +as a command language and as a programming language. +The Appendix contains +a sample script written in \f5ksh-93\fP. +.AE \" abstract end +.H 1 "INTRODUCTION" +.P +The term "shell" is used to describe a program that provides +a command language +interface. +Because the UNIX*\ +.FS * +UNIX is a registered trademark of USL +.FE +system shell is a user level program, and not part of +the operating system itself, +anyone can write a new shell or modify an existing one. +This has caused an evolutionary progress +in the design and implementation of shells, +with the better ones surviving. +The most widely available UNIX system shells are the Bourne shell\*(Rf, +.RS +S. R. Bourne, +.IR "An Introduction to the UNIX Shell" , +Bell System Technical Journal, +Vol. 57, No. 6, Part 2, pp. 1947-1972, July 1978. +.RF +written by Steve Bourne +at AT&T Bell Laboratories, +the C shell\*(Rf, +.RS +W. Joy, +.IR "An Introduction to the C Shell" , +Unix Programmer's Manual, Berkeley Software Distribution, +University of California, Berkeley, 1980. +.RF +written by Bill Joy at the University of California, Berkeley, +and the KornShell language \*(Rf, +.RS +Morris Bolsky and David Korn, +.IR "The KornShell Command and Programming Language" , +Prentice Hall, 1989. +.RF +written by David Korn +at AT&T Bell Laboratories. +The Bourne shell is available on almost all versions of the UNIX +system. +The C Shell is available with all Berkeley Software Distribution (BSD) UNIX systems and on many other systems. +The KornShell +is available on System V Release 4 systems. +In addition, it is available on many other systems. +The source for the KornShell language is available from the AT&T Toolchest, +an electronic software distribution system. +It runs on all known versions of the UNIX system and +on many UNIX system look-alikes. +.P +There have been several articles comparing the UNIX system shells. +Jason Levitt\*(Rf +.RS +Jason Levitt, +.IR "The Korn Shell: An Emerging Standard" , +UNIX/World, pp. 74-81, September 1986. +.RF +highlights some of the new features +introduced by the KornShell language. +Rich Bilancia\*(Rf +.RS +Rich Bilancia, +.IR "Proficiency and Power are Yours With the Korn Shell" , +UNIX/World, pp. 103-107, September 1987. +.RF +explains some of the advantages of using the KornShell language. +John Sebes\*(Rf +.RS +John Sebes, +.I "Comparing UNIX Shells," +UNIX Papers, +Edited by the Waite Group, Howard W. Sams & Co., 1987. +.RF +provides a more detailed comparison of the three shells, +both as a command language and as a programming language. +.P +The KornShell language is a superset of the +Bourne shell. The KornShell language has many of the popular C shell features, +plus additional features of its own. +Its initial popularity stems primarily from its improvements as +a command language. +The primary interactive benefit of the KornShell command language +is a visual command line editor that allows you to +make corrections to your current command line +or to earlier command lines, +without having to retype them. +.P +However, +in the long run, +the power of the KornShell language as a high-level programming language, +as described by Dolotta and Mashey\*(Rf, +.RS +T. A. Dolotta and J. R. Mashey, +.I "Using the shell as a Primary Programming Tool," +Proc. 2nd. Int. Conf. on Software Engineering, 1976, +pages 169-176. +.RF +may prove to be of greater significance. +\f5ksh-93\fP provides the programming power of several +other interpretive languages such as \f5awk\fP, +\f5FIT\fP, +\f5PERL\fP, +and +\f5tcl\fP. +An application that was originally written in the C programming language +was rewritten in the KornShell language. +More than 20,000 lines of C code were replaced with KornShell scripts +totaling fewer than 700 lines. +In most instances there was no perceptible difference in performance +between the two versions of the code. +.P +The KornShell language has been embedded into windowing systems +allowing graphical user interfaces to be developed in shell +rather than having to build applications that need to be +compiled. +The \f5wksh\fP program\*(Rf +.RS +J. S. Pendergrast, +.IR "WKSH - Korn Shell with X-Windows Support", +USL. 1991. +.RF +provides a method of developing OpenLook or Motif +applications as \f5ksh\fP scripts. +.P +This memo is an introduction to \f5ksh-93\fP, +the program that implements an enhanced version +of the KornShell language. +It is referred to as \f5ksh\fP in the rest of this memo. +The memo describes the KornShell language based on the +features of the 12/28/93 release of \f5ksh\fP. +This memo is not a tutorial, only an introduction. +The second edition of reference [9] gives +a more complete treatment of the KornShell language. +.P +A concerted effort has been made to achieve both System V Bourne shell +compatibility and IEEE POSIX compatibility +so that scripts written for either of these shells +can run without modification with \f5ksh\fP. +In addition, \f5ksh-93\fP attempts to +be compatible with older versions of \f5ksh\fP. +When there are conflicts between versions of the shell, +\f5ksh-93\fP selects the behavior dictated by the IEEE POSIX +standard. +The description of features in this memo assumes +that the reader is already familiar with the Bourne shell. +.H 1 "COMMAND LANGUAGE" +There is no separate command language. +All features of the language, except job control, +can be +used both within a script and interactively from a terminal. +However, features that are more likely to be used +while running commands interactively from a terminal +are presented here. +.H 2 "Setting Options" +By convention, UNIX commands +consist of a command name followed by options and other arguments. +Options are either of the form \f5-\fP\fIletter\fP, +or \f5-\fP\fIletter value\fP. +In the former case, several options may be grouped after a single \f5-\fP. +The argument \f5--\fP signifies an end to the option list and is +only required when the first non-option argument begins with +a \f5-\fP. +Most commands print an error message which +shows which options are permitted +when given incorrect arguments. +In addition, the option sequence \f5-?\fP causes most commands +to print a usage message which lists the valid options. +.P +Ordinarily, \f5ksh\fP executes a command by +using the command name to locate a program to run +and by running the program as a separate process. +Some commands, referred to as +.IR built-ins , +are carried out by \f5ksh\fP itself, +without creating a separate process. +The reasons that some commands are built-in are presented later. +In nearly all cases the distinction +between a command that is built-in and one that +is not is invisible to the user. +However, nearly +all commands that are built-in follow command line conventions. +.P +\f5ksh\fP has several options that can be set by the user +as command line arguments at invocation and as option arguments to the +\f5set\fP command. +Most other options can be set with a single letter option or as a name +that follows the \f5-o\fP option. +Use +\f5set\ -o\fP +to display the current option settings. +Some of these options, such as +.B interactive +and +.B monitor +(see +.I "Job Control" +below), +are enabled automatically by \f5ksh\fP +when the shell is connected to a terminal device. +Other options, such as +.B noclobber +and +.BR ignoreeof , +are normally placed in a startup file. +The +.B noclobber +option causes +\f5ksh\fP +to print an error message when you use +.B > +to redirect output to a file that already exists. +If you want to redirect to an existing file, then +you have to use +.B >| +to override +the +.B noclobber +option. +The +.B ignoreeof +option +is used to prevent the +.I end-of-file +character, normally +.B ^D +(Control- d), +from exiting the shell and possibly logging you out. +You must type \f5exit\fP +to log out. +Most of the options are described in this memo as appropriate. +.H 2 "Command Aliases" +.P +Command aliases provide a mechanism of associating a command name and +arguments with a shorter name. +Aliases are defined with the \f5alias\fP +built-in. +The form of an \f5alias\fP +command definition is: +.ce +\f5alias\fP \fIname\fP\f5=\fP\fIvalue\fP +As with most other shell assignments, no space is allowed before or after +the \f5=\fP. +The characters of an alias name cannot be characters that are +special to the shell. +The replacement string, +.I value, +can contain any valid shell script, +including meta-characters such as pipe symbols and i/o-redirection +provided that they are quoted. +Unlike +\f5csh\fP, +aliases in +\f5ksh\fP +cannot take arguments. +The equivalent functionality of aliases with arguments can +be achieved with shell functions, described later. +.P +As a command is being read, +the command name is checked against a list of +.I alias +names. +If it is found, +the name is replaced by the alias value associated with the +.I alias +and then rescanned. +When rescanning the value for an alias, alias substitutions +are performed except for an alias that is currently being processed. +This prevents infinite loops in alias substitutions. +For example with the aliases, \f5alias\ l=ls\ 'ls=ls\ -C'\fP, +the command name \f5l\fP becomes \f5ls\fP, which becomes \f5ls\ -C\fP. +Ordinarily, only the command name word is processed for alias substitution. +However, if the value of an alias ends in a space, +then the word following the alias is also checked for alias substitution. +This makes it possible +to define an alias whose first argument is the name of a command +and have alias substitution performed on this argument, +for example +\f5nohup='nohup\ '\fP. +.P +Aliases can be used to redefine built-in commands so that +the alias, +.ce +\f5alias test=./test\fP +can be used to look for \f5test\fP +in your current working directory rather than +using the built-in \f5test\fP command. +Reserved words such as +\f5for\fP and \f5while\fP +cannot be changed by aliasing. +The command \f5alias\fP, +without arguments, generates +a list of aliases and corresponding alias values. +The \f5unalias\fP command removes the name and text of an alias. +.P +Aliases are used to save typing and to improve readability of scripts. +Several aliases are predefined by \f5ksh\fP. +For example, the predefined alias +.ce +\f5alias integer='typeset -i'\fP +allows the integer variables \f5i\fP and \f5j\fP +to be declared and initialized with the command +.ce +\f5integer i=0 j=1\fP +.P +While aliases can be defined in scripts, +it is not recommended. +The location of an alias command can be important +since aliases are only processed when a command is read. +A \fB\s+2.\s-2\fP +procedure (the shell equivalent of an include file) +is read all at once (unlike +start up files +which are read a command at +a time) so that any aliases defined there will not effect any commands +within this script. +Predefined aliases do not have this problem. +.H 2 "Command Re-entry" +.P +When run interactively, +\f5ksh\fP saves the +commands you type at a terminal in a file. +If the variable +\fB\s-1HISTFILE\s+1\fP +is set to the name of a file to which the user +has write access, +then the commands are stored in this +.I history +file. +Otherwise the file +\fB$\s-1HOME\s+1/.sh_history\fP +is checked for write access and if this fails +an unnamed file is used to hold the history lines. +Commands are always appended to this file. +Instances of \f5ksh\fP +that run concurrently and use the same history file +name, share access to the history file so that a command +entered in one shell will be available for editing in another +shell. +The file may be truncated when \f5ksh\fP +determines that no other shell is using the history file. +The number of commands accessible to the user is determined by the value of the +\fB\s-1HISTSIZE\s+1\fP +variable at the time the shell is invoked. +The default value is 256. +Each command may consist of one or more lines since a compound +command is considered one command. +If the character +.B ! +is placed within the +.I "primary prompt" +string, +\fB\s-1PS1\s+1\fP, +then it is replaced by the command number each time the prompt is given. +.P +A built-in command named \f5hist\fP +is used to list and/or edit +any of these saved commands. +The option +.B \-l +is used to specify listing of previous commands. +The command can always be specified with +a range of one or more commands. +The range can be specified by giving the command +number, relative or absolute, or by giving +the first character or characters of the command. +When given without specifying the range, +the last 16 +commands are listed, each +preceded by the command number. +.P +If the listing option is not selected, +then the range of commands specified, +or the last command if no range is given, +is passed to an editor program before +being re-executed by \f5ksh\fP. +The editor to be used may be specified +with the option +.B \-e +and following it with the editor name. +If this option is not specified, the +value of the shell variable +\fB\s-1HISTEDIT\s+1\fP +is used as the name of the editor, +providing that this variable has a non-null value. +If this variable is not set, or is null, +and the +.B \-e +option has not been selected, +then +\f5/bin/ed\fP +is used. +When editing has been complete, +the edited text automatically becomes +the input for \f5ksh\fP. +As this text is read by \f5ksh\fP, it is echoed onto the terminal. +.P +The +.B \-s +option causes the editing to be bypassed +and just re-executes the command. +In this case only a single command can be specified as the range +and an optional argument of the form +\fIold\fP\fB=\fP\fInew\fP +may be added which requests a simple string substitution +prior to evaluation. +A convenient alias, +.ce +\f5alias r='hist -s'\fP +has been pre-defined so that +the single key-stroke +\f5r\fP +can be used to re-execute the previous command +and the key-stroke sequence, +\f5r\ abc=def\ c\fP +can be used to re-execute the last command that starts with +the letter \f5c\fP +with the first occurrence of the string \f5abc\fP +replaced with the string \f5def\fP. +Typing +\f5r\ c\ >\ file\fP +re-executes the most recent command starting with the letter \f5c\fP, +with standard output redirected to +.IR file . +.H 2 "In-line editing" +.P +Lines typed from a terminal frequently need changes made +before entering them. +With the Bourne shell the only method to fix up commands +is by backspacing or killing the whole line. +\f5ksh\fP offers options that allow the user to edit parts of the +current command line before submitting the command. +The in-line edit options make the command line into a single +line screen edit window. +When the command is longer than the width of the terminal, +only a portion of the command is visible. +Moving within the line automatically makes that portion visible. +Editing can be performed on this window until the +.I return +key is pressed. +The editing modes have editing directives that access the history file +in which previous commands are saved. +A user can copy any of the most recent +\fB\s-1HISTSIZE\s+1\fP +commands from this file into the input edit window. +You can locate commands by searching or by position. +.P +The in-line editing options do not use the +.I termcap +or +.I terminfo +databases. +They work on most standard terminals. +They only require that the backspace character moves the cursor left +and the space character overwrites the current character on the screen +and moves the cursor to the right. +Very few terminals or terminal emulators do not have +this behavior. +.P +There is a choice of editor options. +The +.BR emacs , +.BR gmacs , +or +.B vi +option is selected by turning on the +corresponding +option of the \f5set\fP +command. +If the value of the +\fB\s-1EDITOR\s+1\fP +or +\fB\s-1VISUAL\s+1\fP +variables ends with any of these suffixes +the corresponding option is turned on. +A large subset of each of these editors' +features is available within the shell. Additional +functions, such as file name completion, have also been added. +.P +In the +.B emacs +or +.B gmacs +mode the user positions the cursor to the point +needing correction and inserts, deletes, or replaces +characters as needed. +The only difference between these two modes is the +meaning of the directive +.BR ^T . +Control keys and escape sequences are used for cursor +positioning and control functions. +The available editing functions are listed in the manual page. +.P +The +.B vi +editing mode +starts in insert mode and enters control mode when the +user types ESC ( 033 ). +The +.I return +key, which submits the current command for processing, +can be entered from either mode. +The cursor can be anywhere on the line. +A subset of commonly used +.I vi +editing directives are available. +The +.B k +and +.B j +directives that normally move up and down by one +.IR line , +move up and down one +.I command +in the history file, +copying the command into the input edit window. +For reasons of efficiency, +the terminal is kept in canonical mode until an +ESC +is typed. +On some terminals, +and on earlier versions of the UNIX operating system, +this doesn't work correctly. +The +.B viraw +option, +which always uses +.I raw +or +.I cbreak +mode, +must be used in this case. +.P +Most of the code for the editing options does not rely on the +\f5ksh\fP code and can be used in a stand-alone mode with most any command +to add in-line edit capability. +However, +all versions of the in-line editors have some features that +use some shell specific code. For example, +with all edit modes, the +ESC-= +directive applied to command words +(the first word on the line, +or the first word after a +.BR ; , +.BR | , +.BR ( , +or +.BR & ) +lists all aliases, functions, or commands +that match the portion of the given current word. +When applied to other words, this directive +prints the names of files that match the current +word. +The ESC\fB-*\fP directive +adds the expanded list of matching files to the command line. +A trailing +.B * +is added to the word if it doesn't contain any file pattern matching +characters before the expansion. +In +.B emacs +and +.B gmacs +mode, +ESC-ESC +indicates command completion when applied to +command names, otherwise it indicates pathname completion. +With command or pathname completion, +the list generated by the +ESC-= directive is examined to find +the longest common prefix. +With command completion, only the last component of +the pathname is used to compute the longest command prefix. +If the longest common prefix is a complete match, +then the word is replaced by the pathname, and a +.B / +is appended if +pathname is a directory, otherwise a space is added. +In +.B vi +mode, +.B \e +from control mode gives the same behavior. +.H 2 "Key Binding" +.P +It is possible to intercept keys as they are entered and +apply new meanings or bindings. +A trap named +\fB\s-1KEYBD\s+1\fP +is evaluated each time +\f5ksh\fP processes characters entered +from the keyboard, +other than those typed +while entering a search string or an argument to an +edit directive such as +.B r +in vi-mode. +The action associated with this trap can change the value of +the entered key to cause the key to perform a different +operation. +.P +When the +\fB\s-1KEYBD\s+1\fP +trap is entered, +the \fB.sh.edtext\fP +variable contains the contents of the current input line +and the \fB.sh.edcol\fP +variable gives the current cursor position within this line. +The \fB.sh.edmode\fP +variable contains the +.B ESC +character when the trap is entered from +.B vi +insert mode. +Otherwise, this value is null. +The \fB.sh.edchar\fP +variable contains the character or +escape sequence that caused the trap. +A key sequence is either a single character, +.B ESC +followed by a single character, +or +.B ESC[ +followed by a single character. +In the \fBvi\fP edit mode, +the characters after the +.B ESC +must be entered within half a second after the +.BR ESC . +The value of \fB.sh.edchar\fP +at the end of the trap will be used as +the input sequence. +.P +Using the associative array facility of \f5ksh\fP described later, +and the function facility of \f5ksh\fP, it is easy to write +a single trap so that keys can be bound dynamically. For example, +.sp +.nf +.in .5i +.ta 4i +\f5typeset -A Keytable +trap 'eval "${Keytable[${.sh.edchar}]}"' KEYBD +function keybind # key action +{ + typeset key=$(print -f "%q" "$2") + case $# in + 2) Keytable[$1]='.sh.edchar=${.sh.edmode}'"$key" + ;; + 1) unset Keytable[$1] + ;; + *) print -u2 "Usage: $0 key [action]" + ;; + esac +}\fP +.ta +.in +.fi +.sp +.H 2 "Job Control" +.P +The job control mechanism +is almost identical to the version introduced in \f5csh\fP +of the Berkeley UNIX operating system, +version 4.1 and later. +The job control feature allows the user to stop and +restart programs, and to move programs to and from the +foreground and the background. +It will only work on systems that provide support for +these features. +However, +even systems without job control have a +.B monitor +option which, when enabled, will report the progress +of background jobs and enable the user to \f5kill\fP +jobs by job number or job name. +.P +An interactive shell associates a +.I job +with each pipeline typed in from the terminal +and assigns it a small integer number +called the job number. +If the job is run asynchronously, +the job number is printed at the terminal. +At any given time, only one job owns the terminal, +i.e., keyboard signals are only sent to the processes in one job. +When \f5ksh\fP creates a foreground job, +it gives it ownership of the terminal. +If you are running a job and wish to stop +it you hit the key +.B ^Z +(control-\fBZ\fP) +which sends a +\fB\s-1STOP\s+1\fP +signal to all processes in the current job. +The shell receives notification that the processes +have stopped and takes back control of the terminal. +.P +There are commands to continue programs in the foreground +and background. +There are several ways to refer to jobs. +The character +.B % +introduces a job name. +You can refer to jobs by name or number as described in the manual page. +The built-in command \f5bg\fP +allows you to continue a job in the background, +while the built-in command \f5fg\fP +allows you to continue a job in the foreground even +though you may have started it in the background. +.P +A job being run in the background will stop if it tries +to read from the terminal. +It is also possible to stop background jobs that try to write on +the terminal by setting the terminal options +appropriately. +.P +There is a built-in command \f5jobs\fP +that lists the status of all running and stopped jobs. +In addition, +you are informed of the change of state (running or stopped) +of any background +jobs just before each prompt. +If you want to be notified about background job completions +as soon as they occur without waiting for a prompt, then use the +.B notify +option. +When you try to exit the shell while jobs are stopped or running, +you will receive a message from \f5ksh\fP. +If you ignore this message and try to exit again, +all stopped processes will be terminated. +In addition, for login shells, the +\fB\s-1HUP\s+1\fP +signal will be sent to +all background jobs +unless the job has been disowned with the +.B disown +command. +.P +A built-in version of \f5kill\fP +makes it possible to use +.I job +numbers as targets for signals. +Signals can be selected by number or name. +The name of the signal is the name found in the +.I include +file +.B /usr/include/sys/signal.h +with the prefix +.B \s-1SIG\s+1 +removed. +The +.B \-l +option of \f5kill\fP +provides a means to map individual signal names to and from +signal number. +In addition, if no signal name or number is given, +\f5kill\ -l\fP +generates a list of valid signal names. +.H 2 "Changing Directories" +By default, +\f5ksh\fP +maintains a logical view of the file system hierarchy +which makes symbolic links transparent. +For systems that have symbolic links, +this means that if \f5/bin\fP is a symbolic link to \f5/usr/bin\fP +and you change directory to \f5/bin\fP, \f5pwd\fP will indicate +that you are in \f5/bin\fP, not \f5/usr/bin\fP. +\f5pwd\ -P\fP +generates the physical pathname of the present working +directory by resolving all the symbolic links. +By default, +the \f5cd\fP +command will take you where you expect to go even if you cross +symbolic links. +A subsequent \f5cd\ ..\fP in the example above +will place you in \f5/\fP, not \f5/usr\fP. +On systems with symbolic links, +\f5cd\ -P\fP +causes +.B .. +to be treated physically. +.P +\f5ksh\fP remembers your last directory +in the variable +\fB\s-1OLDPWD\s+1\fP. +The \f5cd\fP +built-in can be given with argument +.B \- +to return to the previous directory +and print the name of the directory. +Note that \f5cd\ -\fP +done twice returns you to the starting directory, +not the second previous directory. +A directory +.I stack +manager has been written as shell +.I functions +to +.I push +and +.I pop +directories from the stack. +.H 2 "Prompts" +.P +When \f5ksh\fP +reads commands from a terminal, +it issues a prompt whenever it is ready +to accept more input and then +waits for the user to respond. +The +\fB\s-1TMOUT\s+1\fP +variable +can be set to be the number of seconds that the shell will wait for +input before terminating. +A 60 second warning message is printed +before terminating. +.P +The shell uses two prompts. +The primary prompt, +defined by the value of the +\fB\s-1PS1\s+1\fP +variable, +is issued at the start of each command. +The secondary prompt, +defined by the value of the +\fB\s-1PS2\s+1\fP +variable, +is issued when more input is needed to complete a command. +.P +\f5ksh\fP allows the user to specify a list of files or directories +to check before issuing the +\fB\s-1PS1\s+1\fP +prompt. +The variable +\fB\s-1MAILPATH\s+1\fP +is a colon ( +.B : +) separated list of file names to be checked for changes +periodically. The user is notified +before the next prompt. +Each of the names in this list can be followed by a +.B ? +and a message to be given when a change has been detected in the file. +The prompt will be evaluated for parameter expansion, command +substitution and arithmetic expansion which are described later. +The parameter +.B $_ +within a mail message will evaluate to the name of the file that +has changed. +The parameter +\fB\s-1MAILCHECK\s+1\fP +is used to specify the minimal interval in seconds before +new mail is checked for. +.P +In addition to replacing each +.B ! +in the prompt with the command number, +\f5ksh\fP expands +the value of the +.B \s-1PS1\s+1 +variable +for parameter expansions, arithmetic expansions, +and command substitutions as described below +to generate the prompt. +The expansion characters that are to be applied when +the prompt is issued must be quoted to prevent the +expansions from occurring when assigning the value to +.B \s-1PS1\s+1. +For example, +\f3\s-1PS1\s+1="$\s-1PWD\s+1"\fP +causes +.B \s-1PS1\s+1 +to be set to the value of +.B \s-1PWD\s+1 +at the time of the assignment whereas +.B \s-1PS1\s+1='$\s-1PWD\s+1' +causes +.B \s-1PWD\s+1 +to be expanded at the time the prompt is issued. +.P +Command substitution may require a separate process +to execute and cause the prompt display to be somewhat +slow, especially +when the return key is pressed several times in a row. +Therefore, its use +within +.B \s-1PS1\s+1 +is discouraged. +Some variables are maintained by \f5ksh\fP +so that their values can be used with +.B \s-1PS1\s+1. +The +.B \s-1PWD\s+1 +variable stores the pathname of the current working directory. +The value of +.B \s-1SECONDS\s+1 +variable +is the value of the most +recent assignment plus the elapsed time. +By default, the time is measured in milli-seconds, +but since +.B \s-1SECONDS\s+1 +is a floating point variable, the +number of places after the decimal point in the expanded +value can be +specified with +\f5typeset\ -F\fP\fIplaces\fP\f5\ SECONDS\fP. +In a roundabout way, this variable +can be used to generate a time stamp into the +.B \s-1PS1\s+1 +prompt without creating a process at each prompt. +The following code explains how you can do this on +System V. On BSD, you need a different command to initialize +the +.B \s-1SECONDS\s+1 +variable. +\f5 +.sp +.nf +.in .5i +# . this script and use $TIME as part of your PS1 string to +# get the time of day in your prompt +typeset -RZ2 _x1 _x2 _x3 +(( SECONDS=$(date '+3600*%H+60*%M+%S') )) +_s='_x1=(SECONDS/3600)%24,_x2=(SECONDS/60)%60,_x3=SECONDS%60,0' +TIME='"${_d[_s]}$_x1:$_x2:$_x3"' +# PS1=${TIME}whatever +.fi +.ta +.in +.sp +\fP +.H 2 "Tilde substitution" +.P +The character +.B \(ap +at the beginning of a word has special meaning to \f5ksh\fP. +If the characters after the +.B \(ap +up to a +.B / +match a user login name in the password database, then the +.B \(ap +and the name are replaced by +that user's login directory. +If no match is found, the original word +is unchanged. +A +.B \(ap +by itself, or in front of a +.BR / , +is replaced by the value of the +\fB\s-1HOME\s+1\fP +parameter. +A +.B \(ap +followed by a +.B + +or +.B \- +is replaced by the value of +.B $\s-1PWD\s+1 +or +.B $\s-1OLDPWD\s+1 +respectively. +.H 2 "Output formats" +The output of built-in commands and traces have values quoted so that they +can be re-input to the shell. +This makes it easy to cut and paste shell output on systems +which use a pointing device such as a mouse. +In addition, output can be saved in a file for reuse. +.P +.H 2 "The \fB\s-1ENV\s+1\fP file" +When an interactive \f5ksh\fP starts, it evaluates the +.B $\s-1ENV\s+1 +variable to arrive at a file name. +If this value is not null, +\f5ksh\fP attempts to read and process +commands in a file by this name. +Earlier versions of \f5ksh\fP read the \fB\s-1ENV\s+1\fP file +for all invocations of the shell primarily to allow +function definitions to be available for all shell +invocations. +The function search path, \fB\s-1FPATH\s+1\fP, described later, +eliminated the primary need for this capability and it was +removed because the high performance cost was no longer +deemed acceptable. +.H 1 "PROGRAMMING LANGUAGE" +The KornShell vastly extends the set of applications that +can be implemented efficiently at the shell level. +It does this by providing simple yet powerful mechanisms +to perform arithmetic, pattern matching, +substring generation, +and arrays. +Users can write applications as separate functions that can +be defined in the same file or in a library of functions +stored in a directory and loaded on demand. +.H 2 "String Processing" +The shell is primarily a string processing language. +By default, variables hold variable length strings. +There are no limits to the length of strings. Storage +management is handled by the shell automatically. +Declarations are not required. +With most programming languages, string constants are designated +by enclosing characters in single quotes or double quotes. +Since most of the words in the language are strings, the shell +requires quotes only when a string contains characters that +are normally processed specially by the shell, but their +literal meaning is intended. +However, since the shell is a string processing language, +and some characters can occur as literals and as language metacharacters, +quoting is an important part of the language. +.P +There are four quoting mechanisms in \f5ksh\fP. +The simplest is to enclose a sequence of characters inside single quotes. +All characters between a pair of single quotes have their literal meaning; +the single quote itself cannot appear. +A +.B $ +immediately preceding +a single quoted string +causes all the characters until the matching single quote +to be interpreted as an ANSI-C language string. +Thus, \f5'\en'\fP represents characters \f5\e\fP and +\f5n\fP, whereas, \f5$'\en'\fP +represents the new-line character. +Double quoted strings remove the special meaning of all characters +except +.BR $ , +.BR \(ga , +and +.BR \e , +so that parameter expansion and command substitution (defined below) +are performed. +The final mechanism for quoting a character is by preceding it with the +escape character +.BR \e\^ . +This mechanism works outside of quoted strings and for the characters +.BR $ , +.BR \(ga , +\fB"\fP, +and +.B \e +in double quoted strings. +.P +Variables are designated by +one or more +strings of alphanumeric +characters beginning with an alphabetic character +separated by a \fB\s+2.\s-2\fP. +Upper and lower case characters are distinct, so that the variable +.B A +and +.B a +are names of different variables. +There is no +limit to the length of the name of a variable. +You do not have to declare variables. +You can assign a value to a variable by writing the name of the +variable, followed by an equal sign, followed by a character string +that represents its value. +To create a variable whose name +contains a \fB\s+2.\s-2\fP, +the variable whose name consists of +the characters before the last \fB\s+2.\s-2\fP +must already exist. +You reference a variable by +putting the name inside curly braces and +preceding the braces with a dollar sign. +The braces may be omitted when the name +is alphanumeric. +If \f5x\fP and \f5y\fP +are two shell variables, then +to define a new variable, +\f5z\fP, +whose value is +the concatenation of the values of +\f5x\fP and \f5y\fP, +you just say +\f5z=$x$y\fP. +It is that easy. +.P +The +.B $ +can be thought of as meaning +"value of." +You can also capture the output of any command with the notation +.BI $( command ) . +This is referred to as command substitution. +For example, +\f5x=$(date)\fP +assigns the output from the \f5date\fP +command to the variable \f5x\fP. +Command substitution in the +Bourne shell is denoted by enclosing the command between +backquotes, +(\fB\(ga\^\(ga\fP). +This notation +suffers from some +complicated quoting rules. +Thus, it is hard to write \f5sed\fP +patterns which contains back slashes within command substitution. +Putting the pattern in single quotes +is of little help. +\f5ksh\fP accepts the Bourne shell command substitution syntax +for backward compatibility. +The +.BI $( command ) +notation allows +the \fIcommand\fP itself to contain quoted strings even if the substitution +occurs within double quotes. Nesting is legal. +.P +The special command substitution of the form +\f5$(cat\ file)\fP +can be replaced by +\f5$(<\ file)\fP, +which is faster because +the \f5cat\fP +command doesn't have to run. +.H 2 "Shell Parameters and Variables" +.P +There are three types of parameters used by \f5ksh\fP, +special parameters, positional parameters, and named +parameters which are called variables. +\f5ksh\fP defines the same special parameters, +.BR 0 , +.BR * , +.BR @ , +.BR # , +.BR ? , +.BR $ , +.BR ! , +and +.BR \- , +as in the Bourne shell. +.P +Positional parameters are set when the shell is invoked, +as arguments to the \f5set\fP built-in, +and by calls to functions (see below) and \fB\s+2.\s-2\fP +procedures. +They are named by numbers starting at 1. +.P +The third type of parameter is a variable. +As mentioned earlier, +\f5ksh\fP uses variables whose names +consist of one or more +alpha-numeric strings separated by a \fB\s+2.\s-2\fP. +There is no need to specify the +.I type +of a variable in the shell because, by default, +variables store strings of arbitrary length +and values will automatically be converted to numbers +when used in an arithmetic context. +However, \f5ksh\fP variables +can have one or more +.I attributes +that control the internal representation of the variable, +the way the variable is printed, and its access or +scope. +In addition, +\f5ksh\fP +allows variables to represent arrays of values +and references to other variables. +The \f5typeset\fP +built-in command of \f5ksh\fP +assigns attributes to variables. +Two of the attributes, +.I readonly +and +.IR export , +are available in the Bourne shell. +Most of the remaining attributes are discussed here. +The complete list of attributes appears in the manual. +The \f5unset\fP +built-in of \f5ksh\fP removes +values and attributes of variables. +When a variable is exported, certain of its attributes are also exported. +.P +Whenever a value is assigned to a variable, +the value is transformed according to the attributes of the variable. +Changing the attribute of a variable can change its value. +The attributes +.B \-L +and +.B \-R +are for left and right field justification respectively. +They are useful for aligning columns in a report. +For each of these attributes, a width can be defined explicitly or else +it is defined the first time an assignment is made to the variable. +Each assignment causes justification of the field, truncating +if necessary. +Assignment to fixed sized variables +provides one way to generate a substring consisting of +a fixed number of characters from +the beginning or end of a string. +Other methods are discussed later. +.P +The attributes +.B \-u +and +.B \-l +are used for upper case and lower case +formatting, respectively. +Since it makes no sense to have both attributes on simultaneously, +turning on either of these attributes turns the other off. +The following script, +using \f5read\fP and \f5print\fP which are described later, +provides an example of the use of shell variables +with attributes. +This script reads a file of lines each consisting of five fields separated by +.B : +and prints fields 4 and 2 in upper case in columns 1-15, left justified, +and columns 20-25 right-justified respectively. +.sp +.nf +.in .5i +.ta 3.4i +\f5typeset -uL15 f4 # 15 character left justified +typeset -uR6 f2 # 6 character right justified +IFS=: # set field separator to : +while read -r f1 f2 f3 f4 f5 # read line, split into fields +do print -r -- "$f4 $f2" # print fields 4 and 2 +done\fP +.fi +.ta +.in +.sp +.P +The +.BR \-i , +.BR \-E , +and +.BR \-F , +attributes are used to represent numbers. +Each can be followed by a decimal number. +The +.B \-i +attribute causes the value to be represented as an integer and it +can be followed by a number representing the numeric base when expanding +its value. +Whenever a value is assigned to an integer variable, it is evaluated +as an arithmetic expression +and then truncated to an integer. +.P +The +.B \-E +attribute causes the value to be represented in scientific +notation whenever its value is expanded. The number following the +.B \-E +determines the number of significant figures, and defaults to 6. +The +.B \-F +attribute causes the value to be represented with a fixed number +of places after the decimal point. +Assignments to variables with the +.B \-E +or +.B \-F +attributes cause the evaluation of the right hand side of the assignment. +.P +\f5ksh\fP allows one-dimensional +.I arrays +in addition to simple variables. +There are two types of arrays; associative arrays +and indexed arrays. +The subscript for an associative array is an arbitrary +string, whereas the subscript for an indexed array is +an arithmetic expression that is evaluated to yield an integer +index. +Any variable can become an indexed array +by referring to it with +an integer +.IR subscript . +All elements of an array need not exist. +Subscripts for arrays +must evaluate to an +integer between 0 and some maximum value, otherwise +an error results. +The maximum value may vary from one machine to another but +is at least 4095. +Evaluation of subscripts is described in +the next section. +Attributes apply to the whole array. +.P +Assignments to array variables can be made to individual elements +via parameter +assignment commands or the +.B typeset +built-in. +Additionally, values can be assigned sequentially with +compound assignment as described below, or by the +.B \-A +.I name +option of the \f5set\fP command. +Referencing of subscripted variables requires the character +.BR $ , +but also requires braces around the array element name. +The braces are needed to avoid conflicts with the +file name generation mechanism. +The form of any array element reference is: +.ce +.BI ${ name [ subscript ]} +Subscript values of +.B * +and +.B @ +can be used to generate all elements of an array, +as they are used for expansion of positional parameters. +The list of currently defined subscripts for a given +variable can be generated with +.BI ${! name [@]} , +or +.BI ${! name [*]} . +.P +The +.B \-n +or +.I nameref +attribute causes the variable to be treated +as a reference to the variable defined by its value. +Once this attribute is set, all references to this variable +become references to the variable named by the value +of this variable. +For example, if \f5foo=bar\fP, then setting the reference +attribute on \f5foo\fP will cause all subsequent references +to \f5foo\fP to behave as the variable whose name is \f5$foo\fP +was referenced, which in this case is the variable \f5bar\fP. +Unsetting this attribute breaks the association. +Reference variables are usually used inside functions whose +arguments are the names of shell variables. +The names for reference variables cannot contain a \fB\s+2.\s-2\fP. +Whenever a shell variable is referenced, the portion of the +variable up to the first \fB\s+2.\s-2\fP +is checked to see whether it matches the name of a reference +variable. +If it does, then the name of the variable actually used +consists of the concatenation of the name of the variable +defined by the reference plus the remaining portion of the +original variable name. +For example, using the predefined alias, \f5alias\ nameref='typeset\ -n'\fP, +.sp +.nf +.in .5i +.ta 3.4i +\f5\^.bar.home.bam="hello world" +nameref foo=.bar.home +print ${foo.bam} +\fBhello world\fP\fP +.fi +.ta +.in +.sp +.H 2 "Compound Assignment" +Compound assignments are used to assign values to arrays +and compound data structures. +The syntax for a compound assignment is +.IB name =( assignment-list ) +where +\fIname\fP +is the name of the variable to which you want to assign values. +No space is permitted between the variable name and the \fB=\fP +but can appear between the \fB=\fP and the open parenthesis. +New-lines can appear between the parentheses. +.P +The \fIassignment-list\fP can be in several different forms +yielding different results. +If \fIassignment-list\fP is simply a list of words, then +the words are processed as they are with the \f5for\fP command +and assigned sequentially as an indexed array. +For example, +.ce +\f5foo=( * )\fP +creates an indexed array \f5foo\fP and assigns the +file names in the current directory to each index starting +at zero. +.P +The second form for \fIassignment-list\fP is a list of assignments +of the special form \fB[\fP\fIword\fP\fB]=\fP\fIword\fP. +No space is permitted before or after the \fB=\fP. +In this case, the variable given by \fIname\fP becomes +an associative array with the given arguments as subscripts. +For example, +.ce +\f5bar=( [color]=red [shape]=box )\fP +creates an associate array named \f5bar\fP whose +subscripts are \f5color\fP and \f5shape\fP. +.P +The third form for \fIassignment-list\fP is a list of +normal assignments, including compound assignments. +These assignments cause sub-variables to be assigned +corresponding to the given assignments. +In addition to assignments, the \fIassignment-list\fP +can contain \f5typeset\fP commands. +In addition to creating sub-variables, +the effect of a compound assignment is to make +the value of the original variable be a parenthesized +assignment list of its components. +For example, the assignment +.sp +.nf +.in .5i +.ta 3.4i +\f5foo=( + left=bar + typeset -i count=3 + point=( + x=50 + y=60 + ) + colors=( red green yellow ) + right=bam +) \fP +.ta +.in +.fi +.sp +is equivalent to the assignments +.sp +.nf +.in .5i +.ta 3.4i +\f5foo.left=bar +foo.count=3 +foo.point.x=50 +foo.point.y=60 +foo.colors=( red green yellow ) +foo.right=bam\fP +.ta +.in +.fi +.sp +In addition, the value of \f5"$foo"\fP is +.sp +.nf +.in .5i +.ta 3.4i +\f5( + colors=( red green yellow ) + left=bar + typeset -i count=3 + point=( + y=60 + x=50 + ) + right=bam +)\fP +.ta +.in +.fi +.sp +.H 2 "Substring Generation" +The expansion of a variable or parameter can be modified so that +only a portion of the value results. +It is often necessary to extract a portion of a shell variable or +a portion of an array. +There are several parameter expansion operators that can do this. +One method to generate a substring is with an expansion of +the form \fB${\fP\fIname\fP\fB:\fP\fIoffset\fP\fB:\fP\fIlength\fP\fB}\fP +where \fIoffset\^\fP is an arithmetic expression that defines the +offset of the first character starting from 0, and +\fIlength\^\fP is an arithmetic expression that defines the +length of the substring. +If +.BI : length\^ +is omitted, +the length of the value of +.I name\^ +starting at +.I offset\^ +is used. +The +.BI : offset : length +operators can also be applied to array expansions and to parameters +.B * +and +.B @ +to generate portions of an array. +For example, the expansion, \fB${\fP\fIname\fP\fB[@]:\fP\fIoffset\fP\fB:\fP\fIlength\fP\fB}\fP, yields up to \fIlength\fP elements of the array \fIname\fP +starting at the element \fIoffset\fP. +.P +The other parameter expansion modifiers use shell patterns +to describe portions of the string to modify and delete. +A description of shell patterns is contained below. +When these +modifiers are applied to special parameters +.B @ +and +.B * +or to array parameters given as +\fIname\fP\fB[@]\fP or \fIname\fP\fB[*]\fP, +the operation is performed on each element. +There are four parameter expansion modifiers that +strip off leading and trailing substrings +during parameter expansion +by removing the characters matching a given pattern. +An expansion of +the form \fB${\fP\fIname\fP\fB#\fP\fIpattern\fP\fB}\fP +causes the smallest matching prefix of the value of +.I name\^ +to be removed. +The largest prefix matching +.I pattern\^ +is removed by using +.B ## +instead of +.BR # . +Similarly, +an expansion of +the form \fB${\fP\fIname\fP\fB%\fP\fIpattern\fP\fB}\fP +causes the smallest matching substring at the end of +.I name\^ +to be removed. +Again, using +.B %% +instead of +.BR % , +causes the largest matching trailing substring to be deleted. +For example, if the shell variable +.B file +has value +.BR foo.c , +then the expression +.B ${file%.c}.o +has value +.BR foo.o . +.P +The value of an expansion can be changed by +specifying a pattern that matches the part that needs to be changed +after the +the parameter expansion modifier +.BR / . +An expansion of the form +\fB${\fP\fIname\fP\fB/\fP\fIpattern\fP\fB/\fP\fIstring\fP\fB}\fP +replaces the first match of \fIpattern\fP with +the value of variable \fIname\fP to \fIstring\fP. +The second +.B / +is not necessary when \fIstring\fP is null. +The expansion +\fB${\fP\fIname\fP\fB//\fP\fIpattern\fP\fB/\fP\fIstring\fP\fB}\fP +changes all occurrences of the \fIpattern\fP into \fIstring\fP. +The parameter expansion modifiers +.B /# +and +.B /% +cause the matching pattern to be anchored to the beginning and +end respectively. +.P +Finally, there are parameter expansion modifiers that yield +the name of the variable, the string length of the value, or the number +of elements of an array. +\fB${!\fP\fIname\fP\fB}\fP +yields the name of the variable which will be \fIname\fP itself +except when \fIname\fP is a reference variable. In this case +it will yield the name of the variable it refers to. +When applied to an array variable, +\fB${!\fP\fIname\fP\fB[@]}\fP and +\fB${!\fP\fIname\fP\fB[*]}\fP +generate the names of all subscripts. +\fB${#\fP\fIname\fP\fB}\fP +will be the length in bytes of +\fB$\fP\fIname\fP. +For an array variable +\fB${#\fP\fIname\fP\fB[*]}\fP +gives the number of elements in the array. +.H 2 "Arithmetic Evaluation" +.P +For the most part, the shell is a string processing +language. However, the need for arithmetic has +long been obvious. +Many of the characters that are special to the +Bourne shell are needed as arithmetic operators. +To make arithmetic easy to use, and to maintain +compatibility with the Bourne shell, \f5ksh\fP uses matching +.B (( +and +.B )) +to delineate arithmetic expressions. +While single parentheses might have been +more desirable, these already mean +.I subshell\^ +so that another notation was required. +The arithmetic expression +inside the double parentheses +follows the same syntax, associativity and precedence +as the ANSI-C\*(Rf +.RS +American National Standard for Information Systems \- Programming +Language \- C, ANSI X3.159-1989. +.RF +programming language. +The characters between the matching double parentheses +are processed with the same rules used for double +quotes so that spaces can be used to aid readability +without additional quoting. +.P +All arithmetic evaluations are performed using +double precision floating point arithmetic. +Floating point constants follow the same rules as +the ANSI-C programming language. +Integer arithmetic constants are written as +.ce +.IB base # number, +where +.I base\^ +is a decimal integer between +two and sixty-four and +.I number\^ +is any non-negative number. +Base ten is used +when no base is specified. +The digits are represented by the characters +.BR 0-9a-zA-Z_@ . +For bases less than or equal to 36, +upper and lower case characters can +be used interchangeably to represent the digits +from 10 thru 35. +.P +Arithmetic expressions are made from constants, +variables, and operators. +Parentheses may be used for grouping. +The contents inside the double parentheses +are processed with the same expansions as occurs in a double quoted string, +so that all +.B $ +expansions are performed before the expression is evaluated. +However, there is usually no need to use the +.B $ +to get the value of a variable +because the arithmetic evaluator replaces the name of the variable +by its value within an arithmetic expression. +The +.B $ +cannot be used when the variable is the subject of assignment +or an increment operation. +As a rule it is better not to use +.B $ +in front of variables in an arithmetic expression. +.P +An arithmetic command of the form +.B +(( ... )) +.R +is a command that evaluates the enclosed arithmetic expression. +For example, the command +.ce +\f5(( x++ ))\fP +can be used to +increment the variable \f5x\fP, +assuming that \f5x\fP contains some numerical value. +The arithmetic command is true (return value 0), when the resulting +expression is non-zero, and false (return value 1) when the +expression evaluates to zero. +This makes the command easy to use with the \f5if\fP and \f5while\fP +compound commands. +.P +The \f5for\fP compound command +has been extended for use in arithmetic contexts. +The syntax, +.ce +\f5for\fP \fB((\fP \fIexpr1\fP\fB;\fP \fIexpr2\fP \fB;\fP \fIexpr3 \fP\fB))\fP +can be used as the first line of a \f5for\fP loop with the same semantics +as the \f5for\fP statement in the ANSI-C programming language. +.P +Arithmetic evaluations can also be performed as part of the evaluation +of a command line. +The syntax +.B +$((\ ...\ )) +.R +expands to the value of the enclosed arithmetic expression. +This expansion can occur wherever parameter expansion is performed. +For example using the \f5ksh\fP command \f5print\fP (described +later) +.ce +\f5print $((2+2))\fP +prints the number 4. +.P +The following script prints the first +.I n +lines of its standard input onto its standard output, +where +.I n +can be supplied as an optional argument whose default value is 20. +.sp +.nf +.in .5i +.ta 4i +\f5integer n=${1-20} # set n +while (( n-- >=0 )) && read -r line # at most n lines +do print -r -- "$line" +done\fP +.fi +.ta +.in +.sp +.H 2 "Shell Expansions" +.P +The commands you enter from the terminal or from a script +are divided into words and each word undergoes several +expansions to generate the command name and its arguments. +This is done in two phases. +The first phase recognizes reserved words, spaces and operators +to decide where command boundaries lie. +Alias substitutions take place during this phase. +The second phase performs expansions in the following order: +.BL +.LI +Tilde substitution, +parameter expansion, +arithmetic expansion, +and command substitution +are performed from left to right. +The option +.B \-u +or +.BR nounset , +will cause an error to occur when any variable +that is not set is expanded. +.LI +The characters that result from parameter expansion and +command substitution above are checked with the characters +in the +\fB\s-1IFS\s+1\fP variable +for possible +field splitting. +(See a description of \f5read\fP below to see how +\fB\s-1IFS\s+1\fP is used.) +Setting +\fB\s-1IFS\s+1\fP to a null +value causes field splitting to be skipped. +.LI +Pathname generation (as described below) +is performed on each of the fields. +Any field that doesn't match a pathname is left alone. +The option, +.B \-f +or +.BR noglob , +is used to disable pathname generation. +.LE +.H 2 "Pattern Matching" +The shell is primarily a string processing language and uses +patterns for matching file names as well as for matching strings. +The characters +.BR ? , +.BR * , +and +.B [ +are processed specially +by the shell when not quoted. +These characters are used to form patterns that +match strings. +Patterns are used by the shell to match pathnames, +to specify substrings, +and for +.B case +commands. +The character +.B ? +matches any one character. +The character +.B * +matches zero or more characters. +The character sequence +.BR [ ... ] +defines a character class +that matches any character contained within +.BR [\^] . +A range of characters can be specified by putting a +.B \- +between the first and last character of the range. +An exclamation mark, +.BR ! , +immediately after the +.BR [ , +means match all characters except the characters specified. +For example, the pattern +\f5a?c*.[!a-z]\fP +matches any string beginning with an +.BR a , +whose third character is a +.BR c , +and that ends in +.B . +(dot) followed by any character except the lower case letters, +.BR a\-z . +The sequence \f5[:alpha:]\fP +inside a character class, matches any set of characters in +the ANSI-C +.B alpha +class. +Similarly, \f5[:\fP\fIclass\fP\f5:]\fP matches +each of the characters in the given \fIclass\fP +for all the ANSI-C character classes. +For example, \f5[[:alnum:]_]\fP +matches any alpha-numeric character or the character +.BR _ . +.P +\f5ksh\fP +treats +strings of the form +.BI ( pattern-list +.BR ) , +where +.I pattern-list +is a list of one or more patterns separated by a +.BR \(bv , +specially when preceded by +.BR * , +.BR ? , +.BR + , +.BR @ , +or +.BR ! . +A +.B ? +preceding +.BI ( pattern-list ) +means that the pattern list enclosed in +.B (\^) +is optional. +An +.BI @( pattern-list ) +matches any pattern in the list of patterns enclosed in +.BR () . +A +.BI *( pattern-list ) +matches any string that contains zero or more of each of the enclosed +patterns, +whereas +.BI +( pattern-list ) +requires a match of one or more of any of the given patterns. +For instance, the pattern +.B +([0\-9])?(.) +matches one or more digits optionally followed by a +.BR . (dot). +A +.BI !( pattern-list ) +matches anything except any of the given patterns. +For example, +\f5print\ !(*.o)\fP +displays all file names in the current directory that do not end in +.BR .o . +.P +When patterns are used to generate pathnames when expanding +commands several other rules apply. +A separate match is made +for each file name component of the pathname. +Read permission is required for +any portion of the pathname that contains any special +pattern character. +Search permission is required for every component except +possibly the last. +.P +By default, +file names in each directory that begin with \fB\s+2.\s-2\fP +are skipped when performing a match. +If the pattern to be matched starts with a leading \fB\s+2.\s-2\fP, +then only files beginning with a \fB\s+2.\s-2\fP, +are examined when reading each directory to find matching files. +If the +\fB\s-1FIGNORE\s+1\fP variable +is set, +then only files that do not match this pattern +are considered. +This overrides the special meaning of \fB\s+2.\s-2\fP +in a pattern and in a file name. +.P +If the +.B markdirs +option is set, +each matching pathname that is the name +of a directory has a trailing +.B / +appended to the name. +.P +.H 2 "Conditional Expressions" +The Bourne shell uses the \f5test\fP +command, or the equivalent \f5[\fP +command, to test files for attributes +and to compare strings or numbers. +The problem with \f5test\fP +is that the shell has expanded the words of the \f5test\fP +command and +split them into arguments before \f5test\fP begins execution. +\f5test\fP +cannot distinguish between operators and operands. +In most cases +\f5test\ "$1"\fP +will test whether argument 1 is non-null. +However, +if argument 1 is +.BR \-f , +then \f5test\fP +will treat +.B \-f +as an operator and +yield a syntax error. +One of the most frequent errors with +\f5test\fP +occurs when its operands are not within double quotes. +In this case, the argument may expand to more than a single +argument or to no argument at all. In either case this +will likely cause a syntax error. +What makes this most insidious is that these errors are frequently +data dependent. A script that appears to run correctly may abort +if given unexpected data. +.P +To get around these problems, +\f5ksh\fP +has a compound command for conditional expression testing +as part of the language. +The reserved words +.B [[ +and +.B ]] +delimit the range of the command. +Because they are reserved words, not operator characters, +they require spaces to separate them +from arguments. +The words between +.B [[ +and +.B ]] +are not processed for field splitting or for pathname generation. +In addition, since \f5ksh\fP +determines the operators before parameter expansion, +expansions that yield no argument cause no problem. +The operators within +.BR [[ ... ]] +are almost the same as those for the \f5test\fP +command. +All unary operators are of the form +.BI \- letter +and are followed by a single operand. +Instead of +.B \-a +and +.BR \-o , +.BR [[ ... ]] +uses +.B && +and +.B \(bv\(bv +to indicate "and" and "or". +Parentheses are used without quoting for grouping. +.P +The right hand side of the string comparison operators +.B == +and +.B != +takes a pattern and tests whether the left hand operand +matches this pattern. Quoting the pattern results +is a string comparison rather than the pattern match. +The operators +.B < +and +.B > +within +.BR [[ ... ]] +designate lexicographical comparison. +.P +In addition there are several other new comparison primitives. +The binary operators +.B \-ot +and +.B \-nt +compare the modification times +of two files to see which file is +.I "older than" +or +.I "newer than" +the other. +The binary operator +.B \-ef +tests whether two files +have the same device and i-node number, +i.\ e., a link to the same file. +.P +The unary operator +.B \-L +returns true if its operand is a symbolic link. +The unary operator +.B \-O +(\fB\-G\fP) +returns true if the owner (or group) of the file operand matches +that of the caller. +The unary operator +.B \-o +returns true when its operand is the name of an option that is +currently on. +.P +The following script illustrates some of the uses of +.BR [[ ... ]] . +The reference manual contains the complete list of operators. +.sp +.nf +.in .5i +.ta 4i +\f5for i +do # execute foo for numeric directory + if [[ \-d $i && $i == +([0\-9]) ]] + then foo + # otherwise if writable or executable file and not mine + elif [[ (\-w $i\(bv\(bv\-x $i) && ! \-O $i ]] + then bar + fi +done\fP +.fi +.ta +.in +.sp +.H 2 "Input and Output" +\f5ksh\fP has +extended I/O capabilities to enhance the +use of the shell as a programming language. +As with the Bourne shell, +you use the I/O redirection operator, +.BR < , +to control where input comes from, +and the I/O redirection operator, +.BR > , +to control where output goes to. +Each of these operators can be preceded with a single digit that +specifies a file unit number to associate with the file stream. +Ordinarily you specify these I/O redirection operators with a specific +command to which it applies. +However, if you specify I/O redirections with the \f5exec\fP +command, +and don't specify arguments to \f5exec\fP, +then the I/O redirection applies to the current program. +For example, the command +\f5exec\ <\ foobar\fP +opens file \f5foobar\fP +for reading. +The \f5exec\fP +command is also used to close files. +A file descriptor unit can be opened as a copy of an existing +file descriptor unit by using either of the +.B <& +or +.B >& +operators and putting the file descriptor unit of the original file +after the +.BR & . +Thus, \f52>&1\fP means open standard error (file descriptor 2) +as a copy of standard output (file descriptor 1). +A file descriptor value of +.B \- +after the +.B & +indicates that the file should be closed. +To close file unit 5, specify +\f5exec\ 5<&-\fP. +There are two additional redirection operators with \f5ksh\fP +and the POSIX shell that are not part of the Bourne shell. +The +.B >| +operator overrides the effect of the +.B noclobber +option described earlier. +The +.B <\^> +operator causes a file to be opened for both reading and writing. +.P +\f5ksh\fP recognizes certain pathnames and treats them +specially. +Pathnames of the form +.BI /dev/fd/ n\^ +are treated as equivalent to the file defined by file descriptor +.IR n . +These name can be used as the script argument to \f5ksh\fP +and in conditional testing as described above. +On underlying systems that support +.B /dev/fd +in the file system, these names can be passed to other commands. +Pathnames of the form +.BI /dev/tcp/ hostid / port +and +.BI /dev/udp/ hostid / port +can be used to create +.B tcp +and +.B udp +connections to services given by the +.I hostid\^ +number and +.I port\^ +number. +The +.I hostid\^ +cannot use symbolic values. In practice these +numbers are typically generated by command substitution. +For example, +\f5exec\ 5>\ /dev/tcp/$(service\ name)\fP +would open file descriptor 5 for sending messages +to hostid and port number defined by the output of \f5service\ name\fP. +.P +The Bourne shell has a built-in command \f5read\fP +for reading lines from standard input (file descriptor 0) +and splitting it into fields based on the value of the +.B \s-1IFS\s+1 +variable, and a command \f5echo\fP +to write strings to standard output. +(On some systems, \f5echo\fP +is not a built-in command and incurs considerable overhead to use.) +Unfortunately, neither of these commands +is able to perform some very basic tasks. +For example. +with the Bourne shell, +the \f5read\fP +built-in cannot read a single line that ends in +.BR \e . +With \f5ksh\fP +the \f5read\fP +built-in has a +.B \-r +option to remove the special meaning for +.B \e +which allows it to be +treated as a regular +character rather than the line continuation character. +With the Bourne shell, +there is no simple way to have more than one file open +at any time for reading. +\f5ksh\fP has options on the \f5read\fP +command to specify the file +descriptor for the input. +The fields that are read from a line can be stored into an indexed +array with the +.B \-A +option to read. +This allows a line to be split into an arbitrary number of fields. +.P +The way the Bourne shell uses the +\fB\s-1IFS\s+1\fP variable to +split lines into fields greatly limits its utility. +Often data files consist of lines that use a character such +as +.B : +to delimit fields with two adjacent delimiters that denote +a null field. +The Bourne shell treats adjacent delimiters as a single +field delimiter. +With \f5ksh\fP, +delimiters that are considered white space characters +have the behavior of the Bourne shell, but other +adjacent delimiters separate +null fields. +.P +The \f5read\fP command is often used in scripts that interact +with the user by prompting the user and then requesting some +input. +With the Bourne shell two commands are needed; one to +prompt the user, the other to read the reply. +\f5ksh\fP allows these two commands to be combined. +The first argument of the \f5read\fP +command can be followed by a +.B ? +and a prompt string which is used whenever the input +device is a terminal. +Because the prompt is associated with the \f5read\fP built-in, +the built-in command line editors will be able to re-output +the prompt whenever the line needs to be refreshed when +reading from a terminal device. +.P +With the Bourne shell, +there is no way to set a time limit for waiting for the user +response to read. +The +.B \-t +option to \f5read\fP takes a floating +point argument that gives the time in seconds, +or fractions of seconds that the shell should wait for a reply. +.P +The version of the \f5echo\fP command in System V +treats certain sequences beginning with +.B \e +as control sequences. +This makes it hard to output strings without interpretation. +Most BSD derived systems do not interpret +.B \e +control sequences. +Unfortunately, the BSD versions of \f5echo\fP accepts a +.B \-n +option to prevent a trailing new-line, but has no way to +cause the string +.B \-n +to be printed. +Neither of these versions is adequate. Also, because they +are incompatible, it is very hard to write portable shell scripts +using \f5echo\fP. +The \f5ksh\fP built-in, \f5print\fP, +outputs characters to the terminal or to a file and +subsumes the functions of all versions of \f5echo\fP. +Ordinarily, escape sequences in arguments beginning with +.B \e +are processed the same as for the System V \f5echo\fP command. +However \f5print\fP follows the standard conventions for +options and has options that make \f5print\fP very versatile. +The +.B \-r +option can be used to output the arguments without any special meaning. +The +.B \-n +option can be used here to suppress the trailing new-line +that is ordinarily appended. +As with \f5read\fP, it is possible to specify the file descriptor number +as an option to the command to avoid having to use +redirection operators with each occurrence of the command. +.P +The IEEE POSIX shell and utilities standard committee was unable +to reconcile the differences between the System V and BSD +versions of \f5echo\fP. +They introduced a new command named \f5printf\fP +which takes an ANSI-C format string and a list of options +and outputs the strings using the ANSI-C formatting rules. +Since \f5ksh\fP is POSIX conforming, it accepts \f5printf\fP. +However, there is a +.B \-f +options to \f5print\fP that can be used to specify +a format string which processes the arguments the same way that +\f5printf\fP does. +.P +The format processing for \f5print\fP and \f5printf\fP has +been extended slightly. +There are three additional formatting directives. +The +.B %b +format causes the +.B \e +escape sequences to be expanded as they are with the System V \f5echo\fP +command. +The +.B %q +format causes quotes to +be placed on the output as required +so that it can be used as shell input. +Special characters in the output of most \f5ksh\fP built-in commands +and in the output from an execution trace +are quoted in an equivalent fashion. +The +.B %P +format causes an extended regular expression string to +be converted into a shell pattern. +This is useful for writing shell applications that have +to accept regular expressions as input. +Finally, the escape sequence +.B \e\^E +which expands to the terminal escape character (octal 033) +has been added. +.P +The shell is frequently used as a programming language for +interactive dialogues. +The +\f5select\fP +statement has been added to the language +to make it easier to +present menu selection alternatives to the +user and evaluate the reply. +The list of alternatives is numbered and put in columns. +A user settable prompt, +\fB\s-1PS3\s+1\fP, +is issued and if the answer is +a number corresponding to one of the alternatives, +the select loop variable is set to this value. +In any case, the +.B \s-1REPLY\s+1 +variable is used to store the user entered reply. +The shell variables +.B \s-1LINES\s+1 +and +.B \s-1COLUMNS\s+1 +are used to control the layout of select lists. +.H 2 "Option Parsing" +The \f5getopts\fP built-in command can be used +to process command arguments in a manner consistent +with the way \f5ksh\fP does for its own built-in commands. +.P +The \f5getopts\fP built-in allows users to specify options +as separate arguments or to group options that do not +take arguments together. Options that require arguments +do not require space to separate them from the option argument. +The +.B \s-1OPTARG\s+1 +variable stores the value of the option argument +after finding a variable that takes an argument. +The +.B \s-1OPTIND\s+1 +variable holds the index of the current options argument. +After processing options, the arguments should be +shifted by +.B \s-1OPTIND\s+1\-1 +to make the +remaining arguments be \f5"$@"\fP. +.P +The \f5getopts\fP argument description allows additional +information to be specified along with the options +that is used to generate \fIusage\fP messages for +incorrect arguments and for the option argument \fB\-?\fP. +The example in the APPENDIX uses \f5getopts\fP to process +its arguments. +.H 2 "Co-process" +\f5ksh\fP can spawn a +.I co-process +by adding a +.B "|&" +after a command. +This process will be run with its standard input and its +standard output connected to the shell. The built-in command \f5print\fP +with the +.B \-p +option will write into the standard input of this +process and +the built-in command \f5read\fP +with the +.B \-p +option will read from the output of this process. +.P +In addition, the I/O redirection operators \fB<&\fP and \fB>&\fP can +be used to move the input or output pipe of the co-process +to a numbered file descriptor. +Use \f5exec\ 3>&\ p\fP to move the input of the co-process +to file descriptor \fB3\fP. +After you have connected to file descriptor \fB3\fP, you +can direct the output of any command to the co-process +by running \fIcommand\fP\f5\ >&3\fP. +Also, by moving the input of the co-process to a numbered descriptor, +it is possible to run a second co-process. +The output of both co-processes will be the file descriptor +associated with \f5read\ -p\fP. +You can use \f5exec\ 4<&\ p\fP to cause the output of these +co-processes to go to file descriptor \fB4\fP of the shell. +Once you have moved the pipe to descriptor \fB4\fP, it is possible +to connect a server to the co-process by running \fIcommand\fP\f5\ 4<&\ p\fP +or to close the co-process pipe with \f5exec\ 4<&\ -\fP. +.H 2 "Functions" +.P +Function definitions are of the form +.sp +.in +.5i +.nf +\f5function\fP \fIname\fP +.br +.B { +.br + any shell script +.br +.B } +.fi +.sp +.in +A function whose name contains a \fB\s+2.\s-2\fP +is called a \fIdiscipline\fP function. +The portion of the name after the last \fB\s+2.\s-2\fP +is the name of the discipline. +Discipline functions named \f5get\fP, \f5set\fP, and \f5unset\fP +can be assigned to any variable to intercept lookups, +assignments and unsetting of the variable +defined by the portion of the name before the last \fB\s+2.\s-2\fP. +Applications can create additional disciplines for variables +that are created as part of user defined built-ins. +The portion of the name before the last \fB\s+2.\s-2\fP +must refer to the name of an existing variable. +Thus, if \f5p\fP is a reference to \f5PATH\fP, then +the function name \f5p.get\fP and \f5PATH.get\fP +refer to the same function. +.P +The function is invoked either +by specifying +.I name +as the command name +and optionally following it with arguments +or by using it as an option to the \fB\s+2.\s-2\fP +built-in command. +Positional parameters are saved before each +function call and restored when completed. +The arguments that follow the function name on the calling +line become positional parameters inside the function. +The \f5return\fP +built-in can be used to cause the function to return to +the statement following +the point of invocation. +.P +Functions can also be defined with the System V notation, +.sp +.in +.5i +.nf +\fIname\fP \f5()\fP +.br +.B { +.br + any shell script +.br +.B } +.fi +.sp +.in +Functions defined with this syntax cannot be used as the first +argument to a \fB\s+2.\s-2\fP procedure. +\f5ksh\fP accepts this notation for compatibility only. +There is no need to use this notation when writing +\f5ksh\fP scripts. +.P +Functions defined with the \f5function\fP\ \fIname\fP syntax +and invoked by name +are executed in the current shell environment +and can share named variables with the calling program. +Options, other than execution trace +.BR \-x , +set by the calling program are +passed down to a function. +The options are +not shared with +the function so that any options set within a function are +restored when the function exits. +Traps ignored by the caller are ignored within the function +and cannot be enabled. +Traps caught by the calling program are reset to their +default action within the function. +In most instances, the default action is +to cause the function to terminate. +A trap on +\fB\s-1EXIT\s+1\fP +defined within a function executes after the function +completes but +before the caller resumes. +Therefore, +any variable assignments and +any options set as part of a trap action will be effective +after the caller resumes. +.P +By default, variables are inherited by the function and shared +by the calling program. +However, +for functions defined with the \f5function\fP\ \fIname\fP syntax +that are invoked by name, +environment substitutions preceding the function call +apply only to the scope of the function call. +Also, variables whose names do not contain a \fB\s+2.\s-2\fP +that are defined with the \f5typeset\fP +built-in command are local to the function that they are declared in. +Thus, for the function defined +.sp +.nf +.in .5i +\f5function name +{ + typeset -i x=10 + let z=x+y + print $z +}\fP +.fi +.ta +.in +.sp +invoked as +\f5y=13\ name\fP, +\f5x\fP and \f5y\fP +are local variables with respect to the function +\f5name\fP +while +\f5z\fP +is global. +.P +Functions defined with the \fIname\fP\f5()\fP syntax, +and functions invoked as an argument to the \fB\s+2.\s-2\fP +command, +share everything other than positional parameters with the caller. +Assignments that precede the call remain in effect after the +function completes. +.P +Alias and function names are not passed down to shell scripts +or carried across separate +invocations of \f5ksh\fP. +The +.B $\s-1FPATH\s+1 +variable gives a colon separated list of directories that +is searched for function definitions when trying to resolve +the command name. +Whenever a file name contained in +.B $\s-1FPATH\s+1 +is found, the complete file is read and all functions +contained within become defined. +.P +Calls that reference functions can be recursive. +Except for special built-ins, +function names take precedence over built-in names and names +of programs when used as command names. +To write a replacement function that invokes the command that +you wish to replace, +you can use the \f5command\fP built-in command. +The arguments to \f5command\fP are the name and arguments +of the program you want to execute. +For example to write a +.B cd +function which changes the directory and prints out the directory name, +you can write +.sp +.nf +.in .5i +\f5function cd +{ + if command cd "$@" + then print -r -- $PWD + fi +}\fP +.fi +.ta +.in +.sp +.P +The +\fB\s-1FPATH\s+1\fP +variable is a colon separated list that \f5ksh\fP +uses to search for function definitions. +When +\f5ksh\fP +encounters an autoload function, +it runs the +.B . +command on the script containing the function, +and then executes the function. +.P +For interactive shells, +function definitions may also be placed in the +\fB\s-1ENV\s+1\fP +file. +However, this +causes the shell to take longer to begin executing. +.H 2 "Process Substitution" +.P +This feature is only available +on versions of the UNIX operating system which support the +.B /dev/fd +directory for naming open files. +Each command argument of the form +\fB<(\fP\fIlist\^\fP\fB)\fP +or +\fB>(\fP\fIlist\^\fP\fB)\fP +will run process +.I list +asynchronously connected to some file in the +.B /dev/fd +directory. +The name of this file will become the argument to the command. +If the form with +.B > +is selected then writing on this file will provide input for +.IR list . +If +.B < +is used, +then the file passed as an argument will contain the output of the +.I list +process. +For example, +.sp +.nf +.in .5i +\f5paste <(cut \-f1 \fP\fIfile1\fP\f5) <(cut \-f2 \fP\fIfile2\fP\f5) | tee >(\fP\fIprocess1\fP\f5) >(\fP\fIprocess2\fP\f5)\fP +.fi +.ta +.in +.sp +extracts +fields 1 and 3 from +the files +.I file1 +and +.I file2 +respectively, +places the +results side by side, and +sends it +to the processes +.I process1 +and +.IR process2 , +as well as putting it onto the standard output. +Note that the file which is passed as an argument to the command is +a UNIX system +.IR pipe (2) +so that the programs that expect to +.IR lseek (2) +on the file will not work. +.H 2 "Finding Commands" +.P +The addition of aliases, functions, +and more built-ins +has made it substantially more difficult to know what +a given command name really means. +.P +Commands that begin with reserved words +are an integral part of the shell language itself +and typically define the control flow of the language. +Some control flow commands are not reserved words in +the language but are \fIspecial\fP built-ins. +Special built-ins are built-ins that are considered a +part of the language rather than user definable commands. +The best examples of commands that fit this description +are \f5break\fP and \f5continue\fP. +Because they are not reserved words, they can be the +result of shell expansions and are not effected by quoting. +These commands have the following special properties: +.BL +.LI +Assignments that precede them apply to the current shell process, +not just to the given command. +.LI +An error in the format of these commands cause a shell script +or function that contains them to abort. +.LI +They cannot be overridden by shell functions. +.LE +.P +Other commands are built-in because they perform side effects +on the current environment that would be nearly impossible +to implement otherwise. +Built-ins such as \f5cd\fP and \f5read\fP +are examples of such built-ins. +These built-ins are semantically equivalent to commands that +are not built-in except that they don't take a path search +to locate. +.P +A third reason to have a command built-in is so that +it will be unaffected by the setting of the +.B \s-1PATH\s+1 +variable. +The \f5print\fP command fits this category. +Scripts that use \f5print\fP will be portable +to all sites that run \f5ksh\fP. +.P +The final reason for having a command be a built-in is +for performance. +On most systems it is more than an order of magnitude +faster to initiate a command that is built-in than +to create a separate process to run the command. +Examples that fit this category are \f5test\fP +and \f5pwd\fP. +.P +Given a command name \f5ksh\fP decides what it means using +the following order: +.BL +.LI +Reserved words define commands that form part of the shell +grammar. +They cannot be quoted. +.LI +Alias substitutions occur first as part of the reading of commands. +Using quotes in the command name will prevent alias substitutions. +.LI +Special built-ins. +.LI +Functions. +.LI +Commands that are built-in that are not associated with a pathname +such as \f5cd\fP and \f5print\fP. +.LI +If the command name contains a +.BR / , +the program or script corresponding to the given name is executed. +.LI +A path search locates the pathname corresponding to the command. +If the pathname where it is found matches the pathname associated +with a built-in command, the built-in command is executed. +If the directory where the command is found is listed in the +.B \s-1FPATH\s+1 +variable, the file is read into the shell +like a dot script, and a function by that name is invoked. +Once a pathname is found, \f5ksh\fP remembers its location +and only checks relative directories in \fB\s-1PATH\s+1\fP +the next time the command name is used. +Assigning a value to \fB\s-1PATH\s+1\fP +causes \f5ksh\fP to forget the location of all command names. +.LI +The +.B \s-1FPATH\s+1 +variable is searched and files found are treated as described above. +.LE +.P +The first argument of the \f5command\fP built-in, described earlier, +skips the checks for reserved words and for function definitions. +In all other ways, \f5command\fP behaves like a built-in +that is not associated with a pathname. +As a result, if the first argument of \f5command\fP is +a special built-in, the special properties of this built-in +do not apply. +For example, whereas, \f5exec\ 3<\ foo\fP will cause a script containing +it to abort if the open fails, \f5command\ exec\ 3<\ foo\fP +results in a non-zero exit status but does not abort the script. +.P +You can get a complete list of the special built-in commands +with \f5builtin\ -s\fP. +In addition \f5builtin\fP without arguments gives a list of +the current built-ins and the pathname that they are associated with. +A built-in can be bound to another pathname by giving +the pathname for the built-in. The basename of this path must +be the name of an existing built-in for this to succeed. +Specifying the name of the built-in without a pathname causes +this built-in to be found before a path search. +A built-in can be deleted with the \fB\-d\fP option. +.P +On systems with run time loading of libraries, built-in commands +can be added with the \f5builtin\fP command. +Each command that is to be built-in must be written as a +C function whose name is of the form \f5b_\fP\fIname\fP, where +\fIname\fP is the name of the built-in that is to be added. +The function has the same argument calling convention as +\f5main\fP. The lower eight bits of the return value become +the exit status for this built-in. +Builtins are added by specifying the pathname of the library +as an argument to the \fB\-f\fP option of \f5builtin\fP. +.P +The built-in command, +\f5whence\fP, +when used with the +.B \-v +option, tells how a given command is bound. +A line is printed for each argument to \f5whence\fP +telling what would happen if this argument were used as a command name. +It reports on reserved words, aliases, built-ins, and +functions. +If the command is none of the above, +it follows the path search rules and prints the full path-name, +if any, otherwise it prints an error message. +.H 2 "Symbolic Names" +To avoid implementation dependencies, \f5ksh\fP +accepts and generates symbolic names +for built-ins that use numerical values in the Bourne shell. +The +.B \-S +option of the +\f5umask\fP built-in command +accepts and displays +default file creation permissions +symbolically. +It uses the same symbolic notation as the \f5chmod\fP command. +.P +The \f5trap\fP and \f5kill\fP built-in commands +allows the signal names to be given symbolically. +The names of signals and traps +corresponding to signals are the same as the signal name with +the +.B \s-1SIG\s+1 +prefix removed. +The trap +.B 0 +is named +\fB\s-1EXIT\s+1\fP. +.H 2 "Additional Variables" +In addition to the variables discussed earlier, \f5ksh\fP +has other variables that it handles specially. +The variable \fB\s-1RANDOM\s+1\fP +produces a random number in the range 0 to 32767 each time it is referenced. +Assignment to this variable sets the seed for the +random number generator. +.P +The parameter \fB\s-1PPID\s+1\fP +is used to generate the process id of the process which invoked this shell. +.H 2 "Added Traps" +A new trap named +\fB\s-1ERR\s+1\fP +has been added. +This trap is invoked whenever the shell would exit if the +.B \-e +option were set. +This trap is used by +Fourth Generation Make\*(Rf +.RS +G. S. Fowler, +.I "The Fourth Generation Make," +Proceedings of the Portland USENIX meeting, pp. 159-174, 1985. +.RF +which runs \f5ksh\fP +as a co-process. +.P +A trap named +\fB\s-1DEBUG\s+1\fP +gets executed after each command. +This trap can be used for debugging and other purposes. +.P +The +\fB\s-1KEYBD\s+1\fP +trap was described earlier. +.H 2 Debugging +The primary method for debugging Bourne shell scripts is to +use the +.B \-x +option to enable the execution trace. +After all +the expansions have been performed, +but before each command is executed, +the trace writes to standard error the name and arguments +of each command preceded by a +.BR + . +While the trace is very useful, there is no way +to find out what line of source a given trace line +corresponds to. +With +\f5ksh\fP +the +\fB\s-1PS4\s+1\fP +variable +is evaluated for parameter expansion and +is displayed before each command, +instead of the +.BR + . +.P +The +\fB\s-1LINENO\s+1\fP +variable is set to the current line number relative to the +beginning of the current script or function. +It is most useful as part of the +\fB\s-1PS4\s+1\fP +prompt. +.P +The +\fB\s-1DEBUG\s+1\fP +trap can be used to write a break point shell +debugger in \f5ksh\fP. +An example of such a debugger is \f5kshdb\fP.\*(Rf +.RS +Bill Rosenblatt, +.IR "Debugging Shell Scripts with \f5kshdb\fP" , +Unix World, Volume X, No. 5, 1993. +.RF +.H 2 "Timing Commands" +.P +Finding the time it takes to execute commands +has been a serious problem with the Bourne shell. +Since the \f5time\fP command is not part of the +language, it is necessary to write a script +in order to time a \f5for\fP or \f5while\fP loop. +The extra time in invoking the shell and processing +the script is accumulated along with the time +to execute the script. +.P +More seriously, the Bourne shell does not give correct +times for pipelines. +The reason for this is that the times for some members +of a pipeline are not counted when computing the time. +As an extreme example, +running \f5time\fP on the script +.ce +\f5cat < /dev/null | sort -u bigfile | wc\fP +with the Bourne shell will show very little +user and system time no matter how +large \f5bigfile\fP is. +.P +To correct these problems, +a reserved word \f5time\fP +has been added to replace +the \f5time\fP +command. +Any function, command or pipeline can be preceded by this reserved word +to obtain information about the elapsed, user, and system times. +Since I/O redirections bind to the command, not to +\f5time\fP, +parentheses should be used to redirect the timing information which +is normally printed on file descriptor 2. +.H 1 SECURITY +There are several documented problems associated with the security of +shell procedures\*(Rf. +.RS +F. T. Grampp and R. H. Morris, +.I "UNIX Operating System Security," +AT&T Bell Labs Tech. Journal, Vol. 63, No. 8, Part 2, pp. 1649-1671, 1984. +.RF +These security holes occur primarily because a user can manipulate the +.I environment +to subvert the intent of a +.I setuid +shell procedure. +Sometimes, shell procedures are initiated from +binary programs, without the author's +awareness, by library routines which invoke shells to carry out +their tasks. +When the binary program is run +.I setuid +then the shell procedure runs with the permissions afforded to the +owner of the binary file. +.P +In the Bourne shell, +the +.B \s-1IFS\s+1 +parameter is used to split each word into separate command arguments. +If a user knows that some +.I setuid +program will run +\f5sh\ -c\ /bin/pwd\fP +(or any other command in +.BR /bin ) +then the user sets and exports +.BR \s-1IFS\s+1=\^/ . +Instead of running +.B /bin/pwd +the shell will run +.B bin +with +.B pwd +as an argument. +The user puts his or her own \f5bin\fP +program into the current directory. +This program can +create a copy of the shell, +make this shell +.IR setuid , +and then run the \f5/bin/pwd\fP +program so that the original program continues to run successfully. +This kind of penetration is not possible with +\f5ksh\fP +since the +.B \s-1IFS\s+1 +parameter only splits arguments that result from command or parameter +substitution. +.P +Some +.I setuid +programs run programs using +.I system() +without giving the full pathname. +If the +user sets the +.B \s-1PATH\s+1 +variable so that the desired command will be found +in his or her local bin, then the same technique described above can +be employed to compromise the security of the system. +To close up this and other security holes, +\f5ksh\fP +resets the effective user id to the real user id and the effective +group id to the real group id unless the +.I privileged +option +.RB ( \-p\^ ) +is specified at invocation. +In +this mode, the +.B privileged +mode, the +.B .profile +and +.B \s-1ENV\s+1 +files are not processed. +Instead, the file +.B /etc/suid_profile +is read and executed. +This gives an administrator control over the +environment to set the +.B \s-1PATH\s+1 +variable or to log setuid shell invocations. +Clearly security of the system is compromised if +.B /etc +or this file is publicly writable. +.P +Some versions of the UNIX operating system look for the characters +\f5#!\fP +as the first two characters of an executable file. +If these characters are found, then the next word on this line is taken +as the interpreter to +invoke +for this command and the interpreter is +.IR exec ed +with the name of the script as argument zero and argument one. +If the +.I setuid +or +.I setgid +bits are on for this file, then the interpreter +is run with the effective uid and/or gid set accordingly. +This scheme has three major drawbacks. +First of all, +putting the pathname of the interpreter into the script +makes the script less portable since the interpreter +may be installed in a different directory on another system. +Secondly, using the +\f5#!\fP +notation forces an +.B exec +of the interpreter even when the call is invoked from the interpreter +which it must exec. This is inefficient since +\f5ksh\fP can handle a failed exec much faster than starting up +again. +More importantly, +.I setuid +and +.I setgid +procedures provide an easy target for intrusion. +By linking a +.I setuid +or +.I setgid +procedure to a name beginning with a +.B \- +the interpreter is fooled into thinking that it is being invoked with +a command line option rather than the name of a file. +When the interpreter is the shell, the user gets a privileged +interactive shell. +There is code in +\f5ksh\fP +to guard against this simple form of intrusion. +.P +A more reliable way to handle +.I setuid +and +.I setgid +procedures is provided with +\f5ksh\fP. +The technique does not require any changes to the operating system +and provides better security. +Another advantage to this method is that it also allows scripts which +have execute permission but no read permission to run. Taking away read +permission makes scripts more secure. +.P +The method relies on a setuid +.B root +program to authenticate the +request and exec the shell with the correct mode bits to carry out +the task. This shell is invoked with the requested file already open +for reading. A script which cannot be opened for reading or which +has its setuid and/or setgid bits turned on causes this setuid +.B root +program to get \fBexec\fPed. +For security reasons, this program is given the full +pathname +\f5/etc/suid_exec\fP. +A description of the implementation of the +\f5/etc/suid_exec\fP +program can be found in +a separate paper\*(Rf. +.RS +D. G Korn +.I "Parlez-vous Kanji?" +TM-59554-860602-03, 1986. +.RF +.H 1 "CODE CHANGES" +\f5ksh\fP is written in ANSI-C as a reusable library. +The code can be compiled with C++ and older K&R C as well. +The code uses the IEEE POSIX 1003.1 and ISO 9945-1 standard\*(Rf +.RS +.I "POSIX \- Part 1: System Application Program Interface," +IEEE Std 1003.1-1990, ISO/IEC 9945-1:1990. +.RF +wherever possible so that \f5ksh\fP should be able to run +on any POSIX compliant system. In addition, it is possible +to compile \f5ksh\fP for older systems. +.P +Unlike earlier version of the Bourne shell, +\f5ksh\fP treats eight bit characters transparently +without stripping off the +leading bit. +There is also a compile time switch to enable handling multi-byte +and multi-width characters sets. +.P +On systems with dynamic libraries, it is possible to add built-in +commands at run time with the built-in command \f5builtin\fP +described earlier. +It is also possible to embed \f5ksh\fP in applications in +a manner analogous to \f5tcl\fP. +.H 1 "EXAMPLE" +.P +An example of a \f5ksh\fP script is included +in the Appendix. +This one page program is a variant of the UNIX system +\f5grep\fP(1) program. +Pattern matching for this version of \f5grep\fP +means shell patterns. +.P +The first half uses the \f5getopts\fP command to +find the option flags. +Nearly all options have been implemented. +The second half goes through each line of each file +to look for a pattern match. +.P +This program is not intended to serve as a +replacement for \f5grep\fP +which has been highly tuned for performance. +It does +illustrate the programming power of \f5ksh\fP. +Note that no auxiliary processes are spawned by this script. +It was written and debugged in under two hours. +While performance is acceptable for small files, +this program runs at only one tenth +the speed of \f5grep\fP +for large files. +.H 1 "PERFORMANCE" +.P +\f5ksh\fP executes many scripts faster than the System V Bourne shell; +in some cases more than 10 times as fast. +The primary reason for this is that \f5ksh\fP creates fewer +processes. +The time to execute a built-in command or a function is one or two +orders of magnitude faster than performing a \f5fork\fP() and +\f5exec\fP() to create a separate process. +Command substitution and commands inside parentheses +are performed without creating another process, unless necessary +to preserve correct behavior. +.P +Another reason for improved performance is the use of the \fBsfio\fP\*(Rf, +.RS +David Korn and Kiem-Phong Vo, +.IR "SFIO - A Safe/Fast String/File I/O," +Proceedings of the Summer Usenix, +pp. 235-255, 1991. +.RF +library for I/O. The \fBsfio\fP library buffers all I/O +and buffers are flushed only when required. +The algorithms used in \fBsfio\fP perform better than +traditional versions of standard I/O so that programs that +spend most of their time +formatting output may actually perform better +than versions written in C. +.P +Several of the internal algorithms have been changed +so that the number of subroutine calls has been +substantially reduced. +\f5ksh\fP uses variable sized hash tables for variables. +Scripts that rely heavily on referencing variables execute faster. +More processing is performed while reading the script +so that execution time is saved while running loops. +These changes are not noticeable for scripts that \f5fork()\fP +and run processes, +but they reduce the time that it takes to interpret commands by +more than a factor of two. +.P +Most importantly, \f5ksh\fP provide mechanisms to write applications +that do not require as many processes. +The arithmetic provided by the shell eliminates the need for the +\f5expr\fP command. +The pattern matching and substring capabilities eliminate the +need to use \f5sed\fP or \f5awk\fP to process strings. +.P +The architecture of \f5ksh\fP makes it easy to make commands +built-ins without changing the semantics at all. +Systems that have run-time binding of libraries allow +applications to be sped up by supplying the critical +programs as shell built-in commands. +Implementations on other systems can add built-in commands +at compile time. +The procedure for writing built-in commands that can be loaded +at run time is in a separate document.\*(Rf, +.RS +David Korn, +.IR "Guidelines for writing \f5ksh-93\fP built-in commands," +to be published, 1994. +.RF +.H 1 "CONCLUSION" +.P +The 1988 version of \f5ksh\fP has tens of thousands of regular users +and is a suitable replacement for the Bourne shell. +The 1993 version of \f5ksh\fP is essentially upward compatible with +both the 1988 version of \f5ksh\fP and with the recent IEEE POSIX +and ISO shell standard. +The 1993 version offers many advantages for programming applications, +and it has been rewritten so that it can be used in embedded applications. +It also offers improved performance. +.SG dgk \" signature typist initials +\" .CS 14 24 38 0 0 16 \" cover sheet for TM +.bp +.ce +\fIAPPENDIX\fP +.nf +\f5 +.ta .66i 1.33i 2i 2.66i 3.33i 4i 4.66i 5.33i 6i 6.66i 7.33i 8i +.so grep.mm +.fi +\fP + + Index: src/lib/libshell/common/shell.3 =================================================================== --- src/lib/libshell/common/shell.3 (revision 0) +++ src/lib/libshell/common/shell.3 (revision 740) @@ -0,0 +1,408 @@ +.fp 5 CW +.TH SHELL 3 "28 Feb 2003" +.PP +.SH NAME +.PP +\fBshell\fR \- a \f5ksh\fP library interface +.PP +.SH SYNOPSIS +.ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i +.SS "HEADERS/LIBRARIES" +.nf +.ft 5 +#include +libshell.a -lshell +.ft R +.fi +.SS "DATA TYPES" +.nf +.ft 5 +Shell_t; +Shopt_t; +Shscope_t; +Shbltin_f; +Shinit_f; +Shwait_f; +.ft R +.fi +.SS "FUNCTIONS" +.nf +.ft 5 +int sh_main(int \fIargc\fP, char *\fIargv\fP[], Sh_init \fIfn\fP); +Shell_t *sh_init(int \fIargc\fP, char *\fIargv\fP); +Shell_t *sh_getinterp(void); + +Namval_t *sh_addbuiltin(const char *\fIname\fP,Sh_bltin_f \fIfn\fP,void *\fIarg\fP); + +unsigned int sh_isoption(int \fIoption\fP); +unsigned int sh_onoption(int \fIoption\fP); +unsigned int sh_offoption(int \fIoption\fP); + +void *sh_parse(Shell_t *\fIshp\fP, Sfio_t *\fIsp\fP, int \fIflags\fP); +int sh_trap(const char *\fIstring\fP, int \fImode\fP); +int sh_eval(Sfio_t *\fIsp\fP,int \fImode\fP); +int sh_fun(Namval_t *\fIfunnode\fP, Namval_t *\fIvarnode\fP, char *\fIargv\fP[]); +int sh_funscope(int \fIargc\fP,char *\fIargv\fP[],int(*\fIfn\fP)(void*),void *\fIarg\fP,int \fIflags\fP); +Shscope_t *sh_getscope(int \fIindex\fP,int \fIwhence\fP); +Shscope_t *sh_setscope(Shscope_t *\fIscope\fP); + +int (*sh_fdnotify(int(*\fIfn\fP)(int,int)))(int,int); +char *sh_fmtq(const char *\fIstring\fP); +void *sh_waitnotify(Shwait_f \fIfn\fP); +void sh_delay(double \fIsec\fP); +Sfio_t *sh_iogetiop(int \fIfd\fP, int \fImode\fP); +int sh_sigcheck(void); +.ft R +.fi +.PP +.SH DESCRIPTION +.PP +The \fIShell\fP library is a set of functions used for +writing extensions to \f5ksh\fP or writing commands +that embed shell command processing. +The include file \f5\fP contains the type definitions, +function prototypes and symbolic constants defined by +this interface. It also defines replacement definitions for +the standard library functions +\f5access()\fP, +\f5close()\fP, +\f5dup()\fP, +\f5exit()\fP, +\f5fcntl()\fP, +\f5lseek()\fP, +\f5open()\fP, +\f5pipe()\fP, +\f5read()\fP, +and +\f5write()\fP +that must be used +with all code +intended to be compiled as built-in commands. +.P +The \f5\fP header includes \f5\fP which +in turn includes the standard include files, \f5\fP, +\f5\fP, \f5\fP, \f5\fP, +\f5\fP, \f5\fP, \f5\fP, +\f5\fP, \f5\fP, and \f5\fP. +The \f5\fP header also includes the headers +\f5\fP, +\f5\fP, +\f5\fP, +\f5\fP, +\f5\fP, +and \f5\fP +so that in most cases, programs only require the single +header \f5\fP. +.PP +Programs can use this library in one of the following ways: +.PD 0 +.TP +.B 1 +To write builtin commands and/or other code that will be loaded +into the shell by loading dynamic libraries +at run time using the \f5builtin\fP(1) command. +In this case the shell will look for a function named \f5lib_init\fP +in your library and, if found, will execute this function with +argument \f50\fP when the library is loaded. +In addition, for each argument named on the \f5builtin\fP +command line, it will look for a function named \f5b_\fP\fIname\fP\f5()\fP +in your library and will \fIname\fP as a built-in. +.TP +.B 2 +To build separate a separate command that uses the shell as a +library at compile or run time. +In this case the \f5sh_init()\fP function must be called to +initialize this library before any other commands in this library +are invoked. +The arguments \fIargc\fP and \fIargv\fP are the number +of arguments and the vector of arguments as supplied by the shell. +It returns a pointer the \f5Shell_t\fP. +.TP +.B 3 +To build a new version of \f5ksh\fP with extended capabilities, +for example \f5tksh\fP(1). +In this case, the user writes a \f5main()\fP function that +calls \f5sh_main()\fP with the \fIargc\fP and \fIargv\fP arguments +from \f5main\fP and pointer to function, \fIfn\fP as a third +argument.. The function \fIfn\fP will +be invoked with argument \f50\fP after \f5ksh\fP has done initialization, +but before \f5ksh\fP has processed any start up files or executed +any commands. The function \fIfn\fP +will be invoked with an argument of \f51\fP before \f5ksh\fP +begins to execute a script that has been invoked by name +since \f5ksh\fP cleans up memory and long jumps back to +the beginning of the shell in this case. +The function \fIfn\fP will be called with argument \f5-1\fP before +the shell exits. +.PD +.PP +The \f5Shell_t\fP structure contains the following fields: +.nf +.ft 5 + Shopt_t \fIoptions\fP; \fR/* set -o options */\fP + Dt_t *\fIvar_tree\fP; \fR/* shell variable dictionary */\fP + Dt_t *\fIfun_tree\fP; \fR/* shell function dictionary */\fP + Dt_t *\fIalias_tree\fP; \fR/* shell alias dictionary */\fP + Dt_t *\fIbltin_tree\fP; \fR/* shell built-in dictionary */\fP + Shscope_t *\fItopscope\fP; \fR/* pointer to top-level scope */\fP + char *\fIinfile_name\fP; \fR/* path name of current input file*/\fP + int \fIinlineno\fP; \fR/* line number of current input file*/\fP + int \fIexitval\fP; \fR/* most recent exit value*/\fP +.ft R +.fi +This structure is returned by \f5sh_init()\fP but can also be retrieved +by a call to \f5sh_getinterp()\fP. +.PP +All built-in commands to the shell are invoked with +three arguments. The first two arguments give the +number of arguments and the argument list +and uses the same conventions as the \f5main()\fP function +of a program. The third argument is a pointer that +can be associated with each built-in. +The \f5sh_addbuiltin()\fP function is used to add, replace or delete +built-in commands. +It takes the name of the built-in, \fIname\fP, a pointer +to the function that implements the built-in, \fIfn\fP, and +a pointer that will be passed to the function when +it is invoked. +If, \fIfn\fP is non-\f5NULL\fP the built-in command +is added or replaced. Otherwise, the given +built-in command will be deleted. +The \fIname\fP argument can be in the format of a pathname. +It cannot be the name of any of the special built-in commands. +If \fIname\fP contains a \f5/\fP, the built-in is the basename of +the pathname and the built-in will only be executed +if the given pathname is encountered when performing +a path search. +When adding or replacing a built-in, +\f5sh_addbuiltin()\fP function returns a pointer to +the name-value pair corresponding to the built-in on success and \f5NULL\fP +if it is unable to add or replace the built-in. +When deleting a built-in, \f5NULL\fP is returned on success or +if not found, and the name-value pair pointer is returned if the built-in +cannot be deleted. +.PP +The functions \f5sh_onoption()\fP, \f5sh_offoption()\fP, \f5sh_isoption()\fP +are used to set, unset, and test for shell options respectively. +The \fIoption\fP argument can be any one of the following: +.IP +\f5SH_ALLEXPORT\fP: +The \f5NV_EXPORT\fP attribute is given to each variable whose +name is an identifier when a value is assigned. +.IP +\f5SH_BGNICE\fP: +Each background process is run at a lower priority. +.IP +\f5SH_ERREXIT\fP: +Causes a non-interactive shell to exit when a command, +other than a conditional command, returns non-zero. +.IP +\f5SH_EMACS\fP: +The emacs editing mode. +.IP +\f5SH_GMACS\fP: +Same as the emacs editing mode except for the behavior of CONTROL-T. +.IP +\f5SH_HISTORY\fP: +Indicates that the history file has been created and that +commands can be logged. +.IP +\f5SH_IGNOREEOF\fP: +Do not treat end-of-file as exit. +.IP +\f5SH_INTERACTIVE\fP: +.IP +Set for interactive shells. +Do not set or unset this option. +\f5SH_MARKDIRS\fP: +A \fB/\fP is added to the end of each directory generated by pathname +expansion. +.IP +\f5SH_MONITOR\fP: +Indicates that the monitor option is enabled for job control. +.IP +\f5SH_NOCLOBBER\fP: +The \fB>\fP redirection will fail if the file exists. Each file +created with \fB>\fP will have the \f5O_EXCL\fP bit set as described +in \f5\fP +.IP +\f5SH_NOGLOB\fP: +Do not perform pathname expansion. +.IP +\f5SH_NOLOG\fP: +Do not save function definitions in the history file. +.IP +\f5SH_NOTIFY\fP: +Cause a message to be generated as soon as each background job completes. +.IP +\f5SH_NOUNSET\fP: +Cause the shell to fail with an error of an unset variable is +referenced. +.IP +\f5SH_PRIVILEGED\fP: +.IP +\f5SH_VERBOSE\fP: +Cause each line to be echoed as it is read by the parser. +.IP +\f5SH_XTRACE\fP: +Cause each command to be displayed after all expansions, but +before execution. +.IP +\f5SH_VI\fP: +The vi edit mode. +.IP +\f5SH_VIRAW\fP: +Read character at a time rather than line at a time when +in vi edit mode. +.IP +.PP +The \f5sh_trap()\fP function can be used to compile and execute +a string or file. +A value of \f50\fP for \fImode\fP indicates that \fIname\fP +refers to a string. A value of \f51\fP for \fImode\fP +indicates that \fIname\fP is an \f5Sfio_t*\fP to an open stream. +A value of \f52\fP for \fImode\fP indicates that \fIname\fP +points to a parse tree that has been returned by \f5sh_parse()\fP. +The complete file associated with the string or file +is compiled and then executed so that aliases defined +within the string or file will not take effect until +the next command is executed. +.PP +The \f5sh_eval()\fP function executes a string or file +stream \fIsp\fP. +If \fImode\fP is non-zero and the history file has +been created, the stream defined by \fIsp\fP +will be appended to the history file as a command. +.PP +The \f5sh_parse()\fP function takes a pointer to the +shell interpreter \fIshp\fP, a pointer to a string or file stream +\fIsp\fP, and compilation flags, and returns a pointer +to a parse tree of the compiled stream. This pointer can +be used in subsequent calls to \f5sh_trap()\fP. +The compilation flags can be zero or more of the following: +.IP +\f5SH_NL\fP: +Treat new-lines as \fB;\fP. +.IP +\f5SH_EOF\fP: +An end of file causes syntax error. By default it will +be treated as a new-line. +.PP +\f5ksh\fP executes each function defined with the \f5function\fP +reserved word in a separate scope. The \f5Shscope_t\fP type +provides an interface to some of the information that +is available on each scope. The structure contains +the following public members: +.nf + \f5Sh_scope_t *par_scope;\fP + \f5int argc;\fP + \f5char **argv;\fP + \f5char *cmdname;\fP + \f5Dt_t *var_tree;\fP +.fi +The \f5sh_getscope()\fP function can be used to the the +scope information associated with existing scope. +Scopes are numbered from \f50\fP for the global scope +up to the current scope level. The \fIwhence\fP +argument uses the symbolic constants associated with \f5lseek()\fP +to indicate whether the \f5Iscope\fP argument is absolute, +relative to the current scope, or relative to the topmost scope. +The\f5sh_setscope()\fP function can be used to make a +a known scope the current scope. It returns a pointer to the +old current scope. +.PP +The \f5sh_funscope()\fP function can be used to run a function +in a new scope. The arguments \fIargc\fP and \fIargv\fP +are the number of arguments and the list of arguments +respectively. If \fIfn\fP is non-\f5NULL\fP, then +this function is invoked with \fIargc\fP, \fIargv\fP, and \fIarg\fP +as arguments. +.PP +The \f5sh_fun()\fP function can be called within a +discipline function or built-in extension to execute a +discipline function script. +The argument \fIfunnode\fP is a pointer to the shell function +or built-in to execute. +The argument \fIvarnode\fP is a pointer to the name +value pair that has defined this discipline. +The array \fIargv\fP is a \f5NULL\fP terminated list of +arguments that are passed to the function. +.PP +By default, \f5ksh\fP only records but does not act +on signals when running a built-in command. +If a built-in takes a substantial amount of time +to execute, then it should check for interrupts +periodically by calling \f5sh_sigcheck()\fP. +If a signal is pending, \f5sh_sigcheck()\fP will exit +the function you are calling and return to the point +where the most recent built-in was invoked, or where +\f5sh_eval()\fP or \f5sh_trap()\fP was called. +.PP +The \f5sh_delay()\fP function is used to cause the +shell to sleep for a period of time defined by \fIsec\fP. +.PP +The \f5sh_fmtq()\fP function can be used to convert a string +into a string that is quoted so that it can be reinput +to the shell. The quoted string returned by \f5sh_fmtq\fP +may be returned on the current stack, so that it +must be saved or copied. +.PP +The \f5sh_fdnotify()\fP function causes the function \fIfn\fP +to be called whenever the shell duplicates or closes a file. +It is provided for extensions that need to keep track of +file descriptors that could be changed by shell commands. +The function \fIfn\fP is called with two arguments. +The first argument is the original file descriptor. The +second argument is the new file descriptor for duplicating +files, and \f5SH_FDCLOSE\fP when a file has been closed. +The previously installed \f5sh_fdnotify()\fP function pointer +is returned. +.PP +The \f5sh_waitnotify()\fP function causes the function \fIfn\fP +to be called whenever the shell is waiting for input from +a slow device or waiting for a process to complete. +This function can process events and run shell commands +until there is input, the timer is reached or a signal arises. +It is called with three arguments. The first is the file +descriptor from which the shell trying to read or \f5\-1\fP +if the shell is waiting for a process to complete. +The second is a timeout in milliseconds. +A value of \f5\-1\fP for the timeout means that +no timeout should be set. +The third argument is 0 for input file descriptors +and 1 for output file descriptor. +The function needs to return a value \f5>0\fP if there +is input on the file descriptor, and a value \f5<0\fP +if the timeout is reached or a signal has occurred. +A value of \f50\fP indicates +that the function has returned without processing and that the shell +should wait for input or process completion. +The previous installed \f5sh_waitnotify()\fP function pointer is returned. +.PP +The \f5sh_iogetiop()\fP function returns a pointer to the +Sfio stream corresponding to file descriptor number \fIfd\fP +and the given mode \fImode\fP. The mode can be either +\f5SF_READ\fP or \f5SF_WRITE\fP. +The \fIfd\fP argument can the number of an open file descriptor or +one of the following symbolic constants: +.IP +\f5SH_IOCOPROCESS\fP: +The stream corresponding to the most recent co-process. +.IP +\f5SH_IOHISTFILE\fP: +The stream corresponding to the history file. +If no stream exists corresponding to \fIfd\fP or the stream +can not be accessed in the specified mode, \f5NULL\fP is returned. +.PP +.SH SEE ALSO +builtin(1) +cdt(3) +error(3) +nval(3) +sfio(3) +stk(3) +tksh(1) +.PP +.SH AUTHOR +David G. Korn (dgk@research.att.com). + Index: src/lib/libshell/common/data/bash_pre_rc.sh =================================================================== --- src/lib/libshell/common/data/bash_pre_rc.sh (revision 0) +++ src/lib/libshell/common/data/bash_pre_rc.sh (revision 740) @@ -0,0 +1,221 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Knowledge Ventures # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn # +# # +######################################################################## +# +# bash compatibility startup script +# +# Author: +# Karsten Fleischer +# Omnium Software Engineering +# An der Luisenburg 7 +# D-51379 Leverkusen +# Germany +# +# +# + +alias declare=typeset + +nameref FUNCNAME=.sh.fun +integer SHLVL +export SHLVL +SHLVL+=1 + +if [[ ! $EUID ]] +then EUID=$(id -u) + readonly EUID +fi + +if [[ ! $UID ]] +then UID=$(id -u) + readonly UID +fi + +readonly SHELLOPTS +if ! shopt -qo restricted; then + IFS=: + for i in $SHELLOPTS + do + [[ -n "$i" ]] && set -o $i + done + unset IFS +fi +function SHELLOPTS.get +{ + .sh.value=$(shopt -so) + .sh.value=${.sh.value//+([[:space:]])on*([[:space:]])/:} + .sh.value=${.sh.value%:} +} + +set -A GROUPS $(id -G) +function GROUPS.set +{ + return 1 +} +function GROUPS.unset +{ + unset -f GROUPS.set + unset -f GROUPS.unset +} + +typeset -A DIRSTACK +function DIRSTACK.get +{ + set -A .sh.value $(dirs) +} +function DIRSTACK.set +{ + integer index + index=_push_max-.sh.subscript + (( index == _push_max || index < _push_top )) && return + _push_stack[index]=${.sh.value} +} +function DIRSTACK.unset +{ + unset -f DIRSTACK.get + unset -f DIRSTACK.set + unset -f DIRSTACK.unset +} + +function PS1.set +{ + typeset prefix remaining=${.sh.value} var= n= k= + while [[ $remaining ]] + do prefix=${remaining%%'\'*} + remaining=${remaining#$prefix} + var+="$prefix" + case ${remaining:1:1} in + t) var+="\$(printf '%(%H:%M:%S)T')";; + d) var+="\$(printf '%(%a %b:%d)T')";; + n) var+=$'\n';; + s) var+=ksh;; + w) var+="\$(pwd)";; + W) var+="\$(basename \"\$(pwd)\")";; + u) var+=$USER;; + h) var+=$(hostname);; + '#') var+=!;; + !) var+=!;; + '$') if (( $(id -u) == 0 )) + then var+='#' + else var+='$' + fi;; + '\') var+='\\';; + '['|']') ;; + [0-7]) case ${remaining:1:3} in + [0-7][0-7][0-7]) + k=4;; + [0-7][0-7]) + k=3;; + *) k=2;; + esac + eval n="\$'"${remaining:0:k}"'" + var+=$n + remaining=${remaining:k} + continue + ;; + "") ;; + *) var+='\'${remaining:0:2};; + esac + remaining=${remaining:2} + done + .sh.value=$var +} +function logout +{ + if shopt -q login_shell; then + exit + else + print ${BASH##*/}: $0: not login shell: use 'exit' >&2 + return 1 + fi +} +PS1="bash$ " + +function source +{ + if ! shopt -qpo posix; then + unset OPATH + typeset OPATH=$PATH + typeset PATH=$PATH + if shopt -q sourcepath; then + PATH=$OPATH:. + else + PATH=. + fi + fi + . "$@" +} +unalias . +alias .=source + +alias enable=builtin + +function help +{ + man=--man + [[ $1 == -s ]] && man=--short && shift + b=$(builtin) + for i + do + for j in $b + do + [[ $i == $j ]] && $j $man + done + done +} + +function cd +{ + + local msg + local args + local i + local a + local ret + + if ! shopt -q cdable_vars; then + command cd "$@" + else + msg=$(command cd "$@" 2>&1) + ret=$? + if [[ $ret != 0 ]]; then + for i + do + case $i in + -*) args="$args $i" ;; + */*) args="$args $i" ;; + *) eval a="$"$i + if [[ -n $a ]]; then args="$args $a" + else args="$args $i" + fi + ;; + esac + done + + command cd $args + else + print -- $msg + return $ret + fi + fi +} + +typeset BASH=$0 +! shopt -qo posix && HISTFILE=~/.bash_history +HOSTNAME=$(hostname) Index: src/lib/libshell/common/data/signals.c =================================================================== --- src/lib/libshell/common/data/signals.c (revision 0) +++ src/lib/libshell/common/data/signals.c (revision 740) @@ -0,0 +1,227 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#include +#include "shtable.h" +#include "fault.h" + +#if defined(SIGCLD) && !defined(SIGCHLD) +# define SIGCHLD SIGCLD +#endif + +#define VAL(sig,mode) ((sig+1)|(mode)<0 + "PWR", VAL(SIGPWR,SH_SIGIGNORE), S("Power fail"), +# endif +#endif /* SIGPWR */ +#ifdef SIGQUIT + "QUIT", VAL(SIGQUIT,SH_SIGDONE|SH_SIGINTERACTIVE), S("Quit"), +#ifdef __SIGRTMIN +#undef SIGRTMIN +#define SIGRTMIN __SIGRTMIN +#else +#ifdef _SIGRTMIN +#undef SIGRTMIN +#define SIGRTMIN _SIGRTMIN +#endif +#endif +#ifdef SIGRTMIN + "RTMIN", VAL(SIGRTMIN,0), S("Lowest priority realtime signal"), +#endif /* SIGRTMIN */ +#ifdef __SIGRTMAX +#undef SIGRTMAX +#define SIGRTMAX __SIGRTMAX +#else +#ifdef _SIGRTMAX +#undef SIGRTMAX +#define SIGRTMAX _SIGRTMAX +#endif +#endif +#ifdef SIGRTMAX + "RTMAX", VAL(SIGRTMAX,0), S("Highest priority realtime signal"), +#endif /* SIGRTMAX */ +#endif /* SIGQUIT */ + "SEGV", VAL(SIGSEGV,0), S("Memory fault"), +#ifdef SIGSTOP + "STOP", VAL(SIGSTOP,0), S("Stopped (SIGSTOP)"), +#endif /* SIGSTOP */ +#ifdef SIGSYS + "SYS", VAL(SIGSYS,SH_SIGDONE), S("Bad system call"), +#endif /* SIGSYS */ + "TERM", VAL(SIGTERM,SH_SIGDONE|SH_SIGINTERACTIVE), S("Terminated"), +#ifdef SIGTINT +# ifdef JOBS + "TINT", VAL(SIGTINT,0), S("Interrupt"), +# else + "TINT", VAL(SIGTINT,0), "". +# endif /* JOBS */ +#endif /* SIGTINT */ +#ifdef SIGTRAP + "TRAP", VAL(SIGTRAP,SH_SIGDONE), S("Trace/BPT trap"), +#endif /* SIGTRAP */ +#ifdef SIGTSTP + "TSTP", VAL(SIGTSTP,0), S("Stopped"), +#endif /* SIGTSTP */ +#ifdef SIGTTIN + "TTIN", VAL(SIGTTIN,0), S("Stopped (SIGTTIN)"), +#endif /* SIGTTIN */ +#ifdef SIGTTOU + "TTOU", VAL(SIGTTOU,0), S("Stopped(SIGTTOU)"), +#endif /* SIGTTOU */ +#ifdef SIGURG + "URG", VAL(SIGURG,SH_SIGIGNORE), S("Socket interrupt"), +#endif /* SIGURG */ +#ifdef SIGUSR1 + "USR1", VAL(SIGUSR1,SH_SIGDONE), S("User signal 1"), +#endif /* SIGUSR1 */ +#ifdef SIGUSR2 + "USR2", VAL(SIGUSR2,SH_SIGDONE), S("User signal 2"), +#endif /* SIGUSR2 */ +#ifdef SIGVTALRM + "VTALRM", VAL(SIGVTALRM,SH_SIGDONE), S("Virtual time alarm"), +#endif /* SIGVTALRM */ +#ifdef SIGWINCH + "WINCH", VAL(SIGWINCH,SH_SIGIGNORE), S("Window size change"), +#endif /* SIGWINCH */ +#ifdef SIGMIGRATE + "MIGRATE", VAL(SIGMIGRATE,0), S("Migrate process"), +#endif /* SIGMIGRATE */ +#ifdef SIGDANGER + "DANGER", VAL(SIGDANGER,0), S("System crash soon"), +#endif /* SIGDANGER */ +#ifdef SIGSOUND + "SOUND", VAL(SIGSOUND,0), S("Sound completed"), +#endif /* SIGSOUND */ +#ifdef SIGTHAW + "THAW", VAL(SIGTHAW,SH_SIGIGNORE), S("Special signal used by CPR"), +#endif /* SIGTHAW */ +#ifdef SIGWAITING + "WAITING", VAL(SIGWAITING,SH_SIGIGNORE), S("All threads blocked"), +#endif /* SIGWAITING */ +#ifdef SIGXCPU + "XCPU", VAL(SIGXCPU,SH_SIGDONE|SH_SIGINTERACTIVE), S("Exceeded CPU time limit"), +#endif /* SIGXCPU */ +#ifdef SIGXFSZ + "XFSZ", VAL(SIGXFSZ,SH_SIGDONE|SH_SIGINTERACTIVE), S("Exceeded file size limit"), +#endif /* SIGXFSZ */ + "", 0, 0 +}; Index: src/lib/libshell/common/data/solaris_cmdlist.h =================================================================== --- src/lib/libshell/common/data/solaris_cmdlist.h (revision 0) +++ src/lib/libshell/common/data/solaris_cmdlist.h (revision 740) @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SOLARIS_CMDLIST_H +#define _SOLARIS_CMDLIST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * List builtins for Solaris. + * The list here is partially autogenerated and partially hand-picked + * based on compatibilty with the native Solaris versions of these + * tools + */ + +/* POSIX compatible commands */ +#ifdef _NOT_YET +#define XPG6CMDLIST(f) { "/usr/xpg6/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, +#define XPG4CMDLIST(f) { "/usr/xpg4/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, +#else +#define XPG6CMDLIST(f) +#define XPG4CMDLIST(f) +#endif /* NOT_YET */ +/* + * Commands which are 100% compatible with native Solaris versions (/bin is + * a softlink to ./usr/bin so both need to be listed here) + */ +#define BINCMDLIST(f) { "/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, +/* Make all ksh93 builtins accessible when /usr/ast/bin was added to ${PATH} */ +#define ASTCMDLIST(f) { "/usr/ast/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, + +/* undo ast_map.h #defines to avoid collision */ +#undef basename +#undef dirname + +/* Generated data, do not edit. */ +XPG4CMDLIST(basename) +ASTCMDLIST(basename) +BINCMDLIST(cat) +ASTCMDLIST(cat) +XPG4CMDLIST(chgrp) +ASTCMDLIST(chgrp) +ASTCMDLIST(chmod) +XPG4CMDLIST(chown) +BINCMDLIST(chown) +ASTCMDLIST(chown) +ASTCMDLIST(cmp) +ASTCMDLIST(comm) +XPG4CMDLIST(cp) +ASTCMDLIST(cp) +ASTCMDLIST(cut) +XPG4CMDLIST(date) +ASTCMDLIST(date) +ASTCMDLIST(dirname) +XPG4CMDLIST(expr) +ASTCMDLIST(expr) +ASTCMDLIST(fds) +ASTCMDLIST(fmt) +ASTCMDLIST(fold) +BINCMDLIST(head) +ASTCMDLIST(head) +XPG4CMDLIST(id) +ASTCMDLIST(id) +ASTCMDLIST(join) +XPG4CMDLIST(ln) +ASTCMDLIST(ln) +ASTCMDLIST(logname) +BINCMDLIST(mkdir) +ASTCMDLIST(mkdir) +ASTCMDLIST(mkfifo) +XPG4CMDLIST(mv) +ASTCMDLIST(mv) +ASTCMDLIST(paste) +ASTCMDLIST(pathchk) +ASTCMDLIST(rev) +XPG4CMDLIST(rm) +ASTCMDLIST(rm) +BINCMDLIST(rmdir) +ASTCMDLIST(rmdir) +XPG4CMDLIST(stty) +ASTCMDLIST(stty) +XPG4CMDLIST(tail) +ASTCMDLIST(tail) +BINCMDLIST(tee) +ASTCMDLIST(tee) +ASTCMDLIST(tty) +ASTCMDLIST(uname) +BINCMDLIST(uniq) +ASTCMDLIST(uniq) +BINCMDLIST(wc) +ASTCMDLIST(wc) +/* End-of-generated-data. */ + +/* Mandatory for ksh93 test suite and AST scripts */ +BINCMDLIST(getconf) + +#ifdef __cplusplus +} +#endif + +#endif /* _SOLARIS_CMDLIST_H */ Index: src/lib/libshell/common/data/aliases.c =================================================================== --- src/lib/libshell/common/data/aliases.c (revision 0) +++ src/lib/libshell/common/data/aliases.c (revision 740) @@ -0,0 +1,58 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#include +#include +#include "FEATURE/options" +#include "FEATURE/dynamic" +#include "shtable.h" +#include "name.h" + +/* + * This is the table of built-in aliases. These should be exported. + */ + +const struct shtable2 shtab_aliases[] = +{ +#if SHOPT_FS_3D + "2d", NV_NOFREE, "set -f;_2d", +#endif /* SHOPT_FS_3D */ + "autoload", NV_NOFREE, "typeset -fu", + "command", NV_NOFREE, "command ", + "fc", NV_NOFREE, "hist", + "float", NV_NOFREE, "typeset -lE", + "functions", NV_NOFREE, "typeset -f", + "hash", NV_NOFREE, "alias -t --", + "history", NV_NOFREE, "hist -l", + "integer", NV_NOFREE, "typeset -li", + "nameref", NV_NOFREE, "typeset -n", + "nohup", NV_NOFREE, "nohup ", + "r", NV_NOFREE, "hist -s", + "redirect", NV_NOFREE, "command exec", + "source", NV_NOFREE, "command .", +#ifdef SIGTSTP + "stop", NV_NOFREE, "kill -s STOP", + "suspend", NV_NOFREE, "kill -s STOP $$", +#endif /*SIGTSTP */ + "times", NV_NOFREE, "{ { time;} 2>&1;}", + "type", NV_NOFREE, "whence -v", + "", 0, (char*)0 +}; + Index: src/lib/libshell/common/data/limits.c =================================================================== --- src/lib/libshell/common/data/limits.c (revision 0) +++ src/lib/libshell/common/data/limits.c (revision 740) @@ -0,0 +1,57 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include +#include "ulimit.h" + +/* + * This is the list of resouce limits controlled by ulimit + * This command requires getrlimit(), vlimit(), or ulimit() + */ + +#ifndef _no_ulimit + +const char e_unlimited[] = "unlimited"; +const char* e_units[] = { 0, "block", "byte", "kbyte", "second" }; + +const int shtab_units[] = { 1, 512, 1, 1024, 1 }; + +const Limit_t shtab_limits[] = +{ +"as", "address space limit", RLIMIT_AS, 0, 'M', LIM_KBYTE, +"core", "core file size", RLIMIT_CORE, 0, 'c', LIM_BLOCK, +"cpu", "cpu time", RLIMIT_CPU, 0, 't', LIM_SECOND, +"data", "data size", RLIMIT_DATA, 0, 'd', LIM_KBYTE, +"fsize", "file size", RLIMIT_FSIZE, 0, 'f', LIM_BLOCK, +"locks", "number of file locks", RLIMIT_LOCKS, 0, 'L', LIM_COUNT, +"memlock", "locked address space", RLIMIT_MEMLOCK, 0, 'l', LIM_KBYTE, +"nofile", "number of open files", RLIMIT_NOFILE, "OPEN_MAX", 'n', LIM_COUNT, +"nproc", "number of processes", RLIMIT_NPROC, "CHILD_MAX", 'u', LIM_COUNT, +"pipe", "pipe buffer size", RLIMIT_PIPE, "PIPE_BUF", 'p', LIM_BYTE, +"rss", "resident set size", RLIMIT_RSS, 0, 'm', LIM_KBYTE, +"sbsize", "socket buffer size", RLIMIT_SBSIZE, "PIPE_BUF", 'b', LIM_BYTE, +"stack", "stack size", RLIMIT_STACK, 0, 's', LIM_KBYTE, +"threads", "number of threads", RLIMIT_PTHREAD, "THREADS_MAX", 'T', LIM_COUNT, +"vmem", "process size", RLIMIT_VMEM, 0, 'v', LIM_KBYTE, +{ 0 } +}; + +#endif Index: src/lib/libshell/common/data/testops.c =================================================================== --- src/lib/libshell/common/data/testops.c (revision 0) +++ src/lib/libshell/common/data/testops.c (revision 740) @@ -0,0 +1,168 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +/* + * tables for the test builin [[...]] and [...] + */ + +#include + +#include "shtable.h" +#include "test.h" + +/* + * This is the list of binary test and [[...]] operators + */ + +const Shtable_t shtab_testops[] = +{ + "!=", TEST_SNE, + "-a", TEST_AND, + "-ef", TEST_EF, + "-eq", TEST_EQ, + "-ge", TEST_GE, + "-gt", TEST_GT, + "-le", TEST_LE, + "-lt", TEST_LT, + "-ne", TEST_NE, + "-nt", TEST_NT, + "-o", TEST_OR, + "-ot", TEST_OT, + "=", TEST_SEQ, + "==", TEST_SEQ, + "=~", TEST_REP, + "<", TEST_SLT, + ">", TEST_SGT, + "]]", TEST_END, + "", 0 +}; + +const char sh_opttest[] = +"[-1c?\n@(#)$Id: test (AT&T Research) 2003-03-18 $\n]" +USAGE_LICENSE +"[+NAME?test - evaluate expression]" +"[+DESCRIPTION?\btest\b evaluates expressions and indicates its " + "results based on the exit status. Option parsing is not " + "performed so that all arguments, including \b--\b are processed " + " as operands. The evaluation of the " + "expression depends on the number of operands as follows:]{" + "[+0?Evaluates to false.]" + "[+1?True if argument is not an empty string.]" + "[+2?If first operand is \b!\b, the result is True if the second " + "operand an empty string. Otherwise, it is evaluated " + "as one of the unary expressions defined below. If the " + "unary operator is invalid and the second argument is \b--\b," + "then the first argument is processed as an option argument.]" + "[+3?If first operand is \b!\b, the result is True if the second " + "and third operand evaluated as a unary expression is False. " + "Otherwise, the three operands are evaluaged as one of the " + "binary expressions listed below.]" + "[+4?If first operand is \b!\b, the result is True if the next " + "three operands are a valid binary expression that is False.]" +"}" +"[If any \afile\a is of the form \b/dev/fd/\b\an\a, then file descriptor " + "\an\a is checked.]" +"[+?Unary expressions can be one of the following:]{" + "[+-a \afile\a?True if \afile\a exists, obsolete.]" + "[+-b \afile\a?True if \afile\a exists and is a block special file.]" + "[+-c \afile\a?True if \afile\a exists and is a character special " + "file.]" + "[+-d \afile\a?True if \afile\a exists and is a directory.]" + "[+-e \afile\a?True if \afile\a exists.]" + "[+-f \afile\a?True if \afile\a exists and is a regular file.]" + "[+-g \afile\a?True if \afile\a exists and has its set-group-id bit " + "set.]" + "[+-h \afile\a?True if \afile\a exists and is a symbolic link.]" + "[+-k \afile\a?True if \afile\a exists and has its sticky bit on.]" +#if SHOPT_TEST_L + "[+-l \afile\a?True if \afile\a exists and is a symbolic link.]" +#endif + "[+-n \astring\a?True if length of \astring\a is non-zero.]" + "[+-o \aoption\a?True if the shell option \aoption\a is enabled.]" + "[+-p \afile\a?True if \afile\a exists and is a pipe or fifo.]" + "[+-r \afile\a?True if \afile\a exists and is readable.]" + "[+-s \afile\a?True if \afile\a exists and has size > 0.]" + "[+-t \afildes\a?True if file descriptor number \afildes\a is " + "open and is associated with a terminal device.]" + "[+-u \afile\a?True if \afile\a exists and has its set-user-id bit " + "set.]" + "[+-w \afile\a?True if \afile\a exists and is writable.]" + "[+-x \afile\a?True if \afile\a exists and is executable. For a " + "directory it means that it can be searched.]" + "[+-z \astring\a?True if \astring\a is a zero length string.]" + "[+-G \afile\a?True if \afile\a exists and group is the effective " + "group id of the current process.]" + "[+-L \afile\a?True if \afile\a exists and is a symbolic link.]" + "[+-N \afile\a?True if \afile\a exists and has been modified since " + "it was last read.]" + "[+-O \afile\a?True if \afile\a exists and owner is the effective " + "user id of the current process.]" + "[+-S \afile\a?True if \afile\a exists and is a socket.]" +"}" +"[+?Binary expressions can be one of the following:]{" + "[+\astring1\a = \astring2\a?True if \astring1\a is equal to " + "\astring2\a.]" + "[+\astring1\a == \astring2\a?True if \astring1\a is equal to " + "\astring2\a.]" + "[+\astring1\a != \astring2\a?True if \astring1\a is not equal to " + "\astring2\a.]" + "[+\anum1\a -eq \anum2\a?True if numerical value of \anum1\a is " + "equal to \anum2\a.]" + "[+\anum1\a -ne \anum2\a?True if numerical value of \anum1\a is not " + "equal to \anum2\a.]" + "[+\anum1\a -lt \anum2\a?True if numerical value of \anum1\a is less " + "than \anum2\a.]" + "[+\anum1\a -le \anum2\a?True if numerical value of \anum1\a is less " + "than or equal to \anum2\a.]" + "[+\anum1\a -gt \anum2\a?True if numerical value of \anum1\a is " + "greater than \anum2\a.]" + "[+\anum1\a -ge \anum2\a?True if numerical value of \anum1\a is " + "greater than or equal to \anum2\a.]" + "[+\afile1\a -nt \afile2\a?True if \afile1\a is newer than \afile2\a " + "or \afile2\a does not exist.]" + "[+\afile1\a -ot \afile2\a?True if \afile1\a is older than \afile2\a " + "or \afile2\a does not exist.]" + "[+\afile1\a -ef \afile2\a?True if \afile1\a is another name for " + "\afile2\a. This will be true if \afile1\a is a hard link " + "or a symbolic link to \afile2\a.]" +"}" +"\n" +"\n[expression]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Indicates that the specified expression is True.]" + "[+1?Indicates that the specified expression is False.]" + "[+>1?An error occurred.]" +"}" + +"[+SEE ALSO?\blet\b(1), \bexpr\b(1)]" +; + +const char test_opchars[] = "HLNSVOGCaeohrwxdcbfugk" +#if SHOPT_TEST_L + "l" +#endif + "psnzt"; +const char e_argument[] = "argument expected"; +const char e_missing[] = "%s missing"; +const char e_badop[] = "%s: unknown operator"; +const char e_tstbegin[] = "[[ ! "; +const char e_tstend[] = " ]]\n"; Index: src/lib/libshell/common/data/strdata.c =================================================================== --- src/lib/libshell/common/data/strdata.c (revision 0) +++ src/lib/libshell/common/data/strdata.c (revision 740) @@ -0,0 +1,104 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * data for string evaluator library + */ + +#include +#include "FEATURE/options" +#include "streval.h" + +const unsigned char strval_precedence[35] = + /* opcode precedence,assignment */ +{ + /* DEFAULT */ MAXPREC|NOASSIGN, + /* DONE */ 0|NOASSIGN|RASSOC, + /* NEQ */ 10|NOASSIGN, + /* NOT */ MAXPREC|NOASSIGN, + /* MOD */ 14, + /* ANDAND */ 6|NOASSIGN|SEQPOINT, + /* AND */ 9|NOFLOAT, + /* LPAREN */ MAXPREC|NOASSIGN|SEQPOINT, + /* RPAREN */ 1|NOASSIGN|RASSOC|SEQPOINT, + /* POW */ 14|NOASSIGN|RASSOC, + /* TIMES */ 14, + /* PLUSPLUS */ 15|NOASSIGN|NOFLOAT|SEQPOINT, + /* PLUS */ 13, + /* COMMA */ 1|NOASSIGN|SEQPOINT, + /* MINUSMINUS */ 15|NOASSIGN|NOFLOAT|SEQPOINT, + /* MINUS */ 13, + /* DIV */ 14, + /* LSHIFT */ 12|NOFLOAT, + /* LE */ 11|NOASSIGN, + /* LT */ 11|NOASSIGN, + /* EQ */ 10|NOASSIGN, + /* ASSIGNMENT */ 2|RASSOC, + /* COLON */ 0|NOASSIGN, + /* RSHIFT */ 12|NOFLOAT, + /* GE */ 11|NOASSIGN, + /* GT */ 11|NOASSIGN, + /* QCOLON */ 3|NOASSIGN|SEQPOINT, + /* QUEST */ 3|NOASSIGN|SEQPOINT|RASSOC, + /* XOR */ 8|NOFLOAT, + /* OROR */ 5|NOASSIGN|SEQPOINT, + /* OR */ 7|NOFLOAT, + /* DEFAULT */ MAXPREC|NOASSIGN, + /* DEFAULT */ MAXPREC|NOASSIGN, + /* DEFAULT */ MAXPREC|NOASSIGN, + /* DEFAULT */ MAXPREC|NOASSIGN +}; + +/* + * This is for arithmetic expressions + */ +const char strval_states[64] = +{ + A_EOF, A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, + A_REG, 0, 0, A_REG, A_REG, A_REG, A_REG, A_REG, + A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, + A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, A_REG, + + 0, A_NOT, 0, A_REG, A_REG, A_MOD, A_AND, A_LIT, + A_LPAR, A_RPAR, A_TIMES,A_PLUS, A_COMMA,A_MINUS,A_DOT, A_DIV, + A_DIG, A_DIG, A_DIG, A_DIG, A_DIG, A_DIG, A_DIG, A_DIG, + A_DIG, A_DIG, A_COLON,A_REG, A_LT, A_ASSIGN,A_GT, A_QUEST + +}; + + +const char e_argcount[] = "%s: function has wrong number of arguments"; +const char e_badnum[] = "%s: bad number"; +const char e_moretokens[] = "%s: more tokens expected"; +const char e_paren[] = "%s: unbalanced parenthesis"; +const char e_badcolon[] = "%s: invalid use of :"; +const char e_divzero[] = "%s: divide by zero"; +const char e_synbad[] = "%s: arithmetic syntax error"; +const char e_notlvalue[] = "%s: assignment requires lvalue"; +const char e_recursive[] = "%s: recursion too deep"; +const char e_questcolon[] = "%s: ':' expected for '?' operator"; +const char e_function[] = "%s: unknown function"; +const char e_incompatible[] = "%s: invalid floating point operation"; +const char e_overflow[] = "%s: overflow exception"; +const char e_domain[] = "%s: domain exception"; +const char e_singularity[] = "%s: singularity exception"; +const char e_charconst[] = "%s: invalid character constant"; + +#include "FEATURE/math" Index: src/lib/libshell/common/data/msg.c =================================================================== --- src/lib/libshell/common/data/msg.c (revision 0) +++ src/lib/libshell/common/data/msg.c (revision 740) @@ -0,0 +1,188 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * UNIX shell + * S. R. Bourne + * Rewritten by David Korn + * + * AT&T Labs + * + */ + +#include +#include +#include "defs.h" +#include "path.h" +#include "io.h" +#include "shlex.h" +#include "timeout.h" +#include "history.h" +#include "builtins.h" +#include "jobs.h" +#include "edit.h" + +/* error messages */ +const char e_timewarn[] = "\r\n\ashell will timeout in 60 seconds due to inactivity"; +const char e_runvi[] = "\\hist -e \"${VISUAL:-${EDITOR:-vi}}\" "; +const char e_timeout[] = "timed out waiting for input"; +const char e_mailmsg[] = "you have mail in $_"; +const char e_query[] = "no query process"; +const char e_history[] = "no history file"; +const char e_histopen[] = "history file cannot open"; +const char e_option[] = "%s: bad option(s)"; +const char e_toomany[] = "open file limit exceeded"; +const char e_argtype[] = "invalid argument of type %c"; +const char e_formspec[] = "%c: unknown format specifier"; +const char e_badregexp[] = "%s: invalid regular expression"; +const char e_number[] = "%s: bad number"; +const char e_badlocale[] = "%s: unknown locale"; +const char e_nullset[] = "%s: parameter null"; +const char e_notset[] = "%s: parameter not set"; +const char e_noparent[] = "%s: no parent"; +const char e_subst[] = "%s: bad substitution"; +const char e_create[] = "%s: cannot create"; +const char e_tmpcreate[] = "cannot create temporary file"; +const char e_restricted[] = "%s: restricted"; +const char e_pfsh[] = "%s: disabled in profile shell"; +const char e_pexists[] = "process already exists"; +const char e_exists[] = "%s: file already exists"; +const char e_pipe[] = "cannot create pipe"; +const char e_alarm[] = "cannot set alarm"; +const char e_open[] = "%s: cannot open"; +const char e_notseek[] = "%s: not seekable"; +const char e_badseek[] = "%s: invalid seek offset"; +const char e_badpattern[] = "%s: invalid shell pattern"; +const char e_noread[] = "%s: pattern seek requires read access"; +const char e_logout[] = "Use 'exit' to terminate this shell"; +const char e_exec[] = "%s: cannot execute"; +const char e_pwd[] = "cannot access parent directories"; +const char e_found[] = "%s: not found"; +const char e_defined[] = "%s: function not defined"; +const char e_nointerp[] = "%s: interpreter not found"; +const char e_subscript[] = "%s: subscript out of range"; +const char e_toodeep[] = "%s: recursion too deep"; +const char e_access[] = "permission denied"; +#ifdef _cmd_universe + const char e_nouniverse[] = "universe not accessible"; +#endif /* _cmd_universe */ +const char e_direct[] = "bad directory"; +const char e_file[] = "%s: bad file unit number"; +const char e_trap[] = "%s: bad trap"; +const char e_readonly[] = "%s: is read only"; +const char e_badfield[] = "%d: negative field size"; +const char e_ident[] = "%s: is not an identifier"; +const char e_badname[] = "%s: invalid name"; +const char e_varname[] = "%s: invalid variable name"; +const char e_badfun[] = "%s: invalid function name"; +const char e_aliname[] = "%s: invalid alias name"; +const char e_badexport[] = "%s: invalid export name"; +const char e_badref[] = "%s: reference variable cannot be an array"; +const char e_noarray[] = "%s: cannot be an array"; +const char e_noref[] = "%s: no reference name"; +const char e_selfref[] = "%s: invalid self reference"; +const char e_noalias[] = "%s: alias not found\n"; +const char e_format[] = "%s: bad format"; +const char e_redef[] = "%s: type cannot be redefined"; +const char e_badtformat[] = "%c: bad format character in time format"; +const char e_nolabels[] = "%s: label not implemented"; +const char e_notimp[] = "%s: not implemented"; +const char e_nosupport[] = "not supported"; +const char e_badrange[] = "%d-%d: invalid range"; +const char e_eneedsarg[] = "-e - requires single argument"; +const char e_badbase[] = "%s unknown base"; +const char e_loop[] = "%s: would cause loop"; +const char e_overlimit[] = "%s: limit exceeded"; +const char e_badsyntax[] = "incorrect syntax"; +const char e_badwrite[] = "write to %d failed"; +const char e_on [] = "on"; +const char e_off[] = "off"; +const char is_reserved[] = " is a keyword"; +const char is_builtin[] = " is a shell builtin"; +const char is_builtver[] = "is a shell builtin version of"; +const char is_alias[] = "%s is an alias for "; +const char is_xalias[] = "%s is an exported alias for "; +const char is_talias[] = "is a tracked alias for"; +const char is_function[] = " is a function"; +const char is_ufunction[] = " is an undefined function"; +#ifdef JOBS +# ifdef SIGTSTP + const char e_newtty[] = "Switching to new tty driver..."; + const char e_oldtty[] = "Reverting to old tty driver..."; + const char e_no_start[] = "Cannot start job control"; +# endif /*SIGTSTP */ + const char e_no_jctl[] = "No job control"; + const char e_terminate[] = "You have stopped jobs"; + const char e_done[] = " Done"; + const char e_nlspace[] = "\n "; + const char e_running[] = " Running"; + const char e_ambiguous[] = "%s: Ambiguous"; + const char e_jobsrunning[] = "You have running jobs"; + const char e_no_job[] = "no such job"; + const char e_no_proc[] = "no such process"; + const char e_jobusage[] = "%s: Arguments must be %%job or process ids"; +#endif /* JOBS */ +const char e_coredump[] = "(coredump)"; +const char e_alphanum[] = "[_[:alpha:]]*([_[:alnum:]])"; +const char e_devfdNN[] = "/dev/fd/+([0-9])"; +const char e_devfdstd[] = "/dev/@(fd/+([0-9])|std@(in|out|err))"; +const char e_signo[] = "Signal %d"; +#if SHOPT_FS_3D + const char e_cantget[] = "cannot get %s"; + const char e_cantset[] = "cannot set %s"; + const char e_mapping[] = "mapping"; + const char e_versions[] = "versions"; +#endif /* SHOPT_FS_3D */ + +/* string constants */ +const char e_heading[] = "Current option settings"; +const char e_sptbnl[] = " \t\n"; +const char e_defpath[] = "/bin:/usr/bin:"; +const char e_defedit[] = "/bin/ed"; +const char e_unknown [] = ""; +const char e_devnull[] = "/dev/null"; +const char e_traceprompt[] = "+ "; +const char e_supprompt[] = "# "; +const char e_stdprompt[] = "$ "; +const char e_profile[] = "$HOME/.profile"; +const char e_sysprofile[] = "/etc/profile"; +const char e_suidprofile[] = "/etc/suid_profile"; +#if SHOPT_SYSRC +const char e_sysrc[] = "/etc/ksh.kshrc"; +#endif +#if SHOPT_BASH +#if SHOPT_SYSRC +const char e_bash_sysrc[] = "/etc/bash.bashrc"; +#endif +const char e_bash_rc[] = "$HOME/.bashrc"; +const char e_bash_login[] = "$HOME/.bash_login"; +const char e_bash_logout[] = "$HOME/.bash_logout"; +const char e_bash_profile[] = "$HOME/.bash_profile"; +#endif +const char e_crondir[] = "/usr/spool/cron/atjobs"; +const char e_prohibited[] = "login setuid/setgid shells prohibited"; +#if SHOPT_SUID_EXEC + const char e_suidexec[] = "/etc/suid_exec"; +#endif /* SHOPT_SUID_EXEC */ +const char hist_fname[] = "/.sh_history"; +const char e_dot[] = "."; +const char e_envmarker[] = "A__z"; +const char e_timeformat[] = "\nreal\t%2lR\nuser\t%2lU\nsys\t%2lS"; +const char e_dict[] = "libshell"; Index: src/lib/libshell/common/data/keywords.c =================================================================== --- src/lib/libshell/common/data/keywords.c (revision 0) +++ src/lib/libshell/common/data/keywords.c (revision 740) @@ -0,0 +1,63 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +#include "shtable.h" +#include +#include "shlex.h" +#include "FEATURE/options" + +/* + * table of reserved words in shell language + * This list must be in in ascii sorted order + */ + +const Shtable_t shtab_reserved[] = +{ + "!", NOTSYM, + "[[", BTESTSYM, + "case", CASESYM, + "do", DOSYM, + "done", DONESYM, + "elif", ELIFSYM, + "else", ELSESYM, + "esac", ESACSYM, + "fi", FISYM, + "for", FORSYM, + "function", FUNCTSYM, + "if", IFSYM, + "in", INSYM, +#if SHOPT_NAMESPACE + "namespace", NSPACESYM, +#endif /* SHOPT_NAMESPACE */ + "select", SELECTSYM, + "then", THENSYM, + "time", TIMESYM, + "until", UNTILSYM, + "while", WHILESYM, + "{", LBRACE, + "}", RBRACE, + "", 0, +}; + +const char e_unexpected[] = "unexpected"; +const char e_unmatched[] = "unmatched"; +const char e_endoffile[] = "end of file"; +const char e_newline[] = "newline"; + Index: src/lib/libshell/common/data/variables.c =================================================================== --- src/lib/libshell/common/data/variables.c (revision 0) +++ src/lib/libshell/common/data/variables.c (revision 740) @@ -0,0 +1,105 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include +#include "FEATURE/options" +#include "FEATURE/dynamic" +#include +#include "shtable.h" +#include "name.h" +#include "defs.h" + +/* + * This is the list of built-in shell variables and default values + * and default attributes. + */ + +const struct shtable2 shtab_variables[] = +{ + "PATH", 0, (char*)0, + "PS1", 0, (char*)0, + "PS2", NV_NOFREE, "> ", + "IFS", NV_NOFREE, " \t\n", + "PWD", 0, (char*)0, + "HOME", 0, (char*)0, + "MAIL", 0, (char*)0, + "REPLY", 0, (char*)0, + "SHELL", NV_NOFREE, "/bin/" SH_STD, + "EDITOR", 0, (char*)0, + "MAILCHECK", NV_NOFREE|NV_INTEGER, (char*)0, + "RANDOM", NV_NOFREE|NV_INTEGER, (char*)0, + "ENV", NV_NOFREE, "$HOME/.kshrc", + "HISTFILE", 0, (char*)0, + "HISTSIZE", 0, (char*)0, + "HISTEDIT", NV_NOFREE, (char*)0, + "HISTCMD", NV_NOFREE|NV_INTEGER, (char*)0, + "FCEDIT", NV_NOFREE, "/bin/ed", + "CDPATH", 0, (char*)0, + "MAILPATH", 0, (char*)0, + "PS3", NV_NOFREE, "#? ", + "OLDPWD", 0, (char*)0, + "VISUAL", 0, (char*)0, + "COLUMNS", 0, (char*)0, + "LINES", 0, (char*)0, + "PPID", NV_NOFREE|NV_INTEGER, (char*)0, + "_", NV_EXPORT, (char*)0, + "TMOUT", NV_NOFREE|NV_INTEGER, (char*)0, + "SECONDS", NV_NOFREE|NV_INTEGER|NV_DOUBLE, (char*)0, + "LINENO", NV_NOFREE|NV_INTEGER, (char*)0, + "OPTARG", 0, (char*)0, + "OPTIND", NV_NOFREE|NV_INTEGER, (char*)0, + "PS4", 0, (char*)0, + "FPATH", 0, (char*)0, + "LANG", 0, (char*)0, + "LC_ALL", 0, (char*)0, + "LC_COLLATE", 0, (char*)0, + "LC_CTYPE", 0, (char*)0, + "LC_MESSAGES", 0, (char*)0, + "LC_NUMERIC", 0, (char*)0, + "FIGNORE", 0, (char*)0, + ".sh", NV_TABLE|NV_RDONLY|NV_NOFREE|NV_NOPRINT,(char*)0, + ".sh.edchar", 0, (char*)0, + ".sh.edcol", 0, (char*)0, + ".sh.edtext", 0, (char*)0, + ".sh.edmode", 0, (char*)0, + ".sh.name", 0, (char*)0, + ".sh.subscript",0, (char*)0, + ".sh.value", 0, (char*)0, + ".sh.version", NV_NOFREE, (char*)(&e_version[10]), + ".sh.dollar", 0, (char*)0, + ".sh.match", 0, (char*)0, + ".sh.command", 0, (char*)0, + ".sh.file", 0, (char*)0, + ".sh.fun", 0, (char*)0, + ".sh.subshell", NV_INTEGER|NV_SHORT|NV_NOFREE, (char*)0, + ".sh.level", 0, (char*)0, +#if SHOPT_FS_3D + "VPATH", 0, (char*)0, +#endif /* SHOPT_FS_3D */ +#if SHOPT_MULTIBYTE + "CSWIDTH", 0, (char*)0, +#endif /* SHOPT_MULTIBYTE */ +#ifdef apollo + "SYSTYPE", 0, (char*)0, +#endif /* apollo */ + "", 0, (char*)0 +}; + Index: src/lib/libshell/common/data/builtins.c =================================================================== --- src/lib/libshell/common/data/builtins.c (revision 0) +++ src/lib/libshell/common/data/builtins.c (revision 740) @@ -0,0 +1,1833 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include +#include +#include "defs.h" +#include "shtable.h" +#include "ulimit.h" +#include "name.h" +#include "version.h" +#if KSHELL +# include "builtins.h" +# include "jobs.h" +# include "FEATURE/cmds" +# define bltin(x) (b_##x) + /* The following is for builtins that do not accept -- options */ +# define Bltin(x) (B_##x) +#else +# define bltin(x) 0 +#endif + +#ifndef SH_CMDLIB_DIR +# define SH_CMDLIB_DIR "/opt/ast/bin" +#endif +#if defined(SHOPT_CMDLIB_DIR) && !defined(SHOPT_CMDLIB_HDR) +# define SHOPT_CMDLIB_HDR +#endif +#define Q(f) #f /* libpp cpp workaround -- fixed 2005-04-11 */ +#define CMDLIST(f) SH_CMDLIB_DIR "/" Q(f), NV_BLTIN|NV_NOFREE, bltin(f), + +#undef basename +#undef dirname + +/* + * The order up through "[" is significant + */ +const struct shtable3 shtab_builtins[] = +{ + "login", NV_BLTIN|BLT_ENV|BLT_SPC, Bltin(login), + "exec", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(exec), + "set", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(set), + ":", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(true), + "true", NV_BLTIN|BLT_ENV, bltin(true), + "command", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(command), + "cd", NV_BLTIN|BLT_ENV, bltin(cd), + "break", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(break), + "continue", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(break), + "typeset", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(typeset), + "test", NV_BLTIN|BLT_ENV|NV_NOFREE, bltin(test), + "[", NV_BLTIN|BLT_ENV, bltin(test), + "let", NV_BLTIN|BLT_ENV, bltin(let), + "export", NV_BLTIN|BLT_SPC|BLT_DCL, bltin(readonly), +#if SHOPT_BASH + "local", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(typeset), +#endif +#if _bin_newgrp || _usr_bin_newgrp + "newgrp", NV_BLTIN|BLT_ENV|BLT_SPC, Bltin(login), +#endif /* _bin_newgrp || _usr_bin_newgrp */ + ".", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(dot_cmd), + "alias", NV_BLTIN|BLT_SPC|BLT_DCL, bltin(alias), + "hash", NV_BLTIN|BLT_SPC|BLT_DCL, bltin(alias), + "exit", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(return), + "eval", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_EXIT,bltin(eval), + "fc", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(hist), + "hist", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(hist), + "readonly", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(readonly), + "return", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(return), + "shift", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(shift), + "trap", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(trap), + "unalias", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(unalias), + "unset", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(unset), + "builtin", NV_BLTIN, bltin(builtin), +#if SHOPT_ECHOPRINT + "echo", NV_BLTIN|BLT_ENV, bltin(print), +#else + "echo", NV_BLTIN|BLT_ENV, Bltin(echo), +#endif /* SHOPT_ECHOPRINT */ +#ifdef JOBS +# ifdef SIGTSTP + "bg", NV_BLTIN|BLT_ENV, bltin(bg), + "fg", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(bg), + "disown", NV_BLTIN|BLT_ENV, bltin(bg), + "kill", NV_BLTIN|BLT_ENV|NV_NOFREE, bltin(kill), +# else + "/bin/kill", NV_BLTIN|BLT_ENV|NV_NOFREE, bltin(kill), +# endif /* SIGTSTP */ + "jobs", NV_BLTIN|BLT_ENV, bltin(jobs), +#endif /* JOBS */ + "false", NV_BLTIN|BLT_ENV, bltin(false), +SH_CMDLIB_DIR "/getconf",NV_BLTIN|BLT_ENV, bltin(getconf), + "getopts", NV_BLTIN|BLT_ENV, bltin(getopts), + "print", NV_BLTIN|BLT_ENV, bltin(print), + "printf", NV_BLTIN|NV_NOFREE, bltin(printf), + "pwd", NV_BLTIN|NV_NOFREE, bltin(pwd), + "read", NV_BLTIN|BLT_ENV, bltin(read), + "sleep", NV_BLTIN|NV_NOFREE, bltin(sleep), + "alarm", NV_BLTIN, bltin(alarm), + "ulimit", NV_BLTIN|BLT_ENV, bltin(ulimit), + "umask", NV_BLTIN|BLT_ENV, bltin(umask), +#ifdef _cmd_universe + "universe", NV_BLTIN|BLT_ENV, bltin(universe), +#endif /* _cmd_universe */ +#if SHOPT_FS_3D + "vpath", NV_BLTIN|BLT_ENV, bltin(vpath), + "vmap", NV_BLTIN|BLT_ENV, bltin(vpath), +#endif /* SHOPT_FS_3D */ + "wait", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(wait), + "type", NV_BLTIN|BLT_ENV, bltin(whence), + "whence", NV_BLTIN|BLT_ENV, bltin(whence), +#ifdef SHOPT_CMDLIB_HDR +#include SHOPT_CMDLIB_HDR +#else + CMDLIST(basename) + CMDLIST(chmod) + CMDLIST(dirname) + CMDLIST(head) + CMDLIST(mkdir) + CMDLIST(logname) + CMDLIST(cat) + CMDLIST(cmp) + CMDLIST(cut) + CMDLIST(uname) + CMDLIST(wc) + CMDLIST(sync) +#endif + "", 0, 0 +}; + + +const char sh_set[] = +"[a?Set the export attribute for each variable whose name does not " + "contain a \b.\b that you assign a value in the current shell " + "environment.]" +"[b?The shell writes a message to standard error as soon it detects that " + "a background job completes rather than waiting until the next prompt.]" +"[e?A simple command that has an non-zero exit status will cause the shell " + "to exit unless the simple command is:]{" + "[++?contained in an \b&&\b or \b||\b list.]" + "[++?the command immediately following \bif\b, \bwhile\b, or \buntil\b.]" + "[++?contained in the pipeline following \b!\b.]" +"}" +"[f?Pathname expansion is disabled.]" +"[h?Obsolete. Causes each command whose name has the syntax of an " + "alias to become a tracked aliase when it is first encountered.]" +"[k?This is obsolete. All arguments of the form \aname\a\b=\b\avalue\a " + "are removed and placed in the variable assignment list for " + "the command. Ordinarily, variable assignments must precede " + "command arguments.]" +"[m?When enabled, the shell runs background jobs in a separate process " + "group and displays a line upon completion. This mode is enabled " + "by default for interactive shells on systems that support job " + "control.]" +"[n?The shell reads commands and checks for syntax errors, but does " + "not execute the command. Usually specified on command invocation.]" +"[o]:?[option?If \aoption\a is not specified, the list of options and " + "their current settings will be written to standard output. When " + "invoked with a \b+\b the options will be written in a format " + "that can be reinput to the shell to restore the settings. " + "This option can be repeated to enable/disable multiple options. " + "The value of \aoption\a must be one of the following:]{" + "[+allexport?Equivalent to \b-a\b.]" + "[+bgnice?Runs background jobs at lower priorities.]" + "[+braceexpand?Equivalent to \b-B\b.] " + "[+emacs?Enables/disables \bemacs\b editing mode.]" + "[+errexit?Equivalent to \b-e\b.]" + "[+globstar?Equivalent to \b-G\b.]" + "[+gmacs?Enables/disables \bgmacs\b editing mode. \bgmacs\b " + "editing mode is the same as \bemacs\b editing mode " + "except for the handling of \b^T\b.]" +#if SHOPT_BASH + "[+hashall?Equivalent to \b-h\b and \b-o trackall\b. Available " + "in bash compatibility mode only.]" + "[+history?Enable command history. Available in bash " + "compatibility mode only. On by default in interactive " + "shells.]" +#endif +#if SHOPT_HISTEXPAND + "[+histexpand?Equivalent to \b-H\b.]" +#endif + "[+ignoreeof?Prevents an interactive shell from exiting on " + "reading an end-of-file.]" + "[+keyword?Equivalent to \b-k\b.]" + "[+markdirs?A trailing \b/\b is appended to directories " + "resulting from pathname expansion.]" + "[+monitor?Equivalent to \b-m\b.]" + "[+multiline?Use multiple lines when editing lines that are " + "longer than the window width.]" + "[+noclobber?Equivalent to \b-C\b.]" + "[+noexec?Equivalent to \b-n\b.]" + "[+noglob?Equivalent to \b-f\b.]" + "[+nolog?This has no effect. It is provided for backward " + "compatibility.]" + "[+notify?Equivalent to \b-b\b.]" + "[+nounset?Equivalent to \b-u\b.]" +#if SHOPT_BASH + "[+onecmd?Equivalent to \b-t\b. Available in bash compatibility " + "mode only.]" + "[+physical?Equivalent to \b-P\b. Available in bash " + "compatibility mode only.]" + "[+posix?Turn on POSIX compatibility. Available in bash " + "compatibility mode only. Bash in POSIX mode is not the " + "same as ksh.]" +#endif + "[+pipefail?A pipeline will not complete until all components " + "of the pipeline have completed, and the exit status " + "of the pipeline will be the value of the last " + "command to exit with non-zero exit status, or will " + "be zero if all commands return zero exit status.]" + "[+privileged?Equivalent to \b-p\b.]" + "[+showme?Simple commands preceded by a \b;\b will be traced " + "as if \b-x\b were enabled but not executed.]" + "[+trackall?Equivalent to \b-h\b.]" + "[+verbose?Equivalent to \b-v\b.]" + "[+vi?Enables/disables \bvi\b editing mode.]" + "[+viraw?Does not use canonical input mode when using \bvi\b " + "edit mode.]" + "[+xtrace?Equivalent to \b-x\b.]" +"}" +"[p?Privileged mode. Disabling \b-p\b sets the effective user id to the " + "real user id, and the effective group id to the real group id. " + "Enabling \b-p\b restores the effective user and group ids to their " + "values when the shell was invoked. The \b-p\b option is on " + "whenever the real and effective user id is not equal or the " + "real and effective group id is not equal. User profiles are " + "not processed when \b-p\b is enabled.]" +"[r?restricted. Enables restricted shell. This option cannot be unset once " + "enabled.]" +"[t?Obsolete. The shell reads one command and then exits.]" +"[u?If enabled, the shell displays an error message when it tries to expand " + "a variable that is unset.]" +"[v?Verbose. The shell displays its input onto standard error as it " + "reads it.]" +"[x?Execution trace. The shell will display each command after all " + "expansion and before execution preceded by the expanded value " + "of the \bPS4\b parameter.]" +#if SHOPT_BASH + "\fbash1\f" +#endif +#if SHOPT_BRACEPAT +"[B?Enable {...} group expansion. On by default.]" +#endif +"[C?Prevents existing regular files from being overwritten using the \b>\b " + "redirection operator. The \b>|\b redirection overrides this " + "\bnoclobber\b option.]" +"[G?Causes \b**\b by itself to also match all sub-directories during pathname " + "expansion.]" +#if SHOPT_HISTEXPAND + "[H?Enable \b!\b-style history expansion similar to \bcsh\b.]" +#endif +; + +const char sh_optbreak[] = +"[-1c?\n@(#)$Id: break (AT&T Research) 1999-04-07 $\n]" +USAGE_LICENSE +"[+NAME?break - break out of loop ]" +"[+DESCRIPTION?\bbreak\b is a shell special built-in that exits the " + "smallest enclosing \bfor\b, \bselect\b, \bwhile\b, or \buntil\b loop, " + "or the \an\a-th enclosing loop if \an\a is specified. " + "Execution continues at the command following the loop(s).]" +"[+?If \an\a is given, it must be a positive integer >= 1. If \an\a " + "is larger than the number of enclosing loops, the last enclosing " + "loop will be exited.]" +"\n" +"\n[n]\n" +"\n" +"[+EXIT STATUS?0]" +"[+SEE ALSO?\bcontinue\b(1), \breturn\b(1)]" +; + +const char sh_optcont[] = +"[-1c?\n@(#)$Id: continue (AT&T Research) 1999-04-07 $\n]" +USAGE_LICENSE +"[+NAME?continue - continue execution at top of the loop]" +"[+DESCRIPTION?\bcontinue\b is a shell special built-in that continues " + "execution at the top of smallest enclosing enclosing \bfor\b, " + "\bselect\b, \bwhile\b, or \buntil\b loop, if any; or the top of " + "the \an\a-th enclosing loop if \an\a is specified.]" +"[+?If \an\a is given, it must be a positive integer >= 1. If \an\a " + "is larger than the number of enclosing loops, the last enclosing " + " loop will be used.]" + +"\n" +"\n[n]\n" +"\n" +"[+SEE ALSO?\bbreak\b(1)]" +; + +const char sh_optalarm[] = "r [varname seconds]"; +const char sh_optalias[] = +"[-1c?\n@(#)$Id: alias (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?alias - define or display aliases]" +"[+DESCRIPTION?\balias\b creates or redefines alias definitions " + "or writes the existing alias definitions to standard output. " + "An alias definitions provides a string value that will replace " + "a command name when the command is read. Alias names can " + "contain any printable character which is not special to the shell. " + "If an alias value ends in a space or tab, then the word " + "following the command name the alias replaces is also checked " + "to see whether it is an alias.]" +"[+?If no \aname\as are specified then the names and values of all " + "aliases are written to standard output. Otherwise, for " + "each \aname\a that is specified, and \b=\b\avalue\a is not " + "specified, the current value of the alias corresponding to " + "\aname\a is written to standard output. If \b=\b\avalue\a is " + "specified, the alias \aname\a will be created or redefined.]" +"[+?\balias\b is built-in to the shell as a declaration command so that " + "field splitting and pathname expansion are not performed on " + "the arguments. Tilde expansion occurs on \avalue\a. An alias " + "definition only affects scripts read by the current shell " + "environment. It does not effect scripts run by this shell.]" +"[p?Causes the output to be in the form of alias commands that can be used " + "as input to the shell to recreate the current aliases.]" +"[t?Used for tracked aliases. These are aliases that connect a " + "command name to the pathname of the command and are reset " + "when the \bPATH\b variable is unset. The tracked aliases feature is " + "now obsolete.]" +"[x?Ignored, this option is obsolete.]" +"\n" +"\n[name[=value]...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?One or more \aname\a operands did not have an alias " + "definition, or an error occurred.]" +"}" + +"[+SEE ALSO?\bsh\b(1), \bunalias\b(1)]" +; + +const char sh_optbuiltin[] = +"[-1c?\n@(#)$Id: builtin (AT&T Research) 1999-07-10 $\n]" +USAGE_LICENSE +"[+NAME?builtin - add, delete, or display shell built-ins]" +"[+DESCRIPTION?\bbuiltin\b can be used to add, delete, or display " + "built-in commands in the current shell environment. A built-in command " + "executes in the current shell process and can have side effects in the " + "current shell. On most systems, the invocation time for built-in " + "commands is one or two orders of magnitude less than commands that " + "create a separate process.]" +"[+?For each \apathname\a specified, the basename of the pathname " + "determines the name of the built-in. For each basename, the shell looks " + "for a C level function in the current shell whose name is determined by " + "prepending \bb_\b to the built-in name. If \apathname\a contains a " + "\b/\b, then the built-in is bound to this pathname. A built-in bound to " + "a pathname will only be executed if \apathname\a is the first " + "executable found during a path search. Otherwise, built-ins are found " + "prior to performing the path search.]" +"[+?If no \apathname\a operands are specified, then \bbuiltin\b displays " + "the current list of built-ins, or just the special built-ins if \b-s\b " + "is specified, on standard output. The full pathname for built-ins that " + "are bound to pathnames are displayed.]" +"[+?Libraries containing built-ins can be specified with the \b-f\b " + "option. If the library contains a function named \blib_init\b(), this " + "function will be invoked with argument \b0\b when the library is " + "loaded. The \blib_init\b() function can load built-ins by invoking an " + "appropriate C level function. In this case there is no restriction on " + "the C level function name.]" +"[+?The C level function will be invoked with three arguments. The first " + "two are the same as \bmain\b() and the third one is a pointer.]" +"[+?\bbuiltin\b cannot be invoked from a restricted shell.]" +"[d?Deletes each of the specified built-ins. Special built-ins cannot be " + "deleted.]" +"[f]:[lib?On systems with dynamic linking, \alib\a names a shared " + "library to load and search for built-ins. Libraries are search for in " + "\b$PATH\b and system dependent library directories. The system " + "dependent shared library prefix and/or suffix may be omitted. Once a " + "library is loaded, its symbols become available for the current and " + "subsequent invocations of \bbuiltin\b. Multiple libraries can be " + "specified with separate invocations of \bbuiltin\b. Libraries are " + "searched in the reverse order in which they are specified.]" +"[s?Display only the special built-ins.]" +"\n" +"\n[pathname ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?All \apathname\a operands and \b-f\b options processed " + "successfully.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bwhence\b(1)]" +; + +const char sh_optcd[] = +"[-1c?\n@(#)$Id: cd (AT&T Research) 1999-06-05 $\n]" +USAGE_LICENSE +"[+NAME?cd - change working directory ]" +"[+DESCRIPTION?\bcd\b changes the current working directory of the " + "current shell environment.]" +"[+?In the first form with one operand, if \adirectory\a begins with " + "\b/\b, or if the first component is \b.\b or \b..\b, the " + "directory will be changed to this directory. If directory is \b-\b, " + "the directory will be changed to the last directory visited. " + "Otherwise, if the \bCDPATH\b environment variable is set, \bcd\b " + "searches for \adirectory\a relative to each directory named in " + "the colon separated list of directories defined by \bCDPATH\b. " + "If \bCDPATH\b not set, \bcd\b changes to the directory specified " + "by \adirectory\a.]" +"[+?In the second form, the first occurrence of the string \aold\a " + "contained in the pathname of the present working directory " + "is replaced by the string \anew\a and the resulting string " + "is used as the directory to which to change.]" +"[+?When invoked without operands and when the \bHOME\b environment " + "variable is set to a nonempty value, the directory named by " + "the \bHOME\b environment variable will be used. If \bHOME\b " + "is empty or unset, \bcd\b will fail.]" +"[+?When \bcd\b is successful, the \bPWD\b environment variable will be set " + "to the name of an absolute pathname that does not contain any " + "\b..\b components corresponding to the new directory. The " + "environment variable \bOLDPWD\b will be set to the previous " + "value of \bPWD\b. If the new directory is found by searching " + "the directories named by \bCDPATH\b, or if \adirectory\a is \b-\b, " + "or if the two operand form is used, the new value of \bPWD\b will be " + "written to standard output.]" +"[+?If both \b-L\b and \b-P\b are specified, the last one specified will " + "be used. If neither \b-P\b or \b-L\b is specified then the " + "behavior will be determined by the \bgetconf\b parameter " + "\bPATH_RESOLVE\b. If \bPATH_RESOLVE\b is \bphysical\b, " + "then the behavior will be as if \b-P\b were specified. Otherwise, " + "the behavior will be as if \b-L\b were specified.]" +"[L?Handle each pathname component \b..\b in a logical fashion by moving " + "up one level by name in the present working directory.]" +"[P?The present working directory is first converted to an absolute pathname " + "that does not contain symbolic link components and symbolic name " + "components are expanded in the resulting directory name.]" +"\n" +"\n[directory]\n" +"old new\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Directory successfully changed.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bpwd\b(1), \bgetconf\b(1)]" +; + +const char sh_optcommand[] = +"[-1c?\n@(#)$Id: command (AT&T Research) 2003-08-01 $\n]" +USAGE_LICENSE +"[+NAME?command - execute a simple command]" +"[+DESCRIPTION?Without \b-v\b or \b-V\b, \bcommand\b executes \acommand\a " + "with arguments given by \aarg\a, suppressing the shell function lookup " + "that normally occurs. In addition, if \acommand\a is a special " + "built-in command, then the special properties are removed so that " + "failures will not cause the script that executes it to terminate.]" +"[+?With the \b-v\b or \b-V\b options, \bcommand\b is equivalent to the " + "\bwhence\b(1) command.]" +"[p?Causes a default path to be searched rather than the one defined by the " + "value of \bPATH\b.]" +"[v?Equivalent to \bwhence\b \acommand\a [\aarg\a ...]].]" +"[x?If \acommand\a fails because there are too many \aarg\as, it will be " + "invoked multiple times with a subset of the arguments on each " + "invocation. Arguments that occur prior to the first word that expand " + "to multiple arguments and arguments that occur after the last word " + "that expands to multiple arguments will be passed on each invocation. " + "The exit status will be the maximum invocation exit status.]" +"[V?Equivalent to \bwhence \b-v\b \acommand\a [\aarg\a ...]].]" +"\n" +"\n[command [arg ...]]\n" +"\n" +"[+EXIT STATUS?If \acommand\a is invoked, the exit status of \bcommand\b " + "will be that of \acommand\a. Otherwise, it will be one of " + "the following:]{" + "[+0?\bcommand\b completed successfully.]" + "[+>0?\b-v\b or \b-V\b has been specified and an error occurred.]" + "[+126?\acommand\a was found but could not be invoked.]" + "[+127?\acommand\a could not be found.]" +"}" + +"[+SEE ALSO?\bwhence\b(1), \bgetconf\b(1)]" +; + +const char sh_optdot[] = +"[-1c?@(#)$Id: \b.\b (AT&T Research) 2000-04-02 $\n]" +USAGE_LICENSE +"[+NAME?\b.\b - execute commands in the current environment]" +"[+DESCRIPTION?\b.\b is a special built-in command that executes commands " + "from a function or a file in the current environment.]" +"[+?If \aname\a refers to a function defined with the \bfunction\b \aname\a " + "syntax, the function executes in the current environment as " + "if it had been defined with the \aname\a\b()\b syntax so that " + "there is no scoping. Otherwise, commands from the file defined " + "by \aname\a are executed in the current environment. Note that " + "the complete script is read before it begins to execute so that " + "any aliases defined in this script will not take effect until " + "the script completes execution.]" +"[+?When \aname\a refers to a file, the \bPATH\b variable is searched " + "for the file containing commands. In this case execute permission " + "is not required for \aname\a.]" +"[+?If any \aarg\as are specified, these become the positional parameters " + "for the duration of the function or script and are restored " + "upon completion.]" +"\n" +"\n name [arg ...]\n" +"\n" +"[+EXIT STATUS?If \aname\a is found, then the exit status is that " + "of the last command executed. Otherwise, since this is a special " + "built-in, an error will cause a non-interactive shell to exit with " + "a non-zero exit status. An interactive shell returns a non-zero exit " + "status to indicate an error.]" + +"[+SEE ALSO?\bcommand\b(1), \bksh\b(1)]" +; + +#ifndef ECHOPRINT + const char sh_optecho[] = " [-n] [arg...]"; +#endif /* !ECHOPRINT */ + +const char sh_opteval[] = +"[-1c?\n@(#)$Id: eval (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?eval - create a shell command and process it]" +"[+DESCRIPTION?\beval\b is a shell special built-in command that constructs " + "a command by concatenating the \aarg\as together, separating each " + "with a space. The resulting string is then taken as input to " + "the shell and evaluated in the current environment. Note that " + "command words are expanded twice; once to construct \aarg\a, and " + "again when the shell executes the constructed command.]" +"[+?It is not an error if \aarg\a is not given.]" +"\n" +"\n[arg...]\n" +"\n" +"[+EXIT STATUS?If \aarg\a is not specified, the exit status is \b0\b. " + "Otherwise, it is the exit status of the command defined by the " + "\aarg\a operands.]" +"[+SEE ALSO?\bexec\b(1), \btrap\b(1), \b.\b(1)]" +; + +const char sh_optexec[] = +"[-1c?\n@(#)$Id: exec (AT&T Research) 1999-07-10 $\n]" +USAGE_LICENSE +"[+NAME?exec - execute command, open/close and duplicate file descriptors]" +"[+DESCRIPTION?\bexec\b is a special built-in command that can be used to " + "manipulate file descriptors or to replace the current shell " + "with a new command.]" +"[+?If \acommand\a is specified, then the current shell process will be " + "replaced by \acommand\a rather than running \acommand\a and waiting " + "for it to complete. Note that there is no need to use " + "\bexec\b to enhance performance since the shell implicitly " + "uses the exec mechanism internally whenever possible.]" +"[+?If no operands are specified, \bexec\b can be used to open or " + "close files, or to manipulate file descriptors from \b0\b to " + "\b9\b in the current shell environment using the standard " + "redirection mechanism available with all commands. The " + "close-on-exec flags will be set on file descriptor numbers " + "greater than \b2\b that are opened this way so that they " + "will be closed when another program is invoked.]" +"[+?Because \bexec\b is a special command, any failure will cause the " + "script that invokes it to exit. This can be prevented by " + "invoking \bexec\b from the \bcommand\b utility.]" +"[+?\bexec\b cannot be invoked from a restricted shell to create " + "files or to open a file for writing or appending.]" +"[c?Clear all environment variables before executions except variable " + "assignments that are part of the current \bexec\b command.]" +"[a]:[name?\bargv[0]]\b will be set to \aname\a for \acommand\a]" +"\n" +"\n[command [arg ...]]\n" +"\n" +"[+EXIT STATUS?If \acommand\a is specified, \bexec\b does not return. " + "Otherwise, the exit status is one of the following:]{" + "[+0?All I/O redirections were successful.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bcommand\b(1), \beval\b(1)]" +; + + +const char sh_optexit[] = +"[-1c?\n@(#)$Id: exit (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?exit - exit the current shell]" +"[+DESCRIPTION?\bexit\b is shell special built-in that causes the " + "shell that invokes it to exit. Before exiting the shell, if the " + "\bEXIT\b trap is set it will be invoked.]" +"[+?If \an\a is given, it will be used to set the exit status.]" +"\n" +"\n[n]\n" +"\n" +"[+EXIT STATUS?If \an\a is specified, the exit status is the least significant " + "eight bits of the value of \an\a. Otherwise, the exit status is the " + "exit status of preceding command. When invoked inside a trap, the " + "preceding command means the command that invoked the trap.]" +"[+SEE ALSO?\bbreak\b(1), \breturn\b(1)]" +; + +const char sh_optexport[] = +"[-1c?\n@(#)$Id: export (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?export - set export attribute on variables]" +"[+DESCRIPTION?\bexport\b sets the export attribute on each of " + "the variables specified by \aname\a which causes them " + "to be in the environment of subsequently executed commands. " + "If \b=\b\avalue\a is specified, the variable \aname\a is " + "set to \avalue\a.]" +"[+?If no \aname\as are specified then the names and values of all " + "exported variables are written to standard output.]" +"[+?\bexport\b is built-in to the shell as a declaration command so that " + "field splitting and pathname expansion are not performed on " + "the arguments. Tilde expansion occurs on \avalue\a.]" +"[p?Causes the output to be in the form of \bexport\b commands that can be " + "used as input to the shell to recreate the current exports.]" +"\n" +"\n[name[=value]...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\bsh\b(1), \btypeset\b(1)]" +; + +const char sh_optgetopts[] = +":[-1c?\n@(#)$Id: getopts (AT&T Research) 2005-01-01 $\n]" +"[-author?Glenn Fowler ]" +USAGE_LICENSE +"[+NAME?\f?\f - parse utility options]" +"[+DESCRIPTION?The \bgetopts\b utility can be used to retrieve options and " + "arguments from a list of arguments given by \aargs\a or the positional " + "parameters if \aargs\a is omitted. It can also generate usage messages " + "and a man page for the command based on the information in \aoptstring\a.]" +"[+?Each time it is invoked, the \bgetopts\b utility places the value " + "of the next option in the shell variable specified by the \aname\a " + "operand and the index of the next argument to be processed in the " + "shell variable \bOPTIND\b. When the shell is invoked \bOPTIND\b " + "is initialized to \b1\b. When an option requires or permits an option " + "argument, \bgetopts\b places the option argument in the shell " + "variable \bOPTARG\b. Otherwise \bOPTARG\b is set to \b1\b when the " + "option is set and \b0\b when the option is unset.]" +"[+?The \aoptstring\a string consists of alpha-numeric characters, " + "the special characters +, -, ?, :, and , or character groups " + "enclosed in [...]]. Character groups may be nested in {...}. " + "Outside of a [...]] group, a single new-line followed by zero or " + "more blanks is ignored. One or more blank lines separate the " + "options from the command argument synopsis.]" +"[+?Each [...]] group consists of an optional label, " + "optional attributes separated by :, and an " + "optional description string following ?. The characters from the ? " + "to the end of the next ]] are ignored for option parsing and short " + "usage messages. They are used for generating verbose help or man pages. " + "The : character may not appear in the label. " + "The ? character must be specified as ?? in the label and the ]] character " + "must be specified as ]]]] in the description string. " + "Text between two \\b (backspace) characters indicates " + "that the text should be emboldened when displayed. " + "Text between two \\a (bell) characters indicates that the text should " + "be emphasised or italicized when displayed. " + "Text between two \\v (vertical tab) characters indicates " + "that the text should displayed in a fixed width font. " + "Text between two \\f (formfeed) characters will be replaced by the " + "output from the shell function whose name is that of the enclosed text.]" +"[+?All output from this interface is written to the standard error.]" +"[+?There are several group types:]{" + "[+1.?A group of the form " + "[-[\aversion\a]][\aflag\a[\anumber\a]]]]...[?\atext\a]]]] " + "appearing as the first group enables the extended interface. \aversion\a " + "specifies the interface version, currently \b1\b. The latest version is " + "assumed if \aversion\a is omitted. Future enhancements " + "may increment \aversion\a, but all versions will be supported. \atext\a " + "typically specifies an SCCS or CVS identification string. Zero or more " + "\aflags\a with optional \anumber\a values may be specified to control " + "option parsing. " + "The flags are:]{" + "[+c?Cache this \aoptstring\a for multiple passes. Used to optimize " + "builtins that may be called many times within the same process.]" + "[+i?Ignore this \aoptstring\a when generating help. Used when " + "combining \aoptstring\a values from multiple passes.]" + "[+l?Display only \alongname\a options in help messages.]" + "[+o?The \b-\b option character prefix is optional (supports " + "obsolete \bps\b(1) option syntax.)]" + "[+p?\anumber\a specifies the number of \b-\b characters that must " + "prefix long option names. The default is \b2\b; \b0\b, \b1\b or " + "\b2\b are accepted (e.g., \bp0\b for \bdd\b(1) and \bp1\b for " + "\bfind\b(1).)]" + "[+s?\anumber\a specifies the \b--??man\b section number, " + "\b1\b by default.]" + "}" + "[+2.?An option specification of the form " + "[\aoption\a[!]][=\anumber\a]][:\alongname\a]][?\atext\a]]]]. In this " + "case the first field is the option character; this is the value returned " + "in the \aname\a operand when the option is matched. If there is no " + "option character then a two or more digit number should be specified. " + "This number will be returned as the value of the \aname\a operand if the " + "long option is matched. If \aoption\a is followed by \b!\b then the option " + "character sense is the inverse of the longname sense. For options that do " + "not take values \bOPTARG\b will be set to \b0\b for \b!\b inverted option " + "characters and \b1\b otherwise. =\anumber\a optionally specifies a number to " + "be returned in the \aname\a operand instead of the option character. A " + "longname is specified by \b--\b\alongname\a and is matched by the shortest " + "non-ambiguous prefix of all long options. * in the \alongname\a field " + "indicates that only characters up to that point need to match, provided " + "any additional characters match exactly. The enclosing [ and ]] can be " + "omitted for an option that does not have a longname or descriptive text.]" + "[+3.?An option argument specification. " + "Options that take arguments can be followed by : (string value) or # " + "(numeric value) and an option argument specification. An option argument " + "specification consists of the option argument name as field 1. " + "The remaining \b:\b separated fields are a type name and zero or more of " + "the special attribute words \blistof\b, \boneof\b, and \bignorecase\b. " + "A default option value may be specified in the final field as " + "\b:=\b\adefault\a. The option argument specification may be followed " + "by a list of option value descriptions enclosed in braces. " + "A long option that takes an argument is specified as " + "\b--\b\alongname\a=\avalue\a. If the : or # is followed by ? then the " + "option argument is optional. If only the option character form is " + "specified then the optional argument value is not set if the next " + "argument starts with - or +.]" + "[+4.?A option value description.]" + "[+5.?A argument specification. A list of valid option argument values " + "can be specified by enclosing them inside a {...} following " + "the option argument specification. Each of the permitted " + "values can be specified with a [...]] containing the " + "value followed by a description.]" + "[+6.?A group of the form [+\\n...]] will display the characters " + "representing ... in fixed with font without adding line breaks.]" + "[+7.?A group of the form [+\aname\a?\atext\a]] specifies a section " + "\aname\a with descriptive \atext\a. If \aname\a is omitted then " + "\atext\a is placed in a new paragraph.]" + "[+8.?A group of the form [-\aname\a?\atext\a]] specifies entries " + "for the \bIMPLEMENTATION\b section.]" +"}" +"[+?If the leading character of \aoptstring\a is +, then arguments " + "beginning with + will also be considered options.]" +"[+?A leading : character or a : following a leading + in \aoptstring\a " + "affects the way errors are handled. If an option character or longname " + "argument not specified in \aoptstring\a is encountered when processing " + "options, the shell variable whose name is \aname\a will be set to the ? " + "character. The shell variable \bOPTARG\b will be set to " + "the character found. If an option argument is missing or has an invalid " + "value, then \aname\a will be set to the : character and the shell variable " + "\bOPTARG\b will be set to the option character found. " + "Without the leading :, \aname\a will be set to the ? character, \bOPTARG\b " + "will be unset, and an error message will be written to standard error " + "when errors are encountered.]" +"[+?The end of options occurs when:]{" + "[+1.?The special argument \b--\b is encountered.]" + "[+2.?An argument that does not begin with a \b-\b is encountered.]" + "[+3.?A help argument is specified.]" + "[+4.?An error is encountered.]" +"}" +"[+?If \bOPTIND\b is set to the value \b1\b, a new set of arguments " + "can be used.]" +"[+?\bgetopts\b can also be used to generate help messages containing command " + "usage and detailed descriptions. Specify \aargs\a as:]" +"{ " + "[+-???To generate a usage synopsis.]" + "[+--?????To generate a verbose usage message.]" + "[+--????man?To generate a formatted man page.]" + "[+--????api?To generate an easy to parse usage message.]" + "[+--????html?To generate a man page in \bhtml\b format.]" + "[+--????nroff?To generate a man page in \bnroff\b format.]" + "[+--????usage?List the current \aoptstring\a.]" + "[+--??????\aname\a?List \bversion=\b\an\a, \an\a>0, " + "if the option \aname\a is recognized by \bgetopts\b.]" +"}" +"[+?When the end of options is encountered, \bgetopts\b exits with a " + "non-zero return value and the variable \bOPTIND\b is set to the " + "index of the first non-option argument.]" +"a:[name?Use \aname\a instead of the command name in usage messages.]" +"\n" +"\nopstring name [args...]\n" +"\n" +"[+EXIT STATUS]{" + "[+0?An option specified was found.]" + "[+1?An end of options was encountered.]" + "[+2?A usage or information message was generated.]" +"}" +; + +const char sh_optbg[] = +"[-1c?@(#)$Id: bg (AT&T Research) 2000-04-02 $\n]" +USAGE_LICENSE +"[+NAME?bg - resume jobs in the background]" +"[+DESCRIPTION?\bbg\b places the given \ajob\as into the background " + "and sends them a \bCONT\b signal to start them running.]" +"[+?If \ajob\a is omitted, the most recently started or stopped " + "background job is resumed or continued in the background.]" +"[+?Each \ajob\a can be specified as one of the following:]{" + "[+\anumber\a?\anumber\a refers to a process id.]" + "[+-\anumber\a?\anumber\a refers to a process group id.]" + "[+%\anumber\a?\anumber\a refer to a job number.]" + "[+%\astring\a?Refers to a job whose name begins with \astring\a.]" + "[+%??\astring\a?Refers to a job whose name contains \astring\a.]" + "[+%+ \bor\b %%?Refers to the current job.]" + "[+%-?Refers to the previous job.]" +"}" +"\n" +"\n[job ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?If all background jobs are started.]" + "[+>0?If one more jobs does not exist or there are no background " + "jobs.]" +"}" + +"[+SEE ALSO?\bwait\b(1), \bfg\b(1), \bdisown\b(1), \bjobs\b(1)]" +; + +const char sh_optfg[] = +"[-1c?@(#)$Id: fg (AT&T Research) 2000-04-02 $\n]" +USAGE_LICENSE +"[+NAME?fg - move jobs to the foreground]" +"[+DESCRIPTION?\bfg\b places the given \ajob\as into the foreground " + "in sequence and sends them a \bCONT\b signal to start each running.]" +"[+?If \ajob\a is omitted, the most recently started or stopped " + "background job is moved to the foreground.]" +"[+?Each \ajob\a can be specified as one of the following:]{" + "[+\anumber\a?\anumber\a refers to a process id.]" + "[+-\anumber\a?\anumber\a refers to a process group id.]" + "[+%\anumber\a?\anumber\a refer to a job number.]" + "[+%\astring\a?Refers to a job whose name begins with \astring\a.]" + "[+%??\astring\a?Refers to a job whose name contains \astring\a.]" + "[+%+ \bor\b %%?Refers to the current job.]" + "[+%-?Refers to the previous job.]" +"}" +"\n" +"\n[job ...]\n" +"\n" +"[+EXIT STATUS?If \bfg\b brings one or more jobs into the foreground, " + "the exit status of \bfg\b will be that of the last \ajob\a. " + "If one or more jobs does not exist or has completed, \bfg\b will " + "return a non-zero exit status.]" +"}" + +"[+SEE ALSO?\bwait\b(1), \bbg\b(1), \bjobs\b(1)]" +; + +const char sh_optdisown[] = +"[-1c?@(#)$Id: disown (AT&T Research) 2000-04-02 $\n]" +USAGE_LICENSE +"[+NAME?disown - disassociate a job with the current shell]" +"[+DESCRIPTION?\bdisown\b prevents the current shell from sending " + "a \bHUP\b signal to each of the given \ajob\as when " + "the current shell terminates a login session.]" +"[+?If \ajob\a is omitted, the most recently started or stopped " + "background job is used.]" +"[+?Each \ajob\a can be specified as one of the following:]{" + "[+\anumber\a?\anumber\a refers to a process id.]" + "[+-\anumber\a?\anumber\a refers to a process group id.]" + "[+%\anumber\a?\anumber\a refer to a job number.]" + "[+%\astring\a?Refers to a job whose name begins with \astring\a.]" + "[+%??\astring\a?Refers to a job whose name contains \astring\a.]" + "[+%+ \bor\b %%?Refers to the current job.]" + "[+%-?Refers to the previous job.]" +"}" +"\n" +"\n[job ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?If all jobs are sucessfully disowned.]" + "[+>0?If one more \ajob\as does not exist.]" +"}" + +"[+SEE ALSO?\bwait\b(1), \bbg\b(1), \bjobs\b(1)]" +; + +const char sh_optjobs[] = +"[-1c?@(#)$Id: jobs (AT&T Research) 2000-04-02 $\n]" +USAGE_LICENSE +"[+NAME?jobs - display status of jobs]" +"[+DESCRIPTION?\bjobs\b displays information about specified \ajob\as " + "that were started by the current shell environment on standard " + "output. The information contains the job number enclosed in " + "[...]], the status, and the command line that started the job.]" +"[+?If \ajob\a is omitted, \bjobs\b displays the status of all stopped jobs, " + "background jobs, and all jobs whose status has changed since last " + "reported by the shell.]" +"[+?When \bjobs\b reports the termination status of a job, the " + "shell removes the jobs from the list of known jobs in " + "the current shell environment.]" +"[+?Each \ajob\a can be specified as one of the following:]{" + "[+\anumber\a?\anumber\a refers to a process id.]" + "[+-\anumber\a?\anumber\a refers to a process group id.]" + "[+%\anumber\a?\anumber\a refer to a job number.]" + "[+%\astring\a?Refers to a job whose name begins with \astring\a.]" + "[+%??\astring\a?Refers to a job whose name contains \astring\a.]" + "[+%+ \bor\b %%?Refers to the current job.]" + "[+%-?Refers to the previous job.]" +"}" +"[l?\bjobs\b displays process id's after the job number in addition " + "to the usual information]" +"[n?Only the jobs whose status has changed since the last prompt " + "is displayed.]" +"[p?The process group leader id's for the specified jobs are displayed.]" +"\n" +"\n[job ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?The information for each job is written to standard output.]" + "[+>0?One or more jobs does not exist.]" +"}" + +"[+SEE ALSO?\bwait\b(1), \bps\b(1), \bfg\b(1), \bbg\b(1)]" +; + +const char sh_opthist[] = +"[-1c?@(#)$Id: hist (AT&T Research) 2000-04-02 $\n]" +USAGE_LICENSE +"[+NAME?\f?\f - process command history list]" +"[+DESCRIPTION?\b\f?\f\b lists, edits, or re-executes, commands " + "previously entered into the current shell environment.]" +"[+?The command history list references commands by number. The first number " + "in the list is selected arbitrarily. The relationship of a number " + "to its command does not change during a login session. When the " + "number reaches 32767 the number wraps around to 1 but " + "maintains the ordering.]" +"[+?When commands are edited (when the \b-l\b option is not specified), the " + "resulting lines will be entered at the end of the history list and " + "then reexecuted by the current shell. The \b\f?\f\b command that " + "caused the editing will not be entered into the history list. If the " + "editor returns a non-zero exit status, this will suppress the " + "entry into the history list and the command reexecution. Command " + "line variable assignments and redirections affect both the \f?\f " + "command and the commands that are reexecuted.]" +"[+?\afirst\a and \alast\a define the range of commands. \afirst\a and " + "\alast\a can be one of the following:]{" + "[+\anumber\a?A positive number representing a command " + "number. A \b+\b sign can precede \anumber\a.]" + "[+-\anumber\a?A negative number representing a command " + "that was executed \anumber\a commands previously. " + "For example, \b-1\b is the previous command.]" + "[+\astring\a?\astring\a indicates the most recently " + "entered command that begins with \astring\a. " + "\astring\a should not contain an \b=\b.]" + "}" +"[+?If \afirst\a is omitted, the previous command is used, unless \b-l\b " + "is specified, in which case it will default to \b-16\b and \alast\a " + "will default to \b-1\b.]" +"[+?If \afirst\a is specified and \alast\a is omitted, then \alast\a will " + "default to \afirst\a unless \b-l\b is specified in which case " + "it will default to \b-1\b.]" +"[+?If no editor is specified, then the editor specfied by the \bHISTEDIT\b " + "variable will be used if set, or the \bFCEDIT\b variable will be " + "used if set, otherwise, \bed\b will be used.]" +"[e]:[editor?\aeditor\a specifies the editor to use to edit the history " + "command. A value of \b-\b for \aeditor\a is equivalent to " + "specifiying the \b-s\b option.]" +"[l?List the commands rather than editing and reexecuting them.]" +"[N]#[num?Start at \anum\a commands back.]" +"[n?Suppress the command numbers when the commands are listed.]" +#if SHOPT_HISTEXPAND +"[p?Writes the result of history expansion for each operand to standard " + "output. All other options are ignored.]" +#endif +"[r?Reverse the order of the commands.]" +"[s?Reexecute the command without invoking an editor. In this case " + "an operand of the form \aold\a\b=\b\anew\a can be specified " + "to change the first occurrence of the string \aold\a in the " + "command to \anew\a before reexecuting the command.]" + +"\n" +"\n[first [last] ]\n" +"\n" +"[+EXIT STATUS?If a command is reexecuted, the exit status is that of " + "the command that gets reexecuted. Otherwise, it is one of the " + "following:]{" + "[+0?Successfully completion of the listing.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\bksh\b(1), \bsh\b(1), \bed\b(1)]" +; + +const char sh_optkill[] = +"[-1c?\n@(#)$Id: kill (AT&T Research) 1999-06-17 $\n]" +USAGE_LICENSE +"[+NAME?kill - terminate or signal process]" +"[+DESCRIPTION?With the first form in which \b-l\b is not specified, " + "\bkill\b sends a signal to one or more processes specified by " + "\ajob\a. This normally terminates the processes unless the signal " + "is being caught or ignored.]" +"[+?A \ajob\a can be specified as one of the following:]{" + "[+\anumber\a?\anumber\a refers to a process id.]" + "[+-\anumber\a?\anumber\a refers to a process group id.]" + "[+%\anumber\a?\anumber\a refer to a job number.]" + "[+%\astring\a?Refers to a job whose name begins with \astring\a.]" + "[+%??\astring\a?Refers to a job whose name contains \astring\a.]" + "[+%+ \bor\b %%?Refers to the current job.]" + "[+%-?Refers to the previous job.]" +"}" +"[+?If the signal is not specified with either the \b-n\b or the \b-s\b " + "option, the \bSIGTERM\b signal is used.]" +"[+?If \b-l\b is specified, and no \aarg\a is specified, then \bkill\b " + "writes the list of signals to standard output. Otherwise, \aarg\a " + "can be either a signal name, or a number representing either a " + "signal number or exit status for a process that was terminated " + "due to a signal. If a name is given the corresponding signal " + "number will be written to standard output. If a number is given " + "the corresponding signal name will be written to standard output.]" +"[l?List signal names or signal numbers rather than sending signals as " + "described above. " + "The \b-n\b and \b-s\b options cannot be specified.]" +"[n]#[signum?Specify a signal number to send. Signal numbers are not " + "portable across platforms, except for the following:]{" + "[+0?No signal]" + "[+1?\bHUP\b]" + "[+2?\bINT\b]" + "[+3?\bQUIT\b]" + "[+6?\bABRT\b]" + "[+9?\bKILL\b]" + "[+14?\bALRM\b]" + "[+15?\bTERM\b]" + "}" +"[s]:[signame?Specify a signal name to send. The signal names are derived " + "from their names in \b\b without the \bSIG\b prefix and " + "are case insensitive. \bkill -l\b will generate the list of " + "signals on the current platform.]" +"\n" +"\njob ...\n" +" -l [arg ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?At least one matching process was found for each \ajob\a " + "operand, and the specified signal was successfully sent to at " + "least one matching process.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bps\b(1), \bjobs\b(1), \bkill\b(2), \bsignal\b(2)]" +; + +const char sh_optlet[] = +"[-1c?@(#)$Id: let (AT&T Research) 2000-04-02 $\n]" +USAGE_LICENSE +"[+NAME?let - evaluate arithmetic expressions]" +"[+DESCRIPTION?\blet\b evaluates each \aexpr\a in the current " + "shell environment as an arithmetic expression using ANSI C " + "syntax. Variables names are shell variables and they " + "are recursively evaluated as arithmetic expressions to " + "get numerical values.]" +"[+?\blet\b has been made obsolete by the \b((\b...\b))\b syntax " + "of \bksh\b(1) which does not require quoting of the operators " + "to pass them as command arguments.]" +"\n" +"\n[expr ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?The last \aexpr\a evaluates to a non-zero value.]" + "[+>0?The last \aexpr\a evaluates to \b0\b or an error occurred.]" +"}" + +"[+SEE ALSO?\bexpr\b(1), \btest\b(1), \bksh\b(1)]" +; + +const char sh_optprint[] = +"[-1c?\n@(#)$Id: print (AT&T Research) 1999-04-07 $\n]" +USAGE_LICENSE +"[+NAME?print - write arguments to standard output]" +"[+DESCRIPTION?By default, \bprint\b writes each \astring\a operand to " + "standard output and appends a newline character.]" +"[+?Unless, the \b-r\b or \b-f\b option is specifed, each \b\\\b " + "character in each \astring\a operand is processed specially as " + "follows:]{" + "[+\\a?Alert character.]" + "[+\\b?Backspace character.]" + "[+\\c?Terminate output without appending newline. The " + "remaining \astring\a operands are ignored.]" + "[+\\f?Formfeed character.]" + "[+\\n?Newline character.]" + "[+\\t?Tab character.]" + "[+\\v?Vertical tab character.]" + "[+\\\\?Backslash character.]" + "[+\\E?Escape character (ASCII octal 033).]" + "[+\\0\ax\a?The 8-bit character whose ASCII code is the " + "1-, 2-, or 3-digit octal number \ax\a.]" + "}" +"[+?If both \b-e\b and \b-r\b are specified, the last one specified is " + "the one that is used.]" +"[+?When the \b-f\b option is specified and there are more \astring\a " + "operands than format specifiers, the format string is " + "reprocessed from the beginning. If there are fewer \astring\a " + "operands than format specifiers, then outputting will end " + "at the first unneeded format specifier.]" +"[e?Unless \b-f\b is specified, process \b\\\b sequences in each \astring\a " + "operand as described above. This is the default behavior.]" +"[n?Do not append a new-line character to the output.]" +"[f]:[format?Write the \astring\a arguments using the format string " + "\aformat\a and do not append a new-line. See \bprintf\b for " + "details on how to specify \aformat\a.]" +"[p?Write to the current co-process instead of standard output.]" +"[r?Do not process \b\\\b sequences in each \astring\a operand as described " + "above.]" +"[s?Write the output as an entry in the shell history file instead of " + "standard output.]" +"[u]:[fd:=1?Write to file descriptor number \afd\a instead of standard output.]" +"\n" +"\n[string ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\becho\b(1), \bprintf\b(1), \bread\b(1)]" +; + +const char sh_optprintf[] = +"[-1c?\n@(#)$Id: printf (AT&T Research) 2006-10-26 $\n]" +USAGE_LICENSE +"[+NAME?printf - write formatted output]" +"[+DESCRIPTION?\bprintf\b writes each \astring\a operand to " + "standard output using \aformat\a to control the output format.]" +"[+?The \aformat\a operands supports the full range of ANSI C formatting " + "specifiers plus the following additional specifiers:]{" + "[+%b?Each character in the \astring\a operand is processed " + "specially as follows:]{" + "[+\\a?Alert character.]" + "[+\\b?Backspace character.]" + "[+\\c?Terminate output without appending newline. " + "The remaining \astring\a operands are ignored.]" + "[+\\f?Formfeed character.]" + "[+\\n?Newline character.]" + "[+\\t?Tab character.]" + "[+\\v?Vertical tab character.]" + "[+\\\\?Backslash character.]" + "[+\\E?Escape character (ASCII octal 033).]" + "[+\\0\ax\a?The 8-bit character whose ASCII code is " + "the 1-, 2-, or 3-digit octal number \ax\a.]" + "}" + "[+%q?Output \astring\a quoted in a manner that it can be read in " + "by the shell to get back the same string. However, empty " + "strings resulting from missing \astring\a operands will " + "not be quoted.]" + "[+%B?Treat the argument as a variable name and output the value " + "without converting it to a string. This is most useful for " + "variables of type \b-b\b.]" + "[+%H?Output \astring\a with characters \b<\b, \b&\b, \b>\b, " + "\b\"\b, and non-printable characters properly escaped for " + "use in HTML and XML documents.]" + "[+%P?Treat \astring\a as an extended regular expression and " + "convert it to a shell pattern.]" + "[+%R?Treat \astring\a as an shell pattern expression and " + "convert it to an extended regular expression.]" + "[+%T?Treat \astring\a as a date/time string and format it. The " + "\bT\b can be preceded by \b(\b\adformat\a\b)\b, where " + "\adformat\a is a date format as defined by the \bdate\b " + "command.]" + "[+%Z?Output a byte whose value is \b0\b.]" +"}" +"[+?When performing conversions of \astring\a to satisfy a numeric " + "format specifier, if the first character of \astring\a " + "is \b\"\b or \b'\b, then the value will be the numeric value " + "in the underlying code set of the character following the " + "\b\"\b or \b'\b. Otherwise, \astring\a is treated like a shell " + "arithmetic expression and evaluated.]" +"[+?If a \astring\a operand cannot be completed converted into a value " + "appropriate for that format specifier, an error will occur, " + "but remaining \astring\a operands will continue to be processed.]" +"[+?In addition to the format specifier extensions, the following " + "extensions of ANSI-C are permitted in format specifiers:]{" + "[+-?The escape sequences \b\\E\b and \b\\e\b expand to the escape " + "character which is octal \b033\b in ASCII.]" + "[+-?The escape sequence \b\\c\b\ax\a expands to Control-\ax\a.]" + "[+-?The escape sequence \b\\C[.\b\aname\a\b.]]\b expands to " + "the collating element \aname\a.]" + "[+-?The escape sequence \b\\x{\b\ahex\a\b}\b expands to the " + "character corresponding to the hexidecimal value \ahex\a.]" + "[+-?The format modifier flag \b=\b can be used to center a field to " + "a specified width. When the output is a terminal, the " + "character width is used rather than the number of bytes.]" + "[+-?Each of the integral format specifiers can have a third " + "modifier after width and precision that specifies the " + "base of the conversion from 2 to 64. In this case the " + "\b#\b modifier will cause \abase\a\b#\b to be prepended to " + "the value.]" + "[+-?The \b#\b modifier can be used with the \bd\b specifier when " + "no base is specified cause the output to be written in units " + "of \b1000\b with a suffix of one of \bk M G T P E\b.]" + "[+-?The \b#\b modifier can be used with the \bi\b specifier to " + "cause the output to be written in units of \b1024\b with " + "a suffix of one of \bKi Mi Gi Ti Pi Ei\b.]" + "}" +"[+?If there are more \astring\a operands than format specifiers, the " + "\aformat\a string is reprocessed from the beginning. If there are " + "fewer \astring\a operands than format specifiers, then string " + "specifiers will be treated as if empty strings were supplied, " + "numeric conversions will be treated as if 0 were supplied, and " + "time conversions will be treated as if \bnow\b were supplied.]" +"[+?\bprintf\b is equivalent to \bprint -f\b which allows additional " + "options to be specified.]" +"\n" +"\nformat [string ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bdate\b(1), \bprint\b(1), \bread\b(1)]" +; + +const char sh_optpwd[] = +"[-1c?\n@(#)$Id: pwd (AT&T Research) 1999-06-07 $\n]" +USAGE_LICENSE +"[+NAME?pwd - write working directory name]" +"[+DESCRIPTION?\bpwd\b writes an absolute pathname of the current working " + "directory to standard output. An absolute pathname is a " + "pathname that begins with \b/\b that does not contains any " + "\b.\b or \b..\b components.]" +"[+?If both \b-L\b and \b-P\b are specified, the last one specified will " + "be used. If neither \b-P\b or \b-L\b is specified then the " + "behavior will be determined by the \bgetconf\b parameter " + "\bPATH_RESOLVE\b. If \bPATH_RESOLVE\b is \bphysical\b, " + "then the behavior will be as if \b-P\b were specified. Otherwise, " + "the behavior will be as if \b-L\b were specified.]" +"[L?The absolute pathname may contains symbolic link components. This is " + "the default.]" +"[P?The absolute pathname will not contain any symbolic link components.]" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bcd\b(1), \bgetconf\b(1)]" +; + +const char sh_optread[] = +"[-1c?\n@(#)$Id: read (AT&T Research) 2006-12-19 $\n]" +USAGE_LICENSE +"[+NAME?read - read a line from standard input]" +"[+DESCRIPTION?\bread\b reads a line from standard input and breaks it " + "into fields using the characters in value of the \bIFS\b variable " + "as separators. The escape character, \b\\\b, is used to remove " + "any special meaning for the next character and for line continuation " + "unless the \b-r\b option is specified.]" +"[+?If there are more variables than fields, the remaining variables are " + "set to empty strings. If there are fewer variables than fields, " + "the leftover fields and their intervening separators are assigned " + "to the last variable. If no \avar\a is specifed then the variable " + "\bREPLY\b is used.]" +"[+?When \avar\a has the binary attribute and \b-n\b or \b-N\b is specified, " + "the bytes that are read are stored directly into \bvar\b.]" +"[+?If you specify \b?\b\aprompt\a after the first \avar\a, then \bread\b " + "will display \aprompt\a on standard error when standard input " + "is a terminal or pipe.]" +"[A?Unset \avar\a and then create an indexed array containing each field in " + "the line starting at index 0.]" +"[d]:[delim?Read until delimiter \adelim\a instead of to the end of line.]" +"[p?Read from the current co-process instead of standard input. An end of " + "file causes \bread\b to disconnect the co-process so that another " + "can be created.]" +"[r?Do not treat \b\\\b specially when processing the input line.]" +"[s?Save a copy of the input as an entry in the shell history file.]" +"[u]#[fd:=0?Read from file descriptor number \afd\a instead of standard input.]" +"[t]:[timeout?Specify a timeout \atimeout\a in seconds when reading from " + "a terminal or pipe.]" +"[n]#[nbyte?Read at most \ansize\a characters. For binary fields \asize\a " + "will be in bytes.]" +"[N]#[nbyte?Read exactly \ansize\a characters. For binary fields \asize\a " + "will be in bytes.]" +"[v?When reading from a terminal the value of the first variable is displayed " + "and used as a default value.]" +"\n" +"\n[var?prompt] [var ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0? Successful completion.]" + "[+>0?End of file was detected or an error occurred.]" +"}" +"[+SEE ALSO?\bprint\b(1), \bprintf\b(1), \bcat\b(1)]" +; + +const char sh_optreadonly[] = +"[-1c?\n@(#)$Id: readonly (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?readonly - set readonly attribute on variables]" +"[+DESCRIPTION?\breadonly\b sets the readonly attribute on each of " + "the variables specified by \aname\a which prevents their " + "values from being changed. If \b=\b\avalue\a is specified, " + "the variable \aname\a is set to \avalue\a before the variable " + "is made readonly.]" +"[+?If no \aname\as are specified then the names and values of all " + "readonly variables are written to standard output.]" +"[+?\breadonly\b is built-in to the shell as a declaration command so that " + "field splitting and pathname expansion are not performed on " + "the arguments. Tilde expansion occurs on \avalue\a.]" +"[p?Causes the output to be in a form of \breadonly\b commands that can be " + "used as input to the shell to recreate the current set of " + "readonly variables.]" +"\n" +"\n[name[=value]...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\bsh\b(1), \btypeset\b(1)]" +; + +const char sh_optreturn[] = +"[-1c?\n@(#)$Id: return (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?return - return from a function or dot script ]" +"[+DESCRIPTION?\breturn\b is a shell special built-in that causes the " + "function or dot script that invokes it to exit. " + "If \breturn\b is invoked outside of a function or dot script " + "it is equivalent to \bexit\b.]" +"[+?If \breturn\b is invoked inside a function defined with the \bfunction\b " + "reserved word syntax, then any \bEXIT\b trap set within the " + "then function will be invoked in the context of the caller " + "before the function returns.]" +"[+?If \an\a is given, it will be used to set the exit status.]" +"\n" +"\n[n]\n" +"\n" +"[+EXIT STATUS?If \an\a is specified, the exit status is the least significant " + "eight bits of the value of \an\a. Otherwise, the exit status is the " + "exit status of preceding command.]" +"[+SEE ALSO?\bbreak\b(1), \bexit\b(1)]" +; + + +const char sh_optksh[] = +"+[-1c?\n@(#)$Id: sh (AT&T Research) "SH_RELEASE" $\n]" +USAGE_LICENSE +"[+NAME?\b\f?\f\b - Shell, the standard command language interpreter]" +"[+DESCRIPTION?\b\f?\f\b is a command language interpreter that " + "executes commands read from a command line string, the " + "standard input, or a specified file.]" +"[+?If the \b-i\b option is present, or there are no \aarg\as and " + "the standard input and standard error are attached to a " + "terminal, the shell is considered to be interactive.]" +"[+?The \b-s\b and \b-c\b options are mutually exclusive. If the \b-c\b " + "option is specified, the first \aarg\a is the command-line string " + "and must be specified. Any remaining \aarg\as will be used " + "to initialize \b$0\b and positional parameters.]" +"[+?If the neither \b-s\b nor \b-c\b is specified, then the first \barg\b " + "will be the pathname of the file containing commands and \b$0\b " + "will be set to this value. If there is no file with this pathname, " + "and this pathame does not contain a \b/\b, then the \bPATH\b " + "will be searched for an executable with this name. Any remaining " + "\aarg\as will be used to initialize the positional parmaeters.]" +"[+?Any option can use a \b+\b instead of a \b-\b to disable the corresponding " + "option.]" +"[c?Read the commands from the first \aarg\a.]" +"[i?Specifies that the shell is interactive.]" +"[l?Invoke the shell as a login shell; \b/etc/profile\b and \b$HOME/.profile\b, " + "if they exist, are read before the first command.]" +"[r\f:restricted\f?Invoke the shell in a restricted mode. A restricted " + "shell does not permit any of the following:]{" + "[+-?Changing the working directory.]" + "[+-?Setting values or attributes of the variables \bSHELL\b, " + "\bENV\b, \bFPATH\b, or \bPATH\b.]" + "[+-?Executing any command whose name as a \b/\b in it.]" + "[+-?Redirecting output of a command with \b>\b, \b>|\b, " + "\b<>\b, or \b>>\b.]" + "[+-?Adding or deleting built-in commands or libraries with " + "\bbuiltin\b.]" + "[+-?Executing \bcommand -p\b \a...\a .]" + "}" +"[s?Read the commands from standard input. The positional parameters will be " + "initialized from \aarg\a.]" +"[D\f:dump-strings\f?Do not execute the script, but output the set of double " + "quoted strings preceded by a \b$\b. These strings are needed for " + "localization of the script to different locales.]" +"[E?Reads the file \b${ENV-$HOME/.kshrc}\b, if it exists, as a profile. " + "On by default for interactive shells; use \b+E\b to disable.]" +#if SHOPT_PFSH +"[P?Invoke the shell as a profile shell. See \bpfexec\b(1).]" +#endif +#if SHOPT_KIA +"[R]:[file?Do not execute the script, but create a cross reference database " + "in \afile\a that can be used a separate shell script browser.]" +#endif /* SHOPT_KIA */ +#if SHOPT_BASH + "\fbash2\f" +#endif +"\fabc\f" +"\n" +"\n[arg ...]\n" +"\n" +"[+EXIT STATUS?If \b\f?\f\b executes command, the exit status will be that " + "of the last command executed. Otherwise, it will be one of " + "the following:]{" + "[+0?The script or command line to be executed consists entirely " + "of zero or more blank lines or comments.]" + "[+>1-125?A noninteractive shell detected a syntax error, a variable " + "assignment error, or an error in a special built-in.]" + "[+126?\b-c\b and \b-s\b were not specified and the command script " + "was found on \bPATH\b but was not executable.]" + "[+127?\b-c\b and \b-s\b were not specified and the command script " + "corresponding to \aarg\a could not be found.]" +"}" + +"[+SEE ALSO?\bset\b(1), \bbuiltin\b(1)]" +; +const char sh_optset[] = +"+[-1c?\n@(#)$Id: set (AT&T Research) 1999-09-28 $\n]" +USAGE_LICENSE +"[+NAME?set - set/unset options and positional parameters]" +"[+DESCRIPTION?\bset\b sets or unsets options and positional parameters. " + "Options that are specified with a \b-\b cause the options to " + "be set. Options that are specified with a \b+\b cause the " + "option to be unset.]" +"[+?\bset\b without any options or arguments displays the names and " + "values of all shell variables in the order of the collation " + "sequence in the current locale. The values are quoted so that " + "they are suitable for reinput to the shell.]" +"[+?If no \aarg\as are specified, not even the end of options argument \b--\b, " + "the positional parameters are unchanged. Otherwise, unless " + "the \b-A\b options has been specified, the positional parameters " + "are replaced by the list of \aarg\as. A first \aarg\a of " + "\b--\b is ignored when setting positional parameters.]" +"[+?For backward compatibility, a \bset\b command without any options " + "specified whose first \aarg\a is \b-\b will turn off " + "the \b-v\b and \b-x\b options. If any additional \aarg\as " + "are specified, they will replace the positional parameters.]" +"[s?Sort the positional parameters.]" +"[A]:[name?Assign the arguments sequentially to the array named by \aname\a " + "starting at subscript 0 rather than to the positional parameters.]" +"\fabc\f" +"\n" +"\n[arg ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?No errors occurred.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\btypeset\b(1), \bshift\b(1)]" +; + + + +const char sh_optshift[] = +"[-1c?\n@(#)$Id: shift (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?shift - shift positional parameters]" +"[+DESCRIPTION?\bshift\b is a shell special built-in that shifts the " + "positional parameters to the left by the number of places " + "defined by \an\a, or \b1\b if \an\a is omitted. The number of " + "positional parameters remaining will be reduced by the " + "number of places that are shifted.]" +"[+?If \an\a is given, it will be evaluated as an arithmetic expression " + "to determinate the number of places to shift. It is an error " + "to shift more than the number of positional parameters or a " + "negative number of places.]" +"\n" +"\n[n]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?The positional parameters were successfully shifted.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bset\b(1)]" +; + +const char sh_optsleep[] = +"[-1c?\n@(#)$Id: sleep (AT&T Research) 1999-04-07 $\n]" +USAGE_LICENSE +"[+NAME?sleep - suspend execution for an interval]" +"[+DESCRIPTION?\bsleep\b suspends execution for at least the time specified " + "by \aseconds\a or until a \bSIGALRM\b signal is received. " + "\aseconds\a can be specifed as a floating point number but the " + "actual granularity depends on the underlying system, normally " + "around 1 millisecond.]" +"\n" +"\nseconds\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?The execution was successfully suspended for at least \atime\a " + "seconds, or a \bSIGALRM\b signal was received.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\btime\b(1), \bwait\b(1)]" +; + +const char sh_opttrap[] = +"[-1c?\n@(#)$Id: trap (AT&T Research) 1999-07-17 $\n]" +USAGE_LICENSE +"[+NAME?trap - trap signals and conditions]" +"[+DESCRIPTION?\btrap\b is a special built-in that defines actions to be " + "taken when conditions such as receiving a signal occur. Also, " + "\btrap\b can be used to display the current trap settings on " + "standard output.]" +"[+?If \aaction\a is \b-\b, \btrap\b resets each \acondition\a " + "to the default value. If \aaction\a is an empty string, the " + "shell ignores each of the \acondition\as if they arise. " + "Otherwise, the argument \aaction\a will be read and executed " + "by the shell as if it were processed by \beval\b(1) when one " + "of the corresponding conditions arise. The action of the trap " + "will override any previous action associated with each specified " + "\acondition\a. The value of \b$?\b is not altered by the trap " + "execution.]" +"[+?\acondition\a can be the name or number of a signal, or one of the " + "following:]{" + "[+EXIT?This trap is executed when the shell exits. If defined " + "within a function defined with the \bfunction\b reserved " + "word, the trap is executed in the caller's environment " + "when the function returns and the trap action is restored " + "to the value it had when it called the function.]" + "[+0?Same as EXIT.]" + "[+DEBUG?Executed before each simple command is executed but after " + "the arguments are expanded.]" + "[+ERR?Executed whenever \bset -e\b would cause the shell to exit.]" + "[+KEYBD?Executed when a key is entered from a terminal device.]" +"}" +"[+?Signal names are case insensitive and the \bsig\b prefix is optional. " + "Signals that were ignored on entry to a noninteractive shell cannot " + "trapped or reset although doing so will not report an error. The " + "use of signal numbers other than \b1\b, \b2\b, \b3\b, \b6\b, " + "\b9\b, \b14\b, and \b15\b is not portable.]" +"[+?Although \btrap\b is a special built-in, specifying a condition that " + "the shell does not know about causes \btrap\b to exit with a " + "non-zero exit status, but does not terminate the invoking shell.]" +"[+?If no \aaction\a or \acondition\as are specified then all the current " + "trap settings are written to standard output.]" +"[p?Causes the current traps to be output in a format that can be processed " + "as input to the shell to recreate the current traps.]" +"\n" +"\n[action condition ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\bkill\b(1), \beval\b(1), \bsignal\b(3)]" +; + +const char sh_opttypeset[] = +"+[-1c?\n@(#)$Id: typeset (AT&T Research) 2003-01-15 $\n]" +USAGE_LICENSE +"[+NAME?\f?\f - declare or display variables with attributes]" +"[+DESCRIPTION?Without the \b-f\b option, \b\f?\f\b sets, unsets, " + "or displays attributes of variables as specified with the " + "options. If the first option is specified with a \b-\b " + "then the attributes are set for each of the given \aname\as. " + "If the first option is specified with a \b+\b, then the specified " + "attributes are unset. If \b=\b\avalue\a is specified value is " + "assigned before the attributes are set.]" +"[+?When \b\f?\f\b is called inside a function defined with the " + "\bfunction\b reserved word, and \aname\a does not contain a " + "\b.\b, then a local variable statically scoped to that function " + "will be created.]" +"[+?Not all option combinations are possible. For example, the numeric " + "options \b-i\b, \b-E\b, and \b-F\b cannot be specified with " + "the justification options \b-L\b, \b-R\b, and \b-Z\b.]" +"[+?Note that the following preset aliases are set by the shell:]{" + "[+float?\b\f?\f -E\b.]" + "[+functions?\b\f?\f -f\b.]" + "[+integer?\b\f?\f -i\b.]" + "[+nameref?\b\f?\f -n\b.]" +"}" +"[+?If no \aname\as are specified then variables that have the specified " + "options are displayed. If the first option is specified with " + "a leading \b-\b then the name and value of each variable is " + "written to standard output. Otherwise, only the names are " + "written. If no options are specified or just \b-p\b is " + "specified, then the names and attributes of all variables that have " + "attributes are written to standard output. When \b-f\b is specified, " + "the names displayed will be function names.]" +"[+?If \b-f\b is specified, then each \aname\a refers to a function " + "and the only valid options are \b-u\b and \b-t\b. In this " + "case no \b=\b\avalue\a can be specified.]" +"[+?\b\f?\f\b is built-in to the shell as a declaration command so that " + "field splitting and pathname expansion are not performed on " + "the arguments. Tilde expansion occurs on \avalue\a.]" +#if SHOPT_BASH +"[a?Ignored, used for bash compatibility.]" +#endif +"[a?Indexed array. this is the default.]" +"[b?Each \aname\a may contain binary data. Its value is the mime " + "base64 encoding of the data. It can be used with \b-Z\b, " + "to specify fixed sized fields.]" +"[f?Each of the options and \aname\as refers to a function.]" +"[i]#?[base:=10?An integer. \abase\a represents the arithmetic base " + "from 2 to 64.]" +"[l?Convert uppercase character to lowercase. Unsets \b-u\b attribute. When " + "used with \b-i\b, \b-E\b, or \b-F\b indicates long variant.]" +"[n?Name reference. The value is the name of a variable that \aname\a " + "references. \aname\a cannot contain a \b.\b.]" +"[p?Causes the output to be in a format that can be used as input to the " + "shell to recreate the attributes for variables.]" +"[r?Enables readonly. Once enabled it cannot be disabled. See " + "\breadonly\b(1).]" +"[s?Used with \b-i\b to restrict integer size to short.]" +"[t?When used with \b-f\b, enables tracing for each of the specified " + "functions. Otherwise, \b-t\b is a user defined attribute and " + "has no meaning to the shell.]" +"[u?Without \b-f\b or \b-i\b, converts lowercase character to uppercase " + "and unsets \b-l\b. With \b-f\b specifies that \aname\a is a function " + "that hasn't been loaded yet. With \b-i\b specifies that the " + "value will be displayed as an unsigned integer.]" +"[x?Puts each \aname\a on the export list. See \bexport\b(1). \aname\a " + "cannot contain a \b.\b.]" +"[A?Associative array. Each \aname\a will converted to an associate " + "array. If a variable already exists, the current value will " + "become index \b0\b.]" +"[E]#?[n:=10?Floating point number represented in scientific notation. " + "\an\a specifies the number of significant figures when the " + "value is expanded.]" +"[F]#?[n:=10?Floating point. \an\a is the number of places after the " + "decimal point when the value is expanded.]" +"[H?Hostname mapping. Each \aname\a holds a native pathname. Assigning a " + "UNIX format pathname will cause it to be converted to a pathname " + "suitable for the current host. This has no effect when the " + "native system is UNIX.]" +"[L]#?[n?Left justify. If \an\a is given it represents the field width. If " + "the \b-Z\b attribute is also specified, then leading zeros are " + "stripped.]" +"[R]#?[n?Right justify. If \an\a is given it represents the field width. If " + "the \b-Z\b attribute is also specified, then zeros will " + "be used as the fill character. Otherwise, spaces are used.]" +"[T]:[tname?\atname\a is the name of a type name given to each \aname\a.]" +"[Z]#?[n?Zero fill. If \an\a is given it represents the field width.]" +"\n" +"\n[name[=value]...]\n" +" -f [name...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?No errors occurred.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\breadonly\b(1), \bexport\b(1)]" +; + +const char sh_optulimit[] = +"[-1c?@(#)$Id: ulimit (AT&T Research) 2003-06-21 $\n]" +USAGE_LICENSE +"[+NAME?ulimit - set or display resource limits]" +"[+DESCRIPTION?\bulimit\b sets or displays resource limits. These " + "limits apply to the current process and to each child process " + "created after the resource limit has been set. If \alimit\a " + "is specified, the resource limit is set, otherwise, its current value " + "is displayed on standard output.]" +"[+?Increasing the limit for a resource usually requires special privileges. " + "Some systems allow you to lower resource limits and later increase " + "them. These are called soft limits. Once a hard limit is " + "set the resource can not be increased.]" +"[+?Different systems allow you to specify different resources and some " + "restrict how much you can raise the limit of the resource.]" +"[+?The value of \alimit\a depends on the unit of the resource listed " + "for each resource. In addition, \alimit\a can be \bunlimited\b " + "to indicate no limit for that resource.]" +"[+?If you do not specify \b-H\b or \b-S\b, then \b-S\b is used for " + "listing and both \b-S\b and \b-H\b are used for setting resources.]" +"[+?If you do not specify any resource, the default is \b-f\b.]" +"[H?A hard limit is set or displayed.]" +"[S?A soft limit is set or displayed.]" +"[a?Displays all current resource limits]" +"\flimits\f" +"\n" +"\n[limit]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?A request for a higher limit was rejected or an error occurred.]" +"}" + +"[+SEE ALSO?\bulimit\b(2), \bgetrlimit\b(2)]" +; + +const char sh_optumask[] = +"[-1c?\n@(#)$Id: umask (AT&T Research) 1999-04-07 $\n]" +USAGE_LICENSE +"[+NAME?umask - get or set the file creation mask]" +"[+DESCRIPTION?\bumask\b sets the file creation mask of the current " + "shell execution environment to the value specified by the " + "\amask\a operand. This mask affects the file permission bits " + "of subsequently created files. \amask\a can either be an " + "octal number or a symbolic value as described in \bchmod\b(1). " + "If a symbolic value is given, the new file creation mask is the " + "complement of the result of applying \amask\a to the complement " + "of the current file creation mask.]" +"[+?If \amask\a is not specified, \bumask\b writes the value of the " + "file creation mask for the current process to standard output.]" +"[S?Causes the file creation mask to be written or treated as a symbolic value " + "rather than an octal number.]" +"\n" +"\n[mask]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?The file creation mask was successfully changed, or no " + "\amask\a operand was supplied.]" + "[+>0?An error occurred.]" +"}" +"[+SEE ALSO?\bchmod\b(1)]" +; +const char sh_optuniverse[] = " [name]"; +const char sh_optunset[] = +"[-1c?\n@(#)$Id: unset (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?unset - unset values and attributes of variables and functions]" +"[+DESCRIPTION?For each \aname\a specified, \bunset\b unsets the variable, " + "or function if \b-f\b is specified, from the current shell " + "execution environment. Readonly variables cannot be unset.]" +"[n?If \aname\a refers to variable that is a reference, the variable \aname\a " + "will be unset rather than the variable it references. Otherwise, " + "is is equivalent to \b-v\b.]" +"[f?\aname\a refers to a function name and the shell will unset the " + "function definition.]" +"[v?\aname\a refers to a variable name and the shell will unset it and " + "remove it from the environment. This is the default behavior.]" +"\n" +"\nname...\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?All \aname\as were successfully unset.]" + "[+>0?One or more \aname\a operands could not be unset " + "or an error occurred.]" +"}" + +"[+SEE ALSO?\btypeset\b(1)]" +; + +const char sh_optunalias[] = +"[-1c?\n@(#)$Id: unalias (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?unalias - remove alias definitions]" +"[+DESCRIPTION?\bunalias\b removes the definition of each named alias " + "from the current shell execution environment, or all aliases if " + "\b-a\b is specified. It will not affect any commands that " + "have already been read and subsequently executed.]" +"[a?Causes all alias definitions to be removed. \aname\a operands " + "are optional and ignored in this case.]" +"\n" +"\nname...\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?\b-a\b was not specified and one or more \aname\a operands " + "did not have an alias definition, or an error occurred.]" +"}" + +"[+SEE ALSO?\balias\b(1)]" +; + +const char sh_optwait[] = +"[-1c?\n@(#)$Id: wait (AT&T Research) 1999-06-17 $\n]" +USAGE_LICENSE +"[+NAME?wait - wait for process or job completion]" +"[+DESCRIPTION?\bwait\b with no operands, waits until all jobs " + "known to the invoking shell have terminated. If one or more " + "\ajob\a operands are specified, \bwait\b waits until all of them " + "have completed.]" +"[+?Each \ajob\a can be specified as one of the following:]{" + "[+\anumber\a?\anumber\a refers to a process id.]" + "[+-\anumber\a?\anumber\a refers to a process group id.]" + "[+%\anumber\a?\anumber\a refer to a job number.]" + "[+%\astring\a?Refers to a job whose name begins with \astring\a.]" + "[+%??\astring\a?Refers to a job whose name contains \astring\a.]" + "[+%+ \bor\b %%?Refers to the current job.]" + "[+%-?Refers to the previous job.]" +"}" +"[+?If one ore more \ajob\a operands is a process id or process group id " + "not known by the current shell environment, \bwait\b treats each " + "of them as if it were a process that exited with status 127.]" +"\n" +"\n[job ...]\n" +"\n" +"[+EXIT STATUS?If \await\a is invoked with one or more \ajob\as, and all of " + "them have terminated or were not known by the invoking shell, " + "the exit status of \bwait\b will be that of the last \ajob\a. " + "Otherwise, it will be one of the following:]{" + "[+0?\bwait\b utility was invoked with no operands and all " + "processes known by the invoking process have terminated.]" + "[+127?\ajob\a is a process id or process group id that is unknown " + "to the current shell environment.]" +"}" + +"[+SEE ALSO?\bjobs\b(1), \bps\b(1)]" +; + +#if SHOPT_FS_3D + const char sh_optvpath[] = " [top] [base]"; + const char sh_optvmap[] = " [dir] [list]"; +#endif /* SHOPT_FS_3D */ + +const char sh_optwhence[] = +"[-1c?\n@(#)$Id: whence (AT&T Research) 1999-07-07 $\n]" +USAGE_LICENSE +"[+NAME?whence - locate a command and describe its type]" +"[+DESCRIPTION?Without \b-v\b, \bwhence\b writes on standard output an " + "absolute pathname, if any, corresponding to \aname\a based " + "on the complete search order that the shell uses. If \aname\a " + "is not found, then no output is produced.]" +"[+?If \b-v\b is specified, the output will also contain information " + "that indicates how the given \aname\a would be interpretted by " + "the shell in the current execution environment.]" +"[a?Displays all uses for each \aname\a rather than the first.]" +"[f?Do not check for functions.]" +"[p?Do not check to see if \aname\a is a reserved word, a built-in, " + "an alias, or a function.]" +"[v?For each name you specify, the shell displays a line that indicates " + "if that name is one of the following:]{" + "[+?Reserved word]" + "[+?Alias]" + "[+?Built-in]" + "[+?Undefined function]" + "[+?Function]" + "[+?Tracked alias]" + "[+?Program]" + "[+?Not found]" +"}" +"\n" +"\nname ...\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Each \aname\a was found by the shell.]" + "[+1?One or more \aname\as were not found by the shell.]" + "[+>1?An error occurred.]" +"}" + +"[+SEE ALSO?\bcommand\b(1)]" +; + + +const char e_alrm1[] = "alarm -r %s +%.3g\n"; +const char e_alrm2[] = "alarm %s %.3f\n"; +const char e_baddisc[] = "%s: invalid discipline function"; +const char e_nospace[] = "out of memory"; +const char e_nofork[] = "cannot fork"; +const char e_nosignal[] = "%s: unknown signal name"; +const char e_numeric[] = "*([0-9])?(.)*([0-9])"; +const char e_condition[] = "condition(s) required"; +const char e_cneedsarg[] = "-c requires argument"; Index: src/lib/libshell/common/data/options.c =================================================================== --- src/lib/libshell/common/data/options.c (revision 0) +++ src/lib/libshell/common/data/options.c (revision 740) @@ -0,0 +1,140 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include +#include "FEATURE/options" +#include "name.h" +#include "shtable.h" + +#if SHOPT_BASH +# define bashopt(a,b) a, b|SH_BASHOPT, +# define bashextra(a,b) a, b|SH_BASHEXTRA, +#else +# define bashopt(a,b) +# define bashextra(a,b) +#endif + +/* + * This is the list of invocation and set options + * This list must be in in ascii sorted order + */ + +const Shtable_t shtab_options[] = +{ + "allexport", SH_ALLEXPORT, +#if SHOPT_BASH + "bash", (SH_BASH|SH_COMMANDLINE), +#endif + "bgnice", SH_BGNICE, + "braceexpand", SH_BRACEEXPAND, + bashopt("cdable_vars", SH_CDABLE_VARS) + bashopt("cdspell", SH_CDSPELL) + bashopt("checkhash", SH_CHECKHASH) + bashopt("checkwinsize", SH_CHECKWINSIZE) + "noclobber", SH_NOCLOBBER, + bashopt("dotglob", SH_DOTGLOB) + "emacs", SH_EMACS, + "errexit", SH_ERREXIT, + "noexec", SH_NOEXEC, + bashopt("execfail", SH_EXECFAIL) + bashopt("expand_aliases", SH_EXPAND_ALIASES) + bashopt("extglob", SH_EXTGLOB) + "noglob", SH_NOGLOB, + "globstar", SH_GLOBSTARS, + "gmacs", SH_GMACS, + bashextra("hashall", SH_TRACKALL) + bashopt("histappend", SH_HISTAPPEND) +#if SHOPT_HISTEXPAND + "histexpand", SH_HISTEXPAND, +#else + bashextra("histexpand", SH_HISTEXPAND) +#endif + bashextra("history", SH_HISTORY2) + bashopt("histreedit", SH_HISTREEDIT) + bashopt("histverify", SH_HISTVERIFY) + bashopt("hostcomplete", SH_HOSTCOMPLETE) + bashopt("huponexit", SH_HUPONEXIT) + "ignoreeof", SH_IGNOREEOF, + "interactive", SH_INTERACTIVE|SH_COMMANDLINE, + bashextra("interactive_comments", SH_INTERACTIVE_COMM) + "keyword", SH_KEYWORD, + bashopt("lithist", SH_LITHIST) + "nolog", SH_NOLOG, + "login_shell", SH_LOGIN_SHELL|SH_COMMANDLINE, + bashopt("mailwarn", SH_MAILWARN) + "markdirs", SH_MARKDIRS, + "monitor", SH_MONITOR, + "multiline", SH_MULTILINE, + bashopt("no_empty_cmd_completion", SH_NOEMPTYCMDCOMPL) + bashopt("nocaseglob", SH_NOCASEGLOB) + "notify", SH_NOTIFY, + bashopt("nullglob", SH_NULLGLOB) + bashextra("onecmd", SH_TFLAG) + "pipefail", SH_PIPEFAIL, + bashextra("physical", SH_PHYSICAL) + bashextra("posix", SH_POSIX) + "privileged", SH_PRIVILEGED, +#if SHOPT_PFSH + "profile", SH_PFSH|SH_COMMANDLINE, +#endif + bashopt("progcomp", SH_PROGCOMP) + bashopt("promptvars", SH_PROMPTVARS) + "rc", SH_RC|SH_COMMANDLINE, + "restricted", SH_RESTRICTED, + bashopt("restricted_shell", SH_RESTRICTED2|SH_COMMANDLINE) + bashopt("shift_verbose", SH_SHIFT_VERBOSE) + "showme", SH_SHOWME, + bashopt("sourcepath", SH_SOURCEPATH) + "trackall", SH_TRACKALL, + "nounset", SH_NOUNSET, + "verbose", SH_VERBOSE, + "vi", SH_VI, + "viraw", SH_VIRAW, + bashopt("xpg_echo", SH_XPG_ECHO) + "xtrace", SH_XTRACE, + "", 0 +}; + +const Shtable_t shtab_attributes[] = +{ + {"-nnameref", NV_REF}, + {"-xexport", NV_EXPORT}, + {"-rreadonly", NV_RDONLY}, + {"-ttagged", NV_TAGGED}, + {"-llong", (NV_INTEGER|NV_DOUBLE|NV_LONG)}, + {"-Eexponential",(NV_INTEGER|NV_DOUBLE|NV_EXPNOTE)}, + {"-Ffloat", (NV_INTEGER|NV_DOUBLE)}, + {"-llong", (NV_INTEGER|NV_LONG)}, + {"-sshort", (NV_INTEGER|NV_SHORT)}, + {"-uunsigned", (NV_INTEGER|NV_UNSIGN)}, + {"-iinteger", NV_INTEGER}, + {"-Hfilename", NV_HOST}, + {"-bbinary", NV_BINARY}, + {"-llowercase", NV_UTOL}, + {"-Zzerofill", NV_ZFILL}, + {"-Lleftjust", NV_LJUST}, + {"-Rrightjust", NV_RJUST}, + {"-uuppercase", NV_LTOU}, + {"-Aassociative array", NV_ARRAY}, + {"-aindexed array", NV_ARRAY}, + {"++namespace", NV_TABLE}, + {"", 0} +}; Index: src/lib/libshell/common/data/lexstates.c =================================================================== --- src/lib/libshell/common/data/lexstates.c (revision 0) +++ src/lib/libshell/common/data/lexstates.c (revision 740) @@ -0,0 +1,411 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include + +#include "FEATURE/options" +#include "lexstates.h" + + +/* + * This is the initial state for tokens + */ +static const char sh_lexstate0[256] = +{ + S_EOF, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, +#if SHOPT_CRNL + S_REG, 0, S_NLTOK,S_REG, S_REG, 0, S_REG, S_REG, +#else + S_REG, 0, S_NLTOK,S_REG, S_REG, S_REG, S_REG, S_REG, +#endif /* SHOPT_CRNL */ + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + + 0, S_REG, S_REG, S_COM, S_REG, S_REG, S_OP, S_REG, + S_OP, S_OP, S_REG, S_REG, S_REG, S_REG, S_NAME, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_OP, S_OP, S_REG, S_OP, S_REG, + + S_REG, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, + S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, + S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, + S_NAME, S_NAME, S_NAME, S_REG, S_REG, S_REG, S_REG, S_NAME, + + S_REG, S_NAME, S_NAME, S_RES, S_RES, S_RES, S_RES, S_NAME, +#if SHOPT_NAMESPACE + S_NAME, S_RES, S_NAME, S_NAME, S_NAME, S_NAME, S_RES, S_NAME, +#else + S_NAME, S_RES, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, +#endif /* SHOPT_NAMESPACE */ + S_NAME, S_NAME, S_NAME, S_RES, S_RES, S_RES, S_NAME, S_RES, + S_NAME, S_NAME, S_NAME, S_REG, S_OP, S_REG, S_TILDE,S_REG, + + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, +}; + +/* + * This state is for identifiers + */ +static const char sh_lexstate1[256] = +{ + S_EOF, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, +#if SHOPT_CRNL + S_REG, S_BREAK,S_BREAK,S_REG, S_REG, S_BREAK,S_REG, S_REG, +#else + S_REG, S_BREAK,S_BREAK,S_REG, S_REG, S_REG, S_REG, S_REG, +#endif /* SHOPT_CRNL */ + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + + S_BREAK,S_EPAT, S_QUOTE,S_REG, S_DOL, S_EPAT, S_BREAK,S_LIT, + S_BREAK,S_BREAK,S_PAT, S_EPAT, S_REG, S_EPAT, S_DOT, S_REG, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_LABEL,S_BREAK,S_BREAK,S_EQ, S_BREAK,S_PAT, + + S_EPAT, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_BRACT,S_ESC, S_REG, S_REG, 0, + + S_GRAVE,0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_BRACE,S_BREAK,S_BRACE,S_EPAT, S_REG, + + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, + S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, +}; + +static const char sh_lexstate2[256] = +{ + S_EOF, 0, 0, 0, 0, 0, 0, 0, +#if SHOPT_CRNL + 0, S_BREAK,S_BREAK,0, 0, S_BREAK,0, 0, +#else + 0, S_BREAK,S_BREAK,0, 0, 0, 0, 0, +#endif /* SHOPT_CRNL */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + S_BREAK,S_EPAT, S_QUOTE,0, S_DOL, S_EPAT, S_BREAK,S_LIT, + S_BREAK,S_BREAK,S_PAT, S_EPAT, 0, S_EPAT, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_COLON,S_BREAK,S_BREAK,0, S_BREAK,S_PAT, + + S_EPAT, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_PAT, S_ESC, 0, 0, 0, + + S_GRAVE,0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_BRACE,S_BREAK,S_BRACE,S_EPAT, 0, +}; + +/* + * for skipping over '...' + */ +static const char sh_lexstate3[256] = +{ + S_EOF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_NL, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, S_LIT, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, S_ESC2, 0, 0, 0 +}; + +/* + * for skipping over "..." and `...` + */ +static const char sh_lexstate4[256] = +{ + S_EOF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_NL, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_QUOTE,0, S_DOL, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, S_ESC, 0, 0, 0, + S_GRAVE,0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, S_RBRA, 0, 0 +}; + +/* + * for skipping over ?(...), [...] + */ +static const char sh_lexstate5[256] = +{ + S_EOF, 0, 0, 0, 0, 0, 0, 0, + 0, S_BLNK, S_NL, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + S_BLNK, 0, S_QUOTE,0, S_DOL, 0, S_META, S_LIT, + S_PUSH, S_POP, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_POP, S_META, 0, S_META, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_BRACT,S_ESC, S_POP, 0, 0, + S_GRAVE,0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_BRACE,S_META, S_POP, 0, 0 +}; + +/* + * Defines valid expansion characters + */ +static const char sh_lexstate6[256] = +{ + S_EOF, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + + S_ERR, S_SPC1, S_ERR, S_SPC1, S_SPC2, S_ERR, S_ERR, S_LIT, + S_PAR, S_ERR, S_SPC2, S_ERR, S_ERR, S_SPC2, S_ALP, S_ERR, + S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, + S_DIG, S_DIG, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_SPC2, + +#if SHOPT_TYPEDEF + S_SPC1, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, +#else + S_SPC2, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, +#endif + S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, + S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, + S_ALP, S_ALP, S_ALP, S_ERR, S_ERR, S_ERR, S_ERR, S_ALP, + + S_ERR, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, + S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, + S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, S_ALP, + S_ALP, S_ALP, S_ALP, S_LBRA, S_ERR, S_RBRA, S_ERR, S_ERR, + + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, +}; + +/* + * for skipping over ${...} until modifier + */ +static const char sh_lexstate7[256] = +{ + S_EOF, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + + S_ERR, S_ERR, S_ERR, S_MOD2, S_ERR, S_MOD2, S_ERR, S_ERR, + S_ERR, S_ERR, S_MOD1, S_MOD1, S_ERR, S_MOD1, S_DOT, S_MOD2, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_MOD1, S_ERR, S_ERR, S_MOD1, S_ERR, S_MOD1, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_BRACT,S_ESC, S_ERR, S_ERR, 0, + + S_ERR, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_ERR, S_ERR, S_POP, S_ERR, S_ERR, + + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, + S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, S_ERR, +}; + +/* + * This state is for $name + */ +static const char sh_lexstate8[256] = +{ + S_EOF, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + + S_EDOL, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_EDOL, S_EDOL, S_EDOL, S_EDOL, 0, + + S_EDOL,0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, + S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, S_EDOL, +}; + +/* + * This is used for macro expansion + */ +static const char sh_lexstate9[256] = +{ + S_EOF, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, S_QUOTE,0, S_DOL, 0, S_PAT, S_LIT, + S_PAT, S_PAT, S_PAT, 0, 0, 0, 0, S_SLASH, + 0, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, + S_DIG, S_DIG, S_COLON,0, 0, S_EQ, 0, S_PAT, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, S_BRACT,S_ESC, S_ENDCH,0, 0, + S_GRAVE,0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +#if SHOPT_BRACEPAT + 0, 0, 0, S_BRACE,S_PAT, S_ENDCH,0, 0 +#else + 0, 0, 0, 0, S_PAT, S_ENDCH,0, 0 +#endif /* SHOPT_BRACEPAT */ +}; + +const char *sh_lexrstates[ST_NONE] = +{ + sh_lexstate0, sh_lexstate1, sh_lexstate2, sh_lexstate3, + sh_lexstate4, sh_lexstate5, sh_lexstate6, sh_lexstate7, + sh_lexstate8, sh_lexstate9, sh_lexstate5 +}; + + +const char e_lexversion[] = "%d: invalid binary script version"; +const char e_lexspace[] = "line %d: use space or tab to separate operators %c and %c"; +const char e_lexslash[] = "line %d: $ not preceeded by \\"; +const char e_lexsyntax1[] = "syntax error at line %d: `%s' %s"; +const char e_lexsyntax2[] = "syntax error: `%s' %s"; +const char e_lexsyntax3[] = "syntax error at line %d: duplicate label %s"; +const char e_lexlabignore[] = "line %d: label %s ignored"; +const char e_lexlabunknown[] = "line %d: %s unknown label"; +const char e_lexobsolete1[] = "line %d: `...` obsolete, use $(...)"; +const char e_lexobsolete2[] = "line %d: -a obsolete, use -e"; +const char e_lexobsolete3[] = "line %d: '=' obsolete, use '=='"; +const char e_lexobsolete4[] = "line %d: %s within [[...]] obsolete, use ((...))"; +const char e_lexobsolete5[] = "line %d: set %s obsolete"; +const char e_lexobsolete6[] = "line %d: `{' instead of `in' is obsolete"; +const char e_lexusebrace[] = "line %d: use braces to avoid ambiguities with $id[...]"; +const char e_lexusequote[] = "line %d: %c within ${} should be quoted"; +const char e_lexescape[] = "line %d: escape %c to avoid ambiguities"; +const char e_lexquote[] = "line %d: quote %c to avoid ambiguities"; +const char e_lexnested[] = "line %d: spaces required for nested subshell"; +const char e_lexbadchar[] = "%c: invalid character in expression - %s"; +const char e_lexfuture[] = "line %d: \\ in front of %c reserved for future use"; +const char e_lexlongquote[] = "line %d: %c quote may be missing"; +const char e_lexzerobyte[] = "zero byte"; +const char e_lexemptyfor[] = "line %d: empty for list"; Index: src/lib/libshell/common/data/math.tab =================================================================== --- src/lib/libshell/common/data/math.tab (revision 0) +++ src/lib/libshell/common/data/math.tab (revision 740) @@ -0,0 +1,64 @@ +# <#floating-point-args> [ ...] +# l variants are handled by features/math.sh +# @(#)math.tab (AT&T Research) 2006-10-18 +f 1 acos +f 1 acosh +f 1 asin +f 1 asinh +f 1 atan +f 2 atan2 +f 1 atanh +f 1 cbrt +f 2 copysign +f 1 cos +f 1 cosh +f 1 erf +f 1 erfc +f 1 exp +f 1 exp2 +f 1 expm1 +f 1 fabs abs +f 2 fdim +f 1 finite +f 1 floor int +f 3 fma +f 2 fmax +f 2 fmin +f 2 fmod +i 1 fpclassify +f 2 hypot +i 1 ilogb +i 1 isfinite +i 2 isgreater +i 2 isgreaterequal +i 1 isinf +i 2 isless +i 2 islessequal +i 2 islessgreater +i 1 isnan +i 1 isnormal +i 1 issubnormal +i 2 isunordered +i 1 iszero +f 1 lgamma +f 1 log +f 1 log1p +f 1 log2 +f 1 logb +f 1 nearbyint +f 2 nextafter +f 2 nexttoward +f 2 pow +f 2 remainder +f 1 rint +f 1 round +f 2 scalb +f 2 scalbn +i 1 signbit +f 1 sin +f 1 sinh +f 1 sqrt +f 1 tan +f 1 tanh +f 1 tgamma +f 1 trunc Index: src/lib/libshell/common/DESIGN =================================================================== --- src/lib/libshell/common/DESIGN (revision 0) +++ src/lib/libshell/common/DESIGN (revision 740) @@ -0,0 +1,163 @@ +Here is an overview of the source code organization for ksh93. + +Directory layout: + + The directory include contains header files for ksh93. + The files nval.h and shell.h are intended to be public + headers and can be used to add runtime builtin command. + The remainder are private. + + The directory data contains readonly data files for ksh93. + + The directory edit contains the code for command line + editing and history. + + The fun directory contains some shell function such as + pushd, popd, and dirs. + + The directory features contains files that are used to generate + header files in the FEATURE directory. Most of these files + are in a format that is processed by iffe. + + The directory bltins contains code for most of the built-in + commands. Additional built-in commands are part of libcmd. + + The directory sh contains most of the code for ksh93. + + The directory tests contains a number of regression tests. + In most cases, when a bug gets fixed, a test is added to + one of these files. The regression tests can be run by + going to this directory and running + SHELL=shell_path shell_path shtests + where shell_path is an absolute pathname for the shell to + be tested. + + The top level directory contains the nmake Makefile, a README, + and several documentation files. The RELEASE file contains + the list of bug fixes and new features since the original + ksh93 release. The file COMPATIBILITY is a list of all + known incompatibilities with ksh88. + + The bash_pre_rc.sh is a startup script used when emulating + bash if the shell is compiled with SHOPT_BASH and the shell + is invoked as bash. The bash emulation is not complete. + +Include directory: + 1. argnod.h contains the type definitions for command + nodes, io nodes, argument nodes, and for positional + parameters.a It defines the prototypes for + all the positional parameters functions. + 2. builtins.h contains prototypes for builtins as well + as symbolic constants that refer to the name-pairs + that are associated with some of the built-ins. + It also contains prototypes for many of the strings. + 3. defs.h is the catch all for all definitions that + don't fit elsewhere and it includes several other + headers. It defines a strucuture that contains ksh + global data, sh, and a structure that contains per + function data, sh.st. + 4. edit.h contains definitions that are common to both + vi and emacs edit modes. + 5. env.h contains interfaces for creating and modifying + environment variables. + 6. fault.h contains prototypes for signal related + functions and trap and fault handling. + 7. fcin.h contains macro and function definitions for + reading from a file or string. + 8. history.h contains macros and functions definitions + related to history file processing. + 9. jobs.h contains the definitions relating to job + processing and control. + 10. lexstates.h contains the states associated with + lexical processing. + 11. name.h contains the internal definitions related + to name-value pair processing. + 12. national.h contains a few I18N definitions, mostly + obsolete. + 13. nval.h is the public interface to the name-value + pair library that is documented with nval.3. + 14. path.h contains the interface for pathname processing + and pathname searching. + 15. shell.h is the public interface for shell functions + that are documented int shell.3. + 16. shlex.h contains the lexical token definitions and + interfaces for lexical analysis. + 17. shnodes.h contains the definition of the structures + for each of the parse nodes and flags for the attributes. + 18. shtable.h contains some interfaces and functions for + table lookup. + 19. streval.h contains the interface to the arithmetic + functions. + 20. terminal.h is a header file that includes the appropriate + terminal include. + 21. test.h contains the definitions for the test and [[...]] + commands. + 22. timeout.h contains the define constant for the maximum + shell timeout. + 23. ulimit.h includes the appropriate resource header. + 24. variables.h contains symbolic constants for the built-in + shell variables. + +sh directory: + 1. args.c contains functions for parsing shell options + and for processing positional parameters. + 2. arith.c contains callback functions for the streval.c + library and the interface to shell arithmetic. + 3. array.c contains the code for indexed and associative + arrays. + 4. bash.h contains code used when compiling with SHOPT_BASH + to add bash specific features such as shopt. + 5. defs.c contains the data definitions for global symbols. + 6. deparse.c contains code to generate shell script from + a parse tree. + 7. env.c contains code to add and delete environment variables + to an environment list. + 8. expand.c contains code for file name expansion and + file name generation. + 9. fault.c contains code for signal processing, trap + handling and termination. + 10. fcin.c contains code for reading and writing a character + at a time from a file or string. + 11. init.c contains initialization code and callbacks + for get and set functions for built-in variables. + 12. io.o contains code for redirections and managing file + descriptors and file streams. + 13. jobs.c contains the code for job management. + 14. lex.c contains the code for the lexical analyzer. + 15. macro.c contains code for the $ macro expansions, including + here-documents. + 16. main.c contains the calls to initialization, profile + processing and the main evaluation loop as well as + mail processing. + 17. name.c contains the name-value pair routines that are + built on the hash library in libast. + 18. nvdisc.c contains code related to name-value pair disciplines. + 19. nvtree.c contains code for compound variables and for + walking the namespace. + 20. parse.c contains the code for the shell parser. + 21. path.c contains the code for pathname lookup and + some path functions. It also contains the code + that executes commands and scripts. + 22. pmain.c is just a calls sh_main() so that all of the + rest of the shell can be in a shared library. + 23. shcomp.c contains the main program to the shell + compiler. This program parses a script and creates + a file that the shell can read containing the parse tree. + 24. streval.c is an C arithmetic evaluator. + 25. string.c contains some string related functions. + 26. subshell.c contains the code to save and restore + environments so that subshells can run without creating + a new process. + 27. suid_exec.c contains the program from running execute + only and/or setuid/setgid scripts. + 28. tdump.c contains the code to dump a parse tree into + a file. + 29. timers.c contains code for multiple event timeouts. + 30. trestore contians the code for restoring the parse + tree from the file created by tdump. + 31. userinit.c contains a dummy userinit() function. + This is now obsolete with the new version of sh_main(). + 32. waitevent.c contains the sh_waitnotify function so + that builtins can handle processing events when the + shell is waiting for input or for process completion. + 33. xec.c is the main shell executuion loop. Index: src/lib/libshell/common/COMPATIBILITY =================================================================== --- src/lib/libshell/common/COMPATIBILITY (revision 0) +++ src/lib/libshell/common/COMPATIBILITY (revision 740) @@ -0,0 +1,129 @@ + + KSH-93 VS. KSH-88 + + +The following is a list of known incompatibilities between ksh-93 and ksh-88. +I have not include cases that are clearly bugs in ksh-88. I also have +omitted features that are completely upward compatible. + +1. Functions, defined with name() with ksh-93 are compatible with + the POSIX standard, not with ksh-88. No local variables are + permitted, and there is no separate scope. Functions defined + with the function name syntax, maintain compatibility. + This also affects function traces. + +2. ! is now a reserved word. As a result, any command by that + name will no longer work with ksh-93. + +3. The -x attribute of alias and typeset -f is no longer + effective and the ENV file is only read for interactive + shells. You need to use FPATH to make function definitions + visible to scripts. + +4. A built-in command named command has been added which is + always found before the PATH search. Any script which uses + this name as the name of a command (or function) will not + be compatible. + +5. The output format for some built-ins has changed. In particular + the output format for set, typeset and alias now have single + quotes around values that have special characters. The output + for trap without arguments has a format that can be used as input. + +6. With ksh-88, a dollar sign ($') followed by a single quote was + interpreted literally. Now it is an ANSI-C string. You + must quote the dollar sign to get the previous behavior. + Also, a $ in front of a " indicates that the string needs + to be translated for locales other than C or POSIX. The $ + is ignored in the C and POSIX locale. + +7. With ksh-88, tilde expansion did not take place inside ${...}. + with ksh-93, ${foo-~} will cause tilde expansion if foo is + not set. You need to escape the ~ for the previous behavior. + +8. Some changes in the tokenizing rules where made that might + cause some scripts with previously ambiguous use of quoting + to produce syntax errors. + +9. Programs that rely on specific exit values for the shell, + (rather than 0 or non-zero) may not be compatible. The + exit status for many shell failures has been changed. + +10. Built-ins in ksh-88 were always executed before looking for + the command in the PATH variable. This is no longer true. + Thus, with ksh-93, if you have the current directory first + in your PATH, and you have a program named test in your + directory, it will be executed when you type test; the + built-in version will be run at the point /bin is found + in your PATH. + +11. Some undocumented combinations of argument passing to ksh + builtins no longer works since ksh-93 is getopts conforming + with respect to its built-ins. For example, typeset -8i + previously would work as a synonym for typeset -i8. + +12. Command substitution and arithmetic expansion are now performed + on PS1, PS3, and ENV when they are expanded. Thus, ` and $( + as part of the value of these variables must be preceded by a \ + to preserve their previous behavior. + +13. The ERRNO variable has been dropped. + +14. If the file name following a redirection symbol contain pattern + characters they will only be expanded for interactive shells. + +15. The arguments to a dot script will be restored when it completes. + +16. The list of tracked aliases is not displayed with alias unless + the -t option is specified. + +17. The POSIX standard requires that test "$arg" have exit status + of 0, if and only if $arg is null. However, since this breaks + programs that use test -t, ksh93 treats an explicit test -t + as if the user had entered test -t 1. + +18. The ^T directive of emacs mode has been changed to work the + way it does in gnu-emacs. + +19. ksh-88 allowed unbalanced parenthes within ${name op val} whereas + ksh-93 does not. Thus, ${foo-(} needs to be written as ${foo-\(} + which works with both versions. + +20. kill -l in ksh-93 lists only the signal names, not their numerical + values. + +21. Local variables defined by typeset are statically scoped in + ksh93. In ksh88 they were dynamically scoped although this + behavior was never documented. + +22. The value of the variable given to getopts is set to ? when + the end-of-options is reached to conform to the POSIX standard. + +23. Since the POSIX standard requires that octal constants be + recongnized, doing arithmetic on typeset -Z variables can + yield different results that with ksh88. Most of these + differences were eliminated in ksh93o. + +24. Starting after ksh93l, If you run ksh name, where name does + not contain a /, the current directory will be searched + before doing a path search on name as required by the POSIX + shell standard. + +25. In ksh93, cd - will output the directory that it changes + to on standard output as required by X/Open. With ksh88, + this only happened for interactive shells. + +26. As an undocumented feature of ksh-88, a leading 0 to an + assignment of an integer variable caused that variable + to be treated as unsigned. This behavior was removed + starting in ksh93p. + +27. The getopts builtin in ksh93 requires that optstring contain + a leading + to allow options to begin with a +. + +28. In emacs/gmacs mode, control-v will not display the version when + the stty lnext character is set to control-v or is unset. + The sequence escape control-v will display the shell version. + +I am interested in expanding this list so please let me know if you +uncover any others. Index: src/lib/libshell/common/sh.1 =================================================================== --- src/lib/libshell/common/sh.1 (revision 0) +++ src/lib/libshell/common/sh.1 (revision 740) @@ -0,0 +1,7453 @@ +.\" +.\" David Korn +.\" AT&T Bell Laboratories +.\" +.\" @(#)sh.1 (dgk@research.att.com) 12/28/93 +.\" +.nr Z 1 \" set to 1 when command name is ksh, 2 for ksh93 +.ds OK [\| +.ds CK \|] +.ds ' \s+4\v@.3m@\'\v@-.3m@\s-4 +.ds ` \s+4\v@.3m@\`\v@-.3m@\s-4 +.if \nZ=0 \{\ +.TH SH 1 +.\} +.if \nZ=1 \{\ +.TH KSH 1 "User Environment Utilities" "RDS Standard" +.\} +.if \nZ=2 \{\ +.TH KSH93 1 +.\} +.SH NAME +.if \nZ=0 \{\ +sh, rsh, pfsh \- shell, the +.\} +.if \nZ=1 \{\ +.\} +ksh, rksh, pfksh \- KornShell, a +.if \nZ=2 \{\ +ksh93, rksh93, pfksh93 \- KornShell, a +.\} +standard/restricted command and programming language +.SH SYNOPSIS +.if \nZ=0 \{\ +.B sh +.if \nZ=1 \{\ +.\} +.B ksh +.\} +.if \nZ=2 \{\ +.B ksh93 +.\} +[ +.B \(+-abcefhikmnoprstuvxBCDP +] [ +.B \-R +file ] [ +.B \(+-o +option ] .\|.\|. [ +.B \- +] [ arg .\|.\|. ] +.br +.if \nZ=0 \{\ +.B rsh +.\} +.if \nZ=1 \{\ +.B rksh +.\} +.if \nZ=2 \{\ +.B rksh93 +.\} +[ +.B \(+-abcefhikmnoprstuvxBCD +] [ +.B \-R +file ] [ +.B \(+-o +option ] .\|.\|. [ +.B \- +] [ arg .\|.\|. ] +.SH DESCRIPTION +.if \nZ=0 .I Sh\^ +.if \nZ=1 .I Ksh\^ +.if \nZ=2 .I Ksh93\^ +is a command and programming language +that executes commands read from a terminal +or a file. +.if \nZ=0 .I Rsh\^ +.if \nZ=1 .I Rksh\^ +.if \nZ=2 .I Rksh93\^ +is a restricted version of the +.if \nZ=0 standard +command interpreter +.if \nZ=0 .IR sh ; +.if \nZ=1 .IR ksh ; +.if \nZ=2 .IR ksh93 ; +it is used to set up login names and execution environments whose +capabilities are more controlled than those of the standard shell. +.if \nZ=0 .I Rpfsh\^ +.if \nZ=1 .I Rpfksh\^ +.if \nZ=2 .I Rpfksh93\^ +is a profile shell version of the +.if \nZ=0 standard +command interpreter +.if \nZ=0 .IR sh ; +.if \nZ=1 .IR ksh ; +.if \nZ=2 .IR ksh93 ; +it is used to to execute commands with the attributes specified by +the user's profiles (see +.IR pfexec (1)). +See +.I Invocation\^ +below +for the meaning of arguments to the shell. +.SS Definitions. +A +.I metacharacter\^ +is one of the following characters: +.PP +.RS +\f3; & ( ) \(bv < > new-line space tab\fP +.RE +.PP +A +.I blank\^ +is a +.B tab +or a +.BR space . +An +.I identifier\^ +is a sequence of letters, digits, or underscores +starting with a letter or underscore. +Identifiers are used as components of +.I variable\^ +names. +A +.I vname\^ +is a sequence of one or more identifiers +separated by a \fB\s+2.\s-2\fP and optionally preceded +by a \fB\s+2.\s-2\fP. +Vnames are used as function and variable names. +A +.I word\^ +is a sequence of +.I characters\^ +from the character set defined by the current locale, +excluding non-quoted +.IR metacharacters . +.PP +A +.I command\^ +is a sequence of characters in the syntax +of the shell language. +The shell reads each command and +carries out the desired action either directly or by invoking +separate utilities. +A built-in command is a command that is carried out by the +shell itself without creating a separate process. +Some commands are built-in purely for convenience +and are not documented here. +Built-ins that cause +side effects in the shell environment and +built-ins that are found before performing a +path search (see +.I Execution\^ +below) +are documented here. +For historical reasons, some of +these built-ins behave differently than +other built-ins and are called +.IR "special built-ins" . +.SS Commands. +A +.I simple-command\^ +is a list of variable assignments +(see +.I Variable Assignments\^ +below) +or a sequence of +.I blank\^ +separated words +which may be preceded by a list of variable assignments +(see +.I Environment\^ +below). +The first word specifies the name of the command to +be executed. +Except as specified below, +the remaining words are passed as arguments +to the invoked command. +The command name is passed as argument 0 +(see +.IR exec (2)). +The +.I value\^ +of a simple-command is its exit status; 0-255 +if it terminates normally; 256+\f2signum\^\fP if +it terminates abnormally (the name of the signal corresponding +to the exit status can be +obtained via the +.B \-l +option of the +.B kill\^ +built-in utility). +.PP +A +.I pipeline\^ +is a sequence of one or more +.I commands\^ +separated by +.BR \(bv . +The standard output of each command but the last +is connected by a +.IR pipe (2) +to the standard input of the next command. +Each command, +except possibly the last, +is run as a separate process; +the shell waits for the last command to terminate. +The exit status of a pipeline is the exit +status of the last command unless the +.B pipefail +option is enabled. +Each pipeline can be preceded by the +.I "reserved word" +.B ! +which causes the exit status of the pipeline to become +0 if the exit status of the last command is non-zero, and +1 if the exit status of the last command is 0. +.PP +A +.I list\^ +is a sequence of one or more +pipelines +separated by +.BR ; , +.BR & , +.BR \(bv& , +.BR && , +or +.BR \(bv\|\(bv , +and optionally terminated by +.BR ; , +.BR & , +or +.BR \(bv& . +Of these five symbols, +.BR ; , +.BR & , +and +.B \(bv& +have equal precedence, +which is lower than that of +.B && +and +.BR \(bv\|\(bv . +The symbols +.B && +and +.B \(bv\|\(bv +also have equal precedence. +A semicolon +.RB ( ; ) +causes sequential execution of the preceding pipeline; an ampersand +.RB ( & ) +causes asynchronous execution of the preceding pipeline (i.e., the shell does +.I not\^ +wait for that pipeline to finish). +The symbol +.B \(bv& +causes asynchronous execution of the preceding pipeline +with a two-way pipe established to the parent shell; +the standard input and output of the spawned pipeline +can be written to and read from by the parent shell +by applying +the redirection operators +.B <& +and +.B >& +with arg +.B p +to commands and by using +.B \-p +option of +the built-in commands +.B read +and +.B print +described later. +The symbol +.B && +.RB (\| \(bv\|\(bv \^) +causes the +.I list\^ +following it to be executed only if the preceding +pipeline +returns a zero (non-zero) value. +One or more new-lines may appear in a +.I list\^ +instead of a semicolon, +to delimit a command. +The first +.I item \^ +of the first +.I pipeline\^ +of a +.I list\^ +that is a simple command not beginning +with a redirection, and not occuring within a +.BR while , +.BR until , +or +.B if +.IR list , +can be prededed by a semicolon. +This semicolon +is ignored unless the +.B showme +option is enabled as described with +the +.B set +built-in below. +.PP +A +.I command\^ +is either a simple-command +or one of the following. +Unless otherwise stated, +the value returned by a command is that of the +last simple-command executed in the command. +.TP +\f3for\fP \f2vname\^\fP \*(OK \f3in\fP \f2word\^\fP .\|.\|. \*(CK \f3;do\fP \f2list\^\fP \f3;done\fP +Each time a +.B for +command is executed, +.I vname\^ +is set to the next +.I word\^ +taken from the +.B in +.I word\^ +list. +If +.BI in " word\^" +\&.\|.\|. +is omitted, then +the +.B for +command executes the \f3do\fP \f2list\^\fP once for each positional parameter +that is set starting from +.B 1 +(see +.I "Parameter Expansion\^" +below). +Execution ends when there are no more words in the list. +.TP +\f3for ((\fP \*(OK\f2expr1\^\fP\*(CK \f3;\fP \*(OK\f2expr2\^\fP\*(CK \f3;\fP \*(OK\f2expr3\^\fP\*(CK \f3))\fP \f3;do\fP \f2list\^\fP \f3;done\fP +The arithmetic expression +.I expr1 +is evaluated first +(see +.I "Arithmetic evaluation" +below). +The arithmetic expression +.I expr2 +is repeatedly evaluated until it evaluates to zero and when non-zero, +.I list +is executed and the arithmetic expression +.I expr3 +evaluated. +If any expression +is omitted, then it behaves as if it evaluated to 1. +.TP +\f3select\fP \f2vname\^\fP \*(OK \f3in\fP \f2word\^\fP .\|.\|. \*(CK \f3;do\fP \f2list\^\fP \f3;done\fP +A +.B select +command prints on standard error (file descriptor 2) the set of +.IR word s, +each preceded by a number. +If +.BI in " word\^" +\&.\|.\|. +is omitted, then +the +positional parameters starting from +.B 1 +are used instead +(see +.I "Parameter Expansion\^" +below). +The +.SM +.B PS3 +prompt is printed +and a line is read from the standard input. +If this line consists of the number +of one of the listed +.IR word s, +then the value of the variable +.I vname\^ +is set to the +.I word\^ +corresponding to this number. +If this line is empty, the selection list is +printed again. +Otherwise the value of the variable +.I vname\^ +is set to +.IR null . +The contents of the line read from standard input is +saved in +the variable +.SM +.BR REPLY . +The +.I list\^ +is executed for each selection until a +.B break\^ +or +.I end-of-file\^ +is encountered. +If the +.SM +.B REPLY +variable is set to +.I null\^ +by the execution of +.IR list , +then the selection list is printed before +displaying the +.SM +.B PS3 +prompt for the next selection. +.TP +\f3case\fP \f2word\^\fP \f3in\fP \*(OK \*(OK\f3(\fP\*(CK\f2pattern\^\fP \*(OK \(bv \f2pattern\^\fP \*(CK .\|.\|. \f3)\fP \f2list\^\fP \f3;;\fP \*(CK .\|.\|. \f3esac\fP +A +.B case +command executes the +.I list\^ +associated with the first +.I pattern\^ +that matches +.IR word . +The form of the patterns is +the same as that used for +file-name generation (see +.I "File Name Generation\^" +below). +The +.B ;; +operator causes execution of +.B case +to terminate. +If +.B ;& +is used in place of +.B ;; +the next subsequent list, if any, is executed. +.TP +\f3if\fP \f2list\^\fP \f3;then\fP \f2list\^\fP \*(OK \ +\f3;elif\fP \f2list\^\fP \f3;then\fP \f2list\^\fP \*(CK .\|.\|. \ +\*(OK \f3;else\fP \f2list\^\fP \*(CK \f3;f\&i\fP +The +.I list\^ +following \f3if\fP is executed and, +if it +returns a zero exit status, the +.I list\^ +following +the first +.B then +is executed. +Otherwise, the +.I list\^ +following \f3elif\fP +is executed and, if its value is zero, +the +.I list\^ +following +the next +.B then +is executed. +Failing each successive +.B elif +.IR list\^ , +the +.B else +.I list\^ +is executed. +If the +.B if +.I list\^ +has non-zero exit status +and there is no +.B else +.IR list , +then the +.B if +command returns a zero exit status. +.TP +.PD 0 +\f3while\fP \f2list\^\fP \f3;do\fP \f2list\^\fP \f3;done\fP +.TP +\f3until\fP \f2list\^\fP \f3;do\fP \f2list\^\fP \f3;done\fP +.PD +A +.B while +command repeatedly executes the +.B while +.I list\^ +and, if the exit status of the last command in the list is zero, executes +the +.B do +.IR list ; +otherwise the loop terminates. +If no commands in the +.B do +.I list\^ +are executed, then the +.B while +command returns a zero exit status; +.B until +may be used in place of +.B while +to negate +the loop termination test. +.TP +\f3((\fP\f2expression\^\fP\f3))\fP +.br +The +.I expression\^ +is evaluated using the rules for arithmetic evaluation described below. +If the value of the arithmetic expression is non-zero, the exit +status is 0, otherwise the exit status is 1. +.TP +\f3(\fP\f2list\^\fP\f3)\fP +.br +Execute +.I list\^ +in a separate environment. +Note, that if two adjacent open parentheses are +needed for nesting, a space must be inserted to avoid +evaluation as an arithmetic command as described above. +.TP +\f3{ \fP\f2list\^\fP\f3;}\fP +.br +.I list\^ +is simply executed. +Note that unlike the metacharacters +.B ( +and +.BR ) , +.B { +and +.B } +are +.IR "reserved word" s +and must occur +at the beginning of a line or after a +.B ; +in order to be recognized. +.TP +\f3[[\fP\f2 expression \^\fP\f3]]\fP +.br +Evaluates +.I expression\^ +and returns a zero exit status when +.I expression\^ +is true. +See +.I "Conditional Expressions\^" +below, for a description of +.IR expression . +.TP +.PD 0 +\f3function\fP \f2varname\^\fP \f3{\fP \f2list\^\fP \f3;}\fP +.TP +\f2varname\^\fP \f3() {\fP \f2list\^\fP \f3;}\fP +.PD +Define a function which is referenced by +.IR varname . +A function whose +.I varname\^ +contains a +.B \s+2.\s-2 +is called a discipline function and the portion +of the +.I varname\^ +preceding the last +.B \s+2.\s-2 +must refer to an existing variable. +The body of the function is the +.I list\^ +of commands between +.B { +and +.BR } . +A function defined with the \f3function\fP \f2varname\^\fP +syntax can also be used as an argument to the \f3.\fP +special built-in command to get the equivalent behavior +as if the \f2varname\^\fP\f3()\fP syntax were used to define it. +(See +.I Functions\^ +below.) +.TP +\f3time\fP \*(OK \f2pipeline\^\fP \*(CK +.br +If \f2pipeline\^\fP is omitted the user and system time for +the current shell and completed child processes is printed +on standard error. +Otherwise, +.I pipeline\^ +is executed and the elapsed time as well as +the user and system time are printed on standard error. +The +.SM +.B TIMEFORMAT +variable may be set to a format string that specifies how the timing +information should be displayed. +See +.B "Shell Variables" +below +for a description of the +.SM +.B TIMEFORMAT +variable. +.PP +The following reserved words +are recognized as reserved only when they are the first word of a command +and are not quoted: +.PP +.if t .RS +.B +.if n if then else elif fi case esac for while until do done { } function select time [[ ]] ! +.if t if then else elif fi case esac for while until do done { } function select time [[ ]] ! +.if t .RE +.SS Variable Assignments. +One or more variable assignments can start a simple command +or can be arguments to the +.BR typeset , +.BR export , +or +.B readonly +special built-in commands. +The syntax for an \f2assignment\^\fP is of the form: +.TP +.PD 0 +\f2varname\^\fP\f3=\fP\f2word\^\fP +.TP +\f2varname\^\fP\f3[\fP\f2word\^\fP\f3]\fP=\fP\f2word\^\fP +.PD +No space is permitted between \f2varname\^\fP and the \f3=\fP or +between \f3=\fP and \fIword\^\fP. +.TP +\f2varname\^\fP\f3=(\fP\f2assign_list\^\fP\f3)\fP +No space is permitted between \f2varname\^\fP and the \f3=\fP. +An \f2assign_list\^\fP can be one of the following: +.RS 15 +.PD 0 +.TP +\f2word\^\fP ... +Indexed array assignment. +.TP +\f3[\fP\f2word\^\fP\f3]=\fP\f2word\^\fP .\|.\|. +Associative array assignment. +If preceded by +.B typeset \-a +this will create an indexed array instead. +.TP +\f2assignment\^\fP .\|.\|. +Compound variable assignment. +This creates a compound variable \f2varname\^\fP with +sub-variables of the form \f2varname\^\fP\f3.\fP\f2name\^\fP, +where \f2name\^\fP is the name portion of \f2assignment\^\fP. +The value of \f2varname\^\fP will contain all the assignment elements. +Additional assignments made to sub-variables of \f2varname\^\fP +will also be displayed as part of the value of \f2varname\^\fP. +If no \f2assignment\fPs are specified, \f2varname\^\fP will be +a compound variable allowing subsequence child elements to be defined. +.TP +\f3typeset\fP \*(OK\f2options\fP\*(CK \f2assignment\^\fP .\|.\|. +Nested variable assignment. Multiple assignments +can be specified by separating each of them with a \f3;\fP. +The previous value is unset before the assignment. +.PD +.RE +.P +In addition, a \f3+=\fP can be used in place of the \f3=\fP +to signify adding to or appending to the previous value. +When \f3+=\fP is applied to an arithmetic type, \f2word\^\fP +is evaluated as an arithmetic expression and added to the current value. +When applied to a string variable, the value defined by \f2word\^\fP +is appended to the value. For compound assignments, the previous +value is not unset and the new values are appended to the +current ones provided that the types are compatible. +.SS Comments. +.PD 0 +A word beginning with +.B # +causes that word and all the following characters up to a new-line +to be ignored. +.SS Aliasing. +The first word of each command is replaced by the text of an +.B alias +if an +.B alias +for this word has been defined. +An +.B alias +name consists of any number of characters excluding metacharacters, +quoting characters, +file expansion characters, +parameter expansion and command substitution +characters, +and +.BR = . +The replacement string can contain any +valid shell script +including the metacharacters listed above. +The first word of each command in the +replaced text, +other than +any that are in the process of being replaced, +will be tested for aliases. +If the last character of the alias value is a +.I blank\^ +then the word following the alias will also be checked for alias +substitution. +Aliases can be used to redefine +built-in commands but cannot be used to redefine +the reserved words listed above. +Aliases can be created and listed with the +.B alias +command and can be removed with the +.B unalias +command. +.PP +.I Aliasing\^ +is performed when +scripts are read, +not while they are executed. +Therefore, +for an alias to take effect, +the +.B +alias +definition command has to be executed before +the command which references the alias is read. +.PP +The following aliases +are compiled into the shell +but can be unset or redefined: +.RS 20 +.PD 0 +.TP +.B "autoload=\(fmtypeset \-fu\(fm" +.TP +.B "command=\(fmcommand \(fm" +.TP +.B "fc=hist" +.TP +.B "float=\(fmtypeset \-lE\(fm" +.TP +.B "functions=\(fmtypeset \-f\(fm" +.TP +.B "hash=\(fmalias \-t \-\^\-\(fm" +.TP +.B "history=\(fmhist \-l\(fm" +.TP +.B "integer=\(fmtypeset \-li\(fm" +.TP +.B "nameref=\(fmtypeset \-n\(fm" +.TP +.B "nohup=\(fmnohup \(fm" +.TP +.B "r=\(fmhist \-s\(fm" +.TP +.B "redirect=\(fmcommand exec\(fm" +.TP +.B "source=\(fmcommand \s+2.\s-2\(fm" +.TP +.B "stop=\(fmkill \-s \s-1STOP\s+1\(fm" +.TP +.B "suspend=\(fmkill \-s \s-1STOP\s+1 $$\(fm" +.TP +.B "times=\(fm{ { time;} 2>&1;}\(fm" +.TP +.B "type=\(fmwhence \-v\(fm" +.PD +.RE +.SS Tilde Substitution. +After alias substitution is performed, each word +is checked to see if it begins with an unquoted +.BR \(ap . +For tilde substitution, +.I word\^ +also refers to the +.I word\^ +portion of parameter expansion +(see +.I "Parameter Expansion\^" +below). +If it does, then the word up to a +.B / +is checked to see if it matches a user name in the +password database (See +.IR getpwname (3).) +If a match is found, the +.B \(ap +and the matched login name are replaced by the +login directory of the matched user. +If no match is found, the original text is left unchanged. +A +.B \(ap +by itself, or in front of a +.BR / , +is replaced by +.SM +.BR $HOME . +A +.B \(ap +followed by a +.B + +or +.B \- +is replaced by the value of +.B +.SM $PWD +and +.B +.SM $OLDPWD +respectively. +.PP +In addition, +when expanding a +.IR "variable assignment" , +.I tilde +substitution is attempted when +the value of the assignment +begins with a +.BR \(ap , +and when a +.B \(ap +appears after a +.BR : . +The +.B : +also terminates a +.B \(ap +login name. +.SS Command Substitution. +The standard output from a command enclosed in +parentheses preceded by a dollar sign ( +.B $(\|) +) +or a pair of grave accents (\^\f3\*`\^\*`\fP\^) +may be used as part or all +of a word; +trailing new-lines are removed. +In the second (obsolete) form, the string between the quotes is processed +for special quoting characters before the command is executed (see +.I Quoting\^ +below). +The command substitution +\^\f3$(\^cat file\^)\fP\^ +can be replaced by the equivalent but faster +\^\f3$(\^(\fP\f2list\^\fP\f3)\fP +will run process +.I list +asynchronously connected to some file in +.BR /dev/fd . +The name of this file will become the argument to the command. +If the form with +.B > +is selected then writing on this file will provide input for +.IR list . +If +.B < +is used, +then the file passed as an argument will contain the output of the +.I list +process. +For example, +.PP +.RS +\f3paste <(cut \-f1\fP \f2file1\fP\f3) <(cut \-f3\fP \f2file2\f3) | tee >(\fP\f2process1\fP\f3) >(\fP\f2process2\fP\f3)\fP +.RE +.PP +.I cuts +fields 1 and 3 from +the files +.I file1 +and +.I file2 +respectively, +.I pastes +the results together, and +sends it +to the processes +.I process1 +and +.IR process2 , +as well as putting it onto the standard output. +Note that the file, which is passed as an argument to the command, +is a UNIX +.IR pipe (2) +so programs that expect to +.IR lseek (2) +on the file will not work. +.SS Parameter Expansion. +A +.I parameter\^ +is a +.IR variable , +one or more digits, +or any of the characters +.BR \(** , +.BR @ , +.BR # , +.BR ? , +.BR \- , +.BR $ , +and +.BR !\\^ . +A +.I variable\^ +is denoted by a \f2vname\fP. +To create a variable whose +.I vname\^ +contains a \f3\s+2.\s-2\fP, +a variable whose +.I vname\^ +consists of everything before the last \f3\s+2.\s-2\fP must already exist. +A +.I variable\^ +has a +.I value\^ +and zero or more +.IR attributes . +.I Variables\^ +can be assigned +.I values\^ +and +.I attributes +by using the +.B typeset\^ +special built-in command. +The attributes supported by the shell are described +later with the +.B typeset\^ +special built-in command. +Exported variables pass values and attributes to +the environment. +.PP +The shell supports both indexed and associative arrays. +An element of an array variable is referenced by a +.IR subscript . +A +.I subscript\^ +for an indexed array is denoted by +an +.I arithmetic expression\^ +(see +.I "Arithmetic evaluation" +below) +between a +.B [ +and a +.BR ] . +To assign values to an indexed array, use +\f3set \-A\fP \f2vname\fP \f2value\fP .\|.\|. . +The value of all +subscripts must be in the +range of +0 through 1,048,575. +Indexed arrays need not be declared. +Any reference to a variable +with a valid subscript is +legal and an array will be created if necessary. +.PP +An associative array is created with the +.B \-A +option to +.BR typeset. +A +.I subscript\^ +for an associative array is denoted by +a string enclosed between +.B [ +and +.BR ] . +.PP +Referencing any array without a subscript +is equivalent to referencing the array with subscript 0. +.PP +The +.I value\^ +of a +.I variable\^ +may be assigned by writing: +.PP +.RS +.IB vname = value\^\| +\*(OK +.IB vname = value\^ +\*(CK .\|.\|. +.RE +.PP +.PD 0 +or +.PP +.RS +.IB vname [ subscript ]= value\^\| +\*(OK +.IB vname [ subscript ]= value\^ +\*(CK .\|.\|. +.sp .5 +.RE +Note that no space is allowed before or after the +.BR = . +.sp .5 +.PP +.PD 0 +A +.I nameref\^ +is a variable that is a reference to another variable. +A nameref is created with the +.B \-n +attribute of +.BR typeset . +The value of the variable at the time of the +.B typeset +command becomes the variable that will be referenced whenever +the nameref variable is used. +The name of a nameref cannot contain a \fB\s+2.\s-2\fP. +When a variable or function name contains a \fB\s+2.\s-2\fP, and the portion +of the name up to the first \fB\s+2.\s-2\fP matches the +name of a nameref, the variable referred to is obtained by +replacing the nameref portion with the name of the variable +referenced by the nameref. +If a nameref is used as the index of a \fBfor\fP loop, +a name reference is established for each item in the list. +A nameref provides a convenient way to refer to the variable +inside a function whose name is passed as an argument to a function. +For example, if the name of a variable is passed as the first +argument to a function, the command +.PP +.RS +\fBtypeset \-n var=$1\fR +.RE +.PP +inside the function causes references and assignments to +.B var +to be references and assignments to the variable whose +name has been passed to the function. +.sp .5 +.PP +If either of the floating point attributes, +.BR \-E , +or +.BR \-F , +or the integer attribute, +.BR \-i , +is set for +.IR vname , +then the +.I value\^ +is subject to arithmetic evaluation as described below. +.sp .5 +.PP +Positional parameters, +parameters denoted by a number, +may be assigned values with the +.B set\^ +special built-in command. +Parameter +.B $0 +is set from argument zero when the shell +is invoked. +.sp .5 +.PP +The character +.B $ +is used to introduce substitutable +.IR parameters . +.TP +\f3${\fP\f2parameter\^\fP\f3}\fP +The shell +reads all the characters from +.B ${ +to the matching +.B } +as part of the same word even if it contains +braces or metacharacters. +The value, if any, of the parameter is substituted. +The braces are required when +.I parameter\^ +is followed by a letter, digit, or underscore +that is not to be interpreted as part of its name, +when the variable name contains a \fB\s+2.\s-2\fP, +or when a variable is subscripted. +If +.I parameter\^ +is one or more digits then it is a positional parameter. +A positional parameter of more than one digit must be +enclosed in braces. +If +.I parameter\^ +is +.B \(** +or +.BR @ , +then all the positional +parameters, starting with +.BR $1 , +are substituted +(separated by a field separator character). +If an array +.I vname\^ +with subscript +.B \(** +or +.B @ +is used, +then the value +for each of the +elements +is substituted, +separated by +the first character of +the value of +.SM +.BR IFS . +.TP +\f3${#\fP\f2parameter\^\fP\f3}\fP +If +.I parameter\^ +is +.B \(** +or +.BR @ , +the number of positional parameters is substituted. +Otherwise, the length of the value of the +.I parameter\^ +is substituted. +.TP +.PD 0 +\f3${#\fP\f2vname\fP\f3[*]}\fP +.TP +.PD +\f3${#\fP\f2vname\fP\f3[@]}\fP +The number of elements in the array +.I vname\^ +is substituted. +.TP +\f3${!\fP\f2vname\^\fP\f3}\fP +Expands to the name of the variable referred to by +.IR vname . +This will be +.I vname\^ +except when +.I vname\^ +is a name reference. +.TP +\f3${!\fP\f2vname\^\fP\f3[\f2subscript\^\f3]}\fP +Expands to name of the subscript unless +.I subscript\^ +is +.B * +or +.BR @ . +When +.I subscript\^ +is +.BR * , +the list of array subscripts for \f2vname\^\fP +is generated. +For a variable that is not an array, the value is 0 if the variable +is set. Otherwise it is null. +When +.I subscript\^ +is +.BR @ , +same as above, except that when used in double quotes, +each array subscript yields a separate +argument. +.TP +\f3${!\fP\f2prefix\^\fP\f3*}\fP +Expands to the names of the variables whose names begin with +.IR prefix . +.TP +\f3${\fP\f2parameter\^\fP\f3:\-\fP\f2word\^\fP\f3}\fP +If +.I parameter\^ +is set and is non-null then substitute its value; +otherwise substitute +.IR word . +.TP +\f3${\fP\f2parameter\^\fP\f3:=\fP\f2word\^\fP\f3}\fP +If +.I parameter\^ +is not set or is null then set it to +.IR word ; +the value of the parameter is then substituted. +Positional parameters may not be assigned to +in this way. +.TP +\f3${\fP\f2parameter\^\fP\f3:?\fP\f2word\^\fP\f3}\fP +If +.I parameter\^ +is set and is non-null then substitute its value; +otherwise, print +.I word\^ +and exit from the shell (if not interactive). +If +.I word\^ +is omitted then a standard message is printed. +.TP +\f3${\fP\f2parameter\^\fP\f3:+\fP\f2word\^\fP\f3}\fP +If +.I parameter\^ +is set and is non-null then substitute +.IR word ; +otherwise substitute nothing. +.PP +In the above, +.I word\^ +is not evaluated unless it is +to be used as the substituted string, +so that, in the following example, +.B pwd +is executed only if +.B d +is not set or is null: +.PP +.RS +print \|${d:\-\^$(\^pwd\^)\^} +.RE +.PP +If the colon ( +.B : ) +is omitted from the above expressions, +then the shell only checks whether +.I parameter\^ +is set or not. +.TP +.PD 0 +\f3${\fP\f2parameter\^\fP\f3:\fP\f2offset\^\fP\f3:\fP\f2length\^\fP\f3}\fP +.TP +\f3${\fP\f2parameter\^\fP\f3:\fP\f2offset\^\fP\f3}\fP +Expands to the portion of the value of +.I parameter\^ +starting at the character (counting from +.BR 0\^ ) +determined by expanding +.I offset\^ +as an arithmetic expression and consisting of the +number of characters determined by the arithmetic expression +defined by +.IR length. +In the second form, the remainder of the value is used. +If +A negative +.I offset\^ +counts backwards from the end of +.IR parameter . +Note that one or more +.IR blank s +is required in front of a minus sign +to prevent the shell from interpreting the operator as +.BR :\- . +If +.I parameter\^ +is +.B \(** +or +.BR @ , +or is an array name indexed by +.B \(** +or +.BR @ , +then +.I offset\^ +and +.I length\^ +refer to the array index and number +of elements respectively. +A negative +.I offset\^ +is taken relative to one greater than the highest subscript +for indexed arrays. +The order for associate arrays is unspecified. +.TP +.PD 0 +\f3${\fP\f2parameter\^\fP\f3#\fP\f2pattern\^\fP\f3}\fP +.TP +\f3${\fP\f2parameter\^\fP\f3##\fP\f2pattern\^\fP\f3}\fP +.PD +If +the shell +.I pattern\^ +matches the beginning of the value of +.IR parameter , +then the value of +this expansion is the value of the +.I parameter\^ +with the matched portion deleted; +otherwise the value of this +.I parameter\^ +is substituted. +In the first form the smallest matching pattern is deleted and in the +second form the largest matching pattern is deleted. +When +.I parameter\^ +is +.BR @ , +.BR * , +or an array variable with subscript +.B @ +or +.BR * , +the substring operation is applied to each element in turn. +.TP +.PD 0 +\f3${\fP\f2parameter\^\fP\f3%\fP\f2pattern\^\fP\f3}\fP +.TP +\f3${\fP\f2parameter\^\fP\f3%%\fP\f2pattern\^\fP\f3}\fP +.PD +If +the shell +.I pattern\^ +matches the end of the value of +.IR parameter , +then the value of +this expansion is the value of the +.I parameter\^ +with the matched part deleted; +otherwise substitute the value of +.IR parameter . +In the first form the smallest matching pattern is deleted and in the +second form the largest matching pattern is deleted. +When +.I parameter\^ +is +.BR @ , +.BR * , +or an array variable with subscript +.B @ +or +.BR * , +the substring operation is applied to each element in turn. +.TP +.PD 0 +\f3${\fP\f2parameter\^\fP\f3/\fP\f2pattern\^\fP\f3/\f2string\^\fP\f3}\fP +.TP +\f3${\fP\f2parameter\^\fP\f3//\fP\f2pattern\^\fP\f3/\f2string\^\fP\f3}\fP +.TP +\f3${\fP\f2parameter\^\fP\f3/#\fP\f2pattern\^\fP\f3/\f2string\^\fP\f3}\fP +.TP +\f3${\fP\f2parameter\^\fP\f3/%\fP\f2pattern\^\fP\f3/\f2string\^\fP\f3}\fP +.PD +Expands +.I parameter\^ +and replaces the longest match of +.I pattern\^ +with the given +.IR string. +Each occurrence of \f3\e\fP\f2n\^\fP in +.I string +is replaced by the portion of \f2parameter\^\fP +that matches the \f2n\^\fP-th sub-pattern. +In the first form, +only the first occurrence of +.I pattern\^ +is replaced. +In the second form, +each match for +.I pattern\^ +is replaced by the given +.IR string. +The third form restricts the pattern match to the beginning of the string +while the fourth form restricts the pattern match to the end of +the string. +When +.I string\^ +is null, the +.I pattern\^ +will be deleted and the +.B / +in front of +.I string\^ +may be omitted. +When +.I parameter\^ +is +.BR @ , +.BR * , +or an array variable with subscript +.B @ +or +.BR * , +the substitution operation is applied to each element in turn. +In this case, the +.I string\^ +portion of +.I word\^ +will be re-evaluated for each element. +.PP +The following +parameters +are automatically set by the shell: +.RS +.PD 0 +.TP +.B # +The number of positional parameters in decimal. +.TP +.B \- +Options supplied to the shell on invocation or by +the +.B set +command. +.TP +.B ? +The decimal value returned by the last executed command. +.TP +.B $ +The process number of this shell. +.TP +.B _ +Initially, the value of +.B _ +is an absolute pathname of the shell or script being executed +as passed in the +.IR environment . +Subsequently it is assigned the last argument of the previous command. +This parameter is not set for commands which are asynchronous. +This parameter is also used to hold the name of the matching +.B +.SM MAIL +file when checking for mail. +.TP +.B ! +The process number of the last background command invoked or +the most recent job put in the background with the +.B bg +built-in command. +.TP +.B .sh.command +When processing a +.SM +.B DEBUG +trap, this variable contains the current command line +that is about to run. +.TP +.B .sh.edchar +This variable contains the value of the keyboard character +(or sequence of characters if the first character is an ESC, ascii +.BR 033\^ ) +that has +been entered when processing a +.B +.SM KEYBD +trap +(see +.I "Key Bindings\^" +below). +If the value is changed as part of the trap action, then the new +value replaces the key (or key sequence) that caused the trap. +.TP +.B .sh.edcol +The character position of the cursor at the time of the most recent +.B +.SM KEYBD +trap. +.TP +.B .sh.edmode +The value is set to ESC when processing a +.B +.SM KEYBD +trap while in +.B vi +insert mode. (See +.I "Vi Editing Mode"\^ +below.) +Otherwise, +.B .sh.edmode +is null when processing a +.B +.SM KEYBD +trap. +.TP +.B .sh.edtext +The characters in the input buffer at the time of the most recent +.B +.SM KEYBD +trap. +The value is null when not processing a +.B +.SM KEYBD +trap. +.TP +.B .sh.file +The pathname of the file than contains the current command. +.TP +.B .sh.fun +The name of the current function that is being executed. +.TP +.B .sh.match +An indexed array which stores the most recent match and sub-pattern +matches after conditional pattern matches that match and after +variables expansions using the operators +.BR # , +.BR % , +or +.BR / . +The +.BR 0 -th +element stores the complete match and the +.IR i\^ -th. +element stores the +.IR i\^ -th +submatch. +The +.B .sh.match +variable +becomes unset when the variable that has expanded +is assigned a new value. +.TP +.B .sh.name +Set to the name of the variable at the time that a +discipline function is invoked. +.TP +.B .sh.subscript +Set to the name subscript of the variable at the time that a +discipline function is invoked. +.TP +.B .sh.subshell +The current depth for subshells and command substitution. +.TP +.B .sh.value +Set to the value of the variable at the time that the +.B set +or +.B append +discipline function is invoked. +.TP +.B .sh.version +Set to a value that identifies the version of this shell. +.TP +.B +.SM LINENO +The current line number within the script or +function being executed. +.TP +.B +.SM OLDPWD +The previous working directory set by the +.B cd +command. +.TP +.B +.SM OPTARG +The value of the last option argument processed by the +.B getopts +built-in command. +.TP +.B +.SM OPTIND +The index of the last option argument processed by the +.B getopts +built-in command. +.TP +.B +.SM PPID +The process number of the parent of the shell. +.TP +.B +.SM PWD +The present working directory set by the +.B cd +command. +.TP +.B +.SM RANDOM +Each time this variable is referenced, a random integer, +uniformly distributed between 0 and 32767, is generated. +The sequence of random numbers can be initialized by assigning +a numeric value to +.SM +.BR RANDOM . +.TP +.B +.SM REPLY +This variable is set by the +.B select +statement and by +the +.B read +built-in command when no arguments are supplied. +.TP +.B +.SM SECONDS +Each time this variable is referenced, the number of +seconds since shell invocation is returned. +If this variable is +assigned a value, then the value returned upon reference will +be the value that was assigned plus the number of seconds since the assignment. +.PD +.RE +.PP +The following +variables +are used by the shell: +.RS +.PD 0 +.TP +.B +.SM CDPATH +The search path for the +.B cd +command. +.TP +.B +.SM COLUMNS +If this variable is set, +the value is used to define the width of the edit window +for the shell edit modes and for printing +.B select +lists. +.TP +.B +.SM EDITOR +If the +.B +.SM VISUAL +variable is not set, +the value of this variable will be checked for the patterns +as described with +.B +.SM VISUAL +below and the corresponding editing option +(see Special Command +.B set +below) +will be turned on. +.TP +.SM +.B ENV +If this variable is set, then +parameter expansion, command substitution, and arithmetic substitution +are performed on +the value to generate +the pathname of the script that will be +executed when the shell +is invoked +(see +.I Invocation\^ +below). +This file is typically used for +.B alias +and +.B function +definitions. +The default value is \fB$HOME/.kshrc\fP. +.TP +.B +.SM FCEDIT +Obsolete name for +the default editor name for the +.B hist +command. +.B +.SM FCEDIT +is not used when +.B +.SM HISTEDIT +is set. +.TP +.SM +.B FIGNORE +A pattern that defines the set of filenames that will be +ignored when performing filename matching. +.TP +.SM +.B FPATH +The search path for function definitions. +The directories in this path are searched for a file with the same name +as the function or command when a function with the +.B \-u +attribute is referenced and when a command is not found. +If an executable file with the name of that command is found, +then it is read and executed +in the current environment. +Unlike +.SM +.BR PATH , +the current directory must be represented +explictily by +.B . +rather than by adjacent +.B : +characters or a beginning or ending +.BR : . +.TP +.B +.SM HISTCMD +Number of the current command in the history file. +.TP +.B +.SM HISTEDIT +Name for +the default editor name for the +.B hist +command. +.TP +.SM +.B HISTFILE +If this variable is set when the shell is invoked, then +the value is the pathname of the file that will be +used to store the command history (see +.I "Command Re-entry\^" +below). +.TP +.SM +.B HISTSIZE +If this variable is set when the shell is invoked, then +the number of previously entered commands that +are accessible by this shell +will be greater than or equal to this number. +The default is 512. +.TP +.B +.SM HOME +The default argument (home directory) for the +.B cd +command. +.TP +.SM +.B IFS +Internal field separators, +normally +.BR space , +.BR tab , +and +.B new-line +that are used to separate the results of +command substitution or parameter expansion +and to separate fields with the built-in command +.BR read . +The first character of the +.SM +.B IFS +variable is used to separate arguments for the +.B +"$\(**" +substitution (see +.I Quoting +below). +Each single occurrence of +an +.SM +.B IFS +character in the string to be split, +that is not in the \f2isspace\^\fP character class, and any +adjacent characters in +.SM +.B IFS +that are in the \f2isspace\^\fP character class, delimit a field. +One or more +characters in +.SM +.B IFS +that belong to the \f2isspace\^\fP character class, +delimit a field. +In addition, if the same \f2isspace\^\fP character appears +consecutively inside +.SM +.BR IFS , +this character is treated as if it were not in the \f2isspace\^\fP +class, so that if +.SM +.BR IFS +consists of two +.B tab +characters, +then two adjacent +.B tab +characters delimit a null field. +.TP +.B +.SM LANG +This variable determines the locale category for any +category not specifically selected with a variable +starting with +.B +.SM LC_ +or +.SM +.BR LANG . +.TP +.B +.SM LC_ALL +This variable overrides the value of the +.B +.SM LANG +variable and any other +.B +.SM LC_ +variable. +.TP +.B +.SM LC_COLLATE +This variable determines the locale category for character +collation information. +.TP +.B +.SM LC_CTYPE +This variable determines the locale category for character +handling functions. +It determines the character classes for pattern matching (see +.I "File Name Generation\^" +below). +.TP +.B +.SM LC_NUMERIC +This variable determines the locale category for the +decimal point character. +.TP +.B +.SM LINES +If this variable is set, +the value is used to determine the column length for printing +.B select +lists. +Select lists will print vertically until about two-thirds of +.B +.SM LINES +lines are filled. +.TP +.B +.SM MAIL +If this variable is set to the name of a mail file +.I and\^ +the +.B +.SM MAILPATH +variable is not set, +then the shell informs the user of arrival of mail +in the specified file. +.TP +.B +.SM MAILCHECK +This variable specifies how often (in seconds) the +shell will check for changes in the modification time +of any of the files specified by the +.B +.SM MAILPATH +or +.B +.SM MAIL +variables. +The default value is 600 seconds. +When the time has elapsed +the shell will check before issuing the next prompt. +.TP +.B +.SM MAILPATH +A colon ( +.B : +) +separated list of file names. +If this variable is set, +then the shell informs the user of +any modifications to the specified files +that have occurred within the last +.B +.SM MAILCHECK +seconds. +Each file name can be followed by a +.B ? +and a message that will be printed. +The message will undergo parameter expansion, command substitution, +and arithmetic substitution +with the variable +.B $_ +defined as the name of the file that has changed. +The default message is +.I you have mail in $_\^. +.TP +.B +.SM PATH +The search path for commands (see +.I Execution\^ +below). +The user may not change +.B \s-1PATH\s+1 +if executing under +.if \nZ=0 .B rsh +.if \nZ=1 .B rksh +.if \nZ=2 .B rksh93 +(except in +.BR .profile\^). +.TP +.SM +.B PS1 +The value of this variable is expanded for parameter +expansion, command substitution, and arithmetic substitution to define the +primary prompt string which by default is +.RB `` "$\|\|\|" ''. +The character +.B ! +in the primary prompt string is replaced by the +.I command\^ +number (see +.I "Command Re-entry\^" +below). +Two successive occurrences of +.B ! +will produce a single +.B ! +when the prompt string is printed. +.TP +.SM +.B PS2 +Secondary prompt string, by default +.RB `` "> \|" ''. +.TP +.SM +.B PS3 +Selection prompt string +used within a +.B select +loop, by default +.RB `` "#? \|" ''. +.TP +.SM +.B PS4 +The value of this variable is expanded for parameter evaluation, +command substitution, and arithmetic substitution +and precedes each line of an execution trace. +By default, +.SM +.B PS4 +is +.RB `` "+ \|" ''. +In addition +when +.SM +.B PS4 +is unset, +the execution trace prompt is also +.RB `` "+ \|" ''. +.TP +.SM +.B SHELL +The pathname of the +.I shell\^ +is kept in the environment. +At invocation, if the basename of this variable is +.BR rsh , +.BR rksh , +or +.BR krsh , +then the shell becomes restricted. +If it is +.BR pfsh +or +.BR pfksh , +then the shell becomes a profile shell (see +.IR pfexec (1)). +.TP +.SM +.B TIMEFORMAT +The value of this parameter is used as a format string specifying +how the timing information for pipelines prefixed with the +.B time +reserved word should be displayed. +The \fB%\fP character introduces a format sequence that is +expanded to a time value or other information. +The format sequences and their meanings are as follows. +.sp .5 +.RS +.PD 0 +.TP 10 +.B %% +A literal \fB%\fP. +.TP +.B %[\fIp\fP][l]R +The elapsed time in seconds. +.TP +.B %[\fIp\fP][l]U +The number of CPU seconds spent in user mode. +.TP +.B %[\fIp\fP][l]S +The number of CPU seconds spent in system mode. +.TP +.B %P +The CPU percentage, computed as (U + S) / R. +.PD +.RE +.IP +The braces denote optional portions. +The optional \fIp\fP is a digit specifying the \fIprecision\fP, +the number of fractional digits after a decimal point. +A value of 0 causes no decimal point or fraction to be output. +At most three places after the decimal point can be displayed; +values of \fIp\fP greater than 3 are treated as 3. +If \fIp\fP is not specified, the value 3 is used. +.IP +The optional \fBl\fP specifies a longer format, including +hours if greater than zero, +minutes, and seconds of the form \fIHH\fPh\fIMM\fPm\fISS\fP.\fIFF\fPs. +The value of \fIp\fP determines whether or not the fraction is +included. +.IP +All other characters are output without change and a trailing +newline is added. +If unset, the default value, \fB$'\enreal\et%2lR\enuser\et%2lU\ensys\t%2lS'\fP, +is used. If the value is null, no timing information is displayed. +.TP +.B +.SM TMOUT +If set to a value greater than zero, +.B +.SM TMOUT +will be the default timeout value for the +.B read +built-in command. +The +.B select +compound command terminates after +.B +.SM TMOUT +seconds when input is from a terminal. +Otherwise, +the shell will terminate if a line is not entered within +the prescribed number of seconds while reading from a terminal. +(Note that the shell can be compiled with a maximum bound +for this value which cannot be exceeded.) +.TP +.B +.SM VISUAL +If the value of this variable matches the pattern +.IR *[Vv][Ii]* , +then the +.B vi +option +(see Special Command +.B set +below) +is turned on. +If the value matches the pattern +.I *gmacs* , +the +.B gmacs +option is turned on. +If the value matches the pattern +.IR *macs* , +then the +.B emacs +option +will be turned on. +The value of +.B +.SM VISUAL +overrides the value of +.B +.SM EDITOR. +.PD +.RE +.PP +The shell gives default values to +\f3\s-1PATH\s+1\fP, \f3\s-1PS1\s+1\fP, \f3\s-1PS2\s+1\fP, +\f3\s-1PS3\s+1\fP, \f3\s-1PS4\s+1\fP, \f3\s-1MAILCHECK\s+1\fP, \f3\s-1FCEDIT\s+1\fP, +\f3\s-1TMOUT\s+1\fP and \f3\s-1IFS\s+1\fP, +while +.SM +.BR HOME , +.SM +.BR SHELL , +.SM +.BR ENV , +and +.SM +.B MAIL +are +not set at all by the shell (although +.SM +.B HOME +.I is\^ +set by +.IR login (1)). +On some systems +.SM +.B MAIL +and +.SM +.B SHELL +are also +set by +.IR login (1). +.SS Field Splitting. +After parameter expansion and command substitution, +the results of substitutions are scanned for the field separator +characters (those found in +.SM +.B IFS\^\c +) +and split into distinct fields where such characters are found. +Explicit null fields (\^\f3"\^"\fP or \f3\(fm\^\(fm\fP\^) are retained. +Implicit null fields +(those resulting from +.I parameters\^ +that have no values or command substitutions with no output) are removed. +.PP +If the +.B braceexpand +.RB ( \-B ) +option is set then each of the fields resulting from +.SM +.B IFS +are checked to see if they contain one or more of the brace patterns +.BR {*,*} , +.BI { l1 .. l2 } +, +.BI { n1 .. n2 } +, +.BI { n1 .. n2 % +.IB fmt } +, +.BI { n1 .. n2 +.BI .. n3 } +, or +.BI { n1 .. n2 +.BI .. n3 % fmt } +, where +.B * +represents any character, +.IR l1\^ , l2\^ +are letters and +.IR n1\^ , n2\^ , n3\^ +are signed numbers and +.I fmt\^ +is a format specified as used by +.BR printf . +In each case, fields are created +by prepending the characters before the +.B { +and appending the characters after the +.B } +to each of the strings generated by the characters between +the +.B { +and +.BR } . +The resulting fields are checked to see if they have any +brace patterns. +.PP +In the first form, a field is created for each string between +.BR { +and +.BR , , +between +.BR , +and +.BR , , +and between +.BR , +and +.BR } . +The string represented by +.B * +can contain embedded matching +.B { +and +.B } +without quoting. +Otherwise, each +.B { +and +.B } +with +.B * +must be quoted. +.PP +In the seconds form, +.I l1\^ +and +.I l2\^ +must both be either upper case or both be lower case characters +in the C locale. In this case a field is created for each character +from +.I l1\^ +thru +.IR l2\^ . +.PP +In the remaining forms, a field is created for each number starting at +.I n1\^ +and continuing until it reaches +.I n2\^ +incrementing +.I n1\^ +by +.IR n3\^ . +The cases where +.I n3\^ +is not specified behave as if +.I n3\^ +where +.B 1 +if +.IB n1 <= n2 +and +.B \-1 +otherwise. +If forms which specify +.BI % fmt\^ +any format flags, widths and precisions can be specified +and +.I fmt\^ +can end in any of the specifiers +.BR cdiouxX . +For example, +.B {a,z}{1..5..3%02d}{b..c}x +expands to the 8 fields, +.BR a01bx , +.BR a01cx , +.BR a04bx , +.BR a04cx , +.BR z01bx , +.BR z01cx , +.B z04bx +and +.BR z4cx . +.SS File Name Generation. +Following splitting, each field is scanned for the characters +.BR \(** , +.BR ? , +.BR ( , +and +.B \*(OK\^ +unless the +.B \-f +option has been set. +If one of these characters appears, +then the word is regarded as a +.IR pattern . +Each file name component that contains any pattern character +is replaced with a lexicographically sorted set of names +that matches the pattern +from +that directory. +If no file name is found that matches the pattern, then +that component of the filename is left unchanged unless +the pattern is prefixed with +.B \(ap(N)\fP +in which case it is removed as described below. +If +.SM +.B FIGNORE +is set, +then each file name component +that matches the pattern defined by the value of +.SM +.B FIGNORE +is ignored when generating the matching filenames. +The names +.B . +and +.B .. +are also ignored. +If +.SM +.B FIGNORE +is not set, +the character +.B . +at the start of each file name component +will be ignored unless the first character of the pattern +corresponding to this component is the character +.BR . +itself. +Note, that for other +uses of pattern matching the +.B / +and +.B . +are not treated specially. +.PP +.PD 0 +.RS +.TP +.B \(** +Matches any string, including the null string. +When used for filename expansion, +if the +.B globstar +option is on, two adjacent +.BR \(** 's +by itself +will match all files and zero or more directories +and subdirectories. +If followed by a +.B / +than only directories and subdirectories will match. +.TP +.B ? +Matches any single character. +.TP +.BR \*(OK \^.\|.\|.\^ \*(CK +Matches any one of the enclosed characters. +A pair of characters separated by +.B \- +matches any +character lexically between the pair, inclusive. +If the first character following the opening +.B \*(OK\^ +is a +.B ! +then any character not enclosed is matched. +A +.B \- +can be included in the character set by putting it as the +first or last character. +.br +Within +.B \*(OK\^ +and +.BR \*(CK\^ , +character classes can be specified with the syntax +\f3[:\fP\f2class\fP\f3:]\fP +where class is one of the following classes defined in the ANSI-C standard: +(Note that \f3word\fP is equivalent to \f3alnum\fP plus the character \f3_\fP). +.PP +.if t .RS +.B +.if n alnum alpha blank cntrl digit graph lower print punct space upper word xdigit +.if t alnum alpha blank cntrl digit graph lower print punct space upper word xdigit +.br +Within +.B \*(OK\^ +and +.BR \*(CK\^ , +an equivalence class can be specified with the syntax +\f3[=\fP\f2c\fP\f3=]\fP +which matches all characters with the same primary +collation weight (as defined by the current locale) as +the character \f2c\fP. +.br +Within +.B \*(OK\^ +and +.BR \*(CK\^ , +\f3[.\fP\f2symbol\fP\f3.]\fP +matches the collating symbol \f2symbol\fP. +.if t .RE +.PD +.RE +A +.I pattern-list +is a list of one or more patterns separated from each other +with a +.B & +or +.BR \(bv . +A +.B & +signifies that all patterns must be matched whereas +.BR \(bv +requires that only one pattern be matched. +Composite patterns can be formed with one or more of the following sub-patterns: +.PD 0 +.RS +.TP +\f3?(\fP\f2pattern-list\^\fP\f3)\fP +Optionally matches any one of the given patterns. +.TP +\f3*(\fP\f2pattern-list\^\fP\f3)\fP +Matches zero or more occurrences of the given patterns. +.TP +\f3+(\fP\f2pattern-list\^\fP\f3)\fP +Matches one or more occurrences of the given patterns. +.TP +\f3{\fP\f2n\^\fP\f3}\fP(\fP\f2pattern-list\^\fP\f3)\fP +Matches \f2n\^\fP occurrences of the given patterns. +.TP +\f3{\fP\f2m\^\fP\f3,\fP\f2n\^\fP\f3}\fP(\fP\f2pattern-list\^\fP\f3)\fP +Matches from \f2m\^\fP to \f2n\^\fP occurrences of the given patterns. +If \f2m\^\fP is omitted, \f30\fP will be used. If \f2n\^\fP +is omitted at least \f2m\^\fP occurrences will be matched. +.TP +\f3\&@\&(\fP\f2pattern-list\^\fP\f3)\fP +Matches exactly one of the given patterns. +.br +.TP +\f3!(\fP\f2pattern-list\^\fP\f3)\fP +Matches anything except one of the given patterns. +.PD +.RE +By default, each pattern, or sub-pattern will match the +longest string possible consistent with generating +the longest overall match. If more than one match is +possible, the one starting closest to the beginning +of the string will be chosen. However, for each of the above +compound patterns a \f3\-\fP can be inserted in front of the \f3(\fP +to cause the shortest match to the specified \f2pattern-list\^\fP +to be used. +.PP +When \f2pattern-list\^\fP is contained within parenthesis, +the backslash character \f3\e\fP is treated specially even +when inside a character class. All ANSI-C character escapes are +recognized and match the specified character. In addition +the following escape sequences are recognized: +.PD 0 +.RS +.TP +.B \ed +Matches any character in the \f3digit\fP class. +.TP +.B \eD +Matches any character not in the \f3digit\fP class. +.TP +.B \es +Matches any character in the \f3space\fP class. +.TP +.B \eS +Matches any character not in the \f3space\fP class. +.TP +.B \ew +Matches any character in the \f3word\fP class. +.TP +.B \eW +Matches any character not in the \f3word\fP class. +.PD +.RE +.PP +A pattern of the form +\f3%(\fP\f2pattern-pair\^\fP(s)\f3)\fP +is a sub-pattern that +can be used to match nested character expressions. +Each +.I pattern-pair\^ +is a two character sequence which cannot contain +.B & +or +.BR \(bv . +The first +.I pattern-pair\^ +specifies the starting and ending characters for the match. +Each subsequent +.I pattern-pair\^ +represents the beginning and ending characters of a nested group that +will be skipped over when counting starting and ending character matches. +The behavior is unspecified when the first character of a +.I pattern-pair\^ +is alpha-numeric +except for the following: +.PD 0 +.RS +.TP +.B D +Causes the ending character to terminate the search for this pattern without +finding a match. +.TP +.B E +Causes the ending character to be interpreted as an escape character. +.TP +.B L +Causes the ending character to be interpreted as a quote character +causing all characters to be ignored when looking for a match. +.TP +.B Q +Causes the ending character to be interpreted as a quote character +causing all characters other than any escape character to be ignored +when looking for a match. +.PD +.RE +Thus, +\f3%(\^{\^}Q"E\e\^)\fP, +matches characters starting at +.B { +until the matching +.B } +is found not counting any +.B { +or +.B } +that is inside a double quoted string or preceded by the escape character +.BR \e . +Without the +.B {\^} +this pattern matches any C language string. +.PP +Each sub-pattern in a composite pattern is numbered, +starting at 1, by the location of the \f3(\fP within +the pattern. +The sequence \f3\e\fP\f2n\^\fP, where \f2n\^\fP +is a single digit and \f3\e\fP\f2n\^\fP comes after +the \f2n\fP-th. sub-pattern, +matches the same string as the sub-pattern itself. +.PP +Finally a pattern can contain sub-patterns of the form +\f3\(ap(\fP\f2options\^\fP\f3:\fP\f2pattern-list\^\fP\f3)\fP. +where either \f2options\^\fP or \f3:\fP\f2pattern-list\^\fP +can be omitted. Unlike, the other compound patterns, +these sub-patterns are not counted in the numbered sub-patterns. +If \f2options\^\fP is present, it can consist of one or more +of the following: +.PD 0 +.RS +.TP +.B + +Enable the following options. This is the default. +.TP +.B \- +Disable the following options. +.TP +.B E +The remainder of the pattern uses extended regular expression syntax +like the +.IR egrep (1) +command. +.TP +.B F +The remainder of the pattern uses +.IR fgrep (1) +expression syntax. +.TP +.B G +The remainder of the pattern uses basic regular expression syntax +like the +.IR grep (1) +command. +.TP +.B K +The remainder of the pattern uses shell pattern syntax. +This is the default. +.TP +.B N +This is ignored. However, when it is the first letter and is +used with file name generation, and no matches occur, +the file pattern expands to the empty string. +.TP +.B i +Treat the match as case insensitive. +.TP +.B g +File the longest match (greedy). This is the default. +.TP +.B l +Left anchor the pattern. This is the default for +.B K +style patterns. +.TP +.B r +Right anchor the pattern. This is the default for +.B K +style patterns. +.PD +.RE +If both \f2options\^\fP and \f3:\fP\f2pattern-list\^\fP +are specified, then the options apply only to \f2pattern-list\^\fP. +Otherwise, these options remain in effect until they are disabled +by a subsequent \f3\(ap(\fP\f2...\^\fP\f3)\fP or at the end of +the sub-pattern containing \f3\(ap(\fP\f2...\^\fP\f3)\fP. +.SS Quoting. +Each of the +.I metacharacters\^ +listed earlier (see +.I Definitions\^ +above) +has a special meaning to the shell +.TP +.B i +Treat the match as case insensitive. +.TP +.B g +File the longest match (greedy). This is the default. +.PD +.RE +If both \f2options\^\fP and \f3:\fP\f2pattern-list\^\fP +are specified, then the options apply only to \f2pattern-list\^\fP. +Otherwise, these options remain in effect until they are disabled +by a subsequent \f3\(ap(\fP\f2...\^\fP\f3)\fP or at the end of +the sub-pattern containing \f3\(ap(\fP\f2...\^\fP\f3)\fP. +.SS Quoting. +Each of the +.I metacharacters\^ +listed earlier (see +.I Definitions\^ +above) +has a special meaning to the shell +and causes termination of a word unless quoted. +A character may be +.I quoted\^ +(i.e., made to stand for itself) +by preceding +it with a +.BR \e . +The pair +.B \enew-line +is removed. +All characters enclosed between a pair of single quote marks +(\^\f3\(fm\^\(fm\fP\^) +that is not preceded by a +.B $ +are quoted. +A single quote cannot appear within the single quotes. +A single quoted string preceded by an unquoted +.B $ +is processed as an ANSI-C string +except for the following: +.PD 0 +.TP +.B \e0 +Causes the remainder of the string to be ignored. +.TP +.B \eE +Equivalent to the escape character +(ascii +.BR 033 ), +.TP +.B \ee +Equivalent to the escape character +(ascii +.BR 033 ), +.TP +.BI \ec x +Expands to the character control-\f2x\fP. +.TP +.BI \eC[. name .] +Expands to the collating element \f2name\fP. +.PD +.PP +Inside double quote marks +(\f3"\^"\fP), +parameter and command substitution occur and +.B \e +quotes the characters +.BR \e , +.BR \*` , +\f3"\fP, +and +.BR $ . +A +.B $ +in front of a double quoted string will be ignored +in the "C" or "POSIX" locale, and may cause +the string to be replaced by a locale specific string otherwise. +The meaning of +.B "$\(**" +and +.B "$@" +is identical when not quoted or when used as a variable assignment value +or as a file name. +However, when used as a command argument, +.B +"$\(**" +is equivalent to +\f3"$1\fP\f2d\fP\f3\|$2\fP\f2d\fP\|.\|.\|.\f3"\fP, +where +.I d +is the first character of the +.SM +.B IFS +variable, whereas +.B +"$@" +is equivalent to +.B +"$1"\| +.B +"$2"\| +\&.\|.\|.\^. +Inside grave quote marks +(\f3\*`\^\*`\fP), +.B \e +quotes the characters +.BR \e , +.BR \*` , +and +.BR $ . +If the grave quotes occur within double quotes, then +.B \e +also quotes the character +\f3"\fP. +.PP +The special meaning of reserved words or aliases can be removed by quoting any +character of the reserved word. +The recognition of function names or built-in command names listed below +cannot be altered by quoting them. +.SS Arithmetic Evaluation. +The shell performs arithmetic evaluation for +arithmetic substitution, to evaluate an arithmetic command, +to evaluate an indexed array subscript, +and to evaluate arguments to +the built-in commands +.B shift\^ +and +.BR let . +Evaluations are performed using +double precision floating point +arithmetic or long double precision floating point for +systems that provide this data type. +Floating point constants follow the ANSI-C programming language +floating point conventions. +Integer constants follow the ANSI-C programming language +integer constant conventions although only single byte +character constants are recognized and character casts +are not recognized. +In addition constants can be of the form +\*(OK\f2base\f3#\^\f1\*(CK\f2n\^\fP +where +.I base\^ +is a decimal number between two and sixty-four +representing the arithmetic base +and +.I n\^ +is a number in that base. +The digits above 9 are represented +by the lower case letters, the upper case letters, +.BR @ , +and +.B _ +respectively. +For bases less than or equal to 36, upper and lower case +characters can be used interchangeably. +.PP +An arithmetic expression uses the same syntax, precedence, and +associativity of +expression as the C language. +All the C language operators +that apply to floating point quantities can be used. +In addition, the operator +.B ** +can be used for exponentiation. +It has higher precedence than multiplication as is left associative. +In addition, when the value of an arithmetic variable +or sub-expression can be represented as a long integer, +all C language integer arithmetic operations can be performed. +Variables can be referenced by name within an arithmetic expression +without using the parameter expansion syntax. +When a variable is referenced, its value is evaluated as +an arithmetic expression. +.PP +Any of the following math library functions that are in the C math library +can be used within an arithmetic expression: +.PP +.if t .RS +.B +.if n abs acos acosh asin asinh atan atan2 atanh cbrt copysign cos cosh erf erfc exp exp2 expm1 fabs fdim finite floor fma fmax fmod hypot ilogb int isinf isnan lgamma log log2 logb nearbyint nextafter nexttoward pow remainder rint round sin sinh sqrt tan tanh tgamma trunc +.if t abs acos acosh asin asinh atan atan2 atanh cbrt copysign cos cosh erf erfc exp exp2 expm1 fabs fdim finite floor fma fmax fmod hypot ilogb int isinf isnan lgamma log log2 logb nearbyint nextafter nextroward pow rint round sin sinh sqrt tan tanh tgamma trunc +.if t .RE +.PP +An internal representation of a +.I variable\^ +as a double precision floating point can be specified with the +\f3\-E\fP \*(OK\f2n\^\fP\*(CK +or +\f3\-F\fP \*(OK\f2n\^\fP\*(CK +option of the +.B typeset +special built-in command. +The +.B \-E +option causes the expansion of the value to be represented using +scientific notation when it is expanded. +The optional option argument +.I n +defines the number of significant figures. +The +.B \-F +option causes the expansion to be represented as a floating decimal number +when it is expanded. +The optional option argument +.I n +defines the number of places after the decimal point in this case. +.PP +An internal integer representation of a +.I variable\^ +can be specified with the +\f3\-i\fP \*(OK\f2n\^\fP\*(CK +option of the +.B typeset +special built-in command. +The optional option argument +.I n +specifies an arithmetic base to be used when expanding the variable. +If you do not specify an arithmetic base, +base 10 will be used. +.PP +Arithmetic evaluation is performed on the value of each +assignment to a variable with the +.BR \-E , +.BR \-F , +or +.B \-i +attribute. +Assigning a floating point number to a +variable whose type is an integer causes the fractional +part to be truncated. +.PP +.SS Prompting. +When used interactively, +the shell prompts with the value of +.SM +.B PS1 +after expanding it for parameter expansion, command substitution, and +arithmetic substitution, +before reading a command. +In addition, each single +.B ! +in the prompt is replaced by the command number. +A +.B !! +is required to place +.B ! +in the prompt. +If at any time a new-line is typed and further input is needed +to complete a command, then the secondary prompt +(i.e., the value of +.BR \s-1PS2\s+1 ) +is issued. +.SS Conditional Expressions. +A +.I "conditional expression" +is used with the +.B [[ +compound command to test attributes of files and to compare +strings. +Field splitting and file name generation are +not performed on the words between +.B [[ +and +.BR ]] . +Each expression can be constructed from one or more +of the following unary or binary expressions: +.PD 0 +.TP +\f2string\fP +True, if +.I string +is not null. +.TP +\f3\-a\fP \f2file\fP +Same as \f3\-e\fP below. +This is obsolete. +.TP +\f3\-b\fP \f2file\fP +True, if +.I file\^ +exists and is a block special file. +.TP +\f3\-c\fP \f2file\fP +True, if +.I file\^ +exists and is a character special file. +.TP +\f3\-d\fP \f2file\fP +True, if +.I file\^ +exists and is a directory. +.TP +\f3\-e\fP \f2file\fP +True, if +.I file\^ +exists. +.TP +\f3\-f\fP \f2file\fP +True, if +.I file\^ +exists and is an ordinary file. +.TP +\f3\-g\fP \f2file\fP +True, if +.I file\^ +exists and it has its setgid bit set. +.TP +\f3\-k\fP \f2file\fP +True, if +.I file\^ +exists and it has its sticky bit set. +.TP +\f3\-n\fP \f2string\fP +True, if length of +.I string\^ +is non-zero. +.TP +\f3\-o\fP \f3?\fP\f2option\fP +True, if option named +.I option\^ +is a valid option name. +.TP +\f3\-o\fP \f2option\fP +True, if option named +.I option\^ +is on. +.TP +\f3\-p\fP \f2file\fP +True, if +.I file\^ +exists and is a fifo special file or a pipe. +.TP +\f3\-r\fP \f2file\fP +True, if +.I file\^ +exists and is readable by current process. +.TP +\f3\-s\fP \f2file\fP +True, if +.I file\^ +exists and has size greater than zero. +.TP +\f3\-t\fP \f2fildes\fP +True, if file descriptor number +.I fildes\^ +is open and associated with a terminal device. +.TP +\f3\-u\fP \f2file\fP +True, if +.I file\^ +exists and it has its setuid bit set. +.TP +\f3\-w\fP \f2file\fP +True, if +.I file\^ +exists and is writable by current process. +.TP +\f3\-x\fP \f2file\fP +True, if +.I file\^ +exists and is executable by current process. +If +.I file\^ +exists and is a directory, then true if the current process +has permission to search in the directory. +.TP +\f3\-z\fP \f2string\fP +True, if length of +.I string\^ +is zero. +.TP +\f3\-L\fP \f2file\fP +True, if +.I file\^ +exists and is a symbolic link. +.TP +\f3\-h\fP \f2file\fP +True, if +.I file\^ +exists and is a symbolic link. +.TP +\f3\-N\fP \f2file\fP +True, if +.I file\^ +exists and the modification time is greater than the last access time. +.TP +\f3\-O\fP \f2file\fP +True, if +.I file\^ +exists and is owned by the effective user id of this process. +.TP +\f3\-G\fP \f2file\fP +True, if +.I file\^ +exists and its group matches the effective group id of this process. +.TP +\f3\-S\fP \f2file\fP +True, if +.I file\^ +exists and is a socket. +.TP +\f2file1\fP \f3\-nt\fP \f2file2\fP +True, if +.I file1\^ +exists and +.I file2\^ +does not, or +.I file1\^ +is newer than +.IR file2 . +.TP +\f2file1\fP \f3\-ot\fP \f2file2\fP +True, if +.I file2\^ +exists and +.I file1\^ +does not, or +.I file1\^ +is older than +.IR file2 . +.TP +\f2file1\fP \f3\-ef\fP \f2file2\fP +True, if +.I file1\^ +and +.I file2\^ +exist and refer to the same file. +.TP +\f2string\fP \f3==\fP \f2pattern\fP +True, if +.I string\^ +matches +.IR pattern . +Any part of +.I pattern\^ +can be quoted to cause it to be matched as a string. +With a successful match to a pattern, the +.B .sh.match +array variable will contain the match and sub-pattern matches. +.TP +\f2string\fP \f3=\fP \f2pattern\fP +Same as \f3==\fP above, but is obsolete. +.TP +\f2string\fP \f3!=\fP \f2pattern\fP +True, if +.I string\^ +does not match +.IR pattern . +With the +.I string\^ +matches the +.I pattern\^ +the +.B .sh.match +array variable will contain the match and sub-pattern matches. +.TP +\f2string\fP \f3=\(ap\fP \f2ere\fP +True if +.I string\^ +matches the pattern +.BI \(ap(E) ere\^ +where +.I ere\^ +is an extended regular expression. +.TP +\f2string1\fP \f3<\fP \f2string2\fP +True, if +.I string1\^ +comes before +.I string2\^ +based on ASCII value of their characters. +.TP +\f2string1\fP \f3>\fP \f2string2\fP +True, if +.I string1\^ +comes after +.I string2\^ +based on ASCII value of their characters. +.PP +The following obsolete arithmetic comparisons are also permitted: +.PD 0 +.TP +\f2exp1\fP \f3\-eq\fP \f2exp2\fP +True, if +.I exp1\^ +is equal to +.IR exp2 . +.TP +\f2exp1\fP \f3\-ne\fP \f2exp2\fP +True, if +.I exp1\^ +is not equal to +.IR exp2 . +.TP +\f2exp1\fP \f3\-lt\fP \f2exp2\fP +True, if +.I exp1\^ +is less than +.IR exp2 . +.TP +\f2exp1\fP \f3\-gt\fP \f2exp2\fP +True, if +.I exp1\^ +is greater than +.IR exp2 . +.TP +\f2exp1\fP \f3\-le\fP \f2exp2\fP +True, if +.I exp1\^ +is less than or equal to +.IR exp2 . +.TP +\f2exp1\fP \f3\-ge\fP \f2exp2\fP +True, if +.I exp1\^ +is greater than or equal to +.IR exp2 . +.PD +.PP +In each of the above expressions, if +.I file\^ +is of the form +\f3/dev/fd/\fP\f2n\fP, +where +.I n\^ +is an integer, +then the test is applied to the open file whose +descriptor number is +.IR n . +.PP +A compound expression can be constructed from these primitives by +using any of the following, listed in decreasing order of precedence. +.PD 0 +.TP +\f3(\fP\f2expression\fP\f3)\fP +True, if +.I expression\^ +is true. +Used to group expressions. +.TP +\f3!\fP \f2expression\fP +True if +.I expression\^ +is false. +.TP +\f2expression1\fP \f3&&\fP \f2expression2\fP +True, if +.I expression1\^ +and +.I expression2\^ +are both true. +.TP +\f2expression1\fP \f3\(bv\(bv\fP \f2expression2\fP +True, if either +.I expression1\^ +or +.I expression2\^ +is true. +.PD +.SS Input/Output. +Before a command is executed, its input and output +may be redirected using a special notation interpreted by the shell. +The following may appear anywhere in a simple-command +or may precede or follow a +.I command\^ +and are +.I not\^ +passed on to the invoked command. +Command substitution, parameter expansion, +and arithmetic substitution occur before +.I word\^ +or +.I digit\^ +is used except as noted below. +File name generation +occurs only if the shell is interactive and +the pattern matches a single file. +Field splitting is not performed. +.PP +In each of the following redirections, if +.I file\^ +is of the form +\f3/dev/sctp/\fP\f2host\fP\f3/\fP\f2port\fP, +\f3/dev/tcp/\fP\f2host\fP\f3/\fP\f2port\fP, +or +\f3/dev/udp/\fP\f2host\fP\f3/\fP\f2port\fP, +where +.I host\^ +is a hostname or host address, +and +.I port\^ +is a service given by name or an integer port number, +then the redirection attempts to make a +\f3tcp\fP, \f3sctp\fP or \f3udp\fP connection to the corresponding +socket. +.PP +No intervening space is allowed between the characters of redirection operators. +.TP 14 +.BI < word +Use file +.I word\^ +as standard input (file descriptor 0). +.TP +.BI > word +Use file +.I word\^ +as standard output (file descriptor 1). +If the file does not exist then it is created. +If the file exists, and the +.B noclobber +option is on, +this causes an error; +otherwise, it is truncated to zero length. +.TP +.BI >| word +Sames as +.BR > , +except that it overrides the +.B noclobber +option. +.TP +.BI >> word +Use file +.I word\^ +as standard output. +If the file exists, then output is appended to it (by first seeking to the end-of-file); +otherwise, the file is created. +.TP +.BI <> word +Open file +.I word\^ +for reading and writing +as standard input. +.TP +\f3<<\fP\*(OK\f3\-\fP\*(CK\f2word\fP +The shell input is read up to a line that is the same as +.IR word +after any quoting has been removed, +or to an end-of-file. +No parameter substitution, command substitution, arithmetic substitution or +file name generation is performed on +.IR word . +The resulting document, +called a +.IR here-document , +becomes +the standard input. +If any character of +.I word\^ +is quoted, then no interpretation +is placed upon the characters of the document; +otherwise, parameter expansion, command substitution, and arithmetic +substitution occur, +.B \enew-line +is ignored, +and +.B \e +must be used to quote the characters +.BR \e , +.BR $ , +.BR \*` . +If +.B \- +is appended to +.BR << , +then all leading tabs are stripped from +.I word\^ +and from the document. +If +.B # +is appended to +.BR << , +then leading spaces and tabs will be stripped off the first +line of the document and up to an equivalent indentation will +be stripped from the remaining lines and from +.IR word . +A tab stop is assumend to occur at every 8 columns for the +purposes of determining the indentation. +.TP +\f3<<<\fP\f2word\fP +A short form of here document in which \f2word\fP becomes the +contents of the here-document after any +parameter expansion, command substitution, and arithmetic +substitution occur. +.TP +.BI <& digit +The standard input is duplicated from file descriptor +.I digit +(see +.IR dup (2)). +Similarly for the standard output using +\f3>&\^\f2digit\fR. +.TP +.BI <& digit \- +The file descriptor given by +.I digit +is moved to standard input. +Similarly for the standard output using +\f3>&\^\f2digit\f3\-\fR. +.TP +.B <&\- +The standard input is closed. +Similarly for the standard output using +.BR >&\- . +.TP +.B <&p +The input from the co-process is moved to standard input. +.TP +.B >&p +The output to the co-process is moved to standard output. +.TP +.BI <# \^\^\^ (( expr )) +Evaluate arithmetic expression +.I expr\^ +and position file descriptor 0 +to the resulting value +bytes from the start of the file. +The variables +.B CUR +and +.B EOF +evaluate to the current offset and end-of-file offset +respectively when evaluating +.IR expr. +.TP +.BI ># \^\^\^ (( offset )) +The same as +.B <# +except applies to file descriptor 1. +.TP +.BI <# pattern +Seeks forward to the beginning of the next line containing +.IR pattern . +.TP +.BI <## pattern +The same as +.B <# +except that the portion of the file that is skipped is copied to +standard output. +.PP +If one of the above is preceded by a digit, +with no intervening space, then the +file descriptor number referred to is that specified +by the digit +(instead of the default 0 or 1). +If one of the above, other than +.BR >&\- +and the +.B ># +and +.B ># +forms, +is preceded by +.BI { varname } +with no intervening space, +then a file descriptor number > 10 +will be selected by +the shell and stored in the variable +.IR varname . +If +.B >&\- +or the any of the +.B ># +and +.B ># +forms +is preceded by +.BI { varname } +the value of +.I varname\^ +defines the file descriptor to close or position. +For example: +.PP +.RS +\fB\&.\|.\|. \|2>&1\fR +.RE +.PP +means file descriptor 2 is to be opened +for writing as a duplicate +of file descriptor 1 and +.PP +.RS +\fBexec {n}\f2fname\^\fP 2>&1\fR +.RE +.PP +first associates file descriptor 1 with file +.IR fname\^ . +It then associates file descriptor 2 with the file associated with file +descriptor 1 (i.e. +.IR fname\^ ). +If the order of redirections were reversed, file descriptor 2 would be associated +with the terminal (assuming file descriptor 1 had been) and then file descriptor +1 would be associated with file +.IR fname\^ . +.PP +If a command is followed by +.B & +and job control is not active, +then the default standard input +for the command +is the empty file +.BR /dev/null . +Otherwise, the environment for the execution of a command contains the +file descriptors of the invoking shell as modified by +input/output specifications. +.SS Environment. +The +.I environment\^ +(see +.IR environ (7)) +is a list of name-value pairs that is passed to +an executed program in the same way as a normal argument list. +The names must be +.I identifiers\^ +and the values are character strings. +The shell interacts with the environment in several ways. +On invocation, the shell scans the environment +and creates a +variable +for each name found, +giving it the corresponding value and attributes and marking it +.IR export . +Executed commands inherit the environment. +If the user modifies the values of these +variables +or creates new ones, +using the +.B export +or +.B typeset \-x +commands, they become part of the +environment. +The environment seen by any executed command is thus composed +of any name-value pairs originally inherited by the shell, +whose values may be modified by the current shell, +plus any additions +which must be noted in +.B export +or +.B typeset \-x +commands. +.PP +The environment for any +.I simple-command\^ +or function +may be augmented by prefixing it with one or more variable assignments. +A variable assignment argument is a word of the form +.IR identifier=value . +Thus: +.PP +.RS +\fB\s-1TERM\s+1=450 \|cmd \|args\fR and +.br +\fB(export \|\s-1TERM\s+1; \|\s-1TERM\s+1=450; \|cmd \|args)\fR +.RE +.PP +are equivalent (as far as the above execution of +.I cmd\^ +is concerned except for special built-in commands listed below \- +those that are +preceded with a dagger). +.PP +If the obsolete +.B \-k +option is set, +.I all\^ +variable assignment arguments are placed in the environment, +even if they occur after the command name. +The following +first prints +.B "a=b c" +and then +.BR c : +.PP +.RS +.nf +.ft B +echo \|a=b \|c +set \|\-k +echo \|a=b \|c +.ft R +.fi +.RE +This feature is intended for use with scripts written +for early versions of the shell and its use in new scripts +is strongly discouraged. +It is likely to disappear someday. +.SS Functions. +.PP +For historical reasons, there are two +ways to define functions, +the +.IB name (\^) +syntax and +the +.B function +.I name\^ +syntax, described in the +.I Commands +section above. +Shell functions are read in and stored internally. +Alias names are resolved when the function is read. +Functions are executed like commands with the arguments +passed as positional parameters. +(See +.I Execution +below.) +.PP +Functions defined by the +.B function +.I name +syntax and called by name execute in the same process as the caller and +share all files +and present working directory with the +caller. +Traps caught by the caller are reset to their default action +inside the function. +A trap condition that is not caught or ignored by the +function causes the function to terminate and the condition +to be passed on to the caller. +A trap on +.SM +.B EXIT +set inside a function +is executed +in the environment +of the caller +after the function completes. +Ordinarily, +variables are shared between the calling program +and the function. +However, +the +.B typeset +special built-in command used within a function +defines local variables whose scope includes +the current function. +They can be passed to functions that they call in the +variable assignment list the precedes the call or as arguments +passed as name references. +Errors within functions return control to the caller. +.PP +Functions defined with the +.IB name (\^) +syntax and functions defined with the +.B function +.I name +syntax that are invoked with the \f3\s+2.\s-2\fP +special built-in +are executed in the caller's +environment and share all variables +and traps with the caller. +Errors within these function executions cause the script that contains +them to abort. +.PP +The special built-in command +.B return +is used to return +from function calls. +.PP +Function names +can be listed with the +.B \-f +or +.B +f +option of the +.B typeset +special built-in command. +The text of functions, when available, will also +be listed with +.BR \-f . +Functions can be undefined with the +.B \-f +option of the +.B unset +special built-in command. +.PP +Ordinarily, functions are unset when the shell executes a shell script. +Functions that need to be defined across separate +invocations of the shell should +be placed in a directory and the +.B +.SM +FPATH +variable should contain the name of this directory. +They may also +be specified in the +.B +.SM +ENV +file. +.SS Discipline Functions. +Each variable can have zero or more discipline functions +associated with it. +The shell initially understands the discipline names \f3get\fP, +\f3set\fP, \f3append\fP, and \f3unset\fP but on most systems +others can be added at run time via the +C programming interface extension provided by the +.B builtin +built-in utility. +If the \f3get\fP discipline is defined for a variable, it is invoked +whenever the given variable is referenced. +If the variable \f3.sh.value\fP is assigned a value inside +the discipline function, the referenced variable will evaluate +to this value instead. +If the \f3set\fP discipline is defined for a variable, it is invoked +whenever the given variable is assigned a value. +If the \f3append\fP discipline is defined for a variable, it is invoked +whenever a value is appended to the given variable. +The variable \f3.sh.value\fP is given the value +of the variable before invoking the discipline, and +the variable will be assigned the value of \f3.sh.value\fP +after the discipline completes. +If \f3.sh.value\fP is unset inside the discipline, then +that value is unchanged. +If the \f3unset\fP discipline is defined for a variable, it is invoked +whenever the given variable is unset. +The variable will not be unset unless it is unset explicitly +from within this discipline function. +.PP +The variable +.B .sh.name +contains the name of the variable for which the discipline function is called, +.B .sh.subscript +is the subscript of the variable, and +.B .sh.value +will contain the value being assigned inside the +.B .set +discipline function. +For the \f3set\fP discipline, +changing +.B .sh.value +will change the value that gets assigned. +.SS Jobs. +.PP +If the +.B monitor +option of the +.B set +command is turned on, +an interactive shell associates a \fIjob\fR with each pipeline. +It keeps +a table of current jobs, printed by the +.B jobs +command, and assigns them small integer numbers. +When a job is started asynchronously with +.BR & , +the shell prints a line which looks +like: +.PP +.DT + [1] 1234 +.PP +indicating that the job which was started asynchronously was job number +1 and had one (top-level) process, whose process id was 1234. +.PP +This paragraph and the next require features that are +not in all versions of UNIX and may not apply. +If you are running a job and wish to do something else you may hit the key +\fB^Z\fR (control-Z) which sends a STOP signal to the current job. +The shell will then normally indicate that the job has been `Stopped', +and print another prompt. +You can then manipulate the state of this job, +putting it in the background with the +.B bg +command, or run some other +commands and then eventually bring the job back into the foreground with +the foreground command +.BR fg . +A \fB^Z\fR takes effect immediately and +is like an interrupt in that pending output and unread input are discarded +when it is typed. +.PP +A job being run in the background will stop if it tries to read +from the terminal. +Background jobs are normally allowed to produce output, +but this can be disabled by giving the command +.BR "stty tostop" . +If you set this +tty option, then background jobs will stop when they try to produce +output like they do when they try to read input. +.PP +There are several ways to refer to jobs in the shell. +A job can be referred to by the process id of any process of the job +or by one of the following: +.PD 0 +.TP +.BI % number +The job with the given number. +.TP +.BI % string +Any job whose command line begins with +.IR string . +.TP +.BI %? string +Any job whose command line contains +.IR string . +.TP +.BI %% +Current job. +.TP +.BI %+ +Equivalent to +.BR %% . +.TP +.BI %\- +Previous job. +.PD +.PP +The shell learns immediately whenever a process changes state. +It normally informs you whenever a job becomes blocked so that +no further progress is possible, but only just before it prints +a prompt. +This is done so that it does not otherwise disturb your work. +The +.B notify +option of the +.B set +command causes +the shell to print these job change messages +as soon as they occur. +.PP +When the +.B monitor +option is on, each background job that completes +triggers any trap set for +.BR CHLD . +.PP +When you try to leave the shell while jobs are running or stopped, you will +be warned that `You have stopped(running) jobs.' +You may use the +.B jobs +command to see what they are. +If you immediately try to +exit again, the shell will not warn you a second time, and the stopped +jobs will be terminated. +When a login shell receives a HUP signal, it sends +a HUP signal to each job that has not been disowned with the +.B disown +built-in command described below. +.SS Signals. +The \s-1INT\s+1 and \s-1QUIT\s+1 signals for an invoked +command are ignored if the command is followed by +.B & +and the +.B monitor +option is not active. +Otherwise, signals have the values +inherited by the shell from its parent +(but see also +the +.B trap +built-in command below). +.SS Execution. +Each time a command is read, the above substitutions +are carried out. +If the command name matches one +of the +.I "Special Built-in Commands\^" +listed below, +it is executed within the +current shell process. +Next, the command name is checked to see if +it matches a user defined function. +If it does, +the positional parameters are saved +and then reset to the arguments of the +.I function\^ +call. +A function is also executed in the +current shell process. +When the +.I function\^ +completes or issues a +.BR return , +the positional parameter list is restored. +For functions defined with the +.B function +.I name\^ +syntax, +any trap set on +.SM +.B EXIT +within the function is executed. +The exit value of a +.I function\^ +is the value of the last command executed. +If a command name is not a +.I "special built-in command\^" +or a user defined +.IR function , +but it is one of the built-in commands listed below, +it is executed in the current shell process. +.PP +The shell variable +.B +.SM PATH +defines the search path for +the directory containing the command. +Alternative directory names are separated by +a colon +.RB ( : ). +The default path is +.B /bin:/usr/bin: +(specifying +.BR /bin , +.BR /usr/bin , +and the current directory +in that order). +The current directory can be specified by +two or more adjacent colons, or by a colon +at the beginning or end of the path list. +If the command name contains a \f3/\fP, then the search path +is not used. +Otherwise, each directory in the path is +searched for an executable file +of the given name +that is not a directory. +If found, and if the shell +determines that there is a built-in version +of a command corresponding to a given pathname, +this built-in is invoked in the current process. +If found, and this directory is also contained in the value of the +.B +.SM FPATH +variable, +then this file is loaded into the current shell environment +as if it were the argument to the \fB.\fP command +except that only preset aliases are expanded, +and a function of the given name is executed +as described above. +If not found, and the file +.B .paths +is found, and the this file contains a line of the form +.BI FPATH= path +where +.I path\^ +names an +existing directory, and this directory contains +a file of the given name, +then this file is loaded into the current shell environment +as if it were the argument to the \fB.\fP special built-in command +and a function of the given name is executed. +Otherwise, if found, +a process is created and +an attempt is made to execute the command via +.IR exec (2). +.P +When an executable is found, the directory where it is found +in is searched for a file named +.BR .paths . +If this file is found and it contains a line of the form +.BI BUILTIN_LIB= value\^ +, then the library named by +.I value\^ +will be searched for as if it were an option argument to +.BR "builtin -f" , +and if it contains a built-in of the specified name +this will be executed instead of a command by this name. +Otherwise, if this file is found and it contains a line of the form +.IB name\^ = value\^ +in the first or second line, then the environment variable +.I name\^ +is modified by prepending the directory specified by +.I value\^ +to the directory list. +If +.I value\^ +is not an absolute directory, then it +specifies a directory relative to the directory that the +executable was found. +If the environment variable +.I name\^ +does not already exist it will be added to the environment +list for the specified command. +.P +If the file has execute permission but is not an +.B a.out +file, +it is assumed to be a file containing shell commands. +A separate shell is spawned to read it. +All non-exported variables are removed in this case. +If the shell command +file doesn't have read permission, +or if the +.I setuid +and/or +.I setgid +bits are set on the file, +then the shell executes an agent whose job it is to +set up the permissions and execute the shell with the +shell command file passed down as an open file. +A parenthesized command is executed in +a sub-shell without removing non-exported variables. +.SS Command Re-entry. +The text of the last +.B +.SM +HISTSIZE +(default 512) +commands entered from a terminal device +is saved in a +.I history +file. +The file +.B \s-1$HOME\s+1/.sh_history +is used if the +.B +.SM +HISTFILE +variable is not set +or if the file it names is not writable. +A shell can access the commands of +all +.I interactive +shells which use the same named +.SM +.BR HISTFILE . +The built-in command +.B hist\^ +is used to list or +edit a portion of this file. +The portion of the file to be edited or listed can be selected by +number or by giving the first character or +characters of the command. +A single command or range of commands can be specified. +If you do not specify an editor program as +an argument to +.B hist\^ +then the value of the variable +.SM +.B HISTEDIT +is used. +If +.SM +.B HISTEDIT +is unset, the obsolete variable +.SM +.B FCEDIT +is used. +If +.SM +.B FCEDIT +is not defined, then +.B /bin/ed +is used. +The edited command(s) is printed and re-executed upon +leaving the editor unless you quit without writing. +The +.B \-s +option +(and in obsolete versions, the editor name +.BR \-\^ ) +is used to skip the editing phase and +to re-execute the command. +In this case a substitution parameter of the form +\f2old\fP\f3=\fP\f2new\fP +can be used to modify the command before execution. +For example, with the preset alias +.BR r , +which is aliased to +.BR "\(fmhist \-s\(fm" , +typing +`\f3r bad=good c\fP' +will re-execute the most recent command which starts with the letter +.BR c , +replacing the first occurrence of the string +.B bad +with the string +.BR good . +.SS In-line Editing Options. +Normally, each command line entered from a terminal device is simply +typed followed by a \f3new-line\fP (`RETURN' or `LINE\ FEED'). +If either the +.BR emacs , +.BR gmacs , +or +.B vi +option is active, the user can edit the command line. +To be in either of these edit modes +.B set +the corresponding +option. +An editing option is automatically selected each time the +.SM +.B VISUAL +or +.SM +.B EDITOR +variable is assigned a value ending in either of these +option names. +.PP +The editing features require that the user's terminal +accept `RETURN' as carriage return without line feed +and that a space (`\ ') must overwrite the current character on +the screen. +.PP +Unless the +.B multiline +option is on, +the editing modes implement a concept where the user is looking through a +window at the current line. +The window width is the value of +.SM +.B COLUMNS +if it is defined, otherwise 80. +If the window width is too small to display the prompt and leave +at least 8 columns to enter input, the prompt is truncated from the +left. +If the line is longer than the window width minus two, a mark is +displayed at the end of the window to notify the user. +As the cursor moves and reaches the window boundaries the window will be +centered about the cursor. +The mark is a +.BR > " (<" , +.BR * ) +if the line extends on the +right (left, both) side(s) of the window. +.PP +The search commands in each edit mode provide access to the history file. +Only strings are matched, not patterns, although a leading +.B ^ +in the string restricts the match +to begin at the first character in the line. +.PP +Each of the edit modes has an operation to list the files +or commands that match a partially entered word. +When applied to the first word on the line, +or the first word after a +.BR ; , +.BR \(bv , +.BR & , +or +.BR ( , +and the word does not begin with +.B \(ap +or contain a +.BR / , +the list of aliases, functions, and executable commands +defined by the +.B +.SM PATH +variable that could match the partial word is displayed. +Otherwise, the list of files that match the given +word is displayed. +If the partially entered word does not contain any +file expansion characters, a +.B * +is appended before generating these lists. +After displaying the generated list, the input line +is redrawn. +These operations are called command name listing and file name listing, +respectively. +There are additional operations, referred to as command name +completion and file name completion, which compute the list +of matching commands or files, but instead of printing the list, +replace +the current word with a complete or partial match. +For file name completion, +if the match is unique, a +.B / +is appended if the file is a directory and a space is +appended if the file is not a directory. +Otherwise, the longest common prefix for all the matching +files replaces the word. +For command name completion, only the portion of the file names +after the last +.B / +are used to find the longest command prefix. +If only a single name matches this prefix, then the +word is replaced with the command name followed by a space. +When using a +.I tab\^ +for completion that does not yield a unique match, +a subsequent +.I tab\^ +will provide a numbered list of matching alternatives. +A specific selection can be made by entering the +selection number followed by a +.IR tab . +.SS Key Bindings. +The +.B +.SM KEYBD +trap can be used to intercept keys as they are typed +and change the characters that are actually seen by +the shell. +This trap is executed after each character +(or sequence of characters when the first character is ESC) +is entered while reading from a terminal. +The variable +.B .sh.edchar +contains the character or character sequence which +generated the trap. +Changing the value of +.B .sh.edchar +in the trap action causes the shell to behave as if the +new value were entered from the keyboard rather than +the original value. +.PP +The variable +.B .sh.edcol +is set to the input column number of the cursor at the time +of the input. +The variable +.B .sh.edmode +is set to +ESC +when in +.B vi +insert mode (see below) and is null otherwise. +By prepending +.B ${.sh.editmode} +to a value assigned to +.B .sh.edchar +it will cause the shell +to change to control mode if it is not already in this mode. +.PP +This trap is not invoked for characters entered as arguments to +editing directives, or while reading input for a character search. +.SS Emacs Editing Mode. +This mode is entered by enabling either the +.B emacs +or +.B gmacs +option. +The only difference between these two modes is the way +they handle +.BR ^T . +To edit, the user +moves the cursor to the point needing correction and +then inserts or deletes characters or words as needed. +All the editing commands are control characters or escape +sequences. +The notation for control characters is caret +.RB ( ^ ) +followed +by the character. +For example, +.B ^F +is the notation for control +.BR F . +This is entered by depressing `f' while holding down the +`CTRL' (control) key. +The `SHIFT' key is +.I not +depressed. +(The notation +.B ^? +indicates the DEL (delete) key.) +.PP +The notation for escape sequences is +.B M- +followed by a +character. +For example, +.B M-f +(pronounced Meta f) +is entered by depressing ESC +(ascii +.BR 033 ) +followed by `f'. +.RB ( M-F +would be the notation for ESC followed by `SHIFT' (capital) `F'.) +.PP +All edit commands +operate from any place on the line +(not just at the beginning). +Neither the `RETURN' nor the `LINE FEED' key is +entered after edit commands except when noted. +.PP +.PD 0 +.TP 10 +.BI ^F +Move cursor forward (right) one character. +.PP +.TP 10 +.BI M-[C +Move cursor forward (right) one character. +.PP +.TP 10 +.BI M-f +Move cursor forward one word. +(The +.B emacs +editor's idea of a word is a string of characters +consisting of only letters, digits and underscores.) +.PP +.TP 10 +.BI ^B +Move cursor backward (left) one character. +.PP +.TP 10 +.BI M-[D +Move cursor backward (left) one character. +.PP +.TP 10 +.BI M-b +Move cursor backward one word. +.PP +.TP 10 +.BI ^A +Move cursor to start of line. +.PP +.TP 10 +.BI M-[H +Move cursor to start of line. +.PP +.TP 10 +.BI ^E +Move cursor to end of line. +.PP +.TP 10 +.BI M-[Y +Move cursor to end of line. +.PP +.TP 10 +.BI ^] char +Move cursor forward to character +.I char +on current line. +.PP +.TP 10 +.BI M-^] char +Move cursor backward to character +.I char +on current line. +.PP +.TP 10 +.BI ^X^X +Interchange the cursor and mark. +.PP +.TP 10 +.I erase +(User defined erase character as defined +by the +.IR stty (1) +command, usually +.B ^H +or +.BR # .) +Delete previous character. +.PP +.TP 10 +.I lnext +(User defined literal next character as defined +by the +.IR stty (1) +command. +or +.B ^V +if not defined.) +Removes the next character's +editing features (if any). +.PP +.TP 10 +.BI ^D +Delete current character. +.PP +.TP 10 +.BI M-d +Delete current word. +.PP +.TP 10 +.BI M-^H +(Meta-backspace) Delete previous word. +.PP +.TP 10 +.BI M-h +Delete previous word. +.PP +.TP 10 +.BI M-^? +(Meta-DEL) Delete previous word (if your interrupt character is +.B ^? +(DEL, the default) then this command will not work). +.PP +.TP 10 +.BI ^T +Transpose current character with previous character +and advance the cursor +in +.I emacs +mode. +Transpose two previous characters in +.I gmacs +mode. +.PP +.TP 10 +.BI ^C +Capitalize current character. +.PP +.TP 10 +.BI M-c +Capitalize current word. +.PP +.TP 10 +.BI M-l +Change the current word to lower case. +.PP +.TP 10 +.BI ^K +Delete from the cursor to the end of the line. +If preceded by a numerical parameter whose value is less than the +current cursor position, then delete from given position +up to the cursor. +If preceded by a numerical parameter whose value is greater than the +current cursor position, then delete from cursor up to +given cursor position. +.PP +.TP 10 +.BI ^W +Kill from the cursor to the mark. +.PP +.TP 10 +.BI M-p +Push the region from the cursor to the mark on the stack. +.PP +.TP 10 +.I kill +(User defined kill character as defined +by the stty command, usually +.B ^G +or +.BR @ .) +Kill the entire current line. +If two +.I kill +characters are entered in succession, all +kill characters from then on cause a line feed +(useful when using paper terminals). +.PP +.TP 10 +.BI ^Y +Restore last item removed from line. (Yank item back to the line.) +.PP +.TP 10 +.BI ^L +Line feed and print current line. +.PP +.TP 10 +.BI M-^L +Clear the screen. +.PP +.TP 10 +.BI ^@ +(Null character) Set mark. +.PP +.TP 10 +.BI M- space +(Meta space) Set mark. +.PP +.TP 10 +.BI ^J +(New\ line) Execute the current line. +.PP +.TP 10 +.BI ^M +(Return) Execute the current line. +.PP +.TP 10 +.I eof +End-of-file character, +normally +.BR ^D , +is processed as an End-of-file only +if the current line is null. +.PP +.TP 10 +.BI ^P +Fetch previous command. +Each time +.B ^P +is entered +the previous command back in time is accessed. +Moves back one line when not on the first line of a multi-line command. +.PP +.TP 10 +.BI M-[A +Equivalent to +.BR ^P. +.PP +.TP 10 +.BI M-< +Fetch the least recent (oldest) history line. +.PP +.TP 10 +.BI M-> +Fetch the most recent (youngest) history line. +.PP +.TP 10 +.BI ^N +Fetch next command line. +Each time +.B ^N +is entered +the next command line forward in time is accessed. +.PP +.TP 10 +.BI M-[B +Equivalent to +.BR ^N. +.PP +.TP 10 +.BI ^R string +Reverse search history for a previous command line containing +.IR string . +If a parameter of zero is given, the search is forward. +.I String +is terminated by a `RETURN' or `NEW\ LINE'. +If string is preceded by a +.BR ^ , +the matched line must begin with +.IR string . +If +.I string +is omitted, +then the next command line containing the most recent +.I string +is accessed. +In this case a parameter of zero +reverses the direction of the search. +.PP +.TP 10 +.B ^O +Operate \- Execute the current line and fetch +the next line relative to current line from the +history file. +.PP +.TP 10 +.BI M- digits +(Escape) Define numeric parameter, the digits +are taken as a parameter to the next command. +The commands that accept a parameter are +.BR ^F , +.BR ^B , +.IR erase , +.BR ^C , +.BR ^D , +.BR ^K , +.BR ^R , +.BR ^P , +.BR ^N , +.BR ^] , +.BR M-. , +.BR M-^] , +.BR M-_ , +.BR M-= , +.BR M-b , +.BR M-c , +.BR M-d , +.BR M-f , +.BR M-h , +.B M-l +and +.BR M-^H . +.PP +.TP 10 +.BI M- letter +Soft-key \- Your alias list is searched for an +alias by the name +.BI _ letter +and if an alias of this name is defined, its +value will be inserted on the input queue. +The +.I letter +must not be one of the above meta-functions. +.PP +.TP 10 +.BI M-[ letter +Soft-key \- Your alias list is searched for an +alias by the name +.BI _\&_ letter +and if an alias of this name is defined, its +value will be inserted on the input queue. +The can be used to program functions keys on many terminals. +.PP +.TP 10 +.B M-. +The last word of the previous command is inserted +on the line. +If preceded by a numeric parameter, the value +of this parameter determines which word to insert rather than +the last word. +.PP +.TP 10 +.B M-_ +Same as +.BR M-. . +.PP +.TP 10 +.B M-* +Attempt file name generation on the current word. +An asterisk is appended if the word doesn't match any file +or contain any special +pattern characters. +.PP +.TP 10 +.B M-ESC +Command or file name completion as described above. +.PP +.TP 10 +.BI ^I " tab" +Attempts command or file name completion as described above. +If a partial completion occurs, repeating this will +behave as if +.B M-= +were entered. +If no match is found or entered after +.IR space\^ , +a +.I tab\^ +is inserted. +.PP +.TP 10 +.B M-= +If not preceded by a numeric parameter, +it generates the list of matching commands or +file names as described above. +Otherwise, the word under the cursor is replaced by +the item corresponding to the value of the numeric parameter +from the most recently generated command or file list. +If the cursor is not on a word, it is inserted instead. +.PP +.TP 10 +.BI ^U +Multiply parameter of next command by 4. +.PP +.TP 10 +.BI \e +Escape next character. +Editing characters, the user's erase, kill and +interrupt (normally +.BR ^? ) +characters +may be entered +in a command line or in a search string if preceded by a +.BR \e . +The +.B \e +removes the next character's +editing features (if any). +.PP +.TP 10 +.B M-^V +Display version of the shell. +.PP +.TP 10 +.B M-# +If the line does not begin with a +.BR # , +a +.B # +is inserted +at the beginning of the line +and after each new-line, +and the line is entered. +This causes a comment to be inserted in the history file. +If the line begins with a +.BR # , +the +.B # +is deleted and one +.B # +after each new-line is also deleted. +.PD +.SS Vi Editing Mode. +There are two typing modes. +Initially, when you enter a command you are in the +.I input\^ +mode. +To edit, the user enters +.I control\^ +mode by typing ESC +.RB ( 033 ) +and moves the cursor to the point needing correction and +then inserts or deletes characters or words as needed. +Most control commands accept an optional repeat +.I count +prior to the command. +.PP +When in +.B vi +mode on most systems, +canonical processing is initially enabled and the +command will be echoed again if the speed is 1200 baud or greater and it +contains any control characters or less than one second has elapsed +since the prompt was printed. +The ESC character terminates canonical processing for the remainder of the command +and the user can then modify the command line. +This scheme has the advantages of canonical processing with the type-ahead +echoing of raw mode. +.PP +If the option +.B viraw +is also set, the terminal will always have canonical processing +disabled. +This mode is implicit for systems that do not support two +alternate end of line delimiters, +and may be helpful for certain terminals. +.SS "\ \ \ \ \ Input Edit Commands" +.PP +.RS +By default the editor is in input mode. +.PD 0 +.TP 10 +.I erase +(User defined erase character as defined +by the stty command, usually +.B ^H +or +.BR # .) +Delete previous character. +.TP 10 +.BI ^W +Delete the previous blank separated word. +On some systems the \f3viraw\fP option +may be required for this to work. +.TP 10 +.I eof +As the first character of the line causes +the shell to terminate unless the \f3ignoreeof\fP +option is set. +Otherwise this character is ignored. +.TP 10 +.I lnext +(User defined literal next character as defined +by the +.IR stty (1) +or +.B ^V +if not defined.) +Removes the next character's +editing features (if any). +On some systems the \f3viraw\fP option +may be required for this to work. +.TP 10 +.BI \e +Escape the next +.I erase +or +.I kill +character. +.TP 10 +.BI ^I " tab" +Attempts command or file name completion as described above +and returns to input mode. +If a partial completion occurs, repeating this will +behave as if +.B = +were entered from control mode. +If no match is found or entered after +.IR space\^ , +a +.I tab\^ +is inserted. +.RE +.SS "\ \ \ \ \ Motion Edit Commands" +.RS +These commands will move the cursor. +.TP 10 +[\f2count\fP]\f3l\fP +Cursor forward (right) one character. +.TP 10 +[\f2count\fP]\f3[C\fP +Cursor forward (right) one character. +.TP 10 +[\f2count\fP]\f3w\fP +Cursor forward one alpha-numeric word. +.TP 10 +[\f2count\fP]\f3W\fP +Cursor to the beginning of the next word that follows a blank. +.TP 10 +[\f2count\fP]\f3e\fP +Cursor to end of word. +.TP 10 +[\f2count\fP]\f3E\fP +Cursor to end of the current blank delimited word. +.TP 10 +[\f2count\fP]\f3h\fP +Cursor backward (left) one character. +.TP 10 +[\f2count\fP]\f3[D\fP +Cursor backward (left) one character. +.TP 10 +[\f2count\fP]\f3b\fP +Cursor backward one word. +.TP 10 +[\f2count\fP]\f3B\fP +Cursor to preceding blank separated word. +.TP 10 +[\f2count\fP]\f3\(bv\fP +Cursor to column +.IR count . +.TP 10 +[\f2count\fP]\f3f\fP\f2c\fP +Find the next character \fIc\fP in the current line. +.TP 10 +[\f2count\fP]\f3F\fP\f2c\fP +Find the previous character \fIc\fP in the current line. +.TP 10 +[\f2count\fP]\f3t\fP\f2c\fP +Equivalent to +.B f +followed by +.BR h . +.TP 10 +[\f2count\fP]\f3T\fP\f2c\fP +Equivalent to +.B F +followed by +.BR l . +.TP 10 +[\f2count\fP]\f3;\fP +Repeats +.I count +times, +the last single character find command, +.BR f , +.BR F , +.BR t , +or +.BR T . +.TP 10 +[\f2count\fP]\f3,\fP +Reverses the last single character find command +.I count +times. +.TP 10 +.B 0 +Cursor to start of line. +.TP 10 +.B ^ +Cursor to start of line. +.TP 10 +.B [H +Cursor to first non-blank character in line. +.TP 10 +.B $ +Cursor to end of line. +.TP 10 +.B [Y +Cursor to end of line. +.TP 10 +.B % +Moves to balancing +.BR ( , +.BR ) , +.BR { , +.BR } , +.BR [ , +or +.BR ] . +If cursor is not on one of the above characters, +the remainder of the line is searched for the first +occurrence of one of the above characters first. +.RE +.SS "\ \ \ \ \ Search Edit Commands" +.RS +These commands access your command history. +.TP 10 +[\f2count\fP]\f3k\fP +Fetch previous command. +Each time +.B k +is entered +the previous command back in time is accessed. +.TP 10 +[\f2count\fP]\f3\-\fP +Equivalent to +.BR k . +.TP 10 +[\f2count\fP]\f3[A\fP +Equivalent to +.BR k . +.TP 10 +[\f2count\fP]\f3j\fP +Fetch next command. +Each time +.B j +is entered +the next command forward in time is accessed. +.TP 10 +[\f2count\fP]\f3+\fP +Equivalent to +.BR j . +.TP 10 +[\f2count\fP]\f3[B\fP +Equivalent to +.BR j . +.TP 10 +[\f2count\fP]\f3G\fP +The command number +.I count +is fetched. +The default is the least recent history command. +.TP 10 +.BI / string +Search backward through history for a previous command containing +.IR string . +.I String +is terminated by a `RETURN' or `NEW\ LINE'. +If string is preceded by a +.BR ^ , +the matched line must begin with +.IR string . +If \fIstring\fP is null, the previous string will be used. +.TP 10 +.BI ? string +Same as +.B / +except that search will be in the forward direction. +.TP 10 +.B n +Search for next match of the last pattern to +.B / +or +.B ? +commands. +.TP 10 +.B N +Search for next match of the last pattern to +.B / +or +.BR ? , +but in reverse direction. +.RE +.SS "\ \ \ \ \ Text Modification Edit Commands" +.RS +These commands will modify the line. +.TP 10 +.B a +Enter input mode and enter text after the current character. +.TP 10 +.B A +Append text to the end of the line. +Equivalent to +.BR $a . +.TP 10 +[\f2count\fP]\f3c\fP\f2motion\fP +.TP 10 +\f3c\fP[\f2count\fP]\f2motion\fP +Delete current character through the character that +.I motion +would move the cursor to and enter input mode. +If \fImotion\fP is +.BR c , +the entire line will be deleted and +input mode entered. +.TP 10 +.B C +Delete the current character through the end of line and enter input mode. +Equivalent to +.BR c$ . +.TP 10 +.B S +Equivalent to +.BR cc . +.TP 10 +[\f2count\fP]\f3s\fP +Replace characters under the cursor in input mode. +.TP 10 +.B D +Delete the current character through the end of line. +Equivalent to +.BR d$ . +.TP 10 +[\f2count\fP]\f3d\fP\f2motion\fP +.TP 10 +\f3d\fP[\f2count\fP]\f2motion\fP +Delete current character through the character that +.I motion +would move to. +If \fImotion\fP is +.B d , +the entire line will be deleted. +.TP 10 +.B i +Enter input mode and insert text before the current character. +.TP 10 +.B I +Insert text before the beginning of the line. +Equivalent to +.BR 0i . +.TP 10 +[\f2count\fP]\f3P\fP +Place the previous text modification before the cursor. +.TP 10 +[\f2count\fP]\f3p\fP +Place the previous text modification after the cursor. +.TP 10 +.B R +Enter input mode and +replace characters on the screen with characters you type overlay fashion. +.TP 10 +[\f2count\fP]\f3r\fP\f2c\fP +Replace the +.I count +character(s) starting at the current cursor position with +.IR c , +and advance the cursor. +.TP 10 +[\f2count\fP]\f3x\fP +Delete current character. +.TP 10 +[\f2count\fP]\f3X\fP +Delete preceding character. +.TP 10 +[\f2count\fP]\f3.\fP +Repeat the previous text modification command. +.TP 10 +[\f2count\fP]\f3\(ap\fP +Invert the case of the +.I count +character(s) starting at the current cursor position and advance the cursor. +.TP 10 +[\f2count\fP]\f3_\fP +Causes the +.I count\^ +word of the previous command to be appended and +input mode entered. +The last word is used +if +.I count\^ +is omitted. +.TP 10 +.B * +Causes an +.B * +to be appended to the current word and file name generation attempted. +If no match is found, +it rings the bell. +Otherwise, the word is replaced +by the matching pattern and input mode is entered. +.TP 10 +.B \e +Command or file name completion as described above. +.RE +.SS "\ \ \ \ \ Other Edit Commands" +.RS +Miscellaneous commands. +.TP 10 +[\f2count\fP]\f3y\fP\f2motion\fP +.TP 10 +\f3y\fP[\f2count\fP]\f2motion\fP +Yank current character through character that +.I motion +would move the cursor to and puts them into the delete buffer. +The text and cursor are unchanged. +.TP 10 +.B yy +Yanks the entire line. +.TP 10 +.B Y +Yanks from current position to end of line. +Equivalent to +.BR y$ . +.TP 10 +.B u +Undo the last text modifying command. +.TP 10 +.B U +Undo all the text modifying commands performed on the line. +.TP 10 +[\f2count\fP]\f3v\fP +Returns the command +.BI "hist \-e ${\s-1VISUAL\s+1:\-${\s-1EDITOR\s+1:\-vi}}" " count" +in the input buffer. +If +.I count\^ +is omitted, then the current line is used. +.TP 10 +.BI ^L +Line feed and print current line. +Has effect only in control mode. +.TP 10 +.BI ^J +(New\ line) Execute the current line, regardless of mode. +.TP 10 +.BI ^M +(Return) Execute the current line, regardless of mode. +.TP 10 +.B # +If the first character of the command is a +.BR # , +then this command deletes this +.B # +and each +.B # +that follows a newline. +Otherwise, +sends the line after +inserting a +.B # +in front of each line in the command. +Useful for causing the current line to be +inserted in the history as a comment and +uncommenting previously commented commands +in the history file. +.TP 10 +[\f2count\fP]\f3=\fP +If \f2count\fP is not specified, +it generates the list of matching commands or +file names as described above. +Otherwise, the word under the the cursor is replaced by the +\f2count\fP item from the most recently generated command or file list. +If the cursor is not on a word, it is inserted instead. +.TP 10 +.BI @ letter +Your alias list is searched for an +alias by the name +.BI _ letter +and if an alias of this name is defined, its +value will be inserted on the input queue for processing. +.TP 10 +.BI ^V +Display version of the shell. +.RE +.PD +.SS Built-in Commands. +The following simple-commands are executed in the shell process. +Input/Output redirection is permitted. +Unless otherwise indicated, the output is written on file descriptor 1 +and the exit status, when there is no syntax error, is zero. +Except for +.BR : , +.BR true , +.BR false , +.BR echo , +.BR newgrp , +and +.BR login , +all built-in commands accept +.B \-\- +to indicate end of options. +They also interpret the option +.B \-\-man +as a request to display the man page onto +standard error and +.B \-? +as a help request which prints a +.I usage\^ +message +on standard error. +Commands that are preceded by one or two \(dg symbols +are special built-in commands and +are treated specially in the following ways: +.PD 0 +.TP +1. +Variable assignment lists preceding the command +remain in effect when the command completes. +.TP +2. +I/O redirections are processed after variable assignments. +.TP +3. +Errors +cause a script +that contains them to abort. +.TP +4. +They are not valid function names. +.TP +5. +Words +following a command preceded by \(dg\(dg +that are in the format of a variable assignment +are expanded with the same rules as a variable assignment. +This means that +tilde substitution is performed after the +.B = +sign and field splitting and file name generation are not +performed. +.PD +.TP +\(dg \f3:\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +The command only expands parameters. +.br +.ne 2 +.TP +\(dg \f3\|. \f2name\^\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +If +.I name\^ +is a function defined with the +.B function +.I name\^ +reserved word syntax, +the function is executed in the current environment +(as if it had been defined with the +.IB name () +syntax.) +Otherwise if +.I name\^ +refers to a file, the +file is read in its entirety and the commands are +executed in the current shell environment. +The search path +specified by +.B +.SM PATH +is used to find the directory containing the file. +If any arguments +.I arg\^ +are given, +they become the positional parameters while processing +the +.B . +command and the original positional parameters are restored upon completion. +Otherwise the positional parameters are unchanged. +The exit status is the exit status of the last command executed. +.TP +\(dg\(dg \f3alias\fP \*(OK \f3\-ptx\fP \*(CK \*(OK \f2name\fP\*(OK \f3=\fP\f2value\^\fP \*(CK \*(CK .\|.\|. +.B alias\^ +with no arguments prints the list of aliases +in the form +.I name=value\^ +on standard output. +The +.B \-p +option +causes the word +.B alias +to be inserted before each one. +When one or more arguments are given, +an +.I alias\^ +is defined +for each +.I name\^ +whose +.I value\^ +is given. +A trailing space in +.I value\^ +causes the next word to be checked for +alias substitution. +The obsolete +.B \-t +option is used to set and list tracked aliases. +The value of a tracked alias is the full pathname +corresponding to the given +.IR name . +The value becomes undefined when the value of +.SM +.B PATH +is reset but the alias remains tracked. +Without the +.B \-t +option, +for each +.I name\^ +in the argument list +for which no +.I value\^ +is given, the name +and value of the alias is printed. +The obsolete +.B \-x +option has no effect. +The exit status is non-zero if a +.I name\^ +is given, but no value, and no alias has been defined for the +.IR name\^ . +.TP +\f3bg\fP \*(OK \f2job\^\fP.\|.\|. \*(CK +This command is only on systems that support job control. +Puts each specified +.I job\^ +into the background. +The current job is put in the background +if +.I job\^ +is not specified. +See +.I Jobs +for a description of the format of +.IR job . +.TP +\(dg \f3break\fP \*(OK \f2n\^\fP \*(CK +Exit from the enclosing +.BR for\^ , +.BR while\^ , +.BR until\^ , +or +.B select\^ +loop, if any. +If +.I n\^ +is specified, then break +.I n\^ +levels. +.TP +\f3builtin\fP \*(OK \f3\-ds\fP \*(CK \*(OK \f3\-f\fP \f2file\^\fP \*(CK \*(OK \f2name\^\fP .\|.\|. \*(CK +If +.I name\^ +is not specified, +and no +.B \-f +option is specified, +the built-ins are printed on standard output. +The +.B \-s +option prints only the special built-ins. +Otherwise, each +.I name\^ +represents the pathname whose basename is the name of the built-in. +The entry point function name is determined by prepending +.B b_ +to the built-in name. +The ISO C/C++ prototype is +\f3b_\fP\f2mycommand\fP\f3(int\fP \f2argc\fP, \f3char *\fP\f2argv\fP\f3[]\fP, \f3void *\fP\f2context\fP\f3)\fP +for the builtin command +.I mycommand\^ +where +.I argv\^ +is array an of +.I argc\^ +elements and context is an optional pointer to a +.B Shell_t +structure as described in +.BR . +.sp .5 +Special built-ins cannot be bound to a pathname or deleted. +The +.B \-d +option deletes each of the given built-ins. +On systems that support dynamic loading, the +.B \-f +option names a shared library containing the code for built-ins. +The shared library prefix and/or suffix, which depend on the system, +can be omitted. +Once a library is loaded, its symbols become available +for subsequent invocations of +.BR builtin . +Multiple libraries can be specified with separate invocations +of the +.B builtin +command. +Libraries are searched in the reverse order in which they are specified. +When a library is loaded, it looks for a function in the library +whose name is +.B lib_init() +and invokes this function with an argument of +.BR 0 . +.TP +.PD 0 +\f3cd\fP \*(OK \f3\-LP\fP \*(CK \*(OK \f2arg\^\fP \*(CK +.TP +\f3cd\fP \*(OK \f3\-LP\fP \*(CK \f2old\^\fP \f2new\^\fP +.PD +This command can be in either of two forms. +In the first form it +changes the current directory to +.IR arg . +If +.I arg\^ +is +.B \- +the directory is changed to the previous +directory. +The shell +variable +.B +.SM HOME +is the default +.IR arg . +The variable +.SM +.B PWD +is set to the current directory. +The shell variable +.B +.SM CDPATH +defines the search path for +the directory containing +.IR arg . +Alternative directory names are separated by +a colon +.RB ( : ). +The default path is +.B +(specifying the current directory). +Note that the current directory is specified by a null path name, +which can appear immediately after the equal sign +or between the colon delimiters anywhere else in the path list. +If +.I arg +begins with a \f3/\fP then the search path +is not used. +Otherwise, each directory in the path is +searched for +.IR arg . +.sp .5 +The second form of +.B cd +substitutes the string +.I new +for the string +.I old +in the current directory name, +.SM +.BR PWD , +and tries to change to this new directory. +.sp .5 +By default, symbolic link names are treated literally when +finding the directory name. +This is equivalent to the +.B \-L +option. +The +.B \-P +option causes +symbolic links to be resolved when determining the directory. +The last instance of +.B \-L +or +.B \-P +on the command line +determines which method is used. +.sp .5 +The +.B cd\^ +command may not be executed by +.if \nZ=0 .B rsh\^. +.if \nZ=1 .B rksh\^. +.if \nZ=1 .B rksh93\^. +.TP +\f3command\fP \*(OK \f3\-pvxV\fP \*(CK \f2name\^\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +Without the +.B \-v +or +.B \-V +options, +.B command +executes +.I name\^ +with the arguments given by +.IR arg . +The +.B \-p +option causes +a default path to be searched +rather than the one defined by the value of +.SM +.BR PATH . +Functions will not be searched for when finding +.IR name . +In addition, if +.I name\^ +refers to a special built-in, +none of the special properties associated with the leading +daggers will be honored. +(For example, the predefined alias +.B "redirect=\(fmcommand exec\(fm" +prevents a script from terminating when an invalid +redirection is given.) +With the +.B \-x +option, +if command execution would result in a failure because +there are too many arguments, errno +.SM +.BR E2BIG , +the shell will invoke command +.I name\^ +multiple times with a subset of the arguments on each invocation. +Arguments that occur prior to the first word that +expands to multiple arguments and after the last word +that expands to multiple arguments will be passed on each invocation. +The exit status will be the maximum invocation exit status. +With the +.B \-v +option, +.B command +is equivalent to the built-in +.B whence +command described below. +The +.B \-V +option causes +.B command +to act like +.BR "whence \-v" . +.TP +\(dg \f3continue\fP \*(OK \f2n\^\fP \*(CK +Resume the next iteration of the enclosing +.BR for\^ , +.BR while\^ , +.BR until\^ , +or +.B select\^ +loop. +If +.I n\^ +is specified, then resume at the +.IR n -th +enclosing loop. +.TP +\f3disown\fP \*(OK \f2job\^\fP.\|.\|. \*(CK +Causes the shell not to send a HUP signal to +each given +.IR job , +or all active jobs if +.I job +is omitted, +when a login shell terminates. +.TP +\f3echo\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +When the first +.I arg\^ +does not begin with a \-, and +none of the arguments contain a \e, +then +.B echo +prints each of its arguments separated by a space +and terminated by a new-line. +Otherwise, the behavior of +.B echo +is system dependent +and +.B print +or +.B printf +described below should be used. +See +.IR echo (1) +for usage and description. +.TP +\(dg \f3eval\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +The arguments are read as input +to the shell +and the resulting command(s) executed. +.TP +\(dg \f3exec\fP \*(OK \f3\-c\fP \*(CK \*(OK \f3\-a\fP \f2name\^\fP \*(CK \*(OK \f2arg\^\fP .\|.\|. \*(CK +If +.I arg\^ +is given, +the command specified by +the arguments is executed in place of this shell +without creating a new process. +The +.B \-c +option causes the environment to be cleared before applying +variable assignments associated with the +.B exec +invocation. +The +.B \-a +option +causes +.I name\^ +rather than the first +.IR arg , +to become +.B argv[0] +for the new process. +Input/output arguments may appear and +affect the current process. +If +.I arg\^ +is not given, +the effect of this command is to +modify file descriptors +as prescribed by the input/output redirection list. +In this case, +any file descriptor numbers greater than 2 that are +opened with this mechanism are closed when invoking +another program. +.TP +\(dg \f3exit\fP \*(OK \f2n\^\fP \*(CK +Causes the shell to exit +with the exit status specified by +.IR n . +The value will be the least significant 8 bits of the specified status. +If +.I n\^ +is omitted, then the exit status is that of the last command executed. +An end-of-file will also cause the shell to exit +except for a +shell which has the +.B ignoreeof +option (see +.B set +below) turned on. +.TP +\(dg\(dg \f3export\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2name\^\fP\*(OK\f3=\fP\f2value\^\fP\*(CK \*(CK .\|.\|. +If +.I name\^ +is not given, +the names and values of each variable with +the export attribute are printed with the values +quoted in a manner that allows them to be re-input. +The +.B \-p +option +causes the word +.B export +to be inserted before each one. +Otherwise, the given +.IR name s +are marked for automatic +export to the +.I environment\^ +of subsequently-executed commands. +.TP +\f3false\fP +Does nothing, and exits 1. Used with +.B until +for infinite loops. +.TP +\f3fg\fP \*(OK \f2job\^\fP.\|.\|. \*(CK +This command is only on systems that support job control. +Each +.I job\^ +specified is brought to the foreground and waited for in +the specified order. +Otherwise, the current job is +brought into the foreground. +See +.I Jobs +for a description of the format of +.IR job . +.TP +\f3getconf\fP \*(OK \f2name\^\fP \*(OK \f2pathname\^\fP \*(CK \*(CK +Prints the current value of the configuration parameter given by +.IR name . +The configuration parameters are defined by the IEEE POSIX 1003.1 +and IEEE POSIX 1003.2 standards. +(See +.IR pathconf (2) +and +.IR sysconf (2).) +The +.I pathname +argument is required for parameters whose value depends on +the location in the file system. +If no arguments are given, +.B getconf +prints the names and values of the current configuration +parameters. +The pathname +.B / +is used for each of the parameters that requires +.IR pathname . +.TP +\f3getopts\fP \*(OK \f3\ -a\fP \f2name\^\fP \*(CK \f2optstring vname\^\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +Checks +.I arg +for legal options. +If +.I arg +is omitted, +the positional parameters are used. +An option argument begins with a +.B + +or a +.BR \- . +An option not beginning with +.B + +or +.B \- +or the argument +.B \-\|\- +ends the options. +Options beginning with +.B + +are only recognized when +.I optstring\^ +begins with a +.BR + . +.I optstring\^ +contains the letters that +.B getopts +recognizes. +If a letter is followed by a +.BR : , +that option is expected to have an argument. +The options can be separated from the argument by blanks. +The option +.B \-? +causes +.B getopts +to generate a usage message on standard error. +The +.B \-a +argument can be used to specify the name to use for the +usage message, which defaults to +.BR $0 . +.sp .5 +.B +getopts +places the next option letter it finds inside variable +.I vname\^ +each time it is invoked. +The option letter will be prepended with a +.B + +when +.I arg +begins with a +.BR + . +The index of the next +.I arg +is stored in +.SM +.BR OPTIND . +The option argument, +if any, +gets stored in +.SM +.BR OPTARG . +.sp .5 +A leading +.B : +in +.I optstring +causes +.B getopts +to store the letter of an invalid +option in +.SM +.BR OPTARG , +and to set +.I vname +to +.B ? +for an unknown option and to +.B : +when a required option argument is missing. +Otherwise, +.B getopts +prints an error message. +The exit status is non-zero when there are no more options. +.sp .5 +There is no way to specify any of the options +.BR : , +.BR + , +.BR \- , +.BR ? , +.BR [ , +and +.BR ] . +The option +.B # +can only be specified as the first option. +.TP +.PD 0 +\f3hist\fP \*(OK \f3\-e\fP \f2ename\^\fP \ \*(CK \*(OK \f3\-nlr\^\fP \*(CK \*(OK \f2first\^\fP \*(OK \f2last\^\fP \*(CK \*(CK +.TP +\f3hist \-s \fP \*(OK \f2old\fP\f3\=\fP\f2new\^\fP \*(CK \*(OK \f2command\^\fP \*(CK +.PD +In the first form, +a range of commands from +.I first\^ +to +.I last\^ +is selected from the last +.SM +.B HISTSIZE +commands that were typed at the terminal. +The arguments +.I first\^ +and +.I last\^ +may be specified as a number or as a string. +A string is used to locate the most recent command starting with +the given string. +A negative number is used as an offset to the current command number. +If the +.B \-l +option +is selected, +the commands are listed on standard output. +Otherwise, the editor program +.I ename\^ +is invoked on a file containing these +keyboard commands. +If +.I ename\^ +is not supplied, then the value of the variable +.SM +.B HISTEDIT +is used. +If +.SM +.B HISTEDIT +is not set, then +.SM +.B FCEDIT +(default +.BR /bin/ed\^ ) +is used as the editor. +When editing is complete, the edited command(s) +is executed if the changes have been saved. +If +.I last\^ +is not specified, +then it will be set to +.IR first . +If +.I first\^ +is not specified, +the default is the previous command +for editing and \-16 for listing. +The option +.B \-r +reverses the order of the commands and +the option +.B \-n +suppresses command numbers when listing. +In the second form, +.I command\^ +is interpreted as +.I first\^ +described above +and defaults to the last command executed. +The resulting command is executed +after the optional substitution +\f2old\^\fP\f3=\fP\f2new\^\fP +is performed. +.TP +\f3jobs\fP \*(OK \f3\-lnp\^\fP \*(CK \*(OK \f2job\^\fP \.\|.\|. \*(CK +Lists information about each given job; or all active jobs if +.I job +is omitted. +The +.B \-l +option lists process ids in addition to the normal information. +The +.B \-n +option only displays jobs that have stopped or exited since last +notified. +The +.B \-p +option causes only the process group to be listed. +See +.I Jobs +for a description of the format of +.IR job . +.TP +.PD 0 +\f3kill\fP \*(OK \f3\-s\fP \f2signame\^\fP \*(CK \f2job\^\fP .\|.\|. +.TP +.PD 0 +\f3kill\fP \*(OK \f3\-n\fP \f2signum\^\fP \*(CK \f2job\^\fP .\|.\|. +.TP +\f3kill\fP \f3\-l\fP \*(OK \f2sig\^\fP .\|.\|. \*(CK +.PD +Sends either the TERM (terminate) signal or the +specified signal to the specified jobs or processes. +Signals are either given by number with the +.B \-n +option or by name with the +.B \-s +option +(as given in +.BR , +stripped of the prefix ``SIG'' with +the exception that SIGCLD is named CHLD). +For backward compatibility, the +.B n +and +.B s +can be omitted and the number or name placed immediately +after the +.BR \- . +If the signal being sent is TERM (terminate) or HUP (hangup), +then the job or process will be sent a CONT (continue) signal +if it is stopped. +The argument +.I job\^ +can be the process id of a process that is not a member of one of the +active jobs. +See +.I Jobs +for a description of the format of +.IR job . +In the third form, +.BR "kill \-l" , +if +.I sig\^ +is not specified, +the signal names are listed. +Otherwise, for each +.I sig\^ +that is a name, the corresponding signal number is listed. +For each +.I sig\^ +that is a number, the signal name corresponding to the +least significant 8 bits of +.I sig\^ +is listed. +.TP +\f3let\fP \f2arg\^\fP .\|.\|. +Each +.I arg +is a separate +.I "arithmetic expression" +to be evaluated. +See +.I "Arithmetic Evaluation" +above, for a description of arithmetic expression evaluation. +.sp .5 +The exit status is +0 if the value of the last expression +is non-zero, and 1 otherwise. +.TP +\(dg \f3newgrp\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +Equivalent to +.BI "exec /bin/newgrp" " arg\^" +\&.\|.\|.\^. +.TP +\f3print\fP \*(OK \f3\-Renprs\^\fP \*(CK \*(OK \f3\-u\fP \f2unit\^\fP\*(CK \*(OK \f3\-f\fP \f2format\^\fP \*(CK \*(OK \f2arg\^\fP .\|.\|. \*(CK +With no options or with option +.B \- +or +.BR \-\|\- , +each +.I arg +is printed +on standard output. +The +.B \-f +option causes the arguments to be printed as +described by +.BR printf . +In this case, any +.BR e , +.BR n , +.BR r , +.B R +options are ignored. +Otherwise, +unless the +.B \-R +or +.BR \-r , +are specified, the following +escape conventions will be applied: +.RS +.PD 0 +.TP +.B \ea +The alert character (ascii +.BR 07 ). +.TP +.B \eb +The backspace character (ascii +.BR 010 ). +.TP +.B \ec +Causes +.B print +to end without processing more arguments and +not adding a new-line. +.TP +.B \ef +The formfeed character (ascii +.BR 014 ). +.TP +.B \en +The new-line character (ascii +.BR 012 ). +.TP +.B \er +The carriage return character (ascii +.BR 015 ). +.TP +.B \et +The tab character (ascii +.BR 011 ). +.TP +.B \ev +The vertical tab character (ascii +.BR 013 ). +.TP +.B \eE +The escape character (ascii +.BR 033 ). +.TP +.B \e\e +The backslash character \e. +.TP +.BI \e0 x +The character defined by the 1, 2, or 3-digit octal +string given by \fIx\fP. +.PD +.PP +The +.B \-R +option will print all subsequent arguments and options +other than +.BR \-n . +The +.B \-e +causes the above escape conventions to be applied +This is the default behavior. +It reverses the effect of an earlier +.BR \-r . +The +.B \-p +option causes the +arguments to be written onto the pipe +of the process spawned with +.B \(bv& +instead of standard output. +The +.B \-s +option causes the +arguments to be written onto the history file +instead of standard output. +The +.B \-u +option can be used to specify a one digit +file descriptor unit number +.I unit\^ +on which the +output will be placed. +The default is 1. +If the option +.B \-n +is used, no +.B new-line\^ +is added to the output. +.RE +.TP +\f3printf\fP \f2format\^\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK +The arguments +.I arg\^ +are printed on standard output +in accordance with the ANSI-C +formatting rules associated with the format string +.IR format . +If the number of arguments exceeds the number of +format specifications, the +.B format\^ +string is reused to format remaining arguments. +The following extensions can also be used: +.BL +.LI +A +.B %b +format can be used instead of +.B %s +to cause escape sequences in the corresponding +.I arg\^ +to be expanded as described in +.BR print. +.LI +A +.B %B +option causes each of the arguments to be treated +as variable names and the binary value of variable +will be printed. +This is most useful for variables whose attribute +is +.BR \-b . +.LI +A +.B %H +format can be used instead of +.B %s +to cause characters in +.I arg\^ +that are special in HTML and XML +to be output to be output as their entity name. +.LI +A +.B %P +format can be used instead of +.B %s +to cause +.I arg\^ +to be interpreted as an extended regular +expression and be printed as a shell pattern. +.LI +A +.B %R +format can be used instead of +.B %s +to cause +.I arg\^ +interpreted as a shell pattern +and to be printed as an extended regular expression. +.LI +A +.B %q +format can be used instead of +.B %s +to cause the resulting string to be quoted in a manner than can +be reinput to the shell. +.LI +A +.BI %( date-format )T +format can be use to treat an argument as a date/time string +and to format the date/time according to the +.I date-format\^ +as defined for the +.BR date (1) +command. +.LI +A +.B %Z +format will output a byte whose value is 0. +.LI +The precision field of the +.B %d +format can be followed by a +.B . +and the output base. +In this case, the +.B # +flag character caues +.IB base # +to be prepended. +.LI +The +.B # +flag when used with the +.B d +specifier without an output base, +causes the output to be displayed in thousands units with one of the suffixes +.B "k M G T P E" +to indicate the unit. +.LI +The +.B # +flag when used with the +.B i +specifier causes the output to be displayed in 1024 with one of the suffixes +.B "Ki Mi Gi Ti Pi Ei" +to indicate the unit. +.LI +The +.B = +flag has been added to center the output within the specified field width. +.LE +.TP +\f3pwd\fP \*(OK \f3\-LP\fP \*(CK +Outputs the value of the current working +directory. +The +.B \-L +option is the default; it prints the logical name of the current directory. +If the +.B \-P +option is given, +all symbolic links are resolved from the name. +The last instance of +.B \-L +or +.B \-P +on the command line +determines which method is used. +.TP +\f3read\fP \*(OK \f3\-Aprs\^\fP \*(CK \*(OK \f3\-d\fP \f2delim\^\fP\*(CK \*(OK \f3\-n\fP \f2n\^\fP\*(CK \*(OK \*(OK \f3\-N\fP \f2n\^\fP\*(CK \*(OK \*(OK \f3\-t\fP \f2timeout\^\fP\*(CK \*(OK \f3\-u\fP \f2unit\^\fP\*(CK \*(OK \f2vname\f3?\f2prompt\^\f1 \*(CK \*(OK \f2vname\^\fP .\|.\|. \*(CK +The shell input mechanism. +One line is read and +is broken up into fields using the characters in +.B +.SM IFS +as separators. +The escape character, +.BR \e , +is used to remove any special meaning for the next +character and for line continuation. +The +.B \-d +option +causes the read to continue to the first character of +.I delim\^ +rather than new-line. +The +.B \-n +option causes at most +.I n\^ +bytes to read rather a full line +but will return when reading from a slow device +as soon as any characters have been read. +The +.B \-N +option causes exactly +.I n\^ +to be read unless an end-of-file has been encountered or +the read times out because of the +.B \-t +option. +In raw mode, +.B \-r, +the +.B \e +character is not treated specially. +The first +field is assigned to the first +.IR vname , +the second field +to the second +.IR vname , +etc., with leftover fields assigned to the last +.IR vname . +When +.IR vname +has the binary attribute and +.B \-n +or +.B \-N +is specified, the bytes that are read are stored directly +into the variable. +If the +.B \-v +is specified, then the value of the first +.I vname\^ +will be used as a default value when reading from a terminal device. +The +.B \-A +option causes the variable +.I vname\^ +to be unset and each field that is read to be stored in +successive elements of the indexed array +.IR vname. +The +.B \-p +option causes the input line +to be taken from the input pipe +of a process spawned by the shell +using +.BR \(bv& . +If the +.B \-s +option is present, +the input will be saved as a command in the history file. +The option +.B \-u +can be used to specify a one digit file +descriptor unit +.I unit\^ +to read from. +The file descriptor can be opened with the +.B exec\^ +special built-in command. +The default value of unit +.I n\^ +is 0. +The option +.B \-t +is used to specify a timeout in +seconds when reading from a terminal or pipe. +If +.I vname\^ +is omitted, then +.SM +.B REPLY +is used as the default +.IR vname . +An end-of-file with the +.B \-p +option causes cleanup for this process +so that another can be spawned. +If the first argument contains a +.BR ? , +the remainder of this word is used as a +.I prompt\^ +on standard error +when the shell is interactive. +The exit status is 0 unless an end-of-file is encountered +or +.B read +has timed out. +.TP +\(dg\(dg \f3readonly\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2vname\fP\*(OK\f3=\fP\f2value\^\fP\*(CK \*(CK .\|.\|. +If +.I vname\^ +is not given, +the names and values of each variable with +the readonly attribute is printed with the values +quoted in a manner that allows them to be re-inputted. +The +.B \-p +option +causes the word +.B readonly +to be inserted before each one. +Otherwise, the given +.IR vname s +are marked +readonly and these +names cannot be changed +by subsequent assignment. +.TP +\(dg \f3return\fP \*(OK \f2n\^\fP \*(CK +Causes a shell +.I function +or +\f3\|.\fP +script to return +to the invoking script +with the exit status specified by +.IR n . +The value will be the least significant 8 bits of the specified status. +If +.I n\^ +is omitted, then the return status is that of the last command executed. +If +.B return +is invoked while not in a +.I function +or a +\f3\|.\fP +script, +then it behaves the same as +.BR exit . +.TP +\(dg \f3set\fP \*(OK \f3\(+-CGabefhkmnoprstuvx\fP \*(CK \*(OK \f3\(+-o\fP \*(OK \f2option\^\fP \*(CK \*(CK .\|.\|. \*(OK \f3\(+-A\fP \f2vname\^\fP \*(CK \*(OK \f2arg\^\fP .\|.\|. \*(CK +The options for this command have meaning as follows: +.RS +.PD 0 +.TP 8 +.B \-A +Array assignment. +Unset the variable +.I vname +and assign values sequentially from the +.I arg\^ +list. +If +.B +A +is used, the variable +.I vname +is not unset first. +.TP 8 +.B \-B +Enable brace pattern field generation. +This is the default behavior. +.TP 8 +.B \-C +Prevents redirection +.B > +from truncating existing files. +Files that are created are opened with the O_EXCL mode. +Requires +.B >\(bv +to truncate a file when turned on. +.TP 8 +.B \-G +Causes the pattern +.B \(**\(** +by itself to match files and zero or more directories and sub-directories +when used for file name generation. +If followed by a +.B / +only directories and sub-directories are matched. +.TP 8 +.B \-a +All subsequent variables that are defined are automatically exported. +.TP 8 +.B \-b +Prints job completion messages as soon as a background job changes +state rather than waiting for the next prompt. +.TP 8 +.B \-e +If a command has a non-zero exit status, +execute the +.SM +.B ERR +trap, if set, +and exit. +This mode is disabled while reading profiles. +.TP 8 +.B \-f +Disables file name generation. +.TP 8 +.B \-h +Each command +becomes a tracked alias when first encountered. +.TP 8 +.B \-k +(Obsolete). All variable assignment arguments are placed in the environment for a command, +not just those that precede the command name. +.TP 8 +.B \-m +Background jobs will run in a separate process group +and a line will print upon completion. +The exit status of background jobs is reported in a completion message. +On systems with job control, +this option is turned on automatically for +interactive shells. +.TP 8 +.B \-n +Read commands and check them for syntax errors, but do not execute them. +Ignored for interactive shells. +.TP 8 +.B \-o +The following argument can be one of the following option names: +.RS +.TP 8 +.B allexport +Same as +.BR \-a . +.TP 8 +.B errexit +Same as +.BR \-e . +.TP 8 +.B bgnice +All background jobs are run at a lower priority. +This is the default mode. +.TP 8 +.B bracexpand +Sans as +.BR \-B . +.TP 8 +.B emacs +Puts you in an +.I emacs +style in-line editor for command entry. +.TP 8 +.B globstar +Same as +.BR \-G . +.TP 8 +.B gmacs +Puts you in a +.I gmacs +style in-line editor for command entry. +.TP 8 +.B ignoreeof +The shell will not exit on end-of-file. +The command +.B exit +must be used. +.TP 8 +.B keyword +Same as +.BR \-k . +.TP 8 +.B markdirs +All directory names resulting from file name generation have a trailing +.B / +appended. +.TP 8 +.B monitor +Same as +.BR \-m . +.TP 8 +.B multiline +The built-in editors will use multiple lines on the screen for lines +that are longer than the width of the screen. This may not work +for all terminals. +.TP 8 +.B noclobber +Same as +.BR \-C . +.TP 8 +.B noexec +Same as +.BR \-n . +.TP 8 +.B noglob +Same as +.BR \-f . +.TP 8 +.B nolog +Do not save function definitions in the history file. +.TP 8 +.B notify +Same as +.BR \-b . +.TP 8 +.B nounset +Same as +.BR \-u . +.TP 8 +.B pipefail +A pipeline will not complete until all components +of the pipeline have completed, and the return value +will be the value of the last non-zero command +to fail or zero of no command has failed. +.TP 8 +.B showme +When enabled, simple commands or pipelines preceded by a a semicolon +.RB ( ; ) +will be displayed as if the +.B xtrace +option were enabled but will not be executed. +Otherwise, the leading +.B ; +will be ignored. +.TP 8 +.B privileged +Same as +.BR \-p . +.TP 8 +.B verbose +Same as +.BR \-v . +.TP 8 +.B trackall +Same as +.BR \-h . +.TP 8 +.B vi +Puts you in insert mode of a +.I vi\^ +style in-line editor +until you hit the escape character +.BR 033 . +This puts you in control mode. +A return sends the line. +.TP 8 +.B viraw +Each character is processed as it is typed +in +.I vi\^ +mode. +.TP 8 +.B xtrace +Same as +.BR \-x . +.PP +If no option name is supplied, then the current option settings are printed. +.RE +.TP 8 +.B \-p +Disables processing of the +.B \s-1$HOME\s+1/.profile +file and uses the file +.B /etc/suid_profile +instead of the +.SM +.B ENV +file. +This mode is on whenever the effective uid (gid) +is not equal to the real uid (gid). +Turning this off causes the effective uid and gid to be +set to the real uid and gid. +.TP 8 +.B \-r +Enables the restricted shell. This option cannot be unset +once set. +.TP 8 +.B \-s +Sort the positional parameters lexicographically. +.TP 8 +.B \-t +(Obsolete). Exit after reading and executing one command. +.TP 8 +.B \-u +Treat unset parameters as an error when substituting. +.TP 8 +.B \-v +Print shell input lines as they are read. +.TP 8 +.B \-x +Print commands and their arguments as they are executed. +.TP 8 +.B \-\|\- +Do not change any of the options; useful in setting +.B $1 +to a value beginning with +.BR \- . +If no arguments follow this option then the positional parameters are unset. +.PD +.PP +As an obsolete feature, +if the first +.I arg\^ +is +.B \- +then the +.B \-x +and +.B \-v +options are turned off and the next +.I arg +is treated as the first argument. +Using +.B \+ +rather than +.B \- +causes these options to be turned off. +These options can also be used upon invocation of the shell. +The current set of options may be found in +.BR $\- . +Unless +.B \-A +is specified, +the remaining arguments are positional +parameters and are assigned, in order, to +.B $1 +.B $2 +\&.\|.\|.\^. +If no arguments are given, then the names and values +of all variables are printed on the standard output. +.RE +.TP +\(dg \f3shift\fP \*(OK \f2n\^\fP \*(CK +.br +The positional parameters from +\f3$\fP\f2n\fP\f3+1\fP +\&.\|.\|. +are renamed +.B $1 +\&.\|.\|.\^ +, default +.I n\^ +is 1. +The parameter +.I n\^ +can be any arithmetic expression that evaluates to a non-negative +number less than or equal to +.BR $# . +.TP +\f3sleep\fP \f2seconds\^\fP +Suspends execution for the number of decimal seconds or fractions of a +second given by +.IR seconds . +.TP +\(dg \f3trap\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2action\^\fP \*(CK \*(OK \f2sig\^\fP \*(CK .\|.\|. +The +.B \-p +option causes the trap +action associated with each trap as specified by the arguments +to be printed with appropriate quoting. +Otherwise, +.I action\^ +will be processed as if it were an argument to +.B eval +when the shell +receives signal(s) +.IR sig . +Each +.I sig\^ +can be given as a number or as the name of the signal. +Trap commands are executed in order of signal number. +Any attempt to set a trap on a signal that +was ignored on entry to the current shell +is ineffective. +If +.I action\^ +is omitted and the first +.I sig\^ +is a number, or if +.I action\^ +is +.BR \- , +then the trap(s) for each +.I sig\^ +are reset +to their original values. +If +.I action\^ +is the null +string then this signal is ignored by the shell and by the commands +it invokes. +If +.I sig\^ +is +.SM +.B ERR +then +.I action\^ +will be executed whenever a command has a non-zero exit status. +If +.I sig\^ +is +.SM +.B DEBUG +then +.I action\^ +will be executed before each command. +The variable +.B .sh.command +will contain the contents of the current command line +when +.I action\^ +is running. +If +.I sig\^ +is +.B 0 +or +.SM +.B EXIT +and the +.B trap +statement is executed inside the body of a function defined with the +.B function +.I name\^ +syntax, +then the command +.I action\^ +is executed +after the function completes. +If +.I sig\^ +is +.B 0 +or +.SM +.B EXIT +for a +.B trap +set outside any function +then the command +.I action\^ +is executed +on exit from the shell. +If +.I sig\^ +is +.SM +.BR KEYBD , +then +.I action\^ +will be executed whenever a key is read +while in +.BR emacs , +.BR gmacs , +or +.B vi\^ +mode. +The +.B trap +command +with no arguments prints a list +of commands associated with each signal number. +.TP +\f3true\fP +Does nothing, and exits 0. Used with +.B while +for infinite loops. +.TP +\(dg\(dg \f3typeset\fP \*(OK \f3\(+-AHflabnprtux\^\fP \*(CK \*(OK \f3\(+-EFLRZi\*(OK\f2n\^\fP\*(CK \*(CK \*(OK \f2vname\^\fP\*(OK\f3=\fP\f2value\^\fP \*(CK \^ \*(CK .\|.\|. +Sets attributes and values for shell variables and functions. +When invoked inside a function defined with the +.B function +.I name\^ +syntax, +a new instance of the variable +.I vname\^ +is created, +and the variable's value and type are restored +when the function completes. +The following list of attributes may be specified: +.RS +.PD 0 +.TP +.B \-A +Declares +.I vname\^ +to be an associative array. +Subscripts are strings rather than arithmetic +expressions. +.TP +.B \-a +Declares +.I vname\^ +to be an indexed array. +This is optional unless except for compound variable assignments. +.TP +.B \-E +Declares +.I vname\^ +to be a double precision floating point number. +If +.I n\^ +is non-zero, it defines the number of significant figures +that are used when expanding +.IR vname . +Otherwise, ten significant figures will be used. +.TP +.B \-F +Declares +.I vname\^ +to be a double precision floating point number. +If +.I n\^ +is non-zero, it defines the number of places after the +decimal point that are used when expanding +.IR vname . +Otherwise ten places after the decimal point will be used. +.TP +.B \-H +This option provides UNIX to host-name file mapping on non-UNIX +machines. +.TP +.B \-L +Left justify and remove leading blanks from +.IR value . +If +.I n\^ +is non-zero, it defines the width +of the field, +otherwise it is determined by the width of the value of +first assignment. +When the variable is assigned to, it is +filled on the right with blanks or truncated, if necessary, to +fit into the field. +The +.B \-R +option is turned off. +.TP +.B \-R +Right justify and fill with leading blanks. +If +.I n\^ +is non-zero, it defines the width +of the field, +otherwise it is determined by the width of the value of +first assignment. +The field is left filled with blanks or +truncated from the end if the +variable is reassigned. +The +.B \-L +option is turned off. +.TP +.B \-Z +Right justify and fill with leading zeros if +the first non-blank character is a digit and the +.B \-L +option has not been set. +Remove leading zeros if the +.B \-L +option is also set. +If +.I n\^ +is non-zero, it defines the width +of the field, +otherwise it is determined by the width of the value of +first assignment. +.TP +.B \-f +The names refer to function names rather than +variable names. +No assignments can be made and the only other +valid options are +.BR \-t , +.B \-u +and +.BR \-x . +The +.B \-t +option +turns on execution tracing for this function. +The +.B \-u +option +causes this function to be marked undefined. +The +.SM +.B FPATH +variable will be searched to find the function definition +when the function is referenced. +If no options other than +.B \-f +is specified, then the function definition will be displayed +on standard output. If +.B +f +is specified, then a line containing the function name followed +by a shell comment containing the line number and path name of the +file where this function was defined, if any, is displayed. +.TP +.B \-b +The variable can hold any number of bytes of data. +The data can be text or binary. +The value is represented by the base64 encoding of the data. +If +.B \-Z +is also specified, the size in bytes of the +data in the buffer will be determined by the size associated with the +.BR \-Z . +If the base64 string assigned results in more data, it will be +truncated. Otherwise, it will be filled with bytes +whose value is zero. +The +.B printf +format +.B %B +can be used to output the actual data in this buffer instead +of the base64 encoding of the data. +.TP +.B \-i +Declares +.I vname\^ +to be represented internally as integer. +The right hand side of an assignment is evaluated as an +arithmetic expression when assigning to an integer. +If +.I n\^ +is non-zero, it defines the output arithmetic base, +otherwise the output base will be ten. +.TP +.B \-l +All upper-case characters are +converted to lower-case. +The upper-case option, +.BR \-u , +is turned off. +.TP +.B \-n +Declares +.I vname\^ +to be a reference to the variable whose name is +defined by the value of variable +.IR vname . +This is usually used to reference a variable inside +a function whose name has been passed as an argument. +.TP +.B \-r +The given +.IR vname s +are marked +readonly and these +names cannot be changed +by subsequent assignment. +.TP +.B \-t +Tags the variables. +Tags are user definable and have no special +meaning to the shell. +.TP +.B \-u +All lower-case characters are converted +to upper-case. +The lower-case option, +.BR \-l , +is turned off. +.TP +.B \-x +The given +.IR vname s +are marked for automatic +export to the +.I environment\^ +of subsequently-executed commands. +Variables whose names contain a \fB\s+2.\s-2\fP +cannot be exported. +.PD +.PP +The +.B \-i +attribute cannot be specified along with +.BR \-R , +.BR \-L , +.BR \-Z , +or +.BR \-f . +.PP +Using +.B \+ +rather than +.B \- +causes these options to be turned off. +If no +.I vname\^ +arguments are given, +a list of +.I vnames\^ +(and optionally the +.IR values\^ ) +of the +.I variables\^ +is printed. +(Using +.B \+ +rather than +.B \- +keeps the +values from being printed.) +The +.B \-p +option causes +.B typeset +followed by the option letters +to be printed before each name +rather than the names of the options. +If any option other than +.B \-p +is given, +only those variables +which have all of the given +options are printed. +Otherwise, the +.IR vname s +and +.I attributes\^ +of all +.I variables\^ +that have attributes +are printed. +.RE +.TP +\f3ulimit\fP \*(OK \f3\-HSacdfmnpstv\fP \*(CK \*(OK \f2limit\^\fP \*(CK +Set or display a resource limit. +The available resource limits are listed below. +Many systems do not support one or more of these limits. +The limit for a specified resource is set when +.I limit\^ +is specified. +The value of +.I limit\^ +can be a number in the unit specified below with each resource, +or the value +.BR unlimited . +The +.B \-H +and +.B \-S +options specify whether the hard limit or the +soft limit for the given resource is set. +A hard limit cannot be increased once it is set. A soft +limit can be increased up to the value of the hard limit. +If neither the +.B H +nor +.B S +options is specified, the limit applies to both. +The current resource limit is printed when +.I limit\^ +is omitted. +In this case, the soft limit is printed unless +.B H +is specified. +When more than one resource is specified, then the limit +name and unit is printed before the value. +.RS +.PD 0 +.TP +.B \-a +Lists all of the current resource limits. +.TP +.B \-c +The number of 512-byte blocks on the size of core dumps. +.TP +.B \-d +The number of K-bytes on the size of the data area. +.TP +.B \-f +The number of 512-byte blocks on files that can be written by the +current process or by child processes (files of any size may be read). +.TP +.B \-m +The number of K-bytes on the size of physical memory. +.TP +.B \-n +The number of file descriptors plus 1. +.TP +.B \-p +The number of 512-byte blocks for pipe buffering. +.TP +.B \-s +The number of K-bytes on the size of the stack area. +.TP +.B \-t +The number of CPU seconds to be used by each process. +.TP +.B \-v +The number of K-bytes for virtual memory. +.PD +.PP +If no option is given, +.B \-f +is assumed. +.RE +.TP +\f3umask\fP \*(OK \f3\-S\fP \*(CK \*(OK \f2mask\^\fP \*(CK +The user file-creation mask is set to +.I mask\^ +(see +.IR umask (2)). +.I mask +can either be an octal number or +a symbolic value as described in +.IR chmod (1). +If a symbolic value is given, +the new +umask value is the complement of the result of +applying +.I mask\^ +to the complement of the previous umask value. +If +.I mask\^ +is omitted, the current value of the mask is printed. +The +.B \-S +option causes the mode to be printed as a symbolic +value. Otherwise, the +mask is printed in octal. +.TP +\(dg \f3unalias\fP \*(OK \f3\-a\fP \*(CK \f2name\^\fP .\|.\|. +The aliases +given by the list of +.IR name s +are removed from the alias list. +The +.B \-a +option causes all the +aliases to be unset. +.TP +\(dg\f3unset\fP \*(OK \f3\-fnv\fP \*(CK \f2vname\^\fP .\|.\|. +The variables given by the list of +.IR vname s +are unassigned, +i.e., +their values and attributes are erased. +Readonly variables cannot be unset. +If the +.B \-f +option +is set, then the names refer to +.I function\^ +names. +If the +.B \-v +option is set, then the names refer to +.I variable\^ +names. +The +.B \-f +option overrides +.BR \-v . +If +.B \-n +is set and +.I name\^ +is a name reference, then +.I name\^ +will be unset rather than the variable +that it references. +The default is equivalent to +.BR \-v . +Unsetting +.SM +.BR LINENO , +.SM +.BR MAILCHECK , +.SM +.BR OPTARG , +.SM +.BR OPTIND , +.SM +.BR RANDOM , +.SM +.BR SECONDS , +.SM +.BR TMOUT , +and +.SM +.B _ +removes their special meaning even if they are +subsequently assigned to. +.TP +\f3wait\fP \*(OK \f2job\^\fP .\|.\|. \*(CK +Wait for the specified +.I job +and +report its termination status. +If +.I job\^ +is not given, then all currently active child processes are waited for. +The exit status from this command is that of +the last process waited for if +.I job\^ +is specified; otherwise it is zero. +See +.I Jobs +for a description of the format of +.IR job . +.TP +\f3whence\fP \*(OK \f3\-afpv\fP \*(CK \f2name\^\fP .\|.\|. +For each +.IR name , +indicate how it +would be interpreted if used as a command name. +.sp .5 +The +.B \-v +option +produces a more verbose report. +The +.B \-f +options skips the search for functions. +The +.B \-p +option +does a path search for +.I name\^ +even if name is an alias, a function, or a reserved word. +The +.B \-a +option +is similar to the +.B \-v +option but causes +all interpretations of the given name to be reported. +.SS Invocation. +If the shell is invoked by +.IR exec (2), +and the first character of argument zero +.RB ( $0 ) +is +.BR \- , +then the shell is assumed to be a +.I login +shell and +commands are read from +.B /etc/profile +and then from either +.B .profile +in the current directory or +.BR \s-1$HOME\s+1/.profile , +if either file exists. +Next, for interactive shells, commands are read from +the file named by +performing parameter expansion, command substitution, +and arithmetic substitution on +the value of the environment variable +.SM +.B ENV +if the file exists. +If the +.B \-s +option is not present and +.I arg\^ +and a file by the name of +.I arg\^ +exits, then it reads and executes this script. +Otherwise, if the first +.I arg\^ +does not contain a +.BR / , +a path search is performed on the first +.I arg\^ +to determine the name of the script to execute. +The script +.I arg\^ +must have execute permission and any +.I setuid +and +.I setgid +settings will be ignored. +If the script is not found on the path, +.I arg\^ +is processed as if it named a built-in command or function. +Commands are then read as described below; +the following options are interpreted by the shell +when it is invoked: +.PP +.PD 0 +.TP 10 +.BI \-c +If the +.B \-c +option is present, then +commands are read from the first +.IR arg . +Any remaining arguments become +positional parameters starting at +.BR 0 . +.TP +.B \-s +If the +.B \-s +option is present or if no +arguments remain, +then commands are read from the standard input. +Shell output, +except for the output of the +.I Special Commands\^ +listed above, +is written to +file descriptor 2. +.TP +.B \-i +If the +.B \-i +option is present or +if the shell input and output are attached to a terminal (as told by +.IR tcgetattr (2)), +then this shell is +.IR interactive . +In this case \s-1TERM\s+1 is ignored (so that \f3kill 0\fP +does not kill an interactive shell) and \s-1INTR\s+1 is caught and ignored +(so that +.B wait +is interruptible). +In all cases, \s-1QUIT\s+1 is ignored by the shell. +.TP +.B \-r +If the +.B \-r +option is present, the shell is a restricted shell. +.TP +.B \-D +A list of all double quoted strings that are preceded by a +.B $ +will be printed on standard output and the shell will exit. +This set of strings will be subject to language translation +when the locale is not C or POSIX. +No commands will be executed. +.PD +.TP +.B \-P +If +.B \-P +or +.B \-o profile +is present, the shell is a profile shell (see +.IR pfexec (1)). +.TP +.BI \-R " filename\^" +The +.B \-R +.I filename\^ +option is used +to generate a cross reference database +that can be used by a separate utility +to find definitions and references for variables and commands. +.PP +The remaining options and arguments are described under the +.B set +command above. +An optional +.B \- +as the first argument is ignored. +.if \nZ=0 \{.SS Rsh Only. +.I Rsh\} +.if \nZ=1 \{.SS Rksh Only. +.I Rksh\} +.if \nZ=2 \{.SS Rksh93 Only. +.I Rksh93\} +is used to set up login names and execution environments whose +capabilities are more controlled than those of the standard shell. +The actions of +.if \nZ=0 .B rsh\^ +.if \nZ=1 .B rksh\^ +.if \nZ=2 .B rksh93\^ +are identical to those of +.if \nZ=0 .BR sh\^ , +.if \nZ=1 .BR ksh\^ , +.if \nZ=2 .BR ksh93\^ , +except that the following are disallowed: +.RS +.PD 0 +.PP +Unsetting the restricted option. +.br +changing directory (see +.IR cd (1)), +.br +setting or unsetting the value or attributes of +.SM +.BR SHELL , +.SM +.BR ENV , +.SM +.BR FPATH , +or +.SM +.BR PATH\*S, +.br +specifying path or +command names containing +.BR / , +.br +redirecting output +.RB ( > , +.BR >| , +.BR <> , +and +.BR >> ). +.br +adding or deleting built-in commands. +.br +using +.B "command -p" +to invoke a command. +.PD +.RE +.PP +The restrictions above are enforced +after \f3.profile\fP and the +.SM +.B ENV +files are interpreted. +.PP +When a command to be executed is found to be a shell procedure, +.if \nZ=0 \{.B rsh\^ +invokes +.I sh\^\} +.if \nZ=1 \{.B rksh\^ +invokes +.I ksh\^\} +.if \nZ=2 \{.B rksh93\^ +invokes +.I ksh93\^\} +to execute it. +Thus, it is possible to provide to the end-user shell procedures +that have access to the full power of +the standard shell, +while imposing a limited menu of commands; +this scheme assumes that the end-user does not have write and +execute permissions in the same directory. +.PP +The net effect of these rules is that the writer of the +.B .profile +has complete control over user actions, +by performing guaranteed setup actions +and leaving the user in an appropriate directory +(probably +.I not\^ +the login directory). +.PP +The system administrator often sets up a directory +of commands +(e.g., +.BR /usr/rbin ) +that can be safely invoked by +.if \nZ=0 .BR rsh . +.if \nZ=1 .BR rksh . +.if \nZ=2 .BR rksh93 . +.SH EXIT STATUS +Errors detected by the shell, such as syntax errors, +cause the shell +to return a non-zero exit status. +If the shell is being used non-interactively, +then execution of the shell file is abandoned +unless the error occurs inside a subshell in which case +the subshell is abandoned. +Otherwise, the shell returns the exit status of +the last command executed (see also the +.B exit +command above). +Run time errors detected by the shell are reported by +printing the command or function name and the error condition. +If the line number that the error occurred on is greater than one, +then the line number is also printed in square brackets +.RB ( "[]" ) +after the command or function name. +.SH FILES +/etc/profile +The system wide initialization file, executed for login shells. +.RE +.if \nZ=2 \{.br +/etc/ksh.kshrc +.RS +The system wide startup file, executed for interactive shells. +.RE\} +.br +\s-1$HOME\s+1/\f3.\fPprofile +.RS +The personal initialization file, executed for login shells after /etc/profile. +.RE +.br +\s-1$HOME\s+1/\f3.\fP.kshrc +.RS +Default personal initialization file, executed for interactive shells when +.SM +.B ENV +is not set. +.RE +.br +/etc/suid_profile +.RS +Alternative initialization file, executed when instead of personal initialization file when the real and effective user or group id do not match. +.RE +.br +/dev/null +.RS +NULL device +.RE +.SH SEE ALSO +cat(1), +cd(1), +chmod(1), +cut(1), +egrep(1), +echo(1), +emacs(1), +env(1), +fgrep(1), +gmacs(1), +grep(1), +newgrp(1), +pfexec(1), +stty(1), +test(1), +umask(1), +vi(1), +dup(2), +exec(2), +fork(2), +getpwnam(3), +ioctl(2), +lseek(2), +paste(1), +pathconf(2), +pipe(2), +sysconf(2), +umask(2), +ulimit(2), +wait(2), +rand(3), +a.out(5), +profile(5), +environ(7). +.PP +Morris I. Bolsky and David G. Korn, +.IR "The New KornShell Command and Programming Language" , +Prentice Hall, 1995. +.PP +.I "POSIX \- Part 2: Shell and Utilities," +IEEE Std 1003.2-1992, ISO/IEC 9945-2, IEEE, 1993. +.SH CAVEATS +.PP +If a command +is executed, and then a command with the same name is +installed in a directory in the search path before the directory where the +original command was found, the shell will continue to +.I exec\^ +the original command. +Use the +.B \-t +option of the +.B alias\^ +command to correct this situation. +.PP +Some very old shell scripts contain a +.B ^ +as a synonym for the pipe character +.BR \(bv . +.PP +Using the +.B hist\^ +built-in command within a compound command will cause the whole +command to disappear from the history file. +.PP +The built-in command \f3\|.\fP \f2file\^\fP +reads the whole file before any commands are executed. +Therefore, +.B alias +and +.B unalias +commands in the file +will not apply to any commands defined in the file. +.PP +Traps are not processed while a job is waiting for a foreground process. +Thus, a trap on +.B CHLD +won't be executed until the foreground job terminates. +.PP +It is a good idea to leave a space after the comma operator in +arithmetic expressions to prevent the comma from being interpreted +as the decimal point character in certain locales. Index: src/lib/libshell/common/Mamfile =================================================================== --- src/lib/libshell/common/Mamfile (revision 0) +++ src/lib/libshell/common/Mamfile (revision 740) @@ -0,0 +1,1551 @@ +info mam static 00000 1994-07-17 make (AT&T Research) 5.2 2007-03-26 +setv INSTALLROOT ../../.. +setv PACKAGE_ast_INCLUDE ${INSTALLROOT}/include/ast +setv PACKAGE_ast_LIB ${INSTALLROOT}/lib +setv PACKAGEROOT ../../../../.. +setv AR ar +setv ARFLAGS cr +setv AS as +setv ASFLAGS +setv CC cc +setv mam_cc_FLAGS +setv CCFLAGS ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${mam_cc_OPTIMIZE}?} +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 ksh +make pmain.o +make sh/pmain.c +make include/shell.h implicit +make include/nval.h implicit +make ${PACKAGE_ast_INCLUDE}/cdt.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 +make ${PACKAGE_ast_INCLUDE}/endian.h implicit +make ${PACKAGE_ast_INCLUDE}/bytesex.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +done ${PACKAGE_ast_INCLUDE}/bytesex.h dontcare +done ${PACKAGE_ast_INCLUDE}/endian.h dontcare +done ${PACKAGE_ast_INCLUDE}/ast_common.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +make ${PACKAGE_ast_INCLUDE}/regex.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.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}/regex.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 +make ${PACKAGE_ast_INCLUDE}/ast_fs.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_fs.h dontcare +done ${PACKAGE_ast_INCLUDE}/ast_fcntl.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_getopt.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast_sys.h implicit +make ${PACKAGE_ast_INCLUDE}/getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_getopt.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/getopt.h dontcare +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 +done ${PACKAGE_ast_INCLUDE}/cdt.h dontcare +make ${PACKAGE_ast_INCLUDE}/ast.h implicit +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 +make ${PACKAGE_ast_INCLUDE}/sfio_s.h implicit +done ${PACKAGE_ast_INCLUDE}/sfio_s.h dontcare +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 +make ${PACKAGE_ast_INCLUDE}/ast_version.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_version.h dontcare +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast.h dontcare +done include/nval.h dontcare +make include/name.h implicit +prev include/nval.h implicit +make include/shtable.h implicit +done include/shtable.h dontcare +prev ${PACKAGE_ast_INCLUDE}/cdt.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done include/name.h dontcare +prev ${PACKAGE_ast_INCLUDE}/cdt.h implicit +make ${PACKAGE_ast_INCLUDE}/cmd.h implicit +make ${PACKAGE_ast_INCLUDE}/dlldefs.h implicit +done ${PACKAGE_ast_INCLUDE}/dlldefs.h dontcare +make ${PACKAGE_ast_INCLUDE}/cmdext.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/cmdext.h dontcare +make ${PACKAGE_ast_INCLUDE}/stak.h implicit +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 dontcare +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/stak.h dontcare +make ${PACKAGE_ast_INCLUDE}/error.h implicit +make ${PACKAGE_ast_INCLUDE}/option.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +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 dontcare +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/cmd.h dontcare +done include/shell.h +done sh/pmain.c +meta pmain.o %.c>%.o sh/pmain.c pmain +prev sh/pmain.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_DYNAMIC -D_BLD_shell -D_PACKAGE_ast -DERROR_CONTEXT_T=Error_context_t -c sh/pmain.c +done pmain.o generated +make libshell.a archive +make shell.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 shell dll cmd ast m jobs i socket nsl secdb dl +exec - do case $i in +exec - "shell"|shell) +exec - ;; +exec - *) if test ! -f ${INSTALLROOT}/lib/lib$i.a +exec - then case `{ ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -L../../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 - } > shell.req +exec - rm -f 1.${COTEMP}.* +done shell.req generated +make alarm.o +make bltins/alarm.c +make FEATURE/time implicit +meta FEATURE/time features/%>FEATURE/% features/time time +make features/time +done features/time +prev shell.req +bind -ldll +bind -lcmd +bind -last +bind -lm dontcare +bind -ldl dontcare +make +ljobs +done +ljobs dontcare virtual +make +li +done +li dontcare virtual +make ${mam_libsocket} +done ${mam_libsocket} dontcare virtual +bind -lnsl dontcare +make ${mam_libsecdb} +done ${mam_libsecdb} dontcare virtual +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/time +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 +done FEATURE/time generated +make include/builtins.h implicit +prev include/shtable.h implicit +make FEATURE/dynamic implicit +meta FEATURE/dynamic features/%>FEATURE/% features/dynamic dynamic +make features/dynamic +done features/dynamic +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/dynamic +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 dontcare +done FEATURE/dynamic dontcare generated +make FEATURE/options implicit +meta FEATURE/options features/%>FEATURE/% features/options options +make features/options +done features/options +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/options +done FEATURE/options dontcare generated +prev ${PACKAGE_ast_INCLUDE}/option.h implicit +done include/builtins.h +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +make include/defs.h implicit +prev include/shell.h implicit +make include/env.h implicit +done include/env.h dontcare +make include/argnod.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +done include/argnod.h +make include/fault.h implicit +make FEATURE/sigfeatures implicit +meta FEATURE/sigfeatures features/%>FEATURE/% features/sigfeatures sigfeatures +make features/sigfeatures +done features/sigfeatures +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/sigfeatures +done FEATURE/sigfeatures dontcare generated +make FEATURE/setjmp implicit +meta FEATURE/setjmp features/%>FEATURE/% features/setjmp setjmp +make features/setjmp +done features/setjmp +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/setjmp +done FEATURE/setjmp dontcare generated +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +make ${PACKAGE_ast_INCLUDE}/sig.h implicit +done ${PACKAGE_ast_INCLUDE}/sig.h dontcare +done include/fault.h +make include/history.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done include/history.h +prev ${PACKAGE_ast_INCLUDE}/cdt.h implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done include/defs.h +done bltins/alarm.c +meta alarm.o %.c>%.o bltins/alarm.c alarm +prev bltins/alarm.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_shell -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c bltins/alarm.c +done alarm.o generated +make cd_pwd.o +make bltins/cd_pwd.c +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +make ${PACKAGE_ast_INCLUDE}/ls.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_mode.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_mode.h dontcare +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 +prev include/builtins.h implicit +prev include/name.h implicit +make include/path.h implicit +make FEATURE/acct implicit +meta FEATURE/acct >FEATURE/% acct +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : def acct +done FEATURE/acct dontcare generated +prev include/nval.h implicit +prev FEATURE/options implicit +done include/path.h +make include/variables.h implicit +prev FEATURE/dynamic implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/option.h implicit +done include/variables.h +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev include/defs.h implicit +done bltins/cd_pwd.c +meta cd_pwd.o %.c>%.o bltins/cd_pwd.c cd_pwd +prev bltins/cd_pwd.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c bltins/cd_pwd.c +done cd_pwd.o generated +make cflow.o +make bltins/cflow.c +prev include/builtins.h implicit +make include/shnodes.h implicit +prev include/argnod.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done include/shnodes.h +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev include/defs.h implicit +done bltins/cflow.c +meta cflow.o %.c>%.o bltins/cflow.c cflow +prev bltins/cflow.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_KIA -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c bltins/cflow.c +done cflow.o generated +make deparse.o +make sh/deparse.c +make include/test.h implicit +prev include/shtable.h implicit +prev FEATURE/options implicit +done include/test.h +prev include/shnodes.h implicit +prev include/defs.h implicit +done sh/deparse.c +meta deparse.o %.c>%.o sh/deparse.c deparse +prev sh/deparse.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_KIA -D_BLD_shell -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/deparse.c +done deparse.o generated +make getopts.o +make bltins/getopts.c +prev include/builtins.h implicit +prev include/nval.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +done bltins/getopts.c +meta getopts.o %.c>%.o bltins/getopts.c getopts +prev bltins/getopts.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c bltins/getopts.c +done getopts.o generated +make hist.o +make bltins/hist.c +make include/edit.h implicit +make include/national.h implicit +done include/national.h dontcare +make include/terminal.h implicit +make FEATURE/ttys implicit +meta FEATURE/ttys features/%>FEATURE/% features/ttys ttys +make features/ttys +done features/ttys +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/ttys +done FEATURE/ttys dontcare generated +done include/terminal.h dontcare +prev FEATURE/setjmp implicit +prev ${PACKAGE_ast_INCLUDE}/sig.h implicit +make FEATURE/locale implicit +meta FEATURE/locale features/%>FEATURE/% features/locale locale +make features/locale +done features/locale +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/locale +done FEATURE/locale dontcare generated +prev FEATURE/options implicit +done include/edit.h dontcare +prev include/builtins.h implicit +prev include/history.h implicit +prev include/name.h implicit +make include/io.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done include/io.h +prev include/variables.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev include/defs.h implicit +done bltins/hist.c +meta hist.o %.c>%.o bltins/hist.c hist +prev bltins/hist.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_HISTEXPAND -DSHOPT_MULTIBYTE -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DERROR_CONTEXT_T=Error_context_t -DSHOPT_DYNAMIC -c bltins/hist.c +done hist.o generated +make misc.o +make bltins/misc.c +make include/jobs.h implicit +prev include/terminal.h implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done include/jobs.h +prev include/builtins.h implicit +prev include/history.h implicit +prev include/name.h implicit +prev include/io.h implicit +prev include/path.h implicit +prev include/shnodes.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +done bltins/misc.c +meta misc.o %.c>%.o bltins/misc.c misc +prev bltins/misc.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DSHOPT_ESH -DKSHELL -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_KIA -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c bltins/misc.c +done misc.o generated +make print.o +make bltins/print.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.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ccode.h +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 +prev ${PACKAGE_ast_INCLUDE}/times.h implicit +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 include/streval.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_float.h implicit +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_common.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_float.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done include/streval.h +prev include/builtins.h implicit +prev include/history.h implicit +prev include/name.h implicit +prev include/io.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/defs.h implicit +done bltins/print.c +meta print.o %.c>%.o bltins/print.c print +prev bltins/print.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -D_PACKAGE_ast -D_BLD_shell -DSHOPT_PFSH -DSHOPT_ESH -DKSHELL -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -c bltins/print.c +done print.o generated +make read.o +make bltins/read.c +prev include/edit.h implicit +prev include/terminal.h implicit +prev include/history.h implicit +prev include/builtins.h implicit +prev include/name.h implicit +prev include/io.h implicit +make include/lexstates.h implicit +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +make ${PACKAGE_ast_INCLUDE}/wchar.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_wchar.h implicit +make ${PACKAGE_ast_INCLUDE}/stdio.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_stdio.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio_s.h implicit +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 +done ${PACKAGE_ast_INCLUDE}/wchar.h dontcare +prev FEATURE/locale implicit +done include/lexstates.h +prev include/variables.h implicit +prev include/defs.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done bltins/read.c +meta read.o %.c>%.o bltins/read.c read +prev bltins/read.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -DSHOPT_HISTEXPAND -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c bltins/read.c +done read.o generated +make sleep.o +make bltins/sleep.c +make FEATURE/poll implicit +meta FEATURE/poll features/%>FEATURE/% features/poll poll +make features/poll +done features/poll +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/poll +done FEATURE/poll generated +prev FEATURE/time implicit +prev include/builtins.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/defs.h implicit +done bltins/sleep.c +meta sleep.o %.c>%.o bltins/sleep.c sleep +prev bltins/sleep.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_shell -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c bltins/sleep.c +done sleep.o generated +make trap.o +make bltins/trap.c +prev include/builtins.h implicit +prev include/jobs.h implicit +prev include/defs.h implicit +done bltins/trap.c +meta trap.o %.c>%.o bltins/trap.c trap +prev bltins/trap.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c bltins/trap.c +done trap.o generated +make test.o +make bltins/test.c +prev ${PACKAGE_ast_INCLUDE}/tmx.h implicit +prev FEATURE/poll implicit +make FEATURE/externs implicit +meta FEATURE/externs features/%>FEATURE/% features/externs externs +make features/externs +done features/externs +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/externs +done FEATURE/externs generated +prev include/builtins.h implicit +prev include/test.h implicit +prev include/terminal.h implicit +prev include/io.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/defs.h implicit +done bltins/test.c +meta test.o %.c>%.o bltins/test.c test +prev bltins/test.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_shell -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DKSHELL -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c bltins/test.c +done test.o generated +make typeset.o +make bltins/typeset.c +prev ${PACKAGE_ast_INCLUDE}/dlldefs.h implicit +prev include/variables.h implicit +prev include/builtins.h implicit +prev include/history.h implicit +prev include/name.h implicit +prev include/path.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/defs.h implicit +done bltins/typeset.c +meta typeset.o %.c>%.o bltins/typeset.c typeset +prev bltins/typeset.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_DYNAMIC -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DSHOPT_ESH -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -c bltins/typeset.c +done typeset.o generated +make ulimit.o +make bltins/ulimit.c +make include/ulimit.h implicit +make FEATURE/rlimits implicit +meta FEATURE/rlimits features/%>FEATURE/% features/rlimits rlimits +make features/rlimits +done features/rlimits +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/rlimits +done FEATURE/rlimits dontcare generated +prev FEATURE/time implicit +done include/ulimit.h +prev include/name.h implicit +prev include/builtins.h implicit +prev include/shell.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done bltins/ulimit.c +meta ulimit.o %.c>%.o bltins/ulimit.c ulimit +prev bltins/ulimit.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_shell -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c bltins/ulimit.c +done ulimit.o generated +make umask.o +make bltins/umask.c +prev include/builtins.h implicit +prev include/shell.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done bltins/umask.c +meta umask.o %.c>%.o bltins/umask.c umask +prev bltins/umask.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c bltins/umask.c +done umask.o generated +make whence.o +make bltins/whence.c +prev include/builtins.h implicit +make include/shlex.h implicit +prev include/lexstates.h implicit +prev include/shtable.h implicit +prev include/shnodes.h implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/cdt.h implicit +done include/shlex.h +prev include/path.h implicit +prev include/name.h implicit +prev include/shtable.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/defs.h implicit +done bltins/whence.c +meta whence.o %.c>%.o bltins/whence.c whence +prev bltins/whence.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_KIA -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c bltins/whence.c +done whence.o generated +make main.o +make sh/main.c +make execargs.h implicit +done execargs.h dontcare virtual +make ${PACKAGE_ast_INCLUDE}/fts.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/fts.h dontcare +make nc.h implicit +done nc.h dontcare virtual +prev FEATURE/externs implicit +make FEATURE/execargs implicit +meta FEATURE/execargs >FEATURE/% execargs +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : def execargs +done FEATURE/execargs generated +make FEATURE/pstat implicit +meta FEATURE/pstat >FEATURE/% pstat +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : def pstat +done FEATURE/pstat generated +prev FEATURE/time implicit +make include/timeout.h implicit +done include/timeout.h +prev include/history.h implicit +prev include/shnodes.h implicit +prev include/jobs.h implicit +prev include/io.h implicit +prev include/path.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +make include/fcin.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +done include/fcin.h +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done sh/main.c +meta main.o %.c>%.o sh/main.c main +prev sh/main.c +exec - ${CC} ${mam_cc_FLAGS} ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${CCFLAGS.FORCE}?} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_BRACEPAT -D_PACKAGE_ast -DSHOPT_ESH -DSHOPT_KIA -D_BLD_shell -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DKSHELL -DSHOPT_SUID_EXEC -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c sh/main.c +done main.o generated +make nvdisc.o +make sh/nvdisc.c +prev include/path.h implicit +prev include/builtins.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +done sh/nvdisc.c +meta nvdisc.o %.c>%.o sh/nvdisc.c nvdisc +prev sh/nvdisc.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_NAMESPACE -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -D_BLD_shell -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/nvdisc.c +done nvdisc.o generated +make arith.o +make sh/arith.c +prev include/variables.h implicit +prev include/streval.h implicit +prev include/name.h implicit +prev include/lexstates.h implicit +prev include/defs.h implicit +done sh/arith.c +meta arith.o %.c>%.o sh/arith.c arith +prev sh/arith.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/arith.c +done arith.o generated +make args.o +make sh/args.c +prev include/io.h implicit +prev include/shlex.h implicit +prev FEATURE/poll implicit +prev include/edit.h implicit +prev include/terminal.h implicit +prev include/builtins.h implicit +prev include/path.h implicit +prev include/defs.h implicit +done sh/args.c +meta args.o %.c>%.o sh/args.c args +prev sh/args.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_OPTIMIZE -DSHOPT_RAWONLY -DSHOPT_HISTEXPAND -DSHOPT_PFSH -D_BLD_shell -DKSHELL -D_PACKAGE_ast -DSHOPT_KIA -DSHOPT_MULTIBYTE -DSHOPT_ESH -DSHOPT_VSH -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c sh/args.c +done args.o generated +make array.o +make sh/array.c +prev include/name.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev include/defs.h implicit +done sh/array.c +meta array.o %.c>%.o sh/array.c array +prev sh/array.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -D_PACKAGE_ast -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -DSHOPT_MULTIBYTE -DSHOPT_PFSH -c sh/array.c +done array.o generated +make completion.o +make edit/completion.c +prev include/history.h implicit +prev include/edit.h implicit +prev include/io.h implicit +prev include/path.h implicit +prev include/lexstates.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_wchar.h implicit +prev include/defs.h implicit +done edit/completion.c +meta completion.o %.c>%.o edit/completion.c completion +prev edit/completion.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -DSHOPT_ESH -D_PACKAGE_ast -DSHOPT_HISTEXPAND -DKSHELL -DSHOPT_VSH -DSHOPT_PFSH -D_BLD_shell -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c edit/completion.c +done completion.o generated +make defs.o +make sh/defs.c +prev include/timeout.h implicit +prev include/edit.h implicit +prev include/shlex.h implicit +prev include/jobs.h implicit +prev include/defs.h implicit +done sh/defs.c +meta defs.o %.c>%.o sh/defs.c defs +prev sh/defs.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_HISTEXPAND -DSHOPT_MULTIBYTE -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_KIA -D_BLD_shell -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c sh/defs.c +done defs.o generated +make edit.o +make edit/edit.c +prev include/edit.h implicit +prev include/history.h implicit +prev include/terminal.h implicit +prev include/io.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +make FEATURE/cmds implicit +meta FEATURE/cmds features/%>FEATURE/% features/cmds cmds +make features/cmds +done features/cmds +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/cmds +done FEATURE/cmds generated +prev FEATURE/time implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/ccode.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done edit/edit.c +meta edit.o %.c>%.o edit/edit.c edit +prev edit/edit.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_RAWONLY -DSHOPT_ESH -DSHOPT_VSH -DSHOPT_MULTIBYTE -DSHOPT_HISTEXPAND -DKSHELL -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c edit/edit.c +done edit.o generated +make expand.o +make sh/expand.c +prev include/path.h implicit +prev include/io.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_dir.h implicit +make ${PACKAGE_ast_INCLUDE}/dirent.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_dirent.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_std.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_dirent.h dontcare +done ${PACKAGE_ast_INCLUDE}/dirent.h dontcare +make dirlib.h implicit +done dirlib.h dontcare virtual +prev ${PACKAGE_ast_INCLUDE}/ast_lib.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_dir.h +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +make ${PACKAGE_ast_INCLUDE}/glob.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/glob.h +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev include/test.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +done sh/expand.c +meta expand.o %.c>%.o sh/expand.c expand +prev sh/expand.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_BRACEPAT -DSHOPT_SUID_EXEC -D_BLD_shell -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DKSHELL -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/expand.c +done expand.o generated +make fault.o +make sh/fault.c +prev ${PACKAGE_ast_INCLUDE}/vmalloc.h implicit +prev include/path.h implicit +prev include/jobs.h implicit +prev include/variables.h implicit +prev include/shnodes.h implicit +prev include/history.h implicit +prev include/io.h implicit +prev include/fcin.h implicit +prev include/defs.h implicit +done sh/fault.c +meta fault.o %.c>%.o sh/fault.c fault +prev sh/fault.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_KIA -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -D_BLD_shell -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DKSHELL -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c sh/fault.c +done fault.o generated +make fcin.o +make sh/fcin.c +prev include/fcin.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done sh/fcin.c +meta fcin.o %.c>%.o sh/fcin.c fcin +prev sh/fcin.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DERROR_CONTEXT_T=Error_context_t -c sh/fcin.c +done fcin.o generated +make history.o +make edit/history.c +prev ${PACKAGE_ast_INCLUDE}/stdio.h implicit +prev include/history.h implicit +prev include/io.h implicit +prev include/builtins.h implicit +prev include/path.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev FEATURE/time implicit +prev ${PACKAGE_ast_INCLUDE}/sfio.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done edit/history.c +meta history.o %.c>%.o edit/history.c history +prev edit/history.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_VSH -DSHOPT_ESH -DSHOPT_MULTIBYTE -D_PACKAGE_ast -D_BLD_shell -DKSHELL -DSHOPT_PFSH -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c edit/history.c +done history.o generated +make init.o +make sh/init.c +make include/version.h implicit +done include/version.h +prev include/lexstates.h implicit +prev FEATURE/dynamic implicit +prev FEATURE/time implicit +prev include/builtins.h implicit +prev include/shlex.h implicit +prev include/io.h implicit +prev include/jobs.h implicit +prev include/edit.h implicit +prev include/name.h implicit +prev include/fault.h implicit +prev include/path.h implicit +prev include/variables.h implicit +prev ${PACKAGE_ast_INCLUDE}/ccode.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev include/defs.h implicit +done sh/init.c +meta init.o %.c>%.o sh/init.c init +prev sh/init.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_PFSH -DSHOPT_NAMESPACE -DSHOPT_MULTIBYTE -D_PACKAGE_ast -D_BLD_shell -DSHOPT_KIA -DKSHELL -DSHOPT_HISTEXPAND -DSHOPT_ESH -DSHOPT_VSH -DERROR_CONTEXT_T=Error_context_t -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_DYNAMIC -c sh/init.c +done init.o generated +make io.o +make sh/io.c +prev ${PACKAGE_ast_INCLUDE}/endian.h implicit +prev FEATURE/poll implicit +prev FEATURE/dynamic implicit +prev FEATURE/externs implicit +prev include/timeout.h implicit +prev include/edit.h implicit +prev include/history.h implicit +prev include/shnodes.h implicit +prev include/jobs.h implicit +prev include/io.h implicit +prev include/path.h implicit +prev include/variables.h implicit +prev ${PACKAGE_ast_INCLUDE}/regex.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev include/fcin.h implicit +prev include/defs.h implicit +done sh/io.c +meta io.o %.c>%.o sh/io.c io +prev sh/io.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_RAWONLY -DSHOPT_VSH -DSHOPT_ESH -DSHOPT_HISTEXPAND -DSHOPT_MULTIBYTE -DKSHELL -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_KIA -D_BLD_shell -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c sh/io.c +done io.o generated +make jobs.o +make sh/jobs.c +prev include/history.h implicit +prev include/jobs.h implicit +prev include/io.h implicit +make ${PACKAGE_ast_INCLUDE}/wait.h implicit +make ${PACKAGE_ast_INCLUDE}/ast_wait.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_wait.h dontcare +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev ${PACKAGE_ast_INCLUDE}/prototyped.h implicit +done ${PACKAGE_ast_INCLUDE}/wait.h +prev include/defs.h implicit +done sh/jobs.c +meta jobs.o %.c>%.o sh/jobs.c jobs +prev sh/jobs.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_VSH -DSHOPT_ESH -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_PFSH -D_BLD_shell -DKSHELL -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c sh/jobs.c +done jobs.o generated +make lex.o +make sh/lex.c +prev include/shlex.h implicit +prev include/io.h implicit +prev include/lexstates.h implicit +prev include/test.h implicit +prev include/argnod.h implicit +prev include/shell.h implicit +prev include/defs.h implicit +prev FEATURE/options implicit +prev include/nval.h implicit +prev include/fcin.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done sh/lex.c +meta lex.o %.c>%.o sh/lex.c lex +prev sh/lex.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -DSHOPT_KIA -D_PACKAGE_ast -D_BLD_shell -DSHOPT_PFSH -DKSHELL -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_HISTEXPAND -DSHOPT_ESH -c sh/lex.c +done lex.o generated +make macro.o +make sh/macro.c +prev include/streval.h implicit +prev include/national.h implicit +prev include/path.h implicit +prev include/shnodes.h implicit +prev include/io.h implicit +prev include/shlex.h implicit +prev include/variables.h implicit +prev include/name.h implicit +prev include/fcin.h implicit +prev include/defs.h implicit +done sh/macro.c +meta macro.o %.c>%.o sh/macro.c macro +prev sh/macro.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_BRACEPAT -DKSHELL -DSHOPT_OPTIMIZE -DSHOPT_FILESCAN -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_SUID_EXEC -D_BLD_shell -DSHOPT_PFSH -DSHOPT_KIA -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/macro.c +done macro.o generated +make name.o +make sh/name.c +prev include/builtins.h implicit +prev include/shnodes.h implicit +prev include/streval.h implicit +prev FEATURE/externs implicit +prev include/timeout.h implicit +prev include/lexstates.h implicit +prev include/path.h implicit +prev include/variables.h implicit +prev include/defs.h implicit +done sh/name.c +meta name.o %.c>%.o sh/name.c name +prev sh/name.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_OPTIMIZE -D_BLD_shell -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_KIA -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/name.c +done name.o generated +make nvtree.o +make sh/nvtree.c +prev include/argnod.h implicit +prev include/name.h implicit +prev include/defs.h implicit +done sh/nvtree.c +meta nvtree.o %.c>%.o sh/nvtree.c nvtree +prev sh/nvtree.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -D_BLD_shell -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -DSHOPT_MULTIBYTE -DSHOPT_PFSH -c sh/nvtree.c +done nvtree.o generated +make parse.o +make sh/parse.c +prev include/path.h implicit +prev include/test.h implicit +prev include/builtins.h implicit +prev include/history.h implicit +prev include/shlex.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/fcin.h implicit +prev include/shell.h implicit +prev include/defs.h implicit +done sh/parse.c +meta parse.o %.c>%.o sh/parse.c parse +prev sh/parse.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_NAMESPACE -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -D_BLD_shell -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_KIA -DSHOPT_ESH -DERROR_CONTEXT_T=Error_context_t -DSHOPT_DYNAMIC -DSHOPT_HISTEXPAND -DKSHELL -c sh/parse.c +done parse.o generated +make path.o +make sh/path.c +prev FEATURE/time implicit +make exec_attr.h implicit +done exec_attr.h dontcare virtual +prev FEATURE/externs implicit +prev include/test.h implicit +prev include/history.h implicit +prev include/jobs.h implicit +prev include/io.h implicit +prev include/path.h implicit +prev include/variables.h implicit +prev ${PACKAGE_ast_INCLUDE}/dlldefs.h implicit +prev include/nval.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev include/fcin.h implicit +prev include/defs.h implicit +done sh/path.c +meta path.o %.c>%.o sh/path.c path +prev sh/path.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DSHOPT_SUID_EXEC -DSHOPT_PFSH -DSHOPT_MULTIBYTE -DSHOPT_ESH -D_BLD_shell -DKSHELL -DSHOPT_BRACEPAT -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c sh/path.c +done path.o generated +make string.o +make sh/string.c +prev include/national.h implicit +prev include/lexstates.h implicit +prev include/shtable.h implicit +prev ${PACKAGE_ast_INCLUDE}/ccode.h implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev include/defs.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast_wchar.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done sh/string.c +meta string.o %.c>%.o sh/string.c string +prev sh/string.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -D_BLD_shell -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -DSHOPT_PFSH -c sh/string.c +done string.o generated +make streval.o +make sh/streval.c +prev FEATURE/externs implicit +prev ${PACKAGE_ast_INCLUDE}/stak.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev include/streval.h implicit +done sh/streval.c +meta streval.o %.c>%.o sh/streval.c streval +prev sh/streval.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -D_PACKAGE_ast -DERROR_CONTEXT_T=Error_context_t -c sh/streval.c +done streval.o generated +make subshell.o +make sh/subshell.c +prev include/path.h implicit +prev include/variables.h implicit +prev include/jobs.h implicit +prev include/shlex.h implicit +prev include/shnodes.h implicit +prev include/fault.h implicit +prev include/io.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev include/defs.h implicit +done sh/subshell.c +meta subshell.o %.c>%.o sh/subshell.c subshell +prev sh/subshell.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -D_BLD_shell -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_KIA -DERROR_CONTEXT_T=Error_context_t -DKSHELL -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DSHOPT_ESH -c sh/subshell.c +done subshell.o generated +make tdump.o +make sh/tdump.c +prev include/io.h implicit +prev include/path.h implicit +prev include/shnodes.h implicit +prev include/defs.h implicit +prev ${PACKAGE_ast_INCLUDE}/ccode.h implicit +done sh/tdump.c +meta tdump.o %.c>%.o sh/tdump.c tdump +prev sh/tdump.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DKSHELL -D_PACKAGE_ast -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_KIA -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/tdump.c +done tdump.o generated +make timers.o +make sh/timers.c +prev FEATURE/time implicit +prev FEATURE/sigfeatures implicit +prev include/defs.h implicit +prev include/fault.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/sig.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done sh/timers.c +meta timers.o %.c>%.o sh/timers.c timers +prev sh/timers.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -D_BLD_shell -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -DSHOPT_MULTIBYTE -DSHOPT_PFSH -c sh/timers.c +done timers.o generated +make trestore.o +make sh/trestore.c +prev include/io.h implicit +prev include/path.h implicit +prev include/shnodes.h implicit +prev include/defs.h implicit +prev ${PACKAGE_ast_INCLUDE}/ccode.h implicit +done sh/trestore.c +meta trestore.o %.c>%.o sh/trestore.c trestore +prev sh/trestore.c +exec - ${CC} ${mam_cc_FLAGS} ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${CCFLAGS.FORCE}?} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -DKSHELL -D_PACKAGE_ast -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_KIA -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/trestore.c +done trestore.o generated +make waitevent.o +make sh/waitevent.c +prev include/defs.h implicit +done sh/waitevent.c +meta waitevent.o %.c>%.o sh/waitevent.c waitevent +prev sh/waitevent.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -D_BLD_shell -D_PACKAGE_ast -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -DSHOPT_MULTIBYTE -DSHOPT_PFSH -c sh/waitevent.c +done waitevent.o generated +make xec.o +make sh/xec.c +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}/vmalloc.h implicit +prev include/streval.h implicit +prev FEATURE/locale implicit +prev FEATURE/externs implicit +prev FEATURE/time implicit +prev include/builtins.h implicit +prev include/test.h implicit +prev include/jobs.h implicit +prev include/shnodes.h implicit +prev include/io.h implicit +prev include/name.h implicit +prev include/path.h implicit +prev include/variables.h implicit +prev include/fcin.h implicit +prev include/defs.h implicit +done sh/xec.c +meta xec.o %.c>%.o sh/xec.c xec +prev sh/xec.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DSHOPT_FILESCAN -DSHOPT_NAMESPACE -DSHOPT_OPTIMIZE -D_BLD_shell -DSHOPT_MULTIBYTE -DSHOPT_PFSH -DSHOPT_KIA -DKSHELL -DSHOPT_SUID_EXEC -DSHOPT_BRACEPAT -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c sh/xec.c +done xec.o generated +make env.o +make sh/env.c +prev include/env.h implicit +prev ${PACKAGE_ast_INCLUDE}/cdt.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done sh/env.c +meta env.o %.c>%.o sh/env.c env +prev sh/env.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -c sh/env.c +done env.o generated +make limits.o +make data/limits.c +prev include/ulimit.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done data/limits.c +meta limits.o %.c>%.o data/limits.c limits +prev data/limits.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -c data/limits.c +done limits.o generated +make msg.o +make data/msg.c +prev include/edit.h implicit +prev include/jobs.h implicit +prev include/builtins.h implicit +prev include/history.h implicit +prev include/timeout.h implicit +prev include/shlex.h implicit +prev include/io.h implicit +prev include/path.h implicit +prev include/defs.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done data/msg.c +meta msg.o %.c>%.o data/msg.c msg +prev data/msg.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_SUID_EXEC -DSHOPT_HISTEXPAND -DSHOPT_MULTIBYTE -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DSHOPT_KIA -DSHOPT_BRACEPAT -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c data/msg.c +done msg.o generated +make strdata.o +make data/strdata.c +make FEATURE/math implicit +meta FEATURE/math features/%.sh>FEATURE/% features/math.sh math +make features/math.sh +make data/math.tab implicit +done data/math.tab +done features/math.sh dontcare +prev shell.req +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - iffe -v -c '${CC} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ' ref ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -I${PACKAGE_ast_INCLUDE} -I${INSTALLROOT}/include ${mam_libdll} ${mam_libcmd} ${mam_libast} ${mam_libm} ${mam_libdl} ${mam_libnsl} : run features/math.sh data/math.tab +make ${PACKAGE_ast_INCLUDE}/ast_standards.h implicit +done ${PACKAGE_ast_INCLUDE}/ast_standards.h dontcare +done FEATURE/math generated +prev include/streval.h implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/ast_standards.h implicit +done data/strdata.c +meta strdata.o %.c>%.o data/strdata.c strdata +prev data/strdata.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_PFSH -c data/strdata.c +done strdata.o generated +make testops.o +make data/testops.c +prev include/test.h implicit +prev include/shtable.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done data/testops.c +meta testops.o %.c>%.o data/testops.c testops +prev data/testops.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DUSAGE_LICENSE=\""[-author?David Korn ][-copyright?Copyright (c) 1982-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?libshell]"\" -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -c data/testops.c +done testops.o generated +make keywords.o +make data/keywords.c +prev FEATURE/options implicit +prev include/shlex.h implicit +prev include/shell.h implicit +prev include/shtable.h implicit +done data/keywords.c +meta keywords.o %.c>%.o data/keywords.c keywords +prev data/keywords.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_NAMESPACE -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_KIA -D_BLD_shell -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c data/keywords.c +done keywords.o generated +make options.o +make data/options.c +prev include/shtable.h implicit +prev include/name.h implicit +prev FEATURE/options implicit +prev include/defs.h implicit +done data/options.c +meta options.o %.c>%.o data/options.c options +prev data/options.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_PFSH -DSHOPT_HISTEXPAND -D_BLD_shell -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c data/options.c +done options.o generated +make signals.o +make data/signals.c +prev include/fault.h implicit +prev include/shtable.h implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done data/signals.c +meta signals.o %.c>%.o data/signals.c signals +prev data/signals.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_PACKAGE_ast -DERROR_CONTEXT_T=Error_context_t -c data/signals.c +done signals.o generated +make aliases.o +make data/aliases.c +prev include/name.h implicit +prev include/shtable.h implicit +prev FEATURE/dynamic implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done data/aliases.c +meta aliases.o %.c>%.o data/aliases.c aliases +prev data/aliases.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -D_BLD_shell -D_PACKAGE_ast -DSHOPT_MULTIBYTE -DSHOPT_PFSH -c data/aliases.c +done aliases.o generated +make builtins.o +make data/builtins.c +prev FEATURE/cmds implicit +prev include/jobs.h implicit +prev include/builtins.h implicit +prev include/version.h implicit +prev include/name.h implicit +prev include/ulimit.h implicit +prev include/shtable.h implicit +prev include/defs.h implicit +prev include/shell.h implicit +done data/builtins.c +meta builtins.o %.c>%.o data/builtins.c builtins +prev data/builtins.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_KIA -DSHOPT_PFSH -DUSAGE_LICENSE=\""[-author?David Korn ][-copyright?Copyright (c) 1982-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?libshell]"\" -DSHOPT_BRACEPAT -DSHOPT_HISTEXPAND -DSHOPT_MULTIBYTE -D_PACKAGE_ast -D_BLD_shell -DKSHELL -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -c data/builtins.c +done builtins.o generated +make variables.o +make data/variables.c +prev include/defs.h implicit +prev include/name.h implicit +prev include/shtable.h implicit +prev include/shell.h implicit +prev FEATURE/dynamic implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done data/variables.c +meta variables.o %.c>%.o data/variables.c variables +prev data/variables.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -DSHOPT_HISTEXPAND -DSHOPT_DYNAMIC -D_BLD_shell -D_PACKAGE_ast -DERROR_CONTEXT_T=Error_context_t -DSHOPT_ESH -DSHOPT_PFSH -c data/variables.c +done variables.o generated +make lexstates.o +make data/lexstates.c +prev include/lexstates.h implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done data/lexstates.c +meta lexstates.o %.c>%.o data/lexstates.c lexstates +prev data/lexstates.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_BRACEPAT -DSHOPT_NAMESPACE -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_PFSH -c data/lexstates.c +done lexstates.o generated +make emacs.o +make edit/emacs.c +prev include/terminal.h implicit +prev include/edit.h implicit +prev include/history.h implicit +prev include/io.h implicit +prev include/defs.h implicit +prev FEATURE/cmds implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done edit/emacs.c +meta emacs.o %.c>%.o edit/emacs.c emacs +prev edit/emacs.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_MULTIBYTE -DSHOPT_HISTEXPAND -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_PFSH -D_BLD_shell -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c edit/emacs.c +done emacs.o generated +make vi.o +make edit/vi.c +prev include/lexstates.h implicit +prev FEATURE/time implicit +prev include/terminal.h implicit +prev include/edit.h implicit +prev include/history.h implicit +prev include/io.h implicit +prev FEATURE/options implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +prev include/defs.h implicit +done edit/vi.c +meta vi.o %.c>%.o edit/vi.c vi +prev edit/vi.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_RAWONLY -DSHOPT_MULTIBYTE -D_PACKAGE_ast -DSHOPT_HISTEXPAND -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -DSHOPT_PFSH -D_BLD_shell -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -c edit/vi.c +done vi.o generated +make hexpand.o +make edit/hexpand.c +prev include/edit.h implicit +prev include/defs.h implicit +done edit/hexpand.c +meta hexpand.o %.c>%.o edit/hexpand.c hexpand +prev edit/hexpand.c +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DSHOPT_HISTEXPAND -DSHOPT_MULTIBYTE -DKSHELL -DSHOPT_ESH -DSHOPT_VSH -D_PACKAGE_ast -DSHOPT_PFSH -DSHOPT_DYNAMIC -D_BLD_shell -DERROR_CONTEXT_T=Error_context_t -c edit/hexpand.c +done hexpand.o generated +exec - ${AR} cr libshell.a alarm.o cd_pwd.o cflow.o deparse.o getopts.o hist.o misc.o print.o read.o sleep.o trap.o test.o typeset.o ulimit.o umask.o whence.o main.o nvdisc.o arith.o args.o array.o completion.o defs.o edit.o expand.o fault.o fcin.o history.o init.o io.o +exec - ${AR} cr libshell.a jobs.o lex.o macro.o name.o nvtree.o parse.o path.o string.o streval.o subshell.o tdump.o timers.o trestore.o waitevent.o xec.o env.o limits.o msg.o strdata.o testops.o keywords.o options.o signals.o aliases.o builtins.o variables.o lexstates.o emacs.o vi.o hexpand.o +exec - (ranlib libshell.a) >/dev/null 2>&1 || true +done libshell.a generated +bind -lshell +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +exec - ${CC} ${CCLDFLAGS} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -o ksh pmain.o ${mam_libshell} ${mam_libnsl} ${mam_libdl} ${mam_libast} +done ksh generated +make shcomp +make shcomp.o +make sh/shcomp.c +prev include/shnodes.h implicit +prev include/shell.h implicit +done sh/shcomp.c +meta shcomp.o %.c>%.o sh/shcomp.c shcomp +prev sh/shcomp.c +setv CC.DLL -UCC.DLL +setv SH_DICT -DSH_DICT="\"libshell\"" +setv _BLD_shell -U_BLD_shell +setv _BLD_DLL -U_BLD_DLL +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -Iinclude -I${PACKAGE_ast_INCLUDE} -DSH_DICT=\""libshell"\" -DSHOPT_KIA -D_PACKAGE_ast -DSHOPT_DYNAMIC -DERROR_CONTEXT_T=Error_context_t -DUSAGE_LICENSE=\""[-author?David Korn ][-copyright?Copyright (c) 1982-2007 AT&T Knowledge Ventures][-license?http://www.opensource.org/licenses/cpl1.0.txt][--catalog?libshell]"\" -c sh/shcomp.c +done shcomp.o generated +prev libshell.a archive +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +setv CC.DLL -UCC.DLL +setv SH_DICT -DSH_DICT="\"libshell\"" +exec - ${CC} ${CCLDFLAGS} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -o shcomp shcomp.o ${mam_libshell} ${mam_libnsl} ${mam_libdl} ${mam_libast} +done shcomp generated +make suid_exec +make suid_exec.o +make sh/suid_exec.c +prev include/version.h implicit +prev ${PACKAGE_ast_INCLUDE}/error.h implicit +prev ${PACKAGE_ast_INCLUDE}/sig.h implicit +prev ${PACKAGE_ast_INCLUDE}/ls.h implicit +prev FEATURE/externs implicit +prev ${PACKAGE_ast_INCLUDE}/ast.h implicit +done sh/suid_exec.c +meta suid_exec.o %.c>%.o sh/suid_exec.c suid_exec +prev sh/suid_exec.c +setv CC.DLL -UCC.DLL +setv _BLD_shell -U_BLD_shell +exec - ${CC} ${mam_cc_FLAGS} ${CCFLAGS} -I. -Iinclude -I${PACKAGE_ast_INCLUDE} -DERROR_CONTEXT_T=Error_context_t -D_PACKAGE_ast -c sh/suid_exec.c +done suid_exec.o generated +prev +ljobs +prev +li +prev ${mam_libsocket} +prev ${mam_libsecdb} +setv CC.DLL -UCC.DLL +exec - ${CC} ${CCLDFLAGS} ${mam_cc_FLAGS} ${CCFLAGS} ${LDFLAGS} ${mam_cc_L+-L.} ${mam_cc_L+-L${INSTALLROOT}/lib} -o suid_exec suid_exec.o ${mam_libast} ${mam_libnsl} ${mam_libdl} ${mam_libast} +done suid_exec generated +make shell +prev libshell.a archive +done shell virtual +prev libshell.a archive +make ${INSTALLROOT}/bin +exec - if silent test ! -d ${INSTALLROOT}/bin +exec - then mkdir -p ${INSTALLROOT}/bin +exec - fi +done ${INSTALLROOT}/bin generated +make ${INSTALLROOT}/bin/ksh +prev ${INSTALLROOT}/bin +prev ksh +exec - test '' = 'ksh' || ${STDCMP} 2>/dev/null -s ksh ${INSTALLROOT}/bin/ksh || { ${STDMV} ${INSTALLROOT}/bin/ksh ${INSTALLROOT}/bin/ksh.old 2>/dev/null || true; ${STDCP} ksh ${INSTALLROOT}/bin/ksh ;} +done ${INSTALLROOT}/bin/ksh generated +make ${INSTALLROOT}/man/man1 +exec - if silent test ! -d ${INSTALLROOT}/man/man1 +exec - then mkdir -p ${INSTALLROOT}/man/man1 +exec - fi +done ${INSTALLROOT}/man/man1 generated +make ${INSTALLROOT}/man/man1/sh.1 +prev ${INSTALLROOT}/man/man1 +make sh.1 +done sh.1 +exec - test '' = 'sh.1' || ${STDCMP} 2>/dev/null -s sh.1 ${INSTALLROOT}/man/man1/sh.1 || { ${STDMV} ${INSTALLROOT}/man/man1/sh.1 ${INSTALLROOT}/man/man1/sh.1.old 2>/dev/null || true; ${STDCP} sh.1 ${INSTALLROOT}/man/man1/sh.1 ;} +done ${INSTALLROOT}/man/man1/sh.1 generated +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/libshell.a archive +prev ${INSTALLROOT}/lib +prev libshell.a archive +exec - test '' = 'libshell.a' || ${STDCMP} 2>/dev/null -s libshell.a ${INSTALLROOT}/lib/libshell.a || { ${STDMV} ${INSTALLROOT}/lib/libshell.a ${INSTALLROOT}/lib/libshell.a.old 2>/dev/null || true; ${STDCP} libshell.a ${INSTALLROOT}/lib/libshell.a ;} +exec - (ranlib ${INSTALLROOT}/lib/libshell.a) >/dev/null 2>&1 || true +done ${INSTALLROOT}/lib/libshell.a generated +make ${INSTALLROOT}/man/man3 +exec - if silent test ! -d ${INSTALLROOT}/man/man3 +exec - then mkdir -p ${INSTALLROOT}/man/man3 +exec - fi +done ${INSTALLROOT}/man/man3 generated +make ${INSTALLROOT}/man/man3/shell.3 +prev ${INSTALLROOT}/man/man3 +make shell.3 +done shell.3 +exec - test '' = 'shell.3' || ${STDCMP} 2>/dev/null -s shell.3 ${INSTALLROOT}/man/man3/shell.3 || { ${STDMV} ${INSTALLROOT}/man/man3/shell.3 ${INSTALLROOT}/man/man3/shell.3.old 2>/dev/null || true; ${STDCP} shell.3 ${INSTALLROOT}/man/man3/shell.3 ;} +done ${INSTALLROOT}/man/man3/shell.3 generated +make ${INSTALLROOT}/man/man3/nval.3 +make nval.3 +done nval.3 +exec - test '' = 'nval.3' || ${STDCMP} 2>/dev/null -s nval.3 ${INSTALLROOT}/man/man3/nval.3 || { ${STDMV} ${INSTALLROOT}/man/man3/nval.3 ${INSTALLROOT}/man/man3/nval.3.old 2>/dev/null || true; ${STDCP} nval.3 ${INSTALLROOT}/man/man3/nval.3 ;} +done ${INSTALLROOT}/man/man3/nval.3 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/shell +prev ${INSTALLROOT}/lib/lib +prev shell.req +exec - test '' = 'shell.req' || ${STDCMP} 2>/dev/null -s shell.req ${INSTALLROOT}/lib/lib/shell || { ${STDMV} ${INSTALLROOT}/lib/lib/shell ${INSTALLROOT}/lib/lib/shell.old 2>/dev/null || true; ${STDCP} shell.req ${INSTALLROOT}/lib/lib/shell ;} +done ${INSTALLROOT}/lib/lib/shell 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}/nval.h +prev ${PACKAGE_ast_INCLUDE} +prev include/nval.h +exec - proto -p -s -l ${PACKAGEROOT}/lib/package/ast.lic '-o since=1982,author=dgk' include/nval.h > 1.${COTEMP}.x +exec - if cmp 2>/dev/null -s ${PACKAGE_ast_INCLUDE}/nval.h 1.${COTEMP}.x +exec - then rm -f 1.${COTEMP}.x +exec - else mv 1.${COTEMP}.x ${PACKAGE_ast_INCLUDE}/nval.h +exec - fi +done ${PACKAGE_ast_INCLUDE}/nval.h generated +make ${PACKAGE_ast_INCLUDE}/shell.h +prev include/shell.h +exec - proto -p -s -l ${PACKAGEROOT}/lib/package/ast.lic '-o since=1982,author=dgk' include/shell.h > 1.${COTEMP}.x +exec - if cmp 2>/dev/null -s ${PACKAGE_ast_INCLUDE}/shell.h 1.${COTEMP}.x +exec - then rm -f 1.${COTEMP}.x +exec - else mv 1.${COTEMP}.x ${PACKAGE_ast_INCLUDE}/shell.h +exec - fi +done ${PACKAGE_ast_INCLUDE}/shell.h generated +make ${PACKAGE_ast_INCLUDE}/history.h +prev include/history.h +exec - proto -p -s -l ${PACKAGEROOT}/lib/package/ast.lic '-o since=1982,author=dgk' include/history.h > 1.${COTEMP}.x +exec - if cmp 2>/dev/null -s ${PACKAGE_ast_INCLUDE}/history.h 1.${COTEMP}.x +exec - then rm -f 1.${COTEMP}.x +exec - else mv 1.${COTEMP}.x ${PACKAGE_ast_INCLUDE}/history.h +exec - fi +done ${PACKAGE_ast_INCLUDE}/history.h generated +make ${INSTALLROOT}/bin/suid_exec +prev suid_exec +exec - test '' = 'suid_exec' || ${STDCMP} 2>/dev/null -s suid_exec ${INSTALLROOT}/bin/suid_exec || { ${STDMV} ${INSTALLROOT}/bin/suid_exec ${INSTALLROOT}/bin/suid_exec.old 2>/dev/null || true; ${STDCP} suid_exec ${INSTALLROOT}/bin/suid_exec ;} +done ${INSTALLROOT}/bin/suid_exec generated +make ${INSTALLROOT}/bin/shcomp +prev shcomp +exec - test '' = 'shcomp' || ${STDCMP} 2>/dev/null -s shcomp ${INSTALLROOT}/bin/shcomp || { ${STDMV} ${INSTALLROOT}/bin/shcomp ${INSTALLROOT}/bin/shcomp.old 2>/dev/null || true; ${STDCP} shcomp ${INSTALLROOT}/bin/shcomp ;} +done ${INSTALLROOT}/bin/shcomp generated +make ${INSTALLROOT}/fun +exec - if silent test ! -d ${INSTALLROOT}/fun +exec - then mkdir -p ${INSTALLROOT}/fun +exec - fi +done ${INSTALLROOT}/fun generated +make ${INSTALLROOT}/fun/dirs +prev ${INSTALLROOT}/fun +make fun/dirs +done fun/dirs +setv mode -Dmode="+x" +exec - test '' = 'fun/dirs' || ${STDCMP} 2>/dev/null -s fun/dirs ${INSTALLROOT}/fun/dirs || { ${STDMV} ${INSTALLROOT}/fun/dirs ${INSTALLROOT}/fun/dirs.old 2>/dev/null || true; ${STDCP} fun/dirs ${INSTALLROOT}/fun/dirs && chmod ugo+x ${INSTALLROOT}/fun/dirs ;} +done ${INSTALLROOT}/fun/dirs generated +make ${INSTALLROOT}/fun/popd +make fun/popd +done fun/popd +setv mode -Dmode="+x" +exec - test '' = 'fun/popd' || ${STDCMP} 2>/dev/null -s fun/popd ${INSTALLROOT}/fun/popd || { ${STDMV} ${INSTALLROOT}/fun/popd ${INSTALLROOT}/fun/popd.old 2>/dev/null || true; ${STDCP} fun/popd ${INSTALLROOT}/fun/popd && chmod ugo+x ${INSTALLROOT}/fun/popd ;} +done ${INSTALLROOT}/fun/popd generated +make ${INSTALLROOT}/fun/pushd +make fun/pushd +done fun/pushd +setv mode -Dmode="+x" +exec - test '' = 'fun/pushd' || ${STDCMP} 2>/dev/null -s fun/pushd ${INSTALLROOT}/fun/pushd || { ${STDMV} ${INSTALLROOT}/fun/pushd ${INSTALLROOT}/fun/pushd.old 2>/dev/null || true; ${STDCP} fun/pushd ${INSTALLROOT}/fun/pushd && chmod ugo+x ${INSTALLROOT}/fun/pushd ;} +done ${INSTALLROOT}/fun/pushd generated +done install virtual +make test +make test.ksh +prev ${INSTALLROOT}/bin/ksh +prev ksh +make tests/shtests +done tests/shtests +exec - silent cmp 2>/dev/null -s ${INSTALLROOT}/bin/ksh ksh 2>/dev/null || +exec - echo "make install to run the tests on the latest ksh" >&2 +exec - cd ${PACKAGEROOT}/src/cmd/ksh93/tests +exec - SHELL=${INSTALLROOT}/bin/ksh ${INSTALLROOT}/bin/ksh shtests +done test.ksh virtual +make test.shcomp +prev ${INSTALLROOT}/bin/ksh +prev ksh +prev tests/shtests +prev shcomp +exec - silent cmp 2>/dev/null -s ${INSTALLROOT}/bin/ksh ksh 2>/dev/null || +exec - echo "make install to run the tests on the latest ksh" >&2 +exec - cd ${PACKAGEROOT}/src/cmd/ksh93/tests +exec - tmp=/tmp/ksh-${COTEMP} +exec - mkdir $tmp +exec - for i in *.sh +exec - do tst=$tmp/shcomp-${i%.sh}.ksh +exec - if shcomp $i > $tst +exec - then SHELL=${INSTALLROOT}/bin/ksh ${INSTALLROOT}/bin/ksh shtests $tst +exec - fi +exec - rm -f $tst +exec - done +exec - rm -rf $tmp +done test.shcomp virtual +done test dontcare virtual Index: src/lib/libshell/common/sh/macro.c =================================================================== --- src/lib/libshell/common/sh/macro.c (revision 0) +++ src/lib/libshell/common/sh/macro.c (revision 740) @@ -0,0 +1,2301 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * Shell macro expander + * expands ~ + * expands ${...} + * expands $(...) + * expands $((...)) + * expands `...` + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include +#include "name.h" +#include "variables.h" +#include "shlex.h" +#include "io.h" +#include "shnodes.h" +#include "path.h" +#include "national.h" +#include "streval.h" + +#undef STR_GROUP +#ifndef STR_GROUP +# define STR_GROUP 0 +#endif + +#if !SHOPT_MULTIBYTE +#define mbchar(p) (*(unsigned char*)p++) +#endif + +static int _c_; +typedef struct _mac_ +{ + Shell_t *shp; /* pointer to shell interpreter */ + Sfio_t *sp; /* stream pointer for here-document */ + struct argnod **arghead; /* address of head of argument list */ + char *ifsp; /* pointer to IFS value */ + int fields; /* number of fields */ + short quoted; /* set when word has quotes */ + unsigned char ifs; /* first char of IFS */ + char quote; /* set within double quoted contexts */ + char lit; /* set within single quotes */ + char split; /* set when word splittin is possible */ + char pattern; /* set when file expansion follows */ + char patfound; /* set if pattern character found */ + char assign; /* set for assignments */ + char arith; /* set for ((...)) */ + char let; /* set when expanding let arguments */ + char zeros; /* strip leading zeros when set */ + void *nvwalk; /* for name space walking*/ +} Mac_t; + +#define mac (*((Mac_t*)(sh.mac_context))) + +#undef ESCAPE +#define ESCAPE '\\' +#define isescchar(s) ((s)>S_QUOTE) +#define isqescchar(s) ((s)>=S_QUOTE) +#define isbracechar(c) ((c)==RBRACE || (_c_=sh_lexstates[ST_BRACE][c])==S_MOD1 ||_c_==S_MOD2) +#define ltos(x) fmtbase((long)(x),0,0) + +/* type of macro expansions */ +#define M_BRACE 1 /* ${var} */ +#define M_TREE 2 /* ${var.} */ +#define M_SIZE 3 /* ${#var} */ +#define M_VNAME 4 /* ${!var} */ +#define M_SUBNAME 5 /* ${!var[sub]} */ +#define M_NAMESCAN 6 /* ${!var*} */ +#define M_NAMECOUNT 7 /* ${#var*} */ +#define M_TYPE 8 /* ${@var} */ + +static int substring(const char*, const char*, int[], int); +static void copyto(Mac_t*, int, int); +static void comsubst(Mac_t*,int); +static int varsub(Mac_t*); +static void mac_copy(Mac_t*,const char*, int); +static void tilde_expand2(int); +static char *sh_tilde(const char*); +static char *special(int); +static void endfield(Mac_t*,int); +static void mac_error(Namval_t*); +static char *mac_getstring(char*); +static int charlen(const char*,int); +#if SHOPT_MULTIBYTE + static char *lastchar(const char*,const char*); +#endif /* SHOPT_MULTIBYTE */ + +void *sh_macopen(Shell_t *shp) +{ + void *addr = newof(0,Mac_t,1,0); + Mac_t *mp = (Mac_t*)addr; + mp->shp = shp; + return(addr); +} + +/* + * perform only parameter substitution and catch failures + */ +char *sh_mactry(register char *string) +{ + if(string) + { + int jmp_val; + int savexit = sh.savexit; + struct checkpt buff; + sh_pushcontext(&buff,SH_JMPSUB); + jmp_val = sigsetjmp(buff.buff,0); + if(jmp_val == 0) + string = sh_mactrim(string,0); + sh_popcontext(&buff); + sh.savexit = savexit; + return(string); + } + return(""); +} + +/* + * Perform parameter expansion, command substitution, and arithmetic + * expansion on . + * If greater than 1 file expansion is performed if the result + * yields a single pathname. + * If negative, than expansion rules for assignment are applied. + */ +char *sh_mactrim(char *str, register int mode) +{ + register Mac_t *mp = (Mac_t*)sh.mac_context; + Mac_t savemac; + savemac = *mp; + stakseek(0); + mp->arith = (mode==3); + mp->let = 0; + sh.argaddr = 0; + mp->pattern = (mode==1||mode==2); + mp->patfound = 0; + mp->assign = (mode<0); + mp->quoted = mp->lit = mp->split = mp->quote = 0; + mp->sp = 0; + if(mp->ifsp=nv_getval(nv_scoped(IFSNOD))) + mp->ifs = *mp->ifsp; + else + mp->ifs = ' '; + stakseek(0); + fcsopen(str); + copyto(mp,0,mp->arith); + str = stakfreeze(1); + if(mode==2) + { + /* expand only if unique */ + struct argnod *arglist=0; + if((mode=path_expand(str,&arglist))==1) + str = arglist->argval; + else if(mode>1) + errormsg(SH_DICT,ERROR_exit(1),e_ambiguous,str); + sh_trim(str); + } + *mp = savemac; + return(str); +} + +/* + * Perform all the expansions on the argument + */ +int sh_macexpand(register struct argnod *argp, struct argnod **arghead,int flag) +{ + register int flags = argp->argflag; + register char *str = argp->argval; + register Mac_t *mp = (Mac_t*)sh.mac_context; + char **saveargaddr = sh.argaddr; + Mac_t savemac; + savemac = *mp; + mp->sp = 0; + if(mp->ifsp=nv_getval(nv_scoped(IFSNOD))) + mp->ifs = *mp->ifsp; + else + mp->ifs = ' '; + if(flag&ARG_OPTIMIZE) + sh.argaddr = (char**)&argp->argchn.ap; + else + sh.argaddr = 0; + mp->arghead = arghead; + mp->quoted = mp->lit = mp->quote = 0; + mp->arith = ((flag&ARG_ARITH)!=0); + mp->let = ((flag&ARG_LET)!=0); + mp->split = !(flag&ARG_ASSIGN); + mp->assign = !mp->split; + mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB); + str = argp->argval; + fcsopen(str); + mp->fields = 0; + if(!arghead) + { + mp->split = 0; + mp->pattern = ((flag&ARG_EXP)!=0); + stakseek(0); + } + else + { + stakseek(ARGVAL); + *stakptr(ARGVAL-1) = 0; + } + mp->patfound = 0; + copyto(mp,0,mp->arith); + if(!arghead) + { + argp->argchn.cp = stakfreeze(1); + if(sh.argaddr) + argp->argflag |= ARG_MAKE; + } + else + { + endfield(mp,mp->quoted); + flags = mp->fields; + if(flags==1 && sh.argaddr) + argp->argchn.ap = *arghead; + } + sh.argaddr = saveargaddr; + *mp = savemac; + return(flags); +} + +/* + * Expand here document which is stored in or + * The result is written to + */ +void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string) +{ + register int c,n; + register const char *state = sh_lexstates[ST_QUOTE]; + register char *cp; + register Mac_t *mp = (Mac_t*)sh.mac_context; + Fcin_t save; + Mac_t savemac; + savemac = *mp; + stakseek(0); + sh.argaddr = 0; + mp->sp = outfile; + mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0; + mp->quote = 1; + mp->ifsp = nv_getval(nv_scoped(IFSNOD)); + mp->ifs = ' '; + fcsave(&save); + if(infile) + fcfopen(infile); + else + fcsopen(string); + fcnotify(0); + cp = fcseek(0); + while(1) + { +#if SHOPT_MULTIBYTE + if(mbwide()) + { + do + { + ssize_t len; + switch(len = mbsize(cp)) + { + case -1: /* illegal multi-byte char */ + case 0: + case 1: + n=state[*(unsigned char*)cp++]; + break; + default: + /* use state of alpah character */ + n=state['a']; + cp += len; + } + } + while(n == 0); + } + else +#endif /* SHOPT_MULTIBYTE */ + while((n=state[*(unsigned char*)cp++])==0); + if(n==S_NL || n==S_QUOTE || n==S_RBRA) + continue; + if(c=(cp-1)-fcseek(0)) + sfwrite(outfile,fcseek(0),c); + cp = fcseek(c+1); + switch(n) + { + case S_EOF: + if((n=fcfill()) <=0) + { + /* ignore 0 byte when reading from file */ + if(n==0 && fcfile()) + continue; + fcrestore(&save); + *mp = savemac; + return; + } + cp = fcseek(-1); + continue; + case S_ESC: + fcgetc(c); + cp=fcseek(-1); + if(c>0) + cp++; + if(!isescchar(state[c])) + sfputc(outfile,ESCAPE); + continue; + case S_GRAVE: + comsubst(mp,0); + break; + case S_DOL: + c = fcget(); + if(c=='.') + goto regular; + again: + switch(n=sh_lexstates[ST_DOL][c]) + { + case S_ALP: case S_SPC1: case S_SPC2: + case S_DIG: case S_LBRA: + { + Fcin_t save2; + int offset = staktell(); + int offset2; + stakputc(c); + if(n==S_LBRA) + sh_lexskip(RBRACE,1,ST_BRACE); + else if(n==S_ALP) + { + while(fcgetc(c),isaname(c)) + stakputc(c); + fcseek(-1); + } + stakputc(0); + offset2 = staktell(); + fcsave(&save2); + fcsopen(stakptr(offset)); + varsub(mp); + if(c=staktell()-offset2) + sfwrite(outfile,(char*)stakptr(offset2),c); + fcrestore(&save2); + stakseek(offset); + break; + } + case S_PAR: + comsubst(mp,1); + break; + case S_EOF: + if((c=fcfill()) > 0) + goto again; + /* FALL THRU */ + default: + regular: + sfputc(outfile,'$'); + fcseek(-1); + break; + } + } + cp = fcseek(0); + } +} + +/* + * expand argument but do not trim pattern characters + */ +char *sh_macpat(register struct argnod *arg, int flags) +{ + register char *sp = arg->argval; + if((arg->argflag&ARG_RAW)) + return(sp); + if(flags&ARG_OPTIMIZE) + arg->argchn.ap=0; + if(!(sp=arg->argchn.cp)) + { + sh_macexpand(arg,NIL(struct argnod**),flags); + sp = arg->argchn.cp; + if(!(flags&ARG_OPTIMIZE) || !(arg->argflag&ARG_MAKE)) + arg->argchn.cp = 0; + arg->argflag &= ~ARG_MAKE; + } + else + sh.optcount++; + return(sp); +} + +/* + * Process the characters up to or end of input string + */ +static void copyto(register Mac_t *mp,int endch, int newquote) +{ + register int c,n; + register const char *state = sh_lexstates[ST_MACRO]; + register char *cp,*first; + int tilde = -1; + int oldquote = mp->quote; + int ansi_c = 0; + int paren = 0; + int ere = 0; + int brace = 0; + Sfio_t *sp = mp->sp; + mp->sp = NIL(Sfio_t*); + mp->quote = newquote; + first = cp = fcseek(0); + if(!mp->quote && *cp=='~') + tilde = staktell(); + /* handle // operator specially */ + if(mp->pattern==2 && *cp=='/') + cp++; + while(1) + { +#if SHOPT_MULTIBYTE + if(mbwide()) + { + ssize_t len; + do + { + switch(len = mbsize(cp)) + { + case -1: /* illegal multi-byte char */ + case 0: + len = 1; + case 1: + n = state[*(unsigned char*)cp++]; + break; + default: + /* treat as if alpha */ + cp += len; + n=state['a']; + } + } + while(n == 0); + c = (cp-len) - first; + } + else +#endif /* SHOPT_MULTIBYTE */ + { + while((n=state[*(unsigned char*)cp++])==0); + c = (cp-1) - first; + } + switch(n) + { + case S_ESC: + if(ansi_c) + { + /* process ANSI-C escape character */ + char *addr= --cp; + if(c) + stakwrite(first,c); + c = chresc(cp,&addr); + cp = addr; + first = fcseek(cp-first); +#if SHOPT_MULTIBYTE + if(c > UCHAR_MAX && mbwide()) + { + int i; + unsigned char mb[8]; + + n = wctomb((char*)mb, c); + for(i=0;ipattern) + stakputc(ESCAPE); + break; + } + else if(sh_isoption(SH_BRACEEXPAND) && mp->pattern==4 && (*cp==',' || *cp==LBRACE || *cp==RBRACE || *cp=='.')) + break; + else if(mp->split && endch && !mp->quote && !mp->lit) + { + if(c) + mac_copy(mp,first,c); + cp = fcseek(c+2); + if(c= cp[-1]) + { + stakputc(c); + if(c==ESCAPE) + stakputc(ESCAPE); + } + else + cp--; + first = cp; + break; + } + n = state[*(unsigned char*)cp]; + if(n==S_ENDCH && *cp!=endch) + n = S_PAT; + if(mp->pattern) + { + /* preserve \digit for pattern matching */ + /* also \alpha for extended patterns */ + if(!mp->lit && !mp->quote && (n==S_DIG || ((paren+ere) && sh_lexstates[ST_DOL][*(unsigned char*)cp]==S_ALP))) + break; + /* followed by file expansion */ + if(!mp->lit && (n==S_ESC || (!mp->quote && + (n==S_PAT||n==S_ENDCH||n==S_SLASH||n==S_BRACT||*cp=='-')))) + { + cp += (n!=S_EOF); + break; + } + if(mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH)) + { + /* add \ for file expansion */ + stakwrite(first,c+1); + first = fcseek(c); + break; + } + } + if(mp->lit) + break; + if(!mp->quote || isqescchar(n) || n==S_ENDCH) + { + /* eliminate \ */ + if(c) + stakwrite(first,c); + /* check new-line joining */ + first = fcseek(c+1); + } + cp += (n!=S_EOF); + break; + case S_GRAVE: case S_DOL: + if(mp->lit) + break; + if(c) + { + if(mp->split && !mp->quote && endch) + mac_copy(mp,first,c); + else + stakwrite(first,c); + } + first = fcseek(c+1); + c = mp->pattern; + if(n==S_GRAVE) + comsubst(mp,0); + else if((n= *cp)==0 || !varsub(mp)) + { + if(n=='\'' && !mp->quote) + ansi_c = 1; + else if(mp->quote || n!='"') + stakputc('$'); + } + cp = first = fcseek(0); + if(*cp) + mp->pattern = c; + break; + case S_ENDCH: + if((mp->lit || cp[-1]!=endch || mp->quote!=newquote)) + goto pattern; + if(endch==RBRACE && *cp==LPAREN && mp->pattern && brace) + goto pattern; + case S_EOF: + if(c) + { + if(mp->split && !mp->quote && !mp->lit && endch) + mac_copy(mp,first,c); + else + stakwrite(first,c); + } + c += (n!=S_EOF); + first = fcseek(c); + if(tilde>=0) + tilde_expand2(tilde); + goto done; + case S_QUOTE: + if(mp->lit || mp->arith) + break; + case S_LIT: + if(mp->arith) + { + if((*cp=='`' || *cp=='[') && cp[1]=='\'') + cp +=2; + break; + } + if(n==S_LIT && mp->quote) + break; + if(c) + { + if(mp->split && endch && !mp->quote && !mp->lit) + mac_copy(mp,first,c); + else + stakwrite(first,c); + } + first = fcseek(c+1); + if(n==S_LIT) + { + if(mp->quote) + continue; + if(mp->lit) + mp->lit = ansi_c = 0; + else + mp->lit = 1; + } + else + mp->quote = !mp->quote; + mp->quoted++; + break; + case S_BRACT: + if(mp->arith || ((mp->assign==1 || endch==RBRACT) && + !(mp->quote || mp->lit))) + { + int offset=0,oldpat = mp->pattern; + int oldarith = mp->arith; + stakwrite(first,++c); + if(mp->assign==1 && first[c-2]=='.') + offset = staktell(); + first = fcseek(c); + mp->pattern = 4; + mp->arith = 0; + copyto(mp,RBRACT,0); + mp->arith = oldarith; + mp->pattern = oldpat; + stakputc(RBRACT); + if(offset) + { + cp = stakptr(staktell()); + if(sh_checkid(stakptr(offset),cp)!=cp) + stakseek(staktell()-2); + } + cp = first = fcseek(0); + break; + } + case S_PAT: + if(mp->pattern && !(mp->quote || mp->lit)) + { + mp->patfound = mp->pattern; + if((n=cp[-1])==LPAREN) + { + paren++; + if((cp-first)>1 && cp[-2]=='~') + { + char *p = cp; + while((c=mbchar(p)) && c!=RPAREN && c!='E'); + ere = c=='E'; + } + } + else if(n==RPAREN) + --paren; + } + goto pattern; + case S_BRACE: + if(!(mp->quote || mp->lit)) + { + mp->patfound = mp->split && sh_isoption(SH_BRACEEXPAND); + brace = 1; + } + pattern: + if(!mp->pattern || !(mp->quote || mp->lit)) + { + /* mark beginning of {a,b} */ + if(n==S_BRACE && endch==0 && mp->pattern) + mp->pattern=4; + if(n==S_SLASH && mp->pattern==2) + mp->pattern=3; + break; + } + if(mp->pattern==3) + break; + if(c) + stakwrite(first,c); + first = fcseek(c); + stakputc(ESCAPE); + break; + case S_EQ: + if(mp->assign==1) + { + if(*cp=='~' && !endch && !mp->quote && !mp->lit) + tilde = staktell()+(c+1); + mp->assign = 2; + } + break; + case S_SLASH: + case S_COLON: + if(tilde >=0) + { + if(c) + stakwrite(first,c); + first = fcseek(c); + tilde_expand2(tilde); + tilde = -1; + c=0; + } + if(n==S_COLON && mp->assign==2 && *cp=='~' && endch==0 && !mp->quote &&!mp->lit) + tilde = staktell()+(c+1); + else if(n==S_SLASH && mp->pattern==2) +#if 0 + goto pattern; +#else + { + if(mp->quote || mp->lit) + goto pattern; + stakwrite(first,c+1); + first = fcseek(c+1); + c = staktell(); + sh_lexskip(RBRACE,0,ST_NESTED); + stakseek(c); + cp = fcseek(-1); + stakwrite(first,cp-first); + first=cp; + } +#endif + break; + } + } +done: + mp->sp = sp; + mp->quote = oldquote; +} + +/* + * copy to stack performing sub-expression substitutions + */ +static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int subexp[],int subsize) +{ + register int c,n; +#if 0 + register char *first=cp; +#else + register char *first=fcseek(0); + char *ptr; + Mac_t savemac; + n = staktell(); + savemac = *mp; + mp->pattern = 3; + mp->split = 0; + fcsopen(cp); + copyto(mp,0,0); + stakputc(0); + ptr = cp = strdup(stakptr(n)); + stakseek(n); + *mp = savemac; + fcsopen(first); + first = cp; +#endif + while(1) + { + while((c= *cp++) && c!=ESCAPE); + if(c==0) + break; + if((n= *cp++)=='\\' || n==RBRACE || (n>='0' && n<='9' && (n-='0')=0) + { + if((n=subexp[2*n+1]-c)>0) + mac_copy(mp,str+c,n); + } + } + else if(n==0) + break; + } + if(n=cp-first-1) + mac_copy(mp,first,n); +#if 1 + free(ptr); +#endif +} + +#if SHOPT_FILESCAN +#define MAX_OFFSETS (sizeof(shp->offsets)/sizeof(shp->offsets[0])) +#define MAX_ARGN (32*1024) + +/* + * compute the arguments $1 ... $n and $# from the current line as needed + * save line offsets in the offsets array. + */ +static char *getdolarg(Shell_t *shp, int n, int *size) +{ + register int c=S_DELIM, d=shp->ifstable['\\']; + register unsigned char *first,*last,*cp = (unsigned char*)shp->cur_line; + register int m=shp->offsets[0],delim=0; + if(m==0) + return(0); + if(m<0) + m = 0; + else if(n<=m) + m = n-1; + else + m--; + if(m >= MAX_OFFSETS-1) + m = MAX_OFFSETS-2; + cp += shp->offsets[m+1]; + n -= m; + shp->ifstable['\\'] = 0; + shp->ifstable[0] = S_EOF; + while(1) + { + if(c==S_DELIM) + while(shp->ifstable[*cp++]==S_SPACE); + first = --cp; + if(++m < MAX_OFFSETS) + shp->offsets[m] = (first-(unsigned char*)shp->cur_line); + while((c=shp->ifstable[*cp++])==0); + last = cp-1; + if(c==S_SPACE) + while((c=shp->ifstable[*cp++])==S_SPACE); + if(--n==0 || c==S_EOF) + { + if(last==first && c==S_EOF && (!delim || (m>1))) + { + n++; + m--; + } + break; + } + delim = (c==S_DELIM); + } + shp->ifstable['\\'] = d; + if(m > shp->offsets[0]) + shp->offsets[0] = m; + if(n) + first = last = 0; + if(size) + *size = last-first; + return((char*)first); +} +#endif /* SHOPT_FILESCAN */ + +/* + * get the prefix after name reference resolution + */ +static char *prefix(char *id) +{ + Namval_t *np; + register char *cp = strchr(id,'.'); + if(cp) + { + *cp = 0; + np = nv_search(id, sh.var_tree,0); + *cp = '.'; + if(isastchar(cp[1])) + cp[1] = 0; + if(np && nv_isref(np)) + { + int n; + char *sp; + sh.argaddr = 0; + while(nv_isref(np)) + np = nv_refnode(np); + id = (char*)malloc(strlen(cp)+1+(n=strlen(sp=nv_name(np)))+1); + strcpy(&id[n],cp); + memcpy(id,sp,n); + return(id); + } + } + return(strdup(id)); +} + +/* + * copy to ']' onto the stack and return offset to it + */ +static int subcopy(Mac_t *mp, int flag) +{ + int split = mp->split; + int xpattern = mp->pattern; + int loc = staktell(); + int xarith = mp->arith; + mp->split = 0; + mp->arith = 0; + mp->pattern = flag?4:0; + copyto(mp,RBRACT,0); + mp->pattern = xpattern; + mp->split = split; + mp->arith = xarith; + return(loc); +} + +static int namecount(Mac_t *mp,const char *prefix) +{ + int count = 0; + mp->nvwalk = nv_diropen(prefix); + while(nv_dirnext(mp->nvwalk)) + count++; + nv_dirclose(mp->nvwalk); + return(count); +} + +static char *nextname(Mac_t *mp,const char *prefix, int len) +{ + char *cp; + if(len==0) + { + mp->nvwalk = nv_diropen(prefix); + return((char*)mp->nvwalk); + } + if(!(cp=nv_dirnext(mp->nvwalk))) + nv_dirclose(mp->nvwalk); + return(cp); +} + +/* + * This routine handles $param, ${parm}, and ${param op word} + * The input stream is assumed to be a string + */ +static int varsub(Mac_t *mp) +{ + register int c; + register int type=0; /* M_xxx */ + register char *v,*argp=0; + register Namval_t *np = NIL(Namval_t*); + register int dolg=0, mode=0; + Namarr_t *ap=0; + int dolmax=0, vsize= -1, offset= -1, nulflg, replen=0, bysub=0; + char idbuff[3], *id = idbuff, *pattern=0, *repstr; + int oldpat=mp->pattern,idnum=0,flag=0,d; +retry1: + mp->zeros = 0; + idbuff[0] = 0; + idbuff[1] = 0; + c = fcget(); + switch(c>0x7f?S_ALP:sh_lexstates[ST_DOL][c]) + { + case S_RBRA: + if(type0x7f||isaname(c)))||type && c=='.'); + while(c==LBRACT && type) + { + sh.argaddr=0; + if((c=fcget(),isastchar(c)) && fcpeek(0)==RBRACT) + { + if(type==M_VNAME) + type = M_SUBNAME; + idbuff[0] = mode = c; + fcget(); + c = fcget(); + if(c=='.' || c==LBRACT) + { + stakputc(LBRACT); + stakputc(mode); + stakputc(RBRACT); + } + else + flag = NV_ARRAY; + break; + } + else + { + fcseek(-1); + if(type==M_VNAME) + type = M_SUBNAME; + stakputc(LBRACT); + v = stakptr(subcopy(mp,1)); + stakputc(RBRACT); + c = fcget(); + } + } + } + while(type && c=='.'); + if(c==RBRACE && type && fcpeek(-2)=='.') + { + stakseek(staktell()-1); + type = M_TREE; + } + stakputc(0); + id=stakptr(offset); + if(isastchar(c) && type) + { + if(type==M_VNAME || type==M_SIZE) + { + idbuff[0] = mode = c; + if((d=fcpeek(0))==c) + idbuff[1] = fcget(); + if(type==M_VNAME) + type = M_NAMESCAN; + else + type = M_NAMECOUNT; + break; + } + goto nosub; + } + flag |= NV_NOASSIGN|NV_VARNAME|NV_NOADD; + if(c=='=' || c=='?' || (c==':' && ((d=fcpeek(0))=='=' || d=='?'))) + flag &= ~NV_NOADD; +#if SHOPT_FILESCAN + if(sh.cur_line && *id=='R' && strcmp(id,"REPLY")==0) + { + sh.argaddr=0; + np = REPLYNOD; + } + else +#endif /* SHOPT_FILESCAN */ + if(sh.argaddr) + flag &= ~NV_NOADD; + np = nv_open(id,sh.var_tree,flag|NV_NOFAIL); + ap = np?nv_arrayptr(np):0; + if(type) + { + if(ap && isastchar(mode) && !(ap->nelem&ARRAY_SCAN)) + nv_putsub(np,NIL(char*),ARRAY_SCAN); + if(!isbracechar(c)) + goto nosub; + else + fcseek(-1); + } + else + fcseek(-1); + if((type==M_VNAME||type==M_SUBNAME) && sh.argaddr && strcmp(nv_name(np),id)) + sh.argaddr = 0; + c = (type>M_BRACE && isastchar(mode)); + if(np && (!c || !ap)) + { + if(type==M_VNAME) + { + type = M_BRACE; + v = nv_name(np); + } +#ifdef SHOPT_TYPEDEF + else if(type==M_TYPE) + { +#if 0 + Namval_t *nq = nv_type(np); +#else + Namval_t *nq = 0; +#endif + type = M_BRACE; + if(nq) + v = nv_name(nq); + else + { + nv_attribute(np,sh.strbuf,"typeset",1); + v = sfstruse(sh.strbuf); + } + } +#endif /* SHOPT_TYPEDEF */ +#if SHOPT_FILESCAN + else if(sh.cur_line && np==REPLYNOD) + v = sh.cur_line; +#endif /* SHOPT_FILESCAN */ + else if(type==M_TREE) + v = nv_getvtree(np,(Namfun_t*)0); + else + { + v = nv_getval(np); + /* special case --- ignore leading zeros */ + if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && (offset==0 || !isalnum(*((unsigned char*)stakptr(offset-1))))) + mp->zeros = 1; + } + } + else + v = 0; + stakseek(offset); + if(ap) + { +#if SHOPT_OPTIMIZE + if(sh.argaddr) + nv_optimize(np); +#endif + if(isastchar(mode) && array_elem(ap)> !c) + dolg = -1; + else + dolg = 0; + } + break; + case S_EOF: + fcseek(-1); + default: + goto nosub; + } + c = fcget(); + if(type>M_TREE) + { + if(c!=RBRACE) + mac_error(np); + if(type==M_NAMESCAN || type==M_NAMECOUNT) + { + id = prefix(id); + stakseek(offset); + if(type==M_NAMECOUNT) + { + c = namecount(mp,id); + v = ltos(c); + } + else + { + dolmax = strlen(id); + dolg = -1; + nextname(mp,id,0); + v = nextname(mp,id,dolmax); + } + } + else if(type==M_SUBNAME) + { + if(dolg<0) + { + v = nv_getsub(np); + bysub=1; + } + else if(v) + { + if(!ap || isastchar(mode)) + v = "0"; + else + v = nv_getsub(np); + } + } + else + { + if(!isastchar(mode)) + c = charlen(v,vsize); + else if(dolg>0) + { +#if SHOPT_FILESCAN + if(sh.cur_line) + { + getdolarg(&sh,MAX_ARGN,(int*)0); + c = sh.offsets[0]; + } + else +#endif /* SHOPT_FILESCAN */ + c = sh.st.dolc; + } + else if(dolg<0) + c = array_elem(ap); + else + c = (v!=0); + dolg = dolmax = 0; + v = ltos(c); + } + c = RBRACE; + } + nulflg = 0; + if(type && c==':') + { + c = fcget(); + if(sh_lexstates[ST_BRACE][c]==S_MOD1 && c!='*' && c!= ':') + nulflg=1; + else if(c!='%' && c!='#') + { + fcseek(-1); + c = ':'; + } + } + if(type) + { + if(!isbracechar(c)) + { + if(!nulflg) + mac_error(np); + fcseek(-1); + c = ':'; + } + if(c!=RBRACE) + { + int newops = (c=='#' || c == '%' || c=='/'); + offset = staktell(); + if(c=='/' ||c==':' || ((!v || (nulflg && *v==0)) ^ (c=='+'||c=='#'||c=='%'))) + { + int newquote = mp->quote; + int split = mp->split; + int quoted = mp->quoted; + int arith = mp->arith; + int zeros = mp->zeros; + if(newops) + { + type = fcget(); + if(type=='%' || type=='#') + { + int d = fcget(); + fcseek(-1); + if(d=='(') + type = 0; + } + fcseek(-1); + mp->pattern = 1+(c=='/'); + mp->split = 0; + mp->quoted = 0; + mp->arith = mp->zeros = 0; + newquote = 0; + } + else if(c=='?' || c=='=') + mp->split = mp->pattern = 0; + copyto(mp,RBRACE,newquote); + if(!oldpat) + mp->patfound = 0; + mp->pattern = oldpat; + mp->split = split; + mp->quoted = quoted; + mp->arith = arith; + mp->zeros = zeros; + /* add null byte */ + stakputc(0); + stakseek(staktell()-1); + } + else + { + sh_lexskip(RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED); + stakseek(offset); + } + argp=stakptr(offset); + } + } + else + { + fcseek(-1); + c=0; + } + if(c==':') /* ${name:expr1[:expr2]} */ + { + char *ptr; + type = (int)sh_strnum(argp,&ptr,1); + if(isastchar(mode)) + { + if(id==idbuff) /* ${@} or ${*} */ + { + if(type<0 && (type+= dolmax)<0) + type = 0; + if(type==0) + v = special(dolg=0); +#if SHOPT_FILESCAN + else if(sh.cur_line) + { + v = getdolarg(&sh,dolg=type,&vsize); + if(!v) + dolmax = type; + } +#endif /* SHOPT_FILESCAN */ + else if(type < dolmax) + v = sh.st.dolv[dolg=type]; + else + v = 0; + } + else if(ap) + { + if(type<0) + { + if(array_assoc(ap)) + type = -type; + else + type += array_maxindex(np); + } + if(array_assoc(ap)) + { + while(type-- >0 && (v=0,nv_nextsub(np))) + v = nv_getval(np); + } + else if(type > 0) + { + if(nv_putsub(np,NIL(char*),type|ARRAY_SCAN)) + v = nv_getval(np); + else + v = 0; + } + } + else if(type>0) + v = 0; + } + else if(v) + { + vsize = charlen(v,vsize); + if(type<0 && (type += vsize)<0) + type = 0; + if(vsize < type) + v = 0; +#if SHOPT_MULTIBYTE + else if(mbwide()) + { + mbinit(); + while(type-->0) + { + if((c=mbsize(v))<1) + c = 1; + v += c; + } + c = ':'; + } +#endif /* SHOPT_MULTIBYTE */ + else + v += type; + vsize -= type; + } + if(*ptr==':') + { + if((type = (int)sh_strnum(ptr+1,&ptr,1)) <=0) + v = 0; + else if(isastchar(mode)) + { + if(dolg>=0) + { + if(dolg+type < dolmax) + dolmax = dolg+type; + } + else + dolmax = type; + } + else if(type < vsize) + { +#if SHOPT_MULTIBYTE + if(mbwide()) + { + char *vp = v; + mbinit(); + while(type-->0) + { + if((c=mbsize(vp))<1) + c = 1; + vp += c; + } + type = vp-v; + c = ':'; + } +#endif /* SHOPT_MULTIBYTE */ + vsize = type; + } + } + if(*ptr) + mac_error(np); + stakseek(offset); + argp = 0; + } + /* check for substring operations */ + else if(c == '#' || c == '%' || c=='/') + { + if(c=='/') + { + if(type=='/' || type=='#' || type=='%') + { + c = type; + type = '/'; + argp++; + } + else + type = 0; + } + else + { + if(type==c) /* ## or %% */ + argp++; + else + type = 0; + } + pattern = strdup(argp); + if((type=='/' || c=='/') && (repstr = mac_getstring(pattern))) + replen = strlen(repstr); + if(v || c=='/' && offset>=0) + stakseek(offset); + } + /* check for quoted @ */ + if(mode=='@' && mp->quote && !v && c!='-') + mp->quoted-=2; +retry2: + if(v && (!nulflg || *v ) && c!='+') + { + register int d = (mode=='@'?' ':mp->ifs); + int match[2*(MATCH_MAX+1)], nmatch, vsize_last; + char *vlast; + while(1) + { + if(!v) + v= ""; + if(c=='/' || c=='#' || c== '%') + { + flag = (type || c=='/')?STR_GROUP|STR_MAXIMAL:STR_GROUP; + if(c!='/') + flag |= STR_LEFT; + while(1) + { + vsize = strlen(v); + if(c=='%') + nmatch=substring(v,pattern,match,flag&STR_MAXIMAL); + else + nmatch=strgrpmatch(v,pattern,match,elementsof(match)/2,flag); + if(replen>0) + sh_setmatch(v,vsize,nmatch,match); + if(nmatch) + { + vlast = v; + vsize_last = vsize; + vsize = match[0]; + } + else if(c=='#') + vsize = 0; + if(vsize) + mac_copy(mp,v,vsize); + if(nmatch && replen>0) + mac_substitute(mp,repstr,v,match,nmatch); + if(nmatch==0) + v += vsize; + else + v += match[1]; + if(*v && c=='/' && type) + { + /* avoid infinite loop */ + if(nmatch && match[1]==0) + v++; + continue; + } + vsize = -1; + break; + } + if(replen==0) + sh_setmatch(vlast,vsize_last,nmatch,match); + } + if(vsize) + mac_copy(mp,v,vsize>0?vsize:strlen(v)); + if(dolg==0 && dolmax==0) + break; + if(dolg>=0) + { + if(++dolg >= dolmax) + break; +#if SHOPT_FILESCAN + if(sh.cur_line) + { + if(dolmax==MAX_ARGN && isastchar(mode)) + break; + if(!(v=getdolarg(&sh,dolg,&vsize))) + { + dolmax = dolg; + break; + } + } + else +#endif /* SHOPT_FILESCAN */ + v = sh.st.dolv[dolg]; + } + else if(!np) + { + if(!(v = nextname(mp,id,dolmax))) + break; + } + else + { + if(dolmax && --dolmax <=0) + { + nv_putsub(np,NIL(char*),ARRAY_UNDEF); + break; + } + if(nv_nextsub(np) == 0) + break; + if(bysub) + v = nv_getsub(np); + else + v = nv_getval(np); + } + if(mp->split && (!mp->quote || mode=='@')) + { + if(!np) + mp->pattern = 0; + endfield(mp,mp->quoted); + mp->pattern = oldpat; + } + else if(d) + { + if(mp->sp) + sfputc(mp->sp,d); + else + stakputc(d); + } + } + if(pattern) + free((void*)pattern); + } + else if(argp) + { + if(c=='/' && replen>0 && pattern && strmatch("",pattern)) + mac_substitute(mp,repstr,v,0,0); + if(c=='?') + { + if(np) + id = nv_name(np); + else if(idnum) + id = ltos(idnum); + if(*argp) + { + stakputc(0); + errormsg(SH_DICT,ERROR_exit(1),"%s: %s",id,argp); + } + else if(v) + errormsg(SH_DICT,ERROR_exit(1),e_nullset,id); + else + errormsg(SH_DICT,ERROR_exit(1),e_notset,id); + } + else if(c=='=') + { + if(np) + { + if(sh.subshell) + np = sh_assignok(np,1); + nv_putval(np,argp,0); + v = nv_getval(np); + nulflg = 0; + stakseek(offset); + goto retry2; + } + else + mac_error(np); + } + } + else if(sh_isoption(SH_NOUNSET) && (!np || nv_isnull(np) || (nv_isarray(np) && !np->nvalue.cp))) + { + if(np) + { + if(nv_isarray(np)) + { + sfprintf(sh.strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np)); + id = nv_getsub(np); + id = sfstruse(sh.strbuf); + } + else + id = nv_name(np); + nv_close(np); + } + errormsg(SH_DICT,ERROR_exit(1),e_notset,id); + } + if(np) + nv_close(np); + return(1); +nosub: + if(type) + mac_error(np); + fcseek(-1); + nv_close(np); + return(0); +} + +/* + * This routine handles command substitution + * is 0 for older `...` version + */ +static void comsubst(Mac_t *mp,int type) +{ + Sfdouble_t num; + register int c; + register char *str; + Sfio_t *sp; + Fcin_t save; + struct slnod *saveslp = sh.st.staklist; + struct _mac_ savemac; + int savtop = staktell(); + char lastc, *savptr = stakfreeze(0); + int was_history = sh_isstate(SH_HISTORY); + int was_verbose = sh_isstate(SH_VERBOSE); + int newlines,bufsize; + register Shnode_t *t; + Namval_t *np; + sh.argaddr = 0; + savemac = *mp; + sh.st.staklist=0; + if(type) + { + sp = 0; + fcseek(-1); + t = sh_dolparen(); + if(t && t->tre.tretyp==TARITH) + { + str = t->ar.arexpr->argval; + fcsave(&save); + if(!(t->ar.arexpr->argflag&ARG_RAW)) + str = sh_mactrim(str,3); + num = sh_arith(str); + out_offset: + stakset(savptr,savtop); + *mp = savemac; + if((Sflong_t)num==num) + sfprintf(sh.strbuf,"%lld",(Sflong_t)num); + else + sfprintf(sh.strbuf,"%.*Lg",LDBL_DIG,num); + str = sfstruse(sh.strbuf); + mac_copy(mp,str,strlen(str)); + sh.st.staklist = saveslp; + fcrestore(&save); + return; + } + } + else + { + while(fcgetc(c)!='`' && c) + { + if(c==ESCAPE) + { + fcgetc(c); + if(!(isescchar(sh_lexstates[ST_QUOTE][c]) || + (c=='"' && mp->quote)) || (c=='$' && fcpeek(0)=='\'')) + stakputc(ESCAPE); + } + stakputc(c); + } + c = staktell(); + str=stakfreeze(1); + /* disable verbose and don't save in history file */ + sh_offstate(SH_HISTORY); + sh_offstate(SH_VERBOSE); + if(mp->sp) + sfsync(mp->sp); /* flush before executing command */ + sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ); + c = sh.inlineno; + sh.inlineno = error_info.line+sh.st.firstline; + t = (Shnode_t*)sh_parse(mp->shp, sp,SH_EOF|SH_NL); + sh.inlineno = c; + } +#if KSHELL + if(t) + { + fcsave(&save); + sfclose(sp); + if(t->tre.tretyp==0 && !t->com.comarg) + { + /* special case $(tre.treio) && + ((ip->iofile&IOLSEEK) || !(ip->iofile&IOUFD)) && + (r=sigsetjmp(buff.buff,0))==0) + fd = sh_redirect(ip,3); + else + fd = sh_chkopen(e_devnull); + sh_popcontext(&buff); + if(r==0 && ip && (ip->iofile&IOLSEEK)) + { + if(sp=sh.sftable[fd]) + num = sftell(sp); + else + num = lseek(fd, (off_t)0, SEEK_CUR); + goto out_offset; + } + sp = sfnew(NIL(Sfio_t*),(char*)malloc(IOBSIZE+1),IOBSIZE,fd,SF_READ|SF_MALLOC); + } + else + sp = sh_subshell(t,sh_isstate(SH_ERREXIT),1); + fcrestore(&save); + } + else + sp = sfopen(NIL(Sfio_t*),"","sr"); + sh_freeup(); + sh.st.staklist = saveslp; + if(was_history) + sh_onstate(SH_HISTORY); + if(was_verbose) + sh_onstate(SH_VERBOSE); +#else + sp = sfpopen(NIL(Sfio_t*),str,"r"); +#endif + *mp = savemac; + np = nv_scoped(IFSNOD); + nv_putval(np,mp->ifsp,0); + mp->ifsp = nv_getval(np); + stakset(savptr,savtop); + newlines = 0; + lastc = 0; + sfsetbuf(sp,(void*)sp,0); + bufsize = sfvalue(sp); + /* read command substitution output and put on stack or here-doc */ + sfpool(sp, NIL(Sfio_t*), SF_WRITE); + while((str=(char*)sfreserve(sp,SF_UNBOUND,0)) && (c = sfvalue(sp))>0) + { +#if SHOPT_CRNL + /* eliminate */ + register char *dp; + char *buff = str; + while(c>1 && (*str !='\r'|| str[1]!='\n')) + { + c--; + str++; + } + dp = str; + while(c>1) + { + str++; + c--; + while(c>1 && (*str!='\r' || str[1]!='\n')) + { + c--; + *dp++ = *str++; + } + } + if(c) + *dp++ = *str++; + *dp = 0; + str = buff; + c = dp-str; +#endif /* SHOPT_CRNL */ + if(newlines >0) + { + if(mp->sp) + sfnputc(mp->sp,'\n',newlines); + else if(!mp->quote && mp->split && sh.ifstable['\n']) + endfield(mp,0); + else while(newlines--) + stakputc('\n'); + newlines = 0; + } + else if(lastc) + { + mac_copy(mp,&lastc,1); + lastc = 0; + } + /* delay appending trailing new-lines */ + while(str[--c]=='\n') + newlines++; + if(++c < bufsize) + str[c] = 0; + else + { + /* can't write past buffer so save last character */ + lastc = str[--c]; + str[c] = 0; + } + mac_copy(mp,str,c); + } + if(--newlines>0 && sh.ifstable['\n']==S_DELIM) + { + if(mp->sp) + sfnputc(mp->sp,'\n',newlines); + else if(!mp->quote && mp->split && sh.ifstable['\n']) + endfield(mp,0); + else while(newlines--) + stakputc('\n'); + } + if(lastc) + mac_copy(mp,&lastc,1); + sfclose(sp); + return; +} + +/* + * copy onto the stack + */ +static void mac_copy(register Mac_t *mp,register const char *str, register int size) +{ + register char *state; + register const char *cp=str; + register int c,n,nopat; + nopat = (mp->quote||mp->assign==1||mp->arith); + if(mp->zeros) + { + /* prevent leading 0's from becomming octal constants */ + while(size>1 && *str=='0') + str++,size--; + mp->zeros = 0; + cp = str; + } + if(mp->sp) + sfwrite(mp->sp,str,size); + else if(mp->pattern>=2 || (mp->pattern && nopat)) + { + state = sh_lexstates[ST_MACRO]; + /* insert \ before file expansion characters */ + while(size-->0) + { + c = state[n= *(unsigned char*)cp++]; + if(nopat&&(c==S_PAT||c==S_ESC||c==S_BRACT||c==S_ENDCH) && mp->pattern!=3) + c=1; + else if(mp->pattern==4 && (c==S_ESC||c==S_BRACT||c==S_ENDCH || isastchar(n))) + c=1; + else if(mp->pattern==2 && c==S_SLASH) + c=1; + else if(mp->pattern==3 && c==S_ESC && (state[*(unsigned char*)cp]==S_DIG||(*cp==ESCAPE))) + { + if(!(c=mp->quote)) + cp++; + } + else + c=0; + if(c) + { + if(c = (cp-1) - str) + stakwrite(str,c); + stakputc(ESCAPE); + str = cp-1; + } + } + if(c = cp-str) + stakwrite(str,c); + } + else if(!mp->quote && mp->split && (mp->ifs||mp->pattern)) + { + /* split words at ifs characters */ + state = sh.ifstable; + if(mp->pattern) + { + char *sp = "&|()"; + while(c = *sp++) + { + if(state[c]==0) + state[c] = S_EPAT; + } + sp = "*?[{"; + while(c = *sp++) + { + if(state[c]==0) + state[c] = S_PAT; + } + if(state[ESCAPE]==0) + state[ESCAPE] = S_ESC; + } + while(size-->0) + { + if((n=state[c= *(unsigned char*)cp++])==S_ESC || n==S_EPAT) + { + /* don't allow extended patterns in this case */ + mp->patfound = mp->pattern; + stakputc(ESCAPE); + } + else if(n==S_PAT) + mp->patfound = mp->pattern; + else if(n && mp->ifs) + { +#if SHOPT_MULTIBYTE + if(n==S_MBYTE) + { + if(sh_strchr(mp->ifsp,cp-1)<0) + continue; + n = mbsize(cp-1) - 1; + if(n==-2) + n = 0; + cp += n; + size -= n; + n= S_DELIM; + } +#endif /* SHOPT_MULTIBYTE */ + if(n==S_SPACE || n==S_NL) + { + while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL)) + size--; +#if SHOPT_MULTIBYTE + if(n==S_MBYTE && sh_strchr(mp->ifsp,cp-1)>=0) + { + n = mbsize(cp-1) - 1; + if(n==-2) + n = 0; + cp += n; + size -= n; + n=S_DELIM; + } + else +#endif /* SHOPT_MULTIBYTE */ + if(n==S_DELIM) + size--; + } + endfield(mp,n==S_DELIM||mp->quoted); + mp->patfound = 0; + if(n==S_DELIM) + while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL)) + size--; + if(size<=0) + break; + cp--; + continue; + + } + stakputc(c); + } + if(mp->pattern) + { + cp = "&|()"; + while(c = *cp++) + { + if(state[c]==S_EPAT) + state[c] = 0; + } + cp = "*?[{"; + while(c = *cp++) + { + if(state[c]==S_PAT) + state[c] = 0; + } + if(sh.ifstable[ESCAPE]==S_ESC) + sh.ifstable[ESCAPE] = 0; + } + } + else + stakwrite(str,size); +} + +/* + * Terminate field. + * If field is null count field if is non-zero + * Do filename expansion of required + */ +static void endfield(register Mac_t *mp,int split) +{ + register struct argnod *argp; + register int count=0; + if(staktell() > ARGVAL || split) + { + argp = (struct argnod*)stakfreeze(1); + argp->argnxt.cp = 0; + argp->argflag = 0; + if(mp->patfound) + { + sh.argaddr = 0; +#if SHOPT_BRACEPAT + count = path_generate(argp,mp->arghead); +#else + count = path_expand(argp->argval,mp->arghead); +#endif /* SHOPT_BRACEPAT */ + if(count) + mp->fields += count; + else if(split) /* pattern is null string */ + *argp->argval = 0; + else /* pattern expands to nothing */ + count = -1; + } + if(count==0) + { + argp->argchn.ap = *mp->arghead; + *mp->arghead = argp; + mp->fields++; + } + if(count>=0) + { + (*mp->arghead)->argflag |= ARG_MAKE; + if(mp->assign || sh_isoption(SH_NOGLOB)) + argp->argflag |= ARG_RAW|ARG_EXP; + } + stakseek(ARGVAL); + } + mp->quoted = mp->quote; +} + +/* + * Finds the right substring of STRING using the expression PAT + * the longest substring is found when FLAG is set. + */ +static int substring(register const char *string,const char *pat,int match[], int flag) +{ + register const char *sp=string; + register int size,len,nmatch,n; + int smatch[2*(MATCH_MAX+1)]; + if(flag) + { + if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_MAXIMAL)) + { + memcpy(match,smatch,n*2*sizeof(smatch[0])); + return(n); + } + return(0); + } + size = len = strlen(sp); + sp += size; + while(sp>=string) + { +#if SHOPT_MULTIBYTE + if(mbwide()) + sp = lastchar(string,sp); +#endif /* SHOPT_MULTIBYTE */ + if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_LEFT|STR_MAXIMAL)) + { + nmatch = n; + memcpy(match,smatch,n*2*sizeof(smatch[0])); + size = sp-string; + break; + } + sp--; + } + if(size==len) + return(0); + if(nmatch) + { + nmatch *=2; + while(--nmatch>=0) + match[nmatch] += size; + } + return(n); +} + +#if SHOPT_MULTIBYTE + static char *lastchar(const char *string, const char *endstring) + { + register char *str = (char*)string; + register int c; + mbinit(); + while(*str) + { + if((c=mbsize(str))<0) + c = 1; + if(str+c > endstring) + break; + str += c; + } + return(str); + } +#endif /* SHOPT_MULTIBYTE */ +static int charlen(const char *string,int len) +{ + if(!string) + return(0); +#if SHOPT_MULTIBYTE + if(mbwide()) + { + register const char *str = string, *strmax=string+len; + register int n=0; + mbinit(); + if(len>0) + { + while(str is byte offset for beginning of tilde string + */ +static void tilde_expand2(register int offset) +{ + char shtilde[10], *av[3], *ptr=stakfreeze(1); + Sfio_t *iop, *save=sfstdout; + Namval_t *np; + static int beenhere=0; + strcpy(shtilde,".sh.tilde"); + np = nv_open(shtilde,sh.fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL); + if(np && !beenhere) + { + beenhere = 1; + sh_addbuiltin(shtilde,sh_btilde,0); + } + av[0] = ".sh.tilde"; + av[1] = &ptr[offset]; + av[2] = 0; + iop = sftmp(IOBSIZE+1);; + sfset(iop,SF_READ,0); + sfstdout = iop; + if(np) + sh_fun(np, (Namval_t*)0, av); + else + sh_btilde(2, av, &sh); + sfstdout = save; + stakset(ptr, offset); + sfseek(iop,(Sfoff_t)0,SEEK_SET); + sfset(iop,SF_READ,1); + if(ptr = sfreserve(iop, SF_UNBOUND, -1)) + { + Sfoff_t n = sfvalue(iop); + while(ptr[n-1]=='\n') + n--; + if(n==1 && fcpeek(0)=='/' && ptr[n-1]) + n--; + if(n) + stakwrite(ptr,n); + } + else + stakputs(av[1]); + sfclose(iop); +} + +/* + * This routine is used to resolve ~ expansion. + * A ~ by itself is replaced with the users login directory. + * A ~- is replaced by the previous working directory in shell. + * A ~+ is replaced by the present working directory in shell. + * If ~name is replaced with login directory of name. + * If string doesn't start with ~ or ~... not found then 0 returned. + */ + +static char *sh_tilde(register const char *string) +{ + register char *cp; + register int c; + register struct passwd *pw; + register Namval_t *np=0; + static Dt_t *logins_tree; + if(*string++!='~') + return(NIL(char*)); + if((c = *string)==0) + { + if(!(cp=nv_getval(nv_scoped(HOME)))) + cp = getlogin(); + return(cp); + } + if((c=='-' || c=='+') && string[1]==0) + { + if(c=='+') + cp = nv_getval(nv_scoped(PWDNOD)); + else + cp = nv_getval(nv_scoped(OLDPWDNOD)); + return(cp); + } + if(logins_tree && (np=nv_search(string,logins_tree,0))) + return(nv_getval(np)); + if(!(pw = getpwnam(string))) + return(NIL(char*)); + if(!logins_tree) + logins_tree = dtopen(&_Nvdisc,Dtbag); + if(np=nv_search(string,logins_tree,NV_ADD)) + nv_putval(np, pw->pw_dir,0); + return(pw->pw_dir); +} + +/* + * return values for special macros + */ +static char *special(register int c) +{ + register Namval_t *np; + if(c!='$') + sh.argaddr = 0; + switch(c) + { + case '@': + case '*': + return(sh.st.dolc>0?sh.st.dolv[1]:NIL(char*)); + case '#': +#if SHOPT_FILESCAN + if(sh.cur_line) + { + getdolarg(&sh,MAX_ARGN,(int*)0); + return(ltos(sh.offsets[0])); + } +#endif /* SHOPT_FILESCAN */ + return(ltos(sh.st.dolc)); + case '!': + if(sh.bckpid) + return(ltos(sh.bckpid)); + break; + case '$': + if(nv_isnull(SH_DOLLARNOD)) + return(ltos(sh.pid)); + return(nv_getval(SH_DOLLARNOD)); + case '-': + return(sh_argdolminus()); + case '?': + return(ltos(sh.savexit)); + case 0: + if(sh_isstate(SH_PROFILE) || !error_info.id || ((np=nv_search(error_info.id,sh.bltin_tree,0)) && nv_isattr(np,BLT_SPC))) + return(sh.shname); + else + return(error_info.id); + } + return(NIL(char*)); +} + +/* + * Handle macro expansion errors + */ +static void mac_error(Namval_t *np) +{ + if(np) + nv_close(np); + errormsg(SH_DICT,ERROR_exit(1),e_subst,fcfirst()); +} + +/* + * Given pattern/string, replace / with 0 and return pointer to string + * \ characters are stripped from string. + */ +static char *mac_getstring(char *pattern) +{ + register char *cp = pattern; + register int c; + while(c = *cp++) + { + if(c==ESCAPE) + cp++; + else if(c=='/') + { + cp[-1] = 0; + return(cp); + } + } + return(NIL(char*)); +} Index: src/lib/libshell/common/sh/timers.c =================================================================== --- src/lib/libshell/common/sh/timers.c (revision 0) +++ src/lib/libshell/common/sh/timers.c (revision 740) @@ -0,0 +1,248 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include +#include +#include +#include "fault.h" +#include "defs.h" +#include "FEATURE/sigfeatures" +#include "FEATURE/time" + +typedef struct _timer +{ + double wakeup; + double incr; + struct _timer *next; + void (*action)(void*); + void *handle; +} Timer_t; + +#define IN_ADDTIMEOUT 1 +#define IN_SIGALRM 2 +#define DEFER_SIGALRM 4 +#define SIGALRM_CALL 8 + +static Timer_t *tptop, *tpmin, *tpfree; +static char time_state; + +static double getnow(void) +{ + register double now; +#ifdef timeofday + struct timeval tp; + timeofday(&tp); + now = tp.tv_sec + 1.e-6*tp.tv_usec; + +#else + now = (double)time((time_t*)0); +#endif /* timeofday */ + return(now+.001); +} + +/* + * set an alarm for seconds + */ +static double setalarm(register double t) +{ +#if defined(_lib_setitimer) && defined(ITIMER_REAL) + struct itimerval tnew, told; + tnew.it_value.tv_sec = t; + tnew.it_value.tv_usec = 1.e6*(t- (double)tnew.it_value.tv_sec); + if(t && tnew.it_value.tv_sec==0 && tnew.it_value.tv_usec<1000) + tnew.it_value.tv_usec = 1000; + tnew.it_interval.tv_sec = 0; + tnew.it_interval.tv_usec = 0; + if(setitimer(ITIMER_REAL,&tnew,&told) < 0) + errormsg(SH_DICT,ERROR_system(1),e_alarm); + t = told.it_value.tv_sec + 1.e-6*told.it_value.tv_usec; +#else + unsigned seconds = (unsigned)t; + if(t && seconds<1) + seconds=1; + t = (double)alarm(seconds); +#endif + return(t); +} + +/* signal handler for alarm call */ +static void sigalrm(int sig) +{ + register Timer_t *tp, *tplast, *tpold, *tpnext; + double now; + static double left; + NOT_USED(sig); + left = 0; + if(time_state&SIGALRM_CALL) + time_state &= ~SIGALRM_CALL; + else if(alarm(0)) + sh_fault(SIGALRM|SH_TRAP); + if(time_state) + { + if(time_state&IN_ADDTIMEOUT) + time_state |= DEFER_SIGALRM; + errno = EINTR; + return; + } + time_state |= IN_SIGALRM; + sigrelease(SIGALRM); + while(1) + { + now = getnow(); + tpold = tpmin = 0; + for(tplast=0,tp=tptop; tp; tp=tpnext) + { + tpnext = tp->next; + if(tp->action) + { + if(tp->wakeup <=now) + { + if(!tpold || tpold->wakeup>tp->wakeup) + tpold = tp; + } + else + { + if(!tpmin || tpmin->wakeup>tp->wakeup) + tpmin=tp; + } + tplast = tp; + } + else + { + if(tplast) + tplast->next = tp->next; + else + tptop = tp->next; + tp->next = tpfree; + tpfree = tp; + } + } + if((tp=tpold) && tp->incr) + { + while((tp->wakeup += tp->incr) <= now); + if(!tpmin || tpmin->wakeup>tp->wakeup) + tpmin=tp; + } + if(tpmin && (left==0 || (tp && tpmin->wakeup < (now+left)))) + { + if(left==0) + signal(SIGALRM,sigalrm); + left = setalarm(tpmin->wakeup-now); + if(left && (now+left) < tpmin->wakeup) + setalarm(left); + else + left=tpmin->wakeup-now; + } + if(tp) + { + void (*action)(void*); + action = tp->action; + if(!tp->incr) + tp->action = 0; + errno = EINTR; + time_state &= ~IN_SIGALRM; + (*action)(tp->handle); + time_state |= IN_SIGALRM; + } + else + break; + } + if(!tpmin) + signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL); + time_state &= ~IN_SIGALRM; + errno = EINTR; +} + +static void oldalrm(void *handle) +{ + Handler_t fn = *(Handler_t*)handle; + free(handle); + (*fn)(SIGALRM); +} + +void *sh_timeradd(unsigned long msec,int flags,void (*action)(void*),void *handle) +{ + register Timer_t *tp; + double t; + Handler_t fn; + t = ((double)msec)/1000.; + if(t<=0 || !action) + return((void*)0); + if(tp=tpfree) + tpfree = tp->next; + else if(!(tp=(Timer_t*)malloc(sizeof(Timer_t)))) + return((void*)0); + tp->wakeup = getnow() + t; + tp->incr = (flags?t:0); + tp->action = action; + tp->handle = handle; + time_state |= IN_ADDTIMEOUT; + tp->next = tptop; + tptop = tp; + if(!tpmin || tp->wakeup < tpmin->wakeup) + { + tpmin = tp; + fn = (Handler_t)signal(SIGALRM,sigalrm); + if((t= setalarm(t))>0 && fn && fn!=(Handler_t)sigalrm) + { + Handler_t *hp = (Handler_t*)malloc(sizeof(Handler_t)); + if(hp) + { + *hp = fn; + sh_timeradd((long)(1000*t), 0, oldalrm, (void*)hp); + } + } + tp = tptop; + } + else if(tpmin && !tpmin->action) + time_state |= DEFER_SIGALRM; + time_state &= ~IN_ADDTIMEOUT; + if(time_state&DEFER_SIGALRM) + { + time_state=SIGALRM_CALL; + sigalrm(SIGALRM); + if(tp!=tptop) + tp=0; + } + return((void*)tp); +} + +/* + * delete timer . If is NULL, all timers are deleted + */ +void timerdel(void *handle) +{ + register Timer_t *tp = (Timer_t*)handle; + if(tp) + tp->action = 0; + else + { + for(tp=tptop; tp; tp=tp->next) + tp->action = 0; + if(tpmin) + { + tpmin = 0; + setalarm((double)0); + } + signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL); + } +} + Index: src/lib/libshell/common/sh/nvtree.c =================================================================== --- src/lib/libshell/common/sh/nvtree.c (revision 0) +++ src/lib/libshell/common/sh/nvtree.c (revision 740) @@ -0,0 +1,679 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +/* + * code for tree nodes and name walking + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include "name.h" +#include "argnod.h" + +struct nvdir +{ + Dt_t *root; + Namval_t *hp; + Namval_t *table; + Namval_t *(*nextnode)(Namval_t*,Dt_t*,Namfun_t*); + Namfun_t *fun; + struct nvdir *prev; + int len; + int offset; + char data[1]; +}; + +char *nv_getvtree(Namval_t*, Namfun_t *); +static void put_tree(Namval_t*, const char*, int,Namfun_t*); + +static Namval_t *create_tree(Namval_t *np,const char *name,int flag,Namfun_t *dp) +{ + register Namfun_t *fp=dp; + while(fp=fp->next) + { + if(fp->disc && fp->disc->createf) + { + if(np=(*fp->disc->createf)(np,name,flag,fp)) + dp->last = fp->last; + return(np); + } + } + return((flag&NV_NOADD)?0:np); +} + +static const Namdisc_t treedisc = +{ + 0, + put_tree, + nv_getvtree, + 0, + 0, + create_tree +}; + +static char *nextdot(const char *str) +{ + register char *cp; + if(*str=='.') + str++; + if(*str =='[') + { + cp = nv_endsubscript((Namval_t*)0,(char*)str,0); + return(*cp=='.'?cp:0); + } + else + return(strchr(str,'.')); +} + +static Namfun_t *nextdisc(Namval_t *np) +{ + register Namfun_t *fp; + if(nv_isref(np)) + return(0); + for(fp=np->nvfun;fp;fp=fp->next) + { + if(fp && fp->disc && fp->disc->nextf) + return(fp); + } + return(0); +} + +void *nv_diropen(const char *name) +{ + char *next,*last; + int c,len=strlen(name); + struct nvdir *save, *dp = new_of(struct nvdir,len); + Namval_t *np, fake; + Namfun_t *nfp; + if(!dp) + return(0); + memset((void*)dp, 0, sizeof(*dp)); + last=dp->data; + if(name[len-1]=='*' || name[len-1]=='@') + len -= 1; + name = memcpy(last,name,len); + last[len] = 0; + dp->len = len; + dp->root = sh.var_tree; + dp->table = sh.last_table; + if(*name) + { + fake.nvname = (char*)name; + dp->hp = (Namval_t*)dtprev(dp->root,&fake); + dp->hp = (Namval_t*)dtnext(dp->root,dp->hp); + } + else + dp->hp = (Namval_t*)dtfirst(dp->root); + while(next= nextdot(last)) + { + c = *next; + *next = 0; + np = nv_search(last,dp->root,0); + *next = c; + if(np && ((nfp=nextdisc(np)) || nv_istable(np))) + { + if(!(save = new_of(struct nvdir,0))) + return(0); + *save = *dp; + dp->prev = save; + if(nv_istable(np)) + dp->root = nv_dict(np); + else + dp->root = (Dt_t*)dp; + dp->offset = last-(char*)name; + if(dp->offsetlen = len-dp->offset; + else + dp->len = 0; + if(nfp) + { + dp->nextnode = nfp->disc->nextf; + dp->table = np; + dp->fun = nfp; + dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp); + } + else + dp->nextnode = 0; + } + else + break; + last = next+1; + } + return((void*)dp); +} + + +static Namval_t *nextnode(struct nvdir *dp) +{ + if(dp->nextnode) + return((*dp->nextnode)(dp->hp,dp->root,dp->fun)); + if(dp->len && memcmp(dp->data, dp->hp->nvname, dp->len)) + return(0); + return((Namval_t*)dtnext(dp->root,dp->hp)); +} + +char *nv_dirnext(void *dir) +{ + register struct nvdir *save, *dp = (struct nvdir*)dir; + register Namval_t *np, *last_table; + register char *cp; + Namfun_t *nfp; + while(1) + { + while(np=dp->hp) + { + dp->hp = nextnode(dp); + if(nv_isnull(np)) + continue; + last_table = sh.last_table; + sh.last_table = dp->table; + cp = nv_name(np); + sh.last_table = last_table; + if(!dp->len || memcmp(cp+dp->offset,dp->data,dp->len)==0) + { + if((nfp=nextdisc(np)) || nv_istable(np)) + { + Dt_t *root; + if(nv_istable(np)) + root = nv_dict(np); + else + root = (Dt_t*)dp; + /* check for recursive walk */ + for(save=dp; save; save=save->prev) + { + if(save->root==root) + break; + } + if(save) + continue; + if(!(save = new_of(struct nvdir,0))) + return(0); + *save = *dp; + dp->prev = save; + dp->root = root; + dp->len = 0; + if(nfp && np->nvfun) + { + dp->nextnode = nfp->disc->nextf; + dp->table = np; + dp->fun = nfp; + dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp); + } + else + dp->nextnode = 0; + } + return(cp); + } + } + if(!(save=dp->prev)) + break; +#if 0 + sh.last_table = dp->table; +#endif + *dp = *save; + free((void*)save); + } + return(0); +} + +void nv_dirclose(void *dir) +{ + struct nvdir *dp = (struct nvdir*)dir; + if(dp->prev) + nv_dirclose((void*)dp->prev); + free(dir); +} + +static void outtype(Namval_t *np, Namfun_t *fp, Sfio_t* out, const char *prefix) +{ + char *type=0; + Namval_t *tp = fp->type; + if(!tp && fp->disc && fp->disc->typef) + tp = (*fp->disc->typef)(np,fp); + for(fp=fp->next;fp;fp=fp->next) + { + if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp))) + { + outtype(np,fp,out,prefix); + break; + } + } + if(prefix && *prefix=='t') + type = "-T"; + else if(!prefix) + type = "type"; + if(type) + sfprintf(out,"%s %s ",type,tp->nvname); +} + +/* + * print the attributes of name value pair give by + */ +void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname) +{ + register const Shtable_t *tp; + register char *cp; + register unsigned val; + register unsigned mask; + register unsigned attr; + Namfun_t *fp=0; + for(fp=np->nvfun;fp;fp=fp->next) + { + if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp))) + break; + } +#if 0 + if(!fp && !nv_isattr(np,~NV_ARRAY)) + { + if(!nv_isattr(np,NV_ARRAY) || nv_aindex(np)>=0) + return; + } +#else + if(!fp && !nv_isattr(np,~NV_MINIMAL)) + return; +#endif + + if ((attr=nv_isattr(np,~NV_NOFREE)) || fp) + { + if((attr&NV_NOPRINT)==NV_NOPRINT) + attr &= ~NV_NOPRINT; + if(!attr && !fp) + return; + if(prefix) + sfputr(out,prefix,' '); + for(tp = shtab_attributes; *tp->sh_name;tp++) + { + val = tp->sh_number; + mask = val; + if(fp && (val&NV_INTEGER)) + break; + /* + * the following test is needed to prevent variables + * with E attribute from being given the F + * attribute as well + */ + if(val==(NV_INTEGER|NV_DOUBLE) && (attr&NV_EXPNOTE)) + continue; + if(val&NV_INTEGER) + mask |= NV_DOUBLE; + else if(val&NV_HOST) + mask = NV_HOST; + if((attr&mask)==val) + { + if(val==NV_ARRAY) + { + Namarr_t *ap = nv_arrayptr(np); + if(array_assoc(ap)) + { + if(tp->sh_name[1]!='A') + continue; + } + else if(tp->sh_name[1]=='A') + continue; +#if 0 + cp = "associative"; + else + cp = "indexed"; + if(!prefix) + sfputr(out,cp,' '); + else if(*cp=='i') + tp++; +#endif + } + if(prefix) + { + if(*tp->sh_name=='-') + sfprintf(out,"%.2s ",tp->sh_name); + } + else + sfputr(out,tp->sh_name+2,' '); + if ((val&(NV_LJUST|NV_RJUST|NV_ZFILL)) && !(val&NV_INTEGER) && val!=NV_HOST) + sfprintf(out,"%d ",nv_size(np)); + } + if(val==NV_INTEGER && nv_isattr(np,NV_INTEGER)) + { + if(nv_size(np) != 10) + { + if(nv_isattr(np, NV_DOUBLE)) + cp = "precision"; + else + cp = "base"; + if(!prefix) + sfputr(out,cp,' '); + sfprintf(out,"%d ",nv_size(np)); + } + break; + } + } + if(fp) + outtype(np,fp,out,prefix); + if(noname) + return; + sfputr(out,nv_name(np),'\n'); + } +} + +struct Walk +{ + Sfio_t *out; + Dt_t *root; + int noscope; + int indent; +}; + +static void outval(char *name, const char *vname, struct Walk *wp) +{ + register Namval_t *np, *nq; + register Namfun_t *fp; + int isarray=0, associative=0, special=0; + if(!(np=nv_open(vname,wp->root,NV_ARRAY|NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope))) + return; + if(nv_isarray(np) && *name=='.') + special = 1; + if(!special && (fp=nv_hasdisc(np,&treedisc))) + { + if(!wp->out) + { + fp = nv_stack(np,fp); + if(fp = nv_stack(np,NIL(Namfun_t*))) + free((void*)fp); + np->nvfun = 0; + } + return; + } + if(nv_isnull(np)) + return; + if(special || nv_isarray(np)) + { + isarray=1; + associative= nv_aindex(np)<0; + if(array_elem(nv_arrayptr(np))==0) + isarray=2; + else + nq = nv_putsub(np,NIL(char*),ARRAY_SCAN|(wp->out?ARRAY_NOCHILD:0)); + } + if(!wp->out) + { + _nv_unset(np,NV_RDONLY); + nv_close(np); + return; + } + if(isarray==1 && !nq) + return; + if(special) + { + associative = 1; + sfnputc(wp->out,'\t',wp->indent); + } + else + { + sfnputc(wp->out,'\t',wp->indent); + nv_attribute(np,wp->out,"typeset",'='); + nv_outname(wp->out,name,-1); + sfputc(wp->out,(isarray==2?'\n':'=')); + if(isarray) + { + if(isarray==2) + return; + sfwrite(wp->out,"(\n",2); + sfnputc(wp->out,'\t',++wp->indent); + } + } + while(1) + { + char *fmtq,*ep; + if(isarray && associative) + { + if(!(fmtq = nv_getsub(np))) + break; + sfprintf(wp->out,"[%s]",sh_fmtq(fmtq)); + sfputc(wp->out,'='); + } + if(!(fmtq = sh_fmtq(nv_getval(np)))) + fmtq = ""; + else if(!associative && (ep=strchr(fmtq,'='))) + { + char *qp = strchr(fmtq,'\''); + if(!qp || qp>ep) + { + sfwrite(wp->out,fmtq,ep-fmtq); + sfputc(wp->out,'\\'); + fmtq = ep; + } + } + if(*name=='[' && !isarray) + sfprintf(wp->out,"(%s)\n",fmtq); + else + sfputr(wp->out,fmtq,'\n'); + if(!nv_nextsub(np)) + break; + sfnputc(wp->out,'\t',wp->indent); + } + if(isarray && !special) + { + sfnputc(wp->out,'\t',--wp->indent); + sfwrite(wp->out,")\n",2); + } +} + +/* + * format initialization list given a list of assignments + */ +static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp) +{ + register char *cp,*nextcp,*arg; + register int m,r; + register Sfio_t *outfile = wp->out; + if(n==0) + m = strlen(prefix); + else if(cp=nextdot(prefix)) + m = cp-prefix; + else + m = strlen(prefix)-1; + m++; + if(outfile) + { + sfwrite(outfile,"(\n",2); + wp->indent++; + } + for(; arg= *argv; argv++) + { + cp = arg + n; + if(n==0 && cp[m-1]!='.') + continue; + if(n && cp[m-1]==0) + break; + if(n==0 || strncmp(arg,prefix-n,m+n)==0) + { + cp +=m; + r = 0; + if(*cp=='.') + cp++,r++; + if(nextcp=nextdot(cp)) + { + if(outfile) + { + sfnputc(outfile,'\t',wp->indent); + nv_outname(outfile,cp,nextcp-cp); + sfputc(outfile,'='); + } + argv = genvalue(argv,cp,n+m+r,wp); + if(outfile) + sfputc(outfile,'\n'); + if(*argv) + continue; + break; + } + else if(outfile && argv[1] && memcmp(arg,argv[1],r=strlen(arg))==0 && argv[1][r]=='[') + { + Namval_t *np = nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope); + if(!np) + continue; + sfnputc(outfile,'\t',wp->indent); + nv_attribute(np,outfile,"typeset",1); + nv_close(np); + sfputr(outfile,arg+m+(n?n+1:0),'='); + argv = genvalue(++argv,cp,cp-arg ,wp); + sfputc(outfile,'\n'); + } + else if(outfile && *cp=='[') + { + sfnputc(outfile,'\t',wp->indent); + sfputr(outfile,cp,'='); + argv = genvalue(++argv,cp,cp-arg ,wp); + sfputc(outfile,'\n'); + } + else + outval(cp,arg,wp); + } + else + break; + } + if(outfile) + { + int c = prefix[m-1]; + cp = (char*)prefix; + if(c=='.') + cp[m-1] = 0; + outval(".",prefix-n,wp); + if(c=='.') + cp[m-1] = c; + sfnputc(outfile,'\t',wp->indent-1); + sfputc(outfile,')'); + } + return(--argv); +} + +/* + * walk the virtual tree and print or delete name-value pairs + */ +static char *walk_tree(register Namval_t *np, int dlete) +{ + static Sfio_t *out; + struct Walk walk; + Sfio_t *outfile; + int savtop = staktell(); + char *savptr = stakfreeze(0); + register struct argnod *ap=0; + struct argnod *arglist=0; + char *name,*cp, **argv; + char *subscript=0; + void *dir; + int n=0, noscope=(dlete&NV_NOSCOPE); + stakputs(nv_name(np)); + if(subscript = nv_getsub(np)) + { + stakputc('['); + stakputs(subscript); + stakputc(']'); + stakputc('.'); + } + name = stakfreeze(1); + dir = nv_diropen(name); + if(subscript) + name[strlen(name)-1] = 0; + while(cp = nv_dirnext(dir)) + { + stakseek(ARGVAL); + stakputs(cp); + ap = (struct argnod*)stakfreeze(1); + ap->argflag = ARG_RAW; + ap->argchn.ap = arglist; + n++; + arglist = ap; + } + argv = (char**)stakalloc((n+1)*sizeof(char*)); + argv += n; + *argv = 0; + for(; ap; ap=ap->argchn.ap) + *--argv = ap->argval; + nv_dirclose(dir); + if(dlete&1) + outfile = 0; + else if(!(outfile=out)) + outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); + else + sfseek(outfile,0L,SEEK_SET); + walk.out = outfile; + walk.root = sh.last_root; + walk.indent = 0; + walk.noscope = noscope; + genvalue(argv,name,0,&walk); + stakset(savptr,savtop); + if(!outfile) + return((char*)0); + sfputc(out,0); + return((char*)out->_data); +} + +/* + * get discipline for compound initializations + */ +char *nv_getvtree(register Namval_t *np, Namfun_t *fp) +{ + NOT_USED(fp); + if(nv_isattr(np,NV_BINARY) && nv_isattr(np,NV_RAW)) + return(nv_getv(np,fp)); + if(nv_isattr(np,NV_ARRAY) && nv_arraychild(np,(Namval_t*)0,0)==np) + return(nv_getv(np,fp)); + return(walk_tree(np,0)); +} + +/* + * put discipline for compound initializations + */ +static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t *fp) +{ + struct Namarray *ap; + int nleft = 0; + if(!nv_isattr(np,NV_INTEGER)) + walk_tree(np,(flags&NV_NOSCOPE)|1); + nv_putv(np, val, flags,fp); + if(nv_isattr(np,NV_INTEGER)) + return; + if(ap= nv_arrayptr(np)) + nleft = array_elem(ap); + if(nleft==0) + { + fp = nv_stack(np,fp); + if(fp = nv_stack(np,NIL(Namfun_t*))) + { + free((void*)fp); + } + } +} + +/* + * Insert discipline to cause $x to print current tree + */ +void nv_setvtree(register Namval_t *np) +{ + register Namfun_t *nfp; + if(nv_hasdisc(np, &treedisc)) + return; + nfp = newof(NIL(void*),Namfun_t,1,0); + nfp->disc = &treedisc; + nv_stack(np, nfp); +} + Index: src/lib/libshell/common/sh/init.c =================================================================== --- src/lib/libshell/common/sh/init.c (revision 0) +++ src/lib/libshell/common/sh/init.c (revision 740) @@ -0,0 +1,1465 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * + * Shell initialization + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include +#include +#include +#include "variables.h" +#include "path.h" +#include "fault.h" +#include "name.h" +#include "edit.h" +#include "jobs.h" +#include "io.h" +#include "shlex.h" +#include "builtins.h" +#include "FEATURE/time" +#include "FEATURE/dynamic" +#include "lexstates.h" +#include "version.h" + +#if SHOPT_MULTIBYTE + char e_version[] = "\n@(#)$Id: Version M "SH_RELEASE" $\0\n"; +#else + char e_version[] = "\n@(#)$Id: Version "SH_RELEASE" $\0\n"; +#endif /* SHOPT_MULTIBYTE */ + +#if SHOPT_BASH + extern void bash_init(int); +#endif + +#define RANDMASK 0x7fff +#ifndef CLK_TCK +# define CLK_TCK 60 +#endif /* CLK_TCK */ + +#ifndef environ + extern char **environ; +#endif + +#undef getconf +#define getconf(x) strtol(astconf(x,NiL,NiL),NiL,0) + +struct seconds +{ + Namfun_t hdr; + Shell_t *sh; +}; + +struct rand +{ + Namfun_t hdr; + Shell_t *sh; + int32_t rand_last; +}; + +struct ifs +{ + Namfun_t hdr; + Shell_t *sh; + Namval_t *ifsnp; +}; + +struct shell +{ + Namfun_t hdr; + Shell_t *sh; +}; + +struct match +{ + Namfun_t hdr; + char *val; + char *rval; + int vsize; + int nmatch; + int lastsub; + int match[2*(MATCH_MAX+1)]; +}; + +typedef struct _init_ +{ + Shell_t *sh; +#if SHOPT_FS_3D + Namfun_t VPATH_init; +#endif /* SHOPT_FS_3D */ + struct ifs IFS_init; + struct shell PATH_init; +#ifdef PATH_BFPATH + struct shell FPATH_init; + struct shell CDPATH_init; +#endif + struct shell SHELL_init; + struct shell ENV_init; + struct shell VISUAL_init; + struct shell EDITOR_init; + struct shell OPTINDEX_init; + struct seconds SECONDS_init; + struct rand RAND_init; + struct shell LINENO_init; + struct shell L_ARG_init; + struct match SH_MATCH_init; +#ifdef _hdr_locale + struct shell LC_TYPE_init; + struct shell LC_NUM_init; + struct shell LC_COLL_init; + struct shell LC_MSG_init; + struct shell LC_ALL_init; + struct shell LANG_init; +#endif /* _hdr_locale */ +} Init_t; + +static void env_init(Shell_t*); +static Init_t *nv_init(Shell_t*); +static Dt_t *inittree(Shell_t*,const struct shtable2*); + +#ifdef _WINIX +# define EXE "?(.exe)" +#else +# define EXE +#endif + +static int rand_shift; + + +/* + * Invalidate all path name bindings + */ +static void rehash(register Namval_t *np,void *data) +{ + NOT_USED(data); + nv_onattr(np,NV_NOALIAS); +} + +/* + * out of memory routine for stak routines + */ +static char *nospace(int unused) +{ + NOT_USED(unused); + errormsg(SH_DICT,ERROR_exit(3),e_nospace); + return(NIL(char*)); +} + +/* Trap for VISUAL and EDITOR variables */ +static void put_ed(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + register const char *cp, *name=nv_name(np); + if(*name=='E' && nv_getval(nv_scoped(VISINOD))) + goto done; + sh_offoption(SH_VI); + sh_offoption(SH_EMACS); + sh_offoption(SH_GMACS); + if(!(cp=val) && (*name=='E' || !(cp=nv_getval(nv_scoped(EDITNOD))))) + goto done; + /* turn on vi or emacs option if editor name is either*/ + cp = path_basename(cp); + if(strmatch(cp,"*[Vv][Ii]*")) + sh_onoption(SH_VI); + else if(strmatch(cp,"*gmacs*")) + sh_onoption(SH_GMACS); + else if(strmatch(cp,"*macs*")) + sh_onoption(SH_EMACS); +done: + nv_putv(np, val, flags, fp); +} + +/* Trap for OPTINDEX */ +static void put_optindex(Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + Shell_t *shp = ((struct shell*)fp)->sh; + shp->st.opterror = shp->st.optchar = 0; + nv_putv(np, val, flags, fp); +} + +static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp) +{ + return((Sfdouble_t)*np->nvalue.lp); +} + +/* Trap for restricted variables FPATH, PATH, SHELL, ENV */ +static void put_restricted(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + Shell_t *shp = ((struct shell*)fp)->sh; + int path_scoped = 0; +#ifdef PATH_BFPATH + Pathcomp_t *pp; + char *name = nv_name(np); +#endif + if(!(flags&NV_RDONLY) && sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np)); + if(np==PATHNOD || (path_scoped=(strcmp(name,PATHNOD->nvname)==0))) + { +#ifndef PATH_BFPATH + shp->lastpath = 0; +#endif + nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED); + if(path_scoped && !val) + val = PATHNOD->nvalue.cp; + } + if(val && !(flags&NV_RDONLY) && np->nvalue.cp && strcmp(val,np->nvalue.cp)==0) + return; +#ifdef PATH_BFPATH + if(shp->pathlist && np==FPATHNOD) + shp->pathlist = (void*)path_unsetfpath((Pathcomp_t*)shp->pathlist); +#endif + nv_putv(np, val, flags, fp); +#ifdef PATH_BFPATH + if(shp->pathlist) + { + val = np->nvalue.cp; + if(np==PATHNOD || path_scoped) + pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_PATH); + else if(val && np==FPATHNOD) + pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_FPATH); + else + return; + if(shp->pathlist = (void*)pp) + pp->shp = shp; + if(!val && (flags&NV_NOSCOPE)) + { + Namval_t *mp = dtsearch(shp->var_tree,np); + if(mp && (val=nv_getval(mp))) + nv_putval(mp,val,NV_RDONLY); + } +#if 0 +sfprintf(sfstderr,"%d: name=%s val=%s\n",getpid(),name,val); +path_dump((Pathcomp_t*)shp->pathlist); +#endif + } +#endif +} + +#ifdef PATH_BFPATH +static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + Pathcomp_t *pp; + Shell_t *shp = ((struct shell*)fp)->sh; + nv_putv(np, val, flags, fp); + if(!shp->cdpathlist) + return; + val = np->nvalue.cp; + pp = (void*)path_addpath((Pathcomp_t*)shp->cdpathlist,val,PATH_CDPATH); + if(shp->cdpathlist = (void*)pp) + pp->shp = shp; +} +#endif + +#ifdef _hdr_locale + /* + * This function needs to be modified to handle international + * error message translations + */ +#if ERROR_VERSION >= 20000101L + static char* msg_translate(const char* catalog, const char* message) + { + NOT_USED(catalog); + return((char*)message); + } +#else + static char* msg_translate(const char* message, int type) + { + NOT_USED(type); + return((char*)message); + } +#endif + + /* Trap for LC_ALL, LC_TYPE, LC_MESSAGES, LC_COLLATE and LANG */ + static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp) + { + int type; + char *lc_all = nv_getval(LCALLNOD); + char *name = nv_name(np); + if(name==(LCALLNOD)->nvname) + type = LC_ALL; + else if(name==(LCTYPENOD)->nvname) + type = LC_CTYPE; + else if(name==(LCMSGNOD)->nvname) + type = LC_MESSAGES; + else if(name==(LCCOLLNOD)->nvname) + type = LC_COLLATE; + else if(name==(LCNUMNOD)->nvname) + type = LC_NUMERIC; + else if(name==(LANGNOD)->nvname && (!lc_all || *lc_all==0)) + type = LC_ALL; + else + type= -1; + if(sh_isstate(SH_INIT) && type>=0 && type!=LC_ALL && lc_all && *lc_all) + type= -1; + if(type>=0) + { + if(!setlocale(type,val?val:"")) + { + if(!sh_isstate(SH_INIT) || sh.login_sh==0) + errormsg(SH_DICT,0,e_badlocale,val); + return; + } + } + if(CC_NATIVE==CC_ASCII && (type==LC_ALL || type==LC_CTYPE)) + { + if(sh_lexstates[ST_BEGIN]!=sh_lexrstates[ST_BEGIN]) + free((void*)sh_lexstates[ST_BEGIN]); + if(ast.locale.set&(1<ifsnp = 0; + if(val != np->nvalue.cp) + nv_putv(np, val, flags, fp); + +} + +/* + * This is the lookup function for IFS + * It keeps the sh.ifstable up to date + */ +static char* get_ifs(register Namval_t* np, Namfun_t *fp) +{ + register struct ifs *ip = (struct ifs*)fp; + register char *cp, *value; + register int c,n; + register Shell_t *shp = ip->sh; + value = nv_getv(np,fp); + if(np!=ip->ifsnp) + { + ip->ifsnp = np; + memset(shp->ifstable,0,(1<1) + { + cp += (n-1); + shp->ifstable[c] = S_MBYTE; + continue; + } +#endif /* SHOPT_MULTIBYTE */ + n = S_DELIM; + if(c== *cp) + cp++; + else if(c=='\n') + n = S_NL; + else if(isspace(c)) + n = S_SPACE; + shp->ifstable[c] = n; + } + } + else + { + shp->ifstable[' '] = shp->ifstable['\t'] = S_SPACE; + shp->ifstable['\n'] = S_NL; + } + } + return(value); +} + +/* + * these functions are used to get and set the SECONDS variable + */ +#ifdef timeofday +# define dtime(tp) ((double)((tp)->tv_sec)+1e-6*((double)((tp)->tv_usec))) +# define tms timeval +#else +# define dtime(tp) (((double)times(tp))/sh.lim.clk_tck) +# define timeofday(a) +#endif + +static void put_seconds(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + double d; + struct tms tp; + if(!val) + { + nv_stack(np, NIL(Namfun_t*)); + nv_unset(np); + return; + } + if(!np->nvalue.dp) + { + nv_setsize(np,3); + np->nvalue.dp = new_of(double,0); + } + nv_putv(np, val, flags, fp); + d = *np->nvalue.dp; + timeofday(&tp); + *np->nvalue.dp = dtime(&tp)-d; +} + +static char* get_seconds(register Namval_t* np, Namfun_t *fp) +{ + register int places = nv_size(np); + struct tms tp; + double d, offset = (np->nvalue.dp?*np->nvalue.dp:0); + NOT_USED(fp); + timeofday(&tp); + d = dtime(&tp)- offset; + sfprintf(sh.strbuf,"%.*f",places,d); + return(sfstruse(sh.strbuf)); +} + +static Sfdouble_t nget_seconds(register Namval_t* np, Namfun_t *fp) +{ + struct tms tp; + double offset = (np->nvalue.dp?*np->nvalue.dp:0); + NOT_USED(fp); + timeofday(&tp); + return(dtime(&tp)- offset); +} + +/* + * These three functions are used to get and set the RANDOM variable + */ +static void put_rand(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + struct rand *rp = (struct rand*)fp; + register long n; + if(!val) + { + nv_stack(np, NIL(Namfun_t*)); + nv_unset(np); + return; + } + if(flags&NV_INTEGER) + n = *(double*)val; + else + n = sh_arith(val); + srand((int)(n&RANDMASK)); + rp->rand_last = -1; + if(!np->nvalue.lp) + np->nvalue.lp = &rp->rand_last; +} + +/* + * get random number in range of 0 - 2**15 + * never pick same number twice in a row + */ +static Sfdouble_t nget_rand(register Namval_t* np, Namfun_t *fp) +{ + register long cur, last= *np->nvalue.lp; + NOT_USED(fp); + do + cur = (rand()>>rand_shift)&RANDMASK; + while(cur==last); + *np->nvalue.lp = cur; + return((Sfdouble_t)cur); +} + +static char* get_rand(register Namval_t* np, Namfun_t *fp) +{ + register long n = nget_rand(np,fp); + return(fmtbase(n, 10, 0)); +} + +/* + * These three routines are for LINENO + */ +static Sfdouble_t nget_lineno(Namval_t* np, Namfun_t *fp) +{ + double d=1; + if(error_info.line >0) + d = error_info.line; + else if(error_info.context && error_info.context->line>0) + d = error_info.context->line; + NOT_USED(np); + NOT_USED(fp); + return(d); +} + +static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + register long n; + Shell_t *shp = ((struct shell*)fp)->sh; + if(!val) + { + nv_stack(np, NIL(Namfun_t*)); + nv_unset(np); + return; + } + if(flags&NV_INTEGER) + n = *(double*)val; + else + n = sh_arith(val); + shp->st.firstline += nget_lineno(np,fp)+1-n; +} + +static char* get_lineno(register Namval_t* np, Namfun_t *fp) +{ + register long n = nget_lineno(np,fp); + return(fmtbase(n, 10, 0)); +} + +static char* get_lastarg(Namval_t* np, Namfun_t *fp) +{ + NOT_USED(np); + return(sh.lastarg); +} + +static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + if(flags&NV_INTEGER) + { + sfprintf(sh.strbuf,"%.*g",12,*((double*)val)); + val = sfstruse(sh.strbuf); + } + if(sh.lastarg && !nv_isattr(np,NV_NOFREE)) + free((void*)sh.lastarg); + else + nv_offattr(np,NV_NOFREE); + if(val) + sh.lastarg = strdup(val); + else + sh.lastarg = 0; +} + +static int hasgetdisc(register Namfun_t *fp) +{ + while(fp && !fp->disc->getnum && !fp->disc->getval) + fp = fp->next; + return(fp!=0); +} + +/* + * store the most recent value for use in .sh.match + */ +void sh_setmatch(const char *v, int vsize, int nmatch, int match[]) +{ + struct match *mp = (struct match*)(SH_MATCHNOD->nvfun); + register int i,n; + if(mp->nmatch = nmatch) + { + memcpy(mp->match,match,nmatch*2*sizeof(match[0])); + for(n=match[0],i=1; i < 2*nmatch; i++) + { + if(mp->match[i] < n) + n = mp->match[i]; + } + for(vsize=0,i=0; i < 2*nmatch; i++) + { + if((mp->match[i] -= n) > vsize) + vsize = mp->match[i]; + } + v += n; + if(vsize >= mp->vsize) + { + if(mp->vsize) + mp->val = (char*)realloc(mp->val,vsize+1); + else + mp->val = (char*)malloc(vsize+1); + mp->vsize = vsize; + } + memcpy(mp->val,v,vsize); + mp->val[vsize] = 0; + nv_putsub(SH_MATCHNOD, NIL(char*), nmatch|ARRAY_FILL); + mp->lastsub = -1; + } +} + +#define array_scan(np) ((nv_arrayptr(np)->nelem&ARRAY_SCAN)) + +static char* get_match(register Namval_t* np, Namfun_t *fp) +{ + struct match *mp = (struct match*)fp; + int sub,n; + char *val; + sub = nv_aindex(np); + if(sub>=mp->nmatch) + return(0); + if(sub==mp->lastsub) + return(mp->rval); + if(mp->rval) + { + free((void*)mp->rval); + mp->rval = 0; + } + n = mp->match[2*sub+1]-mp->match[2*sub]; + if(n<=0) + return(""); + val = mp->val+mp->match[2*sub]; + if(mp->val[mp->match[2*sub+1]]==0) + return(val); + mp->rval = (char*)malloc(n+1); + mp->lastsub = sub; + memcpy(mp->rval,val,n); + mp->rval[n] = 0; + return(mp->rval); +} + +static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match }; + +#if SHOPT_FS_3D + /* + * set or unset the mappings given a colon separated list of directories + */ + static void vpath_set(char *str, int mode) + { + register char *lastp, *oldp=str, *newp=strchr(oldp,':'); + if(!sh.lim.fs3d) + return; + while(newp) + { + *newp++ = 0; + if(lastp=strchr(newp,':')) + *lastp = 0; + mount((mode?newp:""),oldp,FS3D_VIEW,0); + newp[-1] = ':'; + oldp = newp; + newp=lastp; + } + } + + /* catch vpath assignments */ + static void put_vpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp) + { + register char *cp; + if(cp = nv_getval(np)) + vpath_set(cp,0); + if(val) + vpath_set((char*)val,1); + nv_putv(np,val,flags,fp); + } + static const Namdisc_t VPATH_disc = { 0, put_vpath }; + static Namfun_t VPATH_init = { &VPATH_disc, 1 }; +#endif /* SHOPT_FS_3D */ + + +static const Namdisc_t IFS_disc = { sizeof(struct ifs), put_ifs, get_ifs }; +const Namdisc_t RESTRICTED_disc = { sizeof(struct shell), put_restricted }; +#ifdef PATH_BFPATH +static const Namdisc_t CDPATH_disc = { sizeof(struct shell), put_cdpath }; +#endif +static const Namdisc_t EDITOR_disc = { sizeof(struct shell), put_ed }; +static const Namdisc_t OPTINDEX_disc = { sizeof(struct shell), put_optindex, 0, nget_optindex }; +static const Namdisc_t SECONDS_disc = { sizeof(struct seconds), put_seconds, get_seconds, nget_seconds }; +static const Namdisc_t RAND_disc = { sizeof(struct rand), put_rand, get_rand, nget_rand }; +static const Namdisc_t LINENO_disc = { sizeof(struct shell), put_lineno, get_lineno, nget_lineno }; +static const Namdisc_t L_ARG_disc = { sizeof(struct shell), put_lastarg, get_lastarg }; + +#if SHOPT_NAMESPACE + static char* get_nspace(Namval_t* np, Namfun_t *fp) + { + if(sh.namespace) + return(nv_name(sh.namespace)); + return((char*)np->nvalue.cp); + } + static const Namdisc_t NSPACE_disc = { 0, 0, get_nspace }; + static Namfun_t NSPACE_init = { &NSPACE_disc, 1}; +#endif /* SHOPT_NAMESPACE */ + +#ifdef _hdr_locale + static const Namdisc_t LC_disc = { sizeof(struct shell), put_lang }; +#endif /* _hdr_locale */ + +/* + * This function will get called whenever a configuration parameter changes + */ +static int newconf(const char *name, const char *path, const char *value) +{ + register char *arg; + if(!name) + setenviron(value); + else if(strcmp(name,"UNIVERSE")==0 && strcmp(astconf(name,0,0),value)) + { + sh.universe = 0; + /* set directory in new universe */ + if(*(arg = path_pwd(0))=='/') + chdir(arg); + /* clear out old tracked alias */ + stakseek(0); + stakputs(nv_getval(PATHNOD)); + stakputc(0); + nv_putval(PATHNOD,stakseek(0),NV_RDONLY); + } + return(1); +} + +#if (CC_NATIVE != CC_ASCII) + static void a2e(char *d, const char *s) + { + register const unsigned char *t; + register int i; + t = CCMAP(CC_ASCII, CC_NATIVE); + for(i=0; i<(1<= 20000102L + error_info.catalog = e_dict; +#endif + sh.cpipe[0] = -1; + sh.coutpipe = -1; + sh.userid=getuid(); + sh.euserid=geteuid(); + sh.groupid=getgid(); + sh.egroupid=getegid(); + for(n=0;n < 10; n++) + { + /* don't use lower bits when rand() generates large numbers */ + if(rand() > RANDMASK) + { + rand_shift = 3; + break; + } + } + sh.lim.clk_tck = getconf("CLK_TCK"); + sh.lim.arg_max = getconf("ARG_MAX"); + sh.lim.open_max = getconf("OPEN_MAX"); + sh.lim.child_max = getconf("CHILD_MAX"); + sh.lim.ngroups_max = getconf("NGROUPS_MAX"); + sh.lim.posix_version = getconf("VERSION"); + sh.lim.posix_jobcontrol = getconf("JOB_CONTROL"); + if(sh.lim.arg_max <=0) + sh.lim.arg_max = ARG_MAX; + if(sh.lim.child_max <=0) + sh.lim.child_max = CHILD_MAX; + if(sh.lim.open_max <0) + sh.lim.open_max = OPEN_MAX; + if(sh.lim.open_max > (SHRT_MAX-2)) + sh.lim.open_max = SHRT_MAX-2; + if(sh.lim.clk_tck <=0) + sh.lim.clk_tck = CLK_TCK; +#if SHOPT_FS_3D + if(fs3d(FS3D_TEST)) + sh.lim.fs3d = 1; +#endif /* SHOPT_FS_3D */ + sh_ioinit(); + /* initialize signal handling */ + sh_siginit(); + stakinstall(NIL(Stak_t*),nospace); + /* set up memory for name-value pairs */ + sh.init_context = nv_init(&sh); + /* read the environment */ + if(argc>0) + { + type = sh_type(*argv); + if(type&SH_TYPE_LOGIN) + sh.login_sh = 2; + } + env_init(&sh); +#if SHOPT_SPAWN + { + /* + * try to find the pathname for this interpreter + * try using environment variable _ or argv[0] + */ + char *last, *cp=nv_getval(L_ARGNOD); + char buff[PATH_MAX+1]; + sh.shpath = 0; + sfprintf(sh.strbuf,"/proc/%d/exe",getpid()); + if((n=readlink(sfstruse(sh.strbuf),buff,sizeof(buff)-1))>0) + { + buff[n] = 0; + sh.shpath = strdup(buff); + } + else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/'))) + { + if(*cp=='/') + sh.shpath = strdup(cp); + else if(cp = nv_getval(PWDNOD)) + { + int offset = staktell(); + stakputs(cp); + stakputc('/'); + stakputs(argv[0]); + pathcanon(stakptr(offset),PATH_DOTDOT); + sh.shpath = strdup(stakptr(offset)); + stakseek(offset); + } + } + } +#endif + nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY); +#if SHOPT_FS_3D + nv_stack(VPATHNOD, &VPATH_init); +#endif /* SHOPT_FS_3D */ + astconfdisc(newconf); +#if SHOPT_TIMEOUT + sh.st.tmout = SHOPT_TIMEOUT; +#endif /* SHOPT_TIMEOUT */ + /* initialize jobs table */ + job_clear(); + if(argc>0) + { + /* check for restricted shell */ + if(type&SH_TYPE_RESTRICTED) + sh_onoption(SH_RESTRICTED); +#if SHOPT_PFSH + /* check for profile shell */ + else if(type&SH_TYPE_PROFILE) + sh_onoption(SH_PFSH); +#endif +#if SHOPT_BASH + /* check for invocation as bash */ + if(type&SH_TYPE_BASH) + { + sh.userinit = userinit = bash_init; + sh_onoption(SH_BASH); + sh_onstate(SH_PREINIT); + (*userinit)(0); + sh_offstate(SH_PREINIT); + } +#endif + /* look for options */ + /* sh.st.dolc is $# */ + if((sh.st.dolc = sh_argopts(-argc,argv)) < 0) + { + sh.exitval = 2; + sh_done(0); + } + opt_info.disc = 0; + sh.st.dolv=argv+(argc-1)-sh.st.dolc; + sh.st.dolv[0] = argv[0]; + if(sh.st.dolc < 1) + sh_onoption(SH_SFLAG); + if(!sh_isoption(SH_SFLAG)) + { + sh.st.dolc--; + sh.st.dolv++; +#if _WINIX + { + char* name; + name = sh.st.dolv[0]; + if(name[1]==':' && (name[2]=='/' || name[2]=='\\')) + { +#if _lib_pathposix + char* p; + + if((n = pathposix(name, NIL(char*), 0)) > 0 && (p = (char*)malloc(++n))) + { + pathposix(name, p, n); + name = p; + } + else +#endif + { + name[1] = name[0]; + name[0] = name[2] = '/'; + } + } + } +#endif /* _WINIX */ + } + } +#if SHOPT_PFSH + if (sh_isoption(SH_PFSH)) + { + struct passwd *pw = getpwuid(sh.userid); + if(pw) + sh.user = strdup(pw->pw_name); + + } +#endif + /* set[ug]id scripts require the -p flag */ + if(sh.userid!=sh.euserid || sh.groupid!=sh.egroupid) + { +#if SHOPT_P_SUID + /* require sh -p to run setuid and/or setgid */ + if(!sh_isoption(SH_PRIVILEGED) && sh.euserid < SHOPT_P_SUID) + { + setuid(sh.euserid=sh.userid); + setgid(sh.egroupid=sh.groupid); + } + else +#else + sh_onoption(SH_PRIVILEGED); +#endif /* SHOPT_P_SUID */ +#ifdef SHELLMAGIC + /* careful of #! setuid scripts with name beginning with - */ + if(sh.login_sh && argv[1] && strcmp(argv[0],argv[1])==0) + errormsg(SH_DICT,ERROR_exit(1),e_prohibited); +#endif /*SHELLMAGIC*/ + } + else + sh_offoption(SH_PRIVILEGED); + /* shname for $0 in profiles and . scripts */ + if(strmatch(argv[1],e_devfdNN)) + sh.shname = strdup(argv[0]); + else + sh.shname = strdup(sh.st.dolv[0]); + /* + * return here for shell script execution + * but not for parenthesis subshells + */ + error_info.id = strdup(sh.st.dolv[0]); /* error_info.id is $0 */ + sh.jmpbuffer = (void*)&sh.checkbase; + sh_pushcontext(&sh.checkbase,SH_JMPSCRIPT); + sh.st.self = &sh.global; + sh.topscope = (Shscope_t*)sh.st.self; + sh_offstate(SH_INIT); + login_files[0] = (char*)e_profile; + login_files[1] = ".profile"; + sh.login_files = login_files; + if(sh.userinit=userinit) + (*userinit)(0); + return(&sh); +} + +Shell_t *sh_getinterp(void) +{ + return(&sh); +} + +/* + * reinitialize before executing a script + */ +int sh_reinit(char *argv[]) +{ + Shopt_t opt; + dtclear(sh.fun_tree); + dtclose(sh.alias_tree); + sh.alias_tree = inittree(&sh,shtab_aliases); + sh.namespace = 0; + sh.inuse_bits = 0; + if(sh.userinit) + (*sh.userinit)(1); + if(sh.heredocs) + { + sfclose(sh.heredocs); + sh.heredocs = 0; + } + /* remove locals */ + sh_onstate(SH_INIT); + nv_scan(sh.var_tree,sh_envnolocal,(void*)0,NV_EXPORT,0); + nv_scan(sh.var_tree,sh_envnolocal,(void*)0,NV_ARRAY,NV_ARRAY); + sh_offstate(SH_INIT); + memset(sh.st.trapcom,0,(sh.st.trapmax+1)*sizeof(char*)); + memset((void*)&opt,0,sizeof(opt)); + if(sh_isoption(SH_TRACKALL)) + on_option(&opt,SH_TRACKALL); + if(sh_isoption(SH_EMACS)) + on_option(&opt,SH_EMACS); + if(sh_isoption(SH_GMACS)) + on_option(&opt,SH_GMACS); + if(sh_isoption(SH_VI)) + on_option(&opt,SH_VI); + if(sh_isoption(SH_VIRAW)) + on_option(&opt,SH_VIRAW); + sh.options = opt; + /* set up new args */ + if(argv) + sh.arglist = sh_argcreate(argv); + if(sh.arglist) + sh_argreset(sh.arglist,NIL(struct dolnod*)); + sh.envlist=0; + sh.curenv = 0; + sh.shname = error_info.id = strdup(sh.st.dolv[0]); + sh_offstate(SH_FORKED); + sh.fn_depth = sh.dot_depth = 0; + sh_sigreset(0); + return(1); +} + +/* + * set when creating a local variable of this name + */ +Namfun_t *nv_cover(register Namval_t *np) +{ +#ifdef PATH_BFPATH + if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==FPATHNOD || np==CDPNOD || np==SECONDS) +#else + if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==SECONDS) +#endif + return(np->nvfun); +#ifdef _hdr_locale + if(np==LCALLNOD || np==LCTYPENOD || np==LCMSGNOD || np==LCCOLLNOD || np==LCNUMNOD || np==LANGNOD) + return(np->nvfun); +#endif + return(0); +} + +static Namtype_t typeset; +static const char *shdiscnames[] = { "tilde", 0}; + +/* + * Initialize the shell name and alias table + */ +static Init_t *nv_init(Shell_t *shp) +{ + Namval_t *np; + register Init_t *ip; + double d=0; + ip = newof(0,Init_t,1,0); + if(!ip) + return(0); + ip->sh = shp; + shp->var_base = shp->var_tree = inittree(shp,shtab_variables); + ip->IFS_init.hdr.disc = &IFS_disc; + ip->IFS_init.hdr.nofree = 1; + ip->IFS_init.sh = shp; + ip->PATH_init.hdr.disc = &RESTRICTED_disc; + ip->PATH_init.hdr.nofree = 1; + ip->PATH_init.sh = shp; +#ifdef PATH_BFPATH + ip->FPATH_init.hdr.disc = &RESTRICTED_disc; + ip->FPATH_init.hdr.nofree = 1; + ip->FPATH_init.sh = shp; + ip->CDPATH_init.hdr.disc = &CDPATH_disc; + ip->CDPATH_init.hdr.nofree = 1; + ip->CDPATH_init.sh = shp; +#endif + ip->SHELL_init.hdr.disc = &RESTRICTED_disc; + ip->SHELL_init.sh = shp; + ip->SHELL_init.hdr.nofree = 1; + ip->ENV_init.hdr.disc = &RESTRICTED_disc; + ip->ENV_init.hdr.nofree = 1; + ip->ENV_init.sh = shp; + ip->VISUAL_init.hdr.disc = &EDITOR_disc; + ip->VISUAL_init.hdr.nofree = 1; + ip->VISUAL_init.sh = shp; + ip->EDITOR_init.hdr.disc = &EDITOR_disc; + ip->EDITOR_init.hdr.nofree = 1; + ip->EDITOR_init.sh = shp; + ip->OPTINDEX_init.hdr.disc = &OPTINDEX_disc; + ip->OPTINDEX_init.hdr.nofree = 1; + ip->OPTINDEX_init.sh = shp; + ip->SECONDS_init.hdr.disc = &SECONDS_disc; + ip->SECONDS_init.hdr.nofree = 1; + ip->SECONDS_init.sh = shp; + ip->RAND_init.hdr.disc = &RAND_disc; + ip->RAND_init.hdr.nofree = 1; + ip->SH_MATCH_init.hdr.disc = &SH_MATCH_disc; + ip->SH_MATCH_init.hdr.nofree = 1; + ip->LINENO_init.hdr.disc = &LINENO_disc; + ip->LINENO_init.hdr.nofree = 1; + ip->LINENO_init.sh = shp; + ip->L_ARG_init.hdr.disc = &L_ARG_disc; + ip->L_ARG_init.hdr.nofree = 1; +#ifdef _hdr_locale + ip->LC_TYPE_init.hdr.disc = &LC_disc; + ip->LC_TYPE_init.hdr.nofree = 1; + ip->LC_NUM_init.hdr.disc = &LC_disc; + ip->LC_NUM_init.hdr.nofree = 1; + ip->LC_COLL_init.hdr.disc = &LC_disc; + ip->LC_COLL_init.hdr.nofree = 1; + ip->LC_MSG_init.hdr.disc = &LC_disc; + ip->LC_MSG_init.hdr.nofree = 1; + ip->LC_ALL_init.hdr.disc = &LC_disc; + ip->LC_ALL_init.hdr.nofree = 1; + ip->LANG_init.hdr.disc = &LC_disc; + ip->LANG_init.hdr.nofree = 1; + ip->LC_TYPE_init.sh = shp; + ip->LC_NUM_init.sh = shp; + ip->LC_COLL_init.sh = shp; + ip->LC_MSG_init.sh = shp; + ip->LANG_init.sh = shp; +#endif /* _hdr_locale */ + nv_stack(IFSNOD, &ip->IFS_init.hdr); + nv_stack(PATHNOD, &ip->PATH_init.hdr); +#ifdef PATH_BFPATH + nv_stack(FPATHNOD, &ip->FPATH_init.hdr); + nv_stack(CDPNOD, &ip->CDPATH_init.hdr); +#endif + nv_stack(SHELLNOD, &ip->SHELL_init.hdr); + nv_stack(ENVNOD, &ip->ENV_init.hdr); + nv_stack(VISINOD, &ip->VISUAL_init.hdr); + nv_stack(EDITNOD, &ip->EDITOR_init.hdr); + nv_stack(OPTINDNOD, &ip->OPTINDEX_init.hdr); + nv_stack(SECONDS, &ip->SECONDS_init.hdr); + nv_stack(L_ARGNOD, &ip->L_ARG_init.hdr); + nv_putval(SECONDS, (char*)&d, NV_INTEGER|NV_DOUBLE); + nv_stack(RANDNOD, &ip->RAND_init.hdr); + d = (shp->pid&RANDMASK); + nv_putval(RANDNOD, (char*)&d, NV_INTEGER|NV_DOUBLE); + nv_stack(LINENO, &ip->LINENO_init.hdr); + nv_putsub(SH_MATCHNOD,(char*)0,10); + nv_onattr(SH_MATCHNOD,NV_RDONLY); + nv_stack(SH_MATCHNOD, &ip->SH_MATCH_init.hdr); +#ifdef _hdr_locale + nv_stack(LCTYPENOD, &ip->LC_TYPE_init.hdr); + nv_stack(LCALLNOD, &ip->LC_ALL_init.hdr); + nv_stack(LCMSGNOD, &ip->LC_MSG_init.hdr); + nv_stack(LCCOLLNOD, &ip->LC_COLL_init.hdr); + nv_stack(LCNUMNOD, &ip->LC_NUM_init.hdr); + nv_stack(LANGNOD, &ip->LANG_init.hdr); +#endif /* _hdr_locale */ + (PPIDNOD)->nvalue.lp = (&shp->ppid); + (TMOUTNOD)->nvalue.lp = (&shp->st.tmout); + (MCHKNOD)->nvalue.lp = (&sh_mailchk); + (OPTINDNOD)->nvalue.lp = (&shp->st.optindex); + /* set up the seconds clock */ + shp->alias_tree = inittree(shp,shtab_aliases); + shp->track_tree = dtopen(&_Nvdisc,Dtset); + shp->bltin_tree = inittree(shp,(const struct shtable2*)shtab_builtins); + typeset.shp = shp; + typeset.optstring = sh_opttypeset; + nv_search("typeset",shp->bltin_tree,0)->nvfun = (void*)&typeset; +#if SHOPT_BASH + nv_search("local",shp->bltin_tree,0)->nvfun = (void*)&typeset; +#endif + shp->fun_tree = dtopen(&_Nvdisc,Dtoset); + dtview(shp->fun_tree,shp->bltin_tree); +#if SHOPT_NAMESPACE + if(np = nv_mount(DOTSHNOD, "global", shp->var_tree)) + nv_onattr(np,NV_RDONLY); + np = nv_search("namespace",nv_dict(DOTSHNOD),NV_ADD); + nv_putval(np,".sh.global",NV_RDONLY|NV_NOFREE); + nv_stack(np, &NSPACE_init); +#endif /* SHOPT_NAMESPACE */ + np = nv_mount(DOTSHNOD, "type", dtopen(&_Nvdisc,Dtoset)); + nv_adddisc(DOTSHNOD, shdiscnames, (Namval_t**)0); + return(ip); +} + +/* + * initialize name-value pairs + */ + +static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals) +{ + register Namval_t *np; + register const struct shtable2 *tp; + register unsigned n = 0; + register Dt_t *treep; + Dt_t *base_treep, *dict; + for(tp=name_vals;*tp->sh_name;tp++) + n++; + np = (Namval_t*)calloc(n,sizeof(Namval_t)); + if(!shp->bltin_nodes) + shp->bltin_nodes = np; + else if(name_vals==(const struct shtable2*)shtab_builtins) + shp->bltin_cmds = np; + base_treep = treep = dtopen(&_Nvdisc,Dtoset); + for(tp=name_vals;*tp->sh_name;tp++,np++) + { + if((np->nvname = strrchr(tp->sh_name,'.')) && np->nvname!=((char*)tp->sh_name)) + np->nvname++; + else + { + np->nvname = (char*)tp->sh_name; + treep = base_treep; + } + np->nvenv = 0; + if(name_vals==(const struct shtable2*)shtab_builtins) + np->nvalue.bfp = ((struct shtable3*)tp)->sh_value; + else + np->nvalue.cp = (char*)tp->sh_value; + nv_setattr(np,tp->sh_number); + if(nv_istable(np)) + nv_mount(np,(const char*)0,dict=dtopen(&_Nvdisc,Dtoset)); + if(nv_isattr(np,NV_INTEGER)) + nv_setsize(np,10); + else + nv_setsize(np,0); + dtinsert(treep,np); + if(nv_istable(np)) + treep = dict; + } + return(treep); +} + +/* + * read in the process environment and set up name-value pairs + * skip over items that are not name-value pairs + */ + +static void env_init(Shell_t *shp) +{ + register char *cp; + register Namval_t *np; + register char **ep=environ; + register char *next=0; +#ifdef _ENV_H + shp->env = env_open(environ,3); + env_delete(shp->env,"_"); +#endif + if(ep) + { + while(cp= *ep++) + { + if(*cp=='A' && cp[1]=='_' && cp[2]=='_' && cp[3]=='z' && cp[4]=='=') + next = cp+4; + else if(np=nv_open(cp,shp->var_tree,(NV_EXPORT|NV_IDENT|NV_ASSIGN|NV_NOFAIL))) + { + nv_onattr(np,NV_IMPORT); + np->nvenv = cp; + nv_close(np); + } + } + while(cp=next) + { + if(next = strchr(++cp,'=')) + *next = 0; + np = nv_search(cp+2,shp->var_tree,NV_ADD); + if(nv_isattr(np,NV_IMPORT|NV_EXPORT)) + { + int flag = *(unsigned char*)cp-' '; + int size = *(unsigned char*)(cp+1)-' '; + if((flag&NV_INTEGER) && size==0) + { + /* check for floating*/ + char *ep,*val = nv_getval(np); + strtol(val,&ep,10); + if(*ep=='.' || *ep=='e' || *ep=='E') + { + char *lp; + flag |= NV_DOUBLE; + if(*ep=='.') + { + strtol(ep+1,&lp,10); + if(*lp) + ep = lp; + } + if(*ep && *ep!='.') + { + flag |= NV_EXPNOTE; + size = ep-val; + } + else + size = strlen(ep); + size--; + } + } + nv_newattr(np,flag|NV_IMPORT|NV_EXPORT,size); + } + } + } +#ifdef _ENV_H + env_delete(sh.env,e_envmarker); +#endif + if(nv_isnull(PWDNOD) || nv_isattr(PWDNOD,NV_TAGGED)) + { + nv_offattr(PWDNOD,NV_TAGGED); + path_pwd(0); + } + if((cp = nv_getval(SHELLNOD)) && (sh_type(cp)&SH_TYPE_RESTRICTED)) + sh_onoption(SH_RESTRICTED); /* restricted shell */ + return; +} + +/* + * terminate shell and free up the space + */ +int sh_term(void) +{ + sfdisc(sfstdin,SF_POPDISC); + free((char*)sh.outbuff); + stakset(NIL(char*),0); + return(0); +} + +/* function versions of these */ + +#define DISABLE /* proto workaround */ + +unsigned long sh_isoption DISABLE (int opt) +{ + return(sh_isoption(opt)); +} + +unsigned long sh_onoption DISABLE (int opt) +{ + return(sh_onoption(opt)); +} + +unsigned long sh_offoption DISABLE (int opt) +{ + return(sh_offoption(opt)); +} + +void sh_sigcheck DISABLE (void) +{ + sh_sigcheck(); +} + +Dt_t* sh_bltin_tree DISABLE (void) +{ + return(sh.bltin_tree); +} Index: src/lib/libshell/common/sh/waitevent.c =================================================================== --- src/lib/libshell/common/sh/waitevent.c (revision 0) +++ src/lib/libshell/common/sh/waitevent.c (revision 740) @@ -0,0 +1,54 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include "defs.h" +/* + * This installs a hook to allow the processing of events when + * the shell is waiting for input and when the shell is + * waiting for job completion. + * The previous waitevent hook function is returned + */ + + +void *sh_waitnotify(int(*newevent)(int,long,int)) +{ + int (*old)(int,long,int); + old = sh.waitevent; + sh.waitevent = newevent; + return((void*)old); +} + +#if __OBSOLETE__ < 20080101 +/* + * this used to be a private symbol + * retain the old name for a bit for a smooth transition + */ + +#if defined(__EXPORT__) +#define extern __EXPORT__ +#endif + +extern void *_sh_waitnotify(int(*newevent)(int,long,int)) +{ + return sh_waitnotify(newevent); +} + +#endif Index: src/lib/libshell/common/sh/string.c =================================================================== --- src/lib/libshell/common/sh/string.c (revision 0) +++ src/lib/libshell/common/sh/string.c (revision 740) @@ -0,0 +1,704 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * string processing routines for Korn shell + * + */ + +#include +#include +#include "defs.h" +#include +#include +#include +#include "shtable.h" +#include "lexstates.h" +#include "national.h" + +#if !SHOPT_MULTIBYTE +#define mbchar(p) (*(unsigned char*)p++) +#endif + +#if _hdr_wctype +# include +#endif + +#if !_lib_iswprint && !defined(iswprint) +# define iswprint(c) (((c)&~0377) || isprint(c)) +#endif + + +/* + * Table lookup routine + * is searched for string and corresponding value is returned + * This is only used for small tables and is used to save non-sharable memory + */ + +const Shtable_t *sh_locate(register const char *sp,const Shtable_t *table,int size) +{ + register int first; + register const Shtable_t *tp; + register int c; + static const Shtable_t empty = {0,0}; + if(sp==0 || (first= *sp)==0) + return(&empty); + tp=table; + while((c= *tp->sh_name) && (CC_NATIVE!=CC_ASCII || c <= first)) + { + if(first == c && strcmp(sp,tp->sh_name)==0) + return(tp); + tp = (Shtable_t*)((char*)tp+size); + } + return(&empty); +} + +/* + * shtab_options lookup routine + */ + +#define sep(c) ((c)=='-'||(c)=='_') + +int sh_lookopt(register const char *sp, int *invert) +{ + register int first; + register const Shtable_t *tp; + register int c; + register const char *s, *t, *sw, *tw; + int amb; + int hit; + int inv; + int no; + if(sp==0) + return(0); + if(*sp=='n' && *(sp+1)=='o' && (*(sp+2)!='t' || *(sp+3)!='i')) + { + sp+=2; + if(sep(*sp)) + sp++; + *invert = !*invert; + } + if((first= *sp)==0) + return(0); + tp=shtab_options; + amb=hit=0; + for(;;) + { + t=tp->sh_name; + if(no = *t=='n' && *(t+1)=='o' && *(t+2)!='t') + t+=2; + if(!(c= *t)) + break; + if(first == c) + { + if(strcmp(sp,t)==0) + { + *invert ^= no; + return(tp->sh_number); + } + s=sw=sp; + tw=t; + for(;;) + { + if(!*s || *s=='=') + { + if (*s == '=' && !strtol(s+1, NiL, 0)) + no = !no; + if (!*t) + { + *invert ^= no; + return(tp->sh_number); + } + if (hit || amb) + { + hit = 0; + amb = 1; + } + else + { + hit = tp->sh_number; + inv = no; + } + break; + } + else if(!*t) + break; + else if(sep(*s)) + sw = ++s; + else if(sep(*t)) + tw = ++t; + else if(*s==*t) + { + s++; + t++; + } + else if(s==sw && t==tw) + break; + else + { + if(t!=tw) + { + while(*t && !sep(*t)) + t++; + if(!*t) + break; + tw = ++t; + } + while (s>sw && *s!=*t) + s--; + } + } + } + tp = (Shtable_t*)((char*)tp+sizeof(*shtab_options)); + } + if(hit) + *invert ^= inv; + return(hit); +} + +/* + * look for the substring in and replace with + * The new string is put on top of the stack + */ +char *sh_substitute(const char *string,const char *oldsp,char *newsp) +/*@ + assume string!=NULL && oldsp!=NULL && newsp!=NULL; + return x satisfying x==NULL || + strlen(x)==(strlen(in string)+strlen(in newsp)-strlen(in oldsp)); +@*/ +{ + register const char *sp = string; + register const char *cp; + const char *savesp = 0; + stakseek(0); + if(*sp==0) + return((char*)0); + if(*(cp=oldsp) == 0) + goto found; +#if SHOPT_MULTIBYTE + mbinit(); +#endif /* SHOPT_MULTIBYTE */ + do + { + /* skip to first character which matches start of oldsp */ + while(*sp && (savesp==sp || *sp != *cp)) + { +#if SHOPT_MULTIBYTE + /* skip a whole character at a time */ + int c = mbsize(sp); + if(c < 0) + sp++; + while(c-- > 0) +#endif /* SHOPT_MULTIBYTE */ + stakputc(*sp++); + } + if(*sp == 0) + return((char*)0); + savesp = sp; + for(;*cp;cp++) + { + if(*cp != *sp++) + break; + } + if(*cp==0) + /* match found */ + goto found; + sp = savesp; + cp = oldsp; + } + while(*sp); + return((char*)0); + +found: + /* copy new */ + stakputs(newsp); + /* copy rest of string */ + stakputs(sp); + return(stakfreeze(1)); +} + +/* + * TRIM(sp) + * Remove escape characters from characters in and eliminate quoted nulls. + */ + +void sh_trim(register char *sp) +/*@ + assume sp!=NULL; + promise strlen(in sp) <= in strlen(sp); +@*/ +{ + register char *dp; + register int c; + if(sp) + { + dp = sp; + while(c= *sp) + { +#if SHOPT_MULTIBYTE + int len; + if(mbwide() && (len=mbsize(sp))>1) + { + dp += len; + sp += len; + continue; + } +#endif /* SHOPT_MULTIBYTE */ + sp++; + if(c == '\\') + c = *sp++; + if(c) + *dp++ = c; + } + *dp = 0; + } +} + +/* + * copy to changing upper case to lower case + * must be big enough to hold + * and may point to the same place. + */ + +void sh_utol(register char const *str1,register char *str2) +/*@ + assume str1!=0 && str2!=0 + return x satisfying strlen(in str1)==strlen(in str2); +@*/ +{ + register int c; + for(; c= *((unsigned char*)str1); str1++,str2++) + { + if(isupper(c)) + *str2 = tolower(c); + else + *str2 = c; + } + *str2 = 0; +} + +/* + * print quoting chars so that it can be read by the shell + * puts null terminated result on stack, but doesn't freeze it + */ +char *sh_fmtq(const char *string) +{ + register const char *cp = string; + register int c, state; + int offset; + if(!cp) + return((char*)0); + offset = staktell(); +#if SHOPT_MULTIBYTE + state = ((c= mbchar(cp))==0); +#else + state = ((c= *(unsigned char*)cp++)==0); +#endif + if(isaletter(c)) + { +#if SHOPT_MULTIBYTE + while((c=mbchar(cp)),isaname(c)); +#else + while((c = *(unsigned char*)cp++),isaname(c)); +#endif + if(c==0) + return((char*)string); + if(c=='=') + { + if(*cp==0) + return((char*)string); + c = cp - string; + stakwrite(string,c); + string = cp; +#if SHOPT_MULTIBYTE + c = mbchar(cp); +#else + c = *(unsigned char*)cp++; +#endif + } + } + if(c==0 || c=='#' || c=='~') + state = 1; +#if SHOPT_MULTIBYTE + for(;c;c= mbchar(cp)) +#else + for(;c; c= *(unsigned char*)cp++) +#endif + { +#if SHOPT_MULTIBYTE + if(c>=0x200) + continue; + if(c=='\'' || !iswprint(c)) +#else + if(c=='\'' || !isprint(c)) +#endif /* SHOPT_MULTIBYTE */ + state = 2; + else if(c==']' || (c!=':' && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT)) + state |=1; + } + if(state<2) + { + if(state==1) + stakputc('\''); + if(c = --cp - string) + stakwrite(string,c); + if(state==1) + stakputc('\''); + } + else + { + stakwrite("$'",2); + cp = string; +#if SHOPT_MULTIBYTE + while(c= mbchar(cp)) +#else + while(c= *(unsigned char*)cp++) +#endif + { + state=1; + switch(c) + { + case ('a'==97?'\033':39): + c = 'E'; + break; + case '\n': + c = 'n'; + break; + case '\r': + c = 'r'; + break; + case '\t': + c = 't'; + break; + case '\f': + c = 'f'; + break; + case '\b': + c = 'b'; + break; + case '\a': + c = 'a'; + break; + case '\\': case '\'': + break; + default: +#if SHOPT_MULTIBYTE + if(!iswprint(c)) +#else + if(!isprint(c)) +#endif + { + sfprintf(staksp,"\\%.3o",c); + continue; + } + state=0; + break; + } + if(state) + stakputc('\\'); + stakputc(c); + } + stakputc('\''); + } + stakputc(0); + return(stakptr(offset)); +} + +/* + * print quoting chars so that it can be read by the shell + * puts null terminated result on stack, but doesn't freeze it + * single!=0 limits quoting to '...' + * fold>0 prints raw newlines and inserts appropriately + * escaped newlines every (fold-x) chars + */ +char *sh_fmtqf(const char *string, int single, int fold) +{ + register const char *cp = string; + register const char *bp; + register const char *vp; + register int c; + register int n; + register int q; + register int a; + int offset; + + if (--fold < 8) + fold = 0; + if (!cp || !*cp || !single && !fold || fold && strlen(string) < fold) + return sh_fmtq(cp); + offset = staktell(); + single = single ? 1 : 3; + c = mbchar(string); + a = isaletter(c) ? '=' : 0; + vp = cp + 1; + do + { + q = 0; + n = fold; + bp = cp; + while ((!n || n-- > 0) && (c = mbchar(cp))) + { + if (a && !isaname(c)) + a = 0; +#if SHOPT_MULTIBYTE + if (c >= 0x200) + continue; + if (c == '\'' || !iswprint(c)) +#else + if (c == '\'' || !isprint(c)) +#endif /* SHOPT_MULTIBYTE */ + { + q = single; + break; + } + if (c == '\n') + q = 1; + else if (c == a) + { + stakwrite(bp, cp - bp); + bp = cp; + vp = cp + 1; + a = 0; + } + else if ((c == '#' || c == '~') && cp == vp || c == ']' || c != ':' && (c = sh_lexstates[ST_NORM][c]) && c != S_EPAT) + q = 1; + } + if (q & 2) + { + stakputc('$'); + stakputc('\''); + cp = bp; + n = fold - 3; + q = 1; + while (c = mbchar(cp)) + { + switch (c) + { + case ('a'==97?'\033':39): + c = 'E'; + break; + case '\n': + q = 0; + n = fold - 1; + break; + case '\r': + c = 'r'; + break; + case '\t': + c = 't'; + break; + case '\f': + c = 'f'; + break; + case '\b': + c = 'b'; + break; + case '\a': + c = 'a'; + break; + case '\\': + if (*cp == 'n') + { + c = '\n'; + q = 0; + n = fold - 1; + break; + } + case '\'': + break; + default: +#if SHOPT_MULTIBYTE + if(!iswprint(c)) +#else + if(!isprint(c)) +#endif + { + if ((n -= 4) <= 0) + { + stakwrite("'\\\n$'", 5); + n = fold - 7; + } + sfprintf(staksp, "\\%03o", c); + continue; + } + q = 0; + break; + } + if ((n -= q + 1) <= 0) + { + if (!q) + { + stakputc('\''); + cp = bp; + break; + } + stakwrite("'\\\n$'", 5); + n = fold - 5; + } + if (q) + stakputc('\\'); + else + q = 1; + stakputc(c); + bp = cp; + } + if (!c) + stakputc('\''); + } + else if (q & 1) + { + stakputc('\''); + cp = bp; + n = fold ? (fold - 2) : 0; + while (c = mbchar(cp)) + { + if (c == '\n') + n = fold - 1; + else if (n && --n <= 0) + { + n = fold - 2; + stakwrite(bp, --cp - bp); + bp = cp; + stakwrite("'\\\n'", 4); + } + else if (n == 1 && *cp == '\'') + { + n = fold - 5; + stakwrite(bp, --cp - bp); + bp = cp; + stakwrite("'\\\n\\''", 6); + } + else if (c == '\'') + { + stakwrite(bp, cp - bp - 1); + bp = cp; + if (n && (n -= 4) <= 0) + { + n = fold - 5; + stakwrite("'\\\n\\''", 6); + } + else + stakwrite("'\\''", 4); + } + } + stakwrite(bp, cp - bp - 1); + stakputc('\''); + } + else if (n = fold) + { + cp = bp; + while (c = mbchar(cp)) + { + if (--n <= 0) + { + n = fold; + stakwrite(bp, --cp - bp); + bp = cp; + stakwrite("\\\n", 2); + } + } + stakwrite(bp, cp - bp - 1); + } + else + stakwrite(bp, cp - bp); + if (c) + { + stakputc('\\'); + stakputc('\n'); + } + } while (c); + stakputc(0); + return(stakptr(offset)); +} + +#if SHOPT_MULTIBYTE + int sh_strchr(const char *string, register const char *dp) + { + wchar_t c, d; + register const char *cp=string; + mbinit(); + d = mbchar(dp); + mbinit(); + while(c = mbchar(cp)) + { + if(c==d) + return(cp-string); + } + if(d==0) + return(cp-string); + return(-1); + } +#endif /* SHOPT_MULTIBYTE */ + +const char *_sh_translate(const char *message) +{ +#if ERROR_VERSION >= 20000317L + return(ERROR_translate(0,0,e_dict,message)); +#else +#if ERROR_VERSION >= 20000101L + return(ERROR_translate(e_dict,message)); +#else + return(ERROR_translate(message,1)); +#endif +#endif +} + +/* + * change '['identifier']' to identifier + * character before must be a '[' + * returns pointer to last character + */ +char *sh_checkid(char *str, char *last) +{ + register unsigned char *cp = (unsigned char*)str; + register unsigned char *v = cp; + register int c; + if(c= *cp++,isaletter(c)) + while(c= *cp++,isaname(c)); + if(c==']' && (!last || ((char*)cp==last))) + { + /* eliminate [ and ] */ + while(v < cp) + { + v[-1] = *v; + v++; + } + if(last) + last -=2; + else + { + while(*v) + { + v[-2] = *v; + v++; + } + v[-2] = 0; + last = (char*)v; + } + } + return(last); +} + +#if _AST_VERSION <= 20000317L +char *fmtident(const char *string) +{ + return((char*)string); +} +#endif Index: src/lib/libshell/common/sh/io.c =================================================================== --- src/lib/libshell/common/sh/io.c (revision 0) +++ src/lib/libshell/common/sh/io.c (revision 740) @@ -0,0 +1,2215 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +/* + * Input/output file processing + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include +#include +#include +#include +#include "variables.h" +#include "path.h" +#include "io.h" +#include "jobs.h" +#include "shnodes.h" +#include "history.h" +#include "edit.h" +#include "timeout.h" +#include "FEATURE/externs" +#include "FEATURE/dynamic" +#include "FEATURE/poll" + +#ifdef FNDELAY +# ifdef EAGAIN +# if EAGAIN!=EWOULDBLOCK +# undef EAGAIN +# define EAGAIN EWOULDBLOCK +# endif +# else +# define EAGAIN EWOULDBLOCK +# endif /* EAGAIN */ +# ifndef O_NONBLOCK +# define O_NONBLOCK FNDELAY +# endif /* !O_NONBLOCK */ +#endif /* FNDELAY */ + +#ifndef O_SERVICE +# define O_SERVICE O_NOCTTY +#endif + +#define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH) + +static void *timeout; +static int (*fdnotify)(int,int); + +#if defined(_lib_socket) && defined(_sys_socket) && defined(_hdr_netinet_in) +# include +# include +# include +# if !defined(htons) && !_lib_htons +# define htons(x) (x) +# endif +# if !defined(htonl) && !_lib_htonl +# define htonl(x) (x) +# endif +# if _pipe_socketpair +# if _socketpair_shutdown_mode +# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[0],1)<0||fchmod((v)[0],S_IRUSR)<0||shutdown((v)[1],0)<0||fchmod((v)[1],S_IWUSR)<0)?(-1):0) +# else +# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[0],1)<0||shutdown((v)[1],0)<0)?(-1):0) +# endif +# endif + +#if !_lib_getaddrinfo + +#undef EAI_SYSTEM + +#define EAI_SYSTEM 1 + +#undef addrinfo +#undef getaddrinfo +#undef freeaddrinfo + +#define addrinfo local_addrinfo +#define getaddrinfo local_getaddrinfo +#define freeaddrinfo local_freeaddrinfo + +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + struct sockaddr* ai_addr; + struct addrinfo* ai_next; +}; + +static int +getaddrinfo(const char* node, const char* service, const struct addrinfo* hint, struct addrinfo **addr) +{ + unsigned long ip_addr = 0; + unsigned short ip_port = 0; + struct addrinfo* ap; + struct hostent* hp; + struct sockaddr_in* ip; + char* prot; + long n; + + if (!(hp = gethostbyname(node)) || hp->h_addrtype!=AF_INET || hp->h_length>sizeof(struct in_addr)) + { + errno = EADDRNOTAVAIL; + return EAI_SYSTEM; + } + ip_addr = (unsigned long)((struct in_addr*)hp->h_addr)->s_addr; + if ((n = strtol(service, &prot, 10)) > 0 && n <= USHRT_MAX && !*prot) + ip_port = htons((unsigned short)n); + else + { + struct servent* sp; + const char* protocol = 0; + + if (hint) + switch (hint->ai_socktype) + { + case SOCK_STREAM: + switch (hint->ai_protocol) + { + case 0: + protocol = "tcp"; + break; +#ifdef IPPROTO_SCTP + case IPPROTO_SCTP: + protocol = "sctp"; + break; +#endif + } + break; + case SOCK_DGRAM: + protocol = "udp"; + break; + } + if (!protocol) + { + errno = EPROTONOSUPPORT; + return 1; + } + if (sp = getservbyname(service, protocol)) + ip_port = sp->s_port; + } + if (!ip_port) + { + errno = EADDRNOTAVAIL; + return EAI_SYSTEM; + } + if (!(ap = newof(0, struct addrinfo, 1, sizeof(struct sockaddr_in)))) + return EAI_SYSTEM; + if (hint) + *ap = *hint; + ap->ai_family = hp->h_addrtype; + ap->ai_addrlen = sizeof(struct sockaddr_in); + ap->ai_addr = (struct sockaddr *)(ap+1); + ip = (struct sockaddr_in *)ap->ai_addr; + ip->sin_family = AF_INET; + ip->sin_port = ip_port; + ip->sin_addr.s_addr = ip_addr; + *addr = ap; + return 0; +} + +static void +freeaddrinfo(struct addrinfo* ap) +{ + if (ap) + free(ap); +} + +#endif + +/* + * return // fd + */ + +typedef int (*Inetintr_f)(struct addrinfo*, void*); + +static int +inetopen(const char* path, int server, Inetintr_f onintr, void* handle) +{ + register char* s; + register char* t; + int fd; + int oerrno; + struct addrinfo hint; + struct addrinfo* addr; + struct addrinfo* p; + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = PF_UNSPEC; + switch (path[0]) + { +#ifdef IPPROTO_SCTP + case 's': + if (path[1]!='c' || path[2]!='t' || path[3]!='p' || path[4]!='/') + { + errno = ENOTDIR; + return -1; + } + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_SCTP; + path += 5; + break; +#endif + case 't': + if (path[1]!='c' || path[2]!='p' || path[3]!='/') + { + errno = ENOTDIR; + return -1; + } + hint.ai_socktype = SOCK_STREAM; + path += 4; + break; + case 'u': + if (path[1]!='d' || path[2]!='p' || path[3]!='/') + { + errno = ENOTDIR; + return -1; + } + hint.ai_socktype = SOCK_DGRAM; + path += 4; + break; + default: + errno = ENOTDIR; + return -1; + } + if (!(s = strdup(path))) + return -1; + if (t = strchr(s, '/')) + { + *t++ = 0; + if (streq(s, "local")) + s = "localhost"; + fd = getaddrinfo(s, t, &hint, &addr); + } + else + fd = -1; + free(s); + if (fd) + { + if (fd != EAI_SYSTEM) + errno = ENOTDIR; + return -1; + } + oerrno = errno; + errno = 0; + fd = -1; + for (p = addr; p; p = p->ai_next) + { + /* + * some api's don't take the hint + */ + + if (!p->ai_protocol) + p->ai_protocol = hint.ai_protocol; + if (!p->ai_socktype) + p->ai_socktype = hint.ai_socktype; + while ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) >= 0) + { + if (server && !bind(fd, p->ai_addr, p->ai_addrlen) && !listen(fd, 5) || !server && !connect(fd, p->ai_addr, p->ai_addrlen)) + goto done; + close(fd); + fd = -1; + if (errno != EINTR || !onintr) + break; + if ((*onintr)(addr, handle)) + return -1; + } + } + done: + freeaddrinfo(addr); + if (fd >= 0) + errno = oerrno; + return fd; +} + +#else + +#undef O_SERVICE + +#endif + +struct fdsave +{ + int orig_fd; /* original file descriptor */ + int save_fd; /* saved file descriptor */ + int subshell; /* saved for subshell */ +}; + +static int subexcept(Sfio_t*, int, void*, Sfdisc_t*); +static int eval_exceptf(Sfio_t*, int, void*, Sfdisc_t*); +static int slowexcept(Sfio_t*, int, void*, Sfdisc_t*); +static int pipeexcept(Sfio_t*, int, void*, Sfdisc_t*); +static ssize_t piperead(Sfio_t*, void*, size_t, Sfdisc_t*); +static ssize_t slowread(Sfio_t*, void*, size_t, Sfdisc_t*); +static ssize_t subread(Sfio_t*, void*, size_t, Sfdisc_t*); +static ssize_t tee_write(Sfio_t*,const void*,size_t,Sfdisc_t*); +static int io_prompt(Sfio_t*,int); +static int io_heredoc(register struct ionod*, const char*, int); +static void sftrack(Sfio_t*,int,int); +static const Sfdisc_t eval_disc = { NULL, NULL, NULL, eval_exceptf, NULL}; +static Sfdisc_t tee_disc = {NULL,tee_write,NULL,NULL,NULL}; +static Sfio_t *subopen(Sfio_t*, off_t, long); +static const Sfdisc_t sub_disc = { subread, 0, 0, subexcept, 0 }; + +struct subfile +{ + Sfdisc_t disc; + Sfio_t *oldsp; + off_t offset; + long size; + long left; +}; + +struct Eof +{ + Namfun_t hdr; + int fd; +}; + +static Sfdouble_t nget_cur_eof(register Namval_t* np, Namfun_t *fp) +{ + struct Eof *ep = (struct Eof*)fp; + Sfoff_t end, cur =lseek(ep->fd, (Sfoff_t)0, SEEK_CUR); + if(*np->nvname=='C') + return((Sfdouble_t)cur); + if(cur<0) + return((Sfdouble_t)-1); + end =lseek(ep->fd, (Sfoff_t)0, SEEK_END); + lseek(ep->fd, (Sfoff_t)0, SEEK_CUR); + return((Sfdouble_t)end); +} + +static const Namdisc_t EOF_disc = { sizeof(struct Eof), 0, 0, nget_cur_eof}; + +#define MATCH_BUFF (64*1024) +struct Match +{ + Sfoff_t offset; + char *base; +}; + +static int matchf(void *handle, char *ptr, size_t size) +{ + struct Match *mp = (struct Match*)handle; + mp->offset += (ptr-mp->base); + return(1); +} + + +static struct fdsave *filemap; +static short filemapsize; + +/* ======== input output and file copying ======== */ + +void sh_ioinit(void) +{ + register int n; + filemapsize = 8; + filemap = (struct fdsave*)malloc(8*sizeof(struct fdsave)); +#if SHOPT_FASTPIPE + n = sh.lim.open_max+2; +#else + n = sh.lim.open_max; +#endif /* SHOPT_FASTPIPE */ + sh.fdstatus = (unsigned char*)malloc((unsigned)n); + memset((char*)sh.fdstatus,0,n); + sh.fdptrs = (int**)malloc(n*sizeof(int*)); + memset((char*)sh.fdptrs,0,n*sizeof(int*)); + sh.sftable = (Sfio_t**)malloc(n*sizeof(Sfio_t*)); + memset((char*)sh.sftable,0,n*sizeof(Sfio_t*)); + sh.sftable[0] = sfstdin; + sh.sftable[1] = sfstdout; + sh.sftable[2] = sfstderr; + sfnotify(sftrack); + sh_iostream(0); + /* all write steams are in the same pool and share outbuff */ + sh.outpool = sfopen(NIL(Sfio_t*),NIL(char*),"sw"); /* pool identifier */ + sh.outbuff = (char*)malloc(IOBSIZE); + sh.errbuff = (char*)malloc(IOBSIZE/4); + sfsetbuf(sfstderr,sh.errbuff,IOBSIZE/4); + sfsetbuf(sfstdout,sh.outbuff,IOBSIZE); + sfpool(sfstdout,sh.outpool,SF_WRITE); + sfpool(sfstderr,sh.outpool,SF_WRITE); + sfset(sfstdout,SF_LINE,0); +} + +/* + * create or initialize a stream corresponding to descriptor + * a buffer with room for a sentinal is allocated for a read stream. + * A discipline is inserted when read stream is a tty or a pipe + * For output streams, the buffer is set to sh.output and put into + * the sh.outpool synchronization pool + */ +Sfio_t *sh_iostream(register int fd) +{ + register Sfio_t *iop; + register int status = sh_iocheckfd(fd); + register int flags = SF_WRITE; + char *bp; +#if SHOPT_FASTPIPE + if(fd>=sh.lim.open_max) + return(sh.sftable[fd]); +#endif /* SHOPT_FASTPIPE */ + if(status==IOCLOSE) + { + switch(fd) + { + case 0: + return(sfstdin); + case 1: + return(sfstdout); + case 2: + return(sfstderr); + } + return(NIL(Sfio_t*)); + } + if(status&IOREAD) + { + if(!(bp = (char *)malloc(IOBSIZE+1))) + return(NIL(Sfio_t*)); + flags |= SF_READ; + if(!(status&IOWRITE)) + flags &= ~SF_WRITE; + } + else + bp = sh.outbuff; + if(status&IODUP) + flags |= SF_SHARE|SF_PUBLIC; + if((iop = sh.sftable[fd]) && sffileno(iop)>=0) + sfsetbuf(iop, bp, IOBSIZE); + else if(!(iop=sfnew((fd<=2?iop:0),bp,IOBSIZE,fd,flags))) + return(NIL(Sfio_t*)); + if(status&IOREAD) + { + Sfdisc_t *dp; + sfset(iop,SF_MALLOC,1); + { + dp = newof(0,Sfdisc_t,1,0); + dp->exceptf = slowexcept; + if(status&IOTTY) + dp->readf = slowread; + else if(status&IONOSEEK) + { + dp->readf = piperead; + sfset(iop, SF_IOINTR,1); + } + else + dp->readf = 0; + dp->seekf = 0; + dp->writef = 0; + sfdisc(iop,dp); + } + } + else + sfpool(iop,sh.outpool,SF_WRITE); + sh.sftable[fd] = iop; + return(iop); +} + +/* + * preserve the file descriptor or stream by moving it + */ +static void io_preserve(register Sfio_t *sp, register int f2) +{ + register int fd; + if(sp) + fd = sfsetfd(sp,10); + else + fd = sh_fcntl(f2,F_DUPFD,10); + if(f2==sh.infd) + sh.infd = fd; + if(fd<0) + errormsg(SH_DICT,ERROR_system(1),e_toomany); + if(sh.fdptrs[fd]=sh.fdptrs[f2]) + { + if(f2==job.fd) + job.fd=fd; + *sh.fdptrs[fd] = fd; + sh.fdptrs[f2] = 0; + } + sh.sftable[fd] = sp; + sh.fdstatus[fd] = sh.fdstatus[f2]; + if(fcntl(f2,F_GETFD,0)&1) + { + fcntl(fd,F_SETFD,FD_CLOEXEC); + sh.fdstatus[fd] |= IOCLEX; + } + sh.sftable[f2] = 0; +} + +/* + * Given a file descriptor , move it to a file descriptor number + * If is needed move it, otherwise it is closed first. + * The original stream is closed. + * The new file descriptor is returned; + */ +int sh_iorenumber(register int f1,register int f2) +{ + register Sfio_t *sp = sh.sftable[f2]; + if(f1!=f2) + { + /* see whether file descriptor is in use */ + if(sh_inuse(f2) || (f2>2 && sp)) + { + if(!(sh.inuse_bits&(1<2) + sh.sftable[fd] = 0; + sh.fdstatus[fd] = IOCLOSE; + if(sh.fdptrs[fd]) + *sh.fdptrs[fd] = -1; + sh.fdptrs[fd] = 0; + if(fd < 10) + sh.inuse_bits &= ~(1<trapnote&SH_SIGSET) + { + freeaddrinfo(addr); + sh_exit(SH_EXITSIG); + return -1; + } + if (sh->trapnote) + sh_chktrap(); + return 0; +} + +/* + * Mimic open(2) with checks for pseudo /dev/ files. + */ +int sh_open(register const char *path, int flags, ...) +{ + register int fd = -1; + mode_t mode; + char *e; + va_list ap; + va_start(ap, flags); + mode = (flags & O_CREAT) ? va_arg(ap, int) : 0; + va_end(ap); + errno = 0; + if(*path==0) + { + errno = ENOENT; + return(-1); + } + if (path[0]=='/' && path[1]=='d' && path[2]=='e' && path[3]=='v' && path[4]=='/') + { + switch (path[5]) + { + case 'f': + if (path[6]=='d' && path[7]=='/') + { + fd = (int)strtol(path+8, &e, 10); + if (*e) + fd = -1; + } + break; + case 's': + if (path[6]=='t' && path[7]=='d') + switch (path[8]) + { + case 'e': + if (path[9]=='r' && path[10]=='r' && !path[11]) + fd = 2; + break; + case 'i': + if (path[9]=='n' && !path[10]) + fd = 0; + break; + case 'o': + if (path[9]=='u' && path[10]=='t' && !path[11]) + fd = 1; + break; + } + } +#ifdef O_SERVICE + if (fd < 0) + { + if ((fd = inetopen(path+5, !!(flags & O_SERVICE), onintr, &sh)) < 0 && errno != ENOTDIR) + return -1; + if (fd >= 0) + goto ok; + } +#endif + } + if (fd >= 0) + { + if((mode=sh_iocheckfd(fd))==IOCLOSE) + return(-1); + flags &= O_ACCMODE; + if(!(mode&IOWRITE) && ((flags==O_WRONLY) || (flags==O_RDWR))) + return(-1); + if(!(mode&IOREAD) && ((flags==O_RDONLY) || (flags==O_RDWR))) + return(-1); + if((fd=dup(fd))<0) + return(-1); + } + else while((fd = open(path, flags, mode)) < 0) + if(errno!=EINTR || sh.trapnote) + return(-1); +#ifdef O_SERVICE + ok: +#endif + flags &= O_ACCMODE; + if(flags==O_WRONLY) + mode = IOWRITE; + else if(flags==O_RDWR) + mode = (IOREAD|IOWRITE); + else + mode = IOREAD; + sh.fdstatus[fd] = mode; + return(fd); +} + +/* + * Open a file for reading + * On failure, print message. + */ +int sh_chkopen(register const char *name) +{ + register int fd = sh_open(name,O_RDONLY,0); + if(fd < 0) + errormsg(SH_DICT,ERROR_system(1),e_open,name); + return(fd); +} + +/* + * move open file descriptor to a number > 2 + */ +int sh_iomovefd(register int fdold) +{ + register int fdnew; + if(fdold<0 || fdold>2) + return(fdold); + fdnew = sh_iomovefd(dup(fdold)); + sh.fdstatus[fdnew] = (sh.fdstatus[fdold]&~IOCLEX); + close(fdold); + sh.fdstatus[fdold] = IOCLOSE; + return(fdnew); +} + +/* + * create a pipe and print message on failure + */ +int sh_pipe(register int pv[]) +{ + int fd[2]; + if(pipe(fd)<0 || (pv[0]=fd[0])<0 || (pv[1]=fd[1])<0) + errormsg(SH_DICT,ERROR_system(1),e_pipe); + pv[0] = sh_iomovefd(pv[0]); + pv[1] = sh_iomovefd(pv[1]); + sh.fdstatus[pv[0]] = IONOSEEK|IOREAD; + sh.fdstatus[pv[1]] = IONOSEEK|IOWRITE; + sh_subsavefd(pv[0]); + sh_subsavefd(pv[1]); + return(0); +} + +static int pat_seek(void *handle, const char *str, size_t sz) +{ + char **bp = (char**)handle; + *bp = (char*)str; + return(-1); +} + +static int pat_line(const regex_t* rp, const char *buff, register size_t n) +{ + register const char *cp=buff, *sp; + while(n>0) + { + for(sp=cp; n-->0 && *cp++ != '\n';); + if(regnexec(rp,sp,cp-sp, 0, (regmatch_t*)0, 0)==0) + return(sp-buff); + } + return(cp-buff); +} + +static int io_patseek(regex_t *rp, Sfio_t* sp, int flags) +{ + char *cp, *match; + int r, fd=sffileno(sp), close_exec = sh.fdstatus[fd]&IOCLEX; + int was_share,s=(PIPE_BUF>SF_BUFSIZE?SF_BUFSIZE:PIPE_BUF); + size_t n,m; + sh.fdstatus[sffileno(sp)] |= IOCLEX; + if(fd==0) + was_share = sfset(sp,SF_SHARE,1); + while((cp=sfreserve(sp, -s, SF_LOCKR)) || (cp=sfreserve(sp,SF_UNBOUND, SF_LOCKR))) + { + m = n = sfvalue(sp); + while(n>0 && cp[n-1]!='\n') + n--; + if(n) + m = n; + r = regrexec(rp,cp,m,0,(regmatch_t*)0, 0, '\n', (void*)&match, pat_seek); + if(r<0) + m = match-cp; + else if(r==2) + { + if((m = pat_line(rp,cp,m)) < n) + r = -1; + } + if(m && (flags&IOCOPY)) + sfwrite(sfstdout,cp,m); + sfread(sp,cp,m); + if(r<0) + break; + } + if(!close_exec) + sh.fdstatus[sffileno(sp)] &= ~IOCLEX; + if(fd==0 && !(was_share&SF_SHARE)) + sfset(sp, SF_SHARE,0); + return(0); +} + +static Sfoff_t file_offset(int fn, char *fname) +{ + Sfio_t *sp = sh.sftable[fn]; + char *cp; + Sfoff_t off; + struct Eof endf; + Namval_t *mp = nv_open("EOF",sh.var_tree,0); + Namval_t *pp = nv_open("CUR",sh.var_tree,0); + memset(&endf,0,sizeof(struct Eof)); + endf.fd = fn; + endf.hdr.disc = &EOF_disc; + endf.hdr.nofree = 1; + if(mp) + nv_stack(mp, &endf.hdr); + if(pp) + nv_stack(pp, &endf.hdr); + if(sp) + sfsync(sp); + off = sh_strnum(fname, &cp, 0); + if(mp) + nv_stack(mp, NiL); + if(pp) + nv_stack(pp, NiL); + return(*cp?(Sfoff_t)-1:off); +} + +/* + * close a pipe + */ +void sh_pclose(register int pv[]) +{ + if(pv[0]>=2) + sh_close(pv[0]); + if(pv[1]>=2) + sh_close(pv[1]); + pv[0] = pv[1] = -1; +} + +/* + * I/O redirection + * flag = 0 if files are to be restored + * flag = 2 if files are to be closed on exec + * flag = 3 when called from $( < ...), just open file and return + * flag = SH_SHOWME for trace only + */ +int sh_redirect(struct ionod *iop, int flag) +{ + Sfoff_t off; + register char *fname; + register int fd, iof; + const char *message = e_open; + int o_mode; /* mode flag for open */ + static char io_op[7]; /* used for -x trace info */ + int clexec=0, fn, traceon; + int r, indx = sh.topfd; + char *after="", *trace = sh.st.trap[SH_DEBUGTRAP]; + Namval_t *np=0; + if(flag==2) + clexec = 1; + if(iop) + traceon = sh_trace(NIL(char**),0); + for(;iop;iop=iop->ionxt) + { + iof=iop->iofile; + fn = (iof&IOUFD); + io_op[0] = '0'+(iof&IOUFD); + if(iof&IOPUT) + { + io_op[1] = '>'; + o_mode = O_WRONLY|O_CREAT; + } + else + { + io_op[1] = '<'; + o_mode = O_RDONLY|O_NONBLOCK; + } + io_op[2] = 0; + io_op[3] = 0; + io_op[4] = 0; + fname = iop->ioname; + if(!(iof&IORAW)) + { + if(iof&IOLSEEK) + { + struct argnod *ap = (struct argnod*)stakalloc(ARGVAL+strlen(iop->ioname)); + memset(ap, 0, ARGVAL); + ap->argflag = ARG_MAC; + strcpy(ap->argval,iop->ioname); + fname=sh_macpat(ap,(iof&IOARITH)?ARG_ARITH:ARG_EXP); + } + else + fname=sh_mactrim(fname,(!sh_isoption(SH_NOGLOB)&&sh_isoption(SH_INTERACTIVE))?2:0); + } + errno=0; + if(iop->iovname) + { + np = nv_open(iop->iovname,sh.var_tree,NV_NOASSIGN|NV_VARNAME); + if(nv_isattr(np,NV_RDONLY)) + errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); + io_op[0] = '}'; + if((iof&IOMOV) && *fname=='-') + fn = nv_getnum(np); + } + if(iof&IOLSEEK) + { + io_op[2] = '#'; + if(iof&IOARITH) + { + strcpy(&io_op[3]," (("); + after = "))"; + } + else if(iof&IOCOPY) + io_op[3] = '#'; + goto traceit; + } + if(*fname) + { + if(iof&IODOC) + { + if(traceon) + sfputr(sfstderr,io_op,'<'); + fd = io_heredoc(iop,fname,traceon); + if(traceon && (flag==SH_SHOWME)) + sh_close(fd); + fname = 0; + } + else if(iof&IOMOV) + { + int dupfd,toclose= -1; + io_op[2] = '&'; + if((fd=fname[0])>='0' && fd<='9') + { + char *number = fname; + dupfd = strtol(fname,&number,10); + if(*number=='-') + { + toclose = dupfd; + number++; + } + if(*number || dupfd > IOUFD) + { + message = e_file; + goto fail; + } + if(sh.subshell && dupfd==1) + { + sh_subtmpfile(); + dupfd = sffileno(sfstdout); + } + else if(sh.sftable[dupfd]) + sfsync(sh.sftable[dupfd]); + } + else if(fd=='-' && fname[1]==0) + { + fd= -1; + goto traceit; + } + else if(fd=='p' && fname[1]==0) + { + if(iof&IOPUT) + dupfd = sh.coutpipe; + else + dupfd = sh.cpipe[0]; + if(flag) + toclose = dupfd; + } + else + { + message = e_file; + goto fail; + } + if(flag==SH_SHOWME) + goto traceit; + if((fd=sh_fcntl(dupfd,F_DUPFD,3))<0) + goto fail; + sh_iocheckfd(dupfd); + sh.fdstatus[fd] = (sh.fdstatus[dupfd]&~IOCLEX); + if(toclose<0 && sh.fdstatus[fd]&IOREAD) + sh.fdstatus[fd] |= IODUP; + else if(dupfd==sh.cpipe[0]) + sh_pclose(sh.cpipe); + else if(toclose>=0) + { + if(flag==0) + sh_iosave(toclose,indx); /* save file descriptor */ + sh_close(toclose); + } + } + else if(iof&IORDW) + { + if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,fname); + io_op[2] = '>'; + o_mode = O_RDWR|O_CREAT; + goto openit; + } + else if(!(iof&IOPUT)) + { + if(flag==SH_SHOWME) + goto traceit; + fd=sh_chkopen(fname); + } + else if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,fname); + else + { + if(iof&IOAPP) + { + io_op[2] = '>'; + o_mode |= O_APPEND; + } + else + { + o_mode |= O_TRUNC; + if(iof&IOCLOB) + io_op[2] = '|'; + else if(sh_isoption(SH_NOCLOBBER)) + { + struct stat sb; + if(stat(fname,&sb)>=0) + { +#if SHOPT_FS_3D + if(S_ISREG(sb.st_mode)&& + (!sh.lim.fs3d || iview(&sb)==0)) +#else + if(S_ISREG(sb.st_mode)) +#endif /* SHOPT_FS_3D */ + { + errno = EEXIST; + errormsg(SH_DICT,ERROR_system(1),e_exists,fname); + } + } + else + o_mode |= O_EXCL; + } + } + openit: + if(flag!=SH_SHOWME) + { + if((fd=sh_open(fname,o_mode,RW_ALL)) <0) + errormsg(SH_DICT,ERROR_system(1),((o_mode&O_CREAT)?e_create:e_open),fname); + } + } + traceit: + if(traceon && fname) + { + if(np) + sfprintf(sfstderr,"{%s",nv_name(np)); + sfprintf(sfstderr,"%s %s%s%c",io_op,fname,after,iop->ionxt?' ':'\n'); + } + if(flag==SH_SHOWME) + return(indx); + if(trace && fname) + { + char *argv[7], **av=argv; + av[3] = io_op; + av[4] = fname; + av[5] = 0; + av[6] = 0; + if(iof&IOARITH) + av[5] = after; + if(np) + { + av[0] = "{"; + av[1] = nv_name(np); + av[2] = "}"; + } + else + av +=3; + sh_debug(trace,(char*)0,(char*)0,av,ARG_NOGLOB); + } + if(iof&IOLSEEK) + { + Sfio_t *sp = sh.sftable[fn]; + r = sh.fdstatus[fn]; + if(!(r&(IOSEEK|IONOSEEK))) + r = sh_iocheckfd(fn); + sfsprintf(io_op,sizeof(io_op),"%d\0",fn); + if(r==IOCLOSE) + { + fname = io_op; + message = e_file; + goto fail; + } + if(iof&IOARITH) + { + if(r&IONOSEEK) + { + fname = io_op; + message = e_notseek; + goto fail; + } + message = e_badseek; + if((off = file_offset(fn,fname))<0) + goto fail; + if(sp) + r=sfseek(sp, off, SEEK_SET); + else + r=lseek(fn, off, SEEK_SET); + } + else + { + regex_t *rp; + extern const char e_notimp[]; + if(!(r&IOREAD)) + { + message = e_noread; + goto fail; + } + if(!(rp = regcache(fname, REG_SHELL|REG_NOSUB|REG_NEWLINE|REG_AUGMENTED|REG_FIRST|REG_LEFT|REG_RIGHT, &r))) + { + message = e_badpattern; + goto fail; + } + if(!sp) + sp = sh_iostream(fn); + r=io_patseek(rp,sp,iof); + if(sp && flag==3) + { + /* close stream but not fn */ + sfsetfd(sp,-1); + sfclose(sp); + } + } + if(r<0) + goto fail; + if(flag==3) + return(fn); + continue; + } + if(!np) + { + if(flag==0) + { + if(fd==fn) + { + if((r=sh_fcntl(fd,F_DUPFD,10)) > 0) + { + fd = r; + sh_close(fn); + } + } + sh_iosave(fn,indx); + } + else if(sh_subsavefd(fn)) + sh_iosave(fn,indx|IOSUBSHELL); + } + if(fd<0) + { + if(sh_inuse(fn) || fn==sh.infd) + { + if(fn>9 || !(sh.inuse_bits&(1<=0) + { + if(np) + { + int32_t v; + fn = fd; + if(fd<10) + { + if((fn=fcntl(fd,F_DUPFD,10)) < 0) + goto fail; + sh.fdstatus[fn] = sh.fdstatus[fd]; + sh_close(fd); + fd = fn; + } + nv_unset(np); + nv_onattr(np,NV_INT32); + v = fn; + nv_putval(np,(char*)&v, NV_INT32); + sh_iocheckfd(fd); + } + else + { + fd = sh_iorenumber(sh_iomovefd(fd),fn); + if(fn>2 && fn<10) + sh.inuse_bits |= (1<2 && clexec) + { + fcntl(fd,F_SETFD,FD_CLOEXEC); + sh.fdstatus[fd] |= IOCLEX; + } + } + else + goto fail; + } + return(indx); +fail: + errormsg(SH_DICT,ERROR_system(1),message,fname); + /* NOTREACHED */ + return(0); +} +/* + * Create a tmp file for the here-document + */ +static int io_heredoc(register struct ionod *iop, const char *name, int traceon) +{ + register Sfio_t *infile = 0, *outfile; + register int fd; + if(!(iop->iofile&IOSTRG) && (!sh.heredocs || iop->iosize==0)) + return(sh_open(e_devnull,O_RDONLY)); + /* create an unnamed temporary file */ + if(!(outfile=sftmp(0))) + errormsg(SH_DICT,ERROR_system(1),e_tmpcreate); + if(iop->iofile&IOSTRG) + { + if(traceon) + sfprintf(sfstderr,"< %s\n",name); + sfputr(outfile,name,'\n'); + } + else + { + infile = subopen(sh.heredocs,iop->iooffset,iop->iosize); + if(traceon) + { + char *cp = sh_fmtq(iop->iodelim); + fd = (*cp=='$' || *cp=='\'')?' ':'\\'; + sfprintf(sfstderr," %c%s\n",fd,cp); + sfdisc(outfile,&tee_disc); + } + if(iop->iofile&IOQUOTE) + { + /* This is a quoted here-document, not expansion */ + sfmove(infile,outfile,SF_UNBOUND,-1); + sfclose(infile); + } + else + { + char *lastpath = sh.lastpath; + sh_machere(infile,outfile,iop->ioname); + sh.lastpath = lastpath; + if(infile) + sfclose(infile); + } + } + /* close stream outfile, but save file descriptor */ + fd = sffileno(outfile); + sfsetfd(outfile,-1); + sfclose(outfile); + if(traceon && !(iop->iofile&IOSTRG)) + sfputr(sfstderr,iop->ioname,'\n'); + lseek(fd,(off_t)0,SEEK_SET); + sh.fdstatus[fd] = IOREAD; + return(fd); +} + +/* + * This write discipline also writes the output on standard error + * This is used when tracing here-documents + */ +static ssize_t tee_write(Sfio_t *iop,const void *buff,size_t n,Sfdisc_t *unused) +{ + NOT_USED(unused); + sfwrite(sfstderr,buff,n); + return(write(sffileno(iop),buff,n)); +} + +/* + * copy file into a save place + * The saved file is set close-on-exec + * if < 0, then -origfd is saved, but not duped so that it + * will be closed with sh_iorestore. + */ +void sh_iosave(register int origfd, int oldtop) +{ +/*@ + assume oldtop>=0 && oldtop=oldtop; ) + { + if(filemap[savefd].orig_fd == origfd) + return; + } + /* make sure table is large enough */ + if(sh.topfd >= filemapsize) + { + filemapsize += 8; + if(!(filemap = (struct fdsave*)realloc(filemap,filemapsize*sizeof(struct fdsave)))) + errormsg(SH_DICT,ERROR_exit(4),e_nospace); + + } +#if SHOPT_DEVFD + if(origfd <0) + { + savefd = origfd; + origfd = -origfd; + } + else +#endif /* SHOPT_DEVFD */ + { + if((savefd = sh_fcntl(origfd, F_DUPFD, 10)) < 0 && errno!=EBADF) + errormsg(SH_DICT,ERROR_system(1),e_toomany); + } + filemap[sh.topfd].subshell = flag; + filemap[sh.topfd].orig_fd = origfd; + filemap[sh.topfd++].save_fd = savefd; + if(savefd >=0) + { + register Sfio_t* sp = sh.sftable[origfd]; + /* make saved file close-on-exec */ + sh_fcntl(savefd,F_SETFD,FD_CLOEXEC); + if(origfd==job.fd) + job.fd = savefd; + sh.fdstatus[savefd] = sh.fdstatus[origfd]; + sh.fdptrs[savefd] = &filemap[sh.topfd-1].save_fd; + if(!(sh.sftable[savefd]=sp)) + return; + sfsync(sp); + if(origfd <=2) + { + /* copy standard stream to new stream */ + sp = sfswap(sp,NIL(Sfio_t*)); + sh.sftable[savefd] = sp; + } + else + sh.sftable[origfd] = 0; + } +} + +/* + * close all saved file descriptors + */ +void sh_iounsave(void) +{ + register int fd, savefd, newfd; + for(newfd=fd=0; fd < sh.topfd; fd++) + { + if((savefd = filemap[fd].save_fd)< 0) + filemap[newfd++] = filemap[fd]; + else + { + sh.sftable[savefd] = 0; + sh_close(savefd); + } + } + sh.topfd = newfd; +} + +/* + * restore saved file descriptors from on + */ +void sh_iorestore(int last, int jmpval) +{ + register int origfd, savefd, fd; + int flag = (last&IOSUBSHELL); + last &= ~IOSUBSHELL; + for (fd = sh.topfd - 1; fd >= last; fd--) + { + if(!flag && filemap[fd].subshell) + continue; + if(jmpval==SH_JMPSCRIPT) + { + if ((savefd = filemap[fd].save_fd) >= 0) + { + sh.sftable[savefd] = 0; + sh_close(savefd); + } + continue; + } + origfd = filemap[fd].orig_fd; + sh_close(origfd); + if ((savefd = filemap[fd].save_fd) >= 0) + { + sh_fcntl(savefd, F_DUPFD, origfd); + if(savefd==job.fd) + job.fd=origfd; + sh.fdstatus[origfd] = sh.fdstatus[savefd]; + /* turn off close-on-exec if flag if necessary */ + if(sh.fdstatus[origfd]&IOCLEX) + fcntl(origfd,F_SETFD,FD_CLOEXEC); + if(origfd<=2) + { + sfswap(sh.sftable[savefd],sh.sftable[origfd]); + if(origfd==0) + sh.st.ioset = 0; + } + else + sh.sftable[origfd] = sh.sftable[savefd]; + sh.sftable[savefd] = 0; + sh_close(savefd); + } + else + sh.fdstatus[origfd] = IOCLOSE; + } + if(!flag) + { + /* keep file descriptors for subshell restore */ + for (fd = last ; fd < sh.topfd; fd++) + { + if(filemap[fd].subshell) + filemap[last++] = filemap[fd]; + } + } + if(last < sh.topfd) + sh.topfd = last; +} + +/* + * returns access information on open file + * returns -1 for failure, 0 for success + * is the same as for access() + */ +int sh_ioaccess(int fd,register int mode) +{ + register int flags; + if(mode==X_OK) + return(-1); + if((flags=sh_iocheckfd(fd))!=IOCLOSE) + { + if(mode==F_OK) + return(0); + if(mode==R_OK && (flags&IOREAD)) + return(0); + if(mode==W_OK && (flags&IOWRITE)) + return(0); + } + return(-1); +} + +/* + * Handle interrupts for slow streams + */ +static int slowexcept(register Sfio_t *iop,int type,void *data,Sfdisc_t *handle) +{ + register int n,fno; + NOT_USED(handle); + if(type==SF_DPOP || type==SF_FINAL) + free((void*)handle); + if(type!=SF_READ) + return(0); + if((sh.trapnote&(SH_SIGSET|SH_SIGTRAP)) && errno!=EIO && errno!=ENXIO) + errno = EINTR; + fno = sffileno(iop); + if((n=sfvalue(iop))<=0) + { +#ifndef FNDELAY +# ifdef O_NDELAY + if(errno==0 && (n=fcntl(fno,F_GETFL,0))&O_NDELAY) + { + n &= ~O_NDELAY; + fcntl(fno, F_SETFL, n); + return(1); + } +# endif /* O_NDELAY */ +#endif /* !FNDELAY */ +#ifdef O_NONBLOCK + if(errno==EAGAIN) + { + n = fcntl(fno,F_GETFL,0); + n &= ~O_NONBLOCK; + fcntl(fno, F_SETFL, n); + return(1); + } +#endif /* O_NONBLOCK */ + if(errno!=EINTR) + return(0); + n=1; + } + errno = 0; + if(sh.trapnote&SH_SIGSET) + { + if(isatty(fno)) + sfputc(sfstderr,'\n'); + sh_exit(SH_EXITSIG); + } + if(sh.trapnote&SH_SIGTRAP) + sh_chktrap(); + return(n); +} + +/* + * called when slowread times out + */ +static void time_grace(void *handle) +{ + NOT_USED(handle); + timeout = 0; + if(sh_isstate(SH_GRACE)) + { + sh_offstate(SH_GRACE); + if(!sh_isstate(SH_INTERACTIVE)) + return; + ((struct checkpt*)sh.jmplist)->mode = SH_JMPEXIT; + errormsg(SH_DICT,2,e_timeout); + sh.trapnote |= SH_SIGSET; + return; + } + errormsg(SH_DICT,0,e_timewarn); + sh_onstate(SH_GRACE); + sigrelease(SIGALRM); + sh.trapnote |= SH_SIGTRAP; +} + +static ssize_t piperead(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *handle) +{ + int fd = sffileno(iop); + NOT_USED(handle); + if(sh.trapnote) + { + errno = EINTR; + return(-1); + } + if(sh_isstate(SH_INTERACTIVE) && io_prompt(iop,sh.nextprompt)<0 && errno==EIO) + return(0); + if(!(sh.fdstatus[sffileno(iop)]&IOCLEX) && (sfset(iop,0,0)&SF_SHARE)) + size = ed_read(sh.ed_context, fd, (char*)buff, size,0); + else + size = sfrd(iop,buff,size,handle); + return(size); +} +/* + * This is the read discipline that is applied to slow devices + * This routine takes care of prompting for input + */ +static ssize_t slowread(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *handle) +{ + int (*readf)(void*, int, char*, int, int); + int reedit=0, rsize; +#if SHOPT_HISTEXPAND + char *xp=0; +#endif + NOT_USED(handle); +# if SHOPT_ESH + if(sh_isoption(SH_EMACS) || sh_isoption(SH_GMACS)) + readf = ed_emacsread; + else +# endif /* SHOPT_ESH */ +# if SHOPT_VSH +# if SHOPT_RAWONLY + if(sh_isoption(SH_VI) || ((SHOPT_RAWONLY-0) && mbwide())) +# else + if(sh_isoption(SH_VI)) +# endif + readf = ed_viread; + else +# endif /* SHOPT_VSH */ + readf = ed_read; + if(sh.trapnote) + { + errno = EINTR; + return(-1); + } + while(1) + { + if(io_prompt(iop,sh.nextprompt)<0 && errno==EIO) + return(0); + if(sh.timeout) + timeout = (void*)sh_timeradd(sh_isstate(SH_GRACE)?1000L*TGRACE:1000L*sh.timeout,0,time_grace,NIL(void*)); + rsize = (*readf)(sh.ed_context, sffileno(iop), (char*)buff, size, reedit); + if(timeout) + timerdel(timeout); + timeout=0; +#if SHOPT_HISTEXPAND + if(rsize && *(char*)buff != '\n' && sh.nextprompt==1 && sh_isoption(SH_HISTEXPAND)) + { + int r; + ((char*)buff)[rsize] = '\0'; + if(xp) + { + free(xp); + xp = 0; + } + r = hist_expand(buff, &xp); + if((r & (HIST_EVENT|HIST_PRINT)) && !(r & HIST_ERROR) && xp) + { + strlcpy(buff, xp, size); + rsize = strlen(buff); + if(!sh_isoption(SH_HISTVERIFY) || readf==ed_read) + { + sfputr(sfstderr, xp, -1); + break; + } + reedit = rsize - 1; + continue; + } + if((r & HIST_ERROR) && sh_isoption(SH_HISTREEDIT)) + { + reedit = rsize - 1; + continue; + } + if(r & (HIST_ERROR|HIST_PRINT)) + { + *(char*)buff = '\n'; + rsize = 1; + } + } +#endif + break; + } + return(rsize); +} + +/* + * check and return the attributes for a file descriptor + */ + +int sh_iocheckfd(register int fd) +{ + register int flags, n; + if((n=sh.fdstatus[fd])&IOCLOSE) + return(n); + if(!(n&(IOREAD|IOWRITE))) + { +#ifdef F_GETFL + if((flags=fcntl(fd,F_GETFL,0)) < 0) + return(sh.fdstatus[fd]=IOCLOSE); + if((flags&O_ACCMODE)!=O_WRONLY) + n |= IOREAD; + if((flags&O_ACCMODE)!=O_RDONLY) + n |= IOWRITE; +#else + struct stat statb; + if((flags = fstat(fd,&statb))< 0) + return(sh.fdstatus[fd]=IOCLOSE); + n |= (IOREAD|IOWRITE); + if(read(fd,"",0) < 0) + n &= ~IOREAD; +#endif /* F_GETFL */ + } + if(!(n&(IOSEEK|IONOSEEK))) + { + struct stat statb; + /* /dev/null check is a workaround for select bug */ + static ino_t null_ino; + static dev_t null_dev; + if(null_ino==0 && stat(e_devnull,&statb) >=0) + { + null_ino = statb.st_ino; + null_dev = statb.st_dev; + } + if(tty_check(fd)) + n |= IOTTY; + if(lseek(fd,NIL(off_t),SEEK_CUR)<0) + { + n |= IONOSEEK; +#ifdef S_ISSOCK + if((fstat(fd,&statb)>=0) && S_ISSOCK(statb.st_mode)) + n |= IOREAD|IOWRITE; +#endif /* S_ISSOCK */ + } + else if((fstat(fd,&statb)>=0) && ( + S_ISFIFO(statb.st_mode) || +#ifdef S_ISSOCK + S_ISSOCK(statb.st_mode) || +#endif /* S_ISSOCK */ + /* The following is for sockets on the sgi */ + (statb.st_ino==0 && (statb.st_mode & ~(S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH|S_IXUSR|S_IXGRP|S_IXOTH|S_ISUID|S_ISGID))==0) || + (S_ISCHR(statb.st_mode) && (statb.st_ino!=null_ino || statb.st_dev!=null_dev)) + )) + n |= IONOSEEK; + else + n |= IOSEEK; + } + sh.fdstatus[fd] = n; + return(n); +} + +/* + * Display prompt PS on standard error + */ + +static int io_prompt(Sfio_t *iop,register int flag) +{ + register char *cp; + char buff[1]; + char *endprompt; + static short cmdno; + int sfflags; + if(flag<3 && !sh_isstate(SH_INTERACTIVE)) + flag = 0; + if(flag==2 && sfpkrd(sffileno(iop),buff,1,'\n',0,1) >= 0) + flag = 0; + if(flag==0) + return(sfsync(sfstderr)); + sfflags = sfset(sfstderr,SF_SHARE|SF_PUBLIC|SF_READ,0); + if(!(sh.prompt=(char*)sfreserve(sfstderr,0,0))) + sh.prompt = ""; + switch(flag) + { + case 1: + { + register int c; +#if defined(TIOCLBIC) && defined(LFLUSHO) + if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS)) + { + /* + * re-enable output in case the user has + * disabled it. Not needed with edit mode + */ + int mode = LFLUSHO; + ioctl(sffileno(sfstderr),TIOCLBIC,&mode); + } +#endif /* TIOCLBIC */ + cp = sh_mactry(nv_getval(nv_scoped(PS1NOD))); + for(;c= *cp;cp++) + { + if(c==HIST_CHAR) + { + /* look at next character */ + c = *++cp; + /* print out line number if not !! */ + if(c!= HIST_CHAR) + { + sfprintf(sfstderr,"%d", sh.hist_ptr?(int)sh.hist_ptr->histind:++cmdno); + } + if(c==0) + goto done; + } + sfputc(sfstderr,c); + } + goto done; + } + case 2: + cp = nv_getval(nv_scoped(PS2NOD)); + break; + case 3: + cp = nv_getval(nv_scoped(PS3NOD)); + break; + default: + goto done; + } + if(cp) + sfputr(sfstderr,cp,-1); +done: + if(*sh.prompt && (endprompt=(char*)sfreserve(sfstderr,0,0))) + *endprompt = 0; + sfset(sfstderr,sfflags&SF_READ|SF_SHARE|SF_PUBLIC,1); + return(sfsync(sfstderr)); +} + +/* + * This discipline is inserted on write pipes to prevent SIGPIPE + * from causing an infinite loop + */ +static int pipeexcept(Sfio_t* iop, int mode, void *data, Sfdisc_t* handle) +{ + NOT_USED(iop); + if(mode==SF_DPOP || mode==SF_FINAL) + free((void*)handle); + else if(mode==SF_WRITE && errno==EINTR && sh.lastsig==SIGPIPE) + return(-1); + return(0); +} + +/* + * keep track of each stream that is opened and closed + */ +static void sftrack(Sfio_t* sp,int flag, int newfd) +{ + register int fd = sffileno(sp); + register struct checkpt *pp; + register int mode; + if(flag==SF_SETFD || flag==SF_CLOSING) + { + if(newfd<0) + flag = SF_CLOSING; + if(fdnotify) + (*fdnotify)(sffileno(sp),flag==SF_CLOSING?-1:newfd); + } +#ifdef DEBUG + if(flag==SF_READ || flag==SF_WRITE) + { + char *z = fmtbase((long)getpid(),0,0); + write(ERRIO,z,strlen(z)); + write(ERRIO,": ",2); + write(ERRIO,"attempt to ",11); + if(flag==SF_READ) + write(ERRIO,"read from",9); + else + write(ERRIO,"write to",8); + write(ERRIO," locked stream\n",15); + return; + } +#endif + if((unsigned)fd >= sh.lim.open_max) + return; + if(sh_isstate(SH_NOTRACK)) + return; + mode = sfset(sp,0,0); + if(sp==sh.heredocs && fd < 10 && flag==SF_NEW) + { + fd = sfsetfd(sp,10); + fcntl(fd,F_SETFD,FD_CLOEXEC); + } + if(fd < 3) + return; + if(flag==SF_NEW) + { + if(!sh.sftable[fd] && sh.fdstatus[fd]==IOCLOSE) + { + sh.sftable[fd] = sp; + flag = (mode&SF_WRITE)?IOWRITE:0; + if(mode&SF_READ) + flag |= IOREAD; + sh.fdstatus[fd] = flag; +#if 0 + if(flag==IOWRITE) + sfpool(sp,sh.outpool,SF_WRITE); + else +#else + if(flag!=IOWRITE) +#endif + sh_iostream(fd); + } + if((pp=(struct checkpt*)sh.jmplist) && pp->mode==SH_JMPCMD) + { + struct openlist *item; + /* + * record open file descriptors so they can + * be closed in case a longjmp prevents + * built-ins from cleanup + */ + item = new_of(struct openlist, 0); + item->strm = sp; + item->next = pp->olist; + pp->olist = item; + } + if(fdnotify) + (*fdnotify)(-1,sffileno(sp)); + } + else if(flag==SF_CLOSING || (flag==SF_SETFD && newfd<=2)) + { + sh.sftable[fd] = 0; + sh.fdstatus[fd]=IOCLOSE; + if(pp=(struct checkpt*)sh.jmplist) + { + struct openlist *item; + for(item=pp->olist; item; item=item->next) + { + if(item->strm == sp) + { + item->strm = 0; + break; + } + } + } + } +} + +struct eval +{ + Sfdisc_t disc; + char **argv; + short slen; + char addspace; +}; + +/* + * Create a stream consisting of a space separated argv[] list + */ + +Sfio_t *sh_sfeval(register char *argv[]) +{ + register Sfio_t *iop; + register char *cp; + if(argv[1]) + cp = ""; + else + cp = argv[0]; + iop = sfopen(NIL(Sfio_t*),(char*)cp,"s"); + if(argv[1]) + { + register struct eval *ep; + if(!(ep = new_of(struct eval,0))) + return(NIL(Sfio_t*)); + ep->disc = eval_disc; + ep->argv = argv; + ep->slen = -1; + ep->addspace = 0; + sfdisc(iop,&ep->disc); + } + return(iop); +} + +/* + * This code gets called whenever an end of string is found with eval + */ + +static int eval_exceptf(Sfio_t *iop,int type, void *data, Sfdisc_t *handle) +{ + register struct eval *ep = (struct eval*)handle; + register char *cp; + register int len; + + /* no more to do */ + if(type!=SF_READ || !(cp = ep->argv[0])) + { + if(type==SF_CLOSING) + sfdisc(iop,SF_POPDISC); + else if(ep && (type==SF_DPOP || type==SF_FINAL)) + free((void*)ep); + return(0); + } + + if(!ep->addspace) + { + /* get the length of this string */ + ep->slen = len = strlen(cp); + /* move to next string */ + ep->argv++; + } + else /* insert space between arguments */ + { + len = 1; + cp = " "; + } + /* insert the new string */ + sfsetbuf(iop,cp,len); + ep->addspace = !ep->addspace; + return(1); +} + +/* + * This routine returns a stream pointer to a segment of length from + * the stream starting at offset + * The stream can be read with the normal stream operations + */ + +static Sfio_t *subopen(Sfio_t* sp, off_t offset, long size) +{ + register struct subfile *disp; + if(sfseek(sp,offset,SEEK_SET) <0) + return(NIL(Sfio_t*)); + if(!(disp = (struct subfile*)malloc(sizeof(struct subfile)+IOBSIZE+1))) + return(NIL(Sfio_t*)); + disp->disc = sub_disc; + disp->oldsp = sp; + disp->offset = offset; + disp->size = disp->left = size; + sp = sfnew(NIL(Sfio_t*),(char*)(disp+1),IOBSIZE,sh.lim.open_max,SF_READ); + sfdisc(sp,&disp->disc); + return(sp); +} + +/* + * read function for subfile discipline + */ +static ssize_t subread(Sfio_t* sp,void* buff,register size_t size,Sfdisc_t* handle) +{ + register struct subfile *disp = (struct subfile*)handle; + NOT_USED(sp); + if(disp->left == 0) + return(0); + if(size > disp->left) + size = disp->left; + disp->left -= size; + return(sfread(disp->oldsp,buff,size)); +} + +/* + * exception handler for subfile discipline + */ +static int subexcept(Sfio_t* sp,register int mode, void *data, Sfdisc_t* handle) +{ + register struct subfile *disp = (struct subfile*)handle; + if(mode==SF_CLOSING) + { + sfdisc(sp,SF_POPDISC); + return(0); + } + else if(disp && (mode==SF_DPOP || mode==SF_FINAL)) + { + free((void*)disp); + return(0); + } +#ifdef SF_ATEXIT + else if (mode==SF_ATEXIT) + { + sfdisc(sp, SF_POPDISC); + return(0); + } +#endif + else if(mode==SF_READ) + return(0); + return(-1); +} + +#define NROW 15 /* number of rows before going to multi-columns */ +#define LBLSIZ 3 /* size of label field and interfield spacing */ +/* + * print a list of arguments in columns + */ +void sh_menu(Sfio_t *outfile,int argn,char *argv[]) +{ + register int i,j; + register char **arg; + int nrow, ncol=1, ndigits=1; + int fldsize, wsize = ed_window(); + char *cp = nv_getval(nv_scoped(LINES)); + nrow = (cp?1+2*((int)strtol(cp, (char**)0, 10)/3):NROW); + for(i=argn;i >= 10;i /= 10) + ndigits++; + if(argn < nrow) + { + nrow = argn; + goto skip; + } + i = 0; + for(arg=argv; *arg;arg++) + { + if((j=strlen(*arg)) > i) + i = j; + } + i += (ndigits+LBLSIZ); + if(i < wsize) + ncol = wsize/i; + if(argn > nrow*ncol) + { + nrow = 1 + (argn-1)/ncol; + } + else + { + ncol = 1 + (argn-1)/nrow; + nrow = 1 + (argn-1)/ncol; + } +skip: + fldsize = (wsize/ncol)-(ndigits+LBLSIZ); + for(i=0;i= argn) + break; + sfnputc(outfile,' ',fldsize-strlen(*arg)); + } + sfputc(outfile,'\n'); + } +} + +#undef read +/* + * shell version of read() for user added builtins + */ +ssize_t sh_read(register int fd, void* buff, size_t n) +{ + register Sfio_t *sp; + if(sp=sh.sftable[fd]) + return(sfread(sp,buff,n)); + else + return(read(fd,buff,n)); +} + +#undef write +/* + * shell version of write() for user added builtins + */ +ssize_t sh_write(register int fd, const void* buff, size_t n) +{ + register Sfio_t *sp; + if(sp=sh.sftable[fd]) + return(sfwrite(sp,buff,n)); + else + return(write(fd,buff,n)); +} + +#undef lseek +/* + * shell version of lseek() for user added builtins + */ +off_t sh_seek(register int fd, off_t offset, int whence) +{ + register Sfio_t *sp; + if((sp=sh.sftable[fd]) && (sfset(sp,0,0)&(SF_READ|SF_WRITE))) + return(sfseek(sp,offset,whence)); + else + return(lseek(fd,offset,whence)); +} + +#undef dup +int sh_dup(register int old) +{ + register int fd = dup(old); + if(fd>=0) + { + if(sh.fdstatus[old] == IOCLOSE) + sh.fdstatus[old] = 0; + sh.fdstatus[fd] = (sh.fdstatus[old]&~IOCLEX); + if(fdnotify) + (*fdnotify)(old,fd); + } + return(fd); +} + +#undef fcntl +int sh_fcntl(register int fd, int op, ...) +{ + int newfd, arg; + va_list ap; + va_start(ap, op); + arg = va_arg(ap, int) ; + va_end(ap); + newfd = fcntl(fd,op,arg); + if(newfd>=0) switch(op) + { + case F_DUPFD: + if(sh.fdstatus[fd] == IOCLOSE) + sh.fdstatus[fd] = 0; + sh.fdstatus[newfd] = (sh.fdstatus[fd]&~IOCLEX); + if(fdnotify) + (*fdnotify)(fd,newfd); + break; + case F_SETFD: + if(sh.fdstatus[fd] == IOCLOSE) + sh.fdstatus[fd] = 0; + if(arg&FD_CLOEXEC) + sh.fdstatus[fd] |= IOCLEX; + else + sh.fdstatus[fd] &= ~IOCLEX; + } + return(newfd); +} + +#undef umask +mode_t sh_umask(mode_t m) +{ + sh.mask = m; + return(umask(m)); +} + +/* + * give file descriptor and , return an iostream pointer + * must be SF_READ or SF_WRITE + * must be a non-negative number ofr SH_IOCOPROCESS or SH_IOHISTFILE. + * returns NULL on failure and may set errno. + */ + +Sfio_t *sh_iogetiop(int fd, int mode) +{ + int n; + Sfio_t *iop=0; + if(mode!=SF_READ && mode!=SF_WRITE) + { + errno = EINVAL; + return(iop); + } + switch(fd) + { + case SH_IOHISTFILE: + if(!sh_histinit()) + return(iop); + fd = sffileno(sh.hist_ptr->histfp); + break; + case SH_IOCOPROCESS: + if(mode==SF_WRITE) + fd = sh.coutpipe; + else + fd = sh.cpipe[0]; + break; + default: + if(fd<0 || fd >= sh.lim.open_max) + fd = -1; + } + if(fd<0) + { + errno = EBADF; + return(iop); + } + if(!(n=sh.fdstatus[fd])) + n = sh_iocheckfd(fd); + if(mode==SF_WRITE && !(n&IOWRITE)) + return(iop); + if(mode==SF_READ && !(n&IOREAD)) + return(iop); + if(!(iop = sh.sftable[fd])) + iop=sh_iostream(fd); + return(iop); +} + +typedef int (*Notify_f)(int,int); + +Notify_f sh_fdnotify(Notify_f notify) +{ + Notify_f old; + old = fdnotify; + fdnotify = notify; + return(old); +} + +Sfio_t *sh_fd2sfio(int fd) +{ + register int status; + Sfio_t *sp = sh.sftable[fd]; + if(!sp && (status = sh_iocheckfd(fd))!=IOCLOSE) + { + register int flags=0; + if(status&IOREAD) + flags |= SF_READ; + if(status&IOWRITE) + flags |= SF_WRITE; + sp = sfnew(NULL, NULL, -1, fd,flags); + sh.sftable[fd] = sp; + } + return(sp); +} + +Sfio_t *sh_pathopen(const char *cp) +{ + int n; +#ifdef PATH_BFPATH + if((n=path_open(cp,path_get(cp))) < 0) + n = path_open(cp,(Pathcomp_t*)0); +#else + if((n=path_open(cp,path_get(cp))) < 0) + n = path_open(cp,""); +#endif + if(n < 0) + errormsg(SH_DICT,ERROR_system(1),e_open,cp); + return(sh_iostream(n)); +} Index: src/lib/libshell/common/sh/fault.c =================================================================== --- src/lib/libshell/common/sh/fault.c (revision 0) +++ src/lib/libshell/common/sh/fault.c (revision 740) @@ -0,0 +1,573 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * Fault handling routines + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include "io.h" +#include "history.h" +#include "shnodes.h" +#include "variables.h" +#include "jobs.h" +#include "path.h" + +#define abortsig(sig) (sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV) + +static char indone; + +#if !_std_malloc +# include +#endif +#if defined(VMFL) && (VMALLOC_VERSION>=20031205L) + /* + * This exception handler is called after vmalloc() unlocks the region + */ + static int malloc_done(Vmalloc_t* vm, int type, Void_t* val, Vmdisc_t* dp) + { + dp->exceptf = 0; + sh_exit(SH_EXITSIG); + return(0); + } +#endif + +/* + * Most signals caught or ignored by the shell come here +*/ +void sh_fault(register int sig) +{ + register int flag=0; + register char *trap; + register struct checkpt *pp = (struct checkpt*)sh.jmplist; + int action=0; + /* reset handler */ + if(!(sig&SH_TRAP)) + signal(sig, sh_fault); + sig &= ~SH_TRAP; +#ifdef SIGWINCH + if(sig==SIGWINCH) + { + int rows=0, cols=0; + int32_t v; + astwinsize(2,&rows,&cols); + if(v = cols) + nv_putval(COLUMNS, (char*)&v, NV_INT32); + if(v = rows) + nv_putval(LINES, (char*)&v, NV_INT32); + } +#endif /* SIGWINCH */ + if(sh.savesig) + { + /* critical region, save and process later */ + sh.savesig = sig; + return; + } + + /* handle ignored signals */ + if((trap=sh.st.trapcom[sig]) && *trap==0) + return; + flag = sh.sigflag[sig]&~SH_SIGOFF; + if(!trap) + { + if(flag&SH_SIGIGNORE) + return; + if(flag&SH_SIGDONE) + { + void *ptr=0; + if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! sh.subshell) + { + /* check for TERM signal between fork/exec */ + if(sig==SIGTERM && job.in_critical) + sh.trapnote |= SH_SIGTERM; + return; + } + sh.lastsig = sig; + sigrelease(sig); + if(pp->mode < SH_JMPFUN) + pp->mode = SH_JMPFUN; + else + pp->mode = SH_JMPEXIT; + if(sig==SIGABRT || (abortsig(sig) && (ptr = malloc(1)))) + { + if(ptr) + free(ptr); + if(!sh.subshell) + sh_done(sig); + sh_exit(SH_EXITSIG); + } + /* mark signal and continue */ + sh.trapnote |= SH_SIGSET; + if(sig < sh.sigmax) + sh.sigflag[sig] |= SH_SIGSET; +#if defined(VMFL) && (VMALLOC_VERSION>=20031205L) + if(abortsig(sig)) + { + /* abort inside malloc, process when malloc returns */ + /* VMFL defined when using vmalloc() */ + Vmdisc_t* dp = vmdisc(Vmregion,0); + if(dp) + dp->exceptf = malloc_done; + } +#endif + return; + } + } + errno = 0; + if(pp->mode==SH_JMPCMD) + sh.lastsig = sig; + if(trap) + { + /* + * propogate signal to foreground group + */ + if(sig==SIGHUP && job.curpgid) + killpg(job.curpgid,SIGHUP); + flag = SH_SIGTRAP; + } + else + { + sh.lastsig = sig; + flag = SH_SIGSET; +#ifdef SIGTSTP + if(sig==SIGTSTP) + { + sh.trapnote |= SH_SIGTSTP; + if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK)) + { + sigrelease(sig); + sh_exit(SH_EXITSIG); + flag = 0; + } + } +#endif /* SIGTSTP */ + } +#ifdef ERROR_NOTIFY + if((error_info.flags&ERROR_NOTIFY) && sh.bltinfun) + action = (*sh.bltinfun)(-sig,(char**)0,(void*)0); +#endif + if(action>0) + return; + sh.trapnote |= flag; + if(sig < sh.sigmax) + sh.sigflag[sig] |= flag; + if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK)) + { + if(action<0) + return; + sigrelease(sig); + sh_exit(SH_EXITSIG); + } +} + +/* + * initialize signal handling + */ +void sh_siginit(void) +{ + register int sig, n=SIGTERM+1; + register const struct shtable2 *tp = shtab_signals; + sig_begin(); + /* find the largest signal number in the table */ + while(*tp->sh_name) + { + if((sig=tp->sh_number&((1<n && sign && sigsh_number; tp++) + { + n = (sig>>SH_SIGBITS); + if((sig &= ((1< sh.sigmax) + continue; + sig--; +#if defined(_SC_SIGRT_MIN) && defined(_SIGRTMIN) + if(sig==_SIGRTMIN) + sig = SIGRTMIN; +#endif +#if defined(_SC_SIGRT_MAX) && defined(_SIGRTMAX) + if(sig==_SIGRTMAX) + sig = SIGRTMAX; +#endif + if(sig>=0) + { + sh.sigflag[sig] = n; + if(*tp->sh_name) + sh.sigmsg[sig] = (char*)tp->sh_value; + } + } +} + +/* + * Turn on trap handler for signal + */ +void sh_sigtrap(register int sig) +{ + register int flag; + void (*fun)(int); + sh.st.otrapcom = 0; + if(sig==0) + sh_sigdone(); + else if(!((flag=sh.sigflag[sig])&(SH_SIGFAULT|SH_SIGOFF))) + { + /* don't set signal if already set or off by parent */ + if((fun=signal(sig,sh_fault))==SIG_IGN) + { + signal(sig,SIG_IGN); + flag |= SH_SIGOFF; + } + else + { + flag |= SH_SIGFAULT; + if(sig==SIGALRM && fun!=SIG_DFL && fun!=sh_fault) + signal(sig,fun); + } + flag &= ~(SH_SIGSET|SH_SIGTRAP); + sh.sigflag[sig] = flag; + } +} + +/* + * set signal handler so sh_done is called for all caught signals + */ +void sh_sigdone(void) +{ + register int flag, sig = sh.sigmax; + sh.sigflag[0] |= SH_SIGFAULT; + while(--sig>0) + { + flag = sh.sigflag[sig]; + if((flag&(SH_SIGDONE|SH_SIGIGNORE|SH_SIGINTERACTIVE)) && !(flag&(SH_SIGFAULT|SH_SIGOFF))) + sh_sigtrap(sig); + } +} + +/* + * Restore to default signals + * Free the trap strings if mode is non-zero + * If mode>1 then ignored traps cause signal to be ignored + */ +void sh_sigreset(register int mode) +{ + register char *trap; + register int flag, sig=sh.st.trapmax; + while(sig-- > 0) + { + if(trap=sh.st.trapcom[sig]) + { + flag = sh.sigflag[sig]&~(SH_SIGTRAP|SH_SIGSET); + if(*trap) + { + if(mode) + free(trap); + sh.st.trapcom[sig] = 0; + } + else if(sig && mode>1) + { + signal(sig,SIG_IGN); + flag &= ~SH_SIGFAULT; + flag |= SH_SIGOFF; + } + sh.sigflag[sig] = flag; + } + } + for(sig=SH_DEBUGTRAP;sig>=0;sig--) + { + if(trap=sh.st.trap[sig]) + { + if(mode) + free(trap); + sh.st.trap[sig] = 0; + } + + } + sh.st.trapcom[0] = 0; + if(mode) + sh.st.trapmax = 0; + sh.trapnote=0; +} + +/* + * free up trap if set and restore signal handler if modified + */ +void sh_sigclear(register int sig) +{ + register int flag = sh.sigflag[sig]; + register char *trap; + sh.st.otrapcom=0; + if(!(flag&SH_SIGFAULT)) + return; + flag &= ~(SH_SIGTRAP|SH_SIGSET); + if(trap=sh.st.trapcom[sig]) + { + free(trap); + sh.st.trapcom[sig]=0; + } + sh.sigflag[sig] = flag; +} + +/* + * check for traps + */ + +void sh_chktrap(void) +{ + register int sig=sh.st.trapmax; + register char *trap; + if(!sh.trapnote) + sig=0; + sh.trapnote &= ~SH_SIGTRAP; + /* execute errexit trap first */ + if(sh_isstate(SH_ERREXIT) && sh.exitval) + { + int sav_trapnote = sh.trapnote; + sh.trapnote &= ~SH_SIGSET; + if(sh.st.trap[SH_ERRTRAP]) + sh_trap(sh.st.trap[SH_ERRTRAP],0); + sh.trapnote = sav_trapnote; + if(sh_isoption(SH_ERREXIT)) + { + struct checkpt *pp = (struct checkpt*)sh.jmplist; + pp->mode = SH_JMPEXIT; + sh_exit(sh.exitval); + } + } + if(sh.sigflag[SIGALRM]&SH_SIGALRM) + sh_timetraps(); + while(--sig>=0) + { + if(sh.sigflag[sig]&SH_SIGTRAP) + { + sh.sigflag[sig] &= ~SH_SIGTRAP; + if(trap=sh.st.trapcom[sig]) + sh_trap(trap,0); + } + } +} + + +/* + * parse and execute the given trap string, stream or tree depending on mode + * mode==0 for string, mode==1 for stream, mode==2 for parse tree + */ +int sh_trap(const char *trap, int mode) +{ + int jmpval, savxit = sh.exitval; + int was_history = sh_isstate(SH_HISTORY); + int was_verbose = sh_isstate(SH_VERBOSE); + int staktop = staktell(); + char *savptr = stakfreeze(0); + struct checkpt buff; + Fcin_t savefc; + fcsave(&savefc); + sh_offstate(SH_HISTORY); + sh_offstate(SH_VERBOSE); + sh.intrap++; + sh_pushcontext(&buff,SH_JMPTRAP); + jmpval = sigsetjmp(buff.buff,0); + if(jmpval == 0) + { + if(mode==2) + sh_exec((Shnode_t*)trap,sh_isstate(SH_ERREXIT)); + else + { + Sfio_t *sp; + if(mode) + sp = (Sfio_t*)trap; + else + sp = sfopen(NIL(Sfio_t*),trap,"s"); + sh_eval(sp,0); + } + } + else if(indone) + { + if(jmpval==SH_JMPSCRIPT) + indone=0; + else + { + if(jmpval==SH_JMPEXIT) + savxit = sh.exitval; + jmpval=SH_JMPTRAP; + } + } + sh_popcontext(&buff); + sh.intrap--; + sfsync(sh.outpool); + if(jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN) + sh.exitval=savxit; + stakset(savptr,staktop); + fcrestore(&savefc); + if(was_history) + sh_onstate(SH_HISTORY); + if(was_verbose) + sh_onstate(SH_VERBOSE); + exitset(); + if(jmpval>SH_JMPTRAP) + siglongjmp(*sh.jmplist,jmpval); + return(sh.exitval); +} + +/* + * exit the current scope and jump to an earlier one based on pp->mode + */ +void sh_exit(register int xno) +{ + register struct checkpt *pp = (struct checkpt*)sh.jmplist; + register int sig=0; + register Sfio_t* pool; + sh.exitval=xno; + if(xno==SH_EXITSIG) + sh.exitval |= (sig=sh.lastsig); +#ifdef SIGTSTP + if(sh.trapnote&SH_SIGTSTP) + { + /* ^Z detected by the shell */ + sh.trapnote = 0; + sh.sigflag[SIGTSTP] = 0; + if(!sh.subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK)) + return; + if(sh_isstate(SH_TIMING)) + return; + /* Handles ^Z for shell builtins, subshells, and functs */ + sh.lastsig = 0; + sh_onstate(SH_MONITOR); + sh_offstate(SH_STOPOK); + sh.trapnote = 0; + if(!sh.subshell && (sig=sh_fork(0,NIL(int*)))) + { + job.curpgid = 0; + job.parent = (pid_t)-1; + job_wait(sig); + job.parent = 0; + sh.sigflag[SIGTSTP] = 0; + /* wait for child to stop */ + sh.exitval = (SH_EXITSIG|SIGTSTP); + /* return to prompt mode */ + pp->mode = SH_JMPERREXIT; + } + else + { + if(sh.subshell) + sh_subfork(); + /* child process, put to sleep */ + sh_offstate(SH_STOPOK); + sh_offstate(SH_MONITOR); + sh.sigflag[SIGTSTP] = 0; + /* stop child job */ + killpg(job.curpgid,SIGTSTP); + /* child resumes */ + job_clear(); + sh.forked = 1; + sh.exitval = (xno&SH_EXITMASK); + return; + } + } +#endif /* SIGTSTP */ + /* unlock output pool */ + sh_offstate(SH_NOTRACK); + if(!(pool=sfpool(NIL(Sfio_t*),sh.outpool,SF_WRITE))) + pool = sh.outpool; /* can't happen? */ + sfclrlock(pool); +#ifdef SIGPIPE + if(sh.lastsig==SIGPIPE) + sfpurge(pool); +#endif /* SIGPIPE */ + sfclrlock(sfstdin); + if(!pp) + sh_done(sig); + sh.prefix = 0; + if(pp->mode == SH_JMPSCRIPT && !pp->prev) + sh_done(sig); + siglongjmp(pp->buff,pp->mode); +} + +/* + * This is the exit routine for the shell + */ + +void sh_done(register int sig) +{ + register char *t; + register int savxit = sh.exitval; + sh.trapnote = 0; + indone=1; + if(sig==0) + sig = sh.lastsig; + if(sh.userinit) + (*sh.userinit)(-1); + if(t=sh.st.trapcom[0]) + { + sh.st.trapcom[0]=0; /*should free but not long */ + sh.oldexit = savxit; + sh_trap(t,0); + savxit = sh.exitval; + } + else + { + /* avoid recursive call for set -e */ + sh_offstate(SH_ERREXIT); + sh_chktrap(); + } + sh_freeup(); +#if SHOPT_ACCT + sh_accend(); +#endif /* SHOPT_ACCT */ +#if SHOPT_VSH || SHOPT_ESH + if(sh_isoption(SH_EMACS)||sh_isoption(SH_VI)||sh_isoption(SH_GMACS)) + tty_cooked(-1); +#endif +#ifdef JOBS + if((sh_isoption(SH_INTERACTIVE) && sh.login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP))) + job_walk(sfstderr,job_terminate,SIGHUP,NIL(char**)); +#endif /* JOBS */ + job_close(); + if(nv_search("VMTRACE", sh.var_tree,0)) + strmatch((char*)0,(char*)0); + sfsync((Sfio_t*)sfstdin); + sfsync((Sfio_t*)sh.outpool); + sfsync((Sfio_t*)sfstdout); + if(sig) + { + /* generate fault termination code */ + signal(sig,SIG_DFL); + sigrelease(sig); + kill(getpid(),sig); + pause(); + } +#if SHOPT_KIA + if(sh_isoption(SH_NOEXEC)) + kiaclose(); +#endif /* SHOPT_KIA */ + exit(savxit&SH_EXITMASK); +} + Index: src/lib/libshell/common/sh/bash.c =================================================================== --- src/lib/libshell/common/sh/bash.c (revision 0) +++ src/lib/libshell/common/sh/bash.c (revision 740) @@ -0,0 +1,430 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +/* + * bash specific extensions + * originally provided by Karsten Fleischer + */ + +#include "defs.h" +#include "path.h" +#include "io.h" +#include "builtins.h" +#include "name.h" + +#ifndef BASH_MAJOR +# define BASH_MAJOR "1" +# define BASH_MINOR "0" +# define BASH_PATCH "0" +# define BASH_BUILD "0" +# define BASH_RELEASE "experimental" +#endif +#define BASH_VERSION BASH_MAJOR "." BASH_MINOR "." BASH_PATCH "(" BASH_BUILD ")-" BASH_RELEASE + + +void sh_applyopts(Shopt_t); + +extern const char bash_pre_rc[]; + +static char *login_files[4]; + +const char sh_bash1[] = + "[B?Enable brace group expansion. This option is only availabe in bash " + "compatibility mode. In ksh mode, brace group expansion is always on.]" + "[P?Do not follow symbolic links, use physical directory structure " + "instead. Only available in bash compatibility mode.]"; +const char sh_bash2[] = +"[l:login?Make the shell act as if it had been invoked as a login shell. " +"Only available if invoked as \bbash\b.]" +"[O]:?[shopt_option?\ashopt_option\a is one of the shell options accepted by " + "the \bshopt\b builtin. If \ashopt_option\a is present, \b-O\b sets " + "the value of that option; \b+O\b unsets it. If \ashopt_option\a is " + "not supplied, the names and values of the shell options accepted by " + "\bshopt\b are printed on the standard output. If the invocation " + "option is \b+O\b, the output is displayed in a format that may be " + "reused as input. Only available if invoked as \bbash\b.]" +"[01:init-file|rcfile]:[file?Execute commands from \afile\a instead of the " + "standard personal initialization file ~/.bashrc if the shell is " + "interactive. Only available if invoked as \bbash\b.]" +"[02:editing?For option compatibility with \bbash\b only. Ignored.]" +"[03:profile?Read either the system-wide startup file or any of the " + "personal initialization files. On by default for interactive " + "shells. Only available if invoked as \bbash\b.]" +"[04:rc?Read and execute the personal initialization file " + "\b$HOME/.bashrc\b. On by default for interactive shells. Only " + "available if invoked as \bbash\b.]" +"[05:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in " + "POSIX mode is not the same as \bksh\b.]" +"[06:version?Print version number and exit.]"; + +const char sh_optshopt[] = +"+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]" +"[-author?Karsten Fleischer ]" +USAGE_LICENSE +"[+NAME?shopt - set/unset variables controlling optional shell behavior]" +"[+DESCRIPTION?\bshopt\b sets or unsets variables controlling optional shell " + "behavior. With no options, or with the \b-p\b option, a list of all " + "settable options is displayed, with an indication of whether or not " + "each is set.]" +"[p?Causes output to be displayed in a form that may be reused as input.]" +"[s?Set each \aoptname\a.]" +"[u?Unset each \aoptname\a.]" +"[q?Suppress output (quiet mode). The return status indicates whether the " + "\aoptname\a is set or unset. If multiple \aoptname\a arguments are " + "given with \b-q\b, the return status is zero if all \aoptname\as are " + "enabled; non-zero otherwise.]" +"[o?Restricts the values of \aoptname\a to be those defined for the \b-o\b " + "option to the set builtin.]" +"[+?If either \b-s\b or \b-u\b is used with no \aoptname\a arguments, the " + "display is limited to those options which are set or unset.]" +"[+?\bshopt\b supports all bash options. Some settings do not have any effect " + "or are are always on and cannot be changed.]" +"[+?The value of \aoptname\a must be one of the following:]{" + "[+cdable_vars?If set, arguments to the \bcd\b command are " + "assumed to be names of variables whose values are to " + "be used if the usual \bcd\b proceeding fails.]" + "[+cdspell?Currently ignored.]" + "[+checkhash?Always on.]" + "[+checkwinsize?Currently ignored.]" + "[+cmdhist?Always on.]" + "[+dotglob?If set, include filenames beginning with a \b.\b " + "in the results of pathname expansion.]" + "[+execfail?Always on.]" + "[+expand_aliases?Always on.]" + "[+extglob?Enable extended pattern matching features.]" + "[+histappend?Always on.]" + "[+histreedit?If set and an edit mode is selected, the user " + "is given the opportunity to re-edit a failed history " + "substitution.]" + "[+histverify?If set and an edit mode is selected, the result " + "of a history substitution will not be executed " + "immediately but be placed in the edit buffer for " + "further modifications.]" + "[+hostcomplete?Currently ignored.]" + "[+huponexit?Currently ignored.]" + "[+interactive_comments?Always on.]" + "[+lithist?Always on.]" + "[+login_shell?This option is set if the shell is started as " + "a login shell. The value cannot be changed.]" + "[+mailwarn?Currently ignored.]" + "[+no_empty_cmd_completion?Always on.]" + "[+nocaseglob?Match filenames in a case-insensitive fashion " + "when performing filename expansion.]" + "[+nullglob?Allows filename patterns which match no files to " + "expand to a null string, rather than themselves.]" + "[+progcomp?Currently ignored.]" + "[+promptvars?Currently ignored.]" + "[+restricted_shell?This option is set if the shell is started " + "as a restricted shell. The value cannot be changed. " + "It is not reset during execution of startup files, " + "allowing the startup files to determine whether the " + "shell is restricted.]" + "[+shift_verbose?Currently ignored.]" + "[+sourcepath?If set, the \b.\b builtin uses the value of PATH " + "to find the directory containing the file supplied " + "as an argument.]" + "[+xpg_echo?If set, the \becho\b and \bprint\b builtins " + "expand backslash-escape sequences.]" +"}" +"\n" +"\n[optname ...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+?The return status when listing options is zero if all \aoptnames\a " + "are enabled, non-zero otherwise. When setting or unsetting options, " + "the return status is zero unless an \aoptname\a is not a valid shell " + "option.]" +"}" + +"[+SEE ALSO?\bset\b(1)]" +; + +/* GLOBIGNORE discipline. Turn on SH_DOTGLOB on set, turn off on unset. */ + +static void put_globignore(register Namval_t* np, const char *val, int flags, Namfun_t *fp) +{ + if(val) + sh_onoption(SH_DOTGLOB); + else + sh_offoption(SH_DOTGLOB); + + nv_putv(np,val,flags,fp); +} + +const Namdisc_t SH_GLOBIGNORE_disc = { sizeof(Namfun_t), put_globignore }; + +/* FUNCNAME discipline */ + +struct funcname +{ + Namfun_t hdr; +}; + +static void put_funcname(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + /* bash silently returns with an error when FUNCNAME is set, + unsetting FUNCNAME is allowed */ + if(val && !(flags&NV_RDONLY)) + error_info.exit(1); + + nv_putv(np,val,flags,fp); +} + +const Namdisc_t SH_FUNCNAME_disc = { sizeof(struct funcname), put_funcname }; + +#define SET_SET 1 +#define SET_UNSET 2 +#define SET_NOARGS 4 + +/* shopt builtin */ + +int b_shopt(int argc,register char *argv[],void *extra) +{ + Shell_t *shp = (Shell_t*)extra; + int n, f, ret=0; + Shopt_t newflags=shp->options, opt; + int verbose=PRINT_SHOPT|PRINT_ALL|PRINT_NO_HEADER|PRINT_VERBOSE; + int setflag=0, quietflag=0, oflag=0; + memset(&opt,0,sizeof(opt)); +#if SHOPT_RAWONLY + on_option(&newflags,SH_VIRAW); +#endif + while((n = optget(argv,sh_optshopt))) + { + switch(n) + { + case 'p': + verbose&=~PRINT_VERBOSE; + break; + case 's': + case 'u': + setflag|=n=='s'?SET_SET:SET_UNSET; + if(setflag==(SET_SET|SET_UNSET)) + { + errormsg(SH_DICT,ERROR_ERROR,"cannot set and unset options simultaneously"); + error_info.errors++; + } + break; + case 'q': + quietflag=1; + break; + case 'o': + oflag=1; + verbose&=~PRINT_SHOPT; + break; + case ':': + errormsg(SH_DICT,2, "%s", opt_info.arg); + continue; + case '?': + errormsg(SH_DICT,ERROR_usage(0), "%s", opt_info.arg); + return(-1); + } + } + if(error_info.errors) + errormsg(SH_DICT,ERROR_usage(2),"%s",optusage(NIL(char*))); + argc -= opt_info.index; + if(argc==0) + { + /* no args, -s => mask=current options, -u mask=~(current options) + else mask=all bits */ + if(setflag&SET_SET) + opt=newflags; + else if(setflag&SET_UNSET) + for(n=0;n<4;n++) + opt.v[n]=~newflags.v[n]; + else + memset(&opt,0xff,sizeof(opt)); + setflag=SET_NOARGS; + } + while(argc>0) + { + f=1; + n=sh_lookopt(argv[opt_info.index],&f); + if(n<=0||(setflag + && (is_option(&opt,SH_INTERACTIVE) + || is_option(&opt,SH_RESTRICTED) + || is_option(&opt,SH_RESTRICTED2) + || is_option(&opt,SH_BASH) + || is_option(&opt,SH_LOGIN_SHELL))) + ||(oflag&&(n&SH_BASHOPT))) + { + errormsg(SH_DICT,ERROR_ERROR, e_option, argv[opt_info.index]); + error_info.errors++; + ret=1; + } + else if(f) + on_option(&opt,n&0xff); + else + off_option(&opt,n&0xff); + opt_info.index++; + argc--; + } + if(setflag&(SET_SET|SET_UNSET)) + { + if(setflag&SET_SET) + { + if(sh_isoption(SH_INTERACTIVE)) + off_option(&opt,SH_NOEXEC); + if(is_option(&opt,SH_VI)||is_option(&opt,SH_EMACS)||is_option(&opt,SH_GMACS)) + { + off_option(&newflags,SH_VI); + off_option(&newflags,SH_EMACS); + off_option(&newflags,SH_GMACS); + } + for(n=0;n<4;n++) + newflags.v[n] |= opt.v[n]; + } + else if(setflag&SET_UNSET) + for(n=0;n<4;n++) + newflags.v[n] &= ~opt.v[n]; + sh_applyopts(newflags); + shp->options = newflags; + if(is_option(&newflags,SH_XTRACE)) + sh_trace(argv,1); + } + else if(!(setflag&SET_NOARGS)) /* no -s,-u but args, ret=0 if opt&mask==mask */ + { + for(n=0;n<4;n++) + ret+=((newflags.v[n]&opt.v[n])!=opt.v[n]); + } + if(!quietflag&&!(setflag&(SET_SET|SET_UNSET))) + sh_printopts(newflags,verbose,&opt); + return(ret); +} + +/* mode = 0: init, called two times + before parsing shell args with SH_PREINIT state turned on + second time after sh_init() is through and with SH_PREINIT state turned off + mode > 1: re-init + mode < 0: shutdown +*/ + +void bash_init(int mode) +{ + Sfio_t *iop; + Namval_t *np; + int n=0,xtrace,verbose; + if(mode>0) + goto reinit; + if(mode < 0) + { + /* termination code */ + if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX)) + sh_source(&sh, NiL, sh_mactry((char*)e_bash_logout)); + return; + } + + if(sh_isstate(SH_PREINIT)) + { /* pre-init stage */ + if(sh_isoption(SH_RESTRICTED)) + sh_onoption(SH_RESTRICTED2); + sh_onoption(SH_HISTORY2); + sh_onoption(SH_INTERACTIVE_COMM); + sh_onoption(SH_SOURCEPATH); + sh_onoption(SH_HISTAPPEND); + sh_onoption(SH_CMDHIST); + sh_onoption(SH_LITHIST); + sh_onoption(SH_NOEMPTYCMDCOMPL); + if(sh.login_sh==2) + sh_onoption(SH_LOGIN_SHELL); + if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0) + sh_onoption(SH_POSIX); + if(strcmp(astconf("UNIVERSE",0,0),"att")==0) + sh_onoption(SH_XPG_ECHO); + else + sh_offoption(SH_XPG_ECHO); + if(strcmp(astconf("PATH_RESOLVE",0,0),"physical")==0) + sh_onoption(SH_PHYSICAL); + else + sh_offoption(SH_PHYSICAL); + + /* add builtins */ + sh_addbuiltin("shopt", b_shopt, &sh); + + /* set up some variables needed for --version + * needs to go here because --version option is parsed before the init script. + */ + if(np=nv_open("HOSTTYPE",sh.var_tree,0)) + nv_putval(np, BASH_HOSTTYPE, NV_NOFREE); + if(np=nv_open("MACHTYPE",sh.var_tree,0)) + nv_putval(np, BASH_MACHTYPE, NV_NOFREE); + if(np=nv_open("BASH_VERSION",sh.var_tree,0)) + nv_putval(np, BASH_VERSION, NV_NOFREE); + if(np=nv_open("BASH_VERSINFO",sh.var_tree,0)) + { + char *argv[7]; + argv[0] = BASH_MAJOR; + argv[1] = BASH_MINOR; + argv[2] = BASH_PATCH; + argv[3] = BASH_BUILD; + argv[4] = BASH_RELEASE; + argv[5] = BASH_MACHTYPE; + argv[6] = 0; + nv_setvec(np, 0, 6, argv); + nv_onattr(np,NV_RDONLY); + } + return; + } + + /* rest of init stage */ + + /* restrict BASH_ENV */ + if(np=nv_open("BASH_ENV",sh.var_tree,0)) + { + const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT); + Namfun_t *fp = calloc(dp->dsize,1); + fp->disc = dp; + nv_disc(np, fp, 0); + } + + /* open GLOBIGNORE node */ + if(np=nv_open("GLOBIGNORE",sh.var_tree,0)) + { + const Namdisc_t *dp = &SH_GLOBIGNORE_disc; + Namfun_t *fp = calloc(dp->dsize,1); + fp->disc = dp; + nv_disc(np, fp, 0); + } + + /* set startup files */ + n=0; + if(!sh_isoption(SH_NOPROFILE)) + { + if(!sh_isoption(SH_POSIX)) + { + login_files[n++] = (char*)e_bash_profile; + login_files[n++] = (char*)e_bash_login; + } + login_files[n++] = (char*)e_profile; + } + sh.login_files = login_files; +reinit: + xtrace = sh_isoption(SH_XTRACE); + sh_offoption(SH_XTRACE); + verbose = sh_isoption(SH_VERBOSE); + sh_offoption(SH_VERBOSE); + if(np = nv_open("SHELLOPTS", sh.var_tree, NV_NOADD)) + nv_offattr(np,NV_RDONLY); + iop = sfopen(NULL, bash_pre_rc, "s"); + sh_eval(iop,0); + if(xtrace) + sh_offoption(SH_XTRACE); + if(verbose) + sh_offoption(SH_VERBOSE); +} Index: src/lib/libshell/common/sh/array.c =================================================================== --- src/lib/libshell/common/sh/array.c (revision 0) +++ src/lib/libshell/common/sh/array.c (revision 740) @@ -0,0 +1,865 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * Array processing routines + * + * David Korn + * AT&T Labs + * dgk@research.att.com + * + */ + +#include "defs.h" +#include +#include "name.h" + +#define NUMSIZE (4+(ARRAY_MAX>999)+(ARRAY_MAX>9999)+(ARRAY_MAX>99999)) +#define is_associative(ap) array_assoc((Namarr_t*)(ap)) +#define array_setbit(cp, n) (cp[(n)/CHAR_BIT] |= 1<<(((n)&(CHAR_BIT-1)))) +#define array_clrbit(cp, n) (cp[(n)/CHAR_BIT] &= ~(1<<(((n)&(CHAR_BIT-1))))) +#define array_isbit(cp, n) (cp[(n)/CHAR_BIT] & 1<<(((n)&(CHAR_BIT-1)))) +#define NV_CHILD NV_EXPORT + +static char Empty[] = ""; + +struct index_array +{ + Namarr_t header; + int cur; /* index of current element */ + int maxi; /* maximum index for array */ + unsigned char *bits; /* bit array for child subscripts */ + union Value val[1]; /* array of value holders */ +}; + +struct assoc_array +{ + Namarr_t header; + Dt_t *table; + Namval_t *pos; + Namval_t *nextpos; + Namval_t *cur; +}; + +/* + * replace discipline with new one + */ +static void array_setptr(register Namval_t *np, struct index_array *old, struct index_array *new) +{ + register Namfun_t **fp = &np->nvfun; + while(*fp && *fp!= &old->header.hdr) + fp = &((*fp)->next); + if(*fp) + { + new->header.hdr.next = (*fp)->next; + *fp = &new->header.hdr; + } + else sfprintf(sfstderr,"discipline not replaced\n"); +} + +/* + * Calculate the amount of space to be allocated to hold an + * indexed array into which is a legal index. The number of + * elements that will actually fit into the array (> + * but <= ARRAY_MAX) is returned. + * + */ +static int arsize(register int maxi) +{ + register int i = roundof(maxi,ARRAY_INCR); + return (i>ARRAY_MAX?ARRAY_MAX:i); +} + +static struct index_array *array_grow(Namval_t*, struct index_array*,int); + +/* return index of highest element of an array */ +int array_maxindex(Namval_t *np) +{ + register struct index_array *ap = (struct index_array*)nv_arrayptr(np); + register int i = ap->maxi; + if(is_associative(ap)) + return(-1); + while(i>0 && ap->val[--i].cp==0); + return(i+1); +} + +static union Value *array_getup(Namval_t *np, Namarr_t *arp) +{ + register struct index_array *ap = (struct index_array*)arp; + register union Value *up; + if(!nv_isarray(np)) + return(&np->nvalue); + if(is_associative(ap)) + up = (union Value*)((*arp->fun)(np,NIL(char*),0)); + else + { + if(ap->cur >= ap->maxi) + errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np)); + up = &(ap->val[ap->cur]); + } + return(up); +} + +/* + * Get the Value pointer for an array. + * Delete space as necessary if flag is ARRAY_DELETE + * After the lookup is done the last @ or * subscript is incremented + */ +static Namval_t *array_find(Namval_t *np,Namarr_t *arp, int flag) +{ + register struct index_array *ap = (struct index_array*)arp; + register union Value *up; + Namval_t *mp; + int wasundef; + if(wasundef = ap->header.nelem&ARRAY_UNDEF) + { + ap->header.nelem &= ~ARRAY_UNDEF; + /* delete array is the same as delete array[@] */ + if(flag&ARRAY_DELETE) + { + nv_putsub(np, NIL(char*), ARRAY_SCAN); + ap->header.nelem |= ARRAY_SCAN; + } + else /* same as array[0] */ + { + if(is_associative(ap)) + (*ap->header.fun)(np,"0",flag==ARRAY_ASSIGN?NV_AADD:0); + else + ap->cur = 0; + } + } + if(is_associative(ap)) + { + mp = (Namval_t*)((*arp->fun)(np,NIL(char*),NV_ACURRENT)); + if(!mp) + up = (union Value*)∓ + else if(nv_isattr(mp,NV_CHILD)) + { + if(wasundef && nv_isarray(mp->nvalue.np)) + nv_putsub(mp->nvalue.np,NIL(char*),ARRAY_UNDEF); + return(mp->nvalue.np); + } + else + up = &mp->nvalue; + } + else + { + if(!(ap->header.nelem&ARRAY_SCAN) && ap->cur >= ap->maxi) + ap = array_grow(np, ap, (int)ap->cur); + if(ap->cur>=ap->maxi) + errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np)); + up = &(ap->val[ap->cur]); + if(up->np && array_isbit(ap->bits,ap->cur)) + { + if(wasundef && nv_isarray(up->np)) + nv_putsub(up->np,NIL(char*),ARRAY_UNDEF); + return(up->np); + } + } + np->nvalue.cp = up->cp; + if(!up->cp) + { + if(flag!=ARRAY_ASSIGN) + return(0); + ap->header.nelem++; + } + return(np); +} + +static Namfun_t *array_clone(Namval_t *np, Namval_t *mp, int flags, Namfun_t *fp) +{ + Namarr_t *ap = (Namarr_t*)fp; + Namval_t *nq, *mq; + char *name, *sub=0; + int nelem = ap->nelem,offset=staktell(); + struct index_array *aq, *ar; + if(nelem&ARRAY_NOCLONE) + return(0); + if(array_assoc(ap)) + nv_setarray(mp,ap->fun); + else + { + nv_putsub(mp,NIL(char*),ap->nelem); + if(aq=(struct index_array*)nv_arrayptr(mp)) + aq->bits = (unsigned char*)&aq->val[aq->maxi]; + } + if(!(nelem&(ARRAY_SCAN|ARRAY_UNDEF)) && (sub=nv_getsub(np))) + sub = strdup(sub); + ar = (struct index_array*)ap; + nv_onattr(mp,nv_isattr(np,NV_INTEGER|NV_UTOL|NV_LTOU|NV_LJUST|NV_RJUST|NV_ZFILL|NV_BINARY)); + nv_putsub(np,NIL(char*),ARRAY_SCAN); + do + { + if(array_assoc(ap)) + name = (char*)((*ap->fun)(np,NIL(char*),NV_ANAME)); + else + name = nv_getsub(np); + nv_putsub(mp,name,ARRAY_ADD); + if((!array_assoc(ap) && array_isbit(ar->bits,ar->cur) && (nq=np)) || + (array_assoc(ap) && (nq = (Namval_t*)((*ap->fun)(np,NIL(char*),NV_ACURRENT))) && nv_isattr(nq, NV_CHILD))) + { + sfprintf(stkstd,"%s[%s]",nv_name(mp),name); + stakputc(0); + mq = nv_search(stakptr(offset), sh.var_tree, NV_ADD); + stakseek(offset); + if(mq) + { + nv_clone(nq->nvalue.np,mq,0); + if(array_assoc(ap)) + { + nq = (Namval_t*)((*ap->fun)(mp,NIL(char*),NV_ACURRENT)); + nq->nvalue.np = mp; + nv_onattr(nq,NV_CHILD); + } + else if(aq) + { + array_setbit(aq->bits,aq->cur); + aq->val[aq->cur].np = mq; + } + } + } + else if(nv_isattr(np,NV_INTEGER)) + { + Sfdouble_t d= nv_getnum(np); + nv_putval(mp,(char*)&d,NV_LDOUBLE); + } + else + nv_putval(mp,nv_getval(np),NV_RDONLY); + } + while(nv_nextsub(np)); + if(sub) + { + nv_putsub(np,sub,0L); + free((void*)sub); + } + ap->nelem = nelem; + ((Namarr_t*)mp->nvfun)->nelem = nelem; + return(nv_stack(mp,(Namfun_t*)0)); +} + +static char *array_getval(Namval_t *np, Namfun_t *disc) +{ + register Namarr_t *ap = (Namarr_t*)disc; + register Namval_t *mp; + if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np) + return(mp?nv_getval(mp):0); + return(nv_getv(np,&ap->hdr)); +} + +static Sfdouble_t array_getnum(Namval_t *np, Namfun_t *disc) +{ + register Namarr_t *ap = (Namarr_t*)disc; + register Namval_t *mp; + if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np) + return(mp?nv_getnum(mp):0); + return(nv_getn(np,&ap->hdr)); +} + +static void array_putval(Namval_t *np, const char *string, int flags, Namfun_t *dp) +{ + register Namarr_t *ap = (Namarr_t*)dp; + register union Value *up; + register Namval_t *mp; + register struct index_array *aq = (struct index_array*)ap; + do + { + mp = array_find(np,ap,string?ARRAY_ASSIGN:ARRAY_DELETE); + if(mp && mp!=np) + nv_putval(mp, string, flags); + if(!string) + { + if(mp) + { + if(mp!=np) + { + dtdelete(sh.var_tree,(void*)mp); + free((void*)mp); + } + if(is_associative(ap)) + (*ap->fun)(np,NIL(char*),NV_ADELETE); + else if(mp!=np) + { + array_clrbit(aq->bits,aq->cur); + aq->val[aq->cur].cp = 0; + } + ap->nelem--; + } + if(array_elem(ap)==0 && ((ap->nelem&ARRAY_SCAN) || !is_associative(ap))) + { + if(is_associative(ap)) + (*ap->fun)(np, NIL(char*), NV_AFREE); + nv_offattr(np,NV_ARRAY); + } + if(!mp || mp!=np) + continue; + } + /* prevent empty string from being deleted */ + if(np->nvalue.cp == Empty) + np->nvalue.cp = 0; + nv_putv(np,string,flags,&ap->hdr); + up = array_getup(np,ap); + up->cp = np->nvalue.cp; + } + while(!string && nv_nextsub(np)); + if(!string && !nv_isattr(np,NV_ARRAY)) + { + Namfun_t *nfp; + if(nfp = nv_disc(np,(Namfun_t*)ap,NV_POP)) + free((void*)nfp); + } +} + +static const Namdisc_t array_disc = +{ + sizeof(Namarr_t), + array_putval, + array_getval, + array_getnum, + 0, + 0, + array_clone +}; + +/* + * Increase the size of the indexed array of elements in + * so that is a legal index. If is 0, an array + * of the required size is allocated. A pointer to the + * allocated Namarr_t structure is returned. + * becomes the current index of the array. + */ +static struct index_array *array_grow(Namval_t *np, register struct index_array *arp,int maxi) +{ + register struct index_array *ap; + register int i=0; + register int newsize = arsize(maxi+1); + if (maxi >= ARRAY_MAX) + errormsg(SH_DICT,ERROR_exit(1),e_subscript, fmtbase((long)maxi,10,0)); + ap = new_of(struct index_array,(newsize-1)*sizeof(union Value*)+newsize/CHAR_BIT); + memset((void*)ap,0,sizeof(*ap)); + ap->maxi = newsize; + ap->cur = maxi; + ap->bits = (unsigned char*)&ap->val[newsize]; + memset(ap->bits, 0, newsize/CHAR_BIT); + if(arp) + { + ap->header = arp->header; + for(;i < arp->maxi;i++) + ap->val[i].cp = arp->val[i].cp; + memcpy(ap->bits, arp->bits, (arp->maxi/CHAR_BIT)); + array_setptr(np,arp,ap); + free((void*)arp); + } + else + { + ap->header.fun = 0; + if((ap->val[0].cp=np->nvalue.cp)) + i++; + else if(nv_hasdisc(np,&array_disc)) + { + Namval_t *mp; + int offset = staktell(); + sfprintf(stkstd,"%s[0]",nv_name(np)); + stakputc(0); + mp = nv_search(stakptr(offset), sh.var_tree, NV_ADD); + stakseek(offset); + if(mp && nv_isnull(mp)) + { + nv_clone(np,mp,0); + ap->val[0].np = mp; + array_setbit(ap->bits,0); + } + i++; + } + else if(nv_isattr(np,NV_INTEGER)) + { + Sfdouble_t d= nv_getnum(np); + i++; + } + ap->header.nelem = i; + ap->header.hdr.nofree = 1; + ap->header.hdr.disc = &array_disc; + nv_disc(np,(Namfun_t*)ap, NV_LAST); + } + for(;i < newsize;i++) + ap->val[i].cp = 0; + return(ap); +} + +Namarr_t *nv_arrayptr(register Namval_t *np) +{ + if(nv_isattr(np,NV_ARRAY)) + return((Namarr_t*)nv_hasdisc(np, &array_disc)); + return(0); +} + +/* + * Verify that argument is an indexed array and convert to associative, + * freeing relevant storage + */ +static Namarr_t *nv_changearray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int)) +{ + register Namarr_t *ap; + char numbuff[NUMSIZE+1]; + unsigned dot, digit, n; + union Value *up; + struct index_array *save_ap; + register char *string_index=&numbuff[NUMSIZE]; + numbuff[NUMSIZE]='\0'; + + if(!fun || !(ap = nv_arrayptr(np)) || is_associative(ap)) + return(NIL(Namarr_t*)); + + nv_stack(np,&ap->hdr); + save_ap = (struct index_array*)nv_stack(np,0); + ap = (Namarr_t*)((*fun)(np, NIL(char*), NV_AINIT)); + ap->nelem = 0; + ap->fun = fun; + nv_onattr(np,NV_ARRAY); + + for(dot = 0; dot < (unsigned)save_ap->maxi; dot++) + { + if(save_ap->val[dot].cp) + { + if ((digit = dot)== 0) + *--string_index = '0'; + else while( n = digit ) + { + digit /= 10; + *--string_index = '0' + (n-10*digit); + } + nv_putsub(np, string_index, ARRAY_ADD); + up = (union Value*)((*ap->fun)(np,NIL(char*),0)); + ap->nelem++; + up->cp = save_ap->val[dot].cp; + save_ap->val[dot].cp = 0; + } + string_index = &numbuff[NUMSIZE]; + } + free((void*)save_ap); + return(ap); +} + +/* + * set the associative array processing method for node to + * The array pointer is returned if sucessful. + */ +Namarr_t *nv_setarray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int)) +{ + register Namarr_t *ap; + char *value; + if(fun && (ap = nv_arrayptr(np))) + { + /* + * if it's already an indexed array, convert to + * associative structure + */ + if(!is_associative(ap)) + ap = nv_changearray(np, fun); + return(ap); + } + value = nv_getval(np); + if(fun && !ap && (ap = (Namarr_t*)((*fun)(np, NIL(char*), NV_AINIT)))) + { + /* check for preexisting initialization and save */ + ap->nelem = 0; + ap->fun = fun; + nv_onattr(np,NV_ARRAY); + if(value) + { + nv_putsub(np, "0", ARRAY_ADD); + nv_putval(np, value, 0); + } + return(ap); + } + return(NIL(Namarr_t*)); +} + +/* + * move parent subscript into child + */ +Namval_t *nv_arraychild(Namval_t *np, Namval_t *nq, int c) +{ + register Namarr_t *ap = nv_arrayptr(np); + union Value *up; + if(!(up = array_getup(np,ap))) + return((Namval_t*)0); + if(!nq) + return(array_find(np,ap, ARRAY_LOOKUP)); + np->nvalue.cp = up->cp; + ap->nelem |= ARRAY_NOCLONE; + nv_clone(np, nq, NV_NODISC); + nv_offattr(nq,NV_ARRAY); + ap->nelem &= ~ARRAY_NOCLONE; + if(ap->fun) + { + up->np = (Namval_t*)((*ap->fun)(np,NIL(char*),NV_ACURRENT)); + nv_onattr(up->np, NV_CHILD); + (up->np)->nvalue.np = nq; + } + else + { + struct index_array *aq = (struct index_array*)ap; + array_setbit(aq->bits,aq->cur); + up->np = nq; + } + if(c=='.') + nv_setvtree(nq); + return(nq); +} + +/* + * This routine sets subscript of to the next element, if any. + * The return value is zero, if there are no more elements + * Otherwise, 1 is returned. + */ +int nv_nextsub(Namval_t *np) +{ + register struct index_array *ap = (struct index_array*)nv_arrayptr(np); + register unsigned dot; + if(!ap || !(ap->header.nelem&ARRAY_SCAN)) + return(0); + if(is_associative(ap)) + { + struct assoc_array *aq; + if(aq=(*ap->header.fun)(np,NIL(char*),NV_ANEXT)) + { + if(nv_isattr(aq->cur,NV_CHILD)) + nv_putsub(aq->cur->nvalue.np,NIL(char*),ARRAY_UNDEF); + return(1); + } + ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD); + return(0); + } + for(dot=ap->cur+1; dot < (unsigned)ap->maxi; dot++) + { + if(ap->val[dot].cp) + { + ap->cur = dot; + if(array_isbit(ap->bits, dot)) + { + + if(ap->header.nelem&ARRAY_NOCHILD) + continue; + nv_putsub(ap->val[dot].np,NIL(char*),ARRAY_UNDEF); + } + return(1); + } + } + ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD); + ap->cur = 0; + return(0); +} + +/* + * Set an array subscript for node given the subscript + * An array is created if necessary. + * can be a number, plus or more of symbolic constants + * ARRAY_SCAN, ARRAY_UNDEF, ARRAY_ADD + * The node pointer is returned which can be NULL if is + * not already array and the ARRAY_ADD bit of is not set. + * ARRAY_FILL sets the specified subscript to the empty string when + * ARRAY_ADD is specified and there is no value or sets all + * the elements up to the number specified if ARRAY_ADD is not specified + */ +Namval_t *nv_putsub(Namval_t *np,register char *sp,register long mode) +{ + register struct index_array *ap = (struct index_array*)nv_arrayptr(np); + register int size = (mode&ARRAY_MASK); + if(!ap || !ap->header.fun) + { + if(sp) + size = (int)sh_arith((char*)sp); + if(size >= ARRAY_MAX || (size < 0)) + { + errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np)); + return(NIL(Namval_t*)); + } + if(!ap || size>=ap->maxi) + { + if(size==0 && !(mode&ARRAY_FILL)) + return(NIL(Namval_t*)); + if(sh.subshell) + np = sh_assignok(np,1); + ap = array_grow(np, ap,size); + nv_onattr(np,NV_ARRAY); + } + ap->header.nelem &= ~ARRAY_UNDEF; + ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF)); + ap->cur = size; + if((mode&ARRAY_SCAN) && !ap->val[size].cp && !nv_nextsub(np)) + np = 0; + if(mode&ARRAY_FILL) + { + if(!(mode&ARRAY_ADD)) + { + int n; + for(n=0; n < size; n++) + { + if(!ap->val[n].cp) + ap->val[n].cp = Empty; + } + ap->header.nelem = n|(ap->header.nelem&(ARRAY_SCAN|ARRAY_UNDEF)); + if(n=ap->maxi-ap->maxi) + memset(&ap->val[size],0,n*sizeof(union Value)); + } + else if(!ap->val[size].cp) + { + if(sh.subshell) + np = sh_assignok(np,1); + ap->val[size].cp = Empty; + ap->header.nelem++; + } + } + else if(!(mode&ARRAY_SCAN)) + { + ap->header.nelem &= ~ARRAY_SCAN; + if(array_isbit(ap->bits,size)) + nv_putsub(ap->val[size].np,NIL(char*),ARRAY_UNDEF); + } + return((Namval_t*)np); + } + ap->header.nelem &= ~ARRAY_UNDEF; + if(!(mode&ARRAY_FILL)) + ap->header.nelem &= ~ARRAY_SCAN; + ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF)); + if(sp) + { + union Value *up; + if(mode&ARRAY_SETSUB) + { + (*ap->header.fun)(np, sp, NV_ASETSUB); + return(np); + } + up = (union Value*)(*ap->header.fun)(np, sp, (mode&ARRAY_ADD)?NV_AADD:0); + if(up && !up->cp && (mode&ARRAY_ADD) && (mode&ARRAY_FILL)) + { + if(sh.subshell) + np = sh_assignok(np,1); + up->cp = Empty; + ap->header.nelem++; + } + } + else if(mode&ARRAY_SCAN) + (*ap->header.fun)(np,(char*)np,0); + else if(mode&ARRAY_UNDEF) + (*ap->header.fun)(np, "",0); + if((mode&ARRAY_SCAN) && !nv_nextsub(np)) + np = 0; + return(np); +} + +/* + * process an array subscript for node given the subscript + * returns pointer to character after the subscript + */ +char *nv_endsubscript(Namval_t *np, register char *cp, int mode) +{ + register int count=1, quoted=0, c; + register char *sp = cp+1; + /* first find matching ']' */ + while(count>0 && (c= *++cp)) + { + if(c=='\\' && (!(mode&NV_SUBQUOTE) || (c=cp[1])=='[' || c==']' || c=='\\' || c=='*' || c=='@')) + { + quoted=1; + cp++; + } + else if(c=='[') + count++; + else if(c==']') + count--; + } + *cp = 0; + if(quoted) + { + /* strip escape characters */ + count = staktell(); + stakwrite(sp,1+cp-sp); + sh_trim(sp=stakptr(count)); + } + if(mode && np) + nv_putsub(np, sp, ARRAY_ADD|(cp[1]?ARRAY_FILL:mode&ARRAY_FILL)); + if(quoted) + stakseek(count); + *cp++ = c; + return(cp); +} + + +Namval_t *nv_opensub(Namval_t* np) +{ + register struct index_array *ap = (struct index_array*)nv_arrayptr(np); + if(ap && is_associative(ap)) + return((Namval_t*)((*ap->header.fun)(np,NIL(char*),NV_ACURRENT))); + return(NIL(Namval_t*)); +} + +char *nv_getsub(Namval_t* np) +{ + static char numbuff[NUMSIZE]; + register struct index_array *ap; + register unsigned dot, n; + register char *cp = &numbuff[NUMSIZE]; + if(!np || !(ap = (struct index_array*)nv_arrayptr(np))) + return(NIL(char*)); + if(is_associative(ap)) + return((char*)((*ap->header.fun)(np,NIL(char*),NV_ANAME))); + if((dot = ap->cur)==0) + *--cp = '0'; + else while(n=dot) + { + dot /= 10; + *--cp = '0' + (n-10*dot); + } + return(cp); +} + +/* + * If is an indexed array node, the current subscript index + * returned, otherwise returns -1 + */ +int nv_aindex(register Namval_t* np) +{ + Namarr_t *ap = nv_arrayptr(np); + if(!ap || is_associative(ap)) + return(-1); + return(((struct index_array*)(ap))->cur&ARRAY_MASK); +} + + +/* + * This is the default implementation for associate arrays + */ +void *nv_associative(register Namval_t *np,const char *sp,int mode) +{ + register struct assoc_array *ap = (struct assoc_array*)nv_arrayptr(np); + register int type; + switch(mode) + { + case NV_AINIT: + if(ap = (struct assoc_array*)calloc(1,sizeof(struct assoc_array))) + { + ap->table = dtopen(&_Nvdisc,Dtbag); + ap->cur = 0; + ap->pos = 0; + ap->header.hdr.disc = &array_disc; + ap->header.hdr.nofree = 1; + nv_disc(np,(Namfun_t*)ap, NV_LAST); + } + return((void*)ap); + case NV_ADELETE: + if(ap->cur) + { + if(nv_isattr(ap->cur,NV_NOFREE)) + nv_offattr(ap->cur,NV_NOFREE); + else + { + dtdelete(ap->table,(void*)ap->cur); + free((void*)ap->cur); + ap->cur = 0; + } + } + return((void*)ap); + case NV_AFREE: + ap->pos = 0; + dtclose(ap->table); + return((void*)ap); + case NV_ANEXT: + if(!ap->pos) + { + if(!(ap->pos=ap->cur)) + ap->pos = (Namval_t*)dtfirst(ap->table); + } + else + ap->pos = ap->nextpos; + for(;ap->cur=ap->pos; ap->pos=ap->nextpos) + { + ap->nextpos = (Namval_t*)dtnext(ap->table,ap->pos); + if(ap->cur->nvalue.cp) + { + if((ap->header.nelem&ARRAY_NOCHILD) && nv_isattr(ap->cur,NV_CHILD)) + continue; + return((void*)ap); + } + } + return(NIL(void*)); + case NV_ASETSUB: + ap->cur = (Namval_t*)sp; + /* FALL THROUGH*/ + case NV_ACURRENT: + return((void*)ap->cur); + case NV_ANAME: + if(ap->cur) + return((void*)nv_name(ap->cur)); + return(NIL(void*)); + default: + if(sp) + { + if(sp==(char*)np) + { + ap->cur = 0; + return(0); + } + else if(!(ap->header.nelem&ARRAY_SCAN)) + ap->pos = 0; + type = nv_isattr(np,NV_PUBLIC&~(NV_ARRAY|NV_CHILD)); + if((np=nv_search(sp,ap->table,mode?NV_ADD:0)) && nv_isnull(np)) + nv_onattr(np,type); + ap->cur = np; + } + if(ap->cur) + return((void*)(&ap->cur->nvalue)); + else + return((void*)(&ap->cur)); + } +} + +/* + * Assign values to an array + */ +void nv_setvec(register Namval_t *np,int append,register int argc,register char *argv[]) +{ + int arg0=0; + struct index_array *ap=0; + if(nv_isarray(np)) + { + ap = (struct index_array*)nv_arrayptr(np); + if(ap && is_associative(ap)) + errormsg(SH_DICT,ERROR_exit(1),"cannot append index array to associate array %s",nv_name(np)); + } + if(append) + { + if(ap) + { + arg0 = ap->maxi; + while(--arg0>0 && ap->val[arg0].cp==0); + arg0++; + } + else if(!nv_isnull(np)) + arg0=1; + } + while(--argc >= 0) + { + if((argc+arg0)>0 || nv_isattr(np,NV_ARRAY)) + nv_putsub(np,NIL(char*),(long)argc+arg0); + nv_putval(np,argv[argc],0); + } +} + Index: src/lib/libshell/common/sh/fcin.c =================================================================== --- src/lib/libshell/common/sh/fcin.c (revision 0) +++ src/lib/libshell/common/sh/fcin.c (revision 740) @@ -0,0 +1,211 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * Routines to implement fast character input + * + * David Korn + * AT&T Labs + * + */ + +#include +#include +#include +#include + +Fcin_t _Fcin = {0}; + +/* + * open stream for fast character input + */ +int fcfopen(register Sfio_t* f) +{ + register int n; + char *buff; + Fcin_t save; + errno = 0; + _Fcin.fcbuff = _Fcin.fcptr; + _Fcin._fcfile = f; + fcsave(&save); + if(!(buff=(char*)sfreserve(f,SF_UNBOUND,SF_LOCKR))) + { + fcrestore(&save); + _Fcin.fcchar = 0; + _Fcin.fcptr = _Fcin.fcbuff = &_Fcin.fcchar; + _Fcin.fclast = 0; + _Fcin._fcfile = (Sfio_t*)0; + return(EOF); + } + n = sfvalue(f); + fcrestore(&save); + sfread(f,buff,0); + _Fcin.fcoff = sftell(f);; + buff = (char*)sfreserve(f,SF_UNBOUND,SF_LOCKR); + _Fcin.fclast = (_Fcin.fcptr=_Fcin.fcbuff=(unsigned char*)buff)+n; + if(sffileno(f) >= 0) + *_Fcin.fclast = 0; + return(n); +} + + +/* + * With _Fcin.fcptr>_Fcin.fcbuff, the stream pointer is advanced and + * If _Fcin.fclast!=0, performs an sfreserve() for the next buffer. + * If a notify function has been set, it is called + * If last is non-zero, and the stream is a file, 0 is returned when + * the previous character is a 0 byte. + */ +int fcfill(void) +{ + register int n; + register Sfio_t *f; + register unsigned char *last=_Fcin.fclast, *ptr=_Fcin.fcptr; + if(!(f=fcfile())) + { + /* see whether pointer has passed null byte */ + if(ptr>_Fcin.fcbuff && *--ptr==0) + _Fcin.fcptr=ptr; + else + _Fcin.fcoff = 0; + return(0); + } + if(last) + { + if( ptr_Fcin.fcbuff && *(ptr-1)==0) + return(0); + if(_Fcin.fcchar) + *last = _Fcin.fcchar; + if(ptr > last) + _Fcin.fcptr = ptr = last; + } + if((n = ptr-_Fcin.fcbuff) && _Fcin.fcfun) + (*_Fcin.fcfun)(f,(const char*)_Fcin.fcbuff,n); + sfread(f, (char*)_Fcin.fcbuff, n); + _Fcin.fcoff +=n; + _Fcin._fcfile = 0; + if(!last) + return(0); + else if(fcfopen(f) < 0) + return(EOF); + return(*_Fcin.fcptr++); +} + +/* + * Synchronize and close the current stream + */ +int fcclose(void) +{ + register unsigned char *ptr; + if(_Fcin.fclast==0) + return(0); + if((ptr=_Fcin.fcptr)>_Fcin.fcbuff && *(ptr-1)==0) + _Fcin.fcptr--; + if(_Fcin.fcchar) + *_Fcin.fclast = _Fcin.fcchar; + _Fcin.fclast = 0; + _Fcin.fcleft = 0; + return(fcfill()); +} + +/* + * Set the notify function that is called for each fcfill() + */ +void fcnotify(void (*fun)(Sfio_t*,const char*,int)) +{ + _Fcin.fcfun = fun; +} + +#ifdef __EXPORT__ +# define extern __EXPORT__ +#endif + +#undef fcsave +extern void fcsave(Fcin_t *fp) +{ + *fp = _Fcin; +} + +#undef fcrestore +extern void fcrestore(Fcin_t *fp) +{ + _Fcin = *fp; +} + +struct Extra +{ + unsigned char buff[2*MB_LEN_MAX]; + unsigned char *next; +}; + +int fcmbstate(const char *state, int *s, int *len) +{ + static struct Extra extra; + register int i, c, n; + if(_Fcin.fcleft) + { + if((c = mbsize(extra.next)) < 0) + c = 1; + if((_Fcin.fcleft -= c) <=0) + { + _Fcin.fcptr = (unsigned char*)fcfirst() - _Fcin.fcleft; + _Fcin.fcleft = 0; + } + *len = c; + if(c==1) + *s = state[*extra.next++]; + else if(c==0) + _Fcin.fcleft = 0; + else + { + c = mbchar(extra.next); + *s = state['a']; + } + return(c); + } + switch(*len = mbsize(_Fcin.fcptr)) + { + case -1: + if(_Fcin._fcfile && (n=(_Fcin.fclast-_Fcin.fcptr)) < MB_LEN_MAX) + { + memcmp(extra.buff, _Fcin.fcptr, n); + _Fcin.fcptr = _Fcin.fclast; + for(i=n; i < MB_LEN_MAX+n; i++) + { + if((extra.buff[i] = fcgetc(c))==0) + break; + } + _Fcin.fcleft = n; + extra.next = extra.buff; + return(fcmbstate(state,s,len)); + } + *len = 1; + /* fall through */ + case 0: + case 1: + *s = state[c=fcget()]; + break; + default: + c = mbchar(_Fcin.fcptr); + *s = state['a']; + } + return(c); +} + Index: src/lib/libshell/common/sh/streval.c =================================================================== --- src/lib/libshell/common/sh/streval.c (revision 0) +++ src/lib/libshell/common/sh/streval.c (revision 740) @@ -0,0 +1,904 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +/* + * D. G. Korn + * AT&T Labs + * + * arithmetic expression evaluator + * + * this version compiles the expression onto a stack + * and has a separate executor + */ + +#include "streval.h" +#include +#include +#include +#include "FEATURE/externs" + +#ifndef ERROR_dictionary +# define ERROR_dictionary(s) (s) +#endif +#ifndef SH_DICT +# define SH_DICT "libshell" +#endif + +#define MAXLEVEL 9 +#define SMALL_STACK 12 + +/* + * The following are used with tokenbits() macro + */ +#define T_OP 0x3f /* mask for operator number */ +#define T_BINARY 0x40 /* binary operators */ +#define T_NOFLOAT 0x80 /* non floating point operator */ +#define A_LVALUE (2*MAXPREC+2) + +#define pow2size(x) ((x)<=2?2:(x)<=4?4:(x)<=8?8:(x)<=16?16:(x)<=32?32:64) +#define round(x,size) (((x)+(size)-1)&~((size)-1)) +#define stakpush(v,val,type) ((((v)->offset=round(staktell(),pow2size(sizeof(type)))),\ + stakseek((v)->offset+sizeof(type)), \ + *((type*)stakptr((v)->offset)) = (val)),(v)->offset) +#define roundptr(ep,cp,type) (((unsigned char*)(ep))+round(cp-((unsigned char*)(ep)),pow2size(sizeof(type)))) + +static int level; + +struct vars /* vars stacked per invocation */ +{ + const char *expr; /* current expression */ + const char *nextchr; /* next char in current expression */ + const char *errchr; /* next char after error */ + const char *errstr; /* error string */ + struct lval errmsg; /* error message text */ + int offset; /* offset for pushchr macro */ + int staksize; /* current stack size needed */ + int stakmaxsize; /* maximum stack size needed */ + unsigned char paren; /* parenthesis level */ + char infun; /* incremented by comma inside function */ + int emode; + Sfdouble_t (*convert)(const char**,struct lval*,int,Sfdouble_t); +}; + +typedef int (*Math_0_f)(Sfdouble_t); +typedef Sfdouble_t (*Fun_t)(Sfdouble_t,...); +typedef Sfdouble_t (*Math_1_f)(Sfdouble_t); +typedef Sfdouble_t (*Math_2_f)(Sfdouble_t,Sfdouble_t); +typedef Sfdouble_t (*Math_3_f)(Sfdouble_t,Sfdouble_t,Sfdouble_t); + +#define getchr(vp) (*(vp)->nextchr++) +#define peekchr(vp) (*(vp)->nextchr) +#define ungetchr(vp) ((vp)->nextchr--) + +#if ('a'==97) /* ASCII encodings */ +# define getop(c) (((c) >= sizeof(strval_states))? \ + ((c)=='|'?A_OR:((c)=='^'?A_XOR:((c)=='~'?A_TILDE:A_REG))):\ + strval_states[(c)]) +#else +# define getop(c) (isdigit(c)?A_DIG:((c==' '||c=='\t'||c=='\n'||c=='"')?0: \ + (c=='<'?A_LT:(c=='>'?A_GT:(c=='='?A_ASSIGN: \ + (c=='+'?A_PLUS:(c=='-'?A_MINUS:(c=='*'?A_TIMES: \ + (c=='/'?A_DIV:(c=='%'?A_MOD:(c==','?A_COMMA: \ + (c=='&'?A_AND:(c=='!'?A_NOT:(c=='('?A_LPAR: \ + (c==')'?A_RPAR:(c==0?A_EOF:(c==':'?A_COLON: \ + (c=='?'?A_QUEST:(c=='|'?A_OR:(c=='^'?A_XOR: \ + (c=='\''?A_LIT: \ + (c=='.'?A_DOT:(c=='~'?A_TILDE:A_REG))))))))))))))))))))))) +#endif + +#define seterror(v,msg) _seterror(v,ERROR_dictionary(msg)) +#define ERROR(vp,msg) return(seterror((vp),msg)) + +/* + * set error message string and return(0) + */ +static int _seterror(struct vars *vp,const char *msg) +{ + if(!vp->errmsg.value) + vp->errmsg.value = (char*)msg; + vp->errchr = vp->nextchr; + vp->nextchr = ""; + level = 0; + return(0); +} + + +static void arith_error(const char *message,const char *expr, int mode) +{ + level = 0; + mode = (mode&3)!=0; + errormsg(SH_DICT,ERROR_exit(mode),message,expr); +} + +#if _ast_no_um2fm +static Sfdouble_t U2F(Sfulong_t u) +{ + Sflong_t s = u; + Sfdouble_t f; + + if (s >= 0) + return s; + s = u / 2; + f = s; + f *= 2; + if (u & 1) + f++; + return f; +} +#else +#define U2F(x) x +#endif + +Sfdouble_t arith_exec(Arith_t *ep) +{ + register Sfdouble_t num=0,*dp,*sp; + register unsigned char *cp = ep->code; + register int c,type=0; + register char *tp; + Sfdouble_t small_stack[SMALL_STACK+1]; + const char *ptr = ""; + Fun_t fun; + struct lval node; + node.emode = ep->emode; + node.expr = ep->expr; + node.elen = ep->elen; + if(level++ >=MAXLEVEL) + { + arith_error(e_recursive,ep->expr,ep->emode); + return(0); + } + if(ep->staksize < SMALL_STACK) + sp = small_stack; + else + sp = (Sfdouble_t*)stakalloc(ep->staksize*(sizeof(Sfdouble_t)+1)); + tp = (char*)(sp+ep->staksize); + tp--,sp--; + while(c = *cp++) + { + if(c&T_NOFLOAT) + { + if(type==1 || ((c&T_BINARY) && (c&T_OP)!=A_MOD && tp[-1]==1)) + arith_error(e_incompatible,ep->expr,ep->emode); + } + switch(c&T_OP) + { + case A_JMP: case A_JMPZ: case A_JMPNZ: + c &= T_OP; + cp = roundptr(ep,cp,short); + if((c==A_JMPZ && num) || (c==A_JMPNZ &&!num)) + cp += sizeof(short); + else + cp = (unsigned char*)ep + *((short*)cp); + continue; + case A_NOTNOT: + num = (num!=0); + type=0; + break; + case A_PLUSPLUS: + (*ep->fun)(&ptr,&node,ASSIGN,num+1); + break; + case A_MINUSMINUS: + (*ep->fun)(&ptr,&node,ASSIGN,num-1); + break; + case A_INCR: + num = num+1; + num = (*ep->fun)(&ptr,&node,ASSIGN,num); + break; + case A_DECR: + num = num-1; + num = (*ep->fun)(&ptr,&node,ASSIGN,num); + break; + case A_SWAP: + num = sp[-1]; + sp[-1] = *sp; + type = tp[-1]; + tp[-1] = *tp; + break; + case A_POP: + sp--; + continue; + case A_PUSHV: + cp = roundptr(ep,cp,Sfdouble_t*); + dp = *((Sfdouble_t**)cp); + cp += sizeof(Sfdouble_t*); + c = *(short*)cp; + cp += sizeof(short); + node.value = (char*)dp; + node.flag = c; + node.isfloat=0; + node.level = level; + num = (*ep->fun)(&ptr,&node,VALUE,num); + if(node.value != (char*)dp) + arith_error(node.value,ptr,ep->emode); + *++sp = num; + type = node.isfloat; + if(num > LDBL_ULLONG_MAX || num < LDBL_LLONG_MIN) + type = 1; + else + { + Sfdouble_t d=num; + if(num > LDBL_LLONG_MAX && num <= LDBL_ULLONG_MAX) + { + type = 2; + d -= LDBL_LLONG_MAX; + } + if((Sflong_t)d!=d) + type = 1; + } + *++tp = type; + c = 0; + break; + case A_STORE: + cp = roundptr(ep,cp,Sfdouble_t*); + dp = *((Sfdouble_t**)cp); + cp += sizeof(Sfdouble_t*); + c = *(short*)cp; + if(c<0) + c = 0; + cp += sizeof(short); + node.value = (char*)dp; + node.flag = c; + num = (*ep->fun)(&ptr,&node,ASSIGN,num); + break; + case A_PUSHF: + cp = roundptr(ep,cp,Fun_t); + *++sp = (Sfdouble_t)(cp-ep->code); + cp += sizeof(Fun_t); + *++tp = *cp++; + continue; + case A_PUSHN: + cp = roundptr(ep,cp,Sfdouble_t); + num = *((Sfdouble_t*)cp); + cp += sizeof(Sfdouble_t); + *++sp = num; + *++tp = type = *cp++; + break; + case A_NOT: + type=0; + num = !num; + break; + case A_UMINUS: + num = -num; + break; + case A_TILDE: + num = ~((Sflong_t)(num)); + break; + case A_PLUS: + num += sp[-1]; + break; + case A_MINUS: + num = sp[-1] - num; + break; + case A_TIMES: + num *= sp[-1]; + break; + case A_POW: + num = pow(sp[-1],num); + break; + case A_MOD: + if(!(Sflong_t)num) + arith_error(e_divzero,ep->expr,ep->emode); + if(type==2 || tp[-1]==2) + num = U2F((Sfulong_t)(sp[-1]) % (Sfulong_t)(num)); + else + num = (Sflong_t)(sp[-1]) % (Sflong_t)(num); + break; + case A_DIV: + if(type==1 || tp[-1]==1) + { + num = sp[-1]/num; + type = 1; + } + else if((Sfulong_t)(num)==0) + arith_error(e_divzero,ep->expr,ep->emode); + else if(type==2 || tp[-1]==2) + num = U2F((Sfulong_t)(sp[-1]) / (Sfulong_t)(num)); + else + num = (Sflong_t)(sp[-1]) / (Sflong_t)(num); + break; + case A_LSHIFT: + if(tp[-1]==2) + num = U2F((Sfulong_t)(sp[-1]) << (long)(num)); + else + num = (Sflong_t)(sp[-1]) << (long)(num); + break; + case A_RSHIFT: + if(tp[-1]==2) + num = U2F((Sfulong_t)(sp[-1]) >> (long)(num)); + else + num = (Sflong_t)(sp[-1]) >> (long)(num); + break; + case A_XOR: + if(type==2 || tp[-1]==2) + num = U2F((Sfulong_t)(sp[-1]) ^ (Sfulong_t)(num)); + else + num = (Sflong_t)(sp[-1]) ^ (Sflong_t)(num); + break; + case A_OR: + if(type==2 || tp[-1]==2) + num = U2F((Sfulong_t)(sp[-1]) | (Sfulong_t)(num)); + else + num = (Sflong_t)(sp[-1]) | (Sflong_t)(num); + break; + case A_AND: + if(type==2 || tp[-1]==2) + num = U2F((Sfulong_t)(sp[-1]) & (Sfulong_t)(num)); + else + num = (Sflong_t)(sp[-1]) & (Sflong_t)(num); + break; + case A_EQ: + num = (sp[-1]==num); + type=0; + break; + case A_NEQ: + num = (sp[-1]!=num); + type=0; + break; + case A_LE: + num = (sp[-1]<=num); + type=0; + break; + case A_GE: + num = (sp[-1]>=num); + type=0; + break; + case A_GT: + num = (sp[-1]>num); + type=0; + break; + case A_LT: + num = (sp[-1]code+(int)(*sp))); + type = 0; + num = (*((Math_0_f)fun))(num); + break; + case A_CALL1: + sp--,tp--; + fun = *((Fun_t*)(ep->code+(int)(*sp))); + type = *tp; + num = (*fun)(num); + break; + case A_CALL2: + sp-=2,tp-=2; + fun = *((Fun_t*)(ep->code+(int)(*sp))); + type = *tp; + num = (*((Math_2_f)fun))(sp[1],num); + break; + case A_CALL3: + sp-=3,tp-=3; + fun = *((Fun_t*)(ep->code+(int)(*sp))); + type = *tp; + num = (*((Math_3_f)fun))(sp[1],sp[2],num); + break; + } + if(c&T_BINARY) + sp--,tp--; + *sp = num; + *tp = type; + } + if(level>0) + level--; + return(num); +} + +/* + * This returns operator tokens or A_REG or A_NUM + */ +static int gettok(register struct vars *vp) +{ + register int c,op; + vp->errchr = vp->nextchr; + while(1) + { + c = getchr(vp); + switch(op=getop(c)) + { + case 0: + vp->errchr = vp->nextchr; + continue; + case A_EOF: + vp->nextchr--; + break; + /*FALL THRU*/ + case A_DIG: case A_REG: case A_DOT: case A_LIT: + if(op==A_DOT) + { + if((c=peekchr(vp))>='0' && c<='9') + op = A_DIG; + else + op = A_REG; + } + ungetchr(vp); + break; + case A_QUEST: + if(peekchr(vp)==':') + { + getchr(vp); + op = A_QCOLON; + } + break; + case A_LT: case A_GT: + if(peekchr(vp)==c) + { + getchr(vp); + op -= 2; + break; + } + /* FALL THRU */ + case A_NOT: case A_COLON: + c = '='; + /* FALL THRU */ + case A_ASSIGN: + case A_TIMES: + case A_PLUS: case A_MINUS: + case A_OR: case A_AND: + if(peekchr(vp)==c) + { + getchr(vp); + op--; + } + } + return(op); + } +} + +/* + * evaluate a subexpression with precedence + */ + +static int expr(register struct vars *vp,register int precedence) +{ + register int c, op; + int invalid,wasop=0; + struct lval lvalue,assignop; + const char *pos; + Sfdouble_t d; + + lvalue.value = 0; + lvalue.fun = 0; +again: + op = gettok(vp); + c = 2*MAXPREC+1; + switch(op) + { + case A_PLUS: + goto again; + case A_EOF: + if(precedence>5) + ERROR(vp,e_moretokens); + return(1); + case A_MINUS: + op = A_UMINUS; + goto common; + case A_NOT: + goto common; + case A_MINUSMINUS: + c = A_LVALUE; + op = A_DECR|T_NOFLOAT; + goto common; + case A_PLUSPLUS: + c = A_LVALUE; + op = A_INCR|T_NOFLOAT; + /* FALL THRU */ + case A_TILDE: + op |= T_NOFLOAT; + common: + if(!expr(vp,c)) + return(0); + stakputc(op); + break; + default: + vp->nextchr = vp->errchr; + wasop = 1; + } + invalid = wasop; + while(1) + { + assignop.value = 0; + op = gettok(vp); + if(op==A_DIG || op==A_REG || op==A_LIT) + { + if(!wasop) + ERROR(vp,e_synbad); + goto number; + } + if(wasop++ && op!=A_LPAR) + ERROR(vp,e_synbad); + /* check for assignment operation */ + if(peekchr(vp)== '=' && !(strval_precedence[op]&NOASSIGN)) + { + if((!lvalue.value || precedence > 3)) + ERROR(vp,e_notlvalue); + if(precedence==3) + precedence = 2; + assignop = lvalue; + getchr(vp); + c = 3; + } + else + { + c = (strval_precedence[op]&PRECMASK); + if(c==MAXPREC || op==A_POW) + c++; + c *= 2; + } + /* from here on c is the new precedence level */ + if(lvalue.value && (op!=A_ASSIGN)) + { + if(vp->staksize++>=vp->stakmaxsize) + vp->stakmaxsize = vp->staksize; + stakputc(A_PUSHV); + stakpush(vp,lvalue.value,char*); + if(lvalue.flag<0) + lvalue.flag = 0; + stakpush(vp,lvalue.flag,short); + if(vp->nextchr==0) + ERROR(vp,e_badnum); + if(!(strval_precedence[op]&SEQPOINT)) + lvalue.value = 0; + invalid = 0; + } + else if(precedence==A_LVALUE) + ERROR(vp,e_notlvalue); + if(invalid && op>A_ASSIGN) + ERROR(vp,e_synbad); + if(precedence >= c) + goto done; + if(strval_precedence[op]&RASSOC) + c--; + if((c < (2*MAXPREC+1)) && !(strval_precedence[op]&SEQPOINT)) + { + wasop = 0; + if(!expr(vp,c)) + return(0); + } + switch(op) + { + case A_RPAR: + if(!vp->paren) + ERROR(vp,e_paren); + if(invalid) + ERROR(vp,e_synbad); + goto done; + + case A_COMMA: + wasop = 0; + if(vp->infun) + vp->infun++; + else + { + stakputc(A_POP); + vp->staksize--; + } + if(!expr(vp,c)) + return(0); + lvalue.value = 0; + break; + + case A_LPAR: + { + int infun = vp->infun; + Sfdouble_t (*fun)(Sfdouble_t,...); + int nargs = lvalue.nargs; + fun = lvalue.fun; + lvalue.fun = 0; + if(fun) + { + if(vp->staksize++>=vp->stakmaxsize) + vp->stakmaxsize = vp->staksize; + vp->infun=1; + stakputc(A_PUSHF); + stakpush(vp,fun,Fun_t); + stakputc(1); + } + else + vp->infun = 0; + if(!invalid) + ERROR(vp,e_synbad); + vp->paren++; + if(!expr(vp,1)) + return(0); + vp->paren--; + if(fun) + { + int x= (nargs>7); + nargs &= 7; + if(vp->infun != nargs) + ERROR(vp,e_argcount); + if(vp->staksize+=nargs>=vp->stakmaxsize) + vp->stakmaxsize = vp->staksize+nargs; + stakputc(A_CALL0+nargs -x); + vp->staksize -= nargs; + } + vp->infun = infun; + if (gettok(vp) != A_RPAR) + ERROR(vp,e_paren); + wasop = 0; + break; + } + + case A_PLUSPLUS: + case A_MINUSMINUS: + wasop=0; + op |= T_NOFLOAT; + case A_ASSIGN: + if(!lvalue.value) + ERROR(vp,e_notlvalue); + if(op==A_ASSIGN) + { + stakputc(A_STORE); + stakpush(vp,lvalue.value,char*); + stakpush(vp,lvalue.flag,short); + vp->staksize--; + } + else + stakputc(op); + lvalue.value = 0; + break; + + case A_QUEST: + { + int offset1,offset2; + stakputc(A_JMPZ); + offset1 = stakpush(vp,0,short); + stakputc(A_POP); + if(!expr(vp,1)) + return(0); + if(gettok(vp)!=A_COLON) + ERROR(vp,e_questcolon); + stakputc(A_JMP); + offset2 = stakpush(vp,0,short); + *((short*)stakptr(offset1)) = staktell(); + stakputc(A_POP); + if(!expr(vp,3)) + return(0); + *((short*)stakptr(offset2)) = staktell(); + lvalue.value = 0; + wasop = 0; + break; + } + + case A_COLON: + ERROR(vp,e_badcolon); + break; + + case A_QCOLON: + case A_ANDAND: + case A_OROR: + { + int offset; + if(op==A_ANDAND) + op = A_JMPZ; + else + op = A_JMPNZ; + stakputc(op); + offset = stakpush(vp,0,short); + stakputc(A_POP); + if(!expr(vp,c)) + return(0); + *((short*)stakptr(offset)) = staktell(); + if(op!=A_QCOLON) + stakputc(A_NOTNOT); + lvalue.value = 0; + wasop=0; + break; + } + case A_AND: case A_OR: case A_XOR: case A_LSHIFT: + case A_RSHIFT: case A_MOD: + op |= T_NOFLOAT; + /* FALL THRU */ + case A_PLUS: case A_MINUS: case A_TIMES: case A_DIV: + case A_EQ: case A_NEQ: case A_LT: case A_LE: + case A_GT: case A_GE: case A_POW: + stakputc(op|T_BINARY); + vp->staksize--; + break; + case A_NOT: case A_TILDE: + default: + ERROR(vp,e_synbad); + number: + wasop = 0; + if(*vp->nextchr=='L' && vp->nextchr[1]=='\'') + { + vp->nextchr++; + op = A_LIT; + } + pos = vp->nextchr; + lvalue.isfloat = 0; + lvalue.expr = vp->expr; + lvalue.emode = vp->emode; + if(op==A_LIT) + { + /* character constants */ + if(pos[1]=='\\' && pos[2]=='\'' && pos[3]!='\'') + { + d = '\\'; + vp->nextchr +=2; + } + else + d = chresc(pos+1,(char**)&vp->nextchr); + /* posix allows the trailing ' to be optional */ + if(*vp->nextchr=='\'') + vp->nextchr++; + } + else + d = (*vp->convert)(&vp->nextchr, &lvalue, LOOKUP, 0); + if (vp->nextchr == pos) + { + if(vp->errmsg.value = lvalue.value) + vp->errstr = pos; + ERROR(vp,op==A_LIT?e_charconst:e_synbad); + } + if(op==A_DIG || op==A_LIT) + { + stakputc(A_PUSHN); + if(vp->staksize++>=vp->stakmaxsize) + vp->stakmaxsize = vp->staksize; + stakpush(vp,d,Sfdouble_t); + stakputc(lvalue.isfloat); + } + + /* check for function call */ + if(lvalue.fun) + continue; + break; + } + invalid = 0; + if(assignop.value) + { + if(vp->staksize++>=vp->stakmaxsize) + vp->stakmaxsize = vp->staksize; + if(assignop.flag<0) + assignop.flag = 0; + stakputc(A_STORE); + stakpush(vp,assignop.value,char*); + stakpush(vp,assignop.flag,short); + } + } + done: + vp->nextchr = vp->errchr; + return(1); +} + +Arith_t *arith_compile(const char *string,char **last,Sfdouble_t(*fun)(const char**,struct lval*,int,Sfdouble_t),int emode) +{ + struct vars cur; + register Arith_t *ep; + int offset; + memset((void*)&cur,0,sizeof(cur)); + cur.emode = emode; + cur.expr = cur.nextchr = string; + cur.convert = fun; + cur.emode = emode; + cur.errmsg.value = 0; + cur.errmsg.emode = emode; + stakseek(sizeof(Arith_t)); + if(!expr(&cur,0) && cur.errmsg.value) + { + if(cur.errstr) + string = cur.errstr; + (*fun)( &string , &cur.errmsg, MESSAGE, 0); + cur.nextchr = cur.errchr; + } + stakputc(0); + offset = staktell(); + ep = (Arith_t*)stakfreeze(0); + ep->expr = string; + ep->elen = strlen(string); + ep->code = (unsigned char*)(ep+1); + ep->fun = fun; + ep->emode = emode; + ep->size = offset - sizeof(Arith_t); + ep->staksize = cur.stakmaxsize+1; + if(last) + *last = (char*)(cur.nextchr); + return(ep); +} + +/* + * evaluate an integer arithmetic expression in s + * + * (Sfdouble_t)(*convert)(char** end, struct lval* string, int type, Sfdouble_t value) + * is a user supplied conversion routine that is called when unknown + * chars are encountered. + * *end points to the part to be converted and must be adjusted by convert to + * point to the next non-converted character; if typ is MESSAGE then string + * points to an error message string + * + * NOTE: (*convert)() may call strval() + */ + +Sfdouble_t strval(const char *s,char **end,Sfdouble_t(*conv)(const char**,struct lval*,int,Sfdouble_t),int emode) +{ + Arith_t *ep; + Sfdouble_t d; + char *sp=0; + int offset; + if(offset=staktell()) + sp = stakfreeze(1); + ep = arith_compile(s,end,conv,emode); + ep->emode = emode; + d = arith_exec(ep); + stakset(sp?sp:(char*)ep,offset); + return(d); +} + +#if _mem_name__exception +#undef _mem_name_exception +#define _mem_name_exception 1 +#undef exception +#define exception _exception +#undef matherr +#endif + +#if _mem_name_exception + +#undef error + +#if _BLD_shell && defined(__EXPORT__) +#define extern __EXPORT__ +#endif + +#ifndef DOMAIN +#define DOMAIN _DOMAIN +#endif +#ifndef OVERFLOW +#define OVERFLOW _OVERFLOW +#endif +#ifndef SING +#define SING _SING +#endif + + extern int matherr(struct exception *ep) + { + const char *message; + switch(ep->type) + { +#ifdef DOMAIN + case DOMAIN: + message = ERROR_dictionary(e_domain); + break; +#endif +#ifdef OVERFLOW + case OVERFLOW: + message = ERROR_dictionary(e_overflow); + break; +#endif +#ifdef SING + case SING: + message = ERROR_dictionary(e_singularity); + break; +#endif + default: + return(1); + } + level=0; + errormsg(SH_DICT,ERROR_exit(1),message,ep->name); + return(0); + } + +#undef extern + +#endif /* _mem_name_exception */ Index: src/lib/libshell/common/sh/name.c =================================================================== --- src/lib/libshell/common/sh/name.c (revision 0) +++ src/lib/libshell/common/sh/name.c (revision 740) @@ -0,0 +1,2382 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * AT&T Labs + * + */ + +#define putenv ___putenv + +#include "defs.h" +#include +#include "variables.h" +#include "path.h" +#include "lexstates.h" +#include "timeout.h" +#include "FEATURE/externs" +#include "streval.h" + +static char *savesub = 0; + +#if !_lib_pathnative && _lib_uwin_path + +#define _lib_pathnative 1 + +extern int uwin_path(const char*, char*, int); + +size_t +pathnative(const char* path, char* buf, size_t siz) +{ + return uwin_path(path, buf, siz); +} + +#endif /* _lib_pathnative */ + +static void attstore(Namval_t*,void*); +#ifndef _ENV_H +static void pushnam(Namval_t*,void*); +static char *staknam(Namval_t*, char*); +#endif +static void ltou(const char*,char*); +static void rightjust(char*, int, int); + +struct adata +{ + char **argnam; + int attsize; + char *attval; +}; + +char nv_local = 0; +#ifndef _ENV_H +static void(*nullscan)(Namval_t*,void*); +#endif + +#if ( SFIO_VERSION <= 20010201L ) +# define _data data +#endif + +#if !SHOPT_MULTIBYTE +# define mbchar(p) (*(unsigned char*)p++) +#endif /* SHOPT_MULTIBYTE */ + +/* ======== name value pair routines ======== */ + +#include "shnodes.h" +#include "builtins.h" + +static char *getbuf(size_t len) +{ + static char *buf; + static size_t buflen; + if(buflen < len) + { + if(buflen==0) + buf = (char*)malloc(len); + else + buf = (char*)realloc(buf,len); + buflen = len; + } + return(buf); +} + +#ifdef _ENV_H +void sh_envput(Env_t* ep,Namval_t *np) +{ + int offset = staktell(); + Namarr_t *ap = nv_arrayptr(np); + char *val; + if(ap) + { + if(ap->nelem&ARRAY_UNDEF) + nv_putsub(np,"0",0L); + else if(!(val=nv_getsub(np)) || strcmp(val,"0")) + return; + } + if(!(val = nv_getval(np))) + return; + stakputs(nv_name(np)); + stakputc('='); + stakputs(val); + stakseek(offset); + env_add(ep,stakptr(offset),ENV_STRDUP); +} +#endif + +/* + * output variable name in format for re-input + */ +void nv_outname(Sfio_t *out, char *name, int len) +{ + const char *cp=name, *sp; + int c, offset = staktell(); + while(sp= strchr(cp,'[')) + { + if(len>0 && cp+len <= sp) + break; + sfwrite(out,cp,++sp-cp); + stakseek(offset); + for(; c= *sp; sp++) + { + if(c==']') + break; + else if(c=='\\') + { + if(*sp=='[' || *sp==']' || *sp=='\\') + c = *sp++; + } + stakputc(c); + } + stakputc(0); + sfputr(out,sh_fmtq(stakptr(offset)),-1); + if(len>0) + { + sfputc(out,']'); + return; + } + cp = sp; + } + if(*cp) + { + if(len>0) + sfwrite(out,cp,len); + else + sfputr(out,cp,-1); + } + stakseek(offset); +} + +/* + * Perform parameter assignment for a linked list of parameters + * contains attributes for the parameters + */ +void nv_setlist(register struct argnod *arg,register int flags) +{ + register char *cp; + register Namval_t *np; + char *trap=sh.st.trap[SH_DEBUGTRAP]; + int traceon = (sh_isoption(SH_XTRACE)!=0); + int array = (flags&(NV_ARRAY|NV_IARRAY)); + flags &= ~(NV_TYPE|NV_ARRAY); + if(sh_isoption(SH_ALLEXPORT)) + flags |= NV_EXPORT; + if(sh.prefix) + { + flags &= ~(NV_IDENT|NV_EXPORT); + flags |= NV_VARNAME; + } + for(;arg; arg=arg->argnxt.ap) + { + sh.used_pos = 0; + if(arg->argflag&ARG_MAC) + cp = sh_mactrim(arg->argval,-1); + else + { + Namval_t *mp; + stakseek(0); + if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED))) + { + int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN); + struct fornod *fp=(struct fornod*)arg->argchn.ap; + register Shnode_t *tp=fp->fortre; + char *prefix = sh.prefix; + flag |= (flags&NV_NOSCOPE); + if(arg->argflag&ARG_QUOTED) + cp = sh_mactrim(fp->fornam,-1); + else + cp = fp->fornam; + error_info.line = fp->fortyp-sh.st.firstline; + if(sh.fn_depth && (Namval_t*)tp->com.comnamp==SYSTYPESET) + flag |= NV_NOSCOPE; + if(prefix && tp->com.comset && *cp=='[') + { + sh.prefix = 0; + np = nv_open(prefix,sh.var_tree,flag); + sh.prefix = prefix; + if(np) + { + if(!nv_isarray(np)) + { + stakputc('.'); + stakputs(cp); + cp = stakfreeze(1); + } + nv_close(np); + } + } + np = nv_open(cp,sh.var_tree,flag); + if(array) + { + if(!(flags&NV_APPEND)) + nv_unset(np); + if(array&NV_ARRAY) + { + nv_setarray(np,nv_associative); + } + else + { + nv_onattr(np,NV_ARRAY); + } + } + /* check for array assignment */ + if(tp->tre.tretyp!=TLST && tp->com.comarg && !tp->com.comset && !((mp=tp->com.comnamp) && nv_isattr(mp,BLT_DCL))) + { + int argc; + char **argv = sh_argbuild(&argc,&tp->com,0); + if(!(arg->argflag&ARG_APPEND)) + { + nv_unset(np); + } + nv_setvec(np,(arg->argflag&ARG_APPEND),argc,argv); + if(traceon || trap) + { + int n = -1; + char *name = nv_name(np); + if(arg->argflag&ARG_APPEND) + n = '+'; + if(trap) + sh_debug(trap,name,(char*)0,argv,(arg->argflag&ARG_APPEND)|ARG_ASSIGN); + if(traceon) + { + sh_trace(NIL(char**),0); + sfputr(sfstderr,name,n); + sfwrite(sfstderr,"=( ",3); + while(cp= *argv++) + sfputr(sfstderr,sh_fmtq(cp),' '); + sfwrite(sfstderr,")\n",2); + } + } + continue; + } + if(tp->tre.tretyp==TLST || !tp->com.comset || tp->com.comset->argval[0]!='[') + { + if(*cp!='.' && *cp!='[' && strchr(cp,'[')) + { + nv_close(np); + np = nv_open(cp,sh.var_tree,flag); + } + if((arg->argflag&ARG_APPEND) && !nv_isarray(np)) + nv_unset(np); + } + else + { + if(sh_isoption(SH_BASH) || (array&NV_IARRAY)) + { + if(!(arg->argflag&ARG_APPEND)) + nv_unset(np); + } + else if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0))) + { + nv_unset(np); + nv_setarray(np,nv_associative); + } + else + nv_setarray(np,nv_associative); + } + if(prefix) + cp = stakcopy(nv_name(np)); + sh.prefix = cp; + sh_exec(tp,sh_isstate(SH_ERREXIT)); + sh.prefix = prefix; + if(nv_isarray(np) && (mp=nv_opensub(np))) + np = mp; + nv_setvtree(np); + continue; + } + cp = arg->argval; + } + if(sh.prefix && *cp=='.' && cp[1]=='=') + cp++; + np = nv_open(cp,sh.var_tree,flags); + if(!np->nvfun) + { + if(sh.used_pos) + nv_onattr(np,NV_PARAM); + else + nv_offattr(np,NV_PARAM); + } + if(traceon || trap) + { + register char *sp=cp; + char *name=nv_name(np); + char *sub=0; + int append = 0; + if(nv_isarray(np)) + sub = savesub; + if(cp=strchr(sp,'=')) + { + if(cp[-1]=='+') + append = ARG_APPEND; + cp++; + } + if(traceon) + { + sh_trace(NIL(char**),0); + nv_outname(sfstderr,name,-1); + if(sub) + sfprintf(sfstderr,"[%s]",sh_fmtq(sub)); + if(cp) + { + if(append) + sfputc(sfstderr,'+'); + sfprintf(sfstderr,"=%s\n",sh_fmtq(cp)); + } + } + if(trap) + { + char *av[2]; + av[0] = cp; + av[1] = 0; + sh_debug(trap,name,sub,av,append); + } + } + } +} + +/* + * copy the subscript onto the stack + */ +static void stak_subscript(const char *sub, int last) +{ + register int c; + stakputc('['); + while(c= *sub++) + { + if(c=='[' || c==']' || c=='\\') + stakputc('\\'); + stakputc(c); + } + stakputc(last); +} + +/* + * construct a new name from a prefix and base name on the stack + */ +static char *copystack(const char *prefix, register const char *name, const char *sub) +{ + register int last=0,offset = staktell(); + if(prefix) + { + stakputs(prefix); + if(*stakptr(staktell()-1)=='.') + stakseek(staktell()-1); + if(*name=='.' && name[1]=='[') + last = staktell()+2; + if(*name!='[' && *name!='.' && *name!='=' && *name!='+') + stakputc('.'); + } + if(last) + { + stakputs(name); + if(sh_checkid(stakptr(last),(char*)0)) + stakseek(staktell()-2); + } + if(sub) + stak_subscript(sub,']'); + if(!last) + stakputs(name); + stakputc(0); + return(stakptr(offset)); +} + +/* + * grow this stack string by bytes and move from cp-1 to end + * right by . Returns beginning of string on the stack + */ +static char *stack_extend(const char *cname, char *cp, int n) +{ + register char *name = (char*)cname; + int offset = name - stakptr(0); + int m = cp-name; + stakseek(strlen(name)+n+1); + name = stakptr(offset); + cp = name + m; + m = strlen(cp)+1; + while(m-->0) + cp[n+m]=cp[m]; + return((char*)name); +} + +Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) +{ + char *cp=(char*)name, *sp, *xp; + register int c; + register Namval_t *np=0, *nq=0; + Namfun_t *fp=0; + long mode, add=0; + int copy=1,isref,top=0,noscope=(flags&NV_NOSCOPE); + if(root==sh.var_tree) + { + if(dtvnext(root)) + top = 1; + else + flags &= ~NV_NOSCOPE; + } + if(!dp->disc) + copy = dp->nofree; + if(*cp=='.') + cp++; + while(1) + { + switch(c = *(unsigned char*)(sp = cp)) + { + case '[': + if(flags&NV_NOARRAY) + { + dp->last = cp; + return(np); + } + cp = nv_endsubscript((Namval_t*)0,sp,0); + if(sp==name || sp[-1]=='.') + c = *(sp = cp); + goto skip; + case '.': + if(flags&NV_IDENT) + return(0); + if(root==sh.var_tree) + flags &= ~NV_EXPORT; + if(!copy && !(flags&NV_NOREF)) + { + c = sp-name; + copy = cp-name; + dp->nofree = 1; + name = copystack((const char*)0, name,(const char*)0); + cp = (char*)name+copy; + sp = (char*)name+c; + c = '.'; + } + skip: + case '+': + case '=': + *sp = 0; + case 0: + isref = 0; + dp->last = cp; + mode = (c=='.' || (flags&NV_NOADD))?add:NV_ADD; + if(flags&NV_NOSCOPE) + mode |= HASH_NOSCOPE; + if(top) + nq = nv_search(name,sh.var_base,0); + if(np = nv_search(name,root,mode)) + { + isref = nv_isref(np); + if(top) + { + if(nq==np) + flags &= ~NV_NOSCOPE; + else if(nq) + { + if(nv_isnull(np) && c!='.' && (np->nvfun=nv_cover(nq))) + np->nvname = nq->nvname; + flags |= NV_NOSCOPE; + } + } + else if(add && nv_isnull(np) && c=='.') + nv_setvtree(np); + } + if(c) + *sp = c; + top = 0; + if(isref) + { + char *sub=0; + if(c=='.') /* don't optimize */ + sh.argaddr = 0; + else if(flags&NV_NOREF) + { + if(c) + nv_unref(np); + return(np); + } + while(nv_isref(np)) + { + root = nv_reftree(np); + sh.last_table = nv_reftable(np); + sub = nv_refsub(np); + np = nv_refnode(np); + if(sub && c!='.') + nv_putsub(np,sub,0L); + flags |= NV_NOSCOPE; + } + if(sub && c==0) + return(np); + if(np==nq) + flags &= ~(noscope?0:NV_NOSCOPE); + else if(c) + { + c = (cp-sp); + copy = strlen(cp=nv_name(np)); + dp->nofree = 1; + name = copystack(cp,sp,sub); + sp = (char*)name + copy; + cp = sp+c; + c = *sp; + if(!noscope) + flags &= ~NV_NOSCOPE; + } + flags |= NV_NOREF; + } + sh.last_root = root; + do + { + if(!np) + { + if(*sp=='[' && *cp==0 && cp[-1]==']') + { + /* + * for backward compatibility + * evaluate subscript for + * possible side effects + */ + cp[-1] = 0; + sh_arith(sp+1); + cp[-1] = ']'; + } + return(np); + } + if(c=='[' || (c=='.' && nv_isarray(np))) + { + int n = 0; + if(c=='[') + { + n = mode|nv_isarray(np); + if(!mode && (flags&NV_ARRAY) && ((c=sp[1])=='*' || c=='@') && sp[2]==']') + { + /* not implemented yet */ + dp->last = cp; + return(np); + } + if(n&&(flags&NV_ARRAY)) + n |= ARRAY_FILL; + cp = nv_endsubscript(np,sp,n); + } + else + cp = sp; + if((c = *cp)=='.' || c=='[' || (n&ARRAY_FILL)) + + { + int m = cp-sp; + char *sub = m?nv_getsub(np):0; + if(!sub) + sub = "0"; + n = strlen(sub)+2; + if(!copy) + { + copy = cp-name; + dp->nofree = 1; + name = copystack((const char*)0, name,(const char*)0); + cp = (char*)name+copy; + sp = cp-m; + } + if(n <= m) + { + if(n) + { + memcpy(sp+1,sub,n-2); + sp[n-1] = ']'; + } + if(n < m) + cp=strcpy(sp+n,cp); + } + else + { + int r = n-m; + m = sp-name; + name = stack_extend(name, cp-1, r); + sp = (char*)name + m; + *sp = '['; + memcpy(sp+1,sub,n-2); + sp[n-1] = ']'; + cp = sp+n; + + } + } + else if(c==0 && mode && (n=nv_aindex(np))>0) + nv_putsub(np,(char*)0,n|ARRAY_FILL); + else if(n==0 && c==0) + { + /* subscript must be 0*/ + cp[-1] = 0; + c = sh_arith(sp+1); + cp[-1] = ']'; + if(c) + return(0); + } + dp->last = cp; + if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY))) + { + *(sp=cp) = 0; + nq = nv_search(name,root,mode); + *sp = c; + if(nq && nv_isnull(nq)) + nq = nv_arraychild(np,nq,c); + if(!(np=nq)) + return(np); + } + } + else if(nv_isarray(np)) + nv_putsub(np,NIL(char*),ARRAY_UNDEF); + if(c=='.' && (fp=np->nvfun)) + { + for(; fp; fp=fp->next) + { + if(fp->disc && fp->disc->createf) + break; + } + if(fp) + { + if((nq = (*fp->disc->createf)(np,cp+1,flags,fp)) == np) + { + add = NV_ADD; + break; + } + else if((np=nq) && (c = *(cp=dp->last=fp->last))==0) + return(np); + } + } + } + while(c=='['); + if(c!='.') + return(np); + cp++; + break; + default: + dp->last = cp; + if((c = mbchar(cp)) && !isaletter(c)) + return(np); + while(xp=cp, c=mbchar(cp), isaname(c)); + cp = xp; + } + } + return(np); +} + +/* + * Put into associative memory. + * If & NV_ARRAY then follow array to next subscript + * If & NV_NOARRAY then subscript is not allowed + * If & NV_NOSCOPE then use the current scope only + * If & NV_ASSIGN then assignment is allowed + * If & NV_IDENT then name must be an identifier + * If & NV_VARNAME then name must be a valid variable name + * If & NV_NOADD then node will not be added if not found + * If & NV_NOREF then don't follow reference + * If & NV_NOFAIL then don't generate an error message on failure + * SH_INIT is only set while initializing the environment + */ +Namval_t *nv_open(const char *name, Dt_t *root, int flags) +{ + register char *cp=(char*)name; + register int c; + register Namval_t *np; + Namfun_t fun; + int append=0; + const char *msg = e_varname; + char *fname = 0; + int offset = staktell(); + Dt_t *funroot; + + memset(&fun,0,sizeof(fun)); + sh.last_table = sh.namespace; + if(!root) + root = sh.var_tree; + sh.last_root = root; + if(root==sh_subfuntree(1)) + { + flags |= NV_NOREF; + msg = e_badfun; + if((np=sh.namespace) || strchr(name,'.')) + { + name = cp = copystack(np?nv_name(np):0,name,(const char*)0); + fname = strrchr(cp,'.'); + *fname = 0; + fun.nofree = 1; + flags &= ~NV_IDENT; + funroot = root; + root = sh.var_tree; + } + } + else if(!(flags&(NV_IDENT|NV_VARNAME|NV_ASSIGN))) + { + long mode = ((flags&NV_NOADD)?0:NV_ADD); + if(flags&NV_NOSCOPE) + mode |= HASH_SCOPE|HASH_NOSCOPE; + np = nv_search(name,root,mode); + if(np && !(flags&NV_REF)) + { + while(nv_isref(np)) + { + sh.last_table = nv_reftable(np); + np = nv_refnode(np); + } + } + return(np); + } + else if(sh.prefix && /**name!='.' &&*/ (flags&NV_ASSIGN)) + { + name = cp = copystack(sh.prefix,name,(const char*)0); + fun.nofree = 1; + } + c = *(unsigned char*)cp; + if(root==sh.alias_tree) + { + msg = e_aliname; + while((c= *(unsigned char*)cp++) && (c!='=') && (c!='/') && + (c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT)); + if(sh.subshell && c=='=') + root = sh_subaliastree(1); + if(c= *--cp) + *cp = 0; + np = nv_search(name, root, (flags&NV_NOADD)?0:NV_ADD); + if(c) + *cp = c; + goto skip; + } + else if(flags&NV_IDENT) + msg = e_ident; + else if(c=='.') + { + c = *++cp; + flags |= NV_NOREF; + if(root==sh.var_tree) + root = sh.var_base; + sh.last_table = 0; + } + if(c= !isaletter(c)) + goto skip; + np = nv_create(name, root, flags, &fun); + cp = fun.last; + if(fname) + { + c = ((flags&NV_NOSCOPE)?HASH_NOSCOPE:0)|((flags&NV_NOADD)?0:NV_ADD); + *fname = '.'; + np = nv_search(name, funroot, c); + *fname = 0; + } + else if(*cp=='+' && cp[1]=='=') + { + append=NV_APPEND; + cp++; + } + c = *cp; +skip: + if(c=='=' && np && (flags&NV_ASSIGN)) + { + cp++; + if(sh_isstate(SH_INIT)) + { + nv_putval(np, cp, NV_RDONLY); + if(np==PWDNOD) + nv_onattr(np,NV_TAGGED); + } + else + { + char *sub=0; + if(sh_isoption(SH_XTRACE) && nv_isarray(np)) + sub = nv_getsub(np); + c = msg==e_aliname? 0: (append | (flags&NV_EXPORT)); + nv_putval(np, cp, c); + savesub = sub; + } + nv_onattr(np, flags&NV_ATTRIBUTES); + } + else if(c) + { + if(flags&NV_NOFAIL) + return(0); + if(c=='.') + msg = e_noparent; + else if(c=='[') + msg = e_noarray; + errormsg(SH_DICT,ERROR_exit(1),msg,name); + } + if(fun.nofree) + stakseek(offset); + return(np); +} + +#if SHOPT_MULTIBYTE + static int ja_size(char*, int, int); + static void ja_restore(void); + static char *savep; + static char savechars[8+1]; +#endif /* SHOPT_MULTIBYTE */ + +/* + * put value into name-value node . + * If is an array, then the element given by the + * current index is assigned to. + * If contains NV_RDONLY, readonly attribute is ignored + * If contains NV_INTEGER, string is a pointer to a number + * If contains NV_NOFREE, previous value is freed, and + * becomes value of node and becomes attributes + */ +void nv_putval(register Namval_t *np, const char *string, int flags) +{ + register const char *sp=string; + register union Value *up; + register char *cp; + register int size = 0; + register int dot; + int was_local = nv_local; + if(!(flags&NV_RDONLY) && nv_isattr (np, NV_RDONLY)) + errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); + /* The following could cause the shell to fork if assignment + * would cause a side effect + */ + sh.argaddr = 0; + if(sh.subshell && !nv_local) + np = sh_assignok(np,1); + if(np->nvfun && !nv_isattr(np,NV_REF)) + { + /* This function contains disc */ + if(!nv_local) + { + nv_local=1; + nv_putv(np,sp,flags,np->nvfun); + if(sp && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) + sh_envput(sh.env,np); + return; + } + /* called from disc, assign the actual value */ + } + flags &= ~NV_NODISC; + if(flags&(NV_NOREF|NV_NOFREE)) + { + if(!nv_isnull(np) && np->nvalue.cp!=sp) + nv_unset(np); + nv_local=0; + np->nvalue.cp = (char*)sp; + nv_setattr(np,(flags&~NV_RDONLY)|NV_NOFREE); + return; + } + nv_local=0; + up= &np->nvalue; +#if !SHOPT_BSH + if(nv_isattr(np,NV_EXPORT)) + nv_offattr(np,NV_IMPORT); + else if(!nv_isattr(np,NV_MINIMAL)) + np->nvenv = 0; +#endif /* SHOPT_BSH */ + if(nv_isattr (np, NV_INTEGER)) + { + if(nv_isattr(np, NV_DOUBLE)) + { + if(nv_isattr(np, NV_LONG) && sizeof(double)ldp) + up->ldp = new_of(Sfdouble_t,0); + else if(flags&NV_APPEND) + old = *(up->ldp); + *(up->ldp) = ld+old; + } + else + { + double d,od=0; + if(flags&NV_INTEGER) + { + if(flags&NV_LONG) + d = (double)(*(Sfdouble_t*)sp); + else if(flags&NV_SHORT) + d = (double)(*(float*)sp); + else + d = *(double*)sp; + } + else + d = sh_arith(sp); + if(!up->dp) + up->dp = new_of(double,0); + else if(flags&NV_APPEND) + od = *(up->dp); + *(up->dp) = d+od; + } + } + else + { + if(nv_isattr(np, NV_LONG) && sizeof(int32_t)llp) + up->llp = new_of(Sflong_t,0); + else if(flags&NV_APPEND) + oll = *(up->llp); + *(up->llp) = ll+oll; + } + else + { + int32_t l=0,ol=0; + if(flags&NV_INTEGER) + { + if(flags&NV_DOUBLE) + { + Sflong_t ll; + if(flags&NV_LONG) + ll = *((Sfdouble_t*)sp); + else if(flags&NV_SHORT) + ll = *((float*)sp); + else + ll = *((double*)sp); + l = (int32_t)ll; + } + else if(nv_isattr(np,NV_UNSIGN)) + { + if(flags&NV_LONG) + l = *((Sfulong_t*)sp); + else if(flags&NV_SHORT) + l = *((uint16_t*)sp); + else + l = *(uint32_t*)sp; + } + else + { + if(flags&NV_LONG) + l = *((Sflong_t*)sp); + else if(flags&NV_SHORT) + l = *((int16_t*)sp); + else + l = *(int32_t*)sp; + } + } + else if(sp) + { + Sfdouble_t ld = sh_arith(sp); + if(ld<0) + l = (int32_t)ld; + else + l = (uint32_t)ld; + } + if(nv_size(np) <= 1) + nv_setsize(np,10); + if(nv_isattr (np, NV_SHORT)) + { + int16_t s=0; + if(flags&NV_APPEND) + s = up->s; + up->s = s+(int16_t)l; + nv_onattr(np,NV_NOFREE); + } + else + { + if(!up->lp) + up->lp = new_of(int32_t,0); + else if(flags&NV_APPEND) + ol = *(up->lp); + *(up->lp) = l+ol; + } + } + } + } + else + { + const char *tofree=0; + int offset; +#if _lib_pathnative + char buff[PATH_MAX]; +#endif /* _lib_pathnative */ + if(flags&NV_INTEGER) + { + if(flags&NV_DOUBLE) + { + if(flags&NV_LONG) + sfprintf(sh.strbuf,"%.*Lg",LDBL_DIG,*((Sfdouble_t*)sp)); + else + sfprintf(sh.strbuf,"%.*g",DBL_DIG,*((double*)sp)); + } + else if(flags&NV_LONG) + sfprintf(sh.strbuf,"%lld\0",*((Sflong_t*)sp)); + else + sfprintf(sh.strbuf,"%ld\0",*((int32_t*)sp)); + sp = sfstruse(sh.strbuf); + } + if(nv_isattr(np, NV_HOST)==NV_HOST && sp) + { +#ifdef _lib_pathnative + /* + * return the host file name given the UNIX name + */ + pathnative(sp,buff,sizeof(buff)); + if(buff[1]==':' && buff[2]=='/') + { + buff[2] = '\\'; + if(*buff>='A' && *buff<='Z') + *buff += 'a'-'A'; + } + sp = buff; +#else + ; +#endif /* _lib_pathnative */ + } + else if((nv_isattr(np, NV_RJUST|NV_ZFILL|NV_LJUST)) && sp) + { + for(;*sp == ' '|| *sp=='\t';sp++); + if((nv_isattr(np,NV_ZFILL)) && (nv_isattr(np,NV_LJUST))) + for(;*sp=='0';sp++); + size = nv_size(np); +#if SHOPT_MULTIBYTE + if(size) + size = ja_size((char*)sp,size,nv_isattr(np,NV_RJUST|NV_ZFILL)); +#endif /* SHOPT_MULTIBYTE */ + } + if(!up->cp) + flags &= ~NV_APPEND; + if((flags&NV_APPEND) && !nv_isattr(np,NV_BINARY)) + { + offset = staktell(); + stakputs(up->cp); + stakputs(sp); + stakputc(0); + sp = stakptr(offset); + } + if(!nv_isattr(np, NV_NOFREE)) + { + /* delay free in case points into free region */ + tofree = up->cp; + } + nv_offattr(np,NV_NOFREE); + if (sp) + { + dot = strlen(sp); +#if (_AST_VERSION>=20030127L) + if(nv_isattr(np,NV_BINARY)) + { + int oldsize = (flags&NV_APPEND)?nv_size(np):0; + if(flags&NV_RAW) + { + if(tofree) + free((void*)tofree); + up->cp = sp; + return; + } + size = 0; + if(nv_isattr(np,NV_ZFILL)) + size = nv_size(np); + if(size==0) + size = oldsize + (3*dot/4); + cp = (char*)malloc(size+1); + if(oldsize) + memcpy((void*)cp,(void*)up->cp,oldsize); + up->cp = cp; + if(size <= oldsize) + return; + dot = base64decode(sp,dot, (void**)0, cp+oldsize, size-oldsize,(void**)0); + dot += oldsize; + if(!nv_isattr(np,NV_ZFILL) || nv_size(np)==0) + nv_setsize(np,dot); + else if(nv_isattr(np,NV_ZFILL) && (size>dot)) + memset((void*)&cp[dot],0,size-dot); + return; + } + else +#endif + if(size==0 && nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL)) + nv_setsize(np,size=dot); + else if(size > dot) + dot = size; + cp = (char*)malloc(((unsigned)dot+1)); + } + else + cp = 0; + up->cp = cp; + if(sp) + { + if(nv_isattr(np, NV_LTOU)) + ltou(sp,cp); + else if(nv_isattr (np, NV_UTOL)) + sh_utol(sp,cp); + else + strcpy(cp, sp); + if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL)) + rightjust(cp,size,'0'); + else if(nv_isattr(np, NV_RJUST)) + rightjust(cp,size,' '); + else if(nv_isattr(np, NV_LJUST)) + { + register char *dp; + dp = strlen (cp) + cp; + *(cp = (cp + size)) = 0; + for (; dp < cp; *dp++ = ' '); + } +#if SHOPT_MULTIBYTE + /* restore original string */ + if(savep) + ja_restore(); +#endif /* SHOPT_MULTIBYTE */ + } + if(flags&NV_APPEND) + stakseek(offset); + if(tofree) + free((void*)tofree); + } + if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) + sh_envput(sh.env,np); + return; +} + +/* + * + * Right-justify so that it contains no more than + * characters. If contains fewer than + * characters, left-pad with . Trailing blanks + * in will be ignored. + * + * If the leftmost digit in is not a digit, + * will default to a blank. + */ +static void rightjust(char *str, int size, int fill) +{ + register int n; + register char *cp,*sp; + n = strlen(str); + + /* ignore trailing blanks */ + for(cp=str+n;n && *--cp == ' ';n--); + if (n == size) + return; + if(n > size) + { + *(str+n) = 0; + for (sp = str, cp = str+n-size; sp <= str+size; *sp++ = *cp++); + return; + } + else *(sp = str+size) = 0; + if (n == 0) + { + while (sp > str) + *--sp = ' '; + return; + } + while(n--) + { + sp--; + *sp = *cp--; + } + if(!isdigit(*str)) + fill = ' '; + while(sp>str) + *--sp = fill; + return; +} + +#if SHOPT_MULTIBYTE + /* + * handle left and right justified fields for multi-byte chars + * given physical size, return a logical size which reflects the + * screen width of multi-byte characters + * Multi-width characters replaced by spaces if they cross the boundary + * is non-zero for right justified fields + */ + + static int ja_size(char *str,int size,int type) + { + register char *cp = str; + register int c, n=size; + register int outsize; + register char *oldcp=cp; + int oldn; + wchar_t w; + while(*cp) + { + oldn = n; + w = mbchar(cp); + outsize = mbwidth(w); + size -= outsize; + c = cp-oldcp; + n += (c-outsize); + oldcp = cp; + if(size<=0 && type==0) + break; + } + /* check for right justified fields that need truncating */ + if(size <0) + { + if(type==0) + { + /* left justified and character crosses field boundary */ + n = oldn; + /* save boundary char and replace with spaces */ + size = c; + savechars[size] = 0; + while(size--) + { + savechars[size] = cp[size]; + cp[size] = ' '; + } + savep = cp; + } + size = -size; + if(type) + n -= (ja_size(str,size,0)-size); + } + return(n); + } + + static void ja_restore(void) + { + register char *cp = savechars; + while(*cp) + *savep++ = *cp++; + savep = 0; + } +#endif /* SHOPT_MULTIBYTE */ + +#ifndef _ENV_H +static char *staknam(register Namval_t *np, char *value) +{ + register char *p,*q; + q = stakalloc(strlen(nv_name(np))+(value?strlen(value):0)+2); + p=strcopy(q,nv_name(np)); + if(value) + { + *p++ = '='; + strcpy(p,value); + } + return(q); +} +#endif + +/* + * put the name and attribute into value of attributes variable + */ +#ifdef _ENV_H +static void attstore(register Namval_t *np, void *data) +{ + register int flag, c = ' '; + NOT_USED(data); + if(!(nv_isattr(np,NV_EXPORT))) + return; + flag = nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER); + stakputc('='); + if((flag&NV_DOUBLE) && (flag&NV_INTEGER)) + { + /* export doubles as integers for ksh88 compatibility */ + stakputc(c+(flag&~(NV_DOUBLE|NV_EXPNOTE))); + } + else + { + stakputc(c+flag); + if(flag&NV_INTEGER) + c += nv_size(np); + } + stakputc(c); + stakputs(nv_name(np)); +} +#else +static void attstore(register Namval_t *np, void *data) +{ + register int flag = np->nvflag; + register struct adata *ap = (struct adata*)data; + if(!(flag&NV_EXPORT) || (flag&NV_FUNCT)) + return; + flag &= (NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER); + *ap->attval++ = '='; + if((flag&NV_DOUBLE) && (flag&NV_INTEGER)) + { + /* export doubles as integers for ksh88 compatibility */ + *ap->attval++ = ' '+(flag&~(NV_DOUBLE|NV_EXPNOTE)); + *ap->attval = ' '; + } + else + { + *ap->attval++ = ' '+flag; + if(flag&NV_INTEGER) + *ap->attval = ' ' + nv_size(np); + else + *ap->attval = ' '; + } + ap->attval = strcopy(++ap->attval,nv_name(np)); +} +#endif + +#ifndef _ENV_H +static void pushnam(Namval_t *np, void *data) +{ + register char *value; + register struct adata *ap = (struct adata*)data; + if(nv_isattr(np,NV_IMPORT)) + { + if(np->nvenv) + *ap->argnam++ = np->nvenv; + } + else if(value=nv_getval(np)) + *ap->argnam++ = staknam(np,value); + if(nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)) + ap->attsize += (strlen(nv_name(np))+4); +} +#endif + +/* + * Generate the environment list for the child. + */ + +#ifdef _ENV_H +char **sh_envgen(void) +{ + int offset,tell; + register char **er; + env_delete(sh.env,"_"); + er = env_get(sh.env); + offset = staktell(); + stakputs(e_envmarker); + tell = staktell(); + nv_scan(sh.var_tree, attstore,(void*)0,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)); + if(tell ==staktell()) + stakseek(offset); + else + *--er = stakfreeze(1)+offset; + return(er); +} +#else +char **sh_envgen(void) +{ + register char **er; + register int namec; + register char *cp; + struct adata data; + /* L_ARGNOD gets generated automatically as full path name of command */ + nv_offattr(L_ARGNOD,NV_EXPORT); + data.attsize = 6; + namec = nv_scan(sh.var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT); + er = (char**)stakalloc((namec+4)*sizeof(char*)); + data.argnam = (er+=2); + nv_scan(sh.var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT); + *data.argnam = (char*)stakalloc(data.attsize); + cp = data.attval = strcopy(*data.argnam,e_envmarker); + nv_scan(sh.var_tree, attstore,&data,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)); + *data.attval = 0; + if(cp!=data.attval) + data.argnam++; + *data.argnam = 0; + return(er); +} +#endif + +struct scan +{ + void (*scanfn)(Namval_t*, void*); + int scanmask; + int scanflags; + int scancount; + void *scandata; +}; + + +static int scanfilter(Dt_t *dict, void *arg, void *data) +{ + register Namval_t *np = (Namval_t*)arg; + register int k=np->nvflag; + register struct scan *sp = (struct scan*)data; + NOT_USED(dict); + if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags))) + { + if(!np->nvalue.cp && !nv_isattr(np,~NV_DEFAULT)) + return(0); + if(sp->scanfn) + { + if(nv_isarray(np)) + nv_putsub(np,NIL(char*),0L); + (*sp->scanfn)(np,sp->scandata); + } + sp->scancount++; + } + return(0); +} + +/* + * Walk through the name-value pairs + * if is non-zero, then only nodes with (nvflags&mask)==flags + * are visited + * If is zero, and non-zero, then nodes with one or + * more of is visited + * If and are zero, then all nodes are visted + */ +int nv_scan(Dt_t *root, void (*fn)(Namval_t*,void*), void *data,int mask, int flags) +{ + Dt_t *base=0; + struct scan sdata; + int (*hashfn)(Dt_t*, void*, void*); + sdata.scanmask = mask; + sdata.scanflags = flags&~NV_NOSCOPE; + sdata.scanfn = fn; + sdata.scancount = 0; + sdata.scandata = data; + hashfn = scanfilter; + if(flags&NV_NOSCOPE) + base = dtview((Dt_t*)root,0); + dtwalk(root, hashfn,&sdata); + if(base) + dtview((Dt_t*)root,base); + return(sdata.scancount); +} + +/* + * create a new environment scope + */ +void nv_scope(struct argnod *envlist) +{ + register Dt_t *newscope; + newscope = dtopen(&_Nvdisc,Dtoset); + dtview(newscope,(Dt_t*)sh.var_tree); + sh.var_tree = (Dt_t*)newscope; + if(envlist) + nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN); +} + +/* + * Remove freeable local space associated with the nvalue field + * of nnod. This includes any strings representing the value(s) of the + * node, as well as its dope vector, if it is an array. + */ + +void sh_envnolocal (register Namval_t *np, void *data) +{ + char *cp=0; + NOT_USED(data); + if(nv_isattr(np,NV_EXPORT) && nv_isarray(np)) + { + nv_putsub(np,NIL(char*),0); + if(cp = nv_getval(np)) + cp = strdup(cp); + } + if(nv_isattr(np,NV_EXPORT|NV_NOFREE)) + { + if(nv_isref(np)) + { + nv_offattr(np,NV_NOFREE|NV_REF); + free((void*)np->nvalue.nrp); + np->nvalue.cp = 0; + } + if(!cp) + return; + } + if(nv_isarray(np)) + nv_putsub(np,NIL(char*),ARRAY_UNDEF); + _nv_unset(np,NV_RDONLY); + nv_setattr(np,0); + if(cp) + { + nv_putval(np,cp,0); + free((void*)cp); + } +} + +/* + * Currently this is a dummy, but someday will be needed + * for reference counting + */ +void nv_close(Namval_t *np) +{ + NOT_USED(np); +} + +static void table_unset(register Dt_t *root, int flags, Dt_t *oroot) +{ + register Namval_t *np,*nq; + for(np=(Namval_t*)dtfirst(root);np;np=nq) + { + _nv_unset(np,flags); + if(oroot && (nq=nv_search(nv_name(np),oroot,0)) && nv_isattr(nq,NV_EXPORT)) + sh_envput(sh.env,nq); + nq = (Namval_t*)dtnext(root,np); + dtdelete(root,np); + free((void*)np); + } +} + +/* + * + * Set the value of to 0, and nullify any attributes + * that may have had. Free any freeable space occupied + * by the value of . If denotes an array member, it + * will retain its attributes. + * can contain NV_RDONLY to override the readonly attribute + * being cleared. + */ +void _nv_unset(register Namval_t *np,int flags) +{ + register union Value *up; + if(!(flags&NV_RDONLY) && nv_isattr (np,NV_RDONLY)) + errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); + if(is_afunction(np) && np->nvalue.ip) + { + register struct slnod *slp = (struct slnod*)(np->nvenv); + if(slp && !nv_isattr(np,NV_NOFREE)) + { + /* free function definition */ + register char *name=nv_name(np),*cp= strrchr(name,'.'); + if(cp) + { + Namval_t *npv; + *cp = 0; + npv = nv_open(name,sh.var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD); + *cp++ = '.'; + if(npv) + nv_setdisc(npv,cp,NIL(Namval_t*),(Namfun_t*)npv); + } + stakdelete(slp->slptr); + free((void*)np->nvalue.ip); + np->nvalue.ip = 0; + } + goto done; + } + if(sh.subshell && !nv_isnull(np)) + np = sh_assignok(np,0); + nv_offattr(np,NV_NODISC); + if(np->nvfun && !nv_isref(np)) + { + /* This function contains disc */ + if(!nv_local) + { + nv_local=1; + nv_putv(np,NIL(char*),flags,np->nvfun); + return; + } + /* called from disc, assign the actual value */ + nv_local=0; + } + up = &np->nvalue; + if(up->cp) + { + if(!nv_isattr (np, NV_NOFREE)) + free((void*)up->cp); + up->cp = 0; + } +done: + if(!nv_isarray(np) || !nv_arrayptr(np)) + { + if(nv_isref(np)) + free((void*)np->nvalue.nrp); + nv_setsize(np,0); + if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT)) + { + if(nv_isattr(np,NV_EXPORT) && !strchr(np->nvname,'[')) + env_delete(sh.env,nv_name(np)); + np->nvenv = 0; + nv_setattr(np,0); + } + else + nv_setattr(np,NV_MINIMAL); + } +} + +void nv_unset(register Namval_t *np) +{ + _nv_unset(np,0); +} + +/* + * return the node pointer in the highest level scope + */ +Namval_t *nv_scoped(register Namval_t *np) +{ + if(!dtvnext(sh.var_tree)) + return(np); + return(dtsearch(sh.var_tree,np)); +} + +#if 1 +/* + * return space separated list of names of variables in given tree + */ +static char *tableval(Dt_t *root) +{ + static Sfio_t *out; + register Namval_t *np; + register int first=1; + register Dt_t *base = dtview(root,0); + if(out) + sfseek(out,(Sfoff_t)0,SEEK_SET); + else + out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); + for(np=(Namval_t*)dtfirst(root);np;np=(Namval_t*)dtnext(root,np)) + { + if(!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE)) + { + if(!first) + sfputc(out,' '); + else + first = 0; + sfputr(out,np->nvname,-1); + } + } + sfputc(out,0); + if(base) + dtview(root,base); + return((char*)out->_data); +} +#endif + +#if SHOPT_OPTIMIZE +struct optimize +{ + Namfun_t hdr; + char **ptr; + struct optimize *next; + Namval_t *np; +}; + +static struct optimize *opt_free; + +static void optimize_clear(Namval_t* np, Namfun_t *fp) +{ + struct optimize *op = (struct optimize*)fp; + nv_stack(np,fp); + nv_stack(np,(Namfun_t*)0); + for(;op && op->np==np; op=op->next) + { + if(op->ptr) + { + *op->ptr = 0; + op->ptr = 0; + } + } +} + +static void put_optimize(Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + nv_putv(np,val,flags,fp); + optimize_clear(np,fp); +} + +static const Namdisc_t optimize_disc = {sizeof(struct optimize),put_optimize}; + +void nv_optimize(Namval_t *np) +{ + register Namfun_t *fp; + register struct optimize *op, *xp; + if(sh.argaddr) + { + for(fp=np->nvfun; fp; fp = fp->next) + { + if(fp->disc->getnum || fp->disc->getval) + { + sh.argaddr = 0; + return; + } + if(fp->disc== &optimize_disc) + break; + } + if((xp= (struct optimize*)fp) && xp->ptr==sh.argaddr) + return; + if(op = opt_free) + opt_free = op->next; + else + op=(struct optimize*)calloc(1,sizeof(struct optimize)); + op->ptr = sh.argaddr; + op->np = np; + if(xp) + { + op->hdr.disc = 0; + op->next = xp->next; + xp->next = op; + } + else + { + op->hdr.disc = &optimize_disc; + op->next = (struct optimize*)sh.optlist; + sh.optlist = (void*)op; + nv_stack(np,&op->hdr); + } + } +} + +void sh_optclear(Shell_t *shp, void *old) +{ + register struct optimize *op,*opnext; + for(op=(struct optimize*)shp->optlist; op; op = opnext) + { + opnext = op->next; + if(op->ptr && op->hdr.disc) + { + nv_stack(op->np,&op->hdr); + nv_stack(op->np,(Namfun_t*)0); + } + op->next = opt_free; + opt_free = op; + } + shp->optlist = old; +} + +#else +# define optimize_clear(np,fp) +#endif /* SHOPT_OPTIMIZE */ + +/* + * Return a pointer to a character string that denotes the value + * of . If refers to an array, return a pointer to + * the value associated with the current index. + * + * If the value of is an integer, the string returned will + * be overwritten by the next call to nv_getval. + * + * If has no value, 0 is returned. + */ + +char *nv_getval(register Namval_t *np) +{ + register union Value *up= &np->nvalue; + register int numeric; +#if SHOPT_OPTIMIZE + if(!nv_local && sh.argaddr) + nv_optimize(np); +#endif /* SHOPT_OPTIMIZE */ + if(!np->nvfun && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF|NV_TABLE)) + goto done; + if(nv_isref(np)) + { + sh.last_table = nv_reftable(np); + return(nv_name(nv_refnode(np))); + } + if(np->nvfun) + { + if(!nv_local) + { + nv_local=1; + return(nv_getv(np, np->nvfun)); + } + nv_local=0; + } + numeric = ((nv_isattr (np, NV_INTEGER)) != 0); + if(numeric) + { + Sflong_t ll; + if(!up->cp) + return("0"); + if(nv_isattr (np,NV_DOUBLE)) + { + Sfdouble_t ld; + double d; + char *format; + if(nv_isattr(np,NV_LONG)) + { + ld = *up->ldp; + if(nv_isattr (np,NV_EXPNOTE)) + format = "%.*Lg"; + else + format = "%.*Lf"; + sfprintf(sh.strbuf,format,nv_size(np),ld); + } + else + { + d = *up->dp; + if(nv_isattr (np,NV_EXPNOTE)) + format = "%.*g"; + else + format = "%.*f"; + sfprintf(sh.strbuf,format,nv_size(np),d); + } + return(sfstruse(sh.strbuf)); + } + else if(nv_isattr(np,NV_UNSIGN)) + { + if(nv_isattr (np,NV_LONG)) + ll = *(Sfulong_t*)up->llp; + else if(nv_isattr (np,NV_SHORT)) + ll = (uint16_t)up->s; + else + ll = *(uint32_t*)(up->lp); + } + else if(nv_isattr (np,NV_LONG)) + ll = *up->llp; + else if(nv_isattr (np,NV_SHORT)) + ll = up->s; + else + ll = *(up->lp); + if((numeric=nv_size(np))==10) + { + if(nv_isattr(np,NV_UNSIGN)) + { + sfprintf(sh.strbuf,"%I*u",sizeof(ll),ll); + return(sfstruse(sh.strbuf)); + } + numeric = 0; + } + return(fmtbasell(ll,numeric, numeric&&numeric!=10)); + } +done: +#if (_AST_VERSION>=20030127L) + /* + * if NV_RAW flag is on, return pointer to binary data + * otherwise, base64 encode the data and return this string + */ + if(up->cp && nv_isattr(np,NV_BINARY) && !nv_isattr(np,NV_RAW)) + { + char *cp; + int size= nv_size(np), insize=(4*size)/3+size/45+8; + base64encode(up->cp, size, (void**)0, cp=getbuf(insize), insize, (void**)0); + return(cp); + } +#endif + if((numeric=nv_size(np)) && up->cp && up->cp[numeric]) + { + char *cp = getbuf(numeric+1); + memcpy(cp,up->cp,numeric); + cp[numeric]=0; + return(cp); + } + return ((char*)up->cp); +} + +Sfdouble_t nv_getnum(register Namval_t *np) +{ + register union Value *up; + register Sfdouble_t r=0; + register char *str; +#if SHOPT_OPTIMIZE + if(!nv_local && sh.argaddr) + nv_optimize(np); +#endif /* SHOPT_OPTIMIZE */ + if(nv_istable(np)) + errormsg(SH_DICT,ERROR_exit(1),e_number,nv_name(np)); + if(np->nvfun) + { + if(!nv_local) + { + nv_local=1; + return(nv_getn(np, np->nvfun)); + } + nv_local=0; + } + if(nv_isattr (np, NV_INTEGER)) + { + up= &np->nvalue; + if(!up->lp) + r = 0; + else if(nv_isattr(np, NV_DOUBLE)) + { + if(nv_isattr(np, NV_LONG)) + r = *up->ldp; + else + r = *up->dp; + } + else if(nv_isattr(np, NV_UNSIGN)) + { + if(nv_isattr(np, NV_LONG)) + r = (Sflong_t)*((Sfulong_t*)up->llp); + else if(nv_isattr(np, NV_SHORT)) + r = (Sflong_t)((uint16_t)up->s); + else + r = *((uint32_t*)up->lp); + } + else + { + if(nv_isattr(np, NV_LONG)) + r = *up->llp; + else if(nv_isattr(np, NV_SHORT)) + r = up->s; + else + r = *up->lp; + } + } + else if((str=nv_getval(np)) && *str!=0) + { + if(np->nvfun || nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL)) + { + while(*str=='0') + str++; + } + r = sh_arith(str); + } + return(r); +} +/* + * Give the attributes and change its current + * value to conform to . The of left and right + * justified fields may be given. + */ +void nv_newattr (register Namval_t *np, unsigned newatts, int size) +{ + register char *sp; + register char *cp = 0; + register unsigned int n; + Namarr_t *ap = 0; + int oldsize,oldatts; + + /* check for restrictions */ + if(sh_isoption(SH_RESTRICTED) && ((sp=nv_name(np))==nv_name(PATHNOD) || sp==nv_name(SHELLNOD) || sp==nv_name(ENVNOD) || sp==nv_name(FPATHNOD))) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np)); + /* handle attributes that do not change data separately */ + n = np->nvflag; +#if SHOPT_BSH + if(newatts&NV_EXPORT) + nv_offattr(np,NV_IMPORT); +#endif /* SHOPT_BSH */ + if(((n^newatts)&NV_EXPORT)) + { + /* record changes to the environment */ + if(n&NV_EXPORT) + env_delete(sh.env,nv_name(np)); + else + sh_envput(sh.env,np); + } + if((size==0||(n&NV_INTEGER)) && ((n^newatts)&~NV_NOCHANGE)==0) + { + if(size) + nv_setsize(np,size); + nv_offattr(np, ~NV_NOFREE); + nv_onattr(np, newatts); + return; + } + /* for an array, change all the elements */ + if((ap=nv_arrayptr(np)) && ap->nelem>0) + nv_putsub(np,NIL(char*),ARRAY_SCAN); + oldsize = nv_size(np); + oldatts = np->nvflag; + if(ap) /* add element to prevent array deletion */ + ap->nelem++; + do + { + nv_setsize(np,oldsize); + np->nvflag = oldatts; + if (sp = nv_getval(np)) + { + if(nv_isattr(np,NV_ZFILL)) + while(*sp=='0') sp++; + cp = (char*)malloc((n=strlen (sp)) + 1); + strcpy(cp, sp); + if(ap) + { + Namval_t *mp; + ap->nelem &= ~ARRAY_SCAN; + if(mp=nv_opensub(np)) + nv_onattr(mp,NV_NOFREE); + } + nv_unset(np); + if(ap) + ap->nelem |= ARRAY_SCAN; + if(size==0 && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL))) + size = n; + } + else + nv_unset(np); + nv_setsize(np,size); + np->nvflag &= NV_ARRAY; + np->nvflag |= newatts; + if (cp) + { + nv_putval (np, cp, NV_RDONLY); + free(cp); + } + } + while(ap && nv_nextsub(np)); + if(ap) + ap->nelem--; + return; +} + +#ifndef _NEXT_SOURCE +static char *oldgetenv(const char *string) +{ + register char c0,c1; + register const char *cp, *sp; + register char **av = environ; + if(!string || (c0= *string)==0) + return(0); + if((c1=*++string)==0) + c1= '='; + while(cp = *av++) + { + if(cp[0]!=c0 || cp[1]!=c1) + continue; + sp = string; + while(*sp && *sp++ == *++cp); + if(*sp==0 && *++cp=='=') + return((char*)(cp+1)); + } + return(0); +} + +/* + * This version of getenv uses the hash storage to access environment values + */ +char *getenv(const char *name) +/*@ + assume name!=0; +@*/ +{ + register Namval_t *np; + if(!sh.var_tree) + return(oldgetenv(name)); + if((np = nv_search(name,sh.var_tree,0)) && nv_isattr(np,NV_EXPORT)) + return(nv_getval(np)); + if(name[0] == 'P' && name[1] == 'A' && name[2] == 'T' && name[3] == 'H' && name[4] == 0) + return(oldgetenv(name)); + return(0); +} +#endif /* _NEXT_SOURCE */ + +#undef putenv +/* + * This version of putenv uses the hash storage to assign environment values + */ +int putenv(const char *name) +{ + register Namval_t *np; + if(name) + { + np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN); + if(!strchr(name,'=')) + nv_unset(np); + } + return(0); +} + + +/* + * Override libast setenv() + */ +char* setenviron(const char *name) +{ + register Namval_t *np; + if(name) + { + np = nv_open(name,sh.var_tree,NV_EXPORT|NV_IDENT|NV_NOARRAY|NV_ASSIGN); + if(strchr(name,'=')) + return(nv_getval(np)); + nv_unset(np); + } + return(""); +} + +/* + * copy to changing lower case to upper case + * must be big enough to hold + * and may point to the same place. + */ + +static void ltou(register char const *str1,register char *str2) +/*@ + assume str1!=0 && str2!=0; + return x satisfying strlen(in str1)==strlen(in str2); +@*/ +{ + register int c; + for(; c= *((unsigned char*)str1); str1++,str2++) + { + if(islower(c)) + *str2 = toupper(c); + else + *str2 = c; + } + *str2 = 0; +} + +/* + * normalize and return pointer to subscript if any + */ +static char *lastdot(register char *cp) +{ + register char *dp=cp, *ep=0; + register int c; + while(c= *cp++) + { + *dp++ = c; + if(c=='[') + ep = cp; + else if(c=='.') + { + if(*cp=='[') + { + ep = nv_endsubscript((Namval_t*)0,cp,0); + c = ep-cp; + memcpy(dp,cp,c); + dp = sh_checkid(dp+1,dp+c); + cp = ep; + } + ep = 0; + } + } + *dp = 0; + return(ep); +} + +/* + * Create a reference node from to $np in dictionary + */ +void nv_setref(register Namval_t *np, Dt_t *hp, int flags) +{ + register Namval_t *nq, *nr; + register char *ep,*cp; + if(nv_isref(np)) + return; + if(nv_isarray(np)) + errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); + if(!(cp=nv_getval(np))) + errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np)); + if((ep = lastdot(cp)) && nv_isattr(np,NV_MINIMAL)) + errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); + if(!hp) + hp = sh.var_tree; + nr= nq = nv_open(cp, hp, flags|NV_NOREF); + while(nv_isref(nr)) + { + sh.last_table = nv_reftable(nr); + hp = nv_reftree(nr); + nr = nv_refnode(nr); + } + if(nr==np) + { + if(sh.namespace && nv_dict(sh.namespace)==hp) + errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np)); + /* bind to earlier scope, or add to global scope */ + if(!(hp=dtvnext(hp)) || (nq=nv_search((char*)np,hp,NV_ADD|HASH_BUCKET))==np) + errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np)); + } + if(ep) + { + /* cause subscript evaluation and return result */ +#if 0 + nv_endsubscript(nq,ep,NV_ADD); +#endif + ep = nv_getsub(nq); + } + nv_unset(np); + np->nvalue.nrp = newof(0,struct Namref,1,0); + np->nvalue.nrp->np = nq; + np->nvalue.nrp->root = hp; + if(ep) + np->nvalue.nrp->sub = strdup(ep); + np->nvalue.nrp->table = sh.last_table; + nv_onattr(np,NV_REF|NV_NOFREE); +} + +/* + * get the scope corresponding to + * whence uses the same values as lseeek() + */ +Shscope_t *sh_getscope(int index, int whence) +{ + register struct sh_scoped *sp, *topmost; + if(whence==SEEK_CUR) + sp = &sh.st; + else + { + if ((struct sh_scoped*)sh.topscope != sh.st.self) + topmost = (struct sh_scoped*)sh.topscope; + else + topmost = &(sh.st); + sp = topmost; + if(whence==SEEK_SET) + { + int n =0; + while(sp = sp->prevst) + n++; + index = n - index; + sp = topmost; + } + } + if(index < 0) + return((Shscope_t*)0); + while(index-- && (sp = sp->prevst)); + return((Shscope_t*)sp); +} + +/* + * make the top scope and return previous scope + */ +Shscope_t *sh_setscope(Shscope_t *scope) +{ + Shscope_t *old = (Shscope_t*)sh.st.self; + *sh.st.self = sh.st; + sh.st = *((struct sh_scoped*)scope); + sh.var_tree = scope->var_tree; + return(old); +} + +void nv_unscope(void) +{ + register Dt_t *root = sh.var_tree; + register Dt_t *dp = dtview(root,(Dt_t*)0); + table_unset(root,NV_RDONLY|NV_NOSCOPE,dp); + sh.var_tree=dp; + dtclose(root); +} + +/* + * The inverse of creating a reference node + */ +void nv_unref(register Namval_t *np) +{ + Namval_t *nq; + if(!nv_isref(np)) + return; + nq = nv_refnode(np); + nv_offattr(np,NV_NOFREE|NV_REF); + free((void*)np->nvalue.nrp); + np->nvalue.cp = strdup(nv_name(nq)); +#if SHOPT_OPTIMIZE + { + Namfun_t *fp; + for(fp=nq->nvfun; fp; fp = fp->next) + { + if(fp->disc== &optimize_disc) + { + optimize_clear(nq,fp); + return; + } + } + } +#endif +} + +/* + * These following are for binary compatibility with the old hash library + * They will be removed someday + */ + +#if defined(__IMPORT__) && defined(__EXPORT__) +# define extern __EXPORT__ +#endif + +#undef hashscope + +extern Dt_t *hashscope(Dt_t *root) +{ + return(dtvnext(root)); +} + +#undef hashfree + +extern Dt_t *hashfree(Dt_t *root) +{ + Dt_t *dp = dtvnext(root); + dtclose(root); + return(dp); +} + +#undef hashname + +extern char *hashname(void *obj) +{ + Namval_t *np = (Namval_t*)obj; + return(np->nvname); +} + +#undef hashlook + +extern void *hashlook(Dt_t *root, const char *name, int mode,int size) +{ + NOT_USED(size); + return((void*)nv_search(name,root,mode)); +} + +char *nv_name(register Namval_t *np) +{ + register Namval_t *table; + register Namfun_t *fp; + char *cp; + if(is_abuiltin(np) || is_afunction(np)) + return(np->nvname); + if(nv_istable(np)) +#if 1 + sh.last_table = nv_parent(np); +#else + sh.last_table = nv_create(np,0, NV_LAST,(Namfun_t*)0); +#endif + else if(!nv_isref(np)) + { + for(fp= np->nvfun ; fp; fp=fp->next) + if(fp->disc && fp->disc->namef) + { + if(np==sh.last_table) + sh.last_table = 0; + return((*fp->disc->namef)(np,fp)); + } + } + if(!(table=sh.last_table) || *np->nvname=='.' || table==sh.namespace || np==table) + return(np->nvname); + cp = nv_name(table); + sfprintf(sh.strbuf,"%s.%s",cp,np->nvname); + return(sfstruse(sh.strbuf)); +} + +Namval_t *nv_lastdict(void) +{ + return(sh.last_table); +} + +#undef nv_context +/* + * returns the data context for a builtin + */ +void *nv_context(Namval_t *np) +{ + return((void*)np->nvfun); +} + +#define DISABLE /* proto workaround */ + +int nv_isnull DISABLE (register Namval_t *np) +{ + return(nv_isnull(np)); +} + +#undef nv_setsize +int nv_setsize(register Namval_t *np, int size) +{ + int oldsize = nv_size(np); + if(size>=0) + np->nvsize = size; + return(oldsize); +} Index: src/lib/libshell/common/sh/subshell.c =================================================================== --- src/lib/libshell/common/sh/subshell.c (revision 0) +++ src/lib/libshell/common/sh/subshell.c (revision 740) @@ -0,0 +1,555 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * Create and manage subshells avoiding forks when possible + * + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" +#include +#include "io.h" +#include "fault.h" +#include "shnodes.h" +#include "shlex.h" +#include "jobs.h" +#include "variables.h" +#include "path.h" + +#ifndef PIPE_BUF +# define PIPE_BUF 512 +#endif + +/* + * Note that the following structure must be the same + * size as the Dtlink_t structure + */ +struct Link +{ + struct Link *next; + Namval_t *node; +}; + +/* + * The following structure is used for command substitution and (...) + */ +static struct subshell +{ + struct subshell *prev; /* previous subshell data */ + struct subshell *pipe; /* subshell where output goes to pipe on fork */ + Dt_t *var; /* variable table at time of subshell */ + struct Link *svar; /* save shell variable table */ + Dt_t *sfun; /* function scope for subshell */ + Dt_t *salias;/* alias scope for subshell */ +#ifdef PATH_BFPATH + Pathcomp_t *pathlist; /* for PATH variable */ +#endif +#if (ERROR_VERSION >= 20030214L) + struct Error_context_s *errcontext; +#else + struct errorcontext *errcontext; +#endif + Shopt_t options;/* save shell options */ + pid_t subpid; /* child process id */ + Sfio_t* saveout;/*saved standard output */ + char *pwd; /* present working directory */ + const char *shpwd; /* saved pointer to sh.pwd */ + void *jobs; /* save job info */ + mode_t mask; /* saved umask */ + short tmpfd; /* saved tmp file descriptor */ + short pipefd; /* read fd if pipe is created */ + char jobcontrol; + char monitor; + unsigned char fdstatus; + int fdsaved; /* bit make for saved files */ + int bckpid; +} *subshell_data; + +static int subenv; + +/* + * This routine will turn the sftmp() file into a real /tmp file or pipe + * if the /tmp file create fails + */ +void sh_subtmpfile(void) +{ + if(sfset(sfstdout,0,0)&SF_STRING) + { + register int fd; + register struct checkpt *pp = (struct checkpt*)sh.jmplist; + register struct subshell *sp = subshell_data->pipe; + /* save file descriptor 1 if open */ + if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0) + { + fcntl(fd,F_SETFD,FD_CLOEXEC); + sh.fdstatus[fd] = sh.fdstatus[1]|IOCLEX; + close(1); + } + else if(errno!=EBADF) + errormsg(SH_DICT,ERROR_system(1),e_toomany); + /* popping a discipline forces a /tmp file create */ + sfdisc(sfstdout,SF_POPDISC); + if((fd=sffileno(sfstdout))<0) + { + /* unable to create the /tmp file so use a pipe */ + int fds[2]; + Sfoff_t off; + sh_pipe(fds); + sp->pipefd = fds[0]; + sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC); + /* write the data to the pipe */ + if(off = sftell(sfstdout)) + write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off); + sfclose(sfstdout); + if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1) + errormsg(SH_DICT,ERROR_system(1),e_file+4); + sh_close(fds[1]); + } + else + { + sh.fdstatus[fd] = IOREAD|IOWRITE; + sfsync(sfstdout); + if(fd==1) + fcntl(1,F_SETFD,0); + else + { + sfsetfd(sfstdout,1); + sh.fdstatus[1] = sh.fdstatus[fd]; + sh.fdstatus[fd] = IOCLOSE; + } + } + sh_iostream(1); + sfset(sfstdout,SF_SHARE|SF_PUBLIC,1); + sfpool(sfstdout,sh.outpool,SF_WRITE); + if(pp && pp->olist && pp->olist->strm == sfstdout) + pp->olist->strm = 0; + } +} + +/* + * This routine creates a temp file if necessary and creates a subshell. + * The parent routine longjmps back to sh_subshell() + * The child continues possibly with its standard output replaced by temp file + */ +void sh_subfork(void) +{ + register struct subshell *sp = subshell_data; + pid_t pid; + /* see whether inside $(...) */ + if(sp->pipe) + sh_subtmpfile(); + if(pid = sh_fork(0,NIL(int*))) + { + /* this is the parent part of the fork */ + if(sp->subpid==0) + sp->subpid = pid; + siglongjmp(*sh.jmplist,SH_JMPSUB); + } + else + { + int16_t subshell; + /* this is the child part of the fork */ + /* setting subpid to 1 causes subshell to exit when reached */ + sh_onstate(SH_FORKED); + sh_onstate(SH_NOLOG); + sh_offstate(SH_MONITOR); + subshell_data = 0; + subshell = sh.subshell = 0; + nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); + sp->subpid=0; + } +} + +/* + * This routine will make a copy of the given node in the + * layer created by the most recent subshell_fork if the + * node hasn't already been copied + */ +Namval_t *sh_assignok(register Namval_t *np,int add) +{ + register Namval_t *mp; + register struct Link *lp; + register struct subshell *sp = (struct subshell*)subshell_data; + int save; + /* don't bother with this */ + if(!sp->shpwd || (nv_isnull(np) && !add)) + return(np); + /* don't bother to save if in newer scope */ + if(nv_search((char*)np,sp->var,HASH_BUCKET)!=np) + return(np); + for(lp=subshell_data->svar; lp; lp = lp->next) + { + if(lp->node==np) + return(np); + } + mp = newof(0,Namval_t,1,0); + lp = (struct Link*)mp; + lp->node = np; + lp->next = subshell_data->svar; + subshell_data->svar = lp; + save = sh.subshell; + sh.subshell = 0;; + nv_clone(np,mp,NV_NOFREE); + sh.subshell = save; + return(np); +} + +/* + * restore the variables + */ +static void nv_restore(struct subshell *sp) +{ + register struct Link *lp, *lq; + register Namval_t *mp, *np; + const char *save = sp->shpwd; + sp->shpwd = 0; /* make sure sh_assignok doesn't save with nv_unset() */ + for(lp=sp->svar; lp; lp=lq) + { + np = (Namval_t*)lp; + mp = lp->node; + lq = lp->next; + if(nv_isarray(mp)) + nv_putsub(mp,NIL(char*),ARRAY_SCAN); + _nv_unset(mp,NV_RDONLY); + nv_setsize(mp,nv_size(np)); + if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT)) + mp->nvenv = np->nvenv; + mp->nvfun = np->nvfun; + mp->nvflag = np->nvflag; + if((mp==nv_scoped(PATHNOD)) || (mp==nv_scoped(IFSNOD))) + nv_putval(mp, np->nvalue.cp,0); + else + mp->nvalue.cp = np->nvalue.cp; + np->nvfun = 0; + if(nv_isattr(mp,NV_EXPORT)) + { + char *name = nv_name(mp); + sh_envput(sh.env,mp); + if(*name=='_' && strcmp(name,"_AST_FEATURES")==0) + astconf(NiL, NiL, NiL); + } + else if(nv_isattr(np,NV_EXPORT)) + env_delete(sh.env,nv_name(mp)); + free((void*)np); + } + sp->shpwd=save; +} + +/* + * return pointer to alias tree + * create new one if in a subshell and one doesn't exist and create is non-zero + */ +Dt_t *sh_subaliastree(int create) +{ + register struct subshell *sp = subshell_data; + if(!sp || sh.curenv==0) + return(sh.alias_tree); + if(!sp->salias && create) + { + sp->salias = dtopen(&_Nvdisc,Dtoset); + dtview(sp->salias,sh.alias_tree); + sh.alias_tree = sp->salias; + } + return(sp->salias); +} + +/* + * return pointer to function tree + * create new one if in a subshell and one doesn't exist and create is non-zero + */ +Dt_t *sh_subfuntree(int create) +{ + register struct subshell *sp = subshell_data; + if(!sp || sh.curenv==0) + return(sh.fun_tree); + if(!sp->sfun && create) + { + sp->sfun = dtopen(&_Nvdisc,Dtoset); + dtview(sp->sfun,sh.fun_tree); + sh.fun_tree = sp->sfun; + } + return(sp->sfun); +} + +static void table_unset(register Dt_t *root) +{ + register Namval_t *np,*nq; + for(np=(Namval_t*)dtfirst(root);np;np=nq) + { + _nv_unset(np,NV_RDONLY); + nq = (Namval_t*)dtnext(root,np); + dtdelete(root,np); + free((void*)np); + } +} + +int sh_subsavefd(register int fd) +{ + register struct subshell *sp = subshell_data; + register int old=0; + if(sp) + { + old = !(sp->fdsaved&(1<<(fd-1))); + sp->fdsaved |= (1<<(fd-1)); + } + return(old); +} + +/* + * Run command tree in a virtual sub-shell + * If comsub is not null, then output will be placed in temp file (or buffer) + * If comsub is not null, the return value will be a stream consisting of + * output of command . Otherwise, NULL will be returned. + */ + +Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) +{ + Shell_t *shp = &sh; + struct subshell sub_data; + register struct subshell *sp = &sub_data; + int jmpval,nsig; + int savecurenv = shp->curenv; + int16_t subshell; + char *savsig; + Sfio_t *iop=0; + struct checkpt buff; + struct sh_scoped savst; + struct dolnod *argsav=0; + memset((char*)sp, 0, sizeof(*sp)); + sfsync(shp->outpool); + argsav = sh_arguse(); + if(shp->curenv==0) + { + subshell_data=0; + subenv = 0; + } + shp->curenv = ++subenv; + savst = shp->st; + sh_pushcontext(&buff,SH_JMPSUB); + subshell = shp->subshell+1; + nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); + shp->subshell = subshell; + sp->prev = subshell_data; + subshell_data = sp; + sp->errcontext = &buff.err; + sp->var = shp->var_tree; + sp->options = shp->options; + sp->jobs = job_subsave(); +#ifdef PATH_BFPATH + /* make sure initialization has occurred */ + if(!shp->pathlist) + path_get("."); + sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist); +#endif + if(!shp->pwd) + path_pwd(0); + sp->bckpid = shp->bckpid; + if(!comsub || !sh_isoption(SH_SUBSHARE)) + { + sp->shpwd = shp->pwd; + sp->pwd = (shp->pwd?strdup(shp->pwd):0); + sp->mask = shp->mask; + /* save trap table */ + shp->st.otrapcom = 0; + if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0]) + { + nsig += sizeof(char*); + memcpy(savsig=malloc(nsig),(char*)&shp->st.trapcom[0],nsig); + /* this nonsense needed for $(trap) */ + shp->st.otrapcom = (char**)savsig; + } + sh_sigreset(0); + } + jmpval = sigsetjmp(buff.buff,0); + if(jmpval==0) + { + if(comsub) + { + /* disable job control */ + sp->jobcontrol = job.jobcontrol; + sp->monitor = (sh_isstate(SH_MONITOR)!=0); + job.jobcontrol=0; + sh_offstate(SH_MONITOR); + sp->pipe = sp; + /* save sfstdout and status */ + sp->saveout = sfswap(sfstdout,NIL(Sfio_t*)); + sp->fdstatus = shp->fdstatus[1]; + sp->tmpfd = -1; + sp->pipefd = -1; + /* use sftmp() file for standard output */ + if(!(iop = sftmp(PIPE_BUF))) + { + sfswap(sp->saveout,sfstdout); + errormsg(SH_DICT,ERROR_system(1),e_tmpcreate); + } + sfswap(iop,sfstdout); + sfset(sfstdout,SF_READ,0); + shp->fdstatus[1] = IOWRITE; + } + else if(sp->prev) + { + sp->pipe = sp->prev->pipe; + flags &= ~sh_state(SH_NOFORK); + } + sh_exec(t,flags); + } + if(jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell) + { + /* trap on EXIT not handled by child */ + char *trap=shp->st.trapcom[0]; + shp->st.trapcom[0] = 0; /* prevent recursion */ + shp->oldexit = shp->exitval; + sh_trap(trap,0); + free(trap); + } + sh_popcontext(&buff); + if(shp->subshell==0) /* must be child process */ + { + subshell_data = sp->prev; + if(jmpval==SH_JMPSCRIPT) + siglongjmp(*shp->jmplist,jmpval); + sh_done(0); + } + if(comsub) + { + /* re-enable job control */ + job.jobcontrol = sp->jobcontrol; + if(sp->monitor) + sh_onstate(SH_MONITOR); + if(sp->pipefd>=0) + { + /* sftmp() file has been returned into pipe */ + iop = sh_iostream(sp->pipefd); + sfdisc(iop,SF_POPDISC); + sfclose(sfstdout); + } + else + { + /* move tmp file to iop and restore sfstdout */ + iop = sfswap(sfstdout,NIL(Sfio_t*)); + if(!iop) + { + /* maybe locked try again */ + sfclrlock(sfstdout); + iop = sfswap(sfstdout,NIL(Sfio_t*)); + } + if(iop && sffileno(iop)==1) + { + int fd=sfsetfd(iop,3); + if(fd<0) + errormsg(SH_DICT,ERROR_system(1),e_toomany); + shp->sftable[fd] = iop; + fcntl(fd,F_SETFD,FD_CLOEXEC); + shp->fdstatus[fd] = (shp->fdstatus[1]|IOCLEX); + shp->fdstatus[1] = IOCLOSE; + } + sfset(iop,SF_READ,1); + } + sfswap(sp->saveout,sfstdout); + /* check if standard output was preserved */ + if(sp->tmpfd>=0) + { + close(1); + fcntl(sp->tmpfd,F_DUPFD,1); + sh_close(sp->tmpfd); + } + shp->fdstatus[1] = sp->fdstatus; + } + if(sp->subpid) + job_wait(sp->subpid); + if(comsub && iop) + sfseek(iop,(off_t)0,SEEK_SET); + if(shp->subshell) + shp->subshell--; + subshell = shp->subshell; + nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); +#ifdef PATH_BFPATH + path_delete((Pathcomp_t*)shp->pathlist); + shp->pathlist = (void*)sp->pathlist; +#endif + job_subrestore(sp->jobs); + shp->jobenv = savecurenv; + shp->bckpid = sp->bckpid; + if(sp->shpwd) /* restore environment if saved */ + { + shp->options = sp->options; + nv_restore(sp); + if(sp->salias) + { + shp->alias_tree = dtview(sp->salias,0); + table_unset(sp->salias); + dtclose(sp->salias); + } + if(sp->sfun) + { + shp->fun_tree = dtview(sp->sfun,0); + table_unset(sp->sfun); + dtclose(sp->sfun); + } + sh_sigreset(1); + shp->st = savst; + shp->curenv = savecurenv; + if(nsig) + { + memcpy((char*)&shp->st.trapcom[0],savsig,nsig); + free((void*)savsig); + } + shp->options = sp->options; + if(!shp->pwd || strcmp(sp->pwd,shp->pwd)) + { + /* restore PWDNOD */ + Namval_t *pwdnod = nv_scoped(PWDNOD); + if(shp->pwd) + { + chdir(shp->pwd=sp->pwd); +#ifdef PATH_BFPATH + path_newdir(shp->pathlist); +#endif + } + if(nv_isattr(pwdnod,NV_NOFREE)) + pwdnod->nvalue.cp = (const char*)sp->pwd; + } + else if(sp->shpwd != shp->pwd) + { + shp->pwd = sp->pwd; + if(PWDNOD->nvalue.cp==sp->shpwd) + PWDNOD->nvalue.cp = sp->pwd; + } + else + free((void*)sp->pwd); + if(sp->mask!=shp->mask) + umask(shp->mask); + } + subshell_data = sp->prev; + sh_argfree(argsav,0); + shp->trapnote = 0; + if(shp->topfd != buff.topfd) + sh_iorestore(buff.topfd|IOSUBSHELL,jmpval); + if(shp->exitval > SH_EXITSIG) + { + int sig = shp->exitval&SH_EXITMASK; + if(sig==SIGINT || sig== SIGQUIT) + sh_fault(sig); + } + return(iop); +} Index: src/lib/libshell/common/sh/deparse.c =================================================================== --- src/lib/libshell/common/sh/deparse.c (revision 0) +++ src/lib/libshell/common/sh/deparse.c (revision 740) @@ -0,0 +1,584 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped +/* + * David Korn + * AT&T Labs + * + * shell deparser + * + */ + +#include "defs.h" +#include "shnodes.h" +#include "test.h" + + +#define HUGE_INT (((unsigned)-1)>>1) +#define BEGIN 0 +#define MIDDLE 1 +#define END 2 +#define PRE 1 +#define POST 2 + + +/* flags that can be specified with p_tree() */ +#define NO_NEWLINE 1 +#define NEED_BRACE 2 +#define NO_BRACKET 4 + +static void p_comlist(const struct dolnod*,int); +static void p_arg(const struct argnod*, int endchar, int opts); +static void p_comarg(const struct comnod*); +static void p_keyword(const char*,int); +static void p_redirect(const struct ionod*); +static void p_switch(const struct regnod*); +static void here_body(const struct ionod*); +static void p_tree(const Shnode_t*,int); + +static int level; +static int begin_line; +static int end_line; +static char io_op[7]; +static char un_op[3] = "-?"; +static const struct ionod *here_doc; +static Sfio_t *outfile; +static const char *forinit = ""; + +extern void sh_deparse(Sfio_t*, const Shnode_t*,int); + +void sh_deparse(Sfio_t *out, const Shnode_t *t,int tflags) +{ + outfile = out; + p_tree(t,tflags); +} +/* + * print script corresponding to shell tree + */ +static void p_tree(register const Shnode_t *t,register int tflags) +{ + register char *cp; + int save = end_line; + int needbrace = (tflags&NEED_BRACE); + tflags &= ~NEED_BRACE; + if(tflags&NO_NEWLINE) + end_line = ' '; + else + end_line = '\n'; + switch(t->tre.tretyp&COMMSK) + { + case TTIME: + if(t->tre.tretyp&COMSCAN) + p_keyword("!",BEGIN); + else + p_keyword("time",BEGIN); + if(t->par.partre) + p_tree(t->par.partre,tflags); + level--; + break; + + case TCOM: + if(begin_line && level>0) + sfnputc(outfile,'\t',level); + begin_line = 0; + p_comarg((struct comnod*)t); + break; + + case TSETIO: + if(t->tre.tretyp&FPCL) + tflags |= NEED_BRACE; + else + tflags = NO_NEWLINE|NEED_BRACE; + p_tree(t->fork.forktre,tflags); + p_redirect(t->fork.forkio); + break; + + case TFORK: + if(needbrace) + tflags |= NEED_BRACE; + if(t->tre.tretyp&(FAMP|FCOOP)) + { + tflags = NEED_BRACE|NO_NEWLINE; + end_line = ' '; + } + else if(t->fork.forkio) + tflags = NO_NEWLINE; + p_tree(t->fork.forktre,tflags); + if(t->fork.forkio) + p_redirect(t->fork.forkio); + if(t->tre.tretyp&FCOOP) + { + sfputr(outfile,"|&",'\n'); + begin_line = 1; + } + else if(t->tre.tretyp&FAMP) + { + sfputr(outfile,"&",'\n'); + begin_line = 1; + } + break; + + case TIF: + p_keyword("if",BEGIN); + p_tree(t->if_.iftre,0); + p_keyword("then",MIDDLE); + p_tree(t->if_.thtre,0); + if(t->if_.eltre) + { + p_keyword("else",MIDDLE); + p_tree(t->if_.eltre,0); + } + p_keyword("fi",END); + break; + + case TWH: + if(t->wh.whinc) + cp = "for"; + else if(t->tre.tretyp&COMSCAN) + cp = "until"; + else + cp = "while"; + p_keyword(cp,BEGIN); + if(t->wh.whinc) + { + struct argnod *arg = (t->wh.whtre)->ar.arexpr; + sfprintf(outfile,"(( %s; ",forinit); + forinit = ""; + sfputr(outfile,arg->argval,';'); + arg = (t->wh.whinc)->arexpr; + sfprintf(outfile," %s))\n",arg->argval); + } + else + p_tree(t->wh.whtre,0); + t = t->wh.dotre; + goto dolist; + + case TLST: + { + Shnode_t *tr = t->lst.lstrit; + if(tr->tre.tretyp==TWH && tr->wh.whinc && t->lst.lstlef->tre.tretyp==TARITH) + { + /* arithmetic for statement */ + struct argnod *init = (t->lst.lstlef)->ar.arexpr; + forinit= init->argval; + p_tree(t->lst.lstrit,tflags); + break; + } + if(needbrace) + p_keyword("{",BEGIN); + p_tree(t->lst.lstlef,0); + if(needbrace) + tflags = 0; + p_tree(t->lst.lstrit,tflags); + if(needbrace) + p_keyword("}",END); + break; + } + + case TAND: + cp = "&&"; + goto andor; + case TORF: + cp = "||"; + goto andor; + case TFIL: + cp = "|"; + andor: + { + int bracket = 0; + if(t->tre.tretyp&TTEST) + { + tflags |= NO_NEWLINE; + if(!(tflags&NO_BRACKET)) + { + p_keyword("[[",BEGIN); + tflags |= NO_BRACKET; + bracket=1; + } + } + p_tree(t->lst.lstlef,NEED_BRACE|NO_NEWLINE|(tflags&NO_BRACKET)); + sfputr(outfile,cp,here_doc?'\n':' '); + if(here_doc) + { + here_body(here_doc); + here_doc = 0; + } + level++; + p_tree(t->lst.lstrit,tflags|NEED_BRACE); + if(bracket) + p_keyword("]]",END); + level--; + break; + } + + case TPAR: + p_keyword("(",BEGIN); + p_tree(t->par.partre,0); + p_keyword(")",END); + break; + + case TARITH: + { + register struct argnod *ap = t->ar.arexpr; + if(begin_line && level) + sfnputc(outfile,'\t',level); + sfprintf(outfile,"(( %s ))%c",ap->argval,end_line); + if(!(tflags&NO_NEWLINE)) + begin_line=1; + break; + } + + case TFOR: + cp = ((t->tre.tretyp&COMSCAN)?"select":"for"); + p_keyword(cp,BEGIN); + sfputr(outfile,t->for_.fornam,' '); + if(t->for_.forlst) + { + sfputr(outfile,"in",' '); + tflags = end_line; + end_line = '\n'; + p_comarg(t->for_.forlst); + end_line = tflags; + } + else + sfputc(outfile,'\n'); + begin_line = 1; + t = t->for_.fortre; + dolist: + p_keyword("do",MIDDLE); + p_tree(t,0); + p_keyword("done",END); + break; + + case TSW: + p_keyword("case",BEGIN); + p_arg(t->sw.swarg,' ',0); + if(t->sw.swlst) + { + begin_line = 1; + sfputr(outfile,"in",'\n'); + tflags = end_line; + end_line = '\n'; + p_switch(t->sw.swlst); + end_line = tflags; + } + p_keyword("esac",END); + break; + + case TFUN: + if(t->tre.tretyp&FPOSIX) + { + sfprintf(outfile,"%s",t->funct.functnam); + p_keyword("()\n",BEGIN); + } + else + { + p_keyword("function",BEGIN); + tflags = (t->funct.functargs?' ':'\n'); + sfputr(outfile,t->funct.functnam,tflags); + if(t->funct.functargs) + { + tflags = end_line; + end_line = '\n'; + p_comarg(t->funct.functargs); + end_line = tflags; + } + } + begin_line = 1; + p_keyword("{\n",MIDDLE); + begin_line = 1; + p_tree(t->funct.functtre,0); + p_keyword("}",END); + break; + /* new test compound command */ + case TTST: + if(!(tflags&NO_BRACKET)) + p_keyword("[[",BEGIN); + if((t->tre.tretyp&TPAREN)==TPAREN) + { + p_keyword("(",BEGIN); + p_tree(t->lst.lstlef,NO_BRACKET|NO_NEWLINE); + p_keyword(")",END); + } + else + { + int flags = (t->tre.tretyp)>>TSHIFT; + if(t->tre.tretyp&TNEGATE) + sfputr(outfile,"!",' '); + if(t->tre.tretyp&TUNARY) + { + un_op[1] = flags; + sfputr(outfile,un_op,' '); + } + else + cp = ((char*)(shtab_testops+(flags&037)-1)->sh_name); + p_arg(&(t->lst.lstlef->arg),' ',0); + if(t->tre.tretyp&TBINARY) + { + sfputr(outfile,cp,' '); + p_arg(&(t->lst.lstrit->arg),' ',0); + } + } + if(!(tflags&NO_BRACKET)) + p_keyword("]]",END); + } + while(begin_line && here_doc) + { + here_body(here_doc); + here_doc = 0; + } + end_line = save; + return; +} + +/* + * print a keyword + * increment indent level for flag==BEGIN + * decrement indent level for flag==END + */ +static void p_keyword(const char *word,int flag) +{ + register int sep; + if(flag==END) + sep = end_line; + else if(*word=='[' || *word=='(') + sep = ' '; + else + sep = '\t'; + if(flag!=BEGIN) + level--; + if(begin_line && level) + sfnputc(outfile,'\t',level); + sfputr(outfile,word,sep); + if(sep=='\n') + begin_line=1; + else + begin_line=0; + if(flag!=END) + level++; +} + +static void p_arg(register const struct argnod *arg,register int endchar,int opts) +{ + register const char *cp; + register int flag; + do + { + if(!arg->argnxt.ap) + flag = endchar; + else if(opts&PRE) + { + /* case alternation lists in reverse order */ + p_arg(arg->argnxt.ap,'|',opts); + flag = endchar; + } + else if(opts) + flag = ' '; + cp = arg->argval; + if(*cp==0 && opts==POST && arg->argchn.ap) + { + /* compound assignment */ + struct fornod *fp=(struct fornod*)arg->argchn.ap; + sfprintf(outfile,"%s=(\n",fp->fornam); + sfnputc(outfile,'\t',++level); + p_tree(fp->fortre,0); + if(--level) + sfnputc(outfile,'\t',level); + sfputc(outfile,')'); + } + else if((arg->argflag&ARG_RAW) && (cp[1] || (*cp!='[' && *cp!=']'))) + cp = sh_fmtq(cp); + sfputr(outfile,cp,flag); + if(flag=='\n') + begin_line = 1; + arg = arg->argnxt.ap; + } + while((opts&POST) && arg); + return; +} + +static void p_redirect(register const struct ionod *iop) +{ + register char *cp; + register int iof,iof2; + for(;iop;iop=iop->ionxt) + { + iof=iop->iofile; + cp = io_op; + if(iop->iovname) + { + sfwrite(outfile,"(;",2); + sfputr(outfile,iop->iovname,')'); + cp++; + } + else + *cp = '0'+(iof&IOUFD); + if(iof&IOPUT) + { + if(*cp == '1' && !iop->iovname) + cp++; + io_op[1] = '>'; + } + else + { + if(*cp == '0' && !iop->iovname) + cp++; + io_op[1] = '<'; + } + io_op[2] = 0; + io_op[3] = 0; + if(iof&IOLSEEK) + { + io_op[1] = '#'; + if(iof&IOARITH) + strcpy(&io_op[3]," (("); + } + else if(iof&IOMOV) + io_op[2] = '&'; + else if(iof&(IORDW|IOAPP)) + io_op[2] = '>'; + else if(iof&IOCLOB) + io_op[2] = '|'; + if(iop->iodelim) + { + /* here document */ +#ifdef xxx + iop->iolink = (char*)here_doc; +#endif + here_doc = iop; + io_op[2] = '<'; +#ifdef future + if(iof&IOSTRIP) + io_op[3] = '-'; +#endif + } + sfputr(outfile,cp,' '); + if(iop->ionxt) + iof = ' '; + else + { + if((iof=end_line)=='\n') + begin_line = 1; + } + if((iof&IOLSEEK) && (iof&IOARITH)) + iof2 = iof, iof = ' '; + if(iop->iodelim) + { + if(!(iop->iofile&IODOC)) + sfwrite(outfile,"''",2); + sfputr(outfile,sh_fmtq(iop->iodelim),iof); + } + else if(iop->iofile&IORAW) + sfputr(outfile,sh_fmtq(iop->ioname),iof); + else + sfputr(outfile,iop->ioname,iof); + if((iof&IOLSEEK) && (iof&IOARITH)) + sfputr(outfile, "))", iof2); + } + return; +} + +static void p_comarg(register const struct comnod *com) +{ + register int flag = end_line; + if(com->comarg || com->comio) + flag = ' '; + if(com->comset) + p_arg(com->comset,flag,POST); + if(com->comarg) + { + if(!com->comio) + flag = end_line; + if(com->comtyp&COMSCAN) + p_arg(com->comarg,flag,POST); + else + p_comlist((struct dolnod*)com->comarg,flag); + } + if(com->comio) + p_redirect(com->comio); + return; +} + +static void p_comlist(const struct dolnod *dol,int endchar) +{ + register char *cp, *const*argv; + register int flag = ' ', special; + argv = dol->dolval+ARG_SPARE; + cp = *argv; + special = (*cp=='[' && cp[1]==0); + do + { + if(cp) + argv++; + else + cp = ""; + if(*argv==0) + { + if((flag=endchar)=='\n') + begin_line = 1; + special = (*cp==']' && cp[1]==0); + } + sfputr(outfile,special?cp:sh_fmtq(cp),flag); + special = 0; + } + while(cp = *argv); + return; +} + +static void p_switch(register const struct regnod *reg) +{ + if(level>1) + sfnputc(outfile,'\t',level-1); + p_arg(reg->regptr,')',PRE); + begin_line = 0; + sfputc(outfile,'\t'); + if(reg->regcom) + p_tree(reg->regcom,0); + level++; + if(reg->regflag) + p_keyword(";&",END); + else + p_keyword(";;",END); + if(reg->regnxt) + p_switch(reg->regnxt); + return; +} + +/* + * output here documents + */ +static void here_body(register const struct ionod *iop) +{ + Sfio_t *infile; +#ifdef xxx + if(iop->iolink) + here_body((struct inode*)iop->iolink); + iop->iolink = 0; +#endif + if(iop->iofile&IOSTRG) + infile = sfnew((Sfio_t*)0,iop->ioname,iop->iosize,-1,SF_STRING|SF_READ); + else + sfseek(infile=sh.heredocs,iop->iooffset,SEEK_SET); + sfmove(infile,outfile,iop->iosize,-1); + if(iop->iofile&IOSTRG) + sfclose(infile); + sfputr(outfile,iop->iodelim,'\n'); +} + Index: src/lib/libshell/common/sh/env.c =================================================================== --- src/lib/libshell/common/sh/env.c (revision 0) +++ src/lib/libshell/common/sh/env.c (revision 740) @@ -0,0 +1,255 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn * +* * +***********************************************************************/ +#pragma prototyped + +#include +#include + +#define env_change() (++ast.env_serial) + +typedef struct _venv_ Evar_t; +struct _venv_ +{ + union + { + Evar_t *next; + char *ptr; + } un; + Dtlink_t link; + int index; +}; + +typedef struct _env_ +{ + Dt_t *dt; + Evar_t *freelist; + char **env; + int count; + int extra; + int max; + int flags; +} Env_t; + +#define _BLD_env 1 +#include + +#define ENV_VALID 2 /* set if env is valid */ +#define ENV_PMALLOC 1 /* set if Evar_t->un.ptr *s malloced */ +#define ENV_VMALLOC 2 /* set of Evar_t was malloced */ +#define ENV_BITS 3 + +/* + * Compares the name portion of name=... only. + */ +static int compare(Dt_t *dt, Void_t* key1, Void_t* key2, Dtdisc_t* disc) +{ + register int c,d; + const unsigned char *s1=(unsigned const char*)key1; + const unsigned char *s2=(unsigned const char*)key2; + while((c= *s1++) && c!='=' && c==*s2) + s2++; + if(c=='=') + c = 0; + if((d=*s2)=='=') + d = 0; + return(c-d); +} + +static Dtdisc_t env_disc = +{ + 0, -1, + sizeof(char*), + 0, + 0, + compare +}; + +/* + * return a pointer to the environment in sorted order + * NULL is returned if there if there is nospace