00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <sys/types.h>
00021 #include <sys/ioctl.h>
00022 #include <sys/select.h>
00023 #include <sys/socket.h>
00024 #include <sys/wait.h>
00025 #include <errno.h>
00026 #include <fcntl.h>
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <string.h>
00030 #include <termios.h>
00031 #include <unistd.h>
00032
00033 #include <ccn/lned.h>
00034
00035 #define MAX_TERM_WIDTH 256
00036 #define CTL(x) ((x)-'@')
00037
00038 static int fillout(char ch, int k);
00039 static int takedown(int n, int r);
00040 static int term_width(int fd);
00041 static int shuttle(int peer, const char *prompt);
00042
00043
00044
00045
00046 static int
00047 term_width(int fd)
00048 {
00049 int ans = 80;
00050 #ifdef TIOCGWINSZ
00051
00052
00053 unsigned short ws[8] = {0};
00054 int res;
00055 res = ioctl(fd, TIOCGWINSZ, ws);
00056 if (res == 0)
00057 ans = ws[1];
00058 #endif
00059 if (ans > MAX_TERM_WIDTH)
00060 ans = MAX_TERM_WIDTH;
00061 else if (ans < 12)
00062 ans = 12;
00063 return(ans);
00064 }
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 static int
00076 shuttle(int peer, const char *prompt)
00077 {
00078 fd_set readfds;
00079 struct timeval tv;
00080 char line[MAX_TERM_WIDTH];
00081 unsigned char buf[32];
00082 ssize_t sres = 0;
00083 int ch;
00084 int exn = 0;
00085 int n = 0;
00086 int nmax;
00087 int ip = 0;
00088 int pl = 0;
00089 int shows = 0;
00090 int res;
00091
00092 nmax = term_width(0);
00093 pl = 0;
00094 if (prompt != NULL) {
00095 pl = strlen(prompt);
00096 if (pl >= nmax)
00097 pl = 0;
00098 memcpy(line, prompt, pl);
00099 n = ip = pl;
00100 }
00101 for (;;) {
00102 if (n == nmax) {
00103 if (shows != 0)
00104 shows = takedown(ip, n - ip);
00105 if (ip == pl)
00106 ip = pl + 1;
00107 sres = write(peer, line + pl, ip - pl);
00108 memmove(line + pl, line + ip, n - ip);
00109 n -= (ip - pl);
00110 ip = pl;
00111 continue;
00112 }
00113 FD_ZERO(&readfds);
00114 FD_SET(0, &readfds);
00115 FD_SET(peer, &readfds);
00116 memset(&tv, 0, sizeof(tv));
00117 tv.tv_usec = 50000;
00118 res = select(peer + 1, &readfds, NULL, NULL, shows ? NULL : &tv);
00119 if (res < 0) {
00120 perror("select");
00121 if (errno == EINTR) {
00122 shows = 0;
00123 continue;
00124 }
00125 else
00126 return(-1);
00127 }
00128 if (res == 0) {
00129 if (shows == 0) {
00130 write(2, line, n);
00131 fillout('\b', n - ip);
00132 shows = 1;
00133 }
00134 }
00135 if (FD_ISSET(peer, &readfds)) {
00136 if (shows != 0)
00137 shows = takedown(ip, n - ip);
00138 sres = read(peer, buf, sizeof(buf));
00139 if (sres == 0)
00140 return(n);
00141 if (sres < 0)
00142 return(-1);
00143 write(1, buf, sres);
00144 }
00145 ch = 0;
00146 sres = 0;
00147 if (FD_ISSET(0, &readfds)) {
00148 sres = read(0, buf, 1);
00149 if (sres == 0 || (sres < 0 && errno != EAGAIN))
00150 ch = -1;
00151 else
00152 ch = buf[0];
00153 }
00154 switch (exn) {
00155 case 1:
00156 if (ch == '[') {
00157 exn++;
00158 continue;
00159 }
00160 write(2, "\007", 1);
00161 exn = 0;
00162 break;
00163 case 2:
00164 ch = CTL((ch == 'D') ? 'B' : (ch == 'C') ? 'F' : 'G');
00165 exn = 0;
00166 break;
00167 default:
00168 break;
00169 }
00170 if (ch != 0) {
00171 if (' ' <= ch && ch <= '~') {
00172 if (ip < n)
00173 memmove(line + ip + 1, line + ip, n - ip);
00174 line[ip++] = ch;
00175 n = n + 1;
00176 if (shows != 0) {
00177 write(2, line + ip - 1, n - ip + 1);
00178 fillout('\b', n - ip);
00179 }
00180 continue;
00181 }
00182 if (ch < 0 || (ch == CTL('D') && ip == n)) {
00183 res = errno;
00184 if (shows)
00185 shows = takedown(ip, n - ip);
00186 write(peer, line + pl, n - pl);
00187 errno = res;
00188 return(sres);
00189 }
00190 if (ch == CTL('B') && ip > pl) {
00191 if (shows != 0)
00192 write(2, "\b", 1);
00193 ip = ip - 1;
00194 continue;
00195 }
00196 if (ch == CTL('F') && ip < n) {
00197 if (shows != 0)
00198 write(2, line + ip, 1);
00199 ip = ip + 1;
00200 continue;
00201 }
00202 if (ch == CTL('K')) {
00203 if (shows != 0)
00204 takedown(0, n - ip);
00205 n = ip;
00206 continue;
00207 }
00208 if (ch == CTL('D') && ip < n) {
00209 if (shows != 0)
00210 shows = takedown(ip, n - ip);
00211 n = n - 1;
00212 memmove(line+ip, line+ip+1, n - ip);
00213 continue;
00214 }
00215 if ((ch == '\b' || ch == '\177') && ip > pl) {
00216 if (ip < n) {
00217 if (shows != 0)
00218 shows = takedown(ip, n - ip);
00219 memmove(line+ip-1, line+ip, n - ip);
00220 }
00221 if (shows != 0)
00222 write(2, "\b \b", 3);
00223 ip = ip - 1;
00224 n = n - 1;
00225 continue;
00226 }
00227 if (ch == '\n') {
00228 if (shows != 0)
00229 shows = takedown(ip, n - ip);
00230 line[n++] = ch;
00231 sres = write(peer, line + pl, n - pl);
00232 n = ip = pl;
00233 continue;
00234 }
00235 if (ch == CTL('A')) {
00236 if (shows != 0)
00237 fillout('\b', ip - pl);
00238 ip = pl;
00239 continue;
00240 }
00241 if (ch == CTL('E')) {
00242 if (shows != 0 && ip < n)
00243 write(2, line + ip, n - ip);
00244 ip = n;
00245 continue;
00246 }
00247 if (ch == CTL('W') && ip > pl) {
00248 res = ip;
00249 while (res > pl && line[res - 1] <= ' ')
00250 res--;
00251 while (res > pl && line[res - 1] > ' ')
00252 res--;
00253 if (shows != 0)
00254 shows = takedown(ip, n - ip);
00255 if (ip < n)
00256 memmove(line + res, line + ip, n - ip);
00257 n = res + n - ip;
00258 ip = res;
00259 continue;
00260 }
00261 if (ch == 033)
00262 exn++;
00263 else
00264 write(2, "\007", 1);
00265 }
00266 }
00267 }
00268
00269
00270
00271
00272 static int
00273 fillout(char ch, int k)
00274 {
00275 char buf[32];
00276
00277 memset(buf, ch, sizeof(buf));
00278 while (k > sizeof(buf)) {
00279 write(2, buf, sizeof(buf));
00280 k -= sizeof(buf);
00281 }
00282 if (k > 0)
00283 write(2, buf, k);
00284 return(0);
00285 }
00286
00287
00288
00289
00290 static int
00291 takedown(int n, int r)
00292 {
00293 if (r > 0) {
00294 fillout(' ', r);
00295 fillout('\b', r);
00296 }
00297 if (n > 0) {
00298 fillout('\b', n);
00299 fillout(' ', n);
00300 fillout('\b', n);
00301 }
00302 return(0);
00303 }
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315 int
00316 lned_run(int argc, char** argv, const char *prompt, int (*worker)(int, char**))
00317 {
00318 struct termios tc[4];
00319 struct termios *t;
00320 char cb[8];
00321 int sp[2] = {-1, -1};
00322 int i;
00323 int res;
00324 int st;
00325 pid_t pid;
00326
00327 memset(tc, 0, sizeof(tc));
00328 for (i = 0; i < 3; i++) {
00329 res = tcgetattr(i, &(tc[i]));
00330 if (res < 0 && i < 2)
00331 goto Direct;
00332 }
00333 res = socketpair(AF_UNIX, SOCK_STREAM, 0, sp);
00334 if (res < 0) goto Direct;
00335 t = &(tc[3]);
00336 *t = tc[0];
00337 t->c_lflag &= ~(ECHO | ICANON);
00338 t->c_cc[VMIN] = 1;
00339 t->c_cc[VTIME] = 0;
00340 res = tcsetattr(0, TCSANOW, t);
00341 if (res < 0) goto Direct;
00342 pid = fork();
00343 if (pid == 0) {
00344 dup2(sp[1], 0);
00345 dup2(sp[1], 1);
00346 if (isatty(2))
00347 dup2(sp[1], 2);
00348 close(sp[0]);
00349 close(sp[1]);
00350 goto Direct;
00351 }
00352 close(sp[1]);
00353 dup2(1, 2);
00354 shuttle(sp[0], prompt);
00355 shutdown(sp[0], SHUT_WR);
00356 while (read(sp[0], cb, 1) == 1)
00357 write(1, cb, 1);
00358 wait(&st);
00359 tcsetattr(0, TCSANOW, &(tc[0]));
00360 return(st);
00361 Direct:
00362 return(worker(argc, argv));
00363 }