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
IIO_WriteOne(struct __filedes * filp,cFIFO * OBuf,wchar_t InCh)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