/* $Id: swlib.c,v 1.23 2000/10/15 04:36:42 jhl Exp jhl $ 
 * swlib.c - General Purpose Routines.
 */

/*
   Copyright (C) 1998-2005  James H. Lowe, Jr.
   All Rights Reserved.
  
   COPYING TERMS AND CONDITIONS
   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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */


#include "swuser_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>		/* needed for mkdir(2) prototype! */
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "misc-fnmatch.h"
#include "taru.h"
#include "uxfio.h"
#include "strob.h"
#include "swfork.h"
#include "shcmd.h"
#include "swpath.h"
#include "swlib.h"
#include "md5.h"
#include "ugetopt_help.h"
#include "atomicio.h"
#include "swutillib.h"
#include "swutilname.h"

static int verbose_levelG;

static unsigned long int * g_pstatbytes = NULL;
static struct timespec * io_req = (struct timespec*)NULL;
static int g_burst_adjust = 65000;

void
e_msg(char * class, char * reason, char * file,
			int line, char * function)
{
	fprintf(stderr, "%s: %s: %s: at %s:%d\n",
		 swlib_utilname_get(), class, reason, file, line);
}

static
int
convert_hex_seq(int d1, int d2)
{
	int n;
	n = d1;	
	if ( isdigit(n) == 0 &&  (n < '\x41' || n > '\x46')) { return -1; }
	if (isdigit(n) == 0) d1 -= 7;
	n = d2;	
	if ( isdigit(n) == 0 &&  (n < '\x41' || n > '\x46')) { return -1; }
	if (isdigit(n) == 0) d2 -= 7;
	return (16 * (d1 - 48)) + (d2 - 48);
}

static
char *
does_have_escape(char * src, int * value)
{
	char * t;
	char *seq;
	int d1;
	int d2;
	
	*value = 0;
	t = strstr(src, "\\x");
	if (t && (t == src || *(t-1) != '\\')) {
		seq = t+2;
		if (*seq == '\0') return (char*)NULL;
		if (*(seq+1) == '\0') return (char*)NULL;
		d1 = toupper((int)(*seq));
		d2 = toupper((int)(*(seq+1)));
		*value = convert_hex_seq(d1, d2);
		if (*value < 0) {
			return (char*)NULL;
		} else {
			return t;
		}
	}
	return (char*)NULL;
}

static
void
process_all_escapes(char * src)
{
	unsigned char * s;
	unsigned char * r;
	int value;

	s = (unsigned char*)src;
	while((r = (unsigned char*)does_have_escape((char*)s, &value))) {
		memmove((void*)(r+1), (void*)(r+4), strlen((char*)(r+4)) + 1);
		*r = (unsigned char)(value);
		s = (r+1);
	}
}

int
doif_writeap(int fd, STROB * buffer, char * format, va_list * pap)
{
	int ret;
	int aret;
	if (fd < 0) return 0;
	ret = strob_vsprintf(buffer, 1, format, *pap);
	if (ret < 0) {
		return -1;
	}
	aret = atomicio(uxfio_write, fd, 
		(void*)strob_str(buffer), strob_strlen(buffer));

	if (aret != ret) return -1;	
	return ret;
}

static
int
squash_trailing_char(char *path, char ch)
{
	if (path && strlen(path) > 1 && (*(path + strlen(path) - 1)) == (int)ch) {
		(*(path + strlen(path) - 1)) = '\0';
		return 0;
	} else {
		return 1;
	}
}

static
int
nano_nanosleep(long nsec)
{
	struct timespec tt;
	tt.tv_sec = 0;
	tt.tv_nsec = nsec;
	return nanosleep(&tt, NULL);
}

static
void
delay_sleep_pattern2(struct timespec * io_req, int * sleepbytes, int c_amount, int  byteswritten)
{
	const int leaddiv = 0;
	int openssh_rcvd_adjust = g_burst_adjust;
	if ( 
		*sleepbytes > openssh_rcvd_adjust && 
		(
			leaddiv == 0 ||
			(
				/* disabled */
			byteswritten > (c_amount/leaddiv) &&
			byteswritten < (c_amount - c_amount/leaddiv)
			)
		)
	)
 	{
		if (verbose_levelG >= SWC_VERBOSE_8)
			fprintf(stderr,
				"%s: pump-delay1: sleeping %d nanoseconds every %d bytes\n",
					swlib_utilname_get(), (int)(io_req->tv_nsec), g_burst_adjust);
		nano_nanosleep(io_req->tv_nsec);
		*sleepbytes = 0;
	}
}

static
int
form_abspath(STROB * sb1, char * s1, char * pcwd)
{
	char cwd[200];
	if (*s1 == '/') return 1;
	if (pcwd == NULL) {
		if (getcwd(cwd, sizeof(cwd) - 2) == NULL) {
			SWLIB_FATAL("path too long");
		}
	} else {
		strncpy(cwd, pcwd, sizeof(cwd) - 2);
	}
	cwd[sizeof(cwd) - 1] = '\0';
	strob_strcpy(sb1, cwd);
	swlib_unix_dircat(sb1, s1);
	return 0;
}

int
swlib_atoi(const char *nptr, int * result)
{
	long ret;
	char * endptr;
	ret = strtol(nptr, &endptr, 10);

	if (result) *result = 0;
	if (
		(endptr == nptr && ret == 0) ||
		((ret == LONG_MIN || ret == LONG_MAX) && errno == ERANGE)
	) {
		if (result) {
			*result = 1;
		}
		fprintf(stderr, "%s: strtol error when converting [%s]\n", swlib_utilname_get(), nptr);
	}
	return (int)ret;
}

unsigned long int
swlib_atoul(const char *nptr, int * result)
{
	unsigned long int ret;
	char * endptr;
	ret = strtoul(nptr, &endptr, 10);

	if (result) *result = 0;
	if (
		(endptr == nptr && ret == 0) ||
		((ret == ULONG_MAX) && errno == ERANGE)
	) {
		if (result) {
			*result = 1;
		}
		fprintf(stderr, "%s: strtoul error when converting [%s]\n", swlib_utilname_get(), nptr);
	}
	return ret;
}

unsigned long int **
swlib_pump_get_ppstatbytes(void)
{
	return &g_pstatbytes;
}

