Exemplo n.º 1
0
Arquivo: setup.c Projeto: CCorreia/git
/*
 * Take an element of a pathspec and check for magic signatures.
 * Append the result to the prefix.
 *
 * For now, we only parse the syntax and throw out anything other than
 * "top" magic.
 *
 * NEEDSWORK: This needs to be rewritten when we start migrating
 * get_pathspec() users to use the "struct pathspec" interface.  For
 * example, a pathspec element may be marked as case-insensitive, but
 * the prefix part must always match literally, and a single stupid
 * string cannot express such a case.
 */
static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
{
	unsigned magic = 0;
	const char *copyfrom = elt;
	int i;

	if (elt[0] != ':') {
		; /* nothing to do */
	} else if (elt[1] == '(') {
		/* longhand */
		const char *nextat;
		for (copyfrom = elt + 2;
		     *copyfrom && *copyfrom != ')';
		     copyfrom = nextat) {
			size_t len = strcspn(copyfrom, ",)");
			if (copyfrom[len] == ')')
				nextat = copyfrom + len;
			else
				nextat = copyfrom + len + 1;
			if (!len)
				continue;
			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
				if (strlen(pathspec_magic[i].name) == len &&
				    !strncmp(pathspec_magic[i].name, copyfrom, len)) {
					magic |= pathspec_magic[i].bit;
					break;
				}
			if (ARRAY_SIZE(pathspec_magic) <= i)
				die("Invalid pathspec magic '%.*s' in '%s'",
				    (int) len, copyfrom, elt);
		}
		if (*copyfrom == ')')
			copyfrom++;
	} else {
		/* shorthand */
		for (copyfrom = elt + 1;
		     *copyfrom && *copyfrom != ':';
		     copyfrom++) {
			char ch = *copyfrom;

			if (!is_pathspec_magic(ch))
				break;
			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
				if (pathspec_magic[i].mnemonic == ch) {
					magic |= pathspec_magic[i].bit;
					break;
				}
			if (ARRAY_SIZE(pathspec_magic) <= i)
				die("Unimplemented pathspec magic '%c' in '%s'",
				    ch, elt);
		}
		if (*copyfrom == ':')
			copyfrom++;
	}

	if (magic & PATHSPEC_FROMTOP)
		return xstrdup(copyfrom);
	else
		return prefix_path(prefix, prefixlen, copyfrom);
}
Exemplo n.º 2
0
/*
 * Take an element of a pathspec and check for magic signatures.
 * Append the result to the prefix. Return the magic bitmap.
 *
 * For now, we only parse the syntax and throw out anything other than
 * "top" magic.
 *
 * NEEDSWORK: This needs to be rewritten when we start migrating
 * get_pathspec() users to use the "struct pathspec" interface.  For
 * example, a pathspec element may be marked as case-insensitive, but
 * the prefix part must always match literally, and a single stupid
 * string cannot express such a case.
 */
