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

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