Browse with TCL interface (part 01/02)

Peter da Silva peter at sugar.hackercorp.com
Tue Mar 6 02:54:31 AEST 1990


Archive-name: browse-tcl/alpha/Part01

[Rewrapped with a fixed version of shar]

This is accompanied with a patch that should be applied to TCL to allow NULL
variables. Currently TCL doesn't distinguish between NULL variables and unset
ones, so if you say:

	set a {}
	print $a\n

You will abort because it thinks 'a' is not set. The default browse key
bindings depend on the ability to have null variables, so you need to apply
this patch to your copy of TCL. See 'tcl.pat.vars'.

If you're not running on a BSD system, where apparently malloc will abort
cleanly if you run out of memory, it would be advisable to put a #include
ckalloc.h and a #define malloc ckalloc in your tcl.h and tclInt.h files as
well. This allows Browse to exit cleanly when it runs out of memory.

I apologise for this stuff, but tcl is still pretty much BSD based. I've
got a cleaned-up system V and system III compatible version of TCL in the
works. I've sent a copy to John Ousterhout, so hopefully this stuff will get
properly integrated with the real thing.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of archive 1 (of 2)."
# Contents:  MANIFEST browse.1 browse.c screen.c tcl.pat.vars
#   tcl_browse.c tcl_browse.h tcl_get.c
# Wrapped by peter at sugar on Mon Mar  5 10:49:49 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(997 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   2	Makefile
X browse.1                   1	Documentation on browse
X browse.c                   1	Main source for browse
X browse.rc                  2	Default key bindings (/etc/browse.rc)
X ckalloc.c                  2	Malloc and check for overflow.
X ckalloc.h                  2	Header for ckalloc.c
X message.c                  2	Varargs printf-to-command-line
X sample.rc                  2	Sample .browserc
X screen.c                   1	Termcap handling functions
X system.h                   2	Define host operating system
X tcl.pat.vars               1	Patch TCL for NULL vars.
X tcl_browse.c               1	TCL extensions: browse command
X tcl_browse.h               1	Common header file for TCL support code
X tcl_get.c                  1	TCL extensions: set command
X tcl_glue.c                 2	Glue Browse and TCL together
END_OF_FILE
if test 997 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'browse.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'browse.1'\"
else
echo shar: Extracting \"'browse.1'\" \(7866 characters\)
sed "s/^X//" >'browse.1' <<'END_OF_FILE'
X.TH BROWSE 1
X.SH NAME
Xbrowse \- Directory browser for UNIX (BROWSE 2.0)
X.SH SYNOPSIS
X.B browse
X[
X.I directory
X]
X.SH DESCRIPTION
X.PP
X.B Browse
Xis a directory browser: it puts up an 'ls -l' listing on the screen and
Xallows you to examine it and the files within it. The default key binding
Xis reminiscent of 'vi'. The core of the program is John Ousterhout's "TCL"
Xcommand language, and you need this library to build this release of
X"browse". Two families of commands have been added to the TCL core command
Xset for this release, as well as a "browse.rc" file that emulates the
Xoriginal browse.
X.PP
XThere are two sets of modes that browse may be in: narrow/wide mode and
Xpage/line mode. In narrow mode the 'ls -l' listing is suppressed, and the
Xentire names of files are displayed. In wide mode only the first 14
Xcharacters of the last component of the file name is displayed. Page mode
Xis like "visual" mode in vi: a full screen display of the directory is used.
XLine mode is like "open" mode in vi: only a single line is available. In
Xthe default bindings, line mode is entered whenever a "browse shell" command
Xis executed or when a long message forces the screen to scroll.
X.PP
XTo bind a key to a browse command, define a function with the name
X"func_'key'", where 'key' is the name of the key you want to bind.
XYou can use "get keyname \key" to find the name. For most keys the
Xname is something like 'x' or '^y' or meta_'z', but to avoid conflicts
Xwith TCL metacharacters, there are some special cases:
X.B backslash,
X.B close_brace,
X.B close_bracket,
X.B ctrl_backslash,
X.B ctrl_close_bracket,
X.B delete,
X.B dollar_sign,
X.B double_quote,
X.B escape,
X.B open_brace,
X.B open_bracket,
X.B quote,
X.B semicolon,
Xand
X.B space.
X.PP
XIf an error occurs during execution of these functions, the TCL error
Xmessage will be displayed in the status/command line.
X.PP
XWhen entering a string, a second set of bindings is used. These are
Xmade the same way, with the prefix "macro_" rather than "key_". The
Xstring returned from thses commands is included literally in the input
Xstring.
X.SH COMMANDS
X.PP
XThe commands are divided into two families: "browse" and "get". The former
Xset perform actions and (if appropriate) return a 0 or 1 for success or
Xfailure. The latter set return a value. None of these commands will "fail"
Xin the tcl sense except for syntax errors:
X.IP "\fBbrowse commands\fR"
XReturns a list of functions available through the "browse" command.
X.IP "\fBbrowse chdir\fR directory"
XChange to, and scan, a new directory.
X.IP "\fBbrowse exit\fR [code]"
XExit from "browse", returning the exit code indicated.
X.IP "\fBbrowse delete\fR file..."
XDelete a file or files, updating the display if required.
X.IP "\fBbrowse rename\fR from to"
XRename a file, updating the display if required.
X.IP "\fBbrowse move\fR line"
XChange the current entry, updating the display to keep it in the screen.
X.IP "\fBbrowse message\fR [text]..."
XPrint a message in the status line. If you are in page mode, control
Xcharacters are displayed in inverse video, and meta characters are under-
Xlines. In line mode, no such transformations are done.
X.IP "\fBbrowse print\fR [text]..."
XPrint text, subject to the same conditions. This is like "browse message",
Xexcept that the status line is not erased before the message comes out.
X.IP "\fBbrowse tag\fR action file..."
XAction is \fB+\fR, \fB-\fR, or \fB/\fR followed by \fBP\fR or \fBT\fR.
XSet (+), clear (-), or toggle (/) the Permanent or Tag bits on the file,
Xupdating the display if necessary. Permanent files remain on the display
Xwhen you change directories. Tagged file names or ids may be found with
Xthe "get files *" command.
X.IP "\fBbrowse bell\fR"
XRing the bell (or flash the display).
X.IP "\fBbrowse redraw\fR"
XRepaint the display.
X.IP "\fBbrowse rescan\fR"
XReload the current directory, attempting to maintain the same current
Xentry.
X.IP "\fBbrowse shell\fR command"
XExecute a shell command interactively, after moving the cursor to the end
Xof the page. Browser is put in line mode after this operation.
X.IP "\fBbrowse mode\fR mode"
XMode may be
X\fBline\fR,
X\fBpage\fR,
X\fBwide\fR,
Xor
X\fBnarrow\fR.
XPut browser into a given mode.
X.IP "\fBbrowse add\fR file..."
XAdd a named file to the display.
X.IP "\fBget commands\fR"
XLike "browse commands", this returns a list of the functions available
Xwith the "get" command.
X.IP "\fBget response\fR [prompt [default [term]]]"
XGet a string response from the user, optionally prompted with "prompt".
XIf necessary, a default value may be included, and an optional terminator
Xcan be used.
X.IP "\fBget key\fR [prompt]"
XGet a single key response from the user, optionally prompted with "prompt".
X.IP "\fBget keyname\fR key"
XGet the name of a key for constructing key_ and macro_ procs.
X.IP "\fBget line\fR id"
XId is \fBhome\fR, \fBlast\fR, \fBend\fR, \fB"*"\fR, or \fB"."\fR.
XGet a line number. home is the first line displayed on the screen, last
Xis the last. End is the last line in the directory (num files - 1). Asterisk is
Xa list of all tagged lines, and dot is the current line.
X.IP "\fBget file\fR id"
XId is a line number, \fB"*"\fR, or \fB"."\fR.
XGet a file name. If you provide a decimal line number it will return the
Xfile name on that line. An asterisk returns a list of all tagged
Xfiles, and dot is the current file.
X.IP "\fBget env\fR name"
XGet an environement variable.
X.IP "\fBget error\fR"
XGet the last error returned from a system call in the "browse" or "get"
Xcommands, in perror format, or an indication of the error. This is used
Xwhen a "browse" command returns 0, to see why. It was done this way to avoid
Xthe complexity of having calls to "catch" liberally sprinkled through your
Xcode... which would be the usual case in an application like this.
X.IP "\fBget mode\fR"
XGets the current valuse of the narrow/wide and line/page modes.
X.IP "\fBget cwd\fR"
XGet current working directory.
X.SH DEFAULT BINDINGS
X.IP "\fB!\fR"
XPrompt for and execute a shell command.
X.IP "\fB+\fR"
XPrompt for a file name and add it to the display.
X.IP "\fB.\fR"
XPrompt for a directory name beginning with '.' and change to that directory.
X.IP "\fB/\fR"
XPrompt for a directory name beginning with '/' and change to that directory.
X.IP "\fB:\fR"
XPrompt for and execute a TCL command.
X.IP "\fB<\fR"
XSwitch to narrow mode.
X.IP "\fB=\fR"
XPrompt for a directory name and change to it.
X.IP "\fB>\fR"
XSwitch to wide mode.
X.IP "\fBH\fR"
XMove to the top line of the screen.
X.IP "\fBJ\fR"
XMove to the top of the directory.
X.IP "\fBK\fR"
XMove to the bottom of the directory.
X.IP "\fBL\fR"
XMove to the last line on the screen.
X.IP "\fBM\fR"
XMove to the middle of the screen.
X.IP "\fBR\fR"
XPrompt for a directory and move (rename) a group of files to that directory.
X.IP "\fB^\fR"
XMove to the top of the directory.
X.IP "\fB^B\fR"
XMove up half a page.
X.IP "\fB^F\fR"
XMove down half a page.
X.IP "\fB^J\fR"
XIf in line mode, switch to page mode.
X.IP "\fB^L\fR"
XRepaint the screen.
X.IP "\fB^R\fR"
XRescan the current directory.
X.IP "\fBdd\fR"
XDelete a file or a group of tagged files, after prompting for verification.
X.IP "\fBj\fR"
XMove down 1 line.
X.IP "\fBk\fR"
XMove up 1 line.
X.IP "\fBp\fR"
XToggle the 'permanent' bit on a file.
X.IP "\fBqq\fR"
XExit from browse.
X.IP "\fBr\fR"
XRename a single file.
X.IP "\fBt\fR"
XToggle the 'tag' bit on a file.
X.IP "\fBv\fR"
XEdit te current file with 'vi'.
X.IP "\fB~\fR"
XPrompt for a directory name beginning with your home directory, then change
Xto the directory.
X.IP "\fB$\fR"
XMove to the end of the directory.
X.IP "\fBspace\fR"
XIf the current file is a directory, go to it. Otherwise, call more (or your
Xstandard pager) on the file.
X.SH SEE ALSO
XTCL(1), John Ousterhout.
X.SH BUGS
X.PP
XIf there are more than 1024 files in a directory, not all will be seen.
X.PP
XNot all commands of the original 'browse' can be implemented.
X.PP
XMore of the input processing could be performed by macros.
END_OF_FILE
if test 7866 -ne `wc -c <'browse.1'`; then
    echo shar: \"'browse.1'\" unpacked with wrong size!
