naughton@Eng (Patrick Naughton) (11/14/90)
Submitted-by: naughton@Eng (Patrick Naughton) Posting-number: Volume 10, Issue 69 Archive-name: xlock/part03 #! /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: xlock.c # Wrapped by naughton@wind on Tue Oct 30 11:26:51 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'xlock.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'xlock.c'\" else echo shar: Extracting \"'xlock.c'\" \(21618 characters\) sed "s/^X//" >'xlock.c' <<'END_OF_FILE' X#ifndef lint Xstatic char sccsid[] = "@(#)xlock.c 23.16 90/10/29 XLOCK SMI"; X#endif X/*- X * xlock.c - X11 client to lock a display and show a screen saver. X * X * Copyright (c) 1988-90 by Patrick Naughton and Sun Microsystems, Inc. X * X * Permission to use, copy, modify, and distribute this software and its X * documentation for any purpose and without fee is hereby granted, X * provided that the above copyright notice appear in all copies and that X * both that copyright notice and this permission notice appear in X * supporting documentation. X * X * This file is provided AS IS with no warranties of any kind. The author X * shall have no liability with respect to the infringement of copyrights, X * trade secrets or any patents by this file or any part thereof. In no X * event will the author be liable for any lost revenue or profits or X * other special, indirect and consequential damages. X * X * Comments and additions should be sent to the author: X * X * naughton@eng.sun.com X * X * Patrick J. Naughton X * MS 14-01 X * Windows and Graphics Group X * Sun Microsystems, Inc. X * 2550 Garcia Ave X * Mountain View, CA 94043 X * X * Revision History: X * 29-Oct-90: added cast to XFree() arg. X * added volume arg to call to XBell(). X * 28-Oct-90: center prompt screen. X * make sure Xlib input buffer does not use up all of swap. X * make displayed text come from resource file for better I18N. X * add backward compatible signal handlers for pre 4.1 machines. X * 31-Aug-90: added blank mode. X * added swarm mode. X * moved usleep() and seconds() out to usleep.c. X * added SVR4 defines to xlock.h X * 29-Jul-90: added support for multiple screens to be locked by one xlock. X * moved global defines to xlock.h X * removed use of allowsig(). X * 07-Jul-90: reworked commandline args and resources to use Xrm. X * moved resource processing out to resource.c X * 02-Jul-90: reworked colors to not use dynamic colormap. X * 23-May-90: added autoraise when obscured. X * 15-Apr-90: added hostent alias searching for host authentication. X * 18-Feb-90: added SunOS3.5 fix. X * changed -mono -> -color, and -saver -> -lock. X * allow non-locking screensavers to display on remote machine. X * added -echokeys to disable echoing of '?'s on input. X * cleaned up all of the parameters and defaults. X * 20-Dec-89: added -xhost to allow access control list to be left alone. X * added -screensaver (don't disable screen saver) for the paranoid. X * Moved seconds() here from all of the display mode source files. X * Fixed bug with calling XUngrabHosts() in finish(). X * 19-Dec-89: Fixed bug in GrabPointer. X * Changed fontname to XLFD style. X * 23-Sep-89: Added fix to allow local hostname:0 as a display. X * Put empty case for Enter/Leave events. X * Moved colormap installation later in startup. X * 20-Sep-89: Linted and made -saver mode grab the keyboard and mouse. X * Replaced SunView code for life mode with Jim Graham's version, X * so I could contrib it without legal problems. X * Sent to expo for X11R4 contrib. X * 19-Sep-89: Added '?'s on input. X * 27-Mar-89: Added -qix mode. X * Fixed GContext->GC. X * 20-Mar-89: Added backup font (fixed) if XQueryLoadFont() fails. X * Changed default font to lucida-sans-24. X * 08-Mar-89: Added -nice, -mode and -display, built vector for life and hop. X * 24-Feb-89: Replaced hopalong display with life display from SunView1. X * 22-Feb-89: Added fix for color servers with n < 8 planes. X * 16-Feb-89: Updated calling conventions for XCreateHsbColormap(); X * Added -count for number of iterations per color. X * Fixed defaulting mechanism. X * Ripped out VMS hacks. X * Sent to expo for X11R3 contrib. X * 15-Feb-89: Changed default font to pellucida-sans-18. X * 20-Jan-89: Added -verbose and fixed usage message. X * 19-Jan-89: Fixed monochrome gc bug. X * 16-Dec-88: Added SunView style password prompting. X * 19-Sep-88: Changed -color to -mono. (default is color on color displays). X * Added -saver option. (just do display... don't lock.) X * 31-Aug-88: Added -time option. X * Removed code for fractals to separate file for modularity. X * Added signal handler to restore host access. X * Installs dynamic colormap with a Hue Ramp. X * If grabs fail then exit. X * Added VMS Hacks. (password 'iwiwuu'). X * Sent to expo for X11R2 contrib. X * 08-Jun-88: Fixed root password pointer problem and changed PASSLENGTH to 20. X * 20-May-88: Added -root to allow root to unlock. X * 12-Apr-88: Added root password override. X * Added screen saver override. X * Removed XGrabServer/XUngrabServer. X * Added access control handling instead. X * 01-Apr-88: Added XGrabServer/XUngrabServer for more security. X * 30-Mar-88: Removed startup password requirement. X * Removed cursor to avoid phosphor burn. X * 27-Mar-88: Rotate fractal by 45 degrees clockwise. X * 24-Mar-88: Added color support. [-color] X * wrote the man page. X * 23-Mar-88: Added HOPALONG routines from Scientific American Sept. 86 p. 14. X * added password requirement for invokation X * removed option for command line password X * added requirement for display to be "unix:0". X * 22-Mar-88: Recieved Walter Milliken's comp.windows.x posting. X * X */ X X#include <stdio.h> X#include <signal.h> X#include <string.h> X#include <pwd.h> X X#include "xlock.h" X#include <X11/cursorfont.h> X Xextern char *crypt(); Xextern char *getenv(); X Xchar *ProgramName; /* argv[0] */ Xperscreen Scr[MAXSCREENS]; XDisplay *dsp = NULL; /* server display connection */ Xint screen; /* current screen */ Xvoid (*callback) () = NULL; Xvoid (*init) () = NULL; X Xstatic int screens; /* number of screens */ Xstatic Window win[MAXSCREENS]; /* window used to cover screen */ Xstatic Window icon[MAXSCREENS]; /* window used during password typein */ Xstatic Window root[MAXSCREENS]; /* convenience pointer to the root window */ Xstatic GC textgc[MAXSCREENS]; /* graphics context used for text rendering */ Xstatic XColor fgcol[MAXSCREENS];/* used for text rendering */ Xstatic XColor bgcol[MAXSCREENS];/* background of text screen */ Xstatic int iconx[MAXSCREENS]; /* location of left edge of icon */ Xstatic int icony[MAXSCREENS]; /* location of top edge of icon */ Xstatic Cursor mycursor; /* blank cursor */ Xstatic Pixmap lockc; Xstatic Pixmap lockm; /* pixmaps for cursor and mask */ Xstatic char no_bits[] = {0}; /* dummy array for the blank cursor */ Xstatic int passx; /* position of the ?'s */ Xstatic int passy; Xstatic XFontStruct *font; Xstatic int sstimeout; /* screen saver parameters */ Xstatic int ssinterval; Xstatic int ssblanking; Xstatic int ssexposures; X X#define FALLBACK_FONTNAME "fixed" X#define ICONW 64 X#define ICONH 64 X X#define AllPointerEventMask \ X (ButtonPressMask | ButtonReleaseMask | \ X EnterWindowMask | LeaveWindowMask | \ X PointerMotionMask | PointerMotionHintMask | \ X Button1MotionMask | Button2MotionMask | \ X Button3MotionMask | Button4MotionMask | \ X Button5MotionMask | ButtonMotionMask | \ X KeymapStateMask) X X X/* VARARGS1 */ Xvoid Xerror(s1, s2) X char *s1, X *s2; X{ X fprintf(stderr, s1, ProgramName, s2); X exit(1); X} X X/* X * Server access control support. X */ X Xstatic XHostAddress *XHosts; /* the list of "friendly" client machines */ Xstatic int HostAccessCount; /* the number of machines in XHosts */ Xstatic Bool HostAccessState; /* whether or not we even look at the list */ X Xstatic void XXGrabHosts(dsp) X Display *dsp; X{ X XHosts = XListHosts(dsp, &HostAccessCount, &HostAccessState); X if (XHosts) X XRemoveHosts(dsp, XHosts, HostAccessCount); X XEnableAccessControl(dsp); X} X Xstatic void XXUngrabHosts(dsp) X Display *dsp; X{ X if (XHosts) { X XAddHosts(dsp, XHosts, HostAccessCount); X XFree((char *)XHosts); X } X if (HostAccessState == False) X XDisableAccessControl(dsp); X} X X X/* X * Simple wrapper to get an asynchronous grab on the keyboard and mouse. X * If either grab fails, we sleep for one second and try again since some X * window manager might have had the mouse grabbed to drive the menu choice X * that picked "Lock Screen..". If either one fails the second time we print X * an error message and exit. X */ Xstatic void XGrabKeyboardAndMouse() X{ X Status status; X X status = XGrabKeyboard(dsp, win[0], True, X GrabModeAsync, GrabModeAsync, CurrentTime); X if (status != GrabSuccess) { X sleep(1); X status = XGrabKeyboard(dsp, win[0], True, X GrabModeAsync, GrabModeAsync, CurrentTime); X X if (status != GrabSuccess) X error("%s: couldn't grab keyboard! (%d)\n", status); X } X status = XGrabPointer(dsp, win[0], True, AllPointerEventMask, X GrabModeAsync, GrabModeAsync, None, mycursor, X CurrentTime); X if (status != GrabSuccess) { X sleep(1); X status = XGrabPointer(dsp, win[0], True, AllPointerEventMask, X GrabModeAsync, GrabModeAsync, None, mycursor, X CurrentTime); X X if (status != GrabSuccess) X error("%s: couldn't grab pointer! (%d)\n", status); X } X} X X X/* X * Assuming that we already have an asynch grab on the pointer, X * just grab it again with a new cursor shape and ignore the return code. X */ Xstatic void XXChangeGrabbedCursor(cursor) X Cursor cursor; X{ X#ifndef DEBUG X (void) XGrabPointer(dsp, win[0], True, AllPointerEventMask, X GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime); X#endif X} X X X/* X * Restore all grabs, reset screensaver, restore colormap, close connection. X */ Xstatic void Xfinish() X{ X XSync(dsp, False); X if (!nolock && !allowaccess) X XUngrabHosts(dsp); X XUngrabPointer(dsp, CurrentTime); X XUngrabKeyboard(dsp, CurrentTime); X if (!enablesaver) X XSetScreenSaver(dsp, sstimeout, ssinterval, ssblanking, ssexposures); X XFlush(dsp); X XCloseDisplay(dsp); X} X X Xstatic int XReadXString(s, slen) X char *s; X int slen; X{ X XEvent event; X char keystr[20]; X char c; X int i; X int bp; X int len; X int thisscreen = screen; X char *pwbuf = (char *) malloc(slen); X X#ifdef DEBUG X XSetInputFocus(dsp, win[screen], RevertToPointerRoot, CurrentTime); X#endif X for (screen = 0; screen < screens; screen++) X if (thisscreen == screen) X init(icon[screen]); X else X init(win[screen]); X bp = 0; X nice(-nicelevel); /* make sure we can read the keys... */ X while (True) { X unsigned long lasteventtime = seconds(); X while (!XPending(dsp)) { X for (screen = 0; screen < screens; screen++) X if (thisscreen == screen) X callback(icon[screen]); X else X callback(win[screen]); X XFlush(dsp); X usleep(delay); X if (seconds() - lasteventtime > timeout) { X nice(nicelevel); X free(pwbuf); X screen = thisscreen; X return 1; X } X } X screen = thisscreen; X XNextEvent(dsp, &event); X switch (event.type) { X case KeyPress: X len = XLookupString((XKeyEvent *) & event, keystr, 20, NULL, NULL); X for (i = 0; i < len; i++) { X c = keystr[i]; X switch (c) { X case 8: /* ^H */ X case 127: /* DEL */ X if (bp > 0) X bp--; X break; X case 10: /* ^J */ X case 13: /* ^M */ X s[bp] = '\0'; X nice(nicelevel); X free(pwbuf); X return 0; X case 21: /* ^U */ X bp = 0; X break; X default: X s[bp] = c; X if (bp < slen - 1) X bp++; X else X XSync(dsp, True); /* flush input buffer */ X X } X } X if (echokeys) { X memset(pwbuf, '?', slen); X XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel); X XFillRectangle(dsp, win[screen], Scr[screen].gc, X passx, passy - font->ascent, X XTextWidth(font, pwbuf, slen), X font->ascent + font->descent); X XDrawString(dsp, win[screen], textgc[screen], X passx, passy, pwbuf, bp); X } X /* X * eat all events if there are more than enough pending... this X * keeps the Xlib event buffer from growing larger than all X * available memory and crashing xlock. X */ X if (XPending(dsp) > 100) { /* 100 is arbitrarily big enough */ X register Status status; X do { X status = XCheckMaskEvent(dsp, X KeyPressMask | KeyReleaseMask, &event); X } while (status); X XBell(dsp, 100); X } X break; X X case ButtonPress: X if (((XButtonEvent *) & event)->window == icon[screen]) { X nice(nicelevel); X free(pwbuf); X return 1; X } X break; X X case VisibilityNotify: X if (event.xvisibility.state != VisibilityUnobscured) { X#ifndef DEBUG X XRaiseWindow(dsp, win[screen]); X#endif X s[0] = '\0'; X nice(nicelevel); X free(pwbuf); X return 1; X } X break; X X case KeymapNotify: X case KeyRelease: X case ButtonRelease: X case MotionNotify: X case LeaveNotify: X case EnterNotify: X break; X X default: X fprintf(stderr, "%s: unexpected event: %d\n", X ProgramName, event.type); X break; X } X } X} X X Xstatic int XgetPassword() X{ X#define PASSLENGTH 20 X char buffer[PASSLENGTH]; X char userpass[PASSLENGTH]; X char rootpass[PASSLENGTH]; X struct passwd *pw; X XWindowAttributes xgwa; X char *user = getenv(USERNAME); X int y, X left, X done; X X XGetWindowAttributes(dsp, win[screen], &xgwa); X X XChangeGrabbedCursor(XCreateFontCursor(dsp, XC_left_ptr)); X X XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel); X XFillRectangle(dsp, win[screen], Scr[screen].gc, X 0, 0, xgwa.width, xgwa.height); X X XMapWindow(dsp, icon[screen]); X XRaiseWindow(dsp, icon[screen]); X X left = iconx[screen] + ICONW + font->max_bounds.width; X y = icony[screen] + font->ascent; X X XDrawString(dsp, win[screen], textgc[screen], X left, y, text_name, strlen(text_name)); X XDrawString(dsp, win[screen], textgc[screen], X left + 1, y, text_name, strlen(text_name)); X XDrawString(dsp, win[screen], textgc[screen], X left + XTextWidth(font, text_name, strlen(text_name)), y, X user, strlen(user)); X X y += font->ascent + font->descent + 2; X XDrawString(dsp, win[screen], textgc[screen], X left, y, text_pass, strlen(text_pass)); X XDrawString(dsp, win[screen], textgc[screen], X left + 1, y, text_pass, strlen(text_pass)); X X passx = left + 1 + XTextWidth(font, text_pass, strlen(text_pass)) X + XTextWidth(font, " ", 1); X passy = y; X X y = icony[screen] + ICONH + font->ascent + 2; X XDrawString(dsp, win[screen], textgc[screen], X iconx[screen], y, text_info, strlen(text_info)); X X XFlush(dsp); X X y += font->ascent + font->descent + 2; X X pw = getpwnam("root"); X strcpy(rootpass, pw->pw_passwd); X X pw = getpwnam(user); X strcpy(userpass, pw->pw_passwd); X X done = False; X while (!done) { X if (ReadXString(buffer, PASSLENGTH)) { X XChangeGrabbedCursor(mycursor); X XUnmapWindow(dsp, icon[screen]); X XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel); X return 1; X } X XSetForeground(dsp, Scr[screen].gc, bgcol[screen].pixel); X XFillRectangle(dsp, win[screen], Scr[screen].gc, X iconx[screen], y - font->ascent, X XTextWidth(font, text_invalid, strlen(text_invalid)), X font->ascent + font->descent + 2); X X XDrawString(dsp, win[screen], textgc[screen], X iconx[screen], y, text_valid, strlen(text_valid)); X X done = !((strcmp(crypt(buffer, userpass), userpass)) X && (!allowroot || strcmp(crypt(buffer, rootpass), rootpass))); X X if (!done) { X XSync(dsp, True); /* flush input buffer */ X sleep(1); X XFillRectangle(dsp, win[screen], Scr[screen].gc, X iconx[screen], y - font->ascent, X XTextWidth(font, text_valid, strlen(text_valid)), X font->ascent + font->descent + 2); X X XDrawString(dsp, win[screen], textgc[screen], X iconx[screen], y, text_invalid, strlen(text_invalid)); X } X } X return 0; X} X X Xstatic void XjustDisplay() X{ X XEvent event; X X for (screen = 0; screen < screens; screen++) X init(win[screen]); X do { X while (!XPending(dsp)) { X for (screen = 0; screen < screens; screen++) X callback(win[screen]); X XFlush(dsp); X usleep(delay); X } X XNextEvent(dsp, &event); X#ifndef DEBUG X if (event.type == VisibilityNotify) X XRaiseWindow(dsp, event.xany.window); X#endif X } while (event.type != ButtonPress && event.type != KeyPress); X for (screen = 0; screen < screens; screen++) X if (event.xbutton.root == RootWindow(dsp, screen)) X break; X} X X Xstatic void Xsigcatch() X{ X finish(); X error("%s: caught terminate signal.\nAccess control list restored.\n"); X} X X Xstatic void XlockDisplay() X{ X if (!allowaccess) { X#ifdef SYSV X sigset_t oldsigmask; X sigset_t newsigmask; X X sigemptyset(&newsigmask); X sigaddset(&newsigmask, SIGHUP); X sigaddset(&newsigmask, SIGINT); X sigaddset(&newsigmask, SIGQUIT); X sigaddset(&newsigmask, SIGTERM); X sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask); X#else X int oldsigmask; X X oldsigmask = sigblock(sigmask(SIGHUP) | X sigmask(SIGINT) | X sigmask(SIGQUIT) | X sigmask(SIGTERM)); X#endif X X signal(SIGHUP, sigcatch); X signal(SIGINT, sigcatch); X signal(SIGQUIT, sigcatch); X signal(SIGTERM, sigcatch); X X XGrabHosts(dsp); X X#ifdef SYSV X sigprocmask(SIG_SETMASK, &oldsigmask, &oldsigmask); X#else X sigsetmask(oldsigmask); X#endif X } X do { X justDisplay(); X } while (getPassword()); X} X X Xint Xmain(argc, argv) X int argc; X char *argv[]; X{ X XSetWindowAttributes xswa; X XGCValues xgcv; X X ProgramName = strrchr(argv[0], '/'); X if (ProgramName) X ProgramName++; X else X ProgramName = argv[0]; X X GetResources(argc, argv); X X CheckResources(); X X font = XLoadQueryFont(dsp, fontname); X if (font == NULL) { X fprintf(stderr, "%s: can't find font: %s, using %s...\n", X ProgramName, fontname, FALLBACK_FONTNAME); X font = XLoadQueryFont(dsp, FALLBACK_FONTNAME); X if (font == NULL) X error("%s: can't even find %s!!!\n", FALLBACK_FONTNAME); X } X screens = ScreenCount(dsp); X if (screens > MAXSCREENS) X error("%s: can only support %d screens.\n", MAXSCREENS); X for (screen = 0; screen < screens; screen++) { X XColor tmp; X Screen *scr = ScreenOfDisplay(dsp, screen); X Colormap cmap = DefaultColormapOfScreen(scr); X X root[screen] = RootWindowOfScreen(scr); X if (mono || CellsOfScreen(scr) == 2) { X XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp); X XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp); X Scr[screen].pixels[0] = fgcol[screen].pixel; X Scr[screen].pixels[1] = bgcol[screen].pixel; X Scr[screen].npixels = 2; X } else { X int colorcount = NUMCOLORS; X u_char red[NUMCOLORS]; X u_char green[NUMCOLORS]; X u_char blue[NUMCOLORS]; X int i; X X if (!XAllocNamedColor(dsp, cmap, background, X &bgcol[screen], &tmp)) { X fprintf(stderr, "couldn't allocate: %s\n", background); X XAllocNamedColor(dsp, cmap, "White", &bgcol[screen], &tmp); X } X if (!XAllocNamedColor(dsp, cmap, foreground, X &fgcol[screen], &tmp)) { X fprintf(stderr, "couldn't allocate: %s\n", foreground); X XAllocNamedColor(dsp, cmap, "Black", &fgcol[screen], &tmp); X } X hsbramp(0.0, saturation, 1.0, 1.0, saturation, 1.0, colorcount, X red, green, blue); X Scr[screen].npixels = 0; X for (i = 0; i < colorcount; i++) { X XColor xcolor; X X xcolor.red = red[i] << 8; X xcolor.green = green[i] << 8; X xcolor.blue = blue[i] << 8; X xcolor.flags = DoRed | DoGreen | DoBlue; X X if (!XAllocColor(dsp, cmap, &xcolor)) X break; X X Scr[screen].pixels[i] = xcolor.pixel; X Scr[screen].npixels++; X } X if (verbose) X fprintf(stderr, "%d pixels allocated\n", Scr[screen].npixels); X } X X xswa.override_redirect = True; X xswa.background_pixel = BlackPixelOfScreen(scr); X xswa.event_mask = KeyPressMask | ButtonPressMask | VisibilityChangeMask; X X win[screen] = XCreateWindow(dsp, root[screen], X 0, 0, X WidthOfScreen(scr), X HeightOfScreen(scr), X 0, CopyFromParent, InputOutput, CopyFromParent, X CWOverrideRedirect | CWBackPixel | CWEventMask, &xswa); X X xswa.background_pixel = bgcol[screen].pixel; X xswa.event_mask = ButtonPressMask; X X iconx[screen] = (DisplayWidth(dsp, screen) - X XTextWidth(font, text_info, strlen(text_info))) / 2; X X icony[screen] = DisplayHeight(dsp, screen) / 6; X icon[screen] = XCreateWindow(dsp, win[screen], X iconx[screen], icony[screen], X ICONW, ICONH, X 1, CopyFromParent, X InputOutput, CopyFromParent, X CWBackPixel | CWEventMask, &xswa); X X XMapWindow(dsp, win[screen]); X XRaiseWindow(dsp, win[screen]); X X xgcv.foreground = WhitePixelOfScreen(scr); X xgcv.background = BlackPixelOfScreen(scr); X Scr[screen].gc = XCreateGC(dsp, win[screen], X GCForeground | GCBackground, &xgcv); X X xgcv.foreground = fgcol[screen].pixel; X xgcv.background = bgcol[screen].pixel; X xgcv.font = font->fid; X textgc[screen] = XCreateGC(dsp, win[screen], X GCFont | GCForeground | GCBackground, &xgcv); X } X lockc = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1); X lockm = XCreateBitmapFromData(dsp, root[0], no_bits, 1, 1); X mycursor = XCreatePixmapCursor(dsp, lockc, lockm, X &fgcol[screen], &bgcol[screen], 0, 0); X XFreePixmap(dsp, lockc); X XFreePixmap(dsp, lockm); X X X if (!enablesaver) { X XGetScreenSaver(dsp, &sstimeout, &ssinterval, X &ssblanking, &ssexposures); X XSetScreenSaver(dsp, 0, 0, 0, 0); /* disable screen saver */ X } X#ifndef DEBUG X GrabKeyboardAndMouse(); X#endif X X srandom(getpid()); X X nice(nicelevel); X X if (nolock) X justDisplay(); X else X lockDisplay(); X X finish(); X X return 0; X} END_OF_FILE if test 21618 -ne `wc -c <'xlock.c'`; then echo shar: \"'xlock.c'\" unpacked with wrong size! fi # end of 'xlock.c' 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 dan ---------------------------------------------------- O'Reilly && Associates argv@sun.com / argv@ora.com Opinions expressed reflect those of the author only. -- dan ---------------------------------------------------- O'Reilly && Associates argv@sun.com / argv@ora.com Opinions expressed reflect those of the author only.