fileops.c 29 KB

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