File:  [ELWIX - Embedded LightWeight unIX -] / libaitio / src / aitio.c
Revision 1.2.2.3: download - view: text, annotated - select for diffs - revision graph
Thu Feb 10 21:54:23 2011 UTC (13 years, 4 months ago) by misho
Branches: io1_4
Diff to: branchpoint 1.2: preferred, colored
added new feature watchdir

/*************************************************************************
* (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
*  by Michael Pounov <misho@openbsd-bg.org>
*
* $Author: misho $
* $Id: aitio.c,v 1.2.2.3 2011/02/10 21:54:23 misho Exp $
*
*************************************************************************/
#include "global.h"


int io_Debug;


#pragma GCC visibility push(hidden)

int io_Errno;
char io_Error[STRSIZ];

#pragma GCC visibility pop


// io_GetErrno() Get error code of last operation
inline int io_GetErrno()
{
	return io_Errno;
}

// io_GetError() Get error text of last operation
inline const char *io_GetError()
{
	return io_Error;
}

// io_SetErr() Set error to variables for internal use!!!
inline void io_SetErr(int eno, char *estr, ...)
{
	va_list lst;

	io_Errno = eno;
	memset(io_Error, 0, STRSIZ);
	va_start(lst, estr);
	vsnprintf(io_Error, STRSIZ, estr, lst);
	va_end(lst);
}


/*
 * ioPromptRead() Read data from input h[0] with prompt to output h[1]
 * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout
 * @csPrompt = Prompt before input, may be NULL
 * @psData = Readed data
 * @dataLen = Length of data
 * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars
*/
int ioPromptRead(int *h, const char *csPrompt, char * __restrict psData, int dataLen)
{
	int ok = 0;
	FILE *inp, *out;
	char szLine[BUFSIZ], *pos;

	if (!psData || !dataLen)
		return -1;

	inp = fdopen(!h ? 0 : h[0], "r");
	if (!inp) {
		LOGERR;
		return -1;
	}
	out = fdopen(!h ? 1 : h[1], "w");
	if (!out) {
		LOGERR;
		return -1;
	}

	while (!ok) {
		if (csPrompt) {
			fprintf(out, "%s", csPrompt);
			fflush(out);
		}

		memset(szLine, 0, BUFSIZ);
		if (!fgets(szLine, BUFSIZ, inp)) {
			clearerr(inp);
			fpurge(out);
			fflush(out);
			return 0;
		}

		if ((pos = strchr(szLine, '\n')))
			*pos = 0;

		strlcpy(psData, szLine, dataLen);
		ok = 1;
	}

	return pos - szLine;
}

/*
 * ioPromptPassword() Read password from input h[0] with prompt to output h[1]
 * @h = file handles h[0] = input, h[1] = output, if NULL use stdin, stdout
 * @csPrompt = Prompt before input, may be NULL
 * @psPass = Readed password
 * @passLen = Length of password
 * @confirm = Confirm password, 0 - get password, !=0 Ask for confirmation
 * return: 0 EOF; -1 error:: can`t read; >0 count of readed chars
*/
int ioPromptPassword(int *h, const char *csPrompt, char * __restrict psPass, int passLen, int confirm)
{
	int ret, ok = 0;
	FILE *inp, *out;
	char szLine[2][STRSIZ];
	struct sgttyb tty_state;

	if (!psPass || !passLen)
		return -1;

	inp = fdopen(!h ? 0 : h[0], "r");
	if (!inp) {
		LOGERR;
		return -1;
	}
	out = fdopen(!h ? 1 : h[1], "w");
	if (!out) {
		LOGERR;
		return -1;
	}

	if (ioctl(fileno(inp), TIOCGETP, &tty_state) == -1) {
		LOGERR;
		return -1;
	} else {
		tty_state.sg_flags &= ~ECHO;
		if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
			LOGERR;
			return -1;
		}
	}

	while (!ok) {
		switch ((ret = ioPromptRead(h, (!csPrompt || !*csPrompt) ? "Password:" : csPrompt, 
						szLine[0], STRSIZ))) {
			case -1:
				LOGERR;
				ok = -1;
			case 0:
				goto next;
		}
		if (confirm) {
			fprintf(out, "\n");
			fflush(out);

			switch (ioPromptRead(h, "Password confirm:", szLine[1], STRSIZ)) {
				case -1:
					LOGERR;
					ok = -1;
					goto next;
				case 0:
				default:
					if (strcmp(szLine[0], szLine[1])) {
						fprintf(out, "\n\07\07Mismatch - Try again!\n");
						fflush(out);
						continue;
					}
			}
		}

		strlcpy(psPass, szLine[0], passLen);
		ok = ret;
		fprintf(out, "\n");
		fflush(out);
	}

