ferguson@cs.rochester.edu (George Ferguson) (08/29/90)
#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 3)." # Contents: xmon.c xmon.man # Wrapped by ferguson@cyan.cs.rochester.edu on Tue Aug 28 16:40:51 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'xmon.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xmon.c'\" else echo shar: Extracting \"'xmon.c'\" \(11059 characters\) sed "s/^X//" >'xmon.c' <<'END_OF_FILE' X/* X * Xmon : Graphically monitor a list X * X * George Ferguson, ferguson@cs.rochester.edu, 4 May 1990. X * X * $Id: xmon.c,v 1.4 90/08/21 11:14:39 ferguson Exp $ X * X */ Xstatic char *rcsid = "$Id: xmon.c,v 1.4 90/08/21 11:14:39 ferguson Exp $"; X#include <stdio.h> X#include <ctype.h> X#include <X11/Intrinsic.h> X#include <X11/StringDefs.h> X#include <X11/Xaw/Label.h> X#include <X11/Xaw/Toggle.h> X#include <X11/Xaw/Cardinals.h> X#include "app-resources.h" Xextern WidgetClass classNameToWidgetClass(); Xextern char *getTextFromWidget(); X X/* - - - - - - - - */ X/* X * Functions defined in this file: X */ Xstatic void initGraphics(), initWidgets(); Xstatic void quit(), update(), info(), reinit(), cd(), cmd(); Xstatic void timeoutProc(); Xstatic void syntax(); Xvoid fail(); X X/* X * Action binding table X */ Xstatic XtActionsRec cmdActionsTable[] = { X { "xmon-update", update }, X { "xmon-quit", quit }, X { "xmon-info", info }, X { "xmon-reinit", reinit }, X { "xmon-cd", cd }, X { "xmon-cmd", cmd }, X}; X X/* X * Global widget data X */ XWidget toplevel,faceBox,infoLabel,radioGroup; XAppResources appResources; Xstatic XtAppContext app_con; Xstatic XtIntervalId interval; X X/* X * Global graphics data X */ XGC gc; X X/* X * Other global data X */ Xchar *program; X X/* X * Non-widget resources obtained from resource manager X */ Xstatic XtResource resources[] = { X { "addNames", "AddNames", XtRBoolean, sizeof(Boolean), X XtOffset(AppResources *,addNames), XtRImmediate, False }, X { "opaqueNames", "OpaqueNames", XtRBoolean, sizeof(Boolean), X XtOffset(AppResources *,opaqueNames), XtRImmediate, False }, X { "smallFont", "SmallFont", XtRFont, sizeof(Font), X XtOffset(AppResources *,smallFont), XtRString, SMALLFONT }, X { "updateInterval", "UpdateInterval", XtRInt, sizeof(int), X XtOffset(AppResources *,updateInterval), XtRImmediate, (XtPointer)60 }, X { "faceDirectory", "FaceDirectory", XtRString, sizeof(String), X XtOffset(AppResources *,faceDirectory), XtRImmediate, FACEDIR }, X { "updateCommand", "UpdateCommand", XtRString, sizeof(String), X XtOffset(AppResources *,updateCommand), XtRImmediate, UPDATECOMMAND }, X { "updateCursor", "UpdateCursor", XtRCursor, sizeof(Cursor), X XtOffset(AppResources *,updateCursor), XtRString, UPDATECURSOR }, X { "nameCommand", "nameCommand", XtRString, sizeof(String), X XtOffset(AppResources *,nameCommand), XtRImmediate, NULL }, X { "debug", "Debug", XtRBoolean, sizeof(Boolean), X XtOffset(AppResources *,debug), XtRImmediate, False }, X { "revision", "Revision", XtRString, sizeof(String), X XtOffset(AppResources *,revision), XtRImmediate, "" }, X { "widgets", "Widgets", XtRString, sizeof(String), X XtOffset(AppResources *,widgets), XtRImmediate, "" }, X}; X X/* X * Non-widget resources settable on command line. X */ Xstatic XrmOptionDescRec options[] = { X { "-addNames", ".addNames", XrmoptionNoArg, "True" }, X { "-opaqueNames", ".opaqueNames", XrmoptionNoArg, "True" }, X { "-smallFont", ".smallFont", XrmoptionSepArg, SMALLFONT }, X { "-updateInterval",".updateInterval",XrmoptionSepArg, "60" }, X { "-faceDirectory", ".faceDirectory",XrmoptionSepArg, FACEDIR }, X { "-updateCommand", ".updateCommand",XrmoptionSepArg, UPDATECOMMAND}, X { "-nameCommand", ".nameCommand", XrmoptionSepArg, NULL}, X { "-debug", ".debug", XrmoptionNoArg, "True"}, X}; X X/* X * Widget and non-widget resources if the application defaults X * file can't be found. X * [ Generated automatically from Xmon.ad. ] X */ Xstatic String fallbackResources[] = { X#include "Xmon.ad.h" X NULL X}; X X/* - - - - - - - - */ X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X program = *argv; X initGraphics(argc,argv); X initWidgets(); X initMemory(); X XtRealizeWidget(toplevel); X updateFaces(); X if (appResources.updateInterval != 0) X interval = XtAppAddTimeOut(app_con,appResources.updateInterval*1000, X timeoutProc,NULL); X XtAppMainLoop(app_con); X} X Xstatic void XinitGraphics(argc,argv) Xint argc; Xchar **argv; X{ X XGCValues gcvals; X char *rev,*strchr(); X X toplevel = XtAppInitialize(&app_con, "Xmon", X options, XtNumber(options), X &argc,argv,fallbackResources,NULL,ZERO); X XtGetApplicationResources(toplevel,(XtPointer)&appResources, X resources,XtNumber(resources),NULL,ZERO); X rev = strchr(fallbackResources[0],'$'); X if (strcmp(appResources.revision,rev) != 0) { X fprintf(stderr,"%s: app-defaults release not %s\n",program,rev); X fprintf(stderr,"%s: you may have an outdated app-defaults file\n", X program); X } X XtAppAddActions(app_con,cmdActionsTable,XtNumber(cmdActionsTable)); X if (argc != 1) { X syntax(argc,argv); X XtDestroyApplicationContext(app_con); X exit(1); X } X gc = XDefaultGC(XtDisplay(toplevel), X XScreenNumberOfScreen(XtScreen(toplevel))); X XSetFont(XtDisplay(toplevel),gc,appResources.smallFont); X} X Xstatic void XinitWidgets() X{ X char name[32],class[32],parent[32]; X char *s; X int i; X Boolean isShell; X WidgetClass wc; X Widget w; X X if ((s=appResources.widgets) == NULL) X fail("no widgets specified!\n",""); X while (*s) { X while (isspace(*s)) X s += 1; X if (!*s) X break; X i = 0; X while (*s && !isspace(*s)) X class[i++] = *s++; X class[i] = '\0'; X while (isspace(*s)) X s += 1; X i = 0; X while (*s && !isspace(*s)) X name[i++] = *s++; X name[i] = '\0'; X while (isspace(*s)) X s += 1; X i = 0; X while (*s && !isspace(*s)) X parent[i++] = *s++; X parent[i] = '\0'; X isShell = False; X if ((wc=classNameToWidgetClass(class,&isShell)) == NULL) X fail("can't convert string \"%s\" to widgetClass\n",class); X if (strcmp(parent,"toplevel") == 0) X w = toplevel; X else if ((w=XtNameToWidget(toplevel,parent)) == NULL) X fail("can't convert string \"%s\" to widget\n",parent); X if (isShell) X w = XtCreatePopupShell(name,wc,w,NULL,ZERO); X else X w = XtCreateManagedWidget(name,wc,w,NULL,ZERO); X } X if ((faceBox=XtNameToWidget(toplevel,"*faceBox")) == NULL) X fail("didn't create widget \"faceBox\"\n",""); X if ((infoLabel=XtNameToWidget(toplevel,"*infoLabel")) == NULL) X fail("didn't create widget \"infoLabel\"\n",""); X} X X/* - - - - - - - - */ X/* Action procedures */ X X/* X * Default action for "quit" button: destroys application. X */ Xstatic void Xquit(w,event,params,num_params) XWidget w; XXEvent *event; XString *params; XCardinal *num_params; X{ X XtDestroyApplicationContext(app_con); X exit(0); X} X X/* X * Default action for all "face" buttons: sets infoLabel. X */ Xstatic void Xinfo(w,event,params,num_params) XWidget w; XXEvent *event; XString *params; XCardinal *num_params; X{ X Arg arg[1]; X X if ((w=(Widget)XawToggleGetCurrent(radioGroup)) == NULL) X XtSetArg(arg[0],XtNlabel,""); X else X XtSetArg(arg[0],XtNlabel,getTextFromWidget(w)); X XtSetValues(infoLabel,arg,ONE); X} X X/* X * Default action for "update" button: Sets all buttons and resets timer. X */ Xstatic void Xupdate(w,event,params,num_params) XWidget w; XXEvent *event; XString *params; XCardinal *num_params; X{ X updateFaces(); X if (appResources.updateInterval != NULL) { X XtRemoveTimeOut(interval); X interval = XtAppAddTimeOut(app_con,appResources.updateInterval*1000, X timeoutProc,NULL); X } X} X X/* X * Forget the bitmaps X */ Xstatic void Xreinit(w,event,params,num_params) XWidget w; XXEvent *event; XString *params; XCardinal *num_params; X{ X if (radioGroup != NULL) X XawToggleUnsetCurrent(radioGroup); X clearBitmapCache(); X update(w,event,params,num_params); X} X X/* X * Change directory X */ Xstatic void Xcd(w,event,params,num_params) XWidget w; XXEvent *event; XString *params; XCardinal *num_params; X{ X char *text,dirname[256]; X int i; X X text = getTextFromWidget((Widget)XawToggleGetCurrent(radioGroup)); X if (text == NULL) X return; X i = 0; X while (*text && !isspace(*text) && i < 255) X dirname[i++] = *text++; X dirname[i] = '\0'; X if (chdir(dirname) < 0) X fprintf(stderr,"%s: couldn't cd to \"%s\"\n",program,dirname); X} X X/* X * General action function for user translations X * Only test for a selection if the fields are needed. X */ Xstatic char buf[1024]; X Xstatic void cmd(w,event,params,num_params) XWidget w; XXEvent *event; XString *params; XCardinal *num_params; X{ X char *s,*t,*text; X int i,field,f,seenDollar,hadBracket; X X if (*num_params < ONE) X return; X text = getTextFromWidget((Widget)XawToggleGetCurrent(radioGroup)); X s = *params; X i = 0; X seenDollar = 0; X while (*s) { X if (*s == '$') { X if (seenDollar) { X buf[i++] = *s; X seenDollar = False; X } else { X seenDollar = True; X } X s += 1; X } else if (seenDollar && (*s == '{' || isdigit(*s))) { X if (text == NULL) X return; X field = 0; X if ((hadBracket = (*s == '{'))) X s += 1; X while (*s && isdigit(*s)) X field = field * 10 + *s++ - '0'; X if (hadBracket) { X if (*s != '}') { X fprintf(stderr,"bad field in cmd: \"%s\"\n",*params); X return; X } else X s += 1; X } X if (field == 0) { X t = text; X while (*t) X buf[i++] = *t++; X } else { X t = text; X f = 1; X while (*t && f < field) X if (isspace(*t)) { X while (*t && isspace(*t)) X t += 1; X f += 1; X } else X t += 1; X if (*t != '\0') X while (*t && !isspace(*t)) X buf[i++] = *t++; X } X seenDollar = False; X } else { X if (seenDollar) { X buf[i++] = '$'; X seenDollar = False; X } X buf[i++] = *s++; X } X } X if (seenDollar) X buf[i++] = '$'; /* final $ if needed */ X buf[i] = '\0'; X if (appResources.debug) X printf("cmd: \"%s\"\n",buf); X else X system(buf); X} X X/* - - - - - - - - */ X/* X * Callback for timer: Sets all buttons and schedules next update. X */ Xstatic void XtimeoutProc(client_data,id) XXtPointer client_data; XXtIntervalId *id; X{ X updateFaces(); X interval = XtAppAddTimeOut(app_con,appResources.updateInterval*1000, X timeoutProc,NULL); X} X X/* - - - - - - - - */ X/* X * syntax() : print the usage message. X */ Xstatic void Xsyntax(argc,argv) Xint argc; Xchar **argv; X{ X argv += 1; X if (argc == 2) X fprintf(stderr,"%s: bad argument: %s\n",program,*argv); X else { X fprintf(stderr,"%s: bad arguments: ",program); X while (--argc) X fprintf(stderr,"%s ",*argv++); X fprintf(stderr,"\n"); X } X fprintf(stderr,"usage: %s\t-addNames\t\tadd labels to icons\n",program); X fprintf(stderr,"\t\t-opaqueNames\t\tprint labels opaquely\n"); X fprintf(stderr,"\t\t-updateInterval N\tseconds between updates\n"); X fprintf(stderr,"\t\t-faceDirectory dir\tlocation of icons\n"); X fprintf(stderr,"\t\t-updateCommand cmd\tcommand to monitor\n"); X fprintf(stderr,"\t\t-nameCommand cmd\tcommand to map names to icons\n"); X fprintf(stderr,"\t\t-debug\t\t\tprint command instead of executing\n"); X} X X/* X * fail() : Print a message and die. X */ Xvoid Xfail(fmt,arg) Xchar *fmt,*arg; X{ X fprintf(stderr,fmt,arg); X XtDestroyApplicationContext(app_con); X exit(1); X} X END_OF_FILE if test 11059 -ne `wc -c <'xmon.c'`; then echo shar: \"'xmon.c'\" unpacked with wrong size! fi # end of 'xmon.c' fi if test -f 'xmon.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xmon.man'\" else echo shar: Extracting \"'xmon.man'\" \(10663 characters\) sed "s/^X//" >'xmon.man' <<'END_OF_FILE' X.\" X.\" Xmon : Graphically monitor a list X.\" X.\" George Ferguson, ferguson@cs.rochester.edu, 4 May 1990. X.\" X.\" $Id: xmon.man,v 1.2 90/08/21 11:11:25 ferguson Exp $ X.\" X.TH XMON 1 "7/8/90" X.ds ]W U of Rochester X.SH NAME Xxmon, xru, xrf, xls, xfrom \- graphical list monitors for users, hosts, files, or mail X.sp X.SH SYNOPSIS X.B xmon X[ X Toolkit options ] X[ -addNames ] X[ -opaqueNames ] X.br X\ \ \ \ \ [ -smallFont font ] X[ -faceDirectory dir ] X.br X\ \ \ \ \ [ -updateInterval N ] X[ -updateCommand cmd ] X.br X\ \ \ \ \ [ -debug ] X.br X.B xru X\- display who's on the system X.br X.B xrf X\- display free machines on the network X.br X.B xls X\- display the contents of a directory X.br X.B xfrom X\- display whom mail is from X.sp X.SH DESCRIPTION X.PP XXmon is a tool which graphically monitors a list of names. That is, it Xperiodically runs a command and interprets the command's output as a list Xof items to display iconically. It provides a fully-customizable Xwidget interface, custom creation and mapping of widgets at run-time, and Xhooks for arbitrary action bindings. Appropriate defaults are provided for Xthe uninterested user X.PP XSeveral programs have been built on top of xmon using the customizations Xdescribed below. These are X.BR xru , X.BR xrf , X.B xls Xand X.BR xfrom . X.sp X.SH OPTIONS X.PP XThe following non-widget resources can be set on the command line as well Xas in a resource file. X.IP "\fB-addNames\fP XAlways add user's name to their icon. The default is to only add names for Xusers without icons, since they share the same default icon. X.IP "\fB-opaqueNames\fP XDraw names opaquely, ie. so that the background of the icon does not show Xthrough. This is the default, for readability. X.IP "\fB-smallFont\fP font" XSpecify the font used for drawing names on icons. It defaults to 8-point XCourier. X.IP "\fB-faceDirectory\fP dir" XSpecify where to look for face icons. See below under FILES for the Xdefault. X.IP "\fB-updateInterval\fP secs" XSpecify how long to wait between updates. Default is 60 seconds. A value of 0 X(zero) means no automatic updating. X.IP "\fB-updateCommand\fP cmd" XSpecify the command to run to do updates. This had better have the username X(or whatever you want the icons indexed by) in the first field, one item Xper line. X.IP "\fB-nameCommand\fP cmd" XSpecify the command to run to map user names to icon names. This should be Xa program which reads user names one per line from stdin and outputs the Xcorresponding icon namee to stdout followed by newline. It should exit Xwhen EOF is reached on stdin. X.IP "\fB-debug\fP XCommands invoked via the xmon-cmd() action are simply printed to stdout, rather Xthan executed by the shell. In addition, the updateCommand and nameCommand X(if there is one) are printed before being invoked. XThis useful for debugging custom application Xdefaults files. X.sp X.SH "REGISTERING YOUR FACE ICON" X.PP XCreate a 32x32 icon with the bitmap editor by doing X X.nf X % bitmap /u/ferguson/lib/images/\fBmyname\fP 32x32 X.fi X X(where X.B myname Xis your name) and save it with the "Write Output" button. For programs such Xas xrf, faces must be created by the xmon administrator to represent Xnon-user icons. X.sp X.SH "DERIVED TOOLS" X.PP XAs mentioned above, several programs have been built using xmon to monitor Xdifferent lists; these will be described below. All the applications are of Xclass X.B Xmon Xand so use the default application-defaults file. You can invoke the Xmore specific defaults by either (a) invoking xmon through an approriately Xnamed symbolic link (e.g. xru), or (b) by using the X.B -name Xtoolkit option (e.g. xmon -name xru). X.SH Xru X.PP XThis tool continually monitors the output of an rwho(1)-style program Xcalled ru(1) (a shell script, ru-awk, is provided for use in place of ru if Xyour site lacks it). The default buttons invoke talk(1) or finger(1) on the Xselected username, or w(1) on the machine corresponding to the selected Xuser. XBy default the keys T, F, and W do the same when typed in a faceButton. XIn all cases the output is to an xterm window. XThe default updateInterval for xru is 60 seconds and addNames is False. X.SH Xrf X.PP XThis tool displays "idle" machines on the network using ruptime(1)-style Xinformation from either rf(1) or a script which mimics its behaviour (rf-awk). XThe default buttons invoke rlogin(1), finger(1), or w(1) on the selected Xmachine in an xterm window. By default the display is only updated when Xthe Update button is clicked (updateInterval is 0) and addNames is True. X.SH Xls X.PP XThis tool provides a graphical filesystem browser by using the output Xof ls(1). The Ls-lg and File buttons pop up small xterms with additional Xinformation about the selected file; the Cd button changes the current Xdirectory to the selected one and displays the new files. XClicking the middle mouse button on Xa face "launches" that file: if it's a directory then a new xls is opened Xin that directory; if it's an executable file then it is executed; Xotherwise the file is paged in an xterm. Users can customize this Xbehaviour by modifying the default script "xls-cmd" (see FILES) Xand changing the program called by xmon-cmd() in the translations Xresources (see CUSTOMIZING). Updates are manual as for xrf. X.PP XThis tool illustrates the use of the X.B nameCommand Xresource to map user names to icon names. If this resource is non-empty Xthen that command is started for each update. It should read user names Xone per line from stdin and output the corresponding icon name to stdout X(followed by newline). XIt should exit after EOF on stdin. The default nameCommand is a script X(name2icon) Xwhich maps certain types of files (e.g. directories, executables, C code) Xto special icons, and all other files to a default icon. This can be Xcustomized by copying and changing the script (see FILES). X.SH Xfrom X.PP XThis tool monitors your mailbox and displays whom mail is from using Xsed(1) on your system mailbox (by default). The default buttons provide Xeither an xterm with Mail(1) running in it, or an xterm with Mail Xinvoked to send to the selected user. Updates are every 2 minutes by Xdefault and addNames is False. X.sp X.SH "CUSTOMIZING XMON" X.PP XXmon and the tools built on it have default resources built in. XIf you wish to customize the Xtool, take a copy of the default application defaults file (see XFILES) and modify it. Then, before invoking xmon, set the environment variable X.B XAPPLRESDIR Xto the directory containing your private copy. Alternatively, you can place Xentries in your .Xdefaults file or provide them with the -xrm toolkit Xoption. X.SH "Widget hierarchy" X.PP XXmon uses standard Athena widgets which can be customized as usual and Xin addition xmon allows you to explicitly specify the desired widget Xhierarchy. The resource entry X.B widgets Xshould be a string consisting of triples specifying the widget class, Xwidget name, and parent widget name of each widget desired. They will be Xcreated as listed in the resource, so arbitrary relationships can be set Xup. Each widget's properties can then be set using other resource entries Xspecific to that widget. X.PP XThe default widget hierarchy provides some buttons across the top, a label Xfor displaying information, and a viewport with a box inside it for Xdisplaying the faces. The resource entry to specify this looks like: X X.nf X.na XXmon.widgets: \e X Form form toplevel \en\e X Command quitbutton form \en\e X Label infoLabel form \en\e X Viewport view form \en\e X Box faceBox form.view X.ad X.fi X X.PP XYou must create at least the widgets X.B infoLabel Xand X.B faceBox X(with those names) since they are used by the action procedures. Note also Xthat parent widget names should be given in a form acceptable to XXtNameToWidget(3X). X.sp X.SH "Translation Actions" X.PP XThe following action procedures are registered for xmon and can be bound Xto widget events using the X.B translations Xresource (see the Xt manual, Appendix C). X.IP \fBxmon-quit()\fP XDestroy the tool and exit with status 0. X.IP \fBxmon-update()\fP XExecute the updateCommand and refresh the faces. X.IP \fBxmon-reinit()\fP XXmon caches the bitmaps used for the faces to avoid having to call XXReadBitmapFromFile(3X) many times on each update. This action causes xmon Xto forget the cached bitmaps and reread them, and also refreshes the faces Xto reflect the new bitmaps. X.IP \fBxmon-info()\fP XThis is the default action for clicking on a "face". XIt places the text associated with the currently selected face X(ie. the line of output from the updateCommand) Xin the infoLabel, or clears the infoLabel if no face is selected. XWhen customizing this, make sure appropriate calls to X.B set() Xor X.B toggle() X(see the Athena widgets manual) are placed before the call to xmon-info() Xto ensure that the right info is obtained. X.IP \fBxmon-cd()\fP XChanges directory to the filename given by the first word of the text Xassociated with the currently selected face. This action is used by xls Xto change directory without forking a new copy of itself (since xmon-cmd() Xactions are executed in a subshell, this must be built-in). X.IP \fBxmon-cmd()\fP XThis command is a hook for translations, particularly for face command buttons. XThis action should be passed a single string, use double-quotes Xto escape spaces. Any instances of "$<num>" or "${<num>}", where X"<num>" is a sequence of digits, is replaced with the corresponding Xfield from the face's text (ie. the text that would appear in Xthe "infoLabel" if xmon-info() was executed when that face was selected). XTo escape such a sequence, use two dollar signs. XThe resulting string is then passed to sh(1) for execution. Make sure that Xthe command is explicitly backgrounded (ie. ends with ampersand "&") if Xyou don't want xmon to block while the command is being executed. X.sp X.SH ENVIRONMENT X.PP XXAPPLRESDIR - Directory containing xmon resource file X.sp X.SH FILES X.PP X.nf X.na X/u/ferguson/lib/app-defaults/Xmon - default xmon resource file X/u/ferguson/lib/images/<username> - xmon face icons X/u/ferguson/lib/xmon/xls-cmd - "launch" files for xls X/u/ferguson/lib/xmon/name2icon - map filenames to icon names X/u/ferguson/lib/xmon/ru-awk - list users X/u/ferguson/lib/xmon/rf-awk - list free machines X.ad X.fi X.sp X.SH BUGS X.PP XResizing the application bigger can result in some faces reappearing. XThis is due to the XToolkit remapping widgets which were unmapped when Xthe number of faces to display dropped. Invoking xmon-update() clears Xthem up. X.sp X.SH "SEE ALSO" X.PP XX(1), Xru(1), Xrwho(1), Xrf(1), Xruptime(1), Xls(1), Xsed(1), XMail(1). X.sp X.SH AUTHOR X.PP XGeorge Ferguson, University of Rochester X.br X(ferguson@cs.rochester.edu) END_OF_FILE if test 10663 -ne `wc -c <'xmon.man'`; then echo shar: \"'xmon.man'\" unpacked with wrong size! fi # end of 'xmon.man' fi echo shar: End of archive 3 \(of 3\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 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 -- George Ferguson ARPA: ferguson@cs.rochester.edu University of Rochester UUCP: {decvax,rutgers}!rochester!ferguson Rochester NY 14627 VOX: (716) 275-2527