2 * rename.c - aeb 2000-01-01
4 --------------------------------------------------------------
6 if [ $# -le 2 ]; then echo call: rename from to files; exit; fi
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.
17 #ifdef HAVE_STDIO_EXT_H
18 # include <stdio_ext.h>
22 # define HAVE___FPURGE 1
23 # define __fpurge fpurge
33 #include <sys/types.h>
39 #include "closestream.h"
43 #define RENAME_EXIT_SOMEOK 2
44 #define RENAME_EXIT_NOTHING 4
45 #define RENAME_EXIT_UNEXPLAINED 64
47 static int tty_cbreak
= 0;
51 static int string_replace(char *from
, char *to
, char *s
, char *orig
, char **newname
)
54 size_t count
= 0, fromlen
= strlen(from
);
56 p
= where
= strstr(s
, from
);
60 while ((all
|| last
) && p
) {
61 p
= strstr(p
+ (last
? 1 : fromlen
), from
);
70 *newname
= xmalloc(strlen(orig
) - count
* fromlen
+ count
* strlen(to
) + 1);
79 where
= strstr(p
, from
);
87 static int ask(char *name
)
91 printf(_("%s: overwrite `%s'? "), program_invocation_short_name
, name
);
93 if ((c
= fgetc(stdin
)) == EOF
) {
99 if (c
!= '\n' && tty_cbreak
) {
101 /* Possibly purge a multi-byte character; or do a
102 required purge of the rest of the line (including
103 the newline) if the tty has been put back in
104 canonical mode (for example by a shell after a
111 while ((c
= fgetc(stdin
)) != '\n' && c
!= EOF
);
114 if (rpmatch(buf
) == RPMATCH_YES
)
120 static int do_symlink(char *from
, char *to
, char *s
, int verbose
, int noact
,
121 int nooverwrite
, int interactive
)
123 char *newname
= NULL
, *target
= NULL
;
128 if ( faccessat(AT_FDCWD
, s
, F_OK
, AT_SYMLINK_NOFOLLOW
) != 0 &&
130 /* Skip if AT_SYMLINK_NOFOLLOW is not supported; lstat() below will
131 detect the access error */
133 warn(_("%s: not accessible"), s
);
137 if (lstat(s
, &sb
) == -1) {
138 warn(_("stat of %s failed"), s
);
141 if (!S_ISLNK(sb
.st_mode
)) {
142 warnx(_("%s: not a symbolic link"), s
);
145 target
= xmalloc(sb
.st_size
+ 1);
147 ssz
= readlink(s
, target
, sb
.st_size
+ 1);
149 warn(_("%s: readlink failed"), s
);
155 if (string_replace(from
, to
, target
, target
, &newname
) != 0)
158 if (ret
== 1 && (nooverwrite
|| interactive
) && lstat(newname
, &sb
) != 0)
159 nooverwrite
= interactive
= 0;
162 (nooverwrite
|| (interactive
&& (noact
|| ask(newname
) != 0))) )
165 printf(_("Skipping existing link: `%s' -> `%s'\n"), s
, target
);
170 if (!noact
&& 0 > unlink(s
)) {
171 warn(_("%s: unlink failed"), s
);
174 else if (!noact
&& symlink(newname
, s
) != 0) {
175 warn(_("%s: symlinking to %s failed"), s
, newname
);
179 if (verbose
&& (noact
|| ret
== 1))
180 printf("%s: `%s' -> `%s'\n", s
, target
, newname
);
186 static int do_file(char *from
, char *to
, char *s
, int verbose
, int noact
,
187 int nooverwrite
, int interactive
)
189 char *newname
= NULL
, *file
=NULL
;
193 if ( faccessat(AT_FDCWD
, s
, F_OK
, AT_SYMLINK_NOFOLLOW
) != 0 &&
195 /* Skip if AT_SYMLINK_NOFOLLOW is not supported; lstat() below will
196 detect the access error */
198 warn(_("%s: not accessible"), s
);
202 if (lstat(s
, &sb
) == -1) {
203 warn(_("stat of %s failed"), s
);
206 if (strchr(from
, '/') == NULL
&& strchr(to
, '/') == NULL
)
207 file
= strrchr(s
, '/');
210 if (string_replace(from
, to
, file
, s
, &newname
) != 0)
213 if ((nooverwrite
|| interactive
) && access(newname
, F_OK
) != 0)
214 nooverwrite
= interactive
= 0;
216 if (nooverwrite
|| (interactive
&& (noact
|| ask(newname
) != 0))) {
218 printf(_("Skipping existing file: `%s'\n"), newname
);
221 else if (!noact
&& rename(s
, newname
) != 0) {
222 warn(_("%s: rename to %s failed"), s
, newname
);
225 if (verbose
&& (noact
|| ret
== 1))
226 printf("`%s' -> `%s'\n", s
, newname
);
231 static void __attribute__((__noreturn__
)) usage(void)
234 fputs(USAGE_HEADER
, out
);
236 _(" %s [options] <expression> <replacement> <file>...\n"),
237 program_invocation_short_name
);
239 fputs(USAGE_SEPARATOR
, out
);
240 fputs(_("Rename files.\n"), out
);
242 fputs(USAGE_OPTIONS
, out
);
243 fputs(_(" -v, --verbose explain what is being done\n"), out
);
244 fputs(_(" -s, --symlink act on the target of symlinks\n"), out
);
245 fputs(_(" -n, --no-act do not make any changes\n"), out
);
246 fputs(_(" -a, --all replace all occurrences\n"), out
);
247 fputs(_(" -l, --last replace only the last occurrence\n"), out
);
248 fputs(_(" -o, --no-overwrite don't overwrite existing files\n"), out
);
249 fputs(_(" -i, --interactive prompt before overwrite\n"), out
);
250 fputs(USAGE_SEPARATOR
, out
);
251 printf(USAGE_HELP_OPTIONS(21));
252 printf(USAGE_MAN_TAIL("rename(1)"));
256 int main(int argc
, char **argv
)
259 int i
, c
, ret
= 0, verbose
= 0, noact
= 0, nooverwrite
= 0, interactive
= 0;
261 int (*do_rename
)(char *from
, char *to
, char *s
, int verbose
, int noact
,
262 int nooverwrite
, int interactive
) = do_file
;
264 static const struct option longopts
[] = {
265 {"verbose", no_argument
, NULL
, 'v'},
266 {"version", no_argument
, NULL
, 'V'},
267 {"help", no_argument
, NULL
, 'h'},
268 {"all", no_argument
, NULL
, 'a'},
269 {"last", no_argument
, NULL
, 'l'},
270 {"no-act", no_argument
, NULL
, 'n'},
271 {"no-overwrite", no_argument
, NULL
, 'o'},
272 {"interactive", no_argument
, NULL
, 'i'},
273 {"symlink", no_argument
, NULL
, 's'},
276 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
281 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
283 setlocale(LC_ALL
, "");
284 bindtextdomain(PACKAGE
, LOCALEDIR
);
286 close_stdout_atexit();
288 while ((c
= getopt_long(argc
, argv
, "vsVhnaloi", longopts
, NULL
)) != -1) {
289 err_exclusive_options(c
, longopts
, excl
, excl_st
);
310 do_rename
= do_symlink
;
314 print_version(EXIT_SUCCESS
);
318 errtryhelp(EXIT_FAILURE
);
326 warnx(_("not enough arguments"));
327 errtryhelp(EXIT_FAILURE
);
333 if (!strcmp(from
, to
))
334 return RENAME_EXIT_NOTHING
;
337 if (interactive
&& isatty(STDIN_FILENO
) != 0) {
338 if (tcgetattr(STDIN_FILENO
, &tio
) != 0)
339 warn(_("failed to get terminal attributes"));
340 else if (!(tio
.c_lflag
& ICANON
) && tio
.c_cc
[VMIN
] == 1)
344 for (i
= 2; i
< argc
; i
++)
345 ret
|= do_rename(from
, to
, argv
[i
], verbose
, noact
, nooverwrite
, interactive
);
349 return RENAME_EXIT_NOTHING
;
355 return RENAME_EXIT_SOMEOK
;
357 return RENAME_EXIT_UNEXPLAINED
;