| /** @file | |
| Write to an Interactive I/O Output device. | |
| The functions assume that isatty() is TRUE at the time they are called. | |
| Since the UEFI console is a WIDE character device, these functions do all | |
| processing using wide characters. | |
| It is the responsibility of the caller, or higher level function, to perform | |
| any necessary translation between wide and narrow characters. | |
| Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials are licensed and made available | |
| under the terms and conditions of the BSD License which accompanies this | |
| distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <Uefi.h> | |
| #include <LibConfig.h> | |
| #include <assert.h> | |
| #include <errno.h> | |
| #include <sys/termios.h> | |
| #include <Device/IIO.h> | |
| static wchar_t Spaces[] = L" "; // Spaces for expanding TABs | |
| #define MAX_TAB_WIDTH ((int)(sizeof(Spaces) / sizeof(wchar_t)) - 1) | |
| #define MAX_EXPANSION 3 | |
| /** Process and buffer one character for output. | |
| @param[in] filp Pointer to a file descriptor structure. | |
| @param[out] OBuf Pointer to the Output Buffer FIFO. | |
| @param[in] InCh The wide character to process. | |
| @retval <0 An error occurred. Reason is in errno. | |
| * EINVAL The pointer to the IIO object is NULL. | |
| * ENOSPC The OBuf FIFO is full. | |
| @retval 0 A character was input but not placed in the output buffer. | |
| @retval >0 The number of characters buffered. Normally 1, or 2. | |
| If a character is discarded because of flag settings, a | |
| 1 will be returned. | |
| **/ | |
| ssize_t | |
| IIO_WriteOne(struct __filedes *filp, cFIFO *OBuf, wchar_t InCh) | |
| { | |
| cIIO *This; | |
| struct termios *Termio; | |
| tcflag_t OFlag; | |
| ssize_t RetVal; | |
| wchar_t wc[MAX_EXPANSION]; // Sub-buffer for conversions | |
| wchar_t *wcb; // Pointer to either wc or spaces | |
| int numW = 0; // Wide characters placed in OBuf | |
| INT32 TabWidth; // Each TAB expands into this number of spaces | |
| UINT32 CurColumn; // Current cursor column on the screen | |
| UINT32 CurRow; // Current cursor row on the screen | |
| UINT32 PrevColumn; // Previous column. Used to detect wrapping. | |
| UINT32 AdjColumn; // Current cursor column on the screen | |
| RetVal = -1; | |
| wcb = wc; | |
| This = filp->devdata; | |
| if((This != NULL) && (OBuf->FreeSpace(OBuf, AsElements) >= MAX_EXPANSION)) { | |
| Termio = &This->Termio; | |
| OFlag = Termio->c_oflag; | |
| TabWidth = (INT32)This->Termio.c_cc[VTABLEN]; | |
| if(TabWidth > MAX_TAB_WIDTH) { | |
| TabWidth = MAX_TAB_WIDTH; | |
| } | |
| CurColumn = This->CurrentXY.Column; | |
| CurRow = This->CurrentXY.Row; | |
| numW = 1; // The majority of characters buffer one character | |
| AdjColumn = 0; | |
| if(OFlag & OPOST) { | |
| /* Perform output processing */ | |
| switch(InCh) { | |
| case CHAR_TAB: //{{ | |
| if(OFlag & OXTABS) { | |
| if(TabWidth > 0) { | |
| int SpaceIndex; | |
| SpaceIndex = CurColumn % TabWidth; // Number of spaces after a Tab Stop | |
| numW = TabWidth - SpaceIndex; // Number of spaces to the next Tab Stop | |
| SpaceIndex = MAX_TAB_WIDTH - numW; // Index into the Spaces array | |
| wcb = &Spaces[SpaceIndex]; // Point to the appropriate number of spaces | |
| } | |
| else { | |
| wc[0] = L' '; | |
| } | |
| AdjColumn = numW; | |
| } | |
| else { | |
| wc[0] = InCh; // Send the TAB itself - assumes that it does not move cursor. | |
| } | |
| break; //}} | |
| case CHAR_CARRIAGE_RETURN: //{{ | |
| if((OFlag & OCRNL) == 0) { | |
| if((OFlag & ONLRET) == 0) { | |
| numW = 0; /* Discard the CR */ | |
| // Cursor doesn't move | |
| } | |
| else { | |
| wc[0] = CHAR_CARRIAGE_RETURN; | |
| CurColumn = 0; | |
| } | |
| break; | |
| } | |
| else { | |
| InCh = CHAR_LINEFEED; | |
| } //}} | |
| // Fall through to the NL case | |
| case CHAR_LINEFEED: //{{ | |
| if(OFlag & ONLCR) { | |
| wc[0] = CHAR_CARRIAGE_RETURN; | |
| wc[1] = CHAR_LINEFEED; | |
| numW = 2; | |
| CurColumn = 0; | |
| } | |
| break; //}} | |
| case CHAR_BACKSPACE: //{{ | |
| if(CurColumn > 0) { | |
| wc[0] = CHAR_BACKSPACE; | |
| CurColumn = (UINT32)ModuloDecrement(CurColumn, (UINT32)This->MaxColumn); | |
| } | |
| else { | |
| numW = 0; // Discard the backspace if in column 0 | |
| } | |
| break; //}} | |
| case CHAR_EOT: //{{ | |
| if(OFlag & ONOEOT) { | |
| numW = 0; // Discard the EOT character | |
| // Cursor doesn't move | |
| break; | |
| } //}} | |
| // Fall through to default in order to potentially output "^D" | |
| default: //{{ | |
| if((InCh >= 0) && (InCh < L' ')) { | |
| // InCh contains a control character | |
| if(OFlag & OCTRL) { | |
| wc[1] = InCh + L'@'; | |
| wc[0] = L'^'; | |
| numW = 2; | |
| AdjColumn = 2; | |
| } | |
| else { | |
| numW = 0; // Discard. Not a UEFI supported control character. | |
| } | |
| } | |
| else { | |
| // Regular printing character | |
| wc[0] = InCh; | |
| AdjColumn = 1; | |
| } | |
| break; //}} | |
| } | |
| if(numW < MAX_EXPANSION) { | |
| wc[numW] = 0; // Terminate the sub-buffer | |
| } | |
| if(AdjColumn != 0) { | |
| // Adjust the cursor position | |
| PrevColumn = CurColumn; | |
| CurColumn = ModuloAdd(PrevColumn, AdjColumn, (UINT32)This->MaxColumn); | |
| if(CurColumn < PrevColumn) { | |
| // We must have wrapped, so we are on the next Row | |
| ++CurRow; | |
| if(CurRow >= This->MaxRow) { | |
| // The screen has scrolled so need to adjust Initial location. | |
| --This->InitialXY.Row; // Initial row has moved up one | |
| CurRow = (UINT32)(This->MaxRow - 1); // We stay on the bottom row | |
| } | |
| } | |
| } | |
| This->CurrentXY.Column = CurColumn; | |
| This->CurrentXY.Row = CurRow; | |
| } | |
| else { | |
| // Output processing disabled -- RAW output mode | |
| wc[0] = InCh; | |
| wc[1] = 0; | |
| } | |
| // Put the character(s) into the output buffer | |
| if(numW > 0) { | |
| (void)OBuf->Write(OBuf, (const void *)wcb, (size_t)numW); | |
| } | |
| RetVal = numW; | |
| } | |
| else { | |
| if(This == NULL) { | |
| errno = EINVAL; | |
| } | |
| else { | |
| errno = ENOSPC; | |
| } | |
| } | |
| return RetVal; | |
| } |