cfengine.el 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461
  1. ;;; cfengine.el --- mode for editing Cfengine files
  2. ;; Copyright (C) 2001-2015 Free Software Foundation, Inc.
  3. ;; Author: Dave Love <fx@gnu.org>
  4. ;; Maintainer: Ted Zlatanov <tzz@lifelogs.com>
  5. ;; Keywords: languages
  6. ;; Version: 1.4
  7. ;; This file is part of GNU Emacs.
  8. ;; GNU Emacs is free software: you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; GNU Emacs is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; Provides support for editing GNU CFEngine files, including
  20. ;; font-locking, Imenu and indentation, but with no special keybindings.
  21. ;; By default, CFEngine 3.x syntax is used.
  22. ;; You can set it up so either `cfengine2-mode' (2.x and earlier) or
  23. ;; `cfengine3-mode' (3.x) will be picked, depending on the buffer
  24. ;; contents:
  25. ;; (add-to-list 'auto-mode-alist '("\\.cf\\'" . cfengine-auto-mode))
  26. ;; OR you can choose to always use a specific version, if you prefer
  27. ;; it:
  28. ;; (add-to-list 'auto-mode-alist '("\\.cf\\'" . cfengine3-mode))
  29. ;; (add-to-list 'auto-mode-alist '("^cf\\." . cfengine2-mode))
  30. ;; (add-to-list 'auto-mode-alist '("^cfagent.conf\\'" . cfengine2-mode))
  31. ;; It's *highly* recommended that you enable the eldoc minor mode:
  32. ;; (add-hook 'cfengine3-mode-hook 'eldoc-mode)
  33. ;; You may also find the command `cfengine3-reformat-json-string'
  34. ;; useful, just bind it to a key you prefer. It will take the current
  35. ;; string and reformat it as JSON. So if you're editing JSON inside
  36. ;; the policy, it's a quick way to make it more legible without
  37. ;; manually reindenting it. For instance:
  38. ;; (global-set-key [(control f4)] 'cfengine3-reformat-json-string)
  39. ;; This is not the same as the mode written by Rolf Ebert
  40. ;; <ebert@waporo.muc.de>, distributed with cfengine-2.0.5. It does
  41. ;; better fontification and indentation, inter alia.
  42. ;;; Code:
  43. (autoload 'json-read "json")
  44. (autoload 'json-pretty-print "json")
  45. (defgroup cfengine ()
  46. "Editing CFEngine files."
  47. :group 'languages)
  48. (defcustom cfengine-indent 2
  49. "Size of a CFEngine indentation step in columns."
  50. :group 'cfengine
  51. :type 'integer)
  52. (defcustom cfengine-cf-promises
  53. (or (executable-find "cf-promises")
  54. (executable-find "/var/cfengine/bin/cf-promises")
  55. (executable-find "/usr/bin/cf-promises")
  56. (executable-find "/usr/sbin/cf-promises")
  57. (executable-find "/usr/local/bin/cf-promises")
  58. (executable-find "/usr/local/sbin/cf-promises")
  59. (executable-find "~/bin/cf-promises")
  60. (executable-find "~/sbin/cf-promises"))
  61. "The location of the cf-promises executable.
  62. Used for syntax discovery and checking. Set to nil to disable
  63. the `compile-command' override. In that case, the ElDoc support
  64. will use a fallback syntax definition."
  65. :version "24.4"
  66. :group 'cfengine
  67. :type '(choice file (const nil)))
  68. (defcustom cfengine-parameters-indent '(promise pname 2)
  69. "Indentation of CFEngine3 promise parameters (hanging indent).
  70. For example, say you have this code:
  71. bundle x y
  72. {
  73. section:
  74. class::
  75. promise ...
  76. promiseparameter => ...
  77. }
  78. You can choose to indent promiseparameter from the beginning of
  79. the line (absolutely) or from the word \"promise\" (relatively).
  80. You can also choose to indent the start of the word
  81. \"promiseparameter\" or the arrow that follows it.
  82. Finally, you can choose the amount of the indent.
  83. The default is to anchor at promise, indent parameter name, and offset 2:
  84. bundle agent rcfiles
  85. {
  86. files:
  87. any::
  88. \"/tmp/netrc\"
  89. comment => \"my netrc\",
  90. perms => mog(\"600\", \"tzz\", \"tzz\");
  91. }
  92. Here we anchor at beginning of line, indent arrow, and offset 10:
  93. bundle agent rcfiles
  94. {
  95. files:
  96. any::
  97. \"/tmp/netrc\"
  98. comment => \"my netrc\",
  99. perms => mog(\"600\", \"tzz\", \"tzz\");
  100. }
  101. Some, including cfengine_stdlib.cf, like to anchor at promise, indent
  102. arrow, and offset 16 or so:
  103. bundle agent rcfiles
  104. {
  105. files:
  106. any::
  107. \"/tmp/netrc\"
  108. comment => \"my netrc\",
  109. perms => mog(\"600\", \"tzz\", \"tzz\");
  110. }
  111. "
  112. :version "24.4"
  113. :group 'cfengine
  114. :type '(list
  115. (choice (const :tag "Anchor at beginning of promise" promise)
  116. (const :tag "Anchor at beginning of line" bol))
  117. (choice (const :tag "Indent parameter name" pname)
  118. (const :tag "Indent arrow" arrow))
  119. (integer :tag "Indentation amount from anchor")))
  120. (defvar cfengine-mode-debug nil
  121. "Whether `cfengine-mode' should print debugging info.")
  122. (defvar cfengine-mode-syntax-cache nil
  123. "Cache for `cfengine-mode' syntax trees obtained from `cf-promises -s json'.")
  124. (defconst cfengine3-fallback-syntax
  125. '((functions
  126. (userexists
  127. (category . "system") (variadic . :json-false)
  128. (parameters . [((range . ".*") (type . "string"))])
  129. (returnType . "context") (status . "normal"))
  130. (usemodule
  131. (category . "utils") (variadic . :json-false)
  132. (parameters . [((range . ".*") (type . "string"))
  133. ((range . ".*") (type . "string"))])
  134. (returnType . "context") (status . "normal"))
  135. (unique
  136. (category . "data") (variadic . :json-false)
  137. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  138. (returnType . "slist") (status . "normal"))
  139. (translatepath
  140. (category . "files") (variadic . :json-false)
  141. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  142. (returnType . "string") (status . "normal"))
  143. (sum
  144. (category . "data") (variadic . :json-false)
  145. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  146. (returnType . "real") (status . "normal"))
  147. (sublist
  148. (category . "data") (variadic . :json-false)
  149. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  150. ((range . "head,tail") (type . "option"))
  151. ((range . "0,99999999999") (type . "int"))])
  152. (returnType . "slist") (status . "normal"))
  153. (strftime
  154. (category . "data") (variadic . :json-false)
  155. (parameters . [((range . "gmtime,localtime") (type . "option"))
  156. ((range . ".*") (type . "string"))
  157. ((range . "0,99999999999") (type . "int"))])
  158. (returnType . "string") (status . "normal"))
  159. (strcmp
  160. (category . "data") (variadic . :json-false)
  161. (parameters . [((range . ".*") (type . "string"))
  162. ((range . ".*") (type . "string"))])
  163. (returnType . "context") (status . "normal"))
  164. (splitstring
  165. (category . "data") (variadic . :json-false)
  166. (parameters . [((range . ".*") (type . "string"))
  167. ((range . ".*") (type . "string"))
  168. ((range . "0,99999999999") (type . "int"))])
  169. (returnType . "slist") (status . "normal"))
  170. (splayclass
  171. (category . "utils") (variadic . :json-false)
  172. (parameters . [((range . ".*") (type . "string"))
  173. ((range . "daily,hourly") (type . "option"))])
  174. (returnType . "context") (status . "normal"))
  175. (sort
  176. (category . "data") (variadic . :json-false)
  177. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  178. ((range . "lex") (type . "string"))])
  179. (returnType . "slist") (status . "normal"))
  180. (some
  181. (category . "data") (variadic . :json-false)
  182. (parameters . [((range . ".*") (type . "string"))
  183. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  184. (returnType . "context") (status . "normal"))
  185. (shuffle
  186. (category . "data") (variadic . :json-false)
  187. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  188. ((range . ".*") (type . "string"))])
  189. (returnType . "slist") (status . "normal"))
  190. (selectservers
  191. (category . "communication") (variadic . :json-false)
  192. (parameters . [((range . "@[(][a-zA-Z0-9]+[)]") (type . "string"))
  193. ((range . "0,99999999999") (type . "int"))
  194. ((range . ".*") (type . "string"))
  195. ((range . ".*") (type . "string"))
  196. ((range . "0,99999999999") (type . "int"))
  197. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  198. (returnType . "int") (status . "normal"))
  199. (reverse
  200. (category . "data") (variadic . :json-false)
  201. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  202. (returnType . "slist") (status . "normal"))
  203. (rrange
  204. (category . "data") (variadic . :json-false)
  205. (parameters . [((range . "-9.99999E100,9.99999E100") (type . "real"))
  206. ((range . "-9.99999E100,9.99999E100") (type . "real"))])
  207. (returnType . "rrange") (status . "normal"))
  208. (returnszero
  209. (category . "utils") (variadic . :json-false)
  210. (parameters . [((range . "\"?(/.*)") (type . "string"))
  211. ((range . "useshell,noshell,powershell") (type . "option"))])
  212. (returnType . "context") (status . "normal"))
  213. (remoteclassesmatching
  214. (category . "communication") (variadic . :json-false)
  215. (parameters . [((range . ".*") (type . "string"))
  216. ((range . ".*") (type . "string"))
  217. ((range . "true,false,yes,no,on,off") (type . "option"))
  218. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  219. (returnType . "context") (status . "normal"))
  220. (remotescalar
  221. (category . "communication") (variadic . :json-false)
  222. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  223. ((range . ".*") (type . "string"))
  224. ((range . "true,false,yes,no,on,off") (type . "option"))])
  225. (returnType . "string") (status . "normal"))
  226. (regldap
  227. (category . "communication") (variadic . :json-false)
  228. (parameters . [((range . ".*") (type . "string"))
  229. ((range . ".*") (type . "string"))
  230. ((range . ".*") (type . "string"))
  231. ((range . ".*") (type . "string"))
  232. ((range . "subtree,onelevel,base") (type . "option"))
  233. ((range . ".*") (type . "string"))
  234. ((range . "none,ssl,sasl") (type . "option"))])
  235. (returnType . "context") (status . "normal"))
  236. (reglist
  237. (category . "data") (variadic . :json-false)
  238. (parameters . [((range . "@[(][a-zA-Z0-9]+[)]") (type . "string"))
  239. ((range . ".*") (type . "string"))])
  240. (returnType . "context") (status . "normal"))
  241. (regline
  242. (category . "io") (variadic . :json-false)
  243. (parameters . [((range . ".*") (type . "string"))
  244. ((range . ".*") (type . "string"))])
  245. (returnType . "context") (status . "normal"))
  246. (registryvalue
  247. (category . "system") (variadic . :json-false)
  248. (parameters . [((range . ".*") (type . "string"))
  249. ((range . ".*") (type . "string"))])
  250. (returnType . "string") (status . "normal"))
  251. (regextract
  252. (category . "data") (variadic . :json-false)
  253. (parameters . [((range . ".*") (type . "string"))
  254. ((range . ".*") (type . "string"))
  255. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  256. (returnType . "context") (status . "normal"))
  257. (regcmp
  258. (category . "data") (variadic . :json-false)
  259. (parameters . [((range . ".*") (type . "string"))
  260. ((range . ".*") (type . "string"))])
  261. (returnType . "context") (status . "normal"))
  262. (regarray
  263. (category . "data") (variadic . :json-false)
  264. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  265. ((range . ".*") (type . "string"))])
  266. (returnType . "context") (status . "normal"))
  267. (readtcp
  268. (category . "communication") (variadic . :json-false)
  269. (parameters . [((range . ".*") (type . "string"))
  270. ((range . "0,99999999999") (type . "int"))
  271. ((range . ".*") (type . "string"))
  272. ((range . "0,99999999999") (type . "int"))])
  273. (returnType . "string") (status . "normal"))
  274. (readstringlist
  275. (category . "io") (variadic . :json-false)
  276. (parameters . [((range . "\"?(/.*)") (type . "string"))
  277. ((range . ".*") (type . "string"))
  278. ((range . ".*") (type . "string"))
  279. ((range . "0,99999999999") (type . "int"))
  280. ((range . "0,99999999999") (type . "int"))])
  281. (returnType . "slist") (status . "normal"))
  282. (readstringarrayidx
  283. (category . "io") (variadic . :json-false)
  284. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  285. ((range . "\"?(/.*)") (type . "string"))
  286. ((range . ".*") (type . "string"))
  287. ((range . ".*") (type . "string"))
  288. ((range . "0,99999999999") (type . "int"))
  289. ((range . "0,99999999999") (type . "int"))])
  290. (returnType . "int") (status . "normal"))
  291. (readstringarray
  292. (category . "io") (variadic . :json-false)
  293. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  294. ((range . "\"?(/.*)") (type . "string"))
  295. ((range . ".*") (type . "string"))
  296. ((range . ".*") (type . "string"))
  297. ((range . "0,99999999999") (type . "int"))
  298. ((range . "0,99999999999") (type . "int"))])
  299. (returnType . "int") (status . "normal"))
  300. (readreallist
  301. (category . "io") (variadic . :json-false)
  302. (parameters . [((range . "\"?(/.*)") (type . "string"))
  303. ((range . ".*") (type . "string"))
  304. ((range . ".*") (type . "string"))
  305. ((range . "0,99999999999") (type . "int"))
  306. ((range . "0,99999999999") (type . "int"))])
  307. (returnType . "rlist") (status . "normal"))
  308. (readrealarray
  309. (category . "io") (variadic . :json-false)
  310. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  311. ((range . "\"?(/.*)") (type . "string"))
  312. ((range . ".*") (type . "string"))
  313. ((range . ".*") (type . "string"))
  314. ((range . "0,99999999999") (type . "int"))
  315. ((range . "0,99999999999") (type . "int"))])
  316. (returnType . "int") (status . "normal"))
  317. (readintlist
  318. (category . "io") (variadic . :json-false)
  319. (parameters . [((range . "\"?(/.*)") (type . "string"))
  320. ((range . ".*") (type . "string"))
  321. ((range . ".*") (type . "string"))
  322. ((range . "0,99999999999") (type . "int"))
  323. ((range . "0,99999999999") (type . "int"))])
  324. (returnType . "ilist") (status . "normal"))
  325. (readintarray
  326. (category . "io") (variadic . :json-false)
  327. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  328. ((range . "\"?(/.*)") (type . "string"))
  329. ((range . ".*") (type . "string"))
  330. ((range . ".*") (type . "string"))
  331. ((range . "0,99999999999") (type . "int"))
  332. ((range . "0,99999999999") (type . "int"))])
  333. (returnType . "int") (status . "normal"))
  334. (readfile
  335. (category . "io") (variadic . :json-false)
  336. (parameters . [((range . "\"?(/.*)") (type . "string"))
  337. ((range . "0,99999999999") (type . "int"))])
  338. (returnType . "string") (status . "normal"))
  339. (randomint
  340. (category . "data") (variadic . :json-false)
  341. (parameters . [((range . "-99999999999,9999999999") (type . "int"))
  342. ((range . "-99999999999,9999999999") (type . "int"))])
  343. (returnType . "int") (status . "normal"))
  344. (product
  345. (category . "data") (variadic . :json-false)
  346. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  347. (returnType . "real") (status . "normal"))
  348. (peerleaders
  349. (category . "communication") (variadic . :json-false)
  350. (parameters . [((range . "\"?(/.*)") (type . "string"))
  351. ((range . ".*") (type . "string"))
  352. ((range . "0,99999999999") (type . "int"))])
  353. (returnType . "slist") (status . "normal"))
  354. (peerleader
  355. (category . "communication") (variadic . :json-false)
  356. (parameters . [((range . "\"?(/.*)") (type . "string"))
  357. ((range . ".*") (type . "string"))
  358. ((range . "0,99999999999") (type . "int"))])
  359. (returnType . "string") (status . "normal"))
  360. (peers
  361. (category . "communication") (variadic . :json-false)
  362. (parameters . [((range . "\"?(/.*)") (type . "string"))
  363. ((range . ".*") (type . "string"))
  364. ((range . "0,99999999999") (type . "int"))])
  365. (returnType . "slist") (status . "normal"))
  366. (parsestringarrayidx
  367. (category . "io") (variadic . :json-false)
  368. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  369. ((range . "\"?(/.*)") (type . "string"))
  370. ((range . ".*") (type . "string"))
  371. ((range . ".*") (type . "string"))
  372. ((range . "0,99999999999") (type . "int"))
  373. ((range . "0,99999999999") (type . "int"))])
  374. (returnType . "int") (status . "normal"))
  375. (parsestringarray
  376. (category . "io") (variadic . :json-false)
  377. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  378. ((range . "\"?(/.*)") (type . "string"))
  379. ((range . ".*") (type . "string"))
  380. ((range . ".*") (type . "string"))
  381. ((range . "0,99999999999") (type . "int"))
  382. ((range . "0,99999999999") (type . "int"))])
  383. (returnType . "int") (status . "normal"))
  384. (parserealarray
  385. (category . "io") (variadic . :json-false)
  386. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  387. ((range . "\"?(/.*)") (type . "string"))
  388. ((range . ".*") (type . "string"))
  389. ((range . ".*") (type . "string"))
  390. ((range . "0,99999999999") (type . "int"))
  391. ((range . "0,99999999999") (type . "int"))])
  392. (returnType . "int") (status . "normal"))
  393. (parseintarray
  394. (category . "io") (variadic . :json-false)
  395. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  396. ((range . "\"?(/.*)") (type . "string"))
  397. ((range . ".*") (type . "string"))
  398. ((range . ".*") (type . "string"))
  399. ((range . "0,99999999999") (type . "int"))
  400. ((range . "0,99999999999") (type . "int"))])
  401. (returnType . "int") (status . "normal"))
  402. (or
  403. (category . "data") (variadic . t)
  404. (parameters . [])
  405. (returnType . "string") (status . "normal"))
  406. (on
  407. (category . "data") (variadic . :json-false)
  408. (parameters . [((range . "1970,3000") (type . "int"))
  409. ((range . "1,12") (type . "int"))
  410. ((range . "1,31") (type . "int"))
  411. ((range . "0,23") (type . "int"))
  412. ((range . "0,59") (type . "int"))
  413. ((range . "0,59") (type . "int"))])
  414. (returnType . "int") (status . "normal"))
  415. (nth
  416. (category . "data") (variadic . :json-false)
  417. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  418. ((range . "0,99999999999") (type . "int"))])
  419. (returnType . "string") (status . "normal"))
  420. (now
  421. (category . "system") (variadic . :json-false)
  422. (parameters . [])
  423. (returnType . "int") (status . "normal"))
  424. (not
  425. (category . "data") (variadic . :json-false)
  426. (parameters . [((range . ".*") (type . "string"))])
  427. (returnType . "string") (status . "normal"))
  428. (none
  429. (category . "data") (variadic . :json-false)
  430. (parameters . [((range . ".*") (type . "string"))
  431. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  432. (returnType . "context") (status . "normal"))
  433. (maplist
  434. (category . "data") (variadic . :json-false)
  435. (parameters . [((range . ".*") (type . "string"))
  436. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  437. (returnType . "slist") (status . "normal"))
  438. (maparray
  439. (category . "data") (variadic . :json-false)
  440. (parameters . [((range . ".*") (type . "string"))
  441. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  442. (returnType . "slist") (status . "normal"))
  443. (lsdir
  444. (category . "files") (variadic . :json-false)
  445. (parameters . [((range . ".+") (type . "string"))
  446. ((range . ".*") (type . "string"))
  447. ((range . "true,false,yes,no,on,off") (type . "option"))])
  448. (returnType . "slist") (status . "normal"))
  449. (length
  450. (category . "data") (variadic . :json-false)
  451. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  452. (returnType . "int") (status . "normal"))
  453. (ldapvalue
  454. (category . "communication") (variadic . :json-false)
  455. (parameters . [((range . ".*") (type . "string"))
  456. ((range . ".*") (type . "string"))
  457. ((range . ".*") (type . "string"))
  458. ((range . ".*") (type . "string"))
  459. ((range . "subtree,onelevel,base") (type . "option"))
  460. ((range . "none,ssl,sasl") (type . "option"))])
  461. (returnType . "string") (status . "normal"))
  462. (ldaplist
  463. (category . "communication") (variadic . :json-false)
  464. (parameters . [((range . ".*") (type . "string"))
  465. ((range . ".*") (type . "string"))
  466. ((range . ".*") (type . "string"))
  467. ((range . ".*") (type . "string"))
  468. ((range . "subtree,onelevel,base") (type . "option"))
  469. ((range . "none,ssl,sasl") (type . "option"))])
  470. (returnType . "slist") (status . "normal"))
  471. (ldaparray
  472. (category . "communication") (variadic . :json-false)
  473. (parameters . [((range . ".*") (type . "string"))
  474. ((range . ".*") (type . "string"))
  475. ((range . ".*") (type . "string"))
  476. ((range . ".*") (type . "string"))
  477. ((range . "subtree,onelevel,base") (type . "option"))
  478. ((range . "none,ssl,sasl") (type . "option"))])
  479. (returnType . "context") (status . "normal"))
  480. (laterthan
  481. (category . "files") (variadic . :json-false)
  482. (parameters . [((range . "0,1000") (type . "int"))
  483. ((range . "0,1000") (type . "int"))
  484. ((range . "0,1000") (type . "int"))
  485. ((range . "0,1000") (type . "int"))
  486. ((range . "0,1000") (type . "int"))
  487. ((range . "0,40000") (type . "int"))])
  488. (returnType . "context") (status . "normal"))
  489. (lastnode
  490. (category . "data") (variadic . :json-false)
  491. (parameters . [((range . ".*") (type . "string"))
  492. ((range . ".*") (type . "string"))])
  493. (returnType . "string") (status . "normal"))
  494. (join
  495. (category . "data") (variadic . :json-false)
  496. (parameters . [((range . ".*") (type . "string"))
  497. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  498. (returnType . "string") (status . "normal"))
  499. (isvariable
  500. (category . "utils") (variadic . :json-false)
  501. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  502. (returnType . "context") (status . "normal"))
  503. (isplain
  504. (category . "files") (variadic . :json-false)
  505. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  506. (returnType . "context") (status . "normal"))
  507. (isnewerthan
  508. (category . "files") (variadic . :json-false)
  509. (parameters . [((range . "\"?(/.*)") (type . "string"))
  510. ((range . "\"?(/.*)") (type . "string"))])
  511. (returnType . "context") (status . "normal"))
  512. (islink
  513. (category . "files") (variadic . :json-false)
  514. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  515. (returnType . "context") (status . "normal"))
  516. (islessthan
  517. (category . "data") (variadic . :json-false)
  518. (parameters . [((range . ".*") (type . "string"))
  519. ((range . ".*") (type . "string"))])
  520. (returnType . "context") (status . "normal"))
  521. (isgreaterthan
  522. (category . "data") (variadic . :json-false)
  523. (parameters . [((range . ".*") (type . "string"))
  524. ((range . ".*") (type . "string"))])
  525. (returnType . "context") (status . "normal"))
  526. (isexecutable
  527. (category . "files") (variadic . :json-false)
  528. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  529. (returnType . "context") (status . "normal"))
  530. (isdir
  531. (category . "files") (variadic . :json-false)
  532. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  533. (returnType . "context") (status . "normal"))
  534. (irange
  535. (category . "data") (variadic . :json-false)
  536. (parameters . [((range . "-99999999999,9999999999") (type . "int"))
  537. ((range . "-99999999999,9999999999") (type . "int"))])
  538. (returnType . "irange") (status . "normal"))
  539. (iprange
  540. (category . "communication") (variadic . :json-false)
  541. (parameters . [((range . ".*") (type . "string"))])
  542. (returnType . "context") (status . "normal"))
  543. (intersection
  544. (category . "data") (variadic . :json-false)
  545. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  546. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  547. (returnType . "slist") (status . "normal"))
  548. (ifelse
  549. (category . "data") (variadic . t)
  550. (parameters . [])
  551. (returnType . "string") (status . "normal"))
  552. (hubknowledge
  553. (category . "communication") (variadic . :json-false)
  554. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  555. (returnType . "string") (status . "normal"))
  556. (hostswithclass
  557. (category . "communication") (variadic . :json-false)
  558. (parameters . [((range . "[a-zA-Z0-9_]+") (type . "string"))
  559. ((range . "name,address") (type . "option"))])
  560. (returnType . "slist") (status . "normal"))
  561. (hostsseen
  562. (category . "communication") (variadic . :json-false)
  563. (parameters . [((range . "0,99999999999") (type . "int"))
  564. ((range . "lastseen,notseen") (type . "option"))
  565. ((range . "name,address") (type . "option"))])
  566. (returnType . "slist") (status . "normal"))
  567. (hostrange
  568. (category . "communication") (variadic . :json-false)
  569. (parameters . [((range . ".*") (type . "string"))
  570. ((range . ".*") (type . "string"))])
  571. (returnType . "context") (status . "normal"))
  572. (hostinnetgroup
  573. (category . "system") (variadic . :json-false)
  574. (parameters . [((range . ".*") (type . "string"))])
  575. (returnType . "context") (status . "normal"))
  576. (ip2host
  577. (category . "communication") (variadic . :json-false)
  578. (parameters . [((range . ".*") (type . "string"))])
  579. (returnType . "string") (status . "normal"))
  580. (host2ip
  581. (category . "communication") (variadic . :json-false)
  582. (parameters . [((range . ".*") (type . "string"))])
  583. (returnType . "string") (status . "normal"))
  584. (hashmatch
  585. (category . "data") (variadic . :json-false)
  586. (parameters . [((range . "\"?(/.*)") (type . "string"))
  587. ((range . "md5,sha1,crypt,cf_sha224,cf_sha256,cf_sha384,cf_sha512") (type . "option"))
  588. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  589. (returnType . "context") (status . "normal"))
  590. (hash
  591. (category . "data") (variadic . :json-false)
  592. (parameters . [((range . ".*") (type . "string"))
  593. ((range . "md5,sha1,sha256,sha512,sha384,crypt") (type . "option"))])
  594. (returnType . "string") (status . "normal"))
  595. (groupexists
  596. (category . "system") (variadic . :json-false)
  597. (parameters . [((range . ".*") (type . "string"))])
  598. (returnType . "context") (status . "normal"))
  599. (grep
  600. (category . "data") (variadic . :json-false)
  601. (parameters . [((range . ".*") (type . "string"))
  602. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  603. (returnType . "slist") (status . "normal"))
  604. (getvalues
  605. (category . "data") (variadic . :json-false)
  606. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  607. (returnType . "slist") (status . "normal"))
  608. (getusers
  609. (category . "system") (variadic . :json-false)
  610. (parameters . [((range . ".*") (type . "string"))
  611. ((range . ".*") (type . "string"))])
  612. (returnType . "slist") (status . "normal"))
  613. (getuid
  614. (category . "system") (variadic . :json-false)
  615. (parameters . [((range . ".*") (type . "string"))])
  616. (returnType . "int") (status . "normal"))
  617. (getindices
  618. (category . "data") (variadic . :json-false)
  619. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  620. (returnType . "slist") (status . "normal"))
  621. (getgid
  622. (category . "data") (variadic . :json-false)
  623. (parameters . [((range . ".*") (type . "string"))])
  624. (returnType . "int") (status . "normal"))
  625. (getfields
  626. (category . "data") (variadic . :json-false)
  627. (parameters . [((range . ".*") (type . "string"))
  628. ((range . "\"?(/.*)") (type . "string"))
  629. ((range . ".*") (type . "string"))
  630. ((range . ".*") (type . "string"))])
  631. (returnType . "int") (status . "normal"))
  632. (getenv
  633. (category . "system") (variadic . :json-false)
  634. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  635. ((range . "0,99999999999") (type . "int"))])
  636. (returnType . "string") (status . "normal"))
  637. (format
  638. (category . "data") (variadic . t)
  639. (parameters . [((range . ".*") (type . "string"))])
  640. (returnType . "string") (status . "normal"))
  641. (filter
  642. (category . "data") (variadic . :json-false)
  643. (parameters . [((range . ".*") (type . "string"))
  644. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  645. ((range . "true,false,yes,no,on,off") (type . "option"))
  646. ((range . "true,false,yes,no,on,off") (type . "option"))
  647. ((range . "0,99999999999") (type . "int"))])
  648. (returnType . "slist") (status . "normal"))
  649. (filestat
  650. (category . "files") (variadic . :json-false)
  651. (parameters . [((range . "\"?(/.*)") (type . "string"))
  652. ((range . "size,gid,uid,ino,nlink,ctime,atime,mtime,mode,modeoct,permstr,permoct,type,devno,dev_minor,dev_major,basename,dirname") (type . "option"))])
  653. (returnType . "string") (status . "normal"))
  654. (filesize
  655. (category . "files") (variadic . :json-false)
  656. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  657. (returnType . "int") (status . "normal"))
  658. (filesexist
  659. (category . "files") (variadic . :json-false)
  660. (parameters . [((range . "@[(][a-zA-Z0-9]+[)]") (type . "string"))])
  661. (returnType . "context") (status . "normal"))
  662. (fileexists
  663. (category . "files") (variadic . :json-false)
  664. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  665. (returnType . "context") (status . "normal"))
  666. (execresult
  667. (category . "utils") (variadic . :json-false)
  668. (parameters . [((range . ".+") (type . "string"))
  669. ((range . "useshell,noshell,powershell") (type . "option"))])
  670. (returnType . "string") (status . "normal"))
  671. (every
  672. (category . "data") (variadic . :json-false)
  673. (parameters . [((range . ".*") (type . "string"))
  674. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  675. (returnType . "context") (status . "normal"))
  676. (escape
  677. (category . "data") (variadic . :json-false)
  678. (parameters . [((range . ".*") (type . "string"))])
  679. (returnType . "string") (status . "normal"))
  680. (diskfree
  681. (category . "files") (variadic . :json-false)
  682. (parameters . [((range . "\"?(/.*)") (type . "string"))])
  683. (returnType . "int") (status . "normal"))
  684. (dirname
  685. (category . "files") (variadic . :json-false)
  686. (parameters . [((range . ".*") (type . "string"))])
  687. (returnType . "string") (status . "normal"))
  688. (difference
  689. (category . "data") (variadic . :json-false)
  690. (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  691. ((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))])
  692. (returnType . "slist") (status . "normal"))
  693. (countlinesmatching
  694. (category . "io") (variadic . :json-false)
  695. (parameters . [((range . ".*") (type . "string"))
  696. ((range . "\"?(/.*)") (type . "string"))])
  697. (returnType . "int") (status . "normal"))
  698. (countclassesmatching
  699. (category . "utils") (variadic . :json-false)
  700. (parameters . [((range . ".*") (type . "string"))])
  701. (returnType . "int") (status . "normal"))
  702. (classesmatching
  703. (category . "utils") (variadic . :json-false)
  704. (parameters . [((range . ".*") (type . "string"))])
  705. (returnType . "slist") (status . "normal"))
  706. (classmatch
  707. (category . "utils") (variadic . :json-false)
  708. (parameters . [((range . ".*") (type . "string"))])
  709. (returnType . "context") (status . "normal"))
  710. (classify
  711. (category . "data") (variadic . :json-false)
  712. (parameters . [((range . ".*") (type . "string"))])
  713. (returnType . "context") (status . "normal"))
  714. (changedbefore
  715. (category . "files") (variadic . :json-false)
  716. (parameters . [((range . "\"?(/.*)") (type . "string"))
  717. ((range . "\"?(/.*)") (type . "string"))])
  718. (returnType . "context") (status . "normal"))
  719. (concat
  720. (category . "data") (variadic . t)
  721. (parameters . [])
  722. (returnType . "string") (status . "normal"))
  723. (canonify
  724. (category . "data") (variadic . :json-false)
  725. (parameters . [((range . ".*") (type . "string"))])
  726. (returnType . "string") (status . "normal"))
  727. (and
  728. (category . "data") (variadic . t)
  729. (parameters . [])
  730. (returnType . "string") (status . "normal"))
  731. (ago
  732. (category . "data") (variadic . :json-false)
  733. (parameters . [((range . "0,1000") (type . "int"))
  734. ((range . "0,1000") (type . "int"))
  735. ((range . "0,1000") (type . "int"))
  736. ((range . "0,1000") (type . "int"))
  737. ((range . "0,1000") (type . "int"))
  738. ((range . "0,40000") (type . "int"))])
  739. (returnType . "int") (status . "normal"))
  740. (accumulated
  741. (category . "data") (variadic . :json-false)
  742. (parameters . [((range . "0,1000") (type . "int"))
  743. ((range . "0,1000") (type . "int"))
  744. ((range . "0,1000") (type . "int"))
  745. ((range . "0,1000") (type . "int"))
  746. ((range . "0,1000") (type . "int"))
  747. ((range . "0,40000") (type . "int"))])
  748. (returnType . "int") (status . "normal"))
  749. (accessedbefore
  750. (category . "files") (variadic . :json-false)
  751. (parameters . [((range . "\"?(/.*)") (type . "string"))
  752. ((range . "\"?(/.*)") (type . "string"))])
  753. (returnType . "context") (status . "normal"))))
  754. "Fallback CFEngine syntax, containing just function definitions.")
  755. (defvar cfengine-mode-syntax-functions-regex
  756. (regexp-opt (mapcar (lambda (def)
  757. (format "%s" (car def)))
  758. (cdr (assq 'functions cfengine3-fallback-syntax)))
  759. 'symbols))
  760. (defcustom cfengine-mode-abbrevs nil
  761. "Abbrevs for CFEngine2 mode."
  762. :group 'cfengine
  763. :type '(repeat (list (string :tag "Name")
  764. (string :tag "Expansion")
  765. (choice :tag "Hook" (const nil) function))))
  766. (make-obsolete-variable 'cfengine-mode-abbrevs 'edit-abbrevs "24.1")
  767. ;; Taken from the doc for pre-release 2.1.
  768. (eval-and-compile
  769. (defconst cfengine2-actions
  770. '("acl" "alerts" "binservers" "broadcast" "control" "classes" "copy"
  771. "defaultroute" "disks" "directories" "disable" "editfiles" "files"
  772. "filters" "groups" "homeservers" "ignore" "import" "interfaces"
  773. "links" "mailserver" "methods" "miscmounts" "mountables"
  774. "processes" "packages" "rename" "required" "resolve"
  775. "shellcommands" "tidy" "unmount"
  776. ;; Keywords for cfservd.
  777. "admit" "grant" "deny")
  778. "List of the action keywords supported by Cfengine.
  779. This includes those for cfservd as well as cfagent.")
  780. (defconst cfengine3-defuns '("bundle" "body")
  781. "List of the CFEngine 3.x defun headings.")
  782. (defconst cfengine3-defuns-regex (regexp-opt cfengine3-defuns t)
  783. "Regex to match the CFEngine 3.x defuns.")
  784. (defconst cfengine3-defun-full-re (concat "^\\s-*" cfengine3-defuns-regex
  785. "\\s-+\\(\\(?:\\w\\|\\s_\\)+\\)" ;type
  786. "\\s-+\\(\\(?:\\w\\|\\s_\\)+\\)" ;id
  787. )
  788. "Regexp matching full defun declaration (excluding argument list).")
  789. (defconst cfengine3-macro-regex "\\(@[a-zA-Z].+\\)")
  790. (defconst cfengine3-class-selector-regex "\\([\"']?[[:alnum:]_().$&|!:]+[\"']?\\)::")
  791. (defconst cfengine3-category-regex "\\([[:alnum:]_]+\\):")
  792. (defconst cfengine3-vartypes '("string" "int" "real" "slist" "ilist" "rlist"
  793. "irange" "rrange" "counter" "data")
  794. "List of the CFEngine 3.x variable types."))
  795. (defvar cfengine2-font-lock-keywords
  796. `(;; Actions.
  797. ;; List the allowed actions explicitly, so that errors are more obvious.
  798. (,(concat "^[ \t]*" (eval-when-compile
  799. (regexp-opt cfengine2-actions t))
  800. ":")
  801. 1 font-lock-keyword-face)
  802. ;; Classes.
  803. ("^[ \t]*\\([[:alnum:]_().|!]+\\)::" 1 font-lock-function-name-face)
  804. ;; Variables.
  805. ("$(\\([[:alnum:]_]+\\))" 1 font-lock-variable-name-face)
  806. ("${\\([[:alnum:]_]+\\)}" 1 font-lock-variable-name-face)
  807. ;; Variable definitions.
  808. ("\\_<\\([[:alnum:]_]+\\)[ \t]*=[ \t]*(" 1 font-lock-variable-name-face)
  809. ;; File, acl &c in group: { token ... }
  810. ("{[ \t]*\\([^ \t\n]+\\)" 1 font-lock-constant-face)))
  811. (defvar cfengine3-font-lock-keywords
  812. `(
  813. ;; Macros
  814. (,(concat "^" cfengine3-macro-regex)
  815. 1 font-lock-error-face)
  816. ;; invalid macros
  817. (,(concat "^[ \t]*" cfengine3-macro-regex)
  818. 1 font-lock-warning-face)
  819. ;; Defuns. This happens early so they don't get caught by looser
  820. ;; patterns.
  821. (,(concat "\\_<" cfengine3-defuns-regex "\\_>"
  822. "[ \t]+\\_<\\([[:alnum:]_.:]+\\)\\_>"
  823. "[ \t]+\\_<\\([[:alnum:]_.:]+\\)"
  824. ;; Optional parentheses with variable names inside.
  825. "\\(?:(\\([^)]*\\))\\)?")
  826. (1 font-lock-builtin-face)
  827. (2 font-lock-constant-face)
  828. (3 font-lock-function-name-face)
  829. (4 font-lock-variable-name-face nil t))
  830. ;; Class selectors.
  831. (,(concat "^[ \t]*" cfengine3-class-selector-regex)
  832. 1 font-lock-keyword-face)
  833. ;; Categories.
  834. (,(concat "^[ \t]*" cfengine3-category-regex)
  835. 1 font-lock-builtin-face)
  836. ;; Variables, including scope, e.g. module.var
  837. ("[@$](\\([[:alnum:]_.:]+\\))" 1 font-lock-variable-name-face)
  838. ("[@$]{\\([[:alnum:]_.:]+\\)}" 1 font-lock-variable-name-face)
  839. ;; Variable definitions.
  840. ("\\_<\\([[:alnum:]_]+\\)[ \t]*=[ \t]*(" 1 font-lock-variable-name-face)
  841. ;; Variable types.
  842. (,(concat "\\_<" (eval-when-compile (regexp-opt cfengine3-vartypes t)) "\\_>")
  843. 1 font-lock-type-face)))
  844. (defvar cfengine2-imenu-expression
  845. `((nil ,(concat "^[ \t]*" (eval-when-compile
  846. (regexp-opt cfengine2-actions t))
  847. ":[^:]")
  848. 1)
  849. ("Variables/classes" "\\_<\\([[:alnum:]_]+\\)[ \t]*=[ \t]*(" 1)
  850. ("Variables/classes" "\\_<define=\\([[:alnum:]_]+\\)" 1)
  851. ("Variables/classes" "\\_<DefineClass\\>[ \t]+\\([[:alnum:]_]+\\)" 1))
  852. "`imenu-generic-expression' for CFEngine mode.")
  853. (defun cfengine2-outline-level ()
  854. "`outline-level' function for CFEngine mode."
  855. (if (looking-at "[^:]+\\(?:[:]+\\)$")
  856. (length (match-string 1))))
  857. (defun cfengine2-beginning-of-defun ()
  858. "`beginning-of-defun' function for CFEngine mode.
  859. Treats actions as defuns."
  860. (unless (<= (current-column) (current-indentation))
  861. (end-of-line))
  862. (if (re-search-backward "^[[:alpha:]]+: *$" nil t)
  863. (beginning-of-line)
  864. (goto-char (point-min)))
  865. t)
  866. (defun cfengine2-end-of-defun ()
  867. "`end-of-defun' function for CFEngine mode.
  868. Treats actions as defuns."
  869. (end-of-line)
  870. (if (re-search-forward "^[[:alpha:]]+: *$" nil t)
  871. (beginning-of-line)
  872. (goto-char (point-max)))
  873. t)
  874. ;; Fixme: Should get an extra indent step in editfiles BeginGroup...s.
  875. (defun cfengine2-indent-line ()
  876. "Indent a line in Cfengine mode.
  877. Intended as the value of `indent-line-function'."
  878. (let ((pos (- (point-max) (point))))
  879. (save-restriction
  880. (narrow-to-defun)
  881. (back-to-indentation)
  882. (cond
  883. ;; Action selectors aren't indented; class selectors are
  884. ;; indented one step.
  885. ((looking-at "[[:alnum:]_().|!]+:\\(:\\)?")
  886. (if (match-string 1)
  887. (indent-line-to cfengine-indent)
  888. (indent-line-to 0)))
  889. ;; Outdent leading close brackets one step.
  890. ((or (eq ?\} (char-after))
  891. (eq ?\) (char-after)))
  892. (condition-case ()
  893. (indent-line-to (save-excursion
  894. (forward-char)
  895. (backward-sexp)
  896. (current-column)))
  897. (error nil)))
  898. ;; Inside brackets/parens: indent to start column of non-comment
  899. ;; token on line following open bracket or by one step from open
  900. ;; bracket's column.
  901. ((condition-case ()
  902. (progn (indent-line-to (save-excursion
  903. (backward-up-list)
  904. (forward-char)
  905. (skip-chars-forward " \t")
  906. (if (looking-at "[^\n#]")
  907. (current-column)
  908. (skip-chars-backward " \t")
  909. (+ (current-column) -1
  910. cfengine-indent))))
  911. t)
  912. (error nil)))
  913. ;; Indent by two steps after a class selector.
  914. ((save-excursion
  915. (re-search-backward "^[ \t]*[[:alnum:]_().|!]+::" nil t))
  916. (indent-line-to (* 2 cfengine-indent)))
  917. ;; Indent by one step if we're after an action header.
  918. ((save-excursion
  919. (goto-char (point-min))
  920. (looking-at "[[:alpha:]]+:[ \t]*$"))
  921. (indent-line-to cfengine-indent))
  922. ;; Else don't indent.
  923. (t
  924. (indent-line-to 0))))
  925. ;; If initial point was within line's indentation,
  926. ;; position after the indentation. Else stay at same point in text.
  927. (if (> (- (point-max) pos) (point))
  928. (goto-char (- (point-max) pos)))))
  929. ;; This doesn't work too well in Emacs 21.2. See 22.1 development
  930. ;; code.
  931. (defun cfengine-fill-paragraph (&optional justify)
  932. "Fill `paragraphs' in Cfengine code."
  933. (interactive "P")
  934. (or (if (fboundp 'fill-comment-paragraph)
  935. (fill-comment-paragraph justify) ; post Emacs 21.3
  936. ;; else do nothing in a comment
  937. (nth 4 (parse-partial-sexp (save-excursion
  938. (beginning-of-defun)
  939. (point))
  940. (point))))
  941. (let ((paragraph-start
  942. ;; Include start of parenthesized block.
  943. "\f\\|[ \t]*$\\|.*\(")
  944. (paragraph-separate
  945. ;; Include action and class lines, start and end of
  946. ;; bracketed blocks and end of parenthesized blocks to
  947. ;; avoid including these in fill. This isn't ideal.
  948. "[ \t\f]*$\\|.*#\\|.*[\){}]\\|\\s-*[[:alpha:]_().|!]+:")
  949. fill-paragraph-function)
  950. (fill-paragraph justify))
  951. t))
  952. (defun cfengine3-beginning-of-defun ()
  953. "`beginning-of-defun' function for Cfengine 3 mode.
  954. Treats body/bundle blocks as defuns."
  955. (unless (<= (current-column) (current-indentation))
  956. (end-of-line))
  957. (if (re-search-backward (concat "^[ \t]*" cfengine3-defuns-regex "\\_>") nil t)
  958. (beginning-of-line)
  959. (goto-char (point-min)))
  960. t)
  961. (defun cfengine3-end-of-defun ()
  962. "`end-of-defun' function for Cfengine 3 mode.
  963. Treats body/bundle blocks as defuns."
  964. (end-of-line)
  965. (if (re-search-forward (concat "^[ \t]*" cfengine3-defuns-regex "\\_>") nil t)
  966. (beginning-of-line)
  967. (goto-char (point-max)))
  968. t)
  969. (defun cfengine3-indent-line ()
  970. "Indent a line in CFEngine 3 mode.
  971. Intended as the value of `indent-line-function'."
  972. (let ((pos (- (point-max) (point)))
  973. parse)
  974. (save-restriction
  975. (narrow-to-defun)
  976. (back-to-indentation)
  977. (setq parse (parse-partial-sexp (point-min) (point)))
  978. (when cfengine-mode-debug
  979. (message "%S" parse))
  980. (cond
  981. ;; Macros start at 0. But make sure we're not inside a string.
  982. ((and (not (nth 3 parse))
  983. (looking-at (concat cfengine3-macro-regex)))
  984. (indent-line-to 0))
  985. ;; Body/bundle blocks start at 0.
  986. ((looking-at (concat cfengine3-defuns-regex "\\_>"))
  987. (indent-line-to 0))
  988. ;; Categories are indented one step.
  989. ((looking-at (concat cfengine3-category-regex "[ \t]*\\(#.*\\)*$"))
  990. (indent-line-to cfengine-indent))
  991. ;; Class selectors are indented two steps.
  992. ((looking-at (concat cfengine3-class-selector-regex "[ \t]*\\(#.*\\)*$"))
  993. (indent-line-to (* 2 cfengine-indent)))
  994. ;; Outdent leading close brackets one step.
  995. ((or (eq ?\} (char-after))
  996. (eq ?\) (char-after)))
  997. (condition-case ()
  998. (indent-line-to (save-excursion
  999. (forward-char)
  1000. (backward-sexp)
  1001. (move-beginning-of-line nil)
  1002. (skip-chars-forward " \t")
  1003. (current-column)))
  1004. (error nil)))
  1005. ;; Inside a string and it starts before this line: do nothing.
  1006. ((and (nth 3 parse)
  1007. (< (nth 8 parse) (save-excursion (beginning-of-line) (point))))
  1008. )
  1009. ;; Inside a defun, but not a nested list (depth is 1). This is
  1010. ;; a promise, usually.
  1011. ;; Indent to cfengine-indent times the nested depth
  1012. ;; plus 2. That way, promises indent deeper than class
  1013. ;; selectors, which in turn are one deeper than categories.
  1014. ((= 1 (nth 0 parse))
  1015. (let ((p-anchor (nth 0 cfengine-parameters-indent))
  1016. (p-what (nth 1 cfengine-parameters-indent))
  1017. (p-indent (nth 2 cfengine-parameters-indent)))
  1018. ;; Do we have the parameter anchor and location and indent
  1019. ;; defined, and are we looking at a promise parameter?
  1020. (if (and p-anchor p-what p-indent
  1021. (looking-at "\\([[:alnum:]_]+[ \t]*\\)=>"))
  1022. (let* ((arrow-offset (* -1 (length (match-string 1))))
  1023. (extra-offset (if (eq p-what 'arrow) arrow-offset 0))
  1024. (base-offset (if (eq p-anchor 'promise)
  1025. (* (+ 2 (nth 0 parse)) cfengine-indent)
  1026. 0)))
  1027. (indent-line-to (max 0 (+ p-indent base-offset extra-offset))))
  1028. ;; Else, indent to cfengine-indent times the nested depth
  1029. ;; plus 2. That way, promises indent deeper than class
  1030. ;; selectors, which in turn are one deeper than categories.
  1031. (indent-line-to (* (+ 2 (nth 0 parse)) cfengine-indent)))))
  1032. ;; Inside brackets/parens: indent to start column of non-comment
  1033. ;; token on line following open bracket or by one step from open
  1034. ;; bracket's column.
  1035. ((condition-case ()
  1036. (progn (indent-line-to (save-excursion
  1037. (backward-up-list)
  1038. (forward-char)
  1039. (skip-chars-forward " \t")
  1040. (cond
  1041. ((looking-at "[^\n#]")
  1042. (current-column))
  1043. ((looking-at "[^\n#]")
  1044. (current-column))
  1045. (t
  1046. (skip-chars-backward " \t")
  1047. (+ (current-column) -1
  1048. cfengine-indent)))))
  1049. t)
  1050. (error nil)))
  1051. ;; Else don't indent.
  1052. (t (indent-line-to 0))))
  1053. ;; If initial point was within line's indentation,
  1054. ;; position after the indentation. Else stay at same point in text.
  1055. (if (> (- (point-max) pos) (point))
  1056. (goto-char (- (point-max) pos)))))
  1057. (defun cfengine3-reformat-json-string ()
  1058. "Reformat the current string as JSON using `json-pretty-print'."
  1059. (interactive)
  1060. (let ((ppss (syntax-ppss)))
  1061. (when (nth 3 ppss) ;inside a string
  1062. (save-excursion
  1063. (goto-char (nth 8 ppss))
  1064. (forward-char 1)
  1065. (let ((start (point)))
  1066. (forward-sexp 1)
  1067. (json-pretty-print start
  1068. (point)))))))
  1069. ;; CFEngine 3.x grammar
  1070. ;; specification: blocks
  1071. ;; blocks: block | blocks block;
  1072. ;; block: bundle typeid blockid bundlebody
  1073. ;; | bundle typeid blockid usearglist bundlebody
  1074. ;; | body typeid blockid bodybody
  1075. ;; | body typeid blockid usearglist bodybody;
  1076. ;; typeid: id
  1077. ;; blockid: id
  1078. ;; usearglist: '(' aitems ')';
  1079. ;; aitems: aitem | aitem ',' aitems |;
  1080. ;; aitem: id
  1081. ;; bundlebody: '{' statements '}'
  1082. ;; statements: statement | statements statement;
  1083. ;; statement: category | classpromises;
  1084. ;; bodybody: '{' bodyattribs '}'
  1085. ;; bodyattribs: bodyattrib | bodyattribs bodyattrib;
  1086. ;; bodyattrib: class | selections;
  1087. ;; selections: selection | selections selection;
  1088. ;; selection: id ASSIGN rval ';' ;
  1089. ;; classpromises: classpromise | classpromises classpromise;
  1090. ;; classpromise: class | promises;
  1091. ;; promises: promise | promises promise;
  1092. ;; category: CATEGORY
  1093. ;; promise: promiser ARROW rval constraints ';' | promiser constraints ';';
  1094. ;; constraints: constraint | constraints ',' constraint |;
  1095. ;; constraint: id ASSIGN rval;
  1096. ;; class: CLASS
  1097. ;; id: ID
  1098. ;; rval: ID | QSTRING | NAKEDVAR | list | usefunction
  1099. ;; list: '{' litems '}' ;
  1100. ;; litems: litem | litem ',' litems |;
  1101. ;; litem: ID | QSTRING | NAKEDVAR | list | usefunction
  1102. ;; functionid: ID | NAKEDVAR
  1103. ;; promiser: QSTRING
  1104. ;; usefunction: functionid givearglist
  1105. ;; givearglist: '(' gaitems ')'
  1106. ;; gaitems: gaitem | gaitems ',' gaitem |;
  1107. ;; gaitem: ID | QSTRING | NAKEDVAR | list | usefunction
  1108. ;; # from lexer:
  1109. ;; bundle: "bundle"
  1110. ;; body: "body"
  1111. ;; COMMENT #[^\n]*
  1112. ;; NAKEDVAR [$@][(][a-zA-Z0-9_\200-\377.]+[)]|[$@][{][a-zA-Z0-9_\200-\377.]+[}]
  1113. ;; ID: [a-zA-Z0-9_\200-\377]+
  1114. ;; ASSIGN: "=>"
  1115. ;; ARROW: "->"
  1116. ;; QSTRING: \"((\\\")|[^"])*\"|\'((\\\')|[^'])*\'|`[^`]*`
  1117. ;; CLASS: [.|&!()a-zA-Z0-9_\200-\377]+::
  1118. ;; CATEGORY: [a-zA-Z_]+:
  1119. (defun cfengine3--current-function ()
  1120. "Look up current CFEngine 3 function"
  1121. (let* ((syntax (cfengine3-make-syntax-cache))
  1122. (flist (assq 'functions syntax)))
  1123. (when flist
  1124. (let ((w (save-excursion
  1125. (skip-syntax-forward "w_")
  1126. (when (search-backward-regexp
  1127. cfengine-mode-syntax-functions-regex
  1128. (point-at-bol)
  1129. t)
  1130. (match-string 1)))))
  1131. (and w (assq (intern w) flist))))))
  1132. ;; format from "cf-promises -s json", e.g. "sort" function:
  1133. ;; ((category . "data")
  1134. ;; (variadic . :json-false)
  1135. ;; (parameters . [((range . "[a-zA-Z0-9_$(){}\\[\\].:]+") (type . "string"))
  1136. ;; ((range . "lex,int,real,IP,ip,MAC,mac") (type . "option"))])
  1137. ;; (returnType . "slist")
  1138. ;; (status . "normal"))
  1139. (defun cfengine3-format-function-docstring (fdef)
  1140. (let* ((f (format "%s" (car-safe fdef)))
  1141. (def (cdr fdef))
  1142. (rtype (cdr (assq 'returnType def)))
  1143. (plist (cdr (assq 'parameters def)))
  1144. (has-some-parameters (> (length plist) 0))
  1145. (variadic (eq t (cdr (assq 'variadic def)))))
  1146. ;; (format "[%S]%s %s(%s%s)" def
  1147. (format "%s %s(%s%s)"
  1148. (if rtype
  1149. (propertize rtype 'face 'font-lock-variable-name-face)
  1150. "???")
  1151. (propertize f 'face 'font-lock-function-name-face)
  1152. (mapconcat (lambda (p)
  1153. (let* ((type (cdr (assq 'type p)))
  1154. (description (cdr (assq 'description p)))
  1155. (desc-string (if (stringp description)
  1156. (concat " /" description "/")
  1157. ""))
  1158. (range (cdr (assq 'range p))))
  1159. (cond
  1160. ((not (stringp type)) "???type???")
  1161. ((not (stringp range)) "???range???")
  1162. ;; options are lists of possible keywords
  1163. ((equal type "option")
  1164. (propertize (concat "[" range "]" desc-string)
  1165. 'face
  1166. 'font-lock-keyword-face))
  1167. ;; anything else is a type name as a variable
  1168. (t (propertize (concat type desc-string)
  1169. 'face
  1170. 'font-lock-variable-name-face)))))
  1171. plist
  1172. ", ")
  1173. (if variadic
  1174. (if has-some-parameters ", ..." "...")
  1175. ""))))
  1176. (defun cfengine3-clear-syntax-cache ()
  1177. "Clear the internal syntax cache.
  1178. Should not be necessary unless you reinstall CFEngine."
  1179. (interactive)
  1180. (setq cfengine-mode-syntax-functions-regex nil)
  1181. (setq cfengine-mode-syntax-cache nil))
  1182. (defun cfengine3-make-syntax-cache ()
  1183. "Build the CFEngine 3 syntax cache and return the syntax.
  1184. Calls `cfengine-cf-promises' with \"-s json\"."
  1185. (or (cdr (assoc cfengine-cf-promises cfengine-mode-syntax-cache))
  1186. (let ((syntax (or (when cfengine-cf-promises
  1187. (with-demoted-errors "cfengine3-make-syntax-cache: %S"
  1188. (with-temp-buffer
  1189. (or (zerop (process-file cfengine-cf-promises
  1190. nil ; no input
  1191. t ; output
  1192. nil ; no redisplay
  1193. "-s" "json"))
  1194. (error "%s" (buffer-substring
  1195. (point-min)
  1196. (progn (goto-char (point-min))
  1197. (line-end-position)))))
  1198. (goto-char (point-min))
  1199. (json-read))))
  1200. cfengine3-fallback-syntax)))
  1201. (push (cons cfengine-cf-promises syntax)
  1202. cfengine-mode-syntax-cache)
  1203. (setq cfengine-mode-syntax-functions-regex
  1204. (regexp-opt (mapcar (lambda (def)
  1205. (format "%s" (car def)))
  1206. (cdr (assq 'functions syntax)))
  1207. 'symbols))
  1208. syntax)))
  1209. (defun cfengine3-documentation-function ()
  1210. "Document CFengine 3 functions around point.
  1211. Intended as the value of `eldoc-documentation-function', which see.
  1212. Use it by enabling `eldoc-mode'."
  1213. (let ((fdef (cfengine3--current-function)))
  1214. (when fdef
  1215. (cfengine3-format-function-docstring fdef))))
  1216. (defun cfengine3-completion-function ()
  1217. "Return completions for function name around or before point."
  1218. (let* ((bounds (save-excursion
  1219. (let ((p (point)))
  1220. (skip-syntax-backward "w_" (point-at-bol))
  1221. (list (point) p))))
  1222. (syntax (cfengine3-make-syntax-cache))
  1223. (flist (assq 'functions syntax)))
  1224. (when bounds
  1225. (append bounds (list (cdr flist))))))
  1226. (defun cfengine-common-settings ()
  1227. (set (make-local-variable 'syntax-propertize-function)
  1228. ;; In the main syntax-table, \ is marked as a punctuation, because
  1229. ;; of its use in DOS-style directory separators. Here we try to
  1230. ;; recognize the cases where \ is used as an escape inside strings.
  1231. (syntax-propertize-rules ("\\(\\(?:\\\\\\)+\\)\"" (1 "\\"))))
  1232. (set (make-local-variable 'parens-require-spaces) nil)
  1233. (set (make-local-variable 'comment-start) "# ")
  1234. (set (make-local-variable 'comment-start-skip)
  1235. "\\(\\(?:^\\|[^\\\\\n]\\)\\(?:\\\\\\\\\\)*\\)#+[ \t]*")
  1236. ;; Like Lisp mode. Without this, we lose with, say,
  1237. ;; `backward-up-list' when there's an unbalanced quote in a
  1238. ;; preceding comment.
  1239. (set (make-local-variable 'parse-sexp-ignore-comments) t))
  1240. (defun cfengine-common-syntax (table)
  1241. ;; The syntax defaults seem OK to give reasonable word movement.
  1242. (modify-syntax-entry ?# "<" table)
  1243. (modify-syntax-entry ?\n ">#" table)
  1244. (modify-syntax-entry ?\" "\"" table) ; "string"
  1245. (modify-syntax-entry ?\' "\"" table) ; 'string'
  1246. ;; Variable substitution.
  1247. (modify-syntax-entry ?$ "." table)
  1248. ;; Doze path separators.
  1249. (modify-syntax-entry ?\\ "." table))
  1250. (defconst cfengine3--prettify-symbols-alist
  1251. '(("->" . ?→)
  1252. ("=>" . ?⇒)
  1253. ("::" . ?∷)))
  1254. (defun cfengine3-create-imenu-index ()
  1255. "A function for `imenu-create-index-function'.
  1256. Note: defun name is separated by space such as `body
  1257. package_method opencsw' and imenu will replace spaces according
  1258. to `imenu-space-replacement' (which see)."
  1259. (goto-char (point-min))
  1260. (let ((defuns ()))
  1261. (while (re-search-forward cfengine3-defun-full-re nil t)
  1262. (push (cons (mapconcat #'match-string '(1 2 3) " ")
  1263. (copy-marker (match-beginning 3)))
  1264. defuns))
  1265. (nreverse defuns)))
  1266. (defun cfengine3-current-defun ()
  1267. "A function for `add-log-current-defun-function'."
  1268. (end-of-line)
  1269. (beginning-of-defun)
  1270. (and (looking-at cfengine3-defun-full-re)
  1271. (mapconcat #'match-string '(1 2 3) " ")))
  1272. ;;;###autoload
  1273. (define-derived-mode cfengine3-mode prog-mode "CFE3"
  1274. "Major mode for editing CFEngine3 input.
  1275. There are no special keybindings by default.
  1276. Action blocks are treated as defuns, i.e. \\[beginning-of-defun] moves
  1277. to the action header."
  1278. (cfengine-common-settings)
  1279. (cfengine-common-syntax cfengine3-mode-syntax-table)
  1280. (set (make-local-variable 'indent-line-function) #'cfengine3-indent-line)
  1281. (setq font-lock-defaults
  1282. '(cfengine3-font-lock-keywords
  1283. nil nil nil beginning-of-defun))
  1284. (setq-local prettify-symbols-alist cfengine3--prettify-symbols-alist)
  1285. ;; `compile-command' is almost never a `make' call with CFEngine so
  1286. ;; we override it
  1287. (when cfengine-cf-promises
  1288. (set (make-local-variable 'compile-command)
  1289. (concat cfengine-cf-promises
  1290. " -f "
  1291. (when buffer-file-name
  1292. (shell-quote-argument buffer-file-name)))))
  1293. ;; For emacs < 25.1 where `eldoc-documentation-function' defaults to
  1294. ;; nil.
  1295. (or eldoc-documentation-function
  1296. (setq-local eldoc-documentation-function #'ignore))
  1297. (add-function :before-until (local 'eldoc-documentation-function)
  1298. #'cfengine3-documentation-function)
  1299. (add-hook 'completion-at-point-functions
  1300. #'cfengine3-completion-function nil t)
  1301. ;; Use defuns as the essential syntax block.
  1302. (setq-local beginning-of-defun-function #'cfengine3-beginning-of-defun)
  1303. (setq-local end-of-defun-function #'cfengine3-end-of-defun)
  1304. (setq-local imenu-create-index-function #'cfengine3-create-imenu-index)
  1305. (setq-local add-log-current-defun-function #'cfengine3-current-defun))
  1306. ;;;###autoload
  1307. (define-derived-mode cfengine2-mode prog-mode "CFE2"
  1308. "Major mode for editing CFEngine2 input.
  1309. There are no special keybindings by default.
  1310. Action blocks are treated as defuns, i.e. \\[beginning-of-defun] moves
  1311. to the action header."
  1312. (cfengine-common-settings)
  1313. (cfengine-common-syntax cfengine2-mode-syntax-table)
  1314. ;; Shell commands can be quoted by single, double or back quotes.
  1315. ;; It's debatable whether we should define string syntax, but it
  1316. ;; should avoid potential confusion in some cases.
  1317. (modify-syntax-entry ?\` "\"" cfengine2-mode-syntax-table)
  1318. (set (make-local-variable 'indent-line-function) #'cfengine2-indent-line)
  1319. (set (make-local-variable 'outline-regexp) "[ \t]*\\(\\sw\\|\\s_\\)+:+")
  1320. (set (make-local-variable 'outline-level) #'cfengine2-outline-level)
  1321. (set (make-local-variable 'fill-paragraph-function)
  1322. #'cfengine-fill-paragraph)
  1323. (define-abbrev-table 'cfengine2-mode-abbrev-table cfengine-mode-abbrevs)
  1324. (setq font-lock-defaults
  1325. '(cfengine2-font-lock-keywords nil nil nil beginning-of-line))
  1326. ;; Fixme: set the args of functions in evaluated classes to string
  1327. ;; syntax, and then obey syntax properties.
  1328. (setq imenu-generic-expression cfengine2-imenu-expression)
  1329. (set (make-local-variable 'beginning-of-defun-function)
  1330. #'cfengine2-beginning-of-defun)
  1331. (set (make-local-variable 'end-of-defun-function) #'cfengine2-end-of-defun))
  1332. ;;;###autoload
  1333. (defun cfengine-auto-mode ()
  1334. "Choose `cfengine2-mode' or `cfengine3-mode' by buffer contents."
  1335. (interactive)
  1336. (if (save-excursion
  1337. (save-restriction
  1338. (widen)
  1339. (goto-char (point-min))
  1340. (forward-comment (point-max))
  1341. (or (eobp)
  1342. (re-search-forward
  1343. (concat "^\\s-*" cfengine3-defuns-regex "\\_>") nil t))))
  1344. (cfengine3-mode)
  1345. (cfengine2-mode)))
  1346. (defalias 'cfengine-mode 'cfengine3-mode)
  1347. (provide 'cfengine3)
  1348. (provide 'cfengine)
  1349. ;;; cfengine.el ends here