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