int
swlib_pump_amount7(int ofd, int uxfio_ifd,
			int amount, int adjunct_ofd)
{
	char buf[512];
	size_t rsize;
	int am = amount;
	int rr, wr;
	int sleepbytes = 0;
	int timebytes = 0;
	time_t newt, oldt;

	oldt = newt = time(NULL);

	while (am > 0) {
		rsize = am > (int)sizeof(buf) ? (int)sizeof(buf) : am;
		rr = taru_tape_buffered_read(uxfio_ifd, buf, rsize);
		if (rr < 0)  {
			SWBIS_ERROR_IMPL();
			return -(amount - am);
		}
		if (ofd >= 0) {
			wr = uxfio_write(ofd, buf, rr);
			if (wr != rr) {
				SWBIS_ERROR_IMPL();
				return -(amount - am);
			}
		} else {
			wr = rr;
		}

		if (adjunct_ofd >= 0) {
			if (uxfio_write(adjunct_ofd, buf, rr) != rr) {
				SWBIS_ERROR_IMPL();
				return -(amount - am);
			}
		}
		timebytes += wr;

		if (io_req) {
			/*
			* This is the bug delay
			*/
			sleepbytes += wr;
			delay_sleep_pattern2(io_req, &sleepbytes, amount, amount - am);
		}

		if (g_pstatbytes && timebytes > 10000) {
			/*
			* Reduce the number of checks to see if it
			* time to update the progress bar.
			*/
			if ((newt=time(NULL)) > oldt) {
				timebytes = 0;
				oldt = newt;
				*g_pstatbytes = (amount - am);
				(void)update_progress_meter(SIGALRM);
                	}
		}
		am -= wr;
	}
	return (amount - am);
}

struct timespec * swlib_get_io_req(void)
{
	return io_req;
}

int * swlib_burst_adjust_p(void)
{
	return &g_burst_adjust;
}

struct timespec ** swlib_get_io_req_p(void)
{
	return &io_req;
}

int
swlib_test_verbose(int verbose_level, int swbis_event,
		int is_swi_event, int event_status, int is_posix_event)
{
	if (
		(verbose_level >= SWC_VERBOSE_1 && 
				event_status != 0) ||
		(verbose_level >= SWC_VERBOSE_3 && is_posix_event) ||
		(verbose_level >= SWC_VERBOSE_4 && swbis_event) ||
		(verbose_level >= SWC_VERBOSE_6 && is_swi_event) ||
		(verbose_level >= SWC_VERBOSE_4 && !is_swi_event)
	) {
		return 1;
	} else {
		return 0;
	}
}

int
swlib_get_verbose_level(void)
{
	return verbose_levelG;
}

void
swlib_set_verbose_level(int n)
{
	verbose_levelG = n;
}

int
swlib_i_pipe_pump(int suction_fd, int discharge_fd, 
		int *amount, int adjunct_ofd, 
		ssize_t (*thisfpread)(int, void*, size_t))
{
	int commandFailed = 0;
	int pumpDead = 0;
	int bytes;
	int ibytes;
	int byteswritten = 0;
	int remains;
	int c_amount = *amount;
	char buf[SWLIB_PIPE_BUF];
	int sleepbytes = 0;

	if (c_amount < 0) {
		do {
			bytes = (*thisfpread)(suction_fd, buf, sizeof(buf));
			if (bytes < 0) {
				SWBIS_ERROR_IMPL();
				commandFailed = 1;	
				pumpDead = 1;
			} else if (bytes == 0) {
				/* if it's not dead yet, it will be when we 
				 * close the pipe 
				*/
				pumpDead = 1;
			} else {
				if (discharge_fd >= 0) {
					if (uxfio_write(discharge_fd, 
						buf, 
						bytes) != bytes) {
						SWBIS_ERROR_IMPL();
						commandFailed = 2;
						/* discharge failure */
						pumpDead = 1;
					}
				}

				if (adjunct_ofd >= 0) {
					if (uxfio_write(adjunct_ofd, 
						buf, bytes) != bytes) {
						SWBIS_ERROR_IMPL();
						commandFailed = 3;
						/* discharge failure */
						pumpDead = 1;
					}
				}
			}
			byteswritten += bytes;
			if (io_req) {
				sleepbytes += bytes;
				delay_sleep_pattern2(io_req, &sleepbytes, c_amount, byteswritten);
			}
		} while (!pumpDead);
		*amount = byteswritten;
		return commandFailed;
	} else {
		remains = sizeof(buf);
		if ((c_amount - byteswritten) < remains) {
			remains = c_amount - byteswritten;
		}
		do {
			bytes = (*thisfpread)(suction_fd, buf, remains);
			if (bytes < 0) {
				SWBIS_ERROR_IMPL();
				commandFailed = 1;
				pumpDead = 1;
			} else if (bytes == 0) {
				/* if it's not dead yet, it will 
				* be when we close the pipe 
				*/
				pumpDead = 1;
			} else if (bytes) {

				if (discharge_fd >= 0) {
					ibytes = uxfio_write(discharge_fd, 
							buf, bytes);
					if (adjunct_ofd >= 0) {
						ibytes = uxfio_write(
							adjunct_ofd, 
							buf, bytes);
					}	
				} else {
					ibytes = bytes;
				}

				if (ibytes != bytes) {
					SWBIS_ERROR_IMPL();
					commandFailed = 2;
					/* discharge failure */
					pumpDead = 1;
				} else {
					byteswritten += bytes;
					if ((c_amount - byteswritten) < 
								remains) {
						if (
					(remains = c_amount - byteswritten) > 
						(int)(sizeof(buf))
						) {
							remains = sizeof(buf);
						}
					}
				}
			}
			if (io_req) {
				sleepbytes += bytes;
				delay_sleep_pattern2(io_req, &sleepbytes, c_amount, byteswritten);
			}
		} while (!pumpDead && remains);
		*amount = byteswritten;
		return commandFailed;
	}
	return -1;
}

int
swlib_is_ascii_noaccept(char * str, char * acc, int minlen) {
	unsigned char *s;
	int ret;
	if (!str || ((int)strlen(str) < minlen)) return 1;
	ret = strpbrk(str, acc) ? 1 : 0;
	if (ret) return ret;
	s = (unsigned char*)str;
	while (s && *s) {
		if (*s > 126 || *s < 33) {
			return 1;
		}
		s++;
	}
	return 0;
}