next:
	tty_state.sg_flags |= ECHO;
	if (ioctl(fileno(inp), TIOCSETP, &tty_state) == -1) {
		LOGERR;
		return -1;
	}

	return ok;
}

/*
 * ioRegexVerify() Function for verify data match in regex expression
 * @csRegex = Regulare expression pattern
 * @csData = Data for check and verify
 * @startPos = Return start positions
 * @endPos = Return end positions
 * return: NULL not match or error; !=NULL begin of matched data
*/
const char *ioRegexVerify(const char *csRegex, const char *csData, int *startPos, int *endPos)
{
	regex_t re;
	regmatch_t match;
	char szErr[STRSIZ];
	int ret, flg;
	const char *pos;

	if (!csRegex || !csData)
		return NULL;

	if ((ret = regcomp(&re, csRegex, REG_EXTENDED))) {
		regerror(ret, &re, szErr, STRSIZ);
		io_SetErr(ret, "Error:: %s\n", szErr);
		regfree(&re);
		return NULL;
	}

	for (ret = flg = 0, pos = csData; !(ret = regexec(&re, pos, 1, &match, flg)); 
			pos += match.rm_eo, flg = REG_NOTBOL) {
		if (startPos)
			*startPos = match.rm_so;
		if (endPos)
			*endPos = match.rm_eo;

		pos += match.rm_so;
		break;
	}

	if (ret) {
		regerror(ret, &re, szErr, STRSIZ);
		io_SetErr(ret, "Error:: %s\n", szErr);
		pos = NULL;
	}

	regfree(&re);
	return pos;
}

/*
 * ioRegexGet() Function for get data match in regex expression
 * @csRegex = Regulare expression pattern
 * @csData = Data from get
 * @psString = Returned string if match
 * @strLen = Length of string
 * return: 0 not match; >0 count of returned chars
*/
int ioRegexGet(const char *csRegex, const char *csData, char * __restrict psString, int strLen)
{
	int sp, ep, len;
	const char *str;

	if (!csRegex || !csData)
		return -1;

	str = ioRegexVerify(csRegex, csData, &sp, &ep);
	if (!str)
		return 0;

	len = ep - sp;
	if (psString && strLen) {
		memset(psString, 0, strLen);
		strncpy(psString, str, strLen <= len ? strLen - 1 : len);
	}

	return len;
}

/*
 * ioRegexReplace() Function for replace data match in regex expression with newdata
 * @csRegex = Regulare expression pattern
 * @csData = Source data
 * @csNew = Data for replace
 * return: NULL not match or error; !=NULL allocated new string, must be free after use!
*/
char *ioRegexReplace(const char *csRegex, const char *csData, const char *csNew)
{
	int sp, ep, len;
	char *str = NULL;

	if (!csRegex || !csData)
		return NULL;

	if (!ioRegexVerify(csRegex, csData, &sp, &ep))
		return NULL;

	// ___ before match
	len = sp + 1;
	str = malloc(len);
	if (!str) {
		LOGERR;
		return NULL;
	} else
		strlcpy(str, csData, len);
	// * replace match *
	if (csNew) {
		len += strlen(csNew);
		str = realloc(str, len);
		if (!str) {
			LOGERR;
			return NULL;
		} else
			strlcat(str, csNew, len);
	}
	// after match ___
	len += strlen(csData) - ep;
	str = realloc(str, len);
	if (!str) {
		LOGERR;
		return NULL;
	} else
		strlcat(str, csData + ep, len);

	return str;
}

/*
 * ioVarAst() Function for evaluate string like asterisk variable "{text[:[-]#[:#]]}"
 * @csString = Input string
 * return: NULL error, !=NULL Allocated new string evaluated from input string, must be free()
*/
char *
ioVarAst(const char *csString)
{
	char *ext, *str, *out = NULL;
	int e[2] = { 0 };

	if (!csString)
		return NULL;

	if (!strchr(csString, '{') || !strrchr(csString, '}')) {
		memset(io_Error, 0, STRSIZ);
		snprintf(io_Error, STRSIZ, "Error:: Invalid input string format ... "
				"must be like {text[:[-]#[:#]]}");
		io_Errno = EINVAL;
		return NULL;
	} else {
		str = strdup(strchr(csString, '{') + 1);
		*strrchr(str, '}') = 0;
	}

	if ((ext = strchr(str, ':'))) {
		*ext++ = 0;
		e[0] = strtol(ext, NULL, 0);
		if ((ext = strchr(ext, ':')))
			e[1] = strtol(++ext, NULL, 0);

		/* make cut prefix */
		if (e[0] >= 0)
			ext = str + e[0];
		else
			ext = str + strlen(str) + e[0];
		/* make cut suffix */
		if (e[1] > 0)
			*(ext + e[1]) = 0;
	} else
		/* ok, clear show */
		ext = str;

	out = strdup(ext);
	free(str);

	return out;
}


