1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045 |
- /*
- * fileops.c Copyright (C) Codemist Ltd 1994-2002
- *
- * cross-platform support for filenames etc
- *
- * A C Norman
- */
- /*
- * This code may be used and modified, and redistributed in binary
- * or source form, subject to the "CCL Public License", which should
- * accompany it. This license is a variant on the BSD license, and thus
- * permits use of code derived from this in either open and commercial
- * projects: but it does require that updates to this code be made
- * available back to the originators of the package.
- * Before merging other code in with this or linking this code
- * with other packages or libraries please check that the license terms
- * of the other material are compatible with those of this.
- */
- /* Signature: 6eab20cb 10-Oct-2002 */
- #include "sys.h"
- /*
- * datestamps that I store away have given me significant
- * trouble with regard to portability - so now I deal with times by
- * talking to the system in terms of broken down local time (struct tm).
- * I then pack things up for myself to get 32-bit timestamps. The
- * encoding I use aims at simplicity - it treats all months as 31 days
- * and thus does not have to worry about leap years etc. The effect will be
- * rather as if dates were stored as strings. And MAYBE I thereby avoid
- * some of the oddities that arise when data files containing packed dates
- * are transported across time-zones.
- *
- * NOTE: dates here are based from 1970, and this will lead to overflow
- * beyond 32-bit offsets in around 2099. At the time of writing that is around
- * 100 years ahead, and I intend not to worry.
- *
- * ANOTHER NOTE: I only allow the "seconds" field to run from 0 to 59.
- * In consequence I am quite possibly going to mess up when there are
- * leap seconds, and this confusion could make times processed here
- * disagree across systems by up to the number of leap seconds that
- * have been used to date. Well I have quite severe doubts about time
- * agreement closer than a few seconds anyway and so again I am going to
- * ignore this oddity! But those who keep systems synchronised at a
- * millisecond or microsecond resolution (GPS anybody?) might need to
- * know I have been sloppy.
- */
- void unpack_date(unsigned long int r,
- int *year, int *mon, int *day,
- int *hour, int *min, int *sec)
- {
- *sec = r%60; r = r/60;
- *min = r%60; r = r/60;
- *hour = r%24; r = r/24;
- *day = r%32; r = r/32;
- *mon = r%12; r = r/12;
- /*
- * Please note that the Standard C "struct tm" structure specifies dates
- * in terms of years since 1900. Thus from the year 2000 on the year will
- * be a value of at least 100, but that is not supposed to be any special
- * cause of disaster. In particular the calculation involving "+70"
- * rather than "+1970" is NOT a bug here!
- */
- *year = 70+r;
- }
- unsigned long int pack_date(int year, int mon, int day,
- int hour, int min, int sec)
- {
- unsigned long int r = (year-70)*12 + mon;
- r = r*32 + day;
- r = r*24 + hour;
- r = r*60 + min;
- return r*60 + sec;
- }
- #ifdef __arm
- /*
- * The following structure MUST match the layout of a FILE as used by
- * the library that is linked with.
- */
- struct FILE_COPY
- { int icnt; /* two separate _cnt fields so we can police ... */
- unsigned char *ptr;
- int ocnt; /* ... restrictions that read/write are fseek separated */
- int flag;
- unsigned char *base; /* buffer base */
- int file; /* RISCOS/Arthur/Brazil file handle */
- long pos; /* position in file */
- int bufsiz; /* maximum buffer size */
- int signature; /* used with temporary files */
- unsigned char lilbuf[2]; /* single byte buffer for them that want it */
- /* plus an unget char is put in __lilbuf[1] */
- long lspos; /* what __pos should be (set after lazy seek) */
- unsigned char *extent; /* extent of writes into the current buffer */
- int buflim; /* used size of buffer */
- int icnt_save; /* after unget contains old icnt */
- int ocnt_save; /* after unget contains old ocnt */
- };
- int truncate_file(FILE *f, long int where)
- {
- os_regset R;
- if (fflush(f) != 0) return 1;
- R.r[0] = 3;
- R.r[1] = (int)((struct FILE_COPY *)f)->file;
- R.r[2] = (int)where;
- os_args(&R);
- return 0;
- }
- int Cmkdir(char *name)
- {
- os_filestr osf;
- osf.action = 8;
- osf.name = name;
- osf.loadaddr = osf.execaddr = osf.start = osf.end = 0;
- os_file(&osf); /* create directory */
- return 1;
- }
- /*
- * I would like archives produced by this program to be portable
- * from one machine to another - and to this end I convert filenames
- * into a (partially) standard form based on the conventions used by
- * Unix.
- */
- static char *implicit_dirs[] =
- /*
- * If I find a directory with one of these names (when I am creating
- * an archive) I map files in it onto suffixed names in the archive -
- * thus for instance a file abc.xyz.lsp.pqr will be known as abc/xyz/pqr.lsp
- * in the archive. When unloading files from an archive this rule takes
- * precedence over the "leave_suffixes" one shown below.
- */
- {
- "!", /* Used by ACN as a place for scratch files */
- "c", /* C source code */
- "f", /* Fortran source code */
- "h", /* C header files */
- "l", /* Listings generated by the C compiler */
- "o", /* Object code */
- "p", /* Pascal? */
- "s", /* Assembly code */
- "lsp", /* Used with CSL */
- "sl", /* Used with CSL/REDUCE */
- "red", /* REDUCE sources */
- "fsl", /* CSL fast-load files */
- "log", /* I guess this is the hardest case */
- "tst", /* REDUCE test files */
- "doc", /* Another hard case */
- "cpp", /* C++ files */
- "hpp", /* C++ header files */
- "txt", /* to help me with som MSDOS transfers */
- "bak", /* Ditto. */
- NULL
- };
- static char *leave_suffixes[] =
- /*
- * If one of these names appears as the leaf name of a file in a directory
- * when I am creating an archive the leaf name is left as a suffix. Thus
- * abc.xyz.pqr.tex gets stored as abc/xyz/pqr.tex
- */
- {
- "300pk", /* Most of these arise with the Archimedes */
- "aux", /* port of TeX, where files are clustered */
- "bat", /* into a directory that relates to a single*/
- "bbl", /* document. */
- "bib",
- "blg",
- "doc",
- "dvi",
- "dvi-alw",
- "exe", /* Used when I store MSDOS images. */
- "img", /* Used when I store MSDOS CSL checkpoints */
- "log", /* "doc" and "log" are in both lists... */
- "sty", /* The effect is that xxx.yyy.log will get */
- "tex", /* inserted in the archive as xxx/yyy.log, */
- "tfm", /* and re-loaded as xxx.log.yyy */
- "toc", /* Some cases I can not win. */
- "rof", /* for "doc/scope.rof" in Reduce! */
- NULL
- };
- static CSLbool namematch(char *s, char *t)
- { int a, b;
- while ((a = *s++, b = *t++) != 0)
- {
- /* I make this a case-insensitive match */
- if (isupper(a)) a = tolower(a);
- if (isupper(b)) b = tolower(b);
- if (a != b) return FALSE;
- }
- /*
- * Since I am matching a component of a file name I allow the string
- * I am looking at to terminate either at string-end (0), or at one
- * of the characters that might separate parts of a file-name. This extra
- * generality may help me when packing up Acorn names - I guess it can give
- * problems if a '/' or '\' appears embedded in a name...
- */
- if (a == 0 || a == '.' || a == '/' || a == '\\') return TRUE;
- else return FALSE;
- }
- static CSLbool member(char *s, char **table)
- {
- char *t;
- while ((t = *table++) != NULL)
- if (namematch(s, t)) return TRUE;
- return FALSE;
- }
- void make_local_style_filename(char *filename, const char *old)
- /*
- * This maps a filename that is in the Unix style into one suitable
- * for the Archimedes. In particular a name of the form ppp/qqq.r will
- * be mapped onto ppp.r.qqq where the directory r will be created
- * automatically. Also fully rooted Unix names, as in /aaa/bbb get
- * turned into $.aaa.bbb
- * I turn a name-component of the form ".." into "^", so that
- * parent-directory references work.
- */
- {
- int j, k, n = strlen(old);
- CSLbool suffix = FALSE;
- char *res = filename;
- if (*old == '/') *res++ = '$'; /* Fully rooted case */
- for (j=n-1; j>=0; j--)
- if (old[j] == '/') break; /* only check last component for suffix */
- else if (old[j] == '.')
- { suffix = TRUE;
- break;
- }
- /*
- * Maybe I found a name of the form xxxx.yyyy, and old[j] is the '.'.
- * If the suffix is in the implicit_dirs list I do a flip.
- */
- if (suffix)
- { if (member(&old[j+1], implicit_dirs))
- { int i;
- for (k=j-1; k>=0; k--) if (old[k] == '/') break;
- if (k>=0) memcpy(res, old, k+1);
- memcpy(&res[k+1], &old[j+1], n-j-1);
- for (i=0; i<k+n-j; i++)
- { int c = res[i];
- if (c == '/') res[i] = '.';
- else if (c == '.') res[i] = '/';
- }
- res[k+n-j] = '.';
- memcpy(&res[k+n-j+1], &old[k+1], j-k-1);
- res[n] = 0;
- }
- else if (member(&old[j+1], leave_suffixes))
- {
- old[j] = '/';
- suffix = FALSE;
- }
- else suffix = FALSE;
- }
- if (!suffix)
- { for (j=0; j<=n; j++) /* Remember to transfer the final '\0' */
- { int c = old[j];
- if (c == '/') c = '.';
- else if (c == '.') c = '/';
- res[j] = c;
- }
- }
- /*
- * Now if in the Unix world I had a component '..' in the file it will
- * appear something like //.aaa.bbb or aaa.//.bbb
- * Similarly I map an isolated '.' (now an isolated '/') into '@'.
- */
- j = k = 0;
- for (;;)
- { int c = res[j];
- if (c == '.' || c == 0)
- { if (j == k+1 && res[k] == '/') res[k] = '@';
- else if (j == k + 2 && res[k] == '/' && res[k+1] == '/')
- { int c1;
- res[k++] = '^';
- do
- { c1 = res[k+1];
- res[k++] = c1;
- } while (c1 != 0);
- }
- k = j+1;
- }
- if (c == 0) break;
- j++;
- }
- /*
- * Now one more attempt to be helpful - I will truncate each component of the
- * resulting file name to be at most 10 characters long.
- */
- j = 0;
- for (;;)
- { k = j;
- while (res[k] != 0 && res[k] != '.') k++;
- if (k - j > 10)
- { int p1 = j + 10, p2 = k;
- for (;;)
- { int c = res[p2++];
- res[p1++] = c;
- if (c == 0) break;
- }
- }
- if (res[k] == 0) break;
- j = k + 1;
- }
- }
- static void unadjust1(char *filename, char *old, int n)
- /*
- * Convert '.' characters into '/' as part of the process of Unixifying
- * file-names. I also convert the other way ('/' -> '.') in case some
- * clown has filenames that contain '/'.
- */
- {
- while (n-- >= 0)
- { int c = *old++;
- if (c == '.') c = '/';
- else if (c == '/') c = '.';
- *filename++ = (char)c;
- }
- }
- static void unadjust2(char *filename)
- /*
- * This maps any '^' in a filename into '..', but does not bother to
- * look for context, or for any other special sorts of file-name.
- */
- {
- int j=0, k, c, c1;
- while ((c = filename[j]) != 0)
- { if (c == '^')
- { filename[j] = '.';
- k = j+1;
- c1 = '.';
- do
- { c = filename[k];
- filename[k++] = c1;
- c1 = c;
- } while (c != 0);
- }
- j++;
- }
- }
- void make_unix_style_filename(char *filename, const char *old)
- /*
- * This procedure maps filenames from Acorn format to Unix format,
- * allowing single-character directories to map onto extensions,
- * together with special cases for RED, LSP, LOG, FSL etc etc
- */
- {
- int j, k, n = strlen(old);
- for (j=n-1; j>=0; j--) if (old[j] == '.') break;
- if (j < 0)
- { strcpy(filename, old);
- unadjust2(filename);
- return; /* No '.' found, so no conversion needed */
- }
- /* Find the last '.' in the file name */
- for (k=j-1; k>=0; k--) if (old[k] == '.') break;
- /*
- * Test to see if penultimate component of file-name is one of the
- * special "implicit-directory" names.
- */
- if (member(&old[k+1], implicit_dirs))
- /*
- * If so turn "." to "/" in the root part of the name, and flip the order
- * of the name and extension.
- */
- { unadjust1(filename, old, k);
- memcpy(&filename[k+1], &old[j+1], n-j-1);
- filename[k+n-j] = '.';
- memcpy(&filename[k+n-j+1], &old[k+1], j-k-1);
- }
- /*
- * If the leaf name is in the second list of special cases leave it in
- * the converted name as a suffix.
- */
- else if (member(&old[j+1], leave_suffixes))
- { old[j] = '/';
- unadjust1(filename, old, n);
- old[j] = '.';
- }
- /* Otherwise just map "." onto "/" throughout the name */
- else unadjust1(filename, old, n);
- filename[n] = 0;
- /*
- * Files that started off as $.xxx now look like $/xxx, and should be
- * just "/xxx" - fix that case up.
- */
- if (filename[0] == '$' && filename[1] == '/')
- memmove(&filename[0], &filename[1], n);
- unadjust2(filename);
- return;
- }
- /* Reinstate date and filetype... */
- void set_filedate(char *name, unsigned long int datestamp,
- unsigned long int filetype)
- { os_filestr ctrl;
- time_t t0;
- unsigned32 high, low;
- struct tm st;
- unpack_date(datestamp, &st.tm_year, &st.tm_mon, &st.tm_mday,
- &st.tm_hour, &st.tm_min, &st.tm_sec);
- st.tm_isdst = -1;
- t0 = mktime(&st);
- low = t0 + (70*365+17)*24*60*60u;
- high = 100*(low >> 16);
- /* If the filetype looks odd I map it into a "text" file */
- if (filetype <= 0xe00) filetype = 0xfff;
- low = 100*(unsigned32)(low & 0xffffU);
- high = high + (low >> 16);
- low = (high << 16) | (low & 0xffffU);
- high = (high >> 16) & 0xff; /* Now in Acorn format */
- high |= 0xfff00000U | ((filetype & 0xfff) << 8);
- if (datestamp == 0) low = high = 0;
- ctrl.action = 1;
- ctrl.name = name;
- ctrl.loadaddr = (int)high;
- ctrl.execaddr = (int)low;
- ctrl.end = 3; /* Readable & writeable */
- os_file(&ctrl); /* Reset date & type */
- }
- void put_fileinfo(date_and_type *p, char *name)
- {
- unsigned long int datestamp, filetype;
- os_filestr parms;
- /*
- * Read file parameters...
- */
- parms.action = 5;
- parms.name = name;
- os_file(&parms); /* load & exec -> type & datestamp */
- if ((parms.loadaddr & 0xfff00000U) == 0xfff00000U)
- /*
- * Acorn keep datestamps accurate to 0.01 second - here I reduce it to
- * a resolution of just one second.
- */
- { unsigned32 dhi = parms.loadaddr & 0xff;
- unsigned32 dlo = parms.execaddr;
- struct tm *st;
- dhi = (dhi << 16) | (dlo >> 16);
- dlo = ((dhi % 100) << 16) | (dlo & 0xffffU);
- dhi = dhi / 100;
- datestamp = (dhi << 16) | (dlo / 100U);
- datestamp -= ((70*(unsigned32)365 + 17)*24)*60*60; /* Base 1970 now */
- filetype = ((int32)parms.loadaddr >> 8) & 0xfff;
- st = localtime(&datestamp);
- datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
- st->tm_hour, st->tm_min, st->tm_sec);
- }
- else
- { datestamp = 0;
- filetype = 0;
- }
- p->date = datestamp;
- p->type = filetype;
- }
- #else /* __arm */
- #ifdef WINDOWS_NT
- /*
- * This version is for Windows NT 3.1 with Microsoft VC++, Windows 95, 98,
- * NT 3.5, 4.0, 2000, XP etc etc, also with Watcom C etc.
- */
- int Cmkdir(char *name)
- {
- SECURITY_ATTRIBUTES s;
- s.nLength = sizeof(s);
- s.lpSecurityDescriptor = NULL;
- s.bInheritHandle = FALSE;
- return CreateDirectory(name, &s);
- }
- int truncate_file(FILE *f, long int where)
- {
- if (fflush(f) != 0) return 1;
- #ifdef _MSC_VER
- return _chsize(_fileno(f), where); /* Returns zero if success */
- #else
- return chsize(fileno(f), where); /* Returns zero if success */
- #endif
- }
- /*
- * For NT all I do to normalise names is swop '/' and '\' characters.
- * Well actually that is not quite enough - the characters
- * * : / \ ? " < > and |
- * are not supported in Windows names, so I will perform a mapping
- *
- * * ~s
- * / ~f
- * \ ~b
- * ? ~q
- * : ~c
- * " ~d
- * < ~l
- * > ~g
- * | ~v
- * ~ ~~
- *
- * so that "unix" names can still contain the funny characters (but I
- * have doubts about the sanity of people who exploit that!) while on
- * windows the names are longer and contain "~" signs at worst.
- */
- void make_local_style_filename(char *filename, const char *old)
- {
- /*
- * This is FROM unix TO Windows
- */
- int k;
- for (;;)
- { int c = *old++;
- switch (c)
- {
- case 0: *filename = 0;
- return;
- case '/':
- *filename++ = '\\';
- continue;
- case '*': k = 's'; break;
- case '\\': k = 'b'; break;
- case ':': k = 'c'; break;
- case '?': k = 'q'; break;
- case '"': k = 'd'; break;
- case '<': k = 'l'; break;
- case '>': k = 'g'; break;
- case '|': k = 'v'; break;
- case '~': k = '~'; break;
- default:
- *filename++ = (char)c;
- continue;
- }
- *filename++ = '~';
- *filename++ = (char)k;
- continue;
- }
- }
- void make_unix_style_filename(char *filename, const char *old)
- {
- /*
- * If the user had a file on Windows/DOS that "ordinarily" has an "~"
- * sign in it then this code will do funny things to it as the name is
- * converted for Unix use. Apologies. But this seems necessary if I am
- * to have any guarantee of ability to move files from unix to DOS.
- */
- for (;;)
- { int c = *old++;
- switch (c)
- {
- case 0:
- *filename = 0;
- return;
- case '\\':
- *filename++ = '/';
- continue;
- case '~':
- switch (c = *old++)
- {
- case 's': *filename++ = '*'; continue;
- case 'b': *filename++ = '\\'; continue;
- case 'c': *filename++ = ':'; continue;
- case 'q': *filename++ = '?'; continue;
- case 'd': *filename++ = '"'; continue;
- case 'l': *filename++ = '<'; continue;
- case 'g': *filename++ = '>'; continue;
- case 'v': *filename++ = '|'; continue;
- case '~': *filename++ = '~'; continue;
- case 0: *filename++ = '~';
- *filename = 0; return;
- default: *filename++ = '~';
- *filename++ = (char)c; continue;
- }
- continue;
- default:
- *filename++ = (char)c;
- continue;
- }
- }
- }
- void set_filedate(char *name, unsigned long int datestamp,
- unsigned long int filetype)
- {
- HANDLE h = CreateFile(name, GENERIC_WRITE, 0, NULL,
- OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- SYSTEMTIME st;
- FILETIME ft;
- int yr, mon, day, hr, min, sec;
- CSL_IGNORE(filetype); /* not a concept on Windows */
- /*
- * Here datestamp is a time expressed (sort of) in seconds since the start
- * of 1970. * I need to convert it into a broken-down SYSTEMTIME so that I
- * can then re-pack it as a Windows-NT FILETIME....
- */
- unpack_date(datestamp, &yr, &mon, &day, &hr, &min, &sec);
- st.wMilliseconds = 0;
- st.wYear = yr + 1900; /* Windows NT uses full dates since the year 0 */
- st.wMonth = mon + 1;
- st.wDay = day;
- st.wHour = hr;
- st.wMinute = min;
- st.wSecond = sec;
- SystemTimeToFileTime(&st, &ft);
- SetFileTime(h, NULL, NULL, &ft);
- CloseHandle(h);
- }
- void put_fileinfo(date_and_type *p, char *name)
- {
- unsigned long int datestamp, filetype;
- #ifdef _MSC_VER
- struct _stat file_info;
- #else
- struct stat file_info;
- #endif
- struct tm *st;
- /*
- * Read file parameters... Maybe I should use a Windows-style not a Unix-style
- * call here?
- */
- #ifdef _MSC_VER
- _stat(name, &file_info);
- #else
- stat(name, &file_info);
- #endif
- st = localtime(&(file_info.st_mtime));
- datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
- st->tm_hour, st->tm_min, st->tm_sec);
- filetype = 0xfff;
- p->date = datestamp;
- p->type = filetype;
- }
- #else /* WINDOWS_NT */
- #ifdef MS_DOS
- int truncate_file(FILE *f, long int where)
- {
- if (fflush(f) != 0) return 1;
- #ifndef __WATCOMC__
- #ifndef __BORLANDC__
- #define fileno(fp) ((fp)->_file) /* see <stdio.h> */
- #endif
- #endif
- #ifdef _MSC_VER
- #define fileno(fp) ((fp)->_file) /* see <stdio.h> */
- return _chsize(_fileno(f), where); /* Returns zero if success */
- #else
- #ifdef GCC386
- return ftruncate(fileno(f), where);
- /* NB the file must be closed just after this call */
- #else
- return chsize(fileno(f), where); /* Returns zero if success */
- #endif
- #endif
- }
- int Cmkdir(char *s)
- {
- #if defined GCC386 || defined GCCWIN
- return mkdir(s, 0775);
- #else
- return mkdir(s);
- #endif
- }
- /*
- * For MSDOS all I do to normalise names is swop '/' and '\' characters.
- * Well actually that is not quite enough - the characters
- * * : / \ ? " < > and |
- * are not supported in Windows names, so I will perform a mapping
- *
- * * ~s
- * / ~f
- * \ ~b
- * ? ~q
- * : ~c
- * " ~d
- * < ~l
- * > ~g
- * | ~v
- * ~ ~~
- *
- * so that "unix" names can still contain the funny characters (but I
- * have doubts about the sanity of people who exploit that!) while on
- * windows the names are longer and contain "~" signs at worst.
- */
- void make_local_style_filename(char *filename, const char *old)
- {
- /*
- * This is FROM unix TO Windows
- */
- int k;
- for (;;)
- { int c = *old++;
- switch (c)
- {
- case 0: *filename = 0;
- return;
- case '/':
- *filename++ = '\\';
- continue;
- case '*': k = 's'; break;
- case '\\': k = 'b'; break;
- case ':': k = 'c'; break;
- case '?': k = 'q'; break;
- case '"': k = 'd'; break;
- case '<': k = 'l'; break;
- case '>': k = 'g'; break;
- case '|': k = 'v'; break;
- case '~': k = '~'; break;
- default:
- *filename++ = (char)c;
- continue;
- }
- *filename++ = '~';
- *filename++ = (char)k;
- continue;
- }
- }
- void make_unix_style_filename(char *filename, const char *old)
- {
- /*
- * If the user had a file on Windows/DOS that "ordinarily" has an "~"
- * sign in it then this code will do funny things to it as the name is
- * converted for Unix use. Apologies. But this seems necessary if I am
- * to have any guarantee of ability to move files from unix to DOS.
- */
- for (;;)
- { int c = *old++;
- switch (c)
- {
- case 0:
- *filename = 0;
- return;
- case '\\':
- *filename++ = '/';
- continue;
- case '~':
- switch (c = *old++)
- {
- case 's': *filename++ = '*'; continue;
- case 'b': *filename++ = '\\'; continue;
- case 'c': *filename++ = ':'; continue;
- case 'q': *filename++ = '?'; continue;
- case 'd': *filename++ = '"'; continue;
- case 'l': *filename++ = '<'; continue;
- case 'g': *filename++ = '>'; continue;
- case 'v': *filename++ = '|'; continue;
- case '~': *filename++ = '~'; continue;
- case 0: *filename++ = '~';
- *filename = 0; return;
- default: *filename++ = '~';
- *filename++ = (char)c; continue;
- }
- continue;
- default:
- *filename++ = (char)c;
- continue;
- }
- }
- }
- #ifdef __WATCOMC__
- #include <sys\utime.h>
- #endif
- #ifdef __LCC__
- #include <utime.h>
- #endif
- void set_filedate(char *name, unsigned long int datestamp,
- unsigned long int filetype)
- {
- #ifdef __WATCOMC__
- struct utimbuf tt;
- #else
- #ifdef __LCC__
- struct _utimbuf tt;
- #else
- time_t tt[2];
- #endif
- #endif
- time_t t0;
- struct tm st;
- CSL_IGNORE(filetype);
- unpack_date(datestamp, &st.tm_year, &st.tm_mon, &st.tm_mday,
- &st.tm_hour, &st.tm_min, &st.tm_sec);
- st.tm_isdst = -1;
- t0 = mktime(&st);
- #ifdef __WATCOMC__
- tt.actime = tt.modtime = t0;
- utime(name, &tt);
- #else
- #ifdef __LCC__
- tt.actime = tt.modtime = t0;
- _utime(name, &tt);
- #else
- tt[0] = tt[1] = t0;
- utime(name, tt);
- #endif
- #endif
- }
- extern int stat(const char *, struct stat*);
- void put_fileinfo(date_and_type *p, char *name)
- {
- unsigned long int datestamp, filetype;
- struct stat file_info;
- struct tm *st;
- /*
- * Read file parameters...
- */
- #ifdef _MSC_VER
- _stat(name, &file_info);
- #else
- stat(name, &file_info);
- #endif
- st = localtime(&(file_info.st_mtime));
- datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
- st->tm_hour, st->tm_min, st->tm_sec);
- filetype = 0xfff;
- p->date = datestamp;
- p->type = filetype;
- }
- #else /* MS_DOS */
- #ifdef UNIX
- /*
- * On some Unix variants I may want this declaration inserted and on others
- * it would clash with a system-provided header file. Ugh! With luck the C
- * compiler will invent a suitable calling convention even if a declaration
- * is not present.
- * extern ftruncate(int, int);
- */
- int truncate_file(FILE *f, long int where)
- {
- #ifdef NCC_LIB
- #define SYS_ftruncate (130)
- extern int _syscall2(int, int, int);
- extern int _fileno(FILE *);
- #endif
- if (fflush(f) != 0) return 1;
- #ifdef NCC_LIB
- /* Returns zero if success */
- return _syscall2(SYS_ftruncate, _fileno(f), (int)where);
- #else
- return ftruncate(fileno(f), where); /* Returns zero if success */
- #endif
- }
- /* extern void mkdir(const char *, unsigned short int); */
- int Cmkdir(char *s)
- {
- mkdir(s, 0775);
- return 1;
- }
- void make_local_style_filename(char *filename, const char *old)
- {
- strcpy(filename, old);
- return;
- }
- void make_unix_style_filename(char *filename, const char *old)
- {
- strcpy(filename, old);
- return;
- }
- #include <utime.h>
- void set_filedate(char *name, unsigned long int datestamp,
- unsigned long int filetype)
- {
- #ifdef UTIME_TIME_T
- time_t tt[2];
- #else
- struct utimbuf tt;
- #endif
- time_t t0;
- struct tm st;
- CSL_IGNORE(filetype);
- unpack_date(datestamp, &st.tm_year, &st.tm_mon, &st.tm_mday,
- &st.tm_hour, &st.tm_min, &st.tm_sec);
- st.tm_isdst = -1;
- t0 = mktime(&st);
- #ifdef UTIME_TIME_T
- tt[0] = tt[1] = t0;
- #else
- tt.actime = tt.modtime = t0;
- #endif
- utime(name, &tt);
- }
- void put_fileinfo(date_and_type *p, char *name)
- {
- unsigned long int datestamp, filetype;
- struct stat file_info;
- struct tm *st;
- /*
- * Read file parameters...
- */
- stat(name, &file_info);
- st = localtime(&(file_info.st_mtime));
- datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
- st->tm_hour, st->tm_min, st->tm_sec);
- filetype = 0xfff; /* should get access status here? */
- p->date = datestamp;
- p->type = filetype;
- }
- #else /* UNIX */
- #ifdef ATARI
- int truncate_file(FILE *f, long int where)
- {
- if (fflush(f) != 0) return 1;
- #define fileno(fp) ((fp)->_file) /* see <stdio.h> */
- return chsize(fileno(f), where); /* Returns zero if success */
- }
- int Cmkdir(char *s)
- {
- mkdir(s);
- return 1;
- }
- /*
- * For TOS all I do to normalise names is swop '/' and '\' characters.
- */
- void make_local_style_filename(char *filename, const char *old)
- {
- int j, n = strlen(old);
- strcpy(filename, old);
- for (j=0; j<n; j++)
- if (filename[j] == '/') filename[j] = '\\';
- else if (filename[j] == '\\') filename[j] = '/';
- return;
- }
- void make_unix_style_filename(char *filename, const char *old)
- {
- int j, n = strlen(old);
- strcpy(filename, old);
- for (j=0; j<n; j++)
- if (filename[j] == '/') filename[j] = '\\';
- else if (filename[j] == '\\') filename[j] = '/';
- return;
- }
- /* No file date support for ATARI yet */
- #else /* ATARI */
- #ifdef macintosh
- int Cmkdir(char *s)
- {
- mkdir(s);
- return 1;
- }
- /*
- * For the Macintosh I normalise names by swapping '/' and ':' characters.
- */
- void make_local_style_filename(char *filename, const char *old)
- {
- int c, j, n = strlen(old);
- filename[0] = ':';
- strcpy(filename+1, old);
- for (j=1; j<=n; j++)
- if (filename[j] == '/') filename[j] = ':';
- else if (filename[j] == ':') filename[j] = '/';
- /*
- * Now if I find a pattern :..: in the string I just squash it to read ::,
- * since that is how that Mac indicates a parent directory. Note that
- * I always have an initial : at the start of a name, so nothing special
- * is needed there.
- */
- j = 0;
- while ((c = filename[j]) != 0)
- { if (c == ':' && filename[j+1] == '.' &&
- filename[j+2] == '.' && filename[j+3] == ':')
- { int k = j+1;
- do
- { c = filename[k+2];
- filename[k++] = c;
- } while (c != 0);
- }
- j++;
- }
- return;
- }
- void make_unix_style_filename(char *filename, const char *old)
- {
- int j, n = strlen(old);
- filename[0] = ':'
- strcpy(filename+1, old);
- for (j=1; j<=n; j++)
- if (filename[j] == '/') filename[j] = ':';
- else if (filename[j] == ':') filename[j] = '/';
- return;
- }
- /* No file date services on Macintosh yet */
- #endif /* macintosh */
- #endif /* ATARI */
- #endif /* UNIX */
- #endif /* MS_DOS */
- #endif /* WINDOWS_NT */
- #endif /* __arm */
- /* End of fileops.c */
|