fileops.c 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. /*
  2. * fileops.c Copyright (C) Codemist Ltd 1994-2002
  3. *
  4. * cross-platform support for filenames etc
  5. *
  6. * A C Norman
  7. */
  8. /*
  9. * This code may be used and modified, and redistributed in binary
  10. * or source form, subject to the "CCL Public License", which should
  11. * accompany it. This license is a variant on the BSD license, and thus
  12. * permits use of code derived from this in either open and commercial
  13. * projects: but it does require that updates to this code be made
  14. * available back to the originators of the package.
  15. * Before merging other code in with this or linking this code
  16. * with other packages or libraries please check that the license terms
  17. * of the other material are compatible with those of this.
  18. */
  19. /* Signature: 6eab20cb 10-Oct-2002 */
  20. #include "sys.h"
  21. /*
  22. * datestamps that I store away have given me significant
  23. * trouble with regard to portability - so now I deal with times by
  24. * talking to the system in terms of broken down local time (struct tm).
  25. * I then pack things up for myself to get 32-bit timestamps. The
  26. * encoding I use aims at simplicity - it treats all months as 31 days
  27. * and thus does not have to worry about leap years etc. The effect will be
  28. * rather as if dates were stored as strings. And MAYBE I thereby avoid
  29. * some of the oddities that arise when data files containing packed dates
  30. * are transported across time-zones.
  31. *
  32. * NOTE: dates here are based from 1970, and this will lead to overflow
  33. * beyond 32-bit offsets in around 2099. At the time of writing that is around
  34. * 100 years ahead, and I intend not to worry.
  35. *
  36. * ANOTHER NOTE: I only allow the "seconds" field to run from 0 to 59.
  37. * In consequence I am quite possibly going to mess up when there are
  38. * leap seconds, and this confusion could make times processed here
  39. * disagree across systems by up to the number of leap seconds that
  40. * have been used to date. Well I have quite severe doubts about time
  41. * agreement closer than a few seconds anyway and so again I am going to
  42. * ignore this oddity! But those who keep systems synchronised at a
  43. * millisecond or microsecond resolution (GPS anybody?) might need to
  44. * know I have been sloppy.
  45. */
  46. void unpack_date(unsigned long int r,
  47. int *year, int *mon, int *day,
  48. int *hour, int *min, int *sec)
  49. {
  50. *sec = r%60; r = r/60;
  51. *min = r%60; r = r/60;
  52. *hour = r%24; r = r/24;
  53. *day = r%32; r = r/32;
  54. *mon = r%12; r = r/12;
  55. /*
  56. * Please note that the Standard C "struct tm" structure specifies dates
  57. * in terms of years since 1900. Thus from the year 2000 on the year will
  58. * be a value of at least 100, but that is not supposed to be any special
  59. * cause of disaster. In particular the calculation involving "+70"
  60. * rather than "+1970" is NOT a bug here!
  61. */
  62. *year = 70+r;
  63. }
  64. unsigned long int pack_date(int year, int mon, int day,
  65. int hour, int min, int sec)
  66. {
  67. unsigned long int r = (year-70)*12 + mon;
  68. r = r*32 + day;
  69. r = r*24 + hour;
  70. r = r*60 + min;
  71. return r*60 + sec;
  72. }
  73. #ifdef __arm
  74. /*
  75. * The following structure MUST match the layout of a FILE as used by
  76. * the library that is linked with.
  77. */
  78. struct FILE_COPY
  79. { int icnt; /* two separate _cnt fields so we can police ... */
  80. unsigned char *ptr;
  81. int ocnt; /* ... restrictions that read/write are fseek separated */
  82. int flag;
  83. unsigned char *base; /* buffer base */
  84. int file; /* RISCOS/Arthur/Brazil file handle */
  85. long pos; /* position in file */
  86. int bufsiz; /* maximum buffer size */
  87. int signature; /* used with temporary files */
  88. unsigned char lilbuf[2]; /* single byte buffer for them that want it */
  89. /* plus an unget char is put in __lilbuf[1] */
  90. long lspos; /* what __pos should be (set after lazy seek) */
  91. unsigned char *extent; /* extent of writes into the current buffer */
  92. int buflim; /* used size of buffer */
  93. int icnt_save; /* after unget contains old icnt */
  94. int ocnt_save; /* after unget contains old ocnt */
  95. };
  96. int truncate_file(FILE *f, long int where)
  97. {
  98. os_regset R;
  99. if (fflush(f) != 0) return 1;
  100. R.r[0] = 3;
  101. R.r[1] = (int)((struct FILE_COPY *)f)->file;
  102. R.r[2] = (int)where;
  103. os_args(&R);
  104. return 0;
  105. }
  106. int Cmkdir(char *name)
  107. {
  108. os_filestr osf;
  109. osf.action = 8;
  110. osf.name = name;
  111. osf.loadaddr = osf.execaddr = osf.start = osf.end = 0;
  112. os_file(&osf); /* create directory */
  113. return 1;
  114. }
  115. /*
  116. * I would like archives produced by this program to be portable
  117. * from one machine to another - and to this end I convert filenames
  118. * into a (partially) standard form based on the conventions used by
  119. * Unix.
  120. */
  121. static char *implicit_dirs[] =
  122. /*
  123. * If I find a directory with one of these names (when I am creating
  124. * an archive) I map files in it onto suffixed names in the archive -
  125. * thus for instance a file abc.xyz.lsp.pqr will be known as abc/xyz/pqr.lsp
  126. * in the archive. When unloading files from an archive this rule takes
  127. * precedence over the "leave_suffixes" one shown below.
  128. */
  129. {
  130. "!", /* Used by ACN as a place for scratch files */
  131. "c", /* C source code */
  132. "f", /* Fortran source code */
  133. "h", /* C header files */
  134. "l", /* Listings generated by the C compiler */
  135. "o", /* Object code */
  136. "p", /* Pascal? */
  137. "s", /* Assembly code */
  138. "lsp", /* Used with CSL */
  139. "sl", /* Used with CSL/REDUCE */
  140. "red", /* REDUCE sources */
  141. "fsl", /* CSL fast-load files */
  142. "log", /* I guess this is the hardest case */
  143. "tst", /* REDUCE test files */
  144. "doc", /* Another hard case */
  145. "cpp", /* C++ files */
  146. "hpp", /* C++ header files */
  147. "txt", /* to help me with som MSDOS transfers */
  148. "bak", /* Ditto. */
  149. NULL
  150. };
  151. static char *leave_suffixes[] =
  152. /*
  153. * If one of these names appears as the leaf name of a file in a directory
  154. * when I am creating an archive the leaf name is left as a suffix. Thus
  155. * abc.xyz.pqr.tex gets stored as abc/xyz/pqr.tex
  156. */
  157. {
  158. "300pk", /* Most of these arise with the Archimedes */
  159. "aux", /* port of TeX, where files are clustered */
  160. "bat", /* into a directory that relates to a single*/
  161. "bbl", /* document. */
  162. "bib",
  163. "blg",
  164. "doc",
  165. "dvi",
  166. "dvi-alw",
  167. "exe", /* Used when I store MSDOS images. */
  168. "img", /* Used when I store MSDOS CSL checkpoints */
  169. "log", /* "doc" and "log" are in both lists... */
  170. "sty", /* The effect is that xxx.yyy.log will get */
  171. "tex", /* inserted in the archive as xxx/yyy.log, */
  172. "tfm", /* and re-loaded as xxx.log.yyy */
  173. "toc", /* Some cases I can not win. */
  174. "rof", /* for "doc/scope.rof" in Reduce! */
  175. NULL
  176. };
  177. static CSLbool namematch(char *s, char *t)
  178. { int a, b;
  179. while ((a = *s++, b = *t++) != 0)
  180. {
  181. /* I make this a case-insensitive match */
  182. if (isupper(a)) a = tolower(a);
  183. if (isupper(b)) b = tolower(b);
  184. if (a != b) return FALSE;
  185. }
  186. /*
  187. * Since I am matching a component of a file name I allow the string
  188. * I am looking at to terminate either at string-end (0), or at one
  189. * of the characters that might separate parts of a file-name. This extra
  190. * generality may help me when packing up Acorn names - I guess it can give
  191. * problems if a '/' or '\' appears embedded in a name...
  192. */
  193. if (a == 0 || a == '.' || a == '/' || a == '\\') return TRUE;
  194. else return FALSE;
  195. }
  196. static CSLbool member(char *s, char **table)
  197. {
  198. char *t;
  199. while ((t = *table++) != NULL)
  200. if (namematch(s, t)) return TRUE;
  201. return FALSE;
  202. }
  203. void make_local_style_filename(char *filename, const char *old)
  204. /*
  205. * This maps a filename that is in the Unix style into one suitable
  206. * for the Archimedes. In particular a name of the form ppp/qqq.r will
  207. * be mapped onto ppp.r.qqq where the directory r will be created
  208. * automatically. Also fully rooted Unix names, as in /aaa/bbb get
  209. * turned into $.aaa.bbb
  210. * I turn a name-component of the form ".." into "^", so that
  211. * parent-directory references work.
  212. */
  213. {
  214. int j, k, n = strlen(old);
  215. CSLbool suffix = FALSE;
  216. char *res = filename;
  217. if (*old == '/') *res++ = '$'; /* Fully rooted case */
  218. for (j=n-1; j>=0; j--)
  219. if (old[j] == '/') break; /* only check last component for suffix */
  220. else if (old[j] == '.')
  221. { suffix = TRUE;
  222. break;
  223. }
  224. /*
  225. * Maybe I found a name of the form xxxx.yyyy, and old[j] is the '.'.
  226. * If the suffix is in the implicit_dirs list I do a flip.
  227. */
  228. if (suffix)
  229. { if (member(&old[j+1], implicit_dirs))
  230. { int i;
  231. for (k=j-1; k>=0; k--) if (old[k] == '/') break;
  232. if (k>=0) memcpy(res, old, k+1);
  233. memcpy(&res[k+1], &old[j+1], n-j-1);
  234. for (i=0; i<k+n-j; i++)
  235. { int c = res[i];
  236. if (c == '/') res[i] = '.';
  237. else if (c == '.') res[i] = '/';
  238. }
  239. res[k+n-j] = '.';
  240. memcpy(&res[k+n-j+1], &old[k+1], j-k-1);
  241. res[n] = 0;
  242. }
  243. else if (member(&old[j+1], leave_suffixes))
  244. {
  245. old[j] = '/';
  246. suffix = FALSE;
  247. }
  248. else suffix = FALSE;
  249. }
  250. if (!suffix)
  251. { for (j=0; j<=n; j++) /* Remember to transfer the final '\0' */
  252. { int c = old[j];
  253. if (c == '/') c = '.';
  254. else if (c == '.') c = '/';
  255. res[j] = c;
  256. }
  257. }
  258. /*
  259. * Now if in the Unix world I had a component '..' in the file it will
  260. * appear something like //.aaa.bbb or aaa.//.bbb
  261. * Similarly I map an isolated '.' (now an isolated '/') into '@'.
  262. */
  263. j = k = 0;
  264. for (;;)
  265. { int c = res[j];
  266. if (c == '.' || c == 0)
  267. { if (j == k+1 && res[k] == '/') res[k] = '@';
  268. else if (j == k + 2 && res[k] == '/' && res[k+1] == '/')
  269. { int c1;
  270. res[k++] = '^';
  271. do
  272. { c1 = res[k+1];
  273. res[k++] = c1;
  274. } while (c1 != 0);
  275. }
  276. k = j+1;
  277. }
  278. if (c == 0) break;
  279. j++;
  280. }
  281. /*
  282. * Now one more attempt to be helpful - I will truncate each component of the
  283. * resulting file name to be at most 10 characters long.
  284. */
  285. j = 0;
  286. for (;;)
  287. { k = j;
  288. while (res[k] != 0 && res[k] != '.') k++;
  289. if (k - j > 10)
  290. { int p1 = j + 10, p2 = k;
  291. for (;;)
  292. { int c = res[p2++];
  293. res[p1++] = c;
  294. if (c == 0) break;
  295. }
  296. }
  297. if (res[k] == 0) break;
  298. j = k + 1;
  299. }
  300. }
  301. static void unadjust1(char *filename, char *old, int n)
  302. /*
  303. * Convert '.' characters into '/' as part of the process of Unixifying
  304. * file-names. I also convert the other way ('/' -> '.') in case some
  305. * clown has filenames that contain '/'.
  306. */
  307. {
  308. while (n-- >= 0)
  309. { int c = *old++;
  310. if (c == '.') c = '/';
  311. else if (c == '/') c = '.';
  312. *filename++ = (char)c;
  313. }
  314. }
  315. static void unadjust2(char *filename)
  316. /*
  317. * This maps any '^' in a filename into '..', but does not bother to
  318. * look for context, or for any other special sorts of file-name.
  319. */
  320. {
  321. int j=0, k, c, c1;
  322. while ((c = filename[j]) != 0)
  323. { if (c == '^')
  324. { filename[j] = '.';
  325. k = j+1;
  326. c1 = '.';
  327. do
  328. { c = filename[k];
  329. filename[k++] = c1;
  330. c1 = c;
  331. } while (c != 0);
  332. }
  333. j++;
  334. }
  335. }
  336. void make_unix_style_filename(char *filename, const char *old)
  337. /*
  338. * This procedure maps filenames from Acorn format to Unix format,
  339. * allowing single-character directories to map onto extensions,
  340. * together with special cases for RED, LSP, LOG, FSL etc etc
  341. */
  342. {
  343. int j, k, n = strlen(old);
  344. for (j=n-1; j>=0; j--) if (old[j] == '.') break;
  345. if (j < 0)
  346. { strcpy(filename, old);
  347. unadjust2(filename);
  348. return; /* No '.' found, so no conversion needed */
  349. }
  350. /* Find the last '.' in the file name */
  351. for (k=j-1; k>=0; k--) if (old[k] == '.') break;
  352. /*
  353. * Test to see if penultimate component of file-name is one of the
  354. * special "implicit-directory" names.
  355. */
  356. if (member(&old[k+1], implicit_dirs))
  357. /*
  358. * If so turn "." to "/" in the root part of the name, and flip the order
  359. * of the name and extension.
  360. */
  361. { unadjust1(filename, old, k);
  362. memcpy(&filename[k+1], &old[j+1], n-j-1);
  363. filename[k+n-j] = '.';
  364. memcpy(&filename[k+n-j+1], &old[k+1], j-k-1);
  365. }
  366. /*
  367. * If the leaf name is in the second list of special cases leave it in
  368. * the converted name as a suffix.
  369. */
  370. else if (member(&old[j+1], leave_suffixes))
  371. { old[j] = '/';
  372. unadjust1(filename, old, n);
  373. old[j] = '.';
  374. }
  375. /* Otherwise just map "." onto "/" throughout the name */
  376. else unadjust1(filename, old, n);
  377. filename[n] = 0;
  378. /*
  379. * Files that started off as $.xxx now look like $/xxx, and should be
  380. * just "/xxx" - fix that case up.
  381. */
  382. if (filename[0] == '$' && filename[1] == '/')
  383. memmove(&filename[0], &filename[1], n);
  384. unadjust2(filename);
  385. return;
  386. }
  387. /* Reinstate date and filetype... */
  388. void set_filedate(char *name, unsigned long int datestamp,
  389. unsigned long int filetype)
  390. { os_filestr ctrl;
  391. time_t t0;
  392. unsigned32 high, low;
  393. struct tm st;
  394. unpack_date(datestamp, &st.tm_year, &st.tm_mon, &st.tm_mday,
  395. &st.tm_hour, &st.tm_min, &st.tm_sec);
  396. st.tm_isdst = -1;
  397. t0 = mktime(&st);
  398. low = t0 + (70*365+17)*24*60*60u;
  399. high = 100*(low >> 16);
  400. /* If the filetype looks odd I map it into a "text" file */
  401. if (filetype <= 0xe00) filetype = 0xfff;
  402. low = 100*(unsigned32)(low & 0xffffU);
  403. high = high + (low >> 16);
  404. low = (high << 16) | (low & 0xffffU);
  405. high = (high >> 16) & 0xff; /* Now in Acorn format */
  406. high |= 0xfff00000U | ((filetype & 0xfff) << 8);
  407. if (datestamp == 0) low = high = 0;
  408. ctrl.action = 1;
  409. ctrl.name = name;
  410. ctrl.loadaddr = (int)high;
  411. ctrl.execaddr = (int)low;
  412. ctrl.end = 3; /* Readable & writeable */
  413. os_file(&ctrl); /* Reset date & type */
  414. }
  415. void put_fileinfo(date_and_type *p, char *name)
  416. {
  417. unsigned long int datestamp, filetype;
  418. os_filestr parms;
  419. /*
  420. * Read file parameters...
  421. */
  422. parms.action = 5;
  423. parms.name = name;
  424. os_file(&parms); /* load & exec -> type & datestamp */
  425. if ((parms.loadaddr & 0xfff00000U) == 0xfff00000U)
  426. /*
  427. * Acorn keep datestamps accurate to 0.01 second - here I reduce it to
  428. * a resolution of just one second.
  429. */
  430. { unsigned32 dhi = parms.loadaddr & 0xff;
  431. unsigned32 dlo = parms.execaddr;
  432. struct tm *st;
  433. dhi = (dhi << 16) | (dlo >> 16);
  434. dlo = ((dhi % 100) << 16) | (dlo & 0xffffU);
  435. dhi = dhi / 100;
  436. datestamp = (dhi << 16) | (dlo / 100U);
  437. datestamp -= ((70*(unsigned32)365 + 17)*24)*60*60; /* Base 1970 now */
  438. filetype = ((int32)parms.loadaddr >> 8) & 0xfff;
  439. st = localtime(&datestamp);
  440. datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
  441. st->tm_hour, st->tm_min, st->tm_sec);
  442. }
  443. else
  444. { datestamp = 0;
  445. filetype = 0;
  446. }
  447. p->date = datestamp;
  448. p->type = filetype;
  449. }
  450. #else /* __arm */
  451. #ifdef WINDOWS_NT
  452. /*
  453. * This version is for Windows NT 3.1 with Microsoft VC++, Windows 95, 98,
  454. * NT 3.5, 4.0, 2000, XP etc etc, also with Watcom C etc.
  455. */
  456. int Cmkdir(char *name)
  457. {
  458. SECURITY_ATTRIBUTES s;
  459. s.nLength = sizeof(s);
  460. s.lpSecurityDescriptor = NULL;
  461. s.bInheritHandle = FALSE;
  462. return CreateDirectory(name, &s);
  463. }
  464. int truncate_file(FILE *f, long int where)
  465. {
  466. if (fflush(f) != 0) return 1;
  467. #ifdef _MSC_VER
  468. return _chsize(_fileno(f), where); /* Returns zero if success */
  469. #else
  470. return chsize(fileno(f), where); /* Returns zero if success */
  471. #endif
  472. }
  473. /*
  474. * For NT all I do to normalise names is swop '/' and '\' characters.
  475. * Well actually that is not quite enough - the characters
  476. * * : / \ ? " < > and |
  477. * are not supported in Windows names, so I will perform a mapping
  478. *
  479. * * ~s
  480. * / ~f
  481. * \ ~b
  482. * ? ~q
  483. * : ~c
  484. * " ~d
  485. * < ~l
  486. * > ~g
  487. * | ~v
  488. * ~ ~~
  489. *
  490. * so that "unix" names can still contain the funny characters (but I
  491. * have doubts about the sanity of people who exploit that!) while on
  492. * windows the names are longer and contain "~" signs at worst.
  493. */
  494. void make_local_style_filename(char *filename, const char *old)
  495. {
  496. /*
  497. * This is FROM unix TO Windows
  498. */
  499. int k;
  500. for (;;)
  501. { int c = *old++;
  502. switch (c)
  503. {
  504. case 0: *filename = 0;
  505. return;
  506. case '/':
  507. *filename++ = '\\';
  508. continue;
  509. case '*': k = 's'; break;
  510. case '\\': k = 'b'; break;
  511. case ':': k = 'c'; break;
  512. case '?': k = 'q'; break;
  513. case '"': k = 'd'; break;
  514. case '<': k = 'l'; break;
  515. case '>': k = 'g'; break;
  516. case '|': k = 'v'; break;
  517. case '~': k = '~'; break;
  518. default:
  519. *filename++ = (char)c;
  520. continue;
  521. }
  522. *filename++ = '~';
  523. *filename++ = (char)k;
  524. continue;
  525. }
  526. }
  527. void make_unix_style_filename(char *filename, const char *old)
  528. {
  529. /*
  530. * If the user had a file on Windows/DOS that "ordinarily" has an "~"
  531. * sign in it then this code will do funny things to it as the name is
  532. * converted for Unix use. Apologies. But this seems necessary if I am
  533. * to have any guarantee of ability to move files from unix to DOS.
  534. */
  535. for (;;)
  536. { int c = *old++;
  537. switch (c)
  538. {
  539. case 0:
  540. *filename = 0;
  541. return;
  542. case '\\':
  543. *filename++ = '/';
  544. continue;
  545. case '~':
  546. switch (c = *old++)
  547. {
  548. case 's': *filename++ = '*'; continue;
  549. case 'b': *filename++ = '\\'; continue;
  550. case 'c': *filename++ = ':'; continue;
  551. case 'q': *filename++ = '?'; continue;
  552. case 'd': *filename++ = '"'; continue;
  553. case 'l': *filename++ = '<'; continue;
  554. case 'g': *filename++ = '>'; continue;
  555. case 'v': *filename++ = '|'; continue;
  556. case '~': *filename++ = '~'; continue;
  557. case 0: *filename++ = '~';
  558. *filename = 0; return;
  559. default: *filename++ = '~';
  560. *filename++ = (char)c; continue;
  561. }
  562. continue;
  563. default:
  564. *filename++ = (char)c;
  565. continue;
  566. }
  567. }
  568. }
  569. void set_filedate(char *name, unsigned long int datestamp,
  570. unsigned long int filetype)
  571. {
  572. HANDLE h = CreateFile(name, GENERIC_WRITE, 0, NULL,
  573. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  574. SYSTEMTIME st;
  575. FILETIME ft;
  576. int yr, mon, day, hr, min, sec;
  577. CSL_IGNORE(filetype); /* not a concept on Windows */
  578. /*
  579. * Here datestamp is a time expressed (sort of) in seconds since the start
  580. * of 1970. * I need to convert it into a broken-down SYSTEMTIME so that I
  581. * can then re-pack it as a Windows-NT FILETIME....
  582. */
  583. unpack_date(datestamp, &yr, &mon, &day, &hr, &min, &sec);
  584. st.wMilliseconds = 0;
  585. st.wYear = yr + 1900; /* Windows NT uses full dates since the year 0 */
  586. st.wMonth = mon + 1;
  587. st.wDay = day;
  588. st.wHour = hr;
  589. st.wMinute = min;
  590. st.wSecond = sec;
  591. SystemTimeToFileTime(&st, &ft);
  592. SetFileTime(h, NULL, NULL, &ft);
  593. CloseHandle(h);
  594. }
  595. void put_fileinfo(date_and_type *p, char *name)
  596. {
  597. unsigned long int datestamp, filetype;
  598. #ifdef _MSC_VER
  599. struct _stat file_info;
  600. #else
  601. struct stat file_info;
  602. #endif
  603. struct tm *st;
  604. /*
  605. * Read file parameters... Maybe I should use a Windows-style not a Unix-style
  606. * call here?
  607. */
  608. #ifdef _MSC_VER
  609. _stat(name, &file_info);
  610. #else
  611. stat(name, &file_info);
  612. #endif
  613. st = localtime(&(file_info.st_mtime));
  614. datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
  615. st->tm_hour, st->tm_min, st->tm_sec);
  616. filetype = 0xfff;
  617. p->date = datestamp;
  618. p->type = filetype;
  619. }
  620. #else /* WINDOWS_NT */
  621. #ifdef MS_DOS
  622. int truncate_file(FILE *f, long int where)
  623. {
  624. if (fflush(f) != 0) return 1;
  625. #ifndef __WATCOMC__
  626. #ifndef __BORLANDC__
  627. #define fileno(fp) ((fp)->_file) /* see <stdio.h> */
  628. #endif
  629. #endif
  630. #ifdef _MSC_VER
  631. #define fileno(fp) ((fp)->_file) /* see <stdio.h> */
  632. return _chsize(_fileno(f), where); /* Returns zero if success */
  633. #else
  634. #ifdef GCC386
  635. return ftruncate(fileno(f), where);
  636. /* NB the file must be closed just after this call */
  637. #else
  638. return chsize(fileno(f), where); /* Returns zero if success */
  639. #endif
  640. #endif
  641. }
  642. int Cmkdir(char *s)
  643. {
  644. #if defined GCC386 || defined GCCWIN
  645. return mkdir(s, 0775);
  646. #else
  647. return mkdir(s);
  648. #endif
  649. }
  650. /*
  651. * For MSDOS all I do to normalise names is swop '/' and '\' characters.
  652. * Well actually that is not quite enough - the characters
  653. * * : / \ ? " < > and |
  654. * are not supported in Windows names, so I will perform a mapping
  655. *
  656. * * ~s
  657. * / ~f
  658. * \ ~b
  659. * ? ~q
  660. * : ~c
  661. * " ~d
  662. * < ~l
  663. * > ~g
  664. * | ~v
  665. * ~ ~~
  666. *
  667. * so that "unix" names can still contain the funny characters (but I
  668. * have doubts about the sanity of people who exploit that!) while on
  669. * windows the names are longer and contain "~" signs at worst.
  670. */
  671. void make_local_style_filename(char *filename, const char *old)
  672. {
  673. /*
  674. * This is FROM unix TO Windows
  675. */
  676. int k;
  677. for (;;)
  678. { int c = *old++;
  679. switch (c)
  680. {
  681. case 0: *filename = 0;
  682. return;
  683. case '/':
  684. *filename++ = '\\';
  685. continue;
  686. case '*': k = 's'; break;
  687. case '\\': k = 'b'; break;
  688. case ':': k = 'c'; break;
  689. case '?': k = 'q'; break;
  690. case '"': k = 'd'; break;
  691. case '<': k = 'l'; break;
  692. case '>': k = 'g'; break;
  693. case '|': k = 'v'; break;
  694. case '~': k = '~'; break;
  695. default:
  696. *filename++ = (char)c;
  697. continue;
  698. }
  699. *filename++ = '~';
  700. *filename++ = (char)k;
  701. continue;
  702. }
  703. }
  704. void make_unix_style_filename(char *filename, const char *old)
  705. {
  706. /*
  707. * If the user had a file on Windows/DOS that "ordinarily" has an "~"
  708. * sign in it then this code will do funny things to it as the name is
  709. * converted for Unix use. Apologies. But this seems necessary if I am
  710. * to have any guarantee of ability to move files from unix to DOS.
  711. */
  712. for (;;)
  713. { int c = *old++;
  714. switch (c)
  715. {
  716. case 0:
  717. *filename = 0;
  718. return;
  719. case '\\':
  720. *filename++ = '/';
  721. continue;
  722. case '~':
  723. switch (c = *old++)
  724. {
  725. case 's': *filename++ = '*'; continue;
  726. case 'b': *filename++ = '\\'; continue;
  727. case 'c': *filename++ = ':'; continue;
  728. case 'q': *filename++ = '?'; continue;
  729. case 'd': *filename++ = '"'; continue;
  730. case 'l': *filename++ = '<'; continue;
  731. case 'g': *filename++ = '>'; continue;
  732. case 'v': *filename++ = '|'; continue;
  733. case '~': *filename++ = '~'; continue;
  734. case 0: *filename++ = '~';
  735. *filename = 0; return;
  736. default: *filename++ = '~';
  737. *filename++ = (char)c; continue;
  738. }
  739. continue;
  740. default:
  741. *filename++ = (char)c;
  742. continue;
  743. }
  744. }
  745. }
  746. #ifdef __WATCOMC__
  747. #include <sys\utime.h>
  748. #endif
  749. #ifdef __LCC__
  750. #include <utime.h>
  751. #endif
  752. void set_filedate(char *name, unsigned long int datestamp,
  753. unsigned long int filetype)
  754. {
  755. #ifdef __WATCOMC__
  756. struct utimbuf tt;
  757. #else
  758. #ifdef __LCC__
  759. struct _utimbuf tt;
  760. #else
  761. time_t tt[2];
  762. #endif
  763. #endif
  764. time_t t0;
  765. struct tm st;
  766. CSL_IGNORE(filetype);
  767. unpack_date(datestamp, &st.tm_year, &st.tm_mon, &st.tm_mday,
  768. &st.tm_hour, &st.tm_min, &st.tm_sec);
  769. st.tm_isdst = -1;
  770. t0 = mktime(&st);
  771. #ifdef __WATCOMC__
  772. tt.actime = tt.modtime = t0;
  773. utime(name, &tt);
  774. #else
  775. #ifdef __LCC__
  776. tt.actime = tt.modtime = t0;
  777. _utime(name, &tt);
  778. #else
  779. tt[0] = tt[1] = t0;
  780. utime(name, tt);
  781. #endif
  782. #endif
  783. }
  784. extern int stat(const char *, struct stat*);
  785. void put_fileinfo(date_and_type *p, char *name)
  786. {
  787. unsigned long int datestamp, filetype;
  788. struct stat file_info;
  789. struct tm *st;
  790. /*
  791. * Read file parameters...
  792. */
  793. #ifdef _MSC_VER
  794. _stat(name, &file_info);
  795. #else
  796. stat(name, &file_info);
  797. #endif
  798. st = localtime(&(file_info.st_mtime));
  799. datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
  800. st->tm_hour, st->tm_min, st->tm_sec);
  801. filetype = 0xfff;
  802. p->date = datestamp;
  803. p->type = filetype;
  804. }
  805. #else /* MS_DOS */
  806. #ifdef UNIX
  807. /*
  808. * On some Unix variants I may want this declaration inserted and on others
  809. * it would clash with a system-provided header file. Ugh! With luck the C
  810. * compiler will invent a suitable calling convention even if a declaration
  811. * is not present.
  812. * extern ftruncate(int, int);
  813. */
  814. int truncate_file(FILE *f, long int where)
  815. {
  816. #ifdef NCC_LIB
  817. #define SYS_ftruncate (130)
  818. extern int _syscall2(int, int, int);
  819. extern int _fileno(FILE *);
  820. #endif
  821. if (fflush(f) != 0) return 1;
  822. #ifdef NCC_LIB
  823. /* Returns zero if success */
  824. return _syscall2(SYS_ftruncate, _fileno(f), (int)where);
  825. #else
  826. return ftruncate(fileno(f), where); /* Returns zero if success */
  827. #endif
  828. }
  829. /* extern void mkdir(const char *, unsigned short int); */
  830. int Cmkdir(char *s)
  831. {
  832. mkdir(s, 0775);
  833. return 1;
  834. }
  835. void make_local_style_filename(char *filename, const char *old)
  836. {
  837. strcpy(filename, old);
  838. return;
  839. }
  840. void make_unix_style_filename(char *filename, const char *old)
  841. {
  842. strcpy(filename, old);
  843. return;
  844. }
  845. #include <utime.h>
  846. void set_filedate(char *name, unsigned long int datestamp,
  847. unsigned long int filetype)
  848. {
  849. #ifdef UTIME_TIME_T
  850. time_t tt[2];
  851. #else
  852. struct utimbuf tt;
  853. #endif
  854. time_t t0;
  855. struct tm st;
  856. CSL_IGNORE(filetype);
  857. unpack_date(datestamp, &st.tm_year, &st.tm_mon, &st.tm_mday,
  858. &st.tm_hour, &st.tm_min, &st.tm_sec);
  859. st.tm_isdst = -1;
  860. t0 = mktime(&st);
  861. #ifdef UTIME_TIME_T
  862. tt[0] = tt[1] = t0;
  863. #else
  864. tt.actime = tt.modtime = t0;
  865. #endif
  866. utime(name, &tt);
  867. }
  868. void put_fileinfo(date_and_type *p, char *name)
  869. {
  870. unsigned long int datestamp, filetype;
  871. struct stat file_info;
  872. struct tm *st;
  873. /*
  874. * Read file parameters...
  875. */
  876. stat(name, &file_info);
  877. st = localtime(&(file_info.st_mtime));
  878. datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
  879. st->tm_hour, st->tm_min, st->tm_sec);
  880. filetype = 0xfff; /* should get access status here? */
  881. p->date = datestamp;
  882. p->type = filetype;
  883. }
  884. #else /* UNIX */
  885. #ifdef ATARI
  886. int truncate_file(FILE *f, long int where)
  887. {
  888. if (fflush(f) != 0) return 1;
  889. #define fileno(fp) ((fp)->_file) /* see <stdio.h> */
  890. return chsize(fileno(f), where); /* Returns zero if success */
  891. }
  892. int Cmkdir(char *s)
  893. {
  894. mkdir(s);
  895. return 1;
  896. }
  897. /*
  898. * For TOS all I do to normalise names is swop '/' and '\' characters.
  899. */
  900. void make_local_style_filename(char *filename, const char *old)
  901. {
  902. int j, n = strlen(old);
  903. strcpy(filename, old);
  904. for (j=0; j<n; j++)
  905. if (filename[j] == '/') filename[j] = '\\';
  906. else if (filename[j] == '\\') filename[j] = '/';
  907. return;
  908. }
  909. void make_unix_style_filename(char *filename, const char *old)
  910. {
  911. int j, n = strlen(old);
  912. strcpy(filename, old);
  913. for (j=0; j<n; j++)
  914. if (filename[j] == '/') filename[j] = '\\';
  915. else if (filename[j] == '\\') filename[j] = '/';
  916. return;
  917. }
  918. /* No file date support for ATARI yet */
  919. #else /* ATARI */
  920. #ifdef macintosh
  921. int Cmkdir(char *s)
  922. {
  923. mkdir(s);
  924. return 1;
  925. }
  926. /*
  927. * For the Macintosh I normalise names by swapping '/' and ':' characters.
  928. */
  929. void make_local_style_filename(char *filename, const char *old)
  930. {
  931. int c, j, n = strlen(old);
  932. filename[0] = ':';
  933. strcpy(filename+1, old);
  934. for (j=1; j<=n; j++)
  935. if (filename[j] == '/') filename[j] = ':';
  936. else if (filename[j] == ':') filename[j] = '/';
  937. /*
  938. * Now if I find a pattern :..: in the string I just squash it to read ::,
  939. * since that is how that Mac indicates a parent directory. Note that
  940. * I always have an initial : at the start of a name, so nothing special
  941. * is needed there.
  942. */
  943. j = 0;
  944. while ((c = filename[j]) != 0)
  945. { if (c == ':' && filename[j+1] == '.' &&
  946. filename[j+2] == '.' && filename[j+3] == ':')
  947. { int k = j+1;
  948. do
  949. { c = filename[k+2];
  950. filename[k++] = c;
  951. } while (c != 0);
  952. }
  953. j++;
  954. }
  955. return;
  956. }
  957. void make_unix_style_filename(char *filename, const char *old)
  958. {
  959. int j, n = strlen(old);
  960. filename[0] = ':'
  961. strcpy(filename+1, old);
  962. for (j=1; j<=n; j++)
  963. if (filename[j] == '/') filename[j] = ':';
  964. else if (filename[j] == ':') filename[j] = '/';
  965. return;
  966. }
  967. /* No file date services on Macintosh yet */
  968. #endif /* macintosh */
  969. #endif /* ATARI */
  970. #endif /* UNIX */
  971. #endif /* MS_DOS */
  972. #endif /* WINDOWS_NT */
  973. #endif /* __arm */
  974. /* End of fileops.c */