1 /** @file
2 Definitions for the Interactive IO library.
3
4 The functions assume that isatty() is TRUE at the time they are called.
5
6 Copyright (c) 2016, Daryl McDaniel. All rights reserved.<BR>
7 Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials are licensed and made available
9 under the terms and conditions of the BSD License which accompanies this
10 distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php.
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 **/
16 #include <Uefi.h>
17 #include <Library/MemoryAllocationLib.h>
18
19 #include <LibConfig.h>
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <sys/syslimits.h>
24 #include <sys/termios.h>
25 #include <Device/IIO.h>
26 #include <MainData.h>
27 #include "IIOutilities.h"
28 #include "IIOechoCtrl.h"
29
30 // Instrumentation used for debugging
31 #define IIO_C_DEBUG 0 ///< Set to 1 to enable instrumentation, 0 to disable
32
33 #if IIO_C_DEBUG
34 static volatile size_t IIO_C_WRemainder = 0; ///< Characters in Out buffer after IIO_Write
35 static volatile size_t IIO_C_RRemainder = 0; ///< Characters in In buffer after IIO_Read
36
37 #define W_INSTRUMENT IIO_C_WRemainder =
38 #define R_INSTRUMENT IIO_C_RRemainder =
39 #else // ! IIO_C_DEBUG -- don't instrument code
40 #define W_INSTRUMENT (void)
41 #define R_INSTRUMENT (void)
42 #endif // IIO_C_DEBUG
43
44 /** Read from an Interactive IO device.
45
46 NOTE: If _S_IWTTY is set, the internal buffer contains WIDE characters.
47 They will need to be converted to MBCS when returned.
48
49 Input is line buffered if ICANON is set,
50 otherwise MIN determines how many characters to input.
51 Currently MIN is always zero, meaning 0 or 1 character is input in
52 noncanonical mode.
53
54 @param[in] filp Pointer to the descriptor of the device (file) to be read.
55 @param[in] BufferSize Maximum number of bytes to be returned to the caller.
56 @param[out] Buffer Pointer to the buffer where the input is to be stored.
57
58 @retval -1 An error occurred. No data is available.
59 @retval 0 No data was available. Try again later.
60 @retval >0 The number of bytes consumed by the returned data.
61 **/
62 static
63 ssize_t
64 EFIAPI
IIO_Read(struct __filedes * filp,size_t BufferSize,VOID * Buffer)65 IIO_Read(
66 struct __filedes *filp,
67 size_t BufferSize,
68 VOID *Buffer
69 )
70 {
71 cIIO *This;
72 ssize_t NumRead;
73 tcflag_t Flags;
74 size_t XlateSz;
75 size_t Needed;
76
77 NumRead = -1;
78 This = filp->devdata;
79 if(This != NULL) {
80 Flags = This->Termio.c_lflag;
81 if(Flags & ICANON) {
82 NumRead = IIO_CanonRead(filp);
83 }
84 else {
85 NumRead = IIO_NonCanonRead(filp);
86 }
87 // At this point, the input has been accumulated in the input buffer.
88 if(filp->f_iflags & _S_IWTTY) {
89 // Data in InBuf is wide characters. Convert to MBCS
90 // First, convert into a linear buffer
91 NumRead = This->InBuf->Copy(This->InBuf, gMD->UString2, (INT32)UNICODE_STRING_MAX-1);
92 gMD->UString2[NumRead] = 0; // Ensure that the buffer is terminated
93 // Determine the needed space
94 XlateSz = EstimateWtoM((const wchar_t *)gMD->UString2, BufferSize, &Needed);
95
96 // Now translate this into MBCS in Buffer
97 NumRead = wcstombs((char *)Buffer, (const wchar_t *)gMD->UString2, XlateSz);
98
99 // Consume the translated characters
100 (void) This->InBuf->Flush(This->InBuf, Needed);
101 }
102 else {
103 // Data in InBuf is narrow characters. Use verbatim.
104 NumRead = This->InBuf->Read(This->InBuf, Buffer, (INT32)BufferSize);
105 }
106 #if IIO_C_DEBUG
107 IIO_C_RRemainder = This->InBuf->Count(This->InBuf, AsElements);
108 #endif // IIO_C_DEBUG
109 }
110 return NumRead;
111 }
112
113 /** Handle write to a Terminal (Interactive) device.
114
115 Processes characters from buffer buf and writes them to the Terminal device
116 specified by filp.
117
118 The parameter buf points to a MBCS string to be output. This is processed
119 and buffered one character at a time by IIO_WriteOne() which handles TAB
120 expansion, NEWLINE to CARRIAGE_RETURN + NEWLINE expansion, as well as
121 basic line editing functions. The number of characters actually written to
122 the output device will seldom equal the number of characters consumed from
123 buf.
124
125 In this implementation, all of the special characters processed by
126 IIO_WriteOne() are single-byte characters with values less than 128.
127 (7-bit ASCII or the single-byte UTF-8 characters)
128
129 Every byte that is not one of the recognized special characters is passed,
130 unchanged, to the Terminal device.
131
132 @param[in] filp Pointer to a file descriptor structure.
133 @param[in] buf Pointer to the MBCS string to be output.
134 @param[in] N Number of bytes in buf.
135
136 @retval >=0 Number of bytes consumed from buf and sent to the
137 Terminal device.
138 **/
139 static
140 ssize_t
141 EFIAPI
IIO_Write(struct __filedes * filp,const char * buf,ssize_t N)142 IIO_Write(
143 struct __filedes *filp,
144 const char *buf,
145 ssize_t N
146 )
147 {
148 cIIO *This;
149 cFIFO *OutBuf;
150 mbstate_t *OutState;
151 char *MbcsPtr;
152 ssize_t NumConsumed;
153 ssize_t NumProc;
154 size_t CharLen;
155 UINTN MaxColumn;
156 UINTN MaxRow;
157 wchar_t OutChar[2]; // Just in case we run into a 4-byte MBCS character
158 int OutMode;
159
160 NumConsumed = -1;
161
162 /* Determine what the current screen size is. Also validates the output device. */
163 OutMode = IIO_GetOutputSize(filp->MyFD, &MaxColumn, &MaxRow);
164
165 This = filp->devdata;
166 if((This != NULL) && (OutMode >= 0)) {
167 if(filp->MyFD == STDERR_FILENO) {
168 OutBuf = This->ErrBuf;
169 OutState = &This->ErrState;
170 }
171 else {
172 OutBuf = This->OutBuf;
173 OutState = &This->OutState;
174 }
175
176 /* Set the maximum screen dimensions. */
177 This->MaxColumn = MaxColumn;
178 This->MaxRow = MaxRow;
179
180 /* Record where the cursor is at the beginning of the Output operation. */
181 (void)IIO_GetCursorPosition(filp->MyFD, &This->InitialXY.Column, &This->InitialXY.Row);
182 This->CurrentXY.Column = This->InitialXY.Column;
183 This->CurrentXY.Row = This->InitialXY.Row;
184
185 NumConsumed = 0;
186 OutChar[0] = (wchar_t)buf[0];
187 while((OutChar[0] != 0) && (NumConsumed < N)) {
188 CharLen = mbrtowc(OutChar, (const char *)&buf[NumConsumed], MB_CUR_MAX, OutState);
189 if (CharLen < 0) { // Encoding Error
190 OutChar[0] = BLOCKELEMENT_LIGHT_SHADE;
191 CharLen = 1; // Consume a byte
192 (void)mbrtowc(NULL, NULL, 1, OutState); // Re-Initialize the conversion state
193 }
194 NumProc = IIO_WriteOne(filp, OutBuf, OutChar[0]);
195 if(NumProc >= 0) {
196 // Successfully processed and buffered one character
197 NumConsumed += CharLen; // Index of start of next character
198 }
199 else {
200 if (errno == ENOSPC) {
201 // Not enough room in OutBuf to hold a potentially expanded character
202 break;
203 }
204 return -1; // Something corrupted and filp->devdata is now NULL
205 }
206 }
207 // At this point, the characters to write are in OutBuf
208 // First, linearize the buffer
209 NumProc = OutBuf->Copy(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
210 gMD->UString[NumProc] = 0; // Ensure that the buffer is terminated
211
212 if(filp->f_iflags & _S_IWTTY) {
213 // Output device expects wide characters, Output what we have
214 NumProc = filp->f_ops->fo_write(filp, NULL, NumProc, gMD->UString);
215
216 // Consume the output characters
217 W_INSTRUMENT OutBuf->Flush(OutBuf, NumProc);
218 }
219 else {
220 // Output device expects narrow characters, convert to MBCS
221 MbcsPtr = (char *)gMD->UString2;
222 // Determine the needed space. NumProc is the number of bytes needed.
223 NumProc = (ssize_t)EstimateWtoM((const wchar_t *)gMD->UString, UNICODE_STRING_MAX * sizeof(wchar_t), &CharLen);
224
225 // Now translate this into MBCS in the buffer pointed to by MbcsPtr.
226 // The returned value, NumProc, is the resulting number of bytes.
227 NumProc = wcstombs(MbcsPtr, (const wchar_t *)gMD->UString, NumProc);
228 MbcsPtr[NumProc] = 0; // Ensure the buffer is terminated
229
230 // Send the MBCS buffer to Output
231 NumProc = filp->f_ops->fo_write(filp, NULL, NumProc, MbcsPtr);
232 // Mark the Mbcs buffer after the last byte actually written
233 MbcsPtr[NumProc] = 0;
234 // Count the CHARACTERS actually sent
235 CharLen = CountMbcsChars(MbcsPtr);
236
237 // Consume the number of output characters actually sent
238 W_INSTRUMENT OutBuf->Flush(OutBuf, CharLen);
239 }
240 }
241 else {
242 if(This == NULL) {
243 errno = EINVAL;
244 }
245 // Otherwise, errno is already set.
246 }
247 return NumConsumed;
248 }
249
250 /** Echo a character to an output device.
251 Performs translation and edit processing depending upon termios flags.
252
253 @param[in] filp A pointer to a file descriptor structure.
254 @param[in] EChar The character to echo.
255 @param[in] EchoIsOK TRUE if the caller has determined that characters
256 should be echoed. Otherwise, just buffer.
257
258 @return Returns the number of characters actually output.
259 **/
260 static
261 ssize_t
262 EFIAPI
IIO_Echo(struct __filedes * filp,wchar_t EChar,BOOLEAN EchoIsOK)263 IIO_Echo(
264 struct __filedes *filp,
265 wchar_t EChar,
266 BOOLEAN EchoIsOK
267 )
268 {
269 cIIO *This;
270 ssize_t NumWritten;
271 cFIFO *OutBuf;
272 char *MbcsPtr;
273 ssize_t NumProc;
274 tcflag_t LFlags;
275
276 NumWritten = -1;
277 This = filp->devdata;
278 if(This != NULL) {
279 OutBuf = This->OutBuf;
280 LFlags = This->Termio.c_lflag & (ECHOK | ECHOE);
281
282 if((EChar >= TtyFunKeyMin) && (EChar < TtyFunKeyMax)) {
283 // A special function key was pressed, buffer it, don't echo, and activate.
284 // Process and buffer the character. May produce multiple characters.
285 NumProc = IIO_EchoOne(filp, EChar, FALSE); // Don't echo this character
286 EChar = CHAR_LINEFEED; // Every line must end with '\n' (legacy)
287 }
288 // Process and buffer the character. May produce multiple characters.
289 NumProc = IIO_EchoOne(filp, EChar, EchoIsOK);
290
291 // At this point, the character(s) to write are in OutBuf
292 // First, linearize the buffer
293 NumWritten = OutBuf->Copy(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
294 gMD->UString[NumWritten] = 0; // Ensure that the buffer is terminated
295
296 if((EChar == IIO_ECHO_KILL) && (LFlags & ECHOE) && EchoIsOK) {
297 // Position the cursor to the start of input.
298 (void)IIO_SetCursorPosition(filp, &This->InitialXY);
299 }
300 // Output the buffer
301 if(filp->f_iflags & _S_IWTTY) {
302 // Output device expects wide characters, Output what we have
303 NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, gMD->UString);
304 }
305 else {
306 // Output device expects narrow characters, convert to MBCS
307 MbcsPtr = (char *)gMD->UString2;
308 // Determine the needed space
309 NumProc = (ssize_t)EstimateWtoM((const wchar_t *)gMD->UString, UNICODE_STRING_MAX * sizeof(wchar_t), NULL);
310
311 // Now translate this into MBCS in Buffer
312 NumWritten = wcstombs(MbcsPtr, (const wchar_t *)gMD->UString, NumProc);
313 MbcsPtr[NumWritten] = 0; // Ensure the buffer is terminated
314
315 // Send the MBCS buffer to Output
316 NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, MbcsPtr);
317 }
318 // Consume the echoed characters
319 (void)OutBuf->Flush(OutBuf, NumWritten);
320
321 if(EChar == IIO_ECHO_KILL) {
322 if(LFlags == ECHOK) {
323 NumWritten = IIO_WriteOne(filp, OutBuf, CHAR_LINEFEED);
324 }
325 else if((LFlags & ECHOE) && EchoIsOK) {
326 // Position the cursor to the start of input.
327 (void)IIO_SetCursorPosition(filp, &This->InitialXY);
328 }
329 NumWritten = 0;
330 }
331 }
332 else {
333 errno = EINVAL;
334 }
335
336 return NumWritten;
337 }
338
339 static
340 void
FifoDelete(cFIFO * Member)341 FifoDelete(cFIFO *Member)
342 {
343 if(Member != NULL) {
344 Member->Delete(Member);
345 }
346 }
347
348 /** Destructor for an IIO instance.
349
350 Releases all resources used by a particular IIO instance.
351 **/
352 static
353 void
354 EFIAPI
IIO_Delete(cIIO * Self)355 IIO_Delete(
356 cIIO *Self
357 )
358 {
359 if(Self != NULL) {
360 FifoDelete(Self->ErrBuf);
361 FifoDelete(Self->OutBuf);
362 FifoDelete(Self->InBuf);
363 if(Self->AttrBuf != NULL) {
364 FreePool(Self->AttrBuf);
365 }
366 FreePool(Self);
367 }
368 }
369
370 /** Constructor for new IIO instances.
371
372 @return Returns NULL or a pointer to a new IIO instance.
373 **/
374 cIIO *
375 EFIAPI
New_cIIO(void)376 New_cIIO(void)
377 {
378 cIIO *IIO;
379 cc_t *TempBuf;
380 int i;
381
382 IIO = (cIIO *)AllocateZeroPool(sizeof(cIIO));
383 if(IIO != NULL) {
384 IIO->InBuf = New_cFIFO(MAX_INPUT, sizeof(wchar_t));
385 IIO->OutBuf = New_cFIFO(MAX_OUTPUT, sizeof(wchar_t));
386 IIO->ErrBuf = New_cFIFO(MAX_OUTPUT, sizeof(wchar_t));
387 IIO->AttrBuf = (UINT8 *)AllocateZeroPool(MAX_OUTPUT);
388
389 if((IIO->InBuf == NULL) || (IIO->OutBuf == NULL) ||
390 (IIO->ErrBuf == NULL) || (IIO->AttrBuf == NULL))
391 {
392 IIO_Delete(IIO);
393 IIO = NULL;
394 }
395 else {
396 IIO->Delete = IIO_Delete;
397 IIO->Read = IIO_Read;
398 IIO->Write = IIO_Write;
399 IIO->Echo = IIO_Echo;
400 }
401 // Initialize Termio member
402 TempBuf = &IIO->Termio.c_cc[0];
403 TempBuf[0] = 8; // Default length for TABs
404 for(i=1; i < NCCS; ++i) {
405 TempBuf[i] = _POSIX_VDISABLE;
406 }
407 TempBuf[VMIN] = 0;
408 TempBuf[VTIME] = 0;
409 IIO->Termio.c_ispeed = B115200;
410 IIO->Termio.c_ospeed = B115200;
411 IIO->Termio.c_iflag = ICRNL;
412 IIO->Termio.c_oflag = OPOST | ONLCR | ONOCR | ONLRET;
413 IIO->Termio.c_cflag = 0;
414 IIO->Termio.c_lflag = ECHO | ECHONL;
415 }
416 return IIO;
417 }
418