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)
23extern int errno;
24#endif
25
26typedef int unix_link_syscall_t __P((const char *, const char *));
27
28#define LN_SYMLINK 0x01
29#define LN_UNLINK  0x02
30
31static unix_link_syscall_t *linkfn;
32static int dolink ();
33
34ln_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
101static char *
102mkdirpath (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
126static int
127dolink (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
188char *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. */
198struct 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};
206