librazor/lua.c
author J. Ali Harlow <ali@juiblex.co.uk>
Sat Oct 04 18:12:58 2014 +0100 (2014-10-04)
changeset 454 56ff755c268c
parent 400 eb6f3496b8e5
permissions -rw-r--r--
Only export symbols starting with razor_ in dynamic library.

Apart from being good practice to avoid clashes with higher-level
libraries and the application, this also fixes an obscure bug: The
gnulib library is used both by librazor (the dynamic library) and
by razor (the executable). In doing so, we want to have two separate
copies of the library despite the code duplication this involves.
Without the explicit limit to export only razor_ symbols, the razor
executable under mingw64 was picking up the getopt_long function
from librazor and the optind variable from libgnu which meant that
it did not see optind changing. Hiding librazor's copy of getopt
causes the linker to find libgnu's copy and everything works.

Note that under mingw librazor-#.dll still contains undocumented
(private) razor_ symbols but these will do no harm as long as nobody
tries to use them.
ali@352
     1
/*
ali@352
     2
 * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
ali@352
     3
 *
ali@352
     4
 * This program is free software; you can redistribute it and/or modify
ali@352
     5
 * it under the terms of the GNU General Public License as published by
ali@352
     6
 * the Free Software Foundation; either version 2 of the License, or
ali@352
     7
 * (at your option) any later version.
ali@352
     8
 *
ali@352
     9
 * This program is distributed in the hope that it will be useful,
ali@352
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@352
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@352
    12
 * GNU General Public License for more details.
ali@352
    13
 *
ali@352
    14
 * You should have received a copy of the GNU General Public License along
ali@352
    15
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@352
    16
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@352
    17
 */
ali@352
    18
ali@352
    19
#include "config.h"
ali@352
    20
ali@352
    21
#include <stdlib.h>
ali@352
    22
#include <string.h>
ali@352
    23
#include <stdio.h>
ali@352
    24
#include <errno.h>
ali@352
    25
#include <sys/stat.h>
ali@352
    26
#include <sys/types.h>
ali@352
    27
#include <lua.h>
ali@352
    28
#include <lualib.h>
ali@352
    29
#include <lauxlib.h>
ali@352
    30
ali@352
    31
#include "razor.h"
ali@352
    32
#include "razor-internal.h"
ali@352
    33
ali@352
    34
#define MAX_ARGS		2
ali@352
    35
ali@352
    36
#define ARG_FLAG_OPT		0x1
ali@352
    37
#define ARG_FLAG_BYPASS		0x2
ali@352
    38
ali@352
    39
#define ARG(flags, swtch, index, type) \
ali@352
    40
				((flags) << 26 | (swtch) << 21 | \
ali@352
    41
				 (index) << 16 | type)
ali@352
    42
#define ARG_FLAGS(arg)		((unsigned)(arg) >> 26)
ali@352
    43
#define ARG_SWTCH(arg)		((unsigned)(arg) >> 21 & 0x1F)
ali@352
    44
#define ARG_INDEX(arg)		((unsigned)(arg) >> 16 & 0x1F)
ali@352
    45
#define ARG_TYPE(arg)		((unsigned)(arg) & 0xFFFF)
ali@352
    46
ali@352
    47
#define ARG_NONE		0
ali@352
    48
#define ARG_STRING(n)		ARG(0, 0, n, LUA_TSTRING)
ali@352
    49
#define ARG_OPT_STRING(n)	ARG(ARG_FLAG_OPT, 0, n, LUA_TSTRING)
ali@352
    50
#define ARG_BYP_STRING(n, s)	ARG(ARG_FLAG_BYPASS, s, n, LUA_TSTRING)
ali@352
    51
ali@352
    52
struct razor_proxy {
ali@352
    53
	char *table, *name;
ali@352
    54
	int result;
ali@352
    55
	unsigned args[MAX_ARGS];
ali@352
    56
};
ali@352
    57
ali@352
    58
