agc@ist.UUCP (Alistair G. Crooks) (07/30/87)
In article <1539@botter.cs.vu.nl>, ast@cs.vu.nl (Andy Tanenbaum) writes: > > 1. I couldn't make a termcap entry for the MINIX "terminal". MINIX numbers > the screen lines with (0, 0) in the lower left hand corner. Perhaps it > was arrogant of me to choose this co-ordinate system. I just thought that > since the entire world has been doing things this way since the time of > Rene Descartes (1596-1650), it might be ok. As far as I can tell, termcap > can't hack it. > > Andy Tanenbaum (ast@cs.vu.nl) I don't think it was arrogant, just unusual. Anyway, what's wrong with an 'inverter' macro definition or function call for the y-coordinate just before the tgoto() call? (Although the keypad stuff is a bit harder, granted). So, onto termcap... Please find enclosed the wherewithal to make the termcap library. If the serial line driver ever works, this should prove quite useful, and it may allow mined to be compiled without some compiler definitions (i.e. more compact mined source files). The files are... Makefile makefile for termcap and show dotprofile a copy of my .profile, includes the TERM and TERMCAP definitions that I use (can be set from the shell, but I'm lazy). etc_termcap a copy of the termcap file I have used. getenv.c the library file - must be used and replaced in library, due to a bug found in the original getenv which matches the string "TERMCAP" at the environment entry "TERM=..." logo a sample file for show show.c the browser program, try "show -v logo" (Ctl-C to quit) termcap.c the library routines. The show program works under BSD as well. There will be a curses library to follow, if anyone's interested. Alistair G. Crooks (agc@ist.co.uk) #!/bin/sh # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # Makefile dotprofile etc_termcap getenv.c getopt.c logo show.c termcap.c echo x - Makefile cat > "Makefile" << '//E*O*F Makefile//' LIB = /usr/lib/libtermcap.a CFLAGS = -O -F -T. all : $(LIB) show $(LIB) : termcap.s ar r $(LIB) termcap.s termcap.s : termcap.c cc -S $(CFLAGS) -LIB termcap.c show : show.c $(LIB) getopt.s cc $(CFLAGS) show.c getopt.s -ltermcap -o show //E*O*F Makefile// echo x - dotprofile cat > "dotprofile" << '//E*O*F dotprofile//' HOME=/user/agc PS1="minpc!agc\$ " PATH=:$HOME/bin:/bin:/usr/bin:/lib TERMCAP=$HOME/etc/termcap TERM=ibmpc l=/usr/lib m="/etc/mount /dev/fd0 /usr" u="/etc/umount /dev/fd0" m1="/etc/mount /dev/fd1 /user" u1="/etc/umount /dev/fd1" s="stty" sd="stty default" stty kill "" echo -n "Last logged in at " cat .lastlogin echo "" date > .lastlogin export PATH HOME PS1 PS2 TERMCAP TERM l //E*O*F dotprofile// echo x - etc_termcap cat > "etc_termcap" << '//E*O*F etc_termcap//' pc|ibmpc|ibmpcmono:\ bs:\ cd=\E~0:\ cl=\E 8\E~0:\ cm=\E%+ %+ :\ co#80:\ ho=\E 8:\ li#24:\ se=\Ez\007:\ so=\Ezp:\ sr=\E~1 //E*O*F etc_termcap// echo x - getenv.c cat > "getenv.c" << '//E*O*F getenv.c//' #define NULL (char *) 0 char *getenv(name) register char *name; { extern char **environ; register char **v = environ, *p, *q; while ((p = *v++) != NULL) { for (q = name ; *p && *q && *p == *q ; p++, q++) ; if (*p != '=' || *q != 0) continue; return(++p); } return(0); } //E*O*F getenv.c// echo x - getopt.c cat > "getopt.c" << '//E*O*F getopt.c//' #include <stdio.h> extern char *index(); /* * getopt - parse the arguments given. * retrieved from net.sources */ int opterr = 1; int optind = 1; int optopt; char *optarg; #define BADCH (int)'?' #define EMSG "" #define TELL(s) fputs(*nargv, stderr); fputs(s, stderr);\ fputc(optopt, stderr); fputc('\n', stderr);\ return(BADCH); int getopt(nargc, nargv, ostr) int nargc; char **nargv; char *ostr; { register char *oli; static char *place = EMSG; if (!*place) { if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); if (*place == '-') { ++optind; return(EOF); } } if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr, optopt))) { if (!*place) ++optind; TELL(": illegal option -- "); } if (*++oli != ':') { optarg = NULL; if (!*place) ++optind; } else { if (*place) optarg = place; else if (nargc <= ++optind) { place = EMSG; TELL(": option requires an argument -- "); } else optarg = nargv[optind]; place = EMSG; ++optind; } return(optopt); } //E*O*F getopt.c// echo x - logo cat > "logo" << '//E*O*F logo//' MMMMM MMMMM IIIII NNNNN NNN IIIII XXXXX XXXXX MMMMMM MMMMMM IIIII NNNNNN NNN IIIII XXXXX XXXXX MMMMMMM MMMMMMM III NNNNNNN NNN III XXXXX XXXXX MMM MMMM MMMM MMM III NNN NNNN NNN III XXXXX XXXXX MMM MMMMMMMM MMM III NNN NNNN NNN III XXXXXXXXXX MMM MMMMMM MMM III NNN NNNN NNN III XXXXXXXX MMM MMMM MMM III NNN NNNN NNN III XXXXXXXX MMM MM MMM III NNN NNNN NNN III XXXXXXXXXX MMM MMM III NNN NNNN NNN III XXXXX XXXXX MMM MMM III NNN NNNNNNN III XXXXX XXXXX MMM MMM IIIII NNN NNNNNN IIIII XXXXX XXXXX MMM MMM IIIII NNN NNNNN IIIII XXXXX XXXXX The MINIX documentation is contained in the appendices of the following book: Title: Operating Systems: Design and Implementation Author: Andrew S. Tanenbaum Publisher: Prentice-Hall (1987) For additional information, see the "doc" directory on the /us(e)r diskette. Welcome to MINIX. And welcome to the show.... //E*O*F logo// echo x - show.c cat > "show.c" << '//E*O*F show.c//' /* * show.c 1.1 20/7/87 agc Joypace Ltd * * Copyright Joypace Ltd, London, UK, 1987. All rights reserved. * This file may be freely distributed provided that this notice * remains attached. * * A small browser program(!), the idea from Chris Torek's floop * program, the bad coding from agc. (None of the code is his as * I can't find my copy of his program). Designed to test the * termcap(3) routines. */ #include <stdio.h> #include <signal.h> extern char *tgetstr(); /* termcap getstring capability */ extern char *tgoto(); /* termcap goto (x, y) */ extern FILE *fopen(); /* open a file for buffered io */ extern char *getenv(); /* get an environment variable */ #define MAXX 79 /* Width of display */ #define MAXY 23 /* Height of display */ #define TOTAL MAXX * MAXY * 5 /* Total number of tries */ short buf[MAXY][MAXX];/* buffer to hold characters */ char *prog; /* program name */ char *optstr = "v"; /* -v for ever */ extern int optind; extern char *optarg; char termcap[1024]; /* termcap buffer */ char tc[100]; /* area to hold string capabilities */ char *ttytype; /* terminal type from env */ char *arp; /* pointer for use in tgetstr */ char *cp; /* character pointer */ char *cl; /* clear screen capability */ char *cm; /* cursor motion capability */ char *so; /* start standout capability */ char *se; /* end standout capability */ /* * outc - call putchar, necessary because putchar is a macro. */ int outc(c) int c; { putchar(c); } /* * onint - what to do if you get an interrupt. */ int onint() { tputs(se, 1, outc); cp = tgoto(cm, 0, 0); tputs(cp, 1, outc); fflush(stdout); exit(0); } /* * fillbuf - fill the buffer with the beginning of file s, and * expand the tabs as we go. Returns 0 if file not found, 1 otherwise. */ int fillbuf(s) char *s; { register int lno; register int cno; FILE *fp; int i; if ((fp = fopen(s, "r")) == (FILE *) NULL) return(0); for (lno = 0 ; lno < MAXY ; lno++) for (cno = 0 ; cno < MAXX ; cno++) switch (buf[lno][cno] = getc(fp)) { case EOF : buf[lno][cno] = ' '; cno = MAXX; lno = MAXY; break; case '\t' : for (i = cno ; i < ((cno + 8) & ~07) && i < MAXX ; i++) buf[lno][i] = ' '; cno = i; break; case '\r' : case '\n' : cno = MAXX; } fclose(fp); return(1); } /* * display - go to the x and y coords on the screen, and display the * character c. Note that origin on PC is at bottom left of screen, * rather than top left. */ void display(c, y, x) char c; int y; int x; { if (ttytype && strcmp(ttytype, "ibmpc") == 0) y = MAXY - y; cp = tgoto(cm, x, y); tputs(cp, 1, outc); putchar(c & 0177); fflush(stdout); } void doscreen(blank) char blank; { int c; int tot; int x; int y; for (tot = 0 ; tot < TOTAL ; tot++) { x = rand() % MAXX; y = rand() % MAXY; if ((c = buf[y][x]) & ~0177) continue; c &= 0177; if (c == 0 || c == ' ') continue; display((blank) ? ' ' : c, y, x); buf[y][x] |= ~0177; } for (y = 0 ; y < MAXY ; y++) for (x = 0 ; x < MAXX ; x++) { if ((c = buf[y][x]) & ~0177) continue; c &= 0177; if (c != 0 && c != ' ') display((blank) ? ' ' : c, y, x); } } /* * fatal - report error and die. Never returns */ void fatal(s) char *s; { (void) fprintf(stderr, "%s: %s\n", prog, s); exit(1); } /* * Chocks away... */ main(argc, argv) int argc; char **argv; { int ever = 0; int i; int s = 0; int x; int y; if (argc < 2) { (void) fprintf(stderr, "Usage: %s filename\n", *argv); exit(1); } if ((ttytype = getenv("TERM")) == NULL) fatal("No terminal type set in environment"); if (tgetent(termcap, ttytype) != 1) fatal("No termcap entry for terminal"); arp = tc; cl = tgetstr("cl", &arp); tputs(cl, 1, outc); so = tgetstr("so", &arp); se = tgetstr("se", &arp); cm = tgetstr("cm", &arp); while ((i = getopt(argc, argv, optstr)) != EOF) switch(i) { case 'v' : ever = 1; break; default : fatal("bad option"); } if (!fillbuf(argv[optind])) fatal("file not found"); signal(SIGINT, onint); do { doscreen(s); cp = tgoto(cm, 0, 0); tputs(cp, 1, outc); fflush(stdout); for (y = 0 ; y < MAXY ; y++) for (x = 0 ; x < MAXX ; x++) buf[y][x] &= 0177; if (ever) sleep(5); s = ~s; } while (ever); exit(0); } //E*O*F show.c// echo x - termcap.c cat > "termcap.c" << '//E*O*F termcap.c//' /* * termcap.c 1.1 20/7/87 agc Joypace Ltd * * Copyright Joypace Ltd, London, UK, 1987. All rights reserved. * This file may be freely distributed provided that this notice * remains attached. * * A public domain implementation of the termcap(3) routines. */ #include <stdio.h> #define CAPABLEN 2 #define ISSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n') #define ISDIGIT(x) ((x) >= '0' && (x) <= '9') extern short ospeed; /* output speed */ extern char PC; /* padding character */ extern char *BC; /* back cursor movement */ extern char *UP; /* up cursor movement */ char *capab; /* the capability itself */ extern char *getenv(); /* new, improved getenv */ extern FILE *fopen(); /* old fopen */ /* * tgetent - get the termcap entry for terminal name, and put it * in bp (which must be an array of 1024 chars). Returns 1 if * termcap entry found, 0 if not found, and -1 if file not found. */ int tgetent(bp, name) char *bp; char *name; { FILE *fp; char *file; char *cp; short len = strlen(name); capab = bp; if ((file = getenv("TERMCAP")) != (char *) NULL) { if (*file != '/' && (cp = getenv("TERM")) != NULL && strcmp(name, cp) == 0) { (void) strcpy(bp, file); return(1); } } else file = "/etc/termcap"; if ((fp = fopen(file, "r")) == (FILE *) NULL) return(-1); while (fgets(bp, 1024, fp) != NULL) { /* skip V6 two letter name */ for (cp = bp ; *cp != '|' ; cp++) ; for (++cp ; ISSPACE(*cp) ; cp++) ; if (strncmp(name, cp, len) == 0) { while (*(cp = &bp[strlen(bp) - 2]) == '\\') fgets(cp, 1024, fp); fclose(fp); return(1); } } fclose(fp); return(0); } /* * tgetnum - get the numeric terminal capability corresponding * to id. Returns the value, -1 if invalid. */ int tgetnum(id) char *id; { char *cp; int ret; if ((cp = capab) == NULL || id == NULL) return(-1); while (*++cp != ':') ; for (++cp ; *cp ; cp++) { while (ISSPACE(*cp)) cp++; if (strncmp(cp, id, CAPABLEN) == 0) { while (*cp && *cp != ':' && *cp != '#') cp++; if (*cp != '#') return(-1); for (ret = 0, cp++ ; *cp && ISDIGIT(*cp) ; cp++) ret = ret * 10 + *cp - '0'; return(ret); } while (*cp && *cp != ':') cp++; } return(-1); } /* * tgetflag - get the boolean flag corresponding to id. Returns -1 * if invalid, 0 if the flag is not in termcap entry, or 1 if it is * present. */ int tgetflag(id) char *id; { char *cp; if ((cp = capab) == NULL || id == NULL) return(-1); while (*++cp != ':') ; for (++cp ; *cp ; cp++) { while (ISSPACE(*cp)) cp++; if (strncmp(cp, id, CAPABLEN) == 0) return(1); while (*cp && *cp != ':') cp++; } return(0); } /* * tgetstr - get the string capability corresponding to id and place * it in area (advancing area at same time). Expand escape sequences * etc. Returns the string, or NULL if it can't do it. */ char * tgetstr(id, area) char *id; char **area; { char *cp; char *ret; int i; if ((cp = capab) == NULL || id == NULL) return(NULL); while (*++cp != ':') ; for (++cp ; *cp ; cp++) { while (ISSPACE(*cp)) cp++; if (strncmp(cp, id, CAPABLEN) == 0) { while (*cp && *cp != ':' && *cp != '=') cp++; if (*cp != '=') return(NULL); for (ret = *area, cp++; *cp && *cp != ':' ; (*area)++, cp++) switch(*cp) { case '^' : **area = *++cp - 'A'; break; case '\\' : switch(*++cp) { case 'E' : **area = '\033'; break; case 'n' : **area = '\n'; break; case 'r' : **area = '\r'; break; case 't' : **area = '\t'; break; case 'b' : **area = '\b'; break; case 'f' : **area = '\f'; break; case '0' : case '1' : case '2' : case '3' : for (i=0 ; *cp && ISDIGIT(*cp) ; cp++) i = i * 8 + *cp - '0'; **area = i; cp--; break; case '^' : case '\\' : **area = *cp; break; } break; default : **area = *cp; } *(*area)++ = '\0'; return(ret); } while (*cp && *cp != ':') cp++; } return(NULL); } /* * tgoto - given the cursor motion string cm, make up the string * for the cursor to go to (destcol, destline), and return the string. * Returns "OOPS" if something's gone wrong, or the string otherwise. */ char * tgoto(cm, destcol, destline) char *cm; int destcol; int destline; { register char *rp; static char ret[24]; int *dp = &destcol; for (rp = ret ; *cm ; cm++) { switch(*cm) { case '%' : switch(*++cm) { case '+' : if (dp == NULL) return("OOPS"); *rp++ = *dp + *++cm; dp = (dp == &destcol) ? &destline : NULL; break; case '%' : *rp++ = '%'; break; } break; default : *rp++ = *cm; } } *rp = '\0'; return(ret); } /* * tputs - put the string cp out onto the terminal, using the function * outc. This should do padding for the terminal, but I can't find a * terminal that needs padding at the moment... */ int tputs(cp, affcnt, outc) register char *cp; int affcnt; int (*outc)(); { if (cp == NULL) return(1); /* do any padding interpretation - left null for MINIX just now */ while (*cp) (*outc)(*cp++); return(1); } /* * That's all, folks... */ //E*O*F termcap.c// exit 0