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