aboutsummaryrefslogtreecommitdiffstats
path: root/examples/loadables/ln.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/loadables/ln.c')
-rw-r--r--examples/loadables/ln.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c
new file mode 100644
index 0000000..ec73636
--- /dev/null
+++ b/examples/loadables/ln.c
@@ -0,0 +1,226 @@
+/* ln - make links */
+
+/* See Makefile for compilation details. */
+
+/*
+ Copyright (C) 1999-2009 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash.
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include "bashtypes.h"
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "posixstat.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+#include "common.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+typedef int unix_link_syscall_t __P((const char *, const char *));
+
+#define LN_SYMLINK 0x01
+#define LN_UNLINK 0x02
+
+static unix_link_syscall_t *linkfn;
+static int dolink ();
+
+ln_builtin (list)
+ WORD_LIST *list;
+{
+ int rval, opt, flags;
+ WORD_LIST *l;
+ char *sdir;
+ struct stat sb;
+
+ flags = 0;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "fs")) != -1)
+ {
+ switch (opt)
+ {
+ case 'f':
+ flags |= LN_UNLINK;
+ break;
+ case 's':
+ flags |= LN_SYMLINK;
+ break;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ if (list == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ linkfn = (flags & LN_SYMLINK) ? symlink : link;
+
+ if (list->next == 0) /* ln target, equivalent to ln target . */
+ return (dolink (list->word->word, ".", flags));
+
+ if (list->next->next == 0) /* ln target source */
+ return (dolink (list->word->word, list->next->word->word, flags));
+
+ /* ln target1 target2 ... directory */
+
+ /* find last argument: target directory, and make sure it's an existing
+ directory. */
+ for (l = list; l->next; l = l->next)
+ ;
+ sdir = l->word->word;
+
+ if (stat(sdir, &sb) < 0)
+ {
+ builtin_error ("%s", sdir);
+ return (EXECUTION_FAILURE);
+ }
+
+ if (S_ISDIR (sb.st_mode) == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ for (rval = EXECUTION_SUCCESS; list != l; list = list->next)
+ rval += dolink (list->word->word, sdir, flags);
+
+ return rval;
+}
+
+static char *
+mkdirpath (dir, file)
+ char *dir, *file;
+{
+ int dlen, flen;
+ char *ret;
+
+ dlen = strlen (dir);
+ flen = strlen (file);
+
+ ret = xmalloc (2 + dlen + flen);
+
+ strcpy (ret, dir);
+ if (ret[dlen - 1] != '/')
+ ret[dlen++] = '/';
+ strcpy (ret + dlen, file);
+ return ret;
+}
+
+#if defined (HAVE_LSTAT)
+# define LSTAT lstat
+#else
+# define LSTAT stat
+#endif
+
+static int
+dolink (src, dst, flags)
+ char *src, *dst;
+ int flags;
+{
+ struct stat ssb, dsb;
+ int exists;
+ char *dst_path, *p;
+
+ /* If we're not doing symlinks, the source must exist and not be a
+ directory. */
+ if ((flags & LN_SYMLINK) == 0)
+ {
+ if (stat (src, &ssb) != 0)
+ {
+ builtin_error ("%s: %s", src, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ if (S_ISDIR (ssb.st_mode))
+ {
+ errno = EISDIR;
+ builtin_error ("%s: %s", src, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ }
+
+ /* If the destination is a directory, create the final filename by appending
+ the basename of the source to the destination. */
+ dst_path = 0;
+ if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
+ {
+ if ((p = strrchr (src, '/')) == 0)
+ p = src;
+ else
+ p++;
+
+ dst_path = mkdirpath (dst, p);
+ dst = dst_path;
+ }
+
+ exists = LSTAT (dst, &dsb) == 0;
+
+ /* If -f was specified, and the destination exists, unlink it. */
+ if ((flags & LN_UNLINK) && exists && unlink (dst) != 0)
+ {
+ builtin_error ("%s: cannot unlink: %s", dst, strerror (errno));
+ FREE (dst_path);
+ return (EXECUTION_FAILURE);
+ }
+
+ /* Perform the link. */
+ if ((*linkfn) (src, dst) != 0)
+ {
+ builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno));
+ FREE (dst_path);
+ return (EXECUTION_FAILURE);
+ }
+
+ FREE (dst_path);
+ return (EXECUTION_SUCCESS);
+}
+
+char *ln_doc[] = {
+ "Link files.",
+ "",
+ "Create a new directory entry with the same modes as the original",
+ "file. The -f option means to unlink any existing file, permitting",
+ "the link to occur. The -s option means to create a symbolic link.",
+ "By default, ln makes hard links.",
+ (char *)NULL
+};
+
+/* The standard structure describing a builtin command. bash keeps an array
+ of these structures. */
+struct builtin ln_struct = {
+ "ln", /* builtin name */
+ ln_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ ln_doc, /* array of long documentation strings. */
+ "ln [-fs] file1 [file2] OR ln [-fs] file ... directory", /* usage synopsis; becomes short_doc */
+ 0 /* reserved for internal use */
+};