shithub: qk2

ref: ed82668431413cf8d5389a631cad56a6c4d2d6da
dir: /plan9/misc.c/

View raw version
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include "../q_shared.h"

int curtime;

static uchar *membase;
static int maxhunksize, curhunksize;
static char findbase[MAX_OSPATH], findpath[MAX_OSPATH], findpattern[MAX_OSPATH];
static Dir *dirs;
static long dirn, di;

static int glob_match(char *, char *);


/* Like glob_match, but match PATTERN against any final segment of TEXT.  */
static int
glob_match_after_star(char *pattern, char *text)
{
	char *p = pattern, *t = text;
	char c, c1;

	while ((c = *p++) == '?' || c == '*')
		if (c == '?' && *t++ == '\0')
			return 0;

	if (c == '\0')
		return 1;

	if (c == '\\')
		c1 = *p;
	else
		c1 = c;

	while (1) {
		if ((c == '[' || *t == c1) && glob_match(p - 1, t))
			return 1;
		if (*t++ == '\0')
			return 0;
	}
}

/* Return nonzero if PATTERN has any special globbing chars in it.  */
static int
glob_pattern_p(char *pattern)
{
	char *p = pattern;
	char c;
	int open = 0;

	while ((c = *p++) != '\0')
		switch (c) {
		case '?':
		case '*':
			return 1;

		case '[':		/* Only accept an open brace if there is a close */
			open++;		/* brace to match it.  Bracket expressions must be */
			continue;	/* complete, according to Posix.2 */
		case ']':
			if (open)
				return 1;
			continue;

		case '\\':
			if (*p++ == '\0')
				return 0;
		}

	return 0;
}

/* Match the pattern PATTERN against the string TEXT;
   return 1 if it matches, 0 otherwise.

   A match means the entire string TEXT is used up in matching.

   In the pattern string, `*' matches any sequence of characters,
   `?' matches any character, [SET] matches any character in the specified set,
   [!SET] matches any character not in the specified set.

   A set is composed of characters or ranges; a range looks like
   character hyphen character (as in 0-9 or A-Z).
   [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
   Any other character in the pattern must be matched exactly.

   To suppress the special syntactic significance of any of `[]*?!-\',
   and match the character exactly, precede it with a `\'.
*/

static int
glob_match(char *pattern, char *text)
{
	char *p = pattern, *t = text;
	char c, c1, cstart, cend;
	int invert;

	while ((c = *p++) != '\0')
		switch (c) {
		case '?':
			if (*t == '\0')
				return 0;
			else
				++t;
			break;

		case '\\':
			if (*p++ != *t++)
				return 0;
			break;

		case '*':
			return glob_match_after_star(p, t);

		case '[':
			{
				c1 = *t++;

				if (!c1)
					return (0);

				invert = ((*p == '!') || (*p == '^'));
				if (invert)
					p++;

				c = *p++;
				while (1) {
					cstart = c;
					cend = c;

					if (c == '\\') {
						cstart = *p++;
						cend = cstart;
					}
					if (c == '\0')
						return 0;

					c = *p++;
					if (c == '-' && *p != ']') {
						cend = *p++;
						if (cend == '\\')
							cend = *p++;
						if (cend == '\0')
							return 0;
						c = *p++;
					}
					if (c1 >= cstart && c1 <= cend)
						goto match;
					if (c == ']')
						break;
				}
				if (!invert)
					return 0;
				break;

			  match:
				/* Skip the rest of the [...] construct that already matched.  */
				while (c != ']') {
					if (c == '\0')
						return 0;
					c = *p++;
					if (c == '\0')
						return 0;
					else if (c == '\\')
						++p;
				}
				if (invert)
					return 0;
				break;
			}

		default:
			if (c != *t++)
				return 0;
		}

	/* if the pattern is empty, Sys_FindNext looks at the current file anyway */
	return *t == '\0';
}

void *
Hunk_Begin(int maxsize)
{
	// reserve a huge chunk of memory, but don't commit any yet
	maxhunksize = maxsize;
	curhunksize = 0;
	if((membase = mallocz(maxhunksize, 1)) == nil)
		sysfatal("Hunk_Begin:malloc %d: %r", maxhunksize);
	return membase;
}

void *
Hunk_Alloc(int size)
{
	byte *buf;

	// round to cacheline
	size = (size+31)&~31;
	if(curhunksize + size > maxhunksize)
		Sys_Error("Hunk_Alloc overflow");
	buf = membase + curhunksize;
	curhunksize += size;
	return buf;
}

int
Hunk_End(void)
{
	if(realloc(membase, curhunksize) != membase)
		sysfatal("Hunk_End:realloc: %r");
	return curhunksize;
}

void
Hunk_Free(void *base)
{
	if(base != nil)
		free(base);
}

int
Sys_Milliseconds(void)
{
	static long msbase;

	if(msbase == 0)
		msbase = time(nil)*1000;
	curtime = nsec()/1000000 - msbase;
	return curtime;
}

void
Sys_Mkdir(char *path)
{
	int d;

	if((d = create(path, OREAD, DMDIR|0777)) < 0)
		fprint(2, "Sys_Mkdir:create: %r\n");
	else
		close(d);
}

static qboolean
CompareAttributes(ulong m, uint musthave, uint canthave)
{
	if(m & DMDIR && canthave & SFF_SUBDIR)
		return false;
	if(musthave & SFF_SUBDIR && ~m & DMDIR)
		return false;
	return true;
}

char *
Sys_FindFirst(char *path, uint musthave, uint canhave)
{
	char *p;
	int fd;

	if(dirs != nil)
		Sys_Error("Sys_BeginFind without close");

	strncpy(findbase, path, sizeof findbase-1);
	if((p = strrchr(findbase, '/')) != nil){
		*p = 0;
		strncpy(findpattern, p+1, sizeof findpattern-1);
	}else
		strcpy(findpattern, "*");
	if(strcmp(findpattern, "*.*") == 0)
		strcpy(findpattern, "*");
	if(*findpattern == '\0'){
		Com_Printf("Sys_BeginFind: empty pattern\n");
		return nil;
	}

	if((fd = open(findbase, OREAD)) < 0){
		fprint(2, "Sys_BeginFind:open: %r\n");
		return nil;
	}
	dirn = dirreadall(fd, &dirs);
	close(fd);
	if(dirn == 0)
		return nil;
	if(dirn < 0){
		fprint(2, "Sys_BeginFind:dirread: %r\n");
		return nil;
	}

	di = 0;
	return Sys_FindNext (musthave, canhave);
}

char *
Sys_FindNext(uint musthave, uint canhave)
{
	int i;

	if(dirs == nil)
		Sys_Error("Sys_FindNext without open\n");

	while(di < dirn){
		i = di++;
		if(glob_match(findpattern, dirs[i].name)){
			if(CompareAttributes(dirs[i].mode, musthave, canhave)){
				snprintf(findpath, sizeof findpath, "%s/%s", findbase, dirs[i].name);
				return findpath;
			}
		}
	}
	return nil;
}

void
Sys_FindClose(void)
{
	if(dirs != nil){
		free(dirs);
		dirs = nil;
	}
}