// @(#)root/unix:$Name:  $:$Id: TUnixSystem.cxx,v 1.41 2002/08/20 10:51:50 rdm Exp $
// Author: Fons Rademakers   15/09/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TUnixSystem                                                          //
//                                                                      //
// Class providing an interface to the UNIX Operating System.           //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#ifdef HAVE_CONFIG
#include "config.h"
#endif

#include "TUnixSystem.h"
#include "TROOT.h"
#include "TError.h"
#include "TMath.h"
#include "TOrdCollection.h"
#include "TRegexp.h"
#include "TException.h"
#include "Demangle.h"
#include "TEnv.h"
#include "TSocket.h"
#include "Getline.h"
#include "TInterpreter.h"
#include "TApplication.h"

//#define G__OLDEXPAND

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#if defined(R__SUN) || defined(R__SGI) || defined(R__HPUX) || \
    defined(R__AIX) || defined(R__LINUX) || defined(R__SOLARIS) || \
    defined(R__ALPHA) || defined(R__HIUX) || defined(R__FBSD) || \
    defined(R__MACOSX) || defined(R__HURD)
#   include <dirent.h>
#else
#   include <sys/dir.h>
#endif
#if defined(ULTRIX) || defined(R__SUN)
#   include <sgtty.h>
#endif
#if defined(R__AIX) || defined(R__LINUX) || defined(R__ALPHA) || \
    defined(R__SGI) || defined(R__HIUX) || defined(R__FBSD) || \
    defined(R__LYNXOS) || defined(R__MACOSX) || defined(R__HURD)
#   include <sys/ioctl.h>
#endif
#if defined(R__AIX) || defined(R__SOLARIS)
#   include <sys/select.h>
#endif
#if (defined(R__LINUX) && !defined(R__MKLINUX)) || defined(R__HURD)
#   ifndef SIGSYS
#      define SIGSYS  SIGUNUSED       // SIGSYS does not exist in linux ??
#   endif
#   include <dlfcn.h>
#endif
#if defined(R__ALPHA)
#   include <sys/mount.h>
#   ifndef R__TRUE64
    extern "C" int statfs(const char *file, struct statfs *buffer);
#   endif
#elif defined(R__MACOSX)
#   include <sys/mount.h>
    extern "C" int statfs(const char *file, struct statfs *buffer);
#elif defined(R__LINUX) || defined(R__HPUX) || defined(R__HURD)
#   include <sys/vfs.h>
#elif defined(R__FBSD)
#   include <sys/param.h>
#   include <sys/mount.h>
#else
#   include <sys/statfs.h>
#endif

#include <utime.h>
#include <syslog.h>
#include <sys/stat.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/param.h>
#include <pwd.h>
#include <errno.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#if defined(R__AIX)
#   define _XOPEN_EXTENDED_SOURCE
#   include <arpa/inet.h>
#   undef _XOPEN_EXTENDED_SOURCE
#   if !defined(_AIX41) && !defined(_AIX43)
    // AIX 3.2 doesn't have it
#   define HASNOT_INETATON
#   endif
#else
#   include <arpa/inet.h>
#endif
#include <sys/un.h>
#include <netdb.h>
#include <fcntl.h>
#if defined(R__SGI)
#   include <net/soioctl.h>
#endif
#if defined(R__SOLARIS)
#   include <sys/systeminfo.h>
#   include <sys/filio.h>
#   include <sys/sockio.h>
#   define HASNOT_INETATON
#   define INADDR_NONE (UInt_t)-1
#endif
#if defined(R__HPUX)
#   include <symlink.h>
#   include <dl.h>
#   if defined(R__GNU)
       extern "C" {
          extern shl_t cxxshl_load(const char *path, int flags, long address);
          extern int   cxxshl_unload(shl_t handle);
       }
#   elif !defined(__STDCPP__)
#      include <cxxdl.h>
#   endif
    // print stack trace (HP undocumented internal call)
    extern "C" void U_STACK_TRACE();
#   if defined(hpux9)
extern "C" {
   extern void openlog(const char *, int, int);
   extern void syslog(int, const char *, ...);
   extern void closelog(void);
   extern int setlogmask(int);
}
#   define HASNOT_INETATON
#   endif
#endif
#if defined(R__ALPHA) && !defined(R__GNU)
#   define HASNOT_INETATON
#endif
#if defined(R__HIUX)
#   define HASNOT_INETATON
#endif

