/************************************************************/
/* Windows Write, Copyright 1985-1992 Microsoft Corporation */
/************************************************************/

/* fileutil.c -- WRITE file-related utilities */
#define NOVIRTUALKEYCODES
#define NOCTLMGR
#define NOWINMESSAGES
#define NOWINSTYLES
#define NOCLIPBOARD
#define NOGDICAPMASKS
#define NOSYSMETRICS
#define NOMENUS
#define NOCOMM
#define NOSOUND
#include <windows.h>

#include "mw.h"
#include "doslib.h"
#include "str.h"
#include "machdefs.h"
#include "cmddefs.h"
#include "propdefs.h"
#include "fkpdefs.h"
#include "docdefs.h"
#include "debug.h"
#include "editdefs.h"
#include "wwdefs.h"
#define NOKCCODES
#include "ch.h"



/***	    FNormSzFile - Normalize MSDOS filename
 *
 *	Converts a MSDOS filename into an unambiguous representation
 *
 *	ENTRY:	szFile - a filename; drive, path, and extension
 *			 are optional
 *		dty - the type of the document file (used to determine
 *		      extensions)
 *	EXIT:	szNormal - A normalized filename
 *	RETURNS: FALSE - Errors found in filename     (szNormal left undefined)
 *		 TRUE  - No errors found in filename  ( but there may be some
 *							that we didn't find )
 *
 *	The form of the filename on entry is:
 *
 *	{ <drive-letter>: }{ <amb-path> }<filename>{.<extension>}
 *
 *	The form of the normalized filename is:
 *
 *	<drive-letter>:<unamb-path><filename>.<extension>
 *
 *	Where all alphabetics in the normalized name are in upper case
 *	and <unamb-path> contains no "." or ".." uses nor any forward
 *	slashes.
 *
 *	All attributes required in the normalized filename and not
 *	provided in the szFile are taken from the defaults:
 *	    drive - current (DOS)
 *	    path - current (DOS)
 *	    extension - derived from the passed dty
 *
 *	It is permissible to call this routine with szFile containing a path
 *	name instead of a filename.  The resulting szNormal will be backslash
 *	terminated if szFile was, not if szFile was not.
 *	"" is converted into the current path
 *
 *	WARNING:  The paths "." and ".." will produce errors
 *		  (but ".\" and "..\" are OK)
 *
 ******
 *NOTE*   szFile is expected in OEM; szNormal is returned as ANSI!
 ******
 *
 */

FNormSzFile( szNormal, szFile, dty )
CHAR *szNormal;
CHAR *szFile;
int  dty;
{
/* Treat separators like terminators */

#define FIsTermCh( ch )     ((ch) == '\0' || (ch) == ',' || (ch == ' ') || \
			     (ch) == '+' || (ch) == '\011')
extern CHAR *mpdtyszExt [];

 CHAR szPath [cchMaxFile];
 CHAR szFileT[cchMaxFile];

 int  cchPath;
 CHAR *pchFileEye=&szFileT[0];	    /* We read szFile with the Eye */
 CHAR *pchNormPen;		    /* and write szNormal with the Pen */
 CHAR *pchNormPath;
 CHAR *pchPath;

/* Assert( CchSz( szFile ) <= cchMaxFile );*/
 if (CchSz(szFile) > cchMaxFile)
     return(FALSE);

#if WINVER >= 0x300
 /* Convert input filename, which is passed in OEM, 
    to ANSI so entire return pathname will be ANSI */
 OemToAnsi((LPSTR) szFile, (LPSTR) szFileT);
#endif

#ifdef DBCS
    /* Get current (DOS) path: "X:\...\...\" */
 if( IsDBCSLeadByte(*szFileT) )
     cchPath = CchCurSzPath(szPath, 0 );
 else
     cchPath = CchCurSzPath(szPath, szFileT [1]==':' ? 
                     (pchFileEye+=2,(ChUpper(szFileT [0])-('A'-1))):0 );
 if( cchPath < 3 )
#else
    /* Get current (DOS) path: "X:\...\...\" */
 if ((cchPath = CchCurSzPath(&szPath [0], szFileT [1]==':' ? 
                     (pchFileEye+=2,(ChUpper(szFileT [0])-('A'-1))):0 )) < 3)
#endif
    {	/* Hardcore error -- could not get path */
    extern int ferror;

    if (FpeFromCchDisk(cchPath) == fpeNoDriveError)
	Error( IDPMTNoPath );

    ferror = TRUE;  /* Windows already reported this one */
    return FALSE;
    }

#if WINVER >= 0x300
 {
 CHAR szT[cchMaxFile];

 /* CchCurSzPath returns OEM; we should only be dealing 
    with ANSI filenames at this level! ..pault 1/11/90 */

 bltsz(szPath, szT);
 OemToAnsi((LPSTR) szT, (LPSTR) szPath);
 }
