#include "args.h"
#include "config.h"
/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#ifndef lint
static const char *rcsid = "$Header: /usr/local/src/mm/mm-0.94/mm/RCS/compat.c,v 1.2 2005/05/28 22:39:23 beebe Exp $";
#endif

#include "args.h"
#include "config.h"
#include "osfiles.h"
#include "compat.h"

extern FILE *mm_popen ARGS((const char *command, const char *type));
extern int mm_pclose ARGS((FILE *stream));

#if !defined(HAVE_GNU_LINUX)
#ifdef HAVE_GETCWD
extern int sys_nerr, errno;
extern char *sys_errlist[];
#endif
#endif /* !defined(HAVE_GNU_LINUX) */

/*
 * If getwd is a macro, we need to supply our own getwd() routine.
 */

#ifdef getwd
#if HAVE_STDC
char *
getwd (char *buf)
#else /* K&R style */
char *
getwd (buf)
char *buf;
#endif /* HAVE_STDC */
{
#ifdef HAVE_GETCWD
    if (getcwd (buf, MAXPATHLEN) == 0) {
#if defined(HAVE_GNU_LINUX)
	strcpy (buf, strerror(errno));
#else
	if (errno > 0 && errno <= sys_nerr)
	    strcpy (buf, sys_errlist[errno]);
	else
	    strcpy (buf, "getcwd failed");
#endif
	return (char *) 0;
    }
#else
    if (evalpipe("/bin/pwd", buf, MAXPATHLEN) != buf) {
	strcpy (buf, "/bin/pwd failed");
	return 0;
    }
#endif
    return buf;
}
#endif

/*
 * If "rename" is a macro, we need to supply our own rename() routine.
 * This version is careful not to delete the source file; if your kernel
 * has a rename(2) that can unlink the source file accidentally, or may
 * succeed without effectively changing the name of the file, undefine
 * HAVE_RENAME in config.h and use this one instead.
 */
#ifdef rename
int
#if HAVE_STDC
rename (const char *from, const char *to)
#else /* K&R style */
rename (from, to)
const char *from, *to;
#endif /* HAVE_STDC */
{
    struct stat s_from, s_to;

    /*
     * make sure file exists, and get the inode info
     */
    if (stat (from, &s_from) != 0)
	return -1;

    /*
     * short circuit attempts to link/unlink directory files
     * in case we're running as root.
     */
    if ((s_from.st_mode & S_IFMT) != S_IFREG) {
	errno = EINVAL;
	return -1;
    }

    /*
     * make sure that the source != destination, and
     * that the destination is not a directory.
     */
    if (stat (to, &s_to) == 0) {
	if (s_to.st_ino == s_from.st_ino ||
	    (s_to.st_mode & S_IFMT) != S_IFREG) {
	    errno = EINVAL;
	    return -1;
	}
	if (unlink (to) != 0)
	    return -1;
    }

    if (link (from, to) != 0)		/* make the link */
	return -1;

    if (unlink (from) != 0)		/* delete the source file */
	return -1;

    return 0;
}
#endif

/*
 * lock/unlock a file.
 */

int
#if HAVE_STDC
lock_file (const char *name, int fd)
#else /* K&R style */
lock_file (name, fd)
const char *name;
int fd;
#endif /* HAVE_STDC */
{
    int status;
#ifdef HAVE_F_SETLK
    struct flock flk;
    flk.l_type = F_WRLCK;
    flk.l_whence = SEEK_SET;
    flk.l_start = 0;
    flk.l_len = 0;
    status = fcntl (fd, F_SETLK, &flk);
    /* printf(">>> LOCK_FILE HAVE_F_SETLK[%s]=%d\n",name,status); */
#else
#ifdef HAVE_FLOCK
    status = flock (fd, LOCK_NB|LOCK_EX);
    /* printf(">>> LOCK_FILE HAVE_FLOCK[%s]=%d\n",name,status); */
#else
#ifdef HAVE_LOCKF
    status = lockf (fd, F_TLOCK, 0);
    /* printf(">>> LOCK_FILE HAVE_LOCKF[%s]=%d\n",name,status); */
#else
    status = 0;			/* pretend we locked it */
    /* printf(">>> LOCK_FILE nothing[%s]=%d\n",name,status); */
#ifdef undef
    int lockfd;
    char lockfname[MAXPATHLEN];

    sprintf(lockfname, "%s.lock", name); /* XXX filename may exceed 14 chars */

    if ((lockfd = open(lockfname, O_CREAT|O_EXCL|O_WRONLY, 0)) >= 0) {
	status = 0;
	(void) close(lockfd);
    } else {
	status = -1;
	if (errno != EEXIST)
	    perror(lockfname);
    }
#endif /* undef */
#endif /* HAVE_LOCKF */
#endif /* HAVE_FLOCK */
#endif /* HAVE_F_SETLK */
    return status;
}

