/*
 *   util.c - Common utility routines for xmcd, cda and libdi.
 *
 *   xmcd  - Motif(tm) CD Audio Player
 *   cda   - Command-line CD Audio Player
 *   libdi - CD Audio Player Device Interface Library
 *
 *
 *   Copyright (C) 1993-1996  Ti Kan
 *   E-mail: ti@amb.org
 *
 *   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 of the License, 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.
 *
 */
#ifndef LINT
static char *_util_c_ident_ = "@(#)util.c	6.16 96/01/07";
#endif

#include "common_d/appenv.h"
#include "common_d/util.h"

#ifdef USE_SELECT
#include <sys/time.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#endif

#ifdef __VMS
#include <fscndef.h>
STATIC int		context;
#endif


extern appdata_t	app_data;
extern FILE		*errfp;


STATIC uid_t		ouid = 30001;	/* Default to something safe */
STATIC gid_t		ogid = 30001;	/* Default to something safe */


/***********************
 *   public routines   *
 ***********************/


/*
 * util_init
 *	Initialize the libutil module.  This should be called before
 *	the calling program does a setuid.
 *
 * Args:
 *	Nothing
 *
 * Return:
 *	Nothing
 */
void
util_init(void)
{
	ouid = getuid();
	ogid = getgid();
}


/*
 * get_ouid
 *	Get original user ID
 *
 * Args:
 *	Nothing
 *
 * Return:
 *	Original uid value.
 */
uid_t
get_ouid(void)
{
	return (ouid);
}


/*
 * get_ogid
 *	Get original group ID
 *
 * Args:
 *	Nothing
 *
 * Return:
 *	Original gid value.
 */
gid_t
get_ogid(void)
{
	return (ogid);
}


/*
 * ltobcd
 *	32-bit integer to BCD conversion routine
 *
 * Args:
 *	n - 32-bit integer
 *
 * Return:
 *	BCD representation of n
 */
sword32_t
ltobcd(sword32_t n)
{
	return ((n % 10) | ((n / 10) << 4));
}


/*
 * bcdtol
 *	BCD to 32-bit integer conversion routine
 *
 * Args:
 *	n - BCD value
 *
 * Return:
 *	integer representation of n
 */
sword32_t
bcdtol(sword32_t n)
{
	return ((n & 0x0f) + ((n >> 4) * 10));
}


/*
 * stob
 *	String to boolean conversion routine
 *
 * Args:
 *	s - text string "True", "true", "False" or "false"
 *
 * Return:
 *	Boolean value representing the string
 */
bool_t
stob(char *s)
{
	if (strcmp(s, "True") == 0 || strcmp(s, "true") == 0 ||
	    strcmp(s, "TRUE") == 0)
		return TRUE;

	return FALSE;
}


/*
 * basename
 *	Return the basename of a file path
 *
 * Args:
 *	path - The file path string
 *
 * Return:
 *	The basename string
 */
char *
basename(char *path)
{
	char	*p;

#ifndef __VMS
	if ((p = strrchr(path, '/')) == NULL)
#else
	if ((p = strrchr(path, ']')) == NULL)
#endif
		return (path);
	
	return (p + 1);
}


/*
 * dirname
 *	Return the dirname of a file path
 *
 * Args:
 *	path - The file path string
 *
 * Return:
 *	The dirname string
 */
char *
dirname(char *path)
{
	char		*p;
	static char	buf[FILE_PATH_SZ];

	if ((int) strlen(path) >= FILE_PATH_SZ)
		/* Error: path name too long */
		return NULL;

	strcpy(buf, path);

#ifndef __VMS
	if ((p = strrchr(buf, '/')) == NULL)
#else
	if ((p = strrchr(buf, ']')) == NULL)
#endif
		return (buf);

#ifdef __VMS
	p++;
#endif

	*p = '\0';

	return (buf);
}


/*
 * loginname
 *	Return the login name of the current user
 *
 * Args:
 *	None.
 *
 * Return:
 *	The login name string.
 */
char *
loginname(void)
{
#ifndef __VMS
	struct passwd	*pw;
	char		*cp;

	/* Get login name from the password file if possible */
	if ((pw = getpwuid(ouid)) != NULL)
		return (pw->pw_name);

	/* Try the LOGNAME environment variable */
	if ((cp = getenv("LOGNAME")) != NULL)
		return (cp);

	/* Try the USER environment variable */
	if ((cp = getenv("USER")) != NULL)
		return (cp);
#endif
	/* If we still can't get the login name, just set it
	 * to "unknown" (shrug).
	 */
	return ("unknown");
}


