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