add securemodelines.vim
[cmccabe-etc] / .vim / plugin / securemodelines.vim
1 " vim: set sw=4 sts=4 et ft=vim :
2 " Script:           securemodelines.vim
3 " Author:           Ciaran McCreesh <ciaran.mccreesh at googlemail.com>
4 " Homepage:         http://github.com/ciaranm/securemodelines
5 " Requires:         Vim 7
6 " License:          Redistribute under the same terms as Vim itself
7 " Purpose:          A secure alternative to modelines
8
9 if &compatible || v:version < 700 || exists('g:loaded_securemodelines')
10     finish
11 endif
12 let g:loaded_securemodelines = 1
13
14 if (! exists("g:secure_modelines_allowed_items"))
15     let g:secure_modelines_allowed_items = [
16                 \ "textwidth",   "tw",
17                 \ "softtabstop", "sts",
18                 \ "tabstop",     "ts",
19                 \ "shiftwidth",  "sw",
20                 \ "expandtab",   "et",   "noexpandtab", "noet",
21                 \ "filetype",    "ft",
22                 \ "foldmethod",  "fdm",
23                 \ "readonly",    "ro",   "noreadonly", "noro",
24                 \ "rightleft",   "rl",   "norightleft", "norl",
25                 \ "cindent",     "cin",  "nocindent", "nocin",
26                 \ "smartindent", "si",   "nosmartindent", "nosi",
27                 \ "autoindent",  "ai",   "noautoindent", "noai",
28                 \ "spell",
29                 \ "spelllang"
30                 \ ]
31 endif
32
33 if (! exists("g:secure_modelines_verbose"))
34     let g:secure_modelines_verbose = 0
35 endif
36
37 if (! exists("g:secure_modelines_modelines"))
38     let g:secure_modelines_modelines=5
39 endif
40
41 if (! exists("g:secure_modelines_leave_modeline"))
42     if &modeline
43         set nomodeline
44         if g:secure_modelines_verbose
45             echohl WarningMsg
46             echo "Forcibly disabling internal modelines for securemodelines.vim"
47             echohl None
48         endif
49     endif
50 endif
51
52 fun! <SID>IsInList(list, i) abort
53     for l:item in a:list
54         if a:i == l:item
55             return 1
56         endif
57     endfor
58     return 0
59 endfun
60
61 fun! <SID>DoOne(item) abort
62     let l:matches = matchlist(a:item, '^\([a-z]\+\)\%([-+^]\?=[a-zA-Z0-9_\-.]\+\)\?$')
63     if len(l:matches) > 0
64         if <SID>IsInList(g:secure_modelines_allowed_items, l:matches[1])
65             exec "setlocal " . a:item
66         elseif g:secure_modelines_verbose
67             echohl WarningMsg
68             echo "Ignoring '" . a:item . "' in modeline"
69             echohl None
70         endif
71     endif
72 endfun
73
74 fun! <SID>DoNoSetModeline(line) abort
75     for l:item in split(a:line, '[ \t:]')
76         call <SID>DoOne(l:item)
77     endfor
78 endfun
79
80 fun! <SID>DoSetModeline(line) abort
81     for l:item in split(a:line)
82         call <SID>DoOne(l:item)
83     endfor
84 endfun
85
86 fun! <SID>CheckVersion(op, ver) abort
87     if a:op == "="
88         return v:version != a:ver
89     elseif a:op == "<"
90         return v:version < a:ver
91     elseif a:op == ">"
92         return v:version >= a:ver
93     else
94         return 0
95     endif
96 endfun
97
98 fun! <SID>DoModeline(line) abort
99     let l:matches = matchlist(a:line, '\%(\S\@<!\%(vi\|vim\([<>=]\?\)\([0-9]\+\)\?\)\|\sex\):\s*\%(set\s\+\)\?\([^:]\+\):\S\@!')
100     if len(l:matches) > 0
101         let l:operator = ">"
102         if len(l:matches[1]) > 0
103             let l:operator = l:matches[1]
104         endif
105         if len(l:matches[2]) > 0
106             if <SID>CheckVersion(l:operator, l:matches[2]) ? 0 : 1
107                 return
108             endif
109         endif
110         return <SID>DoSetModeline(l:matches[3])
111     endif
112
113     let l:matches = matchlist(a:line, '\%(\S\@<!\%(vi\|vim\([<>=]\?\)\([0-9]\+\)\?\)\|\sex\):\(.\+\)')
114     if len(l:matches) > 0
115         let l:operator = ">"
116         if len(l:matches[1]) > 0
117             let l:operator = l:matches[1]
118         endif
119         if len(l:matches[2]) > 0
120             if <SID>CheckVersion(l:operator, l:matches[2]) ? 0 : 1
121                 return
122             endif
123         endif
124         return <SID>DoNoSetModeline(l:matches[3])
125     endif
126 endfun
127
128 fun! <SID>DoModelines() abort
129     if line("$") > g:secure_modelines_modelines
130         let l:lines={ }
131         call map(filter(getline(1, g:secure_modelines_modelines) +
132                     \ getline(line("$") - g:secure_modelines_modelines, "$"),
133                     \ 'v:val =~ ":"'), 'extend(l:lines, { v:val : 0 } )')
134         for l:line in keys(l:lines)
135             call <SID>DoModeline(l:line)
136         endfor
137     else
138         for l:line in getline(1, "$")
139             call <SID>DoModeline(l:line)
140         endfor
141     endif
142 endfun
143
144 fun! SecureModelines_DoModelines() abort
145     call <SID>DoModelines()
146 endfun
147
148 aug SecureModeLines
149     au!
150     au BufRead,StdinReadPost * :call <SID>DoModelines()
151 aug END
152