/* Copyright (c) 1998 John E. Davis (davis@space.mit.edu)
 *
 * This file is part of slrn.
 *
 * Slrn 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.
 * 
 * Slrn 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 Slrn; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place - Suite 330, 
 * Boston, MA  02111-1307, USA.
 */

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifndef VMS
# include <sys/types.h>
# include <sys/stat.h>
#else
# include "vms.h"
#endif

#include <slang.h>
#include "jdmacros.h"

#include "util.h"
#include "ttymsg.h"


/* This function allows NULL as a parameter. This fact _is_ exploited */
char *slrn_skip_whitespace (char *b) /*{{{*/
{
   register char ch;
   
   if (b == NULL) return NULL;

   while (((ch = *b) == ' ') || (ch == '\t') || (ch == '\n'))
     b++;
   return b;
}

/*}}}*/

/* returns a pointer to the end of the string */
char *slrn_trim_string (char *smin) /*{{{*/
{
   register char *s, ch;
   
   if (smin == NULL) return NULL;
   s = smin + strlen (smin);
   
   while (s > smin)
     {
	s--;
	ch = *s;
	if ((ch == ' ')
	    || (ch == '\n')
	    || (ch == '\t'))
	  {
	     *s = 0;
	     continue;
	  }
	
	s++;
	break;
     }
   return s;
}

/*}}}*/

char *slrn_strchr (char *s, char ch) /*{{{*/
{
   register char ch1;
   
   while (((ch1 = *s) != 0) && (ch != ch1)) s++;
   if (ch1 == 0) return NULL;
   return s;
}

/*}}}*/

/* Search for characters from list in string str.  If found, return a pointer
 * to the first occurrence.  If not found, return NULL. */
char *slrn_strbrk (char *str, char *list) /*{{{*/
{
   char ch, ch1, *p;
   
   while ((ch = *str) != 0)
     {
	p = list;
	while ((ch1 = *p) != 0)
	  {
	     if (ch == ch1) return str;
	     p++;
	  }
	str++;
     }
   return NULL;
}

/*}}}*/

char *slrn_simple_strtok (char *s, char *chp) /*{{{*/
{
   static char *s1;
   char ch = *chp;
   
   if (s == NULL)
     {
	if (s1 == NULL) return NULL;
	s = s1;
     }
   else s1 = s;
   
   while (*s1 && (*s1 != ch)) s1++;
   
   if (*s1 == 0)
     {
	s1 = NULL;
     }
   else *s1++ = 0;
   return s;
}

/*}}}*/


/* Note!!!  These routines assume a flat address space !! */

int slrn_case_strncmp (unsigned char *a, register unsigned char *b, register unsigned int n) /*{{{*/
{
   register unsigned char cha, chb, *bmax;
   register int diff = a - b;
   
   bmax = b + n;
   while (b < bmax)
     {
	cha = UPPER_CASE(b[diff]);
	chb = UPPER_CASE(*b);
	if (cha != chb)
	  {
	     return (int) cha - (int) chb;
	  }
	else if (chb == 0) return 0;
	b++;
     }
   return 0;
}

/*}}}*/

int slrn_case_strcmp (unsigned char *a, register unsigned char *b) /*{{{*/
{
   register unsigned char cha, chb;
   register int diff = a - b;
   
   while (1)
     {
	cha = UPPER_CASE(b[diff]);
	chb = UPPER_CASE(*b);
	if (cha != chb)
	  {
	     return (int) cha - (int) chb;
	  }
	else if (chb == 0) break;
	b++;
     }
   return 0;
}

/*}}}*/

#if defined(IBMPC_SYSTEM)
void slrn_os2_convert_path (char *path)
{
   char ch;
   while ((ch = *path) != 0)
     {
	if (ch == '/') *path = SLRN_PATH_SLASH_CHAR;
	path++;
     }
}
#endif

#ifdef SLRN_USE_OS2_FAT
void slrn_os2_make_fat (char *file, char *name, char *ext)
{
   static char drive[3] = " :";
   char fsys[5];

   strcpy (file, name);
   if (isalpha(file[0]) && (file[1] == ':'))
     drive[0] = file[0];
   else
     drive[0] = _getdrive();

   if ((0 == _filesys (drive, fsys, sizeof (fsys)))
       && (0 == stricmp (fsys, "FAT")))
     {
	/* FAT */
	_remext (file);                      /* Remove the extension */
     }

   strcat (file, ext);
}
#endif

