/* Text file I/O module for the Pawn Abstract Machine * * Copyright (c) ITB CompuPhase, 2003-2015 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * * Version: $Id: amxfile.c 5181 2015-01-21 09:44:28Z thiadmer $ */ #if defined _UNICODE || defined __UNICODE__ || defined UNICODE # if !defined UNICODE /* for Windows */ # define UNICODE # endif # if !defined _UNICODE /* for C library */ # define _UNICODE # endif #endif #include #include #include #include #include #include #include #include "osdefs.h" #if defined __BORLANDC__ #include #endif #if defined __BORLANDC__ || defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS || defined __APPLE__ #include #include #else #include #endif #if defined __WIN32__ || defined __MSDOS__ #include #endif #if defined __WATCOMC__ || defined _MSC_VER #include #endif #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS || defined __APPLE__ #include #else #include #endif #include "amx.h" #if defined __WIN32__ || defined _Windows #include #endif #include "fpattern.c" #if !defined AMXFILE_VAR #define AMXFILE_VAR "AMXFILE" #endif #if !defined sizearray #define sizearray(a) (sizeof(a)/sizeof((a)[0])) #endif #if defined _UNICODE #include #elif !defined __T typedef char TCHAR; #define __T(string) string #define _tchmod chmod #define _tcscat strcat #define _tcschr strchr #define _tcscmp strcmp #define _tcscpy strcpy #define _tcsdup strdup #define _tcslen strlen #define _tcsncpy strncpy #define _tcsnicmp strnicmp #define _tcspbrk strpbrk #define _tcsrchr strrchr #define _tcstol strtol #define _tfopen fopen #define _tfputs fputs #define _tgetenv getenv #define _tremove remove #define _trename rename #if defined __APPLE__ #define _tmkdir mkdir #define _trmdir rmdir #define _tstat stat #define _tutime utime #else #define _tmkdir mkdir #define _trmdir rmdir #define _tstat stat #define _tutime utime #endif #endif #if !(defined __WIN32__ || defined _WIN32 || defined WIN32) #define _stat(n, b) stat(n,b) #endif #if !defined S_ISDIR #define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0) #endif #include "minIni.h" enum filemode { io_read, /* file must exist */ io_write, /* creates a new file */ io_readwrite, /* file must exist */ io_append, /* file must exist, opened for writing only and seek to the end */ }; enum seek_whence { seek_start, seek_current, seek_end, }; /* This function only stores unpacked strings. UTF-8 is used for * Unicode, and packed strings can only store 7-bit and 8-bit * character sets (ASCII, Latin-1). */ static size_t fgets_cell(FILE *fp, cell *string, size_t max, int utf8mode) { size_t index; fpos_t pos; cell c; int follow, lastcr; cell lowmark; assert(sizeof(cell) >= 4); assert(fp != NULL); assert(string != NULL); if (max == 0) return 0; /* get the position, in case we have to back up */ fgetpos(fp, &pos); index = 0; follow = 0; lowmark = 0; lastcr = 0; for (; ;) { assert(index < max); if (index == max - 1) break; /* string fully filled */ if ((c = fgetc(fp)) == EOF) { if (!utf8mode || follow == 0) break; /* no more characters */ /* If an EOF happened halfway an UTF-8 code, the string cannot be * UTF-8 mode, and we must restart. */ index = 0; fsetpos(fp, &pos); continue; } /* if */ /* 8-bit characters are unsigned */ if (c < 0) c = -c; if (utf8mode) { if (follow > 0 && (c & 0xc0) == 0x80) { /* leader code is active, combine with earlier code */ string[index] = (string[index] << 6) | ((unsigned char) c & 0x3f); if (--follow == 0) { /* encoding a character in more bytes than is strictly needed, * is not really valid UTF-8; we are strict here to increase * the chance of heuristic dectection of non-UTF-8 text * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) */ if (string[index] < lowmark) utf8mode = 0; /* the code positions 0xd800--0xdfff and 0xfffe & 0xffff do not * exist in UCS-4 (and hence, they do not exist in Unicode) */ if (string[index] >= 0xd800 && string[index] <= 0xdfff || string[index] == 0xfffe || string[index] == 0xffff) utf8mode = 0; index++; } /* if */ } else if (follow == 0 && (c & 0x80) == 0x80) { /* UTF-8 leader code */ if ((c & 0xe0) == 0xc0) { /* 110xxxxx 10xxxxxx */ follow = 1; lowmark = 0x80; string[index] = c & 0x1f; } else if ((c & 0xf0) == 0xe0) { /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ follow = 2; lowmark = 0x800; string[index] = c & 0x0f; } else if ((c & 0xf8) == 0xf0) { /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ follow = 3; lowmark = 0x10000; string[index] = c & 0x07; } else if ((c & 0xfc) == 0xf8) { /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ follow = 4; lowmark = 0x200000; string[index] = c & 0x03; } else if ((c & 0xfe) == 0xfc) { /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ follow = 5; lowmark = 0x4000000; string[index] = c & 0x01; } else { /* this is invalid UTF-8 */ utf8mode = 0; } /* if */ } else if (follow == 0 && (c & 0x80) == 0x00) { /* 0xxxxxxx (US-ASCII) */ string[index++] = c; if (c == __T('\n')) break; /* read newline, done */ } else { /* this is invalid UTF-8 */ utf8mode = 0; } /* if */ if (!utf8mode) { /* UTF-8 mode was switched just off, which means that non-conforming * UTF-8 codes were found, which means in turn that the string is * probably not intended as UTF-8; start over again */ index = 0; fsetpos(fp, &pos); } /* if */ } else { string[index++] = c; if (c == __T('\n')) { break; /* read newline, done */ } else if (lastcr) { ungetc(c, fp); /* carriage return was read, no newline follows */ break; } /* if */ lastcr = (c == __T('\r')); } /* if */ } /* for */ assert(index < max); string[index] = __T('\0'); return index; } static size_t fputs_cell(FILE *fp, cell *string, int utf8mode) { size_t count = 0; assert(sizeof(cell) >= 4); assert(fp != NULL); assert(string != NULL); while (*string != 0) { if (utf8mode) { cell c = *string; if (c < 0x80) { /* 0xxxxxxx */ fputc((unsigned char) c, fp); } else if (c < 0x800) { /* 110xxxxx 10xxxxxx */ fputc((unsigned char) ((c >> 6) & 0x1f | 0xc0), fp); fputc((unsigned char) (c & 0x3f | 0x80), fp); } else if (c < 0x10000) { /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ fputc((unsigned char) ((c >> 12) & 0x0f | 0xe0), fp); fputc((unsigned char) ((c >> 6) & 0x3f | 0x80), fp); fputc((unsigned char) (c & 0x3f | 0x80), fp); } else if (c < 0x200000) { /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ fputc((unsigned char) ((c >> 18) & 0x07 | 0xf0), fp); fputc((unsigned char) ((c >> 12) & 0x3f | 0x80), fp); fputc((unsigned char) ((c >> 6) & 0x3f | 0x80), fp); fputc((unsigned char) (c & 0x3f | 0x80), fp); } else if (c < 0x4000000) { /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ fputc((unsigned char) ((c >> 24) & 0x03 | 0xf8), fp); fputc((unsigned char) ((c >> 18) & 0x3f | 0x80), fp); fputc((unsigned char) ((c >> 12) & 0x3f | 0x80), fp); fputc((unsigned char) ((c >> 6) & 0x3f | 0x80), fp); fputc((unsigned char) (c & 0x3f | 0x80), fp); } else { /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ fputc((unsigned char) ((c >> 30) & 0x01 | 0xfc), fp); fputc((unsigned char) ((c >> 24) & 0x3f | 0x80), fp); fputc((unsigned char) ((c >> 18) & 0x3f | 0x80), fp); fputc((unsigned char) ((c >> 12) & 0x3f | 0x80), fp); fputc((unsigned char) ((c >> 6) & 0x3f | 0x80), fp); fputc((unsigned char) (c & 0x3f | 0x80), fp); } /* if */ } else { /* not UTF-8 mode */ fputc((unsigned char) *string, fp); } /* if */ string++; count++; } /* while */ return count; } static size_t fgets_char(FILE *fp, char *string, size_t max) { size_t index; int c, lastcr; index = 0; lastcr = 0; for (; ;) { assert(index < max); if (index == max - 1) break; /* string fully filled */ if ((c = fgetc(fp)) == EOF) break; /* no more characters */ string[index++] = (char) c; if (c == __T('\n')) { break; /* read newline, done */ } else if (lastcr) { ungetc(c, fp); /* carriage return was read, no newline follows */ break; } /* if */ lastcr = (c == __T('\r')); } /* for */ assert(index < max); string[index] = __T('\0'); return index; } #if (defined __WIN32__ || defined _WIN32 || defined WIN32) && _MSC_VER < 1500 #if defined _UNICODE wchar_t *_wgetenv(wchar_t *name) { static wchar_t buffer[_MAX_PATH]; buffer[0]=L'\0'; GetEnvironmentVariable(name,buffer,sizearray(buffer)); return buffer[0]!=L'\0' ? buffer : NULL; } #else char *getenv(const char *name) { static char buffer[_MAX_PATH]; buffer[0]='\0'; GetEnvironmentVariable(name,buffer,sizearray(buffer)); return buffer[0]!='\0' ? buffer : NULL; } #endif #endif static char *completename(TCHAR *dest, TCHAR *src, size_t size) { #if defined AMXFILE_VAR TCHAR *prefix, *ptr; size_t len; /* only files below a specific path are accessible */ prefix = getenv(AMXFILE_VAR); /* if no specific path for files is present, use the "temporary" path */ if (prefix == NULL) prefix = getenv(__T("tmp")); /* common under Windows and Unix */ if (prefix == NULL) prefix = getenv(__T("temp")); /* common under Windows */ if (prefix == NULL) prefix = getenv(__T("tmpdir")); /* common under Unix */ /* if no path for files is defined, and no temporary directory exists, * fail the function; this is for security reasons. */ if (prefix == NULL) return NULL; if (_tcslen(prefix) + 1 >= size) /* +1 because directory separator is appended */ return NULL; _tcscpy(dest, prefix); /* append a directory separator (if not already present) */ len = _tcslen(dest); if (len == 0) return NULL; /* empty start directory is not allowed */ if (dest[len - 1] != __T(DIRSEP_CHAR) && dest[len - 1] != __T('/') && len + 1 < size) { dest[len] = __T(DIRSEP_CHAR); dest[len + 1] = __T('\0'); } /* if */ assert(_tcslen(dest) < size); /* for DOS/Windows and Unix/Linux, skip everyting up to a comma, because * this is used to indicate a protocol (e.g. file://C:/myfile.txt) */ #if DIRSEP_CHAR != ':' if ((ptr = _tcsrchr(src, __T(':'))) != NULL) { src = ptr + 1; /* skip protocol/drive and colon */ /* a "drive" specifier is sometimes ended with a vertical bar instead * of a colon in URL specifications */ if ((ptr = _tcschr(src, __T('|'))) != NULL) src = ptr + 1; /* skip drive and vertical bar */ while (src[0] == __T(DIRSEP_CHAR) || src[0] == __T('/')) src++; /* skip slashes behind the protocol/drive letter */ } /* if */ #endif /* skip an initial backslash or a drive specifier in the source */ if ((src[0] == __T(DIRSEP_CHAR) || src[0] == __T('/')) && (src[1] == __T(DIRSEP_CHAR) || src[1] == __T('/'))) { /* UNC path */ char separators[] = {__T(DIRSEP_CHAR), __T('/'), __T('\0')}; src += 2; ptr = _tcspbrk(src, separators); if (ptr != NULL) src = ptr + 1; } else if (src[0] == __T(DIRSEP_CHAR) || src[0] == __T('/')) { /* simple path starting from the root directory */ src++; } /* if */ /* disallow any "../" specifications in the source path * (the check below should be stricter, but directory names with * trailing periods are rare anyway) */ for (ptr = src; *ptr != __T('\0'); ptr++) if (ptr[0] == __T('.') && (ptr[1] == __T(DIRSEP_CHAR) || ptr[1] == __T('/'))) return NULL; /* path name is not allowed */ /* concatenate the drive letter to the destination path */ if (_tcslen(dest) + _tcslen(src) >= size) return NULL; _tcscat(dest, src); /* change forward slashes into proper directory separators */ #if DIRSEP_CHAR != '/' while ((ptr=_tcschr(dest,__T('/')))!=NULL) *ptr=__T(DIRSEP_CHAR); #endif return dest; #else if (_tcslen(src)>=size) return NULL; _tcscpy(dest,src); /* change forward slashes into proper directory separators */ #if DIRSEP_CHAR!='/' while ((ptr=_tcschr(dest,__T('/')))!=NULL) *ptr=__T(DIRSEP_CHAR); #endif return dest; #endif } /* File: fopen(const name[], filemode: mode) */ static cell AMX_NATIVE_CALL n_fopen(AMX *amx, const cell *params) { TCHAR *attrib, *altattrib; TCHAR *name, fullname[_MAX_PATH]; FILE *f = NULL; (void) amx; altattrib = NULL; switch (params[2] & 0x7fff) { case io_read: attrib = __T("rb"); break; case io_write: attrib = __T("wb"); break; case io_readwrite: attrib = __T("r+b"); altattrib = __T("w+b"); break; case io_append: attrib = __T("ab"); break; default: return 0; } /* switch */ /* get the filename */ amx_StrParam(amx, params[1], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { f = _tfopen(fullname, attrib); if (f == NULL && altattrib != NULL) f = _tfopen(fullname, altattrib); } /* if */ return (cell) f; } /* fclose(File: handle) */ static cell AMX_NATIVE_CALL n_fclose(AMX *amx, const cell *params) { (void) amx; return fclose((FILE *) params[1]) == 0; } /* fwrite(File: handle, const string[]) */ static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, const cell *params) { size_t r = 0; cell *cptr; char *str; int len; (void) amx; cptr = amx_Address(amx, params[2]); amx_StrLen(cptr, &len); if (len == 0) return 0; if ((ucell) *cptr > UNPACKEDMAX) { /* the string is packed, write it as an ASCII/ANSI string */ if ((str = (char *) alloca(len + 1)) != NULL) { amx_GetString(str, cptr, 0, len); r = fputs(str, (FILE *) params[1]); } /* if */ } else { /* the string is unpacked, write it as UTF-8 */ r = fputs_cell((FILE *) params[1], cptr, 1); } /* if */ return (cell) r; } /* fread(File: handle, string[], size=sizeof string, bool:pack=false) */ static cell AMX_NATIVE_CALL n_fread(AMX *amx, const cell *params) { size_t chars; int max; char *str; cell *cptr; max = (int) params[3]; if (max <= 0) return 0; if (params[4]) max *= sizeof(cell); cptr = amx_Address(amx, params[2]); str = (char *) alloca(max); if (str == NULL || cptr == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ if (params[4]) { /* store as packed string, read an ASCII/ANSI string */ chars = fgets_char((FILE *) params[1], str, max); assert(chars < max); amx_SetString(cptr, str, 1, 0, max); } else { /* store and unpacked string, interpret UTF-8 */ chars = fgets_cell((FILE *) params[1], cptr, max, 1); } /* if */ assert((int) chars < max); return (cell) chars; } /* fputchar(File: handle, value, bool:utf8 = true) */ static cell AMX_NATIVE_CALL n_fputchar(AMX *amx, const cell *params) { size_t result = 0; (void) amx; if (params[3]) { cell str[2]; str[0] = params[2]; str[1] = 0; result = fputs_cell((FILE *) params[1], str, 1); } else { fputc((int) params[2], (FILE *) params[1]); } /* if */ assert(result == 0 || result == 1); return (cell) result; } /* fgetchar(File: handle, bool:utf8 = true) */ static cell AMX_NATIVE_CALL n_fgetchar(AMX *amx, const cell *params) { cell str[2]; size_t result; (void) amx; if (params[2]) { result = fgets_cell((FILE *) params[1], str, 2, 1); } else { str[0] = fgetc((FILE *) params[1]); result = (str[0] != EOF); } /* if */ assert(result == 0 || result == 1); if (result == 0) return EOF; else return str[0]; } #if PAWN_CELL_SIZE == 16 #define aligncell amx_Align16 #elif PAWN_CELL_SIZE == 32 #define aligncell amx_Align32 #elif PAWN_CELL_SIZE == 64 && (defined _I64_MAX || defined HAVE_I64) #define aligncell amx_Align64 #else #error Unsupported cell size #endif /* fblockwrite(File: handle, buffer[], size=sizeof buffer) */ static cell AMX_NATIVE_CALL n_fblockwrite(AMX *amx, const cell *params) { cell *cptr; cell count = 0; (void) amx; cptr = amx_Address(amx, params[2]); if (cptr != NULL) { cell max = params[3]; ucell v; for (count = 0; count < max; count++) { v = (ucell) *cptr++; if (fwrite(aligncell(&v), sizeof(cell), 1, (FILE *) params[1]) != 1) break; /* write error */ } /* for */ } /* if */ return count; } /* fblockread(File: handle, buffer[], size=sizeof buffer) */ static cell AMX_NATIVE_CALL n_fblockread(AMX *amx, const cell *params) { cell *cptr; cell count = 0; (void) amx; cptr = amx_Address(amx, params[2]); if (cptr != NULL) { cell max = params[3]; ucell v; for (count = 0; count < max; count++) { if (fread(&v, sizeof(cell), 1, (FILE *) params[1]) != 1) break; /* write error */ *cptr++ = (cell) *aligncell(&v); } /* for */ } /* if */ return count; } /* File: ftemp() */ static cell AMX_NATIVE_CALL n_ftemp(AMX *amx, const cell *params) { (void) amx; (void) params; return (cell) tmpfile(); } /* fseek(File: handle, position, seek_whence: whence=seek_start) */ static cell AMX_NATIVE_CALL n_fseek(AMX *amx, const cell *params) { int whence; (void) amx; switch (params[3]) { case seek_start: whence = SEEK_SET; break; case seek_current: whence = SEEK_CUR; break; case seek_end: whence = SEEK_END; //if (params[2]>0) // params[2]=-params[2]; break; default: return 0; } /* switch */ return lseek(fileno((FILE *) params[1]), params[2], whence); } /* bool: fremove(const name[]) */ static cell AMX_NATIVE_CALL n_fremove(AMX *amx, const cell *params) { int r = 1; TCHAR *name, fullname[_MAX_PATH]; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { /* if this is a directory, try _trmdir() */ struct stat stbuf; _tstat(fullname, &stbuf); if (S_ISDIR(stbuf.st_mode)) r = _trmdir(fullname); else r = _tremove(fullname); } /* if */ return r == 0; } /* bool: fcopy(const source[], const target[]) */ static cell AMX_NATIVE_CALL n_fcopy(AMX *amx, const cell *params) { int r = 1; TCHAR *name, oldname[_MAX_PATH], newname[_MAX_PATH]; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(oldname, name, sizearray(oldname)) != NULL) { amx_StrParam(amx, params[2], name); if (name != NULL && completename(newname, name, sizearray(newname)) != NULL) { #if defined __WIN32__ r= CopyFile(oldname,newname,FALSE)==FALSE; #else TCHAR cmd[2 * _MAX_PATH + 10]; sprintf(cmd, "cp %s %s", oldname, newname); r = system(cmd) < 0; #endif } /* if */ } /* if */ return r == 0; } /* bool: frename(const oldname[], const newname[]) */ static cell AMX_NATIVE_CALL n_frename(AMX *amx, const cell *params) { int r = 1; TCHAR *name, oldname[_MAX_PATH], newname[_MAX_PATH]; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(oldname, name, sizearray(oldname)) != NULL) { amx_StrParam(amx, params[2], name); if (name != NULL && completename(newname, name, sizearray(newname)) != NULL) r = _trename(oldname, newname); } /* if */ return r == 0; } /* flength(File: handle) */ static cell AMX_NATIVE_CALL n_flength(AMX *amx, const cell *params) { long l, c; int fn = fileno((FILE *) params[1]); c = lseek(fn, 0, SEEK_CUR); /* save the current position */ l = lseek(fn, 0, SEEK_END); /* return the file position at its end */ lseek(fn, c, SEEK_SET); /* restore the file pointer */ (void) amx; return l; } static int match_optcopy(TCHAR *out, int outlen, const TCHAR *in, int skip) { if (out == NULL || skip != 0 || outlen <= 0) return 0; _tcsncpy(out, in, outlen); out[outlen - 1] = '\0'; return 1; } static int matchfiles(const TCHAR *path, int skip, TCHAR *out, int outlen) { int count = 0; const TCHAR *basename; #if DIRSEP_CHAR != '/' TCHAR *ptr; #endif #if defined __WIN32__ HANDLE hfind; WIN32_FIND_DATA fd; #else /* assume LINUX, FreeBSD, OpenBSD, or some other variant */ DIR *dir; struct dirent *entry; TCHAR dirname[_MAX_PATH]; #endif basename = _tcsrchr(path, DIRSEP_CHAR); basename = (basename == NULL) ? path : basename + 1; #if DIRSEP_CHAR != '/' ptr=_tcsrchr(basename,DIRSEP_CHAR); basename=(ptr==NULL) ? basename : ptr+1; #endif #if defined __WIN32__ if ((hfind=FindFirstFile(path,&fd))!=INVALID_HANDLE_VALUE) { do { if (fpattern_match(basename,fd.cFileName,-1,FALSE)) { count++; if (match_optcopy(out,outlen,fd.cFileName,skip--)) break; } /* if */ } while (FindNextFile(hfind,&fd)); FindClose(hfind); } /* if */ #else /* copy directory part only (zero-terminate) */ if (basename == path) { _tcscpy(dirname, "."); } else { _tcsncpy(dirname, path, (int) (basename - path)); dirname[(int) (basename - path)] = __T('\0'); } /* if */ if ((dir = opendir(dirname)) != NULL) { while ((entry = readdir(dir)) != NULL) { if (fpattern_match(basename, entry->d_name, -1, TRUE)) { count++; if (match_optcopy(out, outlen, entry->d_name, skip--)) break; } /* if */ } /* while */ closedir(dir); } /* if */ #endif return count; } /* fexist(const pattern[]) */ static cell AMX_NATIVE_CALL n_fexist(AMX *amx, const cell *params) { int r = 0; TCHAR *name, fullname[_MAX_PATH]; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) r = matchfiles(fullname, 0, NULL, 0); return r; } /* bool: fmatch(filename[], const pattern[], index=0, maxlength=sizeof filename) */ static cell AMX_NATIVE_CALL n_fmatch(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; cell *cptr; (void) amx; amx_StrParam(amx, params[2], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { if (!matchfiles(fullname, params[3], fullname, sizearray(fullname))) { fullname[0] = '\0'; } else { /* copy the string into the destination */ cptr = amx_Address(amx, params[1]); amx_SetString(cptr, fullname, 1, 0, params[4]); } /* if */ } /* if */ return fullname[0] != '\0'; } /* bool: fstat(const name[], &size = 0, ×tamp = 0, &attrib = 0, &inode = 0) */ static cell AMX_NATIVE_CALL n_fstat(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; cell *cptr; int result = 0; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { struct stat stbuf; if (_tstat(name, &stbuf) == 0) { cptr = amx_Address(amx, params[2]); *cptr = stbuf.st_size; cptr = amx_Address(amx, params[3]); *cptr = (cell) stbuf.st_mtime; cptr = amx_Address(amx, params[4]); *cptr = stbuf.st_mode; /* mode/protection bits */ cptr = amx_Address(amx, params[5]); *cptr = stbuf.st_ino; /* inode number, unique id for a file */ result = 1; } /* if */ } /* if */ return result; } /* bool: fattrib(const name[], timestamp=0, attrib=0x0f) */ static cell AMX_NATIVE_CALL n_fattrib(AMX *amx, const cell *params) { #if !(defined __WIN32__ || defined _WIN32 || defined WIN32) #define _utime(n, t) utime(n,t) #define _utimbuf utimbuf #endif TCHAR *name, fullname[_MAX_PATH] = ""; int result = 0; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { result = 1; if (params[2] != 0) { struct _utimbuf times; times.actime = (unsigned long) params[2]; times.modtime = (unsigned long) params[2]; result = result && (_tutime(name, ×) == 0); } /* if */ if (params[3] != 0x0f) result = result && (_tchmod(name, (int) params[3]) == 0); } /* if */ return result; } /* bool: fcreatedir(const name[]) */ static cell AMX_NATIVE_CALL n_fcreatedir(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; int r = 1; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { #if defined __WIN32__ || defined __DOS__ r=_tmkdir(fullname); #else r = _tmkdir(fullname, 0755); #endif } /* if */ return r == 0; } /* CRC32 functions are adapted from source code from www.networkdls.com * The table generation routines are replaced by a hard-coded table, which * can be stored in Flash ROM. */ const unsigned long ulCRCTable[256] = // CRC Lookup Table Array { 0x00000000Lu, 0x77073096Lu, 0xee0e612cLu, 0x990951baLu, 0x076dc419Lu, 0x706af48fLu, 0xe963a535Lu, 0x9e6495a3Lu, 0x0edb8832Lu, 0x79dcb8a4Lu, 0xe0d5e91eLu, 0x97d2d988Lu, 0x09b64c2bLu, 0x7eb17cbdLu, 0xe7b82d07Lu, 0x90bf1d91Lu, 0x1db71064Lu, 0x6ab020f2Lu, 0xf3b97148Lu, 0x84be41deLu, 0x1adad47dLu, 0x6ddde4ebLu, 0xf4d4b551Lu, 0x83d385c7Lu, 0x136c9856Lu, 0x646ba8c0Lu, 0xfd62f97aLu, 0x8a65c9ecLu, 0x14015c4fLu, 0x63066cd9Lu, 0xfa0f3d63Lu, 0x8d080df5Lu, 0x3b6e20c8Lu, 0x4c69105eLu, 0xd56041e4Lu, 0xa2677172Lu, 0x3c03e4d1Lu, 0x4b04d447Lu, 0xd20d85fdLu, 0xa50ab56bLu, 0x35b5a8faLu, 0x42b2986cLu, 0xdbbbc9d6Lu, 0xacbcf940Lu, 0x32d86ce3Lu, 0x45df5c75Lu, 0xdcd60dcfLu, 0xabd13d59Lu, 0x26d930acLu, 0x51de003aLu, 0xc8d75180Lu, 0xbfd06116Lu, 0x21b4f4b5Lu, 0x56b3c423Lu, 0xcfba9599Lu, 0xb8bda50fLu, 0x2802b89eLu, 0x5f058808Lu, 0xc60cd9b2Lu, 0xb10be924Lu, 0x2f6f7c87Lu, 0x58684c11Lu, 0xc1611dabLu, 0xb6662d3dLu, 0x76dc4190Lu, 0x01db7106Lu, 0x98d220bcLu, 0xefd5102aLu, 0x71b18589Lu, 0x06b6b51fLu, 0x9fbfe4a5Lu, 0xe8b8d433Lu, 0x7807c9a2Lu, 0x0f00f934Lu, 0x9609a88eLu, 0xe10e9818Lu, 0x7f6a0dbbLu, 0x086d3d2dLu, 0x91646c97Lu, 0xe6635c01Lu, 0x6b6b51f4Lu, 0x1c6c6162Lu, 0x856530d8Lu, 0xf262004eLu, 0x6c0695edLu, 0x1b01a57bLu, 0x8208f4c1Lu, 0xf50fc457Lu, 0x65b0d9c6Lu, 0x12b7e950Lu, 0x8bbeb8eaLu, 0xfcb9887cLu, 0x62dd1ddfLu, 0x15da2d49Lu, 0x8cd37cf3Lu, 0xfbd44c65Lu, 0x4db26158Lu, 0x3ab551ceLu, 0xa3bc0074Lu, 0xd4bb30e2Lu, 0x4adfa541Lu, 0x3dd895d7Lu, 0xa4d1c46dLu, 0xd3d6f4fbLu, 0x4369e96aLu, 0x346ed9fcLu, 0xad678846Lu, 0xda60b8d0Lu, 0x44042d73Lu, 0x33031de5Lu, 0xaa0a4c5fLu, 0xdd0d7cc9Lu, 0x5005713cLu, 0x270241aaLu, 0xbe0b1010Lu, 0xc90c2086Lu, 0x5768b525Lu, 0x206f85b3Lu, 0xb966d409Lu, 0xce61e49fLu, 0x5edef90eLu, 0x29d9c998Lu, 0xb0d09822Lu, 0xc7d7a8b4Lu, 0x59b33d17Lu, 0x2eb40d81Lu, 0xb7bd5c3bLu, 0xc0ba6cadLu, 0xedb88320Lu, 0x9abfb3b6Lu, 0x03b6e20cLu, 0x74b1d29aLu, 0xead54739Lu, 0x9dd277afLu, 0x04db2615Lu, 0x73dc1683Lu, 0xe3630b12Lu, 0x94643b84Lu, 0x0d6d6a3eLu, 0x7a6a5aa8Lu, 0xe40ecf0bLu, 0x9309ff9dLu, 0x0a00ae27Lu, 0x7d079eb1Lu, 0xf00f9344Lu, 0x8708a3d2Lu, 0x1e01f268Lu, 0x6906c2feLu, 0xf762575dLu, 0x806567cbLu, 0x196c3671Lu, 0x6e6b06e7Lu, 0xfed41b76Lu, 0x89d32be0Lu, 0x10da7a5aLu, 0x67dd4accLu, 0xf9b9df6fLu, 0x8ebeeff9Lu, 0x17b7be43Lu, 0x60b08ed5Lu, 0xd6d6a3e8Lu, 0xa1d1937eLu, 0x38d8c2c4Lu, 0x4fdff252Lu, 0xd1bb67f1Lu, 0xa6bc5767Lu, 0x3fb506ddLu, 0x48b2364bLu, 0xd80d2bdaLu, 0xaf0a1b4cLu, 0x36034af6Lu, 0x41047a60Lu, 0xdf60efc3Lu, 0xa867df55Lu, 0x316e8eefLu, 0x4669be79Lu, 0xcb61b38cLu, 0xbc66831aLu, 0x256fd2a0Lu, 0x5268e236Lu, 0xcc0c7795Lu, 0xbb0b4703Lu, 0x220216b9Lu, 0x5505262fLu, 0xc5ba3bbeLu, 0xb2bd0b28Lu, 0x2bb45a92Lu, 0x5cb36a04Lu, 0xc2d7ffa7Lu, 0xb5d0cf31Lu, 0x2cd99e8bLu, 0x5bdeae1dLu, 0x9b64c2b0Lu, 0xec63f226Lu, 0x756aa39cLu, 0x026d930aLu, 0x9c0906a9Lu, 0xeb0e363fLu, 0x72076785Lu, 0x05005713Lu, 0x95bf4a82Lu, 0xe2b87a14Lu, 0x7bb12baeLu, 0x0cb61b38Lu, 0x92d28e9bLu, 0xe5d5be0dLu, 0x7cdcefb7Lu, 0x0bdbdf21Lu, 0x86d3d2d4Lu, 0xf1d4e242Lu, 0x68ddb3f8Lu, 0x1fda836eLu, 0x81be16cdLu, 0xf6b9265bLu, 0x6fb077e1Lu, 0x18b74777Lu, 0x88085ae6Lu, 0xff0f6a70Lu, 0x66063bcaLu, 0x11010b5cLu, 0x8f659effLu, 0xf862ae69Lu, 0x616bffd3Lu, 0x166ccf45Lu, 0xa00ae278Lu, 0xd70dd2eeLu, 0x4e048354Lu, 0x3903b3c2Lu, 0xa7672661Lu, 0xd06016f7Lu, 0x4969474dLu, 0x3e6e77dbLu, 0xaed16a4aLu, 0xd9d65adcLu, 0x40df0b66Lu, 0x37d83bf0Lu, 0xa9bcae53Lu, 0xdebb9ec5Lu, 0x47b2cf7fLu, 0x30b5ffe9Lu, 0xbdbdf21cLu, 0xcabac28aLu, 0x53b39330Lu, 0x24b4a3a6Lu, 0xbad03605Lu, 0xcdd70693Lu, 0x54de5729Lu, 0x23d967bfLu, 0xb3667a2eLu, 0xc4614ab8Lu, 0x5d681b02Lu, 0x2a6f2b94Lu, 0xb40bbe37Lu, 0xc30c8ea1Lu, 0x5a05df1bLu, 0x2d02ef8dLu }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // This function uses the ulCRCTable lookup table to generate a CRC for xData static unsigned long PartialCRC(unsigned long ulCRC, unsigned char *sBuf, unsigned long lBufSz) { while (lBufSz--) ulCRC = (ulCRC >> 8) ^ ulCRCTable[(ulCRC & 0xFF) ^ *sBuf++]; return ulCRC; } /* filecrc(const name[]) */ static cell AMX_NATIVE_CALL n_filecrc(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; FILE *fp; unsigned char buffer[256]; unsigned long ulCRC = 0xffffffff; size_t numread; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL && (fp = _tfopen(fullname, "rb")) != NULL) { do { numread = fread(buffer, sizeof(unsigned char), sizeof buffer, fp); ulCRC = PartialCRC(ulCRC, buffer, (unsigned long) numread); } while (numread == sizeof buffer); fclose(fp); } /* if */ return (ulCRC ^ 0xffffffff); } const TCHAR default_ini_name[] = "config.ini"; /* readcfg(const filename[]="", const section[]="", const key[], value[], size=sizeof value, const defvalue[]="", bool:packed=false) */ static cell AMX_NATIVE_CALL n_readcfg(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; TCHAR *section, *key, *defvalue; TCHAR *buffer; int size, result = 0; cell *cptr; size = (int) params[5]; if (size <= 0) return 0; if (params[7]) size *= sizeof(cell); amx_StrParam(amx, params[1], name); if (name != NULL && *name == '\0') name = (TCHAR *) default_ini_name; if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { amx_StrParam(amx, params[2], section); amx_StrParam(amx, params[3], key); amx_StrParam(amx, params[6], defvalue); cptr = amx_Address(amx, params[4]); buffer = (char *) alloca(size); if (buffer == NULL || cptr == NULL) { amx_RaiseError(amx, AMX_ERR_NATIVE); return 0; } /* if */ result = ini_gets(section, key, defvalue, buffer, size, fullname); amx_SetString(cptr, buffer, params[7], 0, size); } /* if */ return result; } /* readcfgvalue(const filename[]="", const section[]="", const key[], defvalue=0) */ static cell AMX_NATIVE_CALL n_readcfgvalue(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; TCHAR *section, *key; long result = 0; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && *name == '\0') name = (TCHAR *) default_ini_name; if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { amx_StrParam(amx, params[2], section); amx_StrParam(amx, params[3], key); result = ini_getl(section, key, (long) params[4], fullname); } /* if */ return result; } /* writecfg(const filename[]="", const section[]="", const key[], const value[]) */ static cell AMX_NATIVE_CALL n_writecfg(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; TCHAR *section, *key, *value; int result = 0; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && *name == '\0') name = (TCHAR *) default_ini_name; if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { amx_StrParam(amx, params[2], section); amx_StrParam(amx, params[3], key); amx_StrParam(amx, params[4], value); result = ini_puts(section, key, value, fullname); } /* if */ return result; } /* writecfgvalue(const filename[]="", const section[]="", const key[], value) */ static cell AMX_NATIVE_CALL n_writecfgvalue(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; TCHAR *section, *key; int result = 0; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && *name == '\0') name = (TCHAR *) default_ini_name; if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { amx_StrParam(amx, params[2], section); amx_StrParam(amx, params[3], key); result = ini_putl(section, key, (long) params[4], fullname); } /* if */ return result; } /* deletecfg(const filename[]="", const section[]="", const key[]="") */ static cell AMX_NATIVE_CALL n_deletecfg(AMX *amx, const cell *params) { TCHAR *name, fullname[_MAX_PATH] = ""; TCHAR *section, *key; int result = 0; (void) amx; amx_StrParam(amx, params[1], name); if (name != NULL && *name == '\0') name = (TCHAR *) default_ini_name; if (name != NULL && completename(fullname, name, sizearray(fullname)) != NULL) { amx_StrParam(amx, params[2], section); if (*section == '\0') section = NULL; amx_StrParam(amx, params[3], key); if (*key == '\0') key = NULL; result = ini_puts(section, key, NULL, fullname); } /* if */ return result; } #if defined __cplusplus extern "C" #endif AMX_NATIVE_INFO file_Natives[] = { {"fopen", n_fopen}, {"fclose", n_fclose}, {"fwrite", n_fwrite}, {"fread", n_fread}, {"fputchar", n_fputchar}, {"fgetchar", n_fgetchar}, {"fblockwrite", n_fblockwrite}, {"fblockread", n_fblockread}, {"ftemp", n_ftemp}, {"fseek", n_fseek}, {"flength", n_flength}, {"fremove", n_fremove}, {"fcopy", n_fcopy}, {"frename", n_frename}, {"fexist", n_fexist}, {"fmatch", n_fmatch}, {"fstat", n_fstat}, {"fattrib", n_fattrib}, {"filecrc", n_filecrc}, {"fcreatedir", n_fcreatedir}, {"readcfg", n_readcfg}, {"readcfgvalue", n_readcfgvalue}, {"writecfg", n_writecfg}, {"writecfgvalue", n_writecfgvalue}, {"deletecfg", n_deletecfg}, {NULL, NULL} /* terminator */ }; int AMXEXPORT AMXAPI amx_FileInit(AMX *amx) { return amx_Register(amx, file_Natives, -1); } int AMXEXPORT AMXAPI amx_FileCleanup(AMX *amx) { (void) amx; return AMX_ERR_NONE; }