diff -r 000000000000 -r 2e3d3ce01487 openenvutils/commandshell/shell/src/modules/zpty.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openenvutils/commandshell/shell/src/modules/zpty.c Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,782 @@ +#ifndef __SYMBIAN32__ +// +// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. +// +/* + * zpty.c - sub-processes with pseudo terminals + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2000 Sven Wischnowsky + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Sven Wischnowsky or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Sven Wischnowsky and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Sven Wischnowsky and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ +#include "zpty.mdh" +#include "zpty.pro" + +#ifdef __SYMBIAN32__ +#ifdef __WINSCW__ +#pragma warn_unusedarg off +#pragma warn_possunwant off +#endif//__WINSCW__ +#endif//__SYMBIAN32__ + +#ifdef __SYMBIAN32__ +#include "dummy.h" +#endif //__SYMBIAN32__ + +/* The number of bytes we normally read when given no pattern and the + * upper bound on the number of bytes we read (even if we are give a + * pattern). */ + +#define READ_MAX (1024 * 1024) + +typedef struct ptycmd *Ptycmd; + +struct ptycmd { + Ptycmd next; + char *name; + char **args; + int fd; + int pid; + int echo; + int nblock; + int fin; + int read; + char *old; + int olen; +}; + +static Ptycmd ptycmds; + +static int +ptynonblock(int fd) +{ +#ifdef O_NDELAY +# ifdef O_NONBLOCK +# define NONBLOCK (O_NDELAY|O_NONBLOCK) +# else /* !O_NONBLOCK */ +# define NONBLOCK O_NDELAY +# endif /* !O_NONBLOCK */ +#else /* !O_NDELAY */ +# ifdef O_NONBLOCK +# define NONBLOCK O_NONBLOCK +# else /* !O_NONBLOCK */ +# define NONBLOCK 0 +# endif /* !O_NONBLOCK */ +#endif /* !O_NDELAY */ + +#if NONBLOCK + long mode; + + mode = fcntl(fd, F_GETFL, 0); + if (mode != -1 && !(mode & NONBLOCK) && + !fcntl(fd, F_SETFL, mode | NONBLOCK)) + return 1; + +#endif /* NONBLOCK */ + return 0; + +#undef NONBLOCK +} + +/**/ +static int +ptygettyinfo(int fd, struct ttyinfo *ti) +{ + if (fd != -1) { +#ifdef HAVE_TERMIOS_H +# ifdef HAVE_TCGETATTR + if (tcgetattr(fd, &ti->tio) == -1) +# else + if (ioctl(fd, TCGETS, &ti->tio) == -1) +# endif + return 1; +#else +# ifdef HAVE_TERMIO_H + ioctl(fd, TCGETA, &ti->tio); +# else + ioctl(fd, TIOCGETP, &ti->sgttyb); + ioctl(fd, TIOCLGET, &ti->lmodes); + ioctl(fd, TIOCGETC, &ti->tchars); + ioctl(fd, TIOCGLTC, &ti->ltchars); +# endif +#endif + return 0; + } + return 1; +} + +/**/ +static void +ptysettyinfo(int fd, struct ttyinfo *ti) +{ + if (fd != -1) { +#ifdef HAVE_TERMIOS_H +# ifdef HAVE_TCGETATTR +# ifndef TCSADRAIN +# define TCSADRAIN 1 /* XXX Princeton's include files are screwed up */ +# endif + tcsetattr(fd, TCSADRAIN, &ti->tio); + /* if (tcsetattr(SHTTY, TCSADRAIN, &ti->tio) == -1) */ +# else + ioctl(fd, TCSETS, &ti->tio); + /* if (ioctl(SHTTY, TCSETS, &ti->tio) == -1) */ +# endif + /* zerr("settyinfo: %e",NULL,errno)*/ ; +#else +# ifdef HAVE_TERMIO_H + ioctl(fd, TCSETA, &ti->tio); +# else + ioctl(fd, TIOCSETN, &ti->sgttyb); + ioctl(fd, TIOCLSET, &ti->lmodes); + ioctl(fd, TIOCSETC, &ti->tchars); + ioctl(fd, TIOCSLTC, &ti->ltchars); +# endif +#endif + } +} + +static Ptycmd +getptycmd(char *name) +{ + Ptycmd p; + + for (p = ptycmds; p; p = p->next) + if (!strcmp(p->name, name)) + return p; + + return NULL; +} + +#ifdef USE_DEV_PTMX + +#ifdef HAVE_SYS_STROPTS_H +#ifndef __SYMBIAN32__ +#include +#endif//__SYMBIAN32__ +#endif + +#if defined(I_FIND) && defined(I_PUSH) +/* + * These tests are ad hoc. Unfortunately if you get the wrong ioctl, + * STREAMS simply hangs up, so there's no obvious way of doing this + * more systematically. + * + * Apparently Solaris needs all three ioctls, but HP-UX doesn't need + * ttcompat. The Solaris definition has been extended to all __SVR4 + * as a guess; I have no idea if this is right. + */ +#ifdef __SVR4 +#define USE_STREAMS_IOCTLS +#define USE_STREAMS_TTCOMPAT +#endif +#ifdef __hpux +#define USE_STREAMS_IOCTLS +#endif +#endif + +static int +get_pty(int master, int *retfd) +{ + static char *name; + static int mfd, sfd; +#ifdef USE_STREAMS_IOCTLS + int ret; +#endif + + if (master) { + if ((mfd = open("/dev/ptmx", O_RDWR|O_NOCTTY)) < 0) + return 1; + + if (grantpt(mfd) || unlockpt(mfd) || !(name = (char*)ptsname(mfd))) { + close(mfd); + return 1; + } + *retfd = mfd; + + return 0; + } + if ((sfd = open(name, O_RDWR +#ifndef __CYGWIN__ + /* It is not clear whether this flag is actually needed. */ + |O_NOCTTY +#endif + )) < 0) { + close(mfd); + return 1; + } +#ifdef USE_STREAMS_IOCTLS + if ((ret = ioctl(sfd, I_FIND, "ptem")) != 1) + if (ret == -1 || ioctl(sfd, I_PUSH, "ptem") == -1) { + close(mfd); + close(sfd); + return 1; + } + if ((ret = ioctl(sfd, I_FIND, "ldterm")) != 1) + if (ret == -1 || ioctl(sfd, I_PUSH, "ldterm") == -1) { + close(mfd); + close(sfd); + return 1; + } +#ifdef USE_STREAMS_TTCOMPAT + if ((ret = ioctl(sfd, I_FIND, "ttcompat")) != 1) + if (ret == -1 || ioctl(sfd, I_PUSH, "ttcompat") == -1) { + close(mfd); + close(sfd); + return 1; + } +#endif +#endif + + *retfd = sfd; + + return 0; +} + +#else /* No /dev/ptmx or no pt functions */ + +static int +get_pty(int master, int *retfd) +{ + +#ifdef __linux + static char char1[] = "abcdefghijklmnopqrstuvwxyz"; + static char char2[] = "0123456789abcdef"; +#elif defined(__FreeBSD__) || defined(__DragonFly__) + static char char1[] = "pqrsPQRS"; + static char char2[] = "0123456789abcdefghijklmnopqrstuv"; +#else /* __FreeBSD__ || __DragonFly__ */ + static char char1[] = "pqrstuvwxyzPQRST"; + static char char2[] = "0123456789abcdef"; +#endif + + static char name[11]; + static int mfd, sfd; + char *p1, *p2; + + if (master) { + strcpy(name, "/dev/ptyxx"); + + for (p1 = char1; *p1; p1++) { + name[8] = *p1; + for (p2 = char2; *p2; p2++) { + name[9] = *p2; + if ((mfd = open(name, O_RDWR|O_NOCTTY)) >= 0) { + *retfd = mfd; + + return 0; + } + } + } + } + name[5] = 't'; + if ((sfd = open(name, O_RDWR|O_NOCTTY)) >= 0) { + *retfd = sfd; + + return 0; + } + close(mfd); + + return 1; +} + +#endif /* /dev/ptmx or alternatives */ + +static int +newptycmd(char *nam, char *pname, char **args, int echo, int nblock) +{ + Ptycmd p; + int master, slave, pid; + Eprog prog; + + prog = parse_string(zjoin(args, ' ', 1)); + if (!prog) { + errflag = 0; + return 1; + } + + if (get_pty(1, &master)) { + zwarnnam(nam, "can't open pseudo terminal: %e", NULL, errno); + return 1; + } + if ((pid = fork()) == -1) { + zwarnnam(nam, "can't create pty command %s: %e", pname, errno); + close(master); + return 1; + } else if (!pid) { + /* This code copied from the clone module, except for getting * + * the descriptor from get_pty() and duplicating it to 0/1/2. */ + + clearjobtab(0); + ppid = getppid(); + mypid = getpid(); +#ifdef HAVE_SETSID + if (setsid() != mypid) { + zwarnnam(nam, "failed to create new session: %e", NULL, errno); +#endif +#ifdef TIOCNOTTY + if (ioctl(SHTTY, TIOCNOTTY, 0)) + zwarnnam(nam, "%e", NULL, errno); + setpgrp(0L, mypid); +#endif +#ifdef HAVE_SETSID + } +#endif + + if (get_pty(0, &slave)) + exit(1); +#ifdef TIOCGWINSZ + /* Set the window size before associating with the terminal * + * so that we don't get hit with a SIGWINCH. I'm paranoid. */ + if (interact) { + struct ttyinfo info; + + if (ioctl(slave, TIOCGWINSZ, (char *) &info.winsize) == 0) { + info.winsize.ws_row = lines; + info.winsize.ws_col = columns; + ioctl(slave, TIOCSWINSZ, (char *) &info.winsize); + } + } +#endif /* TIOCGWINSZ */ + + if (!echo) { + struct ttyinfo info; + + if (!ptygettyinfo(slave, &info)) { +#ifdef HAVE_TERMIOS_H + info.tio.c_lflag &= ~ECHO; +#else +#ifdef HAVE_TERMIO_H + info.tio.c_lflag &= ~ECHO; +#else + info.tio.lmodes &= ~ECHO; /**** dunno if this is right */ +#endif +#endif + ptysettyinfo(slave, &info); + } + } + +#ifdef TIOCSCTTY + ioctl(slave, TIOCSCTTY, 0); +#endif + + close(0); + close(1); + close(2); + + dup2(slave, 0); + dup2(slave, 1); + dup2(slave, 2); + + closem(0); + close(slave); + close(master); + close(coprocin); + close(coprocout); + init_io(); + setsparam("TTY", ztrdup(ttystrname)); + + opts[INTERACTIVE] = 0; + execode(prog, 1, 0); + stopmsg = 2; + zexit(lastval, 0); + } + master = movefd(master); + + p = (Ptycmd) zalloc(sizeof(*p)); + + p->name = ztrdup(pname); + p->args = zarrdup(args); + p->fd = master; + p->pid = pid; + p->echo = echo; + p->nblock = nblock; + p->fin = 0; + p->read = -1; + p->old = NULL; + p->olen = 0; + + p->next = ptycmds; + ptycmds = p; + + if (nblock) + ptynonblock(master); + + return 0; +} + +static void +deleteptycmd(Ptycmd cmd) +{ + Ptycmd p, q; + + for (q = NULL, p = ptycmds; p != cmd; q = p, p = p->next); + + if (p != cmd) + return; + + if (q) + q->next = p->next; + else + ptycmds = p->next; + + zsfree(p->name); + freearray(p->args); + + zclose(cmd->fd); + + /* We kill the process group the command put itself in. */ + + kill(-(p->pid), SIGHUP); + + zfree(p, sizeof(*p)); +} + +static void +deleteallptycmds(void) +{ + Ptycmd p, n; + + for (p = ptycmds; p; p = n) { + n = p->next; + deleteptycmd(p); + } +} + +/**** a better process handling would be nice */ + +static void +checkptycmd(Ptycmd cmd) +{ + char c; + int r; + + if (cmd->read != -1 || cmd->fin) + return; + if ((r = read(cmd->fd, &c, 1)) < 0) { + if (kill(cmd->pid, 0) < 0) { + cmd->fin = 1; + zclose(cmd->fd); + } + return; + } + if (r) cmd->read = (int) c; +} + +static int +ptyread(char *nam, Ptycmd cmd, char **args) +{ + int blen, used, seen = 0, ret = 0; + char *buf; + Patprog prog = NULL; + + if (*args && args[1]) { + char *p; + + if (args[2]) { + zwarnnam(nam, "too many arguments", NULL, 0); + return 1; + } + p = dupstring(args[1]); + tokenize(p); + remnulargs(p); + if (!(prog = patcompile(p, PAT_STATIC, NULL))) { + zwarnnam(nam, "bad pattern: %s", args[1], 0); + return 1; + } + } else + fflush(stdout); + + if (cmd->old) { + used = cmd->olen; + buf = (char *) zhalloc((blen = 256 + used) + 1); + memcpy(buf, cmd->old, cmd->olen); + zfree(cmd->old, cmd->olen); + cmd->old = NULL; + cmd->olen = 0; + } else { + used = 0; + buf = (char *) zhalloc((blen = 256) + 1); + } + if (cmd->read != -1) { + buf[used] = (char) cmd->read; + buf[used + 1] = '\0'; + seen = used = 1; + cmd->read = -1; + } + do { + if (!ret) { + checkptycmd(cmd); + if (cmd->fin) + break; + } + if (cmd->read != -1 || (ret = read(cmd->fd, buf + used, 1)) == 1) { + if (cmd->read != -1) { + ret = 1; + buf[used] = (char) cmd->read; + cmd->read = -1; + } + seen = 1; + if (++used == blen) { + if (!*args) { + write(1, buf, used); + used = 0; + } else { + buf = hrealloc(buf, blen, blen << 1); + blen <<= 1; + } + } + } + buf[used] = '\0'; + + if (!prog && (ret <= 0 || (*args && buf[used - 1] == '\n'))) + break; + } while (!(errflag || breaks || retflag || contflag) && + used < READ_MAX && !(prog && ret && pattry(prog, buf))); + + if (prog && ret < 0 && +#ifdef EWOULDBLOCK + errno == EWOULDBLOCK +#else +#ifdef EAGAIN + errno == EAGAIN +#endif +#endif + ) { + cmd->old = (char *) zalloc(cmd->olen = used); + memcpy(cmd->old, buf, cmd->olen); + + return 1; + } + if (*args) + setsparam(*args, ztrdup(metafy(buf, used, META_HREALLOC))); + else if (used) + write(1, buf, used); + + return (seen ? 0 : cmd->fin + 1); +} + +static int +ptywritestr(Ptycmd cmd, char *s, int len) +{ + int written, all = 0; + + for (; !errflag && !breaks && !retflag && !contflag && len; + len -= written, s += written) { + if ((written = write(cmd->fd, s, len)) < 0 && cmd->nblock && +#ifdef EWOULDBLOCK + errno == EWOULDBLOCK +#else +#ifdef EAGAIN + errno == EAGAIN +#endif +#endif + ) + return !all; + if (written < 0) { + checkptycmd(cmd); + if (cmd->fin) + break; + written = 0; + } + if (written > 0) + all += written; + } + return (all ? 0 : cmd->fin + 1); +} + +static int +ptywrite(Ptycmd cmd, char **args, int nonl) +{ + if (*args) { + char sp = ' ', *tmp; + int len; + + while (*args) { + unmetafy((tmp = dupstring(*args)), &len); + if (ptywritestr(cmd, tmp, len) || + (*++args && ptywritestr(cmd, &sp, 1))) + return 1; + } + if (!nonl) { + sp = '\n'; + if (ptywritestr(cmd, &sp, 1)) + return 1; + } + } else { + int n; + char buf[BUFSIZ]; + + while ((n = read(0, buf, BUFSIZ)) > 0) + if (ptywritestr(cmd, buf, n)) + return 1; + } + return 0; +} + +/**/ +static int +bin_zpty(char *nam, char **args, Options ops, UNUSED(int func)) +{ + if ((OPT_ISSET(ops,'r') && OPT_ISSET(ops,'w')) || + ((OPT_ISSET(ops,'r') || OPT_ISSET(ops,'w')) && + (OPT_ISSET(ops,'d') || OPT_ISSET(ops,'e') || + OPT_ISSET(ops,'b') || OPT_ISSET(ops,'L'))) || + (OPT_ISSET(ops,'w') && OPT_ISSET(ops,'t')) || + (OPT_ISSET(ops,'n') && (OPT_ISSET(ops,'b') || OPT_ISSET(ops,'e') || + OPT_ISSET(ops,'r') || OPT_ISSET(ops,'t') || + OPT_ISSET(ops,'d') || OPT_ISSET(ops,'L'))) || + (OPT_ISSET(ops,'d') && (OPT_ISSET(ops,'b') || OPT_ISSET(ops,'e') || + OPT_ISSET(ops,'L') || OPT_ISSET(ops,'t'))) || + (OPT_ISSET(ops,'L') && (OPT_ISSET(ops,'b') || OPT_ISSET(ops,'e')))) { + zwarnnam(nam, "illegal option combination", NULL, 0); + return 1; + } + if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'w')) { + Ptycmd p; + + if (!*args) { + zwarnnam(nam, "missing pty command name", NULL, 0); + return 1; + } else if (!(p = getptycmd(*args))) { + zwarnnam(nam, "no such pty command: %s", *args, 0); + return 1; + } + if (p->fin) + return 2; + if (OPT_ISSET(ops,'t') && p->read == -1 && + !read_poll(p->fd, &p->read, 0, 0)) + return 1; + + return (OPT_ISSET(ops,'r') ? + ptyread(nam, p, args + 1) : + ptywrite(p, args + 1, OPT_ISSET(ops,'n'))); + } else if (OPT_ISSET(ops,'d')) { + Ptycmd p; + int ret = 0; + + if (*args) { + while (*args) + if ((p = getptycmd(*args++))) + deleteptycmd(p); + else { + zwarnnam(nam, "no such pty command: %s", args[-1], 0); + ret = 1; + } + } else + deleteallptycmds(); + + return ret; + } else if (OPT_ISSET(ops,'t')) { + Ptycmd p; + + if (!*args) { + zwarnnam(nam, "missing pty command name", NULL, 0); + return 1; + } else if (!(p = getptycmd(*args))) { + zwarnnam(nam, "no such pty command: %s", *args, 0); + return 1; + } + checkptycmd(p); + return p->fin; + } else if (*args) { + if (!args[1]) { + zwarnnam(nam, "missing command", NULL, 0); + return 1; + } + if (getptycmd(*args)) { + zwarnnam(nam, "pty command name already used: %s", *args, 0); + return 1; + } + return newptycmd(nam, *args, args + 1, OPT_ISSET(ops,'e'), + OPT_ISSET(ops,'b')); + } else { + Ptycmd p; + char **a; + + for (p = ptycmds; p; p = p->next) { + checkptycmd(p); + if (OPT_ISSET(ops,'L')) + printf("%s %s%s%s ", nam, (p->echo ? "-e " : ""), + (p->nblock ? "-b " : ""), p->name); + else if (p->fin) + printf("(finished) %s: ", p->name); + else + printf("(%d) %s: ", p->pid, p->name); + for (a = p->args; *a; ) { + quotedzputs(*a++, stdout); + if (*a) + putchar(' '); + } + putchar('\n'); + } + return 0; + } +} + +static int +ptyhook(UNUSED(Hookdef d), UNUSED(void *dummy)) +{ + deleteallptycmds(); + return 0; +} + +static struct builtin bintab[] = { + BUILTIN("zpty", 0, bin_zpty, 0, -1, 0, "ebdrwLnt", NULL), +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +boot_(Module m) +{ + ptycmds = NULL; + + addhookfunc("exit", ptyhook); + return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); +} + +/**/ +int +cleanup_(Module m) +{ + deletehookfunc("exit", ptyhook); + deleteallptycmds(); + deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + return 0; +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} + +#endif //__SYMBIAN32__