/*
 * homedir
 *	Return the home directory path of a user given the uid
 *
 * Args:
 *	uid - The uid of the user
 *
 * Return:
 *	The home directory path name string
 */
char *
homedir(uid_t uid)
{
#ifndef __VMS
	struct passwd	*pw;
	char		*cp;

	/* Get home directory from the password file if possible */
	if ((pw = getpwuid(uid)) != NULL)
		return (pw->pw_dir);

	/* Try the HOME environment variable */
	if (uid == ouid && (cp = getenv("HOME")) != NULL)
		return (cp);

	/* If we still can't get the home directory, just set it to the
	 * current directory (shrug).
	 */
	return (".");
#else
	char		*cp;
	static char	buf[FILE_PATH_SZ];

	if ((cp = getenv("HOME")) != NULL) {
		strcpy(buf, cp);
		buf[strlen(buf)-1] = '\0';	/* Drop the "]" */
	}
	else
		strcpy(buf, "SYS$DISK:[");

	return (buf);
#endif	/* __VMS */
}


/*
 * uhomedir
 *	Return the home directory path of a user given the name
 *
 * Args:
 *	name - The name of the user
 *
 * Return:
 *	The home directory path name string
 */
char *
uhomedir(char *name)
{
#ifndef __VMS
	struct passwd	*pw;

	/* Get home directory from the password file if possible */
	if ((pw = getpwnam(name)) != NULL)
		return (pw->pw_dir);

	/* If we still can't get the home directory, just set it to the
	 * current directory (shrug).
	 */
	return (".");
#else
	char		*cp;
	static char	buf[FILE_PATH_SZ];

	if ((cp = getenv("HOME")) != NULL) {
		strcpy(buf, cp);
		buf[strlen(buf)-1] = '\0';	/* Drop the "]" */
	}
	else
		strcpy(buf, "SYS$DISK:[");

	return (buf);
#endif
}


/*
 * isqrt
 *	Fast integer-based square root routine
 *
 * Args:
 *	n - The integer value whose square-root is to be taken
 *
 * Return:
 *	Resultant square-root integer value
 */
int
isqrt(int n)
{
	int	a, b, c, as, bs;

	a = 1;
	b = 1;
	while (a <= n) {
		a = a << 2;
		b = b << 1;
	}
	as = 0;
	bs = 0;
	while (b > 1 && n > 0) {
		a = a >> 2;
		b = b >> 1;
		c = n - (as | a);
		if (c >= 0) {
			n = c;
			as |= (a << 1);
			bs |= b;
		}
		as >>= 1;
	}

	return (bs);
}


/*
 * blktomsf
 *	CD logical block to MSF conversion routine
 *
 * Args:
 *	blk - The logical block address
 *	ret_min - Minute (return)
 *	ret_sec - Second (return)
 *	ret_frame - Frame (return)
 *	offset - Additional logical block address offset
 *
 * Return:
 *	Nothing.
 */
void
blktomsf(word32_t blk, byte_t *ret_min, byte_t *ret_sec, byte_t *ret_frame,
	 word32_t offset)
{
	*ret_min = (blk + offset) / FRAME_PER_SEC / 60;
	*ret_sec = ((blk + offset) / FRAME_PER_SEC) % 60;
	*ret_frame = (blk + offset) % FRAME_PER_SEC;
}


/*
 * msftoblk
 *	CD MSF to logical block conversion routine
 *
 * Args:
 *	min - Minute
 *	sec - Second
 *	frame - Frame
 *	ret_blk - The logical block address (return)
 *	offset - Additional logical block address offset
 *
 * Return:
 *	Nothing.
 */
void
msftoblk(byte_t min, byte_t sec, byte_t frame, word32_t *ret_blk,
	 word32_t offset)
{
	*ret_blk = FRAME_PER_SEC * (min * 60 + sec) + frame - offset;
}


/*
 * delayms
 *	Suspend execution for the specified number of milliseconds
 *
 * Args:
 *	msec - The number of milliseconds
 *
 * Return:
 *	Nothing.
 */
