503 lines
12 KiB
C
503 lines
12 KiB
C
|
/************************************************************/
|
|||
|
/* 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;
|
|||
|
}
|
|||
|
|
|||
|
#ifdef DBCS //T-HIROYN 1992.07.14
|
|||
|
/* CchCurSzPath() [doslib.asm] don't support DBCS code */
|
|||
|
{
|
|||
|
char *pchDb;
|
|||
|
char *pch;
|
|||
|
pchDb = szPath;
|
|||
|
do {
|
|||
|
pch = pchDb;
|
|||
|
pchDb = AnsiNext(pchDb);
|
|||
|
} while(*pchDb);
|
|||
|
if(*pch != '\\') {
|
|||
|
*pchDb++ = '\\';
|
|||
|
*pchDb = 0x00;
|
|||
|
cchPath++;
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#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 );
|
|||
|
}
|
|||
|
|