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