/* begincopyright
  Copyright (c) 1988 Xerox Corporation. All rights reserved.
  Use and copying of this software and preparation of derivative works based
  upon this software are permitted. Any distribution of this software or
  derivative works must comply with all applicable United States export
  control laws. This software is made available AS IS, and Xerox Corporation
  makes no warranty about the software, its performance or its conformity to
  any specification. Any person obtaining a copy of this software is requested
  to send their name and post office or electronic mail address to
  PCRCoordinator.pa@xerox.com, or to:
    PCR Coordinator
    Xerox PARC
    3333 Coyote Hill Rd.
    Palo Alto, CA 94304
  endcopyright */

/*
 * CommandLine.h
 *
 * PCR command line interface.
 *
 * All procs in this interface are usable from initialization code,
 *   (before allocator and threads have been set up)
 *   EXCEPT the ones that use the XR←Fildes type.
 *
 * Demers, April 15, 1990 11:43:36 am PDT
 */

#ifndef ←XR←COMMAND←LINE←
#define ←XR←COMMAND←LINE← 1

#ifndef ←XR←BASIC←TYPES←
#   include "xr/BasicTypes.h"
#endif

#ifndef ←XR←THREADS←
#   include "xr/Threads.h"
#endif


/*
 * Command Lines
 */

typedef XR←Pointer /* opaque */ XR←CLHandle;
/*
    Handle to a command line.
    To construct one see XR←CLCreateHandleFromXXX below.
*/


extern XR←CLHandle
XR←CLCreateHandleFromArgs(/*
    int argc,
    char **argv
*/);
/*
    Create handle from given args.
    The argv vector and the arg strings it points to are NOT copied,
      and so should be immutable.
*/


extern XR←CLHandle
XR←CLCreateHandleFromCharProc(/*
    XR←MesaProc charProc
        -- closure for int (*charProc)(XR←MesaProc self)
    XR←MesaProc promptProc
        -- closure for void (*promptProc)(char *prompt, 
                int nBytes, XR←MesaProc self)
*/);
/*
    The charProc should behave like a Unix getchar -- it should return
      the char as an int, or (-1) at the end.
    If non-NIL, the promptProc is called to print interactive prompts.
    Implementation is lazy -- the charProc is actually called
      as a result of calling CLProcess on the resulting CLHandle.
*/


extern XR←CLHandle
XR←CLCreateHandleFromFile(/*
    XR←Fildes file;
    XR←Fildes promptFile;
*/);
/*
    Create handle for reading from given file, which must be open.
    The prompt may be (usually is) XR←nullFildes;
*/


extern XR←CLHandle
XR←CLCreateHandleFromString(/*
    char *string;
*/);
/*
    Create handle for reading args from given string.
    The string is not copied, and so should be immutable.
*/



extern int
XR←CLPrependArgsToHandle(/*
    XR←CLHandle h,
    int argc,
    char **argv
*/);
/*
    Prepend args to handle.
    The argv vector is copied.
    The underlying strings are not copied, and so should be immutable.
    Return 0 on success, (-1) on failure.
*/


extern void
XR←CLDestroyHandle(/*
    XR←CLHandle h
*/);
/*
    Destroy the Handle.
    It's okay just to drop it on the floor.
    If the handle was created from a file, this does NOT close the file.
*/



/*
 * Command Line Processing Procs --
 *
 *   registration, invocation, ...
 */

typedef XR←Pointer /* opaque */ XR←CLProcsHandle;
/*
    Handle to collection of registered command line processing procs
      (see below).
*/


typedef struct XR←CLCallEnvRep {
    XR←CLHandle clce←h;
    XR←CLProcsHandle clce←pH;
    XR←MesaProc clce←msgSink;
        /* closure for void (*sink)(char *buf, int nBytes, XR←MesaProc self) */
    int *clce←msgVerbosityP;
    struct XR←JmpBufRep clce←jmpBuf;
    XR←MesaProc clce←handlerProc;
        /* closure for int (*handlerProc)(int prevResult, XR←MesaProc self) */
    XR←Pointer clce←clientData;
} *XR←CLCallEnv;
/*
    Call environment passed to CLProc (below).
    The CLProc can either return or call XR←longjmp(clce←jmpBuf, x).
      If it promises not to do the XR←longjmp, the CLProc is free
      to store into clce←jmpbuf.
    The CLProc may store into clce←handlerProc; the stored handler
      proc will be invoked if this or a subsequent CLProc returns
      an error value.
*/

