/************************************************************/
/* 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 );
}