static void fixup_path (char *path) /*{{{*/
{
#ifndef VMS
   unsigned int len;
   
   len = strlen (path);
   if (len == 0) return;
# ifdef IBMPC_SYSTEM
   slrn_os2_convert_path (path);
# endif
   if (path[len - 1] == SLRN_PATH_SLASH_CHAR) return;
   path[len] = SLRN_PATH_SLASH_CHAR;
   path[len + 1] = 0;
#endif
}

/*}}}*/

/* dir and file could be the same in which case this performs a strcat. 
 * If name looks like an absolute path, it will be returned.
 */
int slrn_dircat (char *dir, char *name, char *file)
{
   unsigned int len = 0;
   
   if (name != NULL) 
     {
	if (slrn_is_absolute_path (name))
	  {
	     strcpy (file, name);
#if defined(IBMPC_SYSTEM)
	     slrn_os2_convert_path (file);
#endif
	     return 0;
	  }
	
	len = strlen (name);
     }
   
   if (dir != NULL) len += strlen (dir);
   
   len += 2;			       /* for / and \0 */
   if (len > SLRN_MAX_PATH_LEN)
     {
	slrn_error ("File name too long.");
	return -1;
     }
   
   if (dir != NULL) 
     {
	if (dir != file) strcpy (file, dir);
	fixup_path (file);
     }
   else *file = 0;

   if (name != NULL) strcat (file, name);
#if defined(IBMPC_SYSTEM)
   slrn_os2_convert_path (file);
#endif
   return 0;
}

/*{{{ Memory Allocation Routines */

static char *do_malloc_error (int do_error)
{
   if (do_error) slrn_error ("Memory allocation failure.");
   return NULL;
}

char *slrn_safe_strmalloc (char *s) /*{{{*/
{
   s = SLmake_string (s);
   if (s == NULL) slrn_exit_error ("Out of memory.");
   return s;
}

/*}}}*/

char *slrn_strnmalloc (char *s, unsigned int len, int do_error)
{
   s = SLmake_nstring (s, len);
   
   if (s == NULL)
     return do_malloc_error (do_error);
   
   return s;
}

char *slrn_strmalloc (char *s, int do_error)
{
   if (s == NULL) return NULL;
   return slrn_strnmalloc (s, strlen (s), do_error);
}


char *slrn_malloc (unsigned int len, int do_memset, int do_error)
{   
   char *s;
   
   s = (char *) SLmalloc (len);
   if (s == NULL)
     return do_malloc_error (do_error);

   if (do_memset)
     memset (s, 0, len);
   
   return s;
}

char *slrn_realloc (char *s, unsigned int len, int do_error)
{
   if (s == NULL)
     return slrn_malloc (len, 0, do_error);
   
   s = SLrealloc (s, len);
   if (s == NULL)
     return do_malloc_error (do_error);
	
   return s;
}

char *slrn_safe_malloc (unsigned int len)
{
   char *s;
   
   s = slrn_malloc (len, 1, 0);

   if (s == NULL)
     slrn_exit_error ("Out of memory");
   
   return s;
}

void slrn_free (char *s)
{
   if (s != NULL) SLfree (s);
}

/*}}}*/

char *slrn_fix_regexp (char *pat) /*{{{*/
{
   static char newpat[256];
   char *p, ch;
   unsigned int len;

   len = 1;			       /* For ^ */
   p = pat;
   while (*p != 0)
     {
	if ((*p == '.') || (*p == '*')) len++;
	len++;
	p++;
     }
   len++;			       /* for $ */
   len++;			       /* for \0 */

   if (len > sizeof(newpat))
     slrn_exit_error ("Pattern too long for buffer");
     
   p = newpat;

   *p++ = '^';
   while ((ch = *pat++) != 0)
     {
	if (ch == '.')
	  *p++ = '\\';
	else if (ch == '*')
	  *p++ = '.';

	*p++ = ch;
     }
   
   if (*(p - 1) != '$') 
     *p++ = '$';

   *p = 0;

   return newpat;
}

/*}}}*/

int slrn_is_absolute_path (char *path)
{
   if (path == NULL)
     return 0;

   if (*path == SLRN_PATH_SLASH_CHAR)
     return 1;
#if defined(IBMPC_SYSTEM)
   if (*path == '/')
     return 1;
   if (*path && (path[1] == ':'))
     return 1;
#endif
   return 0;
}