int
swlib_tr(char * src, int to, int from)
{
	int ret = 0;
	char * p1;

	p1 = src;
	while (*(p1++)) {
		if (*(p1 - 1) == from) {
			*(p1 - 1) = to;
			ret ++;
		}
	}
	return ret;
}

int
swlib_check_safe_path(char * s)
{
	if (!s) return 1;
	if (!strlen(s)) return 5;
	if (strstr(s, "..")) return 6;
	if (strchr(s, ' ')) return 7;
	return swlib_is_sh_tainted_string(s);
}

int
swlib_check_clean_path(char * s)
{
	if (!s) return 1;
	if (!strlen(s)) return 2;
	if (strstr(s, "//")) return 3;
	if (strstr(s, "..")) return 4;
	if (strchr(s, ' ')) return 5;
	if (swlib_is_sh_tainted_string(s)) return 6;
	return strpbrk(s, SWBIS_WS_TAINTED_CHARS) ? 7 : 0;
}

int
swlib_check_clean_absolute_path(char * s)
{
	if (!s) return 1;
	if (!strlen(s)) return 2;
	if (*s != '/') return 3;
	return swlib_check_clean_path(s);
}

int
swlib_check_clean_relative_path(char * s)
{
	if (!s) return 1;
	if (*s == '/') return 2;
	return swlib_check_clean_path(s);
}

int
swlib_check_legal_tag_value(char * s) {
	if (!s) return 0; /* Ok */
	return strpbrk(s, ":.,") ? 1 : 0;
}

int
swlib_is_sh_tainted_string(char * s) {
	if (!s) return 0;
	return strpbrk(s, SWBIS_TAINTED_CHARS) ? 1 : 0;
}

void
swlib_is_sh_tainted_string_fatal(char * s) {
	if (!s) return;
	if (strpbrk(s, SWBIS_TAINTED_CHARS) ? 1 : 0) {
		SWLIB_FATAL("tainted string");
	}
	return;
}

ssize_t
swlib_safe_read(int fd, void * buf, int nbyte)
{
	int n=0, nret=1;
	char *p = (char*)(buf);
	
	while(n < nbyte && nret){
		nret = uxfio_read(fd, p+n, nbyte-n); 
		if (nret < 0) {
			if (errno == EINTR || errno == EAGAIN) {
				continue;
			}
			return nret;
		}
		n+=nret;	
	}
	return n;
}

void
swlib_swprog_assert(int error_code, int status, char * reason,
		char * version, char * file, int line, char * function)
{
	if (error_code != 0 && 
		error_code < SWBIS_PROGS_USER_ERROR && 
			error_code >= SWBIS_PROGS_IMPLEMENTATION_ERROR) {
		fprintf(stderr, 
	"%s: Error: code=[%d] : %s: version=[%s] file=[%s] line=[%d]\n",
			swlib_utilname_get(), error_code, reason, version, file, line);
		exit(status);
	} else if (error_code != 0 && 
			error_code >= SWBIS_PROGS_USER_ERROR) {
		exit(status);
	} else if (error_code != 0 && 
			error_code < SWBIS_PROGS_IMPLEMENTATION_ERROR ) {
		fprintf(stderr, 
		"Internal Informative Warning: code=%d : %s version=[%s]"
		", file=[%s], line=[%d]\n", 
			error_code, reason, version, file, line);
	}
}

void
swlib_exception(char * reason, char * file,
			int line, char * function)
{
	e_msg("program exception", reason,file, line, function);
}

void
swlib_internal_error(char * reason, char * file,
			int line, char * function)
{
	e_msg("internal implementation error", reason,file, line, function);
}

void
swlib_resource(char * reason, char * file,
			int line, char * function)
{
	e_msg("resource exception", reason,file, line, function);
}

void
swlib_fatal(char * reason, char * file,
			int line, char * function)
{
	e_msg("fatal error", reason,file, line, function);
	exit(252);
}

void
swlib_assertion_fatal(int assertion_result, char * reason, char * file,
			int line, char * function)
{
	if (assertion_result == 0)
		swlib_fatal(reason, file, line, function);
}

void
swlib_squash_all_trailing_vnewline(char *path)
{
	while (1) 
		if (squash_trailing_char(path, '\n') == 0) {
			squash_trailing_char(path, '\r');
		} else {
			break;
		}
}

void
swlib_squash_trailing_vnewline(char *path)
{
	squash_trailing_char(path, '\n');
	squash_trailing_char(path, '\r');
}

void
swlib_squash_trailing_slash(char *path)
{
	squash_trailing_char(path, '/');
	/*
	if (strlen(path) > 1 && (*(path + strlen(path) - 1)) == '/')
		(*(path + strlen(path) - 1)) = '\0';
	*/
	return;
}

void
swlib_squash_embedded_dot_slash(char *path)
{
	char *p1;
	p1 = strstr(path, "/./");
	if (p1 && p1 != path) {
		memmove(p1+1, p1 + 3, strlen(p1 + 2));
	} else if (p1 && p1 == path) {
		memmove(p1+1, p1 + 3, strlen(p1 + 2));
	}
}

void
swlib_squash_double_slash(char *path)
{
	char *p1;
	while ((p1 = strstr(path, "//")) != (char *) (NULL))
		memmove(p1, p1 + 1, strlen(p1));
}

char *
swlib_return_no_leading(char *path)
{
	char * s;
	if ((s = strstr(path, "/")) == path) return ++path;
	if ((s = strstr(path, "./")) == path) return path+=2;
	return path;
}

void
swlib_squash_all_dot_slash(char *path)
{
	char * s;

	swlib_squash_leading_dot_slash(path);
	s = strstr(path, "/./");
	while(s) {
		swlib_squash_embedded_dot_slash(path);
		s = strstr(path, "/./");
	}
}

void
swlib_squash_leading_dot_slash(char *path)
{
	char *p1;
	/* 
	* squash leading "./" 
	*/
	if (strlen(path) >= 3) {
		if (!strncmp(path, "./", 2)) {
			p1 = path;
			memmove(p1, p1 + 2, strlen(p1 + 1));
		}
	}
}

