ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/utils.c

This is utils.c in view mode; [Download] [Up]

/*
 * utils.c - Miscellaneous utility functions.
 *
 * Written by
 *  Ettore Perazzoli (ettore@comm2000.it)
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

#include "vice.h"

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif

#ifdef __MSDOS__
#include <process.h>
#include <dir.h>
#include <io.h>
#endif

#include "utils.h"

/* ------------------------------------------------------------------------- */

/* Like malloc, but abort if not enough memory is available.  */
void *xmalloc(size_t size)
{
    void *p = malloc(size);

    if (p == NULL) {
	fprintf(stderr,
		"Virtual memory exhausted: cannot allocate %lu bytes.\n",
		(unsigned long)size);
	exit(-1);
    }

    return p;
}

/* Like realloc, but abort if not enough memory is available.  */
void *xrealloc(void *p, size_t size)
{
    void *new_p = realloc(p, size);

    if (new_p == NULL) {
	fprintf(stderr,
		"Virtual memory exhausted: cannot allocate %lu bytes.\n",
		(unsigned long)size);
	exit(-1);
    }

    return new_p;
}

/* Malloc enough space for `str', copy `str' into it and return its
   address.  */
char *stralloc(const char *str)
{
    int l = strlen(str);
    char *p = (char *)xmalloc(l + 1);

    memcpy(p, str, l + 1);
    return p;
}

/* Malloc a new string whose contents concatenate the arguments until the
   first NULL pointer (max `_CONCAT_MAX_ARGS' arguments).  */
char *concat(const char *s, ...)
{
#define _CONCAT_MAX_ARGS 128
    const char *arg;
    char *new, *ptr;
    int arg_len[_CONCAT_MAX_ARGS], tot_len, num_args;
    int i;
    va_list ap;

    arg_len[0] = tot_len = strlen(s);

    va_start(ap, s);
    for (i = 1;
	 i < _CONCAT_MAX_ARGS && (arg = va_arg(ap, const char *)) != NULL;
	 i++) {
	arg_len[i] = strlen(arg);
	tot_len += arg_len[i];
    }
    num_args = i;

    new = (char *) xmalloc(tot_len + 1);

    memcpy(new, s, arg_len[0]);
    ptr = new + arg_len[0];

    va_start(ap, s);
    for (i = 1; i < num_args; i++) {
	 memcpy(ptr, va_arg(ap, const char *), arg_len[i]);
	 ptr += arg_len[i];
    }
    *ptr = '\0';

    va_end(ap);
    return new;
}

/* Add the first `src_size' bytes of `src' to the end of `buf', which is a
   malloc'ed block of `max_buf_size' bytes of which only the first `buf_size'
   ones are used.  If the `buf' is not large enough, realloc it.  Return a
   pointer to the new block.  */
char *bufcat(char *buf, int *buf_size, int *max_buf_size,
	     const char *src, int src_size)
{
#define BUFCAT_GRANULARITY 0x1000
    if (*buf_size + src_size > *max_buf_size) {
	char *new_buf;

	*max_buf_size = (((*buf_size + src_size) / BUFCAT_GRANULARITY + 1)
			  * BUFCAT_GRANULARITY);
	new_buf = (char *)xrealloc(buf, *max_buf_size);
	buf = new_buf;
    }
    memcpy(buf + *buf_size, src, src_size);
    *buf_size += src_size;
    return buf;
}

/* Remove spaces from start and end of string `s'.  The string is not
   reallocated even if it becomes smaller.  */
void remove_spaces(char *s)
{
    char *p;
    int l = strlen(s);

    for (p = s; *p == ' '; p++)
        ;

    l -= (p - s);
    memmove(s, p, l + 1);

    if (l > 0) {
        for (p = s + l - 1; l > 0 && *p == ' '; l--, p--)
            ;
        *(p + 1) = '\0';
    }
}

/* Set a new value to the dynamically allocated string *str.  */
void string_set(char **str, const char *new_value)
{
    if (*str == NULL) {
        if (new_value != NULL)
            *str = stralloc(new_value);
    } else if (new_value == NULL) {
        free(*str);
        *str = NULL;
    } else {
        *str = xrealloc(*str, strlen(new_value) + 1);
        strcpy(*str, new_value);
    }
}

/* ------------------------------------------------------------------------- */