void
delayms(unsigned long msec)
{
#ifdef USE_SELECT
	struct timeval	to;

	to.tv_sec = (long) msec / 1000;
	to.tv_usec = ((long) msec % 1000) * 1000;

	select(0, NULL, NULL, NULL, &to);
#else
#ifdef USE_POLL
	poll(NULL, 0, (int) msec);
#else
#ifdef USE_NAP
	nap((long) msec);
#else
#ifdef USE_USLEEP
	usleep((long) msec * 1000);
#else
	/* shrug: Rounded to the nearest second, with a minimum of 1 second */
	if (msec < 1000)
		sleep(1);
	else
		sleep(((unsigned int) msec + 500) / 1000);
#endif	/* USE_USLEEP */
#endif	/* USE_NAP */
#endif	/* USE_POLL */
#endif	/* USE_SELECT */
}


/*
 * bswap16
 *	16-bit little-endian to big-endian byte-swap routine.
 *	On a big-endian system architecture this routines has no effect.
 *
 * Args:
 *	x - The data to be swapped
 *
 * Return:
 *	The swapped data.
 */
word16_t
bswap16(word16_t x)
{
#if _BYTE_ORDER_ == _L_ENDIAN_
	word16_t	ret;

	ret  = (x & 0x00ff) << 8;
	ret |= (word16_t) (x & 0xff00) >> 8;
	return (ret);
#else
	return (x);
#endif
}


/*
 * bswap24
 *	24-bit little-endian to big-endian byte-swap routine.
 *	On a big-endian system architecture this routines has no effect.
 *
 * Args:
 *	x - The data to be swapped
 *
 * Return:
 *	The swapped data.
 */
word32_t
bswap24(word32_t x)
{
#if _BYTE_ORDER_ == _L_ENDIAN_
	word32_t	ret;

	ret  = (x & 0x0000ff) << 16;
	ret |= (x & 0x00ff00);
	ret |= (x & 0xff0000) >> 16;
	return (ret);
#else
	return (x);
#endif
}


/*
 * bswap32
 *	32-bit little-endian to big-endian byte-swap routine.
 *	On a big-endian system architecture this routines has no effect.
 *
 * Args:
 *	x - The data to be swapped
 *
 * Return:
 *	The swapped data.
 */
word32_t
bswap32(word32_t x)
{
#if _BYTE_ORDER_ == _L_ENDIAN_
	word32_t	ret;

	ret  = (x & 0x000000ff) << 24;
	ret |= (x & 0x0000ff00) << 8;
	ret |= (x & 0x00ff0000) >> 8;
	ret |= (x & 0xff000000) >> 24;
	return (ret);
#else
	return (x);
#endif
}


/*
 * lswap16
 *	16-bit big-endian to little-endian byte-swap routine.
 *	On a little-endian system architecture this routines has no effect.
 *
 * Args:
 *	x - The data to be swapped
 *
 * Return:
 *	The swapped data.
 */
word16_t
lswap16(word16_t x)
{
#if _BYTE_ORDER_ == _L_ENDIAN_
	return (x);
#else
	word16_t	ret;

	ret  = (x & 0x00ff) << 8;
	ret |= (word16_t) (x & 0xff00) >> 8;
	return (ret);
#endif
}


/*
 * lswap24
 *	24-bit big-endian to little-endian byte-swap routine.
 *	On a little-endian system architecture this routines has no effect.
 *
 * Args:
 *	x - The data to be swapped
 *
 * Return:
 *	The swapped data.
 */
word32_t
lswap24(word32_t x)
{
#if _BYTE_ORDER_ == _L_ENDIAN_
	return (x);
#else
	word32_t	ret;

	ret  = (x & 0x0000ff) << 16;
	ret |= (x & 0x00ff00);
	ret |= (x & 0xff0000) >> 16;
	return (ret);
#endif
}


/*
 * lswap32
 *	32-bit big-endian to little-endian byte-swap routine.
 *	On a little-endian system architecture this routines has no effect.
 *
 * Args:
 *	x - The data to be swapped
 *
 * Return:
 *	The swapped data.
 */
word32_t
lswap32(word32_t x)
{
#if _BYTE_ORDER_ == _L_ENDIAN_
	return (x);
#else
	word32_t	ret;

	ret  = (x & 0x000000ff) << 24;
	ret |= (x & 0x0000ff00) << 8;
	ret |= (x & 0x00ff0000) >> 8;
	ret |= (x & 0xff000000) >> 24;
	return (ret);
#endif
}