void
swlib_toggle_trailing_slashdot(char * mode, char * name, int *pflag)
{
	if (strcmp(mode, "drop") == 0) {
		if (strlen(name) < 2) {
			*pflag = 0;
			return;
		}
		if (strcmp(name + strlen(name) - 2,  "/.") == 0) {
			*(name + strlen(name) - 2) = '\0';
			*pflag = 1;
		}
	} else {
		if (*pflag) {
			strcat(name, "/.");
			*pflag = 0;
		}
	}
}

void
swlib_toggle_leading_dotslash(char * mode, char * name, int *pflag)
{
	if (strcmp(mode, "drop") == 0) {
		if (strlen(name) < 2) {
			*pflag = 0;
			return;
		}
		if (strncmp(name,  "./", 2) == 0) {
			memmove(name, name+2, strlen(name)-1);
			*pflag = 1;
		}
	} else {
		if (*pflag) {
			if (strcmp(name, ".") == 0) {
				strcat(name, "/");
			} else {
				memmove(name+2, name, strlen(name)+1);
				*name = '.';
				*(name +1) = '/';
				*pflag = 0;
			}
		}
	}
}

void
swlib_toggle_trailing_slash(char * mode, char * name, int *pflag)
{
	if (strcmp(mode, "drop") == 0) {
		if (name[strlen(name) - 1] == '/') {
			name[strlen(name) - 1] = '\0';
			*pflag = 1;
		} else {
			*pflag = 0;
		}
	} else {
		if (*pflag) {
			name[strlen(name)] = '/';
		}
	}
}

void
swlib_squash_leading_slash(char * name)
{
	if (*name == '/') {
		memmove(name, name+1, strlen(name));
	}
}

char *
swlib_return_relative_path(char * path) {
	char * s = path;
	while(*s == '/') s++;
	return s;
}

void
swlib_squash_all_leading_slash(char * name)
{
	int s;
	int i;
	if (!name) return;

	s = strlen(name);
	i = (*name == '/');

	while(*name == '/') swlib_squash_leading_slash(name);

	if (i && s && strlen(name) == 0) {
		/*
		* squashed '/' down to nothing.
		*/
		strncpy(name, ".", 2);
	}
}

void
swlib_toggle_leading_slash(char * mode, char * name, int *pflag)
{

	if (strcmp(mode, "drop") == 0) {
		if (*name == '/') {
			memmove(name, name+1, strlen(name));
			*pflag = 1;
		} else {
			*pflag = 0;
		}
	} else if (strcmp(mode, "restore") == 0) {
		if (*pflag) {
			memmove(name+1, name, strlen(name)+1);
			*name = '/';
			*pflag = 0;
		}
	}
}

void
swlib_slashclean(char *path)
{
	/* 
	* squash double slashes in path 
	*/
	swlib_squash_double_slash(path);

	swlib_squash_leading_dot_slash(path);

	/* squash leading slash
	* if (*path == '/' && strlen(path) > 1)
	* 	memmove(path, path + 1, strlen(path));
	*/

	swlib_squash_trailing_slash(path);
	return;
}

int
swlib_process_hex_escapes(char * s1)
{
	process_all_escapes(s1);
	return 0;	
}

int
swlib_compare_8859(char * s1, char * s2)
{
	int ret;
	char *r1;
	char *r2;
	int value;
	
	if (does_have_escape(s1, &value)) {
		r1 = strdup(s1);
		process_all_escapes(s1);
	} else {
		r1 = (char*)NULL;
	}

	if (does_have_escape(s2, &value)) {
		r2 = strdup(s2);
		process_all_escapes(s2);
	} else {
		r2 = (char*)NULL;
	}

	ret = swlib_dir_compare(s1, s2, SWC_FC_NOAB /*ignore absolute path*/);

	if (r1) {
		strcpy(s1, r1);
		free(r1);
	}

	if (r2) {
		strcpy(s2, r2);
		free(r2);
	}

	return ret;
}

int
swlib_vrelpath_compare(char * s1, char * s2, char * cwd)
{
	if (
		(
		(*s1 != '/')  &&
		(*s2 != '/')
		) ||
		(
		(*s1 == '/')  &&
		(*s2 == '/')
		) 
	) {
		return swlib_dir_compare(s1, s2, SWC_FC_NOOP);
	} else {
		STROB * tmp = strob_open(100);
		if (form_abspath(tmp, s1, cwd) == 0) {
			/*
			* s1 was the relative path, it is now 
			* in (STROB*)(tmp) as an absolute path.
			*
			* s2 is already absolute.
			*/
			return swlib_dir_compare(strob_str(tmp), s2, SWC_FC_NOOP);
		} else if (form_abspath(tmp, s2, cwd) == 0) {
			return swlib_dir_compare(s1, strob_str(tmp), SWC_FC_NOOP);
		} else {
			/* Never gets here.
			*/
			SWLIB_ALLOC_ASSERT(0);	
			;
		}
	}
	/* Never gets here.
	*/
	SWLIB_ALLOC_ASSERT(0);	
	return -1;
}

int
swlib_basename_compare(char * s1, char * s2)
{
	char *s1b;
	char *s2b;
	int ret;

	s1b = strrchr(s1, '/');
	s2b = strrchr(s2, '/');
	if (s1b && strlen(s1) && *(s1b+1)) {
		s1 = s1b + 1;
	}
	if (s2b && strlen(s2) && *(s2b+1)) {
		s2 = s2b + 1;
	}
	ret = swlib_dir_compare(s1, s2, SWC_FC_NOOP);
	return ret;
}

