1 /* `sln' program to create symbolic links between files.
2    Copyright (C) 1998-2021 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    <https://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 
36 static int makesymlink (const char *src, const char *dest);
37 static int makesymlinks (const char *file);
38 static void usage (void);
39 
40 int
main(int argc,char ** argv)41 main (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 
73 static void
usage(void)74 usage (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 
81 static int
makesymlinks(const char * file)82 makesymlinks (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 
153 static int
makesymlink(const char * src,const char * dest)154 makesymlink (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