Imported from ../bash-4.0-rc1.tar.gz.
[platform/upstream/bash.git] / examples / loadables / ln.c
1 /* ln - make links */
2
3 /* See Makefile for compilation details. */
4
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
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 __P((const char *, const char *));
46
47 #define LN_SYMLINK 0x01
48 #define LN_UNLINK  0x02
49
50 static unix_link_syscall_t *linkfn;
51 static int dolink ();
52
53 ln_builtin (list)
54      WORD_LIST *list;
55 {
56   int rval, opt, flags;
57   WORD_LIST *l;
58   char *sdir;
59   struct stat sb;
60
61   flags = 0;
62   reset_internal_getopt ();
63   while ((opt = internal_getopt (list, "fs")) != -1)
64     {
65       switch (opt)
66         {
67         case 'f':
68           flags |= LN_UNLINK;
69           break;
70         case 's':
71           flags |= LN_SYMLINK;
72           break;
73         default:
74           builtin_usage ();
75           return (EX_USAGE);
76         }
77     }
78   list = loptend;
79
80   if (list == 0)
81     {
82       builtin_usage ();
83       return (EX_USAGE);
84     }
85     
86   linkfn = (flags & LN_SYMLINK) ? symlink : link;  
87
88   if (list->next == 0)                  /* ln target, equivalent to ln target . */
89     return (dolink (list->word->word, ".", flags));
90
91   if (list->next->next == 0)            /* ln target source */
92     return (dolink (list->word->word, list->next->word->word, flags));
93
94   /* ln target1 target2 ... directory */
95
96   /* find last argument: target directory, and make sure it's an existing
97      directory. */
98   for (l = list; l->next; l = l->next)  
99     ;
100   sdir = l->word->word;
101
102   if (stat(sdir, &sb) < 0)
103     {
104       builtin_error ("%s", sdir);
105       return (EXECUTION_FAILURE);
106     }
107
108   if (S_ISDIR (sb.st_mode) == 0)
109     {
110       builtin_usage ();
111       return (EX_USAGE);
112     }
113
114   for (rval = EXECUTION_SUCCESS; list != l; list = list->next)
115     rval += dolink (list->word->word, sdir, flags);
116   
117   return rval;
118 }
119
120 static char *
121 mkdirpath (dir, file)
122      char *dir, *file;
123 {
124   int dlen, flen;
125   char *ret;
126
127   dlen = strlen (dir);
128   flen = strlen (file);
129
130   ret = xmalloc (2 + dlen + flen);
131
132   strcpy (ret, dir);
133   if (ret[dlen - 1] != '/')
134     ret[dlen++] = '/';
135   strcpy (ret + dlen, file);
136   return ret;
137 }
138
139 #if defined (HAVE_LSTAT)
140 #  define LSTAT lstat
141 #else
142 #  define LSTAT stat
143 #endif
144
145 static int
146 dolink (src, dst, flags)
147      char *src, *dst;
148      int flags;
149 {
150   struct stat ssb, dsb;
151   int exists;
152   char *dst_path, *p;
153
154   /* If we're not doing symlinks, the source must exist and not be a 
155      directory. */
156   if ((flags & LN_SYMLINK) == 0)
157     {
158       if (stat (src, &ssb) != 0)
159         {
160           builtin_error ("%s: %s", src, strerror (errno));
161           return (EXECUTION_FAILURE);
162         }
163       if (S_ISDIR (ssb.st_mode))
164         {
165           errno = EISDIR;
166           builtin_error ("%s: %s", src, strerror (errno));
167           return (EXECUTION_FAILURE);
168         }
169     }
170
171   /* If the destination is a directory, create the final filename by appending
172      the basename of the source to the destination. */
173   dst_path = 0;
174   if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
175     {
176       if ((p = strrchr (src, '/')) == 0)
177         p = src;
178       else
179         p++;
180
181       dst_path = mkdirpath (dst, p);
182       dst = dst_path;
183     }
184
185   exists = LSTAT (dst, &dsb) == 0;
186
187   /* If -f was specified, and the destination exists, unlink it. */
188   if ((flags & LN_UNLINK) && exists && unlink (dst) != 0)
189     {
190       builtin_error ("%s: cannot unlink: %s", dst, strerror (errno));
191       FREE (dst_path);
192       return (EXECUTION_FAILURE);
193     }
194
195   /* Perform the link. */
196   if ((*linkfn) (src, dst) != 0)
197     {
198       builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno));
199       FREE (dst_path);
200       return (EXECUTION_FAILURE);
201     }
202
203   FREE (dst_path);
204   return (EXECUTION_SUCCESS);
205 }
206
207 char *ln_doc[] = {
208         "Link files.",
209         "",
210         "Create a new directory entry with the same modes as the original",
211         "file.  The -f option means to unlink any existing file, permitting",
212         "the link to occur.  The -s option means to create a symbolic link.",
213         "By default, ln makes hard links.",
214         (char *)NULL
215 };
216
217 /* The standard structure describing a builtin command.  bash keeps an array
218    of these structures. */
219 struct builtin ln_struct = {
220         "ln",           /* builtin name */
221         ln_builtin,             /* function implementing the builtin */
222         BUILTIN_ENABLED,        /* initial flags for builtin */
223         ln_doc,         /* array of long documentation strings. */
224         "ln [-fs] file1 [file2] OR ln [-fs] file ... directory",        /* usage synopsis; becomes short_doc */
225         0                       /* reserved for internal use */
226 };