root/lib/lua/loslib.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. os_pushresult
  2. os_execute
  3. os_remove
  4. os_rename
  5. os_tmpname
  6. os_getenv
  7. os_clock
  8. setfield
  9. setboolfield
  10. getboolfield
  11. getfield
  12. os_date
  13. os_time
  14. os_difftime
  15. os_difftime
  16. os_setlocale
  17. os_exit
  18. os_mkdir
  19. get_table_optbool
  20. os_listdir
  21. idir_iter
  22. idir_gc
  23. os_idir
  24. idir_register
  25. os_stat
  26. os_utime
  27. luaopen_os

   1 /*
   2 ** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
   3 ** Standard Operating System library
   4 ** See Copyright Notice in lua.h
   5 */
   6 
   7 #include <errno.h>
   8 #include <locale.h>
   9 #include <stdlib.h>
  10 #include <string.h>
  11 #include <time.h>
  12 #include <dirent.h>
  13 #ifdef HOST_LUA
  14 #include <sys/stat.h>
  15 #include <utime.h>
  16 #endif
  17 
  18 #define loslib_c
  19 #define LUA_LIB
  20 
  21 #include "lua.h"
  22 
  23 #include "lauxlib.h"
  24 #include "lualib.h"
  25 
  26 
  27 static int os_pushresult (lua_State *L, int i, const char *filename) {
  28   int en = errno;  /* calls to Lua API may change this value */
  29   if (i) {
  30     lua_pushboolean(L, 1);
  31     return 1;
  32   }
  33   else {
  34     lua_pushnil(L);
  35     lua_pushfstring(L, "%s: %s", filename, strerror(en));
  36     lua_pushinteger(L, en);
  37     return 3;
  38   }
  39 }
  40 
  41 
  42 #ifdef HOST_LUA
  43 static int os_execute (lua_State *L) {
  44   lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
  45   return 1;
  46 }
  47 #endif
  48 
  49 
  50 static int os_remove (lua_State *L) {
  51   const char *filename = luaL_checkstring(L, 1);
  52   return os_pushresult(L, remove(filename) == 0, filename);
  53 }
  54 
  55 
  56 static int os_rename (lua_State *L) {
  57   const char *fromname = luaL_checkstring(L, 1);
  58   const char *toname = luaL_checkstring(L, 2);
  59   return os_pushresult(L, rename(fromname, toname) == 0, fromname);
  60 }
  61 
  62 
  63 // TODO
  64 #if 0
  65 static int os_tmpname (lua_State *L) {
  66   char buff[LUA_TMPNAMBUFSIZE];
  67   int err;
  68   lua_tmpnam(buff, err);
  69   if (err)
  70     return luaL_error(L, "unable to generate a unique filename");
  71   lua_pushstring(L, buff);
  72   return 1;
  73 }
  74 #endif
  75 
  76 #ifdef HOST_LUA
  77 static int os_getenv (lua_State *L) {
  78   lua_pushstring(L, getenv(luaL_checkstring(L, 1)));  /* if NULL push nil */
  79   return 1;
  80 }
  81 #endif
  82 
  83 #if 0
  84 static int os_clock (lua_State *L) {
  85   lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
  86   return 1;
  87 }
  88 #endif
  89 
  90 
  91 /*
  92 ** {======================================================
  93 ** Time/Date operations
  94 ** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
  95 **   wday=%w+1, yday=%j, isdst=? }
  96 ** =======================================================
  97 */
  98 
  99 static void setfield (lua_State *L, const char *key, int value) {
 100   lua_pushinteger(L, value);
 101   lua_setfield(L, -2, key);
 102 }
 103 
 104 static void setboolfield (lua_State *L, const char *key, int value) {
 105   if (value < 0)  /* undefined? */
 106     return;  /* does not set field */
 107   lua_pushboolean(L, value);
 108   lua_setfield(L, -2, key);
 109 }
 110 
 111 static int getboolfield (lua_State *L, const char *key) {
 112   int res;
 113   lua_getfield(L, -1, key);
 114   res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
 115   lua_pop(L, 1);
 116   return res;
 117 }
 118 
 119 
 120 static int getfield (lua_State *L, const char *key, int d) {
 121   int res;
 122   lua_getfield(L, -1, key);
 123   if (lua_isnumber(L, -1))
 124     res = (int)lua_tointeger(L, -1);
 125   else {
 126     if (d < 0)
 127       return luaL_error(L, "field " LUA_QS " missing in date table", key);
 128     res = d;
 129   }
 130   lua_pop(L, 1);
 131   return res;
 132 }
 133 
 134 
 135 static int os_date (lua_State *L) {
 136   const char *s = luaL_optstring(L, 1, "%c");
 137   time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
 138   struct tm *stm;
 139   if (*s == '!') {  /* UTC? */
 140   #if 0
 141   // reyalp - we have no idea about timezones, so just eat the !
 142   // and use local time
 143   // TODO some cams may be timezone/dst aware ?
 144     stm = gmtime(&t);
 145   #endif
 146     stm = localtime(&t);
 147     s++;  /* skip `!' */
 148   }
 149   else
 150     stm = localtime(&t);
 151   if (stm == NULL)  /* invalid date? */
 152     lua_pushnil(L);
 153   else if (strcmp(s, "*t") == 0) {
 154     lua_createtable(L, 0, 9);  /* 9 = number of fields */
 155     setfield(L, "sec", stm->tm_sec);
 156     setfield(L, "min", stm->tm_min);
 157     setfield(L, "hour", stm->tm_hour);
 158     setfield(L, "day", stm->tm_mday);
 159     setfield(L, "month", stm->tm_mon+1);
 160     setfield(L, "year", stm->tm_year+1900);
 161     setfield(L, "wday", stm->tm_wday+1);
 162     setfield(L, "yday", stm->tm_yday+1);
 163     setboolfield(L, "isdst", stm->tm_isdst);
 164   }
 165   else {
 166     char cc[3];
 167     luaL_Buffer b;
 168     cc[0] = '%'; cc[2] = '\0';
 169     luaL_buffinit(L, &b);
 170     for (; *s; s++) {
 171       if (*s != '%' || *(s + 1) == '\0')  /* no conversion specifier? */
 172         luaL_addchar(&b, *s);
 173       else {
 174         size_t reslen;
 175         char buff[200];  /* should be big enough for any conversion result */
 176         cc[1] = *(++s);
 177         reslen = strftime(buff, sizeof(buff), cc, stm);
 178         luaL_addlstring(&b, buff, reslen);
 179       }
 180     }
 181     luaL_pushresult(&b);
 182   }
 183   return 1;
 184 }
 185 
 186 
 187 static int os_time (lua_State *L) {
 188   time_t t;
 189   if (lua_isnoneornil(L, 1))  /* called without args? */
 190     t = time(NULL);  /* get current time */
 191   else {
 192     struct tm ts;
 193     luaL_checktype(L, 1, LUA_TTABLE);
 194     lua_settop(L, 1);  /* make sure table is at the top */
 195     ts.tm_sec = getfield(L, "sec", 0);
 196     ts.tm_min = getfield(L, "min", 0);
 197     ts.tm_hour = getfield(L, "hour", 12);
 198     ts.tm_mday = getfield(L, "day", -1);
 199     ts.tm_mon = getfield(L, "month", -1) - 1;
 200     ts.tm_year = getfield(L, "year", -1) - 1900;
 201     ts.tm_isdst = getboolfield(L, "isdst");
 202     t = mktime(&ts);
 203   }
 204   if (t == (time_t)(-1))
 205     lua_pushnil(L);
 206   else
 207     lua_pushnumber(L, (lua_Number)t);
 208   return 1;
 209 }
 210 
 211 
 212 #if 0
 213 static int os_difftime (lua_State *L) {
 214   lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
 215                              (time_t)(luaL_optnumber(L, 2, 0))));
 216   return 1;
 217 }
 218 #endif
 219 static int os_difftime (lua_State *L) {
 220   lua_pushnumber(L, (time_t)(luaL_checknumber(L, 1) - (time_t)(luaL_optnumber(L, 2, 0))));
 221   return 1;
 222 }
 223 
 224 /* }====================================================== */
 225 
 226 
 227 #if 0
 228 static int os_setlocale (lua_State *L) {
 229   static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
 230                       LC_NUMERIC, LC_TIME};
 231   static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
 232      "numeric", "time", NULL};
 233   const char *l = luaL_optstring(L, 1, NULL);
 234   int op = luaL_checkoption(L, 2, "all", catnames);
 235   lua_pushstring(L, setlocale(cat[op], l));
 236   return 1;
 237 }
 238 #endif
 239 
 240 #ifdef HOST_LUA
 241 static int os_exit (lua_State *L) {
 242   exit(luaL_optint(L, 1, EXIT_SUCCESS));
 243 }
 244 #endif
 245 
 246 // reyalp added
 247 static int os_mkdir (lua_State *L) {
 248   const char *dirname = luaL_checkstring(L, 1);
 249 #if defined(HOST_LUA) && !defined(_WIN32)
 250   return os_pushresult(L, mkdir(dirname,0777) == 0, dirname);
 251 #else
 252   return os_pushresult(L, mkdir(dirname) == 0, dirname);
 253 #endif
 254 }
 255 
 256 
 257 static int get_table_optbool(lua_State *L, int narg, const char *fname, int d)
 258 {
 259     int r;
 260         lua_getfield(L, narg, fname);
 261     // not set - use default
 262         if(lua_isnil(L,-1)) {
 263                 r=d;
 264         } else {// otherwise, treat as bool
 265         r=lua_toboolean(L,-1); 
 266     }
 267         lua_pop(L,1);
 268     return r;
 269 }
 270 
 271 /*
 272   syntax
 273     t=os.listdir("name",[showall|opts])
 274   returns array of filenames, or nil, strerror, errno
 275   if showall is true, t includes ".", ".." and deleted entries
 276   NOTE except for the root directory, names ending in / will not work
 277 */
 278 static int os_listdir (lua_State *L) {
 279 #ifdef HOST_LUA
 280   // TODO: Not implemented for 'hostlua'
 281   (void)L;
 282   return 0;
 283 #else
 284   DIR *dir;
 285   struct dirent *de;
 286   const char *dirname = luaL_checkstring(L, 1);
 287   int all=0,od_flags=OPENDIR_FL_CHDK_LFN;
 288   if(lua_istable(L,2)) {
 289     all=get_table_optbool(L,2,"showall",0);
 290     od_flags=(get_table_optbool(L,2,"chdklfn",1))?OPENDIR_FL_CHDK_LFN:OPENDIR_FL_NONE;
 291   } else {
 292     all=lua_toboolean(L, 2);
 293   }
 294   int i=1;
 295   dir = opendir_chdk(dirname,od_flags);
 296   if(!dir) 
 297     return os_pushresult(L, 0 , dirname);
 298   lua_newtable(L); 
 299   while((de = readdir(dir))) {
 300         if(!all && (de->d_name[0] == '\xE5' || (strcmp(de->d_name,".")==0) || (strcmp(de->d_name,"..")==0)))
 301       continue;
 302         lua_pushinteger(L, i);
 303         lua_pushstring(L, de->d_name);
 304         lua_settable(L,-3);
 305         ++i;
 306   }
 307   closedir(dir);
 308   return 1;
 309 #endif
 310 }
 311 
 312 #define IDIR_META "chdk_idir_meta"
 313 
 314 typedef struct {
 315     DIR *dir;
 316     int all;
 317 } idir_udata_t;
 318 
 319 static int idir_iter(lua_State *L) {
 320     struct dirent *de;
 321     idir_udata_t *ud = (idir_udata_t *)luaL_checkudata(L,1,IDIR_META);
 322     // dir may be on first call if opendir failed, or previous explicit close
 323     if(!ud->dir) {
 324         return 0;
 325     }
 326     // allow explicit close by calling iterator(ud,false)
 327     // need to check type because first invocation is called with nil
 328     if(lua_type(L, 2) == LUA_TBOOLEAN && lua_toboolean(L,2) == 0) {
 329         closedir(ud->dir);
 330         ud->dir=NULL;
 331         return 0;
 332     }
 333     while((de = readdir(ud->dir))) {
 334         // if not all, skip over ignored items
 335         if(!ud->all && (de->d_name[0] == '\xE5' || (strcmp(de->d_name,".")==0) || (strcmp(de->d_name,"..")==0))) {
 336             continue;
 337         }
 338         break;
 339     }
 340     if(de) {
 341         lua_pushstring(L, de->d_name);
 342         return 1;
 343     } else { // on last, close immediately to avoid keeping the handle open until GC
 344         closedir(ud->dir);
 345         ud->dir=NULL;
 346         return 0;
 347     }
 348 }
 349 
 350 static int idir_gc(lua_State *L) {
 351     idir_udata_t *ud = (idir_udata_t *)luaL_checkudata(L,1,IDIR_META);
 352     if(ud->dir) {
 353         closedir(ud->dir);
 354     }
 355     return 0;
 356 }
 357 
 358 /*
 359   syntax
 360     iteratator, userdata = os.idir("name"[,all])
 361   each call to iterator(userdata) returns the next directory entry or nil if all
 362   entries have been returned
 363   typical usage
 364     for fname in os.idir("name"[,all]) do ...
 365 
 366   if all is true, includes ".", ".." and (depending on OS) deleted entries
 367  NOTES:
 368   Except for the root directory, names ending in / will not work
 369   If there is an error opening the directory, the results will be identical to
 370   an empty directory, unless all is true
 371   The directory handle is kept open until all directory entries are iterated
 372   or the userdata is GC'd. This means that:
 373   1) deep recursive traversals may run into handle limits. 
 374   2) If you break out of a loop using the iterator, the handle may remain open for a while.
 375   Explicitly calling iterator(userdata,false) will immediately close the handle. This cannot
 376   be used with the typical for syntax given above, you must use an explicit while loop like:
 377   local idir,ud=os.idir('A/')
 378   repeat
 379     name=idir(ud)
 380     -- break out of loop under some condition
 381     if somefunction(name) then
 382         break
 383     end
 384   until not name
 385   idir(ud,false) -- ensure directory handle is closed
 386 */
 387 static int os_idir (lua_State *L) {
 388 #ifdef HOST_LUA
 389   // TODO: Not implemented for 'hostlua'
 390   (void)L;
 391   return 0;
 392 #else
 393   const char *dirname = luaL_checkstring(L, 1);
 394   int all=0,od_flags=OPENDIR_FL_CHDK_LFN;
 395   if(lua_istable(L,2)) {
 396     all=get_table_optbool(L,2,"showall",0);
 397     od_flags=(get_table_optbool(L,2,"chdklfn",1))?OPENDIR_FL_CHDK_LFN:OPENDIR_FL_NONE;
 398   } else {
 399     all=lua_toboolean(L, 2);
 400   }
 401 
 402   lua_pushcfunction(L, idir_iter);
 403 
 404   idir_udata_t *ud = lua_newuserdata(L,sizeof(idir_udata_t));
 405   ud->dir = opendir_chdk(dirname,od_flags); // may be null, in which case iterator will stop on first iteration
 406                                 // no obvious way to return error status
 407   ud->all = all;
 408 
 409   luaL_getmetatable(L, IDIR_META);
 410   lua_setmetatable(L, -2);
 411   return 2;
 412 #endif
 413 }
 414 
 415 static const luaL_Reg idir_meta_methods[] = {
 416   {"__gc", idir_gc},
 417   {NULL, NULL}
 418 };
 419 static void idir_register(lua_State *L) {
 420     luaL_newmetatable(L,IDIR_META);
 421     luaL_register(L, NULL, idir_meta_methods);  
 422 }
 423 
 424 // t = stat("name")
 425 // nil,strerror,errno on fail
 426 static int os_stat (lua_State *L) {
 427   struct stat st;
 428   const char *name = luaL_checkstring(L, 1);
 429   int result = stat(name,&st);
 430   if (result==0) {
 431     lua_createtable(L, 0, 6);  /* = number of fields */
 432     // don't expose the fields that aren't useful
 433         // but leave them commented out for reference
 434 //#ifndef CAM_DRYOS_2_3_R39
 435 //    setfield(L,"dev",st.st_dev);              /* device ID number */
 436 //    setfield(L,"ino",st.st_ino);              /* no inodes in fat, always -1 */
 437 //    setfield(L,"mode",st.st_mode);    /* file mode (see below) */
 438 //#endif
 439 //    setfield(L,"nlink",st.st_nlink);  /* dryos 0, vxworks 1 */
 440 //    setfield(L,"uid",st.st_uid);              /* no users or groups on fat */
 441 //    setfield(L,"gid",st.st_gid);              /* " */
 442 //#if !CAM_DRYOS
 443 // doesn't appear useful, I wasn't able to stat any special files
 444 //    setfield(L,"rdev",st.st_rdev);    /* device ID, only if special file */
 445 //#endif
 446     setfield(L,"size",st.st_size);      /* size of file, in bytes */
 447 //#ifndef CAM_DRYOS_2_3_R39
 448 //    setfield(L,"atime",st.st_atime);  /* time of last access */
 449 //#endif
 450     setfield(L,"mtime",st.st_mtime);    /* time of last modification */
 451     setfield(L,"ctime",st.st_ctime);    /* time of last change of file status */
 452 #ifdef HOST_LUA
 453 // fill in some sane values if we aren't running on the camera
 454 // from chdk stdlib
 455 #define DOS_ATTR_DIRECTORY      0x10            /* entry is a sub-directory */
 456 #ifndef CAM_DRYOS_2_3_R39
 457     setfield(L,"blksize",512); 
 458     setfield(L,"blocks",(st.st_size/512) + (st.st_size%512)?1:0); 
 459 #endif
 460     if ( S_ISDIR(st.st_mode) ) {
 461       setfield(L,"attrib",DOS_ATTR_DIRECTORY);
 462       setboolfield(L,"is_dir",1);
 463       setboolfield(L,"is_file",0);
 464     }
 465     else {
 466       setboolfield(L,"is_dir",0);
 467       setfield(L,"attrib",0);
 468       if S_ISREG(st.st_mode) {
 469         setboolfield(L,"is_file",1);
 470       }
 471     }
 472 #else
 473 //#ifndef CAM_DRYOS_2_3_R39
 474 //    setfield(L,"blksize",st.st_blksize); /* This is NOT the dos sector size. Appears to be 512 on all I've tested! */
 475 //    setfield(L,"blocks",st.st_blocks);   /* number of blocks required to store file. May not be the same as size on disk, per above*/
 476 //#endif
 477     setfield(L,"attrib",st.st_attrib);  /* file attribute byte (dosFs only) */
 478         // for convenience
 479         // note volume labels are neither file nor directory
 480         setboolfield(L,"is_dir",st.st_attrib & DOS_ATTR_DIRECTORY);
 481         setboolfield(L,"is_file",!(st.st_attrib & (DOS_ATTR_DIRECTORY | DOS_ATTR_VOL_LABEL)));
 482 #endif
 483 #if 0
 484     setfield(L,"reserved1",st.reserved1);
 485     setfield(L,"reserved2",st.reserved2);
 486     setfield(L,"reserved3",st.reserved3);
 487     setfield(L,"reserved4",st.reserved4);
 488     setfield(L,"reserved5",st.reserved5);
 489     setfield(L,"reserved6",st.reserved6);
 490 #endif
 491     return 1;
 492   }
 493   else {
 494     int en = errno;
 495     lua_pushnil(L);
 496     lua_pushfstring(L, "%s: %s", name, strerror(en));
 497     lua_pushinteger(L, en);
 498     return 3;
 499   }
 500 }
 501 
 502 // utime(name,[modtime,[actime]])
 503 // true | nil,strerror, errno
 504 // current time used for missing or nil args
 505 static int os_utime (lua_State *L) {
 506   const char *name = luaL_checkstring(L, 1);
 507   struct utimbuf t;
 508   t.modtime = luaL_optnumber(L, 2, time(NULL));
 509   t.actime = luaL_optnumber(L, 3, time(NULL));
 510   return os_pushresult(L, utime(name,&t) == 0, name);
 511 }
 512 
 513 static const luaL_Reg syslib[] = {
 514 #if 0
 515   {"clock",     os_clock},
 516 #endif
 517   {"date",      os_date},
 518   {"difftime",  os_difftime},
 519 #ifdef HOST_LUA
 520   {"execute",   os_execute},
 521   {"exit",      os_exit},
 522   {"getenv",    os_getenv},
 523 #endif
 524   {"mkdir",     os_mkdir}, // reyalp - NOT STANDARD
 525   {"listdir",   os_listdir}, // reyalp - NOT STANDARD
 526   {"idir",      os_idir}, // reyalp - NOT STANDARD
 527   {"stat",      os_stat}, // reyalp - NOT STANDARD
 528   {"utime",     os_utime}, // reyalp - NOT STANDARD
 529   {"remove",    os_remove},
 530   {"rename",    os_rename},
 531 #if 0
 532   {"setlocale", os_setlocale},
 533 #endif
 534   {"time",      os_time},
 535 #if 0
 536   {"tmpname",   os_tmpname},
 537 #endif
 538   {NULL, NULL}
 539 };
 540 
 541 /* }====================================================== */
 542 
 543 
 544 
 545 LUALIB_API int luaopen_os (lua_State *L) {
 546   idir_register(L);
 547   luaL_register(L, LUA_OSLIBNAME, syslib);
 548   return 1;
 549 }
 550 

/* [<][>][^][v][top][bottom][index][help] */