home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / Samples / CSAPE32.ARJ / SOURCE / CSSRC / MENU.C < prev    next >
C/C++ Source or Header  |  1990-12-19  |  35KB  |  1,316 lines

  1. /*
  2.     menu.c
  3.  
  4.     % menu_Open, menu_Printf, etc.
  5.  
  6.     Menu Generator
  7.  
  8.     C-scape 3.2
  9.     Copyright (c) 1986, 1987, 1988 by Oakland Group, Inc.
  10.     ALL RIGHTS RESERVED.
  11.     written by stephen ng.
  12.     rewritten by Joe DeSantis.
  13.  
  14.     Revision History:
  15.     -----------------
  16.      3/02/86 sng    moved static row, col, and fieldno variables in Printf
  17.                     to the data structure,    so that you can now make
  18.                     more than one menu.
  19.      3/14/86 sng    added new function GetRecordVar to support changed
  20.                     field updating.
  21.      3/22/86 sng    rewrote menu_printf to accept % expansion.
  22.      3/23/86 sng    fixed bug that assumed the textbuf had as many lines
  23.                     as the menu.
  24.      3/27/86 sng    rewrote set_fa to allow % expansion inside fields.
  25.      4/06/86 sng    permitted (per documentation) multiple flushes.
  26.      4/16/86 sng    menu_Printf now
  27.                         1) takes objects of different sizes
  28.                         2) uses new @ syntax instead of & syntax
  29.      4/22/86 sng    rewrote menu_printf as state machine, now does quoting
  30.                     stack, state are both global vars.  added (last week)
  31.                     grid, and support for up, down, left, right fields
  32.      4/24/86 sng    added menu_Merge2Record, address of string no longer
  33.                     passed in menu_Printf.
  34.      4/28/86 sng    added field data manipulators.
  35.      5/05/86 sng    removed "variable" string, because field now holds
  36.                     that information.
  37.      5/19/86 sng    assert now checks to see that closed items aren't used
  38.      5/22/86 sng    added color parsing support.
  39.      5/24/86 sng    added type support.
  40.      6/16/86 sng    menus with no fields in them are now acceptable.
  41.      7/08/86 sng    fixed make_grid NULL pointer assignment bug,
  42.                     put asserts on a single line for easier grep'ing,
  43.                     fixed memory non-deallocation bug in menu_Close.
  44.      7/09/86 sng    #defined out xprint for size.
  45.      7/11/86 sng    made fields xarray.
  46.      7/19/86 sng    removed memory hacks--menu_Printf now returns all
  47.                     insufficient memory conditions via dos errno variable
  48.                     menu_Close checks to see if it's necessary to flush
  49.                     first.
  50.     10/21/86 sng    double %'s weren't handled in percent()--now they are
  51.     10/31/86 jmd    removed ifdefs and externs
  52.     10/31/86 jmd    menu_Open now checks if the text buf opened OK.
  53.     11/06/86 sng    restored some documentation
  54.     12/03/86 jmd    set_color is now a macro
  55.                     documented menu_Printf()
  56.                     fixed '@['
  57.                     made state a local variable.
  58.                     cleaned up state machine.
  59.     12/04/86 jmd    added repeat syntax.
  60.     12/18/86 jmd    Adjusted tb_ commands.
  61.     12/30/86 jmd    Check return value of xa_make()
  62.      1/14/87 jmd    Made default TB size a static variable so that it
  63.                     can be modified at run time if needed.
  64.      1/26/87 jmd    Removed menu width restrictions.
  65.      2/07/87 jmd    Rewrote menu_Printf with new token parser.
  66.                     set_color is now done in line.
  67.      2/08/87 jmd    check length of token_buf as chars are added.
  68.                     process when ever token_buf is full.  This
  69.                     eliminates the 512 char limit.
  70.      2/11/87 jmd    took strlen out of menu_SetTB
  71.      2/21/87 jmd    replaced asserts
  72.      3/19/87 jmd    Increased percent buffer to 511 bytes
  73.      4/20/87 jmd    passed menu to support routines to improve
  74.                     error messages
  75.      4/21/87 jmd    fixed repeat syntax, added @fpd[]
  76.      5/04/87 jmd    Replaced the grid
  77.      5/07/87 jmd    added grid hint, rewrote percent, got rid of strchr
  78.      5/26/87 jmd    split up files.
  79.      6/05/87 jmd    added check for end of string
  80.      7/23/87 jmd    removed quote condition for colors ---> "@c[@]]"
  81.      9/09/87 jmd     added NO_PROTO option
  82.      9/23/87 jmd     added STACK_PLUS option, field_funcs_ptr definition.
  83.     11/15/87 jmd    changed memcpy to memmove
  84.     11/16/87 jmd    put back quote condition for colors ---> "@c[@]]"
  85.      3/06/88 jmd    UNIX symbol is now CS_UNIX
  86.      3/08/88 jmd    Added STRATUS decls, added FANCY_PCT check
  87.      3/28/88 jmd    Changed percent() so that it no longer temporarily
  88.                     alters the format string.
  89.      4/10/88 jmd    Added call to AddFieldToGrid()
  90.                     Removed menu_GetSize function
  91.      4/11/88 jmd    added width to field_Open
  92.      4/12/88 jmd    added @fw[] syntax to menu_Printf()
  93.      4/30/88 jmd    moved buffers into menu structure
  94.      5/09/88 jmd    added WIDE fields
  95.      5/12/88 jmd    fixed test for missing repeat string (p became *p)
  96.      5/19/88 jmd    fixed update of menu->col when using field widths
  97.      6/23/88 jmd    converted generic pointers to VOID*
  98.      6/24/88 jmd    converted to new xarray calls
  99.      8/16/88 jmd    added calls to omalloc, oak_Errno
  100.      8/20/88 jmd    add list of screen objects
  101.                     made set_field() a little nicer
  102.                     added stack_inc macro
  103.      8/21/88 jmd    rewrote, menu_Printf now pulls field args off the
  104.                     stack as commands are received, no more static data
  105.                     field names and object commands now parsed
  106.      9/28/88 jmd    Added @a[] support, fixed menu_Flush (again),
  107.                     removed spiel about menus at top of file (finally)
  108.     10/05/88 jmd    Removed start size
  109.                     Added %G, %i, %hd... %Lf... support to menu_Printf
  110.     11/30/88 jmd    Add o_itoa
  111.     12/02/88 jmd    Fixed %s == "" bug in next_char, 
  112.                     added error handling to menu_Open
  113.     12/20/88 jmd    removed Errno test
  114.  
  115.      2/02/89 jdc    independent bob's shouldn't have their position set
  116.      2/07/89 jmd    fixed Lattice uninitialized variable warning
  117.      3/29/89 jmd    Added CSPRIV modifier
  118.      4/21/89 jmd    added new @fD syntax 
  119.                     (makes data pointers without initializing them)
  120.      4/24/89 jdc    added funcnamea for LNF
  121.      5/29/89 jdc    added varnamea for LNF
  122.      7/02/89 jdc    added frowcount change
  123.      8/10/89 jmd    Field widths can now be 0
  124.      8/20/89 jmd    fixed '\n' in repeat syntax
  125.      8/24/89 jmd    added call to tb_GetCol
  126.  
  127.     11/04/89 jdc    changed toupper & tolower to otoupper & otolower
  128.     11/29/89 jmd    added casts for DG
  129.  
  130.     12/10/89 jmd    added ovarargs, made create data private
  131.     12/11/89 jmd    added @fh, field highlighting command
  132.     12/17/89 jdc    fixed cursor positioning (row in wrap mode)
  133.      1/05/90 jmd    allows negative hichar values
  134.      3/28/90 jmd    ansi-fied
  135.      4/23/90 jmd    added tag to enum
  136.      4/24/90 jmd    added ifdef to menu_P declaration
  137.      5/17/90 jmd    put in a comment to fix de-ansi endif problem
  138.      8/05/90 jmd    added test for sed position when connecting a bob
  139.  
  140.      8/07/90 jdc    fixed @a on '\n' bug, added direct bbc_Attr call
  141.      9/24/90 jmd    added test for tokbuf realloc failure
  142.     10/20/90 bkd    changed ifdef OVARARG to O_VARARG.
  143.     11/01/90 ted    put (void) in arg list of menu_Open.
  144.     12/18/90 jdc    added ja_Ok() in menu_GetField()
  145.  
  146.     Note:    Long doubles (%Lf) are not supported.  #define LONG_DOUBLE
  147.     and recompile this file if you wish to support them.
  148. */
  149.  
  150. #include "menu.h" 
  151. #include "oakarg.h"     /* for variable argument macros */
  152. #include "bordobj.h"      /* we need border stuff for bobs */
  153.  
  154. #include <ctype.h>
  155.  
  156. /***** Static Functions *****/
  157.  
  158. OSTATIC char  *    CSPRIV repeat(menu_type menu, char *tokptr, int *cr_countp);
  159. OSTATIC char  *    CSPRIV menu_TokBufReSize(menu_type menu, int newsize);
  160. OSTATIC char     CSPRIV next_char(menu_type menu, char **p);
  161. OSTATIC void     CSPRIV set_pos(char *buffer, menu_type menu);
  162. OSTATIC field_type CSPRIV set_field(menu_type menu, int dcount, int width, int frow, int fcol);
  163. OSTATIC boolean CSPRIV set_tb(menu_type m, char *end);
  164. OSTATIC char  *    CSPRIV percent(menu_type menu, char *in, char *out);
  165. OSTATIC int     CSPRIV atox(char *string);
  166. OSTATIC char  *    CSPRIV o_itoa(int val, char *out);
  167.  
  168. /***** Private Data types *****/
  169.  
  170. /* menu create struct (used by menu_Printf, destroyed by menu_Flush) */
  171. typedef struct _menucreate {
  172.  
  173.     char         pct_buf[PCT_BUF_LEN + 1];    /* the percent buffer */
  174.     char        *pct;                        /* pointer into the percent buffer */
  175.     ova_list    argp;                        /* argument pointer */
  176.  
  177.     xarray        dptrs;                        /* array of field data pointers */
  178.  
  179. } menu_create;
  180.  
  181. /*** states for menu_Printf() ***/
  182.  
  183. typedef enum _mpstate {
  184.         RESET,
  185.         COMMAND,
  186.         REPEAT,
  187.         REP_QUOTE,
  188.         P_START,
  189.         P_BUILD,
  190.         P_QUOTE,
  191.         A_START,
  192.         A_BUILD,
  193.         A_QUOTE,
  194.         C_START,
  195.         C_BUILD,
  196.         C_QUOTE,
  197.         F_START,
  198.         F_BUILD,
  199.         F_QUOTE,
  200.         F_REPEAT,
  201.         F_REP_QUOTE,
  202.         F_DCOUNT,
  203.         F_NAME,
  204.         F_WIDTH,
  205.         F_HILITE
  206. } mp_state;
  207.  
  208. #define        TOK_BUF_START       80        /* starting size of the token buffer */
  209. #define        TOK_GROW            20        /* size the token buffer grows by */
  210.  
  211. /*    Return TRUE if c is a field command */
  212. #define isfldcmd(c)  ((c) == '[' || (c) == 'p' || (c) == 'd' || (c) == 'o' || \
  213.                       (c) == 'b' || (c) == '{' || (c) == 'w' || (c) == 'h' || (c) == 'D')
  214.  
  215. /**** Constructors *****/
  216.  
  217. menu_type menu_Open(void)
  218. /*
  219.     DESCRIPTION
  220.  
  221.     This function creates a new menu object and returns a handle to it.
  222.     If there is not enough memory to open a new menu object, the
  223.     function returns NULL.
  224.  
  225.     RETURNS
  226.  
  227.     menu_Open returns a handle to the new menu object.  A NULL pointer
  228.     value indicates insufficient memory.
  229. */
  230. {
  231.     menu_type m;
  232.  
  233.     if ((m = (menu_type) omalloc(CSA_MENU, sizeof(struct menu_struct))) == NULL) {
  234.         goto Quit1;
  235.     }
  236.  
  237.     /* build menu token buffer */
  238.     /* get a little extra just in case */
  239.     if ((m->token_buf = (char *) omalloc(CSA_TOKBUF, TOK_BUF_START + 3)) == NULL) {
  240.         goto Quit2;
  241.     }
  242.     m->token_size = TOK_BUF_START;
  243.  
  244.     /* build menu sub-components */
  245.  
  246.     m->colcount = 0;
  247.     m->rowcount = 1;
  248.     m->frowcount = 0;
  249.     m->dirty = FALSE;
  250.  
  251.     m->vheight = -1;
  252.     m->vwidth  = -1;
  253.  
  254.     if ((m->textbuf = tb_Open()) == NULL) {
  255.         goto Quit3;
  256.     }
  257.  
  258.     if ((m->fa = xa_Open(4)) == NULL) {
  259.         goto Quit4;
  260.     }
  261.     if ((m->fgrid = ia_Open(10)) == NULL) {
  262.         goto Quit5;
  263.     }
  264.     m->fieldcount = 0;
  265.  
  266.     if ((m->boba = ia_Open(4)) == NULL) {
  267.         goto Quit6;
  268.     }
  269.     m->bobcount = 0;
  270.  
  271.     m->namelist = NULL;
  272.     m->dptrlist = NULL;
  273.     m->varblock = NULL;
  274.     m->sedptr  = NULL;
  275.  
  276.     m->mp_count = 0;        /* count number of times menu_Printf is called */
  277.     m->row = m->col = 0;
  278.     m->color = DEF_COLOR;
  279.     m->old_color = DEF_COLOR;
  280.  
  281.     m->create = NULL;        /* Created by menu_Printf as needed */
  282.     m->funcnamea = NULL;    /* Created by _sfile_loadmenu as needed */
  283.     m->functypea = NULL;    /* Created by _sfile_loadmenu as needed */
  284.     m->varnamea = NULL;        /* Created by _sfile_loadmenu as needed */
  285.  
  286.     return(m);
  287.  
  288.     /* error handling: Free memory, return NULL */
  289.  
  290. Quit6:
  291.     ia_Close(m->fgrid);
  292. Quit5:
  293.     xa_Close(m->fa);
  294. Quit4:
  295.     tb_Close(m->textbuf);
  296. Quit3:
  297.     ofree(CSA_TOKBUF, (VOID *) m->token_buf);
  298. Quit2:
  299.     ofree(CSA_MENU, (VOID *) m);
  300. Quit1:
  301.     return(NULL);
  302. }
  303.  
  304. #ifdef O_VARARG
  305.     boolean menu_Printf(menu, string ova_alist)
  306.         menu_type menu;
  307.         char *string;
  308.         ova_dcl                /* NO semicolon */
  309. #else
  310.     boolean menu_Printf(menu_type menu, char *string ova_alist)
  311.     /* put this comment here to make the de-ansifier work */
  312.  
  313. #endif
  314. /*
  315.     requires:   a control_string and the right number of arguments (see
  316.                 documentation).
  317.  
  318.     modifies:   menu.
  319.  
  320.     effects:    printf the control string and arguments into the menu.
  321.  
  322.     repeats are allowed in text and in fields but not in @p, @c, or @[.
  323. */
  324. {
  325.     register mp_state state = RESET;
  326.     register int i;
  327.     field_type field;
  328.     bob_type   bob;
  329.     char     c;
  330.     char     *tok, *rpt;
  331.     boolean quit;
  332.     int        size, hgt, wid, cr_count;
  333.     char   *p, *token_buf;
  334.  
  335.     boolean a_ishex;        /* @a info */
  336.  
  337.     boolean skip = FALSE;    /* used for @f parsing */
  338.  
  339.     menu_create *createp;    /* pointer to create data */
  340.  
  341.     VOID   *var;            /* field info */
  342.     field_funcs_ptr funcs;
  343.     int        frow, fcol;
  344.     boolean protect, fdinit;
  345.     int     dcount, fwidth, nameno, fhilite;
  346.  
  347.     /* Create create data if there is none */
  348.     if (menu->create == NULL) {
  349.         if ((menu->create = (VOID *) omalloc(CSA_MCREATE, sizeof(menu_create))) == NULL) {
  350.             return(FALSE);
  351.         }
  352.  
  353.         /* create create xarray */
  354.         if ((((menu_create *)(menu->create))->dptrs = xa_Open(10)) == NULL) {
  355.             ofree(CSA_MCREATE, menu->create);
  356.             return(FALSE);
  357.         }
  358.     }
  359.  
  360.     createp = (menu_create *) menu->create;
  361.     token_buf = menu->token_buf;
  362.  
  363.     menu->mp_count++;        /* count number of times menu_Printf is called */
  364.  
  365.     cs_Assert(menu_Ok(menu), CS_M_P_MENU, 0);
  366.  
  367.     ova_start(createp->argp, string);
  368.  
  369.     /* Initialize buffers. */
  370.     createp->pct_buf[0] = '\0';
  371.     createp->pct = createp->pct_buf;
  372.     tok = token_buf;
  373.     rpt = token_buf;
  374.     c = ' ';
  375.     p = string;
  376.  
  377.     /* watch length of token_buf */
  378.     /* compare against menu->token_size when adding chars to token_buf */
  379.  
  380.       for (quit = FALSE; !quit; ) {
  381.         if (!skip) {    /* skip during field creation sometimes */
  382.             c = next_char(menu, &p);
  383.             /* end of string should only occur in RESET state */
  384.             cs_Assert((state == RESET || c != '\0'), CS_M_P_EOL, menu->mp_count);
  385.         }
  386.  
  387.         switch (state) {
  388.         case RESET:                              /* Start */
  389.             if (c == '@') {
  390.                 *tok = '\0';
  391.                 /* add text to tb */
  392.                 if (!set_tb(menu, tok)) {
  393.                     goto ERROR;
  394.                 }
  395.                 tok = token_buf;
  396.                 state = COMMAND;
  397.             }
  398.             else if (c == '\n') {            /* process newline */
  399.                 *tok = '\0';
  400.                 /* add text to tb */
  401.                 if (!set_tb(menu, tok)) {
  402.                     goto ERROR;
  403.                 }
  404.                 tok = token_buf;
  405.                 menu->row++;
  406.                 menu->col = 0;
  407.                 menu->old_color = menu->color;    /* set correct color */
  408.             }
  409.             else if (c == '\0') {            /* done */
  410.                 *tok = '\0';
  411.                 /* add text to tb */
  412.                 if (!set_tb(menu, tok)) {
  413.                     goto ERROR;
  414.                 }
  415.                 tok = token_buf;
  416.                 quit = TRUE;
  417.             }
  418.             else {                            /* accumulate text */
  419.                 *tok++ = c;
  420.                 /* is token_buf filled? */
  421.                 if (tok - token_buf >= menu->token_size) {
  422.                     /* flush token_buf */
  423.                     *tok = '\0';
  424.                     /* add text to tb */
  425.                     if (!set_tb(menu, tok)) {
  426.                         goto ERROR;
  427.                     }
  428.                     tok = token_buf;
  429.                 }
  430.             }
  431.             break;
  432.  
  433.         case COMMAND:                            /* @ */
  434.             switch (c) {
  435.             case 'p':
  436.                 state = P_START;
  437.                 break;
  438.             case 'f':
  439.                 /* reset field info */
  440.                 bob = NULL;
  441.                 protect = FALSE;
  442.                 dcount = 0;
  443.                 fdinit = TRUE;
  444.                 fwidth = -1;
  445.                 fhilite = -1;
  446.                 nameno = -1;
  447.  
  448.                 /* pull field var and funcs off the stack */
  449.                 /* get variable */
  450.                 var = (VOID *) ova_arg(createp->argp, VOID *);
  451.  
  452.                 /* field functions */
  453.                 funcs = ova_arg(createp->argp, field_funcs_ptr);
  454.  
  455.                 /* remember field row, col */
  456.                 frow = menu->row;
  457.                 fcol = menu->col;
  458.  
  459.                 /* process field commands */
  460.                 state = F_START;
  461.                 break;
  462.             case 'a':
  463.                 a_ishex = FALSE;
  464.                 state = A_START;
  465.                 break;
  466.             case 'c':
  467.                 state = C_START;
  468.                 break;
  469.             case '[':
  470.                 state = REPEAT;             /* @[  repeat command*/
  471.                 break;
  472.             case '@':                           /* @@  @] */
  473.             case ']':
  474.                 *tok++ = c;                     /* add '@' or ']' to tb */
  475.                 if (tok - token_buf >= menu->token_size) {
  476.                     *tok = '\0';
  477.                     /* add text to tb */
  478.                     if (!set_tb(menu, tok)) {
  479.                         goto ERROR;
  480.                     }
  481.                     tok = token_buf;
  482.                 }
  483.                 state = RESET;
  484.                 break;
  485.             default:
  486.                 cs_Assert(FALSE, CS_M_P_AT, menu->mp_count);
  487.                 break;
  488.             }
  489.             break;
  490.  
  491.  
  492.         case REPEAT:                        /* @[... */
  493.             if (c == '@') {
  494.                 state = REP_QUOTE;
  495.             }
  496.             else if (c == ']') {
  497.                 *tok = '\0';          /* process repeat */
  498.                 if ((tok = repeat(menu, token_buf, &cr_count)) == NULL) {
  499.                     goto ERROR;
  500.                 }
  501.                 token_buf = menu->token_buf;     /* reset in case repeat realloced */
  502.  
  503.                 /* if the repeat has newlines in it, don't add it to the tb */
  504.                 if (cr_count > 0) {
  505.                     /* advance menu location by '\n' in repeat */
  506.                     menu->row += cr_count;
  507.                     menu->col = 0;
  508.                 }
  509.                 else {
  510.                     /* add text to tb */
  511.                     if (!set_tb(menu, tok)) {
  512.                         goto ERROR;
  513.                     }
  514.                 }
  515.  
  516.                 tok = token_buf;
  517.                 state = RESET;                   /* reset */
  518.             }
  519.             else {                               /* accumulate repeat arg */
  520.                 *tok++ = c;
  521.                 cs_Assert((tok - token_buf < menu->token_size), CS_M_P_RLEN, menu->mp_count);
  522.             }
  523.             break;
  524.  
  525.         case REP_QUOTE:                           /* throw away quote character */
  526.             *tok++ = c;                        /* add arg character */
  527.             cs_Assert((tok - token_buf < menu->token_size), CS_M_P_RLEN, menu->mp_count);
  528.             state = REPEAT;
  529.             break;
  530.  
  531.         case P_START:                         /* @p */
  532.             cs_Assert(c == '[', CS_M_P_ATP, menu->mp_count);
  533.             state = P_BUILD;                 /* @p[ */
  534.             break;
  535.  
  536.         case P_BUILD:                           /* @p[... get args or ']' */
  537.             if (c == '@') {
  538.                 state = P_QUOTE;
  539.             }
  540.             else if (c == ']') {            /* @p[...] process args */
  541.                 *tok = '\0';
  542.                 tok = token_buf;
  543.                 set_pos(token_buf, menu);
  544.                 menu->old_color = menu->color;    /* set correct color */
  545.                 state = RESET;                /* reset */
  546.             }
  547.             else {                            /* accumulate args */
  548.                 *tok++ = c;
  549.                 cs_Assert((tok - token_buf < menu->token_size), CS_M_P_PLEN, menu->mp_count);
  550.             }
  551.             break;
  552.  
  553.         case P_QUOTE:                           /* throw away quote character */
  554.             *tok++ = c;                        /* add arg character */
  555.             cs_Assert((tok - token_buf < menu->token_size), CS_M_P_PLEN, menu->mp_count);
  556.             state = P_BUILD;
  557.             break;
  558.  
  559.         case A_START:                        /* @a */
  560.             cs_Assert(c == '[', CS_M_P_ATA, menu->mp_count);
  561.             state = A_BUILD;                 /* @a[ */
  562.             break;
  563.  
  564.         case A_BUILD:                        /* @a[ */
  565.             if (c == '@') {                    /* @a[..@ */
  566.                 state = A_QUOTE;
  567.             }
  568.             else if (c == ']') {            /* @a[...] process arg */
  569.                 *tok = '\0';
  570.                 if (a_ishex) {
  571.                     /* Attribute is a hex digit */
  572.                     menu->color = (byte) atox(token_buf);
  573.                 }
  574.                 else {
  575.                     menu->color = (byte) atoi(token_buf);
  576.                 }
  577.                 tok = token_buf;
  578.                 state = RESET;                /* reset */
  579.             }
  580.             else if (c == 'x' || c == 'X') {
  581.                 tok = token_buf;        /* reset token buf; skip x */
  582.                 a_ishex = TRUE;
  583.             }
  584.             else {                            /* accumulate args */
  585.                 *tok++ = c;
  586.                 cs_Assert((tok - token_buf < menu->token_size), CS_M_P_ALEN, menu->mp_count);
  587.             }
  588.             break;
  589.  
  590.         case A_QUOTE:                           /* throw away quote character */
  591.             *tok++ = c;                        /* add arg character */
  592.             cs_Assert((tok - token_buf < menu->token_size), CS_M_P_ALEN, menu->mp_count);
  593.             state = A_BUILD;
  594.             break;
  595.  
  596.         case C_START:                        /* @c */
  597.             cs_Assert(c == '[', CS_M_P_ATC, menu->mp_count);
  598.             state = C_BUILD;                 /* @c[ */
  599.             break;
  600.  
  601.         case C_BUILD:                        /* @c[ */
  602.             if (c == '@') {                    /* @c[..@ */
  603.                 state = C_QUOTE;
  604.             }
  605.             else if (c == ']') {            /* @c[...] process args */
  606.                 menu->color = (byte) *token_buf;    /* set the menu color */
  607.                 tok = token_buf;
  608.                 state = RESET;                /* reset */
  609.             }
  610.             else {                            /* accumulate args */
  611.                 *tok++ = c;
  612.                 cs_Assert((tok - token_buf < menu->token_size), CS_M_P_CLEN, menu->mp_count);
  613.             }
  614.             break;
  615.  
  616.         case C_QUOTE:                           /* throw away quote character */
  617.             *tok++ = c;                        /* add arg character */
  618.             cs_Assert((tok - token_buf < menu->token_size), CS_M_P_CLEN, menu->mp_count);
  619.             state = C_BUILD;
  620.             break;
  621.  
  622.         case F_START:                        /* @f  */
  623.             skip = FALSE;                    /* reset skip */
  624.             switch (c) {
  625.             case '[':
  626.                 state = F_BUILD;            /* @f[ */
  627.                 break;
  628.             case 'p':
  629.                 protect = TRUE;
  630.                 break;
  631.             case 'd':                        /* parse count */
  632.                 state = F_DCOUNT;
  633.                 break;
  634.             case 'D':                        /* parse count (don't init) */
  635.                 fdinit = FALSE;
  636.                 state = F_DCOUNT;
  637.                 break;
  638.             case 'w':                        /* parse width */
  639.                 state = F_WIDTH;
  640.                 break;
  641.             case 'h':                        /* parse highlighting */
  642.                 state = F_HILITE;
  643.                 break;
  644.             case 'b':
  645.                 /* get bob from stack */
  646.                 bob = ova_arg(createp->argp, bob_type);
  647.                 break;
  648.             case 'm':
  649.                 break;
  650.             case '{':
  651.                 state = F_NAME;                /* get name */
  652.                 break;
  653.             default:
  654.                 cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
  655.                 break;
  656.             }
  657.             break;
  658.  
  659.         case F_DCOUNT:                        /* @fd */
  660.             if (isfldcmd(c)) {
  661.                 *tok = '\0';
  662.                 tok = token_buf;
  663.                 dcount = atoi(token_buf);    /* get data count */
  664.                 if (dcount <= 0) {
  665.                     dcount = 1;
  666.                 }
  667.  
  668.                 if (fdinit) {
  669.                     /* pull data pointers off arg lists,
  670.                        store them in the create data xarray for now
  671.                     */
  672.                     for (i = 0; i < dcount; i++) {
  673.                         xa_Put(createp->dptrs, i, ova_arg(createp->argp, VOID *));
  674.                     }
  675.                 }
  676.  
  677.                 /* skip to process new command */
  678.                 skip = TRUE;
  679.                 state = F_START;
  680.             }
  681.             else if (isdigit(c)) {
  682.                 *tok++ = c;                       /* add arg character */
  683.             }
  684.             else {
  685.                 cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
  686.             }
  687.             break;
  688.  
  689.         case F_NAME:                            /* @f{... */
  690.             if (c == '}') {
  691.                 *tok = '\0';
  692.                 tok = token_buf;
  693.                 /* put name into name list */
  694.                 nameno = menu_AddName(menu, tok, menu->fieldcount);
  695.                 state = F_START;
  696.             }
  697.             else {
  698.                 *tok++ = c;                       /* add name character */
  699.                 /* error: name too long */
  700.                 cs_Assert((tok - token_buf < NAME_MAXLEN), CS_M_P_NAME, menu->mp_count);
  701.             }
  702.             break;
  703.  
  704.         case F_WIDTH:                        /* @fw */
  705.             if (isfldcmd(c)) {
  706.                 *tok = '\0';
  707.                 tok = token_buf;
  708.                 fwidth = atoi(token_buf);    /* get field width */
  709.                 if (fwidth < 0) {
  710.                     fwidth = -1;
  711.                 }
  712.                 /* skip to process new command */
  713.                 skip = TRUE;
  714.                 state = F_START;
  715.             }
  716.             else if (isdigit(c)) {
  717.                 *tok++ = c;                       /* add arg character */
  718.             }
  719.             else {
  720.                 cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
  721.             }
  722.             break;
  723.  
  724.         case F_HILITE:                        /* @fh */
  725.             if (isfldcmd(c)) {
  726.                 *tok = '\0';
  727.                 tok = token_buf;
  728.                 fhilite = atoi(token_buf);    /* get hilite char */
  729.                 if (fhilite < 0) {
  730.                     fhilite = -1;
  731.                 }
  732.                 /* skip to process new command */
  733.                 skip = TRUE;
  734.                 state = F_START;
  735.             }
  736.             else if (isdigit(c) || c == '-') {    /* allow negative hichar */
  737.                 *tok++ = c;                       /* add arg character */
  738.             }
  739.             else {
  740.                 cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
  741.             }
  742.             break;
  743.  
  744.         case F_BUILD:                        /* @f(pd)[ */
  745.             if (c == '@') {
  746.                 state = F_QUOTE;
  747.             }
  748.             else if (c == ']') {              /* @f[...] process field */
  749.                 *tok = '\0';
  750.                 tok = token_buf;
  751.  
  752.                 /* put a new field into the menu */
  753.                 if ((field = set_field(menu, dcount, fwidth, frow, fcol)) == NULL) {
  754.                     goto ERROR;
  755.                 }
  756.  
  757.                 /* set the field's var and funcs, etc. */
  758.                 field_SetVar(field, var);
  759.                 field_SetFuncs(field, funcs);
  760.                 field_SetProtected(field, protect);
  761.                 field_SetNameNo(field, nameno);
  762.  
  763.                 if (fhilite >= 0) {
  764.                     field_SetHiChar(field, fhilite);
  765.                 }
  766.  
  767.                 wid = field_GetWidth(field);
  768.                 hgt = 1;
  769.                 if (bob != NULL) {
  770.                     /* add bob to list, attach it to field */
  771.                     /* the list contains the field no the bob is attached to */
  772.                     if (!menu_SetFieldBob(menu, menu->fieldcount - 1, bob)) {
  773.                         goto ERROR;
  774.                     }
  775.                     /* only dependent bobs get their position set,
  776.                         and modify the master menu size */
  777.  
  778.                     if (bob_IsDepend(bob)) {
  779.                         if (menu->sedptr == NULL) {
  780.                             bord_SetPosition(bob, frow, fcol);
  781.                         }
  782.                         else {
  783.                             /* compensate for sed position if it exists */
  784.                             bord_SetPosition(bob, frow + win_GetTopRow(menu->sedptr),
  785.                                                   fcol + win_GetLeftCol(menu->sedptr));
  786.                         }
  787.                         hgt = bord_GetHeight(bob);
  788.                         wid = bord_GetWidth(bob);
  789.                     }
  790.                 }
  791.                 /* keep track of menu size, including bobs */
  792.                 menu->colcount = (menu->col + wid > menu->colcount) ?
  793.                     menu->col + wid :
  794.                     menu->colcount;
  795.  
  796.                 menu->frowcount = (menu->row + hgt > menu->frowcount) ?
  797.                     menu->row + hgt :
  798.                     menu->frowcount;
  799.  
  800.                 menu->col += field_GetWidth(field);
  801.  
  802.                 if (fdinit) {
  803.                     /* initialize the field's data pointers,
  804.                        pull them out of the xarry we stashed them in.
  805.                     */
  806.                     for (i = 0; i < dcount; i++) {
  807.                         field_InitData(field, xa_Get(createp->dptrs, i), i);
  808.                     }
  809.                 }
  810.  
  811.                 state = RESET;            /* reset state machine */
  812.             }
  813.             else {                            /* add char */
  814.                 *tok++ = (c == '#' ? CS_INPUT : c);
  815.                 if ((size = (tok - token_buf)) >= menu->token_size) {
  816.                     if ((token_buf = menu_TokBufReSize(menu, size + TOK_GROW)) == NULL) {
  817.                         goto ERROR;
  818.                     }
  819.                     tok = token_buf + size;
  820.                 }
  821.             }
  822.             break;
  823.  
  824.         case F_QUOTE:                        /* @f[...@ */
  825.             if (c == '[') {                    /* @[  repeat command*/
  826.                 rpt = tok;                    /* initialize repeat */
  827.                 state = F_REPEAT;
  828.             }
  829.             else {                               /* throw away quote character*/
  830.                 *tok++ = c;                    /* add arg character */
  831.                 if ((size = (tok - token_buf)) >= menu->token_size) {
  832.                     if ((token_buf = menu_TokBufReSize(menu, size + TOK_GROW)) == NULL) {
  833.                         goto ERROR;
  834.                     }
  835.                     tok = token_buf + size;
  836.                 }
  837.                 state = F_BUILD;
  838.             }
  839.             break;
  840.  
  841.         case F_REPEAT:                        /* @f[...@[... */
  842.             if (c == '@') {
  843.                 state = F_REP_QUOTE;
  844.             }
  845.             else if (c == ']') {
  846.                 *tok = '\0';
  847.                 if ((tok = repeat(menu, rpt, &cr_count)) == NULL) {
  848.                     goto ERROR;
  849.                 }
  850.                 token_buf = menu->token_buf;     /* reset in case repeat realloced */
  851.                 state = F_BUILD;            /* continue... */
  852.             }
  853.             else  {                            /* accumulate repeat arg */
  854.                 *tok++ = (c == '#' ? CS_INPUT : c);
  855.                 if ((size = (tok - token_buf)) >= menu->token_size) {
  856.                     if ((token_buf = menu_TokBufReSize(menu, size + TOK_GROW)) == NULL) {
  857.                         goto ERROR;
  858.                     }
  859.                     tok = token_buf + size;
  860.                 }
  861.             }
  862.             break;
  863.  
  864.         case F_REP_QUOTE:                    /* throw out quote character */
  865.             *tok++ = c;                        /* add arg character */
  866.             cs_Assert((tok - token_buf < menu->token_size), CS_M_P_FLEN, menu->mp_count);
  867.             state = F_REPEAT;                  /* continue from where we were */
  868.             break;
  869.  
  870.         default:
  871.             cs_Assert(FALSE, CS_M_P_STATE, menu->mp_count);
  872.         }
  873.     }
  874.  
  875.     ova_end(createp->argp);
  876.     return(TRUE);
  877.  
  878. ERROR:
  879.     ova_end(createp->argp);
  880.     return(FALSE);
  881. }
  882.  
  883. /***** menu_Printf support.  these are static, CSPRIV functions *****/
  884.  
  885. static char * CSPRIV repeat(menu_type menu, char *tokptr, int *cr_countp)
  886. /*
  887.     Evaluates a repeat command and stuffs the result into the
  888.     menu's token buffer at the location pointed to by tokptr.
  889.     Resizes the token buffer if necessary.
  890.  
  891.     @[3,xyz] ==> "xyzxyzxyz"
  892.  
  893.     Returns a pointer to the end of the string.
  894.     Note: the token buffer could be different after this call (realloc).
  895. */
  896. {
  897.     int        count, len, offset, i, off;
  898.     char *p;
  899.  
  900.     count = atoi(tokptr);
  901.     for (off = 0; tokptr[off] != ',' && tokptr[off] != '\0'; off++) {
  902.         ;
  903.     }
  904.  
  905.     cs_Assert(tokptr[off] != '\0', CS_M_P_RARG, menu->mp_count);
  906.     off++;
  907.  
  908.     /* compute size of repeat string, and number of '\n's */
  909.     *cr_countp = 0;
  910.     for (p = tokptr + off; *p != '\0'; p++) {
  911.         if (*p == '\n') {
  912.             (*cr_countp)++;
  913.         }
  914.     }
  915.  
  916.     len = p - (tokptr + off);
  917.  
  918.     cs_Assert(len > 0, CS_M_P_RARG, menu->mp_count);
  919.  
  920.     /* check for too big */
  921.     cs_Assert(count >= 0 && (count * len) <= MAX_FIELD_LEN, CS_M_P_RVAL, menu->mp_count);
  922.  
  923.     /* Do we need to resize token buffer? */
  924.     offset = (int) (tokptr - menu->token_buf);
  925.     if ((offset + (count * len)) >= menu->token_size) {
  926.         if (menu_TokBufReSize(menu, (offset + (count * len)) + TOK_GROW) == NULL) {
  927.             return(NULL);
  928.         }
  929.         tokptr = menu->token_buf + offset;
  930.     }
  931.     p = tokptr + off;
  932.  
  933.     if (len == 1) {
  934.         memset((VOID *) tokptr, *p, count);
  935.         tokptr[count] = '\0';
  936.     }
  937.     else {
  938.         memmove((VOID *) tokptr, p, len);
  939.         for (i = count, p = tokptr + len; i > 1; i--, p += len) {
  940.             memmove((VOID *) p, (VOID *) tokptr, len);
  941.         }
  942.         *p = '\0';
  943.     }
  944.  
  945.     /* multiply '\n's by repeat count */
  946.     (*cr_countp) *= count;
  947.  
  948.     return(tokptr + (count * len));
  949. }
  950.  
  951. static char * CSPRIV menu_TokBufReSize(menu_type menu, int newsize)
  952. /*
  953.     reallocates the menu's token buffer to newsize.
  954.     returns a pointer to the new buffer.
  955.     returns NULL if it fails.
  956.     the newsize must be larger than the previous size.
  957. */
  958. {
  959.     /* Check if field is too long (same errno as above) */
  960.     cs_Assert(newsize <= MAX_FIELD_LEN + TOK_GROW, CS_M_P_FLEN, menu->mp_count);
  961.  
  962.     /* increase size */
  963.     menu->token_size = newsize;
  964.  
  965.     /* get a little extra just in case  */
  966.     menu->token_buf = (char *) orealloc(CSA_TOKBUF, menu->token_buf, menu->token_size + 3);
  967.  
  968.     return(menu->token_buf);
  969. }
  970.  
  971. static char CSPRIV next_char(menu_type menu, char **p)
  972. /*
  973.     This routine returns the next format character to be processed.
  974.     If there is a '%' substitution, the substitution is shoved
  975.     into the percent buffer, and then spit out until it is empty.
  976.     'p' is the address of the pointer to format string.
  977. */
  978. {
  979.     char          ret;
  980.     menu_create *createp;    /* pointer to create data */
  981.  
  982.     createp = (menu_create *) menu->create;
  983.  
  984.     while (1) {
  985.         /* Loop until we find a character or a valid percent expansion */
  986.         if (*(createp->pct) != '\0') {
  987.             ret = *((createp->pct)++);
  988.             break;
  989.         }
  990.         else if (**p != '%') {
  991.             ret = *((*p)++);
  992.             break;
  993.         }
  994.         else {
  995.             /* put test char at end of pct_buf */
  996.             createp->pct_buf[PCT_BUF_LEN] = '\0';
  997.             *p = percent(menu, *p, createp->pct = createp->pct_buf);
  998.  
  999.             /* if test char has changed we've overrun pct_buf */
  1000.             cs_Assert(createp->pct_buf[PCT_BUF_LEN] == '\0', CS_M_NC_PCTOVF, menu->mp_count);
  1001.         }
  1002.     }
  1003.  
  1004.     return(ret);
  1005. }
  1006.  
  1007. static void CSPRIV set_pos(char *buffer, menu_type menu)
  1008. /*
  1009.     modifies:   menu.
  1010.  
  1011.     Processes the arguments in buffer and changes the menu state
  1012.     information accordingly.
  1013.  
  1014.     if there is only one argument set the col only.
  1015. */
  1016. {
  1017.     char *p;
  1018.  
  1019.     /* get arguments */
  1020.  
  1021.     /* hold value in col for now */
  1022.     menu->col = atoi(buffer);
  1023.  
  1024.     for (p = buffer; *p != ',' && *p != '\0'; p++) {
  1025.         ;
  1026.     }
  1027.  
  1028.     /* if there are two args then set the row */
  1029.     if (*p != '\0') {
  1030.         menu->row = menu->col;
  1031.         menu->col = atoi(++p);
  1032.     }
  1033. }
  1034.  
  1035. static field_type CSPRIV set_field(menu_type menu, int dcount, int width, int frow, int fcol)
  1036. /*
  1037.     modifies:   fa, row, col.
  1038.  
  1039.     The field spec is in the menu->token_buf.
  1040.  
  1041.     effects:    creates a new field, puts it into the menu.
  1042.                 returns the field
  1043. */
  1044. {
  1045.     field_type field;
  1046.     int        fieldno;
  1047.  
  1048.     cs_Assert(menu_Ok(menu), CS_M_SF_MENU, menu->mp_count);
  1049.  
  1050.     /* create a new field */
  1051.     if ( (field =
  1052.             field_Open(menu->token_buf, width, (dcount <= 0) ? 1 : dcount) ) == NULL) {
  1053.         return(NULL);
  1054.     }
  1055.  
  1056.     fieldno = menu->fieldcount;
  1057.  
  1058.     /* put new field into array */
  1059.     if (!xa_Put(menu->fa, fieldno, (VOID *) field)) {
  1060.         field_Close(field);
  1061.         return(NULL);
  1062.     }
  1063.  
  1064.     /* place the field into the grid */
  1065.     if (!menu_AddFieldToGrid(menu, fieldno, frow, fcol)) {
  1066.         field_Close(field);
  1067.         return(NULL);
  1068.     }
  1069.  
  1070.     menu->fieldcount++;
  1071.  
  1072.     return(field);
  1073. }
  1074.  
  1075. static boolean CSPRIV set_tb(menu_type m, char *end)
  1076. /*
  1077.     effects:    prints menu->token_buf into textbuf.
  1078.  
  1079.     end points to the end of the current token.
  1080.  
  1081.     returns:    TRUE if successful
  1082. */
  1083. {
  1084.     char   *buffer;
  1085.     int        len;
  1086.  
  1087.     /* ignore if nothing there */
  1088.     if ((buffer = m->token_buf) == end) {
  1089.  
  1090.         /* color change on the the '\n' */
  1091.         if (m->color != m->old_color) {
  1092.             return(bbc_Attr(m->textbuf->bbc, (long)m->textbuf->len - 1, m->color, 1));
  1093.         }
  1094.         return(TRUE);
  1095.     }
  1096.  
  1097.     len = (int) (end - buffer);
  1098.  
  1099.     if (!menu_Puts(m, m->row, m->col, buffer, len, m->color, m->old_color)) {
  1100.         return(FALSE);
  1101.     }
  1102.  
  1103.     m->old_color = m->color;
  1104.     m->row = tb_GetRow(m->textbuf);
  1105.     m->col = tb_GetCol(m->textbuf);
  1106.  
  1107.     return(TRUE);
  1108. }
  1109.  
  1110. static char * CSPRIV percent(menu_type menu, char *in, char *out)
  1111. /*
  1112.     effects:    does '%' substitution on the in string; places results into
  1113.                 out string.
  1114.  
  1115.     modifies:    out.
  1116.  
  1117.     returns:    the place in 'in' where conversion finished.
  1118.  
  1119.     note:        It is an error for the % expression to be longer
  1120.                 than 40 characters!
  1121.  
  1122.     %*d  not supported
  1123. */
  1124. {
  1125.     char         *last, fmt[41];
  1126.     int             len;
  1127.     boolean      exit;
  1128.     menu_create *createp;    /* pointer to create data */
  1129.  
  1130.     createp = (menu_create *) menu->create;
  1131.  
  1132.     /* handle %d and %s as special cases */
  1133.     if (*(in + 1) == 'd') {
  1134.         o_itoa(ova_arg(createp->argp, int), out);
  1135.         return(in + 2);
  1136.     }
  1137.     else if (*(in + 1) == 's') {
  1138.         strcpy(out, ova_arg(createp->argp, char *));
  1139.         return(in + 2);
  1140.     }
  1141.  
  1142.     /* find the spec string for sprintf */
  1143.     last = in + 1;
  1144.     exit = FALSE;
  1145.     while(!exit) {
  1146.         switch (*last) {
  1147.         case 'c':
  1148.         case 'd':
  1149.         case 'e':
  1150.         case 'E':
  1151.         case 'f':
  1152.         case 'g':
  1153.         case 'G':
  1154.         case 'i':
  1155.         case 'o':
  1156.         case 's':
  1157.         case 'u':
  1158.         case 'x':
  1159.         case 'X':
  1160.         case '%':
  1161.         case '\0':
  1162.             len = last - in + 1;
  1163.             cs_Assert(len < 40, CS_M_P_CONV, menu->mp_count);
  1164.             memmove((VOID *) fmt, (VOID *) in, len);
  1165.             fmt[len] = '\0';
  1166.             exit = TRUE;
  1167.             break;
  1168.  
  1169.         default:
  1170.             last++;
  1171.             break;
  1172.         }
  1173.     }
  1174.  
  1175.     if ((*(last-1) == 'h') &&
  1176.         (*last == 'd' || *last == 'u' || *last == 'o' ||
  1177.          *last == 'i' || *last == 'x' || *last == 'X')) {
  1178.         sprintf(out, fmt, ova_arg(createp->argp, short));
  1179.     }
  1180.     else if ((*(last-1) == 'l') &&
  1181.         (*last == 'd' || *last == 'u' || *last == 'o' ||
  1182.          *last == 'i' || *last == 'x' || *last == 'X')) {
  1183.         sprintf(out, fmt, ova_arg(createp->argp, long));
  1184.     }
  1185.  
  1186. #ifdef LONG_DOUBLE        /* define this if you want to support long doubles */
  1187.     else if ((*(last-1) == 'L') &&
  1188.              (*last == 'e' || *last == 'E' || *last == 'f' ||
  1189.               *last == 'G' || *last == 'g')) {
  1190.         sprintf(out, fmt, ova_arg(createp->argp, long double));
  1191.     }
  1192. #endif
  1193.  
  1194.     else if (*last == 'e' || *last == 'E' || *last == 'f' ||
  1195.       *last == 'G' || *last == 'g') {
  1196.         sprintf(out, fmt, ova_arg(createp->argp, double));
  1197.     }
  1198.     else if (*last == 'd' || *last == 'i' || *last == 'u' || *last == 'o' ||
  1199.       *last == 'x' || *last == 'X' || *last == 'c') {
  1200.         sprintf(out, fmt, ova_arg(createp->argp, int));
  1201.     }
  1202.     else if (*last == 's') {            /* in */
  1203.         sprintf(out, fmt, ova_arg(createp->argp, char *));
  1204.     }
  1205.     else if (*last == '%') {            /* two %'s */
  1206.         out[0] = '%';
  1207.         out[1] = '\0';
  1208.     }
  1209.     else {
  1210.         cs_Assert(FALSE, CS_M_P_CONV, menu->mp_count);
  1211.     }
  1212.  
  1213.     return(last + 1);
  1214. }
  1215.  
  1216. static int CSPRIV atox(char *string)
  1217. /*
  1218.     Convert a hex number to an integer.
  1219.     Leading spaces and minus signs are NOT allowed.
  1220.     This replaces the call to nasty sscanf.
  1221. */
  1222. {
  1223.     char *p;
  1224.     int val = 0;
  1225.  
  1226.     for (p = string; isxdigit(*p); p++) {
  1227.         val = 16 * val + (isdigit(*p) ? (*p - '0') : (otolower(*p) - 'a' + 10));
  1228.     }
  1229.  
  1230.     return(val);
  1231. }
  1232.  
  1233. static char * CSPRIV o_itoa(int val, char *out)
  1234. /*
  1235.     Convert an integer to a string
  1236.     (replacement for itoa) 
  1237. */
  1238. {
  1239.     int digit, sign = 0;
  1240.     char *p, *q, c;
  1241.  
  1242.     if (val == 0) {
  1243.         out[0] = '0';
  1244.         out[1] = '\0';
  1245.         return(out);
  1246.     }
  1247.  
  1248.     if (val < 0) {
  1249.         val = -val;
  1250.         sign = 1;
  1251.     }
  1252.  
  1253.     for(p = out; val > 0; val = val / 10, p++) {
  1254.         digit = val % 10;
  1255.         *p = (char) (digit + '0');
  1256.     }
  1257.  
  1258.     if (sign) {
  1259.         *p++ = '-';
  1260.     }
  1261.  
  1262.     *p = '\0';
  1263.  
  1264.     /* result is in wrong order, reverse it */
  1265.     for (q = out, p--; q < p; q++, p--) {
  1266.          c = *p;
  1267.         *p = *q;
  1268.         *q =  c;
  1269.     }
  1270.  
  1271.     return(out);
  1272. }
  1273.  
  1274. /****  more menu functions ****/
  1275.  
  1276. void menu_Flush(menu_type menu)
  1277. /*
  1278.     If a menu_create struct has been created, destroy it
  1279.     else do nothing.
  1280. */
  1281. {
  1282.     cs_Assert(menu_Ok(menu), CS_M_F_MENU, 0);
  1283.  
  1284.     if (menu->create != NULL) {
  1285.         xa_Close(((menu_create *)(menu->create))->dptrs);
  1286.         ofree(CSA_MCREATE, (VOID *) menu->create);
  1287.         menu->create = NULL;
  1288.     }
  1289. }
  1290.  
  1291. /**** Mutators *****/
  1292.  
  1293. boolean menu_Ok(menu_type menu)
  1294. /*
  1295.     returns:    TRUE if passed a good menu, FALSE otherwise.
  1296. */
  1297. {
  1298.     if (menu == NULL || menu->textbuf == NULL || menu->fa == NULL ||
  1299.       menu->fgrid == NULL || menu->rowcount < 0 || menu->colcount < 0 ||
  1300.       menu->fieldcount < 0 ) {
  1301.           cs_Assert(menu->rowcount != -555 && menu->colcount != -555 && menu->fieldcount != -555, CS_M_OK_CLS, 0);
  1302.         return(FALSE);
  1303.     }
  1304.     return(TRUE);
  1305. }
  1306.  
  1307. /**** Misc ****/
  1308.  
  1309. field_type menu_GetField(menu_type menu, int fld)
  1310. /*
  1311.     Extracts a field from the field array.
  1312. */
  1313. {
  1314.     return((!ja_Ok(menu->fa)) ? NULL : (field_type) xa_Get(menu->fa, fld));
  1315. }
  1316.