/*
   <p>Force a string to conform to local filename conventions.</p>

   <kbd>
   <b>environ - C programming support functions</b><br>
   Copyright (C) 2005  Martin C Gregorie
   
   This program is free software; you can redistribute it and/or
   modify it under the terms of the Lesser GNU General Public License.
   
   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 Lesser GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   </kbd>
  
   <p>Contact Martin Gregorie at <i>martin@gregorie.org</i></p>
*/

#include "environ.h"
#include <ctype.h>

#ifdef OS9
#include <strings.h>
#endif

#ifdef MSDOS
#include <string.h>
#include <stdlib.h>
#endif

#ifdef LINUX
#include <string.h>
#include <stdlib.h>
#endif

/*
   Force a string to conform to current operating system conventions 
   as regards length, default extension, and numbers of dots.
   <u>name</u> is the file name to be fixed, <u>ext</u> is the
   default extension and <u>type</u> is the file type,
   which must be "f" for a regular file and "d" for a directory.
   There are specific rules for each operating system:
   
   MS-DOS: name processing:
   <ol>
   <li>loose any leading dot (DOS hates them)</li>
   <li>change all but the last dot into underscores
       (DOS can't handle multiple dots in a filename)</li>
   <li>Leave the last dot intact and treat it as the
       start of the extension.</li>
   <li>Names consisting entirely of dots ( . and .. )
       are valid directory references and are left alone.</li>
   <li>Limit the filename size to MAXFILE characters.</li>
   </ol>
   OS/9 and Linux: Dot processing:
   <ol>
   <li>Copy to last dot (or end)</li>
   <li>Limit filename to MAXFILE characters</li>
   </ol>
   Extension processing.
   This applies to OS/9, MS-DOS and Linux:
   <ol>
   <li>If there is an extension (either in the filename or
       the default parameter) append a dot to the filename.</li>
   <li>If the filename contains an extension (all chars after
       the last dot) it is copied onto the end of the
       (truncated) filename.</li>
   <li>Copy the default extension if there isn't one in
       the file name (i.e., no dots other than a leading dot).</li>
   <li>Truncate the extension so that:
       <ul>
       <li>a - It doesnt exceed MAXEXT characters</li>
       <li>b - The complete filename doesn't exceed MAXNAME characters</li>
       </ul>
   </li>
   </ol>
   Now apply operating system-specific rules to capitalisation:
   <ul>
   <li>MS-DOS: All names are upper case.</li>

   <li>OS/9:   Retain the existing capitalisation except that:
   <ol>
       <li>Directory names are entirely upper case</li>
       <li>Filenames that are already entirely upper case
           retain an upper case initial letter with all
           other letters converted to lower case.</li>
   </ol>
   </li>
   <li>Linux:  Anything goes.</li>
   </ul>

   <b>NOTE:</b>    The function allocates storage for the
                   modified name in a safe place. If it is
                   called repeatedly this space should be
                   released as soon as possible by calling
                   <i>free()</i>.

   The function returns the modified filename
   on success and null on failure.
*/
char *fixname(char *name, char *ext, char *type)
{
    char    *newname;
    int     i,
            j,
            k,
            extproc,
            dotpos,
            alphacount,
            uppercount,
            dotcount,
            namesize,
            spaceleft;

    /*
        Make a place to hold the new filename
    */
    newname = (char*)malloc(MAXNAME+1);
    if (newname == NULL)
        return (newname);

    /*
        Enable extension processing.
    */
    extproc = TRUE;

    /*
        Count dots, alphas, and caps in the name.
        Find the position of the last dot (pointer to the
        extension). If there are no dots, this defaults to
        the end of the filename.
    */
    namesize = dotpos = strlen(name);
    alphacount = uppercount = dotcount = 0;
    for (i = 0; name[i] != 0; i++)
    {
        if (name[i] == DOT)
        {
            dotpos = i;
            dotcount++;
        }
        if (isalpha(name[i]))
            alphacount++;
        if (isupper(name[i]))
            uppercount++;
    }

    /*
        Fix up processing for a single leading stop.
        The effect is that the leading stop is never taken
        as the start of the file extension.
    */
    if (dotcount == namesize)        /* found . or .. */
    {
        extproc = FALSE;            /* disable extension processing */
        dotpos = namesize;
    }
    else
    if (dotcount == 1 && dotpos == 0)
    {
        dotcount = 0;
        dotpos = namesize;
    }

#ifdef MSDOS

    /*
        MS-DOS: Name processing:
        1    loose any leading dot (DOS hates them)
        2    change all but the last dot into underscores
             (DOS can't handle multiple dots in a filename)
        3    Leave the last dot intact and treat it as the
             start of the extension.
        4    Names consisting entirely of dots ( . and .. )
             are valid directory references and are left alone.
        5    Limit the filename size to MAXFILE characters.
    */
    if (dotcount == namesize)        /* name must be . or .. */
    {
        for (j=0; j<strlen(name); j++)
            newname[j] = name[j];
    }
    else
    {
        for (i=0, j=0, k=0; i<min(MAXFILE,dotpos); i++)
            if (name[i] == DOT)
            {
                k++;
                if (i != 0)
                {
                    if (k<dotcount)
                        newname[j++] = UNDERSCORE;
                    else
                        newname[j++] = DOT;
                }
            }
            else
                newname[j++] = name[i];
    }
#else
    /*
        OS/9 and Linux: Dot processing:
        1    Copy to last dot (or end)
        2    Limit filename to MAXFILE characters
    */
    for (j=0; j<min(MAXFILE,dotpos);j++)
        newname[j] = name[j];
#endif

    /*
        Extension processing.
        This applies to OS/9, MS-DOS and Linux:
        1    If there is an extension (either in the filename or
             the default parameter) append a dot to the filename.
        2    If the filename contains an extension (all chars after
             the last dot) it is copied onto the end of the
             (truncated) filename.
        3    Copy the default extension if there isn't one in
             the file name (i.e., no dots other than a leading dot).
        4    Truncate the extension so that:
             a    It doesnt exceed MAXEXT characters
             b    The complete filename doesn't exceed MAXNAME
                  characters.
    */
    if (extproc)             /* only do this stuff if required */
    {
        if (dotcount > 0)    /* .ext is in the filename */
        {
            for (i = dotpos, k = 0;
                 j<MAXNAME && name[i] && k<=MAXEXT;
                 i++, j++, k++)
                newname[j] = name[i];
        }
        else    /* no .ext in name. Use the default extension */
        {
            if (strlen(ext) != 0)
            {
                newname[j++] = DOT;
                for (i = 0; j<MAXNAME && i<MAXEXT && ext[i]; i++, j++)
                    newname[j] = ext[i];
            }
        }
    }
    newname[j] = 0;          /* terminate the string */

    /*
        Now apply operating system-specific rules to
        capitalisation:

        MS-DOS: All names are upper case.

        OS/9:   Retain the existing capitalisation except that:
                1    Directory names are entirely upper case
                2    Filenames that are already entirely upper case
                     retain an upper case initial letter with all
                     other letters converted to lower case.
        Linux:  Anything goes.
    */
#ifdef MSDOS
    strupr(newname);                /* MS-DOS names are upper case */
        Now apply operating system-specific rules to
        capitalisation:

        MS-DOS: All names are upper case.

        OS/9:   Retain the existing capitalisation except that:
                1    Directory names are entirely upper case
                2    Filenames that are already entirely upper case
                     retain an upper case initial letter with all
                     other letters converted to lower case.
        Linux:  Anything goes.
#endif
#ifdef OS9
    if (tolower(type[0]) == 'f')        /* name type = 'file' */
    {
        if(alphacount == uppercount)
            capitalise(newname);    /* FILENAME --> Filename */
    }
    else
        strupr(newname);            /* DIRECTORY always upper case */
#endif

    return (newname);
}
