1/* `sln' program to create symbolic links between files.
2 Copyright (C) 1998-2019 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#include <error.h>
20#include <errno.h>
21#include <libintl.h>
22#include <locale.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include <errno.h>
27#include <ctype.h>
28#include <stdio.h>
29#include <string.h>
30#include <limits.h>
31
32#include "../version.h"
33
34#define PACKAGE _libc_intl_domainname
35
36static int makesymlink (const char *src, const char *dest);
37static int makesymlinks (const char *file);
38static void usage (void);
39
40int
41main (int argc, char **argv)
42{
43 /* Set locale via LC_ALL. */
44 setlocale (LC_ALL, "");
45
46 /* Set the text message domain. */
47 textdomain (PACKAGE);
48
49 switch (argc)
50 {
51 case 2:
52 if (strcmp (argv[1], "--version") == 0) {
53 printf ("sln %s%s\n", PKGVERSION, VERSION);
54 return 0;
55 } else if (strcmp (argv[1], "--help") == 0) {
56 usage ();
57 return 0;
58 }
59 return makesymlinks (argv [1]);
60 break;
61
62 case 3:
63 return makesymlink (argv [1], argv [2]);
64 break;
65
66 default:
67 usage ();
68 return 1;
69 break;
70 }
71}
72
73static void
74usage (void)
75{
76 printf (_("Usage: sln src dest|file\n\n"));
77 printf (_("For bug reporting instructions, please see:\n\
78%s.\n"), REPORT_BUGS_TO);
79}
80
81static int
82makesymlinks (const char *file)
83{
84 char *buffer = NULL;
85 size_t bufferlen = 0;
86 int ret;
87 int lineno;
88 FILE *fp;
89
90 if (strcmp (file, "-") == 0)
91 fp = stdin;
92 else
93 {
94 fp = fopen (file, "r");
95 if (fp == NULL)
96 {
97 fprintf (stderr, _("%s: file open error: %m\n"), file);
98 return 1;
99 }
100 }
101
102 ret = 0;
103 lineno = 0;
104 while (!feof_unlocked (fp))
105 {
106 ssize_t n = getline (&buffer, &bufferlen, fp);
107 char *src;
108 char *dest;
109 char *cp = buffer;
110
111 if (n < 0)
112 break;
113 if (buffer[n - 1] == '\n')
114 buffer[n - 1] = '\0';
115
116 ++lineno;
117 while (isspace (*cp))
118 ++cp;
119 if (*cp == '\0')
120 /* Ignore empty lines. */
121 continue;
122 src = cp;
123
124 do
125 ++cp;
126 while (*cp != '\0' && ! isspace (*cp));
127 if (*cp != '\0')
128 *cp++ = '\0';
129
130 while (isspace (*cp))
131 ++cp;
132 if (*cp == '\0')
133 {
134 fprintf (stderr, _("No target in line %d\n"), lineno);
135 ret = 1;
136 continue;
137 }
138 dest = cp;
139
140 do
141 ++cp;
142 while (*cp != '\0' && ! isspace (*cp));
143 if (*cp != '\0')
144 *cp++ = '\0';
145
146 ret |= makesymlink (src, dest);
147 }
148 fclose (fp);
149
150 return ret;
151}
152
153static int
154makesymlink (const char *src, const char *dest)
155{
156 struct stat64 stats;
157 const char *error;
158
159 /* Destination must not be a directory. */
160 if (lstat64 (dest, &stats) == 0)
161 {
162 if (S_ISDIR (stats.st_mode))
163 {
164 fprintf (stderr, _("%s: destination must not be a directory\n"),
165 dest);
166 return 1;
167 }
168 else if (unlink (dest) && errno != ENOENT)
169 {
170 fprintf (stderr, _("%s: failed to remove the old destination\n"),
171 dest);
172 return 1;
173 }
174 }
175 else if (errno != ENOENT)
176 {
177 error = strerror (errno);
178 fprintf (stderr, _("%s: invalid destination: %s\n"), dest, error);
179 return -1;
180 }
181
182 if (symlink (src, dest) == 0)
183 {
184 /* Destination must exist by now. */
185 if (access (dest, F_OK))
186 {
187 error = strerror (errno);
188 unlink (dest);
189 fprintf (stderr, _("Invalid link from \"%s\" to \"%s\": %s\n"),
190 src, dest, error);
191 return 1;
192 }
193 return 0;
194 }
195 else
196 {
197 error = strerror (errno);
198 fprintf (stderr, _("Invalid link from \"%s\" to \"%s\": %s\n"),
199 src, dest, error);
200 return 1;
201 }
202}
203