1 /** @file 2 Write to an Interactive I/O Output device. 3 4 The functions assume that isatty() is TRUE at the time they are called. 5 Since the UEFI console is a WIDE character device, these functions do all 6 processing using wide characters. 7 8 It is the responsibility of the caller, or higher level function, to perform 9 any necessary translation between wide and narrow characters. 10 11 Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR> 12 This program and the accompanying materials are licensed and made available 13 under the terms and conditions of the BSD License which accompanies this 14 distribution. The full text of the license may be found at 15 http://opensource.org/licenses/bsd-license.php. 16 17 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 18 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 19 **/ 20 #include <Uefi.h> 21 22 #include <LibConfig.h> 23 24 #include <assert.h> 25 #include <errno.h> 26 #include <sys/termios.h> 27 #include <Device/IIO.h> 28 29 static wchar_t Spaces[] = L" "; // Spaces for expanding TABs 30 31 #define MAX_TAB_WIDTH ((int)(sizeof(Spaces) / sizeof(wchar_t)) - 1) 32 33 #define MAX_EXPANSION 3 34 35 /** Process and buffer one character for output. 36 37 @param[in] filp Pointer to a file descriptor structure. 38 @param[out] OBuf Pointer to the Output Buffer FIFO. 39 @param[in] InCh The wide character to process. 40 41 @retval <0 An error occurred. Reason is in errno. 42 * EINVAL The pointer to the IIO object is NULL. 43 * ENOSPC The OBuf FIFO is full. 44 45 @retval 0 A character was input but not placed in the output buffer. 46 47 @retval >0 The number of characters buffered. Normally 1, or 2. 48 If a character is discarded because of flag settings, a 49 1 will be returned. 50 **/ 51 ssize_t 52 IIO_WriteOne(struct __filedes *filp, cFIFO *OBuf, wchar_t InCh) 53 { 54 cIIO *This; 55 struct termios *Termio; 56 tcflag_t OFlag; 57 ssize_t RetVal; 58 wchar_t wc[MAX_EXPANSION]; // Sub-buffer for conversions 59 wchar_t *wcb; // Pointer to either wc or spaces 60 int numW = 0; // Wide characters placed in OBuf 61 INT32 TabWidth; // Each TAB expands into this number of spaces 62 UINT32 CurColumn; // Current cursor column on the screen 63 UINT32 CurRow; // Current cursor row on the screen 64 UINT32 PrevColumn; // Previous column. Used to detect wrapping. 65 UINT32 AdjColumn; // Current cursor column on the screen 66 67 RetVal = -1; 68 wcb = wc; 69 This = filp->devdata; 70 if((This != NULL) && (OBuf->FreeSpace(OBuf, AsElements) >= MAX_EXPANSION)) { 71 Termio = &This->Termio; 72 OFlag = Termio->c_oflag; 73 TabWidth = (INT32)This->Termio.c_cc[VTABLEN]; 74 if(TabWidth > MAX_TAB_WIDTH) { 75 TabWidth = MAX_TAB_WIDTH; 76 } 77 CurColumn = This->CurrentXY.Column; 78 CurRow = This->CurrentXY.Row; 79 80 numW = 1; // The majority of characters buffer one character 81 AdjColumn = 0; 82 if(OFlag & OPOST) { 83 /* Perform output processing */ 84 switch(InCh) { 85 case CHAR_TAB: //{{ 86 if(OFlag & OXTABS) { 87 if(TabWidth > 0) { 88 int SpaceIndex; 89 90 SpaceIndex = CurColumn % TabWidth; // Number of spaces after a Tab Stop 91 numW = TabWidth - SpaceIndex; // Number of spaces to the next Tab Stop 92 SpaceIndex = MAX_TAB_WIDTH - numW; // Index into the Spaces array 93 wcb = &Spaces[SpaceIndex]; // Point to the appropriate number of spaces 94 } 95 else { 96 wc[0] = L' '; 97 } 98 AdjColumn = numW; 99 } 100 else { 101 wc[0] = InCh; // Send the TAB itself - assumes that it does not move cursor. 102 } 103 break; //}} 104 105 case CHAR_CARRIAGE_RETURN: //{{ 106 if((OFlag & OCRNL) == 0) { 107 if((OFlag & ONLRET) == 0) { 108 numW = 0; /* Discard the CR */ 109 // Cursor doesn't move 110 } 111 else { 112 wc[0] = CHAR_CARRIAGE_RETURN; 113 CurColumn = 0; 114 } 115 break; 116 } 117 else { 118 InCh = CHAR_LINEFEED; 119 } //}} 120 // Fall through to the NL case 121 case CHAR_LINEFEED: //{{ 122 if(OFlag & ONLCR) { 123 wc[0] = CHAR_CARRIAGE_RETURN; 124 wc[1] = CHAR_LINEFEED; 125 numW = 2; 126 CurColumn = 0; 127 } 128 break; //}} 129 130 case CHAR_BACKSPACE: //{{ 131 if(CurColumn > 0) { 132 wc[0] = CHAR_BACKSPACE; 133 CurColumn = (UINT32)ModuloDecrement(CurColumn, (UINT32)This->MaxColumn); 134 } 135 else { 136 numW = 0; // Discard the backspace if in column 0 137 } 138 break; //}} 139 140 case CHAR_EOT: //{{ 141 if(OFlag & ONOEOT) { 142 numW = 0; // Discard the EOT character 143 // Cursor doesn't move 144 break; 145 } //}} 146 // Fall through to default in order to potentially output "^D" 147 default: //{{ 148 if((InCh >= 0) && (InCh < L' ')) { 149 // InCh contains a control character 150 if(OFlag & OCTRL) { 151 wc[1] = InCh + L'@'; 152 wc[0] = L'^'; 153 numW = 2; 154 AdjColumn = 2; 155 } 156 else { 157 numW = 0; // Discard. Not a UEFI supported control character. 158 } 159 } 160 else { 161 // Regular printing character 162 wc[0] = InCh; 163 AdjColumn = 1; 164 } 165 break; //}} 166 } 167 if(numW < MAX_EXPANSION) { 168 wc[numW] = 0; // Terminate the sub-buffer 169 } 170 if(AdjColumn != 0) { 171 // Adjust the cursor position 172 PrevColumn = CurColumn; 173 CurColumn = ModuloAdd(PrevColumn, AdjColumn, (UINT32)This->MaxColumn); 174 if(CurColumn < PrevColumn) { 175 // We must have wrapped, so we are on the next Row 176 ++CurRow; 177 if(CurRow >= This->MaxRow) { 178 // The screen has scrolled so need to adjust Initial location. 179 --This->InitialXY.Row; // Initial row has moved up one 180 CurRow = (UINT32)(This->MaxRow - 1); // We stay on the bottom row 181 } 182 } 183 } 184 This->CurrentXY.Column = CurColumn; 185 This->CurrentXY.Row = CurRow; 186 } 187 else { 188 // Output processing disabled -- RAW output mode 189 wc[0] = InCh; 190 wc[1] = 0; 191 } 192 // Put the character(s) into the output buffer 193 if(numW > 0) { 194 (void)OBuf->Write(OBuf, (const void *)wcb, (size_t)numW); 195 } 196 RetVal = numW; 197 } 198 else { 199 if(This == NULL) { 200 errno = EINVAL; 201 } 202 else { 203 errno = ENOSPC; 204 } 205 } 206 return RetVal; 207 } 208