fi
# end of 'browse.1'
fi
if test -f 'browse.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'browse.c'\"
else
echo shar: Extracting \"'browse.c'\" \(20486 characters\)
sed "s/^X//" >'browse.c' <<'END_OF_FILE'
X/*             -- Just what the hell am I ??? ---                            */
X/* (If this is made from MAKEFILE, BSD or USG should be set.) */
X
X#include <stdio.h>
X
X#include "system.h"
X
X/* 		-- Miscellaneous include files --		             */
X
X#include <sys/param.h>			/* NCARGS, and others */
X#ifndef M_XENIX
X# include <sys/types.h>                       /* data types for various files */
X#endif
X#include <sys/stat.h>         /* stat data structure for getdir(), statout() */
X#include <sys/dir.h>                      /* dir data structure for getdir() */
X#include <pwd.h>                       /* passwd data structure for u_name() */
X#include <grp.h>                        /* group data structure for g_name() */
X#ifdef BSD
X# include <sys/time.h>                  /* time data structure for printime() */
X#else
X# include <time.h>    	               /* time data structure for printime() */
X#endif
X#include <signal.h>
X
X#ifndef MAXNAMLEN
X# ifdef BSD
X#  define MAXNAMLEN 256
X# else
X#  define MAXNAMLEN DIRSIZ
X# endif
X#endif
X
X/*		-- make information --
XBUILD
Xbrowse: browse.c
X	cc browse.c -O -o browse -ltermlib
XEND
X*/
X
X/*		-- Miscellaneous defines --				     */
X#define FALSE 0
X#define TRUE 1
X
X#define MAXENTS 1024
X#define MAXID 16
X#define MAXLINE 81
X#define NCOL 64
X#define MAXNAME 14
X#define FILENAME 256
X
X/*		-- Extended directory entry format --			     */
Xstruct entry {
X	char *e_name;
X	int e_flags;
X#define FTAGGED (1<<0)
X#define FPERMANENT (1<<1)
X	struct stat e_stat;                             /* file status field */
X	char e_uname[9];                                        /* user name */
X	char e_gname[9];                                /* user's group name */
X} 
X*xentries[MAXENTS], **entries=xentries;
Xint nentries;
X
X/*		-- Look-up cache for user names --			     */
Xstruct idtab {
X	int  id_id;                                    /* user (or group) id */
X	char id_name[9];                                 /* name[8] + filler */
X} 
Xu_list[MAXID],                                       /* Matched user id's. */
Xg_list[MAXID];                                              /* ditto group */
Xint u_ptr=0, g_ptr=0;                                     /* current entries */
X
X/*		-- Global variables --					     */
Xint ccol=NCOL;			    /* Width of used display, current column */
Xint quickmode;			  /* short display mode (files only) */
X
Xint	top, curr;               /* Positions of screen in directory */
X#define bottom ((top+nlines>nentries)?nentries:(top+nlines))
Xchar	*dot;                                   /* name of current directory */
Xint	nlines;                         /* number of lines displayed on page */
Xchar	display_up;                               /* Does the display exist? */
Xint	todump=1;				 /* Do we want to dump data? */
Xint	ended;                                    /* Have we quite finished? */
Xint	intrup;					/* Have we been interrupted? */
Xchar	*HOME;					  /* Where did I start from? */
Xchar	*SHELL;					   /* How do I run programs? */
X
X/*		-- types of functions !!!				     */
Xchar *getenv();
Xchar *malloc();
X
X#define NEW(t) (t *)malloc(sizeof (t))
X#define NIL(t) ((t) 0)
X
X/*		-- Code starts here: dummy main --   			     */
Xmain(ac, av)
Xint ac;
Xchar **av;
X{
X	if(ac>1) chdir(av[1]);
X
X	HOME=getenv("HOME");
X	SHELL=getenv("SHELL");
X
X	intrup=0;
X	if(tcl_init()) {
X		tinit(getenv("TERM"));
X		clear_all();
X		browse();
X		tend();
X		tcl_end();
X	}
X	exit(0);
X}
X
Xclear_all()
X{
X	int i;
X
X	for(i = 0; i < MAXENTS; i++)
X		entries[i] = 0;
X}
X
Xchar *clone(name)
Xchar *name;
X{
X	char *hold;
X	
X	hold = (char *)malloc(strlen(name)+1);
X
X	if(hold==0)
X		return 0;
X	strcpy(hold, name);
X	return hold;
X}
X
Xnewname(e, name)
Xstruct entry *e;
Xchar *name;
X{
X	if(e->e_name)
X		free(e->e_name);
X	e->e_name = clone(name);
X}
X
X#if BSD
Xreadent(dp, buffer)
XDIR *dp;
Xchar *buffer;
X{
X	struct direct *ptr;
X
X	do {
X		ptr = readdir(dp);
X		if(!ptr) return 0;
X	} while(ptr->d_ino == 0);
X
X	strcpy(buffer, ptr->d_name);
X	return 1;
X}
X#else
X#define opendir(n) fopen(n, "r")
X#define DIR FILE
Xreadent(dp, buffer)
XDIR *dp;
Xchar *buffer;
X{
X	struct direct db;
X	while(fread(&db, sizeof(struct direct), 1, dp)) {
X		if(db.d_ino) {
X			strncpy(buffer, db.d_name, MAXNAMLEN);
X			buffer[MAXNAMLEN] = 0;
X			return 1;
X		}
X	}
X	return 0;
X}
X#define closedir fclose
X#endif
X
Xgetdir()
X{
X	char *u_name(), *g_name();
X	DIR *dp;
X	static char buffer[MAXNAMLEN+1];
X	int i, p;
X
X	if(!(dp = opendir("."))) {
X		save_errno(".");
X		return FALSE;
X	}
X
X	p = 0;
X	for(i = 0; i < nentries; i++) {
X		if(entries[i]->e_flags & FPERMANENT) {
X			if(p != i) {
X				struct entry *hold;
X				hold = entries[p];
X				entries[p] = entries[i];
X				entries[i] = hold;
X			}
X			p++;
X		}
X	}
X
X	for(nentries = p; !intrup && nentries < MAXENTS; nentries++) {
X		if(!entries[nentries]) {
X			entries[nentries] = NEW(struct entry);
X			if(!entries[nentries])
X				break;
X			entries[nentries]->e_name = NIL(char *);
X		}
X
X		if(!readent(dp, buffer))
X			break;
X
X		if(stat(buffer, &entries[nentries]->e_stat)==-1) {
X			closedir(dp);
X			save_errno(buffer);
X			return FALSE;
X		}
X
X		newname(entries[nentries], buffer);
X
X		entries[nentries]->e_flags = 0;
X
X		strcpy(entries[nentries]->e_uname,
X			u_name(entries[nentries]->e_stat.st_uid));
X
X		strcpy(entries[nentries]->e_gname,
X			g_name(entries[nentries]->e_stat.st_gid));
X	}
X
X	closedir(dp);
X
X	if(intrup)
X		return FALSE;
X
X	if(nentries>=MAXENTS || entries[nentries]==NIL(struct entry *)) {
X		save_errmsg(".: Directory too large");
X		return FALSE;
X	}
X
X	sortdir();
X
X	if(intrup) {
X		save_errmsg("Interrupted");
X		return FALSE;
X	}
X	return TRUE;
X}
X
Xat_current()
X{
X	at_file(curr);
X}
X
Xat_file(file)
Xint file;
X{
X	if(display_up) {
X		if(file < top || file >= top+nlines)
X			return 0;
X		at(0, curr-top+2);
X	} else {
X		if(file != curr)
X			return 0;
X		cmdline();
X	}
X	return 1;
X}
X
Xdisp_flags(flags)
X{
X	outc((flags&FTAGGED)?'+':((flags&FPERMANENT)?'!':' '));
X}
X
Xshow_flags(ent)
X{
X	if(!display_up) return;
X	if(ent < top || ent >= top+nlines) return;
X	at(quickmode?0:(ccol-1), ent-top+2);
X	disp_flags(entries[ent]->e_flags);
X}
X
Xtag(ent, mode, bit)
X{
X	switch(bit) {
X		case 'P': bit = FPERMANENT; break;
X		case 'T': bit = FTAGGED; break;
X	}
X	switch(mode) {
X		case -1: entries[ent]->e_flags &= ~bit; break;
X		case 0: entries[ent]->e_flags ^= bit; break;
X		case 1: entries[ent]->e_flags |= bit; break;
X	}
X	show_flags(ent);
X}
X
Xtagged(ent)
X{
X	return (entries[ent]->e_flags & FTAGGED) ? 1 : 0;
X}
X
Xdelete_from_display(file)
Xint file;
X{
X	if(!display_up) return;
X
X	if(file>=top+nlines) return;
X
X	if(file < top) file = top;
X
X	scroll(2+file-top, 2+nlines, 1);
X	at(0, 2+nlines-1);
X	if(top+nlines >= nentries)
X		outc('~');
X	else
X		dump(bottom, bottom+1);
X}
X		
Xdelete_entry(file)
Xint file;
X{
X	struct entry *hold;
X	int	i;
X
X	delete_from_display(file);
X
X	hold = entries[file];
X	for(i=file; i<nentries-1; i++)
X		entries[i]=entries[i+1];
X	entries[nentries-1]=hold;
X	nentries--;
X
X	if(file < curr || curr >= nentries) {
X		curr--;
X		if(top >= nentries) {
X			top--;
X			display_up = 0;
X			todump = 1;
X		}
X	}
X} 
X
Xins_at(ent, i)
Xstruct entry *ent;
Xint i;
X{
X	struct entry *hold;
X	int j;
X
X	/* Allocate slot at end */
X	if(!entries[nentries]) {
X		entries[nentries] = NEW(struct entry);
X		if(!entries[nentries]) {
X			save_errno(ent->e_name);
X			return 0;
X		}
X		entries[nentries]->e_name = NIL(char *);
X	} else if(entries[nentries]->e_name) {
X		free(entries[nentries]->e_name);
X		entries[nentries]->e_name = NIL(char *);
X	}
X
X	/* Copy data into slot */
X	*entries[nentries] = *ent;
X	entries[nentries]->e_name = clone(ent->e_name);
X	if(!entries[nentries]->e_name) {
X		save_errno(ent->e_name);
X		return 0;
X	}
X
X	if(i != nentries) {
X		/* Rotate slot to middle */
X		hold = entries[nentries];
X		for(j = nentries; j > i; j--)
X			entries[j] = entries[j-1];
X		entries[i] = hold;
X	}
X	nentries++;
X
X	if(display_up) {
X		if(i < top)
X			i = top;
X		if(i >= bottom)
X			return 1;
X		scroll(2+i-top, 2+nlines, -1);
X		at(0, 2+i-top);
X		dump(i, i+1);
X	}
X	return 1;
X}
X
Xins_entry(ent)
Xstruct entry *ent;
X{
X	int i;
X
X	if(nentries >= MAXENTS) {
X		save_errmsg("Too many files");
X		return 0;
X	}
X
X	for(i = 0; i < nentries; i++)
X		if(entcmp(&ent, &entries[i]) < 0)
X			break;
X
X	return ins_at(ent, i);
X}
X
Xdomove(ent, new_name)
Xint ent;
Xchar *new_name;
X{
X	struct entry hold;
X	hold = *entries[ent];
X
X	if(link(entries[ent]->e_name, new_name)!=0) {
X		save_errno(entries[ent]->e_name);
X		return 0;
X	}
X	if(unlink(entries[ent]->e_name)!=0) {
X		save_errno(entries[ent]->e_name);
X		return 0;
X	}
X
X	hold.e_name = new_name;
X	hold.e_flags &= ~FTAGGED;
X
X	delete_entry(ent);
X	return ins_entry(&hold);
X}
X
Xaddfile(name)
Xchar *name;
X{
X	struct entry hold;
X
X	if(stat(name, &hold.e_stat)==-1) {
X		save_errno(name);
X		return FALSE;
X	}
X
X	hold.e_name = name;
X
X	hold.e_flags = 0;
X
X	strcpy(hold.e_uname, u_name(hold.e_stat.st_uid));
X
X	strcpy(hold.e_gname, g_name(hold.e_stat.st_gid));
X
X	return ins_entry(&hold);
X}
X
Xpname(name, mode)
Xchar *name;
Xint mode;
X{
X	int i;
X	char *slash, *rindex();
X	int max=quickmode?MAXLINE:(MAXNAME+1);
X
X	if((mode&S_IFMT)==S_IFDIR)
X		max--;
X	else if((mode&S_IFMT)==S_IFREG && (mode&0111))
X		max--;
X	else if((mode&S_IFMT)!=S_IFREG)
X		max--;
X
X	if(!quickmode && (slash=rindex(name, '/'))) {
X		name=slash;
X		outc('<');
X		max--;
X	}
X	for(i=0; i<max && *name; name++)
X		i += ctlout(*name);
X	standend();
X	if(i==max && *name) {
X		outc('\b');
X		outc('>');
X	}
X	if((mode&S_IFMT)==S_IFDIR) {
X		outc('/');
X	}
X	else if((mode&S_IFMT)==S_IFREG && (mode&0111)) {
X		outc('*');
X	}
X	else if((mode&S_IFMT)!=S_IFREG) {
X		outc('?');
X	}
X}
X
Xsortdir()
X{
X	int   entcmp();
X	qsort(entries, nentries, sizeof(struct entry *), entcmp);
X}
X
Xentcmp(e1, e2)
Xstruct entry **e1, **e2;
X{
X	if((*e1)->e_name[0] == '/') {
X		if((*e2)->e_name[0] != '/')
X			return 1;
X	} else if((*e2)->e_name[0] == '/')
X		return -1;
X
X	return strcmp((*e1)->e_name, (*e2)->e_name);
X}
X
Xdump(start, end)
Xint start;
Xint end;
X{
X	int  i;
X	int  lo = (start<nentries)?start:nentries;
X	int  hi = (end<nentries)?end:nentries;
X
X	for(i=lo; i<hi; i++) {
X		statout(entries[i]->e_name,
X		&entries[i]->e_stat,
X		entries[i]->e_uname,
X		entries[i]->e_gname,
X		entries[i]->e_flags);
X		nl();
X	}
X	return TRUE;
X}
X
Xstatout(name, sbuf, user, group, flags)
Xchar *name;
Xstruct stat *sbuf;
Xchar *user, *group;
Xint flags;
X{
X	int mode = sbuf->st_mode;
X
X	if(!quickmode) {
X		printf("%5u ", sbuf->st_ino);
X
X		if((mode&S_IFMT)==S_IFCHR) outc('c');
X		else if((mode&S_IFMT)==S_IFBLK) outc('b');
X		else if((mode&S_IFMT)==S_IFDIR) outc('d');
X		else if((mode&S_IFMT)==S_IFREG) outc('-');
X		else outc('?');
X		triad((mode>>6)&7, mode&S_ISUID, 's');
X		triad((mode>>3)&7, mode&S_ISGID, 's');
X		triad(mode&7, mode&S_ISVTX, 't');
X		outc(' ');
X
X		printf("%3u ", sbuf->st_nlink);
X		printf("%-8s ", user);
X		printf("%-8s ", group);
X
X		if((mode&S_IFMT)==S_IFREG || (mode&S_IFMT)==S_IFDIR)
X			printf("%7ld ", sbuf->st_size);
X		else
X			printf("%3d,%3d ",
X			major(sbuf->st_rdev),
X			minor(sbuf->st_rdev));
X
X		outc(' ');
X		printime(&sbuf->st_mtime);
X	}
X
X	disp_flags(flags);
X	pname(name, sbuf->st_mode);
X}
X
Xchar *
Xu_name(uid)
Xint uid;
X{
X	int  i;
X	struct passwd *pwptr, *getpwuid();
X
X	for(i=0; i<u_ptr; i++)
X		if(u_list[i].id_id==uid)
X			return u_list[i].id_name;
X
X	if(u_ptr>=MAXID)                                       /* cache full */
X		u_ptr = 0;        /* simplistic algorithm, wrap to beginning */
X	/* with MAXID >> # common id's it's good enough */
X
X	u_list[u_ptr].id_id=uid;
X
X	if(pwptr=getpwuid(uid)) { /* Copy name */
X		for(i=0; pwptr->pw_name[i]>' '; i++)
X			u_list[u_ptr].id_name[i]=pwptr->pw_name[i];
X		u_list[u_ptr].id_name[i]=0;
X	} 
X	else /* Default to UID */
X		sprintf(u_list[u_ptr].id_name, "%d", uid);
X
X	return u_list[u_ptr++].id_name;
X}
X
Xchar *
Xg_name(gid)
Xint gid;
X{
X	int  i;
X	struct group *grptr, *getgrgid();
X
X	for(i=0; i<g_ptr; i++)
X		if(g_list[i].id_id==gid)
X			return g_list[i].id_name;
X
X	if(g_ptr>=MAXID)                                       /* cache full */
X		g_ptr = 0;        /* simplistic algorithm, wrap to beginning */
X	/* with MAXID >> # common id's it's good enough */
X
X	g_list[g_ptr].id_id=gid;
X
X	if(grptr=getgrgid(gid)) { /* Copy name */
X		for(i=0; grptr->gr_name[i]>' '; i++)
X			g_list[g_ptr].id_name[i]=grptr->gr_name[i];
X		g_list[g_ptr].id_name[i]=0;
X	} 
X	else /* Default to UID */
X		sprintf(g_list[g_ptr].id_name, "%d", gid);
X
X	return g_list[g_ptr++].id_name;
X}
X
Xprintime(clock)
Xlong *clock;
X{
X	struct tm *tmbuf, *localtime();
X	static char *months[12]= {
X		"Jan","Feb","Mar","Apr","May","Jun",
X		"Jul","Aug","Sep","Oct","Nov","Dec"
X	};
X
X	tmbuf=localtime(clock);
X	printf("%2d %3s %02d %2d:%02d",
X	tmbuf->tm_mday,
X	months[tmbuf->tm_mon],
X	tmbuf->tm_year,
X	tmbuf->tm_hour,
X	tmbuf->tm_min);
X}
X
Xheader()
X{
X	if(quickmode)
X		printf(" File Name");
X	else
X		printf(
X"Inode Long mode  LNX User     Group   Size/Dev  Modify Time     File name"
X		    );
X	nl();
X}
X
Xtriad(bits, special, code)
Xint bits, special;
Xchar code;
X{
X	if(bits&4) outc('r');
X	else outc('-');
X
X	if(bits&2) outc('w');
X	else outc('-');
X
X	if(special) outc(code);
X	else if(bits&1) outc('x');
X	else outc('-');
X}
X
Xchar *
Xpwd()
X#ifdef GETCWD
X{
X	static char mydir[FILENAME+1] = "";
X	char *getcwd();
X
X	return getcwd(mydir, FILENAME);
X}
X#else
X{
X	static char buffer[FILENAME+1];
X
X	strcpy(buffer, "exec pwd");
X	if(tcl_call(buffer, sizeof buffer)) {
X		int len = strlen(buffer);
X		while(len > 0 && buffer[len-1] <= ' ')
X			len--;
X		buffer[len] = 0;
X		return buffer;
X	}
X	save_errmsg(buffer);
X	return 0;
X}
X#endif
X
Xbrowse()
X{
X	int  c;
X
X	newdir();
X	redraw();
X	ended=0;
X
X	do {
X		intrup=0;	/* clear interrupt */
X		here_i_am();
X		fflush(stdout);
X		c = getch();
X		cmd(c);
X	} 
X	while(!ended);
X}
X
Xclearin()
X{
X	fseek(stdin, 0L, 1); /* stay here messily */
X}
X
Xchar *name_of(c)
Xint c;
X{
X	static char *p, b[80];
X	int ctrl = 0;
X
X	p = b;
X	if(c & 0x80) {
X		c &= 0x7F;
X		strcpy(b, "meta_");
X		p += 5;
X	}
X
X	switch(c) {
X		case '\033':
X			strcpy(p, "escape");
X			return b;
X		case '\034':
X			strcpy(p, "ctrl_backslash");
X			return b;
X		case '\035':
X			strcpy(p, "ctrl_close_bracket");
X			return b;
X	}
X
X	if(c < ' ') {
X		c += '@';
X		ctrl = 1;
X	}
X
X	switch(c) {
X		case ' ': strcpy(p, "space"); break;
X		case '$': strcpy(p, "dollar_sign"); break;
X		case '\'': strcpy(p, "quote"); break;
X		case '"': strcpy(p, "double_quote"); break;
X		case ';': strcpy(p, "semicolon"); break;
X		case '{': strcpy(p, "open_brace"); break;
X		case '}': strcpy(p, "close_brace"); break;
X		case '\177': strcpy(p, "delete"); break;
X		case '\\': strcpy(p, "backslash"); break;
X		case '[': strcpy(p, "open_bracket"); break;
X		case ']': strcpy(p, "close_bracket"); break;
X		default: 
X			if(ctrl)
X				sprintf(p, "'^%c'", c);
X			else
X				sprintf(p, "'%c'", c);
X			break;
X	}
X	return b;
X}
X
Xcmd(c)
Xint c;
X{
X	char buffer[80];
X
X	if(intrup) {
X		cmdline();
X		outs("Interrupt");
X		return;
X	}
X
X	sprintf(buffer, "key_%s\n", name_of(c));
X	if(!tcl_call(buffer, 80)) {
X		cmdline();
X		ctlouts(buffer);
X		if(!display_up) outc('\n');
X		bell();
X	}
X}
X
Xchar *macro(c)
Xint c;
X{
X	static char buffer[80];
X
X	sprintf(buffer, "macro_%s\n", name_of(c));
X	if(tcl_call(buffer, 80))
X		return buffer;
X	else
X		return 0;
X}
X
Xnewdir()
X{
X	int result = 1;
X
X	if(display_up)
X		at(0,0);
X	fflush(stdout);
X	dot=pwd();
X
X	if(!getdir())
X		result = 0;
X
X	curr=0;
X	top=0;
X	topline();
X	if(display_up)
X		todump=TRUE;
X
X	return result;
X}
X
Xreload()
X{
X	if(getdir()) {
X		curr=(curr>=bottom)?bottom-1:curr;
X		if(display_up) {
X			topline();
X			todump=TRUE;
X		}
X		return 1;
X	}
X	return 0;
X}
X
Xtopline()
X{
X	if(display_up)
X		at(0,0);
X	printf("%s: %d files", dot, nentries);
X	nl();
X}
X
Xset_quickmode(mode)
Xint mode;
X{
X	if(quickmode != mode) {
X		quickmode = mode;
X		if(display_up) {
X			at(0,1);
X			header();
X			todump = 1;
X		}
X	}
X}
X
Xredraw()
X{
X	clear_screen();
X	display_up=0;
X	topline();
X	at(0,1);
X	header();
X	todump=1;
X	display_up=1;
X}
X
Xdumpdata()
X{
X	at(0,2);
X	dump(top,bottom);
X	if(top+nlines>nentries) {
X		int i;
X		at(0, bottom-top+2);
X		for(i=bottom-top; i<nlines; i++) {
X			outc('~');
X			nl();
X		}
X	}
X}
X
Xhere_i_am()
X{
X	if(todump)
X		display_up = 1;
X	if(!display_up) {
X		cmdline();
X		statout(entries[curr]->e_name,
X			&entries[curr]->e_stat,
X			entries[curr]->e_uname,
X			entries[curr]->e_gname,
X			entries[curr]->e_flags);
X		at(quickmode?1:ccol, nlines+2);
X		fflush(stdout);
X 	} else if(todump) {
X 		if(!(curr-top > 0 && curr-top < nlines)) {
X 			top = curr-nlines/2;
X 			if(top<0) top=0;
X 		}
X 		dumpdata();
X 		at(quickmode?1:ccol, curr-top+2);
X 		todump=0;
X 	} else {
X 		int lines_to_scroll = curr-top;
X 		if(lines_to_scroll > 0)
X 			if((lines_to_scroll -= nlines-1) < 0)
X 				lines_to_scroll = 0;
X 		if(lines_to_scroll < 1-nlines) {
X 			top=curr;
X 			at(0,2);
X 			dump(top, bottom);
X 		} else if(lines_to_scroll > nlines-1) {
X 			top=curr-nlines+1;
X 			at(0,2);
X 			dump(top, bottom);
X 		} else if(lines_to_scroll) {
X 			scroll(2, nlines+2, lines_to_scroll);
X 			top += lines_to_scroll;
X 			if(lines_to_scroll < 0) {
X 				at(0, 2);
X 				dump(top, top-lines_to_scroll);
X 			} else {
X 				at(0, 2+nlines-lines_to_scroll);
X 				dump(bottom-lines_to_scroll, bottom);
X 			}
X 		}
X 		at(quickmode?1:ccol, curr-top+2);
X 	}
X}
X
Xbell()
X{
X	outc(7);
X}
X
Xsystem(command)
Xchar *command;
X{
X	int status;
X	int pid;
X	SIGNAL (*sigint)(), (*sigquit)();
X#ifdef BSD
X	SIGNAL  (*sigtstp)();
X#endif
X
X	sigint=signal(SIGINT, SIG_IGN);
X	sigquit=signal(SIGQUIT, SIG_IGN);
X#ifdef BSD
X	sigtstp = signal(SIGTSTP, SIG_IGN);
X#endif
X	end_screenmode();
X	cooktty();
X	fflush(stdout);
X	pid = fork();
X	if(pid == 0) {
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X#ifdef BSD
X		signal(SIGTSTP, SIG_DFL);
X#endif
X		fflush(stdout);
X		execl(SHELL, SHELL, "-c", command, (char *)0);
X		execl("/bin/sh", "sh", "-c", command, (char *)0);
X		perror("/bin/sh");
X		exit(-77);
X	}
X	if(pid==-1) {
X		save_errno("browse");
X		status = -1;
X	} else
X		wait(&status);
X	signal(SIGINT, sigint);
X	signal(SIGQUIT, sigquit);
X#ifdef BSD
X	signal(SIGTSTP, sigtstp);
X#endif
X	rawtty();
X
X	if(status & 0xFF)
X		return -(status&0xFF);
X	else
X		return (status&0xFF00)>>8;
X}
X
Xaddstring(ptr, buf, ip)
Xchar *ptr, *buf;
Xint *ip;
X{
X	while(*ptr)
X		ctlout(buf[(*ip)++] = *ptr++);
X	standend();
X}
X
Xchar
Xinps(buf, text, termin)
Xchar *buf;
Xchar *text;
Xchar termin;
X{
X	int i = 0;
X	char c, *txp;
X	char *s;
X
X	txp=text?text:"!";
X
X	while(!intrup &&
X	    (fflush(stdout), c=getch()) != '\033' &&
X	    c != '\n' &&
X	    c != termin)
X		switch(c) {
X		case '\b': 
X		case '\177':
X			if(i>0) {
X				printf("\b \b");
X				i--;
X			} 
X			else
X				bell();
X			break;
X		case 'U'-'@':
X			txp=text?text:"!";
X		case 'X'-'@':
X			if(i>0)
X				while(i>0) {
X					printf("\b \b");
X					i--;
X				}
X			else
X				bell();
X			break;
X		case 'K'-'@':
X			if(*txp)
X				ctloutc(buf[i++] = *txp++);
X			break;
X		case '\\':
X			outc(c);
X			fflush(stdout);
X			c=getch();
X			if( c=='\\' || c=='\b' || c=='\177' ||
X			    c=='K'-'@' || c=='U'-'@' || c=='X'-'@' ||
X			    c==termin || c=='\033' || c=='\n') {
X				outc('\b');
X				ctloutc(buf[i++]=c);
X				break;
X			} 
X			else if(c>='0' && c<='7') {
X				int n, val=0;
X				for(n=0; n<3 && c>='0' && c<='7'; n++) {
X					val = val*8 + c-'0';
X					outc('\b');
X					ctloutc(val);
X					c=getch();
X				}
X				ungetch(c);
X				c=buf[i++]=val;
X				break;
X			} 
X			else if(c=='^') {
X				outc('\b');
X				outc('^');
X				fflush(stdout);
X				c=getch();
X				if(c>='?'&&c<='_') {
X					outc('\b');
X					ctloutc(buf[i++]=(c-'@')&'\177');
X					break;
X				} 
X				else if(c>='`'&&c<='~') {
X					outc('\b');
X					ctloutc(buf[i++]=c-'`');
X					break;
X				} /* otherwise default */
X				else buf[i++]='^'; /* after adding caret */
X			}
X			else buf[i++]='\\';
X		default:
X			if(c=='V'-'@')
X				ctloutc(buf[i++] = getch());
X			if(s = macro(c))
X				addstring(s, buf, &i);
X			else
X				ctloutc(buf[i++] = c);
X			break;
X		}
X
X	buf[i] = 0;
X	return intrup?'\033':c;
X}
X
Xctlouts(s)
Xchar *s;
X{
X	int cnt = 0;
X
X	while(*s)
X		cnt += ctlout(*s++);
X	cnt += standend();
X
X	return cnt;
X}
X
Xctloutc(c)
Xchar c;
X{
X	int cnt;
X
X	cnt = ctlout(c);
X	cnt += standend();
X
X	return cnt;
X}
X
Xctlout(c)
Xchar c;
X{
X	int cnt = 0;
X	if(c&'\200') {
X		cnt += underline();
X		cnt += ctlout(c&'\177');
X		return cnt;
X	} 
X	else if(c<' ' || c=='\177') {
X		cnt += standout();
X		outc((c+'@')&'\177');
X		cnt++;
X		return cnt;
X	} 
X	else {
X		cnt += standend();
X		outc(c);
X		cnt++;
X		return cnt;
X	}
X}
X
Xget_index(file)
Xchar *file;
X{
X	int i;
X
X	for(i = 0; i<nentries; i++)
X		if(strcmp(entries[i]->e_name, file) == 0)
X			return i;
X	return -1;
X}
X
Xchar *file_name(i)
Xint i;
X{
X	if(i < 0 || i >= nentries)
X		return 0;
X	else
X		return entries[i]->e_name;
X}
X
Xenter(dir)
Xchar *dir;
X{
X	if(access(dir, 5)==0 && chdir(dir)==0)
X		return newdir();
X	save_errno(dir);
X	return 0;
X}
X
Xisdir(entry)
Xstruct entry *entry;
X{
X	return((entry->e_stat.st_mode&S_IFMT)==S_IFDIR);
X}
X
Xgetch()
X{
X	return getchar();
X}
X
Xungetch(c)
Xchar c;
X{
X	ungetc(c, stdin);
X}
X
END_OF_FILE
if test 20486 -ne `wc -c <'browse.c'`; then
    echo shar: \"'browse.c'\" unpacked with wrong size!
