| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 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 |  | 
|  | 44 | static int makesymlink (const char *src, const char *dest); | 
|  | 45 | static int makesymlinks (const char *file); | 
|  | 46 | static void usage (void); | 
|  | 47 |  | 
|  | 48 | int | 
|  | 49 | main (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 |  | 
|  | 81 | static void | 
|  | 82 | usage (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 |  | 
|  | 89 | static int | 
|  | 90 | makesymlinks (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 |  | 
|  | 164 | static int | 
|  | 165 | makesymlink (const char *src, const char *dest) | 
|  | 166 | { | 
|  | 167 | struct stat stats; | 
|  | 168 | const char *error; | 
|  | 169 |  | 
|  | 170 | /* Destination must not be a directory. */ | 
|  | 171 | if (lstat (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 | } |