v08i110: xdbx -- Dbx for X11, Part06/07
Po Cheung
cheung%SW.MCC.COM at MCC.COM
Tue Aug 28 18:05:33 AEST 1990
Submitted-by: cheung%SW.MCC.COM at MCC.COM (Po Cheung)
Posting-number: Volume 8, Issue 110
Archive-name: xdbx/part06
#! /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 6 (of 7)."
# Contents: datadpy.c
# Wrapped by cheung at espresso.sw.mcc.com on Fri Aug 24 03:24:52 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'datadpy.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'datadpy.c'\"
else
echo shar: Extracting \"'datadpy.c'\" \(19682 characters\)
sed "s/^X//" >'datadpy.c' <<'END_OF_FILE'
X/*****************************************************************************
X *
X * xdbx - X Window System interface to the dbx debugger
X *
X * Copyright 1989 The University of Texas at Austin
X * Copyright 1990 Microelectronics and Computer Technology Corporation
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, and that the name of The University of Texas
X * and Microelectronics and Computer Technology Corporation (MCC) not be
X * used in advertising or publicity pertaining to distribution of
X * the software without specific, written prior permission. The
X * University of Texas and MCC makes no representations about the
X * suitability of this software for any purpose. It is provided "as is"
X * without express or implied warranty.
X *
X * THE UNIVERSITY OF TEXAS AND MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO
X * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
X * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF TEXAS OR MCC BE LIABLE FOR
X * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
X * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
X * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
X * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X *
X * Author: Po Cheung
X * Created: March 10, 1989
X *
X *****************************************************************************/
X
X/* dataDpy.c:
X *
X * Provide graphical display of C pointers and structures.
X *
X * BuildLinePos(): Construct an array indexing the character position of
X * each line.
X * PositionToLine(): Return the character position of a given line.
X * SelectPointer(): Action proc for double click on a pointer value,
X * CreateDataPopup(): Create a popup to display the object pointed to by a
X * pointer.
X * UpdateDataPopup(): Update an unused popupshell to display data.
X * AppendList(): Append a popup to the list.
X * DeleteList(): Delete a popup from the list.
X * pop_down(): pop down the popup and free storage.
X * DestroyDataPopup():event handler for destroying a popup, call DeleteList()
X * and pop_down().
X * MovePopup(): Position the popup.
X * print_handler(): Action handler for displaying pointers and structures.
X */
X
X#include "global.h"
X#include "regex.h"
X#include "datadpy.h"
X
X#define MAXLEVELS 20 /* max level of indentation */
X#define INDENT 8 /* # of spaces for each indentation */
X#define EMPTY 0
X#define UNUSED 1
X#define USED 2
X#define LEFT_MARGIN 10
X#define SCROLLBAR_WIDTH 15
X
Xstatic DataDpyRec **dataDpyTable;
Xstatic int dataDpyTableSize = 0;
Xstatic DataDpyRec *Parent = NULL;
Xstatic DataDpyList *TopParentList = NULL;
Xstatic int font_height, font_width;
Xstatic void DestroyDataPopup();
X
X/*
X * Build an array which gives the starting text position of each line.
X * Very similar to the routine in source.c.
X */
Xstatic void BuildLinePos(dataDpy)
XDataDpyRec *dataDpy;
X{
X char *p;
X int line, nlines;
X int max=0;
X
X nlines = MAX(1, dataDpy->buflen/CHARS_PER_LINE);
X dataDpy->linepos = (XawTextPosition *)
X XtMalloc ((nlines+2) * sizeof(XawTextPosition));
X p = dataDpy->buf;
X line = 0;
X dataDpy->linepos[line++] = 0;
X dataDpy->linepos[line++] = 0;
X while (*p) {
X if (*p++ == '\n') {
X if (line == nlines) { /* buffer full, need more memory */
X dataDpy->linepos = (XawTextPosition *)XtRealloc(dataDpy->linepos,
X (nlines + ADD_LINES) * sizeof(XawTextPosition));
X nlines += ADD_LINES;
X }
X dataDpy->linepos[line] = p - dataDpy->buf;
X AssignMax(max, dataDpy->linepos[line] - dataDpy->linepos[line-1]);
X line++;
X }
X }
X dataDpy->numlines = line - 2;
X dataDpy->maxLineLength = max;
X /* shrink to min size */
X dataDpy->linepos = (XawTextPosition *) XtRealloc
X (dataDpy->linepos, line * sizeof(XawTextPosition));
X}
X
X/*
X * Return the line number for the specified text position.
X */
Xstatic int PositionToLine(dataDpy, pos)
XDataDpyRec *dataDpy;
XXawTextPosition pos;
X{
X int line;
X
X if (dataDpy && pos >= 0) {
X for (line = 1; pos >= dataDpy->linepos[line]; line++);
X return (line-1);
X }
X else
X return (0);
X}
X
X/* ARGSUSED */
X/*
X * Called by double click of pointer button.
X * If the selected text is a valid pointer, this routine parses the data
X * output to obtain the full qualified name of the pointer, and asks
X * dbx to print the value of the object the pointer is pointing to.
X */
Xstatic void SelectPointer(w, event, params, num_params)
X Widget w;
X XEvent *event;
X String *params;
X Cardinal *num_params;
X{
X struct re_registers regs;
X XawTextPosition left, right;
X char *selection, *p, *field[MAXLEVELS];
X DataDpyRec *dataDpy;
X int fromLine, line;
X int i, n, nbytes, r, level, newlevel;
X char name[LINESIZ], command[LINESIZ];
X
X /* Find out which data display output does the selection belong to */
X dataDpy = NULL;
X for (i=0; dataDpyTable[i]; i++)
X if ((Widget) w == (Widget) dataDpyTable[i]->dataDpyWindow) {
X dataDpy = dataDpyTable[i];
X Parent = dataDpy;
X break;
X }
X if (!dataDpy) return;
X
X /* Get the selection and check if it's a pointer value, 0x???? */
X selection = XFetchBytes(display, &nbytes);
X if (re_match(dataPattern[D_POINTER].buf, selection, strlen(selection), 0, 0)
X < 0) {
X Parent = NULL;
X return;
X }
X
X /* Parse the output to get the fully qualified name of the pointer */
X XawTextGetSelectionPos(w, &left, &right);
X fromLine = PositionToLine(dataDpy, left);
X p = dataDpy->buf + dataDpy->linepos[fromLine];
X
X if (re_match(dataPattern[D_FIELD].buf, p, strlen(p), 0, ®s) >= 0) {
X r = dataPattern[D_FIELD].reg_token[TK_POINTER];
X if (strncmp(selection, p+regs.start[r], regs.end[r]-regs.start[r]))
X return;
X r = dataPattern[D_FIELD].reg_token[TK_INDENT];
X level = regs.end[r]/INDENT;
X field[level+1] = NULL;
X
X r = dataPattern[D_FIELD].reg_token[TK_FIELD];
X n = regs.end[r] - regs.start[r];
X field[level] = (char *) XtMalloc ((n+1) * sizeof(char));
X strncpy(field[level], p+regs.start[r], n);
X field[level][n] = '\0';
X
X for (line = fromLine-1; line > 0; line--) {
X p = dataDpy->buf + dataDpy->linepos[line];
X if (re_match(dataPattern[D_STRUCT].buf, p, strlen(p), 0, ®s)>=0){
X r = dataPattern[D_STRUCT].reg_token[TK_INDENT];
X newlevel = regs.end[r]/INDENT;
X if (newlevel == level-1) {
X level--;
X r = dataPattern[D_STRUCT].reg_token[TK_FIELD];
X n = regs.end[r] - regs.start[r];
X field[level] = (char *) XtMalloc ((n+1) * sizeof(char));
X strncpy(field[level], p+regs.start[r], n);
X field[level][n] = '\0';
X }
X }
X }
X if (*field[0] == '*' && field[1])
X sprintf(name, "(%s)", field[0]+1);
X else
X strcpy(name, field[0]);
X for (i=1; field[i]; i++) {
X strcat(name, ".");
X strcat(name, field[i]);
X }
X sprintf(command, "print *(%s)\n", name);
X PopupMode = True;
X query_dbx(command);
X }
X}
X
X
X/*
X * Create a data display with a label.
X * The popupshell has a form widget which consists of a label and a text
X * widget.
X */
Xstatic void CreateDataPopup(dataDpy, label)
XDataDpyRec *dataDpy;
Xchar *label;
X{
X Arg args[MAXARGS];
X Cardinal n;
X Dimension dataDpyHeight, dataDpyWidth;
X XFontStruct *text_font;
X
X static XtActionsRec datadpy_actions[] = {
X {"SelectPointer", (XtActionProc) SelectPointer},
X {NULL, NULL}
X };
X
X static String translations = "#override \n\
X <Btn1Down>: SelectStart() SelectWord() SelectPointer() \n\
X <Btn1Up>: SelectEnd() \n\
X ";
X
X n = 0;
X dataDpy->popupshell = XtCreatePopupShell("Data Popup",
X transientShellWidgetClass, toplevel, args, n);
X
X n = 0;
X XtSetArg(args[n], XtNdefaultDistance, 0); n++;
X dataDpy->popup = XtCreateManagedWidget("popup", formWidgetClass,
X dataDpy->popupshell, args, n);
X
X /* Create the label */
X n = 0;
X XtSetArg(args[n], XtNtop, (XtArgVal) XawChainTop); n++;
X XtSetArg(args[n], XtNbottom, (XtArgVal) XawChainTop); n++;
X XtSetArg(args[n], XtNright, (XtArgVal) XawChainRight); n++;
X XtSetArg(args[n], XtNleft, (XtArgVal) XawChainLeft); n++;
X XtSetArg(args[n], XtNlabel, (XtArgVal) label); n++;
X XtSetArg(args[n], XtNresize, (XtArgVal) False); n++;
X XtSetArg(args[n], XtNjustify, (XtArgVal) XtJustifyCenter); n++;
X dataDpy->label = XtCreateManagedWidget("label", labelWidgetClass,
X dataDpy->popup, args, n);
X XtAddEventHandler(dataDpy->label, (EventMask) ButtonPressMask, False,
X DestroyDataPopup, dataDpy);
X
X /* Create the text window */
X n = 0;
X XtSetArg(args[n], XtNfromVert, (XtArgVal) dataDpy->label); n++;
X XtSetArg(args[n], XtNtop, (XtArgVal) XawChainTop); n++;
X XtSetArg(args[n], XtNbottom, (XtArgVal) XawChainBottom); n++;
X XtSetArg(args[n], XtNright, (XtArgVal) XawChainRight); n++;
X XtSetArg(args[n], XtNleft, (XtArgVal) XawChainLeft); n++;
X
X XtSetArg(args[n], XtNleftMargin, (XtArgVal) LEFT_MARGIN); n++;
X XtSetArg(args[n], XtNuseStringInPlace, (XtArgVal) True); n++;
X XtSetArg(args[n], XtNstring, (XtArgVal) dataDpy->buf); n++;
X XtSetArg(args[n], XtNlength, (XtArgVal) dataDpy->buflen); n++;
X XtSetArg(args[n], XtNeditType, (XtArgVal) XawtextRead); n++;
X XtSetArg(args[n], XtNscrollHorizontal, XawtextScrollWhenNeeded); n++;
X XtSetArg(args[n], XtNscrollVertical, XawtextScrollWhenNeeded); n++;
X XtSetArg(args[n], XtNtranslations, XtParseTranslationTable(translations));
X n++;
X dataDpy->dataDpyWindow = XtCreateManagedWidget("dataDpyWindow",
X asciiTextWidgetClass, dataDpy->popup, args, n);
X XtAppAddActions(app_context, datadpy_actions, XtNumber(datadpy_actions));
X
X /* Get the text font */
X n = 0;
X XtSetArg(args[n], XtNfont, &text_font); n++;
X XtGetValues(dataDpy->dataDpyWindow, args, n);
X
X /* Estimate the size of the text widget, dataDpyWindow, with the number
X of lines and the maximum length of a line. Assume fixed font width.
X */
X font_height = text_font->ascent + text_font->descent;
X font_width = text_font->max_bounds.width;
X dataDpyHeight = dataDpy->numlines * font_height + 5;
X dataDpyWidth = dataDpy->maxLineLength * font_width + LEFT_MARGIN;
X if (dataDpyHeight > app_resources.dataDpyMaxHeight)
X dataDpyWidth += SCROLLBAR_WIDTH;
X AssignMin(dataDpyHeight, app_resources.dataDpyMaxHeight);
X AssignMin(dataDpyWidth, app_resources.dataDpyMaxWidth);
X
X n = 0;
X XtSetArg(args[n], XtNheight, (XtArgVal) dataDpyHeight); n++;
X XtSetArg(args[n], XtNwidth, (XtArgVal) dataDpyWidth); n++;
X XtSetValues(dataDpy->dataDpyWindow, args, n);
X
X n = 0;
X XtSetArg(args[n], XtNwidth, (XtArgVal) dataDpyWidth); n++;
X XtSetValues(dataDpy->label, args, n);
X}
X
X/*
X * Instead of creating a new popupshell, this routine uses an already
X * existing popupshell for data display.
X * It changes the label, calculates the size of the popupshell,
X * and sets the source of the text window to that of the new data.
X */
Xstatic void UpdateDataPopup(dataDpy, label)
XDataDpyRec *dataDpy;
Xchar *label;
X{
X Arg args[MAXARGS];
X Cardinal n;
X Dimension popupHeight, popupWidth, dataDpyHeight, dataDpyWidth,
X labelHeight, labelBorderWidth, dataDpyBorderWidth;
X
X /* Update the label */
X n = 0;
X XtSetArg(args[n], XtNlabel, (XtArgVal) label); n++;
X XtSetValues(dataDpy->label, args, n);
X
X /* Calculate the size of popupshell */
X dataDpyHeight = dataDpy->numlines * font_height + 5;
X dataDpyWidth = dataDpy->maxLineLength * font_width + 2*10;
X AssignMin(dataDpyHeight, app_resources.dataDpyMaxHeight);
X AssignMin(dataDpyWidth, app_resources.dataDpyMaxWidth);
X
X n = 0;
X XtSetArg(args[n], XtNheight, (XtArgVal) &labelHeight); n++;
X XtSetArg(args[n], XtNborderWidth, (XtArgVal) &labelBorderWidth); n++;
X XtGetValues(dataDpy->label, args, n);
X n = 0;
X XtSetArg(args[n], XtNborderWidth, (XtArgVal) &dataDpyBorderWidth); n++;
X XtGetValues(dataDpy->dataDpyWindow, args, n);
X
X popupHeight = dataDpyHeight + labelHeight + 2*labelBorderWidth +
X 2*dataDpyBorderWidth;
X popupWidth = dataDpyWidth;
X n = 0;
X XtSetArg(args[n], XtNheight, (XtArgVal) popupHeight); n++;
X XtSetArg(args[n], XtNwidth, (XtArgVal) popupWidth); n++;
X XtSetValues(dataDpy->popupshell, args, n);
X
X /* Set the text source */
X n = 0;
X XtSetArg(args[n], XtNstring, (XtArgVal) dataDpy->buf); n++;
X XtSetArg(args[n], XtNlength, (XtArgVal) dataDpy->buflen); n++;
X XawTextSetSource(dataDpy->dataDpyWindow,
X XtCreateWidget("textsrc", asciiSrcObjectClass,
X dataDpy->dataDpyWindow, args, n),
X 0);
X}
X
X/*
X * Append dataDpy to a DataDpyList pointed to by head.
X */
Xstatic void AppendList(head, dataDpy)
XDataDpyList **head;
XDataDpyRec *dataDpy;
X{
X DataDpyList *p, *q, *r;
X
X p = (DataDpyList *) XtNew (DataDpyList);
X p->dataDpy = dataDpy;
X p->next = NULL;
X q = *head;
X if (!q)
X *head = p;
X else {
X while (r = q->next)
X q = r;
X q->next = p;
X }
X}
X
X/*
X * Removes a dataDpy from its parent's list of children.
X */
Xstatic void DeleteList(head, dataDpy)
XDataDpyList **head;
XDataDpyRec *dataDpy;
X{
X DataDpyList *p, *q;
X
X if (p = *head) {
X if (p->dataDpy == dataDpy)
X *head = p->next;
X else {
X for (q = p->next; q && q->dataDpy != dataDpy;) {
X p = q;
X q = p->next;
X }
X if (q) p->next = q->next;
X }
X }
X}
X
X/*
X * Pop down a dataDpy and all its descendants, freeing storage and
X * reinitializing fields.
X */
Xstatic void pop_down(dataDpy)
XDataDpyRec *dataDpy;
X{
X DataDpyList *p, *q;
X
X XtPopdown(dataDpy->popupshell);
X XtFree(dataDpy->linepos);
X XtFree(dataDpy->buf);
X dataDpy->buf = NULL;
X dataDpy->buflen = 0;
X dataDpy->linepos = NULL;
X dataDpy->state = UNUSED;
X dataDpy->parent = NULL;
X for (p = dataDpy->childlist; p;) {
X pop_down(p->dataDpy);
X q = p;
X p = p->next;
X XtFree(q);
X }
X dataDpy->childlist = NULL;
X}
X
X/*
X * Invoked by a ButtonPress event on the label of a data display to
X * pop down itself and its descendants.
X */
X/* ARGSUSED */
Xstatic void DestroyDataPopup(w, dataDpy, event)
X Widget w;
X DataDpyRec *dataDpy;
X XEvent *event;
X{
X if (!dataDpy->parent)
X DeleteList(&TopParentList, dataDpy);
X else
X DeleteList(&dataDpy->parent->childlist, dataDpy);
X pop_down(dataDpy);
X}
X
X/*
X * Position the data display on the screen to reflect the parent-child
X * relationship.
X */
Xstatic void MovePopup(dataDpy)
XDataDpyRec *dataDpy;
X{
X Arg args[MAXARGS];
X Cardinal n;
X Screen *screen;
X int popupHeight, popupWidth, screenHeight, screenWidth;
X Position x, y;
X Dimension dataDpyWidth, dataDpyHeight, dataDpyBorderWidth,
X labelHeight, labelBorderWidth, width, height, borderWidth;
X DataDpyList *p, *q;
X
X Parent = NULL;
X if (!dataDpy->parent)
X p = TopParentList;
X else
X p = dataDpy->parent->childlist;
X
X /* Look for its previous sibling */
X for (q = p->next; q && q->dataDpy != dataDpy;) {
X p = q;
X q = q->next;
X }
X /* If a sibling exists, place the new popup right next to it */
X if (q) {
X n = 0;
X XtSetArg(args[n], XtNwidth, (XtArgVal) &width); n++;
X XtSetArg(args[n], XtNborderWidth, (XtArgVal) &borderWidth); n++;
X XtGetValues(p->dataDpy->popupshell, args, n);
X XtTranslateCoords(p->dataDpy->popupshell, 0, 0, &x, &y);
X x += width;
X y -= borderWidth;
X }
X else { /* no siblings */
X /* this is the very first popup */
X if (!dataDpy->parent) {
X x = 0;
X y = 0;
X }
X /* place it under its parent */
X else {
X n = 0;
X XtSetArg(args[n], XtNheight, (XtArgVal) &height); n++;
X XtGetValues(dataDpy->parent->popupshell, args, n);
X XtTranslateCoords(dataDpy->parent->popupshell, 30, (Position)height,
X &x, &y);
X }
X }
X
X /* Make sure the popup does not go outside of the screen */
X n = 0;
X XtSetArg(args[n], XtNwidth, (XtArgVal) &dataDpyWidth); n++;
X XtSetArg(args[n], XtNheight, (XtArgVal) &dataDpyHeight); n++;
X XtSetArg(args[n], XtNborderWidth, (XtArgVal) &dataDpyBorderWidth); n++;
X XtGetValues(dataDpy->dataDpyWindow, args, n);
X
X n = 0;
X XtSetArg(args[n], XtNheight, (XtArgVal) &labelHeight); n++;
X XtSetArg(args[n], XtNborderWidth, (XtArgVal) &labelBorderWidth); n++;
X XtGetValues(dataDpy->label, args, n);
X
X popupHeight = dataDpyHeight + labelHeight + 2*labelBorderWidth +
X 2*dataDpyBorderWidth;
X popupWidth = dataDpyWidth;
X
X screen = XtScreen(toplevel);
X screenHeight = XHeightOfScreen(screen);
X screenWidth = XWidthOfScreen(screen);
X
X if (x + popupWidth > screenWidth && y + popupHeight > screenHeight) {
X x = screenWidth - popupWidth;
X y = screenHeight - popupHeight;
X }
X else if (x + popupWidth > screenWidth)
X x = screenWidth - popupWidth;
X else if (y + popupHeight > screenHeight)
X y = screenHeight - popupHeight;
X
X n = 0;
X XtSetArg(args[n], XtNx, x); n++;
X XtSetArg(args[n], XtNy, y); n++;
X XtSetValues(dataDpy->popupshell, args, n);
X}
X
X/*
X * Handler procedure called by parse().
X * The main function to popup a data display.
X */
Xvoid print_handler(output)
Xchar *output;
X{
X DataDpyRec *dataDpy;
X int i, j;
X
X if (!output) return;
X if (!PopupMode) return;
X PopupMode = False;
X XDefineCursor(display, XtWindow(toplevel), watch);
X if (Parent)
X XDefineCursor(display, XtWindow(Parent->dataDpyWindow), watch);
X UpdateMessageWindow("Click the label to pop down the data popup");
X
X /* Searches the table for an unused or empty slot */
X for (i=0; dataDpyTable && dataDpyTable[i] && dataDpyTable[i]->state == USED
X && i < dataDpyTableSize; i++);
X if (i == dataDpyTableSize) { /* Table full */
X dataDpyTableSize += ADD_SIZE;
X dataDpyTable = (DataDpyRec **) XtRealloc (dataDpyTable,
X dataDpyTableSize * sizeof(DataDpyRec *));
X for (j=i; j<dataDpyTableSize; j++)
X dataDpyTable[j] = NULL;
X }
X
X /* Empty slot found, allocate a data structure and initializes some
X of the fields. */
X if (dataDpyTable[i] == NULL) {
X dataDpyTable[i] = (DataDpyRec *) XtMalloc (sizeof(DataDpyRec));
X dataDpyTable[i]->state = EMPTY;
X dataDpyTable[i]->parent = NULL;
X dataDpyTable[i]->childlist = NULL;
X }
X
X dataDpy = dataDpyTable[i];
X dataDpy->id = i; /* not needed */
X dataDpy->buf = XtNewString(output);
X dataDpy->buflen = strlen(output);
X BuildLinePos(dataDpy);
X
X if (dataDpy->state == EMPTY)
X CreateDataPopup(dataDpy, Token.mesg);
X else if (dataDpy->state == UNUSED)
X UpdateDataPopup(dataDpy, Token.mesg);
X
X dataDpy->state = USED; /* mark it used */
X if (dataDpy->parent = Parent)
X AppendList(&Parent->childlist, dataDpy);
X else
X AppendList(&TopParentList, dataDpy);
X
X MovePopup(dataDpy);
X XtPopup(dataDpy->popupshell, XtGrabNone);
X if (dataDpy->parent)
X XUndefineCursor(display, XtWindow(dataDpy->parent->dataDpyWindow));
X XUndefineCursor(display, XtWindow(toplevel));
X}
END_OF_FILE
if test 19682 -ne `wc -c <'datadpy.c'`; then
echo shar: \"'datadpy.c'\" unpacked with wrong size!
fi
# end of 'datadpy.c'
fi
echo shar: End of archive 6 \(of 7\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 7 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 at sun.com / argv at ora.com
Opinions expressed reflect those of the author only.
More information about the Comp.sources.x
mailing list