/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 * Copyright 2007 Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */
/*
 * Copyright 1987, 1988 by MIT Student Information Processing Board
 *
 * For copyright info, see copyright.h.
 */

#include "ss_internal.h"
#include "copyright.h"
#include <errno.h>

enum parse_mode { WHITESPACE, TOKEN, QUOTED_STRING };

/*
 * parse(line_ptr, argc_ptr)
 *
 * Function:
 *      Parses line, dividing at whitespace, into tokens, returns
 *      the "argc" and "argv" values.
 * Arguments:
 *      line_ptr (char *)
 *              Pointer to text string to be parsed.
 *      argc_ptr (int *)
 *              Where to put the "argc" (number of tokens) value.
 * Returns:
 *      argv (char **)
 *              Series of pointers to parsed tokens in the original string.
 */

#define NEW_ARGV(old,n) (char **)realloc((char *)old,                   \
                                         (unsigned)(n+2)*sizeof(char*))

char **ss_parse (sci_idx, line_ptr, argc_ptr)
    int sci_idx;
    register char *line_ptr;
    int *argc_ptr;
{
    register char **argv, *cp;
    char **newargv;
    register int argc;
    register enum parse_mode parse_mode;

    argv = (char **) malloc (sizeof(char *));
    if (argv == (char **)NULL) {
        ss_error(sci_idx, errno, "Can't allocate storage");
        *argc_ptr = 0;
        return(argv);
    }
    *argv = (char *)NULL;

    argc = 0;

    parse_mode = WHITESPACE;    /* flushing whitespace */
    cp = line_ptr;              /* cp is for output */
    while (1) {
#ifdef DEBUG
        {
            printf ("character `%c', mode %d\n", *line_ptr, parse_mode);
        }
#endif
        while (parse_mode == WHITESPACE) {
            if (*line_ptr == '\0')
                goto end_of_line;
            if (*line_ptr == ' ' || *line_ptr == '\t') {
                line_ptr++;
                continue;
            }
            if (*line_ptr == '"') {
                /* go to quoted-string mode */
                parse_mode = QUOTED_STRING;
                cp = line_ptr++;
                newargv = NEW_ARGV (argv, argc);
                if (newargv == NULL) {
                out_of_mem_in_argv:
                    free(argv);
                    ss_error(sci_idx, errno, "Can't allocate storage");
                    *argc_ptr = 0;
                    return NULL;
                }
                argv = newargv;
                argv[argc++] = cp;
                argv[argc] = NULL;
            }
            else {
                /* random-token mode */
                parse_mode = TOKEN;
                cp = line_ptr;
                newargv = NEW_ARGV (argv, argc);
                if (newargv == NULL)
                    goto out_of_mem_in_argv;
                argv = newargv;
                argv[argc++] = line_ptr;
                argv[argc] = NULL;
            }
        }
        while (parse_mode == TOKEN) {
            if (*line_ptr == '\0') {
                *cp++ = '\0';
                goto end_of_line;
            }
            else if (*line_ptr == ' ' || *line_ptr == '\t') {
                *cp++ = '\0';
                line_ptr++;
                parse_mode = WHITESPACE;
            }
            else if (*line_ptr == '"') {
                line_ptr++;
                parse_mode = QUOTED_STRING;
            }
            else {
                *cp++ = *line_ptr++;
            }
        }
        while (parse_mode == QUOTED_STRING) {
            if (*line_ptr == '\0') {
                ss_error (sci_idx, 0,
                          "Unbalanced quotes in command line");
                free (argv);
                *argc_ptr = 0;
                return NULL;
            }
            else if (*line_ptr == '"') {
                if (*++line_ptr == '"') {
                    *cp++ = '"';
                    line_ptr++;
                }
                else {
                    parse_mode = TOKEN;
                }
            }
            else {
                *cp++ = *line_ptr++;
            }
        }
    }
end_of_line:
    *argc_ptr = argc;
#ifdef DEBUG
    {
        int i;
        printf ("argc = %d\n", argc);
        for (i = 0; i <= argc; i++)
            printf ("\targv[%2d] = `%s'\n", i,
                    argv[i] ? argv[i] : "<NULL>");
    }
#endif
    return(argv);
}
