/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
*/
/*
 * Utility functions for rcs2atfs
 *
 * Author: Juergen Nickelsen (Juergen.Nickelsen@cs.tu-berlin.de)
 *
 * $Header: utils.c[3.0] Tue Jun 29 16:38:16 1993 andy@cs.tu-berlin.de frozen $
 */

#include "rcs2atfs.h"
#include "functions.h"
#include <errno.h>


/*
 * The memory mamagement functions do the following: allocate, check
 * if result is valid, initialize fields if necessary
 */


/*** raw allocators ***/

/* allocate memory and check */
char *check_malloc(size)
unsigned size ;
{
    char *tmp ;
    
    tmp = malloc(size) ;
    if (tmp == NULL) {
	fatal(NOMORECORE, NULL) ;
    }
    return tmp ;
}

/* reallocate memory and check */
char *check_realloc(ptr, size)
char *ptr ;
unsigned size ;
{
    char *tmp ;

    tmp = realloc(ptr, size) ;
    if (tmp == NULL) {
	fatal(NOMORECORE, NULL) ;
    }
    return tmp ;
}

/* callocate memory and check */
char *check_calloc(nelem, elsize)
unsigned nelem, elsize ;
{
    char *tmp ;

    tmp = calloc(nelem, elsize) ;
    if (tmp == NULL) {
	fatal(NOMORECORE, NULL) ;
    }
    return tmp ;
}


/*** structure allocators ***/

/* allocate and initialize struct RCSFILE */
struct RCSFILE *new_rcsfile()
{
    struct RCSFILE *new ;

    new = (struct RCSFILE *) check_malloc(sizeof(struct RCSFILE)) ;

    new->dirname = NULL ;
    new->rcs_file = NULL ;
    new->working_file = NULL ;
    new->locks = false ;
    new->comment_leader = NULL ;
    new->description = NULL ;
    new->revisions = NULL ;
    new->branches = false ;
    new->no_of_revs = 0 ;
    new->next = NULL ;

    return new ;
}

/* allocate and initialize struct RCSREV */
struct RCSREV *new_rcsrev()
{
    struct RCSREV *new ;

    new = (struct RCSREV *) check_malloc(sizeof(struct RCSREV)) ;

    new->next = NULL ;
    new->number = NULL ;
    new->date = 0 ;
    new->author = NULL ;
    new->state = NULL ;
    new->log_message = NULL ;
    new->symbolic_names = NULL ;

    return new ;
}

/* allocate and initialize struct SYMNAME */
struct SYMNAME *new_symname()
{
    struct SYMNAME *new ;

    new = (struct SYMNAME *) check_malloc(sizeof(struct SYMNAME)) ;

    new->next = NULL ;
    new->revision = NULL ;
    new->symname = NULL ;

    return new ;
}


/*** free structures ***/

/* free memory allocated for a struct RCSFILE */
void free_rcsfile(rp)
struct RCSFILE *rp ;
{
    (void) free(rp->rcs_file) ;
    (void) free(rp->working_file) ;
    (void) free(rp->comment_leader) ;
    (void) free(rp->description) ;
    (void) free_rev_chain(rp->revisions) ;
    (void) free((char *) rp) ;
}

/* free memory allocated for a chain of struct RCSREV */
void free_rev_chain(rp)
struct RCSREV *rp ;
{
    struct RCSREV *tmp ;
    
    while (rp) {
	tmp = rp ;
	rp = rp->next ;
	free_rcsrev(tmp) ;
    }
}

/* free memory allocated for a struct RCSREV */
void free_rcsrev(rp)
struct RCSREV *rp ;
{
    (void) free(rp->number) ;
    (void) free(rp->author) ;
    (void) free(rp->state) ;
    (void) free(rp->log_message) ;
    (void) free_symnames(rp->symbolic_names) ;
}

/* free memory allocated for a chain of struct SYMNAME */
void free_symnames(sp)
struct SYMNAME *sp ;
{
    struct SYMNAME *tmp ;

    while (sp) {
	tmp = sp ;
	sp = sp->next ;
	(void) free(tmp->symname) ;
	(void) free(tmp->revision) ;
    }
}



/*** string functions ***/

/* duplicate string with check */
char *check_strdup(s)
char *s ;
{
    char *new ;

    new = malloc(strlen(s) + 1) ;
    if (new == NULL) {
	fatal(NOMORECORE, NULL) ;
    }
    strcpy(new, s) ;

    return new ;
}

/* like strdup, but only n characters
 * the resulting string is null padded, freeable
 */
char *strndup(s, n)
char *s ;
int n ;
{
    char *tmp = check_malloc(n + 1) ;

    strncpy0(tmp, s, n) ;
    return tmp ;
}

/* return a string concatenated from s1, s2, and s3.
 * The returned string is freeable.
 * Each argument may be NULL. If all are NULL, NULL is returned.
 */
char *addstr3(s1, s2, s3)
char *s1, *s2, *s3 ;
{
    if (s1 == NULL) {
	return addstr(s2, s3) ;
    } else if (s2 == NULL) {
	return addstr(s1, s3) ;
    } else if (s3 == NULL) {
	return addstr(s1, s2) ;
    } else {			/* Wow! None is NULL! */
	char *tmp = check_malloc(strlen(s1) + strlen(s2) + strlen(s3) + 1) ;

	strcpy(tmp, s1) ;
	strcat(tmp, s2) ;
	strcat(tmp, s3) ;

	return tmp ;
    }
}

