home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Guide / c-cplusplus-interactive-guide.iso / c_ref / csource4 / 283_01 / fafnir.doc < prev    next >
Encoding:
Text File  |  1988-12-17  |  23.2 KB  |  492 lines

  1. /* fafnir.doc -- a general purpose forms engine
  2.                  documentation, history
  3.                  9/10/88, david c. oshel
  4.  
  5.     "Why, Doc?"  The user gets Elysian Fields.  The programmer gets 
  6.                  Forms Nirvana.  Fafnir got the Rheingold (*belch*).  
  7.                  That's why.  Anyone for elevenis? -- Oshel, 9/11/88
  8.                  */
  9.  
  10. /*
  11. ============================================================================
  12.     Changes, 12/18/88:
  13.  
  14.     Added Small Model support (SVIDEO.ASM).  Now supports /AL and /AS.
  15.  
  16.     The Fafnir small model is not especially useful in real life, since
  17.     most C programs which manage "live" databases are written, for practical
  18.     reasons, in large model.  However, the small model console i/o library,
  19.     formerly distributed as CIAO.LIB, is included, and that IS useful!
  20.  
  21.  
  22.  
  23.  
  24.     Changes, 12/11/88:
  25.  
  26.     Eliminated snow from CGA direct screen writes.
  27.  
  28.     Microsoft Systems Journal, November 1988, article by Jeff Prosise,
  29.     detailed an "approved" algorithm for CGA snow elimination, so I did
  30.     a complete rewrite on low-level screen i/o in LIAO.LIB to include it.
  31.     Prosise's method tends toward OS/2 treatment of the screen as a general
  32.     data object (a "handle"), instead of a physical component of specific
  33.     hardware.  Accordingly, I isolated all screen writes/reads in functions
  34.     with an MSJ_ prefix; these are the primitives, and contain NO ptrseg
  35.     macros at all, and NO int86's except for:  ROM-BIOS cursor size and 
  36.     location, and module initialization.  (Other parts of the library still
  37.     use int86's, e.g., to read the system clock or the keyboard status.)
  38.  
  39.     Also, corrected a few minor bugs here and there, esp. in Fafnir.  Added
  40.     a "hook" so sliding text fields can call their help functions if user
  41.     presses Shift-F1, Alt-F1, or Ctrl-F1.  Regular F1 still toggles the
  42.     shadow cursor in 80 column reversed field at the bottom of the screen.
  43.  
  44.     The screenmessage() function no longer disturbs field attributes in the
  45.     receiving region.  Added an automatic flag to the scroll routines so
  46.     the blank line at top or bottom of a scrolling window uses the original
  47.     video attribute instead of being upset by wputs().
  48.  
  49.     I rewrote two of Prosise's MSJ_ functions, and added four of my own
  50.     because some of my LIAO and FAFNIR routines expect a length byte, and
  51.     because Prosise did not include an assembler version of mass moves to 
  52.     and from the screen.  My code is in LVIDEO.ASM.  With monochrome
  53.     adapter, some of these routines are so fast you get a "wagon wheel"
  54.     effect on fields which are constantly refreshed by repeating keystroke.
  55.     The CGA snow elimination is rather slow, but seems acceptable.
  56.  
  57.     I am now using *** L A R G E   M O D E L *** exclusively, since nearly
  58.     all of my development puts heavy stress on the heap, and I am getting
  59.     tired of constantly tweaking code in Small Model to use far pointers.
  60.  
  61.     Bugs remaining:  I have not generalized the low-level I/O sufficiently
  62.     to correct problems caused when the hardware screen is wider than 80 
  63.     chars or more than 25 lines.  Deficiencies do not affect normal BW80
  64.     or CO80, or MA, text modes.  Should always vid_init(3) or vid_init(2),
  65.     which has no effect on MA, in applications where this might be a
  66.     hassle.  Upward mobility toward OS/2 or the like will probably cure
  67.     the problem automatically, since a general screen handle will include
  68.     all these dimensions and force low-level code to deal with them.
  69.  
  70.  
  71.  
  72.  
  73.     Changes, 9/30/88:
  74.  
  75.     Added bomb0( char *msg,... ) routine which calls user's crash function
  76.     Added set_crash_function( PTR_TO_CRASH_FN call_on_crash )
  77.     Added exit_fafnir()
  78.  
  79.  
  80.     Changes, 9/24/88:
  81.  
  82.     Added support for sliding fields.  This turned out to be easier than
  83.     I thought it would be.  Something about clear heads on Saturday a.m.'s?
  84.     Also added some idiot-proofing in the EditField routines, to prevent
  85.     still more nonsense in some really quirk-and-ditty field validaters.
  86.  
  87.     Changes, 9/10/88:
  88.  
  89.     Added "seek to end of text" to the END key routine.  Added title and
  90.     version number to the general info.  Promised that Shift_F1 would always
  91.     give the current version display.  Selected Fafnir and Ciao source
  92.     code, with a stand-alone make file, suitable for distribution.  Put the
  93.     F2 exit key back in; turns out this is a useful signal in some existing
  94.     programs that used earlier versions of the field editor.
  95.  
  96.     Changes, 9/4/88:
  97.  
  98.     Changed the dialogue boxes for shorter, intuitive messages about what
  99.     is meant.  Short, sweet and Anglo-Saxon.
  100.  
  101.     Changes, 9/3/88:
  102.  
  103.     Major changes to formedit.h header file, includes macros for standard
  104.     field types and other elegances and graces to avoid egregious errors 
  105.     and make the job easier and safer.
  106.  
  107.     Changes, 9/2/88:
  108.  
  109.     Added the OldRecord flag to EditForm, OnePageEdit, TwoPageEdit, and
  110.     included opening dialogue if OldRecord.  These functions now return
  111.     functionally named MANIFEST CONSTANTS, instead of booleans or function
  112.     keys; except that EditForm can still return PgDn, PgUp, and End to
  113.     flag particular exit conditions.  All other exits from these functions
  114.     are via user dialogue boxes.
  115.  
  116.     The new return values are: SAVE_FORM, SKIP_FORM, DELETE_FORM, STOP_SEARCH
  117.     EditForm also may return:  PGDN, PGUP, or END
  118.  
  119.  
  120.     Changes, 9/1/88:
  121.  
  122.     Altered the FORM_RULES typedef so that fptr is a char **; this allows
  123.     dynamic field allocation!  Added two functions to take advantage:
  124.  
  125.             void AllocateFields( FORM_RULES form[], int NumFields );
  126.             void ReleaseFields( FORM_RULES form[], int NumFields );
  127.  
  128.     Note that if fields are already allocated, it is not necessary to
  129.     to call either of these.  Allocating and assigning to a char *
  130.     whose address is referred to in the FORM_RULES structure is a
  131.     somewhat slippery idea, but it allows for multiple occurrences of
  132.     the identical object within FORM_RULES, provided the second char **
  133.     is not within range of AllocateFields ... think about it!  We gain
  134.     a major savings in space by trading off a lesser redundancy (the
  135.     cost of a field pointer plus its alias) for a greater (the cost of
  136.     two full-size field allocations, or the cost of special code to
  137.     make the alias good with an explicit memcpy), in the case of 
  138.     virtual fields.  The cost to the programmer, when writing FORM_RULES,
  139.     is simply to add an extra & to the name of the field's char *, plus
  140.     a little extra care in deciding who gets allocated (!).  [Note to
  141.     Oshel, from Oshel:  This will be clear as mud in about a month.
  142.     Please be advised that I knew what I was doing when I wrote this!]
  143.     
  144.     It's a design element not easy to weigh, but it seems to me a more 
  145.     powerful idea than simply allocating and assigning directly to the
  146.     fptr's lvalue within the array of FORM_RULES structures.  
  147.  
  148.     AllocateFields() ensures that we are working with null-terminated
  149.     ASCII strings guaranteed to be the proper length.  (No more debugging
  150.     len in FORM_RULES!)  This is a major simplification, especially
  151.     when moving data to and fro between forms and databases.  (No other
  152.     part of the formedit module makes this assumption; it always uses
  153.     the field width, internally.)  The R:Base program interface, e.g.,
  154.     works explicitly with null-terminated strings (cf. a_att(), etc.).
  155.     
  156.  
  157.  
  158.  
  159.     Changes, 8/27/88:
  160.  
  161.     Defined VNOP, in formedit.h, as the "null function".  Any field
  162.     initializer, character validater or field validater equal to VNOP
  163.     is equivalent to a call to a No-Operation.  That is, the character
  164.     typed is valid (EditField), the field is valid, or the field is
  165.     considered to be initialized.  If a character validater in FORM_RULES
  166.     is VNOP, however, the field is display-only (EditForm).  Use cgood()
  167.     or the like to "validate any" printable ASCII keyboard character.
  168.  
  169.     Defined DISPLAY, same as VNOP, for clarity's sake in FORM_RULES.
  170.  
  171.     EditField will no longer accept F3, etc., as valid keystrokes, even
  172.     if cvalid == VNOP.
  173.  
  174.     InitForm now calls (*field_init)(), as well as EditForm.
  175.  
  176.  
  177.     Services added 8/23/88;
  178.  
  179.     (*field_init)() added to FORM_RULES structure.  This function is only
  180.     called from EditForm.  The field validater, (*fvalid)(), should assume
  181.     responsibility for "de-initializing" an initialized field.  I added
  182.     this to provide a way to left-justify a number in an otherwise
  183.     right-justified field, for data entry convenience with free-form edit
  184.     masks.  Most commonly, this pointer indicates the noop() function.
  185.     
  186.     (*cvalid)( ch, pos ) now includes the relative field position of the
  187.     data entry cursor.  Allows an otherwise numeric fixed mask to suddenly
  188.     switch to alphanumeric, etc.  For example, a phone number field in which
  189.     the last four digits may be either uppercase alpha or digits; or else,
  190.     a "cryptic" field in which the user's keystroke is stored in a hidden
  191.     buffer, but the character returned to the screen is always "*".
  192.  
  193.     These final enhancements make it possible to define an UNLIMITED
  194.     number of DATA TYPES for any particular field.  That's elegance!
  195.  
  196. -----------------------------------------------------------------------------
  197.  
  198.     Functions added 8/20/88, d.c.oshel:
  199.  
  200. -----------------------------------------------------------------------------
  201.  
  202. void InitForm( FORM_RULES Form[], int NumFields );
  203.  
  204.     InitForm copies up to len bytes from each field element's default
  205.     mask (dflt) to its actual field array (fptr), after first clearing
  206.     the receiving field array to len blanks.
  207.  
  208.     Does NOT copy dflt's null terminator if the default character array
  209.     (source) happens to be smaller than fptr (destination).
  210.  
  211. -----------------------------------------------------------------------------
  212.  
  213. int OnePageForm( char *ScreenFileName, FORM_RULES Page[], int NumFields );
  214.  
  215. int TwoPageForm( char *ScreenFileName1, FORM_RULES Page1[], int NumFields1,
  216.                  char *ScreenFileName2, FORM_RULES Page2[], int NumFields2,
  217.                  FALSE);
  218.  
  219.  
  220.     These two functions are complete screen managers for one- and two-page
  221.     forms.  They return either 0 or 1, where 0=Abort, 1=Save.  Much simpler
  222.     and friendlier, they include exit dialogue with the data entry operator.
  223.  
  224.     ScreenFileName is a pointer to the name of a 4000-byte screen image 
  225.     file created using ES.EXE, or a similar program.  The functions load
  226.     the screen file from disk (fairly fast).  There should be one screen
  227.     image file per page of the form being edited.  The screen image should
  228.     include instructions to the data entry operator to press Esc when
  229.     done, how to use F1, PgDn, PgUp, etc.
  230.  
  231.     If ScreenFileName is NULL, the screen is not altered before editing.
  232.  
  233.     EXAMPLE, editing a 2-page form; c2upr, fgood are pointers to a field's
  234.     character-by-character and whole-field input validation functions,
  235.     VNOP indicates no field initialization is required, or a display-only
  236.     field in the cvalid position:
  237.   
  238.     static FORM_RULES checkout [6] = {
  239.             field       df   x  y len  init  char   field   
  240.         { "....v....1", "", 30,10, 10, VNOP, VNOP,  fgood }, <-- element 0, Page 1
  241.         { "....v....1", "", 30,11, 10, VNOP, c2upr, fgood }, <--    "    1
  242.         { "....v....1", "", 30,12, 10, VNOP, c2upr, fgood }, <--    "    2
  243.         { "....v....1", "", 30,13, 10, VNOP, VNOP,  fgood }, <-- element 3, Page 2
  244.         { "....v....1", "", 30,14, 10, VNOP, VNOP,  fgood }, <--    "    4
  245.         { "....v....1", "", 30,15, 10, VNOP, c2upr, fgood }, <--    "    5
  246.     };
  247.  
  248.  
  249.     result = TwoPageForm( "PAGE1.SCR", &checkout[0], 3,
  250.                           "PAGE2.SCR", &checkout[3], 3, TRUE );
  251.  
  252.     if ( result ) save_the_form();
  253.  
  254. ============================================================================
  255.  
  256. **==========================================================================
  257. **
  258. ** Edit Form routine, 8/16/88; simplifies forms handling.
  259. **
  260. **     Multi-page forms are easily managed by providing the address
  261. **     of the next FORM_RULES page, with number of fields on the page.
  262. **
  263. **     When the MultiPage flag is set, EditForm exits on the last field
  264. **     of the page, returning PgDn (or End, special exit).
  265. **
  266. **     Returns an unsigned integer value, representing a key, as follows: 
  267. **
  268. **              ESC           normal exit, done editing
  269. **              F2            abort form editing (caller does this!)
  270. **              PGDN, PGUP    next page, previous page
  271. **              END           special, cursor did not stop in any field 
  272. **
  273. **     Esc, F2, PgUp, PgDn terminate BOTH field and form editing.
  274. **     Caller should assume that F2 is CANCEL FORM, Esc is NORMAL EXIT,
  275. **     and PgUp, PgDn are like Esc unless caller is managing a multi-page 
  276. **     form as several calls to EditForm using different ranges of the
  277. **     appropriate FORM_RULES array.
  278. **
  279. **
  280. **==========================================================================
  281.  
  282. **  :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  283. **  :How to prepare DISPLAY or COMPUTED fields for use by EditForm:
  284. **  :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  285. **
  286. **         If cvalid is equal to VNOP (i.e., to the null function),
  287. **      the field is treated as an INFORMATION ONLY display field,
  288. **      i.e., it is shown on the screen, but cannot be edited.
  289. **
  290. **      However, (*finit)() is always called on all fields, so
  291. **      an appropriate field initializer function may be used to 
  292. **      calculate a COMPUTED FIELD value for it.
  293. **
  294. **      Note:  In EditField, cvalid == VNOP means any key is good!
  295. **             In EditForm,  cvalid == VNOP means display-only field!
  296. **
  297. **      For example, FORM_RULES might contain a field element such as
  298. **
  299. **                { fptr, dflt, xloc, yloc, len, VNOP, VNOP, fcalculate },
  300. **
  301. **      where cvalid == VNOP flags the field as display-only, and fcalculate
  302. **      sets fptr to some arbitrary character value, perhaps based on 
  303. **      the contents of other fields (!).  Note that the fcalculate() 
  304. **      function can take advantage of whatever knowledge the programmer 
  305. **      might have about the form's purpose or environment.  Functions
  306. **      which "validate" a field by altering the field contents return
  307. **      a non-zero value; but when coupled with the VNOP flag, the return
  308. **      value is immaterial -- if altered, fptr is presumed correct!
  309. **
  310. **      This,
  311. **                { fptr, dflt, xloc, yloc, len, VNOP, VNOP, VNOP },
  312. **
  313. **      on the other hand, is simply an INFORMATION-ONLY field, since
  314. **      fvalid == VNOP presumes that the field contents are always valid.
  315. **
  316. **      Then again, 
  317. **                { fptr, dflt, xloc, yloc, len, VNOP, ctst, validater },
  318. **
  319. **      MAY be a computed field even if ctst() validates the user's
  320. **      character input; e.g., if user presses F1, validater() may present
  321. **      a menu of choices and SET its field arg to user's selection, using
  322. **      memcpy() -- and memcpy() is PREFERRED! -- or strcpy().
  323. **
  324. **      Note that "free-form" masks are possible.  These relax the character
  325. **      position requirements of the field, and expect the field validater
  326. **      to carry the burden of formatting input.  The cnzip() function,
  327. **      below, interprets a field of blanks as a signed numeric field, for 
  328. **      example.  (There are many interpretations!  That's why you need the
  329. **      field validater.)
  330. **
  331. **      In other words, the combination of (*cvalid) and (*fvalid) can be
  332. **      used in many different effective combinations to assist the user's
  333. **      data entry task.  This method allows for an unlimited number of
  334. **      field DATA TYPES -- alphanumeric, masked, computed, menu-selected,
  335. **      virtual, display-only, password, special-purpose, etc.
  336.  
  337.  
  338. **==========================================================================
  339. **
  340. ** Edit Field, with length and character-&-field-validation parameters
  341. **
  342. ** The field being edited is NOT NECESSARILY a null-terminated string!
  343. **        caller must ensure that the field has been allocated and that
  344. **        it contains at least len number of ASCII chars which form
  345. **        a self-masking string.  For example, if the validation function
  346. **        is "is_numeric" (a function -- NOT a macro! -- which tests its
  347. **      argument for digits):
  348. **
  349. **            Freely-formed Numeric:  "999999"              ( any length )
  350. **            Phone Number:           "(515) 555-1212"      ( 14 chars )
  351. **            Social Security Number: "123-45-6789"         ( 11 chars )
  352. **            Ten-digit Zipcode:      "00000-0000"          ( 10 chars )
  353. **            Date (cf_date(adr,2)):  "05/02/88"            (  8 chars )
  354. **            Miscellaneous:          "Product # 0.00.000"  ( 18 chars, only...)
  355. **                                               ^ ^^ ^^^   (  6 can be edited )
  356. **
  357. ** [ WARNING ]  EditField does NOT null-terminate any field, so the 
  358. **              len argument is critical to preserve buffer integrity.
  359. **
  360. ** The field is both a source and a receiving field -- therefore, it
  361. **        automatically provides a default or "carry forward" from the 
  362. **        previous data, provided caller has not reset or cleared the field 
  363. **        between calls.  (Note that "Miscellaneous", above, includes 67%
  364. **      extraneous text in a DATA field; it is only given for illustration.)
  365. **
  366. ** May only type over the self-masking field string, can't delete chars
  367. **
  368. ** Skips over (forward or backward) any edit character not "valid",
  369. **        on ->, <-, BS, or valid keypress
  370. **        if char in mask is not valid, char cannot be edited, but is
  371. **        included verbatim in the receiving field
  372. **
  373. ** Calls (*cvalid)() function to determine if user's keypress is ok; this
  374. **        function accepts an unsigned int (the keystroke), and returns 0 if 
  375. **        the char does not meet caller's criteria; ** MUST ** return the
  376. **        character itself, or another non-zero value, if it does.  THE
  377. **        VALUE RETURNED WILL BE STORED IN THE FIELD BEING EDITED; this
  378. **        technique allows caller to convert to uppercase, perform table
  379. **        lookup, etc., but the simplest and easiest case is simply to
  380. **        return the valid character "as is".
  381. **
  382. **      If cvalid == VNOP, any printable ASCII character is valid.
  383. **
  384. **      Note:    (*cvalid)( ch, pos ) sends two arguments to the character
  385. **               validation routine:  the character to be checked, and its
  386. **               position in the field, relative to 0.  This allows for
  387. **               CONSIDERABLE fine-tuning in character validation.  The
  388. **               actual function which receives these arguments need not
  389. **               use both of them, courtesy of C calling conventions.
  390. **
  391. **      Caution: "self-masking" means that the default string being
  392. **               edited must contain AT LEAST ONE editable character,
  393. **               or the cursor will not stop in the field.  For example,
  394. **               a (*cvalid)() function which accepts only digits 0..9
  395. **               will cause the field to seem to be "ignored" if the
  396. **               edit string contains only blanks and no digits!
  397. **
  398. ** Calls (*fvalid)() function to verify that the entire field is correct
  399. **        according to caller's criteria.  For example, this function might
  400. **        check to ensure that numeric values fall within a permissible
  401. **        range.  Must simply return 0 (for no good), or non-zero (for good).
  402. **
  403. **      if (fvalid == VNOP) the field is always valid.
  404. **
  405. **        (*fvalid) sends the fldptr, x, y, len to the function, as arguments.
  406. **        Caller need not use the arguments, but should declare them.
  407. **
  408. **        If (*fvalid) fails, EditField() returns 0, field unchanged.
  409. **
  410. **      Note:    An (*fvalid)() routine which SETS the field being
  411. **               "validated" should modify its first argument and return
  412. **               a non-zero value (COMPUTED FIELDS).  Direct modifications
  413. **               to a field by name may be overwritten (!) otherwise.
  414. **
  415. **               To be perfectly safe, the field validater should use
  416. **               memcpy(), with the length parameter, and not strcpy(),
  417. **               to modify its field buffer argument.  With due attention
  418. **               to detail, especially field length, either method works.
  419. **
  420. **
  421. ** F1, Request Assistance:
  422. **      F1 embeds ' ' in the FIRST POSITION of edit string.  Field validate
  423. **      must detect this flag and take appropriate action.  Designed to
  424. **      be a Request For Assistance signal.  Assumes exit char is CR.
  425. **      Note that if (*fvalid)() returns 0 in this case, the field is not
  426. **      altered.  Allows either fixup or information-only message display.
  427. **
  428. ** If there is a dynamic memory error, EditField will call bomb(), a
  429. **      global system error handler which should exit softly but
  430. **      unconditionally from the program.
  431. **
  432. ** EditField RETURN VALUES:
  433. **      returns 0, End, or value of user's key which terminated the edit;
  434. **
  435. **           Esc, Enter, Up, Down, PgUp, PgDn, F2
  436. **
  437. ** Original field IS NOT modified when field validation fails; in this
  438. **      case, EditField returns 
  439. **
  440. **           0  [9/10/88, d.c.oshel, DOES NOT RETURN!  Continues!]
  441. **
  442. ** Original field IS NOT modified if cursor fails to stop in the field
  443. **      because of a self-mask error; EditField returns
  444. **
  445. **           End
  446. **
  447. ** Original field IS NOT modified (despite changes!) when the user 
  448. **        terminates a field edit with one of:
  449. **
  450. **           Up, Down, PgUp, PgDn, F2
  451. **
  452. ** Original field IS modified, however, if (*fvalid)() is ok and user 
  453. **      terminates the edit with one of:
  454. **
  455. **           Esc, Enter
  456. **
  457. ** These user keys do NOT exit from the field, except as noted:
  458. **
  459. **           Left, Right - behave like Up and Enter at extremities of field
  460. **           Home, End   - move to left or right extremity of field
  461. **
  462. ** Sample call:
  463. **
  464. **   |  memcpy( buffer.ssn, "000-00-0000", 11 );
  465. **   |  EditField( buffer.ssn, 10, 13, 11, FALSE, numeric, goodfield );
  466. **
  467. **        The buffer.ssn contains an 11-char edit mask, "000-00-0000", for a
  468. **        social security number.  The field to be edited is located at
  469. **        screen column 10, row 13, and contains a TOTAL of 11 characters
  470. **      including the hyphens (not normally edited, see (*numeric)).
  471. **
  472. **        The skip flag is FALSE, so user must press an explicit exit key 
  473. **        to terminate the edit, such as Esc, Up Arrow, CR, etc.  (Note that
  474. **      the skip flag is NOT contained in FORM_RULES structures!)
  475. **
  476. **        The (*numeric)() function will test for numeric input, and only allow
  477. **        those characters in the field which are (*numeric)(ch) to be edited.  
  478. **      Note that the validation function MUST be a function, and CANNOT be 
  479. **        a macro!
  480. **
  481. **        The field validation pointer points to goodfield(), a function whose
  482. **      name suggests that it always returns a non-zero value (hint!).
  483. **
  484. **        Once the receiving field is initialized, it need not be initialized
  485. **        again.  The next edit on the same field can use the previous value
  486. **      of the field as its edit mask.  This allows for "carry forward" in
  487. **      long sequences of redundant data entry, such as many addresses with
  488. **      the same zip code, etc.
  489. **
  490. **==========================================================================
  491. */
  492.