Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 231 lines (218 sloc) 5.395 kB
b571d2f @singpolyma Initial commit
authored
1 #include <stdio.h>
2 #include <stdlib.h>
b694531 @singpolyma Implement for POSIX and C89
authored
3 #include <string.h>
4 #include <errno.h>
0653244 @singpolyma Support freegetopt
authored
5
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
6 #if !defined(ASSUME_UNIX) && !defined(ASSUME_WIN32) && !defined(ASSUME_ANSIC)
5a46e26 @dj3vande Added check for __APPLE__ that seems to have gotten lost
dj3vande authored
7 #if defined(__unix__) || defined(__APPLE__)
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
8 #define ASSUME_UNIX
9 #elif defined(_WIN32)
10 #define ASSUME_WIN32
11 #else
12 #define ASSUME_ANSIC
13 #endif
14 #endif
15
16 #ifdef ASSUME_UNIX
9fb0899 @singpolyma Removed note about freegetopt
authored
17 #include <unistd.h>
0653244 @singpolyma Support freegetopt
authored
18 #else
19 #include "getopt.h"
20 #endif
b571d2f @singpolyma Initial commit
authored
21
e6f6b1e @singpolyma Pseudocode
authored
22 #define INTERACTIVE_ASSUME_YES 1
2461e31 @singpolyma POSIX says the default depends on if stdin isatty
authored
23 #define INTERACTIVE_ON_ERROR 2
e6f6b1e @singpolyma Pseudocode
authored
24 #define INTERACTIVE 3
25
1565809 @singpolyma RENAME_ON_REBOOT 110
authored
26 #define RENAME_ON_REBOOT 110
b571d2f @singpolyma Initial commit
authored
27
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
28 #ifdef ASSUME_WIN32
04ca40f @singpolyma Windows basename
authored
29 #include <io.h>
5501326 @singpolyma Detect if file is open on Windows
authored
30 #include <windows.h>
04ca40f @singpolyma Windows basename
authored
31 #define TRY_BACKSLASH_AND_SLASH(p) \
cb7e545 @singpolyma whitespace
authored
32 name = strrchr((p), '\\'); \
04ca40f @singpolyma Windows basename
authored
33 if(!name) { \
34 name = strrchr((p), '/'); \
35 } \
36 if(!name) { \
37 return (p); \
38 }
39 char *basename(char *path) {
40 char *name;
41 if(path == NULL || path[0] == '\0') {
42 return ".";
43 }
44 if(strlen(path) == 1 && (path[0] == '\\' || path[0] == '/')) {
45 return "/";
46 }
47 TRY_BACKSLASH_AND_SLASH(path);
48 if(name[1] == '\0') {
49 *name = '\0';
50 TRY_BACKSLASH_AND_SLASH(path);
51 }
52 if(name[1] == '\0') {
53 return "/";
54 }
55 return name+1;
56 }
5501326 @singpolyma Detect if file is open on Windows
authored
57 int file_exists(const char *dst) {
58 HANDLE fp = CreateFile(dst, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
59 int err = GetLastError();
60 CloseHandle(fp);
61 if(err == ERROR_SUCCESS) {
62 return 1;
63 }
64 if(err == ERROR_SHARING_VIOLATION) {
65 return 2;
66 }
67 return 0;
68 }
04ca40f @singpolyma Windows basename
authored
69 #endif
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
70 #ifdef ASSUME_UNIX
b694531 @singpolyma Implement for POSIX and C89
authored
71 #include <libgen.h>
5501326 @singpolyma Detect if file is open on Windows
authored
72 int file_exists(const char *dst) {
73 FILE *fp;
74 fp = fopen(dst, "rb");
75 if(fp) { /* The file exists */
76 fclose(fp);
77 return 1;
78 }
79 return 0;
80 }
04ca40f @singpolyma Windows basename
authored
81 #endif
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
82 #ifndef ASSUME_ANSIC
b694531 @singpolyma Implement for POSIX and C89
authored
83 #include <sys/stat.h>
84 int isdir(const char *path) {
85 struct stat st;
86 return (!stat(path, &st) && S_ISDIR(st.st_mode));
87 }
88 char *build_dst(const char *src, const char *dst) {
89 if(isdir(dst)) {
04ca40f @singpolyma Windows basename
authored
90 char *src_copy = strdup(src);
b694531 @singpolyma Implement for POSIX and C89
authored
91 char *path = strdup(dst);
04ca40f @singpolyma Windows basename
authored
92 char *file = basename(src_copy);
b694531 @singpolyma Implement for POSIX and C89
authored
93 if(!path || !file) return NULL;
94 path = realloc(path, strlen(path) + strlen(file) + 2);
95 if(!path) return NULL;
96 if(path[strlen(path)-1] != '/') {
97 strcat(path, "/");
98 }
99 strcat(path, file);
04ca40f @singpolyma Windows basename
authored
100 free(src_copy);
b694531 @singpolyma Implement for POSIX and C89
authored
101 return path;
102 } else {
103 return strdup(dst);
104 }
105 }
106 #endif
107
b571d2f @singpolyma Initial commit
authored
108 int do_move(const char *src, const char *dst, int interactive) {
b694531 @singpolyma Implement for POSIX and C89
authored
109 char *built_dst = NULL;
110 int err = 0;
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
111 #ifndef ASSUME_ANSIC
6be9631 @singpolyma Fix some bugs in the C89 code
authored
112 int exists = 0;
113 #endif
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
114 #ifndef ASSUME_ANSIC
62c050d @singpolyma Use fgetc
authored
115 built_dst = build_dst(src, dst);
116 if(!built_dst) {
117 perror("do_move call to build_dst failed");
118 exit(EXIT_FAILURE);
119 }
2461e31 @singpolyma POSIX says the default depends on if stdin isatty
authored
120 /* POSIX says the default depends on if stdin isatty. */
9a3fee9 @singpolyma Added prompts
authored
121 if(interactive == INTERACTIVE_ON_ERROR && !isatty(fileno(stdin))) {
2461e31 @singpolyma POSIX says the default depends on if stdin isatty
authored
122 interactive = INTERACTIVE_ASSUME_YES;
123 }
124 #endif
7d33002 @singpolyma typo
authored
125 /* If running in interactive mode, prompt for overwrite. */
b694531 @singpolyma Implement for POSIX and C89
authored
126 if(interactive > INTERACTIVE_ASSUME_YES) {
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
127 #ifndef ASSUME_ANSIC
5501326 @singpolyma Detect if file is open on Windows
authored
128 exists = file_exists(built_dst);
129 if(exists) {
62c050d @singpolyma Use fgetc
authored
130 int yesno;
5501326 @singpolyma Detect if file is open on Windows
authored
131 if(exists == 1) {
132 fprintf(stderr, "'%s' already exists. Overwrite? [Yn] ", built_dst);
133 } else if(exists == 2) {
134 fprintf(stderr, "'%s' is in use. Overwrite on reboot? [Yn] ", built_dst);
135 }
415660e @singpolyma Call fflush for prompts
authored
136 fflush(stderr);
62c050d @singpolyma Use fgetc
authored
137 if((yesno = fgetc(stdin)) == EOF) {
9a3fee9 @singpolyma Added prompts
authored
138 fputs("Reading from STDIN failed.\n", stderr);
139 return EXIT_FAILURE;
140 }
62c050d @singpolyma Use fgetc
authored
141 if(yesno == 'n' || yesno == 'N') {
5501326 @singpolyma Detect if file is open on Windows
authored
142 return exists == 2 ? EXIT_FAILURE : 0;
9a3fee9 @singpolyma Added prompts
authored
143 }
b694531 @singpolyma Implement for POSIX and C89
authored
144 }
145 #else
146 /* C89 gives us no way to construct the path, so just always prompt. */
62c050d @singpolyma Use fgetc
authored
147 char yesno;
9a3fee9 @singpolyma Added prompts
authored
148 fprintf(stderr, "Can't tell if '%s' exists. Move anyway? [Yn] ", dst);
415660e @singpolyma Call fflush for prompts
authored
149 fflush(stderr);
6be9631 @singpolyma Fix some bugs in the C89 code
authored
150 if((yesno = fgetc(stdin)) == EOF) {
9a3fee9 @singpolyma Added prompts
authored
151 fputs("Reading from STDIN failed.\n", stderr);
152 return EXIT_FAILURE;
153 }
62c050d @singpolyma Use fgetc
authored
154 if(yesno == 'n' || yesno == 'N') {
9a3fee9 @singpolyma Added prompts
authored
155 return 0;
156 }
b694531 @singpolyma Implement for POSIX and C89
authored
157 #endif
158 }
e6f6b1e @singpolyma Pseudocode
authored
159 /* Try to move */
e6e7038 @dj3vande Fixed preprocessor platform detection.
dj3vande authored
160 #ifdef ASSUME_WIN32
5501326 @singpolyma Detect if file is open on Windows
authored
161 if(interactive == INTERACTIVE_ASSUME_YES) {
162 exists = file_exists(built_dst ? built_dst : dst);
163 }
164 if(exists == 2) { /* rename on reboot */
086a78f @singpolyma Implement moving on Windows
authored
165 err = MoveFileEx(src, built_dst ? built_dst : dst, MOVEFILE_DELAY_UNTIL_REBOOT | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
5501326 @singpolyma Detect if file is open on Windows
authored
166 } else { /* just rename */
086a78f @singpolyma Implement moving on Windows
authored
167 err = MoveFileEx(src, built_dst ? built_dst : dst, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
5501326 @singpolyma Detect if file is open on Windows
authored
168 }
086a78f @singpolyma Implement moving on Windows
authored
169 if(err) {
170 err = GetLastError();
b694531 @singpolyma Implement for POSIX and C89
authored
171 }
086a78f @singpolyma Implement moving on Windows
authored
172 #else
173 err = rename(src, built_dst ? built_dst : dst);
b694531 @singpolyma Implement for POSIX and C89
authored
174 if(err) {
175 err = errno;
176 }
177 #endif
178 if(built_dst) {
179 free(built_dst);
180 }
181 return err;
b571d2f @singpolyma Initial commit
authored
182 }
183
5b948c1 @singpolyma program_name need not be global
authored
184 void help(char *program_name) {
b571d2f @singpolyma Initial commit
authored
185 fprintf(stderr, "%s [-i | -f] source_file(s) target_file\n", program_name);
186 exit(EXIT_FAILURE);
187 }
188
189 int main(int argc, char *argv[]) {
190 int i, err;
191 int exit_status = EXIT_SUCCESS;
192 int interactive = INTERACTIVE_ON_ERROR;
5b948c1 @singpolyma program_name need not be global
authored
193 char *program_name = argv[0];
b571d2f @singpolyma Initial commit
authored
194
195 while((i = getopt(argc, argv, "if")) != -1) {
196 switch(i) {
197 case 'i':
198 interactive = INTERACTIVE;
199 break;
200 case 'f':
201 interactive = INTERACTIVE_ASSUME_YES;
202 break;
203 default:
5b948c1 @singpolyma program_name need not be global
authored
204 help(program_name);
b571d2f @singpolyma Initial commit
authored
205 }
206 }
207 argc -= optind;
208 argv += optind;
209 if(argc < 2) {
5b948c1 @singpolyma program_name need not be global
authored
210 help(program_name);
b571d2f @singpolyma Initial commit
authored
211 }
212
213 for(i = 0; i < argc-1; i++) {
214 err = do_move(argv[i], argv[argc-1], interactive);
215 if(err) {
a14ac0d @singpolyma Rename on reboot needs no error message.
authored
216 if(err != RENAME_ON_REBOOT) {
217 fprintf(stderr, "%s: move of '%s' to '%s' failed.\n", program_name, argv[i], argv[argc-1]);
218 }
0fc070a @singpolyma Give RENAME_ON_REBOOT priority
authored
219 if(exit_status != RENAME_ON_REBOOT) {
220 exit_status = err;
221 }
b571d2f @singpolyma Initial commit
authored
222 }
223 }
224
a14ac0d @singpolyma Rename on reboot needs no error message.
authored
225 if(exit_status == RENAME_ON_REBOOT) {
226 fputs("Some files will not be renamed until you reboot.\n", stderr);
227 }
228
b571d2f @singpolyma Initial commit
authored
229 exit(exit_status);
230 }
Something went wrong with that request. Please try again.