/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1992-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
static const char usage[] =
"[-?\n@(#)pty (AT&T Research) 2012-06-11\n]"
"[+NAME?pty - create pseudo terminal and run command]"
"[+DESCRIPTION?\bpty\b creates a pseudo pty and then runs \bcommand\b "
"with arguments given by \aarg\a and the standard input, standard "
"output, and standard error connected to the pseudo terminal. By "
"default, the \bpty\b creates a new session.]"
"[+?If \bcommand\b does not contain a \b/\b, the \bPATH\b variable will "
"be used to locate the \bcommand\b.]"
"[+?Input to \bpty\b will be written to the standard input of this "
"command. The standard output and standard error from the command will "
"be written to the standard output of \bpty\b.]"
"[+?The \bpty\b commmand terminates when the command completes.]"
"[d:dialogue?Execute the dialogue on the standard input. A dialogue is a "
"sequence of commands, one command per line. All \are\a patterns are "
"extended regular expressions. The \are\a \b?1\b will print the subject "
"string on the standard error and match the string; the \are\a \b?0\b "
"will print the subject string on the standard error and not match the "
"string. The \are\a \b?.\b matches EOF. The commands are:]"
"{"
"[\b#\b \acomment\a?comment line]"
"[c \atext\a?write \atext\a to the master; C style escapes "
"in text are converted, including \\E for ESC and \\cX for "
"control-X]"
"[d \amilliseconds\a?set the delay before each master write to "
"\amilliseconds\a; the default is no delay]"
"[i \are\a?read a line from the master; if it matches \are\a "
"then execute lines until matching \be\b or \bf\b]"
"[e [re]]?else [if match re]] then execute lines until matching "
"\be\b or \bf\b]"
"[f?end of \bi\b/\be\b block]"
"[m \atext\a?write \atext\a to the standard error]"
"[p \atext\a?peek input until \atext\a is found at the beginning "
"of a line; input is not consumed]"
"[r [\are\a]]?read a line from the master [and it should match "
"re]]]"
"[s \amilliseconds\a?sleep for \amilliseconds\a]"
"[t \amilliseconds\a?set the master read timout to "
"\amilliseconds\a; the default is \b1000\b]"
"[u \are\a?read lines from the master until one matches \are\a]"
"[v \alevel\a?set the verbose trace \alevel\a, more output for "
"higher levels, disabled for level 0]"
"[w \atext\a?write \atext\a\\r\\n to the master; C style escapes "
"in text are converted, including \\E for ESC and \\cX for "
"control-X]"
"[x [\acode\a]]"
"?exit \bpty\b with exit code \b0\b [\acode\a]]]"
"[I \are\a?ignore master lines matching \are\a]"
"[L \alabel\a?prefix all diagnostics with \alabel\a:]"
"[P \atext\a?delay each master write until the beginning of "
"an unread input line exactly matches \atext\a]"
"}"
"[D:debug?Set the debug trace \alevel\a, higher levels produce more "
"output, disabled for level 0.]#[level]"
"[l:log?Log the master stdout and stderr to \afile\a.]:[file]"
"[m:messages?Redirect diagnostic message output to \afile\a.]:[file]"
"[s!:session?Create a separate session for the process started by "
"\bpty\b.]"
"[t:timeout?Set the master read timeout to "
"\amilliseconds\a.]#[milliseconds:=1000]"
"[T:tty?Pass \astty\a to the \bstty\b(1) command to initialize the "
"pty.]:[stty]"
"[w:delay?Set the delay before each master write to "
"\amilliseconds\a.]#[milliseconds:=0]"
"\n"
"\ncommand [arg ...]\n"
"\n"
"[+EXIT STATUS?If the command determined by \bcommand\b is run the exit "
"status of \bpty\b is that of this command. Otherwise, the exit status "
"is one of the following:]"
"{"
"[+127?The command is found but cannot be executed.]"
"[+128?The command could not be found.]"
"}"
"[+SEE ALSO?\bcommand\b(1), \bexec\b(1)]"
;
#include <cmd.h>
#include <error.h>
#include <fcntl.h>
#include <termios.h>
#include <proc.h>
#include <ctype.h>
#include <regex.h>
#include <vmalloc.h>
#include <ast_time.h>
# if !_lib_grantpt || !_lib_unlock
# if !_lib_ptsname
{
char *last;
return(sname);
}
# endif
{
int n;
if(!name)
{
return(sname);
}
n = strlen(_pty_first);
{
{
return(NULL);
name[n-3]++;
}
{
else
return(0);
}
else
name[n-2]++;
}
else
name[n-1]++;
return(name);
}
#endif
#if !_lib_openpty
{
char *slave=0;
# if _lib__getpty
# else
# if defined(_pty_clone)
if(*master>=0)
# else
int fdm;
char *name=0;
{
if(fdm >= 0)
{
# if _lib_ptsname
# else
# endif
break;
}
}
# endif
# endif
return(slave);
}
# endif
#endif
static int
{
#ifdef TIOCGWINSZ
#endif
#if !_lib_openpty
char* sname;
#endif
/*
* some systems hang hard during the handshake
* if you know why then please let us know
*/
alarm(4);
else
{
ttyp = 0;
}
#ifdef TIOCGWINSZ
else
{
winp = 0;
}
#endif
#if _lib_openpty
return -1;
#else
#if _lib_grantpt && _lib_unlockpt
#if !_lib_posix_openpt
#ifndef _pty_clone
#define _pty_clone "/dev/ptmx"
#endif
#endif
return -1;
if (grantpt(*master) || unlockpt(*master) || !(sname = ptsname(*master)) || (*slave = open(sname, O_RDWR|O_cloexec)) < 0)
{
return -1;
}
#else
return -1;
#endif
#ifdef I_PUSH
if (tcgetattr(*slave, &tst) < 0 && (ioctl(*slave, I_PUSH, "ptem") < 0 || ioctl(*slave, I_PUSH, "ldterm") < 0))
{
return -1;
}
#endif
#endif
#ifdef TIOCSWINSZ
#endif
#if !O_cloexec
#endif
alarm(0);
return 0;
}
static Proc_t*
{
if (session)
{
ops[1] = 0;
}
else
{
ops[3] = 0;
}
}
/*
* default master dance
*/
static int
{
int i;
int n;
int t;
ssize_t r;
char* s;
for (;;)
{
i = 0;
t = timeout;
if (mp)
if (ip)
{
t = -1;
}
if (!i)
break;
{
if (n < 0)
if (t < 0)
break;
}
else
for (i = 0; i < n; i++)
{
/*skip*/;
{
{
mp = 0;
}
{
goto done;
}
}
else
{
ip = 0;
{
goto done;
}
}
}
}
done:
if (mp)
return error_info.errors != 0;
}
/*
* return 1 is extended re pattern matches text
*/
static int
{
int code;
if (!pattern[0])
return 1;
{
switch (pattern[1])
{
case '0':
case '1':
if (text)
else
case '.':
if (!text)
return 1;
if (must)
return 0;
}
}
if (!text)
{
if (must)
return 0;
}
{
return 0;
}
{
if (must)
return 0;
}
return 1;
}
typedef struct Master_s
{
} Master_t;
/*
* read one line from the master
*/
static char*
{
char* r;
char* s;
char* t;
ssize_t n;
ssize_t a;
ptrdiff_t d;
if (prompt)
if (prompt)
{
else
r = 0;
if (r)
{
return r;
}
{
{
return r;
}
{
{
return r;
}
r++;
}
}
}
{
{
}
else
{
}
goto done;
}
{
if (n < 0)
{
if (must)
else
}
{
{
}
{
return r;
}
goto done;
}
else if (must >= 0)
else
{
errno = 0;
}
return 0;
}
{
if (!prompt)
{
{
{
}
goto done;
}
else
{
errno = 0;
}
}
return 0;
}
{
{
return 0;
}
{
}
}
{
{
}
if (prompt)
goto again;
}
else
{
must = 0;
goto again;
}
done:
s = r;
{
}
for (t = 0, n = 0; *s; s++)
if (*s == '\n')
{
if (t)
{
*t++ = '\n';
*t = 0;
t = 0;
n = 0;
}
}
else if (*s == '\r' && *(s + 1) != '\n')
{
n += t - s;
else
n += strlen(s);
t = r;
}
else if (*s == '\a')
{
if (!t)
t = s;
*t = ' ';
n++;
}
else if (*s == '\b')
{
if (!t)
t = s;
if (t > r)
t--;
else
n++;
}
else if (t)
*t++ = *s;
if (t)
if (n)
*(r + strlen(r) - n) = 0;
if (lp)
if (t)
goto again;
return r;
}
/*
* execute dialogue script on stdin
*/
struct Cond_s;
struct Cond_s
{
char* text;
int flags;
};
static int
{
int op;
int line;
int n;
char* s;
char* m;
char* e;
char* id;
int status = 0;
{
goto done;
}
errno = 0;
error_info.id = 0;
error_info.line = 0;
{
error_info.line++;
while (isspace(*s))
s++;
s++;
switch (op)
{
case 0:
case '#':
break;
case 'c':
case 'w':
continue;
goto done;
if (delay)
if (op == 'w')
else
if ((n = stresc(s)) >= 0)
s[n] = 0;
{
goto done;
}
if (delay)
break;
case 'd':
if (*e)
break;
case 'i':
{
goto done;
}
if ((cond->prev->flags & SKIP) && !(cond->text = 0) || !(cond->text = masterline(mp, lp, 0, 0, timeout, master)))
else
break;
case 'e':
{
goto done;
}
if (!*s)
{
{
goto done;
}
else
{
}
}
else
break;
case 'f':
{
goto done;
}
break;
case 'm':
continue;
{
goto done;
}
break;
case 'p':
continue;
goto done;
break;
case 'r':
continue;
goto done;
match(s, m, 1);
break;
case 's':
n = (int)strtol(s, &e, 0);
if (*e)
if (n)
usleep((unsigned long)n * 1000);
break;
case 't':
if (*e)
break;
case 'u':
continue;
do
{
{
match(s, m, 1);
goto done;
}
} while (!match(s, m, 0));
break;
case 'v':
if (*e)
break;
case 'x':
if (*e)
break;
case 'I':
{
}
{
goto done;
}
break;
case 'L':
if (error_info.id)
{
error_info.id = 0;
}
{
goto done;
}
break;
case 'P':
{
}
{
goto done;
}
break;
default:
continue;
goto done;
}
}
done:
if (mp)
if (vm)
}
typedef struct Argv_s
{
char** argv;
char* args;
int argc;
} Argv_t;
int
{
int master;
int slave;
int fd;
int drop;
int n;
char* s;
int delay = 0;
char* log = 0;
char* messages = 0;
char* stty = 0;
for (;;)
{
{
case 'd':
continue;
case 'D':
continue;
case 'l':
case 'm':
continue;
case 's':
continue;
case 't':
continue;
case 'T':
continue;
case 'w':
continue;
case ':':
break;
case '?':
break;
}
break;
}
if (!argv[0])
if (stty)
{
n = 2;
for (s = stty; *s; s++)
if (isspace(*s))
n++;
for (n = 2; *s; s++)
if (isspace(*s))
{
*s = 0;
}
}
if (!log)
lp = 0;
if (messages)
{
drop = 1;
fd = 1;
drop = 0;
else
close(2);
if (drop)
}
}