diff options
author | Steven Barth <cyrus@openwrt.org> | 2008-09-25 12:15:32 +0000 |
---|---|---|
committer | Steven Barth <cyrus@openwrt.org> | 2008-09-25 12:15:32 +0000 |
commit | c1ef612776742b585dce6079685b9cf4f12fef01 (patch) | |
tree | 98fcb35a7ac39998a415ea46edf9d4a3b1a47d58 /package/lua/patches/400-luaposix_5.1.4-embedded.patch | |
parent | 7300d22877be313bee64f736d0fae6d6b830caf7 (diff) | |
download | mtk-20170518-c1ef612776742b585dce6079685b9cf4f12fef01.zip mtk-20170518-c1ef612776742b585dce6079685b9cf4f12fef01.tar.gz mtk-20170518-c1ef612776742b585dce6079685b9cf4f12fef01.tar.bz2 |
Embed luaposix and bitlib into Lua core library
SVN-Revision: 12696
Diffstat (limited to 'package/lua/patches/400-luaposix_5.1.4-embedded.patch')
-rw-r--r-- | package/lua/patches/400-luaposix_5.1.4-embedded.patch | 1458 |
1 files changed, 1458 insertions, 0 deletions
diff --git a/package/lua/patches/400-luaposix_5.1.4-embedded.patch b/package/lua/patches/400-luaposix_5.1.4-embedded.patch new file mode 100644 index 0000000..73d8eb0 --- /dev/null +++ b/package/lua/patches/400-luaposix_5.1.4-embedded.patch @@ -0,0 +1,1458 @@ +Index: lua-5.1.4/src/Makefile +=================================================================== +--- lua-5.1.4.orig/src/Makefile 2008-09-25 12:19:44.000000000 +0200 ++++ lua-5.1.4/src/Makefile 2008-09-25 12:20:03.000000000 +0200 +@@ -12,7 +12,7 @@ + AR= ar rcu + RANLIB= ranlib + RM= rm -f +-LIBS= -lm $(MYLIBS) ++LIBS= -lm -lcrypt $(MYLIBS) + + MYCFLAGS= + MYLDFLAGS= +@@ -29,7 +29,7 @@ + lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \ + lundump.o lvm.o lzio.o lnum.o + LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \ +- lstrlib.o loadlib.o linit.o ++ lstrlib.o loadlib.o linit.o lposix.o + + LUA_T= lua + LUA_O= lua.o +Index: lua-5.1.4/src/linit.c +=================================================================== +--- lua-5.1.4.orig/src/linit.c 2008-09-25 12:19:02.000000000 +0200 ++++ lua-5.1.4/src/linit.c 2008-09-25 12:19:32.000000000 +0200 +@@ -23,6 +23,7 @@ + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, ++ {LUA_POSIXLIBNAME, luaopen_posix}, + {NULL, NULL} + }; + +Index: lua-5.1.4/src/lposix.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ lua-5.1.4/src/lposix.c 2008-09-25 12:16:29.000000000 +0200 +@@ -0,0 +1,1139 @@ ++/* ++* lposix.c ++* POSIX library for Lua 5.1. ++* Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> ++* 07 Apr 2006 23:17:49 ++* Clean up and bug fixes by Leo Razoumov <slonik.az@gmail.com> 2006-10-11 <!LR> ++* Based on original by Claudio Terra for Lua 3.x. ++* With contributions by Roberto Ierusalimschy. ++*/ ++ ++#include <sys/stat.h> ++#include <sys/times.h> ++#include <sys/types.h> ++#include <sys/utsname.h> ++#include <sys/wait.h> ++ ++#include <dirent.h> ++#include <errno.h> ++#include <fcntl.h> ++#include <glob.h> ++#include <grp.h> ++#include <libgen.h> ++#include <limits.h> ++#include <poll.h> ++#include <pwd.h> ++#include <signal.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <string.h> ++#include <syslog.h> ++#include <time.h> ++#include <unistd.h> ++#include <utime.h> ++ ++#define MYNAME "posix" ++#define MYVERSION MYNAME " library for " LUA_VERSION " / Jan 2008" ++ ++#ifndef ENABLE_SYSLOG ++#define ENABLE_SYSLOG 1 ++#endif ++ ++#include "lua.h" ++#include "lualib.h" ++#include "lauxlib.h" ++ ++#include "modemuncher.c" ++ ++/* compatibility with Lua 5.0 */ ++#ifndef LUA_VERSION_NUM ++static int luaL_checkoption (lua_State *L, int narg, const char *def, ++ const char *const lst[]) { ++ const char *name = (def) ? luaL_optstring(L, narg, def) : ++ luaL_checkstring(L, narg); ++ int i = luaL_findstring(name, lst); ++ if (i == -1) ++ luaL_argerror(L, narg, lua_pushfstring(L, "invalid option '%s'", name)); ++ return i; ++} ++#define lua_pushinteger lua_pushnumber ++#define lua_createtable(L,a,r) lua_newtable(L) ++#define LUA_FILEHANDLE "FILE*" ++ ++#define lua_setfield(l,i,k) ++#define lua_getfield(l,i,k) ++ ++#endif ++ ++static const struct { char c; mode_t b; } M[] = ++{ ++ {'r', S_IRUSR}, {'w', S_IWUSR}, {'x', S_IXUSR}, ++ {'r', S_IRGRP}, {'w', S_IWGRP}, {'x', S_IXGRP}, ++ {'r', S_IROTH}, {'w', S_IWOTH}, {'x', S_IXOTH}, ++}; ++ ++ ++static void pushmode(lua_State *L, mode_t mode) ++{ ++ char m[9]; ++ int i; ++ for (i=0; i<9; i++) m[i]= (mode & M[i].b) ? M[i].c : '-'; ++ if (mode & S_ISUID) m[2]= (mode & S_IXUSR) ? 's' : 'S'; ++ if (mode & S_ISGID) m[5]= (mode & S_IXGRP) ? 's' : 'S'; ++ lua_pushlstring(L, m, 9); ++} ++ ++typedef void (*Selector)(lua_State *L, int i, const void *data); ++ ++static int doselection(lua_State *L, int i, int n, ++ const char *const S[], ++ Selector F, ++ const void *data) ++{ ++ if (lua_isnone(L, i) || lua_istable(L, i)) ++ { ++ int j; ++ if (lua_isnone(L, i)) lua_createtable(L,0,n); else lua_settop(L, i); ++ for (j=0; S[j]!=NULL; j++) ++ { ++ lua_pushstring(L, S[j]); ++ F(L, j, data); ++ lua_settable(L, -3); ++ } ++ return 1; ++ } ++ else ++ { ++ int k,n=lua_gettop(L); ++ for (k=i; k<=n; k++) ++ { ++ int j=luaL_checkoption(L, k, NULL, S); ++ F(L, j, data); ++ lua_replace(L, k); ++ } ++ return n-i+1; ++ } ++} ++#define doselection(L,i,S,F,d) (doselection)(L,i,sizeof(S)/sizeof(*S)-1,S,F,d) ++ ++static int pusherror(lua_State *L, const char *info) ++{ ++ lua_pushnil(L); ++ if (info==NULL) ++ lua_pushstring(L, strerror(errno)); ++ else ++ lua_pushfstring(L, "%s: %s", info, strerror(errno)); ++ lua_pushinteger(L, errno); ++ return 3; ++} ++ ++static int pushresult(lua_State *L, int i, const char *info) ++{ ++ if (i==-1) return pusherror(L, info); ++ lua_pushinteger(L, i); ++ return 1; ++} ++ ++static void badoption(lua_State *L, int i, const char *what, int option) ++{ ++ luaL_argerror(L, 2, ++ lua_pushfstring(L, "unknown %s option '%c'", what, option)); ++} ++ ++static uid_t mygetuid(lua_State *L, int i) ++{ ++ if (lua_isnone(L, i)) ++ return -1; ++ else if (lua_isnumber(L, i)) ++ return (uid_t) lua_tonumber(L, i); ++ else if (lua_isstring(L, i)) ++ { ++ struct passwd *p=getpwnam(lua_tostring(L, i)); ++ return (p==NULL) ? -1 : p->pw_uid; ++ } ++ else ++ return luaL_typerror(L, i, "string or number"); ++} ++ ++static gid_t mygetgid(lua_State *L, int i) ++{ ++ if (lua_isnone(L, i)) ++ return -1; ++ else if (lua_isnumber(L, i)) ++ return (gid_t) lua_tonumber(L, i); ++ else if (lua_isstring(L, i)) ++ { ++ struct group *g=getgrnam(lua_tostring(L, i)); ++ return (g==NULL) ? -1 : g->gr_gid; ++ } ++ else ++ return luaL_typerror(L, i, "string or number"); ++} ++ ++ ++static int Perrno(lua_State *L) /** errno([n]) */ ++{ ++ int n = luaL_optint(L, 1, errno); ++ lua_pushstring(L, strerror(n)); ++ lua_pushinteger(L, n); ++ return 2; ++} ++ ++ ++static int Pbasename(lua_State *L) /** basename(path) */ ++{ ++ char b[PATH_MAX]; ++ size_t len; ++ const char *path = luaL_checklstring(L, 1, &len); ++ if (len>=sizeof(b)) luaL_argerror(L, 1, "too long"); ++ lua_pushstring(L, basename(strcpy(b,path))); ++ return 1; ++} ++ ++ ++static int Pdirname(lua_State *L) /** dirname(path) */ ++{ ++ char b[PATH_MAX]; ++ size_t len; ++ const char *path = luaL_checklstring(L, 1, &len); ++ if (len>=sizeof(b)) luaL_argerror(L, 1, "too long"); ++ lua_pushstring(L, dirname(strcpy(b,path))); ++ return 1; ++} ++ ++ ++static int Pdir(lua_State *L) /** dir([path]) */ ++{ ++ const char *path = luaL_optstring(L, 1, "."); ++ DIR *d = opendir(path); ++ if (d == NULL) ++ return pusherror(L, path); ++ else ++ { ++ int i; ++ struct dirent *entry; ++ lua_newtable(L); ++ for (i=1; (entry = readdir(d)) != NULL; i++) ++ { ++ lua_pushstring(L, entry->d_name); ++ lua_rawseti(L, -2, i); ++ } ++ closedir(d); ++ lua_pushinteger(L, i-1); ++ return 2; ++ } ++} ++ ++static int Pglob(lua_State *L) /** glob(pattern) */ ++{ ++ const char *pattern = luaL_optstring(L, 1, "*"); ++ glob_t globres; ++ ++ if (glob(pattern, 0, NULL, &globres)) ++ return pusherror(L, pattern); ++ else ++ { ++ int i; ++ lua_newtable(L); ++ for (i=1; i<=globres.gl_pathc; i++) { ++ lua_pushstring(L, globres.gl_pathv[i-1]); ++ lua_rawseti(L, -2, i); ++ } ++ globfree(&globres); ++ return 1; ++ } ++} ++ ++static int aux_files(lua_State *L) ++{ ++ DIR **p = (DIR **)lua_touserdata(L, lua_upvalueindex(1)); ++ DIR *d = *p; ++ struct dirent *entry; ++ if (d == NULL) return 0; ++ entry = readdir(d); ++ if (entry == NULL) ++ { ++ closedir(d); ++ *p=NULL; ++ return 0; ++ } ++ else ++ { ++ lua_pushstring(L, entry->d_name); ++ return 1; ++ } ++} ++ ++static int dir_gc (lua_State *L) ++{ ++ DIR *d = *(DIR **)lua_touserdata(L, 1); ++ if (d!=NULL) closedir(d); ++ return 0; ++} ++ ++static int Pfiles(lua_State *L) /** files([path]) */ ++{ ++ const char *path = luaL_optstring(L, 1, "."); ++ DIR **d = (DIR **)lua_newuserdata(L, sizeof(DIR *)); ++ if (luaL_newmetatable(L, MYNAME " dir handle")) ++ { ++ lua_pushliteral(L, "__gc"); ++ lua_pushcfunction(L, dir_gc); ++ lua_settable(L, -3); ++ } ++ lua_setmetatable(L, -2); ++ *d = opendir(path); ++ if (*d == NULL) return pusherror(L, path); ++ lua_pushcclosure(L, aux_files, 1); ++ return 1; ++} ++ ++ ++static int Pgetcwd(lua_State *L) /** getcwd() */ ++{ ++ char b[PATH_MAX]; ++ if (getcwd(b, sizeof(b)) == NULL) return pusherror(L, "."); ++ lua_pushstring(L, b); ++ return 1; ++} ++ ++ ++static int Pmkdir(lua_State *L) /** mkdir(path) */ ++{ ++ const char *path = luaL_checkstring(L, 1); ++ return pushresult(L, mkdir(path, 0777), path); ++} ++ ++ ++static int Pchdir(lua_State *L) /** chdir(path) */ ++{ ++ const char *path = luaL_checkstring(L, 1); ++ return pushresult(L, chdir(path), path); ++} ++ ++static int Prmdir(lua_State *L) /** rmdir(path) */ ++{ ++ const char *path = luaL_checkstring(L, 1); ++ return pushresult(L, rmdir(path), path); ++} ++ ++ ++static int Punlink(lua_State *L) /** unlink(path) */ ++{ ++ const char *path = luaL_checkstring(L, 1); ++ return pushresult(L, unlink(path), path); ++} ++ ++static int Plink(lua_State *L) /** link(old,new,[symbolic]) */ ++{ ++ const char *oldpath = luaL_checkstring(L, 1); ++ const char *newpath = luaL_checkstring(L, 2); ++ return pushresult(L, ++ (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL); ++} ++ ++ ++static int Preadlink(lua_State *L) /** readlink(path) */ ++{ ++ char b[PATH_MAX]; ++ const char *path = luaL_checkstring(L, 1); ++ int n = readlink(path, b, sizeof(b)); ++ if (n==-1) return pusherror(L, path); ++ lua_pushlstring(L, b, n); ++ return 1; ++} ++ ++ ++static int Paccess(lua_State *L) /** access(path,[mode]) */ ++{ ++ int mode=F_OK; ++ const char *path=luaL_checkstring(L, 1); ++ const char *s; ++ for (s=luaL_optstring(L, 2, "f"); *s!=0 ; s++) ++ switch (*s) ++ { ++ case ' ': break; ++ case 'r': mode |= R_OK; break; ++ case 'w': mode |= W_OK; break; ++ case 'x': mode |= X_OK; break; ++ case 'f': mode |= F_OK; break; ++ default: badoption(L, 2, "mode", *s); break; ++ } ++ return pushresult(L, access(path, mode), path); ++} ++ ++ ++static int myfclose (lua_State *L) { ++ FILE **p = (FILE **)lua_touserdata(L, 1); ++ int rc = fclose(*p); ++ if (rc == 0) *p = NULL; ++ return pushresult(L, rc, NULL); ++} ++ ++static int pushfile (lua_State *L, int id, const char *mode) { ++ FILE **f = (FILE **)lua_newuserdata(L, sizeof(FILE *)); ++ *f = NULL; ++ luaL_getmetatable(L, LUA_FILEHANDLE); ++ lua_setmetatable(L, -2); ++ lua_getfield(L, LUA_REGISTRYINDEX, "POSIX_PIPEFILE"); ++ if (lua_isnil(L, -1)) { ++ lua_pop(L, 1); ++ lua_newtable(L); ++ lua_pushvalue(L, -1); ++ lua_pushcfunction(L, myfclose); ++ lua_setfield(L, -2, "__close"); ++ lua_setfield(L, LUA_REGISTRYINDEX, "POSIX_PIPEFILE"); ++ } ++ lua_setfenv(L, -2); ++ *f = fdopen(id, mode); ++ return (*f != NULL); ++} ++ ++static int Ppipe(lua_State *L) /** pipe() */ ++{ ++ int fd[2]; ++ if (pipe(fd)==-1) return pusherror(L, NULL); ++ if (!pushfile(L, fd[0], "r") || !pushfile(L, fd[1], "w")) ++ return pusherror(L, "pipe"); ++ return 2; ++} ++ ++ ++static int Pfileno(lua_State *L) /** fileno(filehandle) */ ++{ ++ FILE *f = *(FILE**) luaL_checkudata(L, 1, LUA_FILEHANDLE); ++ return pushresult(L, fileno(f), NULL); ++} ++ ++ ++static int Pfdopen(lua_State *L) /** fdopen(fd, mode) */ ++{ ++ int fd = luaL_checkint(L, 1); ++ const char *mode = luaL_checkstring(L, 2); ++ if (!pushfile(L, fd, mode)) ++ return pusherror(L, "fdpoen"); ++ return 1; ++} ++ ++ ++/* helper func for Pdup */ ++static const char *filemode(int fd) ++{ ++ const char *m; ++ int mode = fcntl(fd, F_GETFL); ++ if (mode < 0) ++ return NULL; ++ switch (mode & O_ACCMODE) { ++ case O_RDONLY: m = "r"; break; ++ case O_WRONLY: m = "w"; break; ++ default: m = "rw"; break; ++ } ++ return m; ++} ++ ++static int Pdup(lua_State *L) /** dup(old,[new]) */ ++{ ++ FILE **oldf = (FILE**)luaL_checkudata(L, 1, LUA_FILEHANDLE); ++ FILE **newf = (FILE **)lua_touserdata(L, 2); ++ int fd; ++ const char *msg = "dup2"; ++ fflush(*newf); ++ if (newf == NULL) { ++ fd = dup(fileno(*oldf)); ++ msg = "dup"; ++ } else { ++ fflush(*newf); ++ fd = dup2(fileno(*oldf), fileno(*newf)); ++ } ++ ++ if ((fd < 0) || !pushfile(L, fd, filemode(fd))) ++ return pusherror(L, msg); ++ return 1; ++} ++ ++ ++static int Pmkfifo(lua_State *L) /** mkfifo(path) */ ++{ ++ const char *path = luaL_checkstring(L, 1); ++ return pushresult(L, mkfifo(path, 0777), path); ++} ++ ++ ++static int runexec(lua_State *L, int use_shell) ++{ ++ const char *path = luaL_checkstring(L, 1); ++ int i,n=lua_gettop(L); ++ char **argv = lua_newuserdata(L,(n+1)*sizeof(char*)); ++ argv[0] = (char*)path; ++ for (i=1; i<n; i++) argv[i] = (char*)luaL_checkstring(L, i+1); ++ argv[n] = NULL; ++ if (use_shell) { ++ execvp(path, argv); ++ } else { ++ execv(path, argv); ++ } ++ return pusherror(L, path); ++} ++ ++ ++static int Pexec(lua_State *L) /** exec(path,[args]) */ ++{ ++ return runexec(L, 0); ++} ++ ++ ++static int Pexecp(lua_State *L) /** execp(path,[args]) */ ++{ ++ return runexec(L, 1); ++} ++ ++ ++static int Pfork(lua_State *L) /** fork() */ ++{ ++ return pushresult(L, fork(), NULL); ++} ++ ++/* from http://lua-users.org/lists/lua-l/2007-11/msg00346.html */ ++static int Ppoll(lua_State *L) /** poll(filehandle, timeout) */ ++{ ++ struct pollfd fds; ++ FILE* file = *(FILE**)luaL_checkudata(L,1,LUA_FILEHANDLE); ++ int timeout = luaL_checkint(L,2); ++ fds.fd = fileno(file); ++ fds.events = POLLIN; ++ return pushresult(L, poll(&fds,1,timeout), NULL); ++} ++ ++static int Pwait(lua_State *L) /** wait([pid]) */ ++{ ++ int status; ++ pid_t pid = luaL_optint(L, 1, -1); ++ pid = waitpid(pid, &status, 0); ++ if (pid == -1) return pusherror(L, NULL); ++ lua_pushinteger(L, pid); ++ if (WIFEXITED(status)) ++ { ++ lua_pushliteral(L,"exited"); ++ lua_pushinteger(L, WEXITSTATUS(status)); ++ return 3; ++ } ++ else if (WIFSIGNALED(status)) ++ { ++ lua_pushliteral(L,"killed"); ++ lua_pushinteger(L, WTERMSIG(status)); ++ return 3; ++ } ++ else if (WIFSTOPPED(status)) ++ { ++ lua_pushliteral(L,"stopped"); ++ lua_pushinteger(L, WSTOPSIG(status)); ++ return 3; ++ } ++ return 1; ++} ++ ++ ++static int Pkill(lua_State *L) /** kill(pid,[sig]) */ ++{ ++ pid_t pid = luaL_checkint(L, 1); ++ int sig = luaL_optint(L, 2, SIGTERM); ++ return pushresult(L, kill(pid, sig), NULL); ++} ++ ++static int Psetpid(lua_State *L) /** setpid(option,...) */ ++{ ++ const char *what=luaL_checkstring(L, 1); ++ switch (*what) ++ { ++ case 'U': ++ return pushresult(L, seteuid(mygetuid(L, 2)), NULL); ++ case 'u': ++ return pushresult(L, setuid(mygetuid(L, 2)), NULL); ++ case 'G': ++ return pushresult(L, setegid(mygetgid(L, 2)), NULL); ++ case 'g': ++ return pushresult(L, setgid(mygetgid(L, 2)), NULL); ++ case 's': ++ return pushresult(L, setsid(), NULL); ++ case 'p': ++ { ++ pid_t pid = luaL_checkint(L, 2); ++ pid_t pgid = luaL_checkint(L, 3); ++ return pushresult(L, setpgid(pid,pgid), NULL); ++ } ++ default: ++ badoption(L, 2, "id", *what); ++ return 0; ++ } ++} ++ ++ ++static int Psleep(lua_State *L) /** sleep(seconds) */ ++{ ++ unsigned int seconds = luaL_checkint(L, 1); ++ lua_pushinteger(L, sleep(seconds)); ++ return 1; ++} ++ ++ ++static int Psetenv(lua_State *L) /** setenv(name,value,[over]) */ ++{ ++ const char *name=luaL_checkstring(L, 1); ++ const char *value=luaL_optstring(L, 2, NULL); ++ if (value==NULL) ++ { ++ unsetenv(name); ++ return pushresult(L, 0, NULL); ++ } ++ else ++ { ++ int overwrite=lua_isnoneornil(L, 3) || lua_toboolean(L, 3); ++ return pushresult(L, setenv(name,value,overwrite), NULL); ++ } ++} ++ ++ ++static int Pgetenv(lua_State *L) /** getenv([name]) */ ++{ ++ if (lua_isnone(L, 1)) ++ { ++ extern char **environ; ++ char **e; ++ lua_newtable(L); ++ for (e=environ; *e!=NULL; e++) ++ { ++ char *s=*e; ++ char *eq=strchr(s, '='); ++ if (eq==NULL) /* will this ever happen? */ ++ { ++ lua_pushstring(L,s); ++ lua_pushboolean(L,1); ++ } ++ else ++ { ++ lua_pushlstring(L,s,eq-s); ++ lua_pushstring(L,eq+1); ++ } ++ lua_settable(L,-3); ++ } ++ } ++ else ++ lua_pushstring(L, getenv(luaL_checkstring(L, 1))); ++ return 1; ++} ++ ++static int Pumask(lua_State *L) /** umask([mode]) */ ++{/* <!LR> from old lposix-5.0 version */ ++ char m[10]; ++ mode_t mode; ++ umask(mode=umask(0)); ++ mode=(~mode)&0777; ++ if (!lua_isnone(L, 1)) ++ { ++ if (mode_munch(&mode, luaL_checkstring(L, 1))) ++ { ++ lua_pushnil(L); ++ return 1; ++ } ++ mode&=0777; ++ umask(~mode); ++ } ++ modechopper(mode, m); ++ lua_pushstring(L, m); ++ return 1; ++} ++ ++ ++static int Pchmod(lua_State *L) /** chmod(path,mode) */ ++{ ++ mode_t mode; ++ struct stat s; ++ const char *path = luaL_checkstring(L, 1); ++ const char *modestr = luaL_checkstring(L, 2); ++ if (stat(path, &s)) return pusherror(L, path); ++ mode = s.st_mode; ++ if (mode_munch(&mode, modestr)) luaL_argerror(L, 2, "bad mode"); ++ return pushresult(L, chmod(path, mode), path); ++} ++ ++ ++static int Pchown(lua_State *L) /** chown(path,uid,gid) */ ++{ ++ const char *path = luaL_checkstring(L, 1); ++ uid_t uid = mygetuid(L, 2); ++ gid_t gid = mygetgid(L, 3); ++ return pushresult(L, chown(path, uid, gid), path); ++} ++ ++ ++static int Putime(lua_State *L) /** utime(path,[mtime,atime]) */ ++{ ++ struct utimbuf times; ++ time_t currtime = time(NULL); ++ const char *path = luaL_checkstring(L, 1); ++ times.modtime = luaL_optnumber(L, 2, currtime); ++ times.actime = luaL_optnumber(L, 3, currtime); ++ return pushresult(L, utime(path, ×), path); ++} ++ ++ ++static void FgetID(lua_State *L, int i, const void *data) ++{ ++ switch (i) ++ { ++ case 0: lua_pushinteger(L, getegid()); break; ++ case 1: lua_pushinteger(L, geteuid()); break; ++ case 2: lua_pushinteger(L, getgid()); break; ++ case 3: lua_pushinteger(L, getuid()); break; ++ case 4: lua_pushinteger(L, getpgrp()); break; ++ case 5: lua_pushinteger(L, getpid()); break; ++ case 6: lua_pushinteger(L, getppid()); break; ++ } ++} ++ ++static const char *const SgetID[] = ++{ ++ "egid", "euid", "gid", "uid", "pgrp", "pid", "ppid", NULL ++}; ++ ++static int Pgetpid(lua_State *L) /** getpid([options]) */ ++{ ++ return doselection(L, 1, SgetID, FgetID, NULL); ++} ++ ++ ++static int Phostid(lua_State *L) /** hostid() */ ++{ ++ char b[32]; ++ sprintf(b,"%ld",gethostid()); ++ lua_pushstring(L, b); ++ return 1; ++} ++ ++ ++static int Pttyname(lua_State *L) /** ttyname([fd]) */ ++{ ++ int fd=luaL_optint(L, 1, 0); ++ lua_pushstring(L, ttyname(fd)); ++ return 1; ++} ++ ++ ++static int Pctermid(lua_State *L) /** ctermid() */ ++{ ++ char b[L_ctermid]; ++ lua_pushstring(L, ctermid(b)); ++ return 1; ++} ++ ++ ++static int Pgetlogin(lua_State *L) /** getlogin() */ ++{ ++ lua_pushstring(L, getlogin()); ++ return 1; ++} ++ ++ ++static void Fgetpasswd(lua_State *L, int i, const void *data) ++{ ++ const struct passwd *p=data; ++ switch (i) ++ { ++ case 0: lua_pushstring(L, p->pw_name); break; ++ case 1: lua_pushinteger(L, p->pw_uid); break; ++ case 2: lua_pushinteger(L, p->pw_gid); break; ++ case 3: lua_pushstring(L, p->pw_dir); break; ++ case 4: lua_pushstring(L, p->pw_shell); break; ++/* not strictly POSIX */ ++ case 5: lua_pushstring(L, p->pw_gecos); break; ++ case 6: lua_pushstring(L, p->pw_passwd); break; ++ } ++} ++ ++static const char *const Sgetpasswd[] = ++{ ++ "name", "uid", "gid", "dir", "shell", "gecos", "passwd", NULL ++}; ++ ++ ++static int Pgetpasswd(lua_State *L) /** getpasswd(name|id,[sel]) */ ++{ ++ struct passwd *p=NULL; ++ if (lua_isnoneornil(L, 1)) ++ p = getpwuid(geteuid()); ++ else if (lua_isnumber(L, 1)) ++ p = getpwuid((uid_t)lua_tonumber(L, 1)); ++ else if (lua_isstring(L, 1)) ++ p = getpwnam(lua_tostring(L, 1)); ++ else ++ luaL_typerror(L, 1, "string or number"); ++ if (p==NULL) ++ lua_pushnil(L); ++ else ++ return doselection(L, 2, Sgetpasswd, Fgetpasswd, p); ++ return 1; ++} ++ ++ ++static int Pgetgroup(lua_State *L) /** getgroup(name|id) */ ++{ ++ struct group *g=NULL; ++ if (lua_isnumber(L, 1)) ++ g = getgrgid((gid_t)lua_tonumber(L, 1)); ++ else if (lua_isstring(L, 1)) ++ g = getgrnam(lua_tostring(L, 1)); ++ else ++ luaL_typerror(L, 1, "string or number"); ++ if (g==NULL) ++ lua_pushnil(L); ++ else ++ { ++ int i; ++ lua_newtable(L); ++ lua_pushliteral(L, "name"); ++ lua_pushstring(L, g->gr_name); ++ lua_settable(L, -3); ++ lua_pushliteral(L, "gid"); ++ lua_pushinteger(L, g->gr_gid); ++ lua_settable(L, -3); ++ for (i=0; g->gr_mem[i]!=NULL; i++) ++ { ++ lua_pushstring(L, g->gr_mem[i]); ++ lua_rawseti(L, -2, i); ++ } ++ } ++ return 1; ++} ++ ++ ++struct mytimes ++{ ++ struct tms t; ++ clock_t elapsed; ++}; ++ ++/* #define pushtime(L,x) lua_pushnumber(L,((lua_Number)x)/CLOCKS_PER_SEC) */ ++#define pushtime(L,x) lua_pushnumber(L, ((lua_Number)x)/clk_tck) ++ ++static void Ftimes(lua_State *L, int i, const void *data) ++{ ++ static long clk_tck = 0; ++ const struct mytimes *t=data; ++ ++ if( !clk_tck){ clk_tck= sysconf(_SC_CLK_TCK);} ++ switch (i) ++ { ++ case 0: pushtime(L, t->t.tms_utime); break; ++ case 1: pushtime(L, t->t.tms_stime); break; ++ case 2: pushtime(L, t->t.tms_cutime); break; ++ case 3: pushtime(L, t->t.tms_cstime); break; ++ case 4: pushtime(L, t->elapsed); break; ++ } ++} ++ ++static const char *const Stimes[] = ++{ ++ "utime", "stime", "cutime", "cstime", "elapsed", NULL ++}; ++ ++static int Ptimes(lua_State *L) /** times([options]) */ ++{ ++ struct mytimes t; ++ t.elapsed = times(&t.t); ++ return doselection(L, 1, Stimes, Ftimes, &t); ++} ++ ++ ++static const char *filetype(mode_t m) ++{ ++ if (S_ISREG(m)) return "regular"; ++ else if (S_ISLNK(m)) return "link"; ++ else if (S_ISDIR(m)) return "directory"; ++ else if (S_ISCHR(m)) return "character device"; ++ else if (S_ISBLK(m)) return "block device"; ++ else if (S_ISFIFO(m)) return "fifo"; ++ else if (S_ISSOCK(m)) return "socket"; ++ else return "?"; ++} ++ ++static void Fstat(lua_State *L, int i, const void *data) ++{ ++ const struct stat *s=data; ++ switch (i) ++ { ++ case 0: pushmode(L, s->st_mode); break; ++ case 1: lua_pushinteger(L, s->st_ino); break; ++ case 2: lua_pushinteger(L, s->st_dev); break; ++ case 3: lua_pushinteger(L, s->st_nlink); break; ++ case 4: lua_pushinteger(L, s->st_uid); break; ++ case 5: lua_pushinteger(L, s->st_gid); break; ++ case 6: lua_pushinteger(L, s->st_size); break; ++ case 7: lua_pushinteger(L, s->st_atime); break; ++ case 8: lua_pushinteger(L, s->st_mtime); break; ++ case 9: lua_pushinteger(L, s->st_ctime); break; ++ case 10:lua_pushstring(L, filetype(s->st_mode)); break; ++ } ++} ++ ++static const char *const Sstat[] = ++{ ++ "mode", "ino", "dev", "nlink", "uid", "gid", ++ "size", "atime", "mtime", "ctime", "type", ++ NULL ++}; ++ ++static int Pstat(lua_State *L) /** stat(path,[options]) */ ++{ ++ struct stat s; ++ const char *path=luaL_checkstring(L, 1); ++ if (lstat(path,&s)==-1) return pusherror(L, path); ++ return doselection(L, 2, Sstat, Fstat, &s); ++} ++ ++ ++static int Puname(lua_State *L) /** uname([string]) */ ++{ ++ struct utsname u; ++ luaL_Buffer b; ++ const char *s; ++ if (uname(&u)==-1) return pusherror(L, NULL); ++ luaL_buffinit(L, &b); ++ for (s=luaL_optstring(L, 1, "%s %n %r %v %m"); *s; s++) ++ if (*s!='%') ++ luaL_putchar(&b, *s); ++ else switch (*++s) ++ { ++ case '%': luaL_putchar(&b, *s); break; ++ case 'm': luaL_addstring(&b,u.machine); break; ++ case 'n': luaL_addstring(&b,u.nodename); break; ++ case 'r': luaL_addstring(&b,u.release); break; ++ case 's': luaL_addstring(&b,u.sysname); break; ++ case 'v': luaL_addstring(&b,u.version); break; ++ default: badoption(L, 2, "format", *s); break; ++ } ++ luaL_pushresult(&b); ++ return 1; ++} ++ ++ ++static const int Kpathconf[] = ++{ ++ _PC_LINK_MAX, _PC_MAX_CANON, _PC_MAX_INPUT, _PC_NAME_MAX, _PC_PATH_MAX, ++ _PC_PIPE_BUF, _PC_CHOWN_RESTRICTED, _PC_NO_TRUNC, _PC_VDISABLE, ++ -1 ++}; ++ ++static void Fpathconf(lua_State *L, int i, const void *data) ++{ ++ const char *path=data; ++ lua_pushinteger(L, pathconf(path, Kpathconf[i])); ++} ++ ++static const char *const Spathconf[] = ++{ ++ "link_max", "max_canon", "max_input", "name_max", "path_max", ++ "pipe_buf", "chown_restricted", "no_trunc", "vdisable", ++ NULL ++}; ++ ++static int Ppathconf(lua_State *L) /** pathconf([path,options]) */ ++{ ++ const char *path = luaL_optstring(L, 1, "."); ++ return doselection(L, 2, Spathconf, Fpathconf, path); ++} ++ ++ ++static const int Ksysconf[] = ++{ ++ _SC_ARG_MAX, _SC_CHILD_MAX, _SC_CLK_TCK, _SC_NGROUPS_MAX, _SC_STREAM_MAX, ++ _SC_TZNAME_MAX, _SC_OPEN_MAX, _SC_JOB_CONTROL, _SC_SAVED_IDS, _SC_VERSION, ++ -1 ++}; ++ ++static void Fsysconf(lua_State *L, int i, const void *data) ++{ ++ lua_pushinteger(L, sysconf(Ksysconf[i])); ++} ++ ++static const char *const Ssysconf[] = ++{ ++ "arg_max", "child_max", "clk_tck", "ngroups_max", "stream_max", ++ "tzname_max", "open_max", "job_control", "saved_ids", "version", ++ NULL ++}; ++ ++static int Psysconf(lua_State *L) /** sysconf([options]) */ ++{ ++ return doselection(L, 1, Ssysconf, Fsysconf, NULL); ++} ++ ++#if ENABLE_SYSLOG ++/* syslog funcs */ ++static int Popenlog(lua_State *L) /** openlog(ident, [option], [facility]) */ ++{ ++ const char *ident = luaL_checkstring(L, 1); ++ int option = 0; ++ int facility = luaL_optint(L, 3, LOG_USER); ++ const char *s = luaL_optstring(L, 2, ""); ++ while (*s) { ++ switch (*s) { ++ case ' ': break; ++ case 'c': option |= LOG_CONS; break; ++ case 'n': option |= LOG_NDELAY; break; ++ case 'e': option |= LOG_PERROR; break; ++ case 'p': option |= LOG_PID; break; ++ default: badoption(L, 2, "option", *s); break; ++ } ++ s++; ++ } ++ openlog(ident, option, facility); ++ return 0; ++} ++ ++ ++static int Psyslog(lua_State *L) /** syslog(priority, message) */ ++{ ++ int priority = luaL_checkint(L, 1); ++ const char *msg = luaL_checkstring(L, 2); ++ syslog(priority, "%s", msg); ++ return 0; ++} ++ ++ ++static int Pcloselog(lua_State *L) /** closelog() */ ++{ ++ closelog(); ++ return 0; ++} ++#endif ++ ++/* ++ * XXX: GNU and BSD handle the forward declaration of crypt() in different ++ * and annoying ways (especially GNU). Declare it here just to make sure ++ * that it's there ++ */ ++char *crypt(const char *, const char *); ++ ++static int Pcrypt(lua_State *L) ++{ ++ const char *str, *salt; ++ char *res; ++ ++ str = luaL_checkstring(L, 1); ++ salt = luaL_checkstring(L, 2); ++ if (strlen(salt) < 2) ++ luaL_error(L, "not enough salt"); ++ ++ res = crypt(str, salt); ++ lua_pushstring(L, res); ++ ++ return 1; ++} ++ ++static const luaL_reg R[] = ++{ ++ {"access", Paccess}, ++ {"basename", Pbasename}, ++ {"chdir", Pchdir}, ++ {"chmod", Pchmod}, ++ {"chown", Pchown}, ++ {"crypt", Pcrypt}, ++ {"ctermid", Pctermid}, ++ {"dirname", Pdirname}, ++ {"dir", Pdir}, ++ {"dup", Pdup}, ++ {"errno", Perrno}, ++ {"exec", Pexec}, ++ {"execp", Pexecp}, ++ {"fdopen", Pfdopen}, ++ {"fileno", Pfileno}, ++ {"files", Pfiles}, ++ {"fork", Pfork}, ++ {"getcwd", Pgetcwd}, ++ {"getenv", Pgetenv}, ++ {"getgroup", Pgetgroup}, ++ {"getlogin", Pgetlogin}, ++ {"getpasswd", Pgetpasswd}, ++ {"getpid", Pgetpid}, ++ {"glob", Pglob}, ++ {"hostid", Phostid}, ++ {"kill", Pkill}, ++ {"link", Plink}, ++ {"mkdir", Pmkdir}, ++ {"mkfifo", Pmkfifo}, ++ {"pathconf", Ppathconf}, ++ {"pipe", Ppipe}, ++ {"readlink", Preadlink}, ++ {"rmdir", Prmdir}, ++ {"rpoll", Ppoll}, ++ {"setenv", Psetenv}, ++ {"setpid", Psetpid}, ++ {"sleep", Psleep}, ++ {"stat", Pstat}, ++ {"sysconf", Psysconf}, ++ {"times", Ptimes}, ++ {"ttyname", Pttyname}, ++ {"unlink", Punlink}, ++ {"umask", Pumask}, ++ {"uname", Puname}, ++ {"utime", Putime}, ++ {"wait", Pwait}, ++ ++#if ENABLE_SYSLOG ++ {"openlog", Popenlog}, ++ {"syslog", Psyslog}, ++ {"closelog", Pcloselog}, ++#endif ++ ++ {NULL, NULL} ++}; ++ ++#define set_const(key, value) \ ++ lua_pushliteral(L, key); \ ++ lua_pushnumber(L, value); \ ++ lua_settable(L, -3) ++ ++LUALIB_API int luaopen_posix (lua_State *L) ++{ ++ luaL_register(L,MYNAME,R); ++ lua_pushliteral(L,"version"); /** version */ ++ lua_pushliteral(L,MYVERSION); ++ lua_settable(L,-3); ++ ++#if ENABLE_SYSLOG ++ set_const("LOG_AUTH", LOG_AUTH); ++ set_const("LOG_AUTHPRIV", LOG_AUTHPRIV); ++ set_const("LOG_CRON", LOG_CRON); ++ set_const("LOG_DAEMON", LOG_DAEMON); ++ set_const("LOG_FTP", LOG_FTP); ++ set_const("LOG_KERN", LOG_KERN); ++ set_const("LOG_LOCAL0", LOG_LOCAL0); ++ set_const("LOG_LOCAL1", LOG_LOCAL1); ++ set_const("LOG_LOCAL2", LOG_LOCAL2); ++ set_const("LOG_LOCAL3", LOG_LOCAL3); ++ set_const("LOG_LOCAL4", LOG_LOCAL4); ++ set_const("LOG_LOCAL5", LOG_LOCAL5); ++ set_const("LOG_LOCAL6", LOG_LOCAL6); ++ set_const("LOG_LOCAL7", LOG_LOCAL7); ++ set_const("LOG_LPR", LOG_LPR); ++ set_const("LOG_MAIL", LOG_MAIL); ++ set_const("LOG_NEWS", LOG_NEWS); ++ set_const("LOG_SYSLOG", LOG_SYSLOG); ++ set_const("LOG_USER", LOG_USER); ++ set_const("LOG_UUCP", LOG_UUCP); ++ ++ set_const("LOG_EMERG", LOG_EMERG); ++ set_const("LOG_ALERT", LOG_ALERT); ++ set_const("LOG_CRIT", LOG_CRIT); ++ set_const("LOG_ERR", LOG_ERR); ++ set_const("LOG_WARNING", LOG_WARNING); ++ set_const("LOG_NOTICE", LOG_NOTICE); ++ set_const("LOG_INFO", LOG_INFO); ++ set_const("LOG_DEBUG", LOG_DEBUG); ++#endif ++ ++ ++ return 1; ++} ++ ++/*EOF*/ +Index: lua-5.1.4/src/lualib.h +=================================================================== +--- lua-5.1.4.orig/src/lualib.h 2008-09-25 12:18:14.000000000 +0200 ++++ lua-5.1.4/src/lualib.h 2008-09-25 12:18:53.000000000 +0200 +@@ -39,6 +39,9 @@ + #define LUA_LOADLIBNAME "package" + LUALIB_API int (luaopen_package) (lua_State *L); + ++#define LUA_POSIXLIBNAME "posix" ++LUALIB_API int (luaopen_posix) (lua_State *L); ++ + + /* open all previous libraries */ + LUALIB_API void (luaL_openlibs) (lua_State *L); +Index: lua-5.1.4/src/modemuncher.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ lua-5.1.4/src/modemuncher.c 2008-09-25 12:16:29.000000000 +0200 +@@ -0,0 +1,261 @@ ++/* ++ Mode Muncher -- modemuncher.c ++ 961110 Claudio Terra ++ ++ munch vb ++ [ME monchen, perh. influenced by MF mangier to eat --more at MANGER] ++ :to chew with a crunching sound: eat with relish ++ :to chew food with a crunching sound: eat food with relish ++ --munch-er n ++ ++ The NeXT Digital Edition of Webster's Ninth New Collegiate Dictionary ++ and Webster's Collegiate Thesaurus ++*/ ++ ++/* struct for rwx <-> POSIX constant lookup tables */ ++struct modeLookup ++{ ++ char rwx; ++ mode_t bits; ++}; ++ ++typedef struct modeLookup modeLookup; ++ ++static modeLookup modesel[] = ++{ ++ /* RWX char Posix Constant */ ++ {'r', S_IRUSR}, ++ {'w', S_IWUSR}, ++ {'x', S_IXUSR}, ++ ++ {'r', S_IRGRP}, ++ {'w', S_IWGRP}, ++ {'x', S_IXGRP}, ++ ++ {'r', S_IROTH}, ++ {'w', S_IWOTH}, ++ {'x', S_IXOTH}, ++ {0, (mode_t)-1} /* do not delete this line */ ++}; ++ ++ ++ ++static int rwxrwxrwx(mode_t *mode, const char *p) ++{ ++ int count; ++ mode_t tmp_mode = *mode; ++ ++ tmp_mode &= ~(S_ISUID | S_ISGID); /* turn off suid and sgid flags */ ++ for (count=0; count<9; count ++) ++ { ++ if (*p == modesel[count].rwx) tmp_mode |= modesel[count].bits; /* set a bit */ ++ else if (*p == '-') tmp_mode &= ~modesel[count].bits; /* clear a bit */ ++ else if (*p=='s') switch(count) ++ { ++ case 2: /* turn on suid flag */ ++ tmp_mode |= S_ISUID | S_IXUSR; ++ break; ++ ++ case 5: /* turn on sgid flag */ ++ tmp_mode |= S_ISGID | S_IXGRP; ++ break; ++ ++ default: ++ return -4; /* failed! -- bad rwxrwxrwx mode change */ ++ break; ++ } ++ p++; ++ } ++ *mode = tmp_mode; ++ return 0; ++} ++ ++static void modechopper(mode_t mode, char *p) ++{ ++ /* requires char p[10] */ ++ int count; ++ char *pp; ++ ++ pp=p; ++ ++ for (count=0; count<9; count ++) ++ { ++ if (mode & modesel[count].bits) *p = modesel[count].rwx; ++ else *p='-'; ++ ++ p++; ++ } ++ *p=0; /* to finish the string */ ++ ++ /* dealing with suid and sgid flags */ ++ if (mode & S_ISUID) pp[2] = (mode & S_IXUSR) ? 's' : 'S'; ++ if (mode & S_ISGID) pp[5] = (mode & S_IXGRP) ? 's' : 'S'; ++ ++} ++ ++static int mode_munch(mode_t *mode, const char* p) ++{ ++ ++ char op=0; ++ mode_t affected_bits, ch_mode; ++ int doneFlag = 0; ++#ifdef DEBUG ++char tmp[10]; ++#endif ++ ++#ifdef DEBUG ++modechopper(*mode, tmp); ++printf("modemuncher: got base mode = %s\n", tmp); ++#endif ++ ++ while (!doneFlag) ++ { ++ /* step 0 -- clear temporary variables */ ++ affected_bits=0; ++ ch_mode=0; ++ ++ /* step 1 -- who's affected? */ ++ ++#ifdef DEBUG ++printf("modemuncher step 1\n"); ++#endif ++ ++ /* mode string given in rwxrwxrwx format */ ++ if (*p== 'r' || *p == '-') return rwxrwxrwx(mode, p); ++ ++ /* mode string given in ugoa+-=rwx format */ ++ for ( ; ; p++) ++ switch (*p) ++ { ++ case 'u': ++ affected_bits |= 04700; ++ break; ++ ++ case 'g': ++ affected_bits |= 02070; ++ break; ++ ++ case 'o': ++ affected_bits |= 01007; ++ break; ++ ++ case 'a': ++ affected_bits |= 07777; ++ break; ++ ++ /* ignore spaces */ ++ case ' ': ++ break; ++ ++ ++ default: ++ goto no_more_affected; ++ } ++ ++ no_more_affected: ++ /* If none specified, affect all bits. */ ++ if (affected_bits == 0) affected_bits = 07777; ++ ++ /* step 2 -- how is it changed? */ ++ ++#ifdef DEBUG ++printf("modemuncher step 2 (*p='%c')\n", *p); ++#endif ++ ++ switch (*p) ++ { ++ case '+': ++ case '-': ++ case '=': ++ op = *p; ++ break; ++ ++ /* ignore spaces */ ++ case ' ': ++ break; ++ ++ default: ++ return -1; /* failed! -- bad operator */ ++ } ++ ++ ++ /* step 3 -- what are the changes? */ ++ ++#ifdef DEBUG ++printf("modemuncher step 3\n"); ++#endif ++ ++ for (p++ ; *p!=0 ; p++) ++ switch (*p) ++ { ++ case 'r': ++ ch_mode |= 00444; ++ break; ++ ++ case 'w': ++ ch_mode |= 00222; ++ break; ++ ++ case 'x': ++ ch_mode |= 00111; ++ break; ++ ++ case 's': ++ /* Set the setuid/gid bits if `u' or `g' is selected. */ ++ ch_mode |= 06000; ++ break; ++ ++ /* ignore spaces */ ++ case ' ': ++ break; ++ ++ default: ++ goto specs_done; ++ } ++ ++ specs_done: ++ /* step 4 -- apply the changes */ ++ ++#ifdef DEBUG ++ printf("modemuncher step 4\n"); ++#endif ++ if (*p != ',') doneFlag = 1; ++ if (*p != 0 && *p != ' ' && *p != ',') ++ { ++ ++#ifdef DEBUG ++printf("modemuncher: comma error!\n"); ++printf("modemuncher: doneflag = %u\n", doneFlag); ++#endif ++ return -2; /* failed! -- bad mode change */ ++ ++ } ++ p++; ++ /*if (!ch_mode) return -2;*/ /* failed! -- bad mode change */ ++ if (ch_mode) switch (op) ++ { ++ case '+': ++ *mode = *mode |= ch_mode & affected_bits; ++ break; ++ ++ case '-': ++ *mode = *mode &= ~(ch_mode & affected_bits); ++ break; ++ ++ case '=': ++ *mode = ch_mode & affected_bits; ++ break; ++ ++ default: ++ return -3; /* failed! -- unknown error */ ++ } ++ } ++#ifdef DEBUG ++modechopper(*mode, tmp); ++printf("modemuncher: returning mode = %s\n", tmp); ++#endif ++ ++ return 0; /* successful call */ ++} ++ ++ |