]> git.ipfire.org Git - thirdparty/bash.git/blob - examples/loadables/ln.c
874e9db886f8c717ee63bfeda3008f21eaad063c
[thirdparty/bash.git] / examples / loadables / ln.c
1 /* ln - make links */
2
3 /* See Makefile for compilation details. */
4
5 /*
6 Copyright (C) 1999-2020 Free Software Foundation, Inc.
7
8 This file is part of GNU Bash.
9 Bash is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Bash is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Bash. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #include "bashtypes.h"
26
27 #if defined (HAVE_UNISTD_H)
28 # include <unistd.h>
29 #endif
30
31 #include "posixstat.h"
32
33 #include <stdio.h>
34 #include <errno.h>
35
36 #include "builtins.h"
37 #include "shell.h"
38 #include "bashgetopt.h"
39 #include "common.h"
40
41 #if !defined (errno)
42 extern int errno;
43 #endif
44
45 typedef int unix_link_syscall_t PARAMS((const char *, const char *));
46
47 #define LN_SYMLINK 0x01
48 #define LN_UNLINK 0x02
49 #define LN_NOFOLLOW 0x04
50
51 static unix_link_syscall_t *linkfn;
52 static int dolink ();
53
54 int
55 ln_builtin (list)
56 WORD_LIST *list;
57 {
58 int rval, opt, flags;
59 WORD_LIST *l;
60 char *sdir;
61 struct stat sb;
62
63 flags = 0;
64 reset_internal_getopt ();
65 while ((opt = internal_getopt (list, "fs")) != -1)
66 {
67 switch (opt)
68 {
69 case 'f':
70 flags |= LN_UNLINK;
71 break;
72 case 's':
73 flags |= LN_SYMLINK;
74 break;
75 case 'h':
76 case 'n':
77 flags |= LN_NOFOLLOW;
78 break;
79 CASE_HELPOPT;
80 default:
81 builtin_usage ();
82 return (EX_USAGE);
83 }
84 }
85 list = loptend;
86
87 if (list == 0)
88 {
89 builtin_usage ();
90 return (EX_USAGE);
91 }
92
93 linkfn = (flags & LN_SYMLINK) ? symlink : link;
94
95 if (list->next == 0) /* ln target, equivalent to ln target . */
96 return (dolink (list->word->word, ".", flags));
97
98 if (list->next->next == 0) /* ln target source */
99 return (dolink (list->word->word, list->next->word->word, flags));
100
101 /* ln target1 target2 ... directory */
102
103 /* find last argument: target directory, and make sure it's an existing
104 directory. */
105 for (l = list; l->next; l = l->next)
106 ;
107 sdir = l->word->word;
108
109 if (stat(sdir, &sb) < 0)
110 {
111 builtin_error ("%s", sdir);
112 return (EXECUTION_FAILURE);
113 }
114
115 if (S_ISDIR (sb.st_mode) == 0)
116 {
117 builtin_usage ();
118 return (EX_USAGE);
119 }
120
121 for (rval = EXECUTION_SUCCESS; list != l; list = list->next)
122 rval += dolink (list->word->word, sdir, flags);
123
124 return rval;
125 }
126
127 static char *
128 mkdirpath (dir, file)
129 char *dir, *file;
130 {
131 int dlen, flen;
132 char *ret;
133
134 dlen = strlen (dir);
135 flen = strlen (file);
136
137 ret = xmalloc (2 + dlen + flen);
138
139 strcpy (ret, dir);
140 if (ret[dlen - 1] != '/')
141 ret[dlen++] = '/';
142 strcpy (ret + dlen, file);
143 return ret;
144 }
145
146 #if defined (HAVE_LSTAT)
147 # define LSTAT lstat
148 # define LSTAT_OR_STAT_IF(c, f, b) ((c) ? lstat((f), (b)) : stat((f), (b)))
149 #else
150 # define LSTAT stat
151 # define LSTAT_OR_STAT_IF(c, f, b) (stat((f), (b)))
152 #endif
153
154 static int
155 dolink (src, dst, flags)
156 char *src, *dst;
157 int flags;
158 {
159 struct stat ssb, dsb;
160 int exists;
161 char *dst_path, *p;
162
163 /* If we're not doing symlinks, the source must exist and not be a
164 directory. */
165 if ((flags & LN_SYMLINK) == 0)
166 {
167 if (stat (src, &ssb) != 0)
168 {
169 builtin_error ("%s: %s", src, strerror (errno));
170 return (EXECUTION_FAILURE);
171 }
172 if (S_ISDIR (ssb.st_mode))
173 {
174 errno = EISDIR;
175 builtin_error ("%s: %s", src, strerror (errno));
176 return (EXECUTION_FAILURE);
177 }
178 }
179
180 /* If the destination is a directory, create the final filename by appending
181 the basename of the source to the destination. */
182 dst_path = 0;
183 if ((LSTAT_OR_STAT_IF((flags & LN_NOFOLLOW), dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
184 {
185 if ((p = strrchr (src, '/')) == 0)
186 p = src;
187 else
188 p++;
189
190 dst_path = mkdirpath (dst, p);
191 dst = dst_path;
192 }
193
194 exists = LSTAT (dst, &dsb) == 0;
195
196 /* If -f was specified, and the destination exists, unlink it. */
197 if ((flags & LN_UNLINK) && exists && unlink (dst) != 0)
198 {
199 builtin_error ("%s: cannot unlink: %s", dst, strerror (errno));
200 FREE (dst_path);
201 return (EXECUTION_FAILURE);
202 }
203
204 /* Perform the link. */
205 if ((*linkfn) (src, dst) != 0)
206 {
207 builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno));
208 FREE (dst_path);
209 return (EXECUTION_FAILURE);
210 }
211
212 FREE (dst_path);
213 return (EXECUTION_SUCCESS);
214 }
215
216 char *ln_doc[] = {
217 "Link files.",
218 "",
219 "Create a new directory entry with the same modes as the original",
220 "file. The -f option means to unlink any existing file, permitting",
221 "the link to occur. The -s option means to create a symbolic link.",
222 "By default, ln makes hard links. Specifying -n or its synonym -h",
223 "causes ln to not resolve symlinks in the target file or directory.",
224 (char *)NULL
225 };
226
227 /* The standard structure describing a builtin command. bash keeps an array
228 of these structures. */
229 struct builtin ln_struct = {
230 "ln", /* builtin name */
231 ln_builtin, /* function implementing the builtin */
232 BUILTIN_ENABLED, /* initial flags for builtin */
233 ln_doc, /* array of long documentation strings. */
234 "ln [-fhns] file1 [file2] OR ln [-fhns] file ... directory", /* usage synopsis; becomes short_doc */
235 0 /* reserved for internal use */
236 };