/* return a string concatenated from s1 and s2.
 * The returned string is freeable.
 * Each argument may be NULL, if both, NULL is returned.
 */
char *addstr(s1, s2)
char *s1, *s2 ;
{
    if (s1 == NULL) {
	return s2 ;		/* if s2 is also NULL, NULL is returned */
    } else if (s2 == NULL) {
	return s1 ;
    } else {			/* both not NULL */
	char *tmp = check_malloc(strlen(s1) + strlen(s2) + 1) ;

	strcpy(tmp, s1) ;
	strcat(tmp, s2) ;
	
	return tmp ;
    }
}

/* safe strncpy with null padding but no return value */
void strncpy0(s1, s2, n)
char *s1, *s2 ;
int n ;
{
    strncpy(s1, s2, n) ;
    s1[n] = '\0' ;
}

/* safe strncat with null padding but no return value */
void strncat0(s1, s2, n)
char *s1, *s2 ;
int n ;
{
    int previous = strlen(s1) ;
    
    strncat(s1, s2, n) ;
    s1[previous + n] = '\0' ;
}


/*** errors and warnings ***/

/* print fatal error message and exit */
void fatal(message, message2)
char *message ;
char *message2 ;
{
    (void) fprintf(stderr, "%s fatal error: %s", progname, message) ;
    if (message2 != NULL) fprintf(stderr, " (%s)", message2) ;
    fputc('\n', stderr) ;

    if (shellscript != NULL) {
	(void) unlink(shellscript) ;
    }

    (void) exit(3) ;
}

/* print error message */
void error(message, message2)
char *message ;
char *message2 ;
{
    fprintf(stderr, "%s error: %s", progname, message) ;
    if (message2 != NULL) fprintf(stderr, " (%s)", message2) ;
    fputc('\n', stderr) ;

    errors++ ;
}

/* print warning message */
void warning(message, message2)
char *message ;
char *message2 ;
{
    fprintf(stderr, "%s warning: %s", progname, message) ;
    if (message2 != NULL) fprintf(stderr, " (%s)", message2) ;
    fputc('\n', stderr) ;

    warnings++ ;
}


/*** miscellaneous stuff ***/

/* check for existence of an AtFS history for file fname */
bool atfs_exists(fname)
char *fname ;
{
    return ! af_access(af_afpath(fname),
		       af_afname(fname),
		       af_aftype(fname),
		       AF_CLASS_SOURCE) ;
}

/* strip last component of pathname.
 * if pathname is NULL or doesn't contain a slash, NULL is returned.
 * returned string may be free()d later
 */
char *strip_last(pathname)
char *pathname ;
{
    char *dir = NULL ;
    char *slash ;

    if (pathname != NULL) {
	dir = check_strdup(pathname) ;
	slash = strrchr(dir, '/') ;
	if (slash == NULL) {
	    free(dir) ;
	    return NULL ;
	}
	*slash = '\0' ;
    }
    return dir ;
}

/* and now the opposite: return the last component of pathname.
 * returned string is free(3)able.
 */
char *basename(pathname)
char *pathname ;
{
    char *bnp ;			/* ptr to basename */

    bnp = strrchr(pathname, '/') ;
    if (bnp == NULL) {
	return check_strdup(pathname) ;
    }

    return check_strdup(++bnp) ;
}
	
/* return type of file: directory, normal file, RCS file,
 * RCS directory, error (not stat()able)
 */
filetype_t file_type(fname)
char *fname ;
{
    struct stat st ;

    if (stat(fname, &st) == -1) {
	switch (errno) {
	  case EACCES:
	    if (! recursive) {
		error(sys_errlist[errno], fname) ;
	    }
	    return f_error ;
	  case EFAULT:
	    fatal(sys_errlist[errno], MUSTNOT) ;
	  case ENOENT:
	    /* can be RCS working file without busy version */
	    return f_plain ;
	  default:
	    error(sys_errlist[errno], fname) ;
	}
    }

    if (S_ISDIR(st.st_mode)) {
	if (strlen(fname) == 3 && ! strcmp(fname, "RCS")) {
	    return f_rcsdir ;
	} else if (! strcmp(fname + strlen(fname) - 4, "/RCS")) {
	    return f_rcsdir ;
	} else {
	    return f_dir ;
	}
    } else if (! strcmp(fname + strlen(fname) - 2, ",v")) {
	return f_rcs ;
    } else {
	return f_plain ;
    }
}


/* quote \, " and $ in text for use in the shell script quoted with " */
char *sh_quote(text)
char *text ;
{
    /* chunk of memory for quoted text */
    char *tmp = check_malloc(strlen(text) * 2) ;
				/* it will NEVER get bigger */
    /* pointer into chunk */
    char *ptr = tmp ;

    /* copy text, looking for $ and \ */
    while (*text != '\0') {
	if (*text == '\\' || *text == '$' || *text == '\"') {
	    /* must quote with \ */
	    *ptr++ = '\\' ;
	}
	*ptr++ = *text++ ;
    }
    *ptr = '\0' ;
    tmp = check_realloc(tmp, strlen(tmp) + 1) ;

    return tmp ;
}

/*EOF*/