int
swlib_dir_compare(char * s1, char * s2, int FC_compare_flag)
{
	int ret;
	int leading_p1 = 0;
	int leading_p2 = 0;
	int leading_d1 = 0;
	int leading_d2 = 0;
	int trailing_p1 = 0;
	int trailing_d1 = 0;
	int trailing_p2 = 0;
	int trailing_d2 = 0;


	if (FC_compare_flag == SWC_FC_NOAB) {
		swlib_toggle_leading_slash("drop", s1, &leading_p1);
		swlib_toggle_leading_slash("drop", s2, &leading_p2);
	} else if (FC_compare_flag == SWC_FC_NORE) {
		/*
		* Not used, dead code.
		*/
		;
	} else {
		;
	}

	swlib_toggle_trailing_slash("drop", s1, &trailing_p1);
	swlib_toggle_trailing_slash("drop", s2, &trailing_p2);

	swlib_toggle_trailing_slashdot("drop", s1, &trailing_d1);
	swlib_toggle_trailing_slashdot("drop", s2, &trailing_d2);
	
	swlib_toggle_leading_dotslash("drop", s1, &leading_d1);
	swlib_toggle_leading_dotslash("drop", s2, &leading_d2);

	ret = strcmp(s1, s2);	
	
	swlib_toggle_leading_dotslash("restore", s1, &leading_d1);
	swlib_toggle_leading_dotslash("restore", s2, &leading_d2);
	
	swlib_toggle_trailing_slashdot("restore", s1, &trailing_d1);
	swlib_toggle_trailing_slashdot("restore", s2, &trailing_d2);
	
	swlib_toggle_trailing_slash("restore", s1, &trailing_p1);
	swlib_toggle_trailing_slash("restore", s2, &trailing_p2);


	if (FC_compare_flag == SWC_FC_NOAB) {
		swlib_toggle_leading_slash("restore", s1, &leading_p1);
		swlib_toggle_leading_slash("restore", s2, &leading_p2);
	} else if (FC_compare_flag == SWC_FC_NORE) {
		/*
		* never gets here
		*/
		;
	} else {
		;
	}

	return ret;
}

char *
swlib_dirname(STROB * dest, char * source)
{
	char * s;
	strob_strcpy(dest, source);
	s = strrchr(strob_str(dest), '/');
	if (!s) {
		strob_strcpy(dest, ".");
	} else {
		if (s != strob_str(dest))
			*s = '\0';
		else
			*(s+1) = '\0';
	}
	return strob_str(dest);
}

char *
swlib_basename(STROB * dest, char * source)
{
	char * s;

	if (dest == NULL) {
		s = strrchr(source, '/');
		if (!s) return source;
		s++;	
		while (*s == '/') s++;
		if (*s == '\0') {
			if (s > source) return s-1;
			return source;
		}
		return s;
	} else {
		strob_strcpy(dest, source);
		s = swlib_basename(NULL, strob_str(dest));
		memmove(strob_str(dest), s, strlen(s)+1);
		return strob_str(dest);
	}
}

int
swlib_unix_dircat(STROB * dest, char * dirname)
{
	char * newp;
	char * s = strob_str(dest);
	
	if (!dirname || strlen(dirname) == 0) return 0;
	if (strlen(s)) {
		if (s[strlen(s) - 1] != '/')
			strob_strcat(dest, "/");
	}
	newp = swlib_strdup(dirname);
	SWLIB_ALLOC_ASSERT(newp != NULL);	
	
	if (strob_strlen(dest))
		swlib_squash_leading_dot_slash(newp);
	strob_strcat(dest, newp);
	swbis_free(newp);
	return 0;
}

int
swlib_resolve_path(char * ppath, int * depth, STROB * resolved_path)
{
	STROB * tmp;
	int startnames = 0;
	int count = 0;
	int numcomponents = 0;
	char * path = swlib_strdup(ppath);	
	char * s;

	swlib_slashclean(path);
	tmp = strob_open(10);

	if (resolved_path) strob_strcpy(resolved_path, "");

	s = strob_strtok(tmp, path, "/");
	while (s) {
		if (strcmp(s, "..") == 0 && !startnames) {
			count++;
		} else if (strcmp(s, ".") == 0) {
			/* do nothing */
		} else if (strcmp(s, "..") == 0 && startnames) {
			count--;
		} else {
			count++;
			startnames = 1;
		}
		
		if (resolved_path) {
			strob_strcat(resolved_path, s);
			strob_strcat(resolved_path, "/");
		}

		numcomponents ++;
		s = strob_strtok(tmp, NULL, "/");
	}

	if (depth) *depth = count;
	strob_close(tmp);
	swbis_free(path);
	return numcomponents;
}

int
swlib_exec_filter(SHCMD ** cmd, int feed_pump_on)
{
	int input_pipe[2];
	int ifd, ofd, i=0;
	int childi;
	int retval;

	ifd=shcmd_get_srcfd(cmd[0]);
	while (cmd[i]) i++;
	ofd=shcmd_get_dstfd(cmd[--i]);

	if (feed_pump_on) {
		pipe(input_pipe);
		childi = swfork((sigset_t*)(NULL));
		if (childi==0) {
			close(0);
			close(1);
			close(input_pipe[0]);
			close(ofd);
			retval = swlib_pump_amount(input_pipe[1], ifd, -1);
			_exit(retval);
		}
		if (childi < 0) {
			SWLIB_INTERNAL("swlib_exec_filter: 0001.");
			return -(INT_MAX);
		}
		close(input_pipe[1]);
		shcmd_set_srcfd(cmd[0], input_pipe[0]);
		
		shcmd_command(cmd);
		close(input_pipe[1]);
	} else {
		shcmd_command(cmd);
	}
	return shcmd_wait(cmd);
}

int
swlib_read_amount(int suction_fd, int amount)
{
	return swlib_pump_amount(-1, suction_fd, amount);
}

int
swlib_pipe_pump(int ofd, int ifd)
{
	return 
	swlib_pump_amount(ofd, ifd, -1);
}

int
swlib_pump_amount(int discharge_fd, int suction_fd, int amount)
{
	int i = amount;
	if (swlib_i_pipe_pump(suction_fd, discharge_fd,  &i, 
			-1, uxfio_read)) {
		return -1;
	}
	return i;
}

int
swlib_fork_to_make_unixfd(int uxfio_fd, sigset_t * blockmask, 
		sigset_t * defaultmask, int * ppid)
{
	int upipe[2];
	pid_t upid;

#ifndef OPEN_MAX
#define OPEN_MAX 256
#endif
	if (ppid) *ppid = (int)0;
	if (uxfio_fd <= OPEN_MAX) 
		return uxfio_fd;

	if (pipe(upipe) < 0) {
		return -1;
	}
	if ((upid = swndfork(blockmask, defaultmask)) > 0) {	/* parent */
		uxfio_close(uxfio_fd);
		close(upipe[1]);
		if (ppid) *ppid = (int)upid;
		return upipe[0];
	} else if (upid == 0) {	/* child */
		int ret = 0;
		close(upipe[0]);
		ret = swlib_pipe_pump(upipe[1], uxfio_fd);
		if (ret >= 0) ret = 0;
		else ret = 255;
		_exit(ret);
	} else {
		SWLIB_RESOURCE("fork failed");
		return -1;
	}
}

