/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Building, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989, 1990, 1991, 1992  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  $Revision: 2.13 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/


/*======================================================================
      args.c
      Command line argument parsing functions 

  ====*/

#include "headers.h"

#if defined(ANSI) && !defined(AIX)
static char *parg(char **, char *, ...);
static void  args_help();
#else
static void args_help();
static char *parg();
#endif


/*----------------------------------------------------------------------
  Parse the command line args and set thing in the pine structure
  
   Args: pine_state  -- The pine_state structure to put results in
         argv        -- The argument list to parse
   
 Result: commands arguments parsed
         possible printing of help for command line
         various flags in pine state set
         returns the string name of the first folder to open
 ----*/

char *
pine_args(pine_state, argv)
     struct pine  *pine_state;
     char        **argv;
{
    char *a_err, *folder, *a0, *sort;
    int   can_suspend_local, use_fkeys_local, do_help, a0_len, 
          local_debug,restricted, do_conf, go_to_index, nr_mode,
          start_entry;

    folder            = NULL;
    can_suspend_local = 0;
    use_fkeys_local   = 0;
    local_debug       = 0;
    restricted        = 0;
    do_conf           = 0;
    go_to_index       = 0;
    start_entry       = 0;
    nr_mode           = 0;
    sort              = NULL;
    
    a0 = argv[0]; /* Save it from parg which rearranges argv */

    a_err = parg(argv,
                 "z0d d1d f1s k0d h0d r0d conf0d i0d n1d sort1s nr0d",
		 &can_suspend_local,
		 &local_debug,
		 &folder,
		 &use_fkeys_local,
		 &do_help,
                 &restricted,
                 &do_conf,
                 &go_to_index,
		 &start_entry,
                 &sort,
                 &nr_mode
		 );

    if(a_err != NULL) {
	fprintf(stderr, "%s\n", a_err);
	args_help();
	exit(-1);
    }

    if(sort && decode_sort(pine_state, sort) == -1) {
        fprintf(stderr, "Sort type \"%s\" is invalid\n", sort);
        exit(-1);
    }

#define PINEF_LEN 5
    a0_len = strlen(a0);
    if(a0_len >= PINEF_LEN && strcmp(a0+ a0_len - PINEF_LEN, "pinef") == 0){
	pine_state->use_fkeys = 1;
    }

    if(do_help) 
      args_help(); 
    if(do_conf)
      dump_global_conf();

    pine_state->use_fkeys       |= use_fkeys_local;
    pine_state->can_suspend      = can_suspend_local;
    pine_state->show_folders_dir = 0;
    pine_state->restricted       = restricted;
    pine_state->start_in_index   = go_to_index;
    pine_state->start_entry      = start_entry;
    pine_state->nr_mode          = nr_mode;

#ifdef DEBUG
    debug = local_debug;
#endif
    
    return(folder);
}

/*----------------------------------------------------------------------
   
 ----*/

decode_sort(ps, sort_spec)
     struct pine *ps;
     char        *sort_spec;
{
    char *sep;
    int    x, reverse;

    reverse = 0;
    if((sep = strindex(sort_spec, '/')) != NULL) {
        *sep = '\0';
        sep++;
        if(struncmp(sep, "reverse", strlen(sep)) == 0)
          reverse = 1;
        else
          return(-1);
    }

    if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0) {
        ps->reverse_sort = 1; 
        return(0);
    }
     
    if(*sort_spec == '\0') {
        ps->reverse_sort = 1;
        return(0);
    }

    for(x = 0; ps_global->sort_types[x] != EndofList; x++)
      if(struncmp(sort_name(ps_global->sort_types[x]),
                  sort_spec, strlen(sort_spec)) == 0)
        break;

    if(ps_global->sort_types[x] == EndofList)
      return(-1);

    ps->reverse_sort = reverse;
    ps->sort_order   = ps_global->sort_types[x];
    return(0);
}
      



/*----------------------------------------------------------------------
    print a few lines of help for command line arguments

  Args:  none

 Result: prints help messages
  ----------------------------------------------------------------------*/
