dict.lua 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. -----------------------------------------------------------------------------
  2. -- Little program to download DICT word definitions
  3. -- LuaSocket sample files
  4. -- Author: Diego Nehab
  5. -----------------------------------------------------------------------------
  6. -----------------------------------------------------------------------------
  7. -- Load required modules
  8. -----------------------------------------------------------------------------
  9. local base = _G
  10. local string = require("string")
  11. local table = require("table")
  12. local socket = require("socket")
  13. local url = require("socket.url")
  14. local tp = require("socket.tp")
  15. module("socket.dict")
  16. -----------------------------------------------------------------------------
  17. -- Globals
  18. -----------------------------------------------------------------------------
  19. HOST = "dict.org"
  20. PORT = 2628
  21. TIMEOUT = 10
  22. -----------------------------------------------------------------------------
  23. -- Low-level dict API
  24. -----------------------------------------------------------------------------
  25. local metat = { __index = {} }
  26. function open(host, port)
  27. local tp = socket.try(tp.connect(host or HOST, port or PORT, TIMEOUT))
  28. return base.setmetatable({tp = tp}, metat)
  29. end
  30. function metat.__index:greet()
  31. return socket.try(self.tp:check(220))
  32. end
  33. function metat.__index:check(ok)
  34. local code, status = socket.try(self.tp:check(ok))
  35. return code,
  36. base.tonumber(socket.skip(2, string.find(status, "^%d%d%d (%d*)")))
  37. end
  38. function metat.__index:getdef()
  39. local line = socket.try(self.tp:receive())
  40. local def = {}
  41. while line ~= "." do
  42. table.insert(def, line)
  43. line = socket.try(self.tp:receive())
  44. end
  45. return table.concat(def, "\n")
  46. end
  47. function metat.__index:define(database, word)
  48. database = database or "!"
  49. socket.try(self.tp:command("DEFINE", database .. " " .. word))
  50. local code, count = self:check(150)
  51. local defs = {}
  52. for i = 1, count do
  53. self:check(151)
  54. table.insert(defs, self:getdef())
  55. end
  56. self:check(250)
  57. return defs
  58. end
  59. function metat.__index:match(database, strat, word)
  60. database = database or "!"
  61. strat = strat or "."
  62. socket.try(self.tp:command("MATCH", database .." ".. strat .." ".. word))
  63. self:check(152)
  64. local mat = {}
  65. local line = socket.try(self.tp:receive())
  66. while line ~= '.' do
  67. database, word = socket.skip(2, string.find(line, "(%S+) (.*)"))
  68. if not mat[database] then mat[database] = {} end
  69. table.insert(mat[database], word)
  70. line = socket.try(self.tp:receive())
  71. end
  72. self:check(250)
  73. return mat
  74. end
  75. function metat.__index:quit()
  76. self.tp:command("QUIT")
  77. return self:check(221)
  78. end
  79. function metat.__index:close()
  80. return self.tp:close()
  81. end
  82. -----------------------------------------------------------------------------
  83. -- High-level dict API
  84. -----------------------------------------------------------------------------
  85. local default = {
  86. scheme = "dict",
  87. host = "dict.org"
  88. }
  89. local function there(f)
  90. if f == "" then return nil
  91. else return f end
  92. end
  93. local function parse(u)
  94. local t = socket.try(url.parse(u, default))
  95. socket.try(t.scheme == "dict", "invalid scheme '" .. t.scheme .. "'")
  96. socket.try(t.path, "invalid path in url")
  97. local cmd, arg = socket.skip(2, string.find(t.path, "^/(.)(.*)$"))
  98. socket.try(cmd == "d" or cmd == "m", "<command> should be 'm' or 'd'")
  99. socket.try(arg and arg ~= "", "need at least <word> in URL")
  100. t.command, t.argument = cmd, arg
  101. arg = string.gsub(arg, "^:([^:]+)", function(f) t.word = f end)
  102. socket.try(t.word, "need at least <word> in URL")
  103. arg = string.gsub(arg, "^:([^:]*)", function(f) t.database = there(f) end)
  104. if cmd == "m" then
  105. arg = string.gsub(arg, "^:([^:]*)", function(f) t.strat = there(f) end)
  106. end
  107. string.gsub(arg, ":([^:]*)$", function(f) t.n = base.tonumber(f) end)
  108. return t
  109. end
  110. local function tget(gett)
  111. local con = open(gett.host, gett.port)
  112. con:greet()
  113. if gett.command == "d" then
  114. local def = con:define(gett.database, gett.word)
  115. con:quit()
  116. con:close()
  117. if gett.n then return def[gett.n]
  118. else return def end
  119. elseif gett.command == "m" then
  120. local mat = con:match(gett.database, gett.strat, gett.word)
  121. con:quit()
  122. con:close()
  123. return mat
  124. else return nil, "invalid command" end
  125. end
  126. local function sget(u)
  127. local gett = parse(u)
  128. return tget(gett)
  129. end
  130. get = socket.protect(function(gett)
  131. if base.type(gett) == "string" then return sget(gett)
  132. else return tget(gett) end
  133. end)