nwh@hrc63.UUCP (07/11/86)
#! /bin/sh # This is a shell archive. To extract the files type 'sh file' # This archive created on Fri Jul 11 13:25:29 WET 1986 export PATH; PATH=/bin:$PATH echo 'shar: extracting 'README' ( 2144 characters)' if test -f 'README' then echo "shar: will not overwrite existing file 'README'" else cat << \SHAR_EOF > 'README' This is the README file for the grotwin window manager for normal terminals. You will have to place the following file definitions contained in grotwin.c in your locally developed program site /usr/bin equivelent (default of /usr/mrc shown) :- manfile[] = "/usr/mrc/grotwin.man"; helpfile[] = "/usr/mrc/grotwin.help"; system_more[] = "more"; You will also need to place the following entry in /etc/termcap (for commands like tset). #================================== # # Following entry for grotwin pseudo terminals # gr|grotty|grotwin pseudo tty:\ :co#78:li#22:cl=^L:cm=\E=%+ %+ :am:bs:nd=^X:ce=^Y:\ :up=^K:do=^J:kb=^H:so=^E:se=^F:al=^A:dl=^B: #================================== Having upshared each file entry via 'sh file', you will need to copy grotwin.make to Makefile before you can make grotwin. Having typed 'make' to make grotwin, you will also need to type 'make man' in order to nroff the manual page. I'm on holiday from August 12 - 27 inclusive, so any problems will have to sorted out by other people on the net during this period. Grotwin has been developed on a Sun 2 running release 2.2. It should however, run on any BSD 4.2 or 4.3 system, or indeed any system which has pseudo terminals and the ability to poll a file descriptor (select(), ioctl(FIONREAD) or otherwise). On my system at least, the screen occasionally gets corrupted. This is due to the serial line as it doesn't happen at 4800 or on a sun window. The fix is simple, simply get grotwin to redraw the screen as and when it occurs (via ctrl-a l); this is much more useful than slowing down the serial line speed. Have fun using it - and remember, it will never be as good as a bit-mapped windowing system. It is however, generally more useful than a normal screen when multiple sessions are required. I'd be grateful for any feedback/gripes but remember that as I'm away there will be a delay in my answering them. Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214 SHAR_EOF if test 2144 -ne `wc -c < 'README'` then echo 'shar: error transmitting 'README' (should have been 2144 charcaters)' fi fi echo 'shar: extracting 'grotwin.help' ( 1976 characters)' if test -f 'grotwin.help' then echo "shar: will not overwrite existing file 'grotwin.help'" else cat << \SHAR_EOF > 'grotwin.help' WIndow Command Terminology ctrl-a 'n' means ctrl-a followed by ctrl-n or 'n'. ctrl-a 'N' means ctrl-a followed by 'N' ctrl-a 'nN' means ctrl-a followed by ctrl-n, 'n' or 'N'. All commands are preceded by ctrl-a, just as you may have used reached this information summary. The valid window commands are as follows :- Command Action 'nN' create new window 'w' make numerically next window current '0' - '9' make selected window current (range 1 to 9) 'xX' expand window to full size 'vV' expand window vertically to full size 'cC' expand window horizontally to full size 'zZ' clear window 'eE' expose window 'hH' hide window 'p' page mode on - type any character for next page 'P' page mode off 'o' overwrite mode on - type any character for next page 'O' overwrite mode off 'tT' toggle between displaying and removing time 'sS' XOFF for current window 'qQ' XON for current window 'R' force process within window to terminate - achieved by sending following signals in implied order until one is successful or all have been used SIGHUP, SIGINT, SIGTERM or SIGKILL 'F' forced exit from program (no questions asked) - same as the 'R' option, but for all windows 'iI' display status of windows '?' this message otherchar Character is input as if not preceded by ctrl-a. Therefore to enter ctrl-a, type 'ctrl-a ctrl-a' SHAR_EOF if test 1976 -ne `wc -c < 'grotwin.help'` then echo 'shar: error transmitting 'grotwin.help' (should have been 1976 charcaters)' fi fi exit Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214
nwh@hrc63.UUCP (07/11/86)
#! /bin/sh # This is a shell archive. To extract the files type 'sh file' # This archive created on Thu Jul 10 13:32:28 WET 1986 echo 'shar: extracting 'grotwin.c' ( 15356 characters)' if test -f 'grotwin.c' then echo "shar: will not overwrite existing file 'grotwin.c'" else cat << \SHAR_EOF > 'grotwin.c' #ifndef lint static char sccsid[] = "@(#)grotwin.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include <sys/ioctl.h> #include <sys/file.h> #include <signal.h> #include "grotwin.h" extern int active_windows; /* number of user windows active */ /* system dependent stuff */ static char manfile[] = "/usr/mrc/grotwin.man"; static char helpfile[] = "/usr/mrc/grotwin.help"; static char system_more[] = "more"; static char default_shell[] = "/bin/sh"; static int ignore_homefile = FALSE; /* look for default file */ static char initfile[BUFSIZ + 1]; /* temp string buffer */ /* default startup values that may be changed by user command line */ int timeon = TRUE; /* display clock on top edge */ int win_border = WIN_STANDEND; /* normal brightness window borders */ int startup = TRUE; /* program startup period */ int confused = FALSE; /* for when program hangs up */ int **screen_priority = NULL; /* which window visible at each point */ char timestring[] = " hh:ss "; /* time on top edge of b/g window */ char nottimestring[] = "-------"; /* used when time is not displayed */ struct windowdetails win[MAX_WINDOWS_PLUS]; /* window details */ struct sgttyb masterterm; /* used to store initial tty state */ struct tchars mtchars; int mlocalmode; int screenlines; /* lines on users screen */ int screencolumns; /* columns on users screen */ int time_start; /* where time string is placed */ int time_end; /* where time string ends */ char **masterscreen; /* hook to stdscr from curses */ char *progname; /* program name stripped of any /'s*/ char *home_shell; /* shell from users $SHELL variable */ main(argc, argv) int argc; char *argv[]; { int length, i; char *rindex(), *getenv(); if ((progname = rindex(argv[0], '/')) != NULL) { ++progname; } else { progname = argv[0]; } /* scan command line */ while (argc > 1 && argv[1][0] == '-') { switch (argv[1][1]) { case 'n' : ignore_homefile = TRUE; break; case 't' : timeon = FALSE; break; case 'b' : win_border = WIN_STANDOUT; break; case 'h' : man_info(); exit(0); break; default : fprintf(stderr, "usage: %s [-n][-t][-b][-h] [file]\n\n", progname); fprintf(stderr, "\t\t-n\t-\tignore "); fprintf(stderr, "$HOME startup file\n"); fprintf(stderr, "\t\t-t\t-\tturn time off\n"); fprintf(stderr, "\t\t-b\t-\tbold windows\n"); fprintf(stderr, "\t\t-h\t-\thelp info\n"); exit(1); break; } --argc; /* skip over program name */ ++argv; } if (isatty(0) == 0) { fprintf(stderr, "%s: standard input not a tty\n", progname); exit(2); } if (argc > 1) { /* check for startup file */ strcpy(initfile, argv[1]); } else { if (ignore_homefile != TRUE) { /* check for default file in users home directory */ strcpy(initfile, getenv("HOME")); if (*initfile) { length = strlen(initfile); strcpy(initfile + length, "/."); strcpy(initfile + length + 2, progname); if (access(initfile, F_OK) == -1) { *initfile = '\0'; } } } else { *initfile = '\0'; } } for (i = 3 ; i < NOFILE ; ++i) { /* just to be sure */ close(i); } /* get user tty characteristics */ ioctl(0, TIOCGETP, &masterterm); ioctl(0, TIOCLGET, &mlocalmode); ioctl(0, TIOCGETC, &mtchars); screen_init(); /* set up curses and screen dimensions */ if (screencolumns < WORLD_MIN_COLUMNS || screenlines < WORLD_MIN_ROWS) { fprintf(stderr, "%s: screen size too small !\r\n", progname); fprintf(stderr, "screen size must be at least"); fprintf(stderr, " %d lines by %d columns\r\n", WORLD_MIN_ROWS, WORLD_MIN_COLUMNS); die(); } initialise(); /* initialise globals */ setup(); /* initial windows */ startup = FALSE; manager(); /* the window manager */ /* should never get here */ fprintf(stderr, "\r\n%s: internal error (fallen off end), sorry !\r\n", progname); die(); } static initialise() /* initialise globals */ { int **priority_win(); int fatal(), forced_die(), child_exit(), update_time(); int i, string_length; char **virtwin(); /* set all windows to inactive status */ for (i = 0 ; i < MAX_WINDOWS_PLUS ; ++i) { win[i].active = FALSE; strcpy(win[i].pseudo_ttyname, "unknown"); } /* work out where time will go */ string_length = sizeof(timestring) - 1; /* -1 for null at end */ time_start = (screencolumns - string_length) / 2; time_end = time_start + string_length - 1; screen_priority = priority_win(); /* to decide display areas */ /* slight liberty */ masterscreen = stdscr->_y; /* from curses package */ /******************* * This window is always the lowest priority one. * It cannot be accessed by the user and therefore provides * a background for the portions of the screen that are not used. *******************/ win[MAX_WINDOWS].y_start = 1; win[MAX_WINDOWS].x_start = 1; win[MAX_WINDOWS].y_end = screenlines - 1; win[MAX_WINDOWS].x_end = screencolumns - 1; win[MAX_WINDOWS].screenptr = virtwin('\0'); win_initialise(MAX_WINDOWS, BACKGROUND); for (i = 0 ; i < sizeof(nottimestring) - 1 ; ++i) { nottimestring[i] = BACKGROUND; } signal(SIGALRM, update_time); update_time(); if (timeon == TRUE) { /* display time first time round */ display_time(DISPLAY); } top_dog(MAX_WINDOWS); /* install background window */ /* stop a core dump - playing safe as leaves tty in previous state */ signal(SIGILL, fatal); signal(SIGTRAP, fatal); signal(SIGIOT, fatal); signal(SIGEMT, fatal); signal(SIGSEGV, fatal); signal(SIGBUS, fatal); signal(SIGSYS, fatal); /* signals to be used to stop program externally */ signal(SIGHUP, forced_die); signal(SIGINT, forced_die); signal(SIGQUIT, forced_die); signal(SIGTERM, forced_die); /* in order to pick up dead children (when a window is removed) */ signal(SIGCHLD, child_exit); } static setup() /* initial windows */ { FILE *fp, *fopen(); int columns, rows, xstart, ystart; int i, limit, items_read, winno; char *getenv(), *rindex(), *fgets(); char *program, buffer[BUFSIZ + 1], buffer2[BUFSIZ + 1]; /* find users favourite shell */ if ((home_shell = getenv("SHELL")) == NULL) { home_shell = default_shell; } /* check for file to define startup windows */ if (*initfile) { if ((fp = fopen(initfile, "r")) == NULL) { fprintf(stderr, "%s: can't open %s\r\n", progname, initfile); die(); } limit = MAX_WINDOWS; } else { /* default windows */ fp = NULL; limit = 2; } winno = -2; /* invalid value to enable test at end */ for (i = 0 ; i < limit ; ++i) { /* create each window */ program = buffer2; if (fp != NULL) { if (fgets(buffer, BUFSIZ, fp) == NULL) { break; } items_read = sscanf(buffer, "%d%d%d%d%[^\n\0]", &columns, &rows, &xstart, &ystart, program); if (items_read < 4) { winno = -2; break; } check_size(&columns, &rows, &xstart, &ystart); if (items_read < 5) { strcpy(program, home_shell); } else { while (*program == ' ' || *program == '\t') { ++program; } /* if just white space default to shell */ if (! *program) { strcpy(program, home_shell); } } } else { /* default window sizes */ columns = -1; rows = -1; xstart = -1; ystart = -1; check_size(&columns, &rows, &xstart, &ystart); strcpy(program, home_shell); } winno = win_instance(rows, columns, ystart, xstart, program); if (winno != -1) { next_input_window(winno); } } if (fp != NULL) { fclose(fp); } if (active_windows <= 0) { if (winno == -2) { fprintf(stderr, "%s: format of %s is invalid\r\n", progname, initfile); } else { fprintf(stderr, "%s: can't open any windows\r\n", progname); } die(); } } /******************* * Check (and change if necessary), that window dimensions selected can * be displayed on output device screen - if not then firstly move start * positions and then change dimensions if this fails. * * Special negative value meanings: * * In size field: * -1 - full screen width or height depending on position * -2 - half full screen width or height depending on position * * In position field: * -1 - screen width or height minus specified width or height * -2 - half full width or height start positiono * *******************/ check_size(columns, rows, xstart, ystart) int *columns, *rows, *xstart, *ystart; { if (*columns == -1 || *columns > screencolumns) { *columns = screencolumns; } else { if (*columns == -2) { /* half width */ *columns = screencolumns / 2; } else { if (*columns < MIN_COLUMNS) { /* too small */ *columns = MIN_COLUMNS; } } } if (*rows == -1 || *rows > screenlines) { *rows = screenlines; } else { if (*rows == -2) { /* half heigth */ *rows = screenlines / 2; } else { if (*rows < MIN_ROWS) { /* too small */ *rows = MIN_ROWS; } } } if (*xstart == -1) { /* pad to fit */ *xstart = screencolumns - *columns; } else { if (*xstart == -2) { /* half width */ *xstart = screencolumns /2 ; } if (*xstart + *columns > screencolumns) { *xstart = screencolumns - *columns; } } if (*ystart == -1) { /* pad to fit */ *ystart = screenlines - *rows; } else { if (*ystart == -2) { /* half height */ *ystart = screenlines /2 ; } if (*ystart + *rows > screenlines) { *ystart = screenlines - *rows; } } } static screen_init() /* initialise curses package and screen size */ { initscr(); screencolumns = COLS; screenlines = LINES; raw(); nonl(); noecho(); } /******************* * Save current screen in temporary window to enable non-window * user interaction, such as a short window command summary * and window status information to occur. Window is replaced * at end of interaction. *******************/ inform(function, winno) int (*function)(); int winno; { WINDOW *helpwin, *newwin(); int old_timeon, waiting; char c; /******************* * Need to stop time updating screen since we are * temporarily not using stdscr. *******************/ alarm(0); /* turn off alarm */ old_timeon = timeon; if (timeon == TRUE) { /* remove time */ timeon = FALSE; display_time(REMOVE); } helpwin = newwin(screenlines, screencolumns, 0, 0); overwrite(stdscr, helpwin); wclear(stdscr); wrefresh(stdscr); /* call supplied function */ if ((waiting = ((*function)(winno))) == TRUE) { printf("[ Hit return to continue ] "); fflush(stdout); read(0, &c, 1); } overwrite(helpwin, stdscr); overwrite(helpwin, curscr); /* don't know where cursor is, so force whole screen redraw */ wrefresh(curscr); delwin(helpwin); if (old_timeon == TRUE) { timeon = TRUE; update_time(); /* display time this time round */ } } man_info() { if (access(manfile, R_OK) != -1) { sprintf(initfile, "%s %s", system_more, manfile); system(initfile); } else { fprintf(stderr, "%s: sorry, can't find manual page %s\n", progname, manfile); } } help_info(winno) /* print help info by pages */ int winno; { FILE *fopen(), *fp, *out; if ((fp = fopen(helpfile, "r")) != NULL) { page(fp); fclose(fp); } else { if (startup == FALSE) { out = stdout; } else { /* grotwin -h invocation */ out = stderr; fprintf(out, "%s: ", progname); } fprintf(out, "sorry, helpfile %s not available\r\n", helpfile); } return(TRUE); } page(fp) FILE *fp; { static char page_string[] = "[ Hit any key for more ] "; int lines; char *fgets(), buffer[80]; lines = 1; while (fgets(buffer, sizeof(buffer), fp) != NULL) { fputs(buffer, stdout); putc('\r', stdout); if (++lines >= screenlines) { lines = 1; fputs(page_string, stdout); fflush(stdout); read(0, buffer, 1); /* wait for user */ clear_line(sizeof(page_string)); } } } clear_line(length) /* clear a line on screen */ int length; { int outchar(); putc('\r', stdout); /******************* * Clear line. Try to use termcap delete to end of line, * delete line or just output plain spaces if all else fails. * Delete to end of line may be faster than delete line * since delete line may require the terminal to * scroll the bottom line. *******************/ if (CE != (char *) NULL) { tputs(CE, 1, outchar); } else { if (DL != (char *) NULL) { tputs(CE, 1, outchar); } else { /* bodge it */ while (length--) { putc(' ', stdout); } } } putc('\r', stdout); fflush(stdout); } static outchar(c) /* output routine for use with tputs in clear_line */ char c; { putc(c, stdout); } forced_die() { alarm(0); signal(SIGINT, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGALRM, SIG_IGN); kill_all_shells(); } die() { static char confused_mess[] = "\7\r\n-- Internal error - please report --\r\n"; alarm(0); if (startup == FALSE) { wclear(stdscr); wmove(stdscr, LINES - 1, 0); wrefresh(stdscr); } endwin(); if (screen_priority != NULL) { free_priority_win(screen_priority); } if (confused == REALLY_CONFUSED) { fprintf(stderr, confused_mess); exit(3); } exit(0); } static fatal() { static char fatalmess[] = "\n\n\7 caught nasty signal - exiting \n"; static int first_time = TRUE; alarm(0); signal(SIGINT, SIG_IGN); signal(SIGALRM, SIG_IGN); /* endwin will do this anyway, good for following printfs though */ ioctl(0, TIOCSETP, &masterterm); ioctl(0, TIOCLBIS, &mlocalmode); ioctl(0, TIOCSETC, &mtchars); fprintf(stderr, fatalmess); /* safety check to stop endless recursion if hopelessly lost */ if (first_time) { first_time = FALSE; forced_die(); die(); } exit(4); } SHAR_EOF if test 15356 -ne `wc -c < 'grotwin.c'` then echo 'shar: error transmitting 'grotwin.c' (should have been 15353 charcaters)' fi fi exit Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214
nwh@hrc63.UUCP (07/11/86)
#! /bin/sh # This is a shell archive. To extract the files type 'sh file' # This archive created on Thu Jul 10 16:38:32 WET 1986 echo 'shar: extracting 'manager.c' ( 22349 characters)' if test -f 'manager.c' then echo "shar: will not overwrite existing file 'manager.c'" else cat << \SHAR_EOF > 'manager.c' #ifndef lint static char sccsid[] = "@(#)manager.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include <sys/ioctl.h> #include <sys/file.h> #include <sys/time.h> #include <sys/wait.h> #include <ctype.h> #include <errno.h> #include "grotwin.h" extern int errno; /* system error */ /* externals defined in grotwin.c */ extern struct windowdetails win[MAX_WINDOWS_PLUS]; /* window details */ extern struct sgttyb masterterm; /* used to store initial tty state */ extern struct tchars mtchars; extern int mlocalmode; extern int startup; /* program startup period */ extern int confused; /* for when program hangs up */ extern int timeon; /* display clock on top edge */ extern int time_start; /* where time string is placed */ extern char *progname; /* program name stripped of any /'s*/ extern char timestring[]; /* time on top edge of b/g window */ extern char nottimestring[]; /* used when time is not displayed */ unsigned int child_died = 0; /* flag to catch SIGCHLD */ /* slavemask used to form mask of active file descriptors for select() */ int slavemask = 1 << 0; /* standard input (fd = 0) */ int active_windows = 0; /* number of user windows active */ int input_winno = 0; /* current window */ int input_changed = FALSE; /* to force cursor to be redrawn */ int alldead = FALSE; /* no user window active */ int exit_forced = FALSE; /* forced exit of program */ long clock; /* time in seconds of last minute */ /* list of priorities - end referenced by tail variable */ int window_priorities[MAX_WINDOWS_PLUS]; manager() /* manage input from active window and output from all */ { /******************* * Nice largeish timeout to make select() return only when something * is ready (stops eating up cpu time). * Must be less than 30 seconds for detection of hangup. *******************/ static struct timeval timeout = { 10L, 0L }; static unsigned int children_dead = 0; struct windowdetails *ptr; int i, readfds, waiting; /* make corners indicate indicate this input window */ top_corners(input_winno); while (1) { /* used to locate internal hangup */ if (confused == TRUE) { confused = FALSE; if (timeon == TRUE) { /* time changed */ display_time(DISPLAY); } } /******************* * Check for dead children. Even though child_died * may be incremented asynchronously, having another * count that is incremented until it is equal to * child_died will overcome any problems of reading * an out of date value of child_died being used. *******************/ while (children_dead != child_died) { ++children_dead; check_exit(); /* remove window */ } /* place cursor in active window if moved */ if (input_changed == TRUE) { ptr = &(win[input_winno]); wmove(stdscr, ptr->y_current + ptr->y_start, ptr->x_current + ptr->x_start); touchwin(stdscr); wrefresh(stdscr); input_changed = FALSE; } /******************* * Check if their is any keyboard input and obey - then * check each window for any output and display it . * Order is important since user expects response to input * immediately (this does mean that window tty could be closed * after select has said there is input waiting though !). *******************/ readfds = slavemask; if (select(NOFILE, &readfds, 0, 0, &timeout) <= 0) { continue; } /* keyboard input waiting */ if (readfds & (1 << 0)) { get_input(); } touchwin(stdscr); wrefresh(stdscr); ptr = win; /******************* * need to check if window is active since it may * have been removed by action of keyboard input ! *******************/ for (i = 0 ; i < MAX_WINDOWS ; ++i, ++ptr) { if (! ptr->active) { continue; } /* check for previous output still waiting */ if (ptr->page_buf_length > 0) { update_screen(i); input_changed = TRUE; continue; } else { /* read current output waiting */ if (readfds & ptr->slavemaskfd) { waiting = read(ptr->masterfd, ptr->page_buf, ptr->readgrain); if (waiting != -1) { ptr->page_buf[waiting] = '\0'; ptr->page_buf_length = waiting; update_screen(i); input_changed = TRUE; } } } } } } /* fire off window of specified dimensions running named program within it */ win_instance(lines, columns, ystart, xstart, program) int lines, columns, ystart, xstart; char *program; { struct windowdetails *ptr; int winno; char *calloc(); if ((winno = freewin()) == -1) { return(-1); } if (newtty(winno) == -1) { return(-1); } ptr = &(win[winno]); ptr->progy = calloc(strlen(program) + 1, sizeof(char)); strcpy(ptr->progy, program); switch (ptr->slavepid = fork()) { case -1 : /* too many system processes ! */ /* can't use removewin since haven't used createwin */ close(ptr->masterfd); close(ptr->slavefd); ptr->active = FALSE; return(-1); break; case 0 : /* child */ action_slave(ptr->slavefd, ptr->progy, lines - 2, columns - 2); /* _exit to stop flushing buffers twice (apparently) */ _exit(10); break; default : /* parent */ /***************** * In case any old (still running) processes started * on pty have not been disassociated from it. *****************/ ioctl(ptr->masterfd, TIOCSPGRP, &(ptr->slavepid)); ioctl(ptr->slavefd, TIOCSPGRP, &(ptr->slavepid)); /* rendezvous with child (see comment in child) */ write(ptr->masterfd, "\n", 1); break; } /* must be after fork (faster startup with createwin here) */ slavemask |= ptr->slavemaskfd; /* tty now accessible */ createwin(winno, lines, columns, ystart, xstart); return(winno); } static freewin() /* return win[] location not in use */ { register int i; register struct windowdetails *ptr; for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr) { if (! ptr->active) { return(i); } } return(-1); } static newtty(winno) /* return number of a free pseudo tty (cyclic ish) */ int winno; { /* not very efficient for pty[q-r] at present, to be changed */ static char *pseudo_termlist[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; static char *masterside[] = { "/dev/ptyp", "/dev/ptyq", "/dev/ptyr" }; static char *slaveside[] = { "/dev/ttyp", "/dev/ttyq", "/dev/ttyr" }; static int elements = sizeof(pseudo_termlist) / sizeof(char *); static int i = (sizeof(pseudo_termlist) / sizeof(char *)) - 1; static int pty_groups = sizeof(masterside) / sizeof(char *); static int j = (sizeof(masterside) / sizeof(char *)) - 1; register struct windowdetails *ptr; char temp[20]; int pty, tty_element, fd; ptr = &(win[winno]); /* if pseudo tty in use, assume can't open either master or slave */ for (pty = 0 ; pty < pty_groups ; ++pty) { for (tty_element = 0 ; tty_element < elements ; ++tty_element) { i = (i + 1) % elements; sprintf(temp, "%s%s", masterside[j],pseudo_termlist[i]); if ((fd = open(temp, O_RDWR)) != -1) { ptr->masterfd = fd; sprintf(temp, "%s%s", slaveside[j], pseudo_termlist[i]); if ((fd = open(temp, O_RDWR)) != -1) { ptr->slavemaskfd = 1 << ptr->masterfd; ptr->slavefd = fd; strcpy(ptr->pseudo_ttyname, temp); return(0); } close(ptr->masterfd); } } /* start at the beginning for next pty group */ if (i != 0) { i = elements - 1; } j = (j + 1) % pty_groups; } return(-1); } child_exit() /* increment global flag of dead children */ { ++child_died; } check_exit() /* check for terminated shells */ { register int i; register struct windowdetails *ptr; int pid; for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr) { if (ptr->active) { if (kill(ptr->slavepid, 0) == -1 && errno == ESRCH) { /******************* * Should really check that pid is equal * to ptr->slaveppid, but what could * be done if it wasn't. *******************/ pid = shell_cleanup(i); } } } /* if no shells left - exit program */ if (alldead && ! startup) { alarm(0); signal(SIGALRM, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGINT, SIG_IGN); die(); } } update_time() /* called via SIGALRM - update global time */ { static long last_clock = 0L; char *ctime(); time(&clock); /******************* * Following is a simple little check to ensure that if program * will terminate within a minute if it hangs up badly. * Relies on manager taking less than 30 seconds on select timeout ! *******************/ if (clock - last_clock > 30) { /* internal hangup */ if (confused == TRUE && startup == FALSE) { confused = REALLY_CONFUSED; forced_die(); } last_clock = clock; } alarm(60 - (int) (clock % 60)); confused = TRUE; /* until proven otherwise */ } display_time(command) /* display or remove time on top border */ int command; { wmove(stdscr, 0, time_start); if (command == DISPLAY) { /* isolate hours and mins */ strncpy(timestring + 1, ctime(&clock) + 11, 5); waddstr(stdscr, timestring); } else { /* REMOVE */ waddstr(stdscr, nottimestring); } input_changed = TRUE; /* place cursor back in input window */ } static shell_cleanup(winno) /* remove all references to a now dead window */ int winno; { union wait status; /* different from sysV */ int pid; /******************* * Stop child waiting for parent to perform a wait. * Otherwise child becomes zombie and hogs a place in process * list table. *******************/ pid = wait(&status); removewin(winno); /* place cursor in current window */ if (! alldead) { /* was current window one terminated */ if (winno == input_winno) { next_input_window(window_priorities[0]); top_dog(input_winno); } } return(pid); } kill_all_shells() { int i; exit_forced = TRUE; for (i = 0 ; i < MAX_WINDOWS ; ++i) { if (win[i].active) { if (kill_shell(i) == -1) { printf(" window %d process wouldn't die ", i); sleep(2); } removewin(i); } } die(); } /* kill process within window - tries gently and then with brute force */ static kill_shell(winno) int winno; { static int kill_list[] = { SIGHUP, SIGINT, SIGTERM, SIGKILL }; int i; kill(win[winno].slavepid, SIGCONT); /* wake up just in case */ for (i = 0 ; i < (sizeof(kill_list) / sizeof(int)) ; ++i) { if (kill(win[winno].slavepid, kill_list[i]) == 0) { return(kill_list[i]); } } return(-1); } static removewin(winno) /* remove window from existance */ int winno; { struct windowdetails *ptr; char *rindex(), *tty; ptr = &(win[winno]); ptr->active = FALSE; slavemask &= ~ ptr->slavemaskfd; if (--active_windows <= 0) { alldead = TRUE; } close(ptr->masterfd); close(ptr->slavefd); tty = 1 + rindex(ptr->pseudo_ttyname, '/'); utmp_delete(tty); free_virt_win(ptr->screenptr); free(ptr->progy); /* test to stop needless screen redrawing if quiting program */ if (! exit_forced) { /* clear screen where window was */ bottom_dog(winno, REMOVE); touchwin(stdscr); wrefresh(stdscr); } } next_input_window(value) int value; { static int prev_winno = MAX_WINDOWS - 1; register struct windowdetails *ptr; int i, oldinput; oldinput = input_winno; if (value == -1) { for (i = 0 ; i < MAX_WINDOWS ; ++i) { prev_winno = (prev_winno + 1) % MAX_WINDOWS; if (win[prev_winno].active) { input_winno = prev_winno; break; } } } else { /* check for illegal window number or inactive window */ if (value >= 0 && value < MAX_WINDOWS) { if (win[value].active) { input_winno = value; prev_winno = value; } else { return(-1); } } else { return(-1); } } /* remove active indicators in top corners of old acitive window */ if (oldinput >= 0) { ptr = &(win[oldinput]); if (ptr->active) { ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] =' '; ptr->screenptr[ptr->y_start - 1][ptr->x_end] = ' '; top_corners(oldinput); } } /* add active indicators to top corners of active window */ ptr = &(win[input_winno]); ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] = '+'; ptr->screenptr[ptr->y_start - 1][ptr->x_end] = '+'; top_corners(input_winno); input_changed = TRUE; /* bodge for now to force corners to be indicated ! */ /* should go before ptr = ... */ if (oldinput == input_winno) { return(-1); } return(oldinput); } static get_input() { int length, i; char input[BUFSIZ + 1]; length = read(0, input, BUFSIZ); for (i = 0 ; i < length ; ++i) { put_char((unsigned char) input[i]); } } static put_char(c) unsigned char c; { extern int window_info(), help_info(); static unsigned int prev_c = '\0'; static int new_winno = 0; /* in case c == WIN_SWITCH_NUMBER */ struct windowdetails *ptr; int upper_case, temp; unsigned char c_orig; c &= 0x7F; /* strip off top bit for WIN_SELECT_NUMBER */ ptr = &(win[input_winno]); if (prev_c == SELECT) { c_orig = c; /******************* * to help distinguish between upper case only commands, * make upper_case variable FALSE in order to execlude * lower case and control instances *******************/ upper_case = FALSE; /* until proved otherwise */ if (isdigit(c)) { new_winno = c - '0'; if (new_winno < MAX_WINDOWS) { c = WIN_SWITCH_NUMBER; } } else { if (isalpha(c)) { if (isupper(c)) { upper_case = TRUE; } /* to allow 'n' and CTRL-N to be the same */ c = c & CTRL_MASK; } } switch(c) { case WIN_SWITCH : new_winno = -1; case WIN_SWITCH_NUMBER : temp = input_winno; if (next_input_window(new_winno) == -1 || temp == input_winno) { write(1, "\007", 1); break; /* nasty practice */ } top_dog(input_winno); touchwin(stdscr); wrefresh(stdscr); break; case NEW_WINDOW : new_window(); break; case SCREEN_REDRAW : touchwin(curscr); wrefresh(curscr); break; case EXPAND : widen_window(input_winno, VERTICAL_EXPAND); widen_window(input_winno, HORIZONTAL_EXPAND); touchwin(stdscr); wrefresh(stdscr); break; case VERTICAL_EXPAND : widen_window(input_winno, VERTICAL_EXPAND); touchwin(stdscr); wrefresh(stdscr); break; case HORIZONTAL_EXPAND : widen_window(input_winno, HORIZONTAL_EXPAND); touchwin(stdscr); wrefresh(stdscr); break; case PAGE_MODE : if (upper_case) { /* page off */ ptr->output_paged = FALSE; if (ptr->paged_page_full) { ptr->paged_page_full = FALSE; } } else { /* page on */ if (! ptr->output_paged) { ptr->output_paged = TRUE; ptr->paged_page_full = FALSE; ptr->line_count = 0; } } break; case REMOVE_WIN : /* only look for upper case */ if (upper_case) { if (kill_shell(input_winno) == -1) { /* failed (why ?) */ write(1, "\007", 1); } break; } /* fall through to default case (nasty) */ goto fallthru; case FORCED_EXIT : if (upper_case) { forced_die(); break; } /* only look for upper case */ /* fall through to default case (nasty) */ goto fallthru; case XON : ptr->output_blocked = FALSE; break; case XOFF : ptr->output_blocked = TRUE; break; case OVERWRITE_MODE : if (upper_case) { /* overwrite off */ ptr->overwrite = FALSE; } else { /* overwrite on */ ptr->overwrite = TRUE; } break; case TIME_TOGGLE : /* toggle time between on and off */ if (timeon == TRUE) { timeon = FALSE; display_time(REMOVE); } else { timeon = TRUE; update_time(); } break; case INFORM : inform(window_info, input_winno); break; case HELP : inform(help_info, input_winno); break; case CLEAR : mywclear(input_winno); input_changed = TRUE; touchwin(stdscr); wrefresh(stdscr); break; case EXPOSE_WINDOW : top_dog(input_winno); touchwin(stdscr); wrefresh(stdscr); break; case HIDE_WINDOW : bottom_dog(input_winno, HIDE); touchwin(stdscr); wrefresh(stdscr); break; fallthru : default : if (ptr->paged_page_full) { ptr->paged_page_full = FALSE; } else { c = c_orig; write(ptr->masterfd, &c, 1); if (c == SELECT) { c = ~c; } } break; } } else { if (c != SELECT) { if (ptr->paged_page_full) { ptr->paged_page_full = FALSE; } else { write(ptr->masterfd, &c, 1); } } } prev_c = c; } /* fire off slave process within window */ static action_slave(slavefd, progy, lines, columns) int slavefd; char *progy; int lines, columns; { extern char **environ; extern char termcap[]; extern char term[]; /* my very own terminal type ! */ int fd, ldisc, pgrp, i, j, k; char **argv, **envp, *calloc(); char c, buffer[BUFSIZ + 1], *ptr1, *ptr2; signal(SIGALRM, SIG_IGN); alarm(0); /* turn off clock signal */ /* reset all caught signals */ for (i = 1 ; i <= NSIG ; ++i) { signal(i, SIG_DFL); } /* make pseudo tty connection */ close(0); dup(slavefd); close(1); dup(slavefd); close(2); dup(slavefd); for (i = 3 ; i < NOFILE ; ++i) { /* close all other files */ close(i); } /* disassociate /dev/tty from parent control group */ if ((fd = open("/dev/tty", O_RDWR, 0)) != -1) { ioctl(fd, TIOCNOTTY, 0); close(fd); } /******************* * From experience, it would appear that the master side of a * pseudo tty must set the process group for the master side (pty) * before the slave side sets its process group equal to its * own pid (master is also set to this value, obtained via fork * of child). * This is achieved by the parent writing '\n' (specifically '\n' * to overcome buffering), on its tty when it has set its * own tty and its childs tty (action duplicated by child) to the * process group equal to the childs process number, thereby * achieving a simple rendezvous. *******************/ /* rendezvous with parent */ read(0, &c, 1); /* set up tty in a usable state as parent tty may not be */ ldisc = NTTYDISC; masterterm.sg_flags |= ECHO; masterterm.sg_flags |= CRMOD; masterterm.sg_flags &= ~CBREAK; masterterm.sg_flags &= ~RAW; ioctl(0, TIOCSETP, &masterterm); ioctl(0, TIOCSETD, &ldisc); ioctl(0, TIOCLBIS, &mlocalmode); ioctl(0, TIOCSETC, &mtchars); /* set up unique process group and attach to process and tty */ pgrp = getpid(); setpgrp(pgrp, pgrp); ioctl(0, TIOCSPGRP, &pgrp); /* parent also performs this */ set_tty_size(0, lines, columns, FALSE); /* set tty size */ for (i = 0 ; environ[i] ; ++i) { /* find last entry */ ; } /* form new environment for progy */ envp = (char **) calloc(i + 2, sizeof(char *)); /* comb through and remove TERM and TERMCAP entries */ for (k = 0, j = 0 ; j < i ; ++j) { if (strncmp(environ[j], "TERMCAP", 7) != 0 && strncmp(environ[j], "TERM", 4) != 0) { envp[k++] = environ[j]; } } /******************* * Now add grotwin terminal type. * Could be clever and place these at start of environment * for quick access ! *******************/ envp[k] = calloc(strlen(term) + 1, sizeof(char)); strcpy(envp[k++], term); sprintf(buffer, termcap, columns, lines); envp[k] = calloc(strlen(buffer) + 1, sizeof(char)); strcpy(envp[k], buffer); envp[++k] = (char *) NULL; environ = envp; argv = (char **) calloc(MAXARGS + 1, sizeof(char *)); strcpy(buffer, progy); i = 0; ptr1 = buffer; while (i < MAXARGS) { for(ptr2 = ptr1 ; *ptr1 != ' ' && *ptr1 ; ++ptr1) { ; } argv[i++] = ptr2; if (! *ptr1) { break; } *ptr1++ = '\0'; } argv[i] = (char *) NULL; execvp(argv[0], argv); /* exec failed */ fprintf(stderr, "\r\n%s: can't find %s ", progname, argv[0]); fflush(stderr); free(argv); free(envp[k - 1]); free(envp[k - 2]); free(envp); close(0); /* close files */ close(1); close(2); sleep(5); } /******************* * Set tty size and inform associated control group * via SIGWINCH that the tty size has changed. *******************/ set_tty_size(fd, lines, columns, inform) int fd, lines, columns, inform; { struct ttysize tty_size; int pgrp; tty_size.ts_lines = lines; /* set up new tty size */ tty_size.ts_cols = columns; if (ioctl(fd, TIOCSSIZE, &tty_size) == -1) { return; /* can't change tty size */ } if (inform == TRUE) { /* inform process */ if (ioctl(fd, TIOCGPGRP, &pgrp) == 0) { killpg(pgrp, SIGWINCH); } } } SHAR_EOF if test 22349 -ne `wc -c < 'manager.c'` then echo 'shar: error transmitting 'manager.c' (should have been 22349 charcaters)' fi fi exit Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214
nwh@hrc63.UUCP (07/11/86)
#! /bin/sh # This is a shell archive. To extract the files type 'sh file' # This archive created on Thu Jul 10 13:33:09 WET 1986 echo 'shar: extracting 'window.c' ( 15624 characters)' if test -f 'window.c' then echo "shar: will not overwrite existing file 'window.c'" else cat << \SHAR_EOF > 'window.c' #ifndef lint static char sccsid[] = "@(#)window.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include <sys/ioctl.h> #include "grotwin.h" /* externals defined in grotwin.c */ /* list of priorities - end referenced by tail variable */ extern struct windowdetails win[MAX_WINDOWS_PLUS]; extern int window_priorities[]; /* window details */ extern int **screen_priority; /* which window visible at each point */ extern int active_windows; /* number of user windows active */ extern int screenlines; /* lines on users screen */ extern int screencolumns; /* columns on users screen */ extern int time_start; /* where time string is placed */ extern int time_end; /* where time string ends */ extern int win_border; /* window border characteristic */ extern int timeon; /* display clock on top edge */ extern char **masterscreen; /* hook to stdscr from curses */ extern char *progname; /* program name stripped of any /'s */ extern char *home_shell; /* shell from users $SHELL variable */ extern char nottimestring[]; /* used when time is not displayed */ int tail = 0; /* tail of window priority list */ /* create instance of window */ createwin(winno, lines, columns, ystart, xstart) int winno, lines, columns, ystart, xstart; { register struct windowdetails *ptr; char **virtwin(), *rindex(), *tty; ptr = &(win[winno]); /* remember that a border surrounds window */ ptr->lines = lines - 2; ptr->columns = columns - 2; ptr->y_start = ystart + 1; ptr->x_start = xstart + 1; ptr->y_end = ptr->y_start + ptr->lines; ptr->x_end = ptr->x_start + ptr->columns; ptr->y_current = 0; ptr->x_current = 0; ptr->screenptr = virtwin('\0'); win_initialise(winno, ' '); /* skip pass "/dev/" */ tty = 1 + rindex(ptr->pseudo_ttyname, '/'); utmp_insert(tty, progname); ptr->page_buf_length = 0; strcpy(ptr->page_buf, ""); ptr->output_blocked = FALSE; ptr->output_paged = FALSE; ptr->paged_page_full = FALSE; ptr->line_count = 0; ptr->overwrite = FALSE; ptr->cursor_addressing = FALSE; ptr->cursor_addr_passes = 0; ptr->cursor_addr_row = 0; ptr->standout_mode = WIN_STANDEND; ptr->readgrain = (ptr->lines / READGRAIN) * ptr->columns; if (ptr->readgrain > BUFSIZ) { ptr->readgrain = BUFSIZ; } ptr->active = TRUE; ++active_windows; top_dog(winno); /* place cursor in new window */ wmove(stdscr, ptr->y_current + ptr->y_start, ptr->x_current + ptr->x_start); touchwin(stdscr); wrefresh(stdscr); } /* provide user with half screen height window at near full screen width */ new_window() { static int i = OVERLAY - 1; int columns, rows, xstart, ystart, new_winno; i = (i + 1) % OVERLAY; columns = screencolumns - (OVERLAY * 2); rows = screenlines / 2; /* offset slightly to enable multpile instances to be seen */ xstart = (i + 1) * 2; ystart = (rows / 2) + i; check_size(&columns, &rows, &xstart, &ystart); new_winno = win_instance(rows, columns, ystart, xstart, home_shell); if (new_winno != -1) { next_input_window(new_winno); } else { write(1, "\007", 1); } } /* allocate a window screen to hold characters and initialise it */ char ** virtwin(filler) char filler; { char *calloc(); register int line, x; register char **virt_ptr, *lineptr; virt_ptr = (char **) calloc(screenlines, sizeof (char *)); for (line = 0 ; line < screenlines ; ++line) { virt_ptr[line] = calloc(screencolumns, sizeof(char)); lineptr = virt_ptr[line]; for (x = 0 ; x < screencolumns ; ++x) { lineptr[x] = filler; } } return(virt_ptr); } free_virt_win(virt_ptr) /* return structure to free list */ char **virt_ptr; { int line; for (line = 0 ; line < screenlines ; ++line) { free(virt_ptr[line]); } free(virt_ptr); } /******************* * Screen priority positions to decide display areas. * Originally declared via virtwin(), but has been changed to use * ints (instead of chars) to hopefully speed it up (by requiring less * conversions to ints for comparisons etc.). *******************/ int ** priority_win() { char *calloc(); register int x, *lineptr, **priority_ptr, line; priority_ptr = (int **) calloc(screenlines, sizeof (int *)); for (line = 0 ; line < screenlines ; ++line) { priority_ptr[line] = (int *) calloc(screencolumns, sizeof(int)); lineptr = priority_ptr[line]; for (x = 0 ; x < screencolumns ; ++x) { lineptr[x] = -1; } } return(priority_ptr); } free_priority_win(priority_ptr) /* return structure to free store */ int **priority_ptr; { register int line; for (line = 0 ; line < screenlines ; ++line) { free(priority_ptr[line]); } free(priority_ptr); } /* fill window with specified fill pattern (usually a ' ') */ win_initialise(winno, filler) int winno; char filler; { register char *lineptr; register int x, y; register int y_end, x_end; register struct windowdetails *ptr; int y_start, x_start; ptr = &(win[winno]); y_start = ptr->y_start; x_start = ptr->x_start; y_end = ptr->y_end; x_end = ptr->x_end; /* fill window with filler */ for (y = y_start ; y < y_end ; ++y) { lineptr = ptr->screenptr[y]; for (x = x_start ; x < x_end ; ++x) { lineptr[x] = filler; } } border(winno, BOX); } border(winno, command) /* place box around window */ int winno, command; { register struct windowdetails *ptr; register int x, y, y_end, x_end; int top, side, y_start, x_start; char winid; if (command == BOX) { top = '-' | win_border; side = '|' | win_border; } else { /* NOT_BOX */ top = ' '; side = ' '; } ptr = &(win[winno]); y_start = ptr->y_start - 1; x_start = ptr->x_start - 1; y_end = ptr->y_end; x_end = ptr->x_end; /* side of box */ for (y = y_start + 1; y < y_end ; ++y) { ptr->screenptr[y][x_start] = side; ptr->screenptr[y][x_end] = side; } /* top and bottom */ for (x = x_start + 1 ; x < x_end ; ++x) { ptr->screenptr[y_start][x] = top; ptr->screenptr[y_end][x] = top; } ptr->screenptr[y_start][x_start] = ' '; ptr->screenptr[y_start][x_end] = ' '; ptr->screenptr[y_end][x_start] = ' '; ptr->screenptr[y_end][x_end] = ' '; /* place window number in top corners (ish) */ if (winno < MAX_WINDOWS && top != ' ') { winid = '0' + winno; ptr->screenptr[y_start][x_start + 3] = winid; ptr->screenptr[y_start][x_end - 3] = winid; } } /* make window topmost one and adjust visibility priority list accordingly */ top_dog(winno) int winno; { register struct windowdetails *ptr; register int x, *line_priority; register char *lineptr, *masterline; int y, y_start, x_start, y_end, x_end, start, i; ptr = &(win[winno]); y_start = ptr->y_start - 1; x_start = ptr->x_start - 1; y_end = ptr->y_end; x_end = ptr->x_end; /* ensure that time is not overwritten on top border */ if (y_start == 0) { lineptr = ptr->screenptr[0]; line_priority = screen_priority[0]; masterline = masterscreen[0]; for (x = x_start ; x <= x_end ; ++x) { if (x < time_start || x > time_end) { line_priority[x] = winno; masterline[x] = lineptr[x]; } else { nottimestring[x - time_start] = lineptr[x]; line_priority[x] = winno; if (timeon == FALSE) { masterline[x] = lineptr[x]; } } } ++y_start; } for (y = y_start ; y <= y_end ; ++y) { lineptr = ptr->screenptr[y]; line_priority = screen_priority[y]; masterline = masterscreen[y]; for (x = x_start; x <= x_end ; ++x) { line_priority[x] = winno; masterline[x] = lineptr[x]; } } /* find window in list */ for (i = 0 ; i < tail ; ++i) { if (window_priorities[i] == winno) { break; } } if (i == tail) { /* new window */ ++tail; } /* push down priorities and place latest on top */ while (i > 0) { window_priorities[i] = window_priorities[i - 1]; --i; } window_priorities[0] = winno; } /******************* * Either make window backmost one or remove completely by * making window lie behind fixed screen background window. * Adjust visibility priority list accordingly. *******************/ bottom_dog(winno, status) int winno, status; { register struct windowdetails *ptr; register int x, *line_priority; register char *lineptr, *masterline; int y, i, j, start, end; /* find position in list */ for (start = 0 ; start < tail ; ++start) { if (window_priorities[start] == winno) { break; } } if (status == HIDE) { end = tail - 2; } else { /* REMOVE */ end = tail - 1; --tail; /* since window no longer exists */ } /* move up and place winno in list */ for (i = start ; i < end ; ++i) { window_priorities[i] = window_priorities[i + 1]; } window_priorities[end] = winno; /* dumb removal of window whilst maintaining window priorities */ ptr = &(win[winno]); for (i = tail - 1 ; i >= 0 ; --i) { j = window_priorities[i]; for (y = ptr->y_start - 1 ; y <= ptr->y_end ; ++y) { lineptr = win[j].screenptr[y]; line_priority = screen_priority[y]; masterline = masterscreen[y]; for (x = ptr->x_start - 1 ; x <= ptr->x_end ; ++x) { /* ensure that time is not overwritten */ if (y == 0 && x >= time_start && x <= time_end){ if (lineptr[x] != '\0') { nottimestring[x - time_start] = lineptr[x]; } if (timeon == TRUE) { continue; } } if (lineptr[x] != '\0') { line_priority[x] = j; masterline[x] = lineptr[x]; } } } } } top_corners(winno) /* expose top corners of a window */ int winno; { register struct windowdetails *ptr; register int y, x; int diff; ptr = &(win[winno]); y = ptr->y_start - 1; x = ptr->x_start - 1; diff = ptr->x_end - x; /* for both top corners */ for ( ; x <= ptr->x_end ; x += diff) { if (screen_priority[y][x] == winno) { if (y == 0) { /* check for overwriting time */ if (x >= time_start && x <= time_end) { nottimestring[x - time_start] = ptr->screenptr[y][x]; if (timeon == TRUE) { continue; } } } masterscreen[y][x] = ptr->screenptr[y][x]; } } } /******************* * Doesn't check if widen action will have any effect. This allows * user to send SIGWINCH to process if he really wants to. *******************/ widen_window(winno, direction) int winno, direction; { struct windowdetails *ptr; int old_x_start, old_x_end, x_diff, x; int old_y_start, old_y_end, y_diff, y; char **virtwin(), **old_screenptr; ptr = &(win[winno]); border(winno, NOT_BOX); /* remove window border */ bottom_dog(winno, REMOVE); /* remove window */ if (direction == HORIZONTAL_EXPAND) { y_diff = 0; old_x_start = ptr->x_start; /* set up new x size */ old_x_end = ptr->x_end; ptr->x_start = 1; x_diff = old_x_start - ptr->x_start; ptr->columns = screencolumns - 2; ptr->x_end = ptr->x_start + ptr->columns; } else { /* VERTICAL_EXPAND */ x_diff = 0; old_y_start = ptr->y_start; /* set up y new size */ old_y_end = ptr->y_end; ptr->y_start = 1; y_diff = old_y_start - ptr->y_start; ptr->lines = screenlines - 2; ptr->y_end = ptr->y_start + ptr->lines; } check_size(&ptr->columns, &ptr->lines, &ptr->x_start, &ptr->y_start); /* copy old window contents to new window */ old_screenptr = ptr->screenptr; ptr->screenptr = virtwin('\0'); win_initialise(winno, ' '); if (direction == HORIZONTAL_EXPAND) { for (y = ptr->y_start ; y < ptr->y_end ; ++y) { for (x = old_x_start ; x < old_x_end ; ++ x) { ptr->screenptr[y][x - x_diff] = old_screenptr[y][x]; } } } else { /* VERTICAL_EXPAND */ for (y = old_y_start ; y < old_y_end ; ++y) { for (x = ptr->x_start ; x < ptr->x_end ; ++ x) { ptr->screenptr[y - y_diff][x] = old_screenptr[y][x]; } } } top_dog(winno); next_input_window(winno); /******************* * place cusor in window - can't rely on manager() to do this * with input_changed flag since it may be servicing output before * input_changed check is performed again ! *******************/ wmove(stdscr, ptr->y_current + ptr->y_start, ptr->x_current + ptr->x_start); free_virt_win(old_screenptr); /* set new (?) tty size */ set_tty_size(ptr->slavefd, ptr->lines, ptr->columns, TRUE); } window_info(winno) /* display information about windows */ int winno; { static char command_string[] = "[ w or 0-9 for next window, return to exit ] "; int found; char c; while (1) { selected_window_info(winno); fputs(command_string, stdout); fflush(stdout); do { read(1, &c, 1); if (c == '\r' || c == '\n') { return(FALSE); } found = FALSE; if (c == 'w') { do { winno = (winno + 1) % MAX_WINDOWS; } while (win[winno].active == FALSE); found = TRUE; } else { winno = c - '0'; if (winno >= 0 && winno <= MAX_WINDOWS) { if (win[winno].active == TRUE) { found = TRUE; } } else { winno += '0'; /* restore */ } } if (found == TRUE) { /* remove command */ clear_line(sizeof(command_string)); } else { /* indicate error */ write(1, "\007", 1); } } while (found != TRUE); } } static selected_window_info(winno) /* display dumbly status of a window */ int winno; { struct windowdetails *ptr; char *status(), *truth_status(); ptr = &(win[winno]); printf("\r\nstatus information for window %d\r\n\n", winno); printf(" tty name - %s\r\n", ptr->pseudo_ttyname); printf(" lines - %-6d\r\n", ptr->lines); printf(" columns - %-6d\r\n", ptr->columns); printf(" running - %s [ pid = %d ]\r\n", ptr->progy, ptr->slavepid); printf(" XOFF - %s\r\n", status(ptr->output_blocked)); printf(" page mode - %s\r\n", status(ptr->output_paged)); if (ptr->output_paged) { printf("page mode full - %s\r\n", truth_status(ptr->paged_page_full)); } printf("page overwrite - %s\r\n", status(ptr->overwrite)); putchar('\n'); } char * status(expr) int expr; { if (expr) { return("ON"); } else { return("OFF"); } } char * truth_status(expr) int expr; { if (expr) { return("TRUE"); } else { return("FALSE"); } } SHAR_EOF if test 15624 -ne `wc -c < 'window.c'` then echo 'shar: error transmitting 'window.c' (should have been 15624 charcaters)' fi fi exit Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214
nwh@hrc63.UUCP (07/11/86)
#! /bin/sh # This is a shell archive. To extract the files type 'sh file' # This archive created on Thu Jul 10 16:38:07 WET 1986 echo 'shar: extracting 'update.c' ( 9169 characters)' if test -f 'update.c' then echo "shar: will not overwrite existing file 'update.c'" else cat << \SHAR_EOF > 'update.c' #ifndef lint static char sccsid[] = "@(#)update.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include "grotwin.h" /* defines specific to grotty terminal type (see term and termcap below) */ #define INSERT_LINE ( 0x01 ) #define DELETE_LINE ( 0x02 ) #define STANDOUT ( 0x05 ) #define STANDEND ( 0x06 ) #define BELL ( 0x07 ) #define BACKSPACE ( 0x08 ) #define TAB ( 0x09 ) #define CURSOR_DOWN ( 0x0A ) #define CURSOR_UP ( 0x0B ) #define CLEAR_SCREEN ( 0x0C ) #define NONDESTRUCT_SPACE ( 0x18 ) #define DELETE_TO_EOL ( 0x19 ) #define CURSOR_ADDR ( 0x1B ) #define TABS ( 0x08 ) #define SPECIAL '\0' extern struct windowdetails win[MAX_WINDOWS_PLUS]; extern int **screen_priority; extern char **masterscreen; /******************* * * My very own terminal type. Loosely based on a adm3a and Newbury 8000 * Used in grotwin.c but defined here since this module handles terminal * escape sequences. Although a vt100 definition would probably be better, * this simple terminal type generally uses less I/O to achieve the same * goals (ie. cursor movement), at the cost of programs that use * vt100 escape sequences without looking at TERMCAP. * * Termcap entry should also be placed in in /etc/termcap as well. * *******************/ char term[] = "TERM=grotty"; char termcap[] = "TERMCAP=gr|grotty|grotwin pseudo tty:\ co#%d:li#%d:cl=\^L:cm=\\E=%%+ %%+ :am:bs:nd=\^X:ce=\^Y:\ up=\^K:do=\^J:kb=\^H:so=\^E:se=\^F:al=\^A:dl=\^B:"; /* updates specified window with string */ update_screen(winno) int winno; { register struct windowdetails *ptr; register int x, y; int length, temp; char *strcpy(), *string; ptr = &(win[winno]); if (ptr->paged_page_full || ptr->output_blocked) { /* can't o/p */ return; } string = ptr->page_buf; length = ptr->page_buf_length; while (length-- > 0) { *string &= 0x7F; /* strip off top most bit */ if (ptr->cursor_addressing) { temp = *string++; /* check for cursor addressing */ if (++(ptr->cursor_addr_passes) == 1) { if (temp != '=') { ptr->cursor_addressing = FALSE; ptr->cursor_addr_passes = 0; mywaddch(winno, CURSOR_ADDR); mywaddch(winno, temp); continue; } } else { if (ptr->cursor_addr_passes == 2) { ptr->cursor_addr_row = temp - ' '; if (ptr->cursor_addr_row >= ptr->lines){ ptr->cursor_addr_row = ptr->lines - 1; } else { if (ptr->cursor_addr_row < 0) { ptr->cursor_addr_row =0; } } } else { /* move it */ ptr->y_current = ptr->cursor_addr_row; ptr->x_current = temp - ' '; if (ptr->x_current >= ptr->columns) { ptr->x_current = ptr->columns-1; } else { if (ptr->x_current < 0) { ptr->x_current = 0; } } ptr->cursor_addressing = FALSE; ptr->cursor_addr_passes = 0; } } continue; } y = ptr->y_current; x = ptr->x_current; switch (*string) { case '\r' : ptr->x_current = 0; break; case '\n' : if (ptr->output_paged && ++ptr->line_count >= ptr->lines) { ptr->paged_page_full = TRUE; ptr->line_count = 0; /* remember rest of output */ strcpy(ptr->page_buf, string); ptr->page_buf_length = length + 1; return; } if (y >= (ptr->y_end - ptr->y_start - 1)) { ptr->y_current = 0; if (! ptr->overwrite) { mywlineop(winno, DELETE_LINE); ptr->y_current = y; } } else { ++ptr->y_current; } if (ptr->overwrite) { mywline_clear(winno, ptr->y_current + ptr->y_start); } break; case CURSOR_UP : if (y > 0) { --y; } ptr->y_current = y; break; case CLEAR_SCREEN : mywclear(winno); break; case CURSOR_ADDR : ptr->cursor_addressing = TRUE; break; case TAB : x = x + TABS - (x % TABS); if (x > ptr->columns - 1) { x = ptr->columns - 1; } ptr->x_current = x; break; case BACKSPACE : if (x > 0) { --ptr->x_current; } break; case STANDOUT : ptr->standout_mode = WIN_STANDOUT; break; case STANDEND : ptr->standout_mode = WIN_STANDEND; break; case BELL : write(1, "\007", 1); break; case NONDESTRUCT_SPACE : if (x < ptr->columns - 1) { ++x; } else { ptr->y_current = ++y; x = 0; } ptr->x_current = x; break; case INSERT_LINE : mywlineop(winno, INSERT_LINE); break; case DELETE_LINE : mywlineop(winno, DELETE_LINE); break; case DELETE_TO_EOL : temp = x; while (temp++ < ptr->columns) { mywaddch(winno, ' '); } ptr->x_current = x; break; case SPECIAL : break; default : mywaddch(winno, *string); if (x >= ptr->columns - 1) { /* insert \r\n into string */ length = string_insert(string + 1, "\r\n"); } break; } ++string; } ptr->page_buf_length = 0; } /* insert s2 at beginning of s1 after shifting s2 to make room */ /* returns length of new string */ static string_insert(s1, s2) char *s1, *s2; { register char *src_ptr, *dest_ptr; register int i; int n1, n2; n1 = strlen(s1); n2 = strlen(s2); src_ptr = s1 + n1; dest_ptr = src_ptr + n2; /* include string delimiter */ i = n1 + 1; while (i--) { *dest_ptr-- = *src_ptr--; } i = n2; while (i--) { *s1++ = *s2++; } return(n1 + n2); } static mywaddch(winno, c) /* add char to window */ int winno; char c; { register struct windowdetails *ptr; register int x, y; ptr = &(win[winno]); x = ptr->x_current + ptr->x_start; y = ptr->y_current + ptr->y_start; c |= ptr->standout_mode; ptr->screenptr[y][x] = c; if (screen_priority[y][x] == winno) { masterscreen[y][x] = c; } ++ptr->x_current; } static mywlineop(winno, command) /* insert - delete a line on the screen */ int winno, command; { register struct windowdetails *ptr; register int x, *line_priority; register char *image, *screenline; int y, y_actual_current; char *temp; ptr = &(win[winno]); y_actual_current = ptr->y_current + ptr->y_start; /* move lines around */ if (command == DELETE_LINE) { temp = ptr->screenptr[y_actual_current]; for (y = y_actual_current ; y < ptr->y_end - 1 ; ++y) { ptr->screenptr[y] = ptr->screenptr[y + 1]; } ptr->screenptr[ptr->y_end - 1] = temp; mywline_clear(winno, ptr->y_end - 1); } else { /* INSERT_LINE */ temp = ptr->screenptr[ptr->y_end - 1]; for (y = ptr->y_end - 1 ; y > y_actual_current ; --y) { ptr->screenptr[y] = ptr->screenptr[y - 1]; } ptr->screenptr[y_actual_current] = temp; mywline_clear(winno, y_actual_current); } /* update real screen */ for (y = y_actual_current ; y < ptr->y_end ; ++y) { line_priority = screen_priority[y]; screenline = masterscreen[y]; image = ptr->screenptr[y]; for (x = ptr->x_start ; x < ptr->x_end ; ++x) { if (line_priority[x] == winno) { screenline[x] = image[x]; } } } } static mywline_clear(winno, line) int winno, line; { register struct windowdetails *ptr; register int x, *line_priority; register char *lineptr, *masterline; ptr = &(win[winno]); lineptr = ptr->screenptr[line]; line_priority = screen_priority[line]; masterline = masterscreen[line]; for (x = ptr->x_start ; x < ptr->x_end ; ++x) { lineptr[x] = ' '; if (line_priority[x] == winno) { masterline[x] = ' '; } } } mywclear(winno) /* clear window */ int winno; { struct windowdetails *ptr; int y; ptr = &(win[winno]); for (y = ptr->y_start ; y < ptr->y_end ; ++y) { mywline_clear(winno, y); } ptr->y_current = 0; ptr->x_current = 0; ptr->line_count = 0; } SHAR_EOF if test 9169 -ne `wc -c < 'update.c'` then echo 'shar: error transmitting 'update.c' (should have been 9169 charcaters)' fi fi echo 'shar: extracting 'utmp.c' ( 3641 characters)' if test -f 'utmp.c' then echo "shar: will not overwrite existing file 'utmp.c'" else cat << \SHAR_EOF > 'utmp.c' #ifndef lint static char sccsid[] = "@(#)utmp.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * This program attempts to overcome the deficiences of 4.2 not * having any visible routines to update the utmp file like Sys V. * Utmp is used by utilities such as who to find out who is * logged on. On Sun 4.2, /etc/utmp can be written by * anyone, so no setuid etc. is required to alter it. * * Accessed only via utmp_insert(tty, id) and utmp_delete(tty) * to enable routines to be kept internal. Uses host field * to indicate that pseudo tty is being used by grotwin (I know its * not what the field is meant for, but it looks better all the same). * * Getpwent() appears to hunk in approx 30 K of unnecessary code * to deal with remote hosts etc. * **************************************/ #include <stdio.h> #include <utmp.h> #include <pwd.h> #define UTMP_INSERT ( 1 ) #define UTMP_DELETE ( 2 ) /* max length of tty name entry in /etc/ttys file */ #define TTYS_MAXLEN ( 20 ) typedef enum { FALSE, TRUE } bool; utmp_insert(tty, id) /* add entry to utmp */ char *tty, *id; { return(utmp_update(tty, id, UTMP_INSERT)); } utmp_delete(tty) /* delete entry from utmp */ char *tty; { return(utmp_update(tty, "", UTMP_DELETE)); } static utmp_update(tty, id, mode) /* update utmp file */ char *tty, *id; int mode; { static char utmp[] = "/etc/utmp"; struct utmp entry; struct passwd *getpwuid(), *passwd_entry; FILE *fp, *fopen(); long *time(); int position, i; char *getlogin(), *user_name; position = utmp_pos(tty); if (position == -1) { return(-1); } if ((fp = fopen(utmp, "r+")) == NULL) { return(-1); } if (mode == UTMP_INSERT) { /* try to find user */ if ((user_name = getlogin()) == NULL) { if ((passwd_entry = getpwuid(getuid())) == NULL) { return(-1); /* give up */ } user_name = passwd_entry->pw_name; } /******************* * In case strings are too long to fit utmp structure, * copy max - 1 chars and null terminate at end. *******************/ strncpy(entry.ut_name, user_name, sizeof(entry.ut_name) - 1); entry.ut_host[sizeof(entry.ut_name) - 1] = '\0'; strncpy(entry.ut_host, id, sizeof(entry.ut_host) - 1); entry.ut_host[sizeof(entry.ut_host) - 1] = '\0'; } else { /* UTMP_DELETE */ for (i = 0 ; i < sizeof(entry.ut_name) ; ++i) { entry.ut_name[i] = '\0'; } for (i = 0 ; i < sizeof(entry.ut_host) ; ++i) { entry.ut_host[i] = '\0'; } } strncpy(entry.ut_line, tty, sizeof(entry.ut_line) - 1); entry.ut_line[sizeof(entry.ut_host) - 1] = '\0'; (void) time(&(entry.ut_time)); if (fseek(fp, (long) (sizeof(entry) * position), 0) != -1) { (void) fwrite(&entry, sizeof(entry), 1, fp); } (void) fclose(fp); return(position); } utmp_pos(tty) /* like ttyslot */ char *tty; { static char ttys[] = "/etc/ttys"; FILE *fp, *fopen(); int position; bool found; char temp[TTYS_MAXLEN]; if ((fp = fopen(ttys, "r")) == NULL) { return(-1); } found = FALSE; for (position = 1 ; fgets(temp, TTYS_MAXLEN, fp) != NULL ; ++position) { temp[strlen(temp) - 1] = '\0'; /* remove trailing \n */ if (strcmp(temp + 2, tty) == 0) { found = TRUE; break; } } (void) fclose(fp); if (found == FALSE) { return(-1); } return(position); } SHAR_EOF if test 3641 -ne `wc -c < 'utmp.c'` then echo 'shar: error transmitting 'utmp.c' (should have been 3641 charcaters)' fi fi exit Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214
nwh@hrc63.UUCP (07/11/86)
#! /bin/sh
# This is a shell archive. To extract the files type 'sh file'
# This archive created on Fri Jul 11 13:47:21 WET 1986
export PATH; PATH=/bin:$PATH
echo 'shar: extracting 'grotwin.h' ( 4001 characters)'
if test -f 'grotwin.h'
then
echo "shar: will not overwrite existing file 'grotwin.h'"
else
cat << \SHAR_EOF > 'grotwin.h'
/***************************************
*
* Author : Nigel Holder
*
* Date : 10 July 1986
*
* Grotwin - provides a somewhat primitive windowing capability
* for people unfortunate enough to use the standard 24 x 80 type
* of terminal when the console is in use. Definitely written for
* 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo
* terminals or have the select() facility (amongst other things !)
* (version 8 should fix this).
*
* Files used :-
*
* grotwin.c - window system initialisation
* deals with startup files
*
* manager.c - window manager
* deals with input and output
*
* window.c - window manipulator
* deals with aspects concerning windows
* during normal usage
*
* update.c - simulates dumb terminal for use
* with window manager.
*
* grotwin.h - header file for above
*
* utmp.c - utmp manipulator
* updates utmp entries for each window
*
* grotwin.make - makefile for above
*
* Bugs :-
*
* Can't have the situation where no windows are present !
* Needs a vt100 like terminal type
*
***************************************/
#include <stdio.h>
#include <sys/param.h>
#include <curses.h>
/*******************
* The following defines are very ascii dependent (which is good since its
* anti-IBM EBSDIC)
*******************/
/*******************
* maximum number of windows (maximum of 10 imposed), depends on
* stdin, stdout, stderr, utmp update and two per window
* (NOFILE is 30 on a Sun 2 running op sys version 2.2, normally 20
* though on unix systems)
*******************/
#define SYSTEM_WINDOWS ( (NOFILE - 4) / 2 )
#define MAX_WINDOWS ( 10 ) /* windows 0 to 9 */
#if ( MAX_WINDOWS > SYSTEM_WINDOWS )
#undef MAX_WINDOWS
#define MAX_WINDOWS ( SYSTEM_WINDOWS )
#endif MAX_WINDOWS
#define MAX_WINDOWS_PLUS ( MAX_WINDOWS + 1 )
#define REALLY_CONFUSED ( 2 )
#define READGRAIN ( 2 )
#define CTRL_MASK ( 0x1F )
#define WIN_SWITCH_NUMBER ( (unsigned char) 0x80 )
/* user window control commands */
#define SELECT ( 'a' & CTRL_MASK )
#define WIN_SWITCH ( 'w' & CTRL_MASK )
#define NEW_WINDOW ( 'n' & CTRL_MASK )
#define FORCED_EXIT ( 'f' & CTRL_MASK )
#define REMOVE_WIN ( 'r' & CTRL_MASK )
#define CLEAR ( 'z' & CTRL_MASK )
#define MOVE ( 'm' & CTRL_MASK )
#define EXPAND ( 'x' & CTRL_MASK )
#define VERTICAL_EXPAND ( 'v' & CTRL_MASK )
#define HORIZONTAL_EXPAND ( 'c' & CTRL_MASK )
#define EXPOSE_WINDOW ( 'e' & CTRL_MASK )
#define HIDE_WINDOW ( 'h' & CTRL_MASK )
#define SCREEN_REDRAW ( 'l' & CTRL_MASK )
#define XON ( 'q' & CTRL_MASK )
#define XOFF ( 's' & CTRL_MASK )
#define PAGE_MODE ( 'p' & CTRL_MASK )
#define OVERWRITE_MODE ( 'o' & CTRL_MASK )
#define TIME_TOGGLE ( 't' & CTRL_MASK )
#define INFORM ( 'i' & CTRL_MASK )
#define HELP ( '?' )
#define BUFLEN ( 256 )
#define TIME_RES ( 30 )
#define HIDE ( 0 )
#define DISPLAY ( 2 )
#define REMOVE ( 1 )
#define MAXARGS ( 20 )
#define WORLD_MIN_COLUMNS ( 20 )
#define WORLD_MIN_ROWS ( 6 )
#define MIN_COLUMNS ( 3 )
#define MIN_ROWS ( 4 ) /* 3 doesn't work */
#define OVERLAY ( 4 )
#define BOX ( 1 )
#define NOT_BOX ( 0 )
#define BAD_INPUT ( 0 )
#define MISTAKE ( 1 )
#define NORMAL ( 2 )
#define BACKGROUND '.'
#define WIN_STANDOUT ( (char) 0x80 )
#define WIN_STANDEND ( (char) 0x00 )
struct windowdetails {
int active;
int masterfd;
int slavefd;
int slavepid;
int slavemaskfd;
int output_blocked;
int output_paged;
int line_count;
int paged_page_full;
int overwrite;
int cursor_addressing;
int cursor_addr_passes;
int cursor_addr_row;
int lines;
int columns;
int x_current;
int y_current;
int x_start;
int y_start;
int x_end;
int y_end;
int readgrain;
int page_buf_length;
char **screenptr;
char *page_ptr;
char page_buf[BUFSIZ + 3];
char pseudo_ttyname[20];
char *progy;
char standout_mode;
};
SHAR_EOF
if test 4001 -ne `wc -c < 'grotwin.h'`
then
echo 'shar: error transmitting 'grotwin.h' (should have been 4001 charcaters)'
fi
fi
echo 'shar: extracting 'grotwin.make' ( 495 characters)'
if test -f 'grotwin.make'
then
echo "shar: will not overwrite existing file 'grotwin.make'"
else
cat << \SHAR_EOF > 'grotwin.make'
# Makefile for grotwin window manager on 24 x 80 type terminals
grotwin: grotwin.o manager.o window.o update.o utmp.o
cc -O -o grotwin grotwin.o manager.o window.o update.o utmp.o -lcurses -ltermlib
grotwin.o: grotwin.c grotwin.h
cc -O -c grotwin.c
manager.o: manager.c grotwin.h
cc -O -c manager.c
window.o: window.c grotwin.h
cc -O -c window.c
update.o: update.c grotwin.h
cc -O -c update.c
utmp.o: utmp.c
cc -O -c utmp.c
man:
nroff -man grotwin.nroffman > grotwin.man
SHAR_EOF
if test 495 -ne `wc -c < 'grotwin.make'`
then
echo 'shar: error transmitting 'grotwin.make' (should have been 495 charcaters)'
fi
fi
exit
Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u
Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs
Chelmsford,
Essex. CM2 8HN.
+44 245 73331 ext. 3219 / 3214
nwh@hrc63.UUCP (07/11/86)
#! /bin/sh # This is a shell archive. To extract the files type 'sh file' # This archive created on Thu Jul 10 13:35:39 WET 1986 echo 'shar: extracting 'grotwin.nroffman' ( 9945 characters)' if test -f 'grotwin.nroffman' then echo "shar: will not overwrite existing file 'grotwin.nroffman'" else cat << \SHAR_EOF > 'grotwin.nroffman' .nh .TH GROTWIN 1 .SH NAME grotwin \- windows on ordinary terminals .SH SYNOPSIS grotwin [-n][-t][-b][-h] [file] .SH GETTING STARTED Grotwin may be initiated on any tty device (even a tty within itself). If there is a file called .grotwin in your home directory, or a file name is supplied, grotwin will create windows as specified by the file with their optional associated application programs. Otherwise grotwin will start up with two default windows of full size. .PP The grotwin system is ready as soon as the screen clears at startup. The last window to fire up is the one into which initial input will be received. Even before it appears, a user may type data and window commands, the data will be queued (for that window), whilst the window commands will act as soon as they can. .SH OPTIONS -n Don't look for startup file in users home directory. Use default of two full page windows instead if no startup file is specified. -t Turn time off at startup (default is on). -b Make window borders standout if capability exists (generally tacky, not advised). -h Display manual page (since it might not be installed in man directories). file optional startup file describing the size and position of required startup windows. .SH DESCRIPTION Grotwin provides a windowing capability for users of normal terminals. It is especially useful for actions such as editing more than one file at a time, editing and correcting errors, or for occasions when more than one screen at a time is desirable. Generally, the screen size of the terminal will not allow more than two fairly decent sized windows to be totally visible at any one time; it is often more useful to have full size overlapping windows when more than two windows are required. Windows The grotwin environment always has one fixed window which covers the whole screen; its sole purpose is to provide a fixed background pattern to enable unused portions of the screen to be visible. It also has a clock that displays the time in hours and minutes at the centre of its top edge. There must always be at least one other window in existence, otherwise grotwin will assume (rightly or wrongly), that the user has finished and will terminate. A window consists of a border with an identifying sequence on its top edge, and an associated tty device within it. Windows act as normal ttys (of varying sizes); they may be generated and manipulated at will. Windows are assigned unique visability properties that enable them to overlap in much the same way as stacking sheets of paper. A window will obscure portions of other windows which overlap and have lower priorities than itself. Priorities are assigned starting with the topmost window as the highest, and the backmost as the lowest. The current window (see Input to Windows), may under user control be made the backmost window by hiding it (see Window Commands); this has the effect of making it the lowest priority window in visibility terms, whilst maintaining it as the current window for input. The maximum number of windows that may be present at any one time is limited by then maximum number of files that the operating system system will allow a user to open concurrently. Input to Windows Only one window at a time may receive input from the keyboard; this is termed the current window. It is denoted by a single '+' character at each top corner of that window. This window is the one into which all typed input (except window commands), will be received. Input characters (as for a normal tty), may be queued for each window. This means that a user may type some characters in one window, move to another window and type some more characters into that window. The characters typed in the first window will be read when the application in that window is ready to do so. To switch input between windows, either the number of the window can be supplied 0 - 9 (as identified on the top edge of the window), or by the 'w' command. The 'w' command takes the numerically next window (including wrapround) and makes it the current window. Output to Windows Output to the windows is capable of occurring at any time (except when looking at the manual entry or status of windows when it is temporarily blocked). Whether such output is displayed on the screen depends on a windows visibility for all of its possible character positions. Window Commands Window commands may be freely interspersed with normal user input. They act immediatley unlike queued input to a tty. WIndow Command Terminology ctrl-a 'n' means ctrl-a followed by ctrl-n or 'n'. ctrl-a 'N' means ctrl-a followed by 'N' ctrl-a 'nN' means ctrl-a followed by ctrl-n, 'n' or 'N'. All commands are preceeded by ctrl-a, just as you may have used reached this information summary. The valid window commands are as follows :- Command Action 'nN' create new window 'w' make numerically next window current '0' - '9' make selected window current (range 1 to 9) 'xX' expand window to full size 'vV' expand window vertically to full size 'cC' expand window horizontally to full size 'zZ' clear window 'eE' expose window 'hH' hide window 'p' page mode on - type any character for next page 'P' page mode off 'o' overwrite mode on - type any character for next page 'O' overwrite mode off 'tT' toggle between displaying and removing time 'sS' XOFF for current window 'qQ' XON for current window 'R' force process within window to terminate - achieved by sending following signals in implied order until one is successful or all have been used SIGHUP, SIGINT, SIGTERM or SIGKILL 'F' forced exit from program (no questions asked) - same as the 'R' option, but for all windows 'iI' display status of windows '?' this message otherchar Character is input as if not preceeded by ctrl-a. Therefore to enter ctrl-a, type 'ctrl-a ctrl-a' Startup File If no startup file is specified, grotwin will search for the file $HOME/.grotwin to define the window sizes (this is expected to be the normal case). If this file is not found or is unreadable, the default is two windows of full screen size. The format of the startup file is as follows :- width height x_start y_start [command] It should be remembered that a window has a border around it; this is included in the window size specified. eg. 80 24 0 0 will define a window with a tty which has 22 lines and 78 columns Each window entry is checked to see if it is displayable and within output device screensize. If not, the start positions are adjusted accordingly. Finally, if required, the width and height dimensions are adjusted. Entry values of -1 and -2 have special meaning; for the width or height -1 means the full screen width or height respectivley, with -2 meaning half full screen width or height. For the start position -1 means the full width or height minus the the specified width or height, with -2 meaning half the full screen width or height. eg. -2 -2 0 0 four non-overlapping windows -2 -2 -1 0 covering the whole screen -2 -2 0 -1 -2 -2 -1 -1 Such values are particularly useful when grotwin is used on different sized terminals. eg. -1 -1 0 0 full width and height window Any adjustments made to actually fit a window on the screen are performed silently. Command is optional and may be any unix command. Since the shell is not used to parse the command, spaces are important; a single space or tab character should be used to separate strings (and of course things like pipes etc. as provided by the shell are not available). eg. 'ftp vax' would not work, whilst 'ftp vax' would. If no command is given (the normal case), grotwin will search the environment for the SHELL variable; if found it will run that shell, otherwise it will default to the Bourne shell (/bin/sh), simply because it is a standard shell that all systems have. .SH FILES $HOME/.grotwin /dev/ptyp[p-r]x /dev/ttyp[p-r]x /etc/utmp .SH BUGS This release has the following bugs (or features if you like): Doesn't pause when a clear screen command comes along if more output is ready. This is to speed things up by accepting any following output and displaying that along with the clear screen command. Particularly noticable in vi where ctrl-l appears to have no action (unless the window is large, you cannot see vi redraw). Output granularity chosen is subject to question. The use of a non-standard terminal type (grotty) does mean that the grotwin display can get well muddled up if escape sequences for other terminals are sent to it. Grotwin really needs a vt100 terminal emulator and associated TERMCAP entry. Grotwin terminates when there are no user windows present. It assumes that in such a situation it is probably better to start grotwin again with selected default windows than to ask it for new windows. Various functions like page mode, XOFF etc. provide no indication that they are selected (although this can be found out via the window status option). The ability to move or stretch windows by single characters. This was tried but removed since this facility is best provided by a mouse (rather than a keyboard), an object that a normal terminal lacks. .SH AUTHOR Nigel Holder Marconi Research, Chelmsford, Essex. CM2 8HN. UK JANET: yf21@uk.co.gec-mrc.u ARPA: yf21%u.gec-mrc.co.uk@ucl-cs +44 245 73331 ext. 3219 / 3214 SHAR_EOF if test 9945 -ne `wc -c < 'grotwin.nroffman'` then echo 'shar: error transmitting 'grotwin.nroffman' (should have been 9945 charcaters)' fi fi exit Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214
nwh@hrc63.UUCP (07/11/86)
The source for grotwin has been posted to net.sources. Grotwin is a windowing system for normal (eg. vt100) terminals. I'm away until July 27 (NOT August as indicated in README) so you'll have to sort out any problems between the net (I just had to get it posted before I went away). Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214
dpz@topaz.RUTGERS.EDU (David P. Zimmerman) (08/29/86)
I got a request to repost "grotwin" after I posted my mods to make it work on non-Suns, so here it is, with my mods included. Disclaimer: I'm just hacking on it, I'm not the author. ========================= cut right here --v ========================= # 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 README grotwin.1 grotwin.c grotwin.h grotwin.help manager.c update.c utmp.c window.c echo x - Makefile cat > "Makefile" << '//E*O*F Makefile//' # Makefile for grotwin window manager on 24 x 80 type terminals # Enable next line to install for Sun # SUNFLAGS=-DSUN grotwin: grotwin.o manager.o window.o update.o utmp.o cc -O -o grotwin grotwin.o manager.o window.o update.o utmp.o -lcurses -ltermlib grotwin.o: grotwin.c grotwin.h cc $(SUNFLAGS) -O -c grotwin.c manager.o: manager.c grotwin.h cc $(SUNFLAGS) -O -c manager.c window.o: window.c grotwin.h cc $(SUNFLAGS) -O -c window.c update.o: update.c grotwin.h cc $(SUNFLAGS) -O -c update.c utmp.o: utmp.c cc $(SUNFLAGS) -O -c utmp.c man: nroff -man grotwin.1 > grotwin.man //E*O*F Makefile// echo x - README cat > "README" << '//E*O*F README//' This is the README file for the grotwin window manager for normal terminals. You will have to place the following file definitions contained in grotwin.c in your locally developed program site /usr/bin equivelent (default of /usr/mrc shown) :- manfile[] = "/usr/mrc/grotwin.man"; helpfile[] = "/usr/mrc/grotwin.help"; system_more[] = "more"; You will also need to place the following entry in /etc/termcap (for commands like tset). #================================== # # Following entry for grotwin pseudo terminals # gr|grotty|grotwin pseudo tty:\ :co#78:li#22:cl=^L:cm=\E=%+ %+ :am:bs:nd=^X:ce=^Y:\ :up=^K:do=^J:kb=^H:so=^E:se=^F:al=^A:dl=^B: #================================== Having upshared each file entry via 'sh file', you will need to copy grotwin.make to Makefile before you can make grotwin. Having typed 'make' to make grotwin, you will also need to type 'make man' in order to nroff the manual page. I'm on holiday from August 12 - 27 inclusive, so any problems will have to sorted out by other people on the net during this period. Grotwin has been developed on a Sun 2 running release 2.2. It should however, run on any BSD 4.2 or 4.3 system, or indeed any system which has pseudo terminals and the ability to poll a file descriptor (select(), ioctl(FIONREAD) or otherwise). On my system at least, the screen occasionally gets corrupted. This is due to the serial line as it doesn't happen at 4800 or on a sun window. The fix is simple, simply get grotwin to redraw the screen as and when it occurs (via ctrl-a l); this is much more useful than slowing down the serial line speed. Have fun using it - and remember, it will never be as good as a bit-mapped windowing system. It is however, generally more useful than a normal screen when multiple sessions are required. I'd be grateful for any feedback/gripes but remember that as I'm away there will be a delay in my answering them. Nigel Holder UK JANET: yf21@uk.co.gec-mrc.u Marconi Research, ARPA: yf21%u.gec-mrc.co.uk@ucl-cs Chelmsford, Essex. CM2 8HN. +44 245 73331 ext. 3219 / 3214 ============================================================================= Changes made to grotwin by dpz, 08/23/86 I made relatively few changes to grotwin to get it to run on non-Sun Unix machines. I added a struct and a few #defines to the end of grotwin.h, and I also modified the Makefile so that enabling a -DSUN will compile grotwin for a Sun, and not enabling it will compile for "the rest of us". I guess you could call this modified grotwin version 2.2a, for want of some way to refer to it. --- David P. Zimmerman Arpa: dpz@topaz.rutgers.edu Uucp: ...{allegra | harvard | seismo | sri-iu | ut-sally}!topaz!dpz //E*O*F README// echo x - grotwin.1 cat > "grotwin.1" << '//E*O*F grotwin.1//' .nh .TH GROTWIN 1 .SH NAME grotwin \- windows on ordinary terminals .SH SYNOPSIS grotwin [-n][-t][-b][-h] [file] .SH GETTING STARTED Grotwin may be initiated on any tty device (even a tty within itself). If there is a file called .grotwin in your home directory, or a file name is supplied, grotwin will create windows as specified by the file with their optional associated application programs. Otherwise grotwin will start up with two default windows of full size. .PP The grotwin system is ready as soon as the screen clears at startup. The last window to fire up is the one into which initial input will be received. Even before it appears, a user may type data and window commands, the data will be queued (for that window), whilst the window commands will act as soon as they can. .SH OPTIONS -n Don't look for startup file in users home directory. Use default of two full page windows instead if no startup file is specified. -t Turn time off at startup (default is on). -b Make window borders standout if capability exists (generally tacky, not advised). -h Display manual page (since it might not be installed in man directories). file optional startup file describing the size and position of required startup windows. .SH DESCRIPTION Grotwin provides a windowing capability for users of normal terminals. It is especially useful for actions such as editing more than one file at a time, editing and correcting errors, or for occasions when more than one screen at a time is desirable. Generally, the screen size of the terminal will not allow more than two fairly decent sized windows to be totally visible at any one time; it is often more useful to have full size overlapping windows when more than two windows are required. Windows The grotwin environment always has one fixed window which covers the whole screen; its sole purpose is to provide a fixed background pattern to enable unused portions of the screen to be visible. It also has a clock that displays the time in hours and minutes at the centre of its top edge. There must always be at least one other window in existence, otherwise grotwin will assume (rightly or wrongly), that the user has finished and will terminate. A window consists of a border with an identifying sequence on its top edge, and an associated tty device within it. Windows act as normal ttys (of varying sizes); they may be generated and manipulated at will. Windows are assigned unique visability properties that enable them to overlap in much the same way as stacking sheets of paper. A window will obscure portions of other windows which overlap and have lower priorities than itself. Priorities are assigned starting with the topmost window as the highest, and the backmost as the lowest. The current window (see Input to Windows), may under user control be made the backmost window by hiding it (see Window Commands); this has the effect of making it the lowest priority window in visibility terms, whilst maintaining it as the current window for input. The maximum number of windows that may be present at any one time is limited by then maximum number of files that the operating system system will allow a user to open concurrently. Input to Windows Only one window at a time may receive input from the keyboard; this is termed the current window. It is denoted by a single '+' character at each top corner of that window. This window is the one into which all typed input (except window commands), will be received. Input characters (as for a normal tty), may be queued for each window. This means that a user may type some characters in one window, move to another window and type some more characters into that window. The characters typed in the first window will be read when the application in that window is ready to do so. To switch input between windows, either the number of the window can be supplied 0 - 9 (as identified on the top edge of the window), or by the 'w' command. The 'w' command takes the numerically next window (including wrapround) and makes it the current window. Output to Windows Output to the windows is capable of occurring at any time (except when looking at the manual entry or status of windows when it is temporarily blocked). Whether such output is displayed on the screen depends on a windows visibility for all of its possible character positions. Window Commands Window commands may be freely interspersed with normal user input. They act immediatley unlike queued input to a tty. WIndow Command Terminology ctrl-a 'n' means ctrl-a followed by ctrl-n or 'n'. ctrl-a 'N' means ctrl-a followed by 'N' ctrl-a 'nN' means ctrl-a followed by ctrl-n, 'n' or 'N'. All commands are preceeded by ctrl-a, just as you may have used reached this information summary. The valid window commands are as follows :- Command Action 'nN' create new window 'w' make numerically next window current '0' - '9' make selected window current (range 1 to 9) 'xX' expand window to full size 'vV' expand window vertically to full size 'cC' expand window horizontally to full size 'zZ' clear window 'eE' expose window 'hH' hide window 'p' page mode on - type any character for next page 'P' page mode off 'o' overwrite mode on - type any character for next page 'O' overwrite mode off 'tT' toggle between displaying and removing time 'sS' XOFF for current window 'qQ' XON for current window 'R' force process within window to terminate - achieved by sending following signals in implied order until one is successful or all have been used SIGHUP, SIGINT, SIGTERM or SIGKILL 'F' forced exit from program (no questions asked) - same as the 'R' option, but for all windows 'iI' display status of windows '?' this message otherchar Character is input as if not preceeded by ctrl-a. Therefore to enter ctrl-a, type 'ctrl-a ctrl-a' Startup File If no startup file is specified, grotwin will search for the file $HOME/.grotwin to define the window sizes (this is expected to be the normal case). If this file is not found or is unreadable, the default is two windows of full screen size. The format of the startup file is as follows :- width height x_start y_start [command] It should be remembered that a window has a border around it; this is included in the window size specified. eg. 80 24 0 0 will define a window with a tty which has 22 lines and 78 columns Each window entry is checked to see if it is displayable and within output device screensize. If not, the start positions are adjusted accordingly. Finally, if required, the width and height dimensions are adjusted. Entry values of -1 and -2 have special meaning; for the width or height -1 means the full screen width or height respectivley, with -2 meaning half full screen width or height. For the start position -1 means the full width or height minus the the specified width or height, with -2 meaning half the full screen width or height. eg. -2 -2 0 0 four non-overlapping windows -2 -2 -1 0 covering the whole screen -2 -2 0 -1 -2 -2 -1 -1 Such values are particularly useful when grotwin is used on different sized terminals. eg. -1 -1 0 0 full width and height window Any adjustments made to actually fit a window on the screen are performed silently. Command is optional and may be any unix command. Since the shell is not used to parse the command, spaces are important; a single space or tab character should be used to separate strings (and of course things like pipes etc. as provided by the shell are not available). eg. 'ftp vax' would not work, whilst 'ftp vax' would. If no command is given (the normal case), grotwin will search the environment for the SHELL variable; if found it will run that shell, otherwise it will default to the Bourne shell (/bin/sh), simply because it is a standard shell that all systems have. .SH FILES $HOME/.grotwin /dev/ptyp[p-r]x /dev/ttyp[p-r]x /etc/utmp .SH BUGS This release has the following bugs (or features if you like): Doesn't pause when a clear screen command comes along if more output is ready. This is to speed things up by accepting any following output and displaying that along with the clear screen command. Particularly noticable in vi where ctrl-l appears to have no action (unless the window is large, you cannot see vi redraw). Output granularity chosen is subject to question. The use of a non-standard terminal type (grotty) does mean that the grotwin display can get well muddled up if escape sequences for other terminals are sent to it. Grotwin really needs a vt100 terminal emulator and associated TERMCAP entry. Grotwin terminates when there are no user windows present. It assumes that in such a situation it is probably better to start grotwin again with selected default windows than to ask it for new windows. Various functions like page mode, XOFF etc. provide no indication that they are selected (although this can be found out via the window status option). The ability to move or stretch windows by single characters. This was tried but removed since this facility is best provided by a mouse (rather than a keyboard), an object that a normal terminal lacks. .SH AUTHOR Nigel Holder Marconi Research, Chelmsford, Essex. CM2 8HN. UK JANET: yf21@uk.co.gec-mrc.u ARPA: yf21%u.gec-mrc.co.uk@ucl-cs +44 245 73331 ext. 3219 / 3214 //E*O*F grotwin.1// echo x - grotwin.c cat > "grotwin.c" << '//E*O*F grotwin.c//' #ifndef lint static char sccsid[] = "@(#)grotwin.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include <sys/ioctl.h> #include <sys/file.h> #include <signal.h> #include "grotwin.h" extern int active_windows; /* number of user windows active */ /* system dependent stuff */ static char manfile[] = "/usr/mrc/grotwin.man"; static char helpfile[] = "/usr/mrc/grotwin.help"; static char system_more[] = "more"; static char default_shell[] = "/bin/sh"; static int ignore_homefile = FALSE; /* look for default file */ static char initfile[BUFSIZ + 1]; /* temp string buffer */ /* default startup values that may be changed by user command line */ int timeon = TRUE; /* display clock on top edge */ int win_border = WIN_STANDEND; /* normal brightness window borders */ int startup = TRUE; /* program startup period */ int confused = FALSE; /* for when program hangs up */ int **screen_priority = NULL; /* which window visible at each point */ char timestring[] = " hh:ss "; /* time on top edge of b/g window */ char nottimestring[] = "-------"; /* used when time is not displayed */ struct windowdetails win[MAX_WINDOWS_PLUS]; /* window details */ struct sgttyb masterterm; /* used to store initial tty state */ struct tchars mtchars; int mlocalmode; int screenlines; /* lines on users screen */ int screencolumns; /* columns on users screen */ int time_start; /* where time string is placed */ int time_end; /* where time string ends */ char **masterscreen; /* hook to stdscr from curses */ char *progname; /* program name stripped of any /'s*/ char *home_shell; /* shell from users $SHELL variable */ main(argc, argv) int argc; char *argv[]; { int length, i; char *rindex(), *getenv(); if ((progname = rindex(argv[0], '/')) != NULL) { ++progname; } else { progname = argv[0]; } /* scan command line */ while (argc > 1 && argv[1][0] == '-') { switch (argv[1][1]) { case 'n' : ignore_homefile = TRUE; break; case 't' : timeon = FALSE; break; case 'b' : win_border = WIN_STANDOUT; break; case 'h' : man_info(); exit(0); break; default : fprintf(stderr, "usage: %s [-n][-t][-b][-h] [file]\n\n", progname); fprintf(stderr, "\t\t-n\t-\tignore "); fprintf(stderr, "$HOME startup file\n"); fprintf(stderr, "\t\t-t\t-\tturn time off\n"); fprintf(stderr, "\t\t-b\t-\tbold windows\n"); fprintf(stderr, "\t\t-h\t-\thelp info\n"); exit(1); break; } --argc; /* skip over program name */ ++argv; } if (isatty(0) == 0) { fprintf(stderr, "%s: standard input not a tty\n", progname); exit(2); } if (argc > 1) { /* check for startup file */ strcpy(initfile, argv[1]); } else { if (ignore_homefile != TRUE) { /* check for default file in users home directory */ strcpy(initfile, getenv("HOME")); if (*initfile) { length = strlen(initfile); strcpy(initfile + length, "/."); strcpy(initfile + length + 2, progname); if (access(initfile, F_OK) == -1) { *initfile = '\0'; } } } else { *initfile = '\0'; } } for (i = 3 ; i < NOFILE ; ++i) { /* just to be sure */ close(i); } /* get user tty characteristics */ ioctl(0, TIOCGETP, &masterterm); ioctl(0, TIOCLGET, &mlocalmode); ioctl(0, TIOCGETC, &mtchars); screen_init(); /* set up curses and screen dimensions */ if (screencolumns < WORLD_MIN_COLUMNS || screenlines < WORLD_MIN_ROWS) { fprintf(stderr, "%s: screen size too small !\r\n", progname); fprintf(stderr, "screen size must be at least"); fprintf(stderr, " %d lines by %d columns\r\n", WORLD_MIN_ROWS, WORLD_MIN_COLUMNS); die(); } initialise(); /* initialise globals */ setup(); /* initial windows */ startup = FALSE; manager(); /* the window manager */ /* should never get here */ fprintf(stderr, "\r\n%s: internal error (fallen off end), sorry !\r\n", progname); die(); } static initialise() /* initialise globals */ { int **priority_win(); int fatal(), forced_die(), child_exit(), update_time(); int i, string_length; char **virtwin(); /* set all windows to inactive status */ for (i = 0 ; i < MAX_WINDOWS_PLUS ; ++i) { win[i].active = FALSE; strcpy(win[i].pseudo_ttyname, "unknown"); } /* work out where time will go */ string_length = sizeof(timestring) - 1; /* -1 for null at end */ time_start = (screencolumns - string_length) / 2; time_end = time_start + string_length - 1; screen_priority = priority_win(); /* to decide display areas */ /* slight liberty */ masterscreen = stdscr->_y; /* from curses package */ /******************* * This window is always the lowest priority one. * It cannot be accessed by the user and therefore provides * a background for the portions of the screen that are not used. *******************/ win[MAX_WINDOWS].y_start = 1; win[MAX_WINDOWS].x_start = 1; win[MAX_WINDOWS].y_end = screenlines - 1; win[MAX_WINDOWS].x_end = screencolumns - 1; win[MAX_WINDOWS].screenptr = virtwin('\0'); win_initialise(MAX_WINDOWS, BACKGROUND); for (i = 0 ; i < sizeof(nottimestring) - 1 ; ++i) { nottimestring[i] = BACKGROUND; } signal(SIGALRM, update_time); update_time(); if (timeon == TRUE) { /* display time first time round */ display_time(DISPLAY); } top_dog(MAX_WINDOWS); /* install background window */ /* stop a core dump - playing safe as leaves tty in previous state */ signal(SIGILL, fatal); signal(SIGTRAP, fatal); signal(SIGIOT, fatal); signal(SIGEMT, fatal); signal(SIGSEGV, fatal); signal(SIGBUS, fatal); signal(SIGSYS, fatal); /* signals to be used to stop program externally */ signal(SIGHUP, forced_die); signal(SIGINT, forced_die); signal(SIGQUIT, forced_die); signal(SIGTERM, forced_die); /* in order to pick up dead children (when a window is removed) */ signal(SIGCHLD, child_exit); } static setup() /* initial windows */ { FILE *fp, *fopen(); int columns, rows, xstart, ystart; int i, limit, items_read, winno; char *getenv(), *rindex(), *fgets(); char *program, buffer[BUFSIZ + 1], buffer2[BUFSIZ + 1]; /* find users favourite shell */ if ((home_shell = getenv("SHELL")) == NULL) { home_shell = default_shell; } /* check for file to define startup windows */ if (*initfile) { if ((fp = fopen(initfile, "r")) == NULL) { fprintf(stderr, "%s: can't open %s\r\n", progname, initfile); die(); } limit = MAX_WINDOWS; } else { /* default windows */ fp = NULL; limit = 2; } winno = -2; /* invalid value to enable test at end */ for (i = 0 ; i < limit ; ++i) { /* create each window */ program = buffer2; if (fp != NULL) { if (fgets(buffer, BUFSIZ, fp) == NULL) { break; } items_read = sscanf(buffer, "%d%d%d%d%[^\n\0]", &columns, &rows, &xstart, &ystart, program); if (items_read < 4) { winno = -2; break; } check_size(&columns, &rows, &xstart, &ystart); if (items_read < 5) { strcpy(program, home_shell); } else { while (*program == ' ' || *program == '\t') { ++program; } /* if just white space default to shell */ if (! *program) { strcpy(program, home_shell); } } } else { /* default window sizes */ columns = -1; rows = -1; xstart = -1; ystart = -1; check_size(&columns, &rows, &xstart, &ystart); strcpy(program, home_shell); } winno = win_instance(rows, columns, ystart, xstart, program); if (winno != -1) { next_input_window(winno); } } if (fp != NULL) { fclose(fp); } if (active_windows <= 0) { if (winno == -2) { fprintf(stderr, "%s: format of %s is invalid\r\n", progname, initfile); } else { fprintf(stderr, "%s: can't open any windows\r\n", progname); } die(); } } /******************* * Check (and change if necessary), that window dimensions selected can * be displayed on output device screen - if not then firstly move start * positions and then change dimensions if this fails. * * Special negative value meanings: * * In size field: * -1 - full screen width or height depending on position * -2 - half full screen width or height depending on position * * In position field: * -1 - screen width or height minus specified width or height * -2 - half full width or height start positiono * *******************/ check_size(columns, rows, xstart, ystart) int *columns, *rows, *xstart, *ystart; { if (*columns == -1 || *columns > screencolumns) { *columns = screencolumns; } else { if (*columns == -2) { /* half width */ *columns = screencolumns / 2; } else { if (*columns < MIN_COLUMNS) { /* too small */ *columns = MIN_COLUMNS; } } } if (*rows == -1 || *rows > screenlines) { *rows = screenlines; } else { if (*rows == -2) { /* half heigth */ *rows = screenlines / 2; } else { if (*rows < MIN_ROWS) { /* too small */ *rows = MIN_ROWS; } } } if (*xstart == -1) { /* pad to fit */ *xstart = screencolumns - *columns; } else { if (*xstart == -2) { /* half width */ *xstart = screencolumns /2 ; } if (*xstart + *columns > screencolumns) { *xstart = screencolumns - *columns; } } if (*ystart == -1) { /* pad to fit */ *ystart = screenlines - *rows; } else { if (*ystart == -2) { /* half height */ *ystart = screenlines /2 ; } if (*ystart + *rows > screenlines) { *ystart = screenlines - *rows; } } } static screen_init() /* initialise curses package and screen size */ { initscr(); screencolumns = COLS; screenlines = LINES; raw(); nonl(); noecho(); } /******************* * Save current screen in temporary window to enable non-window * user interaction, such as a short window command summary * and window status information to occur. Window is replaced * at end of interaction. *******************/ inform(function, winno) int (*function)(); int winno; { WINDOW *helpwin, *newwin(); int old_timeon, waiting; char c; /******************* * Need to stop time updating screen since we are * temporarily not using stdscr. *******************/ alarm(0); /* turn off alarm */ old_timeon = timeon; if (timeon == TRUE) { /* remove time */ timeon = FALSE; display_time(REMOVE); } helpwin = newwin(screenlines, screencolumns, 0, 0); overwrite(stdscr, helpwin); wclear(stdscr); wrefresh(stdscr); /* call supplied function */ if ((waiting = ((*function)(winno))) == TRUE) { printf("[ Hit return to continue ] "); fflush(stdout); read(0, &c, 1); } overwrite(helpwin, stdscr); overwrite(helpwin, curscr); /* don't know where cursor is, so force whole screen redraw */ wrefresh(curscr); delwin(helpwin); if (old_timeon == TRUE) { timeon = TRUE; update_time(); /* display time this time round */ } } man_info() { if (access(manfile, R_OK) != -1) { sprintf(initfile, "%s %s", system_more, manfile); system(initfile); } else { fprintf(stderr, "%s: sorry, can't find manual page %s\n", progname, manfile); } } help_info(winno) /* print help info by pages */ int winno; { FILE *fopen(), *fp, *out; if ((fp = fopen(helpfile, "r")) != NULL) { page(fp); fclose(fp); } else { if (startup == FALSE) { out = stdout; } else { /* grotwin -h invocation */ out = stderr; fprintf(out, "%s: ", progname); } fprintf(out, "sorry, helpfile %s not available\r\n", helpfile); } return(TRUE); } page(fp) FILE *fp; { static char page_string[] = "[ Hit any key for more ] "; int lines; char *fgets(), buffer[80]; lines = 1; while (fgets(buffer, sizeof(buffer), fp) != NULL) { fputs(buffer, stdout); putc('\r', stdout); if (++lines >= screenlines) { lines = 1; fputs(page_string, stdout); fflush(stdout); read(0, buffer, 1); /* wait for user */ clear_line(sizeof(page_string)); } } } clear_line(length) /* clear a line on screen */ int length; { int outchar(); putc('\r', stdout); /******************* * Clear line. Try to use termcap delete to end of line, * delete line or just output plain spaces if all else fails. * Delete to end of line may be faster than delete line * since delete line may require the terminal to * scroll the bottom line. *******************/ if (CE != (char *) NULL) { tputs(CE, 1, outchar); } else { if (DL != (char *) NULL) { tputs(CE, 1, outchar); } else { /* bodge it */ while (length--) { putc(' ', stdout); } } } putc('\r', stdout); fflush(stdout); } static outchar(c) /* output routine for use with tputs in clear_line */ char c; { putc(c, stdout); } forced_die() { alarm(0); signal(SIGINT, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGALRM, SIG_IGN); kill_all_shells(); } die() { static char confused_mess[] = "\7\r\n-- Internal error - please report --\r\n"; alarm(0); if (startup == FALSE) { wclear(stdscr); wmove(stdscr, LINES - 1, 0); wrefresh(stdscr); } endwin(); if (screen_priority != NULL) { free_priority_win(screen_priority); } if (confused == REALLY_CONFUSED) { fprintf(stderr, confused_mess); exit(3); } exit(0); } static fatal() { static char fatalmess[] = "\n\n\7 caught nasty signal - exiting \n"; static int first_time = TRUE; alarm(0); signal(SIGINT, SIG_IGN); signal(SIGALRM, SIG_IGN); /* endwin will do this anyway, good for following printfs though */ ioctl(0, TIOCSETP, &masterterm); ioctl(0, TIOCLBIS, &mlocalmode); ioctl(0, TIOCSETC, &mtchars); fprintf(stderr, fatalmess); /* safety check to stop endless recursion if hopelessly lost */ if (first_time) { first_time = FALSE; forced_die(); die(); } exit(4); } //E*O*F grotwin.c// echo x - grotwin.h cat > "grotwin.h" << '//E*O*F grotwin.h//' /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include <stdio.h> #include <sys/param.h> #include <curses.h> /******************* * The following defines are very ascii dependent (which is good since its * anti-IBM EBSDIC) *******************/ /******************* * maximum number of windows (maximum of 10 imposed), depends on * stdin, stdout, stderr, utmp update and two per window * (NOFILE is 30 on a Sun 2 running op sys version 2.2, normally 20 * though on unix systems) *******************/ #define SYSTEM_WINDOWS ( (NOFILE - 4) / 2 ) #define MAX_WINDOWS ( 10 ) /* windows 0 to 9 */ #if ( MAX_WINDOWS > SYSTEM_WINDOWS ) #undef MAX_WINDOWS #define MAX_WINDOWS ( SYSTEM_WINDOWS ) #endif MAX_WINDOWS #define MAX_WINDOWS_PLUS ( MAX_WINDOWS + 1 ) #define REALLY_CONFUSED ( 2 ) #define READGRAIN ( 2 ) #define CTRL_MASK ( 0x1F ) #define WIN_SWITCH_NUMBER ( (unsigned char) 0x80 ) /* user window control commands */ #define SELECT ( 'a' & CTRL_MASK ) #define WIN_SWITCH ( 'w' & CTRL_MASK ) #define NEW_WINDOW ( 'n' & CTRL_MASK ) #define FORCED_EXIT ( 'f' & CTRL_MASK ) #define REMOVE_WIN ( 'r' & CTRL_MASK ) #define CLEAR ( 'z' & CTRL_MASK ) #define MOVE ( 'm' & CTRL_MASK ) #define EXPAND ( 'x' & CTRL_MASK ) #define VERTICAL_EXPAND ( 'v' & CTRL_MASK ) #define HORIZONTAL_EXPAND ( 'c' & CTRL_MASK ) #define EXPOSE_WINDOW ( 'e' & CTRL_MASK ) #define HIDE_WINDOW ( 'h' & CTRL_MASK ) #define SCREEN_REDRAW ( 'l' & CTRL_MASK ) #define XON ( 'q' & CTRL_MASK ) #define XOFF ( 's' & CTRL_MASK ) #define PAGE_MODE ( 'p' & CTRL_MASK ) #define OVERWRITE_MODE ( 'o' & CTRL_MASK ) #define TIME_TOGGLE ( 't' & CTRL_MASK ) #define INFORM ( 'i' & CTRL_MASK ) #define HELP ( '?' ) #define BUFLEN ( 256 ) #define TIME_RES ( 30 ) #define HIDE ( 0 ) #define DISPLAY ( 2 ) #define REMOVE ( 1 ) #define MAXARGS ( 20 ) #define WORLD_MIN_COLUMNS ( 20 ) #define WORLD_MIN_ROWS ( 6 ) #define MIN_COLUMNS ( 3 ) #define MIN_ROWS ( 4 ) /* 3 doesn't work */ #define OVERLAY ( 4 ) #define BOX ( 1 ) #define NOT_BOX ( 0 ) #define BAD_INPUT ( 0 ) #define MISTAKE ( 1 ) #define NORMAL ( 2 ) #define BACKGROUND '.' #define WIN_STANDOUT ( (char) 0x80 ) #define WIN_STANDEND ( (char) 0x00 ) struct windowdetails { int active; int masterfd; int slavefd; int slavepid; int slavemaskfd; int output_blocked; int output_paged; int line_count; int paged_page_full; int overwrite; int cursor_addressing; int cursor_addr_passes; int cursor_addr_row; int lines; int columns; int x_current; int y_current; int x_start; int y_start; int x_end; int y_end; int readgrain; int page_buf_length; char **screenptr; char *page_ptr; char page_buf[BUFSIZ + 3]; char pseudo_ttyname[20]; char *progy; char standout_mode; }; /* dpz additions to grotwin.h for non-Sun machines 08/23/86 */ #ifndef SUN struct ttysize { int ts_lines; /* number of lines on terminal */ int ts_cols; /* number of columns on terminal */ }; #define TIOCSSIZE _IOW(t,103,struct ttysize)/* set tty size */ #define SIGWINCH 28 /* window changed */ #undef NOFILE /* not on a sun, so make it 20 */ #define NOFILE 20 #endif //E*O*F grotwin.h// echo x - grotwin.help cat > "grotwin.help" << '//E*O*F grotwin.help//' WIndow Command Terminology ctrl-a 'n' means ctrl-a followed by ctrl-n or 'n'. ctrl-a 'N' means ctrl-a followed by 'N' ctrl-a 'nN' means ctrl-a followed by ctrl-n, 'n' or 'N'. All commands are preceded by ctrl-a, just as you may have used reached this information summary. The valid window commands are as follows :- Command Action 'nN' create new window 'w' make numerically next window current '0' - '9' make selected window current (range 1 to 9) 'xX' expand window to full size 'vV' expand window vertically to full size 'cC' expand window horizontally to full size 'zZ' clear window 'eE' expose window 'hH' hide window 'p' page mode on - type any character for next page 'P' page mode off 'o' overwrite mode on - type any character for next page 'O' overwrite mode off 'tT' toggle between displaying and removing time 'sS' XOFF for current window 'qQ' XON for current window 'R' force process within window to terminate - achieved by sending following signals in implied order until one is successful or all have been used SIGHUP, SIGINT, SIGTERM or SIGKILL 'F' forced exit from program (no questions asked) - same as the 'R' option, but for all windows 'iI' display status of windows '?' this message otherchar Character is input as if not preceded by ctrl-a. Therefore to enter ctrl-a, type 'ctrl-a ctrl-a' //E*O*F grotwin.help// echo x - manager.c cat > "manager.c" << '//E*O*F manager.c//' #ifndef lint static char sccsid[] = "@(#)manager.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include <sys/ioctl.h> #include <sys/file.h> #include <sys/time.h> #include <sys/wait.h> #include <ctype.h> #include <errno.h> #include "grotwin.h" extern int errno; /* system error */ /* externals defined in grotwin.c */ extern struct windowdetails win[MAX_WINDOWS_PLUS]; /* window details */ extern struct sgttyb masterterm; /* used to store initial tty state */ extern struct tchars mtchars; extern int mlocalmode; extern int startup; /* program startup period */ extern int confused; /* for when program hangs up */ extern int timeon; /* display clock on top edge */ extern int time_start; /* where time string is placed */ extern char *progname; /* program name stripped of any /'s*/ extern char timestring[]; /* time on top edge of b/g window */ extern char nottimestring[]; /* used when time is not displayed */ unsigned int child_died = 0; /* flag to catch SIGCHLD */ /* slavemask used to form mask of active file descriptors for select() */ int slavemask = 1 << 0; /* standard input (fd = 0) */ int active_windows = 0; /* number of user windows active */ int input_winno = 0; /* current window */ int input_changed = FALSE; /* to force cursor to be redrawn */ int alldead = FALSE; /* no user window active */ int exit_forced = FALSE; /* forced exit of program */ long clock; /* time in seconds of last minute */ /* list of priorities - end referenced by tail variable */ int window_priorities[MAX_WINDOWS_PLUS]; manager() /* manage input from active window and output from all */ { /******************* * Nice largeish timeout to make select() return only when something * is ready (stops eating up cpu time). * Must be less than 30 seconds for detection of hangup. *******************/ static struct timeval timeout = { 10L, 0L }; static unsigned int children_dead = 0; struct windowdetails *ptr; int i, readfds, waiting; /* make corners indicate indicate this input window */ top_corners(input_winno); while (1) { /* used to locate internal hangup */ if (confused == TRUE) { confused = FALSE; if (timeon == TRUE) { /* time changed */ display_time(DISPLAY); } } /******************* * Check for dead children. Even though child_died * may be incremented asynchronously, having another * count that is incremented until it is equal to * child_died will overcome any problems of reading * an out of date value of child_died being used. *******************/ while (children_dead != child_died) { ++children_dead; check_exit(); /* remove window */ } /* place cursor in active window if moved */ if (input_changed == TRUE) { ptr = &(win[input_winno]); wmove(stdscr, ptr->y_current + ptr->y_start, ptr->x_current + ptr->x_start); touchwin(stdscr); wrefresh(stdscr); input_changed = FALSE; } /******************* * Check if their is any keyboard input and obey - then * check each window for any output and display it . * Order is important since user expects response to input * immediately (this does mean that window tty could be closed * after select has said there is input waiting though !). *******************/ readfds = slavemask; if (select(NOFILE, &readfds, 0, 0, &timeout) <= 0) { continue; } /* keyboard input waiting */ if (readfds & (1 << 0)) { get_input(); } touchwin(stdscr); wrefresh(stdscr); ptr = win; /******************* * need to check if window is active since it may * have been removed by action of keyboard input ! *******************/ for (i = 0 ; i < MAX_WINDOWS ; ++i, ++ptr) { if (! ptr->active) { continue; } /* check for previous output still waiting */ if (ptr->page_buf_length > 0) { update_screen(i); input_changed = TRUE; continue; } else { /* read current output waiting */ if (readfds & ptr->slavemaskfd) { waiting = read(ptr->masterfd, ptr->page_buf, ptr->readgrain); if (waiting != -1) { ptr->page_buf[waiting] = '\0'; ptr->page_buf_length = waiting; update_screen(i); input_changed = TRUE; } } } } } } /* fire off window of specified dimensions running named program within it */ win_instance(lines, columns, ystart, xstart, program) int lines, columns, ystart, xstart; char *program; { struct windowdetails *ptr; int winno; char *calloc(); if ((winno = freewin()) == -1) { return(-1); } if (newtty(winno) == -1) { return(-1); } ptr = &(win[winno]); ptr->progy = calloc(strlen(program) + 1, sizeof(char)); strcpy(ptr->progy, program); switch (ptr->slavepid = fork()) { case -1 : /* too many system processes ! */ /* can't use removewin since haven't used createwin */ close(ptr->masterfd); close(ptr->slavefd); ptr->active = FALSE; return(-1); break; case 0 : /* child */ action_slave(ptr->slavefd, ptr->progy, lines - 2, columns - 2); /* _exit to stop flushing buffers twice (apparently) */ _exit(10); break; default : /* parent */ /***************** * In case any old (still running) processes started * on pty have not been disassociated from it. *****************/ ioctl(ptr->masterfd, TIOCSPGRP, &(ptr->slavepid)); ioctl(ptr->slavefd, TIOCSPGRP, &(ptr->slavepid)); /* rendezvous with child (see comment in child) */ write(ptr->masterfd, "\n", 1); break; } /* must be after fork (faster startup with createwin here) */ slavemask |= ptr->slavemaskfd; /* tty now accessible */ createwin(winno, lines, columns, ystart, xstart); return(winno); } static freewin() /* return win[] location not in use */ { register int i; register struct windowdetails *ptr; for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr) { if (! ptr->active) { return(i); } } return(-1); } static newtty(winno) /* return number of a free pseudo tty (cyclic ish) */ int winno; { /* not very efficient for pty[q-r] at present, to be changed */ static char *pseudo_termlist[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; static char *masterside[] = { "/dev/ptyp", "/dev/ptyq", "/dev/ptyr" }; static char *slaveside[] = { "/dev/ttyp", "/dev/ttyq", "/dev/ttyr" }; static int elements = sizeof(pseudo_termlist) / sizeof(char *); static int i = (sizeof(pseudo_termlist) / sizeof(char *)) - 1; static int pty_groups = sizeof(masterside) / sizeof(char *); static int j = (sizeof(masterside) / sizeof(char *)) - 1; register struct windowdetails *ptr; char temp[20]; int pty, tty_element, fd; ptr = &(win[winno]); /* if pseudo tty in use, assume can't open either master or slave */ for (pty = 0 ; pty < pty_groups ; ++pty) { for (tty_element = 0 ; tty_element < elements ; ++tty_element) { i = (i + 1) % elements; sprintf(temp, "%s%s", masterside[j],pseudo_termlist[i]); if ((fd = open(temp, O_RDWR)) != -1) { ptr->masterfd = fd; sprintf(temp, "%s%s", slaveside[j], pseudo_termlist[i]); if ((fd = open(temp, O_RDWR)) != -1) { ptr->slavemaskfd = 1 << ptr->masterfd; ptr->slavefd = fd; strcpy(ptr->pseudo_ttyname, temp); return(0); } close(ptr->masterfd); } } /* start at the beginning for next pty group */ if (i != 0) { i = elements - 1; } j = (j + 1) % pty_groups; } return(-1); } child_exit() /* increment global flag of dead children */ { ++child_died; } check_exit() /* check for terminated shells */ { register int i; register struct windowdetails *ptr; int pid; for (i = 0, ptr = win ; i < MAX_WINDOWS ; ++i, ++ptr) { if (ptr->active) { if (kill(ptr->slavepid, 0) == -1 && errno == ESRCH) { /******************* * Should really check that pid is equal * to ptr->slaveppid, but what could * be done if it wasn't. *******************/ pid = shell_cleanup(i); } } } /* if no shells left - exit program */ if (alldead && ! startup) { alarm(0); signal(SIGALRM, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGINT, SIG_IGN); die(); } } update_time() /* called via SIGALRM - update global time */ { static long last_clock = 0L; char *ctime(); time(&clock); /******************* * Following is a simple little check to ensure that if program * will terminate within a minute if it hangs up badly. * Relies on manager taking less than 30 seconds on select timeout ! *******************/ if (clock - last_clock > 30) { /* internal hangup */ if (confused == TRUE && startup == FALSE) { confused = REALLY_CONFUSED; forced_die(); } last_clock = clock; } alarm(60 - (int) (clock % 60)); confused = TRUE; /* until proven otherwise */ } display_time(command) /* display or remove time on top border */ int command; { wmove(stdscr, 0, time_start); if (command == DISPLAY) { /* isolate hours and mins */ strncpy(timestring + 1, ctime(&clock) + 11, 5); waddstr(stdscr, timestring); } else { /* REMOVE */ waddstr(stdscr, nottimestring); } input_changed = TRUE; /* place cursor back in input window */ } static shell_cleanup(winno) /* remove all references to a now dead window */ int winno; { union wait status; /* different from sysV */ int pid; /******************* * Stop child waiting for parent to perform a wait. * Otherwise child becomes zombie and hogs a place in process * list table. *******************/ pid = wait(&status); removewin(winno); /* place cursor in current window */ if (! alldead) { /* was current window one terminated */ if (winno == input_winno) { next_input_window(window_priorities[0]); top_dog(input_winno); } } return(pid); } kill_all_shells() { int i; exit_forced = TRUE; for (i = 0 ; i < MAX_WINDOWS ; ++i) { if (win[i].active) { if (kill_shell(i) == -1) { printf(" window %d process wouldn't die ", i); sleep(2); } removewin(i); } } die(); } /* kill process within window - tries gently and then with brute force */ static kill_shell(winno) int winno; { static int kill_list[] = { SIGHUP, SIGINT, SIGTERM, SIGKILL }; int i; kill(win[winno].slavepid, SIGCONT); /* wake up just in case */ for (i = 0 ; i < (sizeof(kill_list) / sizeof(int)) ; ++i) { if (kill(win[winno].slavepid, kill_list[i]) == 0) { return(kill_list[i]); } } return(-1); } static removewin(winno) /* remove window from existance */ int winno; { struct windowdetails *ptr; char *rindex(), *tty; ptr = &(win[winno]); ptr->active = FALSE; slavemask &= ~ ptr->slavemaskfd; if (--active_windows <= 0) { alldead = TRUE; } close(ptr->masterfd); close(ptr->slavefd); tty = 1 + rindex(ptr->pseudo_ttyname, '/'); utmp_delete(tty); free_virt_win(ptr->screenptr); free(ptr->progy); /* test to stop needless screen redrawing if quiting program */ if (! exit_forced) { /* clear screen where window was */ bottom_dog(winno, REMOVE); touchwin(stdscr); wrefresh(stdscr); } } next_input_window(value) int value; { static int prev_winno = MAX_WINDOWS - 1; register struct windowdetails *ptr; int i, oldinput; oldinput = input_winno; if (value == -1) { for (i = 0 ; i < MAX_WINDOWS ; ++i) { prev_winno = (prev_winno + 1) % MAX_WINDOWS; if (win[prev_winno].active) { input_winno = prev_winno; break; } } } else { /* check for illegal window number or inactive window */ if (value >= 0 && value < MAX_WINDOWS) { if (win[value].active) { input_winno = value; prev_winno = value; } else { return(-1); } } else { return(-1); } } /* remove active indicators in top corners of old acitive window */ if (oldinput >= 0) { ptr = &(win[oldinput]); if (ptr->active) { ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] =' '; ptr->screenptr[ptr->y_start - 1][ptr->x_end] = ' '; top_corners(oldinput); } } /* add active indicators to top corners of active window */ ptr = &(win[input_winno]); ptr->screenptr[ptr->y_start - 1][ptr->x_start - 1] = '+'; ptr->screenptr[ptr->y_start - 1][ptr->x_end] = '+'; top_corners(input_winno); input_changed = TRUE; /* bodge for now to force corners to be indicated ! */ /* should go before ptr = ... */ if (oldinput == input_winno) { return(-1); } return(oldinput); } static get_input() { int length, i; char input[BUFSIZ + 1]; length = read(0, input, BUFSIZ); for (i = 0 ; i < length ; ++i) { put_char((unsigned char) input[i]); } } static put_char(c) unsigned char c; { extern int window_info(), help_info(); static unsigned int prev_c = '\0'; static int new_winno = 0; /* in case c == WIN_SWITCH_NUMBER */ struct windowdetails *ptr; int upper_case, temp; unsigned char c_orig; c &= 0x7F; /* strip off top bit for WIN_SELECT_NUMBER */ ptr = &(win[input_winno]); if (prev_c == SELECT) { c_orig = c; /******************* * to help distinguish between upper case only commands, * make upper_case variable FALSE in order to execlude * lower case and control instances *******************/ upper_case = FALSE; /* until proved otherwise */ if (isdigit(c)) { new_winno = c - '0'; if (new_winno < MAX_WINDOWS) { c = WIN_SWITCH_NUMBER; } } else { if (isalpha(c)) { if (isupper(c)) { upper_case = TRUE; } /* to allow 'n' and CTRL-N to be the same */ c = c & CTRL_MASK; } } switch(c) { case WIN_SWITCH : new_winno = -1; case WIN_SWITCH_NUMBER : temp = input_winno; if (next_input_window(new_winno) == -1 || temp == input_winno) { write(1, "\007", 1); break; /* nasty practice */ } top_dog(input_winno); touchwin(stdscr); wrefresh(stdscr); break; case NEW_WINDOW : new_window(); break; case SCREEN_REDRAW : touchwin(curscr); wrefresh(curscr); break; case EXPAND : widen_window(input_winno, VERTICAL_EXPAND); widen_window(input_winno, HORIZONTAL_EXPAND); touchwin(stdscr); wrefresh(stdscr); break; case VERTICAL_EXPAND : widen_window(input_winno, VERTICAL_EXPAND); touchwin(stdscr); wrefresh(stdscr); break; case HORIZONTAL_EXPAND : widen_window(input_winno, HORIZONTAL_EXPAND); touchwin(stdscr); wrefresh(stdscr); break; case PAGE_MODE : if (upper_case) { /* page off */ ptr->output_paged = FALSE; if (ptr->paged_page_full) { ptr->paged_page_full = FALSE; } } else { /* page on */ if (! ptr->output_paged) { ptr->output_paged = TRUE; ptr->paged_page_full = FALSE; ptr->line_count = 0; } } break; case REMOVE_WIN : /* only look for upper case */ if (upper_case) { if (kill_shell(input_winno) == -1) { /* failed (why ?) */ write(1, "\007", 1); } break; } /* fall through to default case (nasty) */ goto fallthru; case FORCED_EXIT : if (upper_case) { forced_die(); break; } /* only look for upper case */ /* fall through to default case (nasty) */ goto fallthru; case XON : ptr->output_blocked = FALSE; break; case XOFF : ptr->output_blocked = TRUE; break; case OVERWRITE_MODE : if (upper_case) { /* overwrite off */ ptr->overwrite = FALSE; } else { /* overwrite on */ ptr->overwrite = TRUE; } break; case TIME_TOGGLE : /* toggle time between on and off */ if (timeon == TRUE) { timeon = FALSE; display_time(REMOVE); } else { timeon = TRUE; update_time(); } break; case INFORM : inform(window_info, input_winno); break; case HELP : inform(help_info, input_winno); break; case CLEAR : mywclear(input_winno); input_changed = TRUE; touchwin(stdscr); wrefresh(stdscr); break; case EXPOSE_WINDOW : top_dog(input_winno); touchwin(stdscr); wrefresh(stdscr); break; case HIDE_WINDOW : bottom_dog(input_winno, HIDE); touchwin(stdscr); wrefresh(stdscr); break; fallthru : default : if (ptr->paged_page_full) { ptr->paged_page_full = FALSE; } else { c = c_orig; write(ptr->masterfd, &c, 1); if (c == SELECT) { c = ~c; } } break; } } else { if (c != SELECT) { if (ptr->paged_page_full) { ptr->paged_page_full = FALSE; } else { write(ptr->masterfd, &c, 1); } } } prev_c = c; } /* fire off slave process within window */ static action_slave(slavefd, progy, lines, columns) int slavefd; char *progy; int lines, columns; { extern char **environ; extern char termcap[]; extern char term[]; /* my very own terminal type ! */ int fd, ldisc, pgrp, i, j, k; char **argv, **envp, *calloc(); char c, buffer[BUFSIZ + 1], *ptr1, *ptr2; signal(SIGALRM, SIG_IGN); alarm(0); /* turn off clock signal */ /* reset all caught signals */ for (i = 1 ; i <= NSIG ; ++i) { signal(i, SIG_DFL); } /* make pseudo tty connection */ close(0); dup(slavefd); close(1); dup(slavefd); close(2); dup(slavefd); for (i = 3 ; i < NOFILE ; ++i) { /* close all other files */ close(i); } /* disassociate /dev/tty from parent control group */ if ((fd = open("/dev/tty", O_RDWR, 0)) != -1) { ioctl(fd, TIOCNOTTY, 0); close(fd); } /******************* * From experience, it would appear that the master side of a * pseudo tty must set the process group for the master side (pty) * before the slave side sets its process group equal to its * own pid (master is also set to this value, obtained via fork * of child). * This is achieved by the parent writing '\n' (specifically '\n' * to overcome buffering), on its tty when it has set its * own tty and its childs tty (action duplicated by child) to the * process group equal to the childs process number, thereby * achieving a simple rendezvous. *******************/ /* rendezvous with parent */ read(0, &c, 1); /* set up tty in a usable state as parent tty may not be */ ldisc = NTTYDISC; masterterm.sg_flags |= ECHO; masterterm.sg_flags |= CRMOD; masterterm.sg_flags &= ~CBREAK; masterterm.sg_flags &= ~RAW; ioctl(0, TIOCSETP, &masterterm); ioctl(0, TIOCSETD, &ldisc); ioctl(0, TIOCLBIS, &mlocalmode); ioctl(0, TIOCSETC, &mtchars); /* set up unique process group and attach to process and tty */ pgrp = getpid(); setpgrp(pgrp, pgrp); ioctl(0, TIOCSPGRP, &pgrp); /* parent also performs this */ set_tty_size(0, lines, columns, FALSE); /* set tty size */ for (i = 0 ; environ[i] ; ++i) { /* find last entry */ ; } /* form new environment for progy */ envp = (char **) calloc(i + 2, sizeof(char *)); /* comb through and remove TERM and TERMCAP entries */ for (k = 0, j = 0 ; j < i ; ++j) { if (strncmp(environ[j], "TERMCAP", 7) != 0 && strncmp(environ[j], "TERM", 4) != 0) { envp[k++] = environ[j]; } } /******************* * Now add grotwin terminal type. * Could be clever and place these at start of environment * for quick access ! *******************/ envp[k] = calloc(strlen(term) + 1, sizeof(char)); strcpy(envp[k++], term); sprintf(buffer, termcap, columns, lines); envp[k] = calloc(strlen(buffer) + 1, sizeof(char)); strcpy(envp[k], buffer); envp[++k] = (char *) NULL; environ = envp; argv = (char **) calloc(MAXARGS + 1, sizeof(char *)); strcpy(buffer, progy); i = 0; ptr1 = buffer; while (i < MAXARGS) { for(ptr2 = ptr1 ; *ptr1 != ' ' && *ptr1 ; ++ptr1) { ; } argv[i++] = ptr2; if (! *ptr1) { break; } *ptr1++ = '\0'; } argv[i] = (char *) NULL; execvp(argv[0], argv); /* exec failed */ fprintf(stderr, "\r\n%s: can't find %s ", progname, argv[0]); fflush(stderr); free(argv); free(envp[k - 1]); free(envp[k - 2]); free(envp); close(0); /* close files */ close(1); close(2); sleep(5); } /******************* * Set tty size and inform associated control group * via SIGWINCH that the tty size has changed. *******************/ set_tty_size(fd, lines, columns, inform) int fd, lines, columns, inform; { struct ttysize tty_size; int pgrp; tty_size.ts_lines = lines; /* set up new tty size */ tty_size.ts_cols = columns; if (ioctl(fd, TIOCSSIZE, &tty_size) == -1) { return; /* can't change tty size */ } if (inform == TRUE) { /* inform process */ if (ioctl(fd, TIOCGPGRP, &pgrp) == 0) { killpg(pgrp, SIGWINCH); } } } //E*O*F manager.c// echo x - update.c cat > "update.c" << '//E*O*F update.c//' #ifndef lint static char sccsid[] = "@(#)update.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include "grotwin.h" /* defines specific to grotty terminal type (see term and termcap below) */ #define INSERT_LINE ( 0x01 ) #define DELETE_LINE ( 0x02 ) #define STANDOUT ( 0x05 ) #define STANDEND ( 0x06 ) #define BELL ( 0x07 ) #define BACKSPACE ( 0x08 ) #define TAB ( 0x09 ) #define CURSOR_DOWN ( 0x0A ) #define CURSOR_UP ( 0x0B ) #define CLEAR_SCREEN ( 0x0C ) #define NONDESTRUCT_SPACE ( 0x18 ) #define DELETE_TO_EOL ( 0x19 ) #define CURSOR_ADDR ( 0x1B ) #define TABS ( 0x08 ) #define SPECIAL '\0' extern struct windowdetails win[MAX_WINDOWS_PLUS]; extern int **screen_priority; extern char **masterscreen; /******************* * * My very own terminal type. Loosely based on a adm3a and Newbury 8000 * Used in grotwin.c but defined here since this module handles terminal * escape sequences. Although a vt100 definition would probably be better, * this simple terminal type generally uses less I/O to achieve the same * goals (ie. cursor movement), at the cost of programs that use * vt100 escape sequences without looking at TERMCAP. * * Termcap entry should also be placed in in /etc/termcap as well. * *******************/ char term[] = "TERM=grotty"; char termcap[] = "TERMCAP=gr|grotty|grotwin pseudo tty:\ co#%d:li#%d:cl=\^L:cm=\\E=%%+ %%+ :am:bs:nd=\^X:ce=\^Y:\ up=\^K:do=\^J:kb=\^H:so=\^E:se=\^F:al=\^A:dl=\^B:"; /* updates specified window with string */ update_screen(winno) int winno; { register struct windowdetails *ptr; register int x, y; int length, temp; char *strcpy(), *string; ptr = &(win[winno]); if (ptr->paged_page_full || ptr->output_blocked) { /* can't o/p */ return; } string = ptr->page_buf; length = ptr->page_buf_length; while (length-- > 0) { *string &= 0x7F; /* strip off top most bit */ if (ptr->cursor_addressing) { temp = *string++; /* check for cursor addressing */ if (++(ptr->cursor_addr_passes) == 1) { if (temp != '=') { ptr->cursor_addressing = FALSE; ptr->cursor_addr_passes = 0; mywaddch(winno, CURSOR_ADDR); mywaddch(winno, temp); continue; } } else { if (ptr->cursor_addr_passes == 2) { ptr->cursor_addr_row = temp - ' '; if (ptr->cursor_addr_row >= ptr->lines){ ptr->cursor_addr_row = ptr->lines - 1; } else { if (ptr->cursor_addr_row < 0) { ptr->cursor_addr_row =0; } } } else { /* move it */ ptr->y_current = ptr->cursor_addr_row; ptr->x_current = temp - ' '; if (ptr->x_current >= ptr->columns) { ptr->x_current = ptr->columns-1; } else { if (ptr->x_current < 0) { ptr->x_current = 0; } } ptr->cursor_addressing = FALSE; ptr->cursor_addr_passes = 0; } } continue; } y = ptr->y_current; x = ptr->x_current; switch (*string) { case '\r' : ptr->x_current = 0; break; case '\n' : if (ptr->output_paged && ++ptr->line_count >= ptr->lines) { ptr->paged_page_full = TRUE; ptr->line_count = 0; /* remember rest of output */ strcpy(ptr->page_buf, string); ptr->page_buf_length = length + 1; return; } if (y >= (ptr->y_end - ptr->y_start - 1)) { ptr->y_current = 0; if (! ptr->overwrite) { mywlineop(winno, DELETE_LINE); ptr->y_current = y; } } else { ++ptr->y_current; } if (ptr->overwrite) { mywline_clear(winno, ptr->y_current + ptr->y_start); } break; case CURSOR_UP : if (y > 0) { --y; } ptr->y_current = y; break; case CLEAR_SCREEN : mywclear(winno); break; case CURSOR_ADDR : ptr->cursor_addressing = TRUE; break; case TAB : x = x + TABS - (x % TABS); if (x > ptr->columns - 1) { x = ptr->columns - 1; } ptr->x_current = x; break; case BACKSPACE : if (x > 0) { --ptr->x_current; } break; case STANDOUT : ptr->standout_mode = WIN_STANDOUT; break; case STANDEND : ptr->standout_mode = WIN_STANDEND; break; case BELL : write(1, "\007", 1); break; case NONDESTRUCT_SPACE : if (x < ptr->columns - 1) { ++x; } else { ptr->y_current = ++y; x = 0; } ptr->x_current = x; break; case INSERT_LINE : mywlineop(winno, INSERT_LINE); break; case DELETE_LINE : mywlineop(winno, DELETE_LINE); break; case DELETE_TO_EOL : temp = x; while (temp++ < ptr->columns) { mywaddch(winno, ' '); } ptr->x_current = x; break; case SPECIAL : break; default : mywaddch(winno, *string); if (x >= ptr->columns - 1) { /* insert \r\n into string */ length = string_insert(string + 1, "\r\n"); } break; } ++string; } ptr->page_buf_length = 0; } /* insert s2 at beginning of s1 after shifting s2 to make room */ /* returns length of new string */ static string_insert(s1, s2) char *s1, *s2; { register char *src_ptr, *dest_ptr; register int i; int n1, n2; n1 = strlen(s1); n2 = strlen(s2); src_ptr = s1 + n1; dest_ptr = src_ptr + n2; /* include string delimiter */ i = n1 + 1; while (i--) { *dest_ptr-- = *src_ptr--; } i = n2; while (i--) { *s1++ = *s2++; } return(n1 + n2); } static mywaddch(winno, c) /* add char to window */ int winno; char c; { register struct windowdetails *ptr; register int x, y; ptr = &(win[winno]); x = ptr->x_current + ptr->x_start; y = ptr->y_current + ptr->y_start; c |= ptr->standout_mode; ptr->screenptr[y][x] = c; if (screen_priority[y][x] == winno) { masterscreen[y][x] = c; } ++ptr->x_current; } static mywlineop(winno, command) /* insert - delete a line on the screen */ int winno, command; { register struct windowdetails *ptr; register int x, *line_priority; register char *image, *screenline; int y, y_actual_current; char *temp; ptr = &(win[winno]); y_actual_current = ptr->y_current + ptr->y_start; /* move lines around */ if (command == DELETE_LINE) { temp = ptr->screenptr[y_actual_current]; for (y = y_actual_current ; y < ptr->y_end - 1 ; ++y) { ptr->screenptr[y] = ptr->screenptr[y + 1]; } ptr->screenptr[ptr->y_end - 1] = temp; mywline_clear(winno, ptr->y_end - 1); } else { /* INSERT_LINE */ temp = ptr->screenptr[ptr->y_end - 1]; for (y = ptr->y_end - 1 ; y > y_actual_current ; --y) { ptr->screenptr[y] = ptr->screenptr[y - 1]; } ptr->screenptr[y_actual_current] = temp; mywline_clear(winno, y_actual_current); } /* update real screen */ for (y = y_actual_current ; y < ptr->y_end ; ++y) { line_priority = screen_priority[y]; screenline = masterscreen[y]; image = ptr->screenptr[y]; for (x = ptr->x_start ; x < ptr->x_end ; ++x) { if (line_priority[x] == winno) { screenline[x] = image[x]; } } } } static mywline_clear(winno, line) int winno, line; { register struct windowdetails *ptr; register int x, *line_priority; register char *lineptr, *masterline; ptr = &(win[winno]); lineptr = ptr->screenptr[line]; line_priority = screen_priority[line]; masterline = masterscreen[line]; for (x = ptr->x_start ; x < ptr->x_end ; ++x) { lineptr[x] = ' '; if (line_priority[x] == winno) { masterline[x] = ' '; } } } mywclear(winno) /* clear window */ int winno; { struct windowdetails *ptr; int y; ptr = &(win[winno]); for (y = ptr->y_start ; y < ptr->y_end ; ++y) { mywline_clear(winno, y); } ptr->y_current = 0; ptr->x_current = 0; ptr->line_count = 0; } //E*O*F update.c// echo x - utmp.c cat > "utmp.c" << '//E*O*F utmp.c//' #ifndef lint static char sccsid[] = "@(#)utmp.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * This program attempts to overcome the deficiences of 4.2 not * having any visible routines to update the utmp file like Sys V. * Utmp is used by utilities such as who to find out who is * logged on. On Sun 4.2, /etc/utmp can be written by * anyone, so no setuid etc. is required to alter it. * * Accessed only via utmp_insert(tty, id) and utmp_delete(tty) * to enable routines to be kept internal. Uses host field * to indicate that pseudo tty is being used by grotwin (I know its * not what the field is meant for, but it looks better all the same). * * Getpwent() appears to hunk in approx 30 K of unnecessary code * to deal with remote hosts etc. * **************************************/ #include <stdio.h> #include <utmp.h> #include <pwd.h> #define UTMP_INSERT ( 1 ) #define UTMP_DELETE ( 2 ) /* max length of tty name entry in /etc/ttys file */ #define TTYS_MAXLEN ( 20 ) typedef enum { FALSE, TRUE } bool; utmp_insert(tty, id) /* add entry to utmp */ char *tty, *id; { return(utmp_update(tty, id, UTMP_INSERT)); } utmp_delete(tty) /* delete entry from utmp */ char *tty; { return(utmp_update(tty, "", UTMP_DELETE)); } static utmp_update(tty, id, mode) /* update utmp file */ char *tty, *id; int mode; { static char utmp[] = "/etc/utmp"; struct utmp entry; struct passwd *getpwuid(), *passwd_entry; FILE *fp, *fopen(); long *time(); int position, i; char *getlogin(), *user_name; position = utmp_pos(tty); if (position == -1) { return(-1); } if ((fp = fopen(utmp, "r+")) == NULL) { return(-1); } if (mode == UTMP_INSERT) { /* try to find user */ if ((user_name = getlogin()) == NULL) { if ((passwd_entry = getpwuid(getuid())) == NULL) { return(-1); /* give up */ } user_name = passwd_entry->pw_name; } /******************* * In case strings are too long to fit utmp structure, * copy max - 1 chars and null terminate at end. *******************/ strncpy(entry.ut_name, user_name, sizeof(entry.ut_name) - 1); entry.ut_host[sizeof(entry.ut_name) - 1] = '\0'; strncpy(entry.ut_host, id, sizeof(entry.ut_host) - 1); entry.ut_host[sizeof(entry.ut_host) - 1] = '\0'; } else { /* UTMP_DELETE */ for (i = 0 ; i < sizeof(entry.ut_name) ; ++i) { entry.ut_name[i] = '\0'; } for (i = 0 ; i < sizeof(entry.ut_host) ; ++i) { entry.ut_host[i] = '\0'; } } strncpy(entry.ut_line, tty, sizeof(entry.ut_line) - 1); entry.ut_line[sizeof(entry.ut_host) - 1] = '\0'; (void) time(&(entry.ut_time)); if (fseek(fp, (long) (sizeof(entry) * position), 0) != -1) { (void) fwrite(&entry, sizeof(entry), 1, fp); } (void) fclose(fp); return(position); } utmp_pos(tty) /* like ttyslot */ char *tty; { static char ttys[] = "/etc/ttys"; FILE *fp, *fopen(); int position; bool found; char temp[TTYS_MAXLEN]; if ((fp = fopen(ttys, "r")) == NULL) { return(-1); } found = FALSE; for (position = 1 ; fgets(temp, TTYS_MAXLEN, fp) != NULL ; ++position) { temp[strlen(temp) - 1] = '\0'; /* remove trailing \n */ if (strcmp(temp + 2, tty) == 0) { found = TRUE; break; } } (void) fclose(fp); if (found == FALSE) { return(-1); } return(position); } //E*O*F utmp.c// echo x - window.c cat > "window.c" << '//E*O*F window.c//' #ifndef lint static char sccsid[] = "@(#)window.c 2.2 [ (C) Nigel Holder 1986 ]"; #endif /*************************************** * * Author : Nigel Holder * * Date : 10 July 1986 * * * Copyright (C) 1986 by Nigel Holder * * Permission to use this program is granted, provided it is not * sold, or distributed for direct commercial advantage, and includes * the copyright notice and this clause. * * Grotwin - provides a somewhat primitive windowing capability * for people unfortunate enough to use the standard 24 x 80 type * of terminal when the console is in use. Definitely written for * 4.[2,3] at the present time, as Sys V.2 does not cater for pseudo * terminals or have the select() facility (amongst other things !) * (version 8 should fix this). * * Files used :- * * grotwin.c - window system initialisation * deals with startup files * * manager.c - window manager * deals with input and output * * window.c - window manipulator * deals with aspects concerning windows * during normal usage * * update.c - simulates dumb terminal for use * with window manager. * * grotwin.h - header file for above * * utmp.c - utmp manipulator * updates utmp entries for each window * * grotwin.make - makefile for above * * Bugs :- * * Can't have the situation where no windows are present ! * Needs a vt100 like terminal type * ***************************************/ #include <sys/ioctl.h> #include "grotwin.h" /* externals defined in grotwin.c */ /* list of priorities - end referenced by tail variable */ extern struct windowdetails win[MAX_WINDOWS_PLUS]; extern int window_priorities[]; /* window details */ extern int **screen_priority; /* which window visible at each point */ extern int active_windows; /* number of user windows active */ extern int screenlines; /* lines on users screen */ extern int screencolumns; /* columns on users screen */ extern int time_start; /* where time string is placed */ extern int time_end; /* where time string ends */ extern int win_border; /* window border characteristic */ extern int timeon; /* display clock on top edge */ extern char **masterscreen; /* hook to stdscr from curses */ extern char *progname; /* program name stripped of any /'s */ extern char *home_shell; /* shell from users $SHELL variable */ extern char nottimestring[]; /* used when time is not displayed */ int tail = 0; /* tail of window priority list */ /* create instance of window */ createwin(winno, lines, columns, ystart, xstart) int winno, lines, columns, ystart, xstart; { register struct windowdetails *ptr; char **virtwin(), *rindex(), *tty; ptr = &(win[winno]); /* remember that a border surrounds window */ ptr->lines = lines - 2; ptr->columns = columns - 2; ptr->y_start = ystart + 1; ptr->x_start = xstart + 1; ptr->y_end = ptr->y_start + ptr->lines; ptr->x_end = ptr->x_start + ptr->columns; ptr->y_current = 0; ptr->x_current = 0; ptr->screenptr = virtwin('\0'); win_initialise(winno, ' '); /* skip pass "/dev/" */ tty = 1 + rindex(ptr->pseudo_ttyname, '/'); utmp_insert(tty, progname); ptr->page_buf_length = 0; strcpy(ptr->page_buf, ""); ptr->output_blocked = FALSE; ptr->output_paged = FALSE; ptr->paged_page_full = FALSE; ptr->line_count = 0; ptr->overwrite = FALSE; ptr->cursor_addressing = FALSE; ptr->cursor_addr_passes = 0; ptr->cursor_addr_row = 0; ptr->standout_mode = WIN_STANDEND; ptr->readgrain = (ptr->lines / READGRAIN) * ptr->columns; if (ptr->readgrain > BUFSIZ) { ptr->readgrain = BUFSIZ; } ptr->active = TRUE; ++active_windows; top_dog(winno); /* place cursor in new window */ wmove(stdscr, ptr->y_current + ptr->y_start, ptr->x_current + ptr->x_start); touchwin(stdscr); wrefresh(stdscr); } /* provide user with half screen height window at near full screen width */ new_window() { static int i = OVERLAY - 1; int columns, rows, xstart, ystart, new_winno; i = (i + 1) % OVERLAY; columns = screencolumns - (OVERLAY * 2); rows = screenlines / 2; /* offset slightly to enable multpile instances to be seen */ xstart = (i + 1) * 2; ystart = (rows / 2) + i; check_size(&columns, &rows, &xstart, &ystart); new_winno = win_instance(rows, columns, ystart, xstart, home_shell); if (new_winno != -1) { next_input_window(new_winno); } else { write(1, "\007", 1); } } /* allocate a window screen to hold characters and initialise it */ char ** virtwin(filler) char filler; { char *calloc(); register int line, x; register char **virt_ptr, *lineptr; virt_ptr = (char **) calloc(screenlines, sizeof (char *)); for (line = 0 ; line < screenlines ; ++line) { virt_ptr[line] = calloc(screencolumns, sizeof(char)); lineptr = virt_ptr[line]; for (x = 0 ; x < screencolumns ; ++x) { lineptr[x] = filler; } } return(virt_ptr); } free_virt_win(virt_ptr) /* return structure to free list */ char **virt_ptr; { int line; for (line = 0 ; line < screenlines ; ++line) { free(virt_ptr[line]); } free(virt_ptr); } /******************* * Screen priority positions to decide display areas. * Originally declared via virtwin(), but has been changed to use * ints (instead of chars) to hopefully speed it up (by requiring less * conversions to ints for comparisons etc.). *******************/ int ** priority_win() { char *calloc(); register int x, *lineptr, **priority_ptr, line; priority_ptr = (int **) calloc(screenlines, sizeof (int *)); for (line = 0 ; line < screenlines ; ++line) { priority_ptr[line] = (int *) calloc(screencolumns, sizeof(int)); lineptr = priority_ptr[line]; for (x = 0 ; x < screencolumns ; ++x) { lineptr[x] = -1; } } return(priority_ptr); } free_priority_win(priority_ptr) /* return structure to free store */ int **priority_ptr; { register int line; for (line = 0 ; line < screenlines ; ++line) { free(priority_ptr[line]); } free(priority_ptr); } /* fill window with specified fill pattern (usually a ' ') */ win_initialise(winno, filler) int winno; char filler; { register char *lineptr; register int x, y; register int y_end, x_end; register struct windowdetails *ptr; int y_start, x_start; ptr = &(win[winno]); y_start = ptr->y_start; x_start = ptr->x_start; y_end = ptr->y_end; x_end = ptr->x_end; /* fill window with filler */ for (y = y_start ; y < y_end ; ++y) { lineptr = ptr->screenptr[y]; for (x = x_start ; x < x_end ; ++x) { lineptr[x] = filler; } } border(winno, BOX); } border(winno, command) /* place box around window */ int winno, command; { register struct windowdetails *ptr; register int x, y, y_end, x_end; int top, side, y_start, x_start; char winid; if (command == BOX) { top = '-' | win_border; side = '|' | win_border; } else { /* NOT_BOX */ top = ' '; side = ' '; } ptr = &(win[winno]); y_start = ptr->y_start - 1; x_start = ptr->x_start - 1; y_end = ptr->y_end; x_end = ptr->x_end; /* side of box */ for (y = y_start + 1; y < y_end ; ++y) { ptr->screenptr[y][x_start] = side; ptr->screenptr[y][x_end] = side; } /* top and bottom */ for (x = x_start + 1 ; x < x_end ; ++x) { ptr->screenptr[y_start][x] = top; ptr->screenptr[y_end][x] = top; } ptr->screenptr[y_start][x_start] = ' '; ptr->screenptr[y_start][x_end] = ' '; ptr->screenptr[y_end][x_start] = ' '; ptr->screenptr[y_end][x_end] = ' '; /* place window number in top corners (ish) */ if (winno < MAX_WINDOWS && top != ' ') { winid = '0' + winno; ptr->screenptr[y_start][x_start + 3] = winid; ptr->screenptr[y_start][x_end - 3] = winid; } } /* make window topmost one and adjust visibility priority list accordingly */ top_dog(winno) int winno; { register struct windowdetails *ptr; register int x, *line_priority; register char *lineptr, *masterline; int y, y_start, x_start, y_end, x_end, start, i; ptr = &(win[winno]); y_start = ptr->y_start - 1; x_start = ptr->x_start - 1; y_end = ptr->y_end; x_end = ptr->x_end; /* ensure that time is not overwritten on top border */ if (y_start == 0) { lineptr = ptr->screenptr[0]; line_priority = screen_priority[0]; masterline = masterscreen[0]; for (x = x_start ; x <= x_end ; ++x) { if (x < time_start || x > time_end) { line_priority[x] = winno; masterline[x] = lineptr[x]; } else { nottimestring[x - time_start] = lineptr[x]; line_priority[x] = winno; if (timeon == FALSE) { masterline[x] = lineptr[x]; } } } ++y_start; } for (y = y_start ; y <= y_end ; ++y) { lineptr = ptr->screenptr[y]; line_priority = screen_priority[y]; masterline = masterscreen[y]; for (x = x_start; x <= x_end ; ++x) { line_priority[x] = winno; masterline[x] = lineptr[x]; } } /* find window in list */ for (i = 0 ; i < tail ; ++i) { if (window_priorities[i] == winno) { break; } } if (i == tail) { /* new window */ ++tail; } /* push down priorities and place latest on top */ while (i > 0) { window_priorities[i] = window_priorities[i - 1]; --i; } window_priorities[0] = winno; } /******************* * Either make window backmost one or remove completely by * making window lie behind fixed screen background window. * Adjust visibility priority list accordingly. *******************/ bottom_dog(winno, status) int winno, status; { register struct windowdetails *ptr; register int x, *line_priority; register char *lineptr, *masterline; int y, i, j, start, end; /* find position in list */ for (start = 0 ; start < tail ; ++start) { if (window_priorities[start] == winno) { break; } } if (status == HIDE) { end = tail - 2; } else { /* REMOVE */ end = tail - 1; --tail; /* since window no longer exists */ } /* move up and place winno in list */ for (i = start ; i < end ; ++i) { window_priorities[i] = window_priorities[i + 1]; } window_priorities[end] = winno; /* dumb removal of window whilst maintaining window priorities */ ptr = &(win[winno]); for (i = tail - 1 ; i >= 0 ; --i) { j = window_priorities[i]; for (y = ptr->y_start - 1 ; y <= ptr->y_end ; ++y) { lineptr = win[j].screenptr[y]; line_priority = screen_priority[y]; masterline = masterscreen[y]; for (x = ptr->x_start - 1 ; x <= ptr->x_end ; ++x) { /* ensure that time is not overwritten */ if (y == 0 && x >= time_start && x <= time_end){ if (lineptr[x] != '\0') { nottimestring[x - time_start] = lineptr[x]; } if (timeon == TRUE) { continue; } } if (lineptr[x] != '\0') { line_priority[x] = j; masterline[x] = lineptr[x]; } } } } } top_corners(winno) /* expose top corners of a window */ int winno; { register struct windowdetails *ptr; register int y, x; int diff; ptr = &(win[winno]); y = ptr->y_start - 1; x = ptr->x_start - 1; diff = ptr->x_end - x; /* for both top corners */ for ( ; x <= ptr->x_end ; x += diff) { if (screen_priority[y][x] == winno) { if (y == 0) { /* check for overwriting time */ if (x >= time_start && x <= time_end) { nottimestring[x - time_start] = ptr->screenptr[y][x]; if (timeon == TRUE) { continue; } } } masterscreen[y][x] = ptr->screenptr[y][x]; } } } /******************* * Doesn't check if widen action will have any effect. This allows * user to send SIGWINCH to process if he really wants to. *******************/ widen_window(winno, direction) int winno, direction; { struct windowdetails *ptr; int old_x_start, old_x_end, x_diff, x; int old_y_start, old_y_end, y_diff, y; char **virtwin(), **old_screenptr; ptr = &(win[winno]); border(winno, NOT_BOX); /* remove window border */ bottom_dog(winno, REMOVE); /* remove window */ if (direction == HORIZONTAL_EXPAND) { y_diff = 0; old_x_start = ptr->x_start; /* set up new x size */ old_x_end = ptr->x_end; ptr->x_start = 1; x_diff = old_x_start - ptr->x_start; ptr->columns = screencolumns - 2; ptr->x_end = ptr->x_start + ptr->columns; } else { /* VERTICAL_EXPAND */ x_diff = 0; old_y_start = ptr->y_start; /* set up y new size */ old_y_end = ptr->y_end; ptr->y_start = 1; y_diff = old_y_start - ptr->y_start; ptr->lines = screenlines - 2; ptr->y_end = ptr->y_start + ptr->lines; } check_size(&ptr->columns, &ptr->lines, &ptr->x_start, &ptr->y_start); /* copy old window contents to new window */ old_screenptr = ptr->screenptr; ptr->screenptr = virtwin('\0'); win_initialise(winno, ' '); if (direction == HORIZONTAL_EXPAND) { for (y = ptr->y_start ; y < ptr->y_end ; ++y) { for (x = old_x_start ; x < old_x_end ; ++ x) { ptr->screenptr[y][x - x_diff] = old_screenptr[y][x]; } } } else { /* VERTICAL_EXPAND */ for (y = old_y_start ; y < old_y_end ; ++y) { for (x = ptr->x_start ; x < ptr->x_end ; ++ x) { ptr->screenptr[y - y_diff][x] = old_screenptr[y][x]; } } } top_dog(winno); next_input_window(winno); /******************* * place cusor in window - can't rely on manager() to do this * with input_changed flag since it may be servicing output before * input_changed check is performed again ! *******************/ wmove(stdscr, ptr->y_current + ptr->y_start, ptr->x_current + ptr->x_start); free_virt_win(old_screenptr); /* set new (?) tty size */ set_tty_size(ptr->slavefd, ptr->lines, ptr->columns, TRUE); } window_info(winno) /* display information about windows */ int winno; { static char command_string[] = "[ w or 0-9 for next window, return to exit ] "; int found; char c; while (1) { selected_window_info(winno); fputs(command_string, stdout); fflush(stdout); do { read(1, &c, 1); if (c == '\r' || c == '\n') { return(FALSE); } found = FALSE; if (c == 'w') { do { winno = (winno + 1) % MAX_WINDOWS; } while (win[winno].active == FALSE); found = TRUE; } else { winno = c - '0'; if (winno >= 0 && winno <= MAX_WINDOWS) { if (win[winno].active == TRUE) { found = TRUE; } } else { winno += '0'; /* restore */ } } if (found == TRUE) { /* remove command */ clear_line(sizeof(command_string)); } else { /* indicate error */ write(1, "\007", 1); } } while (found != TRUE); } } static selected_window_info(winno) /* display dumbly status of a window */ int winno; { struct windowdetails *ptr; char *status(), *truth_status(); ptr = &(win[winno]); printf("\r\nstatus information for window %d\r\n\n", winno); printf(" tty name - %s\r\n", ptr->pseudo_ttyname); printf(" lines - %-6d\r\n", ptr->lines); printf(" columns - %-6d\r\n", ptr->columns); printf(" running - %s [ pid = %d ]\r\n", ptr->progy, ptr->slavepid); printf(" XOFF - %s\r\n", status(ptr->output_blocked)); printf(" page mode - %s\r\n", status(ptr->output_paged)); if (ptr->output_paged) { printf("page mode full - %s\r\n", truth_status(ptr->paged_page_full)); } printf("page overwrite - %s\r\n", status(ptr->overwrite)); putchar('\n'); } char * status(expr) int expr; { if (expr) { return("ON"); } else { return("OFF"); } } char * truth_status(expr) int expr; { if (expr) { return("TRUE"); } else { return("FALSE"); } } //E*O*F window.c// exit 0 -- David P. Zimmerman "Unix RULES!!!" - anonymous Arpa: dpz@topaz.rutgers.edu Uucp: ...{allegra | harvard | seismo | sri-iu | ut-sally}!topaz!dpz