#endif

    /* Write Drive Letter and colon */
 CopyChUpper( &szPath [0], &szNormal [0], 2 );

 pchNormPen = pchNormPath = &szNormal [2];
 pchPath = &szPath [2];
 cchPath -= 2;

 /* Now we have pchNormPen, pchPath, pchFileEye pointing at their path names */

    /* Write path name */
 if ( (*pchFileEye == '\\') || (*pchFileEye =='/') )
    {	/* "\....." -- basis is root */
    *pchFileEye++;
    *(pchNormPen++) = '\\';
    }
 else
    {	/* ".\" OR "..\" OR <text> -- basis is current path */
    CopyChUpper( pchPath, pchNormPen, cchPath );
    pchNormPen += cchPath - 1;
    }

  for ( ;; )
    {		/* Loop until we have built the whole szNormal */
    register CHAR ch=*(pchFileEye++);
    register int  cch;

    Assert( *(pchNormPen - 1) == '\\' );
    Assert( (pchNormPen > pchNormPath) &&
			      (pchNormPen <= &szNormal [cchMaxFile]));

    if ( FIsTermCh( ch ) )
	    /* We get here if there is no filename portion  */
	    /* This means we have produced a path name */
	{
	*pchNormPen = '\0';
	break;
	}

    if ( ch == '.' )
	if ( ((ch = *(pchFileEye++)) == '\\') || (ch == '/') )
		/* .\ and ./ do nothing */
	    continue;
	else if ( ch == '.' )
	    if ( ((ch = *(pchFileEye++)) == '\\') || (ch == '/') )
		{   /* ..\ and ../ back up by one directory */
		for ( pchNormPen-- ; *(pchNormPen-1) != '\\' ; pchNormPen-- )
		    if ( pchNormPen <= pchNormPath )
			    /* Can't back up, already at root */
			return FALSE;
		continue;
		}
	    else
		    /* ERROR: .. not followed by slash */
		return FALSE;
	else
		/* Legal file and path names do not begin with periods */
	    return FALSE;

    /* Filename or Path -- copy ONE directory or file name */

    for ( cch = 1; !FIsTermCh(ch) && ( ch != '\\') && ( ch != '/' ) ; cch++ )
#ifdef	DBCS
    {
	if(IsDBCSLeadByte(ch))
	{
		pchFileEye++;
		cch++;
	}
	ch = *(pchFileEye++);
    }
#else
	ch = *(pchFileEye++);
#endif

    /* Check if filename too long or if full pathname will be too long ..pt */
    if ( cch > cchMaxLeaf || cch+cchPath >= cchMaxFile)
	    /* Directory or file name too long */
	return FALSE;

    CopyChUpper( pchFileEye - cch, pchNormPen, cch );
    pchNormPen += cch;
    if ( ch == '/' )
	*(pchNormPen-1) = '\\';
    else if ( FIsTermCh( ch ) )
	{    /* Filename looks good, add extension & exit */
	*(pchNormPen-1) = '\0';

    /* kludge alert: if dtyNormNoExt then don't add extension unless 
        there's one already there to be overwritten. (6.21.91) v-dougk */
    if ((dty != dtyNormNoExt) ||
         index(szNormal,'.'))
	        AppendSzExt( &szNormal [0], 
                mpdtyszExt [ (dty == dtyNormNoExt) ? dtyNormal : dty ], 
                FALSE );
	break;
	}
    }	/* Endfor (loop to build szNormal) */

 /* If there is anything but whitespace after the filename, then it is illegal */

 pchFileEye--;	/* Point at the terminator */
 Assert( FIsTermCh( *pchFileEye ));

 for ( ;; )
    {
#ifdef DBCS
    CHAR ch = *(pchFileEye=AnsiNext(pchFileEye));
#else
    CHAR ch = *(pchFileEye++);
#endif

    if (ch == '\0')
	break;
    else if ((ch != ' ') && (ch != '\011'))
	    /* Non-whitespace after filename; return failure */
	return FALSE;
    }

 Assert( CchSz(szNormal) <= cchMaxFile );
 return TRUE;
}



/* Parses the cch chars stored in rgch.  Returns true if string is a valid
filename.  If the string is not a valid name, pichError is updated to have
ich of first illegal Char in the name. */
/* NOTE: this routine is tuned for ASCII on MS-DOS */

