Edit

kc3-lang/pkgconf/libpkgconf/argvsplit.c

Branch :

  • Show log

    Commit

  • Author : William Pitcock
    Date : 2017-09-17 23:38:25
    Hash : e9fd43ca
    Message : libpkgconf: clean up header includes (closes #137)

  • libpkgconf/argvsplit.c
  • /*
     * argvsplit.c
     * argv_split() routine
     *
     * Copyright (c) 2012, 2017 pkgconf authors (see AUTHORS).
     *
     * Permission to use, copy, modify, and/or distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * This software is provided 'as is' and without any warranty, express or
     * implied.  In no event shall the authors be liable for any damages arising
     * from the use of this software.
     */
    
    #include <libpkgconf/stdinc.h>
    #include <libpkgconf/libpkgconf.h>
    
    /*
     * !doc
     *
     * libpkgconf `argvsplit` module
     * =============================
     *
     * This is a lowlevel module which provides parsing of strings into argument vectors,
     * similar to what a shell would do.
     */
    
    /*
     * !doc
     *
     * .. c:function:: void pkgconf_argv_free(char **argv)
     *
     *    Frees an argument vector.
     *
     *    :param char** argv: The argument vector to free.
     *    :return: nothing
     */
    void
    pkgconf_argv_free(char **argv)
    {
    	free(argv[0]);
    	free(argv);
    }
    
    /*
     * !doc
     *
     * .. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv)
     *
     *    Splits a string into an argument vector.
     *
     *    :param char*   src: The string to split.
     *    :param int*    argc: A pointer to an integer to store the argument count.
     *    :param char*** argv: A pointer to a pointer for an argument vector.
     *    :return: 0 on success, -1 on error.
     *    :rtype: int
     */
    int
    pkgconf_argv_split(const char *src, int *argc, char ***argv)
    {
    	char *buf = malloc(strlen(src) + 1);
    	const char *src_iter;
    	char *dst_iter;
    	int argc_count = 0;
    	int argv_size = 5;
    	char quote = 0;
    	bool escaped = false;
    
    	src_iter = src;
    	dst_iter = buf;
    
    	memset(buf, 0, strlen(src) + 1);
    
    	*argv = calloc(sizeof (void *), argv_size);
    	(*argv)[argc_count] = dst_iter;
    
    	while (*src_iter)
    	{
    		if (escaped)
    		{
    			/* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */
    			if (quote == '\"')
    			{
    				if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\'))
    					*dst_iter++ = '\\';
    
    				*dst_iter++ = *src_iter;
    			}
    			else
    			{
    				if (isspace((unsigned int) *src_iter))
    					*dst_iter++ = '\\';
    
    				*dst_iter++ = *src_iter;
    			}
    
    			escaped = false;
    		}
    		else if (quote)
    		{
    			if (*src_iter == quote)
    				quote = 0;
    			else if (*src_iter == '\\')
    				escaped = true;
    			else
    				*dst_iter++ = *src_iter;
    		}
    		else if (isspace((unsigned int)*src_iter))
    		{
    			if ((*argv)[argc_count] != NULL)
    			{
    				argc_count++, dst_iter++;
    
    				if (argc_count == argv_size)
    				{
    					argv_size += 5;
    					*argv = realloc(*argv, sizeof(void *) * argv_size);
    				}
    
    				(*argv)[argc_count] = dst_iter;
    			}
    		}
    		else switch(*src_iter)
    		{
    			case '\\':
    				escaped = true;
    				break;
    
    			case '\"':
    			case '\'':
    				quote = *src_iter;
    				break;
    
    			default:
    				*dst_iter++ = *src_iter;
    				break;
    		}
    
    		src_iter++;
    	}
    
    	if (escaped || quote)
    	{
    		free(*argv);
    		free(buf);
    		return -1;
    	}
    
    	if (strlen((*argv)[argc_count]))
    	{
    		argc_count++;
    	}
    
    	*argc = argc_count;
    	return 0;
    }