181af778eSCasper H.S. Dik /*
281af778eSCasper H.S. Dik * CDDL HEADER START
381af778eSCasper H.S. Dik *
481af778eSCasper H.S. Dik * The contents of this file are subject to the terms of the
581af778eSCasper H.S. Dik * Common Development and Distribution License (the "License").
681af778eSCasper H.S. Dik * You may not use this file except in compliance with the License.
781af778eSCasper H.S. Dik *
881af778eSCasper H.S. Dik * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
981af778eSCasper H.S. Dik * or http://www.opensolaris.org/os/licensing.
1081af778eSCasper H.S. Dik * See the License for the specific language governing permissions
1181af778eSCasper H.S. Dik * and limitations under the License.
1281af778eSCasper H.S. Dik *
1381af778eSCasper H.S. Dik * When distributing Covered Code, include this CDDL HEADER in each
1481af778eSCasper H.S. Dik * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1581af778eSCasper H.S. Dik * If applicable, add the following below this CDDL HEADER, with the
1681af778eSCasper H.S. Dik * fields enclosed by brackets "[]" replaced with your own identifying
1781af778eSCasper H.S. Dik * information: Portions Copyright [yyyy] [name of copyright owner]
1881af778eSCasper H.S. Dik *
1981af778eSCasper H.S. Dik * CDDL HEADER END
2081af778eSCasper H.S. Dik */
2181af778eSCasper H.S. Dik
2281af778eSCasper H.S. Dik /*
2381af778eSCasper H.S. Dik * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2481af778eSCasper H.S. Dik * Use is subject to license terms.
2581af778eSCasper H.S. Dik */
2681af778eSCasper H.S. Dik
2781af778eSCasper H.S. Dik /*
2881af778eSCasper H.S. Dik * alias.c is a C version of the alias.sh wrapper (which links ksh
2981af778eSCasper H.S. Dik * builtins to commands in /usr/bin/, e.g. calling this wrapper as
3081af778eSCasper H.S. Dik * /usr/bin/alias will call the ksh "alias" builtin, running it as
3181af778eSCasper H.S. Dik * /usr/bin/cut will call the ksh "cut" builtin etc.
3281af778eSCasper H.S. Dik */
3381af778eSCasper H.S. Dik
3481af778eSCasper H.S. Dik #include <shell.h>
3581af778eSCasper H.S. Dik #include <nval.h>
3634f9b3eeSRoland Mainz #include <cmdext.h>
3781af778eSCasper H.S. Dik #include <stdio.h>
3881af778eSCasper H.S. Dik
3934f9b3eeSRoland Mainz typedef struct {
4034f9b3eeSRoland Mainz const char *name;
4134f9b3eeSRoland Mainz int (* func)(int, char **, void *);
4234f9b3eeSRoland Mainz } bfastpathrec;
4334f9b3eeSRoland Mainz
4434f9b3eeSRoland Mainz /*
4534f9b3eeSRoland Mainz * We've disabled the "fastpath" codepath for some commands below
4634f9b3eeSRoland Mainz * because it causes a paradoxon for large input files (as used by
4734f9b3eeSRoland Mainz * ON PerfPIT for testing). For /usr/bin/rev (where the issue was
4834f9b3eeSRoland Mainz * first discovered) it looks like this:
4934f9b3eeSRoland Mainz * - for small files like /etc/profile the fastpath is faster in a loop
5034f9b3eeSRoland Mainz * with 1000 iterations (8 seconds with fastpath, 14 seconds without
5134f9b3eeSRoland Mainz * fastpath)
5234f9b3eeSRoland Mainz * - for large files (/usr/pub/UTF-8 replicated until the test file
5334f9b3eeSRoland Mainz * reaches 24884706 bytes) the benchmark reverses: The fastpath now
5434f9b3eeSRoland Mainz * needs 40 seconds and without fastpath it needs 30 seconds (for 100
5534f9b3eeSRoland Mainz * iterations).
5634f9b3eeSRoland Mainz */
5734f9b3eeSRoland Mainz #if 0
5834f9b3eeSRoland Mainz #define ENABLE_PERFORMANCE_PARADOXON 1
5934f9b3eeSRoland Mainz #endif
6034f9b3eeSRoland Mainz
6134f9b3eeSRoland Mainz /*
6234f9b3eeSRoland Mainz * List of libcmd builtins which do not require a |Shell_t| context.
6334f9b3eeSRoland Mainz * This list was automatically generated from <ast/cmdext.h>
6434f9b3eeSRoland Mainz */
6534f9b3eeSRoland Mainz static const
6634f9b3eeSRoland Mainz bfastpathrec fastpath_builtins[] =
6734f9b3eeSRoland Mainz {
6834f9b3eeSRoland Mainz /* This list must be alphabetically sorted for |strcmp()| usage */
6934f9b3eeSRoland Mainz { "basename", b_basename },
7034f9b3eeSRoland Mainz { "cat", b_cat },
7134f9b3eeSRoland Mainz { "chgrp", b_chgrp },
7234f9b3eeSRoland Mainz { "chmod", b_chmod },
7334f9b3eeSRoland Mainz { "chown", b_chown },
7434f9b3eeSRoland Mainz #ifdef ENABLE_PERFORMANCE_PARADOXON
7534f9b3eeSRoland Mainz { "cksum", b_cksum },
7634f9b3eeSRoland Mainz #endif /* ENABLE_PERFORMANCE_PARADOXON */
7734f9b3eeSRoland Mainz { "cmp", b_cmp },
7834f9b3eeSRoland Mainz { "comm", b_comm },
7934f9b3eeSRoland Mainz { "cp", b_cp },
8034f9b3eeSRoland Mainz { "cut", b_cut },
8134f9b3eeSRoland Mainz { "date", b_date },
8234f9b3eeSRoland Mainz { "dirname", b_dirname },
8334f9b3eeSRoland Mainz { "egrep", b_egrep },
8434f9b3eeSRoland Mainz { "expr", b_expr },
8534f9b3eeSRoland Mainz { "fds", b_fds },
8634f9b3eeSRoland Mainz { "fgrep", b_fgrep },
8734f9b3eeSRoland Mainz { "fmt", b_fmt },
8834f9b3eeSRoland Mainz { "fold", b_fold },
8934f9b3eeSRoland Mainz { "getconf", b_getconf },
9034f9b3eeSRoland Mainz { "grep", b_grep },
9134f9b3eeSRoland Mainz { "head", b_head },
9234f9b3eeSRoland Mainz { "id", b_id },
9334f9b3eeSRoland Mainz { "join", b_join },
9434f9b3eeSRoland Mainz { "ln", b_ln },
9534f9b3eeSRoland Mainz { "logname", b_logname },
9634f9b3eeSRoland Mainz { "md5sum", b_md5sum },
9734f9b3eeSRoland Mainz { "mkdir", b_mkdir },
9834f9b3eeSRoland Mainz { "mkfifo", b_mkfifo },
9934f9b3eeSRoland Mainz { "mktemp", b_mktemp },
10034f9b3eeSRoland Mainz { "mv", b_mv },
10134f9b3eeSRoland Mainz { "paste", b_paste },
10234f9b3eeSRoland Mainz { "pathchk", b_pathchk },
10334f9b3eeSRoland Mainz { "pids", b_pids },
10434f9b3eeSRoland Mainz { "readlink", b_readlink },
10534f9b3eeSRoland Mainz #ifdef ENABLE_PERFORMANCE_PARADOXON
10634f9b3eeSRoland Mainz { "rev", b_rev },
10734f9b3eeSRoland Mainz #endif /* ENABLE_PERFORMANCE_PARADOXON */
10834f9b3eeSRoland Mainz { "rm", b_rm },
10934f9b3eeSRoland Mainz { "rmdir", b_rmdir },
11034f9b3eeSRoland Mainz { "stty", b_stty },
11134f9b3eeSRoland Mainz #ifdef ENABLE_PERFORMANCE_PARADOXON
11234f9b3eeSRoland Mainz { "sum", b_sum },
11334f9b3eeSRoland Mainz #endif /* ENABLE_PERFORMANCE_PARADOXON */
11434f9b3eeSRoland Mainz { "sync", b_sync },
11534f9b3eeSRoland Mainz { "tail", b_tail },
11634f9b3eeSRoland Mainz { "tee", b_tee },
11734f9b3eeSRoland Mainz { "tty", b_tty },
11834f9b3eeSRoland Mainz { "uname", b_uname },
11934f9b3eeSRoland Mainz { "uniq", b_uniq },
12034f9b3eeSRoland Mainz { "wc", b_wc },
12134f9b3eeSRoland Mainz { "xgrep", b_xgrep },
12234f9b3eeSRoland Mainz { NULL, (int (*)(int, char **, void *))NULL }
12334f9b3eeSRoland Mainz };
12434f9b3eeSRoland Mainz
12534f9b3eeSRoland Mainz static inline
12634f9b3eeSRoland Mainz const bfastpathrec *
find_bfastpathrec(const char * name)12734f9b3eeSRoland Mainz find_bfastpathrec(const char *name)
12834f9b3eeSRoland Mainz {
12934f9b3eeSRoland Mainz unsigned int i;
13034f9b3eeSRoland Mainz signed int cmpres;
13134f9b3eeSRoland Mainz for (i = 0; fastpath_builtins[i].name != NULL; i++) {
13234f9b3eeSRoland Mainz cmpres = strcmp(fastpath_builtins[i].name, name);
13334f9b3eeSRoland Mainz if (cmpres == 0)
13434f9b3eeSRoland Mainz return (&fastpath_builtins[i]);
13534f9b3eeSRoland Mainz else if (cmpres > 0)
13634f9b3eeSRoland Mainz return (NULL);
13734f9b3eeSRoland Mainz
13834f9b3eeSRoland Mainz }
13934f9b3eeSRoland Mainz return (NULL);
14034f9b3eeSRoland Mainz }
14134f9b3eeSRoland Mainz
14234f9b3eeSRoland Mainz static inline
14334f9b3eeSRoland Mainz int
fastpath_builtin_main(const bfastpathrec * brec,int argc,char * argv[])14434f9b3eeSRoland Mainz fastpath_builtin_main(const bfastpathrec *brec, int argc, char *argv[])
14534f9b3eeSRoland Mainz {
14634f9b3eeSRoland Mainz setlocale(LC_ALL, ""); /* calls |_ast_setlocale()| */
14734f9b3eeSRoland Mainz
14834f9b3eeSRoland Mainz return ((*brec->func)(argc, argv, NULL));
14934f9b3eeSRoland Mainz }
15034f9b3eeSRoland Mainz
15134f9b3eeSRoland Mainz
15281af778eSCasper H.S. Dik /* Builtin script, original derived from alias.sh */
15381af778eSCasper H.S. Dik static const char *script = "\n"
15481af778eSCasper H.S. Dik /* Get name of builtin */
1559a6f360eSCasper H.S. Dik "typeset cmd=\"${0##*/}\"\n"
15681af778eSCasper H.S. Dik /*
15781af778eSCasper H.S. Dik * If the requested command is not an alias load it explicitly
15881af778eSCasper H.S. Dik * to make sure it is not bound to a path (those built-ins which
15981af778eSCasper H.S. Dik * are mapped via shell aliases point to commands which are
16081af778eSCasper H.S. Dik * "special shell built-ins" which cannot be bound to a specific
16181af778eSCasper H.S. Dik * PATH element) - otherwise we may execute the wrong command
16281af778eSCasper H.S. Dik * if an executable with the same name sits in a PATH element
16381af778eSCasper H.S. Dik * before /usr/bin (e.g. /usr/xpg4/bin/ls would be executed
16481af778eSCasper H.S. Dik * before /usr/bin/ls if the path was something like
16581af778eSCasper H.S. Dik * PATH=/usr/xpg4/bin:/usr/bin).
16681af778eSCasper H.S. Dik */
16781af778eSCasper H.S. Dik "if [[ \"${cmd}\" != ~(Elr)(alias|unalias|command) ]] && "
16881af778eSCasper H.S. Dik "! alias \"${cmd}\" >/dev/null 2>&1 ; then\n"
169*d185aafaSOlga Kryzhanovska "PATH='' builtin \"${cmd}\"\n"
17081af778eSCasper H.S. Dik "fi\n"
17181af778eSCasper H.S. Dik /* command is a keyword and needs to be handled separately */
17281af778eSCasper H.S. Dik "if [[ \"${cmd}\" == \"command\" ]] ; then\n"
17381af778eSCasper H.S. Dik "command \"$@\"\n"
17481af778eSCasper H.S. Dik "else\n"
17534f9b3eeSRoland Mainz #ifdef WORKAROUND_FOR_ALIAS_CRASH
17634f9b3eeSRoland Mainz /*
17734f9b3eeSRoland Mainz * Work around a crash in /usr/bin/alias when invalid options are
17834f9b3eeSRoland Mainz * passed (e.g. $ /usr/bin/alias -c #). The shell code will call
17934f9b3eeSRoland Mainz * an error handler which does a |longjmp()| but somehow the code
18034f9b3eeSRoland Mainz * failed to do the |setjmp()| before this point.
18134f9b3eeSRoland Mainz * Putting the "alias" command in a subshell avoids the crash.
18234f9b3eeSRoland Mainz * Real cause of the issue is under investigation and a fix be
18334f9b3eeSRoland Mainz * delivered with the next ast-ksh update.
18434f9b3eeSRoland Mainz */
18534f9b3eeSRoland Mainz "( \"${cmd}\" \"$@\" )\n"
18634f9b3eeSRoland Mainz #else
18781af778eSCasper H.S. Dik "\"${cmd}\" \"$@\"\n"
18834f9b3eeSRoland Mainz #endif /* WORKAROUND_FOR_ALIAS_CRASH */
18981af778eSCasper H.S. Dik "fi\n"
19081af778eSCasper H.S. Dik "exitval=$?";
19181af778eSCasper H.S. Dik
19234f9b3eeSRoland Mainz
19334f9b3eeSRoland Mainz static inline
19481af778eSCasper H.S. Dik int
script_builtin_main(int argc,char * argv[])19534f9b3eeSRoland Mainz script_builtin_main(int argc, char *argv[])
19681af778eSCasper H.S. Dik {
19781af778eSCasper H.S. Dik int i;
19881af778eSCasper H.S. Dik Shell_t *shp;
19981af778eSCasper H.S. Dik Namval_t *np;
20081af778eSCasper H.S. Dik int exitval;
20181af778eSCasper H.S. Dik
20281af778eSCasper H.S. Dik /*
20381af778eSCasper H.S. Dik * Create copy of |argv| array shifted by one position to
20481af778eSCasper H.S. Dik * emulate $ /usr/bin/sh <scriptname> <args1> <arg2> ... #.
20581af778eSCasper H.S. Dik * First position is set to "/usr/bin/sh" since other
20681af778eSCasper H.S. Dik * values may trigger special shell modes (e.g. *rsh* will
20781af778eSCasper H.S. Dik * trigger "restricted" shell mode etc.).
20881af778eSCasper H.S. Dik */
20981af778eSCasper H.S. Dik char *xargv[argc+2];
21081af778eSCasper H.S. Dik xargv[0] = "/usr/bin/sh";
21181af778eSCasper H.S. Dik xargv[1] = "scriptname";
21281af778eSCasper H.S. Dik for (i = 0; i < argc; i++) {
21381af778eSCasper H.S. Dik xargv[i+1] = argv[i];
21481af778eSCasper H.S. Dik }
21581af778eSCasper H.S. Dik xargv[i+1] = NULL;
21681af778eSCasper H.S. Dik
21781af778eSCasper H.S. Dik shp = sh_init(argc+1, xargv, 0);
21881af778eSCasper H.S. Dik if (!shp)
21981af778eSCasper H.S. Dik error(ERROR_exit(1), "shell initialisation failed.");
22081af778eSCasper H.S. Dik (void) sh_trap(script, 0);
22181af778eSCasper H.S. Dik
22281af778eSCasper H.S. Dik np = nv_open("exitval", shp->var_tree, 0);
22381af778eSCasper H.S. Dik if (!np)
22481af778eSCasper H.S. Dik error(ERROR_exit(1), "variable %s not found.", "exitval");
22581af778eSCasper H.S. Dik exitval = (int)nv_getnum(np);
22681af778eSCasper H.S. Dik nv_close(np);
22781af778eSCasper H.S. Dik
22881af778eSCasper H.S. Dik return (exitval);
22981af778eSCasper H.S. Dik }
23034f9b3eeSRoland Mainz
23134f9b3eeSRoland Mainz int
main(int argc,char * argv[])23234f9b3eeSRoland Mainz main(int argc, char *argv[])
23334f9b3eeSRoland Mainz {
23434f9b3eeSRoland Mainz const char *progname;
23534f9b3eeSRoland Mainz const bfastpathrec *brec;
23634f9b3eeSRoland Mainz char execnamebuff[PATH_MAX+1];
23734f9b3eeSRoland Mainz
23834f9b3eeSRoland Mainz /* Get program name */
23934f9b3eeSRoland Mainz if (pathprog(argv[0], execnamebuff, sizeof (execnamebuff)) <= 0)
24034f9b3eeSRoland Mainz error(ERROR_exit(1), "could not determinate exec name.");
24134f9b3eeSRoland Mainz
24234f9b3eeSRoland Mainz progname = (const char *)strrchr(execnamebuff, '/');
24334f9b3eeSRoland Mainz if (progname != NULL) {
24434f9b3eeSRoland Mainz progname++;
24534f9b3eeSRoland Mainz }
24634f9b3eeSRoland Mainz else
24734f9b3eeSRoland Mainz {
24834f9b3eeSRoland Mainz progname = execnamebuff;
24934f9b3eeSRoland Mainz }
25034f9b3eeSRoland Mainz
25134f9b3eeSRoland Mainz /* Execute command... */
25234f9b3eeSRoland Mainz if (brec = find_bfastpathrec(progname)) {
25334f9b3eeSRoland Mainz /* ... either via a fast path (calling the code directly) ... */
25434f9b3eeSRoland Mainz return (fastpath_builtin_main(brec, argc, argv));
25534f9b3eeSRoland Mainz }
25634f9b3eeSRoland Mainz else
25734f9b3eeSRoland Mainz {
25834f9b3eeSRoland Mainz /* ... or from within a full shell. */
25934f9b3eeSRoland Mainz return (script_builtin_main(argc, argv));
26034f9b3eeSRoland Mainz }
26134f9b3eeSRoland Mainz }
262