]>
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> | |
17 | #include <string.h> | |
18 | #include <stdlib.h> | |
19 | #include <errno.h> | |
d200a926 | 20 | #include <getopt.h> |
5454df9c | 21 | #include <fcntl.h> |
5a2a8177 JM |
22 | #include <unistd.h> |
23 | #include <sys/types.h> | |
24 | #include <sys/stat.h> | |
87f3feac | 25 | |
eb63b9b8 | 26 | #include "nls.h" |
87f3feac | 27 | #include "xalloc.h" |
d200a926 | 28 | #include "c.h" |
c05a80ca | 29 | #include "closestream.h" |
eb63b9b8 | 30 | |
d6cf9e16 SK |
31 | #define RENAME_EXIT_SOMEOK 2 |
32 | #define RENAME_EXIT_NOTHING 4 | |
33 | #define RENAME_EXIT_UNEXPLAINED 64 | |
34 | ||
5651128a | 35 | static int string_replace(char *from, char *to, char *s, char *orig, char **newname) |
d200a926 | 36 | { |
5651128a SK |
37 | char *p, *q, *where; |
38 | ||
39 | where = strstr(s, from); | |
40 | if (where == NULL) | |
41 | return 1; | |
42 | p = orig; | |
43 | *newname = xmalloc(strlen(orig) + strlen(to) + 1); | |
44 | q = *newname; | |
eb63b9b8 KZ |
45 | while (p < where) |
46 | *q++ = *p++; | |
47 | p = to; | |
48 | while (*p) | |
49 | *q++ = *p++; | |
5651128a | 50 | p = where + strlen(from); |
eb63b9b8 KZ |
51 | while (*p) |
52 | *q++ = *p++; | |
22853e4a | 53 | *q = 0; |
5651128a SK |
54 | return 0; |
55 | } | |
eb63b9b8 | 56 | |
9a838c3c | 57 | static int do_symlink(char *from, char *to, char *s, int verbose, int noact, int nooverwrite) |
5651128a SK |
58 | { |
59 | char *newname = NULL, *target = NULL; | |
60 | int ret = 1; | |
61 | struct stat sb; | |
62 | ||
5454df9c M |
63 | if (faccessat(AT_FDCWD, s, F_OK, AT_SYMLINK_NOFOLLOW) != 0) { |
64 | warn(_("%s: not accessible"), s); | |
65 | return 2; | |
66 | } | |
67 | ||
5651128a | 68 | if (lstat(s, &sb) == -1) { |
fc14ceba | 69 | warn(_("stat of %s failed"), s); |
5651128a SK |
70 | return 2; |
71 | } | |
72 | if (!S_ISLNK(sb.st_mode)) { | |
73 | warnx(_("%s: not a symbolic link"), s); | |
74 | return 2; | |
75 | } | |
76 | target = xmalloc(sb.st_size + 1); | |
77 | if (readlink(s, target, sb.st_size + 1) < 0) { | |
78 | warn(_("%s: readlink failed"), s); | |
79 | free(target); | |
80 | return 2; | |
81 | } | |
82 | target[sb.st_size] = '\0'; | |
83 | if (string_replace(from, to, target, target, &newname)) | |
84 | ret = 0; | |
fabb9067 | 85 | |
5bb92700 | 86 | if (ret == 1 && nooverwrite && lstat(target, &sb) == 0) { |
fabb9067 | 87 | if (verbose) |
b98ab303 | 88 | printf(_("Skipping existing link: `%s' -> `%s'\n"), s, target); |
fabb9067 DG |
89 | |
90 | ret = 0; | |
91 | } | |
92 | ||
93 | if (ret == 1) { | |
94 | if (!noact && 0 > unlink(s)) { | |
95 | warn(_("%s: unlink failed"), s); | |
96 | ret = 2; | |
97 | } else if (!noact && symlink(newname, s) != 0) { | |
98 | warn(_("%s: symlinking to %s failed"), s, newname); | |
99 | ret = 2; | |
100 | } | |
5a2a8177 | 101 | } |
990bf1f0 | 102 | if (verbose && (noact || ret == 1)) |
fabb9067 DG |
103 | if (verbose) |
104 | printf("%s: `%s' -> `%s'\n", s, target, newname); | |
d200a926 | 105 | free(newname); |
23b4715b | 106 | free(target); |
d6cf9e16 | 107 | return ret; |
eb63b9b8 KZ |
108 | } |
109 | ||
9a838c3c | 110 | static int do_file(char *from, char *to, char *s, int verbose, int noact, int nooverwrite) |
5651128a | 111 | { |
9fa6088a | 112 | char *newname = NULL, *file=NULL; |
5651128a SK |
113 | int ret = 1; |
114 | ||
5454df9c M |
115 | if (access(s, F_OK) != 0) { |
116 | warn(_("%s: not accessible"), s); | |
117 | return 2; | |
118 | } | |
119 | ||
9fa6088a AH |
120 | if (strchr(from, '/') == NULL && strchr(to, '/') == NULL) |
121 | file = strrchr(s, '/'); | |
5651128a SK |
122 | if (file == NULL) |
123 | file = s; | |
124 | if (string_replace(from, to, file, s, &newname)) | |
125 | return 0; | |
5454df9c | 126 | |
378d58ab | 127 | if (nooverwrite && access(newname, F_OK) == 0) { |
6277e231 M |
128 | if (verbose) |
129 | printf(_("Skipping existing file: `%s'\n"), newname); | |
fabb9067 DG |
130 | ret = 0; |
131 | } | |
990bf1f0 | 132 | else if (!noact && rename(s, newname) != 0) { |
5651128a SK |
133 | warn(_("%s: rename to %s failed"), s, newname); |
134 | ret = 2; | |
135 | } | |
990bf1f0 | 136 | if (verbose && (noact || ret == 1)) |
5651128a SK |
137 | printf("`%s' -> `%s'\n", s, newname); |
138 | free(newname); | |
139 | return ret; | |
140 | } | |
141 | ||
6e1eda6f | 142 | static void __attribute__((__noreturn__)) usage(void) |
d200a926 | 143 | { |
6e1eda6f | 144 | FILE *out = stdout; |
540dfebe | 145 | fputs(USAGE_HEADER, out); |
d200a926 | 146 | fprintf(out, |
09af3db4 | 147 | _(" %s [options] <expression> <replacement> <file>...\n"), |
d200a926 | 148 | program_invocation_short_name); |
451dbcfa BS |
149 | |
150 | fputs(USAGE_SEPARATOR, out); | |
151 | fputs(_("Rename files.\n"), out); | |
152 | ||
540dfebe | 153 | fputs(USAGE_OPTIONS, out); |
9a838c3c DG |
154 | fputs(_(" -v, --verbose explain what is being done\n"), out); |
155 | fputs(_(" -s, --symlink act on the target of symlinks\n"), out); | |
156 | fputs(_(" -n, --no-act do not make any changes\n"), out); | |
157 | fputs(_(" -o, --no-overwrite don't overwrite existing files\n"), out); | |
540dfebe | 158 | fputs(USAGE_SEPARATOR, out); |
f45f3ec3 RM |
159 | printf(USAGE_HELP_OPTIONS(21)); |
160 | printf(USAGE_MAN_TAIL("rename(1)")); | |
6e1eda6f | 161 | exit(EXIT_SUCCESS); |
d200a926 | 162 | } |
eb63b9b8 | 163 | |
d200a926 SK |
164 | int main(int argc, char **argv) |
165 | { | |
166 | char *from, *to; | |
9a838c3c DG |
167 | int i, c, ret = 0, verbose = 0, noact = 0, nooverwrite = 0; |
168 | int (*do_rename)(char *from, char *to, char *s, int verbose, int noact, int nooverwrite) = do_file; | |
d200a926 SK |
169 | |
170 | static const struct option longopts[] = { | |
171 | {"verbose", no_argument, NULL, 'v'}, | |
172 | {"version", no_argument, NULL, 'V'}, | |
173 | {"help", no_argument, NULL, 'h'}, | |
990bf1f0 | 174 | {"no-act", no_argument, NULL, 'n'}, |
9a838c3c | 175 | {"no-overwrite", no_argument, NULL, 'o'}, |
5a2a8177 | 176 | {"symlink", no_argument, NULL, 's'}, |
d200a926 SK |
177 | {NULL, 0, NULL, 0} |
178 | }; | |
eb63b9b8 KZ |
179 | |
180 | setlocale(LC_ALL, ""); | |
181 | bindtextdomain(PACKAGE, LOCALEDIR); | |
182 | textdomain(PACKAGE); | |
c05a80ca | 183 | atexit(close_stdout); |
eb63b9b8 | 184 | |
fabb9067 | 185 | while ((c = getopt_long(argc, argv, "vsVhno", longopts, NULL)) != -1) |
d200a926 | 186 | switch (c) { |
990bf1f0 AR |
187 | case 'n': |
188 | noact = 1; | |
0849ff36 | 189 | break; |
8c1ce08d SK |
190 | case 'v': |
191 | verbose = 1; | |
990bf1f0 | 192 | break; |
0849ff36 M |
193 | case 'o': |
194 | nooverwrite = 1; | |
195 | break; | |
5a2a8177 | 196 | case 's': |
5651128a | 197 | do_rename = do_symlink; |
5a2a8177 | 198 | break; |
d200a926 | 199 | case 'V': |
c717b032 | 200 | printf(UTIL_LINUX_VERSION); |
d200a926 SK |
201 | return EXIT_SUCCESS; |
202 | case 'h': | |
6e1eda6f | 203 | usage(); |
d200a926 | 204 | default: |
677ec86c | 205 | errtryhelp(EXIT_FAILURE); |
eb63b9b8 | 206 | } |
d200a926 SK |
207 | |
208 | argc -= optind; | |
209 | argv += optind; | |
eb63b9b8 KZ |
210 | |
211 | if (argc < 3) { | |
8c219bf4 | 212 | warnx(_("not enough arguments")); |
6e1eda6f | 213 | errtryhelp(EXIT_FAILURE); |
eb63b9b8 KZ |
214 | } |
215 | ||
d200a926 SK |
216 | from = argv[0]; |
217 | to = argv[1]; | |
218 | ||
9f430c8a SK |
219 | if (!strcmp(from, to)) |
220 | return RENAME_EXIT_NOTHING; | |
221 | ||
d200a926 | 222 | for (i = 2; i < argc; i++) |
9a838c3c | 223 | ret |= do_rename(from, to, argv[i], verbose, noact, nooverwrite); |
d6cf9e16 SK |
224 | |
225 | switch (ret) { | |
226 | case 0: | |
227 | return RENAME_EXIT_NOTHING; | |
228 | case 1: | |
229 | return EXIT_SUCCESS; | |
230 | case 2: | |
231 | return EXIT_FAILURE; | |
232 | case 3: | |
233 | return RENAME_EXIT_SOMEOK; | |
234 | default: | |
235 | return RENAME_EXIT_UNEXPLAINED; | |
236 | } | |
eb63b9b8 | 237 | } |