Imported from ../bash-2.05a.tar.gz.
[platform/upstream/bash.git] / examples / loadables / ln.c
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
26 typedef int unix_link_syscall_t __P((const char *, const char *));
27
28 #define LN_SYMLINK 0x01
29 #define LN_UNLINK  0x02
30
31 static unix_link_syscall_t *linkfn;
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 };