static void
args_help()
{
	/**  print out possible starting arguments... **/

	printf("\nPossible Starting Arguments for Pine program:\n\n");
	printf("\tArgument\t\tMeaning\n");
        printf("\t <addrs>...\tGo directly into composer sending to given address\n");
	printf("\t -d n\t\tDebug - set debug level to 'n'\n");
	printf("\t -f <folder>\tFolder - give folder name to open\n");
	printf("\t -h \t\tHelp - give this list of options\n");
	printf("\t -k \t\tKeys - Force use of function keys\n");
	printf("\t -z \t\tSuspend - allow use of ^Z suspension\n");
        printf("\t -r \t\tRestricted - can only send mail to one self\n");
        printf("\t -sort <sort> \tSort - Specify sort order of folder:\n");
        printf("\t\t\t        subject, arrival, date, from, size, /reverse\n");
        printf("\t -i\t\tIndex - Go directly to index, bypassing mail menu\n");
	printf("\t -n <number>\tEntry in index to begin on\n");
        printf("\t -conf\t\tConfiguration - Print out blank global configuration\n");
        printf("\t -nr\t\tNews, R/O - Special mode for readonly news\n");
	printf("\n");
	printf("\n");
	exit(1);
}





/*======================================================================
   parg, a library for parsing command line arguments

   Args: argv  -- The command line arguments to parse
         cs    -- The control string (slighty like scanf)
         ...   -- Address of buffers to put result of parse in

 Result: Various arguments are parsed, including converted to ints
         and such and unmatched arguments left in agrv.
  ====*/

#if !defined(ANSI) || defined(AIX)  /* AIX not ANSI for varaiable # of args */
#include <varargs.h>
#endif

#ifdef UNSIGNED_ARGS
 /* If this is defined arguments to the program that are unsigned may
    be parsed and converted. It is left out, because it is not commonly
    needed and it takes up some space.
 */
#endif

#ifdef BIT32
  /* If the CPU has 32bits turn this on, otherwise an arguments over 32767
     will be considered overflow.
   */
#endif

typedef int       bool;

#ifndef TRUE
#define TRUE      1
#define FALSE     0
#endif
   
#define Tbogus    0    /* Codings for argument types */
#define Tstring   1
#define Tint      2
#define Toccurred 0x20
#ifdef X_UNSIGNED_ARGS /* Don't need unsigned arguments here */
#define Tunsigned 3 
#endif      

struct flag {
    unsigned char  num_e, /* Number of arguments expected */
                   num_g; /* Number of arguments got */
    char           type;  /* See flags above */
    char           *name; /* Points to string naming the flag */
    union where {
        int        *ints;
        char      **strings;
#ifdef UNSIGNED_ARGS
        unsigned   *uns;
#endif
    } where;
};

/* Compute radix based on prefix of string- 0ddd: Octal, 0xdddd: Hex, ddd:Dec */
#define RADIX(foo)      (*(foo) != '0' || !auto_radix ? 10 :\
                         ((*((foo)+1)) | 0x20) == 'x'   ? 16 : 8 )
            
#define used err_pg  /* used is just some unique pointer */

static char err_pg[] = "Programmer goof - ";
static char err_wn[] = "Not enough arguments after \"- \"";
static char err_id[] = "Invalid digits in numeric argument ";
static char err_of[] = "Number too big ";
static char *stoerrors[] = {NULL,err_id,err_of,NULL,err_id};

static char *
#if defined(ANSI) && !defined(AIX)
/* ANSI variable number of arguments */
parg(char **argv_a, char *cs_a, ...)

#else
/* Old UNIX style variable number of arguments */
parg(va_alist)
    va_dcl
#endif

