/*
* Copyright (c) 2004 Chris Pressey, Cat's Eye Technologies.
* Copyright (c) 1980, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This file is distributed under a 3-clause BSD license. For more information,
* see the file LicenseRef-BSD-3-Clause-X-brace.txt in the LICENSES directory.
*
* SPDX-License-Identifier: LicenseRef-BSD-3-Clause-X-brace
*
* This file was derived from:
*
* @(#) Copyright (c) 1980, 1992, 1993 The Regents of the University of California. All rights reserved.
* @(#)script.c 8.1 (Berkeley) 6/6/93
* $FreeBSD: src/usr.bin/script/script.c,v 1.11.2.1 2000/07/20 10:35:21 kris Exp $
* $DragonFly: src/usr.bin/script/script.c,v 1.3 2003/10/04 20:36:50 hmp Exp $
*
* $Id: brace.c 11 2004-09-21 02:08:33Z catseye $
*/
/*
* To build:
* cc brace.c -o brace -lutil
* on NetBSD, add -DBSD
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <sysexits.h>
#ifdef BSD
#include <util.h>
#else
#include <pty.h>
#endif
/* constants */
#define BUFSIZE 4096
/* GLOBALS */
char *this_prog;
char *target_prog;
int master, slave;
int child;
struct termios tt;
int rand_in = 0;
int rand_out = 0;
/* prototypes */
static void done(int);
static void usage(void);
static void process_input(int, char *, size_t);
static void process_output(int, char *, size_t);
static void strrandcase(char *, size_t);
int
main(int argc, char **argv)
{
int cc;
struct termios rtt, stt;
struct winsize win;
int ch, n;
char obuf[BUFSIZ];
char ibuf[BUFSIZ];
fd_set rfd;
int lastc = 1000;
int die, e, pid;
union wait status;
this_prog = argv[0];
/*
* Get command-line arguments.
*/
while ((ch = getopt(argc, argv, "r:")) != -1)
switch(ch) {
case 'r':
rand_in = (strchr(optarg, 'i') != NULL);
rand_out = (strchr(optarg, 'o') != NULL);
if (!(rand_in || rand_out))
usage();
srandom(1); /* XXX should be randomer, but we'll live */
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc == 0)
usage();
target_prog = argv[0];
/*
* Create the pty. Give it the same terminal attributes
* as the terminal we're currently using.
*/
tcgetattr(STDIN_FILENO, &tt);
ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
if (openpty(&master, &slave, NULL, &tt, &win) == -1)
err(EX_OSERR, "openpty");
/*
* Make stdin 'raw' and stop it from echoing,
* otherwise we'll get doubled output.
*/
rtt = tt;
cfmakeraw(&rtt);
rtt.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
/*
* Fork off a child process to handle the execution
* of the target program.
*/
child = fork();
if (child < 0) {
/*
* Oh no! Something went wrong!
*/
warn("fork");
done(EX_OSERR);
}
if (child == 0) {
/*
* OK, looks like *we* are the child.
*/
close(master);
login_tty(slave);
execvp(argv[0], argv);
/*
* If we got this far, we obviously failed to
* execute the program.
*/
warn("%s", argv[0]);
kill(0, SIGTERM);
done(EX_OSERR);
}
/*
* Otherwise, we're the parent.
* Loop, selecting on stdin and 'master' (the program.)
*/
FD_ZERO(&rfd);
for (;;) {
FD_SET(master, &rfd);
FD_SET(STDIN_FILENO, &rfd);
n = select(master + 1, &rfd, 0, 0, NULL);
if (n < 0 && errno != EINTR)
break;
if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) {
cc = read(STDIN_FILENO, ibuf, sizeof(ibuf));
if (cc <= 0)
break;
if (cc > 0) {
/* Send keystrokes to program. */
process_input(master, ibuf, cc);
}
}
if (n > 0 && FD_ISSET(master, &rfd)) {
cc = read(master, obuf, sizeof(obuf));
if (cc <= 0)
break;
/* send program output to stdout */
process_output(STDOUT_FILENO, obuf, cc);
}
}
if (cc < 0)
err(EX_IOERR, "read/write");
/*
* Clean up and exit.
*/
die = e = 0;
while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
if (pid == child) {
die = 1;
if (WIFEXITED(status))
e = WEXITSTATUS(status);
else if (WIFSIGNALED(status))
e = WTERMSIG(status);
else /* can't happen */
e = EX_OSERR;
}
if (die)
done(e);
done(0);
}
void
process_input(int fd, char *buf, size_t len)
{
if (rand_in)
strrandcase(buf, len);
write(fd, buf, len);
}
void
process_output(int fd, char *buf, size_t len)
{
size_t i = 0, j = 0, start = 0;
while (i < len) {
if (rand_out)
strrandcase(buf, len);
/*
* Add indentation.
* Inefficient, but again, we'll live.
*/
for (j = 0; j < len; j++) {
write(fd, buf + j, 1);
if (buf[j] == '\n') {
write(fd, " ", 3);
}
}
/* write(fd, buf, len); */
i = len;
break;
}
}
/*
* cOnvERt a BUfFER tO raNdoM CAse.
*/
void
strrandcase(char *s, size_t len)
{
size_t i;
for(i = 0; i < len; i++)
s[i] = random() & 0x01 ? toupper(s[i]) : tolower(s[i]);
}
static void
usage(void)
{
fprintf(stderr,
"usage: %s [-r i|o|io] command\n", this_prog);
exit(EX_USAGE);
}
void
done(int eno)
{
/*
* Reset the terminal back to its original state.
*/
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
close(master);
exit(eno);
}