1. /* Text file I/O module for the Pawn Abstract Machine
  2.  *
  3.  *  Copyright (c) ITB CompuPhase, 2003-2005
  4.  *
  5.  *  This software is provided "as-is", without any express or implied warranty.
  6.  *  In no event will the authors be held liable for any damages arising from
  7.  *  the use of this software.
  8.  *
  9.  *  Permission is granted to anyone to use this software for any purpose,
  10.  *  including commercial applications, and to alter it and redistribute it
  11.  *  freely, subject to the following restrictions:
  12.  *
  13.  *  1.  The origin of this software must not be misrepresented; you must not
  14.  *      claim that you wrote the original software. If you use this software in
  15.  *      a product, an acknowledgment in the product documentation would be
  16.  *      appreciated but is not required.
  17.  *  2.  Altered source versions must be plainly marked as such, and must not be
  18.  *      misrepresented as being the original software.
  19.  *  3.  This notice may not be removed or altered from any source distribution.
  20.  *
  21.  *  Version: $Id: amxfile.c,v 1.7 2006/04/19 12:00:58 spookie Exp $
  22.  */
  23. #if defined _UNICODE || defined __UNICODE__ || defined UNICODE
  24. # if !defined UNICODE   /* for Windows */
  25. #   define UNICODE
  26. # endif
  27. # if !defined _UNICODE  /* for C library */
  28. #   define _UNICODE
  29. # endif
  30. #endif
  31.  
  32. #include <limits.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <assert.h>
  37. #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__
  38.   #include <io.h>
  39.   #include <malloc.h>
  40. #endif
  41. #if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows
  42.   #include <windows.h>
  43. #endif
  44. #include "osdefs.h"
  45. #include "amx.h"
  46.  
  47. #if !defined AMXFILE_VAR
  48.   #define AMXFILE_VAR   "AMXFILE"
  49. #elif AMXFILE_VAR==""
  50.   #undef AMXFILE_VAR
  51. #endif
  52.  
  53. #if defined _UNICODE
  54. # include <tchar.h>
  55. #elif !defined __T
  56.   typedef char          TCHAR;
  57. # define __T(string)    string
  58. # define _tfopen        fopen
  59. # define _tgetenv       getenv
  60. # define _tfputs        fputs
  61. # define _tcscat        strcat
  62. # define _tcschr        strchr
  63. # define _tcscpy        strcpy
  64. # define _tcsdup        strdup
  65. # define _tcslen        strlen
  66. # define _tcspbrk       strpbrk
  67. # define _tcsrchr       strrchr
  68. #endif
  69.  
  70. #if !defined UNUSED_PARAM
  71.   #define UNUSED_PARAM(p) ((void)(p))
  72. #endif
  73.  
  74. enum filemode {
  75.   io_read,      /* file must exist */
  76.   io_write,     /* creates a new file */
  77.   io_readwrite, /* file must exist */
  78.   io_append,    /* file must exist, opened for writing only and seek to the end */
  79. };
  80.  
  81. enum seek_whence {
  82.   seek_start,
  83.   seek_current,
  84.   seek_end,
  85. };
  86.  
  87.  
  88. /* This function only stores unpacked strings. UTF-8 is used for
  89.  * Unicode, and packed strings can only store 7-bit and 8-bit
  90.  * character sets (ASCII, Latin-1).
  91.  */
  92. static size_t fgets_cell(FILE *fp,cell *string,size_t max,int utf8mode)
  93. {
  94.   size_t index;
  95.   fpos_t pos;
  96.   cell c;
  97.   int follow,lastcr;
  98.   cell lowmark;
  99.  
  100.   assert(sizeof(cell)>=4);
  101.   assert(fp!=NULL);
  102.   assert(string!=NULL);
  103.   if (max<=0)
  104.     return 0;
  105.  
  106.   /* get the position, in case we have to back up */
  107.   fgetpos(fp, &pos);
  108.  
  109.   index=0;
  110.   follow=0;
  111.   lowmark=0;
  112.   lastcr=0;
  113.   for ( ;; ) {
  114.     assert(index<max);
  115.     if (index==max-1)
  116.       break;                    /* string fully filled */
  117.     if ((c=fgetc(fp))==EOF) {
  118.       if (!utf8mode || follow==0)
  119.         break;                  /* no more characters */
  120.       /* If an EOF happened halfway an UTF-8 code, the string cannot be
  121.        * UTF-8 mode, and we must restart.
  122.        */
  123.       index=0;
  124.       fsetpos(fp, &pos);
  125.       continue;
  126.     } /* if */
  127.  
  128.     /* 8-bit characters are unsigned */
  129.     if (c<0)
  130.       c=-c;
  131.  
  132.     if (utf8mode) {
  133.       if (follow>0 && (c & 0xc0)==0x80) {
  134.         /* leader code is active, combine with earlier code */
  135.         string[index]=(string[index] << 6) | ((unsigned char)c & 0x3f);
  136.         if (--follow==0) {
  137.           /* encoding a character in more bytes than is strictly needed,
  138.            * is not really valid UTF-8; we are strict here to increase
  139.            * the chance of heuristic dectection of non-UTF-8 text
  140.            * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid)
  141.            */
  142.           if (string[index]<lowmark)
  143.             utf8mode=0;
  144.           /* the code positions 0xd800--0xdfff and 0xfffe & 0xffff do not
  145.            * exist in UCS-4 (and hence, they do not exist in Unicode)
  146.            */
  147.           if (string[index]>=0xd800 && string[index]<=0xdfff
  148.               || string[index]==0xfffe || string[index]==0xffff)
  149.             utf8mode=0;
  150.           index++;
  151.         } /* if */
  152.       } else if (follow==0 && (c & 0x80)==0x80) {
  153.         /* UTF-8 leader code */
  154.         if ((c & 0xe0)==0xc0) {
  155.           /* 110xxxxx 10xxxxxx */
  156.           follow=1;
  157.           lowmark=0x80;
  158.           string[index]=c & 0x1f;
  159.         } else if ((c & 0xf0)==0xe0) {
  160.           /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */
  161.           follow=2;
  162.           lowmark=0x800;
  163.           string[index]=c & 0x0f;
  164.         } else if ((c & 0xf8)==0xf0) {
  165.           /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
  166.           follow=3;
  167.           lowmark=0x10000;
  168.           string[index]=c & 0x07;
  169.         } else if ((c & 0xfc)==0xf8) {
  170.           /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
  171.           follow=4;
  172.           lowmark=0x200000;
  173.           string[index]=c & 0x03;
  174.         } else if ((c & 0xfe)==0xfc) {
  175.           /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */
  176.           follow=5;
  177.           lowmark=0x4000000;
  178.           string[index]=c & 0x01;
  179.         } else {
  180.           /* this is invalid UTF-8 */
  181.           utf8mode=0;
  182.         } /* if */
  183.       } else if (follow==0 && (c & 0x80)==0x00) {
  184.         /* 0xxxxxxx (US-ASCII) */
  185.         string[index++]=c;
  186.         if (c==__T('\n'))
  187.           break;        /* read newline, done */
  188.       } else {
  189.         /* this is invalid UTF-8 */
  190.         utf8mode=0;
  191.       } /* if */
  192.       if (!utf8mode) {
  193.         /* UTF-8 mode was switched just off, which means that non-conforming
  194.          * UTF-8 codes were found, which means in turn that the string is
  195.          * probably not intended as UTF-8; start over again
  196.          */
  197.         index=0;
  198.         fsetpos(fp, &pos);
  199.       } /* if */
  200.     } else {
  201.       string[index++]=c;
  202.       if (c==__T('\n')) {
  203.         break;                  /* read newline, done */
  204.       } else if (lastcr) {
  205.         ungetc(c,fp);           /* carriage return was read, no newline follows */
  206.         break;
  207.       } /* if */
  208.       lastcr=(c==__T('\r'));
  209.     } /* if */
  210.   } /* for */
  211.   assert(index<max);
  212.   string[index]=__T('\0');
  213.  
  214.   return index;
  215. }
  216.  
  217. static size_t fputs_cell(FILE *fp,cell *string,int utf8mode)
  218. {
  219.   size_t count=0;
  220.  
  221.   assert(sizeof(cell)>=4);
  222.   assert(fp!=NULL);
  223.   assert(string!=NULL);
  224.  
  225.   while (*string!=0) {
  226.     if (utf8mode) {
  227.       cell c=*string;
  228.       if (c<0x80) {
  229.         /* 0xxxxxxx */
  230.         fputc((unsigned char)c,fp);
  231.       } else if (c<0x800) {
  232.         /* 110xxxxx 10xxxxxx */
  233.         fputc((unsigned char)((c>>6) & 0x1f | 0xc0),fp);
  234.         fputc((unsigned char)(c & 0x3f | 0x80),fp);
  235.       } else if (c<0x10000) {
  236.         /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */
  237.         fputc((unsigned char)((c>>12) & 0x0f | 0xe0),fp);
  238.         fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
  239.         fputc((unsigned char)(c & 0x3f | 0x80),fp);
  240.       } else if (c<0x200000) {
  241.         /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
  242.         fputc((unsigned char)((c>>18) & 0x07 | 0xf0),fp);
  243.         fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp);
  244.         fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
  245.         fputc((unsigned char)(c & 0x3f | 0x80),fp);
  246.       } else if (c<0x4000000) {
  247.         /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
  248.         fputc((unsigned char)((c>>24) & 0x03 | 0xf8),fp);
  249.         fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp);
  250.         fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp);
  251.         fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
  252.         fputc((unsigned char)(c & 0x3f | 0x80),fp);
  253.       } else {
  254.         /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */
  255.         fputc((unsigned char)((c>>30) & 0x01 | 0xfc),fp);
  256.         fputc((unsigned char)((c>>24) & 0x3f | 0x80),fp);
  257.         fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp);
  258.         fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp);
  259.         fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp);
  260.         fputc((unsigned char)(c & 0x3f | 0x80),fp);
  261.       } /* if */
  262.     } else {
  263.       /* not UTF-8 mode */
  264.       fputc((unsigned char)*string,fp);
  265.     } /* if */
  266.     string++;
  267.     count++;
  268.   } /* while */
  269.   return count;
  270. }
  271.  
  272. static size_t fgets_char(FILE *fp, char *string, size_t max)
  273. {
  274.   size_t index;
  275.   int c,lastcr;
  276.  
  277.   index=0;
  278.   lastcr=0;
  279.   for ( ;; ) {
  280.     assert(index<max);
  281.     if (index==max-1)
  282.       break;                    /* string fully filled */
  283.     if ((c=fgetc(fp))==EOF)
  284.       break;                    /* no more characters */
  285.     string[index++]=(char)c;
  286.     if (c==__T('\n')) {
  287.       break;                    /* read newline, done */
  288.     } else if (lastcr) {
  289.       ungetc(c,fp);             /* carriage return was read, no newline follows */
  290.       break;
  291.     } /* if */
  292.     lastcr=(c==__T('\r'));
  293.   } /* for */
  294.   assert(index<max);
  295.   string[index]=__T('\0');
  296.  
  297.   return index;
  298. }
  299.  
  300. #if defined __WIN32__ || defined _WIN32 || defined WIN32
  301. #if defined _UNICODE
  302. wchar_t *_wgetenv(wchar_t *name)
  303. {
  304. static wchar_t buffer[_MAX_PATH];
  305.   buffer[0]=L'\0';
  306.   GetEnvironmentVariable(name,buffer,sizeof buffer/sizeof(wchar_t));
  307.   return buffer[0]!=L'\0' ? buffer : NULL;
  308. }
  309. #else
  310. char *amx_getenv(const char *name)
  311. {
  312. static char buffer[_MAX_PATH];
  313.   buffer[0]='\0';
  314.   GetEnvironmentVariable(name,buffer,sizeof buffer);
  315.   return buffer[0]!='\0' ? buffer : NULL;
  316. }
  317. #endif
  318. #else
  319.     #define amx_getenv getenv
  320. #endif
  321.  
  322. static char *completename(TCHAR *dest, TCHAR *src, size_t size)
  323. {
  324.   #if defined AMXFILE_VAR
  325.     TCHAR *prefix,*ptr;
  326.     size_t len;
  327.  
  328.    
  329.     /* only files below a specific path are accessible */
  330.     prefix=amx_getenv(AMXFILE_VAR);
  331.  
  332.     /* if no specific path for files is present, use the "temporary" path */
  333.     if (prefix==NULL)
  334.       prefix=amx_getenv(__T("tmp"));    /* common under Windows and Unix */
  335.     if (prefix==NULL)
  336.       prefix=amx_getenv(__T("temp"));   /* common under Windows */
  337.     if (prefix==NULL)
  338.       prefix=amx_getenv(__T("tmpdir")); /* common under Unix */
  339.  
  340.     /* if no path for files is defined, and no temporary directory exists,
  341.      * fail the function; this is for security reasons.
  342.      */
  343.     if (prefix==NULL)
  344.       return NULL;
  345.  
  346.     if (_tcslen(prefix)+1>=size) /* +1 because directory separator is appended */
  347.       return NULL;
  348.     _tcscpy(dest,prefix);
  349.     /* append a directory separator (if not already present) */
  350.     len=_tcslen(dest);
  351.     if (len==0)
  352.       return NULL;              /* empty start directory is not allowed */
  353.     if (dest[len-1]!=__T(DIRSEP_CHAR) && dest[len-1]!=__T('/') && len+1<size) {
  354.       dest[len]=__T(DIRSEP_CHAR);
  355.       dest[len+1]=__T('\0');
  356.     } /* if */
  357.     assert(_tcslen(dest)<size);
  358.  
  359.     /* for DOS/Windows and Unix/Linux, skip everyting up to a comma, because
  360.      * this is used to indicate a protocol (e.g. file://C:/myfile.txt)
  361.      */
  362.     #if DIRSEP_CHAR!=':'
  363.       if ((ptr=_tcsrchr(src,__T(':')))!=NULL) {
  364.         src=ptr+1;              /* skip protocol/drive and colon */
  365.         /* a "drive" specifier is sometimes ended with a vertical bar instead
  366.          * of a colon in URL specifications
  367.          */
  368.         if ((ptr=_tcschr(src,__T('|')))!=NULL)
  369.           src=ptr+1;            /* skip drive and vertical bar */
  370.         while (src[0]==__T(DIRSEP_CHAR) || src[0]==__T('/'))
  371.           src++;                /* skip slashes behind the protocol/drive letter */
  372.       } /* if */
  373.     #endif
  374.  
  375.     /* skip an initial backslash or a drive specifier in the source */
  376.     if ((src[0]==__T(DIRSEP_CHAR) || src[0]==__T('/')) && (src[1]==__T(DIRSEP_CHAR) || src[1]==__T('/'))) {
  377.       /* UNC path */
  378.       char separators[]={__T(DIRSEP_CHAR),__T('/'),__T('\0')};
  379.       src+=2;
  380.       ptr=_tcspbrk(src,separators);
  381.       if (ptr!=NULL)
  382.         src=ptr+1;
  383.     } else if (src[0]==__T(DIRSEP_CHAR) || src[0]==__T('/')) {
  384.       /* simple path starting from the root directory */
  385.       src++;
  386.     } /* if */
  387.  
  388.     /* disallow any "../" specifications in the source path
  389.      * (the check below should be stricter, but directory names with
  390.      * trailing periods are rare anyway)
  391.      */
  392.     for (ptr=src; *ptr!=__T('\0'); ptr++)
  393.       if (ptr[0]==__T('.') && (ptr[1]==__T(DIRSEP_CHAR) || ptr[1]==__T('/')))
  394.         return NULL;            /* path name is not allowed */
  395.  
  396.     /* concatenate the drive letter to the destination path */
  397.     if (_tcslen(dest)+_tcslen(src)>=size)
  398.       return NULL;
  399.     _tcscat(dest,src);
  400.  
  401.     /* change forward slashes into proper directory separators */
  402.     #if DIRSEP_CHAR!='/'
  403.       while ((ptr=_tcschr(dest,__T('/')))!=NULL)
  404.         *ptr=__T(DIRSEP_CHAR);
  405.     #endif
  406.     return dest;
  407.  
  408.   #else
  409.     if (_tcslen(src)>=size)
  410.       return NULL;
  411.     _tcscpy(dest,src);
  412.     /* change forward slashes into proper directory separators */
  413.     #if DIRSEP_CHAR!='/'
  414.       while ((ptr=_tcschr(dest,__T('/')))!=NULL)
  415.         *ptr=__T(DIRSEP_CHAR);
  416.     #endif
  417.     return dest;
  418.   #endif
  419. }
  420.  
  421. /* File: fopen(const name[], filemode: mode) */
  422. static cell AMX_NATIVE_CALL n_fopen(AMX *amx, cell *params)
  423. {
  424.   TCHAR *attrib,*altattrib;
  425.   TCHAR *name,fullname[_MAX_PATH];
  426.   FILE *f = NULL;
  427.  
  428.   altattrib=NULL;
  429.   switch (params[2] & 0x7fff) {
  430.   case io_read:
  431.     attrib=__T("rb");
  432.     break;
  433.   case io_write:
  434.     attrib=__T("wb");
  435.     break;
  436.   case io_readwrite:
  437.     attrib=__T("r+b");
  438.     altattrib=__T("w+b");
  439.     break;
  440.   case io_append:
  441.     attrib=__T("ab");
  442.     break;
  443.   default:
  444.     return 0;
  445.   } /* switch */
  446.  
  447.   /* get the filename */
  448.   amx_StrParam(amx,params[1],name);
  449.   if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL) {
  450.     f=_tfopen(fullname,attrib);
  451.     if (f==NULL && altattrib!=NULL)
  452.       f=_tfopen(fullname,altattrib);
  453.   } /* if */
  454.   return (cell)f;
  455. }
  456.  
  457. /* fclose(File: handle) */
  458. static cell AMX_NATIVE_CALL n_fclose(AMX *amx, cell *params)
  459. {
  460.   UNUSED_PARAM(amx);
  461.   return fclose((FILE*)params[1]) == 0;
  462. }
  463.  
  464. /* fwrite(File: handle, const string[]) */
  465. static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, cell *params)
  466. {
  467.   int r = 0;
  468.   cell *cptr;
  469.   char *str;
  470.   int len;
  471.  
  472.   amx_GetAddr(amx,params[2],&cptr);
  473.   amx_StrLen(cptr,&len);
  474.   if (len==0)
  475.     return 0;
  476.  
  477.   if ((ucell)*cptr>UNPACKEDMAX) {
  478.     /* the string is packed, write it as an ASCII/ANSI string */
  479.     if ((str=(char*)alloca(len + 1))!=NULL)
  480.       r=_tfputs(str,(FILE*)params[1]);
  481.   } else {
  482.     /* the string is unpacked, write it as UTF-8 */
  483.     r=fputs_cell((FILE*)params[1],cptr,1);
  484.   } /* if */
  485.   return r;
  486. }
  487.  
  488. /* fread(File: handle, string[], size=sizeof string, bool:pack=false) */
  489. static cell AMX_NATIVE_CALL n_fread(AMX *amx, cell *params)
  490. {
  491.   int chars,max;
  492.   char *str;
  493.   cell *cptr;
  494.  
  495.   max=(int)params[3];
  496.   if (max<=0)
  497.     return 0;
  498.   if (params[4])
  499.     max*=sizeof(cell);
  500.  
  501.   amx_GetAddr(amx,params[2],&cptr);
  502.   str=(char *)alloca(max);
  503.   if (str==NULL || cptr==NULL) {
  504.     amx_RaiseError(amx, AMX_ERR_NATIVE);
  505.     return 0;
  506.   } /* if */
  507.  
  508.   if (params[4]) {
  509.     /* store as packed string, read an ASCII/ANSI string */
  510.     chars=fgets_char((FILE*)params[1],str,max);
  511.     assert(chars<max);
  512.     amx_SetString(cptr,str,(int)params[4],0,max);
  513.   } else {
  514.     /* store and unpacked string, interpret UTF-8 */
  515.     chars=fgets_cell((FILE*)params[1],cptr,max,1);
  516.   } /* if */
  517.  
  518.   assert(chars<max);
  519.   return chars;
  520. }
  521.  
  522. /* fputchar(File: handle, value, bool:utf8 = true) */
  523. static cell AMX_NATIVE_CALL n_fputchar(AMX *amx, cell *params)
  524. {
  525.   size_t result;
  526.  
  527.   UNUSED_PARAM(amx);
  528.   if (params[3]) {
  529.     cell str[2];
  530.     str[0]=params[2];
  531.     str[1]=0;
  532.     result=fputs_cell((FILE*)params[1],str,1);
  533.   } else {
  534.     fputc((int)params[2],(FILE*)params[1]);
  535.   } /* if */
  536.   assert(result==0 || result==1);
  537.   return result;
  538. }
  539.  
  540. /* fgetchar(File: handle, value, bool:utf8 = true) */
  541. static cell AMX_NATIVE_CALL n_fgetchar(AMX *amx, cell *params)
  542. {
  543.   cell str[2];
  544.   size_t result;
  545.  
  546.   UNUSED_PARAM(amx);
  547.   if (params[3]) {
  548.     result=fgets_cell((FILE*)params[1],str,2,1);
  549.   } else {
  550.     str[0]=fgetc((FILE*)params[1]);
  551.     result= (str[0]!=EOF);
  552.   } /* if */
  553.   assert(result==0 || result==1);
  554.   if (result==0)
  555.     return EOF;
  556.   else
  557.     return str[0];
  558. }
  559.  
  560. #if PAWN_CELL_SIZE==16
  561.   #define aligncell amx_Align16
  562. #elif PAWN_CELL_SIZE==32
  563.   #define aligncell amx_Align32
  564. #elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64)
  565.   #define aligncell amx_Align64
  566. #else
  567.   #error Unsupported cell size
  568. #endif
  569.  
  570. /* fblockwrite(File: handle, buffer[], size=sizeof buffer) */
  571. static cell AMX_NATIVE_CALL n_fblockwrite(AMX *amx, cell *params)
  572. {
  573.   cell *cptr;
  574.   cell count;
  575.  
  576.   amx_GetAddr(amx,params[2],&cptr);
  577.   if (cptr!=NULL) {
  578.     cell max=params[3];
  579.     ucell v;
  580.     for (count=0; count<max; count++) {
  581.       v=(ucell)*cptr++;
  582.       if (fwrite(aligncell(&v),sizeof(cell),1,(FILE*)params[1])!=1)
  583.         break;          /* write error */
  584.     } /* for */
  585.   } /* if */
  586.   return count;
  587. }
  588.  
  589. /* fblockread(File: handle, buffer[], size=sizeof buffer) */
  590. static cell AMX_NATIVE_CALL n_fblockread(AMX *amx, cell *params)
  591. {
  592.   cell *cptr;
  593.   cell count;
  594.  
  595.   amx_GetAddr(amx,params[2],&cptr);
  596.   if (cptr!=NULL) {
  597.     cell max=params[3];
  598.     ucell v;
  599.     for (count=0; count<max; count++) {
  600.       if (fread(&v,sizeof(cell),1,(FILE*)params[1])!=1)
  601.         break;          /* write error */
  602.       *cptr++=(cell)*aligncell(&v);
  603.     } /* for */
  604.   } /* if */
  605.   return count;
  606. }
  607.  
  608. /* File: ftemp() */
  609. static cell AMX_NATIVE_CALL n_ftemp(AMX *amx, cell *params)
  610. {
  611.   UNUSED_PARAM(amx);
  612.   UNUSED_PARAM(params);
  613.   return (cell)tmpfile();
  614. }
  615.  
  616. /* fseek(File: handle, position, seek_whence: whence=seek_start) */
  617. static cell AMX_NATIVE_CALL n_fseek(AMX *amx, cell *params)
  618. {
  619.   int whence;
  620.   switch (params[3]) {
  621.   case seek_start:
  622.     whence=SEEK_SET;
  623.     break;
  624.   case seek_current:
  625.     whence=SEEK_CUR;
  626.     break;
  627.   case seek_end:
  628.     whence=SEEK_END;
  629.     //if (params[2]>0)
  630.     //  params[2]=-params[2];
  631.     break;
  632.   default:
  633.     return 0;
  634.   } /* switch */
  635.   UNUSED_PARAM(amx);
  636.   return lseek(fileno((FILE*)params[1]),params[2],whence);
  637. }
  638.  
  639. /* bool: fexist(const name[]) */
  640. static cell AMX_NATIVE_CALL n_fexist(AMX *amx, cell *params)
  641. {
  642.   int r=1;
  643.   TCHAR *name,fullname[_MAX_PATH];
  644.  
  645.   amx_StrParam(amx,params[1],name);
  646.   if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL)
  647.     r=access(fullname,0);
  648.   return r==0;
  649. }
  650.  
  651. /* bool: fremove(const name[]) */
  652. static cell AMX_NATIVE_CALL n_fremove(AMX *amx, cell *params)
  653. {
  654.   int r=1;
  655.   TCHAR *name,fullname[_MAX_PATH];
  656.  
  657.   amx_StrParam(amx,params[1],name);
  658.   if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL)
  659.     r=remove(fullname);
  660.   return r==0;
  661. }
  662.  
  663. /* flength(File: handle) */
  664. static cell AMX_NATIVE_CALL n_flength(AMX *amx, cell *params)
  665. {
  666.   long l,c;
  667.   int fn=fileno((FILE*)params[1]);
  668.   c=lseek(fn,0,SEEK_CUR); /* save the current position */
  669.   l=lseek(fn,0,SEEK_END); /* return the file position at its end */
  670.   lseek(fn,c,SEEK_SET);   /* restore the file pointer */
  671.   UNUSED_PARAM(amx);
  672.   return l;
  673. }
  674.  
  675.  
  676. #if defined __cplusplus
  677.   extern "C"
  678. #endif
  679. AMX_NATIVE_INFO file_Natives[] = {
  680.   { "fopen",       n_fopen },
  681.   { "fclose",      n_fclose },
  682.   { "fwrite",      n_fwrite },
  683.   { "fread",       n_fread },
  684.   { "fputchar",    n_fputchar },
  685.   { "fgetchar",    n_fgetchar },
  686.   { "fblockwrite", n_fblockwrite },
  687.   { "fblockread",  n_fblockread },
  688.   { "ftemp",       n_ftemp },
  689.   { "fseek",       n_fseek },
  690.   { "fexist",      n_fexist },
  691.   { "flength",     n_flength },
  692.   { "fremove",     n_fremove },
  693.   { NULL, NULL }        /* terminator */
  694. };
  695.  
  696. int AMXEXPORT amx_FileInit(AMX *amx)
  697. {
  698.   return amx_Register(amx, file_Natives, -1);
  699. }
  700.  
  701. int AMXEXPORT amx_FileCleanup(AMX *amx)
  702. {
  703.   UNUSED_PARAM(amx);
  704.   return AMX_ERR_NONE;
  705. }