1 #include "rar.hpp"
2 #include "log.cpp"
3
4 static MESSAGE_TYPE MsgStream=MSG_STDOUT;
5 static RAR_CHARSET RedirectCharset=RCH_DEFAULT;
6
7 const int MaxMsgSize=2*NM+2048;
8
9 static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false;
10
11 #ifdef _WIN_ALL
IsRedirected(DWORD nStdHandle)12 static bool IsRedirected(DWORD nStdHandle)
13 {
14 HANDLE hStd=GetStdHandle(nStdHandle);
15 DWORD Mode;
16 return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0;
17 }
18 #endif
19
20
InitConsole()21 void InitConsole()
22 {
23 #ifdef _WIN_ALL
24 // We want messages like file names or progress percent to be printed
25 // immediately. Use only in Windows, in Unix they can cause wprintf %ls
26 // to fail with non-English strings.
27 setbuf(stdout,NULL);
28 setbuf(stderr,NULL);
29
30 // Detect if output is redirected and set output mode properly.
31 // We do not want to send Unicode output to files and especially to pipes
32 // like '|more', which cannot handle them correctly in Windows.
33 // In Unix console output is UTF-8 and it is handled correctly
34 // when redirecting, so no need to perform any adjustments.
35 StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE);
36 StderrRedirected=IsRedirected(STD_ERROR_HANDLE);
37 StdinRedirected=IsRedirected(STD_INPUT_HANDLE);
38 #ifdef _MSC_VER
39 if (!StdoutRedirected)
40 _setmode(_fileno(stdout), _O_U16TEXT);
41 if (!StderrRedirected)
42 _setmode(_fileno(stderr), _O_U16TEXT);
43 #endif
44 #elif defined(_UNIX)
45 StdoutRedirected=!isatty(fileno(stdout));
46 StderrRedirected=!isatty(fileno(stderr));
47 StdinRedirected=!isatty(fileno(stdin));
48 #endif
49 }
50
51
SetConsoleMsgStream(MESSAGE_TYPE MsgStream)52 void SetConsoleMsgStream(MESSAGE_TYPE MsgStream)
53 {
54 ::MsgStream=MsgStream;
55 }
56
57
SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset)58 void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset)
59 {
60 ::RedirectCharset=RedirectCharset;
61 }
62
63
64 #ifndef SILENT
cvt_wprintf(FILE * dest,const wchar * fmt,va_list arglist)65 static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist)
66 {
67 // This buffer is for format string only, not for entire output,
68 // so it can be short enough.
69 wchar fmtw[1024];
70 PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw));
71 #ifdef _WIN_ALL
72 safebuf wchar Msg[MaxMsgSize];
73 if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected)
74 {
75 HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE);
76 vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
77 DWORD Written;
78 if (RedirectCharset==RCH_UNICODE)
79 WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL);
80 else
81 {
82 // Avoid Unicode for redirect in Windows, it does not work with pipes.
83 safebuf char MsgA[MaxMsgSize];
84 if (RedirectCharset==RCH_UTF8)
85 WideToUtf(Msg,MsgA,ASIZE(MsgA));
86 else
87 WideToChar(Msg,MsgA,ASIZE(MsgA));
88 if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM)
89 CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding.
90
91 // We already converted \n to \r\n above, so we use WriteFile instead
92 // of C library to avoid unnecessary additional conversion.
93 WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL);
94 }
95 return;
96 }
97 // MSVC2008 vfwprintf writes every character to console separately
98 // and it is too slow. We use direct WriteConsole call instead.
99 vswprintf(Msg,ASIZE(Msg),fmtw,arglist);
100 HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE);
101 DWORD Written;
102 WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL);
103 #else
104 vfwprintf(dest,fmtw,arglist);
105 // We do not use setbuf(NULL) in Unix (see comments in InitConsole).
106 fflush(dest);
107 #endif
108 }
109
110
mprintf(const wchar * fmt,...)111 void mprintf(const wchar *fmt,...)
112 {
113 if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY)
114 return;
115
116 fflush(stderr); // Ensure proper message order.
117
118 va_list arglist;
119 va_start(arglist,fmt);
120 FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout;
121 cvt_wprintf(dest,fmt,arglist);
122 va_end(arglist);
123 }
124 #endif
125
126
127 #ifndef SILENT
eprintf(const wchar * fmt,...)128 void eprintf(const wchar *fmt,...)
129 {
130 if (MsgStream==MSG_NULL)
131 return;
132
133 fflush(stdout); // Ensure proper message order.
134
135 va_list arglist;
136 va_start(arglist,fmt);
137 cvt_wprintf(stderr,fmt,arglist);
138 va_end(arglist);
139 }
140 #endif
141
142
143 #ifndef SILENT
GetPasswordText(wchar * Str,uint MaxLength)144 static void GetPasswordText(wchar *Str,uint MaxLength)
145 {
146 if (MaxLength==0)
147 return;
148 if (StdinRedirected)
149 getwstr(Str,MaxLength); // Read from pipe or redirected file.
150 else
151 {
152 #ifdef _WIN_ALL
153 HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE);
154 HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE);
155 DWORD ConInMode,ConOutMode;
156 DWORD Read=0;
157 GetConsoleMode(hConIn,&ConInMode);
158 GetConsoleMode(hConOut,&ConOutMode);
159 SetConsoleMode(hConIn,ENABLE_LINE_INPUT);
160 SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT);
161
162 ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL);
163 Str[Read]=0;
164 SetConsoleMode(hConIn,ConInMode);
165 SetConsoleMode(hConOut,ConOutMode);
166 #else
167 char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters.
168 #if defined(_EMX) || defined (__VMS)
169 fgets(StrA,ASIZE(StrA)-1,stdin);
170 #elif defined(__sun)
171 strncpyz(StrA,getpassphrase(""),ASIZE(StrA));
172 #else
173 strncpyz(StrA,getpass(""),ASIZE(StrA));
174 #endif
175 CharToWide(StrA,Str,MaxLength);
176 cleandata(StrA,sizeof(StrA));
177 #endif
178 }
179 Str[MaxLength-1]=0;
180 RemoveLF(Str);
181 }
182 #endif
183
184
185 #ifndef SILENT
GetConsolePassword(UIPASSWORD_TYPE Type,const wchar * FileName,SecPassword * Password)186 bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password)
187 {
188 if (!StdinRedirected)
189 uiAlarm(UIALARM_QUESTION);
190
191 while (true)
192 {
193 if (!StdinRedirected)
194 if (Type==UIPASSWORD_GLOBAL)
195 eprintf(L"\n%s: ",St(MAskPsw));
196 else
197 eprintf(St(MAskPswFor),FileName);
198
199 wchar PlainPsw[MAXPASSWORD];
200 GetPasswordText(PlainPsw,ASIZE(PlainPsw));
201 if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL)
202 return false;
203 if (!StdinRedirected && Type==UIPASSWORD_GLOBAL)
204 {
205 eprintf(St(MReAskPsw));
206 wchar CmpStr[MAXPASSWORD];
207 GetPasswordText(CmpStr,ASIZE(CmpStr));
208 if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0)
209 {
210 eprintf(St(MNotMatchPsw));
211 cleandata(PlainPsw,sizeof(PlainPsw));
212 cleandata(CmpStr,sizeof(CmpStr));
213 continue;
214 }
215 cleandata(CmpStr,sizeof(CmpStr));
216 }
217 Password->Set(PlainPsw);
218 cleandata(PlainPsw,sizeof(PlainPsw));
219 break;
220 }
221 return true;
222 }
223 #endif
224
225
226 #ifndef SILENT
getwstr(wchar * str,size_t n)227 bool getwstr(wchar *str,size_t n)
228 {
229 // Print buffered prompt title function before waiting for input.
230 fflush(stderr);
231
232 *str=0;
233 #if defined(_WIN_ALL)
234 // fgetws does not work well with non-English text in Windows,
235 // so we do not use it.
236 if (StdinRedirected) // ReadConsole does not work if redirected.
237 {
238 // fgets does not work well with pipes in Windows in our test.
239 // Let's use files.
240 Array<char> StrA(n*4); // Up to 4 UTF-8 characters per wchar_t.
241 File SrcFile;
242 SrcFile.SetHandleType(FILE_HANDLESTD);
243 int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1);
244 if (ReadSize<=0)
245 {
246 // Looks like stdin is a null device. We can enter to infinite loop
247 // calling Ask(), so let's better exit.
248 ErrHandler.Exit(RARX_USERBREAK);
249 }
250 StrA[ReadSize]=0;
251
252 // We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR,
253 // such as send passwords, we get OEM encoding by default, unless we
254 // use "chcp" in console. But we avoid OEM to ANSI conversion,
255 // because we also want to handle ANSI files redirection correctly,
256 // like "rar ... < ansifile.txt".
257 CharToWide(&StrA[0],str,n);
258 cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords.
259 }
260 else
261 {
262 DWORD ReadSize=0;
263 if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0)
264 return false;
265 str[ReadSize]=0;
266 }
267 #else
268 if (fgetws(str,n,stdin)==NULL)
269 ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop.
270 #endif
271 RemoveLF(str);
272 return true;
273 }
274 #endif
275
276
277 #ifndef SILENT
278 // We allow this function to return 0 in case of invalid input,
279 // because it might be convenient to press Enter to some not dangerous
280 // prompts like "insert disk with next volume". We should call this function
281 // again in case of 0 in dangerous prompt such as overwriting file.
Ask(const wchar * AskStr)282 int Ask(const wchar *AskStr)
283 {
284 uiAlarm(UIALARM_QUESTION);
285
286 const int MaxItems=10;
287 wchar Item[MaxItems][40];
288 int ItemKeyPos[MaxItems],NumItems=0;
289
290 for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_'))
291 {
292 wchar *CurItem=Item[NumItems];
293 wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0]));
294 wchar *EndItem=wcschr(CurItem,'_');
295 if (EndItem!=NULL)
296 *EndItem=0;
297 int KeyPos=0,CurKey;
298 while ((CurKey=CurItem[KeyPos])!=0)
299 {
300 bool Found=false;
301 for (int I=0;I<NumItems && !Found;I++)
302 if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey))
303 Found=true;
304 if (!Found && CurKey!=' ')
305 break;
306 KeyPos++;
307 }
308 ItemKeyPos[NumItems]=KeyPos;
309 NumItems++;
310 }
311
312 for (int I=0;I<NumItems;I++)
313 {
314 eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", ");
315 int KeyPos=ItemKeyPos[I];
316 for (int J=0;J<KeyPos;J++)
317 eprintf(L"%c",Item[I][J]);
318 eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]);
319 }
320 eprintf(L" ");
321 wchar Str[50];
322 getwstr(Str,ASIZE(Str));
323 wchar Ch=toupperw(Str[0]);
324 for (int I=0;I<NumItems;I++)
325 if (Ch==Item[I][ItemKeyPos[I]])
326 return I+1;
327 return 0;
328 }
329 #endif
330
331
IsCommentUnsafe(const wchar * Data,size_t Size)332 static bool IsCommentUnsafe(const wchar *Data,size_t Size)
333 {
334 for (size_t I=0;I<Size;I++)
335 if (Data[I]==27 && Data[I+1]=='[')
336 for (size_t J=I+2;J<Size;J++)
337 {
338 // Return true for <ESC>[{key};"{string}"p used to redefine
339 // a keyboard key on some terminals.
340 if (Data[J]=='\"')
341 return true;
342 if (!IsDigit(Data[J]) && Data[J]!=';')
343 break;
344 }
345 return false;
346 }
347
348
OutComment(const wchar * Comment,size_t Size)349 void OutComment(const wchar *Comment,size_t Size)
350 {
351 if (IsCommentUnsafe(Comment,Size))
352 return;
353 const size_t MaxOutSize=0x400;
354 for (size_t I=0;I<Size;I+=MaxOutSize)
355 {
356 wchar Msg[MaxOutSize+1];
357 size_t CopySize=Min(MaxOutSize,Size-I);
358 wcsncpy(Msg,Comment+I,CopySize);
359 Msg[CopySize]=0;
360 mprintf(L"%s",Msg);
361 }
362 mprintf(L"\n");
363 }
364