/* Various utilities - OS/2 versions
   Copyright (C) 1994, 1995, 1996 the Free Software Foundation.

   Written 1994, 1995, 1996 by:
   Juan Grigera, Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
   Jakub Jelinek, Mauricio Plaza.

   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#define INCL_DOS
#define INCL_PM
#define INCL_DOSPROCESS
#define INCL_DOSFILEMGR
#define INCL_DOSDEVICES   /* Device values */
#define INCL_DOSDATETIME
#define INCL_DOSERRORS
#include "config.h"
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <io.h>
#include <fcntl.h>
#include <signal.h>		/* my_system */
#include <limits.h>		/* INT_MAX */
#include <sys/time.h>		/* select: timeout */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <process.h>
#include <fs.h>
#include <util.h>

char *
get_owner (int uid)
{
    return "none";
}

char *
get_group (int gid)
{
    return "none";
}

/* Pipes are guaranteed to be able to hold at least 4096 bytes */
/* More than that would be unportable */
#define MAX_PIPE_SIZE 4096

static int error_pipe[2];	/* File descriptors of error pipe */
static int old_error;		/* File descriptor of old standard error */

/* Creates a pipe to hold standard error for a later analysis. */
/* The pipe can hold 4096 bytes. Make sure no more is written */
/* or a deadlock might occur. */
void 
open_error_pipe (void)
{
   return;
}

void 
close_error_pipe (int error, char *text)
{
   return;
}

void 
check_error_pipe (void)
{
    char error[MAX_PIPE_SIZE];
    int len = 0;
    if (old_error >= 0){
	while (len < MAX_PIPE_SIZE)
	{
	    int rvalue;

	    rvalue = read (error_pipe[0], error + len, 1);
	    len ++;
	    if (rvalue <= 0)
		break;
	}
	error[len] = 0;
	close (error_pipe[0]);
    }
    if (len > 0)
        message (0, " Warning ", error);
}


static int 
StartWindowsProg (char *name, SHORT type)
{
   PROGDETAILS  pDetails;

   memset(&pDetails, 0, sizeof(PROGDETAILS)) ;
   pDetails.Length = sizeof(pDetails);
   pDetails.pszExecutable = name;      /* program name */
   pDetails.pszStartupDir = NULL;   /* default directory for new app. */
   pDetails.pszParameters = NULL;   /* command line */
   pDetails.progt.fbVisible = SHE_VISIBLE ;
   pDetails.pszEnvironment = NULL;

   switch (type) {
   case 0:
      /* Win Standard */
      pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
      break;
   case 1:
      /* Win 3.1 Protect */
      pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
      break;
   case 2:
      /* Win 3.1 Enh. Protect */
      pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
      break;
   default:
      pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
     break;
   } /* endswitch */
   WinStartApp(NULLHANDLE, 
               &pDetails, 
               NULL, 
               NULL, 
               SAF_INSTALLEDCMDLINE|SAF_STARTCHILDAPP) ;
   return 0;
}


static int 
os2_system (int as_shell_command, const char *shell, const char *command, const char *parm);

/* 
  as_shell_command = 1: If a program is started during input line, CTRL-O
                        or RETURN 
                   = 0: F3, F4
*/
int 
my_system (int as_shell_command, const char *shell, const char *command)
{
   char *sh;            /* This is the shell -- always! */
   char *cmd;           /* This is the command (only the command) */
   char *parm;          /* This is the parameter (can be more than one) */
   register int length, i;
   char temp[4096];     /* That's enough! */

   sh = get_default_shell();
   if (strcmp(sh, shell)) {
      /* 
         Not equal  -- That means: shell is the program and command is the
         parameter 
      */
      cmd  = (char *) shell;
      parm = (char *) command;
   } else {
      /* look into the command and take out the program */
      if (command) {
         strcpy(temp, command);
         length = strlen(command);
         for (i=length-1; i>=0; i--) {
            if (command[i] == ' ') {
               temp[i] = (char) 0;
               length--;
            } else 
                break;
         } /* endfor */
         if (i==-1) {
            /* only blanks */
            return -1;
         } /* endif */
         if (parm = strchr(temp, (char) ' ')) {
            *parm = (char) 0;
            parm++;
         }
         cmd  = (char *) temp;
      } else {
         /* command is NULL */
         cmd = parm = NULL;
      } /* endif */
   } /* endif */
   /* .ado: Konvertierung wenn shell <> get_default_shell */
   os2_system (as_shell_command, sh, cmd, parm);
}