int string_to_long(const char *str, const char **endptr, int base,
		   long *result)
{
    const char *sp, *ep;
    long weight, value;
    long sign;
    char last_letter = 0;       /* Initialize to make compiler happy.  */
    char c;

    if (base > 10)
        last_letter = 'A' + base - 11;

    c = toupper((int) *str);

    if (!isspace((int)c)
        && !isdigit((int)c)
        && (base <= 10 || c > last_letter || c < 'A')
        && c != '+' && c != '-')
        return -1;

    if (*str == '+') {
        sign = +1;
        str++;
    } else if (*str == '-') {
        str++;
        sign = -1;
    } else
        sign = +1;

    for (sp = str; isspace((int)*sp); sp++)
	;

    for (ep = sp;
         (isdigit((int)*ep)
          || (base > 10
              && toupper((int)*ep) <= last_letter
              && toupper((int)*ep) >= 'A')); ep++)
	;

    if (ep == sp)
	return -1;

    if (endptr != NULL)
	*endptr = (char *)ep;

    ep--;

    for (value = 0, weight = 1; ep >= sp; weight *= base, ep--) {
        if (base > 10 && toupper((int) *ep) >= 'A')
            value += weight * (toupper((int)*ep) - 'A' + 10);
	else
            value += weight * (int)(*ep - '0');
    }

    *result = value * sign;
    return 0;
}

/* Replace every occurrence of `string' in `s' with `replacement' and return
   the result as a malloc'ed string.  */
char *subst(const char *s, const char *string, const char *replacement)
{
    int num_occurrences;
    int total_size;
    int s_len = strlen(s);
    int string_len = strlen(string);
    int replacement_len = strlen(replacement);
    const char *sp;
    char *dp;
    char *result;

    /* First, count the occurrences so that we avoid re-allocating every
       time.  */
    for (num_occurrences = 0, sp = s;
         (sp = strstr(sp, string)) != NULL;
         num_occurrences++, sp += string_len)
        ;

    total_size = s_len - (string_len - replacement_len) * num_occurrences + 1;

    result = (char *) xmalloc(total_size);

    sp = s;
    dp = result;
    do {
        char *f = strstr(sp, string);

        if (f == NULL)
            break;

        memcpy(dp, sp, f - sp);
        memcpy(dp + (f - sp), replacement, replacement_len);
        dp += (f - sp) + replacement_len;
        s_len -= (f - sp) + string_len;
        sp = f + string_len;
        num_occurrences--;
    } while (num_occurrences != 0);

    memcpy(dp, sp, s_len + 1);

    return result;
}

/* ------------------------------------------------------------------------- */

/* Return a malloc'ed backup file name for file `fname'.  */
char *make_backup_filename(const char *fname)
{
#ifndef __MSDOS__

    /* Just add a '~' to the end of the name.  */
    int l = strlen(fname);
    char *p = (char *)xmalloc(l + 2);

    memcpy(p, fname, l);
    *(p + l) = '~';
    *(p + l + 1) = '\0';
    return p;

#else  /* !__MSDOS__ */

    /* FIXME: only works with 8+3 names.  */
    char d[MAXDRIVE], p[MAXDIR], f[MAXFILE], e[MAXEXT];
    char new[MAXPATH];

    fnsplit(fname, d, p, f, e);
    fnmerge(new, d, p, f, "BAK");

    return stralloc(new);

#endif /* !__MSDOS__ */
}

/* Make a backup for file `fname'.  */
int make_backup_file(const char *fname)
{
    char *backup_name = make_backup_filename(fname);
    int retval;

    /* Cannot do it...  */
    if (backup_name == NULL)
	return -1;

    retval = rename(fname, backup_name);

    free(backup_name);
    return retval;
}

/* Get the current working directory as a malloc'ed string.  */
char *get_current_dir(void)
{
    static int len = 128;
    char *p = (char *) xmalloc(len);

    while (getcwd(p, len) == NULL) {
        if (errno == ERANGE) {
            len *= 2;
            p = (char *) xrealloc(p, len);
        } else
            return NULL;
    }

    return p;
}

/* ------------------------------------------------------------------------- */

/* Return the length of an open file in bytes.  */
unsigned long file_length(int fd)
{
    struct stat statbuf;

    if (fstat(fd, &statbuf) < 0)
	return -1;

    return statbuf.st_size;
}

/* Load the first `size' bytes of file named `name' into `dest'.  Return 0 on
   success, -1 on failure.  */
