IdOSFileName.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.4 2004.02.03 5:45:42 PM czhower
  18. Name changes
  19. Rev 1.3 24/01/2004 23:16:26 CCostelloe
  20. Removed short-circuits
  21. Rev 1.2 24/01/2004 19:28:28 CCostelloe
  22. Cleaned up warnings
  23. Rev 1.1 5/2/2003 01:16:00 PM JPMugaas
  24. Microware OS/9 and MPE/iX support.
  25. Rev 1.0 4/21/2003 05:32:08 PM JPMugaas
  26. Filename converstion rourintes. Todo: Somehow, figure out what to do about
  27. pathes and add more platform support.
  28. }
  29. unit IdOSFileName;
  30. interface
  31. {$i IdCompilerDefines.inc}
  32. uses
  33. IdBaseComponent, IdFTPCommon;
  34. function FileNameUnixToVMS(const AUnixFileName : String) : String;
  35. function FileNameVMSToUnix(const AVMSFileName : String) : String;
  36. function FileNameMSDOSToUnix(const AMSDOSFileName : String) : String;
  37. function FileNameUnixToMSDOS(const AUnixFileName : String):String;
  38. function FileNameUnixToWin32(const AUnixFileName : String):String;
  39. function FileNameWin32ToUnix(const AWin32FileName : String): String;
  40. function FileNameUnixToVMCMS(const AUnixFileName : String): String;
  41. function FileNameVMCMSToUnix(const AVMCMSFileName : String): String;
  42. function FileNameUnixToMUSICSP(const AUnixFileName : String) : String;
  43. function FileNameMUSICSPToUnix(const AMUSICSPFileName : String) : String;
  44. function FileNameUnixToMVS(const AUnixFileName : String; const AUserID : String;
  45. const AUseAnotherID : Boolean=False) : String;
  46. function FileNameMVSToUnix(const AMVSFileName : String) : String;
  47. function FileNameUnixToMPEiXTraditional(const AUnixFileName : String; const AGroupName : String=''; const AAcountName : String=''): String;
  48. function FileNameUnixToMPEiXHFS(const AUnixFileName : String; const IsRoot : Boolean=False): String;
  49. function FileNameUnixToOS9(const AUnixFileName : String) : String;
  50. implementation
  51. uses
  52. IdException,
  53. IdGlobal, IdGlobalProtocols, SysUtils;
  54. function EnsureValidCharsByValidSet(const AFilePart, AValidChars : String; const AReplaceWith : String='_'): String;
  55. var i : Integer;
  56. begin
  57. Result := '';
  58. for i := 1 to Length(AFilePart) do
  59. begin
  60. if CharIsInSet(AFilePart, i, AValidChars) then
  61. begin
  62. Result := Result + AFilePart[i];
  63. end
  64. else
  65. begin
  66. Result := Result + AReplaceWith;
  67. end;
  68. end;
  69. end;
  70. function EnsureValidCharsByInvalidSet(const AFilePart, AInvalidChars : String;
  71. const AReplaceWith : String='_'): String;
  72. var i : Integer;
  73. begin
  74. Result := '';
  75. for i := 1 to Length(AFilePart) do
  76. begin
  77. if not CharIsInSet(AFilePart, i, AInValidChars) then
  78. begin
  79. Result := Result + AFilePart[i];
  80. end
  81. else
  82. begin
  83. Result := Result + AReplaceWith;
  84. end;
  85. end;
  86. end;
  87. function FileNameUnixToVMS(const AUnixFileName : String) : String;
  88. var LFName, LFExt : String;
  89. {sample VMS fully qualified filename:
  90. DKA0:[MYDIR.SUBDIR1.SUBDIR2]MYFILE.TXT;1
  91. Note VMS uses 39 chars for name and type
  92. valid chars are:
  93. letters A through Z
  94. numbers 0 through 9
  95. underscore ( _ )
  96. hyphen ( -)
  97. dollar sign ( $ )
  98. See: http://www.uh.edu/infotech/services/documentation/vms/v0505.html
  99. }
  100. var
  101. VMS_Valid_Chars : String;
  102. begin
  103. VMS_Valid_Chars := CharRange('A','Z')+CharRange('0','9')+'_-$';
  104. //VMS is case insensitive - UpperCase to simplify processing
  105. Result := UpperCase(AUnixFileName);
  106. LFName := Fetch(Result,'.');
  107. LFExt := Fetch(Result,'.');
  108. LFExt := Fetch(LFExt,';');
  109. LFName := Copy(LFName,1,39);
  110. LFName := EnsureValidCharsByValidSet(LFName,VMS_Valid_Chars);
  111. LFExt := Copy(LFExt,1,39);
  112. LFExt := EnsureValidCharsByValidSet(LFExt,VMS_Valid_Chars);
  113. Result := LFName;
  114. if LFExt <>'' then
  115. begin
  116. Result := Result + '.'+LFExt;
  117. end;
  118. end;
  119. function FileNameVMSToUnix(const AVMSFileName : String) : String;
  120. begin
  121. Result := AVMSFileName;
  122. //We strip off the version marker because that doesn't make sense in
  123. //Win32 and Unix. In VMS, there's a crude type of version control in the file system where
  124. //different versions of the same file are kept.
  125. //For example, if you open IDABOUT.PAS;1, make a change, and save it, it is saved
  126. //as IDABOUT.PAS;2. Make a further change and save it and it will be saved as
  127. //IDABOUT.PAS;3.
  128. Result := Fetch(Result,';');
  129. //VMS is case insensitive
  130. Result := LowerCase(AVMSFileName);
  131. end;
  132. function FileNameMSDOSToUnix(const AMSDOSFileName : String) : String;
  133. begin
  134. Result := LowerCase(AMSDOSFileName);
  135. end;
  136. function FileNameUnixToMSDOS(const AUnixFileName : String):String;
  137. var LFName, LFExt : String;
  138. //From: http://macinfo.its.queensu.ca/Mark/AMT2/AMTCrossPlatfrom.html
  139. //Window V3.1 and DOS file names compatibility:
  140. //Windows 3.1/DOS names cannot have more than eight characters and can
  141. //contain only the letters A through Z, the numbers 0 through 9 and the
  142. //following special characters:
  143. //underscore (_), dollar sign ($), tilde (~), exclamation point (!),
  144. //number sign (#), percent sign (%), ampersand (&), hyphen (-), braces ({}), parenthesis (), at sign (@), apostrophe ('), and the grave accent (').
  145. //Note: Macintosh does not allow colin (:) in it's file name and supports upto 32 characters.
  146. var
  147. MSDOS_Valid_Chars : String;
  148. begin
  149. MSDOS_Valid_Chars := CharRange('A','Z')+CharRange('0','9')+'_$~!#%&-{}()@'''+Char(180); {do not localize}
  150. Result := UpperCase(AUnixFileName);
  151. LFName := Fetch(Result,'.');
  152. LFName := Copy(LFName,1,8);
  153. LFName := EnsureValidCharsByValidSet(LFName,MSDOS_Valid_Chars);
  154. LFExt := Fetch(Result,'.');
  155. LFExt := Copy(LFExt,1,3);
  156. LFExt := EnsureValidCharsByValidSet(LFExt,MSDOS_Valid_Chars);
  157. Result := LFName;
  158. if LFExt <> '' then
  159. begin
  160. Result := Result + '.'+LFExt;
  161. end;
  162. end;
  163. function FileNameUnixToWin32(const AUnixFileName : String):String;
  164. //from: http://linux-ntfs.sourceforge.net/ntfs/concepts/filename_namespace.html
  165. const
  166. WIN32_INVALID_CHARS = '"*/:<>?\|' + #0;
  167. WIN32_INVALID_LAST = ' .'; //not permitted as the last character in Win32
  168. begin
  169. Result := EnsureValidCharsByInvalidSet(AUnixFileName,WIN32_INVALID_CHARS);
  170. if Result <> '' then begin
  171. if CharIsInSet(Result, Length(Result), WIN32_INVALID_LAST) then begin
  172. SetLength(Result, Length(Result)-1);
  173. if Result = '' then begin
  174. Result := '_';
  175. end;
  176. end;
  177. end;
  178. end;
  179. function FileNameWin32ToUnix(const AWin32FileName : String): String;
  180. //from http://linux-ntfs.sourceforge.net/ntfs/concepts/filename_namespace.html
  181. //const UNIX_INVALID_CHARS : TIdValidChars = [#0,'/'];
  182. begin
  183. Result := LowerCase(AWin32FileName);
  184. end;
  185. function FileNameUnixToVMCMS(const AUnixFileName : String): String;
  186. // From: CMS Introductory Guide at the University of Kentuckey
  187. // http://ukcc.uky.edu/~ukccinfo.391/cmsintro.html
  188. // Under CMS a file is identified by a fileid with three parts: the filename, the
  189. // filetype, and the filemode. The filemode, from this point on in this guide, will
  190. // always be A1. In most cases, the filemode is optional when referring to files.
  191. // The filename and filetype can contain from one to eight characters (letters,
  192. // numbers, and these seven special characters: @#$+-:_). Choose filenames and
  193. // filetypes that help to identify the contents of the file.
  194. var
  195. LFName, LFExt : String;
  196. Valid_VMCMS_Chars : String;
  197. begin
  198. Valid_VMCMS_Chars := CharRange('A','Z')+ CharRange('0','9')+'@#$+-:_';
  199. Result := UpperCase(AUnixFileName);
  200. LFName := Fetch(Result,'.');
  201. LFName := EnsureValidCharsByValidSet(LFExt,VALID_VMCMS_CHARS);
  202. LFName := Copy(LFName,1,8);
  203. LFExt := Fetch(Result,'.');
  204. LFExt := EnsureValidCharsByValidSet(LFExt,VALID_VMCMS_CHARS);
  205. LFExt := Copy(LFExt,1,8);
  206. Result := LFName;
  207. if LFExt <> '' then
  208. begin
  209. Result := Result + '.'+LFExt;
  210. end;
  211. end;
  212. function FileNameVMCMSToUnix(const AVMCMSFileName : String): String;
  213. begin
  214. Result := LowerCase(AVMCMSFileName);
  215. end;
  216. function FileNameUnixToMUSICSP(const AUnixFileName : String) : String;
  217. {
  218. Obtained from a ftphelp.txt on a Music/SP Server
  219. The MUSIC/SP file system has a directory structure similar to that
  220. of DOS and Unix. The separator character between directory names
  221. is a backslash (\), but the FTP client can use either a slash (/)
  222. or a backslash (\). Directory names can be up to 17 characters.
  223. File names within a directory can be up to 17 characters. The total
  224. name, including directory names on the front, can be up to 50
  225. characters. Upper/lower case is not significant in file names.
  226. Letters (A to Z), digits (0 to 9), and some special characters
  227. (including $ # @ _ + - . % & !) can be used, but the first character
  228. of each part must not be a digit or + - . % & !.
  229. }
  230. var
  231. Valid_MUSICSP : String;
  232. MUSICSP_Cant_Start : String;
  233. begin
  234. Valid_MUSICSP := CharRange('A','Z')+CharRange('0','9')+'$#@_+-.%&!'; {do not localize}
  235. MUSICSP_Cant_Start := CharRange('0','9')+ '+-.%!'; {do not localize}
  236. // note we have to do our vality checks before truncating the length in
  237. // case we need to replace the default replacement char and the length changes
  238. // because of that.
  239. Result := EnsureValidCharsByValidSet(UpperCase(AUnixFileName),VALID_MUSICSP);
  240. Result := Copy(Result,1,15);
  241. if Result <> '' then begin
  242. if CharIsInSet(Result, 1, MUSICSP_CANT_START) then begin
  243. if Length(Result) > 1 then begin
  244. {$IFDEF STRING_IS_IMMUTABLE}
  245. Result := Copy(Result,2,MaxInt);
  246. {$ELSE}
  247. Delete(Result,1,1);
  248. {$ENDIF}
  249. end else begin
  250. {$IFDEF STRING_IS_IMMUTABLE}
  251. Result := '_'; {do not localize}
  252. {$ELSE}
  253. Result[1] := '_'; {do not localize}
  254. {$ENDIF}
  255. end;
  256. end;
  257. end;
  258. end;
  259. function FileNameMUSICSPToUnix(const AMUSICSPFileName : String) : String;
  260. begin
  261. Result := LowerCase(AMUSICSPFileName);
  262. end;
  263. function FileNameUnixToMVS(const AUnixFileName : String; const AUserID : String; const AUseAnotherID : Boolean=False) : String;
  264. const
  265. MVS_FQN_MAX_LEN = 44;
  266. MVS_MAX_QUAL_LEN = 8;
  267. var
  268. LQualifier : String;
  269. LMaxLen : Integer;
  270. LBuf : String;
  271. MVS_Valid_Qual_Chars : String;
  272. MVS_Valid_First_Char : String;
  273. begin
  274. MVS_Valid_Qual_Chars := CharRange('0','9')+CharRange('A','Z')+'@$#'; {do not localize}
  275. MVS_Valid_First_Char := CharRange('A','Z'); {do not localize}
  276. //in MVS, there's a maximum of 44 characters and MVS prepends a prefix with the userID and
  277. //sometimes process name. Thus, the dataset name can have 44 characters minus the user ID - 1 (for the dot)
  278. //
  279. //e.g. CZHOWER can have a fully qualified name with a maximum of 36 characters
  280. // JPMUGAAS can have a fully qualified name with a maximum of 35 characters
  281. //
  282. //if AUseAnotherID is true, we give a fully qualified name with a prefix in '' which
  283. //is called ticks. That permits someone to access another user's dataset.
  284. //
  285. //e.g. CZHOWER could access a dataset created by JPMUGAAS (named INDY.IDABOUT.PAS)
  286. // by using the name:
  287. //
  288. //'JPMUGAAS.INDY.IDABOUT.PAS'
  289. //
  290. //JPMUGAAS can access the same data with the name:
  291. //
  292. //INDY.IDABOUT.PAS
  293. //
  294. //where the JPMUGAAS. prefix is implied.
  295. LMaxLen := MVS_FQN_MAX_LEN - 1 - Length(AUserID);
  296. LBuf := UpperCase(AUnixFileName);
  297. Result := '';
  298. repeat
  299. LQualifier := Fetch(LBuf,'.');
  300. if LQualifier <>'' then
  301. begin
  302. repeat
  303. if not CharIsInSet(LQualifier, 1, MVS_VALID_FIRST_CHAR) then
  304. begin
  305. Delete(LQualifier,1,1);
  306. if LQualifier='' then
  307. begin
  308. break;
  309. end;
  310. end
  311. else
  312. begin
  313. Break;
  314. end;
  315. until False;
  316. end;
  317. //we do it this way in case the qualifier only had an invalid char such as #
  318. if LQualifier <> '' then
  319. begin
  320. LQualifier := EnsureValidCharsByValidSet(LQualifier,MVS_VALID_QUAL_CHARS,'');
  321. end;
  322. LQualifier := Copy(LQualifier,1,MVS_MAX_QUAL_LEN);
  323. if LQualifier <> '' then
  324. begin
  325. Result := Result + '.' +LQualifier;
  326. if Result<>'' then
  327. begin
  328. if Result[1]='.' then
  329. begin
  330. Delete(Result,1,1);
  331. end;
  332. end;
  333. if (Length(Result)>LMaxLen) or (LBuf='') then
  334. begin
  335. Result := Copy(Result,1,LMaxLen);
  336. Break;
  337. end;
  338. end;
  339. if LBuf = '' then
  340. begin
  341. Break;
  342. end;
  343. until False;
  344. if AUseAnotherID then
  345. begin
  346. Result := ''''+AUserID+'.'+Result+'''';
  347. end;
  348. end;
  349. function FileNameMVSToUnix(const AMVSFileName : String) : String;
  350. begin
  351. Result := LowerCase(AMVSFileName);
  352. end;
  353. {
  354. Note that Account name does not necessarily imply a username. When logging in,
  355. the user provides both the username and account. It's like the username is the key to
  356. a cabnet (several people can have their keys to that cabnit.
  357. The group name is like a drawer in a cabnet. That is only needed if you are logged in with
  358. an account and group but wish to access a file in a different group.
  359. That's how the manual
  360. described it.
  361. The MPE/iX file system is basically flat with an account, group, and file name.
  362. }
  363. function MPEiXValidateFIlePart(AFilePart : String) : String;
  364. var
  365. Valid_MPEIX_Start : String;
  366. Valid_MPEIX_FName : String;
  367. begin
  368. Valid_MPEIX_Start := CharRange('A','Z');
  369. Valid_MPEIX_FName := Valid_MPEIX_Start + CharRange('0','9');
  370. Result := UpperCase(AFilePart);
  371. if IndyPos('.',Result)>1 then
  372. begin
  373. Result := Fetch(Result,'.');
  374. end;
  375. if Result<>'' then
  376. begin
  377. Result := EnsureValidCharsByValidSet(Result,VALID_MPEIX_FNAME,'');
  378. repeat
  379. if not CharIsInSet(Result, 1, VALID_MPEIX_START) then
  380. begin
  381. Delete(Result,1,1);
  382. if Result='' then
  383. begin
  384. break;
  385. end;
  386. end
  387. else
  388. begin
  389. Break;
  390. end;
  391. until False;
  392. end;
  393. //no more than 8 chars
  394. Result := COpy(Result,1,8);
  395. end;
  396. function FileNameUnixToMPEiXTraditional(const AUnixFileName : String; const AGroupName : String=''; const AAcountName : String=''): String;
  397. //based on http://docs.hp.com/mpeix/onlinedocs/32650-90871/32650-90871.html
  398. //
  399. //1) Starts with a letter - case insensitive
  400. //2) Contains only letters and numbers
  401. //3) No other charactors (a / is acceptable but it means a lockword which can cause
  402. // you to be unable to retreive the file
  403. //4) No more than 8 chars
  404. //
  405. //Note that fullname and groupname are 1 to 8 chars and follow the same rules
  406. //just to eliminate some double processing for tests
  407. var LBuf : String;
  408. begin
  409. Result := MPEiXValidateFIlePart(AUnixFileName);
  410. LBuf := MPEiXValidateFIlePart(AGroupName);
  411. if LBuf <> '' then
  412. begin
  413. //if no group, we skip the account part
  414. Result := Result + '.'+LBuf;
  415. LBuf := MPEIxValidateFilePart(AAcountName);
  416. if LBuf <> '' then
  417. begin
  418. Result := Result + '.'+LBuf;
  419. end;
  420. end;
  421. end;
  422. function FileNameUnixToMPEiXHFS(const AUnixFileName : String; const IsRoot : Boolean=False): String;
  423. //based on: http://docs.hp.com/mpeix/onlinedocs/32650-90492/32650-90492.html
  424. {
  425. http://docs.hp.com/mpeix/onlinedocs/32650-90492/32650-90492.html
  426. FS pathnames differ from MPE pathnames in the following ways:
  427. Names are separated with forward slashes (/), rather than dots.
  428. The order of the file name, group name, and account name are presented in
  429. reverse order compared to MPE syntax (/ACCT/GROUP/FILE versus FILE.GROUP.ACCT).
  430. Slash (/) at the beginning of a pathname indicates the root directory.
  431. Dot-slash (./) at the beginning of a pathname indicates the current working
  432. directory (CWD). The CWD is the directory in which you are currently working.
  433. Pathnames can be up to 1023 characters whereas traditional MPE names must be
  434. less than or equal to 26 characters (names can be up to 35 characters if a
  435. lockword is used). See Table 2-2 for CI restrictions.
  436. Using these conventions, the format of the MPE pathname
  437. MYFILE.PAYROLL.FINANCE appears as follows in HFS syntax:
  438. /FINANCE/PAYROLL/MYFILE
  439. In this example, it is assumed that MYFILE is a file under the PAYROLL group
  440. and FINANCE account. However, FINANCE and PAYROLL need not necessarily be an
  441. account and a group as they must in MPE syntax. Using HFS syntax, MYFILE could
  442. be a file under the PAYROLL HFS subdirectory, which is under the FINANCE HFS
  443. directory, which is under the root directory.
  444. }
  445. var
  446. MPEIX_Valid_Chars : String;
  447. MPEIX_CantStart : String;
  448. begin
  449. MPEIX_Valid_Chars := CharRange('a','z')+CharRange('A','Z')+CharRange('0','9')+'._-';
  450. MPEIX_CantStart := '-';
  451. Result := AUnixFileName;
  452. if Result<>'' then
  453. begin
  454. Result := EnsureValidCharsByValidSet(Result,MPEIX_VALID_CHARS,'_');
  455. repeat
  456. if CharIsInSet(Result, 1, MPEIX_CANTSTART) then
  457. begin
  458. Delete(Result,1,1);
  459. if Result='' then
  460. begin
  461. break;
  462. end;
  463. end
  464. else
  465. begin
  466. Break;
  467. end;
  468. until False;
  469. end;
  470. //note that this for clarrifying that this is a HFS file instead of a Traditional
  471. //MPEiX file.
  472. //A file in the foot folder uses the / and a file in the current dir uses ./
  473. if IsRoot then
  474. begin
  475. Result := '/'+Result;
  476. end
  477. else
  478. begin
  479. Result := './'+Result;
  480. end;
  481. Result := Copy(result,1,255);
  482. end;
  483. function FileNameUnixToOS9(const AUnixFileName : String) : String;
  484. //based on:
  485. //http://www.roug.org/soren/6809/os9guide/os9guide.pdf
  486. {
  487. Names can have one to 29 characters, all of which are used for matching. They
  488. must becin with an upper- or lower-case letter followed by any combination of
  489. the following characters:
  490. UpperCase letters: A - Z
  491. LowerCase letters: a - z
  492. decimal digits: 0 - 9
  493. underscore: _
  494. period: .
  495. }
  496. var
  497. OS9_Must_Start : String;
  498. OS9_Valid_Char : String;
  499. begin
  500. OS9_Must_Start := CharRange('a','z')+CharRange('A','Z');
  501. OS9_Valid_Char := CharRange('0','9')+'_.';
  502. Result := AUnixFileName;
  503. if Result<>'' then
  504. begin
  505. Result := EnsureValidCharsByValidSet(Result,OS9_VALID_CHAR,'_');
  506. repeat
  507. if ((CharIsInSet(Result, 1, OS9_MUST_START))=False) then
  508. begin
  509. Delete(Result,1,1);
  510. if Result='' then
  511. begin
  512. break;
  513. end;
  514. end
  515. else
  516. begin
  517. Break;
  518. end;
  519. until False;
  520. end;
  521. Result := Copy(Result,1,29);
  522. end;
  523. end.