]> git.ipfire.org Git - thirdparty/bash.git/blame - examples/loadables/ln.c
Imported from ../bash-3.2.48.tar.gz.
[thirdparty/bash.git] / examples / loadables / ln.c
CommitLineData
b72432fd
JA
1/* ln - make links */
2
3/* See Makefile for compilation details. */
4
5#include "config.h"
6
7#include "bashtypes.h"
8
9#if defined (HAVE_UNISTD_H)
10# include <unistd.h>
11#endif
12
13#include "posixstat.h"
14
15#include <stdio.h>
16#include <errno.h>
17
18#include "builtins.h"
19#include "shell.h"
20#include "bashgetopt.h"
21
22#if !defined (errno)
23extern int errno;
24#endif
25
f73dda09
JA
26typedef int unix_link_syscall_t __P((const char *, const char *));
27
b72432fd
JA
28#define LN_SYMLINK 0x01
29#define LN_UNLINK 0x02
30
f73dda09 31static unix_link_syscall_t *linkfn;
b72432fd
JA
32static int dolink ();
33
34ln_builtin (list)
35 WORD_LIST *list;
36{
37 int rval, opt, flags;
38 WORD_LIST *l;
39 char *sdir;
40 struct stat sb;
41
42 flags = 0;
43 reset_internal_getopt ();
44 while ((opt = internal_getopt (list, "fs")) != -1)
45 {
46 switch (opt)
47 {
48 case 'f':
49 flags |= LN_UNLINK;
50 break;
51 case 's':
52 flags |= LN_SYMLINK;
53 break;
54 default:
55 builtin_usage ();
56 return (EX_USAGE);
57 }
58 }
59 list = loptend;
60
61 if (list == 0)
62 {
63 builtin_usage ();
64 return (EX_USAGE);
65 }
66
67 linkfn = (flags & LN_SYMLINK) ? symlink : link;
68
69 if (list->next == 0) /* ln target, equivalent to ln target . */
70 return (dolink (list->word->word, ".", flags));
71
72 if (list->next->next == 0) /* ln target source */
73 return (dolink (list->word->word, list->next->word->word, flags));
74
75 /* ln target1 target2 ... directory */
76
77 /* find last argument: target directory, and make sure it's an existing
78 directory. */
79 for (l = list; l->next; l = l->next)
80 ;
81 sdir = l->word->word;
82
83 if (stat(sdir, &sb) < 0)
84 {
85 builtin_error ("%s", sdir);
86 return (EXECUTION_FAILURE);
87 }
88
89 if (S_ISDIR (sb.st_mode) == 0)
90 {
91 builtin_usage ();
92 return (EX_USAGE);
93 }
94
95 for (rval = EXECUTION_SUCCESS; list != l; list = list->next)
96 rval += dolink (list->word->word, sdir, flags);
97
98 return rval;
99}
100
101static char *
102mkdirpath (dir, file)
103 char *dir, *file;
104{
105 int dlen, flen;
106 char *ret;
107
108 dlen = strlen (dir);
109 flen = strlen (file);
110
111 ret = xmalloc (2 + dlen + flen);
112
113 strcpy (ret, dir);
114 if (ret[dlen - 1] != '/')
115 ret[dlen++] = '/';
116 strcpy (ret + dlen, file);
117 return ret;
118}
119
120#if defined (HAVE_LSTAT)
121# define LSTAT lstat
122#else
123# define LSTAT stat
124#endif
125
126static int
127dolink (src, dst, flags)
128 char *src, *dst;
129 int flags;
130{
131 struct stat ssb, dsb;
132 int exists;
133 char *dst_path, *p;
134
135 /* If we're not doing symlinks, the source must exist and not be a
136 directory. */
137 if ((flags & LN_SYMLINK) == 0)
138 {
139 if (stat (src, &ssb) != 0)
140 {
141 builtin_error ("%s: %s", src, strerror (errno));
142 return (EXECUTION_FAILURE);
143 }
144 if (S_ISDIR (ssb.st_mode))
145 {
146 errno = EISDIR;
147 builtin_error ("%s: %s", src, strerror (errno));
148 return (EXECUTION_FAILURE);
149 }
150 }
151
152 /* If the destination is a directory, create the final filename by appending
153 the basename of the source to the destination. */
154 dst_path = 0;
155 if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
156 {
157 if ((p = strrchr (src, '/')) == 0)
158 p = src;
159 else
160 p++;
161
162 dst_path = mkdirpath (dst, p);
163 dst = dst_path;
164 }
165
166 exists = LSTAT (dst, &dsb) == 0;
167
168 /* If -f was specified, and the destination exists, unlink it. */
169 if ((flags & LN_UNLINK) && exists && unlink (dst) != 0)
170 {
171 builtin_error ("%s: cannot unlink: %s", dst, strerror (errno));
172 FREE (dst_path);
173 return (EXECUTION_FAILURE);
174 }
175
176 /* Perform the link. */
177 if ((*linkfn) (src, dst) != 0)
178 {
179 builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno));
180 FREE (dst_path);
181 return (EXECUTION_FAILURE);
182 }
183
184 FREE (dst_path);
185 return (EXECUTION_SUCCESS);
186}
187
188char *ln_doc[] = {
189 "Create a new directory entry with the same modes as the original",
190 "file. The -f option means to unlink any existing file, permitting",
191 "the link to occur. The -s option means to create a symbolic link.",
192 "By default, ln makes hard links.",
193 (char *)NULL
194};
195
196/* The standard structure describing a builtin command. bash keeps an array
197 of these structures. */
198struct builtin ln_struct = {
199 "ln", /* builtin name */
200 ln_builtin, /* function implementing the builtin */
201 BUILTIN_ENABLED, /* initial flags for builtin */
202 ln_doc, /* array of long documentation strings. */
203 "ln [-fs] file1 [file2] OR ln [-fs] file ... directory", /* usage synopsis; becomes short_doc */
204 0 /* reserved for internal use */
205};