1/* `sln' program to create symbolic links between files.
2 Copyright (C) 1998-2016 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include <error.h>
24#include <errno.h>
25#include <libintl.h>
26#include <locale.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <errno.h>
31#include <ctype.h>
32#include <stdio.h>
33#include <string.h>
34#include <limits.h>
35
36#include "../version.h"
37
38#define PACKAGE _libc_intl_domainname
39
40#if !defined S_ISDIR && defined S_IFDIR
41#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
42#endif
43
44static int makesymlink (const char *src, const char *dest);
45static int makesymlinks (const char *file);
46static void usage (void);
47
48int
49main (int argc, char **argv)
50{
51 /* Set locale via LC_ALL. */
52 setlocale (LC_ALL, "");
53
54 /* Set the text message domain. */
55 textdomain (PACKAGE);
56
57 switch (argc)
58 {
59 case 2:
60 if (strcmp (argv[1], "--version") == 0) {
61 printf ("sln %s%s\n", PKGVERSION, VERSION);
62 return 0;
63 } else if (strcmp (argv[1], "--help") == 0) {
64 usage ();
65 return 0;
66 }
67 return makesymlinks (argv [1]);
68 break;
69
70 case 3:
71 return makesymlink (argv [1], argv [2]);
72 break;
73
74 default:
75 usage ();
76 return 1;
77 break;
78 }
79}
80
81static void
82usage (void)
83{
84 printf (_("Usage: sln src dest|file\n\n"));
85 printf (_("For bug reporting instructions, please see:\n\
86%s.\n"), REPORT_BUGS_TO);
87}
88
89static int
90makesymlinks (const char *file)
91{
92#ifndef PATH_MAX
93#define PATH_MAX 4095
94#endif
95 char *buffer = NULL;
96 size_t bufferlen = 0;
97 int ret;
98 int lineno;
99 FILE *fp;
100
101 if (strcmp (file, "-") == 0)
102 fp = stdin;
103 else
104 {
105 fp = fopen (file, "r");
106 if (fp == NULL)
107 {
108 fprintf (stderr, _("%s: file open error: %m\n"), file);
109 return 1;
110 }
111 }
112
113 ret = 0;
114 lineno = 0;
115 while (!feof_unlocked (fp))
116 {
117 ssize_t n = getline (&buffer, &bufferlen, fp);
118 char *src;
119 char *dest;
120 char *cp = buffer;
121
122 if (n < 0)
123 break;
124 if (buffer[n - 1] == '\n')
125 buffer[n - 1] = '\0';
126
127 ++lineno;
128 while (isspace (*cp))
129 ++cp;
130 if (*cp == '\0')
131 /* Ignore empty lines. */
132 continue;
133 src = cp;
134
135 do
136 ++cp;
137 while (*cp != '\0' && ! isspace (*cp));
138 if (*cp != '\0')
139 *cp++ = '\0';
140
141 while (isspace (*cp))
142 ++cp;
143 if (*cp == '\0')
144 {
145 fprintf (stderr, _("No target in line %d\n"), lineno);
146 ret = 1;
147 continue;
148 }
149 dest = cp;
150
151 do
152 ++cp;
153 while (*cp != '\0' && ! isspace (*cp));
154 if (*cp != '\0')
155 *cp++ = '\0';
156
157 ret |= makesymlink (src, dest);
158 }
159 fclose (fp);
160
161 return ret;
162}
163
164static int
165makesymlink (const char *src, const char *dest)
166{
167 struct stat64 stats;
168 const char *error;
169
170 /* Destination must not be a directory. */
171 if (lstat64 (dest, &stats) == 0)
172 {
173 if (S_ISDIR (stats.st_mode))
174 {
175 fprintf (stderr, _("%s: destination must not be a directory\n"),
176 dest);
177 return 1;
178 }
179 else if (unlink (dest) && errno != ENOENT)
180 {
181 fprintf (stderr, _("%s: failed to remove the old destination\n"),
182 dest);
183 return 1;
184 }
185 }
186 else if (errno != ENOENT)
187 {
188 error = strerror (errno);
189 fprintf (stderr, _("%s: invalid destination: %s\n"), dest, error);
190 return -1;
191 }
192
193#ifdef S_ISLNK
194 if (symlink (src, dest) == 0)
195#else
196 if (link (src, dest) == 0)
197#endif
198 {
199 /* Destination must exist by now. */
200 if (access (dest, F_OK))
201 {
202 error = strerror (errno);
203 unlink (dest);
204 fprintf (stderr, _("Invalid link from \"%s\" to \"%s\": %s\n"),
205 src, dest, error);
206 return 1;
207 }
208 return 0;
209 }
210 else
211 {
212 error = strerror (errno);
213 fprintf (stderr, _("Invalid link from \"%s\" to \"%s\": %s\n"),
214 src, dest, error);
215 return 1;
216 }
217}
218