/* * Copyright(c) 1999 Andrew Lister * * All rights reserved * Permission to use, copy, modify and distribute this material for * any purpose and without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies, and that the name of the author not be used in advertising * or publicity pertaining to this material without the specific, * prior written permission of the author. * * THE AUTHOR MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES, EX- * PRESS OR IMPLIED, WITH RESPECT TO THE SOFTWARE, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR ANY PARTICULAR PURPOSE, AND THE WARRANTY AGAINST IN- * FRINGEMENT OF PATENTS OR OTHER INTELLECTUAL PROPERTY RIGHTS. THE * SOFTWARE IS PROVIDED "AS IS", AND IN NO EVENT SHALL THE AUTHOR OR * ANY OF ITS AFFILIATES BE LIABLE FOR ANY DAMAGES, INCLUDING ANY * LOST PROFITS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES RELAT- * ING TO THE SOFTWARE. * * Author: Andrew Lister * * $Id: Input.c,v 1.2 2002/04/18 16:31:29 svdavidson Exp $ */ #include /*#define DEBUG*/ #ifdef HAVE_CONFIG_H #include #endif /* For memmove/bcopy */ #include #include #include "InputP.h" #if XmVersion <= 1001 #include #else #include #endif #if !defined(HAVE_MEMMOVE) && !defined(XBAE_NEED_BCOPY) #define XBAE_NEED_BCOPY #endif #if !defined(XBAE_NEED_BCOPY) || defined(SVR4) || defined(VMS) || defined(__EMX__) #define BCOPY(src, dest, n) memmove((void *)(dest), (void *)(src), (n)) #else #define BCOPY(src, dest, n) bcopy((void *)(src), (void *)(dest), (n)) #endif #define CHAR_ALPHA 'a' /* Alphabetic characters only */ #define CHAR_BOTH 'b' /* Both - either character or digit */ #define CHAR_CHARACTER 'c' /* Any character */ #define CHAR_DIGIT 'd' /* Digits only */ #define CHAR_SOPTION '[' /* Start optional sequence */ #define CHAR_EOPTION ']' /* End optional sequence */ #define CHAR_UPPER 'U' /* Uppercase character */ #define CHAR_LOWER 'L' /* Lowercase character */ #define CHAR_ESCAPE '\\' /* Escape character */ #define CHAR_OR '|' /* separator between optional chars */ #define IS_LITERAL(c) (((c) != CHAR_ALPHA && \ (c) != CHAR_BOTH && \ (c) != CHAR_CHARACTER && \ (c) != CHAR_DIGIT && \ (c) != CHAR_SOPTION && \ (c) != CHAR_EOPTION && \ (c) != CHAR_UPPER && \ (c) != CHAR_LOWER && \ (c) != CHAR_OR) || \ (c) == CHAR_ESCAPE) /* Declaration of Actions */ /* ARGSUSED */ static void ToggleOverwriteACT P((Widget, XEvent *, String *, Cardinal *nparams)); static void validate P((Widget, XtPointer, XtPointer)); /* Declaration of Methods */ static void Initialize P((Widget, Widget, ArgList, Cardinal *)); static Boolean SetValues P((Widget, Widget, Widget, ArgList, Cardinal *)); static void Destroy P((Widget)); /* Utility routines */ static void parsePattern P((XbaeInputWidget, String)); static int match P((XbaeInputWidget, char *, int)); /* Internal callbacks */ static void checkInput P((Widget, XtPointer, XtPointer)); static char defaultTranslations[] = "osfInsert : ToggleOverwrite()\n\ KP_Insert : ToggleOverwrite()"; static XtActionsRec actions[] = { {"ToggleOverwrite", ToggleOverwriteACT}, }; #define offset(field) XtOffsetOf(XbaeInputRec, field) static XtResource resources[] = { { XmNalignment, XmCAlignment, XmRAlignment, sizeof(unsigned char), offset(input.alignment), XmRImmediate, (XtPointer)XmALIGNMENT_BEGINNING }, { XmNautoFill, XmCBoolean, XmRBoolean, sizeof(Boolean), offset(input.auto_fill), XmRImmediate, (XtPointer)False }, { XmNconvertCase, XmCBoolean, XmRBoolean, sizeof(Boolean), offset(input.convert_case), XmRImmediate, (XtPointer)True }, { XmNoverwriteMode, XmCBoolean, XmRBoolean, sizeof(Boolean), offset(input.overwrite_mode), XmRImmediate, (XtPointer)False }, { XmNpattern, XmCString, XmRString, sizeof(String), offset(input.pattern), XmRImmediate, (XtPointer)NULL }, { XmNvalidateCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), offset(input.validate_callback), XmRCallback, (XtPointer)NULL } }; #if XmVersion > 1001 static XmPrimitiveClassExtRec primitiveClassExtRec = { NULL, /* next_extension */ NULLQUARK, /* record_type */ XmPrimitiveClassExtVersion, /* version */ sizeof(XmPrimitiveClassExtRec), /* record_size */ XmInheritBaselineProc, /* widget_baseline */ XmInheritDisplayRectProc, /* widget_display_rect */ (XmWidgetMarginsProc)_XtInherit /* widget_margins */ }; #endif XbaeInputClassRec xbaeInputClassRec = { { /* core_class fields */ (WidgetClass) &xmTextClassRec, /* superclass */ "XbaeInput", /* class_name */ sizeof(XbaeInputRec), /* widget_size */ NULL, /* class_initialize */ NULL, /* class_part_initialize*/ False, /* class_inited */ (XtInitProc)Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actions, /* actions */ XtNumber(actions), /* num_actions */ resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ True, /* compress_motion */ XtExposeCompressMaximal, /* compress_exposure */ True, /* compress_enterleave */ False, /* visible_interest */ (XtWidgetProc)Destroy, /* destroy */ XtInheritResize, /* resize */ XtInheritExpose, /* expose */ (XtSetValuesFunc)SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ XtInheritAcceptFocus, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ XtInheritTranslations, /* tm_table */ XtInheritQueryGeometry, /* query_geometry */ XtInheritDisplayAccelerator, /* display_accelerator */ NULL /* extension */ }, { /* Primitive class */ XmInheritBorderHighlight, /* border_highlight */ XmInheritBorderUnhighlight, /* border_unhighlight */ defaultTranslations, /* translations */ NULL, /* arm_and_activate */ NULL, /* syn resources */ 0, /* num_syn_resources */ #if XmVersion <= 1001 NULL, /* extension */ #else &primitiveClassExtRec, /* extension */ #endif }, { /* Text class */ NULL, /* extension */ }, { /* Input class */ NULL, /* extension */ } }; WidgetClass xbaeInputWidgetClass = (WidgetClass) &xbaeInputClassRec; /* * Action routines */ /*ARGSUSED*/ static void validate(w, cd, cb) Widget w; XtPointer cd; XtPointer cb; { XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *)cb; XmTextVerifyCallbackStruct *vcbs = (XmTextVerifyCallbackStruct *)cb; XbaeInputWidget iw = (XbaeInputWidget)w; XbaeInputValidateCallbackStruct call_data; String pattern = NULL; String value; int ret; if (!iw->input.pattern) return; value = XmTextGetString(w); ret = match(iw, value, (int)True); if (iw->input.auto_fill && iw->input.literal_pending) { /* * Fill in any literals at the end of the string */ int i, j = strlen(value); value = XtRealloc(value, (Cardinal)(strlen(value) + 1 + iw->input.literal_count)); for (i = 0; i < iw->input.literal_count; i++) { if (*iw->input.literal_pending == CHAR_ESCAPE) iw->input.literal_pending++; value[i + j] = *iw->input.literal_pending; iw->input.literal_pending++; } value[i + j] = '\0'; XmTextSetString(w, value); } /* * Check that the string matches exactly with the pattern */ call_data.doit = ret; if (iw->input.validate_callback) { if (iw->input.pattern) pattern = XtNewString(iw->input.pattern); else pattern = NULL; call_data.reason = cbs->reason; call_data.event = cbs->event; call_data.pattern = pattern; call_data.value = value; XtCallCallbackList(w, iw->input.validate_callback, (XtPointer)&call_data); } if (cbs->reason == XmCR_LOSING_FOCUS) vcbs->doit = call_data.doit; if (!call_data.doit) { XBell(XtDisplay(w), 0); XmProcessTraversal(w, XmTRAVERSE_CURRENT); if (cbs->reason == XmCR_ACTIVATE) XmProcessTraversal(w, XmTRAVERSE_CURRENT); else /* XmCR_LOSING_FOCUS */ { /* * Should handle what's required - ringing the bell, preventing * traversal, etc by setting the XmTextVerifyCallbackStruct's * doit flag */ vcbs->doit = call_data.doit; } } else if (iw->input.validate_callback && call_data.value != value) /* changed in callback */ { XtFree(value); value = call_data.value; XmTextSetString(w, value); } if (pattern) XtFree(pattern); if (value) XtFree(value); } /* ARGSUSED */ static void ToggleOverwriteACT(w, event, params, nparams) Widget w; XEvent *event; String *params; Cardinal *nparams; { XbaeInputWidget iw = (XbaeInputWidget)w; #ifdef DEBUG printf ("Toggle\n"); #endif if (iw->input.overwrite_mode) iw->input.overwrite_mode = False; else iw->input.overwrite_mode = True; XtCallActionProc(w, "toggle-overstrike", NULL, NULL, 0); } static void Initialize(request, new, args, num_args) Widget request, new; ArgList args; Cardinal *num_args; { XbaeInputWidget iw = (XbaeInputWidget)new; iw->input.literal_pending = NULL; iw->input.literal_count = 0; iw->input.last_insert = 0; /* * Check for a pattern associated with the text field */ if (iw->input.pattern) { iw->input.pattern = XtNewString(iw->input.pattern); parsePattern(iw, iw->input.pattern); XtAddCallback(new, XmNmodifyVerifyCallback, checkInput, NULL); } else { iw->input.pattern_length = 0; iw->input.pattern = NULL; } #if 0 XtAugmentTranslations(new, XtParseTranslationTable (defaultTranslations)); XtVaSetValues(((XmTextRec *)iw)->text, XmNtranslations, default_text_translations, NULL); #endif /* * By default, overwrite mode is off on the text field. By calling * the text widget's action routine it will be turned on. */ if (iw->input.overwrite_mode == True) XtCallActionProc(new, "toggle-overstrike", NULL, NULL, 0); XtAddCallback(new, XmNlosingFocusCallback, validate, NULL); XtAddCallback(new, XmNactivateCallback, validate, NULL); } static Boolean SetValues(current, request, new, args, num_args) Widget current, request, new; ArgList args; Cardinal *num_args; { XbaeInputWidget c = (XbaeInputWidget)current; XbaeInputWidget n = (XbaeInputWidget)new; #define current(field) (c->input.field) #define new(field) (n->input.field) #define NE(field) (current(field) != new(field)) #define EQ(field) (current(field) == new(field)) if (NE(pattern)) { if (new(pattern) == NULL) XtRemoveCallback(new, XmNmodifyVerifyCallback, checkInput, NULL); else new(pattern) = XtNewString(new(pattern)); if (current(pattern) == NULL) XtAddCallback(new, XmNmodifyVerifyCallback, checkInput, NULL); else XtFree(current(pattern)); parsePattern(n, new(pattern)); } if (NE(overwrite_mode)) XtCallActionProc(new, "toggle-overstrike", NULL, NULL, 0); if (NE(auto_fill)) { /* * Call match to set any pending literals. The string up to the * insertion position is what needs to be checked. */ char *text; XmTextPosition pos = XmTextGetCursorPosition(new); text = XmTextGetString(new); if (text && *text) { *(text + pos) = 0; (void)match(n, text, (int)False); } else parsePattern(n, new(pattern)); XtFree(text); } return False; #undef Current #undef New #undef NE #undef EQ } static void Destroy(w) Widget w; { XbaeInputWidget iw = (XbaeInputWidget)w; if (iw->input.pattern) XtFree(iw->input.pattern); } static void parsePattern(iw, pattern) XbaeInputWidget iw; String pattern; { char *ptr = pattern; Boolean optional = False; iw->input.pattern_length = 0; if (!ptr || !*ptr) return; while (*ptr) { if (*ptr == CHAR_SOPTION) { if (optional) { XtAppWarningMsg( XtWidgetToApplicationContext((Widget) iw), "pattern", "badPattern", "XbaeInput", "XbaeInput: Nested optionals in pattern", (String *) NULL, (Cardinal *) NULL); break; } /* * If there is an optional character, then that character * does not need to be counted as part of the length */ optional = True; } else if (*ptr == CHAR_EOPTION) { if (!optional) { XtAppWarningMsg( XtWidgetToApplicationContext((Widget) iw), "pattern", "badPattern", "XbaeInput", "XbaeInput: Error in pattern", (String *) NULL, (Cardinal *) NULL); break; } optional = False; } else { /* * If the first character is a literal, set literal_pending * to True */ if (iw->text.cursor_position == 0 && iw->input.pattern_length == 0 && IS_LITERAL(*ptr)) { char *orig = ptr; if (*ptr == CHAR_ESCAPE) ptr++; iw->input.literal_pending = ptr; iw->input.literal_count = 1; ptr++; while (*ptr && IS_LITERAL(*ptr)) { if (*ptr == CHAR_ESCAPE) ptr++; iw->input.literal_count++; ptr++; } ptr = orig; } iw->input.pattern_length++; } ptr++; } XtVaSetValues((Widget)iw, XmNmaxLength, iw->input.pattern_length, NULL); } static int match(iw, string, exact) XbaeInputWidget iw; char *string; int exact; { char *tptr, *sptr, *ntptr; char *lastopt = NULL; /* last optional block */ char *lastreq = NULL; /* last required block */ int optional = 0; /*int pend_rew = 0;*/ int equal = 1; int escaped; int set_lastopt = 0; tptr = iw->input.pattern; sptr = string; #ifdef DEBUG printf("checking '%s' against '%s'\n", string ? string : "(null)", iw->input.pattern ? iw->input.pattern : "(null)"); #endif if (!tptr || !sptr || !*tptr || !*sptr) { /* check for leading literals */ if (tptr) { if (IS_LITERAL(*tptr)) { if (*tptr == CHAR_ESCAPE) tptr++; iw->input.literal_pending = tptr++; iw->input.literal_count = 1; while (*tptr && IS_LITERAL(*tptr)) { if (*tptr == CHAR_ESCAPE) tptr++; iw->input.literal_count++; tptr++; } } } /* * No pattern or no string - must match! */ return True; } while (*sptr && *tptr) { escaped = 0; do { ntptr = tptr + 1; /* next tptr */ switch (*tptr) { case CHAR_SOPTION: /* start of optional sequence */ optional = 1; /*if (!lastopt)*/ /* XXX -cg */ /* Trouble is with this removed we don't rewind far enough back should another thing fail further along. Also presents problems for autofill since the position of the filled chars may move! Need either another loop which rewinds or a stack of lastopt pointers which can be popped. Thus the beauty of recursion. */ /*lastopt = sptr;*/ lastopt = NULL; set_lastopt = 1; break; case CHAR_EOPTION: /* start of optional sequence */ optional = 0; if (*(tptr + 1) != CHAR_SOPTION) lastreq = tptr + 1; set_lastopt = 0; break; case CHAR_ALPHA: /* alphabetic */ equal = (int)isalpha(*sptr); if (equal && set_lastopt) lastopt = sptr; set_lastopt = 0; break; case CHAR_BOTH: /* both: alphabetic or digit */ equal = (int)isalnum(*sptr); if (equal && set_lastopt) lastopt = sptr; set_lastopt = 0; break; case CHAR_CHARACTER: /* any character */ equal = True; if (equal && set_lastopt) lastopt = sptr; set_lastopt = 0; break; case CHAR_DIGIT: /* any digit */ equal = (int)isdigit(*sptr); if (equal && set_lastopt) lastopt = sptr; set_lastopt = 0; break; case CHAR_UPPER: if (iw->input.convert_case && islower(*sptr)) *sptr = toupper(*sptr); equal = (int)(isalpha(*sptr) && isupper(*sptr)); if (equal && set_lastopt) lastopt = sptr; set_lastopt = 0; break; case CHAR_LOWER: if (iw->input.convert_case && isupper(*sptr)) *sptr = tolower(*sptr); equal = (int)(isalpha(*sptr) && islower(*sptr)); if (equal && set_lastopt) lastopt = sptr; set_lastopt = 0; break; case CHAR_OR: /* If tptr is | then we already had success on the previous char so skip it and then next. */ tptr = ntptr + 1; equal = 0; continue; case CHAR_ESCAPE: tptr++; ntptr++; escaped = 1; /* And fall through to literal */ default: /* treat as a literal */ equal = (int)(*sptr == *tptr); if (equal && set_lastopt) lastopt = sptr; set_lastopt = 0; break; } } while (!equal && *ntptr == CHAR_OR && (tptr = ntptr + 1)); if (!lastreq && !optional) lastreq = tptr; if (!equal) { #ifdef DEBUG int z, w; char *z1; puts("string failed at:"); for (z1 = string, z = 0; *z1; z1++, z++) { if (z1 == sptr) w = z; putchar(*z1); } putchar('\n'); for (z = 0; z < w; z++) putchar(' '); puts("^"); puts("pattern was at:"); for (z1 = iw->input.pattern, z = 0; *z1; z1++, z++) { if (z1 == tptr) w = z; putchar(*z1); } putchar('\n'); for (z = 0; z < w; z++) putchar(' '); puts("^"); printf("opt %d, lastopt %s\n", optional, lastopt ? lastopt : "(null)"); #endif if (optional) { /* * Discard the optional part and move forward to the * required bit, if there is one - if there isn't one * it is a failure */ while (*tptr && (*tptr != CHAR_EOPTION || *(tptr-1) == CHAR_ESCAPE)) tptr++; if (!*tptr) (void)fprintf(stderr, "Mismatched parentheses\n"); else { tptr++; /* move past the ']' */ #ifdef OLD if (!lastopt) { equal = False; break; } #endif /* rewind if necessary */ if (lastopt) { /*if (pend_rew)*/ { #ifdef DEBUG int z, w, w1; char *z1; puts("Rewinding:"); for (z1 = string, z = 0; *z1; z1++, z++) { if (z1 == sptr) w = z; if (z1 == lastopt) w1 = z; putchar(*z1); } putchar('\n'); for (z = 0; z < w; z++) { if (z == w1) putchar('^'); else putchar(' '); } puts("<"); #endif sptr = lastopt; lastopt = NULL; /*pend_rew = 0;*/ } /* else { #ifdef DEBUG puts("rewind pending..."); #endif pend_rew = 1; } */ } equal = True; /* hope for the best! */ /* if (*tptr == CHAR_SOPTION) lastopt = tptr; else*/ } optional = 0; } /* else if (pend_rew) { #ifdef DEBUG int z, w, w1; char *z1; puts("Rewinding:"); for (z1 = string, z = 0; *z1; z1++, z++) { if (z1 == sptr) w = z; if (z1 == lastopt) w1 = z; putchar(*z1); } putchar('\n'); for (z = 0; z < w; z++) { if (z == w1) putchar('^'); else putchar(' '); } puts("<"); #endif sptr = lastopt; lastopt = NULL; pend_rew = 0; } */ else { /* * Failed in a required part. Here, if there was a * previous optional part, that needs to be thrown away * and the section checked again */ if (lastreq && lastopt) { sptr = lastopt; tptr = lastreq; #ifdef DEBUG #if 0 m = mmatch; s = smatch; #endif #endif lastopt = NULL; } else { equal = False; break; } } continue; } if (escaped || (*tptr != CHAR_EOPTION && *tptr != CHAR_SOPTION)) { #ifdef DEBUG printf("%.*s matches %.*s\n", sptr - string +1, string /*strbuf*/, tptr - iw->input.pattern +1, iw->input.pattern/*matchbuf*/); #if 0 s++; #endif #endif sptr++; } tptr++; #ifdef DEBUG #if 0 m++; #endif #endif } if (optional && *tptr && *tptr == CHAR_EOPTION) /* step over last ']' */ tptr++; /* * Determine whether any (and how many) literals are about to be inserted */ iw->input.literal_count = 0; iw->input.literal_pending = NULL; while (*tptr && IS_LITERAL(*tptr) && equal) { if (*tptr == CHAR_ESCAPE) tptr++; if (iw->input.literal_count == 0) iw->input.literal_pending = tptr; iw->input.literal_count++; tptr++; } /* * Ignore any trailing optional bits */ while (*tptr && *tptr == CHAR_SOPTION) { while (*tptr != CHAR_EOPTION) tptr++; tptr++; } /* XXX Might have to skip literals again here for final valid check, not to set literal_pending/literal_count though, unless we want to fill all trailing literals upon final validation, even if they span an optional bit. */ if (*sptr && !*tptr) { #ifdef DEBUG printf("Couldn't fit all chars into pattern. Sorry about that\n"); #endif equal = False; } if (*tptr && !*sptr && exact) equal = False; #ifdef DEBUG printf("----\n"); #endif return equal; } /*ARGSUSED*/ static void checkInput(w, cd, cb) Widget w; XtPointer cd; XtPointer cb; { XbaeInputWidget iw = (XbaeInputWidget)w; XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *)cb; int i, j, pos; int length; char *text; /* * Ignore events generated by convenience routines */ if (!cbs->event) return; /* * Was it a backspace? */ if (cbs->startPos < cbs->currInsert || cbs->text->length == 0 || !cbs->text->ptr || *cbs->text->ptr == 0) { /* * Allow a backspace or delete to do it's bit. */ return; } /* * This is pretty lame but Motif doesn't seem to call the modifyVerifyCB * if the text is pasted from the clipboard. The logic to insert * characters below should handle it if pasting worked correctly. */ if (cbs->text->length > 1) { cbs->doit = False; return; } /* * Has the length exceeded the pattern? */ if (cbs->text->length + cbs->startPos > iw->input.pattern_length) { cbs->doit = False; return; } /* * Otherwise, something was inserted... */ /* * Construct what the string looks like at the moment (+1 for NUL char) */ text = XmTextGetString(w); pos = cbs->startPos; /* * Copy the characters forward if inserting or at end of string. * Include the NUL terminator in the move. */ if (pos >= (length = strlen(text)) || iw->input.overwrite_mode == False) { /* * The new length will be the current length of the string + 1 for * a NUL + the length of the newly inserted string */ length += 1 + cbs->text->length; text = XtRealloc((void *)text, length); for (j = length - 1; j > pos; j--) text[j] = text[j - 1]; } /* * If the insertion point has changed or we are overwriting chars, * any pending literals may also have changed */ if (iw->input.last_insert != pos || (pos < strlen(text) && iw->input.overwrite_mode == True)) { char *buf = XtNewString(text); buf[pos] = 0; (void)match(iw, buf, (int)False); XtFree(buf); } /* * The string needs to reflect what the string will look like * after the text has been inserted so a bit of mucking about is * needed to get that */ for (i = 0; i < cbs->text->length; i++, pos++) { if (iw->input.auto_fill && iw->input.literal_pending && cbs->text->ptr[i] != *iw->input.literal_pending) { int k; for (k = 0; k < iw->input.literal_count; k++) { /* * If in insert mode or at the end of the string, * extra space needs to be added */ if (!iw->input.overwrite_mode || pos >= strlen(text)) { length++; text = XtRealloc((void *)text, length); for (j = length - 1; j > pos; j--) text[j] = text[j - 1]; } /* * The callback's ptr will always need to be changed though */ cbs->text->length++; cbs->text->ptr = XtRealloc((void *)cbs->text->ptr, cbs->text->length + 1); for (j = cbs->text->length; j > i; j--) cbs->text->ptr[j] = cbs->text->ptr[j - 1]; if (iw->input.overwrite_mode) cbs->endPos++; /* (void)BCOPY(cbs->text->ptr + i, cbs->text->ptr + i + 1, cbs->text->length - i);*/ cbs->text->ptr[i] = *iw->input.literal_pending; text[pos] = *iw->input.literal_pending; pos++; i++; iw->input.literal_pending++; if (*iw->input.literal_pending == CHAR_ESCAPE) iw->input.literal_pending++; } } text[pos] = cbs->text->ptr[i]; if (match(iw, text, (int)False) == 0) { cbs->doit = False; XtFree(text); return; } /* * match() may change the current character so it needs to * be copied back */ cbs->text->ptr[i] = text[pos]; } iw->input.last_insert = pos; XtFree(text); } Widget XbaeCreateInput(parent, name, args, ac) Widget parent; String name; ArgList args; Cardinal ac; { return XtCreateWidget(name, xbaeInputWidgetClass, parent, args, ac); }