[MKHIVE/USETUP]
[reactos.git] / reactos / base / shell / cmd / ren.c
1 /*
2 * REN.C - rename internal command.
3 *
4 *
5 * History:
6 *
7 *
8 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
9 * added config.h include
10 *
11 * 18-Dec-1998 (Eric Kohl)
12 * Added support for quoted long file names with spaces.
13 *
14 * 20-Jan-1999 (Eric Kohl)
15 * Unicode and redirection safe!
16 *
17 * 17-Oct-2001 (Eric Kohl)
18 * Implemented basic rename code.
19 *
20 * 30-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
21 * Remove all hardcode string to En.rc
22 * 25-Nov-2008 (Victor Martinez) <vicmarcal@hotmail.com> Patch dedicated to Myrjala because her comprenhension and love :D
23 * Fixing following Bugs:
24 * -Wrong behavior with wildcards when Source and Destiny are Paths(FIXED).
25 * -Wrong general behavior (MSDN:"Rename cant move files between subdirectories")(FIXED)
26 * -Wrong behavior when renaming without path in destiny:(i.e) "ren C:\text\as.txt list.txt" it moves as.txt and then rename it(FIXED)
27 * (MSDN: If there is a Path in Source and no Path in Destiny, then Destiny Path is Source Path,because never Ren has to be used to move.)
28 * -Implemented checkings if SourcePath and DestinyPath are differents.
29 *
30 */
31
32 #include <precomp.h>
33
34 #ifdef INCLUDE_CMD_RENAME
35
36 enum
37 {
38 REN_ATTRIBUTES = 0x001, /* /A : not implemented */
39 REN_ERROR = 0x002, /* /E */
40 REN_NOTHING = 0x004, /* /N */
41 REN_PROMPT = 0x008, /* /P : not implemented */
42 REN_QUIET = 0x010, /* /Q */
43 REN_SUBDIR = 0x020, /* /S */
44 REN_TOTAL = 0x040, /* /T */
45 };
46
47
48 /*
49 * file rename internal command.
50 *
51 */
52 INT cmd_rename (LPTSTR param)
53 {
54 LPTSTR *arg = NULL;
55 INT args = 0;
56 INT nSlash = 0;
57 INT nEvalArgs = 0; /* nunber of evaluated arguments */
58 DWORD dwFlags = 0;
59 DWORD dwFiles = 0; /* number of renamedd files */
60 INT i;
61
62
63 LPTSTR srcPattern = NULL; /* Source Argument*/
64 TCHAR srcPath[MAX_PATH]; /*Source Path Directories*/
65 LPTSTR srcFILE = NULL; /*Contains the files name(s)*/
66 TCHAR srcFinal[MAX_PATH];
67
68
69 LPTSTR dstPattern = NULL; /*Destiny Argument*/
70 TCHAR dstPath[MAX_PATH]; /*Source Path Directories*/
71 LPTSTR dstFILE = NULL; /*Contains the files name(s)*/
72
73 TCHAR dstLast[MAX_PATH]; /*It saves the File name after unmasked with wildcarts*/
74 TCHAR dstFinal[MAX_PATH]; /*It saves the Final destiny Path*/
75
76 BOOL bDstWildcard = FALSE;
77 BOOL bPath = FALSE;
78
79
80
81
82
83
84 LPTSTR p,q,r;
85
86 HANDLE hFile;
87 WIN32_FIND_DATA f;
88 /*If the PARAM=/? then show the help*/
89 if (!_tcsncmp(param, _T("/?"), 2))
90 {
91
92
93 ConOutResPaging(TRUE,STRING_REN_HELP1);
94 return 0;
95 }
96
97 nErrorLevel = 0;
98
99 /* Split the argument list.Args will be saved in arg vector*/
100 arg = split(param, &args, FALSE);
101
102 if (args < 2)
103 {
104 if (!(dwFlags & REN_ERROR))
105 error_req_param_missing();
106 freep(arg);
107 return 1;
108 }
109
110 /* Read options */
111 for (i = 0; i < args; i++)
112 {
113 /* Lets check if we have a special option choosen and set the flag(s)*/
114 if (*arg[i] == _T('/'))
115 {
116 if (_tcslen(arg[i]) >= 2)
117 {
118 switch (_totupper(arg[i][1]))
119 {
120 case _T('E'):
121 dwFlags |= REN_ERROR;
122 break;
123
124 case _T('N'):
125 dwFlags |= REN_NOTHING;
126 break;
127
128 case _T('P'):
129 dwFlags |= REN_PROMPT;
130 break;
131
132 case _T('Q'):
133 dwFlags |= REN_QUIET;
134 break;
135
136 case _T('S'):
137 dwFlags |= REN_SUBDIR;
138 break;
139
140 case _T('T'):
141 dwFlags |= REN_TOTAL;
142 break;
143 }
144 }
145 nEvalArgs++;//Save the number of the options.
146 }
147 }
148
149 /* keep quiet within batch files */
150 if (bc != NULL)
151 dwFlags |= REN_QUIET;
152
153 /* there are only options on the command line --> error!!! */
154 if (args < nEvalArgs + 2)
155 {
156 if (!(dwFlags & REN_ERROR))
157 error_req_param_missing();
158 freep(arg);
159 return 1;
160 }
161
162
163 /* Get destination pattern and source pattern*/
164 for (i = 0; i < args; i++)
165 {
166 if (*arg[i] == _T('/'))//We have find an Option.Jump it.
167 continue;
168 dstPattern = arg[i]; //we save the Last argument as dstPattern
169 srcPattern = arg[i-1];
170
171 }
172
173
174
175
176
177 if (_tcschr(srcPattern, _T('\\'))) //Checking if the Source (srcPattern) is a Path to the file
178 {
179
180 bPath= TRUE;
181
182 //Splitting srcPath and srcFile.
183
184 srcFILE = _tcschr(srcPattern, _T('\\'));
185 nSlash++;
186 while(_tcschr(srcFILE, _T('\\')))
187 {
188 srcFILE++;
189 if(*srcFILE==_T('\\')) nSlash++ ;
190 if(!_tcschr(srcFILE, _T('\\'))) break;
191 }
192 _tcsncpy(srcPath,srcPattern,_tcslen(srcPattern)-_tcslen(srcFILE));
193
194
195
196 if(_tcschr(dstPattern, _T('\\'))) //Checking if the Destiny (dstPattern)is also a Path.And splitting dstPattern in dstPath and srcPath.
197 {
198 dstFILE = _tcschr(dstPattern, _T('\\'));
199 nSlash=0;
200 while(_tcschr(dstFILE, _T('\\')))
201 {
202 dstFILE++;
203 if(*dstFILE==_T('\\')) nSlash++ ;
204 if(!_tcschr(dstFILE, _T('\\'))) break;
205 }
206 _tcsncpy(dstPath,dstPattern,_tcslen(dstPattern)-_tcslen(dstFILE));
207
208 if((_tcslen(dstPath)!=_tcslen(srcPath))||(_tcsncmp(srcPath,dstPath,_tcslen(srcPath))!=0)) //If it has a Path,then MUST be equal than srcPath
209 {
210 error_syntax(dstPath);
211 freep(arg);
212 return 1;
213 }
214 }else { //If Destiny hasnt a Path,then (MSDN says) srcPath is its Path.
215
216 _tcscpy(dstPath,srcPath);
217
218 dstFILE=dstPattern;
219
220 }
221
222
223
224 }
225
226 if (!_tcschr(srcPattern, _T('\\'))) //If srcPattern isnt a Path but a name:
227 {
228 srcFILE=srcPattern;
229 if(_tcschr(dstPattern, _T('\\')))
230 {
231 error_syntax(dstPattern);
232
233 freep(arg);
234 return 1;
235 }else dstFILE=dstPattern;
236 }
237
238 //Checking Wildcards.
239 if (_tcschr(dstFILE, _T('*')) || _tcschr(dstFILE, _T('?')))
240 bDstWildcard = TRUE;
241
242
243
244 TRACE("\n\nSourcePattern: %s SourcePath: %s SourceFile: %s", debugstr_aw(srcPattern),debugstr_aw(srcPath),debugstr_aw(srcFILE));
245 TRACE("\n\nDestinationPattern: %s Destination Path:%s Destination File: %s\n", debugstr_aw(dstPattern),debugstr_aw(dstPath),debugstr_aw(dstFILE));
246
247 hFile = FindFirstFile(srcPattern, &f);
248
249 if (hFile == INVALID_HANDLE_VALUE)
250 {
251 if (!(dwFlags & REN_ERROR))
252 error_file_not_found();
253
254 }
255 do
256 {
257 /* ignore "." and ".." */
258 if (!_tcscmp (f.cFileName, _T(".")) ||
259 !_tcscmp (f.cFileName, _T("..")))
260 continue;
261
262 /* do not rename hidden or system files */
263 if (f.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))
264 continue;
265
266 /* do not rename directories when the destination pattern contains
267 * wildcards, unless option /S is used */
268 if ((f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
269 && bDstWildcard
270 && !(dwFlags & REN_SUBDIR))
271 continue;
272
273 TRACE("Found source name: %s\n", debugstr_aw(f.cFileName));
274 /* So here we have splitted the dstFILE and we have find a f.cFileName(thanks to srcPattern)
275 * Now we have to use the mask (dstFILE) (which can have Wildcards) with f.cFileName to find destination file name(dstLast) */
276 p = f.cFileName;
277 q = dstFILE;
278 r = dstLast;
279 while(*q != 0)
280 {
281 if (*q == '*')
282 {
283 q++;
284 while (*p != 0 && *p != *q)
285 {
286 *r = *p;
287 p++;
288 r++;
289 }
290 }
291 else if (*q == '?')
292 {
293 q++;
294 if (*p != 0)
295 {
296 *r = *p;
297 p++;
298 r++;
299 }
300 }
301 else
302 {
303 *r = *q;
304 if (*p != 0)
305 p++;
306 q++;
307 r++;
308 }
309 }
310 *r = 0;
311 //Well we have splitted the Paths,so now we have to paste them again(if needed),thanks bPath.
312 if( bPath == TRUE)
313 {
314
315 _tcscpy(srcFinal,srcPath);
316
317 _tcscat(srcFinal,f.cFileName);
318
319 _tcscpy(dstFinal,dstPath);
320 _tcscat(dstFinal,dstLast);
321
322
323 }else{
324 _tcscpy(srcFinal,f.cFileName);
325 _tcscpy(dstFinal,dstLast);
326
327 }
328
329
330
331 TRACE("DestinationPath: %s\n", debugstr_aw(dstFinal));
332
333
334 if (!(dwFlags & REN_QUIET) && !(dwFlags & REN_TOTAL))
335
336 ConOutPrintf(_T("%s -> %s\n"),srcFinal , dstFinal);
337
338 /* Rename the file */
339 if (!(dwFlags & REN_NOTHING))
340 {
341
342
343
344 if (MoveFile(srcFinal, dstFinal))
345 {
346 dwFiles++;
347 }
348 else
349 {
350 if (!(dwFlags & REN_ERROR))
351 {
352 ConErrResPrintf(STRING_REN_ERROR1, GetLastError());
353 }
354 }
355 }
356 }
357
358 while (FindNextFile(hFile, &f));
359 //Closing and Printing errors.
360
361 FindClose(hFile);
362
363
364 if (!(dwFlags & REN_QUIET))
365 {
366 if (dwFiles == 1)
367 ConOutResPrintf(STRING_REN_HELP2, dwFiles);
368 else
369 ConOutResPrintf(STRING_REN_HELP3, dwFiles);
370 }
371
372 freep(arg);
373
374 return 0;
375 }
376
377 #endif
378
379 /* EOF */