gitlab-pygments.rb 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (399) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +2 -0
  3. data/README.md +91 -0
  4. data/Rakefile +78 -0
  5. data/bench.rb +22 -0
  6. data/cache-lexers.rb +8 -0
  7. data/lexers +0 -0
  8. data/lib/pygments/lexer.rb +148 -0
  9. data/lib/pygments/mentos.py +344 -0
  10. data/lib/pygments/popen.rb +389 -0
  11. data/lib/pygments/version.rb +3 -0
  12. data/lib/pygments.rb +8 -0
  13. data/pygments.rb.gemspec +24 -0
  14. data/test/test_data.c +2581 -0
  15. data/test/test_data.py +514 -0
  16. data/test/test_data_generated +2582 -0
  17. data/test/test_pygments.rb +276 -0
  18. data/vendor/custom_formatters/gitlab.py +171 -0
  19. data/vendor/custom_lexers/github.py +362 -0
  20. data/vendor/pygments-main/AUTHORS +115 -0
  21. data/vendor/pygments-main/CHANGES +762 -0
  22. data/vendor/pygments-main/LICENSE +25 -0
  23. data/vendor/pygments-main/MANIFEST.in +6 -0
  24. data/vendor/pygments-main/Makefile +59 -0
  25. data/vendor/pygments-main/REVISION +1 -0
  26. data/vendor/pygments-main/TODO +15 -0
  27. data/vendor/pygments-main/docs/generate.py +472 -0
  28. data/vendor/pygments-main/docs/pygmentize.1 +94 -0
  29. data/vendor/pygments-main/docs/src/api.txt +270 -0
  30. data/vendor/pygments-main/docs/src/authors.txt +5 -0
  31. data/vendor/pygments-main/docs/src/changelog.txt +5 -0
  32. data/vendor/pygments-main/docs/src/cmdline.txt +147 -0
  33. data/vendor/pygments-main/docs/src/filterdevelopment.txt +70 -0
  34. data/vendor/pygments-main/docs/src/filters.txt +42 -0
  35. data/vendor/pygments-main/docs/src/formatterdevelopment.txt +169 -0
  36. data/vendor/pygments-main/docs/src/formatters.txt +48 -0
  37. data/vendor/pygments-main/docs/src/index.txt +69 -0
  38. data/vendor/pygments-main/docs/src/installation.txt +71 -0
  39. data/vendor/pygments-main/docs/src/integrate.txt +43 -0
  40. data/vendor/pygments-main/docs/src/lexerdevelopment.txt +551 -0
  41. data/vendor/pygments-main/docs/src/lexers.txt +67 -0
  42. data/vendor/pygments-main/docs/src/moinmoin.txt +39 -0
  43. data/vendor/pygments-main/docs/src/plugins.txt +93 -0
  44. data/vendor/pygments-main/docs/src/quickstart.txt +202 -0
  45. data/vendor/pygments-main/docs/src/rstdirective.txt +22 -0
  46. data/vendor/pygments-main/docs/src/styles.txt +143 -0
  47. data/vendor/pygments-main/docs/src/tokens.txt +349 -0
  48. data/vendor/pygments-main/docs/src/unicode.txt +49 -0
  49. data/vendor/pygments-main/external/markdown-processor.py +67 -0
  50. data/vendor/pygments-main/external/moin-parser.py +112 -0
  51. data/vendor/pygments-main/external/pygments.bashcomp +38 -0
  52. data/vendor/pygments-main/external/rst-directive-old.py +77 -0
  53. data/vendor/pygments-main/external/rst-directive.py +83 -0
  54. data/vendor/pygments-main/ez_setup.py +276 -0
  55. data/vendor/pygments-main/pygmentize +7 -0
  56. data/vendor/pygments-main/pygments/__init__.py +91 -0
  57. data/vendor/pygments-main/pygments/cmdline.py +433 -0
  58. data/vendor/pygments-main/pygments/console.py +74 -0
  59. data/vendor/pygments-main/pygments/filter.py +74 -0
  60. data/vendor/pygments-main/pygments/filters/__init__.py +357 -0
  61. data/vendor/pygments-main/pygments/formatter.py +92 -0
  62. data/vendor/pygments-main/pygments/formatters/__init__.py +68 -0
  63. data/vendor/pygments-main/pygments/formatters/_mapping.py +94 -0
  64. data/vendor/pygments-main/pygments/formatters/bbcode.py +109 -0
  65. data/vendor/pygments-main/pygments/formatters/gitlab.py +171 -0
  66. data/vendor/pygments-main/pygments/formatters/html.py +750 -0
  67. data/vendor/pygments-main/pygments/formatters/img.py +553 -0
  68. data/vendor/pygments-main/pygments/formatters/latex.py +378 -0
  69. data/vendor/pygments-main/pygments/formatters/other.py +117 -0
  70. data/vendor/pygments-main/pygments/formatters/rtf.py +136 -0
  71. data/vendor/pygments-main/pygments/formatters/svg.py +154 -0
  72. data/vendor/pygments-main/pygments/formatters/terminal.py +112 -0
  73. data/vendor/pygments-main/pygments/formatters/terminal256.py +222 -0
  74. data/vendor/pygments-main/pygments/lexer.py +697 -0
  75. data/vendor/pygments-main/pygments/lexers/__init__.py +229 -0
  76. data/vendor/pygments-main/pygments/lexers/_asybuiltins.py +1645 -0
  77. data/vendor/pygments-main/pygments/lexers/_clbuiltins.py +232 -0
  78. data/vendor/pygments-main/pygments/lexers/_luabuiltins.py +249 -0
  79. data/vendor/pygments-main/pygments/lexers/_mapping.py +298 -0
  80. data/vendor/pygments-main/pygments/lexers/_phpbuiltins.py +3787 -0
  81. data/vendor/pygments-main/pygments/lexers/_postgres_builtins.py +232 -0
  82. data/vendor/pygments-main/pygments/lexers/_scilab_builtins.py +29 -0
  83. data/vendor/pygments-main/pygments/lexers/_vimbuiltins.py +3 -0
  84. data/vendor/pygments-main/pygments/lexers/agile.py +1803 -0
  85. data/vendor/pygments-main/pygments/lexers/asm.py +360 -0
  86. data/vendor/pygments-main/pygments/lexers/compiled.py +2891 -0
  87. data/vendor/pygments-main/pygments/lexers/dotnet.py +636 -0
  88. data/vendor/pygments-main/pygments/lexers/functional.py +1832 -0
  89. data/vendor/pygments-main/pygments/lexers/github.py +362 -0
  90. data/vendor/pygments-main/pygments/lexers/hdl.py +356 -0
  91. data/vendor/pygments-main/pygments/lexers/jvm.py +847 -0
  92. data/vendor/pygments-main/pygments/lexers/math.py +1072 -0
  93. data/vendor/pygments-main/pygments/lexers/other.py +3339 -0
  94. data/vendor/pygments-main/pygments/lexers/parsers.py +695 -0
  95. data/vendor/pygments-main/pygments/lexers/shell.py +361 -0
  96. data/vendor/pygments-main/pygments/lexers/special.py +100 -0
  97. data/vendor/pygments-main/pygments/lexers/sql.py +559 -0
  98. data/vendor/pygments-main/pygments/lexers/templates.py +1631 -0
  99. data/vendor/pygments-main/pygments/lexers/text.py +1753 -0
  100. data/vendor/pygments-main/pygments/lexers/web.py +2864 -0
  101. data/vendor/pygments-main/pygments/plugin.py +74 -0
  102. data/vendor/pygments-main/pygments/scanner.py +104 -0
  103. data/vendor/pygments-main/pygments/style.py +117 -0
  104. data/vendor/pygments-main/pygments/styles/__init__.py +70 -0
  105. data/vendor/pygments-main/pygments/styles/autumn.py +65 -0
  106. data/vendor/pygments-main/pygments/styles/borland.py +51 -0
  107. data/vendor/pygments-main/pygments/styles/bw.py +49 -0
  108. data/vendor/pygments-main/pygments/styles/colorful.py +81 -0
  109. data/vendor/pygments-main/pygments/styles/default.py +73 -0
  110. data/vendor/pygments-main/pygments/styles/emacs.py +72 -0
  111. data/vendor/pygments-main/pygments/styles/friendly.py +72 -0
  112. data/vendor/pygments-main/pygments/styles/fruity.py +42 -0
  113. data/vendor/pygments-main/pygments/styles/manni.py +75 -0
  114. data/vendor/pygments-main/pygments/styles/monokai.py +106 -0
  115. data/vendor/pygments-main/pygments/styles/murphy.py +80 -0
  116. data/vendor/pygments-main/pygments/styles/native.py +65 -0
  117. data/vendor/pygments-main/pygments/styles/pastie.py +75 -0
  118. data/vendor/pygments-main/pygments/styles/perldoc.py +69 -0
  119. data/vendor/pygments-main/pygments/styles/rrt.py +33 -0
  120. data/vendor/pygments-main/pygments/styles/tango.py +141 -0
  121. data/vendor/pygments-main/pygments/styles/trac.py +63 -0
  122. data/vendor/pygments-main/pygments/styles/vim.py +63 -0
  123. data/vendor/pygments-main/pygments/styles/vs.py +38 -0
  124. data/vendor/pygments-main/pygments/token.py +195 -0
  125. data/vendor/pygments-main/pygments/unistring.py +130 -0
  126. data/vendor/pygments-main/pygments/util.py +232 -0
  127. data/vendor/pygments-main/scripts/check_sources.py +242 -0
  128. data/vendor/pygments-main/scripts/detect_missing_analyse_text.py +30 -0
  129. data/vendor/pygments-main/scripts/epydoc.css +280 -0
  130. data/vendor/pygments-main/scripts/find_codetags.py +205 -0
  131. data/vendor/pygments-main/scripts/find_error.py +171 -0
  132. data/vendor/pygments-main/scripts/get_vimkw.py +43 -0
  133. data/vendor/pygments-main/scripts/pylintrc +301 -0
  134. data/vendor/pygments-main/scripts/reindent.py +291 -0
  135. data/vendor/pygments-main/scripts/vim2pygments.py +933 -0
  136. data/vendor/pygments-main/setup.cfg +6 -0
  137. data/vendor/pygments-main/setup.py +88 -0
  138. data/vendor/pygments-main/tests/dtds/HTML4-f.dtd +37 -0
  139. data/vendor/pygments-main/tests/dtds/HTML4-s.dtd +869 -0
  140. data/vendor/pygments-main/tests/dtds/HTML4.dcl +88 -0
  141. data/vendor/pygments-main/tests/dtds/HTML4.dtd +1092 -0
  142. data/vendor/pygments-main/tests/dtds/HTML4.soc +9 -0
  143. data/vendor/pygments-main/tests/dtds/HTMLlat1.ent +195 -0
  144. data/vendor/pygments-main/tests/dtds/HTMLspec.ent +77 -0
  145. data/vendor/pygments-main/tests/dtds/HTMLsym.ent +241 -0
  146. data/vendor/pygments-main/tests/examplefiles/ANTLRv3.g +608 -0
  147. data/vendor/pygments-main/tests/examplefiles/AcidStateAdvanced.hs +209 -0
  148. data/vendor/pygments-main/tests/examplefiles/AlternatingGroup.mu +102 -0
  149. data/vendor/pygments-main/tests/examplefiles/CPDictionary.j +611 -0
  150. data/vendor/pygments-main/tests/examplefiles/Constants.mo +158 -0
  151. data/vendor/pygments-main/tests/examplefiles/DancingSudoku.lhs +411 -0
  152. data/vendor/pygments-main/tests/examplefiles/Errors.scala +18 -0
  153. data/vendor/pygments-main/tests/examplefiles/File.hy +174 -0
  154. data/vendor/pygments-main/tests/examplefiles/Intro.java +1660 -0
  155. data/vendor/pygments-main/tests/examplefiles/Makefile +1131 -0
  156. data/vendor/pygments-main/tests/examplefiles/Object.st +4394 -0
  157. data/vendor/pygments-main/tests/examplefiles/OrderedMap.hx +584 -0
  158. data/vendor/pygments-main/tests/examplefiles/SmallCheck.hs +378 -0
  159. data/vendor/pygments-main/tests/examplefiles/Sorting.mod +470 -0
  160. data/vendor/pygments-main/tests/examplefiles/Sudoku.lhs +382 -0
  161. data/vendor/pygments-main/tests/examplefiles/addressbook.proto +30 -0
  162. data/vendor/pygments-main/tests/examplefiles/antlr_throws +1 -0
  163. data/vendor/pygments-main/tests/examplefiles/apache2.conf +393 -0
  164. data/vendor/pygments-main/tests/examplefiles/as3_test.as +143 -0
  165. data/vendor/pygments-main/tests/examplefiles/as3_test2.as +46 -0
  166. data/vendor/pygments-main/tests/examplefiles/as3_test3.as +3 -0
  167. data/vendor/pygments-main/tests/examplefiles/aspx-cs_example +27 -0
  168. data/vendor/pygments-main/tests/examplefiles/badcase.java +2 -0
  169. data/vendor/pygments-main/tests/examplefiles/batchfile.bat +49 -0
  170. data/vendor/pygments-main/tests/examplefiles/boot-9.scm +1557 -0
  171. data/vendor/pygments-main/tests/examplefiles/cells.ps +515 -0
  172. data/vendor/pygments-main/tests/examplefiles/ceval.c +2604 -0
  173. data/vendor/pygments-main/tests/examplefiles/cheetah_example.html +13 -0
  174. data/vendor/pygments-main/tests/examplefiles/classes.dylan +40 -0
  175. data/vendor/pygments-main/tests/examplefiles/condensed_ruby.rb +10 -0
  176. data/vendor/pygments-main/tests/examplefiles/coq_RelationClasses +447 -0
  177. data/vendor/pygments-main/tests/examplefiles/database.pytb +20 -0
  178. data/vendor/pygments-main/tests/examplefiles/de.MoinMoin.po +2461 -0
  179. data/vendor/pygments-main/tests/examplefiles/demo.ahk +181 -0
  180. data/vendor/pygments-main/tests/examplefiles/demo.cfm +38 -0
  181. data/vendor/pygments-main/tests/examplefiles/django_sample.html+django +68 -0
  182. data/vendor/pygments-main/tests/examplefiles/dwarf.cw +17 -0
  183. data/vendor/pygments-main/tests/examplefiles/erl_session +10 -0
  184. data/vendor/pygments-main/tests/examplefiles/escape_semicolon.clj +1 -0
  185. data/vendor/pygments-main/tests/examplefiles/evil_regex.js +48 -0
  186. data/vendor/pygments-main/tests/examplefiles/example.c +2080 -0
  187. data/vendor/pygments-main/tests/examplefiles/example.cls +15 -0
  188. data/vendor/pygments-main/tests/examplefiles/example.cpp +2363 -0
  189. data/vendor/pygments-main/tests/examplefiles/example.gs +106 -0
  190. data/vendor/pygments-main/tests/examplefiles/example.gst +7 -0
  191. data/vendor/pygments-main/tests/examplefiles/example.kt +47 -0
  192. data/vendor/pygments-main/tests/examplefiles/example.lua +250 -0
  193. data/vendor/pygments-main/tests/examplefiles/example.moo +26 -0
  194. data/vendor/pygments-main/tests/examplefiles/example.moon +629 -0
  195. data/vendor/pygments-main/tests/examplefiles/example.nim +1010 -0
  196. data/vendor/pygments-main/tests/examplefiles/example.ns2 +69 -0
  197. data/vendor/pygments-main/tests/examplefiles/example.p +34 -0
  198. data/vendor/pygments-main/tests/examplefiles/example.pas +2708 -0
  199. data/vendor/pygments-main/tests/examplefiles/example.rb +1852 -0
  200. data/vendor/pygments-main/tests/examplefiles/example.rhtml +561 -0
  201. data/vendor/pygments-main/tests/examplefiles/example.sh-session +19 -0
  202. data/vendor/pygments-main/tests/examplefiles/example.sml +156 -0
  203. data/vendor/pygments-main/tests/examplefiles/example.snobol +15 -0
  204. data/vendor/pygments-main/tests/examplefiles/example.tea +34 -0
  205. data/vendor/pygments-main/tests/examplefiles/example.u +548 -0
  206. data/vendor/pygments-main/tests/examplefiles/example.weechatlog +9 -0
  207. data/vendor/pygments-main/tests/examplefiles/example.xhtml +376 -0
  208. data/vendor/pygments-main/tests/examplefiles/example.yaml +302 -0
  209. data/vendor/pygments-main/tests/examplefiles/example2.aspx +29 -0
  210. data/vendor/pygments-main/tests/examplefiles/example_elixir.ex +363 -0
  211. data/vendor/pygments-main/tests/examplefiles/example_file.fy +128 -0
  212. data/vendor/pygments-main/tests/examplefiles/firefox.mak +586 -0
  213. data/vendor/pygments-main/tests/examplefiles/flipflop.sv +19 -0
  214. data/vendor/pygments-main/tests/examplefiles/foo.sce +6 -0
  215. data/vendor/pygments-main/tests/examplefiles/format.ml +1213 -0
  216. data/vendor/pygments-main/tests/examplefiles/fucked_up.rb +77 -0
  217. data/vendor/pygments-main/tests/examplefiles/function.mu +1 -0
  218. data/vendor/pygments-main/tests/examplefiles/functional.rst +1472 -0
  219. data/vendor/pygments-main/tests/examplefiles/genclass.clj +510 -0
  220. data/vendor/pygments-main/tests/examplefiles/genshi_example.xml+genshi +193 -0
  221. data/vendor/pygments-main/tests/examplefiles/genshitext_example.genshitext +33 -0
  222. data/vendor/pygments-main/tests/examplefiles/glsl.frag +7 -0
  223. data/vendor/pygments-main/tests/examplefiles/glsl.vert +13 -0
  224. data/vendor/pygments-main/tests/examplefiles/html+php_faulty.php +1 -0
  225. data/vendor/pygments-main/tests/examplefiles/http_request_example +14 -0
  226. data/vendor/pygments-main/tests/examplefiles/http_response_example +27 -0
  227. data/vendor/pygments-main/tests/examplefiles/import.hs +4 -0
  228. data/vendor/pygments-main/tests/examplefiles/intro.ik +24 -0
  229. data/vendor/pygments-main/tests/examplefiles/ints.php +10 -0
  230. data/vendor/pygments-main/tests/examplefiles/intsyn.fun +675 -0
  231. data/vendor/pygments-main/tests/examplefiles/intsyn.sig +286 -0
  232. data/vendor/pygments-main/tests/examplefiles/irb_heredoc +8 -0
  233. data/vendor/pygments-main/tests/examplefiles/irc.lsp +214 -0
  234. data/vendor/pygments-main/tests/examplefiles/java.properties +16 -0
  235. data/vendor/pygments-main/tests/examplefiles/jbst_example1.jbst +28 -0
  236. data/vendor/pygments-main/tests/examplefiles/jbst_example2.jbst +45 -0
  237. data/vendor/pygments-main/tests/examplefiles/jinjadesignerdoc.rst +713 -0
  238. data/vendor/pygments-main/tests/examplefiles/lighttpd_config.conf +13 -0
  239. data/vendor/pygments-main/tests/examplefiles/linecontinuation.py +47 -0
  240. data/vendor/pygments-main/tests/examplefiles/ltmain.sh +2849 -0
  241. data/vendor/pygments-main/tests/examplefiles/main.cmake +42 -0
  242. data/vendor/pygments-main/tests/examplefiles/markdown.lsp +679 -0
  243. data/vendor/pygments-main/tests/examplefiles/matlab_noreturn +3 -0
  244. data/vendor/pygments-main/tests/examplefiles/matlab_sample +27 -0
  245. data/vendor/pygments-main/tests/examplefiles/matlabsession_sample.txt +37 -0
  246. data/vendor/pygments-main/tests/examplefiles/minimal.ns2 +4 -0
  247. data/vendor/pygments-main/tests/examplefiles/moin_SyntaxReference.txt +340 -0
  248. data/vendor/pygments-main/tests/examplefiles/multiline_regexes.rb +38 -0
  249. data/vendor/pygments-main/tests/examplefiles/nasm_aoutso.asm +96 -0
  250. data/vendor/pygments-main/tests/examplefiles/nasm_objexe.asm +30 -0
  251. data/vendor/pygments-main/tests/examplefiles/nemerle_sample.n +87 -0
  252. data/vendor/pygments-main/tests/examplefiles/nginx_nginx.conf +118 -0
  253. data/vendor/pygments-main/tests/examplefiles/numbers.c +12 -0
  254. data/vendor/pygments-main/tests/examplefiles/objc_example.m +25 -0
  255. data/vendor/pygments-main/tests/examplefiles/objc_example2.m +24 -0
  256. data/vendor/pygments-main/tests/examplefiles/perl_misc +62 -0
  257. data/vendor/pygments-main/tests/examplefiles/perl_perl5db +998 -0
  258. data/vendor/pygments-main/tests/examplefiles/perl_regex-delims +120 -0
  259. data/vendor/pygments-main/tests/examplefiles/perlfunc.1 +856 -0
  260. data/vendor/pygments-main/tests/examplefiles/phpcomplete.vim +567 -0
  261. data/vendor/pygments-main/tests/examplefiles/pleac.in.rb +1223 -0
  262. data/vendor/pygments-main/tests/examplefiles/postgresql_test.txt +47 -0
  263. data/vendor/pygments-main/tests/examplefiles/pppoe.applescript +10 -0
  264. data/vendor/pygments-main/tests/examplefiles/psql_session.txt +122 -0
  265. data/vendor/pygments-main/tests/examplefiles/py3_test.txt +2 -0
  266. data/vendor/pygments-main/tests/examplefiles/pycon_test.pycon +14 -0
  267. data/vendor/pygments-main/tests/examplefiles/pytb_test2.pytb +2 -0
  268. data/vendor/pygments-main/tests/examplefiles/python25-bsd.mak +234 -0
  269. data/vendor/pygments-main/tests/examplefiles/qsort.prolog +13 -0
  270. data/vendor/pygments-main/tests/examplefiles/r-console-transcript.Rout +38 -0
  271. data/vendor/pygments-main/tests/examplefiles/ragel-cpp_rlscan +280 -0
  272. data/vendor/pygments-main/tests/examplefiles/ragel-cpp_snippet +2 -0
  273. data/vendor/pygments-main/tests/examplefiles/regex.js +22 -0
  274. data/vendor/pygments-main/tests/examplefiles/reversi.lsp +427 -0
  275. data/vendor/pygments-main/tests/examplefiles/ruby_func_def.rb +11 -0
  276. data/vendor/pygments-main/tests/examplefiles/scilab.sci +30 -0
  277. data/vendor/pygments-main/tests/examplefiles/sibling.prolog +19 -0
  278. data/vendor/pygments-main/tests/examplefiles/simple.md +747 -0
  279. data/vendor/pygments-main/tests/examplefiles/smarty_example.html +209 -0
  280. data/vendor/pygments-main/tests/examplefiles/source.lgt +343 -0
  281. data/vendor/pygments-main/tests/examplefiles/sources.list +62 -0
  282. data/vendor/pygments-main/tests/examplefiles/sphere.pov +18 -0
  283. data/vendor/pygments-main/tests/examplefiles/sqlite3.sqlite3-console +27 -0
  284. data/vendor/pygments-main/tests/examplefiles/squid.conf +30 -0
  285. data/vendor/pygments-main/tests/examplefiles/string.jl +1031 -0
  286. data/vendor/pygments-main/tests/examplefiles/string_delimiters.d +21 -0
  287. data/vendor/pygments-main/tests/examplefiles/stripheredoc.sh +3 -0
  288. data/vendor/pygments-main/tests/examplefiles/test.R +119 -0
  289. data/vendor/pygments-main/tests/examplefiles/test.adb +211 -0
  290. data/vendor/pygments-main/tests/examplefiles/test.asy +131 -0
  291. data/vendor/pygments-main/tests/examplefiles/test.awk +121 -0
  292. data/vendor/pygments-main/tests/examplefiles/test.bas +29 -0
  293. data/vendor/pygments-main/tests/examplefiles/test.bmx +145 -0
  294. data/vendor/pygments-main/tests/examplefiles/test.boo +39 -0
  295. data/vendor/pygments-main/tests/examplefiles/test.bro +250 -0
  296. data/vendor/pygments-main/tests/examplefiles/test.cs +374 -0
  297. data/vendor/pygments-main/tests/examplefiles/test.css +54 -0
  298. data/vendor/pygments-main/tests/examplefiles/test.d +135 -0
  299. data/vendor/pygments-main/tests/examplefiles/test.dart +23 -0
  300. data/vendor/pygments-main/tests/examplefiles/test.dtd +89 -0
  301. data/vendor/pygments-main/tests/examplefiles/test.ec +605 -0
  302. data/vendor/pygments-main/tests/examplefiles/test.ecl +58 -0
  303. data/vendor/pygments-main/tests/examplefiles/test.eh +315 -0
  304. data/vendor/pygments-main/tests/examplefiles/test.erl +169 -0
  305. data/vendor/pygments-main/tests/examplefiles/test.evoque +33 -0
  306. data/vendor/pygments-main/tests/examplefiles/test.fan +818 -0
  307. data/vendor/pygments-main/tests/examplefiles/test.flx +57 -0
  308. data/vendor/pygments-main/tests/examplefiles/test.gdc +13 -0
  309. data/vendor/pygments-main/tests/examplefiles/test.groovy +97 -0
  310. data/vendor/pygments-main/tests/examplefiles/test.html +339 -0
  311. data/vendor/pygments-main/tests/examplefiles/test.ini +10 -0
  312. data/vendor/pygments-main/tests/examplefiles/test.java +653 -0
  313. data/vendor/pygments-main/tests/examplefiles/test.jsp +24 -0
  314. data/vendor/pygments-main/tests/examplefiles/test.maql +45 -0
  315. data/vendor/pygments-main/tests/examplefiles/test.mod +374 -0
  316. data/vendor/pygments-main/tests/examplefiles/test.moo +51 -0
  317. data/vendor/pygments-main/tests/examplefiles/test.myt +166 -0
  318. data/vendor/pygments-main/tests/examplefiles/test.nim +93 -0
  319. data/vendor/pygments-main/tests/examplefiles/test.pas +743 -0
  320. data/vendor/pygments-main/tests/examplefiles/test.php +505 -0
  321. data/vendor/pygments-main/tests/examplefiles/test.plot +333 -0
  322. data/vendor/pygments-main/tests/examplefiles/test.ps1 +108 -0
  323. data/vendor/pygments-main/tests/examplefiles/test.pypylog +1839 -0
  324. data/vendor/pygments-main/tests/examplefiles/test.r3 +94 -0
  325. data/vendor/pygments-main/tests/examplefiles/test.rb +177 -0
  326. data/vendor/pygments-main/tests/examplefiles/test.rhtml +43 -0
  327. data/vendor/pygments-main/tests/examplefiles/test.scaml +8 -0
  328. data/vendor/pygments-main/tests/examplefiles/test.ssp +12 -0
  329. data/vendor/pygments-main/tests/examplefiles/test.tcsh +830 -0
  330. data/vendor/pygments-main/tests/examplefiles/test.vb +407 -0
  331. data/vendor/pygments-main/tests/examplefiles/test.vhdl +161 -0
  332. data/vendor/pygments-main/tests/examplefiles/test.xqy +138 -0
  333. data/vendor/pygments-main/tests/examplefiles/test.xsl +23 -0
  334. data/vendor/pygments-main/tests/examplefiles/truncated.pytb +15 -0
  335. data/vendor/pygments-main/tests/examplefiles/type.lisp +1202 -0
  336. data/vendor/pygments-main/tests/examplefiles/underscore.coffee +603 -0
  337. data/vendor/pygments-main/tests/examplefiles/unicode.applescript +5 -0
  338. data/vendor/pygments-main/tests/examplefiles/unicodedoc.py +11 -0
  339. data/vendor/pygments-main/tests/examplefiles/webkit-transition.css +3 -0
  340. data/vendor/pygments-main/tests/examplefiles/while.pov +13 -0
  341. data/vendor/pygments-main/tests/examplefiles/wiki.factor +384 -0
  342. data/vendor/pygments-main/tests/examplefiles/xml_example +1897 -0
  343. data/vendor/pygments-main/tests/examplefiles/zmlrpc.f90 +798 -0
  344. data/vendor/pygments-main/tests/old_run.py +138 -0
  345. data/vendor/pygments-main/tests/run.py +48 -0
  346. data/vendor/pygments-main/tests/support.py +15 -0
  347. data/vendor/pygments-main/tests/test_basic_api.py +294 -0
  348. data/vendor/pygments-main/tests/test_clexer.py +31 -0
  349. data/vendor/pygments-main/tests/test_cmdline.py +105 -0
  350. data/vendor/pygments-main/tests/test_examplefiles.py +97 -0
  351. data/vendor/pygments-main/tests/test_html_formatter.py +162 -0
  352. data/vendor/pygments-main/tests/test_latex_formatter.py +55 -0
  353. data/vendor/pygments-main/tests/test_perllexer.py +137 -0
  354. data/vendor/pygments-main/tests/test_regexlexer.py +47 -0
  355. data/vendor/pygments-main/tests/test_token.py +46 -0
  356. data/vendor/pygments-main/tests/test_using_api.py +40 -0
  357. data/vendor/pygments-main/tests/test_util.py +116 -0
  358. data/vendor/simplejson/.gitignore +10 -0
  359. data/vendor/simplejson/.travis.yml +5 -0
  360. data/vendor/simplejson/CHANGES.txt +291 -0
  361. data/vendor/simplejson/LICENSE.txt +19 -0
  362. data/vendor/simplejson/MANIFEST.in +5 -0
  363. data/vendor/simplejson/README.rst +19 -0
  364. data/vendor/simplejson/conf.py +179 -0
  365. data/vendor/simplejson/index.rst +628 -0
  366. data/vendor/simplejson/scripts/make_docs.py +18 -0
  367. data/vendor/simplejson/setup.py +104 -0
  368. data/vendor/simplejson/simplejson/__init__.py +510 -0
  369. data/vendor/simplejson/simplejson/_speedups.c +2745 -0
  370. data/vendor/simplejson/simplejson/decoder.py +425 -0
  371. data/vendor/simplejson/simplejson/encoder.py +567 -0
  372. data/vendor/simplejson/simplejson/ordered_dict.py +119 -0
  373. data/vendor/simplejson/simplejson/scanner.py +77 -0
  374. data/vendor/simplejson/simplejson/tests/__init__.py +67 -0
  375. data/vendor/simplejson/simplejson/tests/test_bigint_as_string.py +55 -0
  376. data/vendor/simplejson/simplejson/tests/test_check_circular.py +30 -0
  377. data/vendor/simplejson/simplejson/tests/test_decimal.py +66 -0
  378. data/vendor/simplejson/simplejson/tests/test_decode.py +83 -0
  379. data/vendor/simplejson/simplejson/tests/test_default.py +9 -0
  380. data/vendor/simplejson/simplejson/tests/test_dump.py +67 -0
  381. data/vendor/simplejson/simplejson/tests/test_encode_basestring_ascii.py +46 -0
  382. data/vendor/simplejson/simplejson/tests/test_encode_for_html.py +32 -0
  383. data/vendor/simplejson/simplejson/tests/test_errors.py +34 -0
  384. data/vendor/simplejson/simplejson/tests/test_fail.py +91 -0
  385. data/vendor/simplejson/simplejson/tests/test_float.py +19 -0
  386. data/vendor/simplejson/simplejson/tests/test_indent.py +86 -0
  387. data/vendor/simplejson/simplejson/tests/test_item_sort_key.py +20 -0
  388. data/vendor/simplejson/simplejson/tests/test_namedtuple.py +121 -0
  389. data/vendor/simplejson/simplejson/tests/test_pass1.py +76 -0
  390. data/vendor/simplejson/simplejson/tests/test_pass2.py +14 -0
  391. data/vendor/simplejson/simplejson/tests/test_pass3.py +20 -0
  392. data/vendor/simplejson/simplejson/tests/test_recursion.py +67 -0
  393. data/vendor/simplejson/simplejson/tests/test_scanstring.py +117 -0
  394. data/vendor/simplejson/simplejson/tests/test_separators.py +42 -0
  395. data/vendor/simplejson/simplejson/tests/test_speedups.py +20 -0
  396. data/vendor/simplejson/simplejson/tests/test_tuple.py +49 -0
  397. data/vendor/simplejson/simplejson/tests/test_unicode.py +109 -0
  398. data/vendor/simplejson/simplejson/tool.py +39 -0
  399. metadata +492 -0
