version 1.7, 1999/03/06 20:27:42 |
version 1.8, 1999/06/29 19:39:40 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* $NetBSD: tput.c,v 1.8 1995/08/31 22:11:37 jtc Exp $ */ |
|
|
|
|
/* |
|
* Copyright (c) 1999 Todd C. Miller <Todd.Miller@courtesan.com> |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. The name of the author may not be used to endorse or promote products |
|
* derived from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
/*- |
/*- |
* Copyright (c) 1980, 1988, 1993 |
* Copyright (c) 1980, 1988, 1993 |
* The Regents of the University of California. All rights reserved. |
* The Regents of the University of California. All rights reserved. |
|
|
static char rcsid[] = "$OpenBSD$"; |
static char rcsid[] = "$OpenBSD$"; |
#endif /* not lint */ |
#endif /* not lint */ |
|
|
#include <termios.h> |
#include <sys/param.h> |
|
|
|
#include <ctype.h> |
#include <err.h> |
#include <err.h> |
#include <curses.h> |
#include <curses.h> |
|
#include <term.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
|
#include <termios.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <string.h> |
#include <string.h> |
|
|
static void prlongname __P((char *)); |
#include <sys/wait.h> |
static void setospeed __P((void)); |
|
static void outc __P((int)); |
static void init __P((void)); |
static void usage __P((void)); |
|
static char **process __P((char *, char *, char **)); |
static char **process __P((char *, char *, char **)); |
|
static void reset __P((void)); |
|
static void usage __P((void)); |
|
|
|
extern char *__progname; |
|
|
int |
int |
main(argc, argv) |
main(argc, argv) |
int argc; |
int argc; |
char **argv; |
char **argv; |
{ |
{ |
extern char *optarg; |
int ch, exitval, n, Sflag; |
extern int optind; |
size_t len; |
int ch, exitval, n; |
char *p, *term, *str; |
char *argv0, *cptr, *p, *term, buf[1024], tbuf[1024]; |
char **oargv; |
|
|
|
oargv = argv; |
term = NULL; |
term = NULL; |
while ((ch = getopt(argc, argv, "T:")) != -1) |
Sflag = exitval = 0; |
|
while ((ch = getopt(argc, argv, "ST:")) != -1) |
switch(ch) { |
switch(ch) { |
case 'T': |
case 'T': |
term = optarg; |
term = optarg; |
break; |
break; |
|
case 'S': |
|
Sflag = 1; |
|
break; |
case '?': |
case '?': |
default: |
default: |
usage(); |
usage(); |
} |
} |
if ((argv0 = (char *)strrchr(argv[0], '/')) != NULL) |
|
argv0++; |
|
else |
|
argv0 = argv[0]; |
|
argc -= optind; |
argc -= optind; |
argv += optind; |
argv += optind; |
|
|
|
if (Sflag && argc > 0) |
|
usage(); |
|
|
if (!term && !(term = getenv("TERM"))) |
if (!term && !(term = getenv("TERM"))) |
errx(2, "no terminal type specified and no TERM environmental variable."); |
errx(2, "No value for $TERM and no -T specified"); |
if (tgetent(tbuf, term) != 1) |
|
err(2, "tgetent failure"); |
/* |
setospeed(); |
* NOTE: tgetent() will call setupterm() and set ospeed for us |
if (strcmp(argv0, "clear") == 0) { |
* (this is ncurses-specific behavior) |
*argv = "clear"; |
*/ |
|
if (tgetent(NULL, term) != 1) |
|
errx(3, "Unknown terminal type `%s'", term); |
|
|
|
if (strcmp(__progname, "clear") == 0) { |
|
if (Sflag) |
|
usage(); |
|
argv = oargv; |
|
*argv = __progname; |
*(argv+1) = NULL; |
*(argv+1) = NULL; |
} |
} |
for (exitval = 0; (p = *argv) != NULL; ++argv) { |
if (Sflag) { |
|
char **av; |
|
|
|
/* Build new argv based on stdin */ |
|
argc = n = 0; |
|
av = NULL; |
|
while ((str = fgetln(stdin, &len)) != NULL) { |
|
if (str[len-1] != '\n') |
|
errx(1, "premature EOF"); |
|
str[len-1] = '\0'; |
|
/* grow av as needed */ |
|
if (argc + 1 >= n) { |
|
n += 64; |
|
av = (char **)realloc(av, sizeof(char *) * n); |
|
if (av == NULL) |
|
errx(1, "out of memory"); |
|
} |
|
while ((p = strsep(&str, " \t")) != NULL) { |
|
if (*p != '\0' && |
|
(av[argc++] = strdup(p)) == NULL) |
|
errx(1, "out of memory"); |
|
} |
|
} |
|
if (argc > 0) { |
|
av[argc] = NULL; |
|
argv = av; |
|
} |
|
} |
|
while ((p = *argv++)) { |
switch (*p) { |
switch (*p) { |
case 'c': |
|
if (!strcmp(p, "clear")) |
|
p = "cl"; |
|
break; |
|
case 'i': |
case 'i': |
if (!strcmp(p, "init")) |
if (!strcmp(p, "init")) { |
p = "is"; |
init(); |
|
continue; |
|
} |
break; |
break; |
case 'l': |
case 'l': |
if (!strcmp(p, "longname")) { |
if (!strcmp(p, "longname")) { |
prlongname(tbuf); |
puts(longname()); |
continue; |
continue; |
} |
} |
break; |
break; |
case 'r': |
case 'r': |
if (!strcmp(p, "reset")) |
if (!strcmp(p, "reset")) { |
p = "rs"; |
reset(); |
|
continue; |
|
} |
break; |
break; |
} |
} |
cptr = buf; |
|
if (tgetstr(p, &cptr)) |
/* First try as terminfo */ |
argv = process(p, buf, argv); |
if ((str = tigetstr(p)) && str != (char *)-1) |
|
argv = process(p, str, argv); |
|
else if ((n = tigetnum(p)) != -2) |
|
(void)printf("%d\n", n); |
|
else if ((n = tigetflag(p)) != -1) |
|
exitval = !n; |
|
/* Then fall back on termcap */ |
|
else if ((str = tgetstr(p, NULL))) |
|
argv = process(p, str, argv); |
else if ((n = tgetnum(p)) != -1) |
else if ((n = tgetnum(p)) != -1) |
(void)printf("%d\n", n); |
(void)printf("%d\n", n); |
else |
else if ((exitval = tgetflag(p)) != 0) |
exitval = !tgetflag(p); |
exitval = !exitval; |
|
else { |
|
warnx("Unknown terminfo capability `%s'", p); |
|
exitval = 4; |
|
} |
|
|
if (argv == NULL) |
if (*argv == NULL) |
break; |
break; |
} |
} |
exit(argv ? exitval : 2); |
exit(*argv ? exitval : 2); |
} |
} |
|
|
static void |
|
prlongname(buf) |
|
char *buf; |
|
{ |
|
int savech; |
|
char *p, *savep; |
|
|
|
for (p = buf; *p && *p != ':'; ++p) |
|
continue; |
|
savech = *(savep = p); |
|
for (*p = '\0'; p >= buf && *p != '|'; --p) |
|
continue; |
|
(void)printf("%s\n", p + 1); |
|
*savep = savech; |
|
} |
|
|
|
static char ** |
static char ** |
process(cap, str, argv) |
process(cap, str, argv) |
char *cap, *str, **argv; |
char *cap, *str, **argv; |
{ |
{ |
static char errfew[] = |
char *cp, *s, *nargv[9]; |
"not enough arguments (%d) for capability `%s'"; |
int arg_need, popcount, i; |
static char errmany[] = |
|
"too many arguments (%d) for capability `%s'"; |
|
static char erresc[] = |
|
"unknown %% escape `%c' for capability `%s'"; |
|
char *cp; |
|
int arg_need, arg_rows, arg_cols; |
|
|
|
/* Count how many values we need for this capability. */ |
/* Count how many values we need for this capability. */ |
for (cp = str, arg_need = 0; *cp != '\0'; cp++) |
for (cp = str, arg_need = popcount = 0; *cp != '\0'; cp++) { |
if (*cp == '%') |
if (*cp == '%') { |
switch (*++cp) { |
switch (*++cp) { |
case 'd': |
case '%': |
case '2': |
cp++; |
case '3': |
break; |
case '.': |
case 'i': |
case '+': |
if (popcount < 2) |
arg_need++; |
popcount = 2; |
break; |
break; |
case '%': |
case 'p': |
case '>': |
cp++; |
case 'i': |
if (isdigit(cp[1]) && popcount < cp[1] - '0') |
case 'r': |
popcount = cp[1] - '0'; |
case 'n': |
break; |
case 'B': |
case 'd': |
case 'D': |
case 's': |
break; |
case '0': |
default: |
case '1': |
/* |
case '2': |
* hpux has lot's of them, but we complain |
case '3': |
*/ |
case '4': |
errx(2, erresc, *cp, cap); |
case '5': |
} |
case '6': |
|
case '7': |
|
case '8': |
|
case '9': |
|
case '.': |
|
case '+': |
|
arg_need++; |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
} |
|
arg_need = MAX(arg_need, popcount); |
|
if (arg_need > 9) |
|
errx(2, "too many arguments (%d) for capability `%s'", |
|
arg_need, cap); |
|
|
|
for (i = 0; i < arg_need; i++) { |
|
long l; |
|
|
/* And print them. */ |
if (argv[i] == NULL) |
switch (arg_need) { |
errx(2, "not enough arguments (%d) for capability `%s'", |
case 0: |
arg_need, cap); |
(void)tputs(str, 1, outc); |
|
break; |
|
case 1: |
|
arg_cols = 0; |
|
|
|
if (*++argv == NULL || *argv[0] == '\0') |
/* convert ascii representation of numbers to longs */ |
errx(2, errfew, 1, cap); |
if (isdigit(argv[i][0]) && (l = strtol(argv[i], &cp, 10)) >= 0 |
arg_rows = atoi(*argv); |
&& l < LONG_MAX && *cp == '\0') |
|
nargv[i] = (char *)l; |
|
else |
|
nargv[i] = argv[i]; |
|
} |
|
|
(void)tputs(tgoto(str, arg_cols, arg_rows), 1, outc); |
s = tparm(str, nargv[0], nargv[1], nargv[2], nargv[3], |
break; |
nargv[4], nargv[5], nargv[6], nargv[7], nargv[8]); |
case 2: |
putp(s); |
if (*++argv == NULL || *argv[0] == '\0') |
fflush(stdout); |
errx(2, errfew, 2, cap); |
|
arg_rows = atoi(*argv); |
|
|
|
if (*++argv == NULL || *argv[0] == '\0') |
return (argv + arg_need); |
errx(2, errfew, 2, cap); |
|
arg_cols = atoi(*argv); |
|
|
|
(void) tputs(tgoto(str, arg_cols, arg_rows), arg_rows, outc); |
|
break; |
|
|
|
default: |
|
errx(2, errmany, arg_need, cap); |
|
} |
|
return (argv); |
|
} |
} |
|
|
static void |
static void |
setospeed() |
init() |
{ |
{ |
#undef ospeed |
FILE *ifile; |
extern short ospeed; |
size_t len; |
struct termios t; |
char *buf; |
|
int waitinfo; |
|
|
if (tcgetattr(STDOUT_FILENO, &t) != -1) |
/* XXX - should we check for existence before exec()'ing? */ |
ospeed = 0; |
if (init_prog && !issetugid()) { |
else |
switch (vfork()) { |
ospeed = cfgetospeed(&t); |
case -1: |
|
err(4, "vfork"); |
|
break; |
|
case 0: |
|
/* child */ |
|
execl(init_prog, init_prog, NULL); |
|
_exit(127); |
|
break; |
|
default: |
|
wait(&waitinfo); |
|
/* XXX - interpret waitinfo? */ |
|
break; |
|
} |
|
} |
|
if (init_1string) |
|
putp(init_1string); |
|
if (init_2string) |
|
putp(init_2string); |
|
if (init_file && !issetugid() && (ifile = fopen(init_file, "r"))) { |
|
while ((buf = fgetln(ifile, &len)) != NULL) { |
|
if (buf[len-1] != '\n') |
|
errx(1, "premature EOF reading %s", init_file); |
|
buf[len-1] = '\0'; |
|
putp(buf); |
|
} |
|
fclose(ifile); |
|
} |
|
if (init_3string) |
|
putp(init_3string); |
|
/* XXX - do tabs and margins */ |
|
fflush(stdout); |
} |
} |
|
|
static void |
static void |
outc(c) |
reset() |
int c; |
|
{ |
{ |
(void)putchar(c); |
FILE *rfile; |
|
size_t len; |
|
char *buf; |
|
|
|
if (reset_1string) |
|
putp(reset_1string); |
|
if (reset_2string) |
|
putp(reset_2string); |
|
/* XXX - cat reset_file */ |
|
if (reset_file && !issetugid() && (rfile = fopen(reset_file, "r"))) { |
|
while ((buf = fgetln(rfile, &len)) != NULL) { |
|
if (buf[len-1] != '\n') |
|
errx(1, "premature EOF reading %s", reset_file); |
|
buf[len-1] = '\0'; |
|
putp(buf); |
|
} |
|
fclose(rfile); |
|
} |
|
if (reset_3string) |
|
putp(reset_3string); |
|
/* XXX - do tabs and margins */ |
|
fflush(stdout); |
} |
} |
|
|
static void |
static void |
usage() |
usage() |
{ |
{ |
(void)fprintf(stderr, "usage: tput [-T term] attribute ...\n"); |
|
|
if (strcmp(__progname, "clear") == 0) |
|
(void)fprintf(stderr, "usage: %s [-T term]\n", __progname); |
|
else |
|
(void)fprintf(stderr, |
|
"usage: %s [-T term] attribute [attribute-args] ...\n" |
|
" %s [-T term] -S\n", __progname, __progname); |
exit(1); |
exit(1); |
} |
} |