/************************************************************/ /* 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 #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: * * { : }{ }{.} * * The form of the normalized filename is: * * :. * * Where all alphabetics in the normalized name are in upper case * and 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 -- 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 ); }