@@ -0,0 +1,1852 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ class Ruby < Scanner
5
+
6
+ RESERVED_WORDS = [
7
+ 'and', 'def', 'end', 'in', 'or', 'unless', 'begin',
8
+ 'defined?', 'ensure', 'module', 'redo', 'super', 'until',
9
+ 'BEGIN', 'break', 'do', 'next', 'rescue', 'then',
10
+ 'when', 'END', 'case', 'else', 'for', 'retry',
11
+ 'while', 'alias', 'class', 'elsif', 'if', 'not', 'return',
12
+ 'undef', 'yield',
13
+ ]
14
+
15
+ DEF_KEYWORDS = ['def']
16
+ MODULE_KEYWORDS = ['class', 'module']
17
+ DEF_NEW_STATE = WordList.new(:initial).
18
+ add(DEF_KEYWORDS, :def_expected).
19
+ add(MODULE_KEYWORDS, :module_expected)
20
+
21
+ WORDS_ALLOWING_REGEXP = [
22
+ 'and', 'or', 'not', 'while', 'until', 'unless', 'if', 'elsif', 'when'
23
+ ]
24
+ REGEXP_ALLOWED = WordList.new(false).
25
+ add(WORDS_ALLOWING_REGEXP, :set)
26
+
27
+ PREDEFINED_CONSTANTS = [
28
+ 'nil', 'true', 'false', 'self',
29
+ 'DATA', 'ARGV', 'ARGF', '__FILE__', '__LINE__',
30
+ ]
31
+
32
+ IDENT_KIND = WordList.new(:ident).
33
+ add(RESERVED_WORDS, :reserved).
34
+ add(PREDEFINED_CONSTANTS, :pre_constant)
35
+
36
+ METHOD_NAME = / #{IDENT} [?!]? /xo
37
+ METHOD_NAME_EX = /
38
+ #{METHOD_NAME} # common methods: split, foo=, empty?, gsub!
39
+ | \*\*? # multiplication and power
40
+ | [-+~]@? # plus, minus
41
+ | [\/%&|^`] # division, modulo or format strings, &and, |or, ^xor, `system`
42
+ | \[\]=? # array getter and setter
43
+ | <=?>? | >=? # comparison, rocket operator
44
+ | << | >> # append or shift left, shift right
45
+ | ===? # simple equality and case equality
46
+ /ox
47
+ GLOBAL_VARIABLE = / \$ (?: #{IDENT} | \d+ | [~&+`'=\/,;_.<>!@0$?*":F\\] | -[a-zA-Z_0-9] ) /ox
48
+
49
+ DOUBLEQ = / " [^"\#\\]* (?: (?: \#\{.*?\} | \#(?:$")? | \\. ) [^"\#\\]* )* "? /ox
50
+ SINGLEQ = / ' [^'\\]* (?: \\. [^'\\]* )* '? /ox
51
+ STRING = / #{SINGLEQ} | #{DOUBLEQ} /ox
52
+ SHELL = / ` [^`\#\\]* (?: (?: \#\{.*?\} | \#(?:$`)? | \\. ) [^`\#\\]* )* `? /ox
53
+ REGEXP = / \/ [^\/\#\\]* (?: (?: \#\{.*?\} | \#(?:$\/)? | \\. ) [^\/\#\\]* )* \/? /ox
54
+
55
+ DECIMAL = /\d+(?:_\d+)*/ # doesn't recognize 09 as octal error
56
+ OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
57
+ HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
58
+ BINARY = /0b[01]+(?:_[01]+)*/
59
+
60
+ EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
61
+ FLOAT = / #{DECIMAL} (?: #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? ) /
62
+ INTEGER = /#{OCTAL}|#{HEXADECIMAL}|#{BINARY}|#{DECIMAL}/
63
+
64
+ def reset
65
+ super
66
+ @regexp_allowed = false
67
+ end
68
+
69
+ def next_token
70
+ return if @scanner.eos?
71
+
72
+ kind = :error
73
+ if @scanner.scan(/\s+/) # in every state
74
+ kind = :space
75
+ @regexp_allowed = :set if @regexp_allowed or @scanner.matched.index(?\n) # delayed flag setting
76
+
77
+ elsif @state == :def_expected
78
+ if @scanner.scan(/ (?: (?:#{IDENT}(?:\.|::))* | (?:@@?|$)? #{IDENT}(?:\.|::) ) #{METHOD_NAME_EX} /ox)
79
+ kind = :method
80
+ @state = :initial
81
+ else
82
+ @scanner.getch
83
+ end
84
+ @state = :initial
85
+
86
+ elsif @state == :module_expected
87
+ if @scanner.scan(/<</)
88
+ kind = :operator
89
+ else
90
+ if @scanner.scan(/ (?: #{IDENT} (?:\.|::))* #{IDENT} /ox)
91
+ kind = :method
92
+ else
93
+ @scanner.getch
94
+ end
95
+ @state = :initial
96
+ end
97
+
98
+ elsif # state == :initial
99
+ # IDENTIFIERS, KEYWORDS
100
+ if @scanner.scan(GLOBAL_VARIABLE)
101
+ kind = :global_variable
102
+ elsif @scanner.scan(/ @@ #{IDENT} /ox)
103
+ kind = :class_variable
104
+ elsif @scanner.scan(/ @ #{IDENT} /ox)
105
+ kind = :instance_variable
106
+ elsif @scanner.scan(/ __END__\n ( (?!\#CODE\#) .* )? | \#[^\n]* | =begin(?=\s).*? \n=end(?=\s|\z)(?:[^\n]*)? /mx)
107
+ kind = :comment
108
+ elsif @scanner.scan(METHOD_NAME)
109
+ if @last_token_dot
110
+ kind = :ident
111
+ else
112
+ matched = @scanner.matched
113
+ kind = IDENT_KIND[matched]
114
+ if kind == :ident and matched =~ /^[A-Z]/
115
+ kind = :constant
116
+ elsif kind == :reserved
117
+ @state = DEF_NEW_STATE[matched]
118
+ @regexp_allowed = REGEXP_ALLOWED[matched]
119
+ end
120
+ end
121
+
122
+ elsif @scanner.scan(STRING)
123
+ kind = :string
124
+ elsif @scanner.scan(SHELL)
125
+ kind = :shell
126
+ elsif @scanner.scan(/<<
127
+ (?:
128
+ ([a-zA-Z_0-9]+)
129
+ (?: .*? ^\1$ | .* )
130
+ |
131
+ -([a-zA-Z_0-9]+)
132
+ (?: .*? ^\s*\2$ | .* )
133
+ |
134
+ (["\'`]) (.+?) \3
135
+ (?: .*? ^\4$ | .* )
136
+ |
137
+ - (["\'`]) (.+?) \5
138
+ (?: .*? ^\s*\6$ | .* )
139
+ )
140
+ /mxo)
141
+ kind = :string
142
+ elsif @scanner.scan(/\//) and @regexp_allowed
143
+ @scanner.unscan
144
+ @scanner.scan(REGEXP)
145
+ kind = :regexp
146
+ /%(?:[Qqxrw](?:\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\\\\])(?:(?!\1)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\1)[^#\\\\])*)*\1?)|\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\s\\\\])(?:(?!\2)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\2)[^#\\\\])*)*\2?|\\\\[^#\\\\]*(?:(?:#\{.*?\}|#)[^#\\\\]*)*\\\\?)/
147
+ elsif @scanner.scan(/:(?:#{GLOBAL_VARIABLE}|#{METHOD_NAME_EX}|#{STRING})/ox)
148
+ kind = :symbol
149
+ elsif @scanner.scan(/
150
+ \? (?:
151
+ [^\s\\]
152
+ |
153
+ \\ (?:M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-))? (?: \\ (?: . | [0-7]{3} | x[0-9A-Fa-f][0-9A-Fa-f] )
154
+ )
155
+ /mox)
156
+ kind = :integer
157
+
158
+ elsif @scanner.scan(/ [-+*\/%=<>;,|&!()\[\]{}~?] | \.\.?\.? | ::? /x)
159
+ kind = :operator
160
+ @regexp_allowed = :set if @scanner.matched[-1,1] =~ /[~=!<>|&^,\(\[+\-\/\*%]\z/
161
+ elsif @scanner.scan(FLOAT)
162
+ kind = :float
163
+ elsif @scanner.scan(INTEGER)
164
+ kind = :integer
165
+ else
166
+ @scanner.getch
167
+ end
168
+ end
169
+
170
+ token = Token.new @scanner.matched, kind
171
+
172
+ if kind == :regexp
173
+ token.text << @scanner.scan(/[eimnosux]*/)
174
+ end
175
+
176
+ @regexp_allowed = (@regexp_allowed == :set) # delayed flag setting
177
+
178
+ token
179
+ end
180
+ end
181
+
182
+ register Ruby, 'ruby', 'rb'
183
+
184
+ end
185
+ end
186
+ class Set
187
+ include Enumerable
188
+
189
+ # Creates a new set containing the given objects.
190
+ def self.[](*ary)
191
+ new(ary)
192
+ end
193
+
194
+ # Creates a new set containing the elements of the given enumerable
195
+ # object.
196
+ #
197
+ # If a block is given, the elements of enum are preprocessed by the
198
+ # given block.
199
+ def initialize(enum = nil, &block) # :yields: o
200
+ @hash ||= Hash.new
201
+
202
+ enum.nil? and return
203
+
204
+ if block
205
+ enum.each { |o| add(block[o]) }
206
+ else
207
+ merge(enum)
208
+ end
209
+ end
210
+
211
+ # Copy internal hash.
212
+ def initialize_copy(orig)
213
+ @hash = orig.instance_eval{@hash}.dup
214
+ end
215
+
216
+ # Returns the number of elements.
217
+ def size
218
+ @hash.size
219
+ end
220
+ alias length size
221
+
222
+ # Returns true if the set contains no elements.
223
+ def empty?
224
+ @hash.empty?
225
+ end
226
+
227
+ # Removes all elements and returns self.
228
+ def clear
229
+ @hash.clear
230
+ self
231
+ end
232
+
233
+ # Replaces the contents of the set with the contents of the given
234
+ # enumerable object and returns self.
235
+ def replace(enum)
236
+ if enum.class == self.class
237
+ @hash.replace(enum.instance_eval { @hash })
238
+ else
239
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
240
+ clear
241
+ enum.each { |o| add(o) }
242
+ end
243
+
244
+ self
245
+ end
246
+
247
+ # Converts the set to an array. The order of elements is uncertain.
248
+ def to_a
249
+ @hash.keys
250
+ end
251
+
252
+ def flatten_merge(set, seen = Set.new)
253
+ set.each { |e|
254
+ if e.is_a?(Set)
255
+ if seen.include?(e_id = e.object_id)
256
+ raise ArgumentError, "tried to flatten recursive Set"
257
+ end
258
+
259
+ seen.add(e_id)
260
+ flatten_merge(e, seen)
261
+ seen.delete(e_id)
262
+ else
263
+ add(e)
264
+ end
265
+ }
266
+
267
+ self
268
+ end
269
+ protected :flatten_merge
270
+
271
+ # Returns a new set that is a copy of the set, flattening each
272
+ # containing set recursively.
273
+ def flatten
274
+ self.class.new.flatten_merge(self)
275
+ end
276
+
277
+ # Equivalent to Set#flatten, but replaces the receiver with the
278
+ # result in place. Returns nil if no modifications were made.
279
+ def flatten!
280
+ if detect { |e| e.is_a?(Set) }
281
+ replace(flatten())
282
+ else
283
+ nil
284
+ end
285
+ end
286
+
287
+ # Returns true if the set contains the given object.
288
+ def include?(o)
289
+ @hash.include?(o)
290
+ end
291
+ alias member? include?
292
+
293
+ # Returns true if the set is a superset of the given set.
294
+ def superset?(set)
295
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
296
+ return false if size < set.size
297
+ set.all? { |o| include?(o) }
298
+ end
299
+
300
+ # Returns true if the set is a proper superset of the given set.
301
+ def proper_superset?(set)
302
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
303
+ return false if size <= set.size
304
+ set.all? { |o| include?(o) }
305
+ end
306
+
307
+ # Returns true if the set is a subset of the given set.
308
+ def subset?(set)
309
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
310
+ return false if set.size < size
311
+ all? { |o| set.include?(o) }
312
+ end
313
+
314
+ # Returns true if the set is a proper subset of the given set.
315
+ def proper_subset?(set)
316
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
317
+ return false if set.size <= size
318
+ all? { |o| set.include?(o) }
319
+ end
320
+
321
+ # Calls the given block once for each element in the set, passing
322
+ # the element as parameter.
323
+ def each
324
+ @hash.each_key { |o| yield(o) }
325
+ self
326
+ end
327
+
328
+ # Adds the given object to the set and returns self. Use +merge+ to
329
+ # add several elements at once.
330
+ def add(o)
331
+ @hash[o] = true
332
+ self
333
+ end
334
+ alias << add
335
+
336
+ # Adds the given object to the set and returns self. If the
337
+ # object is already in the set, returns nil.
338
+ def add?(o)
339
+ if include?(o)
340
+ nil
341
+ else
342
+ add(o)
343
+ end
344
+ end
345
+
346
+ # Deletes the given object from the set and returns self. Use +subtract+ to
347
+ # delete several items at once.
348
+ def delete(o)
349
+ @hash.delete(o)
350
+ self
351
+ end
352
+
353
+ # Deletes the given object from the set and returns self. If the
354
+ # object is not in the set, returns nil.
355
+ def delete?(o)
356
+ if include?(o)
357
+ delete(o)
358
+ else
359
+ nil
360
+ end
361
+ end
362
+
363
+ # Deletes every element of the set for which block evaluates to
364
+ # true, and returns self.
365
+ def delete_if
366
+ @hash.delete_if { |o,| yield(o) }
367
+ self
368
+ end
369
+
370
+ # Do collect() destructively.
371
+ def collect!
372
+ set = self.class.new
373
+ each { |o| set << yield(o) }
374
+ replace(set)
375
+ end
376
+ alias map! collect!
377
+
378
+ # Equivalent to Set#delete_if, but returns nil if no changes were
379
+ # made.
380
+ def reject!
381
+ n = size
382
+ delete_if { |o| yield(o) }
383
+ size == n ? nil : self
384
+ end
385
+
386
+ # Merges the elements of the given enumerable object to the set and
387
+ # returns self.
388
+ def merge(enum)
389
+ if enum.is_a?(Set)
390
+ @hash.update(enum.instance_eval { @hash })
391
+ else
392
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
393
+ enum.each { |o| add(o) }
394
+ end
395
+
396
+ self
397
+ end
398
+
399
+ # Deletes every element that appears in the given enumerable object
400
+ # and returns self.
401
+ def subtract(enum)
402
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
403
+ enum.each { |o| delete(o) }
404
+ self
405
+ end
406
+
407
+ # Returns a new set built by merging the set and the elements of the
408
+ # given enumerable object.
409
+ def |(enum)
410
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
411
+ dup.merge(enum)
412
+ end
413
+ alias + | ##
414
+ alias union | ##
415
+
416
+ # Returns a new set built by duplicating the set, removing every
417
+ # element that appears in the given enumerable object.
418
+ def -(enum)
419
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
420
+ dup.subtract(enum)
421
+ end
422
+ alias difference - ##
423
+
424
+ # Returns a new array containing elements common to the set and the
425
+ # given enumerable object.
426
+ def &(enum)
427
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
428
+ n = self.class.new
429
+ enum.each { |o| n.add(o) if include?(o) }
430
+ n
431
+ end
432
+ alias intersection & ##
433
+
434
+ # Returns a new array containing elements exclusive between the set
435
+ # and the given enumerable object. (set ^ enum) is equivalent to
436
+ # ((set | enum) - (set & enum)).
437
+ def ^(enum)
438
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
439
+ n = dup
440
+ enum.each { |o| if n.include?(o) then n.delete(o) else n.add(o) end }
441
+ n
442
+ end
443
+
444
+ # Returns true if two sets are equal. The equality of each couple
445
+ # of elements is defined according to Object#eql?.
446
+ def ==(set)
447
+ equal?(set) and return true
448
+
449
+ set.is_a?(Set) && size == set.size or return false
450
+
451
+ hash = @hash.dup
452
+ set.all? { |o| hash.include?(o) }
453
+ end
454
+
455
+ def hash # :nodoc:
456
+ @hash.hash
457
+ end
458
+
459
+ def eql?(o) # :nodoc:
460
+ return false unless o.is_a?(Set)
461
+ @hash.eql?(o.instance_eval{@hash})
462
+ end
463
+
464
+ # Classifies the set by the return value of the given block and
465
+ # returns a hash of {value => set of elements} pairs. The block is
466
+ # called once for each element of the set, passing the element as
467
+ # parameter.
468
+ #
469
+ # e.g.:
470
+ #
471
+ # require 'set'
472
+ # files = Set.new(Dir.glob("*.rb"))
473
+ # hash = files.classify { |f| File.mtime(f).year }
474
+ # p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>,
475
+ # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
476
+ # # 2002=>#<Set: {"f.rb"}>}
477
+ def classify # :yields: o
478
+ h = {}
479
+
480
+ each { |i|
481
+ x = yield(i)
482
+ (h[x] ||= self.class.new).add(i)
483
+ }
484
+
485
+ h
486
+ end
487
+
488
+ # Divides the set into a set of subsets according to the commonality
489
+ # defined by the given block.
490
+ #
491
+ # If the arity of the block is 2, elements o1 and o2 are in common
492
+ # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are
493
+ # in common if block.call(o1) == block.call(o2).
494
+ #
495
+ # e.g.:
496
+ #
497
+ # require 'set'
498
+ # numbers = Set[1, 3, 4, 6, 9, 10, 11]
499
+ # set = numbers.divide { |i,j| (i - j).abs == 1 }
500
+ # p set # => #<Set: {#<Set: {1}>,
501
+ # # #<Set: {11, 9, 10}>,
502
+ # # #<Set: {3, 4}>,
503
+ # # #<Set: {6}>}>
504
+ def divide(&func)
505
+ if func.arity == 2
506
+ require 'tsort'
507
+
508
+ class << dig = {} # :nodoc:
509
+ include TSort
510
+
511
+ alias tsort_each_node each_key
512
+ def tsort_each_child(node, &block)
513
+ fetch(node).each(&block)
514
+ end
515
+ end
516
+
517
+ each { |u|
518
+ dig[u] = a = []
519
+ each{ |v| func.call(u, v) and a << v }
520
+ }
521
+
522
+ set = Set.new()
523
+ dig.each_strongly_connected_component { |css|
524
+ set.add(self.class.new(css))
525
+ }
526
+ set
527
+ else
528
+ Set.new(classify(&func).values)
529
+ end
530
+ end
531
+
532
+ InspectKey = :__inspect_key__ # :nodoc:
533
+
534
+ # Returns a string containing a human-readable representation of the
535
+ # set. ("#<Set: {element1, element2, ...}>")
536
+ def inspect
537
+ ids = (Thread.current[InspectKey] ||= [])
538
+
539
+ if ids.include?(object_id)
540
+ return sprintf('#<%s: {...}>', self.class.name)
541
+ end
542
+
543
+ begin
544
+ ids << object_id
545
+ return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2])
546
+ ensure
547
+ ids.pop
548
+ end
549
+ end
550
+
551
+ def pretty_print(pp) # :nodoc:
552
+ pp.text sprintf('#<%s: {', self.class.name)
553
+ pp.nest(1) {
554
+ pp.seplist(self) { |o|
555
+ pp.pp o
556
+ }
557
+ }
558
+ pp.text "}>"
559
+ end
560
+
561
+ def pretty_print_cycle(pp) # :nodoc:
562
+ pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...')
563
+ end
564
+ end
565
+
566
+ # SortedSet implements a set which elements are sorted in order. See Set.
567
+ class SortedSet < Set
568
+ @@setup = false
569
+
570
+ class << self
571
+ def [](*ary) # :nodoc:
572
+ new(ary)
573
+ end
574
+
575
+ def setup # :nodoc:
576
+ @@setup and return
577
+
578
+ begin
579
+ require 'rbtree'
580
+
581
+ module_eval %{
582
+ def initialize(*args, &block)
583
+ @hash = RBTree.new
584
+ super
585
+ end
586
+ }
587
+ rescue LoadError
588
+ module_eval %{
589
+ def initialize(*args, &block)
590
+ @keys = nil
591
+ super
592
+ end
593
+
594
+ def clear
595
+ @keys = nil
596
+ super
597
+ end
598
+
599
+ def replace(enum)
600
+ @keys = nil
601
+ super
602
+ end
603
+
604
+ def add(o)
605
+ @keys = nil
606
+ @hash[o] = true
607
+ self
608
+ end
609
+ alias << add
610
+
611
+ def delete(o)
612
+ @keys = nil
613
+ @hash.delete(o)
614
+ self
615
+ end
616
+
617
+ def delete_if
618
+ n = @hash.size
619
+ @hash.delete_if { |o,| yield(o) }
620
+ @keys = nil if @hash.size != n
621
+ self
622
+ end
623
+
624
+ def merge(enum)
625
+ @keys = nil
626
+ super
627
+ end
628
+
629
+ def each
630
+ to_a.each { |o| yield(o) }
631
+ end
632
+
633
+ def to_a
634
+ (@keys = @hash.keys).sort! unless @keys
635
+ @keys
636
+ end
637
+ }
638
+ end
639
+
640
+ @@setup = true
641
+ end
642
+ end
643
+
644
+ def initialize(*args, &block) # :nodoc:
645
+ SortedSet.setup
646
+ initialize(*args, &block)
647
+ end
648
+ end
649
+
650
+ module Enumerable
651
+ # Makes a set from the enumerable object with given arguments.
652
+ def to_set(klass = Set, *args, &block)
653
+ klass.new(self, *args, &block)
654
+ end
655
+ end
656
+
657
+ # =begin
658
+ # == RestricedSet class
659
+ # RestricedSet implements a set with restrictions defined by a given
660
+ # block.
661
+ #
662
+ # === Super class
663
+ # Set
664
+ #
665
+ # === Class Methods
666
+ # --- RestricedSet::new(enum = nil) { |o| ... }
667
+ # --- RestricedSet::new(enum = nil) { |rset, o| ... }
668
+ # Creates a new restricted set containing the elements of the given
669
+ # enumerable object. Restrictions are defined by the given block.
670
+ #
671
+ # If the block's arity is 2, it is called with the RestrictedSet
672
+ # itself and an object to see if the object is allowed to be put in
673
+ # the set.
674
+ #
675
+ # Otherwise, the block is called with an object to see if the object
676
+ # is allowed to be put in the set.
677
+ #
678
+ # === Instance Methods
679
+ # --- restriction_proc
680
+ # Returns the restriction procedure of the set.
681
+ #
682
+ # =end
683
+ #
684
+ # class RestricedSet < Set
685
+ # def initialize(*args, &block)
686
+ # @proc = block or raise ArgumentError, "missing a block"
687
+ #
688
+ # if @proc.arity == 2
689
+ # instance_eval %{
690
+ # def add(o)
691
+ # @hash[o] = true if @proc.call(self, o)
692
+ # self
693
+ # end
694
+ # alias << add
695
+ #
696
+ # def add?(o)
697
+ # if include?(o) || !@proc.call(self, o)
698
+ # nil
699
+ # else
700
+ # @hash[o] = true
701
+ # self
702
+ # end
703
+ # end
704
+ #
705
+ # def replace(enum)
706
+ # enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
707
+ # clear
708
+ # enum.each { |o| add(o) }
709
+ #
710
+ # self
711
+ # end
712
+ #
713
+ # def merge(enum)
714
+ # enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
715
+ # enum.each { |o| add(o) }
716
+ #
717
+ # self
718
+ # end
719
+ # }
720
+ # else
721
+ # instance_eval %{
722
+ # def add(o)
723
+ # if @proc.call(o)
724
+ # @hash[o] = true
725
+ # end
726
+ # self
727
+ # end
728
+ # alias << add
729
+ #
730
+ # def add?(o)
731
+ # if include?(o) || !@proc.call(o)
732
+ # nil
733
+ # else
734
+ # @hash[o] = true
735
+ # self
736
+ # end
737
+ # end
738
+ # }
739
+ # end
740
+ #
741
+ # super(*args)
742
+ # end
743
+ #
744
+ # def restriction_proc
745
+ # @proc
746
+ # end
747
+ # end
748
+
749
+ if $0 == __FILE__
750
+ eval DATA.read, nil, $0, __LINE__+4
751
+ end
752
+
753
+ # = rweb - CGI Support Library
754
+ #
755
+ # Author:: Johannes Barre (mailto:rweb@igels.net)
756
+ # Copyright:: Copyright (c) 2003, 04 by Johannes Barre
757
+ # License:: GNU Lesser General Public License (COPYING, http://www.gnu.org/copyleft/lesser.html)
758
+ # Version:: 0.1.0
759
+ # CVS-ID:: $Id: example.rb 39 2005-11-05 03:33:55Z murphy $
760
+ #
761
+ # == What is Rweb?
762
+ # Rweb is a replacement for the cgi class included in the ruby distribution.
763
+ #
764
+ # == How to use
765
+ #
766
+ # === Basics
767
+ #
768
+ # This class is made to be as easy as possible to use. An example:
769
+ #
770
+ # require "rweb"
771
+ #
772
+ # web = Rweb.new
773
+ # web.out do
774
+ # web.puts "Hello world!"
775
+ # end
776
+ #
777
+ # The visitor will get a simple "Hello World!" in his browser. Please notice,
778
+ # that won't set html-tags for you, so you should better do something like this:
779
+ #
780
+ # require "rweb"
781
+ #
782
+ # web = Rweb.new
783
+ # web.out do
784
+ # web.puts "<html><body>Hello world!</body></html>"
785
+ # end
786
+ #
787
+ # === Set headers
788
+ # Of course, it's also possible to tell the browser, that the content of this
789
+ # page is plain text instead of html code:
790
+ #
791
+ # require "rweb"
792
+ #
793
+ # web = Rweb.new
794
+ # web.out do
795
+ # web.header("content-type: text/plain")
796
+ # web.puts "Hello plain world!"
797
+ # end
798
+ #
799
+ # Please remember, headers can't be set after the page content has been send.
800
+ # You have to set all nessessary headers before the first puts oder print. It's
801
+ # possible to cache the content until everything is complete. Doing it this
802
+ # way, you can set headers everywhere.
803
+ #
804
+ # If you set a header twice, the second header will replace the first one. The
805
+ # header name is not casesensitive, it will allways converted in to the
806
+ # capitalised form suggested by the w3c (http://w3.org)
807
+ #
808
+ # === Set cookies
809
+ # Setting cookies is quite easy:
810
+ # include 'rweb'
811
+ #
812
+ # web = Rweb.new
813
+ # Cookie.new("Visits", web.cookies['visits'].to_i +1)
814
+ # web.out do
815
+ # web.puts "Welcome back! You visited this page #{web.cookies['visits'].to_i +1} times"
816
+ # end
817
+ #
818
+ # See the class Cookie for more details.
819
+ #
820
+ # === Get form and cookie values
821
+ # There are four ways to submit data from the browser to the server and your
822
+ # ruby script: via GET, POST, cookies and file upload. Rweb doesn't support
823
+ # file upload by now.
824
+ #
825
+ # include 'rweb'
826
+ #
827
+ # web = Rweb.new
828
+ # web.out do
829
+ # web.print "action: #{web.get['action']} "
830
+ # web.puts "The value of the cookie 'visits' is #{web.cookies['visits']}"
831
+ # web.puts "The post parameter 'test['x']' is #{web.post['test']['x']}"
832
+ # end
833
+
834
+ RWEB_VERSION = "0.1.0"
835
+ RWEB = "rweb/#{RWEB_VERSION}"
836
+
837
+ #require 'rwebcookie' -> edit by bunny :-)
838
+
839
+ class Rweb
840
+ # All parameter submitted via the GET method are available in attribute
841
+ # get. This is Hash, where every parameter is available as a key-value
842
+ # pair.
843
+ #
844
+ # If your input tag has a name like this one, it's value will be available
845
+ # as web.get["fieldname"]
846
+ # <input name="fieldname">
847
+ # You can submit values as a Hash
848
+ # <input name="text['index']">
849
+ # <input name="text['index2']">
850
+ # will be available as
851
+ # web.get["text"]["index"]
852
+ # web.get["text"]["index2"]
853
+ # Integers are also possible
854
+ # <input name="int[2]">
855
+ # <input name="int[3]['hi']>
856
+ # will be available as
857
+ # web.get["int"][2]
858
+ # web.get["int"][3]["hi"]
859
+ # If you specify no index, the lowest unused index will be used:
860
+ # <input name="int[]"><!-- First Field -->
861
+ # <input name="int[]"><!-- Second one -->
862
+ # will be available as
863
+ # web.get["int"][0] # First Field
864
+ # web.get["int"][1] # Second one
865
+ # Please notice, this doesn'd work like you might expect:
866
+ # <input name="text[index]">
867
+ # It will not be available as web.get["text"]["index"] but
868
+ # web.get["text[index]"]
869
+ attr_reader :get
870
+
871
+ # All parameters submitted via POST are available in the attribute post. It
872
+ # works like the get attribute.
873
+ # <input name="text[0]">
874
+ # will be available as
875
+ # web.post["text"][0]
876
+ attr_reader :post
877
+
878
+ # All cookies submitted by the browser are available in cookies. This is a
879
+ # Hash, where every cookie is a key-value pair.
880
+ attr_reader :cookies
881
+
882
+ # The name of the browser identification is submitted as USER_AGENT and
883
+ # available in this attribute.
884
+ attr_reader :user_agent
885
+
886
+ # The IP address of the client.
887
+ attr_reader :remote_addr
888
+
889
+ # Creates a new Rweb object. This should only done once. You can set various
890
+ # options via the settings hash.
891
+ #
892
+ # "cache" => true: Everything you script send to the client will be cached
893
+ # until the end of the out block or until flush is called. This way, you
894
+ # can modify headers and cookies even after printing something to the client.
895
+ #
896
+ # "safe" => level: Changes the $SAFE attribute. By default, $SAFE will be set
897
+ # to 1. If $SAFE is already higher than this value, it won't be changed.
898
+ #
899
+ # "silend" => true: Normaly, Rweb adds automaticly a header like this
900
+ # "X-Powered-By: Rweb/x.x.x (Ruby/y.y.y)". With the silend option you can
901
+ # suppress this.
902
+ def initialize (settings = {})
903
+ # {{{
904
+ @header = {}
905
+ @cookies = {}
906
+ @get = {}
907
+ @post = {}
908
+
909
+ # Internal attributes
910
+ @status = nil
911
+ @reasonPhrase = nil
912
+ @setcookies = []
913
+ @output_started = false;
914
+ @output_allowed = false;
915
+
916
+ @mod_ruby = false
917
+ @env = ENV.to_hash
918
+
919
+ if defined?(MOD_RUBY)
920
+ @output_method = "mod_ruby"
921
+ @mod_ruby = true
922
+ elsif @env['SERVER_SOFTWARE'] =~ /^Microsoft-IIS/i
923
+ @output_method = "nph"
924
+ else
925
+ @output_method = "ph"
926
+ end
927
+
928
+ unless settings.is_a?(Hash)
929
+ raise TypeError, "settings must be a Hash"
930
+ end
931
+ @settings = settings
932
+
933
+ unless @settings.has_key?("safe")
934
+ @settings["safe"] = 1
935
+ end
936
+
937
+ if $SAFE < @settings["safe"]
938
+ $SAFE = @settings["safe"]
939
+ end
940
+
941
+ unless @settings.has_key?("cache")
942
+ @settings["cache"] = false
943
+ end
944
+
945
+ # mod_ruby sets no QUERY_STRING variable, if no GET-Parameters are given
946
+ unless @env.has_key?("QUERY_STRING")
947
+ @env["QUERY_STRING"] = ""
948
+ end
949
+
950
+ # Now we split the QUERY_STRING by the seperators & and ; or, if
951
+ # specified, settings['get seperator']
952
+ unless @settings.has_key?("get seperator")
953
+ get_args = @env['QUERY_STRING'].split(/[&;]/)
954
+ else
955
+ get_args = @env['QUERY_STRING'].split(@settings['get seperator'])
956
+ end
957
+
958
+ get_args.each do | arg |
959
+ arg_key, arg_val = arg.split(/=/, 2)
960
+ arg_key = Rweb::unescape(arg_key)
961
+ arg_val = Rweb::unescape(arg_val)
962
+
963
+ # Parse names like name[0], name['text'] or name[]
964
+ pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
965
+ keys = []
966
+ while match = pattern.match(arg_key)
967
+ arg_key = match[1]
968
+ keys = [match[2]] + keys
969
+ end
970
+ keys = [arg_key] + keys
971
+
972
+ akt = @get
973
+ last = nil
974
+ lastkey = nil
975
+ keys.each do |key|
976
+ if key == ""
977
+ # No key specified (like in "test[]"), so we use the
978
+ # lowerst unused Integer as key
979
+ key = 0
980
+ while akt.has_key?(key)
981
+ key += 1
982
+ end
983
+ elsif /^[0-9]*$/ =~ key
984
+ # If the index is numerical convert it to an Integer
985
+ key = key.to_i
986
+ elsif key[0].chr == "'" || key[0].chr == '"'
987
+ key = key[1, key.length() -2]
988
+ end
989
+ if !akt.has_key?(key) || !akt[key].class == Hash
990
+ # create an empty Hash if there isn't already one
991
+ akt[key] = {}
992
+ end
993
+ last = akt
994
+ lastkey = key
995
+ akt = akt[key]
996
+ end
997
+ last[lastkey] = arg_val
998
+ end
999
+
1000
+ if @env['REQUEST_METHOD'] == "POST"
1001
+ if @env.has_key?("CONTENT_TYPE") && @env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" && @env.has_key?('CONTENT_LENGTH')
1002
+ unless @settings.has_key?("post seperator")
1003
+ post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(/[&;]/)
1004
+ else
1005
+ post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(@settings['post seperator'])
1006
+ end
1007
+ post_args.each do | arg |
1008
+ arg_key, arg_val = arg.split(/=/, 2)
1009
+ arg_key = Rweb::unescape(arg_key)
1010
+ arg_val = Rweb::unescape(arg_val)
1011
+
1012
+ # Parse names like name[0], name['text'] or name[]
1013
+ pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
1014
+ keys = []
1015
+ while match = pattern.match(arg_key)
1016
+ arg_key = match[1]
1017
+ keys = [match[2]] + keys
1018
+ end
1019
+ keys = [arg_key] + keys
1020
+
1021
+ akt = @post
1022
+ last = nil
1023
+ lastkey = nil
1024
+ keys.each do |key|
1025
+ if key == ""
1026
+ # No key specified (like in "test[]"), so we use
1027
+ # the lowerst unused Integer as key
1028
+ key = 0
1029
+ while akt.has_key?(key)
1030
+ key += 1
1031
+ end
1032
+ elsif /^[0-9]*$/ =~ key
1033
+ # If the index is numerical convert it to an Integer
1034
+ key = key.to_i
1035
+ elsif key[0].chr == "'" || key[0].chr == '"'
1036
+ key = key[1, key.length() -2]
1037
+ end
1038
+ if !akt.has_key?(key) || !akt[key].class == Hash
1039
+ # create an empty Hash if there isn't already one
1040
+ akt[key] = {}
1041
+ end
1042
+ last = akt
1043
+ lastkey = key
1044
+ akt = akt[key]
1045
+ end
1046
+ last[lastkey] = arg_val
1047
+ end
1048
+ else
1049
+ # Maybe we should print a warning here?
1050
+ $stderr.print("Unidentified form data recived and discarded.")
1051
+ end
1052
+ end
1053
+
1054
+ if @env.has_key?("HTTP_COOKIE")
1055
+ cookie = @env['HTTP_COOKIE'].split(/; ?/)
1056
+ cookie.each do | c |
1057
+ cookie_key, cookie_val = c.split(/=/, 2)
1058
+
1059
+ @cookies [Rweb::unescape(cookie_key)] = Rweb::unescape(cookie_val)
1060
+ end
1061
+ end
1062
+
1063
+ if defined?(@env['HTTP_USER_AGENT'])
1064
+ @user_agent = @env['HTTP_USER_AGENT']
1065
+ else
1066
+ @user_agent = nil;
1067
+ end
1068
+
1069
+ if defined?(@env['REMOTE_ADDR'])
1070
+ @remote_addr = @env['REMOTE_ADDR']
1071
+ else
1072
+ @remote_addr = nil
1073
+ end
1074
+ # }}}
1075
+ end
1076
+
1077
+ # Prints a String to the client. If caching is enabled, the String will
1078
+ # buffered until the end of the out block ends.
1079
+ def print(str = "")
1080
+ # {{{
1081
+ unless @output_allowed
1082
+ raise "You just can write to output inside of a Rweb::out-block"
1083
+ end
1084
+
1085
+ if @settings["cache"]
1086
+ @buffer += [str.to_s]
1087
+ else
1088
+ unless @output_started
1089
+ sendHeaders
1090
+ end
1091
+ $stdout.print(str)
1092
+ end
1093
+ nil
1094
+ # }}}
1095
+ end
1096
+
1097
+ # Prints a String to the client and adds a line break at the end. Please
1098
+ # remember, that a line break is not visible in HTML, use the <br> HTML-Tag
1099
+ # for this. If caching is enabled, the String will buffered until the end
1100
+ # of the out block ends.
1101
+ def puts(str = "")
1102
+ # {{{
1103
+ self.print(str + "\n")
1104
+ # }}}
1105
+ end
1106
+
1107
+ # Alias to print.
1108
+ def write(str = "")
1109
+ # {{{
1110
+ self.print(str)
1111
+ # }}}
1112
+ end
1113
+
1114
+ # If caching is enabled, all cached data are send to the cliend and the
1115
+ # cache emptied.
1116
+ def flush
1117
+ # {{{
1118
+ unless @output_allowed
1119
+ raise "You can't use flush outside of a Rweb::out-block"
1120
+ end
1121
+ buffer = @buffer.join
1122
+
1123
+ unless @output_started
1124
+ sendHeaders
1125
+ end
1126
+ $stdout.print(buffer)
1127
+
1128
+ @buffer = []
1129
+ # }}}
1130
+ end
1131
+
1132
+ # Sends one or more header to the client. All headers are cached just
1133
+ # before body data are send to the client. If the same header are set
1134
+ # twice, only the last value is send.
1135
+ #
1136
+ # Example:
1137
+ # web.header("Last-Modified: Mon, 16 Feb 2004 20:15:41 GMT")
1138
+ # web.header("Location: http://www.ruby-lang.org")
1139
+ #
1140
+ # You can specify more than one header at the time by doing something like
1141
+ # this:
1142
+ # web.header("Content-Type: text/plain\nContent-Length: 383")
1143
+ # or
1144
+ # web.header(["Content-Type: text/plain", "Content-Length: 383"])
1145
+ def header(str)
1146
+ # {{{
1147
+ if @output_started
1148
+ raise "HTTP-Headers are already send. You can't change them after output has started!"
1149
+ end
1150
+ unless @output_allowed
1151
+ raise "You just can set headers inside of a Rweb::out-block"
1152
+ end
1153
+ if str.is_a?Array
1154
+ str.each do | value |
1155
+ self.header(value)
1156
+ end
1157
+
1158
+ elsif str.split(/\n/).length > 1
1159
+ str.split(/\n/).each do | value |
1160
+ self.header(value)
1161
+ end
1162
+
1163
+ elsif str.is_a? String
1164
+ str.gsub!(/\r/, "")
1165
+
1166
+ if (str =~ /^HTTP\/1\.[01] [0-9]{3} ?.*$/) == 0
1167
+ pattern = /^HTTP\/1.[01] ([0-9]{3}) ?(.*)$/
1168
+
1169
+ result = pattern.match(str)
1170
+ self.setstatus(result[0], result[1])
1171
+ elsif (str =~ /^status: [0-9]{3} ?.*$/i) == 0
1172
+ pattern = /^status: ([0-9]{3}) ?(.*)$/i
1173
+
1174
+ result = pattern.match(str)
1175
+ self.setstatus(result[0], result[1])
1176
+ else
1177
+ a = str.split(/: ?/, 2)
1178
+
1179
+ @header[a[0].downcase] = a[1]
1180
+ end
1181
+ end
1182
+ # }}}
1183
+ end
1184
+
1185
+ # Changes the status of this page. There are several codes like "200 OK",
1186
+ # "302 Found", "404 Not Found" or "500 Internal Server Error". A list of
1187
+ # all codes is available at
1188
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10
1189
+ #
1190
+ # You can just send the code number, the reason phrase will be added
1191
+ # automaticly with the recommendations from the w3c if not specified. If
1192
+ # you set the status twice or more, only the last status will be send.
1193
+ # Examples:
1194
+ # web.status("401 Unauthorized")
1195
+ # web.status("410 Sad but true, this lonely page is gone :(")
1196
+ # web.status(206)
1197
+ # web.status("400")
1198
+ #
1199
+ # The default status is "200 OK". If a "Location" header is set, the
1200
+ # default status is "302 Found".
1201
+ def status(str)
1202
+ # {{{
1203
+ if @output_started
1204
+ raise "HTTP-Headers are already send. You can't change them after output has started!"
1205
+ end
1206
+ unless @output_allowed
1207
+ raise "You just can set headers inside of a Rweb::out-block"
1208
+ end
1209
+ if str.is_a?Integer
1210
+ @status = str
1211
+ elsif str.is_a?String
1212
+ p1 = /^([0-9]{3}) ?(.*)$/
1213
+ p2 = /^HTTP\/1\.[01] ([0-9]{3}) ?(.*)$/
1214
+ p3 = /^status: ([0-9]{3}) ?(.*)$/i
1215
+
1216
+ if (a = p1.match(str)) == nil
1217
+ if (a = p2.match(str)) == nil
1218
+ if (a = p3.match(str)) == nil
1219
+ raise ArgumentError, "Invalid argument", caller
1220
+ end
1221
+ end
1222
+ end
1223
+ @status = a[1].to_i
1224
+ if a[2] != ""
1225
+ @reasonPhrase = a[2]
1226
+ else
1227
+ @reasonPhrase = getReasonPhrase(@status)
1228
+ end
1229
+ else
1230
+ raise ArgumentError, "Argument of setstatus must be integer or string", caller
1231
+ end
1232
+ # }}}
1233
+ end
1234
+
1235
+ # Handles the output of your content and rescues all exceptions. Send all
1236
+ # data in the block to this method. For example:
1237
+ # web.out do
1238
+ # web.header("Content-Type: text/plain")
1239
+ # web.puts("Hello, plain world!")
1240
+ # end
1241
+ def out
1242
+ # {{{
1243
+ @output_allowed = true
1244
+ @buffer = []; # We use an array as buffer, because it's more performant :)
1245
+
1246
+ begin
1247
+ yield
1248
+ rescue Exception => exception
1249
+ $stderr.puts "Ruby exception rescued (#{exception.class}): #{exception.message}"
1250
+ $stderr.puts exception.backtrace.join("\n")
1251
+
1252
+ unless @output_started
1253
+ self.setstatus(500)
1254
+ @header = {}
1255
+ end
1256
+
1257
+ unless (@settings.has_key?("hide errors") and @settings["hide errors"] == true)
1258
+ unless @output_started
1259
+ self.header("Content-Type: text/html")
1260
+ self.puts "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
1261
+ self.puts "<html>"
1262
+ self.puts "<head>"
1263
+ self.puts "<title>500 Internal Server Error</title>"
1264
+ self.puts "</head>"
1265
+ self.puts "<body>"
1266
+ end
1267
+ if @header.has_key?("content-type") and (@header["content-type"] =~ /^text\/html/i) == 0
1268
+ self.puts "<h1>Internal Server Error</h1>"
1269
+ self.puts "<p>The server encountered an exception and was unable to complete your request.</p>"
1270
+ self.puts "<p>The exception has provided the following information:</p>"
1271
+ self.puts "<pre style=\"background: #FFCCCC; border: black solid 2px; margin-left: 2cm; margin-right: 2cm; padding: 2mm;\"><b>#{exception.class}</b>: #{exception.message} <b>on</b>"
1272
+ self.puts
1273
+ self.puts "#{exception.backtrace.join("\n")}</pre>"
1274
+ self.puts "</body>"
1275
+ self.puts "</html>"
1276
+ else
1277
+ self.puts "The server encountered an exception and was unable to complete your request"
1278
+ self.puts "The exception has provided the following information:"
1279
+ self.puts "#{exception.class}: #{exception.message}"
1280
+ self.puts
1281
+ self.puts exception.backtrace.join("\n")
1282
+ end
1283
+ end
1284
+ end
1285
+
1286
+ if @settings["cache"]
1287
+ buffer = @buffer.join
1288
+
1289
+ unless @output_started
1290
+ unless @header.has_key?("content-length")
1291
+ self.header("content-length: #{buffer.length}")
1292
+ end
1293
+
1294
+ sendHeaders
1295
+ end
1296
+ $stdout.print(buffer)
1297
+ elsif !@output_started
1298
+ sendHeaders
1299
+ end
1300
+ @output_allowed = false;
1301
+ # }}}
1302
+ end
1303
+
1304
+ # Decodes URL encoded data, %20 for example stands for a space.
1305
+ def Rweb.unescape(str)
1306
+ # {{{
1307
+ if defined? str and str.is_a? String
1308
+ str.gsub!(/\+/, " ")
1309
+ str.gsub(/%.{2}/) do | s |
1310
+ s[1,2].hex.chr
1311
+ end
1312
+ end
1313
+ # }}}
1314
+ end
1315
+
1316
+ protected
1317
+ def sendHeaders
1318
+ # {{{
1319
+
1320
+ Cookie.disallow # no more cookies can be set or modified
1321
+ if !(@settings.has_key?("silent") and @settings["silent"] == true) and !@header.has_key?("x-powered-by")
1322
+ if @mod_ruby
1323
+ header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION}, #{MOD_RUBY})");
1324
+ else
1325
+ header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION})");
1326
+ end
1327
+ end
1328
+
1329
+ if @output_method == "ph"
1330
+ if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
1331
+ header("content-type: text/html")
1332
+ end
1333
+
1334
+ if @status != nil
1335
+ $stdout.print "Status: #{@status} #{@reasonPhrase}\r\n"
1336
+ end
1337
+
1338
+ @header.each do |key, value|
1339
+ key = key *1 # "unfreeze" key :)
1340
+ key[0] = key[0,1].upcase![0]
1341
+
1342
+ key = key.gsub(/-[a-z]/) do |char|
1343
+ "-" + char[1,1].upcase
1344
+ end
1345
+
1346
+ $stdout.print "#{key}: #{value}\r\n"
1347
+ end
1348
+ cookies = Cookie.getHttpHeader # Get all cookies as an HTTP Header
1349
+ if cookies
1350
+ $stdout.print cookies
1351
+ end
1352
+
1353
+ $stdout.print "\r\n"
1354
+
1355
+ elsif @output_method == "nph"
1356
+ elsif @output_method == "mod_ruby"
1357
+ r = Apache.request
1358
+
1359
+ if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
1360
+ header("text/html")
1361
+ end
1362
+
1363
+ if @status != nil
1364
+ r.status_line = "#{@status} #{@reasonPhrase}"
1365
+ end
1366
+
1367
+ r.send_http_header
1368
+ @header.each do |key, value|
1369
+ key = key *1 # "unfreeze" key :)
1370
+
1371
+ key[0] = key[0,1].upcase![0]
1372
+ key = key.gsub(/-[a-z]/) do |char|
1373
+ "-" + char[1,1].upcase
1374
+ end
1375
+ puts "#{key}: #{value.class}"
1376
+ #r.headers_out[key] = value
1377
+ end
1378
+ end
1379
+ @output_started = true
1380
+ # }}}
1381
+ end
1382
+
1383
+ def getReasonPhrase (status)
1384
+ # {{{
1385
+ if status == 100
1386
+ "Continue"
1387
+ elsif status == 101
1388
+ "Switching Protocols"
1389
+ elsif status == 200
1390
+ "OK"
1391
+ elsif status == 201
1392
+ "Created"
1393
+ elsif status == 202
1394
+ "Accepted"
1395
+ elsif status == 203
1396
+ "Non-Authoritative Information"
1397
+ elsif status == 204
1398
+ "No Content"
1399
+ elsif status == 205
1400
+ "Reset Content"
1401
+ elsif status == 206
1402
+ "Partial Content"
1403
+ elsif status == 300
1404
+ "Multiple Choices"
1405
+ elsif status == 301
1406
+ "Moved Permanently"
1407
+ elsif status == 302
1408
+ "Found"
1409
+ elsif status == 303
1410
+ "See Other"
1411
+ elsif status == 304
1412
+ "Not Modified"
1413
+ elsif status == 305
1414
+ "Use Proxy"
1415
+ elsif status == 307
1416
+ "Temporary Redirect"
1417
+ elsif status == 400
1418
+ "Bad Request"
1419
+ elsif status == 401
1420
+ "Unauthorized"
1421
+ elsif status == 402
1422
+ "Payment Required"
1423
+ elsif status == 403
1424
+ "Forbidden"
1425
+ elsif status == 404
1426
+ "Not Found"
1427
+ elsif status == 405
1428
+ "Method Not Allowed"
1429
+ elsif status == 406
1430
+ "Not Acceptable"
1431
+ elsif status == 407
1432
+ "Proxy Authentication Required"
1433
+ elsif status == 408
1434
+ "Request Time-out"
1435
+ elsif status == 409
1436
+ "Conflict"
1437
+ elsif status == 410
1438
+ "Gone"
1439
+ elsif status == 411
1440
+ "Length Required"
1441
+ elsif status == 412
1442
+ "Precondition Failed"
1443
+ elsif status == 413
1444
+ "Request Entity Too Large"
1445
+ elsif status == 414
1446
+ "Request-URI Too Large"
1447
+ elsif status == 415
1448
+ "Unsupported Media Type"
1449
+ elsif status == 416
1450
+ "Requested range not satisfiable"
1451
+ elsif status == 417
1452
+ "Expectation Failed"
1453
+ elsif status == 500
1454
+ "Internal Server Error"
1455
+ elsif status == 501
1456
+ "Not Implemented"
1457
+ elsif status == 502
1458
+ "Bad Gateway"
1459
+ elsif status == 503
1460
+ "Service Unavailable"
1461
+ elsif status == 504
1462
+ "Gateway Time-out"
1463
+ elsif status == 505
1464
+ "HTTP Version not supported"
1465
+ else
1466
+ raise "Unknown Statuscode. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 for more information."
1467
+ end
1468
+ # }}}
1469
+ end
1470
+ end
1471
+
1472
+ class Cookie
1473
+ attr_reader :name, :value, :maxage, :path, :domain, :secure, :comment
1474
+
1475
+ # Sets a cookie. Please see below for details of the attributes.
1476
+ def initialize (name, value = nil, maxage = nil, path = nil, domain = nil, secure = false)
1477
+ # {{{
1478
+ # HTTP headers (Cookies are a HTTP header) can only set, while no content
1479
+ # is send. So an exception will be raised, when @@allowed is set to false
1480
+ # and a new cookie has set.
1481
+ unless defined?(@@allowed)
1482
+ @@allowed = true
1483
+ end
1484
+ unless @@allowed
1485
+ raise "You can't set cookies after the HTTP headers are send."
1486
+ end
1487
+
1488
+ unless defined?(@@list)
1489
+ @@list = []
1490
+ end
1491
+ @@list += [self]
1492
+
1493
+ unless defined?(@@type)
1494
+ @@type = "netscape"
1495
+ end
1496
+
1497
+ unless name.class == String
1498
+ raise TypeError, "The name of a cookie must be a string", caller
1499
+ end
1500
+ if value.class.superclass == Integer || value.class == Float
1501
+ value = value.to_s
1502
+ elsif value.class != String && value != nil
1503
+ raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
1504
+ end
1505
+ if maxage.class == Time
1506
+ maxage = maxage - Time.now
1507
+ elsif !maxage.class.superclass == Integer || !maxage == nil
1508
+ raise TypeError, "The maxage date of a cookie must be an Integer or Time object or nil.", caller
1509
+ end
1510
+ unless path.class == String || path == nil
1511
+ raise TypeError, "The path of a cookie must be nil or a string", caller
1512
+ end
1513
+ unless domain.class == String || domain == nil
1514
+ raise TypeError, "The value of a cookie must be nil or a string", caller
1515
+ end
1516
+ unless secure == true || secure == false
1517
+ raise TypeError, "The secure field of a cookie must be true or false", caller
1518
+ end
1519
+
1520
+ @name, @value, @maxage, @path, @domain, @secure = name, value, maxage, path, domain, secure
1521
+ @comment = nil
1522
+ # }}}
1523
+ end
1524
+
1525
+ # Modifies the value of this cookie. The information you want to store. If the
1526
+ # value is nil, the cookie will be deleted by the client.
1527
+ #
1528
+ # This attribute can be a String, Integer or Float object or nil.
1529
+ def value=(value)
1530
+ # {{{
1531
+ if value.class.superclass == Integer || value.class == Float
1532
+ value = value.to_s
1533
+ elsif value.class != String && value != nil
1534
+ raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
1535
+ end
1536
+ @value = value
1537
+ # }}}
1538
+ end
1539
+
1540
+ # Modifies the maxage of this cookie. This attribute defines the lifetime of
1541
+ # the cookie, in seconds. A value of 0 means the cookie should be discarded
1542
+ # imediatly. If it set to nil, the cookie will be deleted when the browser
1543
+ # will be closed.
1544
+ #
1545
+ # Attention: This is different from other implementations like PHP, where you
1546
+ # gives the seconds since 1/1/1970 0:00:00 GMT.
1547
+ #
1548
+ # This attribute must be an Integer or Time object or nil.
1549
+ def maxage=(maxage)
1550
+ # {{{
1551
+ if maxage.class == Time
1552
+ maxage = maxage - Time.now
1553
+ elsif maxage.class.superclass == Integer || !maxage == nil
1554
+ raise TypeError, "The maxage of a cookie must be an Interger or Time object or nil.", caller
1555
+ end
1556
+ @maxage = maxage
1557
+ # }}}
1558
+ end
1559
+
1560
+ # Modifies the path value of this cookie. The client will send this cookie
1561
+ # only, if the requested document is this directory or a subdirectory of it.
1562
+ #
1563
+ # The value of the attribute must be a String object or nil.
1564
+ def path=(path)
1565
+ # {{{
1566
+ unless path.class == String || path == nil
1567
+ raise TypeError, "The path of a cookie must be nil or a string", caller
1568
+ end
1569
+ @path = path
1570
+ # }}}
1571
+ end
1572
+
1573
+ # Modifies the domain value of this cookie. The client will send this cookie
1574
+ # only if it's connected with this domain (or a subdomain, if the first
1575
+ # character is a dot like in ".ruby-lang.org")
1576
+ #
1577
+ # The value of this attribute must be a String or nil.
1578
+ def domain=(domain)
1579
+ # {{{
1580
+ unless domain.class == String || domain == nil
1581
+ raise TypeError, "The domain of a cookie must be a String or nil.", caller
1582
+ end
1583
+ @domain = domain
1584
+ # }}}
1585
+ end
1586
+
1587
+ # Modifies the secure flag of this cookie. If it's true, the client will only
1588
+ # send this cookie if it is secured connected with us.
1589
+ #
1590
+ # The value od this attribute has to be true or false.
1591
+ def secure=(secure)
1592
+ # {{{
1593
+ unless secure == true || secure == false
1594
+ raise TypeError, "The secure field of a cookie must be true or false", caller
1595
+ end
1596
+ @secure = secure
1597
+ # }}}
1598
+ end
1599
+
1600
+ # Modifies the comment value of this cookie. The comment won't be send, if
1601
+ # type is "netscape".
1602
+ def comment=(comment)
1603
+ # {{{
1604
+ unless comment.class == String || comment == nil
1605
+ raise TypeError, "The comment of a cookie must be a string or nil", caller
1606
+ end
1607
+ @comment = comment
1608
+ # }}}
1609
+ end
1610
+
1611
+ # Changes the type of all cookies.
1612
+ # Allowed values are RFC2109 and netscape (default).
1613
+ def Cookie.type=(type)
1614
+ # {{{
1615
+ unless @@allowed
1616
+ raise "The cookies are allready send, so you can't change the type anymore."
1617
+ end
1618
+ unless type.downcase == "rfc2109" && type.downcase == "netscape"
1619
+ raise "The type of the cookies must be \"RFC2109\" or \"netscape\"."
1620
+ end
1621
+ @@type = type;
1622
+ # }}}
1623
+ end
1624
+
1625
+ # After sending this message, no cookies can be set or modified. Use it, when
1626
+ # HTTP-Headers are send. Rweb does this for you.
1627
+ def Cookie.disallow
1628
+ # {{{
1629
+ @@allowed = false
1630
+ true
1631
+ # }}}
1632
+ end
1633
+
1634
+ # Returns a HTTP header (type String) with all cookies. Rweb does this for
1635
+ # you.
1636
+ def Cookie.getHttpHeader
1637
+ # {{{
1638
+ if defined?(@@list)
1639
+ if @@type == "netscape"
1640
+ str = ""
1641
+ @@list.each do |cookie|
1642
+ if cookie.value == nil
1643
+ cookie.maxage = 0
1644
+ cookie.value = ""
1645
+ end
1646
+ # TODO: Name and value should be escaped!
1647
+ str += "Set-Cookie: #{cookie.name}=#{cookie.value}"
1648
+ unless cookie.maxage == nil
1649
+ expire = Time.now + cookie.maxage
1650
+ expire.gmtime
1651
+ str += "; Expire=#{expire.strftime("%a, %d-%b-%Y %H:%M:%S %Z")}"
1652
+ end
1653
+ unless cookie.domain == nil
1654
+ str += "; Domain=#{cookie.domain}"
1655
+ end
1656
+ unless cookie.path == nil
1657
+ str += "; Path=#{cookie.path}"
1658
+ end
1659
+ if cookie.secure
1660
+ str += "; Secure"
1661
+ end
1662
+ str += "\r\n"
1663
+ end
1664
+ return str
1665
+ else # type == "RFC2109"
1666
+ str = "Set-Cookie: "
1667
+ comma = false;
1668
+
1669
+ @@list.each do |cookie|
1670
+ if cookie.value == nil
1671
+ cookie.maxage = 0
1672
+ cookie.value = ""
1673
+ end
1674
+ if comma
1675
+ str += ","
1676
+ end
1677
+ comma = true
1678
+
1679
+ str += "#{cookie.name}=\"#{cookie.value}\""
1680
+ unless cookie.maxage == nil
1681
+ str += "; Max-Age=\"#{cookie.maxage}\""
1682
+ end
1683
+ unless cookie.domain == nil
1684
+ str += "; Domain=\"#{cookie.domain}\""
1685
+ end
1686
+ unless cookie.path == nil
1687
+ str += "; Path=\"#{cookie.path}\""
1688
+ end
1689
+ if cookie.secure
1690
+ str += "; Secure"
1691
+ end
1692
+ unless cookie.comment == nil
1693
+ str += "; Comment=\"#{cookie.comment}\""
1694
+ end
1695
+ str += "; Version=\"1\""
1696
+ end
1697
+ str
1698
+ end
1699
+ else
1700
+ false
1701
+ end
1702
+ # }}}
1703
+ end
1704
+ end
1705
+
1706
+ require 'strscan'
1707
+
1708
+ module BBCode
1709
+ DEBUG = true
1710
+
1711
+ use 'encoder', 'tags', 'tagstack', 'smileys'
1712
+
1713
+ =begin
1714
+ The Parser class takes care of the encoding.
1715
+ It scans the given BBCode (as plain text), finds tags
1716
+ and smilies and also makes links of urls in text.
1717
+
1718
+ Normal text is send directly to the encoder.
1719
+
1720
+ If a tag was found, an instance of a Tag subclass is created
1721
+ to handle the case.
1722
+
1723
+ The @tagstack manages tag nesting and ensures valid HTML.
1724
+ =end
1725
+
1726
+ class Parser
1727
+ class Attribute
1728
+ # flatten and use only one empty_arg
1729
+ def self.create attr
1730
+ attr = flatten attr
1731
+ return @@empty_attr if attr.empty?
1732
+ new attr
1733
+ end
1734
+
1735
+ private_class_method :new
1736
+
1737
+ # remove leading and trailing whitespace; concat lines
1738
+ def self.flatten attr
1739
+ attr.strip.gsub(/\n/, ' ')
1740
+ # -> ^ and $ can only match at begin and end now
1741
+ end
1742
+
1743
+ ATTRIBUTE_SCAN = /
1744
+ (?!$) # don't match at end
1745
+ \s*
1746
+ ( # $1 = key
1747
+ [^=\s\]"\\]*
1748
+ (?:
1749
+ (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? )
1750
+ [^=\s\]"\\]*
1751
+ )*
1752
+ )
1753
+ (?:
1754
+ =
1755
+ ( # $2 = value
1756
+ [^\s\]"\\]*
1757
+ (?:
1758
+ (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? )
1759
+ [^\s\]"\\]*
1760
+ )*
1761
+ )?
1762
+ )?
1763
+ \s*
1764
+ /x
1765
+
1766
+ def self.parse source
1767
+ source = source.dup
1768
+ # empty_tag: the tag looks like [... /]
1769
+ # slice!: this deletes the \s*/] at the end
1770
+ # \s+ because [url=http://rubybb.org/forum/] is NOT an empty tag.
1771
+ # In RubyBBCode, you can use [url=http://rubybb.org/forum/ /], and this has to be
1772
+ # interpreted correctly.
1773
+ empty_tag = source.sub!(/^:/, '=') or source.slice!(/\/$/)
1774
+ debug 'PARSE: ' + source.inspect + ' => ' + empty_tag.inspect
1775
+ #-> we have now an attr that's EITHER empty OR begins and ends with non-whitespace.
1776
+
1777
+ attr = Hash.new
1778
+ attr[:flags] = []
1779
+ source.scan(ATTRIBUTE_SCAN) { |key, value|
1780
+ if not value
1781
+ attr[:flags] << unescape(key)
1782
+ else
1783
+ next if value.empty? and key.empty?
1784
+ attr[unescape(key)] = unescape(value)
1785
+ end
1786
+ }
1787
+ debug attr.inspect
1788
+
1789
+ return empty_tag, attr
1790
+ end
1791
+
1792
+ def self.unescape_char esc
1793
+ esc[1]
1794
+ end
1795
+
1796
+ def self.unquote qt
1797
+ qt[1..-1].chomp('"').gsub(/\\./) { |esc| unescape_char esc }
1798
+ end
1799
+
1800
+ def self.unescape str
1801
+ str.gsub(/ (\\.) | (" [^"\\]* (?:\\.[^"\\]*)* "?) /x) {
1802
+ if $1
1803
+ unescape_char $1
1804
+ else
1805
+ unquote $2
1806
+ end
1807
+ }
1808
+ end
1809
+
1810
+ include Enumerable
1811
+ def each &block
1812
+ @args.each(&block)
1813
+ end
1814
+
1815
+ attr_reader :source, :args, :value
1816
+
1817
+ def initialize source
1818
+ @source = source
1819
+ debug 'Attribute#new(%p)' % source
1820
+ @empty_tag, @attr = Attribute.parse source
1821
+ @value = @attr[''].to_s
1822
+ end
1823
+
1824
+ def empty?
1825
+ self == @@empty_attr
1826
+ end
1827
+
1828
+ def empty_tag?
1829
+ @empty_tag
1830
+ end
1831
+
1832
+ def [] *keys
1833
+ res = @attr[*keys]
1834
+ end
1835
+
1836
+ def flags
1837
+ attr[:flags]
1838
+ end
1839
+
1840
+ def to_s
1841
+ @attr
1842
+ end
1843
+
1844
+ def inspect
1845
+ 'ATTR[' + @attr.inspect + (@empty_tag ? ' | empty tag' : '') + ']'
1846
+ end
1847
+ end
1848
+ class Attribute
1849
+ @@empty_attr = new ''
1850
+ end
1851
+ end
1852
+