
/* Copyright (C) 1992  AHPCRC, Univeristy of Minnesota
 *
 * 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 in a file named 'Copying'; if not, write to
 * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139.
 */

/* Author:
 *	Ken Chin-Purcell (ken@ahpcrc.umn.edu)
 *	Army High Performance Computing Research Center (AHPCRC)
 *	Univeristy of Minnesota
 *
 * $Header: /usr/people/ken/gvl/lib/RCS/util.c,v 2.2 92/10/19 16:55:43 ken Exp $
 *
 * $Log:	util.c,v $
 * Revision 2.2  92/10/19  16:55:43  ken
 * Trivial change
 * 
 * Revision 1.1  92/10/19  16:37:10  ken
 * Initial revision
 * 
 */

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "util.h"


/*VARARGS1*/
void Error(char *errstr, ...)
{
    /* Print a string to standard eror.
     */
    va_list	args;
    
    va_start(args, errstr);
    (void) vfprintf(stderr, errstr, args);
    (void) fflush(stderr);
}


static int	coreDumpOnError = 0;

void SetCoreDump(int dumpit)
{
    /* When debugging you may want a core dump when BailOut is called.
     */
    coreDumpOnError = dumpit;
}


void BailOut(char *errstr, char *fname, int lineno)
{
    /* Terminate program after printing an error message.
     * Use via the macros Verify and MemCheck.
     */
    Error("Error: %s, at %s:%d\n", errstr, fname, lineno);
    if (coreDumpOnError)
	abort();
    exit(1);
}


int FindFileSize(char *fname)
{
    /* Find the size of a file in bytes,
     */
    struct stat	sbuf;
    
    if (stat(fname, &sbuf) != 0) {
	Error("Can't stat file %s\n", fname);
	return -1;
    }
    return sbuf.st_size;
}


unsigned char *ReadColormap(unsigned char *cmap, char *cmapName)
{
    /* Depending on the file length, read 768 bytes
     * or 1024 ascii integers to create a color map.
     * Bytes ordered:     rgbrgbrgb...
     * Integers ordered:  index red grn blu  index red grn blu ...
     *
     * Return 768 bytes, rgbrgbrgb...
     * NULL on error
     */
    unsigned		ix, r, g, b;
    FILE		*cmapFile;
    
    cmapFile = fopen(cmapName, "r");
    if (!cmapFile)
	return NULL;
    
    if (!cmap) {
	cmap = CallocType(unsigned char, 768);
	MemCheck(cmap);
    }
    
    if (FindFileSize(cmapName) == 768)
	(void) fread(cmap, 1, 768, cmapFile);
    
    else {
	while (fscanf(cmapFile, "%u%u%u%u", &ix, &r, &g, &b) == 4) {
	    if (ix > 255) {
		Error("Color map index %u out of range\n", ix);
		continue;
	    }
	    cmap[3*ix]   = MIN(r, 255);
	    cmap[3*ix+1] = MIN(g, 255);
	    cmap[3*ix+2] = MIN(b, 255);
	}
    }
    (void) fclose(cmapFile);
    
    return cmap;
}


char *NewString(char *old, int extra)
{
    /* Create a new string from an old one, tacking on extra bytes
     */
    int		slen;
    char	*s;
    
    if (old)
	slen = strlen(old) + extra + 1;
    else
	slen = MAX(extra + 1, 1);
    
    s = CallocType(char, slen);
    MemCheck(s);
    
    if (old)
	(void) strcpy(s, old);
    else
	s[0] = '\0';
    
    return s;
}


void CommaLong(char *s, long i)
{
    /* Return a comma separated string representation of a long.
     */
    long	ai = ABS(i);
    
    if (ai < 10000)
	(void) sprintf(s, "%ld", i);
    else if (ai < 1000000)
	(void) sprintf(s, "%ld,%03ld", i / 1000, ai % 1000);
    else
	(void) sprintf(s, "%ld,%03ld,%03ld", 
		       i / 1000000, (ai % 1000000) / 1000, ai % 1000);
}


long ScanLong(char *s)
{
    /* Scan for a long integer, using the letters 'k' and 'm'
     * to signify kilo and mega.
     */
    long	l;
    char	*sEnd;
    
    l = strtol(s, &sEnd, 0);

    if (sEnd == s)
	l = 1;
    while (isalpha(*sEnd)) {
	switch (*sEnd) {
	  case 'k':
	  case 'K':
	    l *= 1024;
	    break;
	  case 'm':
	  case 'M':
	    l *= 1024*1024;
	    break;
	}
	++sEnd;
    }
    
    return l;
}


long RoundUp(long a, long modulo)
{
    /* Round integer up to next modulo.
     */
    long	b = a % modulo;
    
    if (b)
	return a + modulo - b;
    else
	return a;
}


long Factor(long a)
{
    /* Find the largest even divisor of an integer.
     */
    long	i;
    
    for (i = sqrt((double)a); i; i--)
	if (a % i == 0)
	    break;
    return i;
}


unsigned MarkTime(void)
{
    /* Return real wall clock time in milliseconds between this
     * call and last call.
     */
    static struct timeval	lastTime;
    struct timeval		thisTime;
    unsigned			msecs;
    
    (void) gettimeofday(&thisTime, NULL);
    msecs = ((thisTime.tv_sec  - lastTime.tv_sec ) * 1000 +
	     (thisTime.tv_usec - lastTime.tv_usec) / 1000);
    lastTime = thisTime;
    
    return msecs;
}


#define CloseFD(fd) if (fd > 1) (void) close (fd)

int ppopen(char **argv, int *pInOut)
{
    /* Bi-directional version of popen()
     */
    int		toCmd[2];
    int		fromCmd[2];
    int		cmdPID;
    
    if (pipe(toCmd) == -1) {
	perror("pipe toCmd");
	return -1;
    }
    
    if (pipe(fromCmd)) {
	perror("pipe fromCmd");
	return -1;
    }
    
    /* Time to part ways.
     */
    cmdPID = fork();
    
    if (cmdPID == -1) {
	perror("ppopen fork");
	return -1;
    }
    
    if (cmdPID == 0) {
	
	/* The child
	 */
	(void) dup2(toCmd[0], 0);
	(void) dup2(fromCmd[1], 1);
	
	CloseFD(toCmd[0]);
	CloseFD(toCmd[1]);
	CloseFD(fromCmd[0]);
	CloseFD(fromCmd[1]);
	
	(void) execvp(argv[0], argv);
	
	(void) fprintf(stderr, "ppopen: command %s cannot be found.\n",
		       argv[0]);
	(void) _exit(1);	/* use _exit with fork */
    }
    
    /* The parent
     */
    CloseFD(toCmd[0]);
    CloseFD(fromCmd[1]);
    
    pInOut[0] = fromCmd[0];
    pInOut[1] = toCmd[1];
    
    return cmdPID;
}

