/* oslib.c - functions present only in some unix versions. */

/* Copyright (C) 1995 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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, or (at your option) any later
   version.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */

#include "config.h"

#include "bashtypes.h"
#include <sys/param.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "posixstat.h"
#include "filecntl.h"
#include "bashansi.h"

#include <stdio.h>
#include <errno.h>
#include <ctype.h>

#include "shell.h"
#include "maxpath.h"

#if !defined (errno)
extern int errno;
#endif /* !errno */

/* A standard error message to use when getcwd() returns NULL. */
char *bash_getcwd_errstr = "getcwd: cannot access parent directories";

#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK)
#  if !defined (CLK_TCK)
#    if defined (HZ)
#      define CLK_TCK HZ
#    else
#      define CLK_TCK 60
#    endif
#  endif /* !CLK_TCK */
#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */

long
get_clk_tck ()
{
  static long retval = 0;

  if (retval != 0)
    return (retval);

#if defined (HAVE_SYSCONF) && defined (_SC_CLK_TCK)
  retval = sysconf (_SC_CLK_TCK);
#else /* !SYSCONF || !_SC_CLK_TCK */
  retval = CLK_TCK;
#endif /* !SYSCONF || !_SC_CLK_TCK */

  return (retval);
}

/* Make the functions strchr and strrchr if they do not exist. */
#if !defined (HAVE_STRCHR)
char *
strchr (string, c)
     char *string;
     int c;
{
  register char *s;

  for (s = string; s && *s; s++)
    if (*s == c)
      return (s);

  return ((char *) NULL);
}

char *
strrchr (string, c)
     char *string;
     int c;
{
  register char *s, *t;

  for (s = string, t = (char *)NULL; s && *s; s++)
    if (*s == c)
      t = s;
  return (t);
}
#endif /* !HAVE_STRCHR */

#if !defined (HAVE_STRCASECMP)

#if !defined (to_lower)
#  define to_lower(c) (islower(c) ? (c) : toupper(c))
#endif /* to_lower */

/* Compare at most COUNT characters from string1 to string2.  Case
   doesn't matter. */
int
strncasecmp (string1, string2, count)
     char *string1, *string2;
     int count;
{
  register char *s1, *s2;
  register int r;

  if (count > 0)
    {
      s1 = string1;
      s2 = string2;
      do
	{
	  if ((r = to_lower (*s1) - to_lower (*s2)) != 0)
	    return r;
	  if (*s1++ == '\0')
	    break;
	  s2++;
	}
      while (--count != 0);
    }
  return (0);
}

/* strcmp (), but caseless. */
int
strcasecmp (string1, string2)
     char *string1, *string2;
{
  register char *s1, *s2;
  register int r;

  s1 = string1;
  s2 = string2;

  while ((r = to_lower (*s1) - to_lower (*s2)) == 0)
    {
      if (*s1++ == '\0')
        return 0;
      s2++;
    }
  return (r);
}
#endif /* !HAVE_STRCASECMP */

/* Return a string corresponding to the error number E.  From
   the ANSI C spec. */
#if defined (strerror)
#  undef strerror
#endif

#if !defined (HAVE_STRERROR)
char *
strerror (e)
     int e;
{
  static char emsg[40];
#if defined (HAVE_SYS_ERRLIST)
  extern int sys_nerr;
  extern char *sys_errlist[];

  if (e > 0 && e < sys_nerr)
    return (sys_errlist[e]);
  else
#endif /* HAVE_SYS_ERRLIST */
    {
      sprintf (emsg, "Unknown error %d", e);
      return (&emsg[0]);
    }
}
#endif /* HAVE_STRERROR */

#if !defined (HAVE_DUP2) || defined (DUP2_BROKEN)
/* Replacement for dup2 (), for those systems which either don't have it,
   or supply one with broken behaviour. */
int
dup2 (fd1, fd2)
     int fd1, fd2;
{
  int saved_errno, r;

  /* If FD1 is not a valid file descriptor, then return immediately with
     an error. */
  if (fcntl (fd1, F_GETFL, 0) == -1)
    return (-1);

  if (fd2 < 0 || fd2 >= getdtablesize ())
    {
      errno = EBADF;
      return (-1);
    }

  if (fd1 == fd2)
    return (0);

  saved_errno = errno;

  (void) close (fd2);
  r = fcntl (fd1, F_DUPFD, fd2);

  if (r >= 0)
    errno = saved_errno;
  else
    if (errno == EINVAL)
      errno = EBADF;

  /* Force the new file descriptor to remain open across exec () calls. */
  SET_OPEN_ON_EXEC (fd2);
  return (r);
}
#endif /* !HAVE_DUP2 */

/*
 * Return the total number of available file descriptors.
 *
 * On some systems, like 4.2BSD and its descendents, there is a system call
 * that returns the size of the descriptor table: getdtablesize().  There are
 * lots of ways to emulate this on non-BSD systems.
 *
 * On System V.3, this can be obtained via a call to ulimit:
 *	return (ulimit(4, 0L));
 *
 * On other System V systems, NOFILE is defined in /usr/include/sys/param.h
 * (this is what we assume below), so we can simply use it:
 *	return (NOFILE);
 *
 * On POSIX systems, there are specific functions for retrieving various
 * configuration parameters:
 *	return (sysconf(_SC_OPEN_MAX));
 *
 */