int 
swlib_is_escapable(char c) {
    /* 
    * Is it a ANSI C escapeable character 
    */
     if ( (c == 'n') ||
          (c == 't') ||
          (c == 'v') ||
          (c == 'b') ||
          (c == 'r') ||
          (c == 'f') ||
          (c == 'x') ||
          (c == 'a') ||
          (c == '\\') ||
          (c == '?') ||
          (c == '\'') ||
          (c == '\"')
        ) return 1;
     else
          return 0;
}

o__inline__
char *
swlib_strdup(char *s)
{
	return strdup(s);
}

char *
swlib_strncpy(char * dst, const char * src, size_t n)
{
	char *p = strncpy(dst, src, n-1);
	dst[n-1] = '\0';
	return p;
}

int
swlib_writef(int fd, STROB * buffer, char * format, ...)
{
	int ret;
	int newret;
	va_list ap;
	va_start(ap, format);
	ret = strob_vsprintf(buffer, 0, format, ap);
	va_end(ap);
	if (ret < 0) return -1;
	newret = atomicio(uxfio_write, fd, 
			(void*)strob_str(buffer), (size_t)(ret));
	if (newret != ret) return -1;
	return newret;
}

/*
*     swlib_write_catalog_stream
*  write out the catalog half of the package.
*  This does not decode/re-encode the tar header, it
*  passes thru the original bits.
*   * version 0 OBSOLETE *
*/
int
swlib_write_OLDcatalog_stream(XFORMAT * package, int ofd)
{
	int ret;
	ret = taruib_write_catalog_stream((void*)package, 
				ofd, /* version */ 0, /*verbose*/ 0);
	return  ret;
}

int
swlib_write_catalog_stream(XFORMAT * package, int ofd)
{
	int ret;
	ret = taruib_write_catalog_stream((void*)package, 
				ofd, /* version */ 1, /*verbose*/ 0);
	return  ret;
}

/*
*     swlib_write_storage_stream
*  write out the storage half of the package.
*  This does not decode/re-encode the tar header, it
*  passes thru the original bits.
*   * version 0 OBSOLETE *
*
*/
int
swlib_write_OLDstorage_stream(XFORMAT * package, int ofd)
{
	int ret;
	ret = taruib_write_storage_stream((void *)package, ofd, 
			 /*version*/ 0, -1, /*verbose*/ 0, 0 /* md5sum */);
	return ret;
}

int
swlib_write_storage_stream(XFORMAT * package, int ofd)
{
	int ret;
	ret = taruib_write_storage_stream((void *)package, ofd,
			/*version*/ 1, -1, /*verbose*/ 0, 0 /* md5sum */);
	return ret;
}

/*
*     swlib_write_signing_files
*   Does decode and reencode the headers, if the input format is identical
*   to the swbis writing implementation then the output will be the same
*   format as the input.
*/
int
swlib_write_signing_files(XFORMAT * package, int ofd, 
		int which_file /* 0=catalog  1=storage */,
		int do_adjunct_md5)
{
	int nullfd;
	int ifd = xformat_get_ifd(package);
	int ret;
	int bytesret;
	int retval = 0;
	STROB * namebuf = strob_open(100);
	SWPATH * swpath = swpath_open("");
	char * name;
	char nullblock[512];
	int writeit;
	int do_trailer = 0;
	int format = xformat_get_format(package);
	long int bytes = 0;

	memset(nullblock, '\0', sizeof(nullblock));

	if (!swpath) return -21;
	if (ifd < 0) return -32;

	nullfd = open("/dev/null", O_RDWR, 0);
	if (nullfd < 0) return -2;

	while ((ret = xformat_read_header(package)) > 0) {
		if (xformat_is_end_of_archive(package)){
			break;
		}
		xformat_get_name(package, namebuf);
		name = strob_str(namebuf);
		swpath_parse_path(swpath, name);

		/*
		* swpath_get_is_catalog() returns 
		*            -1 for leading directories
		*             1 for /catalog/
		*             0 for storage
		*/
		if (swpath_get_is_catalog(swpath) == SWPATH_CTYPE_DIR) {
			/*
			* Leading directories.  They belong in the storage
			* file.
			*/
			if (which_file == 0)
				writeit = nullfd;
			else 
				writeit = ofd;
		} else if (swpath_get_is_catalog(swpath) == SWPATH_CTYPE_CAT) {
			/*
			* /catalog/
			*/
			if (which_file == 0)
				writeit = ofd;
			else	
				writeit = nullfd;
		} else if (swpath_get_is_catalog(swpath) == SWPATH_CTYPE_STORE) {
			/*
			* storage section.
			*/
			if (which_file == 0) {
				/*
				* Must be finished writing the catalog section.
				* Now write out the null blocks if tar format.
				*/
				do_trailer = 1;
				break;
			} else { 			
				if (do_adjunct_md5) {
					int filetype = 
						xformat_get_tar_typeflag(
								package);
					if (filetype != REGTYPE && 
							filetype != DIRTYPE)
						writeit = nullfd;
					else
						writeit = ofd;
				} else {
					writeit = ofd;
				}
			}
		} else {
			/*
			* Internal error.
			*/
			SWLIB_INTERNAL("internal error returned by swpath_get_is_catalog");
			retval = -1;
			break;
		}

		bytesret = 0;
		xformat_set_ofd(package, writeit);
		bytesret += xformat_write_header(package);
		bytesret += xformat_copy_pass(package, writeit, ifd);
		if (writeit != nullfd) {
			bytes += bytesret;
		}
	}

	if (do_trailer && ( format == arf_ustar || format == arf_tar )) {
		uxfio_write(ofd, nullblock, 512);
		uxfio_write(ofd, nullblock, 512);
	}

	if (which_file == 1) {
		/*
		* write out the trailer.
		*/
		retval += taru_write_archive_trailer(package->taruM, 
						arf_ustar, ofd, 512, 
				(int)bytes, 
				xformat_get_tarheader_flags(package));
	}

	close(nullfd);
	strob_close(namebuf);
	swpath_close(swpath);
	return retval;
}