static int 
ux_startp (const char *shell, const char *command, const char *parm) 
{
    if (parm) {
         spawnlp (P_WAIT, 
              (char *) shell, 
              (char *) shell, 
              "/c", 
              (char *) command, 
              (char *) parm,
              (char *) 0);
    } else {
         spawnlp (P_WAIT, 
              (char *) shell, 
              (char *) shell, 
              "/c", 
              (char *) command, 
              (char *) 0);
    } /* endif */
    return 0;
}


static int 
os2_system (int as_shell_command, const char *shell, const char *command, const char *parm)
{
   register int i, j;
   ULONG        AppType = 0;                    /* Application type flags (returned) */
   APIRET       rc = NO_ERROR;                  /* Return Code */
   char         pathValue[5] = "PATH";          /* For DosSearchPath */
   UCHAR        searchResult[MAX_PATH * 2 + 1];     /* For DosSearchPath */
   
   char         *cmdString;
   char         *postFix[3];
   char         *line;
   /* ------------------------------------------------------- */
   STARTDATA    StartData;
   CHAR         ObjBuf[100];
   ULONG        SessionID;
   PID          pid;

   if (command == NULL) {
      /* .ado: just start a shell, we don't need the parameter */
      spawnl (P_WAIT, 
              (char *) shell, 
              (char *) shell, 
              (char *) command, (char *) 0);  
      return 0;
   }

   memset(&StartData, 0, sizeof(StartData)) ;
   StartData.Length             = sizeof(StartData);    
   StartData.Related            = SSF_RELATED_CHILD;
   StartData.FgBg               = SSF_FGBG_BACK;
   StartData.TraceOpt           = SSF_TRACEOPT_NONE;   
   StartData.PgmTitle           = NULL;                
   StartData.TermQ              = NULL;                 
   StartData.InheritOpt         = SSF_INHERTOPT_PARENT;
   StartData.IconFile           = 0;              
   StartData.PgmHandle          = 0;             
   StartData.PgmControl         = SSF_CONTROL_VISIBLE ; 
   StartData.ObjectBuffer       = ObjBuf;          
   StartData.ObjectBuffLen      = 100;            
   StartData.PgmInputs          = parm;

   postFix[0] = ".exe";
   postFix[1] = ".cmd";
   postFix[2] = ".bat";

   i = strlen(command);
   if (command[i-1] == ' ') {
      /* The user has used ALT-RETURN */
      i--;
   } /* endif */
   cmdString = (char *) malloc(i+1);
   for (j=0; j<i; j++) {
      cmdString[j] = command[j];
   } /* endfor */
   cmdString[j] = (char) 0;

   if ((i < 5) || ((i > 4) && (cmdString[i-4]) != '.')) {
      /* without Extension */
      line = (char *) malloc(i+5);
      rc = 1;
      for (i=0; (i<3 && rc); i++) {
         /* Search for the file */
         strcpy(line, cmdString);
         strcat(line, postFix[i]);
         rc = DosSearchPath((SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY),
                            (PSZ) pathValue,
                            line,
                            searchResult,
                            sizeof(searchResult));
      } /* endfor */
      free (line);
   } else {         
      /* Just search */
      rc = DosSearchPath((SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY),
                         (PSZ) pathValue,
                         cmdString,
                         searchResult,
                         sizeof(searchResult));
   } /* endif */
   free(cmdString);
   if (rc != 0) {
      /* Internal command or the program was written with absolut path */
      return ux_startp(shell, command, parm);
   } /* endif */

   /* Application to be started */
   StartData.PgmName            = searchResult;
   StartData.Environment        = NULL;
   rc = DosQueryAppType(searchResult, &AppType);
   if (rc == NO_ERROR) {
      StartData.SessionType = PROG_WINDOWABLEVIO;
      if ((AppType & 0x00000007) == FAPPTYP_WINDOWAPI) {
         /* Window API */
         StartData.SessionType = PROG_PM;
         return DosStartSession(&StartData, &SessionID, &pid);
      } /* endif */
      if ((AppType & 0x00000007) == FAPPTYP_WINDOWCOMPAT) {
         /* Window compat */
         return ux_startp(shell, command, parm);
      } /* endif */
      if (AppType & 0x0000ffff & FAPPTYP_DOS) {
         /* PC/DOS Format */
        StartData.SessionType = PROG_WINDOWEDVDM;
        return DosStartSession(&StartData, &SessionID, &pid);
      } /* endif */
      if (AppType & 0x0000ffff & FAPPTYP_WINDOWSREAL) {
         /* Windows real mode app */
        return StartWindowsProg(searchResult, 0);
      } /* endif */
      if (AppType & 0x0000ffff & FAPPTYP_WINDOWSPROT) {
         /* Windows Protect mode app*/
        return StartWindowsProg(searchResult, 1);
      } /* endif */
      if (AppType & 0x0000ffff & FAPPTYP_WINDOWSPROT31) {
         /* Windows 3.1 Protect mode app*/
        return StartWindowsProg(searchResult, 2);
      } /* endif */
      rc = DosStartSession(&StartData, &SessionID, &pid) ;
   } else {
      /* It's not a known exe type or it's a CMD/BAT file */
      i = strlen(searchResult);
      if ((toupper(searchResult[--i]) == 'T') && 
          (toupper(searchResult[--i]) == 'A') &&  
          (toupper(searchResult[--i]) == 'B') &&  
          (searchResult[--i] == '.')   ) {
        StartData.SessionType = PROG_WINDOWEDVDM;
        rc = DosStartSession(&StartData, &SessionID, &pid) ;
      } else {
         rc = ux_startp (shell, command, parm);
      } /* endif */
   } /* endif */
   return rc;
}