int
#if HAVE_STDC
unlock_file (const char *name, int fd)
#else /* K&R style */
unlock_file (name, fd)
const char *name;
int fd;
#endif /* HAVE_STDC */
{
    int status;
#ifdef HAVE_F_SETLK
    struct flock flk;
    flk.l_type = F_UNLCK;
    flk.l_whence = 0;
    flk.l_start = 0;
    flk.l_len = 0;
    status = fcntl (fd, F_SETLK, &flk);
#else
#ifdef HAVE_FLOCK
    status = flock (fd, LOCK_UN);
#else
#ifdef HAVE_LOCKF
    status = lockf (fd, F_ULOCK, 0);
#else
    status = 0;			/* pretend success */
#ifdef undef
    char lockfname[MAXPATHLEN];

    sprintf(lockfname, "%s.lock", name);
    status = unlink(lockfname);
#endif /* undef */
#endif /* HAVE_LOCKF */
#endif /* HAVE_FLOCK */
#endif /* HAVE_F_SETLK */
    return status;
}

/*
 * Return the "official" name of the local host.
 */

#ifdef HAVE_GETHOSTBYNAME
#include <netdb.h>
#endif

#ifdef HAVE_UNAME
#include <sys/utsname.h>
#endif

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif

char shorthostname[MAXHOSTNAMELEN];
char fullhostname[MAXHOSTNAMELEN];
char *mailhostname = 0;
static char *localdomain = 0;

char *
#if HAVE_STDC
getlocalhostname (void)
#else /* K&R style */
getlocalhostname (VOID)
#endif /* HAVE_STDC */
{
    char *cp;

#ifdef HAVE_GETHOSTNAME
    gethostname (shorthostname, sizeof shorthostname - 1);
#else
#ifdef HAVE_UNAME
    struct utsname uts;
    uname (&uts);
    strcpy (shorthostname, uts.nodename);
#else
#ifdef PHOSTNAME
    if (evalpipe (PHOSTNAME, shorthostname, sizeof shorthostname) == NULL)
	shorthostname[0] = 0;
#else
#ifdef HOSTNAME
    strncpy (shorthostname, HOSTNAME, MAXHOSTNAMELEN-1);
#endif
#endif
#endif
#endif
    /*
     * See if the hostname already has a domain tacked on
     */
    if ((cp = index (shorthostname, '.'), cp)) {
	strcpy (fullhostname, shorthostname);
	*cp++ = 0;
	localdomain = fullhostname + (cp - shorthostname);
    }
    else
	localdomain = 0;

    if (!localdomain) {
#ifdef HAVE_GETHOSTBYNAME
	struct hostent *hp = gethostbyname (shorthostname);
	if (hp) {
	    cp = index (hp->h_name, '.');
	    if (cp) {
		strcpy (fullhostname, hp->h_name);
		localdomain = fullhostname + (cp - hp->h_name + 1);
	    }
	}
#endif
    }

    if (!localdomain) {
#ifdef LOCALDOMAIN
	sprintf (fullhostname, "%s%s", shorthostname, LOCALDOMAIN);
	localdomain = fullhostname + strlen (shorthostname) + 1;
#else
	strcpy (fullhostname, shorthostname);
#endif
    }
#ifdef HIDDENNET
    mailhostname = HIDDENNET;
#else
    mailhostname = fullhostname;
#endif
    return fullhostname;
}

char *
#if HAVE_STDC
evalpipe (const char *cmd, char *result, int maxlen)
#else /* K&R style */
evalpipe (cmd, result, maxlen)
const char *cmd;
char *result;
int maxlen;
#endif /* HAVE_STDC */
{
    char *cp;
    FILE *fp;

    if ((fp = mm_popen (cmd, "r"), fp)) {
	if (fgets (result, maxlen, fp) == result) {
	    if ((cp = index (result, '\n'), cp))
		*cp = 0;
	    else
		result = 0;
	    (void) mm_pclose (fp);
	    return result;
	}
	else
	    result = NULL;
	(void) mm_pclose (fp);
    }
    return result;
}

#ifdef read
#undef read
int
#if HAVE_STDC
sys_read (int fd, char *buf, int count)
#else /* K&R style */
sys_read (fd, buf, count)
int fd, count;
char *buf;
#endif /* HAVE_STDC */
{
    int n;

    while ((n = read (fd, buf, count)) < 0 && errno == EINTR)
	;
    return n;
}
#endif

#ifdef write
#undef write
int
#if HAVE_STDC
sys_write (int fd, const char *buf, int count)
#else /* K&R style */
sys_write (fd, buf, count)
int fd, count;
const char *buf;
#endif /* HAVE_STDC */
{

    int n, left = count;

    for (left = count; left > 0; left -= n) {
	n = write (fd, buf, count);
	if (n < 0)
	{
	    if (errno != EINTR)
		return n;
	    else
		n = 0;
	}
    }

    return (count - left);
}
#endif