int load_file(const char *name, void *dest, int size)
{
    int fd, r;

    fd = open(name, O_RDONLY);
    if (fd < 0)
	return -1;

    r = read(fd, (char *)dest, size);

    if (r != size) {
	if (r < 0)
	    perror(name);
	close(fd);
	return -1;
    } else {
	close(fd);
	return 0;
    }
}

/* Write the first `size' bytes of `src' into a newly created file `name'.
   If `name' already exists, it is replaced by the new one.  Returns 0 on
   success, -1 on failure.  */
int save_file(const char *name, const void *src, int size)
{
    int fd, r;

    fd = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0666);
    if (fd < 0)
	return -1;

    r = write(fd, (char *)src, size);

    if (r != size) {
	if (r < 0)
	    perror(name);
	close(fd);
	return -1;
    } else {
	close(fd);
	return 0;
    }
}

/* Input one line from the file descriptor `f'.  FIXME: we need something
   better, line GNU `getline()'.  */
int get_line(char *buf, int bufsize, FILE *f)
{
    char *r;
    int len;

    r = fgets(buf, bufsize, f);
    if (r == NULL)
	return -1;

    len = strlen(buf);

    if (len > 0) {
	char *p;

	/* Remove trailing newline character.  */
	if (*(buf + len - 1) == '\n')
	    len--;

	/* Remove useless spaces.  */
	while (*(buf + len - 1) == ' ')
	    len--;
	for (p = buf; *p == ' '; p++, len--)
	    ;
	memmove(buf, p, len + 1);
	*(buf + len) = '\0';
    }

    return len;
}

/* Split `path' into a file name and a directory component.  Unlike
   the MS-DOS `fnsplit', the directory does not have a trailing '/'.  */
void fname_split(const char *path, char **directory_return, char **name_return)
{
    const char *p;

    if (path == NULL) {
	*directory_return = *name_return = NULL;
	return;
    }

    p = strrchr(path, '/');
#if defined __MSDOS__ || defined WIN32
    if (p == NULL)
        p = strrchr(path, '\\');
#endif

    if (p == NULL) {
	if (directory_return != NULL)
	    *directory_return = NULL;
	if (name_return != NULL)
 	    *name_return = stralloc(path);
	return;
    }

    if (directory_return != NULL) {
        *directory_return = xmalloc(p - path + 1);
        memcpy(*directory_return, path, p - path);
	(*directory_return)[p - path] = '\0';
    }

    if (name_return != NULL)
        *name_return = stralloc(p + 1);

    return;
}

/* ------------------------------------------------------------------------- */

/* Launch program `name' (searched via the PATH environment variable) passing
   `argv' as the parameters, wait for it to exit and return its exit status.
   If `stdout_redir' or `stderr_redir' are != NULL, redirect stdout or stderr
   to the corresponding file.  */