char *tilde_expand (char *directory)
{
    return strdup (directory);
}

void 
sleep(unsigned long dwMiliSecs)
{
    DosSleep(dwMiliSecs);
}



/* Canonicalize path, and return a new path. Do everything in situ.
   The new path differs from path in:
	Multiple BACKSLASHs are collapsed to a single BACKSLASH.
	Leading `./'s and trailing `/.'s are removed.
	Trailing BACKSLASHs are removed.
	Non-leading `../'s and trailing `..'s are handled by removing
	portions of the path. */
char *
canonicalize_pathname (char *path)
{
    int i, start;
    char stub_char;

    stub_char = (*path == PATH_SEP) ? PATH_SEP : '.';

    /* Walk along path looking for things to compact. */
    i = 0;
    for (;;) {
        if (!path[i])
	    break;

      	while (path[i] && path[i] != PATH_SEP)
	    i++;

      	start = i++;

      	/* If we didn't find any slashes, then there is nothing left to do. */
      	if (!path[start])
	    break;

        /* Handle multiple BACKSLASHs in a row. */
        while (path[i] == PATH_SEP)
	    i++;

        if ((start + 1) != i) {
	    strcpy (path + start + 1, path + i);
	    i = start + 1;
	}

        /* Handle backquoted BACKSLASH. */
/*         if (start > 0 && path[start - 1] == '\\')
	    continue; */

        /* Check for trailing BACKSLASH. */
        if (start && !path[i]) {
	zero_last:
	    path[--i] = '\0';
	    break;
	}

        /* Check for `../', `./' or trailing `.' by itself. */
        if (path[i] == '.') {
	    /* Handle trailing `.' by itself. */
	    if (!path[i + 1])
	        goto zero_last;

	    /* Handle `./'. */
	    if (path[i + 1] == PATH_SEP) {
	        strcpy (path + i, path + i + 1);
	        i = start;
	        continue;
	    }

	    /* Handle `../' or trailing `..' by itself. 
	       Remove the previous ?/ part with the exception of
	       ../, which we should leave intact. */
	    if (path[i + 1] == '.' && (path[i + 2] == PATH_SEP || !path[i + 2])) {
	        while (--start > -1 && path[start] != PATH_SEP);
	        if (!strncmp (path + start + 1, "..\\", 3))
	            continue;
	        strcpy (path + start + 1, path + i + 2);
	        i = start;
	        continue;
	    }
	}
    }

    if (!*path) {
        *path = stub_char;
        path[1] = '\0';
    }
    return path;
}