{
    register struct flag *f,*flags,*df_flag;
    register char        *q,*s,*e;
    char                 *p,*csp, **argv, *cs;
    bool                 case_ignore, auto_radix;
    int                  n,num_flags;
    register char        **r,**t,**av;
    struct flag          *f2,*fmax;
    va_list              va_ap;


#if defined(ANSI) && !defined(AIX)
    va_start(va_ap, cs);
    argv = argv_a;
    cs   = cs_a;
#else    
    va_start(va_ap);
    argv = va_arg(va_ap, char **);
    cs   = va_arg(va_ap, char *);
#endif
            
    e = NULL;
    df_flag = NULL;

    /*-------------- Parse the control string ---------------*/
    auto_radix = case_ignore = FALSE;
    for(p = cs; ; p++)           
        switch(*p){
           case '*':  case_ignore = TRUE; /*Ignore case of flag name letters */
                      break;

           case '#':  auto_radix = TRUE;  /* Decide radix on string prefix */
                      break;

           default:   goto dykstra;                    
        }                                              

  dykstra:            
    cs = p;
    /*----------- Count the flags so we know how much to malloc ----------*/
    /*            .. and how many argument to expect                      */
    for(num_flags = 0; *p ;num_flags++ ){                
        while(*p && isspace(*p))
            p++;
        while(*p && !isspace(*p))
            p++;
    }
    flags = (struct flag *)fs_get(sizeof(struct flag) * (num_flags + 1));
    csp = fs_get(p - cs + 2);
    strcpy(csp, cs);
    cs = csp;

 
    /*---------- Now really parse the control string -----------*/
    p = cs;
    while(*p && isspace(*p))
       p++;
    for(fmax = flags; *p; fmax++){
        f = fmax;	
        /*------------- Flag name ----------------*/
        if(isdigit(*p)){
            e = fs_get(70); 
            sprintf(e,"\"%.5s\" %sdigits can't be used in flag names",p,err_pg);
            goto end_it_all;
        }
        s = p;
        for(p++; !isdigit(*p) && *p && !isspace(*p); p++);
        if((p - s) > 1){
            /*-------- Insert new flags so longest names occur first -----*/
            for(f = flags; f < fmax; f++) /* Find spot for it */
                if(p - s > strlen(f->name))
                    break;
            for(f2 = fmax - 1; f2 >= f; f2--) /* Shift other down */
                *(f2 + 1) = *f2;
        }
        f->name = s;         
        cs = p; /* Remember end of name */

        /*---------- Number of args ---------------*/
        if( stoi(p,5,&n,10,&p) < 0 || n > 255){
            e = fs_get(50);
            sprintf(e,"\"%.5s\" %sbogus number",f->name,err_pg);
            goto end_it_all;
        }
        f->num_e = (unsigned char)n;
        f->num_g = 0;
 
        /*--------- Type ------------*/
        switch(*p){
            case 's':   f->type          = Tstring;
                        f->where.strings = va_arg(va_ap, char **);
                        break;
            case 'd':   f->type       = Tint;
                        f->where.ints = va_arg(va_ap, int *); 
                        break;
#ifdef X_UNSIGNED_ARGS
            case 'u':   f->type      = Tunsigned;
                        f->where.uns = va_arg(va_ap, unsigned *)
                        break;
#endif
            default:    e = fs_get(50);
                        sprintf(e, "\"%.5s\" %sinvalid type", f->name, err_pg);
                        goto end_it_all;
        }
        *cs = '\0'; /* Terminate the name (and squash number) */
        if(!strcmp("-",f->name))
            df_flag = f;
        if((Tint & f->type) && f->num_e <=0) /* Initialize a switch */
           *(f->where.ints) = FALSE;
        while(*++p && isspace(*p));
    }
    fmax->name = NULL;
    va_end(va_ap);
                                      

    /*--------------- Now go through argv -----------------*/
    f = df_flag; /* Set f for the default flag */
    if(df_flag == NULL)
        *argv = used;
    for(av = argv+1; *av != NULL; ++av){
        p = *av;
        if(*p == '-' && *(p+1)!='\0' && !isdigit(*(p + 1))){
            /*----- We have a flag. Which one? ---------------*/
            if(df_flag == NULL)
                *av = used;	
            for(p++; *p != '\0'; ) {
                for(f = flags; f->name != NULL; f++){
                    for(s = p, q = f->name; *q && *s; q++, s++){
                        if((isupper(*s) ? tolower(*s) :*s) !=
                           (isupper(*q) ? tolower(*q) :*q )|| 
                         (!case_ignore && *s != *q))
                            break;
                    }
                    if( *q == '\0' )
                        break;
                }
                if( f->name == NULL ){
                    e = fs_get(strlen(p) + 20);
                    sprintf(e,"\"-%s\" Invalid flag",p);
                    goto end_it_all;
                }
                p += strlen(f->name);
                f->type |= Toccurred;
                /*---------- Does it take args? ------*/
                if(f->num_e){
                    if(f->num_e-f->num_g <= 0){/* No more args for this one */
                        f = df_flag;
                    } else {
                         goto get_args; /* takes args, go look for 'em */
                    }
                } else {
                    if(f->type & Tint)
                      *(f->where.ints) = TRUE;
                    f = df_flag;		/* it's a switch */
                }
            }
        }             

        /*----------- Here we deal with arguments to flags -------*/
      get_args:
        if(!*p)
            continue;
        if(df_flag == NULL)
            *av = p;
        if(f == NULL || f->num_e - f->num_g <= 0)
            if(df_flag != NULL){
                e = fs_get(60+strlen(p));
                sprintf(e,"\"%s\": must follow a flag that takes arguments",p);
                goto end_it_all;
            } else
                continue;                                             
 
        if(df_flag == NULL)
            *av = used; 
  
        q = NULL;
        switch(f->type & ~(Toccurred)){
            case Tstring:   if(f->num_e > 1)
                              f->where.strings[f->num_g] = p;
                            else
                              *(f->where.strings) = p;
                            break;

            case Tint:      n = stoi(p + (RADIX(p) == 16 ? 2 : 0),
                                     20,
                                     (f->num_e > 1 ?
                                        &(f->where.ints[f->num_g]) :
                                          f->where.ints),
                                     RADIX(p),NULL);
                            if(n != 0)
                              q = stoerrors[n+3];
                            break;
#ifdef X_UNSIGNED_ARGS
            case Tunsigned: n = stou(p + (RADIX(p) == 16 ? 2 : 0),
                                     20,
                                     (f->num_e > 1 ?
                                        &(f->where.uns[f->num_g]) :
                                          f->where.uns),
                                     RADIX(p),NULL);
                            if(n != 0)
                              q = stoerrors[n+3];
                            break;
#endif
        }                                         
        if(q != NULL){ /* Must be an sto error */
            e = fs_get(strlen(q) + strlen(p) + 10); 
            sprintf(e,"%s \"%s\"",q,p);
            goto end_it_all;
        }
        f->num_g++;

    }            

    /*------------ Shift things around in argv ------------*/
    if(df_flag == NULL){
        for(r = t = argv; *r != NULL; *t++ = *r++){
            while(*r == used)
                r++;
            if(*r == NULL)
                break;
        }
        *t = *r; /* Copy the NULL */
    }

    /*------------ Check out the arguments we got -----------*/
    for(f = flags; f->name != NULL; f++)
        if(f->type & Toccurred){
            /* If any args expected at least one must have been given */
            if(f->num_e > 0 && f->num_g <= 0){
                e = fs_get(strlen(f->name) + 40);
                sprintf(e,"No arguments given after \"-%s\"",f->name);
                goto end_it_all;
            }
        } else if(f->num_e > 0 && f->num_g > 0 && f->type & Tstring){
            f->where.strings[f->num_g] = NULL;
        } 
            
   end_it_all:
    f2 = flags; /* flags is a register var, can't take & of it for fs_give */
    fs_give((void **)&f2);
    fs_give((void **)&csp);
    return(e);
}
                      