int
swlib_kill_all_pids(pid_t * pid, int num, int signo, int verbose_level)
{
	int i;
	int ret = 0;
	for(i=0; i<num; i++) {
		if (pid[i] > 0) {
			swlib_doif_writef(verbose_level,  SWC_VERBOSE_SWIDB,
				/* (struct sw_logspec *) */(NULL), STDERR_FILENO,
				"swlib_kill_all_pids: kill[%d] signo=%d\n",
							(int)pid[i], signo);
			if (kill(pid[i], signo) < 0) {
				swlib_doif_writef(verbose_level,  SWC_VERBOSE_SWIDB,
					/* (struct sw_logspec *)*/ (NULL), STDERR_FILENO,
					"kill[%d] signo=%d : error : %s\n",
						(int)pid[i],
						signo, strerror(errno));
				ret++;
			}
		}
	}
	return ret;
}

int
swlib_update_pid_status(pid_t keypid, int value, pid_t * pid, int * status, int len)
{
	int i = 0;
	if (len == 0) return 0;
	for(i=0; i<len; i++) {
		if (pid[i] == keypid) {
			status[i] = value;
			pid[i] = -pid[i];
			return 0;
		}
	}
	return -1;
}

int
swlib_wait_on_all_pids(pid_t * pid, int num, int * status, 
				int flags, int verbose_level)
{
	int wret;
	int done = 0;
	int i = 0;
	int got_one = 0;
	if (num == 0) return 0;
	while (!done) {
		done = 1;
		usleep(100000);  /* Sleep 1/10th second, this greatly */
				 /* improves performance on Cygwin    */
		for(i=0; i<num; i++) {
			if (pid[i] > 0) {
				wret = waitpid(pid[i], &status[i], flags);
				if (wret < 0) {
					swlib_doif_writef(verbose_level,
						SWC_VERBOSE_SWIDB, 
						NULL, STDERR_FILENO,
				"swlib_wait_on_all_pids[%d]: error : %d %s\n",
						(int)pid[i], (int)pid[i], 
						strerror(errno));
					status[i] = 0;
					pid[i] = -pid[i];
					/* return -1; */
				} else if (wret == 0) {
					swlib_doif_writef(verbose_level,
						SWC_VERBOSE_SWIDB,
						NULL, STDERR_FILENO,
"swlib_wait_on_all_pids[%d]: returned zero waiting for process id %d\n",
					(int)pid[i], (int)pid[i]);
					done = 0;
				} else {
					swlib_doif_writef(verbose_level,
					SWC_VERBOSE_SWIDB,
					NULL, STDERR_FILENO,
		"swlib_wait_on_all_pids[%d]: returned exitval=%d\n",
					(int)pid[i], WEXITSTATUS(status[i]));
					got_one = 1;
					pid[i] = -pid[i];
				}
			}
		}
	}
	return status[num-1];
}

int 
swlib_sha1(int uxfio_fd, char *digest) {
	int i;
	int ret;	
	unsigned char resblock[21];
	int digest_hex_bytes = 40;
	char * p;

	ret = sha_stream(uxfio_fd, resblock);
	p = digest;
	for (i = 0; i < (digest_hex_bytes / 2); ++i) {
		sprintf(p,"%02x", resblock[i]);
		p += 2;
	}
	digest[digest_hex_bytes] = '\0';
	return ret;
}

int
swlib_digests(int ifd, char * md5, char * sha1)
{
	int n;
	int blocksize = 4096;  /* this must match the sha_block size */
	int digest_hex_bytes = 40;
	int amount;
	int am;
	char * p5;
	unsigned char buf[4096];
	unsigned char res_sha1[21];
	MD5_CTX md5ctx;
	char sha1ctx[512];
	char * p;
	int i;

	swlib_md5_from_memblocks(&md5ctx, md5, (unsigned char*)NULL, -1);
	sha_block((void*)sha1ctx, res_sha1, NULL, -1);

	n = 1;
	while (n > 0) {
		n = swlib_safe_read(ifd, buf, blocksize);
		if (n < 0) return -1;
		if (n) {
			{
				p5 = (char*)buf;
				am = n;
				while(am > 0) {
					amount = am > 1024 ?  1024 : am;
					swlib_md5_from_memblocks(&md5ctx, md5,
						 (unsigned char*)p5, amount);
					p5 += amount;
					am -= amount;
				}
			}
			sha_block((void*)sha1ctx, res_sha1, buf, n);
		} 
	}
	if (n < 0) return -1;

	swlib_md5_from_memblocks(&md5ctx, md5, (unsigned char*)NULL, 0);
	sha_block((void*)sha1ctx, res_sha1, (unsigned char*)NULL, 0); 

	sha1[0] = '\0';
	sha1[40] = '\0';
	p = sha1;
	for (i = 0; i < (digest_hex_bytes / 2); ++i) {
		sprintf(p,"%02x", res_sha1[i]);
		p += 2;
	}
	sha1[digest_hex_bytes] = '\0';

	md5[32] = '\0';
	sha1[40] = '\0';
	return 0;
}

int
swlib_md5_copy(int ifd, int count, char * md5, int ofd)
{
	int n;
	unsigned char buf[4096];
	int blocksize = 4096;  /* this must match the sha_block size */
	int amount;
	int readamount;
	int ret;
	int retval = 0;
	int am;
	char * p5;
	MD5_CTX md5ctx;

	swlib_md5_from_memblocks(&md5ctx, md5, (unsigned char*)NULL, -1);

	do {
		readamount = (count - retval) > blocksize ? blocksize : (count - retval);
		n = atomicio(uxfio_read, ifd, buf, readamount);
		if (n < 0) return -1;
		if (n) {
			{
				p5 = (char*)buf;
				am = n;
				while(am > 0) {
					amount = am > 1024 ?  1024 : am;
					swlib_md5_from_memblocks(&md5ctx, md5,
						 (unsigned char*)p5, amount);
					p5 += amount;
					am -= amount;
				}
			}
			if (ofd > 0) {
				ret = atomicio(
					(ssize_t (*)(int, void *, size_t))(write),
					ofd, buf, n);
				if (ret < 0) return -1;
				retval += ret;
			} else {
				retval += n;
			}
		}
	} while (retval < count);
	if (n < 0) return -1;

	swlib_md5_from_memblocks(&md5ctx, md5, (unsigned char*)NULL, 0);

	md5[32] = '\0';
	return retval;
}