void 
my_statfs (struct my_statfs *myfs_stats, char *path)
{
    PFSALLOCATE pBuf;
    PFSINFO     pFsInfo;
    ULONG       lghBuf;

    ULONG       diskNum = 0;
    ULONG       logical = 0;

    UCHAR       szDeviceName[3] = "A:";
    ULONG       ulOrdinal       = 0;     /* Ordinal of entry in name list */
    PBYTE       pszFSDName      = NULL;  /* pointer to FS name            */
    APIRET      rc              = NO_ERROR; /* Return code                */
    BYTE        fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
    ULONG       cbBuffer   = sizeof(fsqBuffer);        /* Buffer length) */
    PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2) fsqBuffer;

    int i, len = 0;

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

    lghBuf = sizeof(FSALLOCATE);
    pBuf = (PFSALLOCATE) malloc(lghBuf);

    /* Get the free number of Bytes */
    rc = DosQueryFSInfo(0L, FSIL_ALLOC, (PVOID) pBuf, lghBuf);
    /* KBytes available */
    myfs_stats->avail = pBuf->cSectorUnit * pBuf->cUnitAvail * pBuf->cbSector / 1024;
    /* KBytes total */
    myfs_stats->total = pBuf->cSectorUnit * pBuf->cUnit * pBuf->cbSector / 1024; 
    myfs_stats->nfree = pBuf->cUnitAvail;
    myfs_stats->nodes = pBuf->cbSector;

    lghBuf  = sizeof(FSINFO);
    pFsInfo = (PFSINFO) malloc(lghBuf);
    rc      = DosQueryFSInfo(0L, 
                             FSIL_VOLSER, 
                             (PVOID) pFsInfo, 
                             lghBuf);
    /* Get name */
    myfs_stats->device = strdup(pFsInfo->vol.szVolLabel);    /* Label of the Disk */

    /* Get the current disk for DosQueryFSAttach */
    rc = DosQueryCurrentDisk(&diskNum, &logical);

    szDeviceName[0] = (UCHAR) (diskNum + (ULONG) 'A' - 1);
    /* Now get the type of the disk */
    rc = DosQueryFSAttach(szDeviceName, 
                          0L, 
                          FSAIL_QUERYNAME, 
                          pfsqBuffer, 
                          &cbBuffer);

    pszFSDName = pfsqBuffer->szName + pfsqBuffer->cbName + 1;
    myfs_stats->mpoint = strdup(pszFSDName);    /* FAT, HPFS ... */

    myfs_stats->type = pBuf->idFileSystem;
    /* What is about 3 ?*/
    if (myfs_stats->type == 0) {
       myfs_stats->typename = (char *) malloc(11);
       strcpy(myfs_stats->typename, "Local Disk");
    } else {
       myfs_stats->typename = (char *) malloc(13);
       strcpy(myfs_stats->typename, "Other Device");
    } /* endif */

    free(pBuf);
    free(pFsInfo);
}

