xref: /reactos/base/shell/cmd/ren.c (revision 0b366ea1)
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_ERROR, 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 */
341