]>
Commit | Line | Data |
---|---|---|
eb63b9b8 KZ |
1 | /* |
2 | * rename.c - aeb 2000-01-01 | |
3 | * | |
4 | -------------------------------------------------------------- | |
5 | #!/bin/sh | |
6 | if [ $# -le 2 ]; then echo call: rename from to files; exit; fi | |
7 | FROM="$1" | |
8 | TO="$2" | |
9 | shift | |
10 | shift | |
11 | for i in $@; do N=`echo "$i" | sed "s/$FROM/$TO/g"`; mv "$i" "$N"; done | |
12 | -------------------------------------------------------------- | |
13 | * This shell script will do renames of files, but may fail | |
14 | * in cases involving special characters. Here a C version. | |
15 | */ | |
16 | #include <stdio.h> | |
0dba6b6f M |
17 | #ifdef HAVE_STDIO_EXT_H |
18 | # include <stdio_ext.h> | |
19 | #endif | |
d2523d3d PS |
20 | #ifndef HAVE___FPURGE |
21 | # ifdef HAVE_FPURGE | |
0dba6b6f M |
22 | # define HAVE___FPURGE 1 |
23 | # define __fpurge fpurge | |
d2523d3d | 24 | # endif |
0dba6b6f | 25 | #endif |
eb63b9b8 KZ |
26 | #include <string.h> |
27 | #include <stdlib.h> | |
28 | #include <errno.h> | |
d200a926 | 29 | #include <getopt.h> |
5454df9c | 30 | #include <fcntl.h> |
5a2a8177 | 31 | #include <unistd.h> |
f43bdeda | 32 | #include <termios.h> |
5a2a8177 JM |
33 | #include <sys/types.h> |
34 | #include <sys/stat.h> | |
87f3feac | 35 | |
eb63b9b8 | 36 | #include "nls.h" |
87f3feac | 37 | #include "xalloc.h" |
d200a926 | 38 | #include "c.h" |
c05a80ca | 39 | #include "closestream.h" |
eb4aea8a | 40 | #include "rpmatch.h" |
eb63b9b8 | 41 | |
d6cf9e16 SK |
42 | #define RENAME_EXIT_SOMEOK 2 |
43 | #define RENAME_EXIT_NOTHING 4 | |
44 | #define RENAME_EXIT_UNEXPLAINED 64 | |
45 | ||
f43bdeda M |
46 | static int tty_cbreak = 0; |
47 | ||
5651128a | 48 | static int string_replace(char *from, char *to, char *s, char *orig, char **newname) |
d200a926 | 49 | { |
5651128a SK |
50 | char *p, *q, *where; |
51 | ||
52 | where = strstr(s, from); | |
53 | if (where == NULL) | |
54 | return 1; | |
55 | p = orig; | |
56 | *newname = xmalloc(strlen(orig) + strlen(to) + 1); | |
57 | q = *newname; | |
eb63b9b8 KZ |
58 | while (p < where) |
59 | *q++ = *p++; | |
60 | p = to; | |
61 | while (*p) | |
62 | *q++ = *p++; | |
5651128a | 63 | p = where + strlen(from); |
eb63b9b8 KZ |
64 | while (*p) |
65 | *q++ = *p++; | |
22853e4a | 66 | *q = 0; |
5651128a SK |
67 | return 0; |
68 | } | |
eb63b9b8 | 69 | |
eb4aea8a M |
70 | static int ask(char *name) |
71 | { | |
72 | int c; | |
73 | char buf[2]; | |
74 | printf(_("%s: overwrite `%s'? "), program_invocation_short_name, name); | |
75 | fflush(stdout); | |
76 | if ((c = fgetc(stdin)) == EOF) { | |
0cb2c653 | 77 | buf[0] = 'n'; |
0cb2c653 | 78 | printf("n\n"); |
eb4aea8a M |
79 | } |
80 | else { | |
0cb2c653 | 81 | buf[0] = c; |
33785dd3 | 82 | if (c != '\n' && tty_cbreak) { |
0dba6b6f | 83 | #ifdef HAVE___FPURGE |
33785dd3 M |
84 | /* Possibly purge a multi-byte character; or do a |
85 | required purge of the rest of the line (including | |
86 | the newline) if the tty has been put back in | |
87 | canonical mode (for example by a shell after a | |
88 | SIGTSTP signal). */ | |
89 | __fpurge(stdin); | |
0dba6b6f | 90 | #endif |
f43bdeda | 91 | printf("\n"); |
33785dd3 | 92 | } |
f43bdeda | 93 | else if (c != '\n') |
eb4aea8a M |
94 | while ((c = fgetc(stdin)) != '\n' && c != EOF); |
95 | } | |
0cb2c653 | 96 | buf[1] = '\0'; |
eb4aea8a M |
97 | if (rpmatch(buf) == RPMATCH_YES) |
98 | return 0; | |
99 | else | |
100 | return 1; | |
101 | } | |
102 | ||
103 | static int do_symlink(char *from, char *to, char *s, int verbose, int noact, | |
104 | int nooverwrite, int interactive) | |
5651128a SK |
105 | { |
106 | char *newname = NULL, *target = NULL; | |
107 | int ret = 1; | |
108 | struct stat sb; | |
109 | ||
826538bf M |
110 | if ( faccessat(AT_FDCWD, s, F_OK, AT_SYMLINK_NOFOLLOW) != 0 && |
111 | errno != EINVAL ) | |
112 | /* Skip if AT_SYMLINK_NOFOLLOW is not supported; lstat() below will | |
113 | detect the access error */ | |
114 | { | |
5454df9c M |
115 | warn(_("%s: not accessible"), s); |
116 | return 2; | |
117 | } | |
118 | ||
5651128a | 119 | if (lstat(s, &sb) == -1) { |
fc14ceba | 120 | warn(_("stat of %s failed"), s); |
5651128a SK |
121 | return 2; |
122 | } | |
123 | if (!S_ISLNK(sb.st_mode)) { | |
124 | warnx(_("%s: not a symbolic link"), s); | |
125 | return 2; | |
126 | } | |
127 | target = xmalloc(sb.st_size + 1); | |
128 | if (readlink(s, target, sb.st_size + 1) < 0) { | |
129 | warn(_("%s: readlink failed"), s); | |
130 | free(target); | |
131 | return 2; | |
132 | } | |
133 | target[sb.st_size] = '\0'; | |
7ac394f8 | 134 | if (string_replace(from, to, target, target, &newname) != 0) |
5651128a | 135 | ret = 0; |
fabb9067 | 136 | |
eb4aea8a M |
137 | if (ret == 1 && (nooverwrite || interactive) && lstat(newname, &sb) != 0) |
138 | nooverwrite = interactive = 0; | |
139 | ||
140 | if ( ret == 1 && | |
141 | (nooverwrite || (interactive && (noact || ask(newname) != 0))) ) | |
142 | { | |
fabb9067 | 143 | if (verbose) |
b98ab303 | 144 | printf(_("Skipping existing link: `%s' -> `%s'\n"), s, target); |
fabb9067 DG |
145 | ret = 0; |
146 | } | |
147 | ||
148 | if (ret == 1) { | |
149 | if (!noact && 0 > unlink(s)) { | |
150 | warn(_("%s: unlink failed"), s); | |
151 | ret = 2; | |
7ac394f8 M |
152 | } |
153 | else if (!noact && symlink(newname, s) != 0) { | |
fabb9067 DG |
154 | warn(_("%s: symlinking to %s failed"), s, newname); |
155 | ret = 2; | |
156 | } | |
5a2a8177 | 157 | } |
990bf1f0 | 158 | if (verbose && (noact || ret == 1)) |
7ac394f8 | 159 | printf("%s: `%s' -> `%s'\n", s, target, newname); |
d200a926 | 160 | free(newname); |
23b4715b | 161 | free(target); |
d6cf9e16 | 162 | return ret; |
eb63b9b8 KZ |
163 | } |
164 | ||
eb4aea8a M |
165 | static int do_file(char *from, char *to, char *s, int verbose, int noact, |
166 | int nooverwrite, int interactive) | |
5651128a | 167 | { |
9fa6088a | 168 | char *newname = NULL, *file=NULL; |
5651128a SK |
169 | int ret = 1; |
170 | ||
5454df9c M |
171 | if (access(s, F_OK) != 0) { |
172 | warn(_("%s: not accessible"), s); | |
173 | return 2; | |
174 | } | |
175 | ||
9fa6088a AH |
176 | if (strchr(from, '/') == NULL && strchr(to, '/') == NULL) |
177 | file = strrchr(s, '/'); | |
5651128a SK |
178 | if (file == NULL) |
179 | file = s; | |
7ac394f8 | 180 | if (string_replace(from, to, file, s, &newname) != 0) |
5651128a | 181 | return 0; |
5454df9c | 182 | |
eb4aea8a M |
183 | if ((nooverwrite || interactive) && access(newname, F_OK) != 0) |
184 | nooverwrite = interactive = 0; | |
185 | ||
186 | if (nooverwrite || (interactive && (noact || ask(newname) != 0))) { | |
6277e231 M |
187 | if (verbose) |
188 | printf(_("Skipping existing file: `%s'\n"), newname); | |
fabb9067 DG |
189 | ret = 0; |
190 | } | |
990bf1f0 | 191 | else if (!noact && rename(s, newname) != 0) { |
5651128a SK |
192 | warn(_("%s: rename to %s failed"), s, newname); |
193 | ret = 2; | |
194 | } | |
990bf1f0 | 195 | if (verbose && (noact || ret == 1)) |
5651128a SK |
196 | printf("`%s' -> `%s'\n", s, newname); |
197 | free(newname); | |
198 | return ret; | |
199 | } | |
200 | ||
6e1eda6f | 201 | static void __attribute__((__noreturn__)) usage(void) |
d200a926 | 202 | { |
6e1eda6f | 203 | FILE *out = stdout; |
540dfebe | 204 | fputs(USAGE_HEADER, out); |
d200a926 | 205 | fprintf(out, |
09af3db4 | 206 | _(" %s [options] <expression> <replacement> <file>...\n"), |
d200a926 | 207 | program_invocation_short_name); |
451dbcfa BS |
208 | |
209 | fputs(USAGE_SEPARATOR, out); | |
210 | fputs(_("Rename files.\n"), out); | |
211 | ||
540dfebe | 212 | fputs(USAGE_OPTIONS, out); |
9a838c3c DG |
213 | fputs(_(" -v, --verbose explain what is being done\n"), out); |
214 | fputs(_(" -s, --symlink act on the target of symlinks\n"), out); | |
215 | fputs(_(" -n, --no-act do not make any changes\n"), out); | |
216 | fputs(_(" -o, --no-overwrite don't overwrite existing files\n"), out); | |
eb4aea8a | 217 | fputs(_(" -i, --interactive prompt before overwrite\n"), out); |
540dfebe | 218 | fputs(USAGE_SEPARATOR, out); |
f45f3ec3 RM |
219 | printf(USAGE_HELP_OPTIONS(21)); |
220 | printf(USAGE_MAN_TAIL("rename(1)")); | |
6e1eda6f | 221 | exit(EXIT_SUCCESS); |
d200a926 | 222 | } |
eb63b9b8 | 223 | |
d200a926 SK |
224 | int main(int argc, char **argv) |
225 | { | |
226 | char *from, *to; | |
eb4aea8a | 227 | int i, c, ret = 0, verbose = 0, noact = 0, nooverwrite = 0, interactive = 0; |
f43bdeda | 228 | struct termios tio; |
eb4aea8a M |
229 | int (*do_rename)(char *from, char *to, char *s, int verbose, int noact, |
230 | int nooverwrite, int interactive) = do_file; | |
d200a926 SK |
231 | |
232 | static const struct option longopts[] = { | |
233 | {"verbose", no_argument, NULL, 'v'}, | |
234 | {"version", no_argument, NULL, 'V'}, | |
235 | {"help", no_argument, NULL, 'h'}, | |
990bf1f0 | 236 | {"no-act", no_argument, NULL, 'n'}, |
9a838c3c | 237 | {"no-overwrite", no_argument, NULL, 'o'}, |
eb4aea8a | 238 | {"interactive", no_argument, NULL, 'i'}, |
5a2a8177 | 239 | {"symlink", no_argument, NULL, 's'}, |
d200a926 SK |
240 | {NULL, 0, NULL, 0} |
241 | }; | |
eb63b9b8 KZ |
242 | |
243 | setlocale(LC_ALL, ""); | |
244 | bindtextdomain(PACKAGE, LOCALEDIR); | |
245 | textdomain(PACKAGE); | |
c05a80ca | 246 | atexit(close_stdout); |
eb63b9b8 | 247 | |
eb4aea8a | 248 | while ((c = getopt_long(argc, argv, "vsVhnoi", longopts, NULL)) != -1) |
d200a926 | 249 | switch (c) { |
990bf1f0 AR |
250 | case 'n': |
251 | noact = 1; | |
0849ff36 | 252 | break; |
8c1ce08d SK |
253 | case 'v': |
254 | verbose = 1; | |
990bf1f0 | 255 | break; |
0849ff36 M |
256 | case 'o': |
257 | nooverwrite = 1; | |
eb4aea8a M |
258 | interactive = 0; |
259 | break; | |
260 | case 'i': | |
261 | interactive = 1; | |
262 | nooverwrite = 0; | |
0849ff36 | 263 | break; |
5a2a8177 | 264 | case 's': |
5651128a | 265 | do_rename = do_symlink; |
5a2a8177 | 266 | break; |
d200a926 | 267 | case 'V': |
c717b032 | 268 | printf(UTIL_LINUX_VERSION); |
d200a926 SK |
269 | return EXIT_SUCCESS; |
270 | case 'h': | |
6e1eda6f | 271 | usage(); |
d200a926 | 272 | default: |
677ec86c | 273 | errtryhelp(EXIT_FAILURE); |
eb63b9b8 | 274 | } |
d200a926 SK |
275 | |
276 | argc -= optind; | |
277 | argv += optind; | |
eb63b9b8 KZ |
278 | |
279 | if (argc < 3) { | |
8c219bf4 | 280 | warnx(_("not enough arguments")); |
6e1eda6f | 281 | errtryhelp(EXIT_FAILURE); |
eb63b9b8 KZ |
282 | } |
283 | ||
d200a926 SK |
284 | from = argv[0]; |
285 | to = argv[1]; | |
286 | ||
9f430c8a SK |
287 | if (!strcmp(from, to)) |
288 | return RENAME_EXIT_NOTHING; | |
289 | ||
f43bdeda M |
290 | tty_cbreak = 0; |
291 | if (interactive && isatty(STDIN_FILENO) != 0) { | |
292 | if (tcgetattr(STDIN_FILENO, &tio) != 0) | |
293 | warn(_("failed to get terminal attributes")); | |
294 | else if (!(tio.c_lflag & ICANON) && tio.c_cc[VMIN] == 1) | |
295 | tty_cbreak = 1; | |
296 | } | |
297 | ||
d200a926 | 298 | for (i = 2; i < argc; i++) |
eb4aea8a | 299 | ret |= do_rename(from, to, argv[i], verbose, noact, nooverwrite, interactive); |
d6cf9e16 SK |
300 | |
301 | switch (ret) { | |
302 | case 0: | |
303 | return RENAME_EXIT_NOTHING; | |
304 | case 1: | |
305 | return EXIT_SUCCESS; | |
306 | case 2: | |
307 | return EXIT_FAILURE; | |
308 | case 3: | |
309 | return RENAME_EXIT_SOMEOK; | |
310 | default: | |
311 | return RENAME_EXIT_UNEXPLAINED; | |
312 | } | |
eb63b9b8 | 313 | } |