lned.c

Go to the documentation of this file.
00001 /**
00002  * @file lned.c
00003  * 
00004  * Part of the CCNx C Library.
00005  */
00006 /* Copyright (C) 2012 Palo Alto Research Center, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or modify it
00009  * under the terms of the GNU Lesser General Public License version 2.1
00010  * as published by the Free Software Foundation.
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014  * Lesser General Public License for more details. You should have received
00015  * a copy of the GNU Lesser General Public License along with this library;
00016  * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
00017  * Fifth Floor, Boston, MA 02110-1301 USA.
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  * Get the terminal width, if possible
00045  */
00046 static int
00047 term_width(int fd)
00048 {
00049     int ans = 80;
00050 #ifdef TIOCGWINSZ
00051     /* Just ignore the structs and pull out the second halfword.        */
00052     /* If that is wrong, it will be obvious, and won't break horribly.  */
00053     unsigned short ws[8] = {0}; /* rows, cols, etc. */
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  * Copy from the peer fd to stdout, and from stdin to peer.
00068  *
00069  * A very basic line editor is provided on the input side.
00070  * The peer will get a line at a time (unless the input is
00071  * oversize, in which case the input will arrive in chunks).
00072  * The peer is responsible for echoing the input lines, if
00073  * appropriate for the application.
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];   /* scratch buffer for reading */
00082     ssize_t sres = 0; /* read/write results */
00083     int ch;         /* current input character */ 
00084     int exn = 0;    /* length of current escape sequence */
00085     int n = 0;      /* total valid chars in line, including prompt */
00086     int nmax;       /* limit on n, based on window */
00087     int ip = 0;     /* insertion point */
00088     int pl = 0;     /* prompt length */
00089     int shows = 0;  /* set if the line is showing */
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) { /* Decode left and right arrow keys */
00155             case 1:
00156                 if (ch == '[') {
00157                     exn++;
00158                     continue;
00159                 }
00160                 write(2, "\007", 1);  /* BEL */
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);  /* BEL */
00265         }
00266     }
00267 }
00268 
00269 /**
00270  * Write k instances of ch.
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  * Erase n chars to the left of the cursor, and r to the right.
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  * Interpose a simple line editor in front of a command-line utility
00307  *
00308  * This should be called early in the application's main program,
00309  * in particular before the creation of threads or the use of stdio.
00310  *
00311  * If both stdin and stdout are tty devices, worker() is called in a forked
00312  * process, and it may use the standard file descriptors in a conventional
00313  * fashion.  Otherwise worker() is just called directly.
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 }
Generated on Tue Aug 21 14:54:18 2012 for Content-Centric Networking in C by  doxygen 1.6.3