/*
 * ioMkDir() Function for racursive directory creation and validation
 * @csDir = Full directory path
 * @mode = Mode for directory creation if missing dir
 * return: -1 error, 0 directory path exist, >0 created missing dirs
*/
int
ioMkDir(const char *csDir, int mode)
{
	char *str, *s, *pbrk, szOld[MAXPATHLEN] = { 0 };
	register int cx = -1;

	if (!csDir)
		return cx;

	str = strdup(csDir);
	if (!str) {
		LOGERR;
		return cx;
	}

	getcwd(szOld, MAXPATHLEN);
	if (*str == '/')
		chdir("/");

	for (cx = 0, s = strtok_r(str, "/", &pbrk); s; s = strtok_r(NULL, "/", &pbrk)) {
		if (mkdir(s, mode) == -1) {
			if (errno != EEXIST) {
				LOGERR;
				cx = -1;
				goto end;
			}
		} else
			cx++;

		if (chdir(s) == -1) {
			LOGERR;
			cx = -1;
			goto end;
		}
	}
end:
	chdir(szOld);
	free(str);
	return cx;
}

/*
 * ioWatchDirLoop() Function for watching changes in directory and fire callback
 * @csDir = Full directory path
 * @callback = Callback if raise event! nOp -1 delete, 0 change/move, 1 create
 * return: -1 error, !=-1 ok, number of total signaled events
*/
int
ioWatchDirLoop(const char *csDir, int (*callback)(const char *csName, int nOp))
{
	glob_t g[2] = {{ 0 }, { 0 }};
	int d, kq, n = 0;
	register int j, i;
	struct kevent req, chg;
	char wrk[MAXPATHLEN * 2], str[MAXPATHLEN] = { 0 };

	if (!csDir || !callback)
		return 0;

	strlcpy(str, csDir, MAXPATHLEN);
	strlcat(str, "/*", MAXPATHLEN);

	kq = kqueue();
	if (kq == -1) {
		LOGERR;
		return -1;
	}
	d = open(csDir, O_RDONLY);
	if (d == -1) {
		LOGERR;
		close(kq);
		return -1;
	}

	EV_SET(&req, d, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, NULL);

	if ((n = glob(str, GLOB_NOCHECK, NULL, &g[0]))) {
		LOGERR;
		close(d);
		close(kq);
		return -1;
	} /*else
		ioDEBUG(3, "Start files %d in %s\n", g[0].gl_matchc, str);*/

	while (kevent(kq, &req, 1, &chg, 1, NULL) > 0) {
		/*ioDEBUG(1, "Event:: req=0x%x -> chg=0x%x data=%x\n", req.fflags, chg.fflags, chg.data);*/

		if (!glob(str, GLOB_NOCHECK, NULL, &g[1])) {
			/*ioDEBUG(3, "Diffs %d <> %d\n", g[0].gl_matchc, g[1].gl_matchc);*/

			if (g[0].gl_matchc != g[1].gl_matchc) {
				/* find new items */
				for (j = 0; j < g[1].gl_matchc; j++) {
					for (i = 0; i < g[0].gl_matchc; i++)
						if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
							break;
					if (i == g[0].gl_matchc) {
						if (callback(g[1].gl_pathv[j], 1) < 0)
							break;
						else
							n++;
					}
				}
				/* find del items */
				for (j = 0; j < g[0].gl_matchc; j++) {
					for (i = 0; i < g[1].gl_matchc; i++)
						if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
							break;
					if (i == g[1].gl_matchc) {
						if (callback(g[0].gl_pathv[j], -1) < 0)
							break;
						else
							n++;
					}
				}
			} else {
				/* find chg from items */
				for (j = 0; j < g[0].gl_matchc; j++) {
					for (i = 0; i < g[1].gl_matchc; i++)
						if (!strcmp(g[1].gl_pathv[i], g[0].gl_pathv[j]))
							break;
					if (i == g[1].gl_matchc) {
						strlcpy(wrk, g[0].gl_pathv[j], sizeof wrk);
						strlcat(wrk, ":", sizeof wrk);
					}
				}
				/* find chg to items */
				for (j = 0; j < g[1].gl_matchc; j++) {
					for (i = 0; i < g[0].gl_matchc; i++)
						if (!strcmp(g[0].gl_pathv[i], g[1].gl_pathv[j]))
							break;
					if (i == g[0].gl_matchc) {
						strlcat(wrk, g[1].gl_pathv[j], sizeof wrk);
						if (callback(wrk, 0) < 0)
							break;
						else
							n++;
					}
				}
			}

			globfree(&g[0]);
			g[0] = g[1];
		}
	}

	globfree(&g[0]);
	close(d);
	close(kq);
	return n;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>