/* This is like slrn_dircat except that any dots in name can get mapped to
 * slashes.  It also mallocs space for the resulting file.
 */
char *slrn_spool_dircat (char *root, char *name, int map_dots)
{
   char *spool_group, *p, ch;
   unsigned int len;

   len = strlen (root);

   spool_group = SLmalloc (strlen (name) + len + 2);
   if (spool_group == NULL)
     {
	slrn_exit_error ("Out of memory.");
     }

   strcpy (spool_group, root);

   p = spool_group + len;
   if (len && (*(p - 1) != SLRN_PATH_SLASH_CHAR))
     *p++ = SLRN_PATH_SLASH_CHAR;

   strcpy (p, name);

   if (map_dots) while ((ch = *p) != 0)
     {
	if (ch == '.') *p = SLRN_PATH_SLASH_CHAR;
	p++;
     }
#if defined(IBMPC_SYSTEM)
   slrn_os2_convert_path (spool_group);
#endif
   return spool_group;
}

int slrn_delete_file (char *f) /*{{{*/
{
#ifdef VMS
   return delete(f);
#else
   return unlink(f);
#endif
}

/*}}}*/

int slrn_fclose (FILE *fp) /*{{{*/
{
   if (0 == fclose (fp)) return 0;
   slrn_error ("Error closing file.  File system full? (errno = %d)", errno);
   return -1;
}

/*}}}*/


int slrn_file_exists (char *file) /*{{{*/
{
   struct stat st;
   int m;
   
#ifdef _S_IFDIR
# ifndef S_IFDIR
#  define S_IFDIR _S_IFDIR
# endif
#endif
   
#ifndef S_ISDIR
# ifdef S_IFDIR
#  define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
# else
#  define S_ISDIR(m) 0
# endif
#endif
   
   if (file == NULL)
     return -1;

   if (stat(file, &st) < 0) return 0;
   m = st.st_mode;
   
   if (S_ISDIR(m)) return (2);
   return 1;
}

/*}}}*/

char *slrn_basename (char *file)
{
   char *f;
#ifdef VMS
   f = slrn_strchr (file, ']');
   if (f != NULL) return f + 1;
   return file;
#else

   while (NULL != (f = slrn_strchr (file, SLRN_PATH_SLASH_CHAR)))
     file = f + 1;
   
   return file;
#endif
}


static int file_eqs (char *a, char *b)
{
#ifdef REAL_UNIX_SYSTEM
   struct stat st_a, st_b;
#endif
   
   if (0 == strcmp (a, b))
     return 1;
   
#ifndef REAL_UNIX_SYSTEM
   return 0;
#else
   if (-1 == stat (a, &st_a))
     return 0;
   if (-1 == stat (b, &st_b))
     return 0;
   
   return ((st_a.st_ino == st_b.st_ino)
	   && (st_a.st_dev == st_b.st_dev));
#endif
}

   

int slrn_copy_file (char *infile, char *outfile)
{
   FILE *in, *out;
   int ch;
   int ret;
   
   if ((infile == NULL) || (outfile == NULL))
     return -1;

   if (file_eqs (infile, outfile))
     return 0;

   if (NULL == (in = fopen (infile, "rb")))
     {
	slrn_error ("Error opening %s", infile);
	return -1;
     }
   
   if (NULL == (out = fopen (outfile, "wb")))
     {
	fclose (in);
	slrn_error ("Error opening %s", outfile);
	return -1;
     }

   ret = 0;
   while (EOF != (ch = getc (in)))
     {
	if (EOF == putc (ch, out))
	  {
	     slrn_error ("Write Error: %s", outfile);
	     ret = -1;
	     break;
	  }
     }
   
   fclose (in);
   if (-1 == slrn_fclose (out))
     ret = -1;
   
   return ret;
}

int slrn_move_file (char *infile, char *outfile)
{
   if ((infile == NULL) || (outfile == NULL))
     return -1;

   if (file_eqs (infile, outfile))
     return 0;

   (void) slrn_delete_file (outfile);
   if (-1 == rename (infile, outfile))
     {
	if (-1 == slrn_copy_file (infile, outfile))
	  return -1;
	slrn_delete_file (infile);
     }
   return 0;
}

