ocutil.lua 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. -- ===================================================================
  2. -- Overview.
  3. -- This file is "top/ocutil.lua".
  4. -- This file provides a collection of largely portable Lua utility
  5. -- functions. The collection is descended from one assembled by Old-
  6. -- Coder (Robert Kiraly).
  7. -- ===================================================================
  8. ocutil = {}
  9. ocutil.log_console = true -- Flag: Copy log to console
  10. ocutil.logdir = nil -- Absolute path for log-file direc-
  11. -- tory (or nil)
  12. -- ===================================================================
  13. -- Function: ocutil.fixnil
  14. -- Params: string s
  15. -- Outputs:
  16. --
  17. -- If the string is nil , returns "(nil)"
  18. -- if the string is empty , returns "(empty)"
  19. -- Otherwise, returns the string as it stands
  20. --
  21. -- This function can be used to clarify -- and/or avoid crashes in --
  22. -- debug messages.
  23. -- ===================================================================
  24. ocutil.fixnil = function (s)
  25. if s == nil then s = "(nil)"
  26. elseif s == "" then s = "(empty)"
  27. end
  28. return s
  29. end
  30. -- ===================================================================
  31. -- Function: ocutil.setlogdir
  32. -- Params: string path
  33. -- Outputs:
  34. --
  35. -- The input string should be the absolute path for a writable direc-
  36. -- tory that already exists. This function tells "ocutil.log" to put
  37. -- log files in that directory.
  38. -- ===================================================================
  39. ocutil.setlogdir = function (path)
  40. if path ~= nil and path ~= "" then
  41. ocutil.logdir = path
  42. end
  43. end
  44. -- ===================================================================
  45. -- Function: ocutil.setlogname
  46. -- Params: string name
  47. -- Outputs:
  48. --
  49. -- The input string should be a filename without a path component.
  50. -- This function tells "ocutil.log" to use the specified filename for
  51. -- the main log file.
  52. --
  53. -- "ocutil.setlogdir" must be called separately to set the log-file
  54. -- directory.
  55. -- ===================================================================
  56. ocutil.setlogname = function (name)
  57. if name ~= nil and name ~= "" then
  58. ocutil.logname = name
  59. end
  60. end
  61. -- ===================================================================
  62. -- Function: ocutil.log
  63. -- Params: string s
  64. -- Outputs:
  65. --
  66. -- Logs the specified string. Appends a newline if not already pre-
  67. -- sent.
  68. -- ===================================================================
  69. ocutil.log = function (s)
  70. s = ocutil.fixnil (s)
  71. s = s:gsub ("\n$", "", 1) -- Remove trailing newline initially
  72. if ocutil.log_console then
  73. print (s)
  74. end
  75. if ocutil.logdir ~= nil and
  76. ocutil.logname ~= nil then
  77. s = s .. "\n" -- Add trailing newline
  78. local logpath = ocutil.logdir .. "/" .. ocutil.logname
  79. ocutil.file_append (logpath, s)
  80. end
  81. end
  82. -- ===================================================================
  83. -- Function: ocutil.fatal_error
  84. -- Params: string s
  85. -- Outputs:
  86. --
  87. -- Logs "Fatal error: " plus the specified string. Appends a newline
  88. -- if not already present. Terminates the program.
  89. -- ===================================================================
  90. ocutil.fatal_error = function (s)
  91. ocutil.log ("Fatal Error: " .. s)
  92. os.exit (1)
  93. return nil -- Shouldn't be reached
  94. end
  95. -- ===================================================================
  96. -- Function: ocutil.panic
  97. -- Params: string s
  98. -- Outputs:
  99. --
  100. -- Logs "Internal error: " plus the specified string. Appends a new-
  101. -- line if not already present. Terminates the program.
  102. -- ===================================================================
  103. ocutil.panic = function (s)
  104. ocutil.log ("Internal Error: " .. s)
  105. os.exit (1)
  106. return nil -- Shouldn't be reached
  107. end
  108. -- ===================================================================
  109. -- Function: ocutil.str_empty
  110. -- Params: string x
  111. -- Outputs:
  112. --
  113. -- Returns true if the string is nil or empty
  114. -- Returns false otherwise
  115. -- ===================================================================
  116. ocutil.str_empty = function (x)
  117. if x == nil or x == "" then
  118. return true
  119. else
  120. return false
  121. end
  122. end
  123. -- ===================================================================
  124. -- Function: ocutil.str_nonempty
  125. -- Params: string x
  126. -- Outputs:
  127. --
  128. -- Returns true if the string is nil or empty
  129. -- Returns false otherwise
  130. -- ===================================================================
  131. ocutil.str_nonempty = function (x)
  132. if x == nil or x == "" then
  133. return false
  134. else
  135. return true
  136. end
  137. end
  138. -- ===================================================================
  139. -- Function: ocutil.table_empty
  140. -- Params: table tab
  141. -- Outputs:
  142. --
  143. -- Returns true if the table is empty
  144. -- Returns false otherwise
  145. -- ===================================================================
  146. ocutil.table_empty = function (tab)
  147. local next = next
  148. if next (tab) == nil then return true end
  149. return false
  150. end
  151. -- ===================================================================
  152. -- Function: ocutil.table_nonempty
  153. -- Params: table tab
  154. -- Outputs:
  155. --
  156. -- Returns false if the table is empty
  157. -- Returns true otherwise
  158. -- ===================================================================
  159. ocutil.table_nonempty = function (tab)
  160. if ocutil.table_empty (tab) then return false end
  161. return true
  162. end
  163. -- ===================================================================
  164. -- Function: ocutil.string_contains
  165. -- Params: strings a, b
  166. -- Outputs:
  167. --
  168. -- Returns true if the 1st string contains the 2nd one
  169. -- Returns false otherwise
  170. -- ===================================================================
  171. ocutil.str_contains = function (a, b)
  172. if string.match (a, b) then
  173. return true
  174. else
  175. return false
  176. end
  177. end
  178. -- ===================================================================
  179. -- Function: ocutil.str_false
  180. -- Params: string x
  181. -- Outputs:
  182. --
  183. -- This function checks the string for an explicit false (or equiva-
  184. -- lent) setting (as opposed to empty or nil). If such a setting is
  185. -- found, true is returned. Otherwise, false is returned.
  186. -- ===================================================================
  187. ocutil.str_false = function (x)
  188. if x == "false" or x == "no" or
  189. x == "off" or x == "0" or
  190. x == false or x == 0 then
  191. return true
  192. else
  193. return false
  194. end
  195. end
  196. -- ===================================================================
  197. -- Function: ocutil.str_true
  198. -- Params: string x
  199. -- Outputs:
  200. --
  201. -- This function checks the string for an explicit true (or equiva-
  202. -- lent) setting. If such a setting is found, true is returned. Other-
  203. -- wise, false is returned.
  204. -- ===================================================================
  205. ocutil.str_true = function (x)
  206. if x == "true" or x == "yes" or
  207. x == "on" or x == "1" or
  208. x == true or x == 1 then
  209. return true
  210. else
  211. return false
  212. end
  213. end
  214. -- ===================================================================
  215. -- Function: ocutil.starts_with
  216. -- Params: strings String, Start
  217. -- Outputs:
  218. --
  219. -- Returns true if the 1st string starts with the 2nd one
  220. -- Returns false otherwise
  221. -- ===================================================================
  222. ocutil.starts_with = function (String, Start)
  223. if string.sub (String, 1, string.len (Start)) == Start then
  224. return true
  225. else
  226. return false
  227. end
  228. end
  229. -- ===================================================================
  230. -- Function: ocutil.not_starts_with
  231. -- Params: strings String, Start
  232. -- Outputs:
  233. --
  234. -- Returns false if the 1st string starts with the 2nd one
  235. -- Returns true otherwise
  236. -- ===================================================================
  237. ocutil.not_starts_with = function (String, Start)
  238. if string.sub (String, 1, string.len (Start)) == Start then
  239. return false
  240. else
  241. return true
  242. end
  243. end
  244. -- ===================================================================
  245. -- Function: ocutil.ends_with
  246. -- Params: strings String, End
  247. -- Outputs:
  248. --
  249. -- Returns true if the 1st string ends with the 2nd one
  250. -- Returns false otherwise
  251. -- As a special case, returns true if the 2nd string is empty
  252. -- ===================================================================
  253. ocutil.ends_with = function (String, End)
  254. return End == '' or string.sub (String,
  255. -string.len (End)) == End
  256. end
  257. -- ===================================================================
  258. -- Function: ocutil.not_ends_with
  259. -- Params: strings String, End
  260. -- Outputs:
  261. -- Returns false if the 1st string ends with the 2nd one
  262. -- Returns true otherwise
  263. -- As a special case, returns false if the 2nd string is empty
  264. -- ===================================================================
  265. ocutil.not_ends_with = function (String, End)
  266. if ocutil.ends_with (String, End) then
  267. return false
  268. else
  269. return true
  270. end
  271. end
  272. -- ===================================================================
  273. -- Function: ocutil.firstch
  274. -- Params: string str
  275. -- Outputs:
  276. -- Returns the 1st character of the string
  277. -- ===================================================================
  278. ocutil.firstch = function (str)
  279. return string.sub (str, 1, 1)
  280. end
  281. -- ===================================================================
  282. -- Function: ocutil.first_to_upper
  283. -- Params: string str
  284. -- Outputs:
  285. -- Returns the 1st character of the string in upper case
  286. -- ===================================================================
  287. ocutil.first_to_upper = function (str)
  288. return (str:gsub ("^%l", string.upper))
  289. end
  290. -- ===================================================================
  291. -- Function: ocutil.all_first_to_upper
  292. -- Params: string str, flag cvtspace
  293. -- Outputs:
  294. --
  295. -- Converts the 1st character of each word in the string to upper
  296. -- case and returns the result. Words are delimited by spaces or un-
  297. -- derscores. If the flag is true, also replaces spaces with under-
  298. -- scores.
  299. -- ===================================================================
  300. ocutil.all_first_to_upper = function (str, cvtspace)
  301. str = str:gsub ("^%l", string.upper)
  302. str = str:gsub ("[_ ]%l",
  303. function (a) return string.upper (a) end)
  304. if ocutil.str_true (cvtspace) then
  305. str = str:gsub ("_", " ")
  306. end
  307. return (str)
  308. end
  309. -- ===================================================================
  310. -- Function: ocutil.strtok
  311. -- Params: strings source, delimitch
  312. -- Outputs:
  313. --
  314. -- Breaks the source string up into a list of words and returns the
  315. -- list.
  316. --
  317. -- If "delimitch" is omitted, nil, or empty, words are delimited by
  318. -- spaces. Otherwise, words are delimited by any of the characters in
  319. -- "delimitch".
  320. -- ===================================================================
  321. ocutil.strtok = function (source, delimitch)
  322. if delimitch == nil or
  323. delimitch == "" then delimitch = " " end
  324. local parts = {}
  325. local pattern = '([^' .. delimitch .. ']+)'
  326. string.gsub (source, pattern,
  327. function (value)
  328. value = value:gsub ("^ *", "")
  329. value = value:gsub (" *$", "")
  330. parts [#parts + 1] = value
  331. end)
  332. return parts
  333. end
  334. -- ===================================================================
  335. -- Function: ocutil.swap_key_value
  336. -- Params: table itable
  337. -- Outputs:
  338. -- Turns keys into values and vice versa and returns the resulting
  339. -- table.
  340. -- ===================================================================
  341. ocutil.swap_key_value = function (itable)
  342. if itable == nil then return nil end
  343. local otable = {}
  344. for key, value in pairs (itable) do otable [value] = key end
  345. return otable
  346. end
  347. -- ===================================================================
  348. -- Function: ocutil.ktable_to_vtable
  349. -- Params: table ktable
  350. -- Outputs:
  351. --
  352. -- "ktable_to_vtable" is short for "convert key table to value table".
  353. --
  354. -- The input table is assumed to be an arbitrary key-based list of the
  355. -- general form:
  356. -- { dog="woof", apple="red", book="read" }
  357. --
  358. -- This function takes the keys and returns them as an integer-indexed
  359. -- array with the former keys stored now as values. The array is sort-
  360. -- ed based on the former keys. The original values in the input table
  361. -- are discarded. Sample output:
  362. --
  363. -- { "apple", "book", "dog", ... }
  364. -- Here's a complete code fragment which demonstrates operation of
  365. -- this function:
  366. -- local tabby = { dog="woof", apple="red", book="read" }
  367. --
  368. -- print ("\nInput table:")
  369. -- for ii, value in pairs (tabby) do
  370. -- print (ii .. ") " .. tabby [ii])
  371. -- end
  372. --
  373. -- tabby = ocutil.ktable_to_vtable (tabby)
  374. --
  375. -- print ("\nOutput table:")
  376. -- for ii, value in ipairs (tabby) do
  377. -- print (ii .. ") " .. tabby [ii])
  378. -- end
  379. -- ===================================================================
  380. ocutil.ktable_to_vtable = function (ktable)
  381. local vtable = {}
  382. if ktable == nil then return vtable end
  383. for key, _ in pairs (ktable) do
  384. table.insert (vtable, key)
  385. end
  386. table.sort (vtable)
  387. return vtable
  388. end
  389. -- ===================================================================
  390. -- Function: ocutil.vtable_to_ktable
  391. -- Params: table vtable, scalar set_to
  392. -- Outputs:
  393. --
  394. -- "vtable_to_ktable" is short for "convert value table to key table".
  395. --
  396. -- The input table is assumed to be an integer-indexed array of scalar
  397. -- values.
  398. --
  399. -- This function creates a general table that holds the scalar values
  400. -- stored now as keys and returns the new table. The new table maps
  401. -- each of the keys to the value specified by "set_to".
  402. --
  403. -- If "set_to" is omitted or nil, it defaults to boolean true.
  404. -- Here's a complete code fragment which demonstrates operation of
  405. -- this function:
  406. --
  407. -- local tabby = { "apple", "book", "dog" }
  408. --
  409. -- print ("\nInput table:")
  410. -- for ii, value in ipairs (tabby) do
  411. -- print (ii .. ") " .. tabby [ii])
  412. -- end
  413. --
  414. -- tabby = ocutil.vtable_to_ktable (tabby, 42)
  415. --
  416. -- print ("\nOutput table:")
  417. -- for ii, value in pairs (tabby) do
  418. -- print (ii .. ") " .. tabby [ii])
  419. -- end
  420. -- ===================================================================
  421. ocutil.vtable_to_ktable = function (vtable, set_to)
  422. local ktable = {}
  423. if vtable == nil then return ktable end
  424. if set_to == nil then set_to = true end
  425. for _, value in pairs (vtable) do
  426. ktable [value] = set_to
  427. end
  428. return ktable
  429. end
  430. -- ===================================================================
  431. -- Function: ocutil.make_set
  432. -- Params: array harry
  433. -- Outputs:
  434. --
  435. -- This function makes a set out of an array. Specifically, it returns
  436. -- a new table with the array's values as keys. The new table maps
  437. -- each key to boolean true.
  438. --
  439. -- Here's a complete code fragment which demonstrates operation of
  440. -- this function:
  441. --
  442. -- local color_set = ocutil.make_set { "red", "green", "blue" }
  443. -- if color_set ["red"] ~= nil then print ("Supported color") end
  444. -- ===================================================================
  445. ocutil.make_set = function (list)
  446. local set = {}
  447. for _, l in ipairs (list) do set [l] = true end
  448. return set
  449. end
  450. -- ===================================================================
  451. -- Function: ocutil.pos_to_str
  452. -- Params: position-table pos
  453. -- Outputs:
  454. --
  455. -- This function takes a table of the following form as input:
  456. -- { x=25.0, y=-135.5, z=2750.0 }
  457. --
  458. -- It returns the x-y-z values, separated by commas, as a string.
  459. -- ===================================================================
  460. ocutil.pos_to_str = function (pos)
  461. return pos.x .. "," .. pos.y .. "," .. pos.z
  462. end
  463. -- ===================================================================
  464. -- Function: ocutil.table_length
  465. -- Params: table tabby
  466. -- Outputs: Returns number of entries in table
  467. --
  468. -- Note: This function works for tables in general as well as for int-
  469. -- eger-indexed tables (i.e., arrays).
  470. -- ===================================================================
  471. ocutil.table_length = function (tabby)
  472. local count = 0
  473. for _ in pairs (tabby) do count = count+1 end
  474. return count
  475. end
  476. -- ===================================================================
  477. -- Function: ocutil.clone_table
  478. -- Params: table tabby
  479. -- Outputs:
  480. --
  481. -- This function returns an independent clone of the input table. It
  482. -- should work correctly for most cases, but special cases that re-
  483. -- quire additional work may exist.
  484. --
  485. -- This function may also be called as "ocutil.table_clone".
  486. -- ===================================================================
  487. ocutil.clone_table = function (tabby)
  488. if tabby == nil then return nil end
  489. local copy = {}
  490. for k, v in pairs (tabby) do
  491. if type (v) == 'table' then v = ocutil.clone_table (v) end
  492. copy [k] = v
  493. end
  494. return copy
  495. end
  496. ocutil.table_clone = ocutil.clone_table
  497. -- ===================================================================
  498. -- Function: ocutil.file_exists
  499. -- Params: string path
  500. -- Outputs:
  501. --
  502. -- The input string should be the relative or absolute pathname for a
  503. -- data file. If the file is read-accessible, this function returns
  504. -- true. Otherwise, it returns false.
  505. -- ===================================================================
  506. ocutil.file_exists = function (path)
  507. local file, err
  508. file, err = io.open (path, "rb")
  509. if err then return false end
  510. file:close()
  511. return true
  512. end
  513. -- ===================================================================
  514. -- Function: ocutil.file_missing
  515. -- Params: string path
  516. -- Outputs:
  517. --
  518. -- The input string should be the relative or absolute pathname for a
  519. -- data file. If the file is read-accessible, this function returns
  520. -- false. Otherwise, it returns true.
  521. -- ===================================================================
  522. ocutil.file_missing = function (path)
  523. if ocutil.file_exists (path) then return false end
  524. return true
  525. end
  526. -- ===================================================================
  527. -- Function: ocutil.file_read
  528. -- Params: string path
  529. -- Outputs:
  530. --
  531. -- The input string should be the absolute pathname for a data file.
  532. -- If the file is read-accessible, this function returns the contents
  533. -- of the file. Otherwise, it returns nil.
  534. --
  535. -- Warning: There is presently no check for a maximum file size.
  536. -- ===================================================================
  537. ocutil.file_read = function (path)
  538. local file, err
  539. file, err = io.open (path, "rb")
  540. if err then return nil end
  541. local data = file:read ("*a")
  542. file:close()
  543. return data
  544. end
  545. -- ===================================================================
  546. -- Function: ocutil.file_write
  547. -- Params: strings path, data
  548. -- Outputs:
  549. --
  550. -- The 1st string should be the absolute pathname for a data file. The
  551. -- file doesn't need to exist initially but the directory that will
  552. -- contain it does need to exist.
  553. --
  554. -- This function writes the 2nd string to the file. Existing stored
  555. -- data is discarded.
  556. --
  557. -- This function returns true if successful and false otherwise.
  558. -- ===================================================================
  559. ocutil.file_write = function (path, data)
  560. local file, err
  561. file, err = io.open (path, "wb")
  562. if err then return false end
  563. io.output (file)
  564. io.write (data)
  565. io.close (file)
  566. return true
  567. end
  568. -- ===================================================================
  569. -- Function: ocutil.file_append
  570. -- Params: strings path, data
  571. -- Outputs:
  572. --
  573. -- This function is identical to "ocutil.file_write" except for one
  574. -- difference: It appends to existing files as opposed to overwrites
  575. -- them.
  576. -- ===================================================================
  577. ocutil.file_append = function (path, data)
  578. local file, err
  579. file, err = io.open (path, "ab")
  580. if err then return false end
  581. io.output (file)
  582. io.write (data)
  583. io.close (file)
  584. return true
  585. end
  586. -- ===================================================================
  587. -- End of file.