brachman@grads.cs.ubc.ca (Barry Brachman) (01/25/89)
Enclosed is an X11R3 program that pops up a window and prompts the user for one or more items. Please consult the README for details. ----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE ----- CUT HERE #! /bin/sh # This is a shar archive. Extract with sh, not csh. # This archive ends with exit, so do not worry about trailing junk. # Created by brachman@grads.cs.ubc.ca on Tue Jan 24 14:01:18 PST 1989 # Contents: # README Makefile Imakefile xprompt.c xprompt.man echo 'Extracting README' sed 's/^X//' > README << '+ END-OF-FILE README' X XXprompt provides a means by which programs can ask the user Xfor one or more responses. I have found it especially useful for Xreducing the size of my twm menus. XInstead of hardwiring alternatives into a menu, a single script is used Xto call xprompt and then invoke the appropriate thing. X XFor example, I have a twm menu item: X X "RLOGIN" !"xprompt.rlogin &" X Xthat invokes a shell script called xprompt.rlogin: X X#! /bin/sh X Xdefaulthost=${HOME}/.xprompt.rlogin Xreply= X Xif [ -r $defaulthost ] Xthen X reply=`cat $defaulthost` Xfi X Xreply=`xprompt -p "Hostname" -r "$reply"` Xif [ $? = 1 ] Xthen X exit 0 Xfi Xecho "$reply" > $defaulthost X Xxhost "$reply" > /dev/null 2>&1 Xxterm -T "rlogin $reply" -e rlogin "$reply" & X Xexit 0 X XXprompt relies on the user to set its geometry to something reasonable. XI set the following resources in my .xresources: X XPrompt*Geometry: 800x30+300+2 X XPrompt*Rlen: 70 X XThis is my first X program and there's a good chance I've made a few major XX faux pas. Suggestions on how to clean it up will be gratefully received. XThere are a few minor problems, perhaps my fault, perhaps the fault Xof the text widget. Horizontal scrolling, for example, doesn't seem Xto work correctly. Still, I've found the program useful and perhaps others Xwill as well. X XThe program has been tested on monochrome Sun 3/50's running SunOS 4.0 and Xhas been compiled using gcc 1.32. It has been written for X11R3. XI can't vouch for the Imakefile. X XOne possible enhancement is to provide optional word and file name completion. X XPlease report bugs, enhancements, suggestions, etc. to me rather than Xposting to the net. X X----- XBarry Brachman | UUCP: {alberta,uw-beaver,uunet}! XDept. of Computer Science| ubc-vision!ubc-csgrads!brachman XUniv. of British Columbia| Internet: brachman@cs.ubc.ca XVancouver, B.C. V6T 1W5 | brachman%ubc.csnet@csnet-relay.arpa X(604) 228-4327 | brachman@ubc.csnet X + END-OF-FILE README chmod 'u=rw,g=r,o=r' 'README' echo ' -rw-r--r-- 1 brachman 1925 Jan 24 14:00 README (as sent)' echo -n ' ' /bin/ls -l README echo 'Extracting Makefile' sed 's/^X//' > Makefile << '+ END-OF-FILE Makefile' X XXLIB=/active/X/usr/lib XXINC=/usr/include/X11 X XCC=gcc XCFLAGS=-I$(XINC) -O X Xxprompt: xprompt.o X $(CC) -o xprompt xprompt.o -L$(XLIB) -lXaw -lXt -lXmu -lX11 -lm X X.c.o: X $(CC) $(CFLAGS) -c $*.c X + END-OF-FILE Makefile chmod 'u=rw,g=r,o=r' 'Makefile' echo ' -rw-r--r-- 1 brachman 192 Jan 24 13:31 Makefile (as sent)' echo -n ' ' /bin/ls -l Makefile echo 'Extracting Imakefile' sed 's/^X//' > Imakefile << '+ END-OF-FILE Imakefile' X X INCLUDES = -I$(TOP) XLOCAL_LIBRARIES = $(XAWLIB) $(XMULIB) $(XTOOLLIB) $(XLIB) X X SRCS = xprompt.c X OBJS = xprompt.o X XComplexProgramTarget(xprompt) + END-OF-FILE Imakefile chmod 'u=rw,g=r,o=r' 'Imakefile' echo ' -rw-r--r-- 1 brachman 174 Jan 16 18:45 Imakefile (as sent)' echo -n ' ' /bin/ls -l Imakefile echo 'Extracting xprompt.c' sed 's/^X//' > xprompt.c << '+ END-OF-FILE xprompt.c' X/* vi: set tabstop=4 : */ X X/* X * xprompt - prompt the user for one or more replies X * X * Written for X11R3 X * 24-Jan-89 bjb X * X * Copyright (C) 1989 Barry Brachman and The University of British Columbia X * X * Permission is given to freely copy and distribute this software provided: X * X * 1) You do not sell it, X * 2) You do not use it for commercial advantage, and X * 3) This notice accompanies the distribution X * X * Barry Brachman | UUCP: {alberta,uw-beaver,uunet}! X * Dept. of Computer Science| ubc-vision!ubc-csgrads!brachman X * Univ. of British Columbia| Internet: brachman@cs.ubc.ca X * Vancouver, B.C. V6T 1W5 | brachman%ubc.csnet@csnet-relay.arpa X * (604) 228-4327 | brachman@ubc.csnet X */ X X#include <stdio.h> X#include <X11/Xatom.h> X#include <X11/IntrinsicP.h> X#include <X11/StringDefs.h> X#include <X11/Box.h> X#include <X11/Shell.h> X#include <X11/AsciiText.h> X#include <X11/TextP.h> X#include <X11/Cardinals.h> X#include <X11/Xutil.h> X Xstatic XrmOptionDescRec table[] = { X {"-rlen", "Rlen", XrmoptionSepArg, NULL}, X {"-ibw", "insideborderWidth", XrmoptionSepArg, NULL}, X {"-grab", "Grab", XrmoptionNoArg, (caddr_t) "on"}, X {"-nograb", "Grab", XrmoptionNoArg, (caddr_t) "off"}, X {"-rfn", "replyFont", XrmoptionSepArg, NULL}, X {"-pfn", "promptFont", XrmoptionSepArg, NULL}, X {"-p", "", XrmoptionSkipLine, NULL}, X}; X Xtypedef struct { X int rlen; /* maximum reply length */ X Boolean grab; /* If TRUE, grab the keyboard */ X char *geometry; X int borderwidth; X int insideborderwidth; X XFontStruct *font; X XFontStruct *pfont; X XFontStruct *rfont; X} app_resourceRec, *app_res; X Xstatic app_resourceRec app_resources; X Xstatic XtResource resources[] = { X{"rlen", "Rlen", XtRInt, sizeof(int), X XtOffset(app_res, rlen), XtRImmediate, (caddr_t) 80}, X{"grab", "Grab", XtRBoolean, sizeof(Boolean), X XtOffset(app_res, grab), XtRImmediate, (caddr_t) TRUE }, X{"insideborderWidth", "insideborderWidth", XtRInt, sizeof(int), X XtOffset(app_res, insideborderwidth), XtRImmediate, (caddr_t) 1}, X{"replyfont", "replyFont", XtRFontStruct, sizeof(XFontStruct *), X XtOffset(app_res, rfont), XtRString, NULL}, X{"promptfont", "promptFont", XtRFontStruct, sizeof(XFontStruct *), X XtOffset(app_res, pfont), XtRString, NULL}, X X{XtNgeometry, "Geometry", XtRString, sizeof(caddr_t), X XtOffset(app_res, geometry), XtRString, (caddr_t) "500x150+500+400"}, X{XtNborderWidth, "borderWidth", XtRInt, sizeof(int), X XtOffset(app_res, borderwidth), XtRImmediate, (caddr_t) 1}, X{XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), X XtOffset(app_res, font), XtRString, (caddr_t) XtDefaultFont}, X}; X Xstatic char xprompt_TextTranslations[] = X"\ XCtrl<Key>C: abort() \n\ XCtrl<Key>D: finish-prompt() \n\ XCtrl<Key>J: next-prompt() \n\ XCtrl<Key>M: next-prompt() \n\ X<Key>Down: next-prompt() \n\ X<Key>Up: previous-prompt() \n\ X<Key>Linefeed: next-prompt() \n\ X<Key>Return: next-prompt() \n\ XCtrl<Key>U: erase-line() \n\ X<Btn1Down>: finish-prompt() \n\ X<Btn2Down>: finish-prompt() \n\ X<Btn3Down>: finish-prompt() \n\ X"; X Xstatic char *promptbuf, *replybuf; Xstatic struct promptargs { X char *prompt; X char *reply; X struct promptargs *next; X struct promptargs *prev; X} *promptargs, *npa, *npa_prev; Xstatic int cpromptarg, npromptargs; Xstatic Widget toplevel, box, popup, reply_w, prompt_w; X Xstatic void changeprompt(), unparsegeometry(); X Xstatic void XEraseLine(ctx, event, args, nargs) XTextWidget ctx; XXEvent event; XString *args; XCardinal *nargs; X{ X X/* XStoreBuffer(XtDisplay(ctx), replybuf, strlen(replybuf), 1); */ X replybuf[0] = '\0'; X XtTextSetLastPos(reply_w, 0); X XtTextSetInsertionPoint(reply_w, 0); X XtTextDisplay(reply_w); X} X Xstatic void XFinishPrompt(w, event, args, nargs) XWidget w; XXEvent event; XString *args; XCardinal *nargs; X{ X int i; X X strcpy(npa->reply, replybuf); X for (i = 0, npa = promptargs; i < npromptargs; npa = npa->next, i++) X printf("%s\n", npa->reply); X XtDestroyWidget(toplevel); X exit(0); X} X Xstatic void XPreviousPrompt(w, event, args, nargs) XWidget w; XXEvent event; XString *args; XCardinal *nargs; X{ X X npa_prev = npa; X npa = npa->prev; X if (--cpromptarg == 0) X cpromptarg = npromptargs; X changeprompt(); X} X Xstatic void XNextPrompt(w, event, args, nargs) XWidget w; XXEvent event; XString *args; XCardinal *nargs; X{ X X npa_prev = npa; X npa = npa->next; X if (++cpromptarg > npromptargs) X cpromptarg = 1; X changeprompt(); X} X Xstatic void Xchangeprompt() X{ X int replylen, promptlen; X X if (npromptargs > 1) X sprintf(promptbuf, "%s[%d/%d]:", npa->prompt, cpromptarg, npromptargs); X else X sprintf(promptbuf, "%s:", npa->prompt); X strcpy(npa_prev->reply, replybuf); X strcpy(replybuf, npa->reply); X X replylen = strlen(replybuf); X promptlen = strlen(promptbuf); X XtTextSetLastPos(reply_w, replylen); X XtTextSetLastPos(prompt_w, promptlen); X X XtTextSetInsertionPoint(reply_w, replylen); X XtTextSetInsertionPoint(prompt_w, promptlen); X X XtTextDisplay(reply_w); X XtTextDisplay(prompt_w); X} X Xstatic void XAbort(w, event, args, nargs) XWidget w; XXEvent event; XString *args; XCardinal *nargs; X{ X X XtDestroyWidget(toplevel); X exit(1); X} X Xstatic XtActionsRec xprompt_actions[] = { X {"erase-line", EraseLine }, X {"next-prompt", NextPrompt }, X {"previous-prompt", PreviousPrompt }, X {"finish-prompt", FinishPrompt }, X {"abort", Abort }, X}; X Xstatic XSyntax(call) Xchar *call; X{ X X fprintf(stderr, "Usage: %s [flags] [xtoolkitargs] -p prompt [-r reply] \ X[-p prompt [-r reply]] ...\n", call); X fprintf(stderr, "where <flags> is one or more of:\n"); Xfprintf(stderr, "-rlen # (Maximum length of user's reply: default 80)\n"); Xfprintf(stderr, "-ibw # (Border width of inside window: default 1)\n"); Xfprintf(stderr, "-grab (Grab keyboard)\n"); Xfprintf(stderr, "-nograb (Don't grab keyboard)\n"); Xfprintf(stderr, "-pfn <font> (Prompt font)\n"); Xfprintf(stderr, "-rfn <font> (Reply font)\n"); X exit(1); X} X Xfoo() X{} X Xvoid unparsegeometry(); X Xvoid Xmain(argc, argv) Xunsigned int argc; Xchar **argv; X{ X register int i, j; X int len, maxpromptlen; X TextWidget ctx; X Arg arg[10]; X int geom_mask, geom_x, geom_y, geom_width, geom_height; X char geom_str[100]; X XFontStruct *font; X int bb_width, bb_height; X X toplevel = XtInitialize( argv[0], "XPrompt", table, XtNumber(table), X &argc, argv); X X XtGetApplicationResources(toplevel, &app_resources, resources, X XtNumber(resources), NULL, 0); X X j = 0; X promptargs = NULL; X maxpromptlen = 0; X for (i = 1; i < argc; i++) { X if (i == argc - 1 || strcmp(argv[i], "-p")) X Syntax(argv[0]); X if (promptargs) { X npa->next = X (struct promptargs *) malloc(sizeof(struct promptargs)); X npa->next->prev = npa; X npa = npa->next; X npa->next = NULL; X } X else { X promptargs = npa = X (struct promptargs *) malloc(sizeof(struct promptargs)); X npa->prev = npa->next = NULL; X } X npa->prompt = argv[++i]; X if ((len = strlen(npa->prompt)) > maxpromptlen) X maxpromptlen = len; X if ((npa->reply = (char *) malloc(app_resources.rlen + 1)) == NULL) { X fprintf(stderr, "Can't alloc reply buffer\n"); X exit(1); X } X npa->reply[0] = '\0'; X if (argv[i+1] && !strcmp(argv[i+1], "-r")) { X if (++i == argc - 1) X Syntax(argv[0]); X if (strlen(argv[++i]) > app_resources.rlen) { X fprintf(stderr, "xprompt: default reply is too long\n"); X exit(1); X } X strcpy(npa->reply, argv[i]); X } X j++; X } X if ((npromptargs = j) == 0) X Syntax(argv[0]); X promptargs->prev = npa; X npa->next = promptargs; X npa = promptargs; X cpromptarg = 1; X replybuf = (char *) malloc(app_resources.rlen + 1); X if (npromptargs > 1) { X maxpromptlen += 5; X if (npromptargs < 10) X maxpromptlen += 2; X else if (npromptargs < 100) X maxpromptlen += 4; X else X maxpromptlen += 10; /* yuk */ X } X else X maxpromptlen += 2; X promptbuf = (char *) malloc(maxpromptlen); X X if (app_resources.pfont == NULL) X app_resources.pfont = app_resources.font; X if (app_resources.rfont == NULL) X app_resources.rfont = app_resources.font; X X XtAddActions( xprompt_actions, XtNumber( xprompt_actions )); X X geom_x = 500; X geom_y = 400; X geom_width = 500; X geom_height = 100; X geom_mask = X XParseGeometry( app_resources.geometry, &geom_x, &geom_y, &geom_width, X &geom_height ); X unparsegeometry( geom_str, geom_mask, geom_width, geom_height, geom_x, geom_y); X XtSetArg( arg[0], XtNgeometry, geom_str ); X XtSetArg( arg[1], XtNborderWidth, app_resources.borderwidth ); X popup = XtCreatePopupShell( "popup", overrideShellWidgetClass, X toplevel, arg, 2 ); X Xbox = XXtCreateManagedWidget( "box", boxWidgetClass, popup, NULL, ZERO ); X X font = app_resources.pfont; X bb_width = font->max_bounds.rbearing - font->min_bounds.lbearing; X bb_height = font->max_bounds.ascent + font->max_bounds.descent; X if (npromptargs > 1) X sprintf(promptbuf, "%s[1/%d]:", promptargs->prompt, npromptargs); X else X sprintf(promptbuf, "%s:", promptargs->prompt); X XtSetArg( arg[0], XtNstring, promptbuf ); X XtSetArg( arg[1], XtNlength, maxpromptlen ); X XtSetArg( arg[2], XtNborderWidth, 0); X XtSetArg( arg[3], XtNfont, app_resources.pfont ); X XtSetArg( arg[4], XtNeditType, XttextRead ); X XtSetArg( arg[5], XtNwidth, bb_width * maxpromptlen ); X XtSetArg( arg[6], XtNsensitive, False ); Xprompt_w = XXtCreateManagedWidget( "text", asciiStringWidgetClass, box, arg, 7 ); X /* X * The following kludge is used because I couldn't find any obvious X * way to turn off the cursor in the prompt window X * I suppose one could define a null cursor and use that... X */ X ctx= (TextWidget)prompt_w; X ctx->text.sink->InsertCursor = foo; X X font = app_resources.rfont; X bb_width = font->max_bounds.rbearing - font->min_bounds.lbearing; X bb_height = font->max_bounds.ascent + font->max_bounds.descent; X strcpy(replybuf, promptargs->reply); X XtSetArg( arg[0], XtNtextOptions, editable | scrollOnOverflow ); X XtSetArg( arg[1], XtNheight, bb_height + 6 ); X XtSetArg( arg[2], XtNstring, replybuf ); X XtSetArg( arg[3], XtNlength, app_resources.rlen ); X XtSetArg( arg[4], XtNeditType, XttextEdit ); X XtSetArg( arg[5], XtNborderWidth, app_resources.insideborderwidth ); X XtSetArg( arg[6], XtNwidth, app_resources.rlen * bb_width ); X XtSetArg( arg[7], XtNinsertPosition, strlen(promptargs->reply) ); X XtSetArg( arg[8], XtNfont, app_resources.rfont ); Xreply_w = XXtCreateManagedWidget( "text", asciiStringWidgetClass, box, arg, 9 ); X XXtOverrideTranslations( reply_w, XtParseTranslationTable( xprompt_TextTranslations ) ); X X XtPopup( popup, XtGrabExclusive ); X XtRealizeWidget( popup ); X if ( app_resources.grab ) { X XGrabKeyboard( XtDisplay (reply_w), XtWindow (reply_w), False, X GrabModeAsync, GrabModeAsync, CurrentTime ); X XSetInputFocus( XtDisplay (reply_w), XtWindow (reply_w), X RevertToPointerRoot, CurrentTime ); X } X XtMainLoop(); X} X Xstatic void Xunparsegeometry(buf, mask, w, h, x, y) Xchar *buf; Xint mask, w, h, x, y; X{ X X sprintf( buf, "%dx%d%c%d%c%d", X w, h, X mask & XNegative ? '-' : '+', x, X mask & YNegative ? '-' : '+', y); X} + END-OF-FILE xprompt.c chmod 'u=rw,g=r,o=r' 'xprompt.c' echo ' -rw-r--r-- 1 brachman 11184 Jan 24 13:58 xprompt.c (as sent)' echo -n ' ' /bin/ls -l xprompt.c echo 'Extracting xprompt.man' sed 's/^X//' > xprompt.man << '+ END-OF-FILE xprompt.man' X.TH XPROMPT 1 "24 January 1989" X.SH NAME Xxprompt \- prompt a user for input X.SH SYNOPSIS X.B xprompt X[ X toolkit options ] [-rlen number] [-ibw number] X.ti +8 X[-grab] [-nograb] [-r reply] [-pfn fontname] X.ti +8 X[-rfn fontname] -p prompt [-r reply] X.ti +8 X[-p prompt [-r reply]]... X.SH DESCRIPTION X.I Xprompt Xpops up a window containing a prompt and an optional default reply. XIf more than one prompt argument is given, the cursor-down key and the return Xkey select the next prompt and the cursor-up key selects the previous Xprompt. XThe program exits normally by typing <ctrl> D or Xpressing any mouse button inside the reply area of the window. XThe replies are printed to stdout, one per line. XMost standard editing characters X(e.g., Delete, Backspace, <ctrl> U, cursor movement) Xare available. X.PP X.I Xprompt Xcan be aborted by typing <ctrl> C. XIn this case, no output is generated and an exit code of X1 is returned by the program. X.PP XThe default is to position the window in the middle of the screen Xand to grab keyboard input so that the cursor need not be positioned Xinside the text area. X.SH OPTIONS XThe following command line flags are recognized: X.TP 8 X.BI \-rlen " number" XSpecifies the maximum length of the reply. XThe default is 80 characters. X.TP 8 X.BI \-ibw " number" XSpecifies the border width for the box bounding the text area. XThe default is 1. X.TP 8 X.B \-nograb XThe cursor must be in the text portion of the window for Xinput to be accepted. X.TP 8 X.B \-grab XThe cursor may be anywhere but Xinput is directed to the text area. X.TP 8 X.BI \-r " reply" XInsert X.I reply Xinto the text area as the default reply. X.TP 8 X.BI \-pfn " fontname" XSpecifies the font to be used for the prompt string. X.TP 8 X.BI \-rfn " fontname" XSpecifies the font to be used for the reply string. X.SH X DEFAULTS XThe standard X toolkit options and resources are accepted. XFor example, the default font can be changed by the standard X.I -fn fontname Xflag or by the resource X.IR XPrompt*Font. XIn addition, the following resources are understood: X.TP 8 X.B "Rlen (\fPclass\fB Integer)" XSpecifies the maximum length of the reply. X.TP 8 X.B "insideborderWidth (\fPclass\fB BorderWidth)" XSpecifies the border width for the box bounding the text area. X.TP 8 X.B "Grab (\fPclass\fB Boolean)" XSpecifies whether keyboard input should be focussed on the text area Xregardless of where the cursor is. X.TP 8 X.B "replyFont (\fPclass\fB Font)" XThe font to use for the reply string, overriding XXPrompt*Font. X.TP 8 X.B "promptFont (\fPclass\fB Font)" XThe font to use for the prompt string, overriding XXPrompt*Font. X.SH NOTE XIf X.I xprompt Xis invoked from a Bourne shell script, Xthe following hack can be used to quickly grab multi-reply output: X.sp 2 X.in +4 Xreply=`xprompt -p "Prompt1" -p "Prompt2" -p "Prompt3"` X.br XIFS=" X.br X" X.br Xset $reply X.br Xecho "First reply is: $1" X.br Xecho "Second reply is: $2" X.br Xecho "Third reply is: $3" X.in -4 X.SH EXIT STATUS XOn normal completion 0 is returned. XIf the program is aborted, 1 is returned. X.SH AUTHOR X.nf XBarry Brachman Xbrachman@cs.ubc.ca XValuable suggestions by Rick Morrison. X.SH BUGS XThe reply is limited to a single line. XThe user is responsible for ensuring that a proper window size Xis chosen. X + END-OF-FILE xprompt.man chmod 'u=rw,g=r,o=r' 'xprompt.man' echo ' -rw-r--r-- 1 brachman 3206 Jan 24 13:50 xprompt.man (as sent)' echo -n ' ' /bin/ls -l xprompt.man exit 0