/*
 * dbgdump
 *	Dump a data buffer to screen.
 *
 * Args:
 *	title - Message banner
 *	data - Address of data
 *	len - Number of bytes to dump
 *
 * Return:
 *	Nothing.
 */
void
dbgdump(char *title, byte_t *data, int len)
{
	int	i, j, k, n,
		lines;

	if (title == NULL || data == NULL || len <= 0)
		return;

	fprintf(errfp, "\n%s:", title);

	lines = ((len - 1) / 16) + 1;

	for (i = 0, k = 0; i < lines; i++) {
		fprintf(errfp, "\n%04x    ", k);

		for (j = 0, n = k; j < 16; j++, k++) {
			if (k < len)
				fprintf(errfp, "%02x ", *(data + k));
			else
				fprintf(errfp, "-- ");

			if (j == 7)
				fprintf(errfp, " ");
		}

		fprintf(errfp, "   ");

		for (j = 0, k = n; j < 16; j++, k++) {
			if (k < len) {
				fprintf(errfp, "%c",
				    isprint(*(data + k)) ? *(data + k) : '.'
				);
			}
			else
				fprintf(errfp, ".");
		}
	}

	fprintf(errfp, "\n");
}


#ifdef __VMS
/*
 * The following section provide UNIX-like functionality for Digital OpenVMS
 */

typedef struct {
	short	length;
	short	component;
	int	address;
	int	term;
} item_list;


/*
 *  Do a UNIX opendir by clearing the context value, and creating the 
 *  wild card search by appending *.* to the path name.
 */
DIR *
opendir(char *path)
{
	static DIR		dir;
	static struct dirent	ent;

	context = 0;
	sprintf(ent.d_name, "%s*.*", path);
	dir.dd_buf = &ent;
 	return (&dir);
}


/*
 * Do a UNIX closedir by call LIB$FIND_FILE_END to close 
 * the file context.  (End the wild card search)
 */
void
closedir(DIR *dp)
{
	LIB$FIND_FILE_END(&context);
}


/*
 *  Do a UNIX readdir by calling LIB$FIND_FILE, and SYS$FILESCAN
 *  to return the file name back.
 */
struct dirent *
readdir(DIR *dp)
{
	int			dir_desc[2],
				desc[2],
				i;
	char 			*p,
				*file[FILE_PATH_SZ];
	item_list		list;
	static struct dirent	ent;

	desc[0] = FILE_PATH_SZ;
	desc[1] = file;

	dir_desc[0] = FILE_PATH_SZ;
	dir_desc[1] = dp->dd_buf->d_name;

	if (LIB$FIND_FILE(dir_desc, desc, &context) & 0x01) {
		list.length = 0;
		list.component = FSCN$_NAME;
		list.address = 0;
		list.term = 0;

		SYS$FILESCAN(desc, &list, 0, 0, 0); 

		p = list.address;
		p[list.length] = '\0';

		for (p = list.address; *p != '\0'; p++)
			*p = tolower(*p);

		strcpy(ent.d_name, list.address);
		return (&ent);
	}
	else
		return NULL;
}


/*
 * do a UNIX waitpid by doing a wait call
 */
pid_t
waitpid(pid_t pid, int *statloc, int options)
{
	pid_t	ret;

	ret = wait(statloc);

	/* Under VMS a vfork() call does not create a child process unless
	 * a real process is created.  In the cases where the child does
	 * not follow the vfork with a system() or exec() call to create
	 * a real subprocess, we need to fake things out.
	 */
	if (ret < 0)
		ret = pid;

	/* VMS returns a 1 for success.  Patch it to zero to
	 * make this function compatible with UNIX.
	 */
	if (*statloc == 1)
		*statloc = 0;

	return (ret);
}


/*
 * do a UNIX unlink call
 */
int
unlink(char *file)
{
	delete(file);
	return 0;
}


/*
 * Do a UNIX link call by copying FILE1 to FILE2
 */

int 
link(char *file1, char *file2)
{
	FILE	*fp1,
		*fp2;
	char	buf[STR_BUF_SZ * 2];

	fp1 = fopen(file1, "r");
	fp2 = fopen(file2, "w");

	if (fp1 == NULL || fp2 == NULL)
		return -1;

	while (fgets(buf, sizeof(buf), fp1) != NULL)
		fprintf(fp2, "%s", buf);

	fclose(fp1);	
	fclose(fp2);	

	return 0;	
}

#endif	/* __VMS */

