/*
* 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 */