#ifdef X_PARG_DEBUG
/*  For parg debugging if we ever need it  */
dumpflags(f)
struct flag f[];                                                
{                                                              
    while(f->name != NULL){
       fprintf(stderr,"\"%6s\"%d e:%d g:%d where:%d f:%x\n",
               f->name, strlen(f->name), f->num_e, f->num_g,f->where.int,
               f->type);
        f++;
    }
}
#endif


/*----------------------------------------------------------------------
      Convert a string to an integer with overflow and other checking

  Args: s_start - string to convert
        len     - len of input string
        num     - pointer to int to return value in
        base    - The base or radix of the number expressed in base 10
        rp      - Pointer so point in the string where parsing stopped can
                    can be returned.

           1 conversion stopped on first bogus char 
  Returns: 0 Happy normal completion
          -1
          -2 string passed in is bogus no conversion
          -3 string passed in is emtpy
 

  Who could ask for more in a string to interger conversion fucntion? 
 ---*/
 
stoi(s_start,len,num,base,rp)
     register char *s_start;
     char **rp;                
     long *num;
     register int base;
     int len;
{                        
    register char *s = s_start;
    register long n = 0,v,neg = 1,ov,n2;

    int rv = -3;
    if(s == NULL)
        return(-3);
    while(isspace(*s) && s - s_start < len) 
        s++;
    if(*s == '-') {
        s++;
        neg = -1;
    }         
#ifdef BIT32
    ov = 2147483647/base
#else   
    ov = 32767/base;
#endif
    v  = *s;
    while(s - s_start < len){     
        if(v >= '0' && v <= '9')
            v -= '0';
        else {
            v |= 0x20;               /* Convert to lower case */
            if(v >= 'a' && v <= 'z')
                v -= 'a' - 10;     
            else {
                if(*s && !isspace(*s))
                    rv++;   /* Go from 0 to 1 or -3 to -2 */
                break;  /* Not any sort of digit */
            }
        }                          
        if(v >= base){
            rv++;  /* Go from 0 to 1 or -3 to -2 */  
            break; /* Not in radix */
        }
        n2 = n * base;
#ifdef BIT32
        if(n > ov || n2 > 2147483647 - v){
#else
        if(n > ov || n2 >   32767 - v){
#endif
            rv = -1;
            break;  /* Overflow */
        }
        n = n2 + v;
        v = *++s;
        rv = 0;
    }         
    if(rv >= -1)
        *num = (long)(n * neg);
    if(rp != NULL)
        *rp = s;
    return(rv);
}
 

#ifdef X_UNSIGNED_ARGS
stou(s_start,len,num,base,rp)
register char *s_start;
char **rp;
unsigned long *num;
register int base;
int len;
{                        
    register char *s = s_start;
    register unsigned long n = 0,v,ov,n2; 

    int rv = -3;
    if(s == NULL)
        return(-3);
    while(isspace(*s) && s - s_start < len) 
        s++;

#ifdef X_BIT32
    ov  = (unsigned long)0xffffffff/(unsigned long)base;
#else
    ov  = (unsigned long)0xffff/(unsigned long)base;
#endif
    v = *s;
    while(s - s_start < len){     
        if(v >= '0' && v <= '9')
            v -= '0';                
        else {
            v |= 0x20;               /* Convert to lower case */
            if(v >= 'a' && v <= 'z')
                v -= 'a' - 10;     
            else {
                if(*s && !isspace(*s))
                    rv++;   /* Go from 0 to 1 or -3 to -2 */
                break;  /* Not any sort of digit */
            }
        }                          
        if(v >= base){
            rv++;  /* Go from 0 to 1 or -3 to -2 */  
            break; /* Not in radix */
        }
        n2 = n * base;
#ifdef BIT32
        if(n > ov || n2 > 0xffffffff - v){
#else
        if(n > ov || n2 > 0xffff - v){
#endif
            rv = -1;
            break;  /* Overflow */
        }
        n = n2 + v;
        v = *++s;
        rv = 0;
    }         
    if(rv >= -1)
        *num = (unsigned long)n;
    if(rp != NULL)
        *rp = s;
    return(rv);
}
                              
                             
#endif /* X_UNSIGNED_ARGS */