int 
gettimeofday (struct timeval* tvp, void *p)
{
   DATETIME     pdt = {0};
   if (p != NULL)		/* what is "p"? */
	return 0;	
	
    /* Since MC only calls this func from get_random_hint we return 
     * some value, not exactly the "correct" one
     */
    DosGetDateTime(&pdt);
    tvp->tv_usec = (pdt.hours * 60 + pdt.minutes) * 60 + pdt.seconds;
    /* Number of milliseconds since Windows started */
    tvp->tv_sec = tvp->tv_usec * 1000 + pdt.hundredths * 10;
    return 0;
}

/* FAKE functions */

int 
look_for_exe(const char* pathname)
{
   int j;
   char *p;
   int lgh = strlen(pathname);

   if (lgh < 4) {
      return 0;
   } else {
      p = (char *) pathname;
      for (j=0; j<lgh-4; j++) {
         p++;
      } /* endfor */
      if (!stricmp(p, ".exe") || 
          !stricmp(p, ".bat") || 
          !stricmp(p, ".com") || 
          !stricmp(p, ".cmd")) {
         return 1;
      } /* endif */
   } /* endif */
   return 0;
}

/* lstat - Because of symlinks in Unix, stat will give info 
 *   on the file pointed to and lstat on the symlink itself.
 *   We have no such a difference/trouble.
 */
int 
lstat (const char* pathname, struct stat *buffer)
{
   int rc = stat (pathname, buffer);
   if (rc == 0) {
     if (!(buffer->st_mode & S_IFDIR)) { 
        if (look_for_exe(pathname)) {
           buffer->st_mode |= S_IXUSR | S_IXGRP | \
                              S_IXOTH;
        }
     }
   } /* endif */
   return rc;
}

int 
getuid ()	      
{
    return 0;
}

int 
getgid ()	      
{
    return 0;
}

int 
readlink (char* path, char* buf, int size)
{
    return -1;
}

int 
symlink (char *n1, char *n2)
{
    return -1;
}

int 
link (char *p1, char *p2)
{
    return -1;
}

int 
chown (char *path, int owner, int group)
{
    return -1;
}

int 
mknod (char *path, int mode, int dev)
{
    return -1;
}

void 
init_uid_gid_cache (void)
{
    return;
}

int 
mc_doublepopen (int inhandle, int inlen, pid_t *the_pid, char *command, ...)
{
	return 0;
}

int 
mc_doublepclose (int pipe, pid_t pid)
{
	return 0;
}

/*hacks to get it compile, remove these after vfs works */
char *
vfs_get_current_dir (void)
{
	return NULL;
}

int 
vfs_current_is_extfs (void)
{
	return 0;
}

int 
vfs_file_is_ftp (char *filename)
{
	return 0;
}

int
mc_utime (char *path, struct utimbuf *times)
{
	return 0;
}


void
extfs_run (char *file)
{
   return;
}

int
geteuid(void)
{
   return 0;
}

char *
getcwd(char *buffer, int size)
{
   APIRET rc;
   ULONG  lgh = size;
   ULONG   uDriveNum = 0;               /* Drive number (A=1, B=2 ...) */
   ULONG   uDriveMap = 0;               /* Mapping */
   CHAR    p[4] = "A:\\";
   char    *tmpBuf;

   rc = DosQueryCurrentDisk(&uDriveNum, &uDriveMap);
   p[0] = (char) (uDriveNum + (int) 'A' - 1);

   rc = DosQueryCurrentDir(0L,
                           (PBYTE) buffer,
                           &lgh);
   if (rc == NO_ERROR) {
      tmpBuf = (char *) malloc(strlen(buffer) + 1);
      strcpy(tmpBuf, buffer);
      strcpy(buffer, p);
      strcat(buffer, tmpBuf);
      free(tmpBuf);
      return strdup(buffer);
   } else {
      return NULL;
   } /* endif */
}

