Home | History | Annotate | Download | only in InteractiveIO
      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
     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
    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
    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
    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
    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
    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