1 #include "unixcommandline.h"
2 
3 #include <sys/ioctl.h>
4 
5 #include <fstream>
6 #include <iostream>
7 #include <stdexcept>
8 
NewLine()9 void CUnixCommandLine::NewLine()
10 {
11     _cursor_line = 0;
12     _last_line = 0;
13 
14     std::cout << std::endl;
15 }
16 
Pause()17 void CUnixCommandLine::Pause()
18 {
19     clock_t i = clock() + CLOCKS_PER_SEC / 4;
20     while (clock() < i)
21         ;
22 }
23 
ShowLine(const std::string & prompt,unsigned cursor)24 void CUnixCommandLine::ShowLine(const std::string& prompt, unsigned cursor)
25 {
26     struct winsize w;
27     ioctl(0, TIOCGWINSZ, &w);
28 
29     const std::size_t prompt_len = prompt.length();
30 
31     const int l = (cursor + prompt_len) / w.ws_col;
32     const int c = (cursor + prompt_len) % w.ws_col;
33 
34     if (_cursor_line)
35         std::cout << "\x1b[" << _cursor_line << "F";
36 
37     if (full_line_dirty) {
38         if (_last_line)
39             std::cout << "\x1b[" << _last_line << "B";
40 
41         for (int i = 0; i < _last_line; ++i)
42             std::cout << "\r\x1b[K\x1b[F";
43 
44         std::cout << "\r\x1b[K\x1b[K" << prompt << iSubLine;
45 
46         if (prompt_len + iSubLine.size() != 0 && (prompt_len + iSubLine.size()) % w.ws_col == 0)
47             std::cout << "\n";
48 
49         _last_line = (prompt_len + iSubLine.size()) / w.ws_col;
50         if (_last_line)
51             std::cout << "\x1b[" << _last_line << "F";
52     }
53 
54     if (l)
55         std::cout << "\r\x1b[" << l << "B";
56 
57     std::cout << "\r\x1b[" << c + 1 << "G";
58 
59     std::cout << std::flush;
60 
61     _cursor_line = l;
62 
63     full_line_dirty = false;
64 }
65 
CUnixCommandLine()66 CUnixCommandLine::CUnixCommandLine() :
67     _cursor_line(0),
68     _last_line(0),
69     _max_lines(1024)
70 {
71     struct winsize w;
72     int rc = ioctl(0, TIOCGWINSZ, &w);
73 
74     if (rc < 0 || w.ws_col == 0)
75         throw std::runtime_error("dumb terminal");
76 
77     /* set termio so we can do our own input processing */
78     tcgetattr(0, &orig_termio);
79     rl_termio = orig_termio;
80     rl_termio.c_iflag &= ~(BRKINT | PARMRK | INPCK /*|IUCLC*/ | IXON | IXOFF);
81     rl_termio.c_iflag |= (IGNBRK | IGNPAR);
82     /* rl_termio.c_oflag &= ~(ONOCR); Costas Sphocleous Irvine,CA */
83     rl_termio.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH);
84     rl_termio.c_lflag |= (ISIG);
85     rl_termio.c_cc[VMIN] = 1;
86     rl_termio.c_cc[VTIME] = 0;
87     term_chars[VERASE] = orig_termio.c_cc[VERASE];
88     term_chars[VEOF] = orig_termio.c_cc[VEOF];
89     term_chars[VKILL] = orig_termio.c_cc[VKILL];
90     term_chars[VWERASE] = orig_termio.c_cc[VWERASE];
91     term_chars[VREPRINT] = orig_termio.c_cc[VREPRINT];
92     term_chars[VSUSP] = orig_termio.c_cc[VSUSP];
93     /* disable suspending process on ^Z */
94     rl_termio.c_cc[VSUSP] = 0;
95     tcsetattr(0, TCSADRAIN, &rl_termio);
96 
97     const char* home_dir = getenv("HOME");
98 
99     if (!home_dir)
100         return;
101 
102     const std::string fname = std::string(home_dir) + "/.yacas_history";
103 
104     std::ifstream is(fname.c_str());
105 
106     std::string line;
107     while (std::getline(is, line))
108         iHistoryList.Append(line);
109 }
110 
~CUnixCommandLine()111 CUnixCommandLine::~CUnixCommandLine()
112 {
113     tcsetattr(0, TCSADRAIN, &orig_termio);
114 
115     const char* home_dir = getenv("HOME");
116 
117     if (!home_dir)
118         return;
119 
120     const std::string fname = std::string(home_dir) + "/.yacas_history";
121 
122     std::ofstream os(fname.c_str());
123 
124     if (os) {
125         std::size_t from = 0;
126 
127         if (_max_lines > 0 && iHistoryList.NrLines() > _max_lines)
128             from = iHistoryList.NrLines() - _max_lines;
129 
130         for (std::size_t i = from; i < iHistoryList.NrLines(); ++i)
131             os << iHistoryList.GetLine(i) << "\n";
132     }
133 }
134 
MaxHistoryLinesSaved(std::size_t n)135 void CUnixCommandLine::MaxHistoryLinesSaved(std::size_t n)
136 {
137     _max_lines = n;
138 }
139 
GetKey()140 char32_t CUnixCommandLine::GetKey()
141 {
142     int c = getc(stdin);
143 
144     if (feof(stdin))
145         exit(0);
146 
147     char32_t ch = 0;
148 
149     if (c == term_chars[VERASE]) /* Backspace */
150         ch = eBackSpace;
151     else if (c == term_chars[VEOF]) /* end of file/delete */
152         ch = eCtrlD;
153     else {
154         switch (c) {
155         case 9: //  9   tab
156             ch = eTab;
157             break;
158         case 10: /* Enter */
159             ch = eEnter;
160             break;
161         case 001: /* ^A  (unix home) */
162             ch = eHome;
163             break;
164         case 005: /* ^E  (unix end) */
165             ch = eEnd;
166             break;
167         case 127:
168             ch = eDelete;
169             break;
170         case 8: /* ^H  (unix backspace) */
171             ch = eBackSpace;
172             break;
173         case 11: /* ^K (unix kill to the end of line */
174             ch = eKill;
175             break;
176         case 033: {
177             c = getc(stdin); /* check for CSI */
178 
179             switch (c) {
180             case 27:
181                 ch = eEscape;
182                 break;
183             case '[':
184             case 79: {
185                 c = getc(stdin); /* get command character */
186                 switch (c) {
187                 case 'D': /* left arrow key */
188                     ch = eLeft;
189                     break;
190                 case 'C': /* right arrow key */
191                     ch = eRight;
192                     break;
193                 case 'A': /* up arrow key */
194                     ch = eUp;
195                     break;
196                 case 'B': /* down arrow key */
197                     ch = eDown;
198                     break;
199                 case 'H': /* home */
200                     ch = eHome;
201                     break;
202                 case 'F': /* end */
203                     ch = eEnd;
204                     break;
205                 }
206                 break;
207             }
208             }
209             break;
210         }
211         default: {
212             char p[4] = {static_cast<char>(c), 0, 0, 0};
213             char* q = p + 1;
214             while (!utf8::is_valid(p, q))
215                 *q++ = getc(stdin);
216 
217             utf8::utf8to32(p, q, &ch);
218 
219             break;
220         }
221         }
222     }
223     return ch;
224 }
225