BOOL
FValidFile(rgch, ichMax, pichError)     /* filename presumed to be ANSI */
register char rgch[];
int ichMax;
int *pichError;
    {
    int ich;
    register int ichStart;
    CHAR ch;
    int cchBase;
    int ichDot = iNil;

    for (ichStart = 0; ichStart < ichMax;)
	{
	/* Does the file name begin with ".\" or "..\"? */
	if (rgch[ichStart] == '.' &&
	  (rgch[ichStart + 1] == '\\' || rgch[ichStart + 1] == '/'))
	    {
	    ichStart += 2;
	    }
	else if (rgch[ichStart] == '.' && rgch[ichStart + 1] == '.' &&
	  (rgch[ichStart + 2] == '\\' || rgch[ichStart + 2] == '/'))
	    {
	    ichStart += 3;
	    }
	else
	    {
	    break;
	    }
	}

    cchBase = ichStart;

    if (ichStart >= ichMax)
	    {
	    ich = ichStart;
	    goto badchar;
	    }

    /* Are all characters legal? */
    for(ich = ichStart; ich < ichMax; ich++)
	{
	ch = rgch[ich];
	/* range check */

#ifndef DBCS
	if ((unsigned char)ch >= 0x80)
		/* To allow international filenames, pass everything above 128 */
	    continue;
	if (ch < '!' || ch > '~')
	    goto badchar;
#endif
	switch(ch)
	    {
	    default:
#ifdef	DBCS
		goto CheckDBCS;
#else
		continue;
#endif
	    case '.':
		if (ichDot != iNil || ich == cchBase)
		    /* More than  one dot in the name */
		    /* Or null filename */
		    goto badchar;
		ichDot = ich;
#ifdef DBCS
		goto CheckDBCS;
#else
		continue;
#endif
	    case ':':
		if ( ich != 1 || !(isalpha(rgch[0])))
		    goto badchar;
		/* fall through */
	    case '\\':
	    case '/':
		/* note end of the drive or path */
		if (ich + 1 == ichMax)
			goto badchar;
		cchBase = ich+1;
		ichDot = iNil;
#ifdef DBCS
		goto CheckDBCS;
#else
		continue;
#endif
	    case '"':
#ifdef WRONG	    
        /* This IS a legal filename char! ..pault 10/26/89 */
        case '#':
#endif
	    case '*':
	    case '+':
	    case ',':
	    case ';':
	    case '<':
	    case '=':
	    case '>':
	    case '?':
	    case '[':
	    case ']':
	    case '|':
		goto badchar;
	    }
#ifdef DBCS
CheckDBCS:
	if(IsDBCSLeadByte(ch))	ich++;
#endif	/* DBCS */
	}

    /* Are there no more than eight chars before the '.'? */
	if(((ichDot == -1) ? ichMax : ichDot) - cchBase > 8)
	    {
	    ich = 8+cchBase;
	    goto badchar;
	    }
    /* If there is no '.' we are fine */
	if(ichDot == iNil)
	    return true;
    /* Are there no more than three chars after the '.'? */
	if(ichMax - ichDot - 1 > 3)
	    {
	    ich = ichDot + 3 + 1;
	    goto badchar;
	    }
    return true;

badchar:
    *pichError += ich;
    return false;
    }







#ifdef DBCS
CopyChUpper( szSource, szDest, cch )
register CHAR *szSource;
register CHAR *szDest;
int cch;
{
 while(cch){
    if( IsDBCSLeadByte( *szSource ) ){
        *szDest++ = *szSource++;
        *szDest++ = *szSource++;
        cch--;
    } else
        *szDest++ = ChUpper( *szSource++ );
    cch--;
 }
}
#else
CopyChUpper( szSource, szDest, cch )
CHAR *szSource;
CHAR *szDest;
register int cch;
{
 register CHAR ch;

 while (cch--)
    {
    ch = *(szSource++);
	*(szDest++) = ChUpper( ch );
    }
}
#endif


/***	    AppendSzExt - append extension to filename
 *
 *	Append extension (assumed to contain the ".") to passed filename.
 *	Assumes call allocated enough string space for the append
 *	if fOverride is TRUE, overrides any existing extension
 *	if fOverride is FALSE, appends extension only if szFile has
 *	    no current extension
 */

AppendSzExt( szFile, szExt, fOverride )
CHAR *szFile;
CHAR *szExt;
int fOverride;
{
#define cchMaxExt   3
 CHAR *pch=NULL;
 int cch;
 register int cchT;
 register int chT;

 /* pch <-- pointer to the '.' for szFile's extension (if any) */
 cch = cchT = CchSz( szFile ) - 1;
 while (--cchT > cch - (cchMaxExt + 2))
    if ((chT=szFile[ cchT ]) == '.')
	{
	pch = &szFile[ cchT ];
	break;
	}
    else if ((chT == '\\') || (chT == '/'))
	    /* Catches the weird case: szFile == "C:\X.Y\J" */
	break;

 if (pch == NULL)
	/* No explicit extension: APPEND */
    CchCopySz( szExt, szFile + CchSz( szFile ) - 1 );

 else if ( fOverride )
	/* Override explicit extension */
    CchCopySz( szExt, pch );
}

