]> git.ipfire.org Git - thirdparty/bash.git/blame - examples/loadables/ln.c
bash-5.0 distribution sources and documentation
[thirdparty/bash.git] / examples / loadables / ln.c
CommitLineData
b72432fd
JA
1/* ln - make links */
2
3/* See Makefile for compilation details. */
4
3185942a
JA
5/*
6 Copyright (C) 1999-2009 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
b72432fd
JA
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"
3185942a 39#include "common.h"
b72432fd
JA
40
41#if !defined (errno)
42extern int errno;
43#endif
44
f73dda09
JA
45typedef int unix_link_syscall_t __P((const char *, const char *));
46
b72432fd
JA
47#define LN_SYMLINK 0x01
48#define LN_UNLINK 0x02
a0c0a00f 49#define LN_NOFOLLOW 0x04
b72432fd 50
f73dda09 51static unix_link_syscall_t *linkfn;
b72432fd
JA
52static int dolink ();
53
a0c0a00f 54int
b72432fd
JA
55ln_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;
a0c0a00f
CR
75 case 'h':
76 case 'n':
77 flags |= LN_NOFOLLOW;
78 break;
d233b485 79 CASE_HELPOPT;
b72432fd
JA
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
127static char *
128mkdirpath (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
a0c0a00f 148# define LSTAT_OR_STAT_IF(c, f, b) ((c) ? lstat((f), (b)) : stat((f), (b)))
b72432fd
JA
149#else
150# define LSTAT stat
a0c0a00f 151# define LSTAT_OR_STAT_IF(c, f, b) (stat((f), (b)))
b72432fd
JA
152#endif
153
154static int
155dolink (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;
a0c0a00f 183 if ((LSTAT_OR_STAT_IF((flags & LN_NOFOLLOW), dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
b72432fd
JA
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
216char *ln_doc[] = {
3185942a
JA
217 "Link files.",
218 "",
b72432fd
JA
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.",
a0c0a00f
CR
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.",
b72432fd
JA
224 (char *)NULL
225};
226
227/* The standard structure describing a builtin command. bash keeps an array
228 of these structures. */
229struct 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. */
a0c0a00f 234 "ln [-fhns] file1 [file2] OR ln [-fhns] file ... directory", /* usage synopsis; becomes short_doc */
b72432fd
JA
235 0 /* reserved for internal use */
236};