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