int
swlib_shcmd_output_fd(SHCMD ** cmdvec)
{
	int status;
	SHCMD ** vc;
	SHCMD * lastcmd;
	int ot[2];
	pid_t pid;
	int fd;
		
	vc = cmdvec;
	while(*vc) vc++;
	if (vc == cmdvec) return -1;
	vc--;
	lastcmd = *vc;

	fd = uxfio_open("/dev/null", O_RDONLY, 0);
	if (fd < 0) return fd;
	uxfio_fcntl(fd, UXFIO_F_SET_BUFACTIVE, UXFIO_ON);
	uxfio_fcntl(fd, UXFIO_F_SET_BUFTYPE, UXFIO_BUFTYPE_DYNAMIC_MEM);

	pipe(ot);
	pid = swfork((sigset_t*)(NULL));
	if (pid < 0) return (int)pid;
	if (pid == 0) {
		close(ot[0]);
		shcmd_set_dstfd(lastcmd, ot[1]);
		shcmd_cmdvec_exec(cmdvec);
		shcmd_cmdvec_wait(cmdvec);
		close(ot[1]);
		_exit(0);
	}
	close(ot[1]);
	swlib_pipe_pump(fd, ot[0]);

	waitpid(pid, &status, 0);

	uxfio_lseek(fd, (off_t)(0), SEEK_SET);
	close(ot[0]);
	return fd;
}

int
swlib_shcmd_output_strob(STROB * output, SHCMD ** cmdvec)
{
	char * base = (char*)NULL;
	int data_len = 0;
	int buffer_len = 0;
	int fd;
	strob_strcpy(output, "");
	fd = swlib_shcmd_output_fd(cmdvec);
	if (fd < 0) return -1;
	if (uxfio_get_dynamic_buffer(fd, &base, &buffer_len, &data_len) < 0)
		return -1;
	strob_strncat(output, base, data_len);
	uxfio_close(fd);
	return 0;
}

mode_t 
swlib_apply_mode_umask(char type, mode_t umask, mode_t mode) { 
	if (mode == 0) {
		/* make up default. */
		if (type == SW_ITYPE_d) {
			mode = 0777;
		} else {
			mode = 0666;
		}
	}
	mode &= ~umask;
	return mode;	
}

int
swlib_open_nullfd(void)
{
	int fd = open("/dev/null", O_RDWR, 0);
	if (fd < 0) {
		SWLIB_FATAL("open error");
	}
	return fd;
}

int
swlib_close_nullfd(int fd)
{
	return close(fd);
}

int
swlib_open_memfd(void)
{
	int fd = uxfio_open("/dev/null", O_RDONLY, 0);
	if (fd < 0) return fd;
	/* swi_assert_value(fd >= 0, __FILE__, __LINE__); */
	uxfio_fcntl(fd, UXFIO_F_SET_BUFACTIVE, UXFIO_ON);
	uxfio_fcntl(fd, UXFIO_F_SET_BUFTYPE, UXFIO_BUFTYPE_DYNAMIC_MEM);
	return fd;
}

int
swlib_pad_amount(int fd, int amount)
{
	char nullblock[512];
	int remains = amount;
	int am;
	int ret;
	int count = 0;

	memset(nullblock, '\0', sizeof(nullblock));
	while (remains > 0) {
		if (remains > 512)
			am = 512;
		else
			am = remains;
		if (am <= 0) break;
		ret = atomicio((ssize_t (*)(int, void *, size_t))write,
			fd, nullblock, am);
		
		if (ret <= 0) {
			SWLIB_INTERNAL("")
			if (count <= 0) return -1;
			return -count;
		}
		count += ret;
		remains -= ret;
	}
	return count;
}

int
swlib_drop_root_privilege(void)
{
	int ret = 1;
	if (getuid() == (uid_t)(0)) {
		uid_t nob;
		/*
		 * Only attempt to drop root privilidge if
		 * you are root
		 */
		if (taru_get_uid_by_name("nobody", &nob) < 0) {
			fprintf(stderr, 
	     "%s: Warning: the uname [nobody] not found, not dropping privilege.\n",
			swlib_utilname_get());
			ret = 3;
		} else {
			if (nob < 10) {
				/*
				 * This is a sanity check, which is only a warning
				 * if it fails.  It assumes that the uid of nobody is >10
				 * this to guard against a hacked /etc/group file.
				 */
				fprintf(stderr, "%s: User name 'nobody' has a uid of %d\n",
				swlib_utilname_get(), (int)(nob));
			}
			if(setuid(nob) < 0) {
				fprintf(stderr, 
	     			"%s: setuid(uid=%d) failed at %s:%d: %s\n",
					 swlib_utilname_get(), 
					(int)nob, __FILE__, __LINE__,
					strerror(errno));
				ret = 2;
			} else {
				ret = 0;
			}
		}
	} else {
		ret = 0;
	}
	return ret;
}

void
swlib_add_trailing_slash(STROB *path)
{
	char *p = strob_str(path);
	if (!strlen(p)) {
		strob_strcat(path,"/");
		return;
	}
	if( *(p + strlen(p) -1) != '/')
		strob_strcat(path,"/");
}

int
swlib_altfnmatch(char * s1, char * s2)
{
	STROB * tmp1;
	STROB * tmp2;
	char * t1;
	char * t2;

	tmp1 = strob_open(16);
	tmp2 = strob_open(16);
	t1 = strob_strtok(tmp1, s1, "|\n\r");
	while(t1) {
		t2 = strob_strtok(tmp2, s2, "|\n\r");
		while(t2) {
			if (fnmatch(t1, t2, 0) != FNM_NOMATCH) {
				/*
				 * match
				 */
				strob_close(tmp1);
				strob_close(tmp2);
				return 0;
			}	
			t2 = strob_strtok(tmp2, NULL, "|\n\r");
		}
		t1 = strob_strtok(tmp1, NULL, "|\n\r");
	}
	strob_close(tmp1);
	strob_close(tmp2);
	return 1;
}
