/*
*******************************************************************************
*
*   Copyright (C) 2000, International Business Machines
*   Corporation and others.  All Rights Reserved.
*
*******************************************************************************
*   file name:  uoptions.c
*   encoding:   US-ASCII
*   tab size:   8 (not used)
*   indentation:4
*
*   created on: 2000apr17
*   created by: Markus W. Scherer
*
*   This file provides a command line argument parser.
*/

#include "unicode/utypes.h"
#include "cstring.h"
#include "uoptions.h"

U_CAPI int U_EXPORT2
u_parseArgs(int argc, char* argv[],
            int optionCount, UOption options[]) {
    char *arg;
    int i=1, remaining=1;
    char c, stopOptions=0;

    while(i<argc) {
        arg=argv[i];
        if(!stopOptions && *arg=='-' && (c=arg[1])!=0) {
            /* process an option */
            UOption *option=NULL;
            arg+=2;
            if(c=='-') {
                /* process a long option */
                if(*arg==0) {
                    /* stop processing options after "--" */
                    stopOptions=1;
                } else {
                    /* search for the option string */
                    int j;
                    for(j=0; j<optionCount; ++j) {
                        if(options[j].longName && uprv_strcmp(arg, options[j].longName)==0) {
                            option=options+j;
                            break;
                        }
                    }
                    if(option==NULL) {
                        /* no option matches */
                        return -i;
                    }
                    option->doesOccur=1;

                    if(option->hasArg!=UOPT_NO_ARG) {
                        /* parse the argument for the option, if any */
                        if(i+1<argc && !(argv[i+1][0]=='-' && argv[i+1][1]!=0)) {
                            /* argument in the next argv[], and there is not an option in there */
                            option->value=argv[++i];
                        } else if(option->hasArg==UOPT_REQUIRES_ARG) {
                            /* there is no argument, but one is required: return with error */
                            return -i;
                        }
                    }
                }
            } else {
                /* process one or more short options */
                do {
                    /* search for the option letter */
                    int j;
                    for(j=0; j<optionCount; ++j) {
                        if(c==options[j].shortName) {
                            option=options+j;
                            break;
                        }
                    }
                    if(option==NULL) {
                        /* no option matches */
                        return -i;
                    }
                    option->doesOccur=1;

                    if(option->hasArg!=UOPT_NO_ARG) {
                        /* parse the argument for the option, if any */
                        if(*arg!=0) {
                            /* argument following in the same argv[] */
                            option->value=arg;
                            /* do not process the rest of this arg as option letters */
                            break;
                        } else if(i+1<argc && !(argv[i+1][0]=='-' && argv[i+1][1]!=0)) {
                            /* argument in the next argv[], and there is not an option in there */
                            option->value=argv[++i];
                            /* this break is redundant because we know that *arg==0 */
                            break;
                        } else if(option->hasArg==UOPT_REQUIRES_ARG) {
                            /* there is no argument, but one is required: return with error */
                            return -i;
                        }
                    }

                    /* get the next option letter */
                    option=NULL;
                    c=*arg++;
                } while(c!=0);
            }

            if(option!=0 && option->optionFn!=0 && option->optionFn(option->context, option)<0) {
                /* the option function was called and returned an error */
                return -i;
            }

            /* go to next argv[] */
            ++i;
        } else {
            /* move a non-option up in argv[] */
            argv[remaining++]=arg;
            ++i;
        }
    }
    return remaining;
}