#if !defined (HAVE_GETDTABLESIZE)
int
getdtablesize ()
{
#  if defined (_POSIX_VERSION) && defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
  return (sysconf(_SC_OPEN_MAX));	/* Posix systems use sysconf */
#  else /* ! (_POSIX_VERSION && HAVE_SYSCONF && _SC_OPEN_MAX) */
#    if defined (ULIMIT_MAXFDS)
  return (ulimit (4, 0L));	/* System V.3 systems use ulimit(4, 0L) */
#    else /* !ULIMIT_MAXFDS */
#      if defined (NOFILE)	/* Other systems use NOFILE */
  return (NOFILE);
#      else /* !NOFILE */
  return (20);			/* XXX - traditional value is 20 */
#      endif /* !NOFILE */
#    endif /* !ULIMIT_MAXFDS */
#  endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
}
#endif /* !HAVE_GETDTABLESIZE */

#if !defined (HAVE_BCOPY)
void
bcopy (s,d,n)
     char *d, *s;
     int n;
{
  FASTCOPY (s, d, n);
}
#endif /* !HAVE_BCOPY */

#if !defined (HAVE_BZERO)
void
bzero (s, n)
     char *s;
     int n;
{
  register int i;

  for (i = 0; i < n; i++)
    s[i] = '\0';
}
#endif

#if !defined (HAVE_GETHOSTNAME)
#  if defined (HAVE_UNAME)
#    include <sys/utsname.h>
int
gethostname (name, namelen)
     char *name;
     int namelen;
{
  int i;
  struct utsname ut;

  --namelen;

  uname (&ut);
  i = strlen (ut.nodename) + 1;
  strncpy (name, ut.nodename, i < namelen ? i : namelen);
  name[namelen] = '\0';
  return (0);
}
#  else /* !HAVE_UNAME */
int
gethostname (name, namelen)
     int name, namelen;
{
  strncpy (name, "unknown", namelen);
  name[namelen] = '\0';
  return 0;
}
#  endif /* !HAVE_UNAME */
#endif /* !HAVE_GETHOSTNAME */

#if !defined (HAVE_KILLPG)
int
killpg (pgrp, sig)
     pid_t pgrp;
     int sig;
{
  return (kill (-pgrp, sig));
}
#endif /* !HAVE_KILLPG */


/* We supply our own version of getenv () because we want library
   routines to get the changed values of exported variables. */

/* The NeXT C library has getenv () defined and used in the same file.
   This screws our scheme.  However, Bash will run on the NeXT using
   the C library getenv (), since right now the only environment variable
   that we care about is HOME, and that is already defined.  */
#if defined (CAN_REDEFINE_GETENV)
static char *last_tempenv_value = (char *)NULL;
extern char **environ;

char *
getenv (name)
#if defined (__linux__) || defined (__bsdi__) || defined (convex)
     const char *name;
#else
     char const *name;
#endif /* !__linux__ && !__bsdi__ && !convex */
{
  SHELL_VAR *var;

  var = find_tempenv_variable ((char *)name);
  if (var)
    {
      FREE (last_tempenv_value);

      last_tempenv_value = savestring (value_cell (var));
      dispose_variable (var);
      return (last_tempenv_value);
    }
  else if (shell_variables)
    {
      var = find_variable ((char *)name);
      if (var && exported_p (var))
	return (value_cell (var));
    }
  else
    {
      register int i, len;

      /* In some cases, s5r3 invokes getenv() before main(); BSD systems
	 using gprof also exhibit this behavior.  This means that
	 shell_variables will be 0 when this is invoked.  We look up the
	 variable in the real environment in that case. */

      for (i = 0, len = strlen (name); environ[i]; i++)
	{
	  if ((STREQN (environ[i], name, len)) && (environ[i][len] == '='))
	    return (environ[i] + len + 1);
	}
    }

  return ((char *)NULL);
}

/* Some versions of Unix use _getenv instead. */
char *
_getenv (name)
#if defined (__linux__) || defined (__bsdi__) || defined (convex)
     const char *name;
#else
     char const *name;
#endif /* !__linux__ && !__bsdi__ && !convex */
{
  return (getenv (name));
}

#endif /* CAN_REDEFINE_GETENV */

#if !defined (HAVE_MKFIFO) && defined (PROCESS_SUBSTITUTION)
int
mkfifo (path, mode)
     char *path;
     int mode;
{
#if defined (S_IFIFO)
  return (mknod (path, (mode | S_IFIFO), 0));
#else /* !S_IFIFO */
  return (-1);
#endif /* !S_IFIFO */
}
#endif

#if !defined (HAVE_SETLINEBUF)
/* Cause STREAM to buffer lines as opposed to characters or blocks. */
int
setlinebuf (stream)
     FILE *stream;
{
#if defined (_IOLBF)
#  if defined (SETVBUF_REVERSED)
  setvbuf (stream, _IOLBF, (char *)NULL, BUFSIZ);
#  else /* !SETVBUF_REVERSED */
  setvbuf (stream, (char *)NULL, _IOLBF, BUFSIZ);
#  endif /* !SETVBUF_REVERSED */
#endif /* _IOLBF */
  return (0);
}
#endif /* !HAVE_SETLINEBUF */
