text: // Copyright (C) 2002 Andrew Tridgell
  // Copyright (C) 2009-2016 Joel Rosdahl
  //
  // This program is free software; you can redistribute it and/or modify it
  // under the terms of the GNU General Public License as published by the Free
  // Software Foundation; either version 3 of the License, or (at your option)
  // any later version.
  //
  // This program is distributed in the hope that it will be useful, but WITHOUT
  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  // more details.
  //
  // You should have received a copy of the GNU General Public License along with
  // this program; if not, write to the Free Software Foundation, Inc., 51
  // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  
  #include "ccache.h"
  
  struct args *
  args_init(int init_argc, char **init_args)
  {
  	struct args *args = (struct args *)x_malloc(sizeof(struct args));
  	args->argc = 0;
  	args->argv = (char **)x_malloc(sizeof(char *));
  	args->argv[0] = NULL;
  	for (int i = 0; i < init_argc; i++) {
  		args_add(args, init_args[i]);
  	}
  	return args;
  }
  
  struct args *
  args_init_from_string(const char *command)
  {
  	char *p = x_strdup(command);
  	char *q = p;
  	char *word, *saveptr = NULL;
  	struct args *args = args_init(0, NULL);
  	while ((word = strtok_r(q, " \t\r\n", &saveptr))) {
  		args_add(args, word);
  		q = NULL;
  	}
  
  	free(p);
  	return args;
  }
  
  struct args *
  args_init_from_gcc_atfile(const char *filename)
  {
  	char *argtext;
  	if (!(argtext = read_text_file(filename, 0))) {
  		return NULL;
  	}
  
  	struct args *args = args_init(0, NULL);
  	char *pos = argtext;
  	char *argbuf = x_malloc(strlen(argtext) + 1);
  	char *argpos = argbuf;
  
  	// Used to track quoting state; if \0, we are not inside quotes. Otherwise
  	// stores the quoting character that started it, for matching the end quote.
  	char quoting = '\0';
  
  	while (1) {
  		switch (*pos) {
  		case '\\':
  			pos++;
  			if (*pos == '\0') {
  				continue;
  			}
  			break;
  
  		case '\"':
  		case '\'':
  			if (quoting != '\0') {
  				if (quoting == *pos) {
  					quoting = '\0';
  					pos++;
  					continue;
  				} else {
  					break;
  				}
  			} else {
  				quoting = *pos;
  				pos++;
  				continue;
  			}
  
  		case '\n':
  		case '\r':
  		case '\t':
  		case ' ':
  			if (quoting) {
  				break;
  			}
  		// Fall through.
  
  		case '\0':
  			// End of token
  			*argpos = '\0';
  			if (argbuf[0] != '\0') {
  				args_add(args, argbuf);
  			}
  			argpos = argbuf;
  			if (*pos == '\0') {
  				goto out;
  			} else {
  				pos++;
  				continue;
  			}
  		}
  
  		*argpos = *pos;
  		pos++;
  		argpos++;
  	}
  
  out:
  	free(argbuf);
  	free(argtext);
  	return args;
  }
  
  struct args *
  args_copy(struct args *args)
  {
  	return args_init(args->argc, args->argv);
  }
  
  // Insert all arguments in src into dest at position index. If replace is true,
  // the element at dest->argv[index] is replaced with the contents of src and
  // everything past it is shifted. Otherwise, dest->argv[index] is also shifted.
  //
  // src is consumed by this operation and should not be freed or used again by
  // the caller.
  void
  args_insert(struct args *dest, int index, struct args *src, bool replace)
  {
  	// Adjustments made if we are replacing or shifting the element currently at
  	// dest->argv[index].
  	int offset = replace ? 1 : 0;
  
  	if (replace) {
  		free(dest->argv[index]);
  	}
  
  	if (src->argc == 0) {
  		if (replace) {
  			// Have to shift everything down by 1 since we replaced with an empty
  			// list.
  			for (int i = index; i < dest->argc; i++) {
  				dest->argv[i] = dest->argv[i + 1];
  			}
  			dest->argc--;
  		}
  		args_free(src);
  		return;
  	}
  
  	if (src->argc == 1 && replace) {
  		// Trivial case; replace with 1 element.
  		dest->argv[index] = src->argv[0];
  		src->argc = 0;
  		args_free(src);
  		return;
  	}
  
  	dest->argv = (char **)x_realloc(
  	  dest->argv,
  	  (src->argc + dest->argc + 1 - offset) *
  	  sizeof(char *));
  
  	// Shift arguments over.
  	for (int i = dest->argc; i >= index + offset; i--) {
  		dest->argv[i + src->argc - offset] = dest->argv[i];
  	}
  
  	// Copy the new arguments into place.
  	for (int i = 0; i < src->argc; i++) {
  		dest->argv[i + index] = src->argv[i];
  	}
  
  	dest->argc += src->argc - offset;
  	src->argc = 0;
  	args_free(src);
  }
  
  void
  args_free(struct args *args)
  {
  	if (!args) {
  		return;
  	}
  	for (int i = 0; i < args->argc; ++i) {
  		if (args->argv[i]) {
  			free(args->argv[i]);
  		}
  	}
  	free(args->argv);
  	free(args);
  }
  
  void
  args_add(struct args *args, const char *s)
  {
  	args->argv = (char **)x_realloc(args->argv,
  	                                (args->argc + 2) * sizeof(char *));
  	args->argv[args->argc] = x_strdup(s);
  	args->argc++;
  	args->argv[args->argc] = NULL;
  }
  
  // Add all arguments in to_append to args.
  void
  args_extend(struct args *args, struct args *to_append)
  {
  	for (int i = 0; i < to_append->argc; i++) {
  		args_add(args, to_append->argv[i]);
  	}
  }
  
  // Pop the last element off the args list.
  void
  args_pop(struct args *args, int n)
  {
  	while (n--) {
  		args->argc--;
  		free(args->argv[args->argc]);
  		args->argv[args->argc] = NULL;
  	}
  }
  
  // Set argument at given index.
  void
  args_set(struct args *args, int index, const char *value)
  {
  	assert(index < args->argc);
  	free(args->argv[index]);
  	args->argv[index] = x_strdup(value);
  }
  
  // Remove the first element of the argument list.
  void
  args_remove_first(struct args *args)
  {
  	free(args->argv[0]);
  	memmove(&args->argv[0], &args->argv[1], args->argc * sizeof(args->argv[0]));
  	args->argc--;
  }
  
  // Add an argument into the front of the argument list.
  void
  args_add_prefix(struct args *args, const char *s)
  {
  	args->argv = (char **)x_realloc(args->argv,
  	                                (args->argc + 2) * sizeof(char *));
  	memmove(&args->argv[1], &args->argv[0],
  	        (args->argc+1) * sizeof(args->argv[0]));
  	args->argv[0] = x_strdup(s);
  	args->argc++;
  }
  
  // Strip any arguments beginning with the specified prefix.
  void
  args_strip(struct args *args, const char *prefix)
  {
  	for (int i = 0; i < args->argc; ) {
  		if (str_startswith(args->argv[i], prefix)) {
  			free(args->argv[i]);
  			memmove(&args->argv[i],
  			        &args->argv[i+1],
  			        (args->argc - i) * sizeof(args->argv[i]));
  			args->argc--;
  		} else {
  			i++;
  		}
  	}
  }
  
  // Format args to a space-separated string. Does not quote spaces. Caller
  // frees.
  char *
  args_to_string(struct args *args)
  {
  	unsigned size = 0;
  	for (char **p = args->argv; *p; p++) {
  		size += strlen(*p) + 1;
  	}
  
  	char *result = x_malloc(size + 1);
  	int pos = 0;
  	for (char **p = args->argv; *p; p++) {
  		pos += sprintf(&result[pos], "%s ", *p);
  	}
  	result[pos - 1] = '\0';
  	return result;
  }
  
  // Returns true if args1 equals args2, else false.
  bool
  args_equal(struct args *args1, struct args *args2)
  {
  	if (args1->argc != args2->argc) {
  		return false;
  	}
  	for (int i = 0; i < args1->argc; i++) {
  		if (!str_eq(args1->argv[i], args2->argv[i])) {
  			return false;
  		}
  	}
  	return true;
  }
  