]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/rename.c
rename: check source file access early
[thirdparty/util-linux.git] / misc-utils / rename.c
CommitLineData
eb63b9b8
KZ
1/*
2 * rename.c - aeb 2000-01-01
3 *
4--------------------------------------------------------------
5#!/bin/sh
6if [ $# -le 2 ]; then echo call: rename from to files; exit; fi
7FROM="$1"
8TO="$2"
9shift
10shift
11for 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 35static 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 57static 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 110static 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 142static 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
164int 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}