int is_valid_win32_path()

in compat/mingw.c [3055:3177]


int is_valid_win32_path(const char *path, int allow_literal_nul)
{
	const char *p = path;
	int preceding_space_or_period = 0, i = 0, periods = 0;

	if (!protect_ntfs)
		return 1;

	skip_dos_drive_prefix((char **)&path);
	goto segment_start;

	for (;;) {
		char c = *(path++);
		switch (c) {
		case '\0':
		case '/': case '\\':
			/* cannot end in ` ` or `.`, except for `.` and `..` */
			if (preceding_space_or_period &&
			    (i != periods || periods > 2))
				return 0;
			if (!c)
				return 1;

			i = periods = preceding_space_or_period = 0;

segment_start:
			switch (*path) {
			case 'a': case 'A': /* AUX */
				if (((c = path[++i]) != 'u' && c != 'U') ||
				    ((c = path[++i]) != 'x' && c != 'X')) {
not_a_reserved_name:
					path += i;
					continue;
				}
				break;
			case 'c': case 'C':
				/* COM1 ... COM9, CON, CONIN$, CONOUT$ */
				if ((c = path[++i]) != 'o' && c != 'O')
					goto not_a_reserved_name;
				c = path[++i];
				if (c == 'm' || c == 'M') { /* COM1 ... COM9 */
					c = path[++i];
					if (c < '1' || c > '9')
						goto not_a_reserved_name;
				} else if (c == 'n' || c == 'N') { /* CON */
					c = path[i + 1];
					if ((c == 'i' || c == 'I') &&
					    ((c = path[i + 2]) == 'n' ||
					     c == 'N') &&
					    path[i + 3] == '$')
						i += 3; /* CONIN$ */
					else if ((c == 'o' || c == 'O') &&
						 ((c = path[i + 2]) == 'u' ||
						  c == 'U') &&
						 ((c = path[i + 3]) == 't' ||
						  c == 'T') &&
						 path[i + 4] == '$')
						i += 4; /* CONOUT$ */
				} else
					goto not_a_reserved_name;
				break;
			case 'l': case 'L': /* LPT<N> */
				if (((c = path[++i]) != 'p' && c != 'P') ||
				    ((c = path[++i]) != 't' && c != 'T') ||
				    !isdigit(path[++i]))
					goto not_a_reserved_name;
				break;
			case 'n': case 'N': /* NUL */
				if (((c = path[++i]) != 'u' && c != 'U') ||
				    ((c = path[++i]) != 'l' && c != 'L') ||
				    (allow_literal_nul &&
				     !path[i + 1] && p == path))
					goto not_a_reserved_name;
				break;
			case 'p': case 'P': /* PRN */
				if (((c = path[++i]) != 'r' && c != 'R') ||
				    ((c = path[++i]) != 'n' && c != 'N'))
					goto not_a_reserved_name;
				break;
			default:
				continue;
			}

			/*
			 * So far, this looks like a reserved name. Let's see
			 * whether it actually is one: trailing spaces, a file
			 * extension, or an NTFS Alternate Data Stream do not
			 * matter, the name is still reserved if any of those
			 * follow immediately after the actual name.
			 */
			i++;
			if (path[i] == ' ') {
				preceding_space_or_period = 1;
				while (path[++i] == ' ')
					; /* skip all spaces */
			}

			c = path[i];
			if (c && c != '.' && c != ':' && !is_xplatform_dir_sep(c))
				goto not_a_reserved_name;

			/* contains reserved name */
			return 0;
		case '.':
			periods++;
			/* fallthru */
		case ' ':
			preceding_space_or_period = 1;
			i++;
			continue;
		case ':': /* DOS drive prefix was already skipped */
		case '<': case '>': case '"': case '|': case '?': case '*':
			/* illegal character */
			return 0;
		default:
			if (c > '\0' && c < '\x20')
				/* illegal character */
				return 0;
		}
		preceding_space_or_period = 0;
		i++;
	}
}