-
Notifications
You must be signed in to change notification settings - Fork 1
/
CUI.hs
281 lines (228 loc) · 9.78 KB
/
CUI.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
{-# OPTIONS_GHC -cpp #-}
----------------------------------------------------------------------------------------------------
---- Èíôîðìèðîâàíèå ïîëüçîâàòåëÿ î õîäå âûïîëíåíèÿ ïðîãðàììû (CUI - Console User Interface). ------
----------------------------------------------------------------------------------------------------
module CUI where
import Prelude hiding (catch)
import Control.Monad
import Control.Concurrent
import Control.OldException
import Data.Char
import Data.IORef
import Foreign
import Foreign.C
import Numeric (showFFloat)
import System.CPUTime (getCPUTime)
import System.IO
import System.Time
#ifdef FREEARC_WIN
import System.Win32.Types
#endif
#ifdef FREEARC_UNIX
import System.Posix.IO
import System.Posix.Terminal
#endif
import Utils
import Charsets
import Errors
import Files
import FileInfo
import Options
import UIBase
----------------------------------------------------------------------------------------------------
---- Îòîáðàæåíèå èíäèêàòîðà ïðîãðåññà --------------------------------------------------------------
----------------------------------------------------------------------------------------------------
-- |Çàïóñêàåò background thread äëÿ âûâîäà èíäèêàòîðà ïðîãðåññà
guiStartProgram = do
-- Îáíîâëÿåì èíäèêàòîð ïðîãðåññà è çàãîëîâîê îêíà ðàç â 0.5 ñåêóíäû
indicatorThread 0.5 $ \updateMode indicator indType title b bytes total processed p -> do
setConsoleTitle title
taskbar_SetProgressValue (i bytes) (i total)
withConsoleAccess $ do
myPutStr$ back_percents indicator ++ p
myFlushStdout
-- |Âûçûâàåòñÿ â íà÷àëå îáðàáîòêè àðõèâà
guiStartArchive = doNothing0
-- |Îòìåòèòü íà÷àëî óïàêîâêè èëè ðàñïàêîâêè äàííûõ
guiStartProcessing = doNothing0
-- |Íà÷àëî ñëåäóþùåãî òîìà àðõèâà
guiStartVolume filename = doNothing0
-- |Âûçûâàåòñÿ â íà÷àëå îáðàáîòêè ôàéëà
guiStartFile = do
command <- val ref_command
when (opt_indicator command == "2") $ do
syncUI $ do
uiSuspendProgressIndicator
(msg,filename) <- val uiMessage
imsg <- i18n (msg ||| msgDo(cmd_name command))
myPutStrLn ""
myPutStr$ left_justify 72 (" "++format imsg filename)
uiResumeProgressIndicator
hFlush stdout
-- |Òåêóùèé îáú¸ì èñõîäíûõ/ñæàòûõ äàííûõ
guiUpdateProgressIndicator = doNothing0
-- |Ïðèîñòàíîâèòü âûâîä èíäèêàòîðà ïðîãðåññà è ñòåðåòü åãî ñëåäû
uiSuspendProgressIndicator = do
aProgressIndicatorEnabled =: False
(indicator, indType, arcname, direction, b, bytes', total') <- val aProgressIndicatorState
myPutStr$ clear_percents indicator
myFlushStdout
-- |Âîçîáíîâèòü âûâîä èíäèêàòîðà ïðîãðåññà è âûâåñòè åãî òåêóùåå çíà÷åíèå
uiResumeProgressIndicator = do
(indicator, indType, arcname, direction, b :: Rational, bytes', total') <- val aProgressIndicatorState
bytes <- bytes' (round b); total <- total'
myPutStr$ percents indicator bytes total
myFlushStdout
aProgressIndicatorEnabled =: True
-- |Ñäåëàòü ïàóçó â âûïîëíåíèè ïðîãðàììû
guiPauseAtEnd = do
withoutEcho getHiddenChar
return ()
-- |Çàâåðøèòü âûïîëíåíèå ïðîãðàììû
guiDoneProgram = do
return ()
-- |Pause progress indicator & timing while dialog runs
pauseEverything = pauseTiming . pauseTaskbar
{-# NOINLINE guiStartProgram #-}
{-# NOINLINE guiStartFile #-}
{-# NOINLINE uiSuspendProgressIndicator #-}
{-# NOINLINE uiResumeProgressIndicator #-}
{-# NOINLINE guiDoneProgram #-}
----------------------------------------------------------------------------------------------------
---- Çàïðîñû ê ïîëüçîâàòåëþ ("Ïåðåçàïèñàòü ôàéë?" è ò.ï.) ------------------------------------------
----------------------------------------------------------------------------------------------------
-- |Çàïðîñ î ïåðåçàïèñè ôàéëà
askOverwrite filename _ _ _ = ask ("Overwrite " ++ filename)
{-# NOINLINE askOverwrite #-}
-- |Îáùèé ìåõàíèçì äëÿ âûäà÷è çàïðîñîâ ê ïîëüçîâàòåëþ
ask question ref_answer answer_on_u = do
old_answer <- val ref_answer
new_answer <- case old_answer of
"a" -> return old_answer
"u" -> return old_answer
"s" -> return old_answer
_ -> ask_user question
ref_answer =: new_answer
case new_answer of
"u" -> return answer_on_u
_ -> return (new_answer `elem` ["y","a"])
-- |Ñîáñòâåííî îáùåíèå ñ ïîëüçîâàòåëåì ïðîèñõîäèò çäåñü
ask_user question = syncUI $ pauseEverything go where
go = do myPutStr$ "\n "++question++"?\n "++commented_answers++"? "
hFlush stdout
answer <- getLine >>== strLower
when (answer=="q") $ do
terminateOperation
if (answer `elem` (split '/' valid_answers))
then return answer
else myPutStr askHelp >> go
-- |Ïîäñêàçêà, âûâîäèìàÿ íà ýêðàí ïðè íåäîïóñòèìîì îòâåòå
askHelp = unlines [ " Valid answers are:"
, " y - yes"
, " n - no"
, " a - always, answer yes to all remaining queries"
, " s - skip, answer no to all remaining queries"
, " u - update remaining files (yes for each extracted file that is newer than file on disk)"
, " q - quit program"
]
commented_answers = "(Y)es / (N)o / (A)lways / (S)kip all / (U)pdate all / (Q)uit"
valid_answers = "y/n/a/s/u/q"
----------------------------------------------------------------------------------------------------
---- Çàïðîñ ïàðîëåé --------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
ask_passwords = (ask_encryption_password, ask_decryption_password, bad_decryption_password)
-- |Ïå÷àòàåò ñîîáùåíèå î òîì, ÷òî ââåä¸ííûé ïàðîëü íå ïîäõîäèò äëÿ äåøèôðîâàíèÿ
bad_decryption_password = myPutStrLn "Incorrect password"
-- |Çàïðîñ ïàðîëÿ ïðè ñæàòèè. Èñïîëüçóåòñÿ íåâèäèìûé ââîä
-- è çàïðîñ ïîâòîðÿåòñÿ äâàæäû äëÿ èñêëþ÷åíèÿ îøèáêè ïðè åãî ââîäå
ask_encryption_password opt_parseData = syncUI $ pauseEverything $ do
withoutEcho $ go where
go = do myPutStr "\n Enter encryption password:"
hFlush stdout
answer <- getHiddenLine >>== opt_parseData 't'
myPutStr " Reenter encryption password:"
hFlush stdout
answer2 <- getHiddenLine >>== opt_parseData 't'
if answer/=answer2
then do myPutStrLn " Passwords are different. You need to repeat input"
go
else return answer
-- |Çàïðîñ ïàðîëÿ äëÿ ðàñïàêîâêè. Èñïîëüçóåòñÿ íåâèäèìûé ââîä
ask_decryption_password opt_parseData = syncUI $ pauseEverything $ do
withoutEcho $ do
myPutStr "\n Enter decryption password:"
hFlush stdout
getHiddenLine >>== opt_parseData 't'
-- |Ââåñòè ñòðîêó, íå îòîáðàæàÿ å¸ íà ýêðàíå
getHiddenLine = go ""
where go s = do c <- getHiddenChar
case c of
'\r' -> do myPutStrLn ""; return s
'\n' -> do myPutStrLn ""; return s
c -> go (s++[c])
#ifdef FREEARC_WIN
-- |Ïåðåâåñòè êîíñîëü â ðåæèì íåâèäèìîãî ââîäà
withoutEcho = id
-- |Ââåñòè ñèìâîë áåç ýõà
getHiddenChar = liftM (chr.fromEnum) c_getch
foreign import ccall unsafe "conio.h getch"
c_getch :: IO CInt
#else
getHiddenChar = getChar
withoutEcho action = do
let setAttr attr = setTerminalAttributes stdInput attr Immediately
disableEcho = do origAttr <- getTerminalAttributes stdInput
setAttr$ origAttr.$ flip withMode ProcessInput
.$ flip withoutMode EnableEcho
.$ flip withMode KeyboardInterrupts
.$ flip withoutMode IgnoreBreak
.$ flip withMode InterruptOnBreak
return origAttr
--
bracketCtrlBreak "restoreEcho" disableEcho setAttr (\_ -> action)
#endif
{-# NOINLINE ask_passwords #-}
----------------------------------------------------------------------------------------------------
---- Ââîä/âûâîä êîììåíòàðèåâ ê àðõèâó -------------------------------------------------------------
----------------------------------------------------------------------------------------------------
-- |Âûâåñòè íà ýêðàí êîììåíòàðèé ê àðõèâó
uiPrintArcComment arcComment = do
when (arcComment>"") $ do
myPutStrLn arcComment
-- |Ââåñòè ñ stdin êîììåíòàðèé ê àðõèâó
uiInputArcComment old_comment = syncUI $ pauseEverything $ do
myPutStrLn "Enter archive comment, ending with \".\" on separate line:"
hFlush stdout
let go xs = do line <- myGetLine
if line/="."
then go (line:xs)
else return$ joinWith "\n" $ reverse xs
--
go []
{-# NOINLINE uiPrintArcComment #-}
{-# NOINLINE uiInputArcComment #-}
----------------------------------------------------------------------------------------------------
----- External functions ---------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
#ifdef FREEARC_WIN
type TString = Ptr TCHAR
#else
withTString = withCString
type TString = CString
#endif
-- |Set console title
setConsoleTitle title = do
withTString title c_SetConsoleTitle
-- |Set console title (external)
foreign import ccall safe "Environment.h EnvSetConsoleTitle"
c_SetConsoleTitle :: TString -> IO ()
-- |Reset console title
foreign import ccall safe "Environment.h EnvResetConsoleTitle"
resetConsoleTitle :: IO ()
-- |Synchronize console access
withConsoleAccess = bracket_ c_SynchronizeConio_Enter c_SynchronizeConio_Leave
-- |Enter & leave console access mutex
foreign import ccall safe "Compression/External/C_External.h SynchronizeConio_Enter"
c_SynchronizeConio_Enter :: IO ()
foreign import ccall safe "Compression/External/C_External.h SynchronizeConio_Leave"
c_SynchronizeConio_Leave :: IO ()