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