static struct razor_proxy proxy[] = {
ali@352
    59
	{ NULL, "dofile", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    60
	{ NULL, "loadfile", LUA_TNONE, { ARG_OPT_STRING(1) } },
ali@352
    61
	{ "io", "input", LUA_TNONE, { ARG_OPT_STRING(1) } },
ali@352
    62
	{ "io", "output", LUA_TNONE, { ARG_OPT_STRING(1) } },
ali@352
    63
	{ "io", "lines", LUA_TNONE, { ARG_OPT_STRING(1) } },
ali@352
    64
	{ "io", "open", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    65
	{ "io", "popen", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    66
	/*
ali@352
    67
	 * Note: We do not proxy os.execute
ali@352
    68
	 * We can't do it properly (there is no way to distinguish filenames
ali@352
    69
	 * in arguments from any other strings) so it's better not to try.
ali@352
    70
	 * Scripts that attempt to be portable should use posix.exec[p]
ali@352
    71
	 */
ali@352
    72
	{ "os", "remove", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    73
	{ "os", "rename", LUA_TNONE, { ARG_STRING(1), ARG_STRING(2) } },
ali@352
    74
	/*
ali@352
    75
	 * Note: We do not proxy os.tmpname
ali@352
    76
	 * It should never be used. scripts should use io.tmpfile instead
ali@352
    77
	 * if the rather limited functionality it provides suffices. The
ali@352
    78
	 * proper solution is to implement posix.mkstemp
ali@352
    79
	 */
ali@352
    80
	{ "posix", "access", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    81
	{ "posix", "chdir", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    82
	{ "posix", "chmod", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    83
	{ "posix", "chown", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    84
	{ "posix", "dir", LUA_TNONE, { ARG_OPT_STRING(1) } },
ali@352
    85
	{ "posix", "exec", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    86
	{ "posix", "execp", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    87
	{ "posix", "files", LUA_TNONE, { ARG_OPT_STRING(1) } },
ali@352
    88
	{ "posix", "getcwd", LUA_TSTRING, { ARG_NONE } },
ali@352
    89
	{ "posix", "glob", LUA_TTABLE, { ARG_STRING(1) } },
ali@352
    90
	{ "posix", "link", LUA_TNONE, { ARG_BYP_STRING(1, 3), ARG_STRING(2) } },
ali@352
    91
	{ "posix", "mkdir", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    92
	{ "posix", "mkfifo", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    93
	{ "posix", "pathconf", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    94
	{ "posix", "readlink", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    95
	{ "posix", "rmdir", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    96
	{ "posix", "stat", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    97
	{ "posix", "unlink", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    98
	{ "posix", "utime", LUA_TNONE, { ARG_STRING(1) } },
ali@352
    99
};
ali@352
   100
ali@352
   101
static lua_CFunction execp_handler;
ali@352
   102
ali@352
   103
struct razor_lua {
ali@352
   104
	const char *root;
ali@352
   105
	lua_CFunction handler[ARRAY_SIZE(proxy)];
ali@352
   106
};
ali@352
   107
ali@352
   108
static void *alloc_lua(void *user_data, void *ptr, size_t osize, size_t nsize)
ali@352
   109
{
ali@352
   110
	if (!nsize) {
ali@352
   111
		free(ptr);
ali@352
   112
		return NULL;
ali@352
   113
	} else
ali@352
   114
		return realloc(ptr, nsize);
ali@352
   115
}
ali@352
   116
ali@352
   117
static int proxy_execp(lua_State *L)
ali@352
   118
{
ali@352
   119
	int r, n, err, got_eacces = 0;
ali@352
   120
	char *file = strdup(luaL_checkstring(L, 1));
ali@352
   121
	char *exe;
ali@352
   122
	const char *s, *e, *path;
ali@352
   123
	struct razor_lua *rl;
ali@352
   124
ali@352
   125
	if (*file == '\0' || strchr(file, '/')) {
ali@352
   126
		r = execp_handler(L);
ali@352
   127
		free(file);
ali@352
   128
		return r;
ali@352
   129
	}
ali@352
   130
ali@352
   131
	(void)lua_getallocf(L, (void **)&rl);
ali@352
   132
ali@352
   133
	path = getenv("PATH");
ali@352
   134
	if (!path)
ali@352
   135
		path = ":/bin:/usr/bin";
ali@352
   136
ali@352
   137
	s = path;
ali@352
   138
	for (;;) {
ali@352
   139
		e = strchr(s, ':');
ali@352
   140
		if (!e)
ali@352
   141
			e = s + strlen(s);
ali@352
   142
		if (*s == '/') {
ali@352
   143
			n = strlen(rl->root);
ali@352
   144
			exe = malloc(n + (e - s) + 1 + strlen(file) + 1);
ali@352
   145
			memcpy(exe, rl->root, n);
ali@352
   146
			memcpy(exe + n, s, e - s);
ali@352
   147
			n += e - s;
ali@352
   148
			exe[n++] = '/';
ali@352
   149
			strcpy(exe + n, file);
ali@352
   150
		} else if (e != s) {
ali@352
   151
			exe = malloc((e - s) + 1 + strlen(file) + 1);
ali@352
   152
			memcpy(exe, s, e - s);
ali@352
   153
			n = e - s;
ali@352
   154
			exe[n++] = '/';
ali@352
   155
			strcpy(exe + n, file);
ali@352
   156
		} else {
ali@352
   157
			/* Ensure exe contains a slash to avoid PATH search */
ali@352
   158
			exe = malloc(2 + strlen(file) + 1);
ali@352
   159
			exe[0] = '.';
ali@352
   160
			exe[1] = '/';
ali@352
   161
			strcpy(exe + 2, file);
ali@352
   162
		}
ali@352
   163
		lua_pushstring(L, exe);
ali@352
   164
		lua_replace(L, 1);
ali@352
   165
		free(exe);
ali@352
   166
		n = lua_gettop(L);
ali@352
   167
		r = execp_handler(L);
ali@352
   168
ali@352
   169
		err = lua_tointeger(L, -1);
ali@352
   170
		switch (err) {
ali@352
   171
		case EACCES:
ali@352
   172
			got_eacces = 1;
ali@352
   173
			/* Fall through */
ali@352
   174
		case ENOENT:
ali@400
   175
#ifdef ESTALE
ali@352
   176
		case ESTALE:
ali@400
   177
#endif
ali@352
   178
		case ENOTDIR:
ali@352
   179
		case ENODEV:
ali@400
   180
#ifdef ETIMEDOUT
ali@352
   181
		case ETIMEDOUT:
ali@400
   182
#endif
ali@352
   183
			lua_pop(L, lua_gettop(L) - n);
ali@352
   184
			break;
ali@352
   185
		default:
ali@352
   186
			free(file);
ali@352
   187
			return r;
ali@352
   188
		}
ali@352
   189
ali@352
   190
		if (*e)
ali@352
   191
			s = e + 1;
ali@352
   192
		else
ali@352
   193
			break;
ali@352
   194
	}
ali@352
   195
ali@352
   196
	if (got_eacces)
ali@352
   197
		err = EACCES;
ali@352
   198
ali@352
   199
	lua_pushnil(L);
ali@352
   200
	lua_pushfstring(L, "%s: %s", file, strerror(err));
ali@352
   201
	lua_pushinteger(L, err);
ali@352
   202
ali@352
   203
	free(file);
ali@352
   204
	return 3;
ali@352
   205
}
ali@352
   206
ali@352
   207
static int real2fake(lua_State *L, struct razor_lua *rl, int indx)
ali@352
   208
{
ali@352
   209
	const char *path;
ali@352
   210
ali@352
   211
	if (indx < 0)
ali@352
   212
		indx += lua_gettop(L) + 1;
ali@352
   213
ali@352
   214
	if (lua_type(L, indx) == LUA_TSTRING) {
ali@352
   215
		path = lua_tostring(L, indx);
ali@352
   216
		if (path && !strncmp(path, rl->root, strlen(rl->root)) &&
ali@352
   217
		   (path[strlen(rl->root)] == '/' || !path[strlen(rl->root)])) {
ali@352
   218
			lua_pushstring(L, path + strlen(rl->root));
ali@352
   219
			lua_replace(L, indx);
ali@352
   220
			return 1;
ali@352
   221
		}
ali@352
   222
	}
ali@352
   223
ali@352
   224
	return 0;
ali@352
   225
}
ali@352
   226
ali@352
   227
static int proxy_handler(lua_State *L)
ali@352
   228
{
ali@352
   229
	int i, retval, cond;
ali@352
   230
	unsigned arg;
ali@352
   231
	const char *path;
ali@352
   232
	struct razor_lua *rl;
ali@352
   233
	int method = lua_tointeger(L, lua_upvalueindex(1));
ali@352
   234
	struct razor_proxy *rp = proxy + method;
ali@352
   235
ali@352
   236
	(void)lua_getallocf(L, (void **)&rl);
ali@352
   237
ali@352
   238
	for(i = 0; i < MAX_ARGS; i++) {
ali@352
   239
		arg = rp->args[i];
ali@352
   240
		if (arg == ARG_NONE)
ali@352
   241
			continue;
ali@352
   242
		switch (ARG_TYPE(arg)) {
ali@352
   243
		case LUA_TSTRING:
ali@352
   244
			if (ARG_FLAGS(arg) & ARG_FLAG_BYPASS)
ali@352
   245
				cond = !lua_toboolean(L, ARG_SWTCH(arg));
ali@352
   246
			else
ali@352
   247
				cond = 1;
ali@352
   248
			if (ARG_FLAGS(arg) & ARG_FLAG_OPT) {
ali@352
   249
				if (lua_isstring(L, ARG_INDEX(arg)))
ali@352
   250
					path = lua_tostring(L, ARG_INDEX(arg));
ali@352
   251
				else
ali@352
   252
					path = NULL;
ali@352
   253
			} else
ali@352
   254
				path = luaL_checkstring(L, ARG_INDEX(arg));
ali@352
   255
			if (cond && path && *path == '/') {
ali@352
   256
				lua_pushfstring(L, "%s%s", rl->root, path);
ali@352
   257
				lua_replace(L, ARG_INDEX(arg));
ali@352
   258
			}
ali@352
   259
			break;
ali@352
   260
		default:
ali@352
   261
			lua_pushfstring(L, "razor proxy: Unhandled type (%d)",
ali@352
   262
					ARG_TYPE(arg));
ali@352
   263
			lua_error(L);
ali@352
   264
		}
ali@352
   265
	}
ali@352
   266
ali@352
   267
	retval = rl->handler[method](L);
ali@352
   268
ali@352
   269
	if (rp->result != LUA_TNONE && !lua_isnil(L, 1)) {
ali@352
   270
		switch (rp->result) {
ali@352
   271
		case LUA_TNONE:
ali@352
   272
			break;
ali@352
   273
		case LUA_TSTRING:
ali@352
   274
			real2fake(L, rl, -1);
ali@352
   275
			break;
ali@352
   276
		case LUA_TTABLE:
ali@352
   277
			lua_pushnil(L);
ali@352
   278
			while (lua_next(L, -2)) {
ali@352
   279
				if (real2fake(L, rl, -1)) {
ali@352
   280
					i = lua_tonumber(L, -2);
ali@352
   281
					lua_rawseti(L, -3, i);
ali@352
   282
				} else
ali@352
   283
					lua_pop(L, 1);
ali@352
   284
			}
ali@352
   285
			break;
ali@352
   286
		default:
ali@352
   287
			lua_pushfstring(L, "razor proxy: Unhandled type (%d)",
ali@352
   288
					rp->result);
ali@352
   289
			lua_error(L);
ali@352
   290
		}
ali@352
   291
	}
ali@352
   292
ali@352
   293
	return retval;
ali@352
   294
}
ali@352
   295
ali@361
   296
struct razor_lua_loader {
ali@361
   297
	uint32_t name;
ali@361
   298
	lua_CFunction func;
ali@361
   299
};
ali@361
   300
ali@361
   301
static struct razor_preload {
ali@361
   302
	int init;
ali@361
   303
	struct hashtable modules;
ali@361
   304
	struct array name_pool;
ali@361
   305
	struct array loaders;
ali@361
   306
} razor_preload = {0};
ali@361
   307
ali@361
   308
RAZOR_EXPORT void razor_set_lua_loader(const char *modname, void (*loader)())
ali@361
   309
{
ali@361
   310
	uint32_t name;
ali@361
   311
	struct razor_lua_loader *ploader, *end;
ali@361
   312
ali@361
   313
	if (!razor_preload.init) {
ali@361
   314
		razor_preload.init = 1;
ali@361
   315
		array_init(&razor_preload.name_pool);
ali@361
   316
		array_init(&razor_preload.loaders);
ali@361
   317
		hashtable_init(&razor_preload.modules,
ali@361
   318
			       &razor_preload.name_pool);
ali@361
   319
	}
ali@361
   320
ali@361
   321
	name = hashtable_tokenize(&razor_preload.modules, modname);
ali@361
   322
ali@361
   323
	end = razor_preload.loaders.data + razor_preload.loaders.size;
ali@361
   324
	for(ploader = razor_preload.loaders.data; ploader < end; ploader++)
ali@361
   325
		if (ploader->name == name) {
ali@442
   326
			ploader->func = (lua_CFunction)loader;
ali@361
   327
			return;
ali@361
   328
		}
ali@361
   329
ali@361
   330
	ploader = array_add(&razor_preload.loaders, sizeof(*ploader));
ali@361
   331
	ploader->name = name;
ali@442
   332
	ploader->func = (lua_CFunction)loader;
ali@361
   333
}
ali@361
   334
ali@368
   335
RAZOR_EXPORT void (*razor_get_lua_loader(const char *modname))()
ali@368
   336
{
ali@368
   337
	uint32_t name;
ali@368
   338
	struct razor_lua_loader *ploader, *end;
ali@368
   339
ali@368
   340
	if (!razor_preload.init)
ali@368
   341
		return 0;
ali@368
   342
ali@368
   343
	name = hashtable_lookup(&razor_preload.modules, modname);
ali@368
   344
ali@368
   345
	end = razor_preload.loaders.data + razor_preload.loaders.size;
ali@368
   346
	for(ploader = razor_preload.loaders.data; ploader < end; ploader++)
ali@368
   347
		if (ploader->name == name)
ali@442
   348
			return (void (*)())ploader->func;
ali@368
   349
ali@368
   350
	return 0;
ali@368
   351
}
ali@368
   352
ali@361
   353
static void razor_lua_preload(lua_State *L)
ali@361
   354
{
ali@361
   355
	struct razor_lua_loader *ploader, *end;
ali@361
   356
ali@361
   357
	if (!razor_preload.init)
ali@361
   358
		return;
ali@361
   359
ali@361
   360
	lua_getfield(L, LUA_GLOBALSINDEX, "package");
ali@361
   361
	lua_getfield(L, -1, "preload");
ali@361
   362
	lua_remove(L, -2);
ali@361
   363
ali@361
   364
	end = razor_preload.loaders.data + razor_preload.loaders.size;
ali@361
   365
	for(ploader = razor_preload.loaders.data; ploader < end; ploader++) {
ali@361
   366
		lua_pushcfunction(L, ploader->func);
ali@361
   367
		lua_setfield(L, -2,
ali@361
   368
			     razor_preload.name_pool.data + ploader->name);
ali@361
   369
	}
ali@361
   370
ali@361
   371
	lua_pop(L, 1);
ali@361
   372
}
ali@361
   373
ali@352
   374
int run_lua_script(const char *root, const char *name, const char *body,
ali@376
   375
		   ssize_t len, int arg1)
ali@352
   376
{
ali@352
   377
	int i, n;
ali@352
   378
	lua_State *L;
ali@352
   379
	struct razor_lua rl;
ali@352
   380
ali@352
   381
	if (root && strcmp(root, "/"))
ali@352
   382
		rl.root = root;
ali@352
   383
	else
ali@352
   384
		rl.root = NULL;
ali@352
   385
ali@352
   386
	L = lua_newstate(alloc_lua, &rl);
ali@352
   387
	luaL_openlibs(L);
ali@361
   388
	razor_lua_preload(L);
ali@352
   389
	lua_getglobal(L, "require");
ali@352
   390
	lua_pushstring(L, "posix");
ali@352
   391
	if (lua_pcall(L, 1, 1, 0)) {
ali@352
   392
		fprintf(stderr, "lua posix: %s\n", lua_tostring(L, -1));
ali@352
   393
		lua_pop(L, 1);
ali@352
   394
		lua_close(L);
ali@352
   395
		return -1;
ali@352
   396
	}
ali@352
   397
ali@352
   398
	if (rl.root)
ali@352
   399
		for(i = 0; i < ARRAY_SIZE(proxy); i++) {
ali@352
   400
			if (proxy[i].table) {
ali@352
   401
				lua_getglobal(L, proxy[i].table);
ali@352
   402
				n = lua_gettop(L);
ali@352
   403
			} else
ali@352
   404
				n = LUA_GLOBALSINDEX;
ali@352
   405
			lua_getfield(L, n, proxy[i].name);
ali@352
   406
			rl.handler[i] = lua_tocfunction(L, -1);
ali@352
   407
			lua_getfenv(L, -1);
ali@352
   408
			lua_remove(L, -2);
ali@352
   409
			if (proxy[i].table &&
ali@352
   410
			    !strcmp(proxy[i].table, "posix") &&
ali@352
   411
			    !strcmp(proxy[i].name, "execp")) {
ali@352
   412
				execp_handler = rl.handler[i];
ali@352
   413
				rl.handler[i] = proxy_execp;
ali@352
   414
			}
ali@352
   415
			lua_pushinteger(L, i);
ali@352
   416
			lua_pushcclosure(L, proxy_handler, 1);
ali@352
   417
			lua_pushvalue(L, -2);
ali@352
   418
			lua_setfenv(L, -2);
ali@352
   419
			lua_setfield(L, n, proxy[i].name);
ali@352
   420
			if (proxy[i].table)
ali@352
   421
				lua_pop(L, 2);
ali@352
   422
			else
ali@352
   423
				lua_pop(L, 1);
ali@352
   424
		}
ali@352
   425
ali@352
   426
	if (len < 0)
ali@352
   427
		len = strlen(body);
ali@352
   428
ali@352
   429
	if (luaL_loadbuffer(L, body, len, name)) {
ali@352
   430
		fprintf(stderr, "failed to load lua script\n");
ali@352
   431
		lua_close(L);
ali@352
   432
		return -1;
ali@352
   433
	}
ali@352
   434
ali@376
   435
	lua_newtable(L);
ali@376
   436
	lua_pushvalue(L, LUA_GLOBALSINDEX);
ali@376
   437
	lua_pushliteral(L, "arg");
ali@376
   438
	lua_pushvalue(L, -3);
ali@376
   439
	lua_rawset(L, -3);
ali@376
   440
	lua_pop(L, 1);
ali@376
   441
	lua_pushliteral(L, "<lua>");
ali@376
   442
	lua_rawseti(L, -2, 1);
ali@376
   443
	if (arg1 >= 0) {
ali@376
   444
		lua_pushinteger(L, arg1);
ali@376
   445
		lua_rawseti(L, -2, 2);
ali@376
   446
	}
ali@376
   447
	lua_pop(L, 1);
ali@376
   448
ali@352
   449
	if (lua_pcall(L, 0, 0, 0)) {
ali@352
   450
		fprintf(stderr, "lua script failed: %s\n", lua_tostring(L, -1));
ali@352
   451
		lua_pop(L, 1);
ali@352
   452
		lua_close(L);
ali@352
   453
		return -1;
ali@352
   454
	}
ali@352
   455
ali@352
   456
	lua_close(L);
ali@352
   457
	return 0;
ali@352
   458
}