fi
# end of 'browse.c'
fi
if test -f 'screen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'screen.c'\"
else
echo shar: Extracting \"'screen.c'\" \(5617 characters\)
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X#include "system.h"
X
X#ifdef USG
X# ifdef M_XENIX
X#  include <sys/types.h>
X#  include <sys/ioctl.h>
X# endif
X# include <termio.h>
Xstruct termio rawbuf;
Xstruct termio cookedbuf;
X#else
X# include <sgtty.h>                     /* terminal modes for tinit(), tend() */
Xstruct sgttyb sgbuf;                        /* buffer for terminal mode info */
Xint rawflags, cookflags;		  /* flags for raw & cooked tty mode */
X#endif
X#include <signal.h>
X
X#define TERMBUF 1024		/* Size of term buf for termcap */
X
Xextern int nlines;
X
Xextern char *tgetstr(), *tgoto();
Xextern int display_up;
Xextern int	intrup;				/* Have we been interrupted? */
X
Xchar *tent;                                               /* Pointer to tbuf */
Xchar PC;                                                    /* Pad character */
Xchar *UP, *BC;                                /* Upline, backsapce character */
Xshort ospeed;                                       /* Terminal output speed */
Xchar termbuf[TERMBUF];				   /* Place to put term info */
X
Xchar *cm,                                                   /* Cursor motion */
X     *cs,                                         /* Change scrolling region */
X     *sf,                                         /*  - scroll forward       */
X     *sr,                                         /*  - scroll backwards     */
X     *ce,                                            /* Clear to end of line */
X     *cl,                                                    /* Clear screen */
X     *al,                                                     /* Insert line */
X     *dl,                                                    /* delete ditto */
X     *so,                                                        /* standout */
X     *se,                                                    /* standout end */
X     *us,                                                       /* underline */
X     *ue,                                                   /* underline end */
X     *ti,						    /* Init terminal */
X     *te;						   /* Reset terminal */
Xint  li,                                                  /* lines on screen */
X     co;                                                    /* columns ditto */
Xchar xn;		/* Magic cookie kludge */
X
X/* Screen manipulation primitives: dumb set for smart terminals */
Xat(x, y)
Xint x, y;
X{
X	outs(tgoto(cm, x, y));
X}
X
Xnl()
X{
X	outs(ce);
X	outc('\n');
X}
X
X/* Scroll lines in window (from:to) n lines */
Xscroll(from, to, n)
Xint from, to, n;
X{
X	if(cs && sf && sr) {
X		outs(tgoto(cs, from, to-1));
X		if(n<0) {
X			at(0, from);
X			while(n++)
X				outs(sr);
X		}
X		else {
X			at(0, to-1);
X			while(n--)
X				outs(sf);
X		}
X		outs(tgoto(cs, 0, li-1));
X	} 
X	else if(al && dl) {
X		if(n<0) {
X			int i=n;
X			outs(tgoto(cm, 0, to+n));
X			while(i++)
X				outs(dl);
X			outs(tgoto(cm, 0, from));
X			while(n++)
X				outs(al);
X		} 
X		else {
X			int i=n;
X			outs(tgoto(cm, 0, from));
X			while(i--)
X				outs(dl);
X			outs(tgoto(cm, 0, to-n));
X			while(n--)
X				outs(al);
X		}
X	}
X}
X
Xtinit(name)
Xchar *name;
X{
X	char *termptr;
X	char tbuf[TERMBUF], *tmp;
X	SIGNAL  intr();
X	SIGNAL term();
X#ifdef BSD
X	SIGNAL  stop();
X#endif
X
X	termptr = termbuf;
X
X	tgetent(tbuf, name);
X
X	tmp = tgetstr("pc", &termptr);
X	if(tmp) PC = *tmp;
X	UP = tgetstr("up", &termptr);
X	BC = tgetstr("bc", &termptr);
X	cm = tgetstr("cm", &termptr);
X	cs = tgetstr("cs", &termptr);
X	sf = tgetstr("sf", &termptr);
X	sr = tgetstr("sr", &termptr);
X	ce = tgetstr("ce", &termptr);
X	cl = tgetstr("cl", &termptr);
X	al = tgetstr("al", &termptr);
X	dl = tgetstr("dl", &termptr);
X	us = tgetstr("us", &termptr);
X	ue = tgetstr("ue", &termptr);
X	so = tgetstr("so", &termptr);
X	se = tgetstr("se", &termptr);
X	ti = tgetstr("ti", &termptr);
X	te = tgetstr("te", &termptr);
X	li = tgetnum("li");
X	co = tgetnum("co");
X	xn = tgetflag("xn");
X
X	nlines=li-3;
X
X#ifdef USG
X	ioctl(1, TCGETA, &rawbuf);
X	cookedbuf = rawbuf;
X	rawbuf.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
X	rawbuf.c_cc[VMIN] = 1;
X	rawbuf.c_cc[VTIME] = 0;
X#else
X	gtty(1, &sgbuf);
X	ospeed=sgbuf.sg_ospeed;
X	quickmode=ospeed<10;
X	cookflags=sgbuf.sg_flags;
X	sgbuf.sg_flags = (sgbuf.sg_flags&~ECHO)|CBREAK;
X	rawflags=sgbuf.sg_flags;
X#endif
X	signal(SIGINT, intr);
X	signal(SIGTERM, term);
X#ifdef BSD
X	signal(SIGTSTP, stop);
X#endif
X	rawtty();
X}
X
Xint tmode=0;
X
Xentty()
X{
X	if(!tmode)
X		outs(ti);
X	tmode=1;
X}
X
Xextty()
X{
X	if(tmode)
X		outs(te);
X	tmode=0;
X}
X
Xrawtty()
X{
X#ifdef USG
X	ioctl(1, TCSETA, &rawbuf);
X#else
X	sgbuf.sg_flags=rawflags;
X	stty(1, &sgbuf);
X#endif
X	entty();
X}
X
Xcooktty()
X{
X#ifdef USG
X	ioctl(1, TCSETA, &cookedbuf);
X#else
X	sgbuf.sg_flags=cookflags;
X	stty(1, &sgbuf);
X#endif
X	extty();
X}
X
XSIGNAL intr()
X{
X	signal(SIGINT, intr);
X	intrup=1;
X}
X
XSIGNAL term()
X{
X	tend();
X	tcl_end();
X	exit(0);
X}
X
X#ifdef BSD
XSIGNAL stop()
X{
X	signal(SIGTSTP, stop);
X	intrup=1;
X	tend();
X	kill(getpid(), SIGSTOP);
X	rawtty();
X	display_up=0;
X}
X#endif
X
Xtend()
X{
X	end_screenmode();
X	cooktty();
X	fflush(stdout);
X}
X
Xouts(s)
Xchar *s;
X{
X	int  outc();
X
X	if(s)
X		tputs(s, 0, outc);
X}
X
Xoutc(c)
Xchar c;
X{
X	putchar(c);
X}
X
Xint somode = 0;
Xint ulmode = 0;
X
Xstandout()
X{
X	if(!somode) {
X		outs(so);
X		somode = 1;
X		if(xn) return 1;
X	}
X	return 0;
X}
X
Xunderline()
X{
X	if(!ulmode) {
X		outs(us);
X		ulmode = 1;
X		if(xn) return 1;
X	}
X	return 0;
X}
X
Xstandend()
X{
X	int cnt = 0;
X
X	if(somode) {
X		outs(se);
X		somode = 0;
X		if(xn) cnt++;
X	}
X	if(ulmode) {
X		outs(ue);
X		ulmode = 0;
X		if(xn) cnt++;
X	}
X	return cnt;
X}
X
Xcmdline()
X{
X	at(0, li-1);
X	outs(ce);
X}
X
Xend_screenmode()
X{
X	if(display_up) {
X		at(0, li-1);
X		outc('\n');
X		display_up = 0;
X	}
X}
X
Xend_linemode()
X{
X	if(!display_up) {
X		redraw();
X		display_up = 1;
X	}
X}
X
Xclear_screen()
X{
X	outs(cl);
X}
X
END_OF_FILE
if test 5617 -ne `wc -c <'screen.c'`; then
    echo shar: \"'screen.c'\" unpacked with wrong size!
