xref: /reactos/base/shell/cmd/type.c (revision cce399e7)
1 /*
2  *  TYPE.C - type internal command.
3  *
4  *  History:
5  *
6  *    07/08/1998 (John P. Price)
7  *        started.
8  *
9  *    07/12/98 (Rob Lake)
10  *        Changed error messages
11  *
12  *    27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
13  *        added config.h include
14  *
15  *    07-Jan-1999 (Eric Kohl)
16  *        Added support for quoted arguments (type "test file.dat").
17  *        Cleaned up.
18  *
19  *    19-Jan-1999 (Eric Kohl)
20  *        Unicode and redirection ready!
21  *
22  *    19-Jan-1999 (Paolo Pantaleo <paolopan@freemail.it>)
23  *        Added multiple file support (copied from y.c)
24  *
25  *    30-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
26  *        Remove all hardcoded strings in En.rc
27  */
28 
29 #include "precomp.h"
30 
31 #ifdef INCLUDE_CMD_TYPE
32 
33 static BOOL
34 FileGetString(
35     IN HANDLE hFile,
36     OUT LPTSTR lpBuffer,
37     IN LONG nBufferLength)
38 {
39     PCHAR pString;
40     DWORD dwRead;
41     LONG len = 0;
42 
43 #ifdef _UNICODE
44     pString = cmd_alloc(nBufferLength);
45 #else
46     pString = lpBuffer;
47 #endif
48 
49     if (ReadFile(hFile, pString, nBufferLength - 1, &dwRead, NULL))
50     {
51         /* Break at new line*/
52         PCHAR end = memchr(pString, '\n', dwRead);
53         len = dwRead;
54         if (end)
55         {
56             len = (LONG)(end - pString) + 1;
57             SetFilePointer(hFile, len - dwRead, NULL, FILE_CURRENT);
58         }
59     }
60 
61     if (!len)
62     {
63 #ifdef _UNICODE
64         cmd_free(pString);
65 #endif
66         return FALSE;
67     }
68 
69     pString[len++] = '\0';
70 #ifdef _UNICODE
71     MultiByteToWideChar(OutputCodePage, 0, pString, -1, lpBuffer, len);
72     cmd_free(pString);
73 #endif
74     return TRUE;
75 }
76 
77 static BOOL
78 DoTypeFile(
79     IN LPTSTR FileName,
80     IN HANDLE hConsoleOut,
81     IN BOOL bNoFileName,
82     IN BOOL bPaging)
83 {
84     HANDLE hFile;
85     BOOL   bIsFile;
86     DWORD  dwFileSize;
87     DWORD  dwFilePos;
88     DWORD  dwRet;
89     LPTSTR errmsg;
90     TCHAR  buff[256];
91 
92     hFile = CreateFile(FileName,
93                        GENERIC_READ,
94                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
95                        OPEN_EXISTING,
96                        FILE_ATTRIBUTE_NORMAL, NULL);
97 
98     if (hFile == INVALID_HANDLE_VALUE)
99     {
100         // FIXME: Use ErrorMessage() ?
101         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
102                       FORMAT_MESSAGE_IGNORE_INSERTS |
103                       FORMAT_MESSAGE_FROM_SYSTEM,
104                       NULL,
105                       GetLastError(),
106                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
107                       (LPTSTR)&errmsg,
108                       0,
109                       NULL);
110         ConErrPrintf(_T("%s - %s"), FileName, errmsg);
111         LocalFree(errmsg);
112         nErrorLevel = 1;
113         return TRUE;
114     }
115 
116     /*
117      * When reading from a file, retrieve its original size, so that
118      * we can stop reading it once we are beyond its original ending.
119      * This allows avoiding an infinite read loop in case the output
120      * of the file is redirected back to it.
121      * If we read from somewhere else (device, ...) don't do anything;
122      * we will stop when ReadFile() fails (e.g. when Ctrl-Z is seen...).
123      */
124     bIsFile = ((GetFileType(hFile) & ~FILE_TYPE_REMOTE) == FILE_TYPE_DISK);
125     if (bIsFile)
126     {
127         dwFileSize = GetFileSize(hFile, NULL);
128         if ((dwFileSize == INVALID_FILE_SIZE) &&
129             (GetLastError() != ERROR_SUCCESS))
130         {
131             WARN("Error when retrieving file size, or size too large (%d)\n",
132                  dwFileSize);
133             dwFileSize = 0;
134         }
135         dwFilePos = SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
136         if ((dwFilePos == INVALID_SET_FILE_POINTER) &&
137             (GetLastError() != ERROR_SUCCESS))
138         {
139             WARN("Error when setting file pointer\n");
140             dwFilePos = 0;
141         }
142     }
143     else
144     {
145         dwFileSize = dwFilePos = 0;
146     }
147 
148     /*
149      * Display the file name on StdErr if required, so that if StdOut
150      * alone is redirected, we can obtain the file contents only.
151      */
152     if (!bNoFileName)
153         ConErrPrintf(_T("\n%s\n\n\n"), FileName);
154 
155     if (bPaging)
156     {
157         while (FileGetString(hFile, buff, ARRAYSIZE(buff)))
158         {
159             if (!ConOutPrintfPaging(FALSE, _T("%s"), buff))
160             {
161                 bCtrlBreak = FALSE;
162                 CloseHandle(hFile);
163                 nErrorLevel = 1;
164                 return FALSE;
165             }
166 
167             /*
168              * If we read from a file, check where we are and stop
169              * once we are beyond the original end of the file.
170              */
171             if (bIsFile)
172             {
173                 dwFilePos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
174                 if ((dwFilePos == INVALID_SET_FILE_POINTER) &&
175                     (GetLastError() != ERROR_SUCCESS))
176                 {
177                     WARN("Error when getting file pointer\n");
178                     break;
179                 }
180                 if (dwFilePos >= dwFileSize)
181                     break;
182             }
183         }
184     }
185     else
186     {
187         while (ReadFile(hFile, buff, sizeof(buff), &dwRet, NULL) && dwRet > 0)
188         {
189             WriteFile(hConsoleOut, buff, dwRet, &dwRet, NULL);
190             if (bCtrlBreak)
191             {
192                 bCtrlBreak = FALSE;
193                 CloseHandle(hFile);
194                 nErrorLevel = 1;
195                 return FALSE;
196             }
197 
198             /*
199              * If we read from a file, check where we are and stop
200              * once we are beyond the original end of the file.
201              */
202             if (bIsFile)
203             {
204                 dwFilePos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
205                 if ((dwFilePos == INVALID_SET_FILE_POINTER) &&
206                     (GetLastError() != ERROR_SUCCESS))
207                 {
208                     WARN("Error when getting file pointer\n");
209                     break;
210                 }
211                 if (dwFilePos >= dwFileSize)
212                     break;
213             }
214         }
215     }
216 
217     CloseHandle(hFile);
218     return TRUE;
219 }
220 
221 INT cmd_type(LPTSTR param)
222 {
223     INT argc, i;
224     LPTSTR* argv;
225     LPTSTR errmsg;
226     HANDLE hConsoleOut;
227     BOOL bNoFileName = FALSE;
228     BOOL bPaging = FALSE;
229     BOOL bFileFound;
230     DWORD dwLastError;
231     UINT nFileSpecs = 0;
232     HANDLE hFind;
233     WIN32_FIND_DATA FindData;
234 
235     if (!_tcsncmp(param, _T("/?"), 2))
236     {
237         ConOutResPaging(TRUE, STRING_TYPE_HELP1);
238         return 0;
239     }
240 
241     if (!*param)
242     {
243         error_req_param_missing();
244         return 1;
245     }
246 
247     /* Parse the command line. We will manually expand any file specification present. */
248     argv = split(param, &argc, FALSE, FALSE);
249 
250     /* Loop through the options, count also the specified number of file specifications */
251     for (i = 0; i < argc; ++i)
252     {
253         if (argv[i][0] == _T('/'))
254         {
255             if (_tcslen(argv[i]) == 2)
256             {
257                 switch (_totupper(argv[i][1]))
258                 {
259                 case _T('N'):
260                     bNoFileName = TRUE;
261                     continue;
262 
263                 case _T('P'):
264                     bPaging = TRUE;
265                     continue;
266                 }
267             }
268 
269             // error_invalid_switch(argv[i] + 1);
270             ConErrResPrintf(STRING_TYPE_ERROR, argv[i] + 1);
271             nErrorLevel = 1;
272             goto Quit;
273         }
274 
275         /* This should be a file specification */
276         ++nFileSpecs;
277     }
278 
279     nErrorLevel = 0;
280 
281     hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
282 
283     /* Reset paging state */
284     if (bPaging)
285         ConOutPrintfPaging(TRUE, _T(""));
286 
287     /* Now loop through the files */
288     for (i = 0; i < argc; ++i)
289     {
290         /* Skip the options */
291         if (argv[i][0] == _T('/'))
292             continue;
293 
294         /* If wildcards are present in this file specification, perform a file enumeration */
295         if (_tcschr(argv[i], _T('*')) || _tcschr(argv[i], _T('?')))
296         {
297             dwLastError = ERROR_SUCCESS;
298             bFileFound = FALSE;
299 
300             hFind = FindFirstFile(argv[i], &FindData);
301 
302             if (hFind != INVALID_HANDLE_VALUE)
303             {
304                 /* Loop through all the files */
305                 do
306                 {
307                     /* Ignore any directory silently */
308                     if (!_tcscmp(FindData.cFileName, _T("."))  ||
309                         !_tcscmp(FindData.cFileName, _T("..")) ||
310                         (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
311                     {
312                         continue;
313                     }
314 
315                     bFileFound = TRUE;
316                     if (!DoTypeFile(FindData.cFileName, hConsoleOut, bNoFileName, bPaging))
317                     {
318                         FindClose(hFind);
319                         goto Quit;
320                     }
321 
322                 } while (FindNextFile(hFind, &FindData));
323 
324                 FindClose(hFind);
325             }
326 
327             /*
328              * Return an error if the file specification could not be resolved,
329              * or no actual files were encountered (but only directories).
330              */
331             if (hFind == INVALID_HANDLE_VALUE)
332                 dwLastError = GetLastError();
333             else if (!bFileFound)
334                 dwLastError = ERROR_FILE_NOT_FOUND;
335 
336             if (dwLastError != ERROR_SUCCESS)
337             {
338                 // FIXME: Use ErrorMessage() ?
339                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
340                               FORMAT_MESSAGE_IGNORE_INSERTS |
341                               FORMAT_MESSAGE_FROM_SYSTEM,
342                               NULL,
343                               dwLastError,
344                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
345                               (LPTSTR)&errmsg,
346                               0,
347                               NULL);
348                 ConErrPrintf(_T("%s - %s"), argv[i], errmsg);
349                 LocalFree(errmsg);
350                 nErrorLevel = 1;
351             }
352         }
353         else
354         {
355             if (!DoTypeFile(argv[i], hConsoleOut, (bNoFileName || (nFileSpecs <= 1)), bPaging))
356                 goto Quit;
357         }
358 
359         /* Continue with the next file specification */
360     }
361 
362 Quit:
363     freep(argv);
364     return nErrorLevel;
365 }
366 
367 #endif
368