int spawn(const char *name, char **argv,
	  const char *stdout_redir, const char *stderr_redir)
{
#ifndef __MSDOS__

    /* Unix version.  */

    pid_t child_pid;
#ifndef NeXT
    int child_status;
#else
    union wait child_status;
#endif

    child_pid = vfork();
    if (child_pid < 0) {
	perror("vfork");
	return -1;
    } else if (child_pid == 0) {
	if (stdout_redir && freopen(stdout_redir, "w", stdout) == NULL) {
	    perror(stdout_redir);
	    _exit(-1);
	}
	if (stderr_redir && freopen(stderr_redir, "w", stderr) == NULL) {
	    perror(stderr_redir);
	    _exit(-1);
	}
	execvp(name, argv);
	_exit(-1);
    }

#ifndef NeXT
    if (waitpid(child_pid, &child_status, 0) != child_pid) {
#else
    if (wait4(child_pid, &child_status, 0, 0) != child_pid) {
#endif
	perror("waitpid");
	return -1;
    }

    if (WIFEXITED(child_status))
#ifndef NeXT
	return WEXITSTATUS(child_status);
#else
 	return child_status.w_retcode;
#endif
    else
	return -1;

#else

    /* MS-DOS version.  */

    int new_stdout, new_stderr;
    int old_stdout_mode, old_stderr_mode;
    int old_stdout, old_stderr;
    int retval;

    new_stdout = new_stderr = old_stdout = old_stderr = -1;

    /* Make sure we are in binary mode.  */
    old_stdout_mode = setmode(STDOUT_FILENO, O_BINARY);
    old_stderr_mode = setmode(STDERR_FILENO, O_BINARY);

    /* Redirect stdout and stderr as requested, saving the old
       descriptors.  */
    if (stdout_redir != NULL) {
	old_stdout = dup(STDOUT_FILENO);
	new_stdout = open(stdout_redir, O_WRONLY | O_TRUNC | O_CREAT, 0666);
	if (new_stdout == -1) {
	    perror(stdout_redir);
	    retval = -1;
	    goto cleanup;
	}
	dup2(new_stdout, STDOUT_FILENO);
    }
    if (stderr_redir != NULL) {
	old_stderr = dup(STDERR_FILENO);
	new_stderr = open(stderr_redir, O_WRONLY | O_TRUNC | O_CREAT, 0666);
	if (new_stderr == -1) {
	    perror(stderr_redir);
	    retval = -1;
	    goto cleanup;
	}
	dup2(new_stderr, STDERR_FILENO);
    }

    /* Spawn the child process.  */
    retval = spawnvp(P_WAIT, name, argv);

cleanup:
    if (old_stdout >= 0)
	dup2(old_stdout, STDOUT_FILENO);
    if (old_stderr >= 0)
	dup2(old_stderr, STDERR_FILENO);
    if (old_stdout_mode >= 0)
	setmode(STDOUT_FILENO, old_stdout_mode);
    if (old_stderr_mode >= 0)
	setmode(STDERR_FILENO, old_stderr_mode);
    if (new_stdout >= 0)
	close(new_stdout);
    if (new_stderr >= 0)
	close(new_stderr);

    return retval;
#endif
}

/* ------------------------------------------------------------------------- */

/* This code is grabbed from GNU make.  It returns the maximum path length by
   using `pathconf'.  */
#ifdef NEED_GET_PATH_MAX
unsigned int
get_path_max(void)
{
    static unsigned int value;

    if (value == 0) {
	long int x = pathconf("/", _PC_PATH_MAX);

	if (x > 0)
	    value = x;
	else
	    return MAXPATHLEN;
    }

    return value;
}
#endif

/* The following are replacements for libc functions that could be missing.  */

#if !defined HAVE_MEMMOVE

void *memmove(void *target, const void *source, unsigned int length)
{
    char *tptr = (char *) target;
    const char *sptr = (const char *) source;

    if (tptr > sptr) {
	tptr += length;
	sptr += length;
	while (length--)
	    *(--tptr) = *(--sptr);
    } else if (tptr < sptr) {
	while (length--)
	    *(tptr++) = *(sptr++);
    }

    return target;
}

#endif /* !defined HAVE_MEMMOVE */


#if !defined HAVE_ATEXIT

static void atexit_support_func(int status, void *arg)
{
    void (*f)(void) =(void (*)(void)) arg;

    f();
}

int atexit(void (*function)(void))
{
    return on_exit(atexit_support_func, (void *)function);
}

#endif /* !defined HAVE_ATEXIT */

/* ------------------------------------------------------------------------- */

int read_dword(int fd, DWORD *buf, int num)
{
    int i;
    BYTE *tmpbuf;

    tmpbuf = xmalloc(num);

    if (read(fd, (char *)tmpbuf, num) < num) {
	free(tmpbuf);
	return -1;
    }

    for (i = 0; i < (num / 4); i++)
	buf[i] = tmpbuf[i * 4] + (tmpbuf[i * 4 + 1] << 8)
	    + (tmpbuf[i * 4 + 2] << 16) + (tmpbuf[i * 4 + 3] << 24);

    free(tmpbuf);
    return 0;
}

int write_dword(int fd, DWORD *buf, int num)
{
    int i;
    BYTE *tmpbuf;

    tmpbuf = xmalloc(num);

    for (i = 0; i < (num / 4); i++) {
	tmpbuf[i * 4] = buf[i] & 0xff;
	tmpbuf[i * 4 + 1] = (buf[i] >> 8) & 0xff;
	tmpbuf[i * 4 + 2] = (buf[i] >> 16) & 0xff;
	tmpbuf[i * 4 + 3] = (buf[i] >> 24) & 0xff;
    }

    if (write(fd, (char *)tmpbuf, num) < 0) {
	free(tmpbuf);
	return -1;
    }

    free(tmpbuf);
    return 0;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.