static unsigned prefix_pathspec(struct pathspec_item *item,
				unsigned *p_short_magic,
				const char **raw, unsigned flags,
				const char *prefix, int prefixlen,
				const char *elt)
{
	static int literal_global = -1;
	static int glob_global = -1;
	static int noglob_global = -1;
	static int icase_global = -1;
	unsigned magic = 0, short_magic = 0, global_magic = 0;
	const char *copyfrom = elt, *long_magic_end = NULL;
	char *match;
	int i, pathspec_prefix = -1;

	if (literal_global < 0)
		literal_global = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
	if (literal_global)
		global_magic |= PATHSPEC_LITERAL;

	if (glob_global < 0)
		glob_global = git_env_bool(GIT_GLOB_PATHSPECS_ENVIRONMENT, 0);
	if (glob_global)
		global_magic |= PATHSPEC_GLOB;

	if (noglob_global < 0)
		noglob_global = git_env_bool(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, 0);

	if (glob_global && noglob_global)
		die(_("global 'glob' and 'noglob' pathspec settings are incompatible"));


	if (icase_global < 0)
		icase_global = git_env_bool(GIT_ICASE_PATHSPECS_ENVIRONMENT, 0);
	if (icase_global)
		global_magic |= PATHSPEC_ICASE;

	if ((global_magic & PATHSPEC_LITERAL) &&
	    (global_magic & ~PATHSPEC_LITERAL))
		die(_("global 'literal' pathspec setting is incompatible "
		      "with all other global pathspec settings"));

	if (flags & PATHSPEC_LITERAL_PATH)
		global_magic = 0;

	if (elt[0] != ':' || literal_global ||
	    (flags & PATHSPEC_LITERAL_PATH)) {
		; /* nothing to do */
	} else if (elt[1] == '(') {
		/* longhand */
		const char *nextat;
		for (copyfrom = elt + 2;
		     *copyfrom && *copyfrom != ')';
		     copyfrom = nextat) {
			size_t len = strcspn(copyfrom, ",)");
			if (copyfrom[len] == ',')
				nextat = copyfrom + len + 1;
			else
				/* handle ')' and '\0' */
				nextat = copyfrom + len;
			if (!len)
				continue;
			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
				if (strlen(pathspec_magic[i].name) == len &&
				    !strncmp(pathspec_magic[i].name, copyfrom, len)) {
					magic |= pathspec_magic[i].bit;
					break;
				}
				if (starts_with(copyfrom, "prefix:")) {
					char *endptr;
					pathspec_prefix = strtol(copyfrom + 7,
								 &endptr, 10);
					if (endptr - copyfrom != len)
						die(_("invalid parameter for pathspec magic 'prefix'"));
					/* "i" would be wrong, but it does not matter */
					break;
				}
			}
			if (ARRAY_SIZE(pathspec_magic) <= i)
				die(_("Invalid pathspec magic '%.*s' in '%s'"),
				    (int) len, copyfrom, elt);
		}
		if (*copyfrom != ')')
			die(_("Missing ')' at the end of pathspec magic in '%s'"), elt);
		long_magic_end = copyfrom;
		copyfrom++;
	} else {
		/* shorthand */
		for (copyfrom = elt + 1;
		     *copyfrom && *copyfrom != ':';
		     copyfrom++) {
			char ch = *copyfrom;

			if (!is_pathspec_magic(ch))
				break;
			for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
				if (pathspec_magic[i].mnemonic == ch) {
					short_magic |= pathspec_magic[i].bit;
					break;
				}
			if (ARRAY_SIZE(pathspec_magic) <= i)
				die(_("Unimplemented pathspec magic '%c' in '%s'"),
				    ch, elt);
		}
		if (*copyfrom == ':')
			copyfrom++;
	}

	magic |= short_magic;
	*p_short_magic = short_magic;

	/* --noglob-pathspec adds :(literal) _unless_ :(glob) is specified */
	if (noglob_global && !(magic & PATHSPEC_GLOB))
		global_magic |= PATHSPEC_LITERAL;

	/* --glob-pathspec is overridden by :(literal) */
	if ((global_magic & PATHSPEC_GLOB) && (magic & PATHSPEC_LITERAL))
		global_magic &= ~PATHSPEC_GLOB;

	magic |= global_magic;

	if (pathspec_prefix >= 0 &&
	    (prefixlen || (prefix && *prefix)))
		die("BUG: 'prefix' magic is supposed to be used at worktree's root");

	if ((magic & PATHSPEC_LITERAL) && (magic & PATHSPEC_GLOB))
		die(_("%s: 'literal' and 'glob' are incompatible"), elt);

	if (pathspec_prefix >= 0) {
		match = xstrdup(copyfrom);
		prefixlen = pathspec_prefix;
	} else if (magic & PATHSPEC_FROMTOP) {
		match = xstrdup(copyfrom);
		prefixlen = 0;
	} else {
		match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom);
		if (!match)
			die(_("%s: '%s' is outside repository"), elt, copyfrom);
	}
	*raw = item->match = match;
	/*
	 * Prefix the pathspec (keep all magic) and assign to
	 * original. Useful for passing to another command.
	 */
	if (flags & PATHSPEC_PREFIX_ORIGIN) {
		struct strbuf sb = STRBUF_INIT;
		const char *start = elt;
		if (prefixlen && !literal_global) {
			/* Preserve the actual prefix length of each pattern */
			if (short_magic)
				die("BUG: prefixing on short magic is not supported");
			else if (long_magic_end) {
				strbuf_add(&sb, start, long_magic_end - start);
				strbuf_addf(&sb, ",prefix:%d", prefixlen);
				start = long_magic_end;
			} else {
				if (*start == ':')
					start++;
				strbuf_addf(&sb, ":(prefix:%d)", prefixlen);
			}
		}
		strbuf_add(&sb, start, copyfrom - start);
		strbuf_addstr(&sb, match);
		item->original = strbuf_detach(&sb, NULL);
	} else
		item->original = elt;
	item->len = strlen(item->match);
	item->prefix = prefixlen;

	if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) &&
	    (item->len >= 1 && item->match[item->len - 1] == '/') &&
	    (i = cache_name_pos(item->match, item->len - 1)) >= 0 &&
	    S_ISGITLINK(active_cache[i]->ce_mode)) {
		item->len--;
		match[item->len] = '\0';
	}

	if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE)
		for (i = 0; i < active_nr; i++) {
			struct cache_entry *ce = active_cache[i];
			int ce_len = ce_namelen(ce);

			if (!S_ISGITLINK(ce->ce_mode))
				continue;

			if (item->len <= ce_len || match[ce_len] != '/' ||
			    memcmp(ce->name, match, ce_len))
				continue;
			if (item->len == ce_len + 1) {
				/* strip trailing slash */
				item->len--;
				match[item->len] = '\0';
			} else
				die (_("Pathspec '%s' is in submodule '%.*s'"),
				     elt, ce_len, ce->name);
		}

	if (magic & PATHSPEC_LITERAL)
		item->nowildcard_len = item->len;
	else {
		item->nowildcard_len = simple_length(item->match);
		if (item->nowildcard_len < prefixlen)
			item->nowildcard_len = prefixlen;
	}
	item->flags = 0;
	if (magic & PATHSPEC_GLOB) {
		/*
		 * FIXME: should we enable ONESTAR in _GLOB for
		 * pattern "* * / * . c"?
		 */
	} else {
		if (item->nowildcard_len < item->len &&
		    item->match[item->nowildcard_len] == '*' &&
		    no_wildcard(item->match + item->nowildcard_len + 1))
			item->flags |= PATHSPEC_ONESTAR;
	}

	/* sanity checks, pathspec matchers assume these are sane */
	assert(item->nowildcard_len <= item->len &&
	       item->prefix         <= item->len);
	return magic;
}