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, 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   UINT32              AdjRow;       // Current cursor row on the screen
67 
68   RetVal    = -1;
69   wcb       = wc;
70   This      = filp->devdata;
71   if((This != NULL) && (OBuf->FreeSpace(OBuf, AsElements) >= MAX_EXPANSION)) {
72     Termio    = &This->Termio;
73     OFlag     = Termio->c_oflag;
74     TabWidth  = (INT32)This->Termio.c_cc[VTABLEN];
75     if(TabWidth > MAX_TAB_WIDTH) {
76       TabWidth = MAX_TAB_WIDTH;
77     }
78     CurColumn = This->CurrentXY.Column;
79     CurRow    = This->CurrentXY.Row;
80 
81     numW      = 1;          // The majority of characters buffer one character
82     AdjRow    = 0;          // Most characters just cause horizontal movement
83     AdjColumn = 0;
84     if(OFlag & OPOST) {
85       /* Perform output processing */
86       switch(InCh) {
87         case CHAR_TAB:                //{{
88           if(OFlag & OXTABS) {
89             if(TabWidth > 0) {
90               int   SpaceIndex;
91 
92               SpaceIndex = CurColumn % TabWidth;    // Number of spaces after a Tab Stop
93               numW = TabWidth - SpaceIndex;         // Number of spaces to the next Tab Stop
94               SpaceIndex = MAX_TAB_WIDTH - numW;    // Index into the Spaces array
95               wcb = &Spaces[SpaceIndex];            // Point to the appropriate number of spaces
96             }
97             else {
98               wc[0] = L' ';
99             }
100             AdjColumn = numW;
101           }
102           else {
103             wc[0] = InCh;     // Send the TAB itself - assumes that it does not move cursor.
104           }
105           break;                      //}}
106 
107         case CHAR_CARRIAGE_RETURN:    //{{
108           if((OFlag & OCRNL) == 0) {
109             if((OFlag & ONLRET) == 0) {
110               numW = 0;   /* Discard the CR */
111               // Cursor doesn't move
112             }
113             else {
114               wc[0]     = CHAR_CARRIAGE_RETURN;
115               CurColumn = 0;
116             }
117             break;
118           }
119           else {
120             InCh = CHAR_LINEFEED;
121           }                           //}}
122           // Fall through to the NL case
123         case CHAR_LINEFEED:           //{{
124           if(OFlag & ONLCR) {
125             wc[0] = CHAR_CARRIAGE_RETURN;
126             wc[1] = CHAR_LINEFEED;
127             numW  = 2;
128             CurColumn = 0;
129           }
130           AdjRow = 1;
131           break;                      //}}
132 
133         case CHAR_BACKSPACE:          //{{
134           if(CurColumn > 0) {
135             wc[0] = CHAR_BACKSPACE;
136             CurColumn = (UINT32)ModuloDecrement(CurColumn, (UINT32)This->MaxColumn);
137           }
138           else {
139             numW = 0;   // Discard the backspace if in column 0
140           }
141           break;                      //}}
142 
143         case CHAR_EOT:                //{{
144           if(OFlag & ONOEOT) {
145             numW = 0;             // Discard the EOT character
146             // Cursor doesn't move
147             break;
148           }                           //}}
149           // Fall through to default in order to potentially output "^D"
150         default:                      //{{
151           if((InCh >= 0) && (InCh < L' ')) {
152             // InCh contains a control character
153             if(OFlag & OCTRL) {
154               wc[1]     = InCh + L'@';
155               wc[0]     = L'^';
156               numW      = 2;
157               AdjColumn = 2;
158             }
159             else {
160               numW = 0;   // Discard.  Not a UEFI supported control character.
161             }
162           }
163           else {
164             // Regular printing character
165             wc[0]     = InCh;
166             AdjColumn = 1;
167           }
168           break;                      //}}
169       }
170       if(numW < MAX_EXPANSION) {
171         wc[numW] = 0;             // Terminate the sub-buffer
172       }
173       if(AdjColumn != 0) {
174         // Adjust the cursor position
175         PrevColumn = CurColumn;
176         CurColumn = ModuloAdd(PrevColumn, AdjColumn, (UINT32)This->MaxColumn);
177         if(CurColumn < PrevColumn) {
178           // We must have wrapped, so we are on the next Row
179           ++CurRow;
180           if(CurRow >= This->MaxRow) {
181             // The screen has scrolled so need to adjust Initial location.
182             --This->InitialXY.Row;        // Initial row has moved up one
183             CurRow = (UINT32)(This->MaxRow - 1);    // We stay on the bottom row
184           }
185         }
186       }
187       This->CurrentXY.Column  = CurColumn;
188       This->CurrentXY.Row     = CurRow;
189     }
190     else {
191       // Output processing disabled -- RAW output mode
192       wc[0] = InCh;
193       wc[1] = 0;
194     }
195     // Put the character(s) into the output buffer
196     if(numW > 0) {
197       (void)OBuf->Write(OBuf, (const void *)wcb, (size_t)numW);
198     }
199     RetVal = numW;
200   }
201   else {
202     if(This == NULL) {
203       errno = EINVAL;
204     }
205     else {
206       errno = ENOSPC;
207     }
208   }
209   return RetVal;
210 }
211