core/smn_filesystem.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 26 18:29:02 2007 +0000 (2007-08-26)
changeset 1370 05e9ca38e083
parent 1330 ab94316bb52b
child 1373 4dc1bc06a66e
permissions -rw-r--r--
implemented amb843 - GetFileTime()
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <sys/stat.h>
    33 #include "sm_globals.h"
    34 #include "HandleSys.h"
    35 #include "LibrarySys.h"
    36 #include "sm_stringutil.h"
    37 #include "Logger.h"
    38 #include "PluginSys.h"
    39 #include "sourcemm_api.h"
    40 
    41 HandleType_t g_FileType;
    42 HandleType_t g_DirType;
    43 
    44 class FileNatives : 
    45 	public SMGlobalClass,
    46 	public IHandleTypeDispatch
    47 {
    48 public:
    49 	virtual void OnSourceModAllInitialized()
    50 	{
    51 		g_FileType = g_HandleSys.CreateType("File", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    52 		g_DirType = g_HandleSys.CreateType("Directory", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    53 	}
    54 	virtual void OnSourceModShutdown()
    55 	{
    56 		g_HandleSys.RemoveType(g_DirType, g_pCoreIdent);
    57 		g_HandleSys.RemoveType(g_FileType, g_pCoreIdent);
    58 		g_DirType = 0;
    59 		g_FileType = 0;
    60 	}
    61 	virtual void OnHandleDestroy(HandleType_t type, void *object)
    62 	{
    63 		if (type == g_FileType)
    64 		{
    65 			FILE *fp = (FILE *)object;
    66 			fclose(fp);
    67 		} else if (type == g_DirType) {
    68 			IDirectory *pDir = (IDirectory *)object;
    69 			g_LibSys.CloseDirectory(pDir);
    70 		}
    71 	}
    72 };
    73 
    74 
    75 static cell_t sm_OpenDirectory(IPluginContext *pContext, const cell_t *params)
    76 {
    77 	char *path;
    78 	int err;
    79 	if ((err=pContext->LocalToString(params[1], &path)) != SP_ERROR_NONE)
    80 	{
    81 		pContext->ThrowNativeErrorEx(err, NULL);
    82 		return 0;
    83 	}
    84 
    85 	char realpath[PLATFORM_MAX_PATH];
    86 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", path);
    87 
    88 	IDirectory *pDir = g_LibSys.OpenDirectory(realpath);
    89 	if (!pDir)
    90 	{
    91 		return 0;
    92 	}
    93 
    94 	return g_HandleSys.CreateHandle(g_DirType, pDir, pContext->GetIdentity(), g_pCoreIdent, NULL);
    95 }
    96 
    97 static cell_t sm_ReadDirEntry(IPluginContext *pContext, const cell_t *params)
    98 {
    99 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   100 	IDirectory *pDir;
   101 	HandleError herr;
   102 	HandleSecurity sec;
   103 	int err;
   104 
   105 	sec.pOwner = NULL;
   106 	sec.pIdentity = g_pCoreIdent;
   107 
   108 	if ((herr=g_HandleSys.ReadHandle(hndl, g_DirType, &sec, (void **)&pDir))
   109 		!= HandleError_None)
   110 	{
   111 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   112 	}
   113 
   114 	if (!pDir->MoreFiles())
   115 	{
   116 		return false;
   117 	}
   118 
   119 	cell_t *filetype;
   120 	if ((err=pContext->LocalToPhysAddr(params[4], &filetype)) != SP_ERROR_NONE)
   121 	{
   122 		pContext->ThrowNativeErrorEx(err, NULL);
   123 		return 0;
   124 	}
   125 
   126 	if (pDir->IsEntryDirectory())
   127 	{
   128 		*filetype = 1;
   129 	} else if (pDir->IsEntryFile()) {
   130 		*filetype = 2;
   131 	} else {
   132 		*filetype = 0;
   133 	}
   134 
   135 	const char *path = pDir->GetEntryName();
   136 	if ((err=pContext->StringToLocalUTF8(params[2], params[3], path, NULL))
   137 		!= SP_ERROR_NONE)
   138 	{
   139 		pContext->ThrowNativeErrorEx(err, NULL);
   140 		return 0;
   141 	}
   142 
   143 	pDir->NextEntry();
   144 
   145 	return true;
   146 }
   147 
   148 static cell_t sm_OpenFile(IPluginContext *pContext, const cell_t *params)
   149 {
   150 	char *name, *mode;
   151 	int err;
   152 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   153 	{
   154 		pContext->ThrowNativeErrorEx(err, NULL);
   155 		return 0;
   156 	}
   157 	if ((err=pContext->LocalToString(params[2], &mode)) != SP_ERROR_NONE)
   158 	{
   159 		pContext->ThrowNativeErrorEx(err, NULL);
   160 		return 0;
   161 	}
   162 
   163 	char realpath[PLATFORM_MAX_PATH];
   164 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   165 
   166 	FILE *pFile = fopen(realpath, mode);
   167 	if (!pFile)
   168 	{
   169 		return 0;
   170 	}
   171 
   172 	return g_HandleSys.CreateHandle(g_FileType, pFile, pContext->GetIdentity(), g_pCoreIdent, NULL);
   173 }
   174 
   175 static cell_t sm_DeleteFile(IPluginContext *pContext, const cell_t *params)
   176 {
   177 	char *name;
   178 	int err;
   179 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   180 	{
   181 		pContext->ThrowNativeErrorEx(err, NULL);
   182 		return 0;
   183 	}
   184 
   185 	char realpath[PLATFORM_MAX_PATH];
   186 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   187 
   188 	return (unlink(realpath)) ? 0 : 1;
   189 }
   190 
   191 static cell_t sm_ReadFileLine(IPluginContext *pContext, const cell_t *params)
   192 {
   193 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   194 	HandleError herr;
   195 	HandleSecurity sec;
   196 	FILE *pFile;
   197 	int err;
   198 
   199 	sec.pOwner = NULL;
   200 	sec.pIdentity = g_pCoreIdent;
   201 
   202 	if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   203 		!= HandleError_None)
   204 	{
   205 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   206 	}
   207 
   208 	char *buf;
   209 	if ((err=pContext->LocalToString(params[2], &buf)) != SP_ERROR_NONE)
   210 	{
   211 		pContext->ThrowNativeErrorEx(err, NULL);
   212 		return 0;
   213 	}
   214 
   215 	if (fgets(buf, params[3], pFile) == NULL)
   216 	{
   217 		return 0;
   218 	}
   219 
   220 	return 1;
   221 }
   222 
   223 static cell_t sm_IsEndOfFile(IPluginContext *pContext, const cell_t *params)
   224 {
   225 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   226 	HandleError herr;
   227 	HandleSecurity sec;
   228 	FILE *pFile;
   229 
   230 	sec.pOwner = NULL;
   231 	sec.pIdentity = g_pCoreIdent;
   232 
   233 	if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   234 		!= HandleError_None)
   235 	{
   236 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   237 	}
   238 
   239 	return (feof(pFile)) ? 1 : 0;
   240 }
   241 
   242 static cell_t sm_FileSeek(IPluginContext *pContext, const cell_t *params)
   243 {
   244 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   245 	HandleError herr;
   246 	HandleSecurity sec;
   247 	FILE *pFile;
   248 
   249 	sec.pOwner = NULL;
   250 	sec.pIdentity = g_pCoreIdent;
   251 
   252 	if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   253 		!= HandleError_None)
   254 	{
   255 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   256 	}
   257 
   258 	fseek(pFile, params[2], params[3]);
   259 	
   260 	return 1;
   261 }
   262 
   263 static cell_t sm_FilePosition(IPluginContext *pContext, const cell_t *params)
   264 {
   265 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   266 	HandleError herr;
   267 	HandleSecurity sec;
   268 	FILE *pFile;
   269 
   270 	sec.pOwner = NULL;
   271 	sec.pIdentity = g_pCoreIdent;
   272 
   273 	if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   274 		!= HandleError_None)
   275 	{
   276 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   277 	}
   278 
   279 	return ftell(pFile);
   280 }
   281 
   282 static cell_t sm_FileExists(IPluginContext *pContext, const cell_t *params)
   283 {
   284 	char *name;
   285 	int err;
   286 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   287 	{
   288 		pContext->ThrowNativeErrorEx(err, NULL);
   289 		return 0;
   290 	}
   291 
   292 	char realpath[PLATFORM_MAX_PATH];
   293 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   294 #ifdef PLATFORM_WINDOWS
   295 	struct _stat s;
   296 	if (_stat(realpath, &s) != 0)
   297 	{
   298 		return 0;
   299 	}
   300 	if (s.st_mode & S_IFREG)
   301 	{
   302 		return 1;
   303 	}
   304 	return 0;
   305 #elif defined PLATFORM_POSIX
   306 	struct stat s;
   307 	if (stat(realpath, &s) != 0)
   308 	{
   309 		return 0;
   310 	}
   311 	if (S_ISREG(s.st_mode))
   312 	{
   313 		return 1;
   314 	}
   315 	return 0;
   316 #endif
   317 }
   318 
   319 static cell_t sm_RenameFile(IPluginContext *pContext, const cell_t *params)
   320 {
   321 	char *newpath, *oldpath;
   322 	int err;
   323 	if ((err=pContext->LocalToString(params[1], &newpath)) != SP_ERROR_NONE)
   324 	{
   325 		pContext->ThrowNativeErrorEx(err, NULL);
   326 		return 0;
   327 	}
   328 	if ((err=pContext->LocalToString(params[2], &oldpath)) != SP_ERROR_NONE)
   329 	{
   330 		pContext->ThrowNativeErrorEx(err, NULL);
   331 		return 0;
   332 	}
   333 
   334 	char new_realpath[PLATFORM_MAX_PATH];
   335 	g_SourceMod.BuildPath(Path_Game, new_realpath, sizeof(new_realpath), "%s", newpath);
   336 	char old_realpath[PLATFORM_MAX_PATH];
   337 	g_SourceMod.BuildPath(Path_Game, old_realpath, sizeof(old_realpath), "%s", oldpath);
   338 
   339 #ifdef PLATFORM_WINDOWS
   340 	return (MoveFileA(old_realpath, new_realpath)) ? 1 : 0;
   341 #elif defined PLATFORM_POSIX
   342 	return (rename(old_realpath, new_realpath)) ? 0 : 1;
   343 #endif
   344 }
   345 
   346 static cell_t sm_DirExists(IPluginContext *pContext, const cell_t *params)
   347 {
   348 	char *name;
   349 	int err;
   350 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   351 	{
   352 		pContext->ThrowNativeErrorEx(err, NULL);
   353 		return 0;
   354 	}
   355 
   356 	char realpath[PLATFORM_MAX_PATH];
   357 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   358 #ifdef PLATFORM_WINDOWS
   359 	struct _stat s;
   360 	if (_stat(realpath, &s) != 0)
   361 	{
   362 		return 0;
   363 	}
   364 	if (s.st_mode & S_IFDIR)
   365 	{
   366 		return 1;
   367 	}
   368 	return 0;
   369 #elif defined PLATFORM_POSIX
   370 	struct stat s;
   371 	if (stat(realpath, &s) != 0)
   372 	{
   373 		return 0;
   374 	}
   375 	if (S_ISDIR(s.st_mode))
   376 	{
   377 		return 1;
   378 	}
   379 	return 0;
   380 #endif
   381 }
   382 
   383 static cell_t sm_FileSize(IPluginContext *pContext, const cell_t *params)
   384 {
   385 	char *name;
   386 	int err;
   387 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   388 	{
   389 		pContext->ThrowNativeErrorEx(err, NULL);
   390 		return -1;
   391 	}
   392 
   393 	char realpath[PLATFORM_MAX_PATH];
   394 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   395 #ifdef PLATFORM_WINDOWS
   396 	struct _stat s;
   397 	if (_stat(realpath, &s) != 0)
   398 	{
   399 		return -1;
   400 	}
   401 	if (s.st_mode & S_IFREG)
   402 	{
   403 		return static_cast<cell_t>(s.st_size);
   404 	}
   405 	return -1;
   406 #elif defined PLATFORM_POSIX
   407 	struct stat s;
   408 	if (stat(realpath, &s) != 0)
   409 	{
   410 		return -1;
   411 	}
   412 	if (S_ISREG(s.st_mode))
   413 	{
   414 		return static_cast<cell_t>(s.st_size);
   415 	}
   416 	return -1;
   417 #endif
   418 }
   419 
   420 static cell_t sm_RemoveDir(IPluginContext *pContext, const cell_t *params)
   421 {
   422 	char *name;
   423 	int err;
   424 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   425 	{
   426 		pContext->ThrowNativeErrorEx(err, NULL);
   427 		return 0;
   428 	}
   429 
   430 	char realpath[PLATFORM_MAX_PATH];
   431 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   432 
   433 	return (rmdir(realpath)) ? 0 : 1;
   434 }
   435 
   436 static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
   437 {
   438 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   439 	HandleError herr;
   440 	HandleSecurity sec;
   441 	FILE *pFile;
   442 
   443 	sec.pOwner = NULL;
   444 	sec.pIdentity = g_pCoreIdent;
   445 
   446 	if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   447 		!= HandleError_None)
   448 	{
   449 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   450 	}
   451 
   452 	char *fmt;
   453 	int err;
   454 	if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE)
   455 	{
   456 		pContext->ThrowNativeErrorEx(err, NULL);
   457 		return 0;
   458 	}
   459 
   460 	char buffer[2048];
   461 	int arg = 3;
   462 	atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
   463 	fprintf(pFile, "%s\n", buffer);
   464 
   465 	return 1;
   466 }
   467 
   468 static cell_t sm_FlushFile(IPluginContext *pContext, const cell_t *params)
   469 {
   470 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   471 	HandleError herr;
   472 	HandleSecurity sec;
   473 	FILE *pFile;
   474 
   475 	sec.pOwner = NULL;
   476 	sec.pIdentity = g_pCoreIdent;
   477 
   478 	if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   479 		!= HandleError_None)
   480 	{
   481 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   482 	}
   483 
   484 	return (fflush(pFile) == 0) ? 1 : 0;
   485 }
   486 
   487 static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
   488 {
   489 	char path[PLATFORM_MAX_PATH], *fmt, *buffer;
   490 	int arg = 5;
   491 	pContext->LocalToString(params[2], &buffer);
   492 	pContext->LocalToString(params[4], &fmt);
   493 
   494 	atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
   495 
   496 	return g_SourceMod.BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
   497 }
   498 
   499 static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
   500 {
   501 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   502 
   503 	char buffer[1024];
   504 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
   505 
   506 	if (len >= sizeof(buffer)-2)
   507 	{
   508 		buffer[1022] = '\n';
   509 		buffer[1023] = '\0';
   510 	} else {
   511 		buffer[len++] = '\n';
   512 		buffer[len] = '\0';
   513 	}
   514 
   515 	engine->LogPrint(buffer);
   516 
   517 	return 1;
   518 }
   519 
   520 static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
   521 {
   522 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   523 
   524 	char buffer[1024];
   525 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
   526 
   527 	IPlugin *pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   528 	g_Logger.LogMessage("[%s] %s", pPlugin->GetFilename(), buffer);
   529 
   530 	return 1;
   531 }
   532 
   533 static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
   534 {
   535 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   536 
   537 	char buffer[1024];
   538 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
   539 
   540 	IPlugin *pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   541 	g_Logger.LogError("[%s] %s", pPlugin->GetFilename(), buffer);
   542 
   543 	return 1;
   544 }
   545 
   546 enum
   547 {
   548 	FileTime_LastAccess = 0,	/* Last access (not available on FAT) */
   549 	FileTime_Created = 1,		/* Creation (not available on FAT) */
   550 	FileTime_LastChange = 2,	/* Last modification */
   551 };
   552 
   553 static cell_t sm_GetFileTime(IPluginContext *pContext, const cell_t *params)
   554 {
   555 	char *name;
   556 	int err;
   557 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   558 	{
   559 		pContext->ThrowNativeErrorEx(err, NULL);
   560 		return 0;
   561 	}
   562 
   563 	char realpath[PLATFORM_MAX_PATH];
   564 	g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   565 
   566 #ifdef PLATFORM_WINDOWS
   567 	struct _stat s;
   568 	if (_stat(realpath, &s) != 0)
   569 #elif defined PLATFORM_POSIX
   570 	struct stat s;
   571 	if (stat(path, &s) != 0)
   572 #endif
   573 	{
   574 		return -1;
   575 	} else {
   576 		if (params[2] == FileTime_LastAccess)
   577 		{
   578 			return (cell_t)s.st_atime;
   579 		} else if (params[2] == FileTime_Created) {
   580 			return (cell_t)s.st_ctime;
   581 		} else if (params[2] == FileTime_LastChange) {
   582 			return (cell_t)s.st_mtime;
   583 		}
   584 	}
   585 
   586 	return -1;
   587 }
   588 
   589 static FileNatives s_FileNatives;
   590 
   591 REGISTER_NATIVES(filesystem)
   592 {
   593 	{"OpenDirectory",			sm_OpenDirectory},
   594 	{"ReadDirEntry",			sm_ReadDirEntry},
   595 	{"OpenFile",				sm_OpenFile},
   596 	{"DeleteFile",				sm_DeleteFile},
   597 	{"ReadFileLine",			sm_ReadFileLine},
   598 	{"IsEndOfFile",				sm_IsEndOfFile},
   599 	{"FileSeek",				sm_FileSeek},
   600 	{"FilePosition",			sm_FilePosition},
   601 	{"FileExists",				sm_FileExists},
   602 	{"RenameFile",				sm_RenameFile},
   603 	{"DirExists",				sm_DirExists},
   604 	{"FileSize",				sm_FileSize},
   605 	{"RemoveDir",				sm_RemoveDir},
   606 	{"WriteFileLine",			sm_WriteFileLine},
   607 	{"BuildPath",				sm_BuildPath},
   608 	{"LogToGame",				sm_LogToGame},
   609 	{"LogMessage",				sm_LogMessage},
   610 	{"LogError",				sm_LogError},
   611 	{"FlushFile",				sm_FlushFile},
   612 	{"GetFileTime",				sm_GetFileTime},
   613 	{NULL,						NULL},
   614 };