typedef int (*XR←CLProc)(/*
    XR←CLCallEnv clce,
    int argc,
    char **argv,
    int prevResult,
    XR←MesaProc self
*/);
/*
    "Command Line Proc" -- proc to process a command line argument group.
    The clce value provides command line and procs handles, as well as
      a place to put message output.
    If the result is nonzero, it will be stored with associated
      argument group in command line.  The intent is that 0 means
      "ignored" or "not processed;" positive values are success codes
      and negative values are error codes.
    The prevResult value comes from a previous call to a CLProc; see
      the description of XR←CLApply below.
    Usual result:
      0 means "ignored" or "not processed"
      >0 means success of some sort
      <0 means error of some sort
    but see the description of postProc in XR←CLApply below.
*/


/* Proc header for a CLProc ... */

#define XR←CLPROC(name) \
	int name (clce, argc, argv, prevResult, self) \
	    XR←CLCallEnv clce; \
	    int argc; \
	    char **argv; \
	    int prevResult; \
	    XR←MesaProc self;



/* Message printing macros for CLProcs ...		*/
/*   To use, omit left paren, e.g.			*/
/*   XR←CLXXXMsg "Hello %d %s\n", 17, "world" );	*/

#define XR←CLCEVerbose(clce,which) \
	( *((clce)->clce←msgVerbosityP) >= (which) )

#define XR←CLVerbose(which) \
	XR←CLCEVerbose(clce,(which))

