summaryrefslogtreecommitdiff
path: root/examples/loadables
diff options
context:
space:
mode:
Diffstat (limited to 'examples/loadables')
-rw-r--r--examples/loadables/Makefile.in8
-rw-r--r--examples/loadables/cut.c376
-rw-r--r--examples/loadables/finfo.c6
-rw-r--r--examples/loadables/getconf.c75
-rw-r--r--examples/loadables/push.c95
-rw-r--r--examples/loadables/realpath.c119
6 files changed, 675 insertions, 4 deletions
diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in
index ab3c5d7e..95710890 100644
--- a/examples/loadables/Makefile.in
+++ b/examples/loadables/Makefile.in
@@ -67,7 +67,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \
ALLPROG = print truefalse sleep pushd finfo logname basename dirname \
tty pathchk tee head mkdir rmdir sprintf printenv id whoami \
- uname sync push ln unlink
+ uname sync push ln unlink cut realpath
OTHERPROG = necho getconf hello cat
all: $(SHOBJ_STATUS)
@@ -164,6 +164,11 @@ ln: ln.o
unlink: unlink.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS)
+cut: cut.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cut.o $(SHOBJ_LIBS)
+
+realpath: realpath.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS)
# pushd is a special case. We use the same source that the builtin version
# uses, with special compilation options.
@@ -214,3 +219,4 @@ uname.o: uname.c
sync.o: sync.c
push.o: push.c
mkdir.o: mkdir.c
+realpath.o: realpath.c
diff --git a/examples/loadables/cut.c b/examples/loadables/cut.c
new file mode 100644
index 00000000..d874034a
--- /dev/null
+++ b/examples/loadables/cut.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "bashansi.h"
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#if !defined (_POSIX2_LINE_MAX)
+# define _POSIX2_LINE_MAX 2048
+#endif
+
+static int cflag;
+static char dchar;
+static int dflag;
+static int fflag;
+static int sflag;
+
+static int autostart, autostop, maxval;
+static char positions[_POSIX2_LINE_MAX + 1];
+
+static int c_cut __P((FILE *, char *));
+static int f_cut __P((FILE *, char *));
+static int get_list __P((char *));
+static char *_cut_strsep __P((char **, const char *));
+
+int
+cut_builtin(list)
+ WORD_LIST *list;
+{
+ FILE *fp;
+ int (*fcn) __P((FILE *, char *)) = NULL;
+ int ch;
+
+ fcn = NULL;
+ dchar = '\t'; /* default delimiter is \t */
+
+ /* Since we don't support multi-byte characters, the -c and -b
+ options are equivalent, and the -n option is meaningless. */
+ reset_internal_getopt ();
+ while ((ch = internal_getopt (list, "b:c:d:f:sn")) != -1)
+ switch(ch) {
+ case 'b':
+ case 'c':
+ fcn = c_cut;
+ if (get_list(list_optarg) < 0)
+ return (EXECUTION_FAILURE);
+ cflag = 1;
+ break;
+ case 'd':
+ dchar = *list_optarg;
+ dflag = 1;
+ break;
+ case 'f':
+ fcn = f_cut;
+ if (get_list(list_optarg) < 0)
+ return (EXECUTION_FAILURE);
+ fflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'n':
+ break;
+ case '?':
+ default:
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ list = loptend;
+
+ if (fflag) {
+ if (cflag) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+ } else if (!cflag || dflag || sflag) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ if (list) {
+ while (list) {
+ fp = fopen(list->word->word, "r");
+ if (fp == 0) {
+ builtin_error("%s", list->word->word);
+ return (EXECUTION_FAILURE);
+ }
+ ch = (*fcn)(fp, list->word->word);
+ (void)fclose(fp);
+ if (ch < 0)
+ return (EXECUTION_FAILURE);
+ list = list->next;
+ }
+ } else {
+ ch = (*fcn)(stdin, "stdin");
+ if (ch < 0)
+ return (EXECUTION_FAILURE);
+ }
+
+ return (EXECUTION_SUCCESS);
+}
+
+static int
+get_list(list)
+ char *list;
+{
+ int setautostart, start, stop;
+ char *pos;
+ char *p;
+
+ /*
+ * set a byte in the positions array to indicate if a field or
+ * column is to be selected; use +1, it's 1-based, not 0-based.
+ * This parser is less restrictive than the Draft 9 POSIX spec.
+ * POSIX doesn't allow lists that aren't in increasing order or
+ * overlapping lists. We also handle "-3-5" although there's no
+ * real reason too.
+ */
+ for (; (p = _cut_strsep(&list, ", \t")) != NULL;) {
+ setautostart = start = stop = 0;
+ if (*p == '-') {
+ ++p;
+ setautostart = 1;
+ }
+ if (isdigit((unsigned char)*p)) {
+ start = stop = strtol(p, &p, 10);
+ if (setautostart && start > autostart)
+ autostart = start;
+ }
+ if (*p == '-') {
+ if (isdigit((unsigned char)p[1]))
+ stop = strtol(p + 1, &p, 10);
+ if (*p == '-') {
+ ++p;
+ if (!autostop || autostop > stop)
+ autostop = stop;
+ }
+ }
+ if (*p) {
+ builtin_error("[-cf] list: illegal list value");
+ return -1;
+ }
+ if (!stop || !start) {
+ builtin_error("[-cf] list: values may not include zero");
+ return -1;
+ }
+ if (stop > _POSIX2_LINE_MAX) {
+ builtin_error("[-cf] list: %d too large (max %d)",
+ stop, _POSIX2_LINE_MAX);
+ return -1;
+ }
+ if (maxval < stop)
+ maxval = stop;
+ for (pos = positions + start; start++ <= stop; *pos++ = 1);
+ }
+
+ /* overlapping ranges */
+ if (autostop && maxval > autostop)
+ maxval = autostop;
+
+ /* set autostart */
+ if (autostart)
+ memset(positions + 1, '1', autostart);
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+c_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ int ch, col;
+ char *pos;
+
+ ch = 0;
+ for (;;) {
+ pos = positions + 1;
+ for (col = maxval; col; --col) {
+ if ((ch = getc(fp)) == EOF)
+ return;
+ if (ch == '\n')
+ break;
+ if (*pos++)
+ (void)putchar(ch);
+ }
+ if (ch != '\n') {
+ if (autostop)
+ while ((ch = getc(fp)) != EOF && ch != '\n')
+ (void)putchar(ch);
+ else
+ while ((ch = getc(fp)) != EOF && ch != '\n');
+ }
+ (void)putchar('\n');
+ }
+ return (0);
+}
+
+static int
+f_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ int ch, field, isdelim;
+ char *pos, *p, sep;
+ int output;
+ char lbuf[_POSIX2_LINE_MAX + 1];
+
+ for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) {
+ output = 0;
+ for (isdelim = 0, p = lbuf;; ++p) {
+ if (!(ch = *p)) {
+ builtin_error("%s: line too long.", fname);
+ return -1;
+ }
+ /* this should work if newline is delimiter */
+ if (ch == sep)
+ isdelim = 1;
+ if (ch == '\n') {
+ if (!isdelim && !sflag)
+ (void)printf("%s", lbuf);
+ break;
+ }
+ }
+ if (!isdelim)
+ continue;
+
+ pos = positions + 1;
+ for (field = maxval, p = lbuf; field; --field, ++pos) {
+ if (*pos) {
+ if (output++)
+ (void)putchar(sep);
+ while ((ch = *p++) != '\n' && ch != sep)
+ (void)putchar(ch);
+ } else {
+ while ((ch = *p++) != '\n' && ch != sep)
+ continue;
+ }
+ if (ch == '\n')
+ break;
+ }
+ if (ch != '\n') {
+ if (autostop) {
+ if (output)
+ (void)putchar(sep);
+ for (; (ch = *p) != '\n'; ++p)
+ (void)putchar(ch);
+ } else
+ for (; (ch = *p) != '\n'; ++p);
+ }
+ (void)putchar('\n');
+ }
+ return (0);
+}
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+static char *
+_cut_strsep(stringp, delim)
+ register char **stringp;
+ register const char *delim;
+{
+ register char *s;
+ register const char *spanp;
+ register int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+static char *cut_doc[] = {
+ "Select portions of each line (as specified by LIST) from each FILE",
+ "(by default, the standard input), and write them to the standard output.",
+ "Items specified by LIST are either column positions or fields delimited",
+ "by a special character. Column numbering starts at 1.",
+ (char *)0
+};
+
+struct builtin cut_struct = {
+ "cut",
+ cut_builtin,
+ BUILTIN_ENABLED,
+ cut_doc,
+ "cut -b list [-n] [file ...] OR cut -c list [file ...] OR cut -f list [-s] [-d delim] [file ...]",
+ 0
+};
diff --git a/examples/loadables/finfo.c b/examples/loadables/finfo.c
index ab3c9a41..5fdb6d43 100644
--- a/examples/loadables/finfo.c
+++ b/examples/loadables/finfo.c
@@ -9,6 +9,7 @@
#include <grp.h>
#include <errno.h>
+#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "common.h"
@@ -17,7 +18,6 @@
extern int errno;
#endif
-extern char *strrchr();
extern char **make_builtin_argv ();
static int printst();
@@ -137,7 +137,11 @@ char *f;
fd = lfd;
r = fstat(fd, &st);
} else
+#ifdef HAVE_LSTAT
+ r = lstat(f, &st);
+#else
r = stat(f, &st);
+#endif
if (r < 0) {
builtin_error("%s: cannot stat: %s", f, strerror(errno));
return ((struct stat *)0);
diff --git a/examples/loadables/getconf.c b/examples/loadables/getconf.c
index 64407cc9..cb8344ca 100644
--- a/examples/loadables/getconf.c
+++ b/examples/loadables/getconf.c
@@ -55,7 +55,7 @@
struct conf_variable
{
const char *name;
- enum { SYSCONF, CONFSTR, PATHCONF, CONSTANT } type;
+ enum { SYSCONF, CONFSTR, PATHCONF, CONSTANT, G_UNDEF } type;
long value;
};
@@ -105,19 +105,55 @@ static const struct conf_variable conf_table[] =
#endif /* _CS_XBS5_ILP32_OFF32_CFLAGS */
/* POSIX.2 Utility Limit Minimum Values */
+#ifdef _POSIX2_BC_BASE_MAX
{ "POSIX2_BC_BASE_MAX", CONSTANT, _POSIX2_BC_BASE_MAX },
+#else
+ { "POSIX2_BC_BASE_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_BC_DIM_MAX
{ "POSIX2_BC_DIM_MAX", CONSTANT, _POSIX2_BC_DIM_MAX },
+#else
+ { "POSIX2_BC_DIM_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_BC_SCALE_MAX
{ "POSIX2_BC_SCALE_MAX", CONSTANT, _POSIX2_BC_SCALE_MAX },
+#else
+ { "POSIX2_BC_SCALE_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_BC_STRING_MAX
{ "POSIX2_BC_STRING_MAX", CONSTANT, _POSIX2_BC_STRING_MAX },
+#else
+ { "POSIX2_BC_STRING_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_COLL_WEIGHTS_MAX
{ "POSIX2_COLL_WEIGHTS_MAX", CONSTANT, _POSIX2_COLL_WEIGHTS_MAX },
+#else
+ { "POSIX2_COLL_WEIGHTS_MAX", G_UNDEF, -1 },
+#endif
#if defined (_POSIX2_EQUIV_CLASS_MAX)
{ "POSIX2_EQUIV_CLASS_MAX", CONSTANT, _POSIX2_EQUIV_CLASS_MAX },
#endif
+#ifdef _POSIX2_EXPR_NEST_MAX
{ "POSIX2_EXPR_NEST_MAX", CONSTANT, _POSIX2_EXPR_NEST_MAX },
+#else
+ { "POSIX2_EXPR_NEST_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_LINE_MAX
{ "POSIX2_LINE_MAX", CONSTANT, _POSIX2_LINE_MAX },
+#else
+ { "POSIX2_LINE_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_RE_DUP_MAX
{ "POSIX2_RE_DUP_MAX", CONSTANT, _POSIX2_RE_DUP_MAX },
+#else
+ { "POSIX2_RE_DUP_MAX", G_UNDEF, -1 },
+#endif
#if defined (_POSIX2_VERSION)
{ "POSIX2_VERSION", CONSTANT, _POSIX2_VERSION },
+#else
+# if !defined (_SC_2_VERSION)
+ { "POSIX2_VERSION", G_UNDEF, -1 },
+# endif
#endif
/* POSIX.1 Minimum Values */
@@ -146,20 +182,50 @@ static const struct conf_variable conf_table[] =
{ "RE_DUP_MAX", SYSCONF, _SC_RE_DUP_MAX },
/* POSIX.2 Optional Facility Configuration Values */
+#ifdef _SC_2_C_BIND
{ "POSIX2_C_BIND", SYSCONF, _SC_2_C_BIND },
+#else
+ { "POSIX2_C_BIND", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_C_DEV
{ "POSIX2_C_DEV", SYSCONF, _SC_2_C_DEV },
+#else
+ { "POSIX2_C_DEV", G_UNDEF, -1 },
+#endif
#if defined (_SC_2_C_VERSION)
{ "POSIX2_C_VERSION", SYSCONF, _SC_2_C_VERSION },
+#else
+ { "POSIX2_C_VERSION", G_UNDEF, -1 },
#endif
#if defined (_SC_2_CHAR_TERM)
{ "POSIX2_CHAR_TERM", SYSCONF, _SC_2_CHAR_TERM },
+#else
+ { "POSIX2_CHAR_TERM", G_UNDEF, -1 },
#endif
+#ifdef _SC_2_FORT_DEV
{ "POSIX2_FORT_DEV", SYSCONF, _SC_2_FORT_DEV },
+#else
+ { "POSIX2_FORT_DEV", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_FORT_RUN
{ "POSIX2_FORT_RUN", SYSCONF, _SC_2_FORT_RUN },
+#else
+ { "POSIX2_FORT_RUN", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_LOCALEDEF
{ "POSIX2_LOCALEDEF", SYSCONF, _SC_2_LOCALEDEF },
+#else
+ { "POSIX2_LOCALEDEF", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_SW_DEV
{ "POSIX2_SW_DEV", SYSCONF, _SC_2_SW_DEV },
+#else
+ { "POSIX2_SW_DEV", G_UNDEF, -1 },
+#endif
#if defined (_SC2_UPE)
{ "POSIX2_UPE", SYSCONF, _SC_2_UPE },
+#else
+ { "POSIX2_UPE", G_UNDEF, -1 },
#endif
#if !defined (_POSIX2_VERSION) && defined (_SC_2_VERSION)
{ "POSIX2_VERSION" SYSCONF, _SC_2_VERSION },
@@ -449,6 +515,10 @@ int all;
size_t slen;
switch (cp->type) {
+ case G_UNDEF:
+ printf("undefined\n");
+ break;
+
case CONSTANT:
printf("%ld\n", cp->value);
break;
@@ -508,7 +578,8 @@ int all;
break;
}
- return (ferror(stdout) ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+ return ((ferror(stdout) || cp->type == G_UNDEF) ? EXECUTION_FAILURE
+ : EXECUTION_SUCCESS);
}
static int
diff --git a/examples/loadables/push.c b/examples/loadables/push.c
new file mode 100644
index 00000000..497ecd0e
--- /dev/null
+++ b/examples/loadables/push.c
@@ -0,0 +1,95 @@
+/*
+ * push - anyone remember TOPS-20?
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "jobs.h"
+#include "bashgetopt.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern int dollar_dollar_pid;
+extern int last_command_exit_value;
+
+int
+push_builtin (list)
+ WORD_LIST *list;
+{
+ pid_t pid;
+ int xstatus, opt;
+
+ xstatus = EXECUTION_SUCCESS;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "")) != -1)
+ {
+ switch (opt)
+ {
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ pid = make_child (savestring ("push"), 0);
+ if (pid == -1)
+ {
+ builtin_error ("cannot fork: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ else if (pid == 0)
+ {
+ /* Shell variable adjustments: $SHLVL, $$, $PPID, $! */
+ adjust_shell_level (1);
+ dollar_dollar_pid = getpid ();
+ set_ppid ();
+
+ /* Clean up job control stuff. */
+ stop_making_children ();
+ cleanup_the_pipeline ();
+ delete_all_jobs (0);
+
+ last_asynchronous_pid = NO_PID;
+
+ /* Make sure the job control code has the right values for
+ the shell's process group and tty process group, and that
+ the signals are set correctly for job control. */
+ initialize_job_control (0);
+ initialize_job_signals ();
+
+ /* And read commands until exit. */
+ reader_loop ();
+ exit_shell (last_command_exit_value);
+ }
+ else
+ {
+ stop_pipeline (0, (COMMAND *)NULL);
+ xstatus = wait_for (pid);
+ return (xstatus);
+ }
+}
+
+char *push_doc[] = {
+ "Create a child that is an exact duplicate of the running shell",
+ "and wait for it to exit. The $SHLVL, $!, $$, and $PPID variables",
+ "are adjusted in the child. The return value is the exit status",
+ "of the child.",
+ (char *)NULL
+};
+
+struct builtin push_struct = {
+ "push",
+ push_builtin,
+ BUILTIN_ENABLED,
+ push_doc,
+ "push",
+ 0
+};
diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c
new file mode 100644
index 00000000..16478b79
--- /dev/null
+++ b/examples/loadables/realpath.c
@@ -0,0 +1,119 @@
+/*
+ * realpath -- canonicalize pathnames, resolving symlinks
+ *
+ * usage: realpath [-csv] pathname [pathname...]
+ *
+ * options: -c check whether or not each resolved path exists
+ * -s no output, exit status determines whether path is valid
+ * -v produce verbose output
+ *
+ *
+ * exit status: 0 if all pathnames resolved
+ * 1 if any of the pathname arguments could not be resolved
+ *
+ *
+ * Bash loadable builtin version
+ *
+ * Chet Ramey
+ * chet@po.cwru.edu
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include "bashansi.h"
+#include <maxpath.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern char *sh_realpath();
+
+realpath_builtin(list)
+WORD_LIST *list;
+{
+ int opt, cflag, vflag, sflag, es;
+ char *r, realbuf[PATH_MAX], *p;
+ struct stat sb;
+
+ if (list == 0) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ vflag = cflag = sflag = 0;
+ reset_internal_getopt();
+ while ((opt = internal_getopt (list, "csv")) != -1) {
+ switch (opt) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ list = loptend;
+
+ if (list == 0)
+ usage();
+
+ for (es = EXECUTION_SUCCESS; list; list = list->next) {
+ p = list->word->word;
+ r = sh_realpath(p, realbuf);
+ if (r == 0) {
+ es = EXECUTION_FAILURE;
+ if (sflag == 0)
+ builtin_error("%s: cannot resolve: %s", p, strerror(errno));
+ continue;
+ }
+ if (cflag && (stat(realbuf, &sb) < 0)) {
+ es = EXECUTION_FAILURE;
+ if (sflag == 0)
+ builtin_error("%s: %s", p, strerror(errno));
+ continue;
+ }
+ if (sflag == 0) {
+ if (vflag)
+ printf ("%s -> ", p);
+ printf("%s\n", realbuf);
+ }
+ }
+ return es;
+}
+
+char *realpath_doc[] = {
+ "Display the canonicalized version of each PATHNAME argument, resolving",
+ "symbolic links. The -c option checks whether or not each resolved name",
+ "exists. The -s option produces no output; the exit status determines the",
+ "valididty of each PATHNAME. The -v option produces verbose output. The",
+ "exit status is 0 if each PATHNAME was resolved; non-zero otherwise.",
+ (char *)NULL
+};
+
+struct builtin realpath_struct = {
+ "realpath", /* builtin name */
+ realpath_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ realpath_doc, /* array of long documentation strings */
+ "realpath [-csv] pathname [pathname...]", /* usage synopsis */
+ 0 /* reserved for internal use */
+};