int
mc_chdir(char *pathname)
{
   APIRET       ret;
   register int lgh = strlen(pathname);

   /* Set the current drive */
   if (lgh == 0) {
      return -1;
   } else {
      /* First set the default drive */
      if (lgh > 1) {
         if (pathname[1] == ':') {
             ret = DosSetDefaultDisk(toupper(pathname[0]) - 'A' + 1); 
         } /* endif */
      } /* endif */
      /* After that, set the current dir! */
      ret = DosSetCurrentDir(pathname);
   } /* endif */
   return ret;
}
 
int
mc_chmod(char *pathName, int unxmode)
{
   /* OS/2 does not need S_REG */
   int os2Mode = unxmode & 0x0FFF;
   return chmod(pathName, os2Mode);
}

static int
conv_os2_unx_rc(int os2rc)
{
   int errCode;
   switch (os2rc) {
      case ERROR_FILE_NOT_FOUND:
      case ERROR_PATH_NOT_FOUND:
      case ERROR_FILENAME_EXCED_RANGE:
         errCode = ENOENT;
         break;
      case ERROR_NOT_DOS_DISK:
      case ERROR_SHARING_VIOLATION:
      case ERROR_SHARING_BUFFER_EXCEEDED:
      case ERROR_ACCESS_DENIED:
         errCode = EOS2ERR;
         break;
      case ERROR_INVALID_PARAMETER:
         errCode = EINVAL;
         break;
      default: 
         errCode = EINVAL;
        break;
   } /* endswitch */
   return errCode;
}

int
mc_open (char *file, int flags, int pmode)
{
    return open(file, (flags | O_BINARY), pmode);
}

int
mc_unlink(char *pathName)
{
   /* Use OS/2 API to delete a file, if the file is set as read-only, 
      the file will be deleted without asking the user! */
   APIRET       rc;
   rc = DosDelete(pathName);
   if (!rc) {
      return 0;
   } /* endif */ 
   if (rc == ERROR_ACCESS_DENIED) {
      chmod(pathName, (S_IREAD|S_IWRITE));
      rc = DosDelete(pathName);
      if (rc) {
         errno = conv_os2_unx_rc(rc) ;
         return -1;
      } else {
         return 0;
      } /* endif */
   } else {
      errno = conv_os2_unx_rc(rc) ;
      return -1;
   } /* endif */
}

int
strncasecmp (char *s, char *d, int count)
{
    register char result;

    while (count > 0){
	if (result = (0x20 | *s) - (0x20 | *d))
	    break;
	if (!*s)
	    return 0;
	s++;
	d++;
	count--;
    }
    return result;
}

FILE *
_popen(const char *cmd, const char *mode)
{
   return NULL;
}

int
_pclose(FILE *file)
{
   return 1;
}

char *
get_default_editor (void)
{
	char *tmp;
	APIRET  rc;
	char    pathValue[5] = "PATH";
	UCHAR   searchResult[MAX_PATH + 1];
	
        /* EPM is not always be installed */
	rc = DosSearchPath((SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY),
			   (PSZ) pathValue,
			   "EPM.EXE",
			   searchResult,
			   sizeof(searchResult));
	if (rc != 0) {
                /* The system editor is always there */
		return strdup("e.exe");
	} else {
                /* Let it be searched from my_system */
		return strdup("epm.exe");
	} /* endif */
}

/* get_default_shell
   Get the default shell for the current hardware platform
   TODO: Get the value of %OS2_SHELL% or %SHELL%: which one?
*/
char *
get_default_shell()
{
    return getenv ("COMSPEC");
}

int
getpid (void)
{
	return 0;
}

int
errno_dir_not_empty (int err)
{
    if (err == EACCESS)
	return 1;
    return 0;
}

char *
get_mc_lib_dir ()
{
   char *mchome = getenv("MCHOME");
   if (!mchome) {
      return "D:\\mc";
   } else {
      return mchome;
   } /* endif */
}