#if defined(R__SGI) || defined(R__SOLARIS)
#   define HAVE_UTMPX_H
#   define UTMP_NO_ADDR
#endif
#if defined(R__ALPHA) || defined(R__AIX) || defined(R__FBSD) || \
    defined(R__LYNXOS) || defined(R__MACOSX)
#   define UTMP_NO_ADDR
#endif

#if defined(R__AIX) || (defined(R__FBSD) && !defined(R__ALPHA))
#   define USE_SIZE_T
#elif defined(R__GLIBC) || (defined(R__FBSD) && defined(R__ALPHA))
#   define USE_SOCKLEN_T
#endif

#if defined(R__LYNXOS)
extern "C" {
   extern int putenv(const char *);
   extern int inet_aton(const char *, struct in_addr *);
};
#endif

#ifdef HAVE_UTMPX_H
#include <utmpx.h>
#define STRUCT_UTMP struct utmpx
#else
#if defined(R__MKLINUX) && !defined(R__PPCEGCS)
        extern "C" {
#endif
#include <utmp.h>
#define STRUCT_UTMP struct utmp
#endif
#if !defined(UTMP_FILE) && defined(_PATH_UTMP)      // 4.4BSD
#define UTMP_FILE _PATH_UTMP
#endif
#if defined(UTMPX_FILE)                             // Solaris, SysVr4
#undef  UTMP_FILE
#define UTMP_FILE UTMPX_FILE
#endif
#ifndef UTMP_FILE
#define UTMP_FILE "/etc/utmp"
#endif

static STRUCT_UTMP *gUtmpContents;


const char *kServerPath     = "/tmp";
const char *kProtocolName   = "tcp";


//______________________________________________________________________________
static void SigHandler(ESignals sig)
{
   // Unix signal handler.

   if (gSystem)
      ((TUnixSystem*)gSystem)->DispatchSignals(sig);
}


ClassImp(TUnixSystem)

//______________________________________________________________________________
 TUnixSystem::TUnixSystem() : TSystem("Unix", "Unix System")
{ }

//______________________________________________________________________________
 TUnixSystem::~TUnixSystem()
{
   // Reset to original state.

   UnixResetSignals();
}

//______________________________________________________________________________
 Bool_t TUnixSystem::Init()
{
   // Initialize Unix system interface.

   if (TSystem::Init())
      return kTRUE;

   //--- install default handlers
   UnixSignal(kSigChild,                 SigHandler);
   UnixSignal(kSigBus,                   SigHandler);
   UnixSignal(kSigSegmentationViolation, SigHandler);
   UnixSignal(kSigIllegalInstruction,    SigHandler);
   UnixSignal(kSigSystem,                SigHandler);
   UnixSignal(kSigPipe,                  SigHandler);
   UnixSignal(kSigAlarm,                 SigHandler);
   UnixSignal(kSigUrgent,                SigHandler);
   UnixSignal(kSigFloatingException,     SigHandler);
   UnixSignal(kSigWindowChanged,         SigHandler);

#ifndef ROOTPREFIX
   gRootDir = Getenv("ROOTSYS");
   if (gRootDir == 0)
      gRootDir= "/usr/local/root";
#else
   gRootDir = ROOTPREFIX;
#endif

   return kFALSE;
}

//---- Misc --------------------------------------------------------------------

//______________________________________________________________________________
 void TUnixSystem::SetProgname(const char *name)
{
   // Set the application name (from command line, argv[0]) and copy it in
   // gProgName. Copy the application pathname in gProgPath.

   if (name && strlen(name) > 0) {
      gProgName = StrDup(BaseName(name));
      char *w   = Which(Getenv("PATH"), gProgName);
      gProgPath = StrDup(DirName(w));
      delete [] w;
   }
}

//______________________________________________________________________________
 void TUnixSystem::SetDisplay()
{
   // Set DISPLAY environment variable based on utmp entry. Only for UNIX.

   if (!Getenv("DISPLAY")) {
      char *tty = ::ttyname(0);  // device user is logged in on
      if (tty) {
         tty += 5;               // remove "/dev/"
         STRUCT_UTMP *utmp_entry;
         utmp_entry = (STRUCT_UTMP *)SearchUtmpEntry(ReadUtmpFile(), tty);
         if (utmp_entry) {
            if (utmp_entry->ut_host[0])
               if (strchr(utmp_entry->ut_host, ':')) {
                  Setenv("DISPLAY", utmp_entry->ut_host);
                  Warning("SetDisplay", "DISPLAY not set, setting it to %s",
                          utmp_entry->ut_host);
               } else {
                  char disp[64];
                  sprintf(disp, "%s:0.0", utmp_entry->ut_host);
                  Setenv("DISPLAY", disp);
                  Warning("SetDisplay", "DISPLAY not set, setting it to %s",
                          disp);
               }
#ifndef UTMP_NO_ADDR
            else if (utmp_entry->ut_addr) {
               struct hostent *he;
               if ((he = gethostbyaddr((const char*)&utmp_entry->ut_addr,
                                       sizeof(utmp_entry->ut_addr), AF_INET))) {
                  char disp[64];
                  sprintf(disp, "%s:0.0", he->h_name);
                  Setenv("DISPLAY", disp);
                  Warning("SetDisplay", "DISPLAY not set, setting it to %s",
                          disp);
               }
            }
#endif
         }
         free(gUtmpContents);
      }
   }
}

//______________________________________________________________________________
 const char *TUnixSystem::GetError()
{
   // Return system error string.

   Int_t err = GetErrno();
#if defined(R__SOLARIS) || defined (R__LINUX) || defined(R__AIX) || \
    defined(R__FBSD) || defined(R__HURD)
   return strerror(err);
#else
   if (err < 0 || err >= sys_nerr)
      return Form("errno out of range %d", err);
   return sys_errlist[err];
#endif
}

//______________________________________________________________________________
 const char *TUnixSystem::HostName()
{
   // Return the system's host name.

   if (fHostname == "") {
      char hn[64];
#if defined(R__SOLARIS) && !defined(R__KCC)
      sysinfo(SI_HOSTNAME, hn, sizeof(hn));
#else
      gethostname(hn, sizeof(hn));
#endif
      fHostname = hn;
   }
   return (const char *)fHostname;
}

//---- EventLoop ---------------------------------------------------------------

//______________________________________________________________________________
 void TUnixSystem::AddFileHandler(TFileHandler *h)
{
   // Add a file handler to the list of system file handlers.

   TSystem::AddFileHandler(h);
   if (h) {
      int fd = h->GetFd();
      if (h->HasReadInterest()) {
         fReadmask.Set(fd);
         fMaxrfd = TMath::Max(fMaxrfd, fd);
      }
      if (h->HasWriteInterest()) {
         fWritemask.Set(fd);
         fMaxwfd = TMath::Max(fMaxwfd, fd);
      }
   }
}

//______________________________________________________________________________
 TFileHandler *TUnixSystem::RemoveFileHandler(TFileHandler *h)
{
   // Remove a file handler from the list of file handlers.

   TFileHandler *oh = TSystem::RemoveFileHandler(h);
   if (oh) {       // found
      TFileHandler *th;
      TIter next(fFileHandler);
      fMaxrfd = 0;
      fMaxwfd = 0;
      fReadmask.Zero();
      fWritemask.Zero();
      while ((th = (TFileHandler *) next())) {
         int fd = th->GetFd();
         if (th->HasReadInterest()) {
            fReadmask.Set(fd);
            fMaxrfd = TMath::Max(fMaxrfd, fd);
         }
         if (th->HasWriteInterest()) {
            fWritemask.Set(fd);
            fMaxwfd = TMath::Max(fMaxwfd, fd);
         }
      }
   }
   return oh;
}

//______________________________________________________________________________
 void TUnixSystem::AddSignalHandler(TSignalHandler *h)
{
   // Add a signal handler to list of system signal handlers.

   TSystem::AddSignalHandler(h);
   UnixSignal(h->GetSignal(), SigHandler);
}

//______________________________________________________________________________
 TSignalHandler *TUnixSystem::RemoveSignalHandler(TSignalHandler *h)
{
   // Remove a signal handler from list of signal handlers.

   // if last handler of specific signal need to reset sighandler to default

   return TSystem::RemoveSignalHandler(h);
}

//______________________________________________________________________________
 void TUnixSystem::ResetSignal(ESignals sig, Bool_t reset)
{
   // If reset is true reset the signal handler for the specified signal
   // to the default handler, else restore previous behaviour.

   if (reset)
      UnixResetSignal(sig);
   else
      UnixSignal(sig, SigHandler);
}

//______________________________________________________________________________
 void TUnixSystem::IgnoreSignal(ESignals sig, Bool_t ignore)
{
   // If ignore is true ignore the specified signal, else restore previous
   // behaviour.

   UnixIgnoreSignal(sig, ignore);
}

//______________________________________________________________________________
 void TUnixSystem::DispatchOneEvent(Bool_t pendingOnly)
{
   // Dispatch a single event.

   while (1) {
      // first handle any X11 events
      if (gXDisplay && gXDisplay->Notify()) {
         if (fReadready.IsSet(gXDisplay->GetFd())) {
            fReadready.Clr(gXDisplay->GetFd());
            fNfd--;
         }
         if (!pendingOnly) return;
      }

      // check for file descriptors ready for reading/writing
      if (fNfd > 0 && fFileHandler->GetSize() > 0)
         if (CheckDescriptors())
            if (!pendingOnly) return;
      fNfd = 0;
      fReadready.Zero();
      fWriteready.Zero();

      // check synchronous signals
      if (fSigcnt > 0 && fSignalHandler->GetSize() > 0)
         if (CheckSignals(kTRUE))
            if (!pendingOnly) return;
      fSigcnt = 0;
      fSignals.Zero();

      // check synchronous timers
      if (fTimers && fTimers->GetSize() > 0)
         if (DispatchTimers(kTRUE)) {
            // prevent timers from blocking file descriptor monitoring
            Long_t to = NextTimeOut(kTRUE);
            if (to > kItimerResolution || to == -1)
               return;
         }

      if (pendingOnly) return;

      // nothing ready, so setup select call
      fReadready  = fReadmask;
      fWriteready = fWritemask;
      int mxfd = TMath::Max(fMaxrfd, fMaxwfd) + 1;
      fNfd = UnixSelect(mxfd, &fReadready, &fWriteready, NextTimeOut(kTRUE));
      if (fNfd < 0 && fNfd != -2) {
         int fd, rc;
         TFdSet t;
         for (fd = 0; fd < mxfd; fd++) {
            t.Set(fd);
            if (fReadmask.IsSet(fd)) {
               rc = UnixSelect(fd+1, &t, 0, 0);
               if (rc < 0 && rc != -2) {
                  SysError("DispatchOneEvent", "select: read error on %dn", fd);
                  fReadmask.Clr(fd);
               }
            }
            if (fWritemask.IsSet(fd)) {
               rc = UnixSelect(fd+1, 0, &t, 0);
               if (rc < 0 && rc != -2) {
                  SysError("DispatchOneEvent", "select: write error on %dn", fd);
                  fWritemask.Clr(fd);
               }
            }
            t.Clr(fd);
         }
      }
   }
}

//______________________________________________________________________________
 void TUnixSystem::Sleep(UInt_t milliSec)
{
   // Sleep milliSec milliseconds.

   struct timeval tv;

   tv.tv_sec  = milliSec / 1000;
   tv.tv_usec = (milliSec % 1000) * 1000;

   select(0, 0, 0, 0, &tv);
}

//---- handling of system events -----------------------------------------------

//______________________________________________________________________________
 void TUnixSystem::DispatchSignals(ESignals sig)
{
   // Handle and dispatch signals.

   switch (sig) {
   case kSigAlarm:
      DispatchTimers(kFALSE);
      break;
   case kSigChild:
      CheckChilds();
      return;
   case kSigBus:
   case kSigSegmentationViolation:
   case kSigIllegalInstruction:
   case kSigFloatingException:
      Printf("n *** Break *** %s", UnixSigname(sig));
      StackTrace();
      if (TROOT::Initialized()) {
         if (gException) {
            if (gApplication && gApplication->InheritsFrom("TRint")) {
               Getlinem(kCleanUp, 0);
               Getlinem(kInit, "Root > ");
            }
            gInterpreter->RewindDictionary();
            gInterpreter->ClearFileBusy();
         }
         Throw(sig);
      }
      Abort(-1);
      break;
   case kSigSystem:
   case kSigPipe:
      Printf("n *** Break *** %s", UnixSigname(sig));
      break;
   case kSigWindowChanged:
      Gl_windowchanged();
      break;
   default:
      fSignals.Set(sig);
      fSigcnt++;
      break;
   }

   // check a-synchronous signals
   if (fSigcnt > 0 && fSignalHandler->GetSize() > 0)
      CheckSignals(kFALSE);
}

//______________________________________________________________________________
 Bool_t TUnixSystem::CheckSignals(Bool_t sync)
{
   // Check if some signals were raised and call their Notify() member.

   TSignalHandler *sh;
   Int_t sigdone = -1;
   {
      TOrdCollectionIter it((TOrdCollection*)fSignalHandler);

      while ((sh = (TSignalHandler*)it.Next())) {
         if (sync == sh->IsSync()) {
            ESignals sig = sh->GetSignal();
            if ((fSignals.IsSet(sig) && sigdone == -1) || sigdone == sig) {
               if (sigdone == -1) {
                  fSignals.Clr(sig);
                  sigdone = sig;
                  fSigcnt--;
               }
               sh->Notify();
            }
         }
      }
   }
   if (sigdone != -1)
      return kTRUE;

   return kFALSE;
}

//______________________________________________________________________________
 void TUnixSystem::CheckChilds()
{
   // Check if childs have finished.

#if 0  //rdm
   int pid;
   while ((pid = UnixWaitchild()) > 0) {
      TIter next(zombieHandler);
      register UnixPtty *pty;
      while (pty = (UnixPtty*) next())
         if (pty->GetPid() == pid) {
            zombieHandler->RemovePtr(pty);
            pty->DiedNotify();
         }
   }
#endif
}

//______________________________________________________________________________
 Bool_t TUnixSystem::CheckDescriptors()
{
   // Check if there is activity on some file descriptors and call their
   // Notify() member.

   TFileHandler *fh;
   Int_t  fddone = -1;
   Bool_t read   = kFALSE;
#if defined(R__LINUX) && defined(__alpha__)
   // TOrdCollectionIter it(...) causes segv ?!?!? Also TIter fails.
   Int_t cursor = 0;
   while (cursor < fFileHandler->GetSize()) {
      fh = (TFileHandler*) fFileHandler->At(cursor++);
#else
   TOrdCollectionIter it((TOrdCollection*)fFileHandler);
   while ((fh = (TFileHandler*) it.Next())) {
#endif
      Int_t fd = fh->GetFd();
      if ((fd <= fMaxrfd && fReadready.IsSet(fd) && fddone == -1) ||
          (fddone == fd && read)) {
         if (fddone == -1) {
            fReadready.Clr(fd);
            fddone = fd;
            read = kTRUE;
            fNfd--;
         }
         fh->ReadNotify();
      }
      if ((fd <= fMaxwfd && fWriteready.IsSet(fd) && fddone == -1) ||
          (fddone == fd && !read)) {
         if (fddone == -1) {
            fWriteready.Clr(fd);
            fddone = fd;
            read = kFALSE;
            fNfd--;
         }
         fh->WriteNotify();
      }
   }
   if (fddone != -1)
      return kTRUE;

   return kFALSE;
}

//---- Directories -------------------------------------------------------------

//______________________________________________________________________________
 int TUnixSystem::MakeDirectory(const char *name)
{
   // Make a Unix file system directory. Returns 0 in case of success and
   // -1 if the directory could not be created.

   return UnixMakedir(name);
}

//______________________________________________________________________________
 void *TUnixSystem::OpenDirectory(const char *name)
{
   // Open a Unix file system directory. Returns 0 if directory does not exist.

   return UnixOpendir(name);
}

//______________________________________________________________________________
 void TUnixSystem::FreeDirectory(void *dirp)
{
   // Close a Unix file system directory.

   if (dirp)
      ::closedir((DIR*)dirp);
}

//______________________________________________________________________________
 const char *TUnixSystem::GetDirEntry(void *dirp)
{
   // Get next Unix file system directory entry. Returns 0 if no more entries.

   if (dirp)
      return UnixGetdirentry(dirp);
   return 0;
}

//______________________________________________________________________________
 Bool_t TUnixSystem::ChangeDirectory(const char *path)
{
   // Change directory. Returns kTRUE in case of success, kFALSE otherwise.

   Bool_t ret = (Bool_t) (::chdir(path) == 0);
   if (fWdpath != "")
      fWdpath = "";   // invalidate path cache
   return ret;
}

//______________________________________________________________________________
 const char *TUnixSystem::WorkingDirectory()
{
   // Return working directory.

   if (fWdpath != "")
      return fWdpath.Data();

   static char cwd[kMAXPATHLEN];
   if (::getcwd(cwd, kMAXPATHLEN) == 0) {
      fWdpath = "/";
      Error("WorkingDirectory", "getcwd() failed");
   }
   fWdpath = cwd;
   return fWdpath.Data();
}

//______________________________________________________________________________
 const char *TUnixSystem::HomeDirectory(const Char_t *userName)
{
   // Return the user's home directory.

   return UnixHomedirectory(userName);
}

//______________________________________________________________________________
 char *TUnixSystem::ConcatFileName(const char *dir, const char *name)
{
   // Concatenate a directory and a file name. Returned string must be
   // deleted by user.

   if (name == 0 || strlen(name) <= 0 || strcmp(name, ".") == 0)
      return StrDup(dir);

   char buf[kMAXPATHLEN];
   if (dir && (strcmp(dir, "/") != 0)) {
      if (dir[strlen(dir)-1] == '/')
         sprintf(buf, "%s%s", dir, name);
      else
         sprintf(buf, "%s/%s", dir, name);
   } else
      sprintf(buf, "/%s", name);

   return StrDup(buf);
}

//---- Paths & Files -----------------------------------------------------------

//______________________________________________________________________________
 Bool_t TUnixSystem::AccessPathName(const char *path, EAccessMode mode)
{
   // Returns FALSE if one can access a file using the specified access mode.
   // Mode is the same as for the Unix access(2) function.
   // Attention, bizarre convention of return value!!

   if (::access(path, mode) == 0)
      return kFALSE;
   fLastErrorString = GetError();
   return kTRUE;
}

//______________________________________________________________________________
 void TUnixSystem::Rename(const char *f, const char *t)
{
   // Rename a file.

   ::rename(f, t);
   fLastErrorString = GetError();
}

//______________________________________________________________________________
 int TUnixSystem::GetPathInfo(const char *path, Long_t *id, Long_t *size,
                             Long_t *flags, Long_t *modtime)
{
   // Get info about a file: id, size, flags, modification time.
   // Id      is (statbuf.st_dev << 24) + statbuf.st_ino
   // Size    is the file size
   // Flags   is file type: 0 is regular file, bit 0 set executable,
   //                       bit 1 set directory, bit 2 set special file
   //                       (socket, fifo, pipe, etc.)
   // Modtime is modification time.
   // The function returns 0 in case of success and 1 if the file could
   // not be stat'ed.

   return UnixFilestat(path, id,  size, flags, modtime);
}

//______________________________________________________________________________
 int TUnixSystem::GetFsInfo(const char *path, Long_t *id, Long_t *bsize,
                           Long_t *blocks, Long_t *bfree)
{
   // Get info about a file system: id, bsize, bfree, blocks.
   // Id      is file system type (machine dependend, see statfs())
   // Bsize   is block size of file system
   // Blocks  is total number of blocks in file system
   // Bfree   is number of free blocks in file system
   // The function returns 0 in case of success and 1 if the file system could
   // not be stat'ed.

   return UnixFSstat(path, id,  bsize, blocks, bfree);
}

//______________________________________________________________________________
 int TUnixSystem::Link(const char *from, const char *to)
{
   // Create a link from file1 to file2. Returns 0 when successful,
   // -1 in case of failure.

   return ::link(from, to);
}

//______________________________________________________________________________
 int TUnixSystem::Symlink(const char *from, const char *to)
{
   // Create a symlink from file1 to file2. Returns 0 when succesfull,
   // -1 in case of failure.

#if defined(R__AIX)
   return ::symlink((char*)from, (char*)to);
#else
   return ::symlink(from, to);
#endif
}

//______________________________________________________________________________
 int TUnixSystem::Unlink(const char *name)
{
   // Unlink, i.e. remove, a file or directory. Returns 0 when succesfull,
   // -1 in case of failure.

   struct stat finfo;

   if (stat(name, &finfo) < 0)
      return -1;

   if (S_ISDIR(finfo.st_mode))
      return ::rmdir(name);
   else
      return ::unlink(name);
}

//---- expand the metacharacters as in the shell -------------------------------

// expand the metacharacters as in the shell

static const char
#ifdef G__OLDEXPAND
   shellEscape     = '\\',
   *shellStuff     = "(){}<>\"'",
#endif
   *shellMeta      = "~*[]{}?$";


#ifndef G__OLDEXPAND
//______________________________________________________________________________
 Bool_t TUnixSystem::ExpandPathName(TString &path)
{
   // Expand a pathname getting rid of special shell characters like ~.$, etc.
   // For Unix/Win32 compatibility use $(XXX) instead of $XXX when using
   // environment variables in a pathname. If compatibility is not an issue
   // you can use on Unix directly $XXX.

   const char *p, *patbuf = (const char *)path;

   // skip leading blanks
   while (*patbuf == ' ')
      patbuf++;

   // any shell meta characters ?
   for (p = patbuf; *p; p++)
      if (strchr(shellMeta, *p))
         goto expand;

   return kFALSE;

expand:
   // replace $(XXX) by $XXX
   path.ReplaceAll("$(","$");
   path.ReplaceAll(")","");

   path = ExpandFileName(path.Data());
   return kFALSE;
}
#endif

#ifdef G__OLDEXPAND
//______________________________________________________________________________
 Bool_t TUnixSystem::ExpandPathName(TString &patbuf0)
{
   // Expand a pathname getting rid of special shell characters like ~.$, etc.
   // For Unix/Win32 compatibility use $(XXX) instead of $XXX when using
   // environment variables in a pathname. If compatibility is not an issue
   // you can use on Unix directly $XXX.

   const char *patbuf = (const char *)patbuf0;
   const char *hd, *p;
   char   cmd[kMAXPATHLEN], stuffedPat[kMAXPATHLEN], name[70];
   char  *q;
   FILE  *pf;
   int    ch;

   // skip leading blanks
   while (*patbuf == ' ')
      patbuf++;

   // any shell meta characters ?
   for (p = patbuf; *p; p++)
      if (strchr(shellMeta, *p))
         goto needshell;

   return kFALSE;

needshell:
   // replace $(XXX) by $XXX
   patbuf0.ReplaceAll("$(","$");
   patbuf0.ReplaceAll(")","");

   // escape shell quote characters
   EscChar(patbuf, stuffedPat, sizeof(stuffedPat), (char*)shellStuff, shellEscape);

#ifdef R__HPUX
   strcpy(cmd, "/bin/echo ");
#else
   strcpy(cmd, "echo ");
#endif

   // emulate csh -> popen executes sh
   if (stuffedPat[0] == '~') {
      if (stuffedPat[1] != '0' && stuffedPat[1] != '/') {
         // extract user name
         for (p = &stuffedPat[1], q = name; *p && *p !='/';)
            *q++ = *p++;
         *q = '0';
         hd = UnixHomedirectory(name);
         if (hd == 0)
            strcat(cmd, stuffedPat);
         else {
            strcat(cmd, hd);
            strcat(cmd, p);
         }
      } else {
         hd = UnixHomedirectory(0);
         if (hd == 0) {
            fLastErrorString = GetError();
            return kTRUE;
         }
         strcat(cmd, hd);
         strcat(cmd, &stuffedPat[1]);
      }
   } else
      strcat(cmd, stuffedPat);

   if ((pf = ::popen(&cmd[0], "r")) == NULL) {
      fLastErrorString = GetError();
      return kTRUE;
   }

   // read first argument
   patbuf0 = "";
   int cnt = 0;
#if defined(R__ALPHA) || defined(R__AIX)
again:
#endif
   for (ch = fgetc(pf); ch != EOF && ch != ' ' && ch != '\n'; ch = fgetc(pf)) {
      patbuf0.Append(ch);
      cnt++;
   }
#if defined(R__ALPHA) || defined(R__AIX)
   // Work around bug timing problem due to delay in forking a large program
   if (cnt == 0 && ch == EOF) goto again;
#endif

   // skip rest of pipe
   while (ch != EOF) {
      ch = fgetc(pf);
      if (ch == ' ' || ch == 't') {
         fLastErrorString = "expression ambigous";
         ::pclose(pf);
         return kTRUE;
      }
   }

   ::pclose(pf);

   return kFALSE;
}
#endif

//______________________________________________________________________________
 char *TUnixSystem::ExpandPathName(const char *path)
{
   // Expand a pathname getting rid of special shell characaters like ~.$, etc.
   // For Unix/Win32 compatibility use $(XXX) instead of $XXX when using
   // environment variables in a pathname. If compatibility is not an issue
   // you can use on Unix directly $XXX. The user must delete returned string.

   TString patbuf = path;
   if (ExpandPathName(patbuf))
      return 0;
   return StrDup(patbuf.Data());
}

//______________________________________________________________________________
 int TUnixSystem::Umask(Int_t mask)
{
   // Set the process file creation mode mask.

   return ::umask(mask);
}

//______________________________________________________________________________
 int TUnixSystem::Utime(const char *file, Long_t modtime, Long_t actime)
{
   // Set a files modification and access times. If actime = 0 it will be
   // set to the modtime. Returns 0 on success and -1 in case of error.

   if (!actime)
      actime = modtime;

   struct utimbuf t;
   t.actime  = (time_t)actime;
   t.modtime = (time_t)modtime;
   return ::utime(file, &t);
}

//______________________________________________________________________________
 char *TUnixSystem::Which(const char *search, const char *wfil, EAccessMode mode)
{
   // Find location of file "wfil" in a search path.
   // The search path is specified as a : separated list of directories.
   // User must delete returned string. Returns 0 in case file is not found.

   char name[kMAXPATHLEN], file[kMAXPATHLEN];
   const char *ptr;
   char *next, *exname;

   exname = gSystem->ExpandPathName(wfil);
   if (exname)
      strcpy(file, exname);
   else
      file[0] = 0;
   delete [] exname;

   if (file[0] == '/') {
      exname = StrDup(file);
      if (exname && access(exname, mode) == 0) {
         if (gEnv->GetValue("Root.ShowPath", 0))
            Printf("Which: %s = %s", wfil, exname);
         return exname;
      }
      delete [] exname;
      if (gEnv->GetValue("Root.ShowPath", 0))
         Printf("Which: %s = <not found>", wfil);
      return 0;
   }

   if (search == 0)
      search = ".";

   for (ptr = search; *ptr;) {
      for (next = name; *ptr && *ptr != ':'; )
         *next++ = *ptr++;
      *next = '0';

      if (name[0] != '/' && name[0] != '$' && name[0] != '~') {
         char tmp[kMAXPATHLEN];
         strcpy(tmp, name);
         strcpy(name, gSystem->WorkingDirectory());
         strcat(name, "/");
         strcat(name, tmp);
      }

      if (*(name + strlen(name) - 1) != '/')
         strcat(name, "/");
      strcat(name, file);

      exname = gSystem->ExpandPathName(name);
      if (exname && access(exname, mode) == 0) {
         if (gEnv->GetValue("Root.ShowPath", 0))
            Printf("Which: %s = %s", wfil, exname);
         return exname;
      }
      delete [] exname;
      if (*ptr)
         ptr++;
   }

   if (gEnv->GetValue("Root.ShowPath", 0))
      Printf("Which: %s = <not found>", wfil);
   return 0;
}

//---- environment manipulation ------------------------------------------------

//______________________________________________________________________________
 void TUnixSystem::Setenv(const char *name, const char *value)
{
   // Set environment variable. The string passed will be owned by
   // the environment and can not be reused till a "name" is set
   // again. The solution below will lose the space for the string
   // in that case, but if this functions is not called thousands
   // of times that should not be a problem.

   char *s = new char [strlen(name)+strlen(value) + 2];
   sprintf(s, "%s=%s", name, value);

   ::putenv(s);
}

//______________________________________________________________________________
 const char *TUnixSystem::Getenv(const char *name)
{
   // Get environment variable.

   return ::getenv(name);
}

//---- Processes ---------------------------------------------------------------

//______________________________________________________________________________
 int TUnixSystem::Exec(const char *shellcmd)
{
   // Execute a command.

   return ::system(shellcmd);
}

//______________________________________________________________________________
 FILE *TUnixSystem::OpenPipe(const char *command, const char *mode)
{
   // Open a pipe.

   return ::popen(command, mode);
}

//______________________________________________________________________________
 int TUnixSystem::ClosePipe(FILE *pipe)
{
   // Close the pipe.

   return ::pclose(pipe);
}

//______________________________________________________________________________
 int TUnixSystem::GetPid()
{
   // Get process id.

   return ::getpid();
}

//______________________________________________________________________________
 void TUnixSystem::Exit(int code, Bool_t mode)
{
   // Exit the application.

   if (mode)
      ::exit(code);
   else
      ::_exit(code);
}

//______________________________________________________________________________
 void TUnixSystem::Abort(int)
{
   // Abort the application.

   ::abort();
}

//______________________________________________________________________________
 void TUnixSystem::StackTrace()
{
   // Print a stack trace.

#if defined(R__HPUX) && !defined(R__GNU)
      Printf("");
      U_STACK_TRACE();
      Printf("");
#endif
}

//---- System Logging ----------------------------------------------------------

//______________________________________________________________________________