/* Path parsing library module,
   Rick Smereka, Copyright (C) 2005-2006.

   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, get a copy via the Internet at
   http://gnu.org/copyleft/gpl.html or write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307 USA

   You can contact the author via email at rsmereka@future-lab.com

   Original Linux version Dec/2005, Rick Smereka

   Ported to DOS, Windows, QNX and FreeBSD. Feb/2006, Rick Smereka */

#include "stdhead.h"
#include "path.h"

int path_split(char *pin, int has_fname, char *drive, char *path, char *fname)
{
   /* Split a path into it's component parts. Parts are returned
      in 'drive' (single character), 'path' and 'fname'. 'drive' 
      is always 'EOS' in a Unix/Linux/QNX system. All components 
      must be allocated to sufficient size by the caller. Paths must 
      be supplied in absolute form, not in relative form. The 
      last component of the path is assumed to be the file name.
      The flag 'has_fname' indicates whether a file name is
      present in the input path ('pin'). If the file name is not
      present, a NULL pointer may be passed for 'fname'.
      Function returns 'TRUE' upon success, 'FALSE' upon error. */

   char mname[] = "path_split";
   char *tmp;
   int len, nwords, i, j, opos, last_dir;

   // basic checks

   if (pin == (char *)NULL)
      {
      logman("%s:null[pin]", mname);
      return(FALSE);
      }

   len = strlen(pin);

   if (!len)
      {
      logman("%s:empty[pin]", mname);
      return(FALSE);
      }

   if (drive == (char *)NULL)
      {
      logman("%s:null[drive]", mname);
      return(FALSE);
      }

   *drive = EOS;

   if (path == (char *)NULL)
      {
      logman("%s:null[path]", mname);
      return(FALSE);
      }

   path[0] = EOS;

   if (has_fname)
      {
      if (fname == (char *)NULL)
         {
         logman("%s:null[fname]", mname);
         return(FALSE);
         }

      fname[0] = EOS;
      }

   // printf("%s:pin=%s,%d\n", mname, pin, len);

   /* path must contain a minimum of 3 words if the file name
      is present, 2 words otherwise */

   nwords = ll_words(pin, PATH_SEP);

   if (has_fname)
      {
      if (nwords < 3)
         {
         logman("%s:invalid path[pin] s/b min 3 words", mname);
         return(FALSE);
         }
      }
   else
      if (nwords < 2)
         {
         logman("%s:invalid path[pin] s/b min of 2 words", mname);
         return(FALSE);
         }

   /* isolate drive first, a colon in the second byte is required
      under DOS and Windows */

#ifdef OS_WIN32
   if (pin[1] != ':')
      {
      logman("%s:drive ID must be present under Windows systems", mname);
      return(FALSE);
      }
#endif

#ifdef OS_DOS
   if (pin[1] != ':')
      {
      logman("%s:drive ID must be present under DOS systems", mname);
      return(FALSE);
      }
#endif

#ifdef OS_WIN32
   *drive = toupper(pin[0]);
#endif

#ifdef OS_DOS
   *drive = toupper(pin[0]);
#endif

   // isolate the path next

   if ((tmp = (char *)malloc(len + 1)) == (char *)NULL)
      {
      logman("%s:alloc fail[tmp]", mname);
      return(FALSE);
      }

   // printf("%s:nwords=%d\n", mname, nwords);
   opos = 0;
   
   if (has_fname)
      last_dir = nwords - 1;
   else
      last_dir = nwords;

   // start from word 2

   for(i = 2; i <= last_dir; i++)
      {
      if (!ll_word(pin, tmp, i, PATH_SEP))
         {
         logman("%s:bad rc[FALSE] from ll_word[%d]", mname, i);
         free(tmp);
         return(FALSE);
         }

      // printf("%s:tmp[%d]=%s\n", mname, i, tmp);

      for(j = 0; tmp[j] != EOS; j++)
         path[opos++] = tmp[j];

      if (i < last_dir)
         path[opos++] = PATH_SEP;
      }

   path[opos] = EOS;
   free(tmp);

   // isolate the file name last

   if (has_fname)
      if (!ll_word(pin, fname, nwords, PATH_SEP))
         {
         logman("%s:bad rc from ll_word[%d]", mname, nwords);
         return(FALSE);
         }

   return(TRUE);
}

int path_join(char drive, char *path, char *fname, char *pout)
{
   /* Join a path from it's component parts. In Windows and DOS,
      the 'drive' is required to be a single alphabetic character
      representing the drive ID. The 'path' must not begin with
      a path separator. Upon success, the joined path
      will be placed into 'pout' (this must be allocated to
      sufficient size by the caller). The file name ('fname')
      may be a NULL pointer which indicates no file name.
      Function returns 'TRUE' upon success, 'FALSE' otherwise. */

   char mname[] = "path_join";
   int has_fname;

   // check drive ID in Windows and DOS

#ifdef OS_WIN32
   if (!isalpha(drive))
      {
      logman("%s:'%c' is not a valid drive letter", mname, drive);
      return(FALSE);
      }
#endif

#ifdef OS_DOS
   if (!isalpha(drive))
      {
      logman("%s:'%c' is not a valid drive letter", mname, drive);
      return(FALSE);
      }
#endif

   // make sure 'path' does not start with separator

   if (path[0] == PATH_SEP)
      {
      logman("%s:'path' component must not start with separator", mname);
      return(FALSE);
      }

   // other checks

   if (path == (char *)NULL || !strlen(path))
      {
      logman("%s:null or empty[path]", mname);
      return(FALSE);
      }

   if (fname == (char *)NULL)
      has_fname = FALSE;

   if (has_fname && !strlen(fname))
      {
      logman("%s:empty[fname]", mname);
      return(FALSE);
      }

   // build final joined path

#ifdef OS_WIN32
   if (has_fname)
      sprintf(pout, "%c:\\%s\\%s", drive, path, fname);
   else
      sprintf(pout, "%c:\\%s", drive, path);
#endif

#ifdef OS_DOS
   if (has_fname)
      sprintf(pout, "%c:\\%s\\%s", drive, path, fname);
   else
      sprintf(pout, "%c:\\%s", drive, path);
#endif

#ifdef OS_UNIX
   if (has_fname)
      sprintf(pout, "/%s/%s", path, fname);
   else
      sprintf(pout, "/%s", path);
#endif

   return(TRUE);
}