fi
# end of 'screen.c'
fi
if test -f 'tcl.pat.vars' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tcl.pat.vars'\"
else
echo shar: Extracting \"'tcl.pat.vars'\" \(4732 characters\)
sed "s/^X//" >'tcl.pat.vars' <<'END_OF_FILE'
XFrom ficc%sugar!texbell!allspice.Berkeley.EDU!ouster Sat Mar  3 18:12:05 1990
XReceived: by sugar.hackercorp.com (smail2.5)
X	id AA26093; 3 Mar 90 17:54:45 CST (Sat)
XReceived: by sugar.hackercorp.com (smail2.5)
X	id AA19332; 3 Mar 90 01:11:29 CST (Sat)
XReceived: by texbell (/\=-/\ Smail3.1.16.1 #16.6)
X	id <m0h4R8G-0000uOC at texbell>; Fri, 2 Mar 90 22:54 CST
XPosted-Date: Fri, 2 Mar 90 08:43:20 PST
XReceived: from tyranny.Berkeley.EDU by cs.utexas.edu (5.59/1.50)
X	id AA10952; Fri, 2 Mar 90 10:45:46 CST
XReceived: by sprite.Berkeley.EDU (5.59/1.29)
X	id AA412483; Fri, 2 Mar 90 08:43:20 PST
XDate: Fri, 2 Mar 90 08:43:20 PST
XFrom: ouster at allspice.Berkeley.EDU (John Ousterhout)
XMessage-Id: <9003021643.AA412483 at sprite.Berkeley.EDU>
XTo: ficc!peter at uunet.uu.net
XSubject: Re: Variables fix
XCc: peter at sugar.lonestar.org
XStatus: O
X
XI hope to have the non-existent-variable patches complete in the
Xnext week or two.  In case that's not soon enough for you, I'm
Xreturning a copy of your patches at the end of this message.
X>From ficc!peter at uunet.UU.NET Thu Feb 22 21:14:42 1990
XFrom: ficc!peter at uunet.UU.NET
XTo: ouster at sprite.Berkeley.EDU
XSubject: Patches for empty variables.
XCc: karl at uunet.UU.NET
XDate: Wed Feb 21 16:29:46 1990
X
XThe following patches let you differentiate between empty variables
Xand non-existant ones.
X
X*** old/tclBasic.c
X--- tcl/tclBasic.c
X***************
X*** 686,692 ****
X  			 */
X      
X  			value = Tcl_ParseVar(interp, src, &tmp);
X! 			if (*value == 0) {
X  			    result = TCL_ERROR;
X  			    goto done;
X  			}
X--- 686,692 ----
X  			 */
X      
X  			value = Tcl_ParseVar(interp, src, &tmp);
X! 			if (value == 0) {
X  			    result = TCL_ERROR;
X  			    goto done;
X  			}
X***************
X*** 1211,1216 ****
X--- 1211,1217 ----
X  	char *buffer, *oldVar;
X  
X  	oldVar = Tcl_GetVar(interp, "errorInfo", 1);
X+ 	if(!oldVar) oldVar = "";
X  	length = strlen(oldVar);
X  	buffer = (char *)malloc((unsigned) (length + strlen(message) + 1));
X  	strcpy(buffer, oldVar);
X*** old/tclExpr.c
X--- tcl/tclExpr.c
X***************
X*** 250,256 ****
X  	case '$':
X  	    infoPtr->token = NUMBER;
X  	    var = Tcl_ParseVar(infoPtr->interp, p, &infoPtr->expr);
X! 	    if (*var == '\0') {
X  		return TCL_ERROR;
X  	    }
X  	    if (((Interp *) infoPtr->interp)->noEval) {
X--- 250,256 ----
X  	case '$':
X  	    infoPtr->token = NUMBER;
X  	    var = Tcl_ParseVar(infoPtr->interp, p, &infoPtr->expr);
X! 	    if (var == 0) {
X  		return TCL_ERROR;
X  	    }
X  	    if (((Interp *) infoPtr->interp)->noEval) {
X*** old/tclProc.c
X--- tcl/tclProc.c
X***************
X*** 158,164 ****
X   * Results:
X   *	The return value points to the current value of varName.  If
X   *	the variable is not defined in interp, either as a local or
X!  *	global variable, then a pointer to an empty string is returned.
X   *	Note:  the return value is only valid up until the next call to
X   *	Tcl_SetVar;  if you depend on the value lasting longer than that,
X   *	then make yourself a private copy.
X--- 158,165 ----
X   * Results:
X   *	The return value points to the current value of varName.  If
X   *	the variable is not defined in interp, either as a local or
X!  *	global variable, then a NULL pointer is returned.
X!  *
X   *	Note:  the return value is only valid up until the next call to
X   *	Tcl_SetVar;  if you depend on the value lasting longer than that,
X   *	then make yourself a private copy.
X***************
X*** 185,191 ****
X  	varPtr = FindVar(&iPtr->varFramePtr->varPtr, varName);
X      }
X      if (varPtr == NULL) {
X! 	return "";
X      }
X      if (varPtr->flags & VAR_GLOBAL) {
X  	varPtr = varPtr->globalPtr;
X--- 186,192 ----
X  	varPtr = FindVar(&iPtr->varFramePtr->varPtr, varName);
X      }
X      if (varPtr == NULL) {
X! 	return NULL;
X      }
X      if (varPtr->flags & VAR_GLOBAL) {
X  	varPtr = varPtr->globalPtr;
X***************
X*** 323,329 ****
X      c = *string;
X      *string = 0;
X      result = Tcl_GetVar(interp, name, 0);
X!     if (*result == '\0') {
X  	Tcl_Return(interp, (char *) NULL, TCL_STATIC);
X  	sprintf(interp->result, "couldn't find variable \"%.50s\"", name);
X      }
X--- 324,330 ----
X      c = *string;
X      *string = 0;
X      result = Tcl_GetVar(interp, name, 0);
X!     if (!result) {
X  	Tcl_Return(interp, (char *) NULL, TCL_STATIC);
X  	sprintf(interp->result, "couldn't find variable \"%.50s\"", name);
X      }
X***************
X*** 360,366 ****
X  	char *value;
X  
X  	value = Tcl_GetVar(interp, argv[1], 0);
X! 	if (*value == 0) {
X  	    sprintf(interp->result, "couldn't find variable \"%.50s\"",
X  		    argv[1]);
X  	    return TCL_ERROR;
X--- 361,367 ----
X  	char *value;
X  
X  	value = Tcl_GetVar(interp, argv[1], 0);
X! 	if (value == 0) {
X  	    sprintf(interp->result, "couldn't find variable \"%.50s\"",
X  		    argv[1]);
X  	    return TCL_ERROR;
X
X
END_OF_FILE
if test 4732 -ne `wc -c <'tcl.pat.vars'`; then
    echo shar: \"'tcl.pat.vars'\" unpacked with wrong size!
fi
# end of 'tcl.pat.vars'
fi
if test -f 'tcl_browse.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tcl_browse.c'\"
else
echo shar: Extracting \"'tcl_browse.c'\" \(5712 characters\)
sed "s/^X//" >'tcl_browse.c' <<'END_OF_FILE'
X/* Subcommands for tcl "browse" command */
X#include <stdio.h>
X#include <tcl.h>
X#include "tcl_browse.h"
X
Xint browseRescan();
Xint browseRedraw();
Xint browseCmds();
Xint browseChdir();
Xint browseQuit();
Xint browseDelete();
Xint browseRename();
Xint browseMessage();
Xint browsePrint();
Xint browseMove();
Xint browseBell();
Xint browseShell();
Xint browseTag();
Xint browseAdd();
Xint browseMode();
X
Xstruct subcmd browsecmds[] = {
X	{ browseCmds, "commands", 0, 0, "" },
X	{ browseChdir, "chdir", 1, 1, "directory" },
X	{ browseQuit, "exit", 0, 1, "[code]" },
X	{ browseDelete, "delete", 1, -1, "file..." },
X	{ browseRename, "rename", 2, 2, "from to" },
X	{ browseMove, "move", 1, 1, "entry" },
X	{ browseMessage, "message", 0, -1, "[text]..." },
X	{ browsePrint, "print", 0, -1, "[text]..." },
X	{ browseTag, "tag", 2, -1, "{+,-,/}[PT] file..." },
X	{ browseBell, "bell", 0, 0, "" },
X	{ browseRedraw, "redraw", 0, 0, "" },
X	{ browseRescan, "rescan", 0, 0, "" },
X	{ browseShell, "shell", 1, 1, "command" },
X	{ browseMode, "mode", 1, 1, "[line|page|wide|narrow]" },
X	{ browseAdd, "add", 1, -1, "file..." },
X};
Xint browsecnt = sizeof browsecmds / sizeof *browsecmds;
X
Xint
XcmdBrowse(clientData, interp, argc, argv)
XClientData clientData;
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	return Handle(browsecnt, browsecmds, interp, argc, argv);
X}
X
XbrowseDelete(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	int i;
X	int result;
X	char *file_name();
X
X	while(*argv) {
X		i = get_index(*argv);
X		result = 0;
X
X		if(i==-1) {
X			if(unlink(*argv) == 0)
X				result = 1;
X			else
X				save_errno(*argv);
X		} else {
X			if(unlink(file_name(i)) == 0) {
X				result = 1;
X				delete_entry(i);
X			}
X			else
X				save_errno(*argv);
X		}
X		if(result==0)
X			break;
X		++argv;
X	}
X	sprintf(interp->result, "%d", result);
X	return TCL_OK;
X}
X
XbrowseRename(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	int i;
X	int result;
X
X	i = get_index(*argv);
X	result = 0;
X
X	if(i == -1) {
X		if(link(argv[0], argv[1]) == 0) {
X			if(unlink(argv[0]) == 0)
X				result = 1;
X			else
X				save_errno(*argv);
X		}
X		else
X			save_errno(*argv);
X	} else {
X		if(domove(i, argv[1]))
X			result = 1;
X	}
X	sprintf(interp->result, "%d", result);
X	return TCL_OK;
X}
X
XbrowseChdir(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	strcpy(interp->result, enter(*argv)?"1":"0");
X	return TCL_OK;
X}
X
XbrowseQuit(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	int i;
X
X	if(argc)
X		i = atoi(*argv);
X	else
X		i = 0;
X
X	tcl_end();
X	tend();
X
X	exit(i);
X}
X
XbrowseMessage(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	int result;
X	extern int display_up;
X
X	cmdline();
X	result = browsePrint(interp, argc, argv);
X	if(atoi(interp->result) >= 80)
X		end_screenmode();
X	else
X		if(!display_up) putchar('\n');
X	return result;
X}
X
XbrowseCmds(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	return Format(browsecnt, browsecmds, interp);
X}
X
XbrowsePrint(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	extern int display_up;
X	int count = 0;
X
X	while(*argv) {
X		count += strlen(*argv)+1;
X		if(display_up)
X			ctlouts(*argv);
X		else
X			outs(*argv);
X		++argv;
X		if(*argv)
X			putchar(' ');
X	}
X
X	sprintf(interp->result, "%d", count);
X
X	return TCL_OK;
X}
X
XbrowseMove(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	extern int curr, nentries;
X	int new;
X
X	switch(**argv) {
X		case '*': new = nentries-1; break;
X		case '-': new = curr - atoi(*argv+1); break;
X		case '+': new = curr + atoi(*argv+1); break;
X		default: new = atoi(*argv); break;
X	}
X	if(new >= 0 && new < nentries) {
X		curr = new;
X		strcpy(interp->result, "1");
X	} else {
X		save_errmsg("entry out of range");
X		strcpy(interp->result, "0");
X	}
X	return TCL_OK;
X}
X
XbrowseBell(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	putchar(7);
X	return TCL_OK;
X}
X
XbrowseShell(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	sprintf(interp->result, "%d", system(*argv, 1));
X	return TCL_OK;
X}
X
XbrowseRedraw(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	redraw();
X	return TCL_OK;
X}
X
XbrowseRescan(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	sprintf(interp->result, "%d", newdir());
X	return TCL_OK;
X}
X
XbrowseTag(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	int i;
X	int result;
X	int mode;
X	int tagbit;
X
X	switch(**argv) {
X		case '+': mode = 1; break;
X		case '-': mode = -1; break;
X		case '/': mode = 0; break;
X		default: goto tcl_error;
X	}
X
X	++*argv;
X
X	switch(**argv) {
X		case 'p': case 'P': tagbit = 'P'; break;
X		case 't': case 'T': tagbit = 'T'; break;
X		case 0: tagbit = 'T'; break; /* default */
X		default: goto tcl_error;
X	}
X
X	result = 1;
X	while(*++argv) {
X		i = get_index(*argv);
X		if(i == -1) {
X			result = 0;
X			break;
X		}
X		tag(i, mode, tagbit);
X	}
X	sprintf(interp->result, "%d", result);
X	return TCL_OK;
X
Xtcl_error:
X	Tcl_Return(interp,
X		"invalid tag option:  should be \"tag {+,-,/}[PT] file...\"",
X		TCL_STATIC);
X	return TCL_ERROR;
X}
X
XbrowseMode(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	if(strcmp(*argv, "line") == 0) {
X		end_screenmode();
X	} else if(strcmp(*argv, "page") == 0) {
X		end_linemode();
X	} else if(strcmp(*argv, "narrow") == 0) {
X		set_quickmode(1);
X	} else if(strcmp(*argv, "wide") == 0) {
X		set_quickmode(0);
X	} else {
X		Tcl_Return(interp,
X			"invalid mode name:  should be \"mode [page|line|wide|narrow]\"",
X			TCL_STATIC);
X		return TCL_ERROR;
X	}
X	return TCL_OK;
X}
X
XbrowseAdd(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	int result;
X
X	result = 1;
X	while(*argv) {
X		if(!addfile(*argv)) {
X			result = 0;
X			break;
X		}
X		argv++;
X	}
X	sprintf(interp->result, "%d", result);
X	return TCL_OK;
X}
END_OF_FILE
if test 5712 -ne `wc -c <'tcl_browse.c'`; then
    echo shar: \"'tcl_browse.c'\" unpacked with wrong size!
fi
# end of 'tcl_browse.c'
fi
if test -f 'tcl_browse.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tcl_browse.h'\"
else
echo shar: Extracting \"'tcl_browse.h'\" \(139 characters\)
sed "s/^X//" >'tcl_browse.h' <<'END_OF_FILE'
X/* Browser-defined stuff */
X
Xextern Tcl_Interp *interp;
X
Xstruct subcmd {
X	int (*func)();
X	char *name;
X	int min;
X	int max;
X	char *args;
X};
X
END_OF_FILE
if test 139 -ne `wc -c <'tcl_browse.h'`; then
    echo shar: \"'tcl_browse.h'\" unpacked with wrong size!
fi
# end of 'tcl_browse.h'
fi
if test -f 'tcl_get.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tcl_get.c'\"
else
echo shar: Extracting \"'tcl_get.c'\" \(5209 characters\)
sed "s/^X//" >'tcl_get.c' <<'END_OF_FILE'
X/* Subcommands for tcl "get" command */
X#include <stdio.h>
X#include <ctype.h>
X#include <tcl.h>
X#include "tcl_browse.h"
X
Xint getCmds();
Xint getResponse();
Xint getKey();
Xint getKeyname();
Xint getLine();
Xint getEnv();
Xint getFile();
Xint getError();
Xint getMode();
Xint getCwd();
X
Xstruct subcmd getcmds[] = {
X	{ getCmds, "commands", 0, 0, "" },
X	{ getResponse, "response", 0, 3, "[prompt [default [term]]]" },
X	{ getKey, "key", 0, 1, "[prompt]" },
X	{ getKeyname, "keyname", 1, 1, "key" },
X	{ getLine, "line", 1, 1, "{home,last,end,*,.}" },
X	{ getFile, "file", 1, 1, "{file,*,.}" },
X	{ getEnv, "env", 1, 1, "name" },
X	{ getError, "error", 0, 0, "" },
X	{ getMode, "mode", 0, 0, "" },
X	{ getCwd, "cwd", 0, 0, "" },
X};
Xint getcnt = sizeof getcmds / sizeof *getcmds;
X
Xint
XcmdGet(clientData, interp, argc, argv)
XClientData clientData;
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	return Handle(getcnt, getcmds, interp, argc, argv);
X}
X
XgetCmds(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	return Format(getcnt, getcmds, interp);
X}
X
Xchar *StrToList(s)
Xchar *s;
X{
X	char *argv[2];
X	argv[0] = s;
X	argv[1] = 0;
X	return Tcl_Merge(1, argv);
X}
X
XgetEnv(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	char *getenv();
X	char *s = getenv(*argv);
X	if(!s)
X		Tcl_Return(interp, NULL, TCL_STATIC);
X	else
X		Tcl_Return(interp, s, TCL_STATIC);
X	return TCL_OK;
X}
X
XgetKeyname(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	char *name_of();
X	strcpy(interp->result, name_of(**argv));
X	return TCL_OK;
X}
X
XgetCwd(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	extern char *dot;
X	strcpy(interp->result, dot);
X	return TCL_OK;
X}
X
Xextern int getFile(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	extern char *file_name();
X	extern int curr;
X
X	if(strcmp(*argv, ".") == 0) {
X		Tcl_Return(interp, StrToList(file_name(curr)), TCL_DYNAMIC);
X	} else if(strcmp(*argv, "*") == 0) {
X		extern int nentries;
X		char **argv = (char **)ckalloc(sizeof (char *) * nentries);
X		int argc = 0;
X		int i;
X
X		for(i = 0; i < nentries; i++) {
X			if(tagged(i))
X				argv[argc++] = file_name(i);
X		}
X		argv[argc] = 0;
X		if(argv)
X			Tcl_Return(interp, Tcl_Merge(argc, argv), TCL_DYNAMIC);
X		else
X			Tcl_Return(interp, NULL, TCL_STATIC);
X		ckfree(argv);
X	} else if(isdigit(**argv)) {
X		Tcl_Return(interp,
X			StrToList(file_name(atoi(*argv))),
X			TCL_DYNAMIC);
X	} else {
X		sprintf(interp->result, "bad line number:  should be \"get file {line,*,.}\"");
X		return TCL_ERROR;
X	}
X	return TCL_OK;
X}
X
XgetResponse(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	char buffer[256];
X	char *def = "";
X	char term = 0;
X	extern int display_up;
X
X	if(argc >= 1 && argv[0][0]) {
X		cmdline();
X		outs(argv[0]);
X	} if(argc >= 2)
X		def = argv[1];
X	if(argc >= 3)
X		term = argv[2][0];
X	if(inps(buffer, def, term) == '\033') {
X		Tcl_Return(interp, "Command Killed", TCL_STATIC);
X		return TCL_ERROR;
X	} else {
X		Tcl_Return(interp, buffer, TCL_VOLATILE);
X		if(display_up == 0)
X			outc('\n');
X		return TCL_OK;
X	}
X}
X
XgetLine(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	int line;
X
X	extern int curr;
X	extern int top;
X	extern int nlines;
X	extern int nentries;
X
X	if(strcmp(*argv, "home") == 0) line = top;
X	else if(strcmp(*argv, "last") == 0) line = top+nlines-1;
X	else if(strcmp(*argv, "end") == 0) line = nentries-1;
X	else if(strcmp(*argv, ".") == 0) line = curr;
X	else if(strcmp(*argv, "*") == 0) {
X		extern int nentries;
X		char *string = (char *)ckalloc(8 * nentries);
X		char *p = string;
X		int i;
X
X		for(i = 0; i < nentries; i++) {
X			if(tagged(i)) {
X				sprintf(p, "%d ", i);
X				p += strlen(p) - 1;
X			}
X		}
X		if(p > string) {
X			*--p = 0;
X			Tcl_Return(interp, string, TCL_DYNAMIC);
X		} else {
X			ckfree(string);
X			Tcl_Return(interp, NULL, TCL_STATIC);
X		}
X	} else {
X		sprintf(interp->result,
X			"invalid line type: should be \"get line {home,last,end,*,.}\"");
X		return TCL_ERROR;
X	}
X
X	sprintf(interp->result, "%d", line);
X	return TCL_OK;
X}
X
XgetKey(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	extern int display_up;
X
X	if(*argv) {
X		cmdline();
X		outs(*argv);
X	}
X	interp->result[0] = getch();
X	interp->result[1] = 0;
X	if(*argv && !display_up)
X		outc('\n');
X	return TCL_OK;
X}
X
Xstatic char *last_errmsg = "No Error";
Xstatic char *last_errfile = NULL;
Xstatic char filename[BUFSIZ];
X
Xsave_errmsg(s)
Xchar *s;
X{
X	last_errfile = 0;
X	last_errmsg = s;
X}
X
Xsave_errno(file)
Xchar *file;
X{
X	extern char *strerror();
X	extern int errno;
X	last_errmsg = strerror(errno);
X	last_errfile = filename;
X	strncpy(filename, file, BUFSIZ);
X}
X
XgetError(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	static char buffer[BUFSIZ];
X
X	if(last_errfile)
X		sprintf(buffer, "%s: %s", last_errfile, last_errmsg);
X	else
X		strcpy(buffer, last_errmsg);
X	Tcl_Return(interp, buffer, TCL_VOLATILE);
X	return TCL_OK;
X}
X
XgetMode(interp, argc, argv)
XTcl_Interp *interp;
Xint argc;
Xchar **argv;
X{
X	extern int display_up;
X	extern int quickmode;
X	char buffer[32];
X
X	if(quickmode)
X		strcpy(buffer, "narrow");
X	else
X		strcpy(buffer, "wide");
X	if(display_up)
X		strcat(buffer, " page");
X	else
X		strcat(buffer, " line");
X	Tcl_Return(interp, buffer, TCL_VOLATILE);
X	return TCL_OK;
X}
X
END_OF_FILE
if test 5209 -ne `wc -c <'tcl_get.c'`; then
    echo shar: \"'tcl_get.c'\" unpacked with wrong size!
fi
# end of 'tcl_get.c'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
 _--_|\  Peter da Silva <peter at sugar.hackercorp.com>.
/      \
\_.--._/ I haven't lost my mind, it's backed up on tape somewhere!
      v  "Have you hugged your wolf today?" `-_-'



More information about the Alt.sources mailing list