]> git.proxmox.com Git - mirror_edk2.git/blob - StdLib/LibC/Uefi/InteractiveIO/IIO.c
65b61d9bccde4f4c1e5c46a8cc7ea552dfcf37b4
[mirror_edk2.git] / StdLib / LibC / Uefi / InteractiveIO / IIO.c
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) 2012, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php.
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 **/
15 #include <Uefi.h>
16 #include <Library/MemoryAllocationLib.h>
17
18 #include <LibConfig.h>
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <sys/syslimits.h>
23 #include <sys/termios.h>
24 #include <Device/IIO.h>
25 #include <MainData.h>
26 #include "IIOutilities.h"
27 #include "IIOechoCtrl.h"
28
29 /** Read from an Interactive IO device.
30
31 NOTE: If _S_IWTTY is set, the internal buffer contains WIDE characters.
32 They will need to be converted to MBCS when returned.
33
34 Input is line buffered if ICANON is set,
35 otherwise MIN determines how many characters to input.
36 Currently MIN is always zero, meaning 0 or 1 character is input in
37 noncanonical mode.
38
39 @param[in] filp Pointer to the descriptor of the device (file) to be read.
40 @param[in] BufferSize Maximum number of bytes to be returned to the caller.
41 @param[out] Buffer Pointer to the buffer where the input is to be stored.
42
43 @retval -1 An error occurred. No data is available.
44 @retval 0 No data was available. Try again later.
45 @retval >0 The number of bytes consumed by the returned data.
46 **/
47 static
48 ssize_t
49 EFIAPI
50 IIO_Read(
51 struct __filedes *filp,
52 size_t BufferSize,
53 VOID *Buffer
54 )
55 {
56 cIIO *This;
57 ssize_t NumRead;
58 tcflag_t Flags;
59 size_t XlateSz;
60 size_t Needed;
61
62 NumRead = -1;
63 This = filp->devdata;
64 if(This != NULL) {
65 Flags = This->Termio.c_lflag;
66 if(Flags & ICANON) {
67 NumRead = IIO_CanonRead(filp);
68 }
69 else {
70 NumRead = IIO_NonCanonRead(filp);
71 }
72 // At this point, the input has been accumulated in the input buffer.
73 if(filp->f_iflags & _S_IWTTY) {
74 // Data in InBuf is wide characters. Convert to MBCS
75 // First, convert into a linear buffer
76 NumRead = This->InBuf->Copy(This->InBuf, gMD->UString2, (INT32)UNICODE_STRING_MAX-1);
77 gMD->UString2[NumRead] = 0; // Ensure that the buffer is terminated
78 // Determine the needed space
79 XlateSz = EstimateWtoM((const wchar_t *)gMD->UString2, BufferSize, &Needed);
80
81 // Now translate this into MBCS in Buffer
82 NumRead = wcstombs((char *)Buffer, (const wchar_t *)gMD->UString2, XlateSz);
83
84 // Consume the translated characters
85 (void)This->InBuf->Flush(This->InBuf, Needed);
86 }
87 else {
88 // Data in InBuf is narrow characters. Use verbatim.
89 NumRead = This->InBuf->Read(This->InBuf, Buffer, (INT32)BufferSize);
90 }
91 }
92 return NumRead;
93 }
94
95 /** Process characters from buffer buf and write them to the output device
96 specified by filp.
97
98 @param[in] filp Pointer to a file descriptor structure.
99 @param[in] buf Pointer to the MBCS string to be output.
100 @param[in] N Number of bytes in buf.
101
102 @retval >=0 Number of bytes sent to the output device.
103 **/
104 static
105 ssize_t
106 EFIAPI
107 IIO_Write(
108 struct __filedes *filp,
109 const char *buf,
110 ssize_t N
111 )
112 {
113 cIIO *This;
114 cFIFO *OutBuf;
115 mbstate_t *OutState;
116 char *MbcsPtr;
117 ssize_t NumWritten;
118 ssize_t NumProc;
119 size_t CharLen;
120 UINTN MaxColumn;
121 UINTN MaxRow;
122 wchar_t OutChar[2]; // Just in case we run into 4-byte MBCS character
123 int OutMode;
124
125 errno = 0; // indicate no error as default
126 NumWritten = -1;
127
128 /* Determine what the current screen size is. Also validates the output device. */
129 OutMode = IIO_GetOutputSize(filp->MyFD, &MaxColumn, &MaxRow);
130
131 This = filp->devdata;
132 if((This != NULL) && (OutMode >= 0)) {
133 if(filp->MyFD == STDERR_FILENO) {
134 OutBuf = This->ErrBuf;
135 OutState = &This->ErrState;
136 }
137 else {
138 OutBuf = This->OutBuf;
139 OutState = &This->OutState;
140 }
141
142 /* Set the maximum screen dimensions. */
143 This->MaxColumn = MaxColumn;
144 This->MaxRow = MaxRow;
145
146 /* Record where the cursor is at the beginning of the Output operation. */
147 (void)IIO_GetCursorPosition(filp->MyFD, &This->InitialXY.Column, &This->InitialXY.Row);
148 This->CurrentXY.Column = This->InitialXY.Column;
149 This->CurrentXY.Row = This->InitialXY.Row;
150
151
152 NumWritten = 0;
153 OutChar[0] = (wchar_t)buf[0];
154 while((OutChar[0] != 0) && (NumWritten < N)) {
155 CharLen = mbrtowc(OutChar, (const char *)&buf[NumWritten], MB_CUR_MAX, OutState);
156 NumProc = IIO_WriteOne(filp, OutBuf, OutChar[0]);
157 if(NumProc > 0) {
158 // Successfully processed and buffered one character
159 NumWritten += CharLen; // Index of start of next character
160 }
161 else if(NumProc == -1) {
162 // Encoding Error
163 (void)mbrtowc(NULL, NULL, 1, OutState); // Re-Initialize the conversion state
164 errno = EILSEQ;
165 break;
166 }
167 else {
168 // Last character was incomplete
169 break;
170 }
171 }
172 // At this point, the characters to write are in OutBuf
173 // First, linearize the buffer
174 NumWritten = OutBuf->Copy(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
175 gMD->UString[NumWritten] = 0; // Ensure that the buffer is terminated
176
177 if(filp->f_iflags & _S_IWTTY) {
178 // Output device expects wide characters, Output what we have
179 NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, gMD->UString);
180 }
181 else {
182 // Output device expects narrow characters, convert to MBCS
183 MbcsPtr = (char *)gMD->UString2;
184 // Determine the needed space
185 NumProc = (ssize_t)EstimateWtoM((const wchar_t *)gMD->UString, UNICODE_STRING_MAX * sizeof(wchar_t), &CharLen);
186
187 // Now translate this into MBCS in Buffer
188 NumWritten = wcstombs(MbcsPtr, (const wchar_t *)gMD->UString, NumProc);
189 MbcsPtr[NumWritten] = 0; // Ensure the buffer is terminated
190
191 // Send the MBCS buffer to Output
192 NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, MbcsPtr);
193 }
194 // Consume the translated characters
195 (void)OutBuf->Flush(OutBuf, NumWritten);
196 }
197 else {
198 if(This == NULL) {
199 errno = EINVAL;
200 }
201 // Otherwise, errno is already set.
202 }
203 return NumWritten;
204 }
205
206 /** Echo a character to an output device.
207 Performs translation and edit processing depending upon termios flags.
208
209 @param[in] filp A pointer to a file descriptor structure.
210 @param[in] EChar The character to echo.
211 @param[in] EchoIsOK TRUE if the caller has determined that characters
212 should be echoed. Otherwise, just buffer.
213
214 @return Returns the number of characters actually output.
215 **/
216 static
217 ssize_t
218 EFIAPI
219 IIO_Echo(
220 struct __filedes *filp,
221 wchar_t EChar,
222 BOOLEAN EchoIsOK
223 )
224 {
225 cIIO *This;
226 ssize_t NumWritten;
227 cFIFO *OutBuf;
228 char *MbcsPtr;
229 ssize_t NumProc;
230 tcflag_t LFlags;
231
232 NumWritten = -1;
233 This = filp->devdata;
234 if(This != NULL) {
235 OutBuf = This->OutBuf;
236 LFlags = This->Termio.c_lflag & (ECHOK | ECHOE);
237
238 if((EChar >= TtyFunKeyMin) && (EChar < TtyFunKeyMax)) {
239 // A special function key was pressed, buffer it, don't echo, and activate.
240 // Process and buffer the character. May produce multiple characters.
241 NumProc = IIO_EchoOne(filp, EChar, FALSE); // Don't echo this character
242 EChar = CHAR_LINEFEED; // Every line must end with '\n' (legacy)
243 }
244 // Process and buffer the character. May produce multiple characters.
245 NumProc = IIO_EchoOne(filp, EChar, EchoIsOK);
246
247 // At this point, the character(s) to write are in OutBuf
248 // First, linearize the buffer
249 NumWritten = OutBuf->Copy(OutBuf, gMD->UString, UNICODE_STRING_MAX-1);
250 gMD->UString[NumWritten] = 0; // Ensure that the buffer is terminated
251
252 if((EChar == IIO_ECHO_KILL) && (LFlags & ECHOE) && EchoIsOK) {
253 // Position the cursor to the start of input.
254 (void)IIO_SetCursorPosition(filp, &This->InitialXY);
255 }
256 // Output the buffer
257 if(filp->f_iflags & _S_IWTTY) {
258 // Output device expects wide characters, Output what we have
259 NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, gMD->UString);
260 }
261 else {
262 // Output device expects narrow characters, convert to MBCS
263 MbcsPtr = (char *)gMD->UString2;
264 // Determine the needed space
265 NumProc = (ssize_t)EstimateWtoM((const wchar_t *)gMD->UString, UNICODE_STRING_MAX * sizeof(wchar_t), NULL);
266
267 // Now translate this into MBCS in Buffer
268 NumWritten = wcstombs(MbcsPtr, (const wchar_t *)gMD->UString, NumProc);
269 MbcsPtr[NumWritten] = 0; // Ensure the buffer is terminated
270
271 // Send the MBCS buffer to Output
272 NumWritten = filp->f_ops->fo_write(filp, NULL, NumWritten, MbcsPtr);
273 }
274 // Consume the echoed characters
275 (void)OutBuf->Flush(OutBuf, NumWritten);
276
277 if(EChar == IIO_ECHO_KILL) {
278 if(LFlags == ECHOK) {
279 NumWritten = IIO_WriteOne(filp, OutBuf, CHAR_LINEFEED);
280 }
281 else if((LFlags & ECHOE) && EchoIsOK) {
282 // Position the cursor to the start of input.
283 (void)IIO_SetCursorPosition(filp, &This->InitialXY);
284 }
285 NumWritten = 0;
286 }
287 }
288 else {
289 errno = EINVAL;
290 }
291
292 return NumWritten;
293 }
294
295 static
296 void
297 FifoDelete(cFIFO *Member)
298 {
299 if(Member != NULL) {
300 Member->Delete(Member);
301 }
302 }
303
304 /** Destructor for an IIO instance.
305
306 Releases all resources used by a particular IIO instance.
307 **/
308 static
309 void
310 EFIAPI
311 IIO_Delete(
312 cIIO *Self
313 )
314 {
315 if(Self != NULL) {
316 FifoDelete(Self->ErrBuf);
317 FifoDelete(Self->OutBuf);
318 FifoDelete(Self->InBuf);
319 if(Self->AttrBuf != NULL) {
320 FreePool(Self->AttrBuf);
321 }
322 FreePool(Self);
323 }
324 }
325
326 /** Constructor for new IIO instances.
327
328 @return Returns NULL or a pointer to a new IIO instance.
329 **/
330 cIIO *
331 EFIAPI
332 New_cIIO(void)
333 {
334 cIIO *IIO;
335 cc_t *TempBuf;
336 int i;
337
338 IIO = (cIIO *)AllocateZeroPool(sizeof(cIIO));
339 if(IIO != NULL) {
340 IIO->InBuf = New_cFIFO(MAX_INPUT, sizeof(wchar_t));
341 IIO->OutBuf = New_cFIFO(MAX_OUTPUT, sizeof(wchar_t));
342 IIO->ErrBuf = New_cFIFO(MAX_OUTPUT, sizeof(wchar_t));
343 IIO->AttrBuf = (UINT8 *)AllocateZeroPool(MAX_OUTPUT);
344
345 if((IIO->InBuf == NULL) || (IIO->OutBuf == NULL) ||
346 (IIO->ErrBuf == NULL) || (IIO->AttrBuf == NULL))
347 {
348 IIO_Delete(IIO);
349 IIO = NULL;
350 }
351 else {
352 IIO->Delete = IIO_Delete;
353 IIO->Read = IIO_Read;
354 IIO->Write = IIO_Write;
355 IIO->Echo = IIO_Echo;
356 }
357 // Initialize Termio member
358 TempBuf = &IIO->Termio.c_cc[0];
359 TempBuf[0] = 8; // Default length for TABs
360 for(i=1; i < NCCS; ++i) {
361 TempBuf[i] = _POSIX_VDISABLE;
362 }
363 TempBuf[VMIN] = 0;
364 TempBuf[VTIME] = 0;
365 IIO->Termio.c_ispeed = B115200;
366 IIO->Termio.c_ospeed = B115200;
367 IIO->Termio.c_iflag = ICRNL;
368 IIO->Termio.c_oflag = OPOST | ONLCR | ONOCR | ONLRET;
369 IIO->Termio.c_cflag = 0;
370 IIO->Termio.c_lflag = ECHO | ECHONL;
371 }
372 return IIO;
373 }