Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

502 lines
12 KiB

  1. /************************************************************/
  2. /* Windows Write, Copyright 1985-1992 Microsoft Corporation */
  3. /************************************************************/
  4. /* fileutil.c -- WRITE file-related utilities */
  5. #define NOVIRTUALKEYCODES
  6. #define NOCTLMGR
  7. #define NOWINMESSAGES
  8. #define NOWINSTYLES
  9. #define NOCLIPBOARD
  10. #define NOGDICAPMASKS
  11. #define NOSYSMETRICS
  12. #define NOMENUS
  13. #define NOCOMM
  14. #define NOSOUND
  15. #include <windows.h>
  16. #include "mw.h"
  17. #include "doslib.h"
  18. #include "str.h"
  19. #include "machdefs.h"
  20. #include "cmddefs.h"
  21. #include "propdefs.h"
  22. #include "fkpdefs.h"
  23. #include "docdefs.h"
  24. #include "debug.h"
  25. #include "editdefs.h"
  26. #include "wwdefs.h"
  27. #define NOKCCODES
  28. #include "ch.h"
  29. /*** FNormSzFile - Normalize MSDOS filename
  30. *
  31. * Converts a MSDOS filename into an unambiguous representation
  32. *
  33. * ENTRY: szFile - a filename; drive, path, and extension
  34. * are optional
  35. * dty - the type of the document file (used to determine
  36. * extensions)
  37. * EXIT: szNormal - A normalized filename
  38. * RETURNS: FALSE - Errors found in filename (szNormal left undefined)
  39. * TRUE - No errors found in filename ( but there may be some
  40. * that we didn't find )
  41. *
  42. * The form of the filename on entry is:
  43. *
  44. * { <drive-letter>: }{ <amb-path> }<filename>{.<extension>}
  45. *
  46. * The form of the normalized filename is:
  47. *
  48. * <drive-letter>:<unamb-path><filename>.<extension>
  49. *
  50. * Where all alphabetics in the normalized name are in upper case
  51. * and <unamb-path> contains no "." or ".." uses nor any forward
  52. * slashes.
  53. *
  54. * All attributes required in the normalized filename and not
  55. * provided in the szFile are taken from the defaults:
  56. * drive - current (DOS)
  57. * path - current (DOS)
  58. * extension - derived from the passed dty
  59. *
  60. * It is permissible to call this routine with szFile containing a path
  61. * name instead of a filename. The resulting szNormal will be backslash
  62. * terminated if szFile was, not if szFile was not.
  63. * "" is converted into the current path
  64. *
  65. * WARNING: The paths "." and ".." will produce errors
  66. * (but ".\" and "..\" are OK)
  67. *
  68. ******
  69. *NOTE* szFile is expected in OEM; szNormal is returned as ANSI!
  70. ******
  71. *
  72. */
  73. FNormSzFile( szNormal, szFile, dty )
  74. CHAR *szNormal;
  75. CHAR *szFile;
  76. int dty;
  77. {
  78. /* Treat separators like terminators */
  79. #define FIsTermCh( ch ) ((ch) == '\0' || (ch) == ',' || (ch == ' ') || \
  80. (ch) == '+' || (ch) == '\011')
  81. extern CHAR *mpdtyszExt [];
  82. CHAR szPath [cchMaxFile];
  83. CHAR szFileT[cchMaxFile];
  84. int cchPath;
  85. CHAR *pchFileEye=&szFileT[0]; /* We read szFile with the Eye */
  86. CHAR *pchNormPen; /* and write szNormal with the Pen */
  87. CHAR *pchNormPath;
  88. CHAR *pchPath;
  89. /* Assert( CchSz( szFile ) <= cchMaxFile );*/
  90. if (CchSz(szFile) > cchMaxFile)
  91. return(FALSE);
  92. #if WINVER >= 0x300
  93. /* Convert input filename, which is passed in OEM,
  94. to ANSI so entire return pathname will be ANSI */
  95. OemToAnsi((LPSTR) szFile, (LPSTR) szFileT);
  96. #endif
  97. #ifdef DBCS
  98. /* Get current (DOS) path: "X:\...\...\" */
  99. if( IsDBCSLeadByte(*szFileT) )
  100. cchPath = CchCurSzPath(szPath, 0 );
  101. else
  102. cchPath = CchCurSzPath(szPath, szFileT [1]==':' ?
  103. (pchFileEye+=2,(ChUpper(szFileT [0])-('A'-1))):0 );
  104. if( cchPath < 3 )
  105. #else
  106. /* Get current (DOS) path: "X:\...\...\" */
  107. if ((cchPath = CchCurSzPath(&szPath [0], szFileT [1]==':' ?
  108. (pchFileEye+=2,(ChUpper(szFileT [0])-('A'-1))):0 )) < 3)
  109. #endif
  110. { /* Hardcore error -- could not get path */
  111. extern int ferror;
  112. if (FpeFromCchDisk(cchPath) == fpeNoDriveError)
  113. Error( IDPMTNoPath );
  114. ferror = TRUE; /* Windows already reported this one */
  115. return FALSE;
  116. }
  117. #ifdef DBCS //T-HIROYN 1992.07.14
  118. /* CchCurSzPath() [doslib.asm] don't support DBCS code */
  119. {
  120. char *pchDb;
  121. char *pch;
  122. pchDb = szPath;
  123. do {
  124. pch = pchDb;
  125. pchDb = AnsiNext(pchDb);
  126. } while(*pchDb);
  127. if(*pch != '\\') {
  128. *pchDb++ = '\\';
  129. *pchDb = 0x00;
  130. cchPath++;
  131. }
  132. }
  133. #endif
  134. #if WINVER >= 0x300
  135. {
  136. CHAR szT[cchMaxFile];
  137. /* CchCurSzPath returns OEM; we should only be dealing
  138. with ANSI filenames at this level! ..pault 1/11/90 */
  139. bltsz(szPath, szT);
  140. OemToAnsi((LPSTR) szT, (LPSTR) szPath);
  141. }
  142. #endif
  143. /* Write Drive Letter and colon */
  144. CopyChUpper( &szPath [0], &szNormal [0], 2 );
  145. pchNormPen = pchNormPath = &szNormal [2];
  146. pchPath = &szPath [2];
  147. cchPath -= 2;
  148. /* Now we have pchNormPen, pchPath, pchFileEye pointing at their path names */
  149. /* Write path name */
  150. if ( (*pchFileEye == '\\') || (*pchFileEye =='/') )
  151. { /* "\....." -- basis is root */
  152. *pchFileEye++;
  153. *(pchNormPen++) = '\\';
  154. }
  155. else
  156. { /* ".\" OR "..\" OR <text> -- basis is current path */
  157. CopyChUpper( pchPath, pchNormPen, cchPath );
  158. pchNormPen += cchPath - 1;
  159. }
  160. for ( ;; )
  161. { /* Loop until we have built the whole szNormal */
  162. register CHAR ch=*(pchFileEye++);
  163. register int cch;
  164. Assert( *(pchNormPen - 1) == '\\' );
  165. Assert( (pchNormPen > pchNormPath) &&
  166. (pchNormPen <= &szNormal [cchMaxFile]));
  167. if ( FIsTermCh( ch ) )
  168. /* We get here if there is no filename portion */
  169. /* This means we have produced a path name */
  170. {
  171. *pchNormPen = '\0';
  172. break;
  173. }
  174. if ( ch == '.' )
  175. if ( ((ch = *(pchFileEye++)) == '\\') || (ch == '/') )
  176. /* .\ and ./ do nothing */
  177. continue;
  178. else if ( ch == '.' )
  179. if ( ((ch = *(pchFileEye++)) == '\\') || (ch == '/') )
  180. { /* ..\ and ../ back up by one directory */
  181. for ( pchNormPen-- ; *(pchNormPen-1) != '\\' ; pchNormPen-- )
  182. if ( pchNormPen <= pchNormPath )
  183. /* Can't back up, already at root */
  184. return FALSE;
  185. continue;
  186. }
  187. else
  188. /* ERROR: .. not followed by slash */
  189. return FALSE;
  190. else
  191. /* Legal file and path names do not begin with periods */
  192. return FALSE;
  193. /* Filename or Path -- copy ONE directory or file name */
  194. for ( cch = 1; !FIsTermCh(ch) && ( ch != '\\') && ( ch != '/' ) ; cch++ )
  195. #ifdef DBCS
  196. {
  197. if(IsDBCSLeadByte(ch))
  198. {
  199. pchFileEye++;
  200. cch++;
  201. }
  202. ch = *(pchFileEye++);
  203. }
  204. #else
  205. ch = *(pchFileEye++);
  206. #endif
  207. /* Check if filename too long or if full pathname will be too long ..pt */
  208. if ( cch > cchMaxLeaf || cch+cchPath >= cchMaxFile)
  209. /* Directory or file name too long */
  210. return FALSE;
  211. CopyChUpper( pchFileEye - cch, pchNormPen, cch );
  212. pchNormPen += cch;
  213. if ( ch == '/' )
  214. *(pchNormPen-1) = '\\';
  215. else if ( FIsTermCh( ch ) )
  216. { /* Filename looks good, add extension & exit */
  217. *(pchNormPen-1) = '\0';
  218. /* kludge alert: if dtyNormNoExt then don't add extension unless
  219. there's one already there to be overwritten. (6.21.91) v-dougk */
  220. if ((dty != dtyNormNoExt) ||
  221. index(szNormal,'.'))
  222. AppendSzExt( &szNormal [0],
  223. mpdtyszExt [ (dty == dtyNormNoExt) ? dtyNormal : dty ],
  224. FALSE );
  225. break;
  226. }
  227. } /* Endfor (loop to build szNormal) */
  228. /* If there is anything but whitespace after the filename, then it is illegal */
  229. pchFileEye--; /* Point at the terminator */
  230. Assert( FIsTermCh( *pchFileEye ));
  231. for ( ;; )
  232. {
  233. #ifdef DBCS
  234. CHAR ch = *(pchFileEye=AnsiNext(pchFileEye));
  235. #else
  236. CHAR ch = *(pchFileEye++);
  237. #endif
  238. if (ch == '\0')
  239. break;
  240. else if ((ch != ' ') && (ch != '\011'))
  241. /* Non-whitespace after filename; return failure */
  242. return FALSE;
  243. }
  244. Assert( CchSz(szNormal) <= cchMaxFile );
  245. return TRUE;
  246. }
  247. /* Parses the cch chars stored in rgch. Returns true if string is a valid
  248. filename. If the string is not a valid name, pichError is updated to have
  249. ich of first illegal Char in the name. */
  250. /* NOTE: this routine is tuned for ASCII on MS-DOS */
  251. BOOL
  252. FValidFile(rgch, ichMax, pichError) /* filename presumed to be ANSI */
  253. register char rgch[];
  254. int ichMax;
  255. int *pichError;
  256. {
  257. int ich;
  258. register int ichStart;
  259. CHAR ch;
  260. int cchBase;
  261. int ichDot = iNil;
  262. for (ichStart = 0; ichStart < ichMax;)
  263. {
  264. /* Does the file name begin with ".\" or "..\"? */
  265. if (rgch[ichStart] == '.' &&
  266. (rgch[ichStart + 1] == '\\' || rgch[ichStart + 1] == '/'))
  267. {
  268. ichStart += 2;
  269. }
  270. else if (rgch[ichStart] == '.' && rgch[ichStart + 1] == '.' &&
  271. (rgch[ichStart + 2] == '\\' || rgch[ichStart + 2] == '/'))
  272. {
  273. ichStart += 3;
  274. }
  275. else
  276. {
  277. break;
  278. }
  279. }
  280. cchBase = ichStart;
  281. if (ichStart >= ichMax)
  282. {
  283. ich = ichStart;
  284. goto badchar;
  285. }
  286. /* Are all characters legal? */
  287. for(ich = ichStart; ich < ichMax; ich++)
  288. {
  289. ch = rgch[ich];
  290. /* range check */
  291. #ifndef DBCS
  292. if ((unsigned char)ch >= 0x80)
  293. /* To allow international filenames, pass everything above 128 */
  294. continue;
  295. if (ch < '!' || ch > '~')
  296. goto badchar;
  297. #endif
  298. switch(ch)
  299. {
  300. default:
  301. #ifdef DBCS
  302. goto CheckDBCS;
  303. #else
  304. continue;
  305. #endif
  306. case '.':
  307. if (ichDot != iNil || ich == cchBase)
  308. /* More than one dot in the name */
  309. /* Or null filename */
  310. goto badchar;
  311. ichDot = ich;
  312. #ifdef DBCS
  313. goto CheckDBCS;
  314. #else
  315. continue;
  316. #endif
  317. case ':':
  318. if ( ich != 1 || !(isalpha(rgch[0])))
  319. goto badchar;
  320. /* fall through */
  321. case '\\':
  322. case '/':
  323. /* note end of the drive or path */
  324. if (ich + 1 == ichMax)
  325. goto badchar;
  326. cchBase = ich+1;
  327. ichDot = iNil;
  328. #ifdef DBCS
  329. goto CheckDBCS;
  330. #else
  331. continue;
  332. #endif
  333. case '"':
  334. #ifdef WRONG
  335. /* This IS a legal filename char! ..pault 10/26/89 */
  336. case '#':
  337. #endif
  338. case '*':
  339. case '+':
  340. case ',':
  341. case ';':
  342. case '<':
  343. case '=':
  344. case '>':
  345. case '?':
  346. case '[':
  347. case ']':
  348. case '|':
  349. goto badchar;
  350. }
  351. #ifdef DBCS
  352. CheckDBCS:
  353. if(IsDBCSLeadByte(ch)) ich++;
  354. #endif /* DBCS */
  355. }
  356. /* Are there no more than eight chars before the '.'? */
  357. if(((ichDot == -1) ? ichMax : ichDot) - cchBase > 8)
  358. {
  359. ich = 8+cchBase;
  360. goto badchar;
  361. }
  362. /* If there is no '.' we are fine */
  363. if(ichDot == iNil)
  364. return true;
  365. /* Are there no more than three chars after the '.'? */
  366. if(ichMax - ichDot - 1 > 3)
  367. {
  368. ich = ichDot + 3 + 1;
  369. goto badchar;
  370. }
  371. return true;
  372. badchar:
  373. *pichError += ich;
  374. return false;
  375. }
  376. #ifdef DBCS
  377. CopyChUpper( szSource, szDest, cch )
  378. register CHAR *szSource;
  379. register CHAR *szDest;
  380. int cch;
  381. {
  382. while(cch){
  383. if( IsDBCSLeadByte( *szSource ) ){
  384. *szDest++ = *szSource++;
  385. *szDest++ = *szSource++;
  386. cch--;
  387. } else
  388. *szDest++ = ChUpper( *szSource++ );
  389. cch--;
  390. }
  391. }
  392. #else
  393. CopyChUpper( szSource, szDest, cch )
  394. CHAR *szSource;
  395. CHAR *szDest;
  396. register int cch;
  397. {
  398. register CHAR ch;
  399. while (cch--)
  400. {
  401. ch = *(szSource++);
  402. *(szDest++) = ChUpper( ch );
  403. }
  404. }
  405. #endif
  406. /*** AppendSzExt - append extension to filename
  407. *
  408. * Append extension (assumed to contain the ".") to passed filename.
  409. * Assumes call allocated enough string space for the append
  410. * if fOverride is TRUE, overrides any existing extension
  411. * if fOverride is FALSE, appends extension only if szFile has
  412. * no current extension
  413. */
  414. AppendSzExt( szFile, szExt, fOverride )
  415. CHAR *szFile;
  416. CHAR *szExt;
  417. int fOverride;
  418. {
  419. #define cchMaxExt 3
  420. CHAR *pch=NULL;
  421. int cch;
  422. register int cchT;
  423. register int chT;
  424. /* pch <-- pointer to the '.' for szFile's extension (if any) */
  425. cch = cchT = CchSz( szFile ) - 1;
  426. while (--cchT > cch - (cchMaxExt + 2))
  427. if ((chT=szFile[ cchT ]) == '.')
  428. {
  429. pch = &szFile[ cchT ];
  430. break;
  431. }
  432. else if ((chT == '\\') || (chT == '/'))
  433. /* Catches the weird case: szFile == "C:\X.Y\J" */
  434. break;
  435. if (pch == NULL)
  436. /* No explicit extension: APPEND */
  437. CchCopySz( szExt, szFile + CchSz( szFile ) - 1 );
  438. else if ( fOverride )
  439. /* Override explicit extension */
  440. CchCopySz( szExt, pch );
  441. }
  442.