#define XR←CLCEMsg(clce) \
	XR←PPrintF( (clce)->clce←msgSink, 

#define XR←CLMsg	XR←CLCEMsg(clce)

#define XR←CLMsgIf(which) \
	if( XR←CLVerbose(which) ) XR←CLMsg 

 
#define XR←CLPanicMsg	XR←CLMsg 
#define XR←CLErrorMsg	XR←CLMsgIf(XR←VERBOSITY←ERROR)
#define XR←CLQuietMsg	XR←CLMsgIf(XR←VERBOSITY←QUIET)
#define XR←CLWarningMsg	XR←CLMsgIf(XR←VERBOSITY←WARNING)
#define XR←CLNormalMsg	XR←CLMsgIf(XR←VERBOSITY←NORMAL)
#define XR←CLLogMsg	XR←CLMsgIf(XR←VERBOSITY←LOG)
#define XR←CLStatsMsg	XR←CLMsgIf(XR←VERBOSITY←STATS)
#define XR←CLVerboseMsg	XR←CLMsgIf(XR←VERBOSITY←VERBOSE)


/* Indirect call from one CLProc to another */

extern int
XR←CLCallIndirect(/*
    XR←CLCallEnv clce,
    char *key,
    int argc,
    char **argv
*/);
/*
    Invoke CLProc for key on remaining arguments.
    The prevResult argument will be 0.
    This can be simulated with other procs in this interface,
      and is provided as a convenience.
*/

/* simple utilities for creating CLProcs */

typedef bool /* oldVal = */ (*XR←CLSetBoolProc)(/* bool newVal */);

typedef unsigned /* oldVal = */ (*XR←CLSetUnsignedProc)(/* unsigned newVal */);

extern int XR←CLProc←setBool(/* ... */);
/*
    An XR←CLProc that expects self->mp←x to be an XR←CLSetBoolProc.
    Prints "<commandname> set to <on|off> (was <on|off>)" at normal verbosity.
*/


extern int XR←CLProc←setUnsigned(/* ... */);
/*
    An XR←CLProc that expects self->mp←x to be an XR←CLSetUnsignedProc.
    Prints "<commandname> set to <newnum> (was <oldnum>)" at normal verbosity.
*/




/* ProcsHandles -- collections of registered procs */


extern XR←CLProcsHandle
XR←CLCreateProcsHandle(/*
*/);
/*
    Create a CLProcsHandle.
*/


typedef XR←Pointer /* opaque */ XR←CLRegistration;
/*
    Handle to a registered CLProc.
*/


extern XR←CLRegistration
XR←CLRegisterProc(/*
    XR←CLProcsHandle pH,
    char *key,
    bool caseSensitive,
    char *helpMsg,
    XR←MesaProc proc,	-- closure of an XR←CLProc
    bool replace
*/);
/*
    Add a new Registration to the CLProcsHandle.
    Return it, or NIL on failure.
    Multiple procs may not be registered for given key;
      if a registration already exists the call will fail
      unless `replace' is TRUE.
*/


extern int
XR←CLUnsafeRegisterProcs(/*
    XR←CLProcsHandle pH,
    char **description
*/);
/*
    The description points to a NIL-terminated sequence, where each
      sequence element consists of
        (char *) theMesaProc.mp←proc,
        (char *) theMesaProc.mp←x,
        (char *) helpMessage (may be NIL),
        (char *) firstKey,
          ...
        (char *) lastKey,
        NIL
      (thus, the whole thing ends with two consecutive NILs).
    All the procs are registered, with replace == TRUE.
    Return 0 on success, <0 on failure.
    Like the name says, this is <<<unsafe>>>, but a useful creature comfort.
*/


extern int
XR←CLUnregister(/*
    XR←CLProcsHandle pH,
    XR←CLRegistration r
*/);
/*
    Undo a registration.
    Return 0 on success, (-1) on failure.
*/


extern XR←CLRegistration
XR←CLGetRegistrationWithKey(/*
    XR←CLProcsHandle pH,
    char *key
*/);
/*
    Return specified registration, or NIL if none exists.
*/

extern void
XR←CLEnumerateRegistrations(/*
    XR←CLProcsHandle pH,
    XR←MesaProc eachRegistration
        -- closure of bool (*proc)(XR←CLRegistration r, XR←MesaProc self)
*/);
/*
    Enumerate specified registrations.
    The proc should return FALSE to abort enumeration.
    The CLEnumerateRegistrations call holds a monitor; the
      callback proc may not call into this package
      except for CLGetRegistrationDetails. 
*/



extern void
XR←CLGetRegistrationDetails(/*
    XR←CLRegistration r,
    char **key,			(* result *)
    bool *caseSensitive,	(* result *)
    char **helpMsg,		(* result *)
    XR←MesaProc *proc		(* result *)
*/);
/*
    Fill in non-NIL result buffers.
*/


extern XR←MesaProc
XR←CLGetProcForKey(/*
    XR←CLProcsHandle pH,
    char *key
*/);
/*
    Return registered MesaProc, or NIL if none exists.
    This can be simulated with above procs,
      is provided as a convenience.
*/


extern void
XR←CLDestroyProcsHandle(/*
    XR←CLProcsHandle pH
*/);
/*
    Destroy the ProcsHandle.
    It's okay just to drop it on the floor.
*/



/*
 *
 * Command Line Processing ...
 *
 */

extern int
XR←CLApply(/*
    XR←CLCallEnv clce,
    XR←MesaProc preProc,	-- closure for XR←CLProc, may be NIL
    XR←MesaProc postProc,	-- closure for XR←CLProc, may be NIL
*/);
/*
    Process the argument groups in command line clce->clce←h
      using CLProc values registered with clce->clce←pH.
    For each arg group:
    - If preProc is specified, invoke it on each arg group.
      Interpret the result as follows:
        < 0 => abort processing
        0 => call registered CLProc
        >0 => don't call registered CLProc but continue processing
      A missing preProc is equivalent to one that always
      returns 0.
    - Lookup the registered CLProc and invoke it.  Save the result
      it returns to be passed to the postProc, as described below.
      A missing registered CLProc is equivalent to a CLProc that
      does nothing and returns 0.  The CLProc can simulate
      a nonzero return by doing an XR←longjmp to clce->clce←jb.
    - If postProc is specified, invoke it on each selected arg
      group after calling the registered CLProc.  The result
      returned by the CLProc is passed as the prevResult argument
      in this call.  Interpret the result as follows:
        <= 0 => abort processing
        > 0 => continue processing
      A missing postProc is equivalent to one that always
      returns its prevResult argument; thus, it aborts after
      the first arg group whose CLProc fails or is missing.
    Return the result of the last postProc invoked.
*/


#endif