]>
Commit | Line | Data |
---|---|---|
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) | |
23 | extern int errno; | |
24 | #endif | |
25 | ||
f73dda09 JA |
26 | typedef 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 | 31 | static unix_link_syscall_t *linkfn; |
b72432fd JA |
32 | static int dolink (); |
33 | ||
34 | ln_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 | ||
101 | static char * | |
102 | mkdirpath (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 | ||
126 | static int | |
127 | dolink (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 | ||
188 | char *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. */ | |
198 | struct 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 | }; |