From da51dc9cf202772f60bd2da975dbef257bd9237c Mon Sep 17 00:00:00 2001 From: Eliseo Martínez Date: Mon, 12 May 2014 02:25:17 +0200 Subject: Introduce nvim namespace: Move files. Move files from src/ to src/nvim/. - src/nvim/ becomes the new root dir for nvim executable sources. - src/libnvim/ is planned to become root dir of the neovim library. --- src/CMakeLists.txt | 114 - src/api/buffer.c | 413 - src/api/buffer.h | 145 - src/api/defs.h | 74 - src/api/helpers.c | 578 - src/api/helpers.h | 90 - src/api/tabpage.c | 88 - src/api/tabpage.h | 47 - src/api/vim.c | 321 - src/api/vim.h | 158 - src/api/window.c | 184 - src/api/window.h | 115 - src/arabic.c | 1532 -- src/arabic.h | 16 - src/ascii.h | 97 - src/blowfish.c | 622 - src/blowfish.h | 13 - src/buffer.c | 4657 ------ src/buffer.h | 81 - src/buffer_defs.h | 1081 -- src/charset.c | 1918 --- src/charset.h | 66 - src/crypt.c | 236 - src/crypt.h | 82 - src/cursor_shape.c | 209 - src/cursor_shape.h | 52 - src/diff.c | 2611 ---- src/diff.h | 33 - src/digraph.c | 1881 --- src/digraph.h | 12 - src/edit.c | 8444 ---------- src/edit.h | 58 - src/eval.c | 19819 ------------------------ src/eval.h | 152 - src/eval_defs.h | 116 - src/ex_cmds.c | 6374 -------- src/ex_cmds.h | 75 - src/ex_cmds2.c | 3541 ----- src/ex_cmds2.h | 95 - src/ex_cmds_defs.h | 1191 -- src/ex_docmd.c | 9109 ----------- src/ex_docmd.h | 69 - src/ex_eval.c | 2073 --- src/ex_eval.h | 159 - src/ex_getln.c | 5443 ------- src/ex_getln.h | 66 - src/farsi.c | 2994 ---- src/farsi.h | 183 - src/file_search.c | 1565 -- src/file_search.h | 23 - src/fileio.c | 8148 ---------- src/fileio.h | 94 - src/fold.c | 3033 ---- src/fold.h | 65 - src/func_attr.h | 110 - src/garray.c | 197 - src/garray.h | 31 - src/getchar.c | 4333 ------ src/getchar.h | 77 - src/globals.h | 1140 -- src/hardcopy.c | 3175 ---- src/hardcopy.h | 93 - src/hashtab.c | 431 - src/hashtab.h | 56 - src/if_cscope.c | 2167 --- src/if_cscope.h | 16 - src/if_cscope_defs.h | 71 - src/indent.c | 696 - src/indent.h | 14 - src/indent_c.c | 3316 ---- src/indent_c.h | 12 - src/keymap.c | 777 - src/keymap.h | 515 - src/lib/khash.h | 632 - src/lib/klist.h | 128 - src/log.c | 147 - src/log.h | 57 - src/macros.h | 151 - src/main.c | 2367 --- src/main.h | 16 - src/map.c | 91 - src/map.h | 57 - src/map_defs.h | 14 - src/mark.c | 1539 -- src/mark.h | 42 - src/mark_defs.h | 29 - src/mbyte.c | 4041 ----- src/mbyte.h | 87 - src/memfile.c | 1260 -- src/memfile.h | 25 - src/memfile_defs.h | 107 - src/memline.c | 4378 ------ src/memline.h | 47 - src/memline_defs.h | 59 - src/memory.c | 388 - src/memory.h | 149 - src/menu.c | 1616 -- src/menu.h | 65 - src/message.c | 3881 ----- src/message.h | 85 - src/misc1.c | 3547 ----- src/misc1.h | 76 - src/misc2.c | 1004 -- src/misc2.h | 51 - src/move.c | 2242 --- src/move.h | 43 - src/normal.c | 7474 --------- src/normal.h | 86 - src/nvim/CMakeLists.txt | 114 + src/nvim/api/buffer.c | 413 + src/nvim/api/buffer.h | 145 + src/nvim/api/defs.h | 74 + src/nvim/api/helpers.c | 578 + src/nvim/api/helpers.h | 90 + src/nvim/api/tabpage.c | 88 + src/nvim/api/tabpage.h | 47 + src/nvim/api/vim.c | 321 + src/nvim/api/vim.h | 158 + src/nvim/api/window.c | 184 + src/nvim/api/window.h | 115 + src/nvim/arabic.c | 1532 ++ src/nvim/arabic.h | 16 + src/nvim/ascii.h | 97 + src/nvim/blowfish.c | 622 + src/nvim/blowfish.h | 13 + src/nvim/buffer.c | 4657 ++++++ src/nvim/buffer.h | 81 + src/nvim/buffer_defs.h | 1081 ++ src/nvim/charset.c | 1918 +++ src/nvim/charset.h | 66 + src/nvim/crypt.c | 236 + src/nvim/crypt.h | 82 + src/nvim/cursor_shape.c | 209 + src/nvim/cursor_shape.h | 52 + src/nvim/diff.c | 2611 ++++ src/nvim/diff.h | 33 + src/nvim/digraph.c | 1881 +++ src/nvim/digraph.h | 12 + src/nvim/edit.c | 8444 ++++++++++ src/nvim/edit.h | 58 + src/nvim/eval.c | 19819 ++++++++++++++++++++++++ src/nvim/eval.h | 152 + src/nvim/eval_defs.h | 116 + src/nvim/ex_cmds.c | 6374 ++++++++ src/nvim/ex_cmds.h | 75 + src/nvim/ex_cmds2.c | 3541 +++++ src/nvim/ex_cmds2.h | 95 + src/nvim/ex_cmds_defs.h | 1191 ++ src/nvim/ex_docmd.c | 9109 +++++++++++ src/nvim/ex_docmd.h | 69 + src/nvim/ex_eval.c | 2073 +++ src/nvim/ex_eval.h | 159 + src/nvim/ex_getln.c | 5443 +++++++ src/nvim/ex_getln.h | 66 + src/nvim/farsi.c | 2994 ++++ src/nvim/farsi.h | 183 + src/nvim/file_search.c | 1565 ++ src/nvim/file_search.h | 23 + src/nvim/fileio.c | 8148 ++++++++++ src/nvim/fileio.h | 94 + src/nvim/fold.c | 3033 ++++ src/nvim/fold.h | 65 + src/nvim/func_attr.h | 110 + src/nvim/garray.c | 197 + src/nvim/garray.h | 31 + src/nvim/getchar.c | 4333 ++++++ src/nvim/getchar.h | 77 + src/nvim/globals.h | 1140 ++ src/nvim/hardcopy.c | 3175 ++++ src/nvim/hardcopy.h | 93 + src/nvim/hashtab.c | 431 + src/nvim/hashtab.h | 56 + src/nvim/if_cscope.c | 2167 +++ src/nvim/if_cscope.h | 16 + src/nvim/if_cscope_defs.h | 71 + src/nvim/indent.c | 696 + src/nvim/indent.h | 14 + src/nvim/indent_c.c | 3316 ++++ src/nvim/indent_c.h | 12 + src/nvim/keymap.c | 777 + src/nvim/keymap.h | 515 + src/nvim/lib/khash.h | 632 + src/nvim/lib/klist.h | 128 + src/nvim/log.c | 147 + src/nvim/log.h | 57 + src/nvim/macros.h | 151 + src/nvim/main.c | 2367 +++ src/nvim/main.h | 16 + src/nvim/map.c | 91 + src/nvim/map.h | 57 + src/nvim/map_defs.h | 14 + src/nvim/mark.c | 1539 ++ src/nvim/mark.h | 42 + src/nvim/mark_defs.h | 29 + src/nvim/mbyte.c | 4041 +++++ src/nvim/mbyte.h | 87 + src/nvim/memfile.c | 1260 ++ src/nvim/memfile.h | 25 + src/nvim/memfile_defs.h | 107 + src/nvim/memline.c | 4378 ++++++ src/nvim/memline.h | 47 + src/nvim/memline_defs.h | 59 + src/nvim/memory.c | 388 + src/nvim/memory.h | 149 + src/nvim/menu.c | 1616 ++ src/nvim/menu.h | 65 + src/nvim/message.c | 3881 +++++ src/nvim/message.h | 85 + src/nvim/misc1.c | 3547 +++++ src/nvim/misc1.h | 76 + src/nvim/misc2.c | 1004 ++ src/nvim/misc2.h | 51 + src/nvim/move.c | 2242 +++ src/nvim/move.h | 43 + src/nvim/normal.c | 7474 +++++++++ src/nvim/normal.h | 86 + src/nvim/ops.c | 5174 +++++++ src/nvim/ops.h | 63 + src/nvim/option.c | 8311 ++++++++++ src/nvim/option.h | 81 + src/nvim/option_defs.h | 770 + src/nvim/os/channel.c | 175 + src/nvim/os/channel.h | 29 + src/nvim/os/channel_defs.h | 8 + src/nvim/os/env.c | 80 + src/nvim/os/event.c | 148 + src/nvim/os/event.h | 18 + src/nvim/os/event_defs.h | 25 + src/nvim/os/fs.c | 277 + src/nvim/os/input.c | 195 + src/nvim/os/input.h | 23 + src/nvim/os/job.c | 370 + src/nvim/os/job.h | 78 + src/nvim/os/job_defs.h | 15 + src/nvim/os/mem.c | 10 + src/nvim/os/msgpack_rpc.c | 408 + src/nvim/os/msgpack_rpc.h | 107 + src/nvim/os/os.h | 146 + src/nvim/os/rstream.c | 297 + src/nvim/os/rstream.h | 82 + src/nvim/os/rstream_defs.h | 16 + src/nvim/os/server.c | 243 + src/nvim/os/server.h | 30 + src/nvim/os/server_defs.h | 7 + src/nvim/os/shell.c | 465 + src/nvim/os/shell.h | 45 + src/nvim/os/signal.c | 163 + src/nvim/os/signal.h | 13 + src/nvim/os/time.c | 86 + src/nvim/os/time.h | 35 + src/nvim/os/users.c | 87 + src/nvim/os/uv_helpers.c | 70 + src/nvim/os/uv_helpers.h | 47 + src/nvim/os/wstream.c | 117 + src/nvim/os/wstream.h | 40 + src/nvim/os/wstream_defs.h | 7 + src/nvim/os_unix.c | 1664 ++ src/nvim/os_unix.h | 46 + src/nvim/os_unix_defs.h | 284 + src/nvim/path.c | 1977 +++ src/nvim/path.h | 128 + src/nvim/po/Makefile | 292 + src/nvim/po/af.po | 5393 +++++++ src/nvim/po/ca.po | 6201 ++++++++ src/nvim/po/check.vim | 88 + src/nvim/po/cleanup.vim | 19 + src/nvim/po/cs.cp1250.po | 4664 ++++++ src/nvim/po/cs.po | 4664 ++++++ src/nvim/po/de.po | 6117 ++++++++ src/nvim/po/en_GB.po | 290 + src/nvim/po/eo.po | 6825 ++++++++ src/nvim/po/es.po | 8279 ++++++++++ src/nvim/po/fi.po | 6526 ++++++++ src/nvim/po/fr.po | 7091 +++++++++ src/nvim/po/ga.po | 6503 ++++++++ src/nvim/po/it.po | 6803 ++++++++ src/nvim/po/ja.euc-jp.po | 6808 ++++++++ src/nvim/po/ja.po | 6808 ++++++++ src/nvim/po/ja.sjis.po | 6808 ++++++++ src/nvim/po/ko.UTF-8.po | 6447 ++++++++ src/nvim/po/ko.po | 6447 ++++++++ src/nvim/po/nb.po | 6166 ++++++++ src/nvim/po/nl.po | 5852 +++++++ src/nvim/po/no.po | 6166 ++++++++ src/nvim/po/pl.UTF-8.po | 6904 +++++++++ src/nvim/po/pl.cp1250.po | 6904 +++++++++ src/nvim/po/pl.po | 6904 +++++++++ src/nvim/po/pt_BR.po | 6236 ++++++++ src/nvim/po/ru.cp1251.po | 6893 ++++++++ src/nvim/po/ru.po | 6893 ++++++++ src/nvim/po/sjiscorr.c | 39 + src/nvim/po/sk.cp1250.po | 5821 +++++++ src/nvim/po/sk.po | 5821 +++++++ src/nvim/po/sv.po | 6148 ++++++++ src/nvim/po/uk.cp1251.po | 7027 +++++++++ src/nvim/po/uk.po | 7027 +++++++++ src/nvim/po/vi.po | 5196 +++++++ src/nvim/po/zh_CN.UTF-8.po | 6139 ++++++++ src/nvim/po/zh_CN.cp936.po | 6140 ++++++++ src/nvim/po/zh_CN.po | 6140 ++++++++ src/nvim/po/zh_TW.UTF-8.po | 5282 +++++++ src/nvim/po/zh_TW.po | 5275 +++++++ src/nvim/popupmnu.c | 680 + src/nvim/popupmnu.h | 19 + src/nvim/pos.h | 23 + src/nvim/proto.h | 49 + src/nvim/quickfix.c | 3688 +++++ src/nvim/quickfix.h | 36 + src/nvim/regexp.c | 7117 +++++++++ src/nvim/regexp.h | 27 + src/nvim/regexp_defs.h | 149 + src/nvim/regexp_nfa.c | 6425 ++++++++ src/nvim/screen.c | 8008 ++++++++++ src/nvim/screen.h | 68 + src/nvim/search.c | 4712 ++++++ src/nvim/search.h | 52 + src/nvim/sha256.c | 418 + src/nvim/sha256.h | 18 + src/nvim/sign_defs.h | 24 + src/nvim/spell.c | 13611 ++++++++++++++++ src/nvim/spell.h | 33 + src/nvim/strings.c | 550 + src/nvim/strings.h | 27 + src/nvim/syntax.c | 7818 ++++++++++ src/nvim/syntax.h | 58 + src/nvim/syntax_defs.h | 78 + src/nvim/tag.c | 2933 ++++ src/nvim/tag.h | 28 + src/nvim/term.c | 4732 ++++++ src/nvim/term.h | 63 + src/nvim/term_defs.h | 160 + src/nvim/testdir/Makefile | 156 + src/nvim/testdir/dotest.in | 3 + src/nvim/testdir/sautest/autoload/Test104.vim | 1 + src/nvim/testdir/sautest/autoload/footest.vim | 5 + src/nvim/testdir/test1.in | 57 + src/nvim/testdir/test1.ok | 1 + src/nvim/testdir/test10.in | 114 + src/nvim/testdir/test10.ok | 23 + src/nvim/testdir/test100.in | 42 + src/nvim/testdir/test100.ok | 41 + src/nvim/testdir/test101.in | 45 + src/nvim/testdir/test101.ok | 11 + src/nvim/testdir/test102.in | 12 + src/nvim/testdir/test102.ok | 3 + src/nvim/testdir/test103.in | 37 + src/nvim/testdir/test103.ok | 2 + src/nvim/testdir/test104.in | 30 + src/nvim/testdir/test104.ok | 13 + src/nvim/testdir/test105.in | 45 + src/nvim/testdir/test105.ok | 29 + src/nvim/testdir/test106.in | 16 + src/nvim/testdir/test106.ok | 4 + src/nvim/testdir/test10a.in | 73 + src/nvim/testdir/test10a.ok | 23 + src/nvim/testdir/test11.in | 84 + src/nvim/testdir/test11.ok | 61 + src/nvim/testdir/test12.in | 52 + src/nvim/testdir/test12.ok | 10 + src/nvim/testdir/test13.in | 58 + src/nvim/testdir/test13.ok | 30 + src/nvim/testdir/test14.in | 99 + src/nvim/testdir/test14.ok | 26 + src/nvim/testdir/test15.in | 136 + src/nvim/testdir/test15.ok | 111 + src/nvim/testdir/test16.in | 15 + src/nvim/testdir/test16.ok | 2 + src/nvim/testdir/test17.in | 141 + src/nvim/testdir/test17.ok | 33 + src/nvim/testdir/test17a.in | 3 + src/nvim/testdir/test18.in | 16 + src/nvim/testdir/test18.ok | 4 + src/nvim/testdir/test19.in | 33 + src/nvim/testdir/test19.ok | 10 + src/nvim/testdir/test2.in | 29 + src/nvim/testdir/test2.ok | 4 + src/nvim/testdir/test20.in | 28 + src/nvim/testdir/test20.ok | 10 + src/nvim/testdir/test21.in | 19 + src/nvim/testdir/test21.ok | 2 + src/nvim/testdir/test22.in | 13 + src/nvim/testdir/test22.ok | 4 + src/nvim/testdir/test23.in | 15 + src/nvim/testdir/test23.ok | 2 + src/nvim/testdir/test24.in | Bin 0 -> 1301 bytes src/nvim/testdir/test24.ok | 32 + src/nvim/testdir/test25.in | 31 + src/nvim/testdir/test25.ok | 1 + src/nvim/testdir/test26.in | 44 + src/nvim/testdir/test26.ok | 10 + src/nvim/testdir/test27.in | 20 + src/nvim/testdir/test27.ok | 2 + src/nvim/testdir/test28.in | Bin 0 -> 364 bytes src/nvim/testdir/test28.ok | 2 + src/nvim/testdir/test29.in | 230 + src/nvim/testdir/test29.ok | 97 + src/nvim/testdir/test3.in | 2056 +++ src/nvim/testdir/test3.ok | 1820 +++ src/nvim/testdir/test30.in | 222 + src/nvim/testdir/test30.ok | 121 + src/nvim/testdir/test31.in | 75 + src/nvim/testdir/test31.ok | 12 + src/nvim/testdir/test32.in | 60 + src/nvim/testdir/test32.ok | 15 + src/nvim/testdir/test33.in | 34 + src/nvim/testdir/test33.ok | 23 + src/nvim/testdir/test34.in | 87 + src/nvim/testdir/test34.ok | 10 + src/nvim/testdir/test35.in | 21 + src/nvim/testdir/test35.ok | 4 + src/nvim/testdir/test36.in | 105 + src/nvim/testdir/test36.ok | 96 + src/nvim/testdir/test37.in | 116 + src/nvim/testdir/test37.ok | 33 + src/nvim/testdir/test38.in | 35 + src/nvim/testdir/test38.ok | 13 + src/nvim/testdir/test39.in | 93 + src/nvim/testdir/test39.ok | Bin 0 -> 481 bytes src/nvim/testdir/test4.in | 31 + src/nvim/testdir/test4.ok | 17 + src/nvim/testdir/test40.in | 63 + src/nvim/testdir/test40.ok | 11 + src/nvim/testdir/test41.in | 24 + src/nvim/testdir/test41.ok | 3 + src/nvim/testdir/test42.in | Bin 0 -> 2368 bytes src/nvim/testdir/test42.ok | Bin 0 -> 409 bytes src/nvim/testdir/test43.in | 34 + src/nvim/testdir/test43.ok | 11 + src/nvim/testdir/test44.in | 68 + src/nvim/testdir/test44.ok | 24 + src/nvim/testdir/test45.in | 80 + src/nvim/testdir/test45.ok | 18 + src/nvim/testdir/test46.in | 27 + src/nvim/testdir/test46.ok | 13 + src/nvim/testdir/test47.in | 62 + src/nvim/testdir/test47.ok | 4 + src/nvim/testdir/test48.in | 78 + src/nvim/testdir/test48.ok | 22 + src/nvim/testdir/test49.in | 30 + src/nvim/testdir/test49.ok | 99 + src/nvim/testdir/test49.vim | 9852 ++++++++++++ src/nvim/testdir/test5.in | 29 + src/nvim/testdir/test5.ok | 9 + src/nvim/testdir/test50.in | 90 + src/nvim/testdir/test50.ok | 14 + src/nvim/testdir/test51.in | 36 + src/nvim/testdir/test51.ok | 20 + src/nvim/testdir/test52.in | 65 + src/nvim/testdir/test52.ok | 18 + src/nvim/testdir/test53.in | 123 + src/nvim/testdir/test53.ok | 64 + src/nvim/testdir/test54.in | 22 + src/nvim/testdir/test54.ok | 1 + src/nvim/testdir/test55.in | 404 + src/nvim/testdir/test55.ok | 128 + src/nvim/testdir/test56.in | 21 + src/nvim/testdir/test56.ok | 2 + src/nvim/testdir/test57.in | 500 + src/nvim/testdir/test57.ok | 459 + src/nvim/testdir/test58.in | 639 + src/nvim/testdir/test58.ok | 283 + src/nvim/testdir/test59.in | 626 + src/nvim/testdir/test59.ok | 270 + src/nvim/testdir/test6.in | 24 + src/nvim/testdir/test6.ok | 18 + src/nvim/testdir/test60.in | 610 + src/nvim/testdir/test60.ok | 211 + src/nvim/testdir/test60.vim | 98 + src/nvim/testdir/test61.in | 113 + src/nvim/testdir/test61.ok | 49 + src/nvim/testdir/test62.in | 191 + src/nvim/testdir/test62.ok | 88 + src/nvim/testdir/test63.in | 157 + src/nvim/testdir/test63.ok | 11 + src/nvim/testdir/test64.in | 633 + src/nvim/testdir/test64.ok | 1084 ++ src/nvim/testdir/test65.in | 95 + src/nvim/testdir/test65.ok | 73 + src/nvim/testdir/test66.in | 33 + src/nvim/testdir/test66.ok | 16 + src/nvim/testdir/test67.in | 33 + src/nvim/testdir/test67.ok | 10 + src/nvim/testdir/test68.in | 131 + src/nvim/testdir/test68.ok | 77 + src/nvim/testdir/test69.in | 185 + src/nvim/testdir/test69.ok | 162 + src/nvim/testdir/test7.in | 26 + src/nvim/testdir/test7.ok | 12 + src/nvim/testdir/test70.in | 63 + src/nvim/testdir/test70.ok | 6 + src/nvim/testdir/test71.in | 67 + src/nvim/testdir/test71.ok | 10 + src/nvim/testdir/test71a.in | 14 + src/nvim/testdir/test72.in | 115 + src/nvim/testdir/test72.ok | 27 + src/nvim/testdir/test73.in | 176 + src/nvim/testdir/test73.ok | 21 + src/nvim/testdir/test74.in | 36 + src/nvim/testdir/test74.ok | 5 + src/nvim/testdir/test75.in | 41 + src/nvim/testdir/test75.ok | 7 + src/nvim/testdir/test76.in | 46 + src/nvim/testdir/test76.ok | 4 + src/nvim/testdir/test77.in | 30 + src/nvim/testdir/test77.ok | 1 + src/nvim/testdir/test78.in | 46 + src/nvim/testdir/test78.ok | 3 + src/nvim/testdir/test79.in | Bin 0 -> 3381 bytes src/nvim/testdir/test79.ok | Bin 0 -> 574 bytes src/nvim/testdir/test8.in | 46 + src/nvim/testdir/test8.ok | 7 + src/nvim/testdir/test80.in | 201 + src/nvim/testdir/test80.ok | 131 + src/nvim/testdir/test81.in | 22 + src/nvim/testdir/test81.ok | 6 + src/nvim/testdir/test82.in | 103 + src/nvim/testdir/test82.ok | 5 + src/nvim/testdir/test83-tags2 | 2 + src/nvim/testdir/test83-tags3 | 102 + src/nvim/testdir/test83.in | 76 + src/nvim/testdir/test83.ok | 4 + src/nvim/testdir/test84.in | 35 + src/nvim/testdir/test84.ok | 3 + src/nvim/testdir/test85.in | 85 + src/nvim/testdir/test85.ok | 7 + src/nvim/testdir/test86.in | 1429 ++ src/nvim/testdir/test86.ok | 1266 ++ src/nvim/testdir/test87.in | 1406 ++ src/nvim/testdir/test87.ok | 1266 ++ src/nvim/testdir/test88.in | 88 + src/nvim/testdir/test88.ok | 24 + src/nvim/testdir/test89.in | 71 + src/nvim/testdir/test89.ok | 28 + src/nvim/testdir/test9.in | 12 + src/nvim/testdir/test9.ok | 2 + src/nvim/testdir/test90.in | 53 + src/nvim/testdir/test90.ok | 6 + src/nvim/testdir/test91.in | 111 + src/nvim/testdir/test91.ok | 48 + src/nvim/testdir/test92.in | 48 + src/nvim/testdir/test92.ok | 26 + src/nvim/testdir/test93.in | 48 + src/nvim/testdir/test93.ok | 26 + src/nvim/testdir/test94.in | 95 + src/nvim/testdir/test94.ok | 20 + src/nvim/testdir/test95.in | 135 + src/nvim/testdir/test95.ok | 122 + src/nvim/testdir/test96.in | 142 + src/nvim/testdir/test96.ok | 9 + src/nvim/testdir/test97.in | 17 + src/nvim/testdir/test97.ok | 5 + src/nvim/testdir/test98.in | 43 + src/nvim/testdir/test98.ok | 1 + src/nvim/testdir/test99.in | 68 + src/nvim/testdir/test99.ok | 24 + src/nvim/testdir/test_eval.in | 57 + src/nvim/testdir/test_eval.ok | 11 + src/nvim/testdir/test_eval_func.vim | 12 + src/nvim/testdir/unix.vim | 3 + src/nvim/types.h | 19 + src/nvim/ui.c | 963 ++ src/nvim/ui.h | 29 + src/nvim/undo.c | 2880 ++++ src/nvim/undo.h | 36 + src/nvim/undo_defs.h | 68 + src/nvim/version.c | 927 ++ src/nvim/version.h | 12 + src/nvim/version_defs.h | 47 + src/nvim/vim.h | 1391 ++ src/nvim/window.c | 5394 +++++++ src/nvim/window.h | 85 + src/ops.c | 5174 ------- src/ops.h | 63 - src/option.c | 8311 ---------- src/option.h | 81 - src/option_defs.h | 770 - src/os/channel.c | 175 - src/os/channel.h | 29 - src/os/channel_defs.h | 8 - src/os/env.c | 80 - src/os/event.c | 148 - src/os/event.h | 18 - src/os/event_defs.h | 25 - src/os/fs.c | 277 - src/os/input.c | 195 - src/os/input.h | 23 - src/os/job.c | 370 - src/os/job.h | 78 - src/os/job_defs.h | 15 - src/os/mem.c | 10 - src/os/msgpack_rpc.c | 408 - src/os/msgpack_rpc.h | 107 - src/os/os.h | 146 - src/os/rstream.c | 297 - src/os/rstream.h | 82 - src/os/rstream_defs.h | 16 - src/os/server.c | 243 - src/os/server.h | 30 - src/os/server_defs.h | 7 - src/os/shell.c | 465 - src/os/shell.h | 45 - src/os/signal.c | 163 - src/os/signal.h | 13 - src/os/time.c | 86 - src/os/time.h | 35 - src/os/users.c | 87 - src/os/uv_helpers.c | 70 - src/os/uv_helpers.h | 47 - src/os/wstream.c | 117 - src/os/wstream.h | 40 - src/os/wstream_defs.h | 7 - src/os_unix.c | 1664 -- src/os_unix.h | 46 - src/os_unix_defs.h | 284 - src/path.c | 1977 --- src/path.h | 128 - src/po/Makefile | 292 - src/po/af.po | 5393 ------- src/po/ca.po | 6201 -------- src/po/check.vim | 88 - src/po/cleanup.vim | 19 - src/po/cs.cp1250.po | 4664 ------ src/po/cs.po | 4664 ------ src/po/de.po | 6117 -------- src/po/en_GB.po | 290 - src/po/eo.po | 6825 -------- src/po/es.po | 8279 ---------- src/po/fi.po | 6526 -------- src/po/fr.po | 7091 --------- src/po/ga.po | 6503 -------- src/po/it.po | 6803 -------- src/po/ja.euc-jp.po | 6808 -------- src/po/ja.po | 6808 -------- src/po/ja.sjis.po | 6808 -------- src/po/ko.UTF-8.po | 6447 -------- src/po/ko.po | 6447 -------- src/po/nb.po | 6166 -------- src/po/nl.po | 5852 ------- src/po/no.po | 6166 -------- src/po/pl.UTF-8.po | 6904 --------- src/po/pl.cp1250.po | 6904 --------- src/po/pl.po | 6904 --------- src/po/pt_BR.po | 6236 -------- src/po/ru.cp1251.po | 6893 -------- src/po/ru.po | 6893 -------- src/po/sjiscorr.c | 39 - src/po/sk.cp1250.po | 5821 ------- src/po/sk.po | 5821 ------- src/po/sv.po | 6148 -------- src/po/uk.cp1251.po | 7027 --------- src/po/uk.po | 7027 --------- src/po/vi.po | 5196 ------- src/po/zh_CN.UTF-8.po | 6139 -------- src/po/zh_CN.cp936.po | 6140 -------- src/po/zh_CN.po | 6140 -------- src/po/zh_TW.UTF-8.po | 5282 ------- src/po/zh_TW.po | 5275 ------- src/popupmnu.c | 680 - src/popupmnu.h | 19 - src/pos.h | 23 - src/proto.h | 49 - src/quickfix.c | 3688 ----- src/quickfix.h | 36 - src/regexp.c | 7117 --------- src/regexp.h | 27 - src/regexp_defs.h | 149 - src/regexp_nfa.c | 6425 -------- src/screen.c | 8008 ---------- src/screen.h | 68 - src/search.c | 4712 ------ src/search.h | 52 - src/sha256.c | 418 - src/sha256.h | 18 - src/sign_defs.h | 24 - src/spell.c | 13611 ---------------- src/spell.h | 33 - src/strings.c | 550 - src/strings.h | 27 - src/syntax.c | 7818 ---------- src/syntax.h | 58 - src/syntax_defs.h | 78 - src/tag.c | 2933 ---- src/tag.h | 28 - src/term.c | 4732 ------ src/term.h | 63 - src/term_defs.h | 160 - src/testdir/Makefile | 156 - src/testdir/dotest.in | 3 - src/testdir/sautest/autoload/Test104.vim | 1 - src/testdir/sautest/autoload/footest.vim | 5 - src/testdir/test1.in | 57 - src/testdir/test1.ok | 1 - src/testdir/test10.in | 114 - src/testdir/test10.ok | 23 - src/testdir/test100.in | 42 - src/testdir/test100.ok | 41 - src/testdir/test101.in | 45 - src/testdir/test101.ok | 11 - src/testdir/test102.in | 12 - src/testdir/test102.ok | 3 - src/testdir/test103.in | 37 - src/testdir/test103.ok | 2 - src/testdir/test104.in | 30 - src/testdir/test104.ok | 13 - src/testdir/test105.in | 45 - src/testdir/test105.ok | 29 - src/testdir/test106.in | 16 - src/testdir/test106.ok | 4 - src/testdir/test10a.in | 73 - src/testdir/test10a.ok | 23 - src/testdir/test11.in | 84 - src/testdir/test11.ok | 61 - src/testdir/test12.in | 52 - src/testdir/test12.ok | 10 - src/testdir/test13.in | 58 - src/testdir/test13.ok | 30 - src/testdir/test14.in | 99 - src/testdir/test14.ok | 26 - src/testdir/test15.in | 136 - src/testdir/test15.ok | 111 - src/testdir/test16.in | 15 - src/testdir/test16.ok | 2 - src/testdir/test17.in | 141 - src/testdir/test17.ok | 33 - src/testdir/test17a.in | 3 - src/testdir/test18.in | 16 - src/testdir/test18.ok | 4 - src/testdir/test19.in | 33 - src/testdir/test19.ok | 10 - src/testdir/test2.in | 29 - src/testdir/test2.ok | 4 - src/testdir/test20.in | 28 - src/testdir/test20.ok | 10 - src/testdir/test21.in | 19 - src/testdir/test21.ok | 2 - src/testdir/test22.in | 13 - src/testdir/test22.ok | 4 - src/testdir/test23.in | 15 - src/testdir/test23.ok | 2 - src/testdir/test24.in | Bin 1301 -> 0 bytes src/testdir/test24.ok | 32 - src/testdir/test25.in | 31 - src/testdir/test25.ok | 1 - src/testdir/test26.in | 44 - src/testdir/test26.ok | 10 - src/testdir/test27.in | 20 - src/testdir/test27.ok | 2 - src/testdir/test28.in | Bin 364 -> 0 bytes src/testdir/test28.ok | 2 - src/testdir/test29.in | 230 - src/testdir/test29.ok | 97 - src/testdir/test3.in | 2056 --- src/testdir/test3.ok | 1820 --- src/testdir/test30.in | 222 - src/testdir/test30.ok | 121 - src/testdir/test31.in | 75 - src/testdir/test31.ok | 12 - src/testdir/test32.in | 60 - src/testdir/test32.ok | 15 - src/testdir/test33.in | 34 - src/testdir/test33.ok | 23 - src/testdir/test34.in | 87 - src/testdir/test34.ok | 10 - src/testdir/test35.in | 21 - src/testdir/test35.ok | 4 - src/testdir/test36.in | 105 - src/testdir/test36.ok | 96 - src/testdir/test37.in | 116 - src/testdir/test37.ok | 33 - src/testdir/test38.in | 35 - src/testdir/test38.ok | 13 - src/testdir/test39.in | 93 - src/testdir/test39.ok | Bin 481 -> 0 bytes src/testdir/test4.in | 31 - src/testdir/test4.ok | 17 - src/testdir/test40.in | 63 - src/testdir/test40.ok | 11 - src/testdir/test41.in | 24 - src/testdir/test41.ok | 3 - src/testdir/test42.in | Bin 2368 -> 0 bytes src/testdir/test42.ok | Bin 409 -> 0 bytes src/testdir/test43.in | 34 - src/testdir/test43.ok | 11 - src/testdir/test44.in | 68 - src/testdir/test44.ok | 24 - src/testdir/test45.in | 80 - src/testdir/test45.ok | 18 - src/testdir/test46.in | 27 - src/testdir/test46.ok | 13 - src/testdir/test47.in | 62 - src/testdir/test47.ok | 4 - src/testdir/test48.in | 78 - src/testdir/test48.ok | 22 - src/testdir/test49.in | 30 - src/testdir/test49.ok | 99 - src/testdir/test49.vim | 9852 ------------ src/testdir/test5.in | 29 - src/testdir/test5.ok | 9 - src/testdir/test50.in | 90 - src/testdir/test50.ok | 14 - src/testdir/test51.in | 36 - src/testdir/test51.ok | 20 - src/testdir/test52.in | 65 - src/testdir/test52.ok | 18 - src/testdir/test53.in | 123 - src/testdir/test53.ok | 64 - src/testdir/test54.in | 22 - src/testdir/test54.ok | 1 - src/testdir/test55.in | 404 - src/testdir/test55.ok | 128 - src/testdir/test56.in | 21 - src/testdir/test56.ok | 2 - src/testdir/test57.in | 500 - src/testdir/test57.ok | 459 - src/testdir/test58.in | 639 - src/testdir/test58.ok | 283 - src/testdir/test59.in | 626 - src/testdir/test59.ok | 270 - src/testdir/test6.in | 24 - src/testdir/test6.ok | 18 - src/testdir/test60.in | 610 - src/testdir/test60.ok | 211 - src/testdir/test60.vim | 98 - src/testdir/test61.in | 113 - src/testdir/test61.ok | 49 - src/testdir/test62.in | 191 - src/testdir/test62.ok | 88 - src/testdir/test63.in | 157 - src/testdir/test63.ok | 11 - src/testdir/test64.in | 633 - src/testdir/test64.ok | 1084 -- src/testdir/test65.in | 95 - src/testdir/test65.ok | 73 - src/testdir/test66.in | 33 - src/testdir/test66.ok | 16 - src/testdir/test67.in | 33 - src/testdir/test67.ok | 10 - src/testdir/test68.in | 131 - src/testdir/test68.ok | 77 - src/testdir/test69.in | 185 - src/testdir/test69.ok | 162 - src/testdir/test7.in | 26 - src/testdir/test7.ok | 12 - src/testdir/test70.in | 63 - src/testdir/test70.ok | 6 - src/testdir/test71.in | 67 - src/testdir/test71.ok | 10 - src/testdir/test71a.in | 14 - src/testdir/test72.in | 115 - src/testdir/test72.ok | 27 - src/testdir/test73.in | 176 - src/testdir/test73.ok | 21 - src/testdir/test74.in | 36 - src/testdir/test74.ok | 5 - src/testdir/test75.in | 41 - src/testdir/test75.ok | 7 - src/testdir/test76.in | 46 - src/testdir/test76.ok | 4 - src/testdir/test77.in | 30 - src/testdir/test77.ok | 1 - src/testdir/test78.in | 46 - src/testdir/test78.ok | 3 - src/testdir/test79.in | Bin 3381 -> 0 bytes src/testdir/test79.ok | Bin 574 -> 0 bytes src/testdir/test8.in | 46 - src/testdir/test8.ok | 7 - src/testdir/test80.in | 201 - src/testdir/test80.ok | 131 - src/testdir/test81.in | 22 - src/testdir/test81.ok | 6 - src/testdir/test82.in | 103 - src/testdir/test82.ok | 5 - src/testdir/test83-tags2 | 2 - src/testdir/test83-tags3 | 102 - src/testdir/test83.in | 76 - src/testdir/test83.ok | 4 - src/testdir/test84.in | 35 - src/testdir/test84.ok | 3 - src/testdir/test85.in | 85 - src/testdir/test85.ok | 7 - src/testdir/test86.in | 1429 -- src/testdir/test86.ok | 1266 -- src/testdir/test87.in | 1406 -- src/testdir/test87.ok | 1266 -- src/testdir/test88.in | 88 - src/testdir/test88.ok | 24 - src/testdir/test89.in | 71 - src/testdir/test89.ok | 28 - src/testdir/test9.in | 12 - src/testdir/test9.ok | 2 - src/testdir/test90.in | 53 - src/testdir/test90.ok | 6 - src/testdir/test91.in | 111 - src/testdir/test91.ok | 48 - src/testdir/test92.in | 48 - src/testdir/test92.ok | 26 - src/testdir/test93.in | 48 - src/testdir/test93.ok | 26 - src/testdir/test94.in | 95 - src/testdir/test94.ok | 20 - src/testdir/test95.in | 135 - src/testdir/test95.ok | 122 - src/testdir/test96.in | 142 - src/testdir/test96.ok | 9 - src/testdir/test97.in | 17 - src/testdir/test97.ok | 5 - src/testdir/test98.in | 43 - src/testdir/test98.ok | 1 - src/testdir/test99.in | 68 - src/testdir/test99.ok | 24 - src/testdir/test_eval.in | 57 - src/testdir/test_eval.ok | 11 - src/testdir/test_eval_func.vim | 12 - src/testdir/unix.vim | 3 - src/types.h | 19 - src/ui.c | 963 -- src/ui.h | 29 - src/undo.c | 2880 ---- src/undo.h | 36 - src/undo_defs.h | 68 - src/version.c | 927 -- src/version.h | 12 - src/version_defs.h | 47 - src/vim.h | 1391 -- src/window.c | 5394 ------- src/window.h | 85 - 926 files changed, 501749 insertions(+), 501749 deletions(-) delete mode 100644 src/CMakeLists.txt delete mode 100644 src/api/buffer.c delete mode 100644 src/api/buffer.h delete mode 100644 src/api/defs.h delete mode 100644 src/api/helpers.c delete mode 100644 src/api/helpers.h delete mode 100644 src/api/tabpage.c delete mode 100644 src/api/tabpage.h delete mode 100644 src/api/vim.c delete mode 100644 src/api/vim.h delete mode 100644 src/api/window.c delete mode 100644 src/api/window.h delete mode 100644 src/arabic.c delete mode 100644 src/arabic.h delete mode 100644 src/ascii.h delete mode 100644 src/blowfish.c delete mode 100644 src/blowfish.h delete mode 100644 src/buffer.c delete mode 100644 src/buffer.h delete mode 100644 src/buffer_defs.h delete mode 100644 src/charset.c delete mode 100644 src/charset.h delete mode 100644 src/crypt.c delete mode 100644 src/crypt.h delete mode 100644 src/cursor_shape.c delete mode 100644 src/cursor_shape.h delete mode 100644 src/diff.c delete mode 100644 src/diff.h delete mode 100644 src/digraph.c delete mode 100644 src/digraph.h delete mode 100644 src/edit.c delete mode 100644 src/edit.h delete mode 100644 src/eval.c delete mode 100644 src/eval.h delete mode 100644 src/eval_defs.h delete mode 100644 src/ex_cmds.c delete mode 100644 src/ex_cmds.h delete mode 100644 src/ex_cmds2.c delete mode 100644 src/ex_cmds2.h delete mode 100644 src/ex_cmds_defs.h delete mode 100644 src/ex_docmd.c delete mode 100644 src/ex_docmd.h delete mode 100644 src/ex_eval.c delete mode 100644 src/ex_eval.h delete mode 100644 src/ex_getln.c delete mode 100644 src/ex_getln.h delete mode 100644 src/farsi.c delete mode 100644 src/farsi.h delete mode 100644 src/file_search.c delete mode 100644 src/file_search.h delete mode 100644 src/fileio.c delete mode 100644 src/fileio.h delete mode 100644 src/fold.c delete mode 100644 src/fold.h delete mode 100644 src/func_attr.h delete mode 100644 src/garray.c delete mode 100644 src/garray.h delete mode 100644 src/getchar.c delete mode 100644 src/getchar.h delete mode 100644 src/globals.h delete mode 100644 src/hardcopy.c delete mode 100644 src/hardcopy.h delete mode 100644 src/hashtab.c delete mode 100644 src/hashtab.h delete mode 100644 src/if_cscope.c delete mode 100644 src/if_cscope.h delete mode 100644 src/if_cscope_defs.h delete mode 100644 src/indent.c delete mode 100644 src/indent.h delete mode 100644 src/indent_c.c delete mode 100644 src/indent_c.h delete mode 100644 src/keymap.c delete mode 100644 src/keymap.h delete mode 100644 src/lib/khash.h delete mode 100644 src/lib/klist.h delete mode 100644 src/log.c delete mode 100644 src/log.h delete mode 100644 src/macros.h delete mode 100644 src/main.c delete mode 100644 src/main.h delete mode 100644 src/map.c delete mode 100644 src/map.h delete mode 100644 src/map_defs.h delete mode 100644 src/mark.c delete mode 100644 src/mark.h delete mode 100644 src/mark_defs.h delete mode 100644 src/mbyte.c delete mode 100644 src/mbyte.h delete mode 100644 src/memfile.c delete mode 100644 src/memfile.h delete mode 100644 src/memfile_defs.h delete mode 100644 src/memline.c delete mode 100644 src/memline.h delete mode 100644 src/memline_defs.h delete mode 100644 src/memory.c delete mode 100644 src/memory.h delete mode 100644 src/menu.c delete mode 100644 src/menu.h delete mode 100644 src/message.c delete mode 100644 src/message.h delete mode 100644 src/misc1.c delete mode 100644 src/misc1.h delete mode 100644 src/misc2.c delete mode 100644 src/misc2.h delete mode 100644 src/move.c delete mode 100644 src/move.h delete mode 100644 src/normal.c delete mode 100644 src/normal.h create mode 100644 src/nvim/CMakeLists.txt create mode 100644 src/nvim/api/buffer.c create mode 100644 src/nvim/api/buffer.h create mode 100644 src/nvim/api/defs.h create mode 100644 src/nvim/api/helpers.c create mode 100644 src/nvim/api/helpers.h create mode 100644 src/nvim/api/tabpage.c create mode 100644 src/nvim/api/tabpage.h create mode 100644 src/nvim/api/vim.c create mode 100644 src/nvim/api/vim.h create mode 100644 src/nvim/api/window.c create mode 100644 src/nvim/api/window.h create mode 100644 src/nvim/arabic.c create mode 100644 src/nvim/arabic.h create mode 100644 src/nvim/ascii.h create mode 100644 src/nvim/blowfish.c create mode 100644 src/nvim/blowfish.h create mode 100644 src/nvim/buffer.c create mode 100644 src/nvim/buffer.h create mode 100644 src/nvim/buffer_defs.h create mode 100644 src/nvim/charset.c create mode 100644 src/nvim/charset.h create mode 100644 src/nvim/crypt.c create mode 100644 src/nvim/crypt.h create mode 100644 src/nvim/cursor_shape.c create mode 100644 src/nvim/cursor_shape.h create mode 100644 src/nvim/diff.c create mode 100644 src/nvim/diff.h create mode 100644 src/nvim/digraph.c create mode 100644 src/nvim/digraph.h create mode 100644 src/nvim/edit.c create mode 100644 src/nvim/edit.h create mode 100644 src/nvim/eval.c create mode 100644 src/nvim/eval.h create mode 100644 src/nvim/eval_defs.h create mode 100644 src/nvim/ex_cmds.c create mode 100644 src/nvim/ex_cmds.h create mode 100644 src/nvim/ex_cmds2.c create mode 100644 src/nvim/ex_cmds2.h create mode 100644 src/nvim/ex_cmds_defs.h create mode 100644 src/nvim/ex_docmd.c create mode 100644 src/nvim/ex_docmd.h create mode 100644 src/nvim/ex_eval.c create mode 100644 src/nvim/ex_eval.h create mode 100644 src/nvim/ex_getln.c create mode 100644 src/nvim/ex_getln.h create mode 100644 src/nvim/farsi.c create mode 100644 src/nvim/farsi.h create mode 100644 src/nvim/file_search.c create mode 100644 src/nvim/file_search.h create mode 100644 src/nvim/fileio.c create mode 100644 src/nvim/fileio.h create mode 100644 src/nvim/fold.c create mode 100644 src/nvim/fold.h create mode 100644 src/nvim/func_attr.h create mode 100644 src/nvim/garray.c create mode 100644 src/nvim/garray.h create mode 100644 src/nvim/getchar.c create mode 100644 src/nvim/getchar.h create mode 100644 src/nvim/globals.h create mode 100644 src/nvim/hardcopy.c create mode 100644 src/nvim/hardcopy.h create mode 100644 src/nvim/hashtab.c create mode 100644 src/nvim/hashtab.h create mode 100644 src/nvim/if_cscope.c create mode 100644 src/nvim/if_cscope.h create mode 100644 src/nvim/if_cscope_defs.h create mode 100644 src/nvim/indent.c create mode 100644 src/nvim/indent.h create mode 100644 src/nvim/indent_c.c create mode 100644 src/nvim/indent_c.h create mode 100644 src/nvim/keymap.c create mode 100644 src/nvim/keymap.h create mode 100644 src/nvim/lib/khash.h create mode 100644 src/nvim/lib/klist.h create mode 100644 src/nvim/log.c create mode 100644 src/nvim/log.h create mode 100644 src/nvim/macros.h create mode 100644 src/nvim/main.c create mode 100644 src/nvim/main.h create mode 100644 src/nvim/map.c create mode 100644 src/nvim/map.h create mode 100644 src/nvim/map_defs.h create mode 100644 src/nvim/mark.c create mode 100644 src/nvim/mark.h create mode 100644 src/nvim/mark_defs.h create mode 100644 src/nvim/mbyte.c create mode 100644 src/nvim/mbyte.h create mode 100644 src/nvim/memfile.c create mode 100644 src/nvim/memfile.h create mode 100644 src/nvim/memfile_defs.h create mode 100644 src/nvim/memline.c create mode 100644 src/nvim/memline.h create mode 100644 src/nvim/memline_defs.h create mode 100644 src/nvim/memory.c create mode 100644 src/nvim/memory.h create mode 100644 src/nvim/menu.c create mode 100644 src/nvim/menu.h create mode 100644 src/nvim/message.c create mode 100644 src/nvim/message.h create mode 100644 src/nvim/misc1.c create mode 100644 src/nvim/misc1.h create mode 100644 src/nvim/misc2.c create mode 100644 src/nvim/misc2.h create mode 100644 src/nvim/move.c create mode 100644 src/nvim/move.h create mode 100644 src/nvim/normal.c create mode 100644 src/nvim/normal.h create mode 100644 src/nvim/ops.c create mode 100644 src/nvim/ops.h create mode 100644 src/nvim/option.c create mode 100644 src/nvim/option.h create mode 100644 src/nvim/option_defs.h create mode 100644 src/nvim/os/channel.c create mode 100644 src/nvim/os/channel.h create mode 100644 src/nvim/os/channel_defs.h create mode 100644 src/nvim/os/env.c create mode 100644 src/nvim/os/event.c create mode 100644 src/nvim/os/event.h create mode 100644 src/nvim/os/event_defs.h create mode 100644 src/nvim/os/fs.c create mode 100644 src/nvim/os/input.c create mode 100644 src/nvim/os/input.h create mode 100644 src/nvim/os/job.c create mode 100644 src/nvim/os/job.h create mode 100644 src/nvim/os/job_defs.h create mode 100644 src/nvim/os/mem.c create mode 100644 src/nvim/os/msgpack_rpc.c create mode 100644 src/nvim/os/msgpack_rpc.h create mode 100644 src/nvim/os/os.h create mode 100644 src/nvim/os/rstream.c create mode 100644 src/nvim/os/rstream.h create mode 100644 src/nvim/os/rstream_defs.h create mode 100644 src/nvim/os/server.c create mode 100644 src/nvim/os/server.h create mode 100644 src/nvim/os/server_defs.h create mode 100644 src/nvim/os/shell.c create mode 100644 src/nvim/os/shell.h create mode 100644 src/nvim/os/signal.c create mode 100644 src/nvim/os/signal.h create mode 100644 src/nvim/os/time.c create mode 100644 src/nvim/os/time.h create mode 100644 src/nvim/os/users.c create mode 100644 src/nvim/os/uv_helpers.c create mode 100644 src/nvim/os/uv_helpers.h create mode 100644 src/nvim/os/wstream.c create mode 100644 src/nvim/os/wstream.h create mode 100644 src/nvim/os/wstream_defs.h create mode 100644 src/nvim/os_unix.c create mode 100644 src/nvim/os_unix.h create mode 100644 src/nvim/os_unix_defs.h create mode 100644 src/nvim/path.c create mode 100644 src/nvim/path.h create mode 100644 src/nvim/po/Makefile create mode 100644 src/nvim/po/af.po create mode 100644 src/nvim/po/ca.po create mode 100644 src/nvim/po/check.vim create mode 100644 src/nvim/po/cleanup.vim create mode 100644 src/nvim/po/cs.cp1250.po create mode 100644 src/nvim/po/cs.po create mode 100644 src/nvim/po/de.po create mode 100644 src/nvim/po/en_GB.po create mode 100644 src/nvim/po/eo.po create mode 100644 src/nvim/po/es.po create mode 100644 src/nvim/po/fi.po create mode 100644 src/nvim/po/fr.po create mode 100644 src/nvim/po/ga.po create mode 100644 src/nvim/po/it.po create mode 100644 src/nvim/po/ja.euc-jp.po create mode 100644 src/nvim/po/ja.po create mode 100644 src/nvim/po/ja.sjis.po create mode 100644 src/nvim/po/ko.UTF-8.po create mode 100644 src/nvim/po/ko.po create mode 100644 src/nvim/po/nb.po create mode 100644 src/nvim/po/nl.po create mode 100644 src/nvim/po/no.po create mode 100644 src/nvim/po/pl.UTF-8.po create mode 100644 src/nvim/po/pl.cp1250.po create mode 100644 src/nvim/po/pl.po create mode 100644 src/nvim/po/pt_BR.po create mode 100644 src/nvim/po/ru.cp1251.po create mode 100644 src/nvim/po/ru.po create mode 100644 src/nvim/po/sjiscorr.c create mode 100644 src/nvim/po/sk.cp1250.po create mode 100644 src/nvim/po/sk.po create mode 100644 src/nvim/po/sv.po create mode 100644 src/nvim/po/uk.cp1251.po create mode 100644 src/nvim/po/uk.po create mode 100644 src/nvim/po/vi.po create mode 100644 src/nvim/po/zh_CN.UTF-8.po create mode 100644 src/nvim/po/zh_CN.cp936.po create mode 100644 src/nvim/po/zh_CN.po create mode 100644 src/nvim/po/zh_TW.UTF-8.po create mode 100644 src/nvim/po/zh_TW.po create mode 100644 src/nvim/popupmnu.c create mode 100644 src/nvim/popupmnu.h create mode 100644 src/nvim/pos.h create mode 100644 src/nvim/proto.h create mode 100644 src/nvim/quickfix.c create mode 100644 src/nvim/quickfix.h create mode 100644 src/nvim/regexp.c create mode 100644 src/nvim/regexp.h create mode 100644 src/nvim/regexp_defs.h create mode 100644 src/nvim/regexp_nfa.c create mode 100644 src/nvim/screen.c create mode 100644 src/nvim/screen.h create mode 100644 src/nvim/search.c create mode 100644 src/nvim/search.h create mode 100644 src/nvim/sha256.c create mode 100644 src/nvim/sha256.h create mode 100644 src/nvim/sign_defs.h create mode 100644 src/nvim/spell.c create mode 100644 src/nvim/spell.h create mode 100644 src/nvim/strings.c create mode 100644 src/nvim/strings.h create mode 100644 src/nvim/syntax.c create mode 100644 src/nvim/syntax.h create mode 100644 src/nvim/syntax_defs.h create mode 100644 src/nvim/tag.c create mode 100644 src/nvim/tag.h create mode 100644 src/nvim/term.c create mode 100644 src/nvim/term.h create mode 100644 src/nvim/term_defs.h create mode 100644 src/nvim/testdir/Makefile create mode 100644 src/nvim/testdir/dotest.in create mode 100644 src/nvim/testdir/sautest/autoload/Test104.vim create mode 100644 src/nvim/testdir/sautest/autoload/footest.vim create mode 100644 src/nvim/testdir/test1.in create mode 100644 src/nvim/testdir/test1.ok create mode 100644 src/nvim/testdir/test10.in create mode 100644 src/nvim/testdir/test10.ok create mode 100644 src/nvim/testdir/test100.in create mode 100644 src/nvim/testdir/test100.ok create mode 100644 src/nvim/testdir/test101.in create mode 100644 src/nvim/testdir/test101.ok create mode 100644 src/nvim/testdir/test102.in create mode 100644 src/nvim/testdir/test102.ok create mode 100644 src/nvim/testdir/test103.in create mode 100644 src/nvim/testdir/test103.ok create mode 100644 src/nvim/testdir/test104.in create mode 100644 src/nvim/testdir/test104.ok create mode 100644 src/nvim/testdir/test105.in create mode 100644 src/nvim/testdir/test105.ok create mode 100644 src/nvim/testdir/test106.in create mode 100644 src/nvim/testdir/test106.ok create mode 100644 src/nvim/testdir/test10a.in create mode 100644 src/nvim/testdir/test10a.ok create mode 100644 src/nvim/testdir/test11.in create mode 100644 src/nvim/testdir/test11.ok create mode 100644 src/nvim/testdir/test12.in create mode 100644 src/nvim/testdir/test12.ok create mode 100644 src/nvim/testdir/test13.in create mode 100644 src/nvim/testdir/test13.ok create mode 100644 src/nvim/testdir/test14.in create mode 100644 src/nvim/testdir/test14.ok create mode 100644 src/nvim/testdir/test15.in create mode 100644 src/nvim/testdir/test15.ok create mode 100644 src/nvim/testdir/test16.in create mode 100644 src/nvim/testdir/test16.ok create mode 100644 src/nvim/testdir/test17.in create mode 100644 src/nvim/testdir/test17.ok create mode 100644 src/nvim/testdir/test17a.in create mode 100644 src/nvim/testdir/test18.in create mode 100644 src/nvim/testdir/test18.ok create mode 100644 src/nvim/testdir/test19.in create mode 100644 src/nvim/testdir/test19.ok create mode 100644 src/nvim/testdir/test2.in create mode 100644 src/nvim/testdir/test2.ok create mode 100644 src/nvim/testdir/test20.in create mode 100644 src/nvim/testdir/test20.ok create mode 100644 src/nvim/testdir/test21.in create mode 100644 src/nvim/testdir/test21.ok create mode 100644 src/nvim/testdir/test22.in create mode 100644 src/nvim/testdir/test22.ok create mode 100644 src/nvim/testdir/test23.in create mode 100644 src/nvim/testdir/test23.ok create mode 100644 src/nvim/testdir/test24.in create mode 100644 src/nvim/testdir/test24.ok create mode 100644 src/nvim/testdir/test25.in create mode 100644 src/nvim/testdir/test25.ok create mode 100644 src/nvim/testdir/test26.in create mode 100644 src/nvim/testdir/test26.ok create mode 100644 src/nvim/testdir/test27.in create mode 100644 src/nvim/testdir/test27.ok create mode 100644 src/nvim/testdir/test28.in create mode 100644 src/nvim/testdir/test28.ok create mode 100644 src/nvim/testdir/test29.in create mode 100644 src/nvim/testdir/test29.ok create mode 100644 src/nvim/testdir/test3.in create mode 100644 src/nvim/testdir/test3.ok create mode 100644 src/nvim/testdir/test30.in create mode 100644 src/nvim/testdir/test30.ok create mode 100644 src/nvim/testdir/test31.in create mode 100644 src/nvim/testdir/test31.ok create mode 100644 src/nvim/testdir/test32.in create mode 100644 src/nvim/testdir/test32.ok create mode 100644 src/nvim/testdir/test33.in create mode 100644 src/nvim/testdir/test33.ok create mode 100644 src/nvim/testdir/test34.in create mode 100644 src/nvim/testdir/test34.ok create mode 100644 src/nvim/testdir/test35.in create mode 100644 src/nvim/testdir/test35.ok create mode 100644 src/nvim/testdir/test36.in create mode 100644 src/nvim/testdir/test36.ok create mode 100644 src/nvim/testdir/test37.in create mode 100644 src/nvim/testdir/test37.ok create mode 100644 src/nvim/testdir/test38.in create mode 100644 src/nvim/testdir/test38.ok create mode 100644 src/nvim/testdir/test39.in create mode 100644 src/nvim/testdir/test39.ok create mode 100644 src/nvim/testdir/test4.in create mode 100644 src/nvim/testdir/test4.ok create mode 100644 src/nvim/testdir/test40.in create mode 100644 src/nvim/testdir/test40.ok create mode 100644 src/nvim/testdir/test41.in create mode 100644 src/nvim/testdir/test41.ok create mode 100644 src/nvim/testdir/test42.in create mode 100644 src/nvim/testdir/test42.ok create mode 100644 src/nvim/testdir/test43.in create mode 100644 src/nvim/testdir/test43.ok create mode 100644 src/nvim/testdir/test44.in create mode 100644 src/nvim/testdir/test44.ok create mode 100644 src/nvim/testdir/test45.in create mode 100644 src/nvim/testdir/test45.ok create mode 100644 src/nvim/testdir/test46.in create mode 100644 src/nvim/testdir/test46.ok create mode 100644 src/nvim/testdir/test47.in create mode 100644 src/nvim/testdir/test47.ok create mode 100644 src/nvim/testdir/test48.in create mode 100644 src/nvim/testdir/test48.ok create mode 100644 src/nvim/testdir/test49.in create mode 100644 src/nvim/testdir/test49.ok create mode 100644 src/nvim/testdir/test49.vim create mode 100644 src/nvim/testdir/test5.in create mode 100644 src/nvim/testdir/test5.ok create mode 100644 src/nvim/testdir/test50.in create mode 100644 src/nvim/testdir/test50.ok create mode 100644 src/nvim/testdir/test51.in create mode 100644 src/nvim/testdir/test51.ok create mode 100644 src/nvim/testdir/test52.in create mode 100644 src/nvim/testdir/test52.ok create mode 100644 src/nvim/testdir/test53.in create mode 100644 src/nvim/testdir/test53.ok create mode 100644 src/nvim/testdir/test54.in create mode 100644 src/nvim/testdir/test54.ok create mode 100644 src/nvim/testdir/test55.in create mode 100644 src/nvim/testdir/test55.ok create mode 100644 src/nvim/testdir/test56.in create mode 100644 src/nvim/testdir/test56.ok create mode 100644 src/nvim/testdir/test57.in create mode 100644 src/nvim/testdir/test57.ok create mode 100644 src/nvim/testdir/test58.in create mode 100644 src/nvim/testdir/test58.ok create mode 100644 src/nvim/testdir/test59.in create mode 100644 src/nvim/testdir/test59.ok create mode 100644 src/nvim/testdir/test6.in create mode 100644 src/nvim/testdir/test6.ok create mode 100644 src/nvim/testdir/test60.in create mode 100644 src/nvim/testdir/test60.ok create mode 100644 src/nvim/testdir/test60.vim create mode 100644 src/nvim/testdir/test61.in create mode 100644 src/nvim/testdir/test61.ok create mode 100644 src/nvim/testdir/test62.in create mode 100644 src/nvim/testdir/test62.ok create mode 100644 src/nvim/testdir/test63.in create mode 100644 src/nvim/testdir/test63.ok create mode 100644 src/nvim/testdir/test64.in create mode 100644 src/nvim/testdir/test64.ok create mode 100644 src/nvim/testdir/test65.in create mode 100644 src/nvim/testdir/test65.ok create mode 100644 src/nvim/testdir/test66.in create mode 100644 src/nvim/testdir/test66.ok create mode 100644 src/nvim/testdir/test67.in create mode 100644 src/nvim/testdir/test67.ok create mode 100644 src/nvim/testdir/test68.in create mode 100644 src/nvim/testdir/test68.ok create mode 100644 src/nvim/testdir/test69.in create mode 100644 src/nvim/testdir/test69.ok create mode 100644 src/nvim/testdir/test7.in create mode 100644 src/nvim/testdir/test7.ok create mode 100644 src/nvim/testdir/test70.in create mode 100644 src/nvim/testdir/test70.ok create mode 100644 src/nvim/testdir/test71.in create mode 100644 src/nvim/testdir/test71.ok create mode 100644 src/nvim/testdir/test71a.in create mode 100644 src/nvim/testdir/test72.in create mode 100644 src/nvim/testdir/test72.ok create mode 100644 src/nvim/testdir/test73.in create mode 100644 src/nvim/testdir/test73.ok create mode 100644 src/nvim/testdir/test74.in create mode 100644 src/nvim/testdir/test74.ok create mode 100644 src/nvim/testdir/test75.in create mode 100644 src/nvim/testdir/test75.ok create mode 100644 src/nvim/testdir/test76.in create mode 100644 src/nvim/testdir/test76.ok create mode 100644 src/nvim/testdir/test77.in create mode 100644 src/nvim/testdir/test77.ok create mode 100644 src/nvim/testdir/test78.in create mode 100644 src/nvim/testdir/test78.ok create mode 100644 src/nvim/testdir/test79.in create mode 100644 src/nvim/testdir/test79.ok create mode 100644 src/nvim/testdir/test8.in create mode 100644 src/nvim/testdir/test8.ok create mode 100644 src/nvim/testdir/test80.in create mode 100644 src/nvim/testdir/test80.ok create mode 100644 src/nvim/testdir/test81.in create mode 100644 src/nvim/testdir/test81.ok create mode 100644 src/nvim/testdir/test82.in create mode 100644 src/nvim/testdir/test82.ok create mode 100644 src/nvim/testdir/test83-tags2 create mode 100644 src/nvim/testdir/test83-tags3 create mode 100644 src/nvim/testdir/test83.in create mode 100644 src/nvim/testdir/test83.ok create mode 100644 src/nvim/testdir/test84.in create mode 100644 src/nvim/testdir/test84.ok create mode 100644 src/nvim/testdir/test85.in create mode 100644 src/nvim/testdir/test85.ok create mode 100644 src/nvim/testdir/test86.in create mode 100644 src/nvim/testdir/test86.ok create mode 100644 src/nvim/testdir/test87.in create mode 100644 src/nvim/testdir/test87.ok create mode 100644 src/nvim/testdir/test88.in create mode 100644 src/nvim/testdir/test88.ok create mode 100644 src/nvim/testdir/test89.in create mode 100644 src/nvim/testdir/test89.ok create mode 100644 src/nvim/testdir/test9.in create mode 100644 src/nvim/testdir/test9.ok create mode 100644 src/nvim/testdir/test90.in create mode 100644 src/nvim/testdir/test90.ok create mode 100644 src/nvim/testdir/test91.in create mode 100644 src/nvim/testdir/test91.ok create mode 100644 src/nvim/testdir/test92.in create mode 100644 src/nvim/testdir/test92.ok create mode 100644 src/nvim/testdir/test93.in create mode 100644 src/nvim/testdir/test93.ok create mode 100644 src/nvim/testdir/test94.in create mode 100644 src/nvim/testdir/test94.ok create mode 100644 src/nvim/testdir/test95.in create mode 100644 src/nvim/testdir/test95.ok create mode 100644 src/nvim/testdir/test96.in create mode 100644 src/nvim/testdir/test96.ok create mode 100644 src/nvim/testdir/test97.in create mode 100644 src/nvim/testdir/test97.ok create mode 100644 src/nvim/testdir/test98.in create mode 100644 src/nvim/testdir/test98.ok create mode 100644 src/nvim/testdir/test99.in create mode 100644 src/nvim/testdir/test99.ok create mode 100644 src/nvim/testdir/test_eval.in create mode 100644 src/nvim/testdir/test_eval.ok create mode 100644 src/nvim/testdir/test_eval_func.vim create mode 100644 src/nvim/testdir/unix.vim create mode 100644 src/nvim/types.h create mode 100644 src/nvim/ui.c create mode 100644 src/nvim/ui.h create mode 100644 src/nvim/undo.c create mode 100644 src/nvim/undo.h create mode 100644 src/nvim/undo_defs.h create mode 100644 src/nvim/version.c create mode 100644 src/nvim/version.h create mode 100644 src/nvim/version_defs.h create mode 100644 src/nvim/vim.h create mode 100644 src/nvim/window.c create mode 100644 src/nvim/window.h delete mode 100644 src/ops.c delete mode 100644 src/ops.h delete mode 100644 src/option.c delete mode 100644 src/option.h delete mode 100644 src/option_defs.h delete mode 100644 src/os/channel.c delete mode 100644 src/os/channel.h delete mode 100644 src/os/channel_defs.h delete mode 100644 src/os/env.c delete mode 100644 src/os/event.c delete mode 100644 src/os/event.h delete mode 100644 src/os/event_defs.h delete mode 100644 src/os/fs.c delete mode 100644 src/os/input.c delete mode 100644 src/os/input.h delete mode 100644 src/os/job.c delete mode 100644 src/os/job.h delete mode 100644 src/os/job_defs.h delete mode 100644 src/os/mem.c delete mode 100644 src/os/msgpack_rpc.c delete mode 100644 src/os/msgpack_rpc.h delete mode 100644 src/os/os.h delete mode 100644 src/os/rstream.c delete mode 100644 src/os/rstream.h delete mode 100644 src/os/rstream_defs.h delete mode 100644 src/os/server.c delete mode 100644 src/os/server.h delete mode 100644 src/os/server_defs.h delete mode 100644 src/os/shell.c delete mode 100644 src/os/shell.h delete mode 100644 src/os/signal.c delete mode 100644 src/os/signal.h delete mode 100644 src/os/time.c delete mode 100644 src/os/time.h delete mode 100644 src/os/users.c delete mode 100644 src/os/uv_helpers.c delete mode 100644 src/os/uv_helpers.h delete mode 100644 src/os/wstream.c delete mode 100644 src/os/wstream.h delete mode 100644 src/os/wstream_defs.h delete mode 100644 src/os_unix.c delete mode 100644 src/os_unix.h delete mode 100644 src/os_unix_defs.h delete mode 100644 src/path.c delete mode 100644 src/path.h delete mode 100644 src/po/Makefile delete mode 100644 src/po/af.po delete mode 100644 src/po/ca.po delete mode 100644 src/po/check.vim delete mode 100644 src/po/cleanup.vim delete mode 100644 src/po/cs.cp1250.po delete mode 100644 src/po/cs.po delete mode 100644 src/po/de.po delete mode 100644 src/po/en_GB.po delete mode 100644 src/po/eo.po delete mode 100644 src/po/es.po delete mode 100644 src/po/fi.po delete mode 100644 src/po/fr.po delete mode 100644 src/po/ga.po delete mode 100644 src/po/it.po delete mode 100644 src/po/ja.euc-jp.po delete mode 100644 src/po/ja.po delete mode 100644 src/po/ja.sjis.po delete mode 100644 src/po/ko.UTF-8.po delete mode 100644 src/po/ko.po delete mode 100644 src/po/nb.po delete mode 100644 src/po/nl.po delete mode 100644 src/po/no.po delete mode 100644 src/po/pl.UTF-8.po delete mode 100644 src/po/pl.cp1250.po delete mode 100644 src/po/pl.po delete mode 100644 src/po/pt_BR.po delete mode 100644 src/po/ru.cp1251.po delete mode 100644 src/po/ru.po delete mode 100644 src/po/sjiscorr.c delete mode 100644 src/po/sk.cp1250.po delete mode 100644 src/po/sk.po delete mode 100644 src/po/sv.po delete mode 100644 src/po/uk.cp1251.po delete mode 100644 src/po/uk.po delete mode 100644 src/po/vi.po delete mode 100644 src/po/zh_CN.UTF-8.po delete mode 100644 src/po/zh_CN.cp936.po delete mode 100644 src/po/zh_CN.po delete mode 100644 src/po/zh_TW.UTF-8.po delete mode 100644 src/po/zh_TW.po delete mode 100644 src/popupmnu.c delete mode 100644 src/popupmnu.h delete mode 100644 src/pos.h delete mode 100644 src/proto.h delete mode 100644 src/quickfix.c delete mode 100644 src/quickfix.h delete mode 100644 src/regexp.c delete mode 100644 src/regexp.h delete mode 100644 src/regexp_defs.h delete mode 100644 src/regexp_nfa.c delete mode 100644 src/screen.c delete mode 100644 src/screen.h delete mode 100644 src/search.c delete mode 100644 src/search.h delete mode 100644 src/sha256.c delete mode 100644 src/sha256.h delete mode 100644 src/sign_defs.h delete mode 100644 src/spell.c delete mode 100644 src/spell.h delete mode 100644 src/strings.c delete mode 100644 src/strings.h delete mode 100644 src/syntax.c delete mode 100644 src/syntax.h delete mode 100644 src/syntax_defs.h delete mode 100644 src/tag.c delete mode 100644 src/tag.h delete mode 100644 src/term.c delete mode 100644 src/term.h delete mode 100644 src/term_defs.h delete mode 100644 src/testdir/Makefile delete mode 100644 src/testdir/dotest.in delete mode 100644 src/testdir/sautest/autoload/Test104.vim delete mode 100644 src/testdir/sautest/autoload/footest.vim delete mode 100644 src/testdir/test1.in delete mode 100644 src/testdir/test1.ok delete mode 100644 src/testdir/test10.in delete mode 100644 src/testdir/test10.ok delete mode 100644 src/testdir/test100.in delete mode 100644 src/testdir/test100.ok delete mode 100644 src/testdir/test101.in delete mode 100644 src/testdir/test101.ok delete mode 100644 src/testdir/test102.in delete mode 100644 src/testdir/test102.ok delete mode 100644 src/testdir/test103.in delete mode 100644 src/testdir/test103.ok delete mode 100644 src/testdir/test104.in delete mode 100644 src/testdir/test104.ok delete mode 100644 src/testdir/test105.in delete mode 100644 src/testdir/test105.ok delete mode 100644 src/testdir/test106.in delete mode 100644 src/testdir/test106.ok delete mode 100644 src/testdir/test10a.in delete mode 100644 src/testdir/test10a.ok delete mode 100644 src/testdir/test11.in delete mode 100644 src/testdir/test11.ok delete mode 100644 src/testdir/test12.in delete mode 100644 src/testdir/test12.ok delete mode 100644 src/testdir/test13.in delete mode 100644 src/testdir/test13.ok delete mode 100644 src/testdir/test14.in delete mode 100644 src/testdir/test14.ok delete mode 100644 src/testdir/test15.in delete mode 100644 src/testdir/test15.ok delete mode 100644 src/testdir/test16.in delete mode 100644 src/testdir/test16.ok delete mode 100644 src/testdir/test17.in delete mode 100644 src/testdir/test17.ok delete mode 100644 src/testdir/test17a.in delete mode 100644 src/testdir/test18.in delete mode 100644 src/testdir/test18.ok delete mode 100644 src/testdir/test19.in delete mode 100644 src/testdir/test19.ok delete mode 100644 src/testdir/test2.in delete mode 100644 src/testdir/test2.ok delete mode 100644 src/testdir/test20.in delete mode 100644 src/testdir/test20.ok delete mode 100644 src/testdir/test21.in delete mode 100644 src/testdir/test21.ok delete mode 100644 src/testdir/test22.in delete mode 100644 src/testdir/test22.ok delete mode 100644 src/testdir/test23.in delete mode 100644 src/testdir/test23.ok delete mode 100644 src/testdir/test24.in delete mode 100644 src/testdir/test24.ok delete mode 100644 src/testdir/test25.in delete mode 100644 src/testdir/test25.ok delete mode 100644 src/testdir/test26.in delete mode 100644 src/testdir/test26.ok delete mode 100644 src/testdir/test27.in delete mode 100644 src/testdir/test27.ok delete mode 100644 src/testdir/test28.in delete mode 100644 src/testdir/test28.ok delete mode 100644 src/testdir/test29.in delete mode 100644 src/testdir/test29.ok delete mode 100644 src/testdir/test3.in delete mode 100644 src/testdir/test3.ok delete mode 100644 src/testdir/test30.in delete mode 100644 src/testdir/test30.ok delete mode 100644 src/testdir/test31.in delete mode 100644 src/testdir/test31.ok delete mode 100644 src/testdir/test32.in delete mode 100644 src/testdir/test32.ok delete mode 100644 src/testdir/test33.in delete mode 100644 src/testdir/test33.ok delete mode 100644 src/testdir/test34.in delete mode 100644 src/testdir/test34.ok delete mode 100644 src/testdir/test35.in delete mode 100644 src/testdir/test35.ok delete mode 100644 src/testdir/test36.in delete mode 100644 src/testdir/test36.ok delete mode 100644 src/testdir/test37.in delete mode 100644 src/testdir/test37.ok delete mode 100644 src/testdir/test38.in delete mode 100644 src/testdir/test38.ok delete mode 100644 src/testdir/test39.in delete mode 100644 src/testdir/test39.ok delete mode 100644 src/testdir/test4.in delete mode 100644 src/testdir/test4.ok delete mode 100644 src/testdir/test40.in delete mode 100644 src/testdir/test40.ok delete mode 100644 src/testdir/test41.in delete mode 100644 src/testdir/test41.ok delete mode 100644 src/testdir/test42.in delete mode 100644 src/testdir/test42.ok delete mode 100644 src/testdir/test43.in delete mode 100644 src/testdir/test43.ok delete mode 100644 src/testdir/test44.in delete mode 100644 src/testdir/test44.ok delete mode 100644 src/testdir/test45.in delete mode 100644 src/testdir/test45.ok delete mode 100644 src/testdir/test46.in delete mode 100644 src/testdir/test46.ok delete mode 100644 src/testdir/test47.in delete mode 100644 src/testdir/test47.ok delete mode 100644 src/testdir/test48.in delete mode 100644 src/testdir/test48.ok delete mode 100644 src/testdir/test49.in delete mode 100644 src/testdir/test49.ok delete mode 100644 src/testdir/test49.vim delete mode 100644 src/testdir/test5.in delete mode 100644 src/testdir/test5.ok delete mode 100644 src/testdir/test50.in delete mode 100644 src/testdir/test50.ok delete mode 100644 src/testdir/test51.in delete mode 100644 src/testdir/test51.ok delete mode 100644 src/testdir/test52.in delete mode 100644 src/testdir/test52.ok delete mode 100644 src/testdir/test53.in delete mode 100644 src/testdir/test53.ok delete mode 100644 src/testdir/test54.in delete mode 100644 src/testdir/test54.ok delete mode 100644 src/testdir/test55.in delete mode 100644 src/testdir/test55.ok delete mode 100644 src/testdir/test56.in delete mode 100644 src/testdir/test56.ok delete mode 100644 src/testdir/test57.in delete mode 100644 src/testdir/test57.ok delete mode 100644 src/testdir/test58.in delete mode 100644 src/testdir/test58.ok delete mode 100644 src/testdir/test59.in delete mode 100644 src/testdir/test59.ok delete mode 100644 src/testdir/test6.in delete mode 100644 src/testdir/test6.ok delete mode 100644 src/testdir/test60.in delete mode 100644 src/testdir/test60.ok delete mode 100644 src/testdir/test60.vim delete mode 100644 src/testdir/test61.in delete mode 100644 src/testdir/test61.ok delete mode 100644 src/testdir/test62.in delete mode 100644 src/testdir/test62.ok delete mode 100644 src/testdir/test63.in delete mode 100644 src/testdir/test63.ok delete mode 100644 src/testdir/test64.in delete mode 100644 src/testdir/test64.ok delete mode 100644 src/testdir/test65.in delete mode 100644 src/testdir/test65.ok delete mode 100644 src/testdir/test66.in delete mode 100644 src/testdir/test66.ok delete mode 100644 src/testdir/test67.in delete mode 100644 src/testdir/test67.ok delete mode 100644 src/testdir/test68.in delete mode 100644 src/testdir/test68.ok delete mode 100644 src/testdir/test69.in delete mode 100644 src/testdir/test69.ok delete mode 100644 src/testdir/test7.in delete mode 100644 src/testdir/test7.ok delete mode 100644 src/testdir/test70.in delete mode 100644 src/testdir/test70.ok delete mode 100644 src/testdir/test71.in delete mode 100644 src/testdir/test71.ok delete mode 100644 src/testdir/test71a.in delete mode 100644 src/testdir/test72.in delete mode 100644 src/testdir/test72.ok delete mode 100644 src/testdir/test73.in delete mode 100644 src/testdir/test73.ok delete mode 100644 src/testdir/test74.in delete mode 100644 src/testdir/test74.ok delete mode 100644 src/testdir/test75.in delete mode 100644 src/testdir/test75.ok delete mode 100644 src/testdir/test76.in delete mode 100644 src/testdir/test76.ok delete mode 100644 src/testdir/test77.in delete mode 100644 src/testdir/test77.ok delete mode 100644 src/testdir/test78.in delete mode 100644 src/testdir/test78.ok delete mode 100644 src/testdir/test79.in delete mode 100644 src/testdir/test79.ok delete mode 100644 src/testdir/test8.in delete mode 100644 src/testdir/test8.ok delete mode 100644 src/testdir/test80.in delete mode 100644 src/testdir/test80.ok delete mode 100644 src/testdir/test81.in delete mode 100644 src/testdir/test81.ok delete mode 100644 src/testdir/test82.in delete mode 100644 src/testdir/test82.ok delete mode 100644 src/testdir/test83-tags2 delete mode 100644 src/testdir/test83-tags3 delete mode 100644 src/testdir/test83.in delete mode 100644 src/testdir/test83.ok delete mode 100644 src/testdir/test84.in delete mode 100644 src/testdir/test84.ok delete mode 100644 src/testdir/test85.in delete mode 100644 src/testdir/test85.ok delete mode 100644 src/testdir/test86.in delete mode 100644 src/testdir/test86.ok delete mode 100644 src/testdir/test87.in delete mode 100644 src/testdir/test87.ok delete mode 100644 src/testdir/test88.in delete mode 100644 src/testdir/test88.ok delete mode 100644 src/testdir/test89.in delete mode 100644 src/testdir/test89.ok delete mode 100644 src/testdir/test9.in delete mode 100644 src/testdir/test9.ok delete mode 100644 src/testdir/test90.in delete mode 100644 src/testdir/test90.ok delete mode 100644 src/testdir/test91.in delete mode 100644 src/testdir/test91.ok delete mode 100644 src/testdir/test92.in delete mode 100644 src/testdir/test92.ok delete mode 100644 src/testdir/test93.in delete mode 100644 src/testdir/test93.ok delete mode 100644 src/testdir/test94.in delete mode 100644 src/testdir/test94.ok delete mode 100644 src/testdir/test95.in delete mode 100644 src/testdir/test95.ok delete mode 100644 src/testdir/test96.in delete mode 100644 src/testdir/test96.ok delete mode 100644 src/testdir/test97.in delete mode 100644 src/testdir/test97.ok delete mode 100644 src/testdir/test98.in delete mode 100644 src/testdir/test98.ok delete mode 100644 src/testdir/test99.in delete mode 100644 src/testdir/test99.ok delete mode 100644 src/testdir/test_eval.in delete mode 100644 src/testdir/test_eval.ok delete mode 100644 src/testdir/test_eval_func.vim delete mode 100644 src/testdir/unix.vim delete mode 100644 src/types.h delete mode 100644 src/ui.c delete mode 100644 src/ui.h delete mode 100644 src/undo.c delete mode 100644 src/undo.h delete mode 100644 src/undo_defs.h delete mode 100644 src/version.c delete mode 100644 src/version.h delete mode 100644 src/version_defs.h delete mode 100644 src/vim.h delete mode 100644 src/window.c delete mode 100644 src/window.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 7638346abc..0000000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,114 +0,0 @@ -include(CheckLibraryExists) - -set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/auto) -set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/msgpack-gen.lua) -file(GLOB API_HEADERS api/*.h) -set(MSGPACK_RPC_HEADER ${PROJECT_SOURCE_DIR}/src/os/msgpack_rpc.h) -set(MSGPACK_DISPATCH ${GENERATED_DIR}/msgpack_dispatch.c) - -# Remove helpers.h from API_HEADERS since it doesn't contain public API -# functions -foreach(sfile ${API_HEADERS}) - get_filename_component(f ${sfile} NAME) - if(${f} MATCHES "^(helpers.h)$") - list(APPEND to_remove ${sfile}) - endif() -endforeach() -list(REMOVE_ITEM API_HEADERS ${to_remove}) -set(to_remove) - -file(MAKE_DIRECTORY ${GENERATED_DIR}) - -add_custom_command(OUTPUT ${MSGPACK_DISPATCH} - COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${MSGPACK_DISPATCH} - DEPENDS - ${API_HEADERS} - ${MSGPACK_RPC_HEADER} - ${DISPATCH_GENERATOR} - ) - -file( GLOB NEOVIM_SOURCES *.c ) - -foreach(sfile ${NEOVIM_SOURCES}) - get_filename_component(f ${sfile} NAME) - if(${f} MATCHES "^(regexp_nfa.c)$") - list(APPEND to_remove ${sfile}) - endif() -endforeach() - -list(REMOVE_ITEM NEOVIM_SOURCES ${to_remove}) -list(APPEND NEOVIM_SOURCES "${PROJECT_BINARY_DIR}/config/auto/pathdef.c") -list(APPEND NEOVIM_SOURCES "${MSGPACK_DISPATCH}") - -file( GLOB OS_SOURCES os/*.c ) -file( GLOB API_SOURCES api/*.c ) - -set(CONV_SRCS - api.c - arabic.c - garray.c - memory.c - os/env.c - os/event.c - os/job.c - os/mem.c - os/rstream.c - os/signal.c - os/users.c - os/wstream.c - ) - -set_source_files_properties( - ${CONV_SRCS} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wconversion") - -if(CMAKE_C_COMPILER_ID MATCHES "Clang") - if(DEFINED ENV{SANITIZE}) - message(STATUS "Enabling the sanitizers") - add_definitions(-DEXITFREE) # is this necessary for LeakSanitizer? - add_definitions(-fno-sanitize-recover -fno-omit-frame-pointer - -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined) - set(CMAKE_EXE_LINKER_FLAGS - "-fsanitize=address -fsanitize=undefined ${CMAKE_EXE_LINKER_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS - "-fsanitize=address -fsanitize=undefined ${CMAKE_SHARED_LINKER_FLAGS}") - endif() -endif() - -# Our dependencies come first. - -if (LibIntl_FOUND) - list(APPEND NVIM_LINK_LIBRARIES ${LibIntl_LIBRARY}) -endif() - -check_library_exists(curses tgetent "" HAVE_LIBCURSES) -if (HAVE_LIBCURSES) - list(APPEND NVIM_LINK_LIBRARIES curses) -else() - check_library_exists(tinfo tgetent "" HAVE_LIBTINFO) - if (HAVE_LIBTINFO) - list(APPEND NVIM_LINK_LIBRARIES tinfo) - else() - find_package(Curses REQUIRED) - list(APPEND NVIM_LINK_LIBRARIES ${CURSES_LIBRARIES}) - endif() -endif() - -# Put these last on the link line, since multiple things may depend on them. -list(APPEND NVIM_LINK_LIBRARIES - ${LIBUV_LIBRARIES} - ${MSGPACK_LIBRARIES} - ${LUAJIT_LIBRARIES} - m - ${CMAKE_THREAD_LIBS_INIT}) - -if(NOT DEFINED ENV{SKIP_EXEC}) - add_executable(nvim ${NEOVIM_SOURCES} ${OS_SOURCES} ${API_SOURCES}) - target_link_libraries(nvim ${NVIM_LINK_LIBRARIES}) - install(TARGETS nvim RUNTIME DESTINATION bin) -endif() - -if(NOT DEFINED ENV{SKIP_UNITTEST}) - add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_SOURCES} - ${OS_SOURCES} ${API_SOURCES}) - target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) -endif() diff --git a/src/api/buffer.c b/src/api/buffer.c deleted file mode 100644 index 328b26061d..0000000000 --- a/src/api/buffer.c +++ /dev/null @@ -1,413 +0,0 @@ -// Much of this code was adapted from 'if_py_both.h' from the original -// vim source -#include -#include - -#include "api/buffer.h" -#include "api/helpers.h" -#include "api/defs.h" -#include "../vim.h" -#include "../buffer.h" -#include "memline.h" -#include "memory.h" -#include "misc1.h" -#include "misc2.h" -#include "ex_cmds.h" -#include "mark.h" -#include "fileio.h" -#include "move.h" -#include "../window.h" -#include "undo.h" - -// Find a window that contains "buf" and switch to it. -// If there is no such window, use the current window and change "curbuf". -// Caller must initialize save_curbuf to NULL. -// restore_win_for_buf() MUST be called later! -static void switch_to_win_for_buf(buf_T *buf, - win_T **save_curwinp, - tabpage_T **save_curtabp, - buf_T **save_curbufp); - -static void restore_win_for_buf(win_T *save_curwin, - tabpage_T *save_curtab, - buf_T *save_curbuf); - -// Check if deleting lines made the cursor position invalid. -// Changed the lines from "lo" to "hi" and added "extra" lines (negative if -// deleted). -static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra); - -// Normalizes 0-based indexes to buffer line numbers -static int64_t normalize_index(buf_T *buf, int64_t index); - -int64_t buffer_get_length(Buffer buffer, Error *err) -{ - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return 0; - } - - return buf->b_ml.ml_line_count; -} - -String buffer_get_line(Buffer buffer, int64_t index, Error *err) -{ - String rv = {.size = 0}; - StringArray slice = buffer_get_slice(buffer, index, index, true, true, err); - - if (slice.size) { - rv = slice.items[0]; - } - - return rv; -} - -void buffer_set_line(Buffer buffer, int64_t index, String line, Error *err) -{ - StringArray array = {.items = &line, .size = 1}; - buffer_set_slice(buffer, index, index, true, true, array, err); -} - -void buffer_del_line(Buffer buffer, int64_t index, Error *err) -{ - StringArray array = {.size = 0}; - buffer_set_slice(buffer, index, index, true, true, array, err); -} - -StringArray buffer_get_slice(Buffer buffer, - int64_t start, - int64_t end, - bool include_start, - bool include_end, - Error *err) -{ - StringArray rv = {.size = 0}; - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return rv; - } - - start = normalize_index(buf, start) + (include_start ? 0 : 1); - end = normalize_index(buf, end) + (include_end ? 1 : 0); - - if (start >= end) { - // Return 0-length array - return rv; - } - - rv.size = end - start; - rv.items = xmalloc(sizeof(String) * rv.size); - - for (uint32_t i = 0; i < rv.size; i++) { - rv.items[i].data = xstrdup((char *)ml_get_buf(buf, start + i, false)); - rv.items[i].size = strlen(rv.items[i].data); - } - - return rv; -} - -void buffer_set_slice(Buffer buffer, - int64_t start, - int64_t end, - bool include_start, - bool include_end, - StringArray replacement, - Error *err) -{ - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return; - } - - start = normalize_index(buf, start) + (include_start ? 0 : 1); - end = normalize_index(buf, end) + (include_end ? 1 : 0); - - if (start > end) { - set_api_error("start > end", err); - return; - } - - buf_T *save_curbuf = NULL; - win_T *save_curwin = NULL; - tabpage_T *save_curtab = NULL; - uint32_t new_len = replacement.size; - uint32_t old_len = end - start; - uint32_t i; - int32_t extra = 0; // lines added to text, can be negative - char **lines; - - if (new_len == 0) { - // avoid allocating zero bytes - lines = NULL; - } else { - lines = xcalloc(sizeof(char *), new_len); - } - - for (i = 0; i < new_len; i++) { - String l = replacement.items[i]; - lines[i] = xstrndup(l.data, l.size); - } - - try_start(); - switch_to_win_for_buf(buf, &save_curwin, &save_curtab, &save_curbuf); - - if (u_save(start - 1, end) == FAIL) { - set_api_error("Cannot save undo information", err); - goto cleanup; - } - - // If the size of the range is reducing (ie, new_len < old_len) we - // need to delete some old_len. We do this at the start, by - // repeatedly deleting line "start". - for (i = 0; new_len < old_len && i < old_len - new_len; i++) { - if (ml_delete(start, false) == FAIL) { - set_api_error("Cannot delete line", err); - goto cleanup; - } - } - - extra -= i; - - // For as long as possible, replace the existing old_len with the - // new old_len. This is a more efficient operation, as it requires - // less memory allocation and freeing. - for (i = 0; i < old_len && i < new_len; i++) { - if (ml_replace(start + i, (char_u *)lines[i], false) == FAIL) { - set_api_error("Cannot replace line", err); - goto cleanup; - } - // Mark lines that haven't been passed to the buffer as they need - // to be freed later - lines[i] = NULL; - } - - // Now we may need to insert the remaining new old_len - while (i < new_len) { - if (ml_append(start + i - 1, (char_u *)lines[i], 0, false) == FAIL) { - set_api_error("Cannot insert line", err); - goto cleanup; - } - // Same as with replacing - lines[i] = NULL; - i++; - extra++; - } - - // Adjust marks. Invalidate any which lie in the - // changed range, and move any in the remainder of the buffer. - // Only adjust marks if we managed to switch to a window that holds - // the buffer, otherwise line numbers will be invalid. - if (save_curbuf == NULL) { - mark_adjust(start, end - 1, MAXLNUM, extra); - } - - changed_lines(start, 0, end, extra); - - if (buf == curbuf) { - fix_cursor(start, end, extra); - } - -cleanup: - for (uint32_t i = 0; i < new_len; i++) { - if (lines[i] != NULL) { - free(lines[i]); - } - } - - free(lines); - restore_win_for_buf(save_curwin, save_curtab, save_curbuf); - try_end(err); -} - -Object buffer_get_var(Buffer buffer, String name, Error *err) -{ - Object rv; - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return rv; - } - - return dict_get_value(buf->b_vars, name, err); -} - -Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) -{ - Object rv; - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return rv; - } - - return dict_set_value(buf->b_vars, name, value, err); -} - -Object buffer_get_option(Buffer buffer, String name, Error *err) -{ - Object rv; - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return rv; - } - - return get_option_from(buf, SREQ_BUF, name, err); -} - -void buffer_set_option(Buffer buffer, String name, Object value, Error *err) -{ - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return; - } - - set_option_to(buf, SREQ_BUF, name, value, err); -} - -String buffer_get_name(Buffer buffer, Error *err) -{ - String rv = {.size = 0, .data = ""}; - buf_T *buf = find_buffer(buffer, err); - - if (!buf || buf->b_ffname == NULL) { - return rv; - } - - rv.data = xstrdup((char *)buf->b_ffname); - rv.size = strlen(rv.data); - return rv; -} - -void buffer_set_name(Buffer buffer, String name, Error *err) -{ - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return; - } - - aco_save_T aco; - int ren_ret; - char *val = xstrndup(name.data, name.size); - - try_start(); - // Using aucmd_*: autocommands will be executed by rename_buffer - aucmd_prepbuf(&aco, buf); - ren_ret = rename_buffer((char_u *)val); - aucmd_restbuf(&aco); - - if (try_end(err)) { - return; - } - - if (ren_ret == FAIL) { - set_api_error("failed to rename buffer", err); - } -} - -bool buffer_is_valid(Buffer buffer) -{ - Error stub = {.set = false}; - return find_buffer(buffer, &stub) != NULL; -} - -void buffer_insert(Buffer buffer, int64_t index, StringArray lines, Error *err) -{ - buffer_set_slice(buffer, index, index, false, true, lines, err); -} - -Position buffer_get_mark(Buffer buffer, String name, Error *err) -{ - Position rv; - buf_T *buf = find_buffer(buffer, err); - - if (!buf) { - return rv; - } - - if (name.size != 0) { - set_api_error("mark name must be a single character", err); - return rv; - } - - pos_T *posp; - buf_T *savebuf; - char mark = *name.data; - - try_start(); - switch_buffer(&savebuf, buf); - posp = getmark(mark, false); - restore_buffer(savebuf); - - if (try_end(err)) { - return rv; - } - - if (posp == NULL) { - set_api_error("invalid mark name", err); - return rv; - } - - rv.row = posp->lnum; - rv.col = posp->col; - return rv; -} - -static void switch_to_win_for_buf(buf_T *buf, - win_T **save_curwinp, - tabpage_T **save_curtabp, - buf_T **save_curbufp) -{ - win_T *wp; - tabpage_T *tp; - - if (find_win_for_buf(buf, &wp, &tp) == FAIL - || switch_win(save_curwinp, save_curtabp, wp, tp, true) == FAIL) - switch_buffer(save_curbufp, buf); -} - -static void restore_win_for_buf(win_T *save_curwin, - tabpage_T *save_curtab, - buf_T *save_curbuf) -{ - if (save_curbuf == NULL) { - restore_win(save_curwin, save_curtab, true); - } else { - restore_buffer(save_curbuf); - } -} - -static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) -{ - if (curwin->w_cursor.lnum >= lo) { - // Adjust the cursor position if it's in/after the changed - // lines. - if (curwin->w_cursor.lnum >= hi) { - curwin->w_cursor.lnum += extra; - check_cursor_col(); - } else if (extra < 0) { - curwin->w_cursor.lnum = lo; - check_cursor(); - } else { - check_cursor_col(); - } - changed_cline_bef_curs(); - } - invalidate_botline(); -} - -static int64_t normalize_index(buf_T *buf, int64_t index) -{ - // Fix if < 0 - index = index < 0 ? buf->b_ml.ml_line_count + index : index; - // Convert the index to a vim line number - index++; - // Fix if > line_count - index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index; - return index; -} diff --git a/src/api/buffer.h b/src/api/buffer.h deleted file mode 100644 index 04f9422c19..0000000000 --- a/src/api/buffer.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef NEOVIM_API_BUFFER_H -#define NEOVIM_API_BUFFER_H - -#include -#include - -#include "api/defs.h" - -/// Gets the buffer line count -/// -/// @param buffer The buffer handle -/// @param[out] err Details of an error that may have occurred -/// @return The line count -int64_t buffer_get_length(Buffer buffer, Error *err); - -/// Gets a buffer line -/// -/// @param buffer The buffer handle -/// @param index The line index -/// @param[out] err Details of an error that may have occurred -/// @return The line string -String buffer_get_line(Buffer buffer, int64_t index, Error *err); - -/// Sets a buffer line -/// -/// @param buffer The buffer handle -/// @param index The line index -/// @param line The new line. -/// @param[out] err Details of an error that may have occurred -void buffer_set_line(Buffer buffer, int64_t index, String line, Error *err); - -/// Deletes a buffer line -/// -/// @param buffer The buffer handle -/// @param index The line index -/// @param[out] err Details of an error that may have occurred -void buffer_del_line(Buffer buffer, int64_t index, Error *err); - -/// Retrieves a line range from the buffer -/// -/// @param buffer The buffer handle -/// @param start The first line index -/// @param end The last line index -/// @param include_start True if the slice includes the `start` parameter -/// @param include_end True if the slice includes the `end` parameter -/// @param[out] err Details of an error that may have occurred -/// @return An array of lines -StringArray buffer_get_slice(Buffer buffer, - int64_t start, - int64_t end, - bool include_start, - bool include_end, - Error *err); - -/// Replaces a line range on the buffer -/// -/// @param buffer The buffer handle -/// @param start The first line index -/// @param end The last line index -/// @param include_start True if the slice includes the `start` parameter -/// @param include_end True if the slice includes the `end` parameter -/// @param lines An array of lines to use as replacement(A 0-length array -/// will simply delete the line range) -/// @param[out] err Details of an error that may have occurred -void buffer_set_slice(Buffer buffer, - int64_t start, - int64_t end, - bool include_start, - bool include_end, - StringArray replacement, - Error *err); - -/// Gets a buffer variable -/// -/// @param buffer The buffer handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value -Object buffer_get_var(Buffer buffer, String name, Error *err); - -/// Sets a buffer variable. Passing 'nil' as value deletes the variable. -/// -/// @param buffer The buffer handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return The old value -Object buffer_set_var(Buffer buffer, String name, Object value, Error *err); - -/// Gets a buffer option value -/// -/// @param buffer The buffer handle -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return The option value -Object buffer_get_option(Buffer buffer, String name, Error *err); - -/// Sets a buffer option value. Passing 'nil' as value deletes the option(only -/// works if there's a global fallback) -/// -/// @param buffer The buffer handle -/// @param name The option name -/// @param value The option value -/// @param[out] err Details of an error that may have occurred -void buffer_set_option(Buffer buffer, String name, Object value, Error *err); - -/// Gets the full file name for the buffer -/// -/// @param buffer The buffer handle -/// @param[out] err Details of an error that may have occurred -/// @return The buffer name -String buffer_get_name(Buffer buffer, Error *err); - -/// Sets the full file name for a buffer -/// -/// @param buffer The buffer handle -/// @param name The buffer name -/// @param[out] err Details of an error that may have occurred -void buffer_set_name(Buffer buffer, String name, Error *err); - -/// Checks if a buffer is valid -/// -/// @param buffer The buffer handle -/// @return true if the buffer is valid, false otherwise -bool buffer_is_valid(Buffer buffer); - -/// Inserts a sequence of lines to a buffer at a certain index -/// -/// @param buffer The buffer handle -/// @param lnum Insert the lines before `lnum`. If negative, it will append -/// to the end of the buffer. -/// @param lines An array of lines -/// @param[out] err Details of an error that may have occurred -void buffer_insert(Buffer buffer, int64_t index, StringArray lines, Error *err); - -/// Return a tuple (row,col) representing the position of the named mark -/// -/// @param buffer The buffer handle -/// @param name The mark's name -/// @param[out] err Details of an error that may have occurred -/// @return The (row, col) tuple -Position buffer_get_mark(Buffer buffer, String name, Error *err); - -#endif // NEOVIM_API_BUFFER_H - diff --git a/src/api/defs.h b/src/api/defs.h deleted file mode 100644 index 0ac2e790d2..0000000000 --- a/src/api/defs.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef NEOVIM_API_DEFS_H -#define NEOVIM_API_DEFS_H - -#include -#include - -// Basic types -typedef struct { - char msg[256]; - bool set; -} Error; - -typedef struct { - char *data; - size_t size; -} String; - -typedef uint16_t Buffer; -typedef uint16_t Window; -typedef uint16_t Tabpage; - -typedef struct object Object; - -typedef struct { - String *items; - size_t size; -} StringArray; - -typedef struct { - uint16_t row, col; -} Position; - -typedef struct { - Object *items; - size_t size; -} Array; - -typedef struct key_value_pair KeyValuePair; - -typedef struct { - KeyValuePair *items; - size_t size; -} Dictionary; - -typedef enum { - kObjectTypeNil, - kObjectTypeBool, - kObjectTypeInt, - kObjectTypeFloat, - kObjectTypeString, - kObjectTypeArray, - kObjectTypeDictionary -} ObjectType; - -struct object { - ObjectType type; - union { - bool boolean; - int64_t integer; - double floating_point; - String string; - Array array; - Dictionary dictionary; - } data; -}; - -struct key_value_pair { - String key; - Object value; -}; - - -#endif // NEOVIM_API_DEFS_H - diff --git a/src/api/helpers.c b/src/api/helpers.c deleted file mode 100644 index 51b1dcb754..0000000000 --- a/src/api/helpers.c +++ /dev/null @@ -1,578 +0,0 @@ -#include -#include -#include - -#include "api/helpers.h" -#include "api/defs.h" -#include "../vim.h" -#include "../buffer.h" -#include "../window.h" -#include "memory.h" -#include "eval.h" -#include "option.h" -#include "option_defs.h" - -#include "lib/khash.h" - -#if defined(ARCH_64) -typedef uint64_t ptr_int_t; -KHASH_SET_INIT_INT64(Lookup) -#elif defined(ARCH_32) -typedef uint32_t ptr_int_t; -KHASH_SET_INIT_INT(Lookup) -#endif - -/// Recursion helper for the `vim_to_object`. This uses a pointer table -/// to avoid infinite recursion due to cyclic references -/// -/// @param obj The source object -/// @param lookup Lookup table containing pointers to all processed objects -/// @return The converted value -static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup); - -static bool object_to_vim(Object obj, typval_T *tv, Error *err); - -static void set_option_value_for(char *key, - int numval, - char *stringval, - int opt_flags, - int opt_type, - void *from, - Error *err); - -static void set_option_value_err(char *key, - int numval, - char *stringval, - int opt_flags, - Error *err); - -void try_start() -{ - ++trylevel; -} - -bool try_end(Error *err) -{ - --trylevel; - - // Without this it stops processing all subsequent VimL commands and - // generates strange error messages if I e.g. try calling Test() in a - // cycle - did_emsg = false; - - if (got_int) { - if (did_throw) { - // If we got an interrupt, discard the current exception - discard_current_exception(); - } - - set_api_error("Keyboard interrupt", err); - got_int = false; - } else if (msg_list != NULL && *msg_list != NULL) { - int should_free; - char *msg = (char *)get_exception_string(*msg_list, - ET_ERROR, - NULL, - &should_free); - strncpy(err->msg, msg, sizeof(err->msg)); - err->set = true; - free_global_msglist(); - - if (should_free) { - free(msg); - } - } else if (did_throw) { - set_api_error((char *)current_exception->value, err); - } - - return err->set; -} - -Object dict_get_value(dict_T *dict, String key, Error *err) -{ - Object rv; - hashitem_T *hi; - dictitem_T *di; - char *k = xstrndup(key.data, key.size); - hi = hash_find(&dict->dv_hashtab, (uint8_t *)k); - free(k); - - if (HASHITEM_EMPTY(hi)) { - set_api_error("Key not found", err); - return rv; - } - - di = dict_lookup(hi); - rv = vim_to_object(&di->di_tv); - - return rv; -} - -Object dict_set_value(dict_T *dict, String key, Object value, Error *err) -{ - Object rv = {.type = kObjectTypeNil}; - - if (dict->dv_lock) { - set_api_error("Dictionary is locked", err); - return rv; - } - - if (key.size == 0) { - set_api_error("Empty dictionary keys aren't allowed", err); - return rv; - } - - dictitem_T *di = dict_find(dict, (uint8_t *)key.data, key.size); - - if (value.type == kObjectTypeNil) { - // Delete the key - if (di == NULL) { - // Doesn't exist, fail - set_api_error("Key doesn't exist", err); - } else { - // Return the old value - rv = vim_to_object(&di->di_tv); - // Delete the entry - hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key); - hash_remove(&dict->dv_hashtab, hi); - dictitem_free(di); - } - } else { - // Update the key - typval_T tv; - - // Convert the object to a vimscript type in the temporary variable - if (!object_to_vim(value, &tv, err)) { - return rv; - } - - if (di == NULL) { - // Need to create an entry - char *k = xstrndup(key.data, key.size); - di = dictitem_alloc((uint8_t *)k); - free(k); - dict_add(dict, di); - } else { - // Return the old value - clear_tv(&di->di_tv); - } - - // Update the value - copy_tv(&tv, &di->di_tv); - // Clear the temporary variable - clear_tv(&tv); - } - - return rv; -} - -Object get_option_from(void *from, int type, String name, Error *err) -{ - Object rv = {.type = kObjectTypeNil}; - - if (name.size == 0) { - set_api_error("Empty option name", err); - return rv; - } - - // Return values - int64_t numval; - char *stringval = NULL; - // copy the option name into 0-delimited string - char *key = xstrndup(name.data, name.size); - int flags = get_option_value_strict(key, &numval, &stringval, type, from); - free(key); - - if (!flags) { - set_api_error("invalid option name", err); - return rv; - } - - if (flags & SOPT_BOOL) { - rv.type = kObjectTypeBool; - rv.data.boolean = numval ? true : false; - } else if (flags & SOPT_NUM) { - rv.type = kObjectTypeInt; - rv.data.integer = numval; - } else if (flags & SOPT_STRING) { - if (stringval) { - rv.type = kObjectTypeString; - rv.data.string.data = stringval; - rv.data.string.size = strlen(stringval); - } else { - set_api_error(N_("Unable to get option value"), err); - } - } else { - set_api_error(N_("internal error: unknown option type"), err); - } - - return rv; -} - -void set_option_to(void *to, int type, String name, Object value, Error *err) -{ - if (name.size == 0) { - set_api_error("Empty option name", err); - return; - } - - char *key = xstrndup(name.data, name.size); - int flags = get_option_value_strict(key, NULL, NULL, type, to); - - if (flags == 0) { - set_api_error("invalid option name", err); - goto cleanup; - } - - if (value.type == kObjectTypeNil) { - if (type == SREQ_GLOBAL) { - set_api_error("unable to unset option", err); - goto cleanup; - } else if (!(flags & SOPT_GLOBAL)) { - set_api_error("cannot unset option that doesn't have a global value", - err); - goto cleanup; - } else { - unset_global_local_option(key, to); - goto cleanup; - } - } - - int opt_flags = (type ? OPT_LOCAL : OPT_GLOBAL); - - if (flags & SOPT_BOOL) { - if (value.type != kObjectTypeBool) { - set_api_error("option requires a boolean value", err); - goto cleanup; - } - bool val = value.data.boolean; - set_option_value_for(key, val, NULL, opt_flags, type, to, err); - - } else if (flags & SOPT_NUM) { - if (value.type != kObjectTypeInt) { - set_api_error("option requires an integer value", err); - goto cleanup; - } - - int val = value.data.integer; - set_option_value_for(key, val, NULL, opt_flags, type, to, err); - } else { - if (value.type != kObjectTypeString) { - set_api_error("option requires a string value", err); - goto cleanup; - } - - char *val = xstrndup(value.data.string.data, value.data.string.size); - set_option_value_for(key, 0, val, opt_flags, type, to, err); - } - -cleanup: - free(key); -} - -Object vim_to_object(typval_T *obj) -{ - Object rv; - // We use a lookup table to break out of cyclic references - khash_t(Lookup) *lookup = kh_init(Lookup); - rv = vim_to_object_rec(obj, lookup); - // Free the table - kh_destroy(Lookup, lookup); - return rv; -} - -buf_T *find_buffer(Buffer buffer, Error *err) -{ - buf_T *buf = buflist_findnr(buffer); - - if (buf == NULL) { - set_api_error("Invalid buffer id", err); - } - - return buf; -} - -win_T * find_window(Window window, Error *err) -{ - tabpage_T *tp; - win_T *wp; - - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (!--window) { - return wp; - } - } - - set_api_error("Invalid window id", err); - return NULL; -} - -tabpage_T * find_tab(Tabpage tabpage, Error *err) -{ - tabpage_T *rv = find_tabpage(tabpage); - - if (!rv) { - set_api_error("Invalid tabpage id", err); - } - - return rv; -} - -static bool object_to_vim(Object obj, typval_T *tv, Error *err) -{ - tv->v_type = VAR_UNKNOWN; - tv->v_lock = 0; - - switch (obj.type) { - case kObjectTypeNil: - tv->v_type = VAR_NUMBER; - tv->vval.v_number = 0; - break; - - case kObjectTypeBool: - tv->v_type = VAR_NUMBER; - tv->vval.v_number = obj.data.boolean; - break; - - case kObjectTypeInt: - tv->v_type = VAR_NUMBER; - tv->vval.v_number = obj.data.integer; - break; - - case kObjectTypeFloat: - tv->v_type = VAR_FLOAT; - tv->vval.v_float = obj.data.floating_point; - break; - - case kObjectTypeString: - tv->v_type = VAR_STRING; - tv->vval.v_string = (uint8_t *)xstrndup(obj.data.string.data, - obj.data.string.size); - break; - - case kObjectTypeArray: - tv->v_type = VAR_LIST; - tv->vval.v_list = list_alloc(); - - for (uint32_t i = 0; i < obj.data.array.size; i++) { - Object item = obj.data.array.items[i]; - listitem_T *li = listitem_alloc(); - - if (!object_to_vim(item, &li->li_tv, err)) { - // cleanup - listitem_free(li); - list_free(tv->vval.v_list, true); - return false; - } - - list_append(tv->vval.v_list, li); - } - tv->vval.v_list->lv_refcount++; - break; - - case kObjectTypeDictionary: - tv->v_type = VAR_DICT; - tv->vval.v_dict = dict_alloc(); - - for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { - KeyValuePair item = obj.data.dictionary.items[i]; - String key = item.key; - - if (key.size == 0) { - set_api_error("Empty dictionary keys aren't allowed", err); - // cleanup - dict_free(tv->vval.v_dict, true); - return false; - } - - char *k = xstrndup(key.data, key.size); - dictitem_T *di = dictitem_alloc((uint8_t *)k); - free(k); - - if (!object_to_vim(item.value, &di->di_tv, err)) { - // cleanup - dictitem_free(di); - dict_free(tv->vval.v_dict, true); - return false; - } - - dict_add(tv->vval.v_dict, di); - } - tv->vval.v_dict->dv_refcount++; - break; - } - - return true; -} - -static Object vim_to_object_rec(typval_T *obj, khash_t(Lookup) *lookup) -{ - Object rv = {.type = kObjectTypeNil}; - - if (obj->v_type == VAR_LIST || obj->v_type == VAR_DICT) { - int ret; - // Container object, add it to the lookup table - kh_put(Lookup, lookup, (ptr_int_t)obj, &ret); - if (!ret) { - // It's already present, meaning we alredy processed it so just return - // nil instead. - return rv; - } - } - - switch (obj->v_type) { - case VAR_STRING: - if (obj->vval.v_string != NULL) { - rv.type = kObjectTypeString; - rv.data.string.data = xstrdup((char *)obj->vval.v_string); - rv.data.string.size = strlen(rv.data.string.data); - } - break; - - case VAR_NUMBER: - rv.type = kObjectTypeInt; - rv.data.integer = obj->vval.v_number; - break; - - case VAR_FLOAT: - rv.type = kObjectTypeFloat; - rv.data.floating_point = obj->vval.v_float; - break; - - case VAR_LIST: - { - list_T *list = obj->vval.v_list; - listitem_T *item; - - if (list != NULL) { - rv.type = kObjectTypeArray; - rv.data.array.size = list->lv_len; - rv.data.array.items = xmalloc(list->lv_len * sizeof(Object)); - - uint32_t i = 0; - for (item = list->lv_first; item != NULL; item = item->li_next) { - rv.data.array.items[i] = vim_to_object_rec(&item->li_tv, lookup); - i++; - } - } - } - break; - - case VAR_DICT: - { - dict_T *dict = obj->vval.v_dict; - hashtab_T *ht; - uint64_t todo; - hashitem_T *hi; - dictitem_T *di; - - if (dict != NULL) { - ht = &obj->vval.v_dict->dv_hashtab; - todo = ht->ht_used; - rv.type = kObjectTypeDictionary; - - // Count items - rv.data.dictionary.size = 0; - for (hi = ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - rv.data.dictionary.size++; - } - } - - rv.data.dictionary.items = - xmalloc(rv.data.dictionary.size * sizeof(KeyValuePair)); - todo = ht->ht_used; - uint32_t i = 0; - - // Convert all - for (hi = ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - di = dict_lookup(hi); - // Convert key - rv.data.dictionary.items[i].key.data = - xstrdup((char *)hi->hi_key); - rv.data.dictionary.items[i].key.size = - strlen((char *)hi->hi_key); - // Convert value - rv.data.dictionary.items[i].value = - vim_to_object_rec(&di->di_tv, lookup); - todo--; - i++; - } - } - } - } - break; - } - - return rv; -} - - -static void set_option_value_for(char *key, - int numval, - char *stringval, - int opt_flags, - int opt_type, - void *from, - Error *err) -{ - win_T *save_curwin = NULL; - tabpage_T *save_curtab = NULL; - buf_T *save_curbuf = NULL; - - try_start(); - switch (opt_type) - { - case SREQ_WIN: - if (switch_win(&save_curwin, &save_curtab, (win_T *)from, - win_find_tabpage((win_T *)from), false) == FAIL) - { - if (try_end(err)) { - return; - } - set_api_error("problem while switching windows", err); - return; - } - set_option_value_err(key, numval, stringval, opt_flags, err); - restore_win(save_curwin, save_curtab, true); - break; - case SREQ_BUF: - switch_buffer(&save_curbuf, (buf_T *)from); - set_option_value_err(key, numval, stringval, opt_flags, err); - restore_buffer(save_curbuf); - break; - case SREQ_GLOBAL: - set_option_value_err(key, numval, stringval, opt_flags, err); - break; - } - - if (err->set) { - return; - } - - try_end(err); -} - - -static void set_option_value_err(char *key, - int numval, - char *stringval, - int opt_flags, - Error *err) -{ - char *errmsg; - - if ((errmsg = (char *)set_option_value((uint8_t *)key, - numval, - (uint8_t *)stringval, - opt_flags))) - { - if (try_end(err)) { - return; - } - - set_api_error(errmsg, err); - } -} diff --git a/src/api/helpers.h b/src/api/helpers.h deleted file mode 100644 index afa16e54f4..0000000000 --- a/src/api/helpers.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef NEOVIM_API_HELPERS_H -#define NEOVIM_API_HELPERS_H - -#include - -#include "api/defs.h" -#include "../vim.h" - -#define set_api_error(message, err) \ - do { \ - strncpy(err->msg, message, sizeof(err->msg)); \ - err->set = true; \ - } while (0) - -/// Start block that may cause vimscript exceptions -void try_start(void); - -/// End try block, set the error message if any and return true if an error -/// occurred. -/// -/// @param err Pointer to the stack-allocated error object -/// @return true if an error occurred -bool try_end(Error *err); - -/// Recursively expands a vimscript value in a dict -/// -/// @param dict The vimscript dict -/// @param key The key -/// @param[out] err Details of an error that may have occurred -Object dict_get_value(dict_T *dict, String key, Error *err); - -/// Set a value in a dict. Objects are recursively expanded into their -/// vimscript equivalents. Passing 'nil' as value deletes the key. -/// -/// @param dict The vimscript dict -/// @param key The key -/// @param value The new value -/// @param[out] err Details of an error that may have occurred -/// @return the old value, if any -Object dict_set_value(dict_T *dict, String key, Object value, Error *err); - -/// Gets the value of a global or local(buffer, window) option. -/// -/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer -/// to the window or buffer. -/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return the option value -Object get_option_from(void *from, int type, String name, Error *err); - -/// Sets the value of a global or local(buffer, window) option. -/// -/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer -/// to the window or buffer. -/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF` -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -void set_option_to(void *to, int type, String name, Object value, Error *err); - -/// Convert a vim object to an `Object` instance, recursively expanding -/// Arrays/Dictionaries. -/// -/// @param obj The source object -/// @return The converted value -Object vim_to_object(typval_T *obj); - -/// Finds the pointer for a window number -/// -/// @param window the window number -/// @param[out] err Details of an error that may have occurred -/// @return the window pointer -buf_T *find_buffer(Buffer buffer, Error *err); - -/// Finds the pointer for a window number -/// -/// @param window the window number -/// @param[out] err Details of an error that may have occurred -/// @return the window pointer -win_T * find_window(Window window, Error *err); - -/// Finds the pointer for a tabpage number -/// -/// @param tabpage the tabpage number -/// @param[out] err Details of an error that may have occurred -/// @return the tabpage pointer -tabpage_T * find_tab(Tabpage tabpage, Error *err); - -#endif // NEOVIM_API_HELPERS_H - diff --git a/src/api/tabpage.c b/src/api/tabpage.c deleted file mode 100644 index 27f0aba99c..0000000000 --- a/src/api/tabpage.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include - -#include "api/tabpage.h" -#include "api/vim.h" -#include "api/defs.h" -#include "api/helpers.h" - -int64_t tabpage_get_window_count(Tabpage tabpage, Error *err) -{ - uint64_t rv = 0; - tabpage_T *tab = find_tab(tabpage, err); - - if (!tab) { - return rv; - } - - tabpage_T *tp; - win_T *wp; - - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (tp != tab) { - break; - } - rv++; - } - - return rv; -} - -Object tabpage_get_var(Tabpage tabpage, String name, Error *err) -{ - Object rv; - tabpage_T *tab = find_tab(tabpage, err); - - if (!tab) { - return rv; - } - - return dict_get_value(tab->tp_vars, name, err); -} - -Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) -{ - Object rv; - tabpage_T *tab = find_tab(tabpage, err); - - if (!tab) { - return rv; - } - - return dict_set_value(tab->tp_vars, name, value, err); -} - -Window tabpage_get_window(Tabpage tabpage, Error *err) -{ - Window rv = 0; - tabpage_T *tab = find_tab(tabpage, err); - - if (!tab) { - return rv; - } - - if (tab == curtab) { - return vim_get_current_window(); - } else { - tabpage_T *tp; - win_T *wp; - rv = 1; - - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (tp == tab && wp == tab->tp_curwin) { - return rv; - } - rv++; - } - // There should always be a current window for a tabpage - abort(); - } -} - -bool tabpage_is_valid(Tabpage tabpage) -{ - Error stub = {.set = false}; - return find_tab(tabpage, &stub) != NULL; -} - diff --git a/src/api/tabpage.h b/src/api/tabpage.h deleted file mode 100644 index ccd6c7ab9e..0000000000 --- a/src/api/tabpage.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef NEOVIM_API_TABPAGE_H -#define NEOVIM_API_TABPAGE_H - -#include -#include - -#include "api/defs.h" - -/// Gets the number of windows in a tabpage -/// -/// @param tabpage The tabpage -/// @param[out] err Details of an error that may have occurred -/// @return The number of windows in `tabpage` -int64_t tabpage_get_window_count(Tabpage tabpage, Error *err); - -/// Gets a tabpage variable -/// -/// @param tabpage The tab page handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value -Object tabpage_get_var(Tabpage tabpage, String name, Error *err); - -/// Sets a tabpage variable. Passing 'nil' as value deletes the variable. -/// -/// @param tabpage handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return The tab page handle -Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err); - -/// Gets the current window in a tab page -/// -/// @param tabpage The tab page handle -/// @param[out] err Details of an error that may have occurred -/// @return The Window handle -Window tabpage_get_window(Tabpage tabpage, Error *err); - -/// Checks if a tab page is valid -/// -/// @param tabpage The tab page handle -/// @return true if the tab page is valid, false otherwise -bool tabpage_is_valid(Tabpage tabpage); - -#endif // NEOVIM_API_TABPAGE_H - diff --git a/src/api/vim.c b/src/api/vim.c deleted file mode 100644 index f9fa21550e..0000000000 --- a/src/api/vim.c +++ /dev/null @@ -1,321 +0,0 @@ -#include -#include -#include -#include - -#include "api/vim.h" -#include "api/helpers.h" -#include "api/defs.h" -#include "api/buffer.h" -#include "../vim.h" -#include "../buffer.h" -#include "../window.h" -#include "types.h" -#include "ascii.h" -#include "ex_docmd.h" -#include "screen.h" -#include "memory.h" -#include "message.h" -#include "eval.h" -#include "misc2.h" - -#define LINE_BUFFER_SIZE 4096 - -/// Writes a message to vim output or error buffer. The string is split -/// and flushed after each newline. Incomplete lines are kept for writing -/// later. -/// -/// @param message The message to write -/// @param to_err True if it should be treated as an error message(use -/// `emsg` instead of `msg` to print each line) -static void write_msg(String message, bool to_err); - -void vim_push_keys(String str) -{ - abort(); -} - -void vim_command(String str, Error *err) -{ - // We still use 0-terminated strings, so we must convert. - char *cmd_str = xstrndup(str.data, str.size); - // Run the command - try_start(); - do_cmdline_cmd((char_u *)cmd_str); - free(cmd_str); - update_screen(VALID); - try_end(err); -} - -Object vim_eval(String str, Error *err) -{ - Object rv; - char *expr_str = xstrndup(str.data, str.size); - // Evaluate the expression - try_start(); - typval_T *expr_result = eval_expr((char_u *)expr_str, NULL); - free(expr_str); - - if (!try_end(err)) { - // No errors, convert the result - rv = vim_to_object(expr_result); - } - - // Free the vim object - free_tv(expr_result); - return rv; -} - -int64_t vim_strwidth(String str) -{ - return mb_string2cells((char_u *)str.data, str.size); -} - -StringArray vim_list_runtime_paths(void) -{ - StringArray rv = {.size = 0}; - uint8_t *rtp = p_rtp; - - if (*rtp == NUL) { - // No paths - return rv; - } - - // Count the number of paths in rtp - while (*rtp != NUL) { - if (*rtp == ',') { - rv.size++; - } - rtp++; - } - - // index - uint32_t i = 0; - // Allocate memory for the copies - rv.items = xmalloc(sizeof(String) * rv.size); - // reset the position - rtp = p_rtp; - // Start copying - while (*rtp != NUL) { - rv.items[i].data = xmalloc(MAXPATHL); - // Copy the path from 'runtimepath' to rv.items[i] - rv.items[i].size = copy_option_part(&rtp, - (char_u *)rv.items[i].data, - MAXPATHL, - ","); - i++; - } - - return rv; -} - -void vim_change_directory(String dir, Error *err) -{ - char string[MAXPATHL]; - strncpy(string, dir.data, dir.size); - - try_start(); - - if (vim_chdir((char_u *)string)) { - if (!try_end(err)) { - set_api_error("failed to change directory", err); - } - return; - } - - post_chdir(false); - try_end(err); -} - -String vim_get_current_line(Error *err) -{ - return buffer_get_line(curbuf->b_fnum, curwin->w_cursor.lnum - 1, err); -} - -void vim_set_current_line(String line, Error *err) -{ - buffer_set_line(curbuf->b_fnum, curwin->w_cursor.lnum - 1, line, err); -} - -void vim_del_current_line(Error *err) -{ - buffer_del_line(curbuf->b_fnum, curwin->w_cursor.lnum - 1, err); -} - -Object vim_get_var(String name, Error *err) -{ - return dict_get_value(&globvardict, name, err); -} - -Object vim_set_var(String name, Object value, Error *err) -{ - return dict_set_value(&globvardict, name, value, err); -} - -Object vim_get_vvar(String name, Error *err) -{ - return dict_get_value(&vimvardict, name, err); -} - -Object vim_get_option(String name, Error *err) -{ - return get_option_from(NULL, SREQ_GLOBAL, name, err); -} - -void vim_set_option(String name, Object value, Error *err) -{ - set_option_to(NULL, SREQ_GLOBAL, name, value, err); -} - -void vim_out_write(String str) -{ - write_msg(str, false); -} - -void vim_err_write(String str) -{ - write_msg(str, true); -} - -int64_t vim_get_buffer_count(void) -{ - buf_T *b = firstbuf; - uint64_t n = 0; - - while (b) { - n++; - b = b->b_next; - } - - return n; -} - -Buffer vim_get_current_buffer(void) -{ - return curbuf->b_fnum; -} - -void vim_set_current_buffer(Buffer buffer, Error *err) -{ - try_start(); - if (do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buffer, 0) == FAIL) { - if (try_end(err)) { - return; - } - - char msg[256]; - snprintf(msg, sizeof(msg), "failed to switch to buffer %d", (int)buffer); - set_api_error(msg, err); - return; - } - - try_end(err); -} - -int64_t vim_get_window_count(void) -{ - tabpage_T *tp; - win_T *wp; - uint64_t rv = 0; - - FOR_ALL_TAB_WINDOWS(tp, wp) { - rv++; - } - - return rv; -} - -Window vim_get_current_window(void) -{ - tabpage_T *tp; - win_T *wp; - Window rv = 1; - - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (wp == curwin) { - return rv; - } - - rv++; - } - - // There should always be a current window - abort(); -} - -void vim_set_current_window(Window window, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return; - } - - try_start(); - win_goto(win); - - if (win != curwin) { - if (try_end(err)) { - return; - } - set_api_error("did not switch to the specified window", err); - return; - } - - try_end(err); -} - -int64_t vim_get_tabpage_count(void) -{ - tabpage_T *tp = first_tabpage; - uint64_t rv = 0; - - while (tp != NULL) { - tp = tp->tp_next; - rv++; - } - - return rv; -} - -Tabpage vim_get_current_tabpage(void) -{ - Tabpage rv = 1; - tabpage_T *t; - - for (t = first_tabpage; t != NULL && t != curtab; t = t->tp_next) { - rv++; - } - - return rv; -} - -void vim_set_current_tabpage(Tabpage tabpage, Error *err) -{ - try_start(); - goto_tabpage(tabpage); - try_end(err); -} - -static void write_msg(String message, bool to_err) -{ - static int pos = 0; - static char line_buf[LINE_BUFFER_SIZE]; - - for (uint32_t i = 0; i < message.size; i++) { - if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { - // Flush line - line_buf[pos] = NUL; - if (to_err) { - emsg((uint8_t *)line_buf); - } else { - msg((uint8_t *)line_buf); - } - - pos = 0; - continue; - } - - line_buf[pos++] = message.data[i]; - } -} diff --git a/src/api/vim.h b/src/api/vim.h deleted file mode 100644 index ceb696ff76..0000000000 --- a/src/api/vim.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef NEOVIM_API_VIM_H -#define NEOVIM_API_VIM_H - -#include -#include - -#include "api/defs.h" - -/// Send keys to vim input buffer, simulating user input. -/// -/// @param str The keys to send -void vim_push_keys(String str); - -/// Executes an ex-mode command str -/// -/// @param str The command str -/// @param[out] err Details of an error that may have occurred -void vim_command(String str, Error *err); - -/// Evaluates the expression str using the vim internal expression -/// evaluator (see |expression|). -/// Dictionaries and lists are recursively expanded. -/// -/// @param str The expression str -/// @param[out] err Details of an error that may have occurred -/// @return The expanded object -Object vim_eval(String str, Error *err); - -/// Calculates the number of display cells `str` occupies, tab is counted as -/// one cell. -/// -/// @param str Some text -/// @return The number of cells -int64_t vim_strwidth(String str); - -/// Returns a list of paths contained in 'runtimepath' -/// -/// @return The list of paths -StringArray vim_list_runtime_paths(void); - -/// Changes vim working directory -/// -/// @param dir The new working directory -/// @param[out] err Details of an error that may have occurred -void vim_change_directory(String dir, Error *err); - -/// Return the current line -/// -/// @param[out] err Details of an error that may have occurred -/// @return The current line string -String vim_get_current_line(Error *err); - -/// Delete the current line -/// -/// @param[out] err Details of an error that may have occurred -void vim_del_current_line(Error *err); - -/// Sets the current line -/// -/// @param line The line contents -/// @param[out] err Details of an error that may have occurred -void vim_set_current_line(String line, Error *err); - -/// Gets a global variable -/// -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value -Object vim_get_var(String name, Error *err); - -/// Sets a global variable. Passing 'nil' as value deletes the variable. -/// -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return the old value if any -Object vim_set_var(String name, Object value, Error *err); - -/// Gets a vim variable -/// -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value -Object vim_get_vvar(String name, Error *err); - -/// Get an option value string -/// -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return The option value -Object vim_get_option(String name, Error *err); - -/// Sets an option value -/// -/// @param name The option name -/// @param value The new option value -/// @param[out] err Details of an error that may have occurred -void vim_set_option(String name, Object value, Error *err); - -/// Write a message to vim output buffer -/// -/// @param str The message -void vim_out_write(String str); - -/// Write a message to vim error buffer -/// -/// @param str The message -void vim_err_write(String str); - -/// Gets the number of buffers -/// -/// @return The number of buffers -int64_t vim_get_buffer_count(void); - -/// Return the current buffer -/// -/// @reqturn The buffer handle -Buffer vim_get_current_buffer(void); - -/// Sets the current buffer -/// -/// @param id The buffer handle -/// @param[out] err Details of an error that may have occurred -void vim_set_current_buffer(Buffer buffer, Error *err); - -/// Gets the number of windows -/// -/// @return The number of windows -int64_t vim_get_window_count(void); - -/// Return the current window -/// -/// @return The window handle -Window vim_get_current_window(void); - -/// Sets the current window -/// -/// @param handle The window handle -void vim_set_current_window(Window window, Error *err); - -/// Gets the number of tab pages -/// -/// @return The number of tab pages -int64_t vim_get_tabpage_count(void); - -/// Return the current tab page -/// -/// @return The tab page handle -Tabpage vim_get_current_tabpage(void); - -/// Sets the current tab page -/// -/// @param handle The tab page handle -/// @param[out] err Details of an error that may have occurred -void vim_set_current_tabpage(Tabpage tabpage, Error *err); - -#endif // NEOVIM_API_VIM_H - diff --git a/src/api/window.c b/src/api/window.c deleted file mode 100644 index 2c67fc2a74..0000000000 --- a/src/api/window.c +++ /dev/null @@ -1,184 +0,0 @@ -#include -#include -#include - -#include "api/window.h" -#include "api/defs.h" -#include "api/helpers.h" -#include "../vim.h" -#include "../window.h" -#include "screen.h" -#include "misc2.h" - - -Buffer window_get_buffer(Window window, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return 0; - } - - return win->w_buffer->b_fnum; -} - -Position window_get_cursor(Window window, Error *err) -{ - Position rv = {.row = 0, .col = 0}; - win_T *win = find_window(window, err); - - if (win) { - rv.row = win->w_cursor.lnum; - rv.col = win->w_cursor.col; - } - - return rv; -} - -void window_set_cursor(Window window, Position pos, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return; - } - - if (pos.row <= 0 || pos.row > win->w_buffer->b_ml.ml_line_count) { - set_api_error("cursor position outside buffer", err); - return; - } - - win->w_cursor.lnum = pos.row; - win->w_cursor.col = pos.col; - win->w_cursor.coladd = 0; - // When column is out of range silently correct it. - check_cursor_col_win(win); - update_screen(VALID); -} - -int64_t window_get_height(Window window, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return 0; - } - - return win->w_height; -} - -void window_set_height(Window window, int64_t height, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return; - } - - win_T *savewin = curwin; - curwin = win; - try_start(); - win_setheight(height); - curwin = savewin; - try_end(err); -} - -int64_t window_get_width(Window window, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return 0; - } - - return win->w_width; -} - -void window_set_width(Window window, int64_t width, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return; - } - - win_T *savewin = curwin; - curwin = win; - try_start(); - win_setwidth(width); - curwin = savewin; - try_end(err); -} - -Object window_get_var(Window window, String name, Error *err) -{ - Object rv; - win_T *win = find_window(window, err); - - if (!win) { - return rv; - } - - return dict_get_value(win->w_vars, name, err); -} - -Object window_set_var(Window window, String name, Object value, Error *err) -{ - Object rv; - win_T *win = find_window(window, err); - - if (!win) { - return rv; - } - - return dict_set_value(win->w_vars, name, value, err); -} - -Object window_get_option(Window window, String name, Error *err) -{ - Object rv; - win_T *win = find_window(window, err); - - if (!win) { - return rv; - } - - return get_option_from(win, SREQ_WIN, name, err); -} - -void window_set_option(Window window, String name, Object value, Error *err) -{ - win_T *win = find_window(window, err); - - if (!win) { - return; - } - - set_option_to(win, SREQ_WIN, name, value, err); -} - -Position window_get_position(Window window, Error *err) -{ - Position rv; - win_T *win = find_window(window, err); - - if (win) { - rv.col = win->w_wincol; - rv.row = win->w_winrow; - } - - return rv; -} - -Tabpage window_get_tabpage(Window window, Error *err) -{ - set_api_error("Not implemented", err); - return 0; -} - -bool window_is_valid(Window window) -{ - Error stub = {.set = false}; - return find_window(window, &stub) != NULL; -} - diff --git a/src/api/window.h b/src/api/window.h deleted file mode 100644 index e9baa3f170..0000000000 --- a/src/api/window.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef NEOVIM_API_WINDOW_H -#define NEOVIM_API_WINDOW_H - -#include -#include - -#include "api/defs.h" - -/// Gets the current buffer in a window -/// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return The buffer handle -Buffer window_get_buffer(Window window, Error *err); - -/// Gets the cursor position in the window -/// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return the (row, col) tuple -Position window_get_cursor(Window window, Error *err); - -/// Sets the cursor position in the window -/// -/// @param window The window handle -/// @param pos the (row, col) tuple representing the new position -/// @param[out] err Details of an error that may have occurred -void window_set_cursor(Window window, Position pos, Error *err); - -/// Gets the window height -/// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return the height in rows -int64_t window_get_height(Window window, Error *err); - -/// Sets the window height. This will only succeed if the screen is split -/// horizontally. -/// -/// @param window The window handle -/// @param height the new height in rows -/// @param[out] err Details of an error that may have occurred -void window_set_height(Window window, int64_t height, Error *err); - -/// Gets the window width -/// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return the width in columns -int64_t window_get_width(Window window, Error *err); - -/// Sets the window width. This will only succeed if the screen is split -/// vertically. -/// -/// @param window The window handle -/// @param width the new width in columns -/// @param[out] err Details of an error that may have occurred -void window_set_width(Window window, int64_t width, Error *err); - -/// Gets a window variable -/// -/// @param window The window handle -/// @param name The variable name -/// @param[out] err Details of an error that may have occurred -/// @return The variable value -Object window_get_var(Window window, String name, Error *err); - -/// Sets a window variable. Passing 'nil' as value deletes the variable. -/// -/// @param window The window handle -/// @param name The variable name -/// @param value The variable value -/// @param[out] err Details of an error that may have occurred -/// @return The old value -Object window_set_var(Window window, String name, Object value, Error *err); - -/// Gets a window option value -/// -/// @param window The window handle -/// @param name The option name -/// @param[out] err Details of an error that may have occurred -/// @return The option value -Object window_get_option(Window window, String name, Error *err); - -/// Sets a window option value. Passing 'nil' as value deletes the option(only -/// works if there's a global fallback) -/// -/// @param window The window handle -/// @param name The option name -/// @param value The option value -/// @param[out] err Details of an error that may have occurred -void window_set_option(Window window, String name, Object value, Error *err); - -/// Gets the window position in display cells. First position is zero. -/// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return The (row, col) tuple with the window position -Position window_get_position(Window window, Error *err); - -/// Gets the window tab page -/// -/// @param window The window handle -/// @param[out] err Details of an error that may have occurred -/// @return The tab page that contains the window -Tabpage window_get_tabpage(Window window, Error *err); - -/// Checks if a window is valid -/// -/// @param window The window handle -/// @return true if the window is valid, false otherwise -bool window_is_valid(Window window); - -#endif // NEOVIM_API_WINDOW_H - diff --git a/src/arabic.c b/src/arabic.c deleted file mode 100644 index 54f88f8757..0000000000 --- a/src/arabic.c +++ /dev/null @@ -1,1532 +0,0 @@ -/// @file arabic.c -/// -/// Functions for Arabic language. -/// -/// Arabic characters are categorized into following types: -/// -/// Isolated - iso-8859-6 form char denoted with a_* -/// Initial - unicode form-B start char denoted with a_i_* -/// Medial - unicode form-B middle char denoted with a_m_* -/// Final - unicode form-B final char denoted with a_f_* -/// Stand-Alone - unicode form-B isolated char denoted with a_s_* (NOT USED) -/// - -#include "vim.h" -#include "arabic.h" - -// Arabic ISO-10646-1 character set definition - -// Arabic ISO-8859-6 (subset of 10646; 0600 - 06FF) -#define a_COMMA 0x060C -#define a_SEMICOLON 0x061B -#define a_QUESTION 0x061F -#define a_HAMZA 0x0621 -#define a_ALEF_MADDA 0x0622 -#define a_ALEF_HAMZA_ABOVE 0x0623 -#define a_WAW_HAMZA 0x0624 -#define a_ALEF_HAMZA_BELOW 0x0625 -#define a_YEH_HAMZA 0x0626 -#define a_ALEF 0x0627 -#define a_BEH 0x0628 -#define a_TEH_MARBUTA 0x0629 -#define a_TEH 0x062a -#define a_THEH 0x062b -#define a_JEEM 0x062c -#define a_HAH 0x062d -#define a_KHAH 0x062e -#define a_DAL 0x062f -#define a_THAL 0x0630 -#define a_REH 0x0631 -#define a_ZAIN 0x0632 -#define a_SEEN 0x0633 -#define a_SHEEN 0x0634 -#define a_SAD 0x0635 -#define a_DAD 0x0636 -#define a_TAH 0x0637 -#define a_ZAH 0x0638 -#define a_AIN 0x0639 -#define a_GHAIN 0x063a -#define a_TATWEEL 0x0640 -#define a_FEH 0x0641 -#define a_QAF 0x0642 -#define a_KAF 0x0643 -#define a_LAM 0x0644 -#define a_MEEM 0x0645 -#define a_NOON 0x0646 -#define a_HEH 0x0647 -#define a_WAW 0x0648 -#define a_ALEF_MAKSURA 0x0649 -#define a_YEH 0x064a - -#define a_FATHATAN 0x064b -#define a_DAMMATAN 0x064c -#define a_KASRATAN 0x064d -#define a_FATHA 0x064e -#define a_DAMMA 0x064f -#define a_KASRA 0x0650 -#define a_SHADDA 0x0651 -#define a_SUKUN 0x0652 - -#define a_MADDA_ABOVE 0x0653 -#define a_HAMZA_ABOVE 0x0654 -#define a_HAMZA_BELOW 0x0655 - -#define a_ZERO 0x0660 -#define a_ONE 0x0661 -#define a_TWO 0x0662 -#define a_THREE 0x0663 -#define a_FOUR 0x0664 -#define a_FIVE 0x0665 -#define a_SIX 0x0666 -#define a_SEVEN 0x0667 -#define a_EIGHT 0x0668 -#define a_NINE 0x0669 -#define a_PERCENT 0x066a -#define a_DECIMAL 0x066b -#define a_THOUSANDS 0x066c -#define a_STAR 0x066d -#define a_MINI_ALEF 0x0670 -// Rest of 8859-6 does not relate to Arabic - -// Arabic Presentation Form-B (subset of 10646; FE70 - FEFF) -// -// s -> isolated -// i -> initial -// m -> medial -// f -> final -// -#define a_s_FATHATAN 0xfe70 -#define a_m_TATWEEL_FATHATAN 0xfe71 -#define a_s_DAMMATAN 0xfe72 - -#define a_s_KASRATAN 0xfe74 - -#define a_s_FATHA 0xfe76 -#define a_m_FATHA 0xfe77 -#define a_s_DAMMA 0xfe78 -#define a_m_DAMMA 0xfe79 -#define a_s_KASRA 0xfe7a -#define a_m_KASRA 0xfe7b -#define a_s_SHADDA 0xfe7c -#define a_m_SHADDA 0xfe7d -#define a_s_SUKUN 0xfe7e -#define a_m_SUKUN 0xfe7f - -#define a_s_HAMZA 0xfe80 -#define a_s_ALEF_MADDA 0xfe81 -#define a_f_ALEF_MADDA 0xfe82 -#define a_s_ALEF_HAMZA_ABOVE 0xfe83 -#define a_f_ALEF_HAMZA_ABOVE 0xfe84 -#define a_s_WAW_HAMZA 0xfe85 -#define a_f_WAW_HAMZA 0xfe86 -#define a_s_ALEF_HAMZA_BELOW 0xfe87 -#define a_f_ALEF_HAMZA_BELOW 0xfe88 -#define a_s_YEH_HAMZA 0xfe89 -#define a_f_YEH_HAMZA 0xfe8a -#define a_i_YEH_HAMZA 0xfe8b -#define a_m_YEH_HAMZA 0xfe8c -#define a_s_ALEF 0xfe8d -#define a_f_ALEF 0xfe8e -#define a_s_BEH 0xfe8f -#define a_f_BEH 0xfe90 -#define a_i_BEH 0xfe91 -#define a_m_BEH 0xfe92 -#define a_s_TEH_MARBUTA 0xfe93 -#define a_f_TEH_MARBUTA 0xfe94 -#define a_s_TEH 0xfe95 -#define a_f_TEH 0xfe96 -#define a_i_TEH 0xfe97 -#define a_m_TEH 0xfe98 -#define a_s_THEH 0xfe99 -#define a_f_THEH 0xfe9a -#define a_i_THEH 0xfe9b -#define a_m_THEH 0xfe9c -#define a_s_JEEM 0xfe9d -#define a_f_JEEM 0xfe9e -#define a_i_JEEM 0xfe9f -#define a_m_JEEM 0xfea0 -#define a_s_HAH 0xfea1 -#define a_f_HAH 0xfea2 -#define a_i_HAH 0xfea3 -#define a_m_HAH 0xfea4 -#define a_s_KHAH 0xfea5 -#define a_f_KHAH 0xfea6 -#define a_i_KHAH 0xfea7 -#define a_m_KHAH 0xfea8 -#define a_s_DAL 0xfea9 -#define a_f_DAL 0xfeaa -#define a_s_THAL 0xfeab -#define a_f_THAL 0xfeac -#define a_s_REH 0xfead -#define a_f_REH 0xfeae -#define a_s_ZAIN 0xfeaf -#define a_f_ZAIN 0xfeb0 -#define a_s_SEEN 0xfeb1 -#define a_f_SEEN 0xfeb2 -#define a_i_SEEN 0xfeb3 -#define a_m_SEEN 0xfeb4 -#define a_s_SHEEN 0xfeb5 -#define a_f_SHEEN 0xfeb6 -#define a_i_SHEEN 0xfeb7 -#define a_m_SHEEN 0xfeb8 -#define a_s_SAD 0xfeb9 -#define a_f_SAD 0xfeba -#define a_i_SAD 0xfebb -#define a_m_SAD 0xfebc -#define a_s_DAD 0xfebd -#define a_f_DAD 0xfebe -#define a_i_DAD 0xfebf -#define a_m_DAD 0xfec0 -#define a_s_TAH 0xfec1 -#define a_f_TAH 0xfec2 -#define a_i_TAH 0xfec3 -#define a_m_TAH 0xfec4 -#define a_s_ZAH 0xfec5 -#define a_f_ZAH 0xfec6 -#define a_i_ZAH 0xfec7 -#define a_m_ZAH 0xfec8 -#define a_s_AIN 0xfec9 -#define a_f_AIN 0xfeca -#define a_i_AIN 0xfecb -#define a_m_AIN 0xfecc -#define a_s_GHAIN 0xfecd -#define a_f_GHAIN 0xfece -#define a_i_GHAIN 0xfecf -#define a_m_GHAIN 0xfed0 -#define a_s_FEH 0xfed1 -#define a_f_FEH 0xfed2 -#define a_i_FEH 0xfed3 -#define a_m_FEH 0xfed4 -#define a_s_QAF 0xfed5 -#define a_f_QAF 0xfed6 -#define a_i_QAF 0xfed7 -#define a_m_QAF 0xfed8 -#define a_s_KAF 0xfed9 -#define a_f_KAF 0xfeda -#define a_i_KAF 0xfedb -#define a_m_KAF 0xfedc -#define a_s_LAM 0xfedd -#define a_f_LAM 0xfede -#define a_i_LAM 0xfedf -#define a_m_LAM 0xfee0 -#define a_s_MEEM 0xfee1 -#define a_f_MEEM 0xfee2 -#define a_i_MEEM 0xfee3 -#define a_m_MEEM 0xfee4 -#define a_s_NOON 0xfee5 -#define a_f_NOON 0xfee6 -#define a_i_NOON 0xfee7 -#define a_m_NOON 0xfee8 -#define a_s_HEH 0xfee9 -#define a_f_HEH 0xfeea -#define a_i_HEH 0xfeeb -#define a_m_HEH 0xfeec -#define a_s_WAW 0xfeed -#define a_f_WAW 0xfeee -#define a_s_ALEF_MAKSURA 0xfeef -#define a_f_ALEF_MAKSURA 0xfef0 -#define a_s_YEH 0xfef1 -#define a_f_YEH 0xfef2 -#define a_i_YEH 0xfef3 -#define a_m_YEH 0xfef4 -#define a_s_LAM_ALEF_MADDA_ABOVE 0xfef5 -#define a_f_LAM_ALEF_MADDA_ABOVE 0xfef6 -#define a_s_LAM_ALEF_HAMZA_ABOVE 0xfef7 -#define a_f_LAM_ALEF_HAMZA_ABOVE 0xfef8 -#define a_s_LAM_ALEF_HAMZA_BELOW 0xfef9 -#define a_f_LAM_ALEF_HAMZA_BELOW 0xfefa -#define a_s_LAM_ALEF 0xfefb -#define a_f_LAM_ALEF 0xfefc - -#define a_BYTE_ORDER_MARK 0xfeff - -static int A_is_a(int cur_c); -static int A_is_s(int cur_c); -static int A_is_f(int cur_c); -static int chg_c_a2s(int cur_c); -static int chg_c_a2i(int cur_c); -static int chg_c_a2m(int cur_c); -static int chg_c_a2f(int cur_c); -static int chg_c_i2m(int cur_c); -static int chg_c_f2m(int cur_c); -static int chg_c_laa2i(int hid_c); -static int chg_c_laa2f(int hid_c); -static int half_shape(int c); -static int A_firstc_laa(int c1, int c); -static int A_is_harakat(int c); -static int A_is_iso(int c); -static int A_is_formb(int c); -static int A_is_ok(int c); -static int A_is_valid(int c); -static int A_is_special(int c); - -// Returns True if c is an ISO-8859-6 shaped ARABIC letter (user entered). -static int A_is_a(int cur_c) -{ - switch (cur_c) { - case a_HAMZA: - case a_ALEF_MADDA: - case a_ALEF_HAMZA_ABOVE: - case a_WAW_HAMZA: - case a_ALEF_HAMZA_BELOW: - case a_YEH_HAMZA: - case a_ALEF: - case a_BEH: - case a_TEH_MARBUTA: - case a_TEH: - case a_THEH: - case a_JEEM: - case a_HAH: - case a_KHAH: - case a_DAL: - case a_THAL: - case a_REH: - case a_ZAIN: - case a_SEEN: - case a_SHEEN: - case a_SAD: - case a_DAD: - case a_TAH: - case a_ZAH: - case a_AIN: - case a_GHAIN: - case a_TATWEEL: - case a_FEH: - case a_QAF: - case a_KAF: - case a_LAM: - case a_MEEM: - case a_NOON: - case a_HEH: - case a_WAW: - case a_ALEF_MAKSURA: - case a_YEH: - return TRUE; - } - - return FALSE; -} - -// Returns True if c is an Isolated Form-B ARABIC letter -static int A_is_s(int cur_c) -{ - switch (cur_c) { - case a_s_HAMZA: - case a_s_ALEF_MADDA: - case a_s_ALEF_HAMZA_ABOVE: - case a_s_WAW_HAMZA: - case a_s_ALEF_HAMZA_BELOW: - case a_s_YEH_HAMZA: - case a_s_ALEF: - case a_s_BEH: - case a_s_TEH_MARBUTA: - case a_s_TEH: - case a_s_THEH: - case a_s_JEEM: - case a_s_HAH: - case a_s_KHAH: - case a_s_DAL: - case a_s_THAL: - case a_s_REH: - case a_s_ZAIN: - case a_s_SEEN: - case a_s_SHEEN: - case a_s_SAD: - case a_s_DAD: - case a_s_TAH: - case a_s_ZAH: - case a_s_AIN: - case a_s_GHAIN: - case a_s_FEH: - case a_s_QAF: - case a_s_KAF: - case a_s_LAM: - case a_s_MEEM: - case a_s_NOON: - case a_s_HEH: - case a_s_WAW: - case a_s_ALEF_MAKSURA: - case a_s_YEH: - return TRUE; - } - - return FALSE; -} - -// Returns True if c is a Final shape of an ARABIC letter -static int A_is_f(int cur_c) -{ - switch (cur_c) { - case a_f_ALEF_MADDA: - case a_f_ALEF_HAMZA_ABOVE: - case a_f_WAW_HAMZA: - case a_f_ALEF_HAMZA_BELOW: - case a_f_YEH_HAMZA: - case a_f_ALEF: - case a_f_BEH: - case a_f_TEH_MARBUTA: - case a_f_TEH: - case a_f_THEH: - case a_f_JEEM: - case a_f_HAH: - case a_f_KHAH: - case a_f_DAL: - case a_f_THAL: - case a_f_REH: - case a_f_ZAIN: - case a_f_SEEN: - case a_f_SHEEN: - case a_f_SAD: - case a_f_DAD: - case a_f_TAH: - case a_f_ZAH: - case a_f_AIN: - case a_f_GHAIN: - case a_f_FEH: - case a_f_QAF: - case a_f_KAF: - case a_f_LAM: - case a_f_MEEM: - case a_f_NOON: - case a_f_HEH: - case a_f_WAW: - case a_f_ALEF_MAKSURA: - case a_f_YEH: - case a_f_LAM_ALEF_MADDA_ABOVE: - case a_f_LAM_ALEF_HAMZA_ABOVE: - case a_f_LAM_ALEF_HAMZA_BELOW: - case a_f_LAM_ALEF: - return TRUE; - } - return FALSE; -} - -// Change shape - from ISO-8859-6/Isolated to Form-B Isolated -static int chg_c_a2s(int cur_c) -{ - int tempc; - - switch (cur_c) { - case a_HAMZA: - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: - tempc = a_s_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_s_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: - tempc = a_s_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_s_ALEF_HAMZA_BELOW; - break; - - case a_YEH_HAMZA: - tempc = a_s_YEH_HAMZA; - break; - - case a_ALEF: - tempc = a_s_ALEF; - break; - - case a_TEH_MARBUTA: - tempc = a_s_TEH_MARBUTA; - break; - - case a_DAL: - tempc = a_s_DAL; - break; - - case a_THAL: - tempc = a_s_THAL; - break; - - case a_REH: - tempc = a_s_REH; - break; - - case a_ZAIN: - tempc = a_s_ZAIN; - break; - - case a_TATWEEL: // exceptions - tempc = cur_c; - break; - - case a_WAW: - tempc = a_s_WAW; - break; - - case a_ALEF_MAKSURA: - tempc = a_s_ALEF_MAKSURA; - break; - - case a_BEH: - tempc = a_s_BEH; - break; - - case a_TEH: - tempc = a_s_TEH; - break; - - case a_THEH: - tempc = a_s_THEH; - break; - - case a_JEEM: - tempc = a_s_JEEM; - break; - - case a_HAH: - tempc = a_s_HAH; - break; - - case a_KHAH: - tempc = a_s_KHAH; - break; - - case a_SEEN: - tempc = a_s_SEEN; - break; - - case a_SHEEN: - tempc = a_s_SHEEN; - break; - - case a_SAD: - tempc = a_s_SAD; - break; - - case a_DAD: - tempc = a_s_DAD; - break; - - case a_TAH: - tempc = a_s_TAH; - break; - - case a_ZAH: - tempc = a_s_ZAH; - break; - - case a_AIN: - tempc = a_s_AIN; - break; - - case a_GHAIN: - tempc = a_s_GHAIN; - break; - - case a_FEH: - tempc = a_s_FEH; - break; - - case a_QAF: - tempc = a_s_QAF; - break; - - case a_KAF: - tempc = a_s_KAF; - break; - - case a_LAM: - tempc = a_s_LAM; - break; - - case a_MEEM: - tempc = a_s_MEEM; - break; - - case a_NOON: - tempc = a_s_NOON; - break; - - case a_HEH: - tempc = a_s_HEH; - break; - - case a_YEH: - tempc = a_s_YEH; - break; - - default: - tempc = 0; - } - - return tempc; -} - -// Change shape - from ISO-8859-6/Isolated to Initial -static int chg_c_a2i(int cur_c) -{ - int tempc; - - switch (cur_c) { - case a_YEH_HAMZA: - tempc = a_i_YEH_HAMZA; - break; - - case a_HAMZA: // exceptions - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: // exceptions - tempc = a_s_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: // exceptions - tempc = a_s_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: // exceptions - tempc = a_s_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: // exceptions - tempc = a_s_ALEF_HAMZA_BELOW; - break; - - case a_ALEF: // exceptions - tempc = a_s_ALEF; - break; - - case a_TEH_MARBUTA: // exceptions - tempc = a_s_TEH_MARBUTA; - break; - - case a_DAL: // exceptions - tempc = a_s_DAL; - break; - - case a_THAL: // exceptions - tempc = a_s_THAL; - break; - - case a_REH: // exceptions - tempc = a_s_REH; - break; - - case a_ZAIN: // exceptions - tempc = a_s_ZAIN; - break; - - case a_TATWEEL: // exceptions - tempc = cur_c; - break; - - case a_WAW: // exceptions - tempc = a_s_WAW; - break; - - case a_ALEF_MAKSURA: // exceptions - tempc = a_s_ALEF_MAKSURA; - break; - - case a_BEH: - tempc = a_i_BEH; - break; - - case a_TEH: - tempc = a_i_TEH; - break; - - case a_THEH: - tempc = a_i_THEH; - break; - - case a_JEEM: - tempc = a_i_JEEM; - break; - - case a_HAH: - tempc = a_i_HAH; - break; - - case a_KHAH: - tempc = a_i_KHAH; - break; - - case a_SEEN: - tempc = a_i_SEEN; - break; - - case a_SHEEN: - tempc = a_i_SHEEN; - break; - - case a_SAD: - tempc = a_i_SAD; - break; - - case a_DAD: - tempc = a_i_DAD; - break; - - case a_TAH: - tempc = a_i_TAH; - break; - - case a_ZAH: - tempc = a_i_ZAH; - break; - - case a_AIN: - tempc = a_i_AIN; - break; - - case a_GHAIN: - tempc = a_i_GHAIN; - break; - - case a_FEH: - tempc = a_i_FEH; - break; - - case a_QAF: - tempc = a_i_QAF; - break; - - case a_KAF: - tempc = a_i_KAF; - break; - - case a_LAM: - tempc = a_i_LAM; - break; - - case a_MEEM: - tempc = a_i_MEEM; - break; - - case a_NOON: - tempc = a_i_NOON; - break; - - case a_HEH: - tempc = a_i_HEH; - break; - - case a_YEH: - tempc = a_i_YEH; - break; - - default: - tempc = 0; - } - - return tempc; -} - -// Change shape - from ISO-8859-6/Isolated to Medial -static int chg_c_a2m(int cur_c) -{ - int tempc; - - switch (cur_c) { - case a_HAMZA: // exception - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: // exception - tempc = a_f_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: // exception - tempc = a_f_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: // exception - tempc = a_f_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: // exception - tempc = a_f_ALEF_HAMZA_BELOW; - break; - - case a_YEH_HAMZA: - tempc = a_m_YEH_HAMZA; - break; - - case a_ALEF: // exception - tempc = a_f_ALEF; - break; - - case a_BEH: - tempc = a_m_BEH; - break; - - case a_TEH_MARBUTA: // exception - tempc = a_f_TEH_MARBUTA; - break; - - case a_TEH: - tempc = a_m_TEH; - break; - - case a_THEH: - tempc = a_m_THEH; - break; - - case a_JEEM: - tempc = a_m_JEEM; - break; - - case a_HAH: - tempc = a_m_HAH; - break; - - case a_KHAH: - tempc = a_m_KHAH; - break; - - case a_DAL: // exception - tempc = a_f_DAL; - break; - - case a_THAL: // exception - tempc = a_f_THAL; - break; - - case a_REH: // exception - tempc = a_f_REH; - break; - - case a_ZAIN: // exception - tempc = a_f_ZAIN; - break; - - case a_SEEN: - tempc = a_m_SEEN; - break; - - case a_SHEEN: - tempc = a_m_SHEEN; - break; - - case a_SAD: - tempc = a_m_SAD; - break; - - case a_DAD: - tempc = a_m_DAD; - break; - - case a_TAH: - tempc = a_m_TAH; - break; - - case a_ZAH: - tempc = a_m_ZAH; - break; - - case a_AIN: - tempc = a_m_AIN; - break; - - case a_GHAIN: - tempc = a_m_GHAIN; - break; - - case a_TATWEEL: // exception - tempc = cur_c; - break; - - case a_FEH: - tempc = a_m_FEH; - break; - - case a_QAF: - tempc = a_m_QAF; - break; - - case a_KAF: - tempc = a_m_KAF; - break; - - case a_LAM: - tempc = a_m_LAM; - break; - - case a_MEEM: - tempc = a_m_MEEM; - break; - - case a_NOON: - tempc = a_m_NOON; - break; - - case a_HEH: - tempc = a_m_HEH; - break; - - case a_WAW: // exception - tempc = a_f_WAW; - break; - - case a_ALEF_MAKSURA: // exception - tempc = a_f_ALEF_MAKSURA; - break; - - case a_YEH: - tempc = a_m_YEH; - break; - - default: - tempc = 0; - } - - return tempc; -} - -// Change shape - from ISO-8859-6/Isolated to final -static int chg_c_a2f(int cur_c) -{ - int tempc; - - // NOTE: these encodings need to be accounted for - // - // a_f_ALEF_MADDA; - // a_f_ALEF_HAMZA_ABOVE; - // a_f_ALEF_HAMZA_BELOW; - // a_f_LAM_ALEF_MADDA_ABOVE; - // a_f_LAM_ALEF_HAMZA_ABOVE; - // a_f_LAM_ALEF_HAMZA_BELOW; - - switch (cur_c) { - case a_HAMZA: // exception - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: - tempc = a_f_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_f_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: - tempc = a_f_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_f_ALEF_HAMZA_BELOW; - break; - - case a_YEH_HAMZA: - tempc = a_f_YEH_HAMZA; - break; - - case a_ALEF: - tempc = a_f_ALEF; - break; - - case a_BEH: - tempc = a_f_BEH; - break; - - case a_TEH_MARBUTA: - tempc = a_f_TEH_MARBUTA; - break; - - case a_TEH: - tempc = a_f_TEH; - break; - - case a_THEH: - tempc = a_f_THEH; - break; - - case a_JEEM: - tempc = a_f_JEEM; - break; - - case a_HAH: - tempc = a_f_HAH; - break; - - case a_KHAH: - tempc = a_f_KHAH; - break; - - case a_DAL: - tempc = a_f_DAL; - break; - - case a_THAL: - tempc = a_f_THAL; - break; - - case a_REH: - tempc = a_f_REH; - break; - - case a_ZAIN: - tempc = a_f_ZAIN; - break; - - case a_SEEN: - tempc = a_f_SEEN; - break; - - case a_SHEEN: - tempc = a_f_SHEEN; - break; - - case a_SAD: - tempc = a_f_SAD; - break; - - case a_DAD: - tempc = a_f_DAD; - break; - - case a_TAH: - tempc = a_f_TAH; - break; - - case a_ZAH: - tempc = a_f_ZAH; - break; - - case a_AIN: - tempc = a_f_AIN; - break; - - case a_GHAIN: - tempc = a_f_GHAIN; - break; - - case a_TATWEEL: // exception - tempc = cur_c; - break; - - case a_FEH: - tempc = a_f_FEH; - break; - - case a_QAF: - tempc = a_f_QAF; - break; - - case a_KAF: - tempc = a_f_KAF; - break; - - case a_LAM: - tempc = a_f_LAM; - break; - - case a_MEEM: - tempc = a_f_MEEM; - break; - - case a_NOON: - tempc = a_f_NOON; - break; - - case a_HEH: - tempc = a_f_HEH; - break; - - case a_WAW: - tempc = a_f_WAW; - break; - - case a_ALEF_MAKSURA: - tempc = a_f_ALEF_MAKSURA; - break; - - case a_YEH: - tempc = a_f_YEH; - break; - - default: - tempc = 0; - } - - return tempc; -} - -// Change shape - from Initial to Medial -static int chg_c_i2m(int cur_c) -{ - int tempc; - - switch (cur_c) { - case a_i_YEH_HAMZA: - tempc = a_m_YEH_HAMZA; - break; - - case a_i_BEH: - tempc = a_m_BEH; - break; - - case a_i_TEH: - tempc = a_m_TEH; - break; - - case a_i_THEH: - tempc = a_m_THEH; - break; - - case a_i_JEEM: - tempc = a_m_JEEM; - break; - - case a_i_HAH: - tempc = a_m_HAH; - break; - - case a_i_KHAH: - tempc = a_m_KHAH; - break; - - case a_i_SEEN: - tempc = a_m_SEEN; - break; - - case a_i_SHEEN: - tempc = a_m_SHEEN; - break; - - case a_i_SAD: - tempc = a_m_SAD; - break; - - case a_i_DAD: - tempc = a_m_DAD; - break; - - case a_i_TAH: - tempc = a_m_TAH; - break; - - case a_i_ZAH: - tempc = a_m_ZAH; - break; - - case a_i_AIN: - tempc = a_m_AIN; - break; - - case a_i_GHAIN: - tempc = a_m_GHAIN; - break; - - case a_i_FEH: - tempc = a_m_FEH; - break; - - case a_i_QAF: - tempc = a_m_QAF; - break; - - case a_i_KAF: - tempc = a_m_KAF; - break; - - case a_i_LAM: - tempc = a_m_LAM; - break; - - case a_i_MEEM: - tempc = a_m_MEEM; - break; - - case a_i_NOON: - tempc = a_m_NOON; - break; - - case a_i_HEH: - tempc = a_m_HEH; - break; - - case a_i_YEH: - tempc = a_m_YEH; - break; - - default: - tempc = 0; - } - - return tempc; -} - -// Change shape - from Final to Medial -static int chg_c_f2m(int cur_c) -{ - int tempc; - - switch (cur_c) { - // NOTE: these encodings are multi-positional, no ? - // case a_f_ALEF_MADDA: - // case a_f_ALEF_HAMZA_ABOVE: - // case a_f_ALEF_HAMZA_BELOW: - case a_f_YEH_HAMZA: - tempc = a_m_YEH_HAMZA; - break; - - case a_f_WAW_HAMZA: // exceptions - case a_f_ALEF: - case a_f_TEH_MARBUTA: - case a_f_DAL: - case a_f_THAL: - case a_f_REH: - case a_f_ZAIN: - case a_f_WAW: - case a_f_ALEF_MAKSURA: - tempc = cur_c; - break; - - case a_f_BEH: - tempc = a_m_BEH; - break; - - case a_f_TEH: - tempc = a_m_TEH; - break; - - case a_f_THEH: - tempc = a_m_THEH; - break; - - case a_f_JEEM: - tempc = a_m_JEEM; - break; - - case a_f_HAH: - tempc = a_m_HAH; - break; - - case a_f_KHAH: - tempc = a_m_KHAH; - break; - - case a_f_SEEN: - tempc = a_m_SEEN; - break; - - case a_f_SHEEN: - tempc = a_m_SHEEN; - break; - - case a_f_SAD: - tempc = a_m_SAD; - break; - - case a_f_DAD: - tempc = a_m_DAD; - break; - - case a_f_TAH: - tempc = a_m_TAH; - break; - - case a_f_ZAH: - tempc = a_m_ZAH; - break; - - case a_f_AIN: - tempc = a_m_AIN; - break; - - case a_f_GHAIN: - tempc = a_m_GHAIN; - break; - - case a_f_FEH: - tempc = a_m_FEH; - break; - - case a_f_QAF: - tempc = a_m_QAF; - break; - - case a_f_KAF: - tempc = a_m_KAF; - break; - - case a_f_LAM: - tempc = a_m_LAM; - break; - - case a_f_MEEM: - tempc = a_m_MEEM; - break; - - case a_f_NOON: - tempc = a_m_NOON; - break; - - case a_f_HEH: - tempc = a_m_HEH; - break; - - case a_f_YEH: - tempc = a_m_YEH; - break; - - /* NOTE: these encodings are multi-positional, no ? - case a_f_LAM_ALEF_MADDA_ABOVE: - case a_f_LAM_ALEF_HAMZA_ABOVE: - case a_f_LAM_ALEF_HAMZA_BELOW: - case a_f_LAM_ALEF: - */ - default: - tempc = 0; - } - - return tempc; -} - -/* - * Change shape - from Combination (2 char) to an Isolated - */ -static int chg_c_laa2i(int hid_c) -{ - int tempc; - - switch (hid_c) { - case a_ALEF_MADDA: - tempc = a_s_LAM_ALEF_MADDA_ABOVE; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_s_LAM_ALEF_HAMZA_ABOVE; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_s_LAM_ALEF_HAMZA_BELOW; - break; - - case a_ALEF: - tempc = a_s_LAM_ALEF; - break; - - default: - tempc = 0; - } - - return tempc; -} - -/* - * Change shape - from Combination-Isolated to Final - */ -static int chg_c_laa2f(int hid_c) -{ - int tempc; - - switch (hid_c) { - case a_ALEF_MADDA: - tempc = a_f_LAM_ALEF_MADDA_ABOVE; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_f_LAM_ALEF_HAMZA_ABOVE; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_f_LAM_ALEF_HAMZA_BELOW; - break; - - case a_ALEF: - tempc = a_f_LAM_ALEF; - break; - - default: - tempc = 0; - } - - return tempc; -} - -/* - * Do "half-shaping" on character "c". Return zero if no shaping. - */ -static int half_shape(int c) -{ - if (A_is_a(c)) { - return chg_c_a2i(c); - } - - if (A_is_valid(c) && A_is_f(c)) { - return chg_c_f2m(c); - } - return 0; -} - -/* - * Do Arabic shaping on character "c". Returns the shaped character. - * out: "ccp" points to the first byte of the character to be shaped. - * in/out: "c1p" points to the first composing char for "c". - * in: "prev_c" is the previous character (not shaped) - * in: "prev_c1" is the first composing char for the previous char - * (not shaped) - * in: "next_c" is the next character (not shaped). - */ -int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, - int next_c) -{ - int curr_c; - int shape_c; - int curr_laa; - int prev_laa; - - /* Deal only with Arabic character, pass back all others */ - if (!A_is_ok(c)) { - return c; - } - - /* half-shape current and previous character */ - shape_c = half_shape(prev_c); - - /* Save away current character */ - curr_c = c; - - curr_laa = A_firstc_laa(c, *c1p); - prev_laa = A_firstc_laa(prev_c, prev_c1); - - if (curr_laa) { - if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) && - !prev_laa) { - curr_c = chg_c_laa2f(curr_laa); - } else { - curr_c = chg_c_laa2i(curr_laa); - } - - /* Remove the composing character */ - *c1p = 0; - } else if (!A_is_valid(prev_c) && A_is_valid(next_c)) { - curr_c = chg_c_a2i(c); - } else if (!shape_c || A_is_f(shape_c) || A_is_s(shape_c) || prev_laa) { - curr_c = A_is_valid(next_c) ? chg_c_a2i(c) : chg_c_a2s(c); - } else if (A_is_valid(next_c)) { - curr_c = A_is_iso(c) ? chg_c_a2m(c) : chg_c_i2m(c); - } else if (A_is_valid(prev_c)) { - curr_c = chg_c_a2f(c); - } else { - curr_c = chg_c_a2s(c); - } - - /* Sanity check -- curr_c should, in the future, never be 0. - * We should, in the future, insert a fatal error here. */ - if (curr_c == NUL) { - curr_c = c; - } - - if ((curr_c != c) && (ccp != NULL)) { - char_u buf[MB_MAXBYTES + 1]; - - /* Update the first byte of the character. */ - (*mb_char2bytes)(curr_c, buf); - *ccp = buf[0]; - } - - /* Return the shaped character */ - return curr_c; -} - -/// Check whether we are dealing with Arabic combining characters. -/// Note: these are NOT really composing characters! -/// -/// @param one First character. -/// @param two Character just after "one". -int arabic_combine(int one, int two) -{ - if (one == a_LAM) { - return arabic_maycombine(two); - } - return FALSE; -} - -/// Check whether we are dealing with a character that could be regarded as an -/// Arabic combining character, need to check the character before this. -int arabic_maycombine(int two) -{ - if (p_arshape && !p_tbidi) { - return two == a_ALEF_MADDA - || two == a_ALEF_HAMZA_ABOVE - || two == a_ALEF_HAMZA_BELOW - || two == a_ALEF; - } - return FALSE; -} - -/* - * A_firstc_laa returns first character of LAA combination if it exists - * in: "c" base character - * in: "c1" first composing character - */ -static int A_firstc_laa(int c, int c1) -{ - if ((c1 != NUL) && (c == a_LAM) && !A_is_harakat(c1)) { - return c1; - } - return 0; -} - -/* - * A_is_harakat returns TRUE if 'c' is an Arabic Harakat character - * (harakat/tanween) - */ -static int A_is_harakat(int c) -{ - return c >= a_FATHATAN && c <= a_SUKUN; -} - -/* - * A_is_iso returns TRUE if 'c' is an Arabic ISO-8859-6 character - * (alphabet/number/punctuation) - */ -static int A_is_iso(int c) -{ - return (c >= a_HAMZA && c <= a_GHAIN) || - (c >= a_TATWEEL && c <= a_HAMZA_BELOW) || - c == a_MINI_ALEF; -} - -/* - * A_is_formb returns TRUE if 'c' is an Arabic 10646-1 FormB character - * (alphabet/number/punctuation) - */ -static int A_is_formb(int c) -{ - return (c >= a_s_FATHATAN && c <= a_s_DAMMATAN) || - c == a_s_KASRATAN || - (c >= a_s_FATHA && c <= a_f_LAM_ALEF) || - c == a_BYTE_ORDER_MARK; -} - -/* - * A_is_ok returns TRUE if 'c' is an Arabic 10646 (8859-6 or Form-B) - */ -static int A_is_ok(int c) -{ - return A_is_iso(c) || A_is_formb(c); -} - -/* - * A_is_valid returns TRUE if 'c' is an Arabic 10646 (8859-6 or Form-B) - * with some exceptions/exclusions - */ -static int A_is_valid(int c) -{ - return A_is_ok(c) && !A_is_special(c); -} - -/* - * A_is_special returns TRUE if 'c' is not a special Arabic character. - * Specials don't adhere to most of the rules. - */ -static int A_is_special(int c) -{ - return c == a_HAMZA || c == a_s_HAMZA; -} diff --git a/src/arabic.h b/src/arabic.h deleted file mode 100644 index 5129b5a56a..0000000000 --- a/src/arabic.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef NEOVIM_ARABIC_H -#define NEOVIM_ARABIC_H - -/// Whether c belongs to the range of Arabic characters that might be shaped. -static inline int arabic_char(int c) -{ - // return c >= a_HAMZA && c <= a_MINI_ALEF; - return c >= 0x0621 && c <= 0x0670; -} - -int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, - int next_c); -int arabic_combine(int one, int two); -int arabic_maycombine(int two); - -#endif // NEOVIM_ARABIC_H diff --git a/src/ascii.h b/src/ascii.h deleted file mode 100644 index cf488c56a7..0000000000 --- a/src/ascii.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - */ - -#ifndef NEOVIM_ASCII_H -#define NEOVIM_ASCII_H - -/* - * Definitions of various common control characters. - * For EBCDIC we have to use different values. - */ - - -/* IF_EB(ASCII_constant, EBCDIC_constant) */ -#define IF_EB(a, b) a - -#define CharOrd(x) ((x) < 'a' ? (x) - 'A' : (x) - 'a') -#define CharOrdLow(x) ((x) - 'a') -#define CharOrdUp(x) ((x) - 'A') -#define ROT13(c, a) (((((c) - (a)) + 13) % 26) + (a)) - -#define NUL '\000' -#define BELL '\007' -#define BS '\010' -#define TAB '\011' -#define NL '\012' -#define NL_STR (char_u *)"\012" -#define FF '\014' -#define CAR '\015' /* CR is used by Mac OS X */ -#define ESC '\033' -#define ESC_STR (char_u *)"\033" -#define ESC_STR_nc "\033" -#define DEL 0x7f -#define DEL_STR (char_u *)"\177" -#define CSI 0x9b /* Control Sequence Introducer */ -#define CSI_STR "\233" -#define DCS 0x90 /* Device Control String */ -#define STERM 0x9c /* String Terminator */ - -#define POUND 0xA3 - -#define Ctrl_chr(x) (TOUPPER_ASC(x) ^ 0x40) /* '?' -> DEL, '@' -> ^@, etc. */ -#define Meta(x) ((x) | 0x80) - -#define CTRL_F_STR "\006" -#define CTRL_H_STR "\010" -#define CTRL_V_STR "\026" - -#define Ctrl_AT 0 /* @ */ -#define Ctrl_A 1 -#define Ctrl_B 2 -#define Ctrl_C 3 -#define Ctrl_D 4 -#define Ctrl_E 5 -#define Ctrl_F 6 -#define Ctrl_G 7 -#define Ctrl_H 8 -#define Ctrl_I 9 -#define Ctrl_J 10 -#define Ctrl_K 11 -#define Ctrl_L 12 -#define Ctrl_M 13 -#define Ctrl_N 14 -#define Ctrl_O 15 -#define Ctrl_P 16 -#define Ctrl_Q 17 -#define Ctrl_R 18 -#define Ctrl_S 19 -#define Ctrl_T 20 -#define Ctrl_U 21 -#define Ctrl_V 22 -#define Ctrl_W 23 -#define Ctrl_X 24 -#define Ctrl_Y 25 -#define Ctrl_Z 26 -/* CTRL- [ Left Square Bracket == ESC*/ -#define Ctrl_BSL 28 /* \ BackSLash */ -#define Ctrl_RSB 29 /* ] Right Square Bracket */ -#define Ctrl_HAT 30 /* ^ */ -#define Ctrl__ 31 - - -/* - * Character that separates dir names in a path. - */ -#ifdef BACKSLASH_IN_FILENAME -# define PATHSEP psepc -# define PATHSEPSTR pseps -#else -# define PATHSEP '/' -# define PATHSEPSTR "/" -#endif - -#endif /* NEOVIM_ASCII_H */ diff --git a/src/blowfish.c b/src/blowfish.c deleted file mode 100644 index 67712a2fbc..0000000000 --- a/src/blowfish.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - * - * Blowfish encryption for Vim; in Blowfish cipher feedback mode. - * Contributed by Mohsin Ahmed, http://www.cs.albany.edu/~mosh - * Based on http://www.schneier.com/blowfish.html by Bruce Schneier. - */ - -#include - -#include "vim.h" -#include "blowfish.h" -#include "message.h" -#include "sha256.h" - -#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0])) - -#define BF_BLOCK 8 -#define BF_BLOCK_MASK 7 -#define BF_CFB_LEN (8 * (BF_BLOCK)) - -typedef union { - uint32_t ul[2]; - char_u uc[8]; -} block8; - - -static void bf_e_block(uint32_t *p_xl, uint32_t *p_xr); -static void bf_e_cblock(char_u *block); -static int bf_check_tables(uint32_t a_ipa[18], uint32_t a_sbi[4][256], - uint32_t val); -static int bf_self_test(void); - -/* Blowfish code */ -static uint32_t pax[18]; -static uint32_t ipa[18] = { - 0x243f6a88u, 0x85a308d3u, 0x13198a2eu, - 0x03707344u, 0xa4093822u, 0x299f31d0u, - 0x082efa98u, 0xec4e6c89u, 0x452821e6u, - 0x38d01377u, 0xbe5466cfu, 0x34e90c6cu, - 0xc0ac29b7u, 0xc97c50ddu, 0x3f84d5b5u, - 0xb5470917u, 0x9216d5d9u, 0x8979fb1bu -}; - -static uint32_t sbx[4][256]; -static uint32_t sbi[4][256] = { - {0xd1310ba6u, 0x98dfb5acu, 0x2ffd72dbu, 0xd01adfb7u, - 0xb8e1afedu, 0x6a267e96u, 0xba7c9045u, 0xf12c7f99u, - 0x24a19947u, 0xb3916cf7u, 0x0801f2e2u, 0x858efc16u, - 0x636920d8u, 0x71574e69u, 0xa458fea3u, 0xf4933d7eu, - 0x0d95748fu, 0x728eb658u, 0x718bcd58u, 0x82154aeeu, - 0x7b54a41du, 0xc25a59b5u, 0x9c30d539u, 0x2af26013u, - 0xc5d1b023u, 0x286085f0u, 0xca417918u, 0xb8db38efu, - 0x8e79dcb0u, 0x603a180eu, 0x6c9e0e8bu, 0xb01e8a3eu, - 0xd71577c1u, 0xbd314b27u, 0x78af2fdau, 0x55605c60u, - 0xe65525f3u, 0xaa55ab94u, 0x57489862u, 0x63e81440u, - 0x55ca396au, 0x2aab10b6u, 0xb4cc5c34u, 0x1141e8ceu, - 0xa15486afu, 0x7c72e993u, 0xb3ee1411u, 0x636fbc2au, - 0x2ba9c55du, 0x741831f6u, 0xce5c3e16u, 0x9b87931eu, - 0xafd6ba33u, 0x6c24cf5cu, 0x7a325381u, 0x28958677u, - 0x3b8f4898u, 0x6b4bb9afu, 0xc4bfe81bu, 0x66282193u, - 0x61d809ccu, 0xfb21a991u, 0x487cac60u, 0x5dec8032u, - 0xef845d5du, 0xe98575b1u, 0xdc262302u, 0xeb651b88u, - 0x23893e81u, 0xd396acc5u, 0x0f6d6ff3u, 0x83f44239u, - 0x2e0b4482u, 0xa4842004u, 0x69c8f04au, 0x9e1f9b5eu, - 0x21c66842u, 0xf6e96c9au, 0x670c9c61u, 0xabd388f0u, - 0x6a51a0d2u, 0xd8542f68u, 0x960fa728u, 0xab5133a3u, - 0x6eef0b6cu, 0x137a3be4u, 0xba3bf050u, 0x7efb2a98u, - 0xa1f1651du, 0x39af0176u, 0x66ca593eu, 0x82430e88u, - 0x8cee8619u, 0x456f9fb4u, 0x7d84a5c3u, 0x3b8b5ebeu, - 0xe06f75d8u, 0x85c12073u, 0x401a449fu, 0x56c16aa6u, - 0x4ed3aa62u, 0x363f7706u, 0x1bfedf72u, 0x429b023du, - 0x37d0d724u, 0xd00a1248u, 0xdb0fead3u, 0x49f1c09bu, - 0x075372c9u, 0x80991b7bu, 0x25d479d8u, 0xf6e8def7u, - 0xe3fe501au, 0xb6794c3bu, 0x976ce0bdu, 0x04c006bau, - 0xc1a94fb6u, 0x409f60c4u, 0x5e5c9ec2u, 0x196a2463u, - 0x68fb6fafu, 0x3e6c53b5u, 0x1339b2ebu, 0x3b52ec6fu, - 0x6dfc511fu, 0x9b30952cu, 0xcc814544u, 0xaf5ebd09u, - 0xbee3d004u, 0xde334afdu, 0x660f2807u, 0x192e4bb3u, - 0xc0cba857u, 0x45c8740fu, 0xd20b5f39u, 0xb9d3fbdbu, - 0x5579c0bdu, 0x1a60320au, 0xd6a100c6u, 0x402c7279u, - 0x679f25feu, 0xfb1fa3ccu, 0x8ea5e9f8u, 0xdb3222f8u, - 0x3c7516dfu, 0xfd616b15u, 0x2f501ec8u, 0xad0552abu, - 0x323db5fau, 0xfd238760u, 0x53317b48u, 0x3e00df82u, - 0x9e5c57bbu, 0xca6f8ca0u, 0x1a87562eu, 0xdf1769dbu, - 0xd542a8f6u, 0x287effc3u, 0xac6732c6u, 0x8c4f5573u, - 0x695b27b0u, 0xbbca58c8u, 0xe1ffa35du, 0xb8f011a0u, - 0x10fa3d98u, 0xfd2183b8u, 0x4afcb56cu, 0x2dd1d35bu, - 0x9a53e479u, 0xb6f84565u, 0xd28e49bcu, 0x4bfb9790u, - 0xe1ddf2dau, 0xa4cb7e33u, 0x62fb1341u, 0xcee4c6e8u, - 0xef20cadau, 0x36774c01u, 0xd07e9efeu, 0x2bf11fb4u, - 0x95dbda4du, 0xae909198u, 0xeaad8e71u, 0x6b93d5a0u, - 0xd08ed1d0u, 0xafc725e0u, 0x8e3c5b2fu, 0x8e7594b7u, - 0x8ff6e2fbu, 0xf2122b64u, 0x8888b812u, 0x900df01cu, - 0x4fad5ea0u, 0x688fc31cu, 0xd1cff191u, 0xb3a8c1adu, - 0x2f2f2218u, 0xbe0e1777u, 0xea752dfeu, 0x8b021fa1u, - 0xe5a0cc0fu, 0xb56f74e8u, 0x18acf3d6u, 0xce89e299u, - 0xb4a84fe0u, 0xfd13e0b7u, 0x7cc43b81u, 0xd2ada8d9u, - 0x165fa266u, 0x80957705u, 0x93cc7314u, 0x211a1477u, - 0xe6ad2065u, 0x77b5fa86u, 0xc75442f5u, 0xfb9d35cfu, - 0xebcdaf0cu, 0x7b3e89a0u, 0xd6411bd3u, 0xae1e7e49u, - 0x00250e2du, 0x2071b35eu, 0x226800bbu, 0x57b8e0afu, - 0x2464369bu, 0xf009b91eu, 0x5563911du, 0x59dfa6aau, - 0x78c14389u, 0xd95a537fu, 0x207d5ba2u, 0x02e5b9c5u, - 0x83260376u, 0x6295cfa9u, 0x11c81968u, 0x4e734a41u, - 0xb3472dcau, 0x7b14a94au, 0x1b510052u, 0x9a532915u, - 0xd60f573fu, 0xbc9bc6e4u, 0x2b60a476u, 0x81e67400u, - 0x08ba6fb5u, 0x571be91fu, 0xf296ec6bu, 0x2a0dd915u, - 0xb6636521u, 0xe7b9f9b6u, 0xff34052eu, 0xc5855664u, - 0x53b02d5du, 0xa99f8fa1u, 0x08ba4799u, 0x6e85076au}, - {0x4b7a70e9u, 0xb5b32944u, 0xdb75092eu, 0xc4192623u, - 0xad6ea6b0u, 0x49a7df7du, 0x9cee60b8u, 0x8fedb266u, - 0xecaa8c71u, 0x699a17ffu, 0x5664526cu, 0xc2b19ee1u, - 0x193602a5u, 0x75094c29u, 0xa0591340u, 0xe4183a3eu, - 0x3f54989au, 0x5b429d65u, 0x6b8fe4d6u, 0x99f73fd6u, - 0xa1d29c07u, 0xefe830f5u, 0x4d2d38e6u, 0xf0255dc1u, - 0x4cdd2086u, 0x8470eb26u, 0x6382e9c6u, 0x021ecc5eu, - 0x09686b3fu, 0x3ebaefc9u, 0x3c971814u, 0x6b6a70a1u, - 0x687f3584u, 0x52a0e286u, 0xb79c5305u, 0xaa500737u, - 0x3e07841cu, 0x7fdeae5cu, 0x8e7d44ecu, 0x5716f2b8u, - 0xb03ada37u, 0xf0500c0du, 0xf01c1f04u, 0x0200b3ffu, - 0xae0cf51au, 0x3cb574b2u, 0x25837a58u, 0xdc0921bdu, - 0xd19113f9u, 0x7ca92ff6u, 0x94324773u, 0x22f54701u, - 0x3ae5e581u, 0x37c2dadcu, 0xc8b57634u, 0x9af3dda7u, - 0xa9446146u, 0x0fd0030eu, 0xecc8c73eu, 0xa4751e41u, - 0xe238cd99u, 0x3bea0e2fu, 0x3280bba1u, 0x183eb331u, - 0x4e548b38u, 0x4f6db908u, 0x6f420d03u, 0xf60a04bfu, - 0x2cb81290u, 0x24977c79u, 0x5679b072u, 0xbcaf89afu, - 0xde9a771fu, 0xd9930810u, 0xb38bae12u, 0xdccf3f2eu, - 0x5512721fu, 0x2e6b7124u, 0x501adde6u, 0x9f84cd87u, - 0x7a584718u, 0x7408da17u, 0xbc9f9abcu, 0xe94b7d8cu, - 0xec7aec3au, 0xdb851dfau, 0x63094366u, 0xc464c3d2u, - 0xef1c1847u, 0x3215d908u, 0xdd433b37u, 0x24c2ba16u, - 0x12a14d43u, 0x2a65c451u, 0x50940002u, 0x133ae4ddu, - 0x71dff89eu, 0x10314e55u, 0x81ac77d6u, 0x5f11199bu, - 0x043556f1u, 0xd7a3c76bu, 0x3c11183bu, 0x5924a509u, - 0xf28fe6edu, 0x97f1fbfau, 0x9ebabf2cu, 0x1e153c6eu, - 0x86e34570u, 0xeae96fb1u, 0x860e5e0au, 0x5a3e2ab3u, - 0x771fe71cu, 0x4e3d06fau, 0x2965dcb9u, 0x99e71d0fu, - 0x803e89d6u, 0x5266c825u, 0x2e4cc978u, 0x9c10b36au, - 0xc6150ebau, 0x94e2ea78u, 0xa5fc3c53u, 0x1e0a2df4u, - 0xf2f74ea7u, 0x361d2b3du, 0x1939260fu, 0x19c27960u, - 0x5223a708u, 0xf71312b6u, 0xebadfe6eu, 0xeac31f66u, - 0xe3bc4595u, 0xa67bc883u, 0xb17f37d1u, 0x018cff28u, - 0xc332ddefu, 0xbe6c5aa5u, 0x65582185u, 0x68ab9802u, - 0xeecea50fu, 0xdb2f953bu, 0x2aef7dadu, 0x5b6e2f84u, - 0x1521b628u, 0x29076170u, 0xecdd4775u, 0x619f1510u, - 0x13cca830u, 0xeb61bd96u, 0x0334fe1eu, 0xaa0363cfu, - 0xb5735c90u, 0x4c70a239u, 0xd59e9e0bu, 0xcbaade14u, - 0xeecc86bcu, 0x60622ca7u, 0x9cab5cabu, 0xb2f3846eu, - 0x648b1eafu, 0x19bdf0cau, 0xa02369b9u, 0x655abb50u, - 0x40685a32u, 0x3c2ab4b3u, 0x319ee9d5u, 0xc021b8f7u, - 0x9b540b19u, 0x875fa099u, 0x95f7997eu, 0x623d7da8u, - 0xf837889au, 0x97e32d77u, 0x11ed935fu, 0x16681281u, - 0x0e358829u, 0xc7e61fd6u, 0x96dedfa1u, 0x7858ba99u, - 0x57f584a5u, 0x1b227263u, 0x9b83c3ffu, 0x1ac24696u, - 0xcdb30aebu, 0x532e3054u, 0x8fd948e4u, 0x6dbc3128u, - 0x58ebf2efu, 0x34c6ffeau, 0xfe28ed61u, 0xee7c3c73u, - 0x5d4a14d9u, 0xe864b7e3u, 0x42105d14u, 0x203e13e0u, - 0x45eee2b6u, 0xa3aaabeau, 0xdb6c4f15u, 0xfacb4fd0u, - 0xc742f442u, 0xef6abbb5u, 0x654f3b1du, 0x41cd2105u, - 0xd81e799eu, 0x86854dc7u, 0xe44b476au, 0x3d816250u, - 0xcf62a1f2u, 0x5b8d2646u, 0xfc8883a0u, 0xc1c7b6a3u, - 0x7f1524c3u, 0x69cb7492u, 0x47848a0bu, 0x5692b285u, - 0x095bbf00u, 0xad19489du, 0x1462b174u, 0x23820e00u, - 0x58428d2au, 0x0c55f5eau, 0x1dadf43eu, 0x233f7061u, - 0x3372f092u, 0x8d937e41u, 0xd65fecf1u, 0x6c223bdbu, - 0x7cde3759u, 0xcbee7460u, 0x4085f2a7u, 0xce77326eu, - 0xa6078084u, 0x19f8509eu, 0xe8efd855u, 0x61d99735u, - 0xa969a7aau, 0xc50c06c2u, 0x5a04abfcu, 0x800bcadcu, - 0x9e447a2eu, 0xc3453484u, 0xfdd56705u, 0x0e1e9ec9u, - 0xdb73dbd3u, 0x105588cdu, 0x675fda79u, 0xe3674340u, - 0xc5c43465u, 0x713e38d8u, 0x3d28f89eu, 0xf16dff20u, - 0x153e21e7u, 0x8fb03d4au, 0xe6e39f2bu, 0xdb83adf7u}, - {0xe93d5a68u, 0x948140f7u, 0xf64c261cu, 0x94692934u, - 0x411520f7u, 0x7602d4f7u, 0xbcf46b2eu, 0xd4a20068u, - 0xd4082471u, 0x3320f46au, 0x43b7d4b7u, 0x500061afu, - 0x1e39f62eu, 0x97244546u, 0x14214f74u, 0xbf8b8840u, - 0x4d95fc1du, 0x96b591afu, 0x70f4ddd3u, 0x66a02f45u, - 0xbfbc09ecu, 0x03bd9785u, 0x7fac6dd0u, 0x31cb8504u, - 0x96eb27b3u, 0x55fd3941u, 0xda2547e6u, 0xabca0a9au, - 0x28507825u, 0x530429f4u, 0x0a2c86dau, 0xe9b66dfbu, - 0x68dc1462u, 0xd7486900u, 0x680ec0a4u, 0x27a18deeu, - 0x4f3ffea2u, 0xe887ad8cu, 0xb58ce006u, 0x7af4d6b6u, - 0xaace1e7cu, 0xd3375fecu, 0xce78a399u, 0x406b2a42u, - 0x20fe9e35u, 0xd9f385b9u, 0xee39d7abu, 0x3b124e8bu, - 0x1dc9faf7u, 0x4b6d1856u, 0x26a36631u, 0xeae397b2u, - 0x3a6efa74u, 0xdd5b4332u, 0x6841e7f7u, 0xca7820fbu, - 0xfb0af54eu, 0xd8feb397u, 0x454056acu, 0xba489527u, - 0x55533a3au, 0x20838d87u, 0xfe6ba9b7u, 0xd096954bu, - 0x55a867bcu, 0xa1159a58u, 0xcca92963u, 0x99e1db33u, - 0xa62a4a56u, 0x3f3125f9u, 0x5ef47e1cu, 0x9029317cu, - 0xfdf8e802u, 0x04272f70u, 0x80bb155cu, 0x05282ce3u, - 0x95c11548u, 0xe4c66d22u, 0x48c1133fu, 0xc70f86dcu, - 0x07f9c9eeu, 0x41041f0fu, 0x404779a4u, 0x5d886e17u, - 0x325f51ebu, 0xd59bc0d1u, 0xf2bcc18fu, 0x41113564u, - 0x257b7834u, 0x602a9c60u, 0xdff8e8a3u, 0x1f636c1bu, - 0x0e12b4c2u, 0x02e1329eu, 0xaf664fd1u, 0xcad18115u, - 0x6b2395e0u, 0x333e92e1u, 0x3b240b62u, 0xeebeb922u, - 0x85b2a20eu, 0xe6ba0d99u, 0xde720c8cu, 0x2da2f728u, - 0xd0127845u, 0x95b794fdu, 0x647d0862u, 0xe7ccf5f0u, - 0x5449a36fu, 0x877d48fau, 0xc39dfd27u, 0xf33e8d1eu, - 0x0a476341u, 0x992eff74u, 0x3a6f6eabu, 0xf4f8fd37u, - 0xa812dc60u, 0xa1ebddf8u, 0x991be14cu, 0xdb6e6b0du, - 0xc67b5510u, 0x6d672c37u, 0x2765d43bu, 0xdcd0e804u, - 0xf1290dc7u, 0xcc00ffa3u, 0xb5390f92u, 0x690fed0bu, - 0x667b9ffbu, 0xcedb7d9cu, 0xa091cf0bu, 0xd9155ea3u, - 0xbb132f88u, 0x515bad24u, 0x7b9479bfu, 0x763bd6ebu, - 0x37392eb3u, 0xcc115979u, 0x8026e297u, 0xf42e312du, - 0x6842ada7u, 0xc66a2b3bu, 0x12754cccu, 0x782ef11cu, - 0x6a124237u, 0xb79251e7u, 0x06a1bbe6u, 0x4bfb6350u, - 0x1a6b1018u, 0x11caedfau, 0x3d25bdd8u, 0xe2e1c3c9u, - 0x44421659u, 0x0a121386u, 0xd90cec6eu, 0xd5abea2au, - 0x64af674eu, 0xda86a85fu, 0xbebfe988u, 0x64e4c3feu, - 0x9dbc8057u, 0xf0f7c086u, 0x60787bf8u, 0x6003604du, - 0xd1fd8346u, 0xf6381fb0u, 0x7745ae04u, 0xd736fcccu, - 0x83426b33u, 0xf01eab71u, 0xb0804187u, 0x3c005e5fu, - 0x77a057beu, 0xbde8ae24u, 0x55464299u, 0xbf582e61u, - 0x4e58f48fu, 0xf2ddfda2u, 0xf474ef38u, 0x8789bdc2u, - 0x5366f9c3u, 0xc8b38e74u, 0xb475f255u, 0x46fcd9b9u, - 0x7aeb2661u, 0x8b1ddf84u, 0x846a0e79u, 0x915f95e2u, - 0x466e598eu, 0x20b45770u, 0x8cd55591u, 0xc902de4cu, - 0xb90bace1u, 0xbb8205d0u, 0x11a86248u, 0x7574a99eu, - 0xb77f19b6u, 0xe0a9dc09u, 0x662d09a1u, 0xc4324633u, - 0xe85a1f02u, 0x09f0be8cu, 0x4a99a025u, 0x1d6efe10u, - 0x1ab93d1du, 0x0ba5a4dfu, 0xa186f20fu, 0x2868f169u, - 0xdcb7da83u, 0x573906feu, 0xa1e2ce9bu, 0x4fcd7f52u, - 0x50115e01u, 0xa70683fau, 0xa002b5c4u, 0x0de6d027u, - 0x9af88c27u, 0x773f8641u, 0xc3604c06u, 0x61a806b5u, - 0xf0177a28u, 0xc0f586e0u, 0x006058aau, 0x30dc7d62u, - 0x11e69ed7u, 0x2338ea63u, 0x53c2dd94u, 0xc2c21634u, - 0xbbcbee56u, 0x90bcb6deu, 0xebfc7da1u, 0xce591d76u, - 0x6f05e409u, 0x4b7c0188u, 0x39720a3du, 0x7c927c24u, - 0x86e3725fu, 0x724d9db9u, 0x1ac15bb4u, 0xd39eb8fcu, - 0xed545578u, 0x08fca5b5u, 0xd83d7cd3u, 0x4dad0fc4u, - 0x1e50ef5eu, 0xb161e6f8u, 0xa28514d9u, 0x6c51133cu, - 0x6fd5c7e7u, 0x56e14ec4u, 0x362abfceu, 0xddc6c837u, - 0xd79a3234u, 0x92638212u, 0x670efa8eu, 0x406000e0u}, - {0x3a39ce37u, 0xd3faf5cfu, 0xabc27737u, 0x5ac52d1bu, - 0x5cb0679eu, 0x4fa33742u, 0xd3822740u, 0x99bc9bbeu, - 0xd5118e9du, 0xbf0f7315u, 0xd62d1c7eu, 0xc700c47bu, - 0xb78c1b6bu, 0x21a19045u, 0xb26eb1beu, 0x6a366eb4u, - 0x5748ab2fu, 0xbc946e79u, 0xc6a376d2u, 0x6549c2c8u, - 0x530ff8eeu, 0x468dde7du, 0xd5730a1du, 0x4cd04dc6u, - 0x2939bbdbu, 0xa9ba4650u, 0xac9526e8u, 0xbe5ee304u, - 0xa1fad5f0u, 0x6a2d519au, 0x63ef8ce2u, 0x9a86ee22u, - 0xc089c2b8u, 0x43242ef6u, 0xa51e03aau, 0x9cf2d0a4u, - 0x83c061bau, 0x9be96a4du, 0x8fe51550u, 0xba645bd6u, - 0x2826a2f9u, 0xa73a3ae1u, 0x4ba99586u, 0xef5562e9u, - 0xc72fefd3u, 0xf752f7dau, 0x3f046f69u, 0x77fa0a59u, - 0x80e4a915u, 0x87b08601u, 0x9b09e6adu, 0x3b3ee593u, - 0xe990fd5au, 0x9e34d797u, 0x2cf0b7d9u, 0x022b8b51u, - 0x96d5ac3au, 0x017da67du, 0xd1cf3ed6u, 0x7c7d2d28u, - 0x1f9f25cfu, 0xadf2b89bu, 0x5ad6b472u, 0x5a88f54cu, - 0xe029ac71u, 0xe019a5e6u, 0x47b0acfdu, 0xed93fa9bu, - 0xe8d3c48du, 0x283b57ccu, 0xf8d56629u, 0x79132e28u, - 0x785f0191u, 0xed756055u, 0xf7960e44u, 0xe3d35e8cu, - 0x15056dd4u, 0x88f46dbau, 0x03a16125u, 0x0564f0bdu, - 0xc3eb9e15u, 0x3c9057a2u, 0x97271aecu, 0xa93a072au, - 0x1b3f6d9bu, 0x1e6321f5u, 0xf59c66fbu, 0x26dcf319u, - 0x7533d928u, 0xb155fdf5u, 0x03563482u, 0x8aba3cbbu, - 0x28517711u, 0xc20ad9f8u, 0xabcc5167u, 0xccad925fu, - 0x4de81751u, 0x3830dc8eu, 0x379d5862u, 0x9320f991u, - 0xea7a90c2u, 0xfb3e7bceu, 0x5121ce64u, 0x774fbe32u, - 0xa8b6e37eu, 0xc3293d46u, 0x48de5369u, 0x6413e680u, - 0xa2ae0810u, 0xdd6db224u, 0x69852dfdu, 0x09072166u, - 0xb39a460au, 0x6445c0ddu, 0x586cdecfu, 0x1c20c8aeu, - 0x5bbef7ddu, 0x1b588d40u, 0xccd2017fu, 0x6bb4e3bbu, - 0xdda26a7eu, 0x3a59ff45u, 0x3e350a44u, 0xbcb4cdd5u, - 0x72eacea8u, 0xfa6484bbu, 0x8d6612aeu, 0xbf3c6f47u, - 0xd29be463u, 0x542f5d9eu, 0xaec2771bu, 0xf64e6370u, - 0x740e0d8du, 0xe75b1357u, 0xf8721671u, 0xaf537d5du, - 0x4040cb08u, 0x4eb4e2ccu, 0x34d2466au, 0x0115af84u, - 0xe1b00428u, 0x95983a1du, 0x06b89fb4u, 0xce6ea048u, - 0x6f3f3b82u, 0x3520ab82u, 0x011a1d4bu, 0x277227f8u, - 0x611560b1u, 0xe7933fdcu, 0xbb3a792bu, 0x344525bdu, - 0xa08839e1u, 0x51ce794bu, 0x2f32c9b7u, 0xa01fbac9u, - 0xe01cc87eu, 0xbcc7d1f6u, 0xcf0111c3u, 0xa1e8aac7u, - 0x1a908749u, 0xd44fbd9au, 0xd0dadecbu, 0xd50ada38u, - 0x0339c32au, 0xc6913667u, 0x8df9317cu, 0xe0b12b4fu, - 0xf79e59b7u, 0x43f5bb3au, 0xf2d519ffu, 0x27d9459cu, - 0xbf97222cu, 0x15e6fc2au, 0x0f91fc71u, 0x9b941525u, - 0xfae59361u, 0xceb69cebu, 0xc2a86459u, 0x12baa8d1u, - 0xb6c1075eu, 0xe3056a0cu, 0x10d25065u, 0xcb03a442u, - 0xe0ec6e0eu, 0x1698db3bu, 0x4c98a0beu, 0x3278e964u, - 0x9f1f9532u, 0xe0d392dfu, 0xd3a0342bu, 0x8971f21eu, - 0x1b0a7441u, 0x4ba3348cu, 0xc5be7120u, 0xc37632d8u, - 0xdf359f8du, 0x9b992f2eu, 0xe60b6f47u, 0x0fe3f11du, - 0xe54cda54u, 0x1edad891u, 0xce6279cfu, 0xcd3e7e6fu, - 0x1618b166u, 0xfd2c1d05u, 0x848fd2c5u, 0xf6fb2299u, - 0xf523f357u, 0xa6327623u, 0x93a83531u, 0x56cccd02u, - 0xacf08162u, 0x5a75ebb5u, 0x6e163697u, 0x88d273ccu, - 0xde966292u, 0x81b949d0u, 0x4c50901bu, 0x71c65614u, - 0xe6c6c7bdu, 0x327a140au, 0x45e1d006u, 0xc3f27b9au, - 0xc9aa53fdu, 0x62a80f00u, 0xbb25bfe2u, 0x35bdd2f6u, - 0x71126905u, 0xb2040222u, 0xb6cbcf7cu, 0xcd769c2bu, - 0x53113ec0u, 0x1640e3d3u, 0x38abbd60u, 0x2547adf0u, - 0xba38209cu, 0xf746ce76u, 0x77afa1c5u, 0x20756060u, - 0x85cbfe4eu, 0x8ae88dd8u, 0x7aaaf9b0u, 0x4cf9aa7eu, - 0x1948c25cu, 0x02fb8a8cu, 0x01c36ae4u, 0xd6ebe1f9u, - 0x90d4f869u, 0xa65cdea0u, 0x3f09252du, 0xc208e69fu, - 0xb74e6132u, 0xce77e25bu, 0x578fdfe3u, 0x3ac372e6u} -}; - -#define F1(i) \ - xl ^= pax[i]; \ - xr ^= ((sbx[0][xl >> 24] + \ - sbx[1][(xl & 0xFF0000) >> 16]) ^ \ - sbx[2][(xl & 0xFF00) >> 8]) + \ - sbx[3][xl & 0xFF]; - -#define F2(i) \ - xr ^= pax[i]; \ - xl ^= ((sbx[0][xr >> 24] + \ - sbx[1][(xr & 0xFF0000) >> 16]) ^ \ - sbx[2][(xr & 0xFF00) >> 8]) + \ - sbx[3][xr & 0xFF]; - - -static void bf_e_block(uint32_t *p_xl, uint32_t *p_xr) -{ - uint32_t temp; - uint32_t xl = *p_xl; - uint32_t xr = *p_xr; - - F1(0) F2(1) F1(2) F2(3) F1(4) F2(5) F1(6) F2(7) - F1(8) F2(9) F1(10) F2(11) F1(12) F2(13) F1(14) F2(15) - xl ^= pax[16]; - xr ^= pax[17]; - temp = xl; - xl = xr; - xr = temp; - *p_xl = xl; - *p_xr = xr; -} - -#ifdef WORDS_BIGENDIAN -# define htonl2(x) \ - x = ((((x) & 0xffL) << 24) | (((x) & 0xff00L) << 8) | \ - (((x) & 0xff0000L) >> 8) | (((x) & 0xff000000L) >> 24)) -#else // ifdef WORDS_BIGENDIAN -# define htonl2(x) -#endif // ifdef WORDS_BIGENDIAN - -static void bf_e_cblock(char_u *block) -{ - block8 bk; - - memcpy(bk.uc, block, 8); - htonl2(bk.ul[0]); - htonl2(bk.ul[1]); - bf_e_block(&bk.ul[0], &bk.ul[1]); - htonl2(bk.ul[0]); - htonl2(bk.ul[1]); - memcpy(block, bk.uc, 8); -} - -// Initialize the crypt method using "password" as the encryption key and -// "salt[salt_len]" as the salt. -void bf_key_init(char_u *password, char_u *salt, int salt_len) -{ - // Process the key 1000 times. - // See http://en.wikipedia.org/wiki/Key_strengthening. - char_u *key = sha256_key(password, salt, salt_len); - - int i; - for (i = 0; i < 1000; i++) { - key = sha256_key(key, salt, salt_len); - } - - // Convert the key from 64 hex chars to 32 binary chars. - int keylen = (int)STRLEN(key) / 2; - - if (keylen == 0) { - EMSG(_("E831: bf_key_init() called with empty password")); - return; - } - - unsigned u; - for (i = 0; i < keylen; i++) { - sscanf((char *)&key[i * 2], "%2x", &u); - key[i] = u; - } - - memmove(sbx, sbi, 4 * 4 * 256); - - int keypos = 0; - for (i = 0; i < 18; i++) { - uint32_t val = 0; - - int j; - for (j = 0; j < 4; j++) { - val = (val << 8) | key[keypos++ % keylen]; - } - pax[i] = ipa[i] ^ val; - } - - uint32_t data_l = 0; - uint32_t data_r = 0; - for (i = 0; i < 18; i += 2) { - bf_e_block(&data_l, &data_r); - pax[i + 0] = data_l; - pax[i + 1] = data_r; - } - - for (i = 0; i < 4; i++) { - int j; - for (j = 0; j < 256; j += 2) { - bf_e_block(&data_l, &data_r); - sbx[i][j + 0] = data_l; - sbx[i][j + 1] = data_r; - } - } -} - -/// BF Self test for corrupted tables or instructions -static int bf_check_tables(uint32_t a_ipa[18], uint32_t a_sbi[4][256], - uint32_t val) -{ - uint32_t c = 0; - int i; - for (i = 0; i < 18; i++) { - c ^= a_ipa[i]; - } - - for (i = 0; i < 4; i++) { - int j; - for (j = 0; j < 256; j++) { - c ^= a_sbi[i][j]; - } - } - return c == val; -} - -typedef struct { - char_u password[64]; - char_u salt[9]; - char_u plaintxt[9]; - char_u cryptxt[9]; - char_u badcryptxt[9]; // cryptxt when big/little endian is wrong. - uint32_t keysum; -} struct_bf_test_data; - -// Assert bf(password, plaintxt) is cryptxt. -// Assert csum(pax sbx(password)) is keysum. -static struct_bf_test_data bf_test_data[] = { - { - "password", - "salt", - "plaintxt", - "\xad\x3d\xfa\x7f\xe8\xea\x40\xf6", // cryptxt - "\x72\x50\x3b\x38\x10\x60\x22\xa7", // badcryptxt - 0x56701b5du // keysum - }, -}; - -// Return FAIL when there is something wrong with blowfish encryption. -static int bf_self_test(void) -{ - int err = 0; - if (!bf_check_tables(ipa, sbi, 0x6ffa520a)) { - err++; - } - - int bn = ARRAY_LENGTH(bf_test_data); - int i; - for (i = 0; i < bn; i++) { - bf_key_init((char_u *)(bf_test_data[i].password), bf_test_data[i].salt, - (int)STRLEN(bf_test_data[i].salt)); - - if (!bf_check_tables(pax, sbx, bf_test_data[i].keysum)) { - err++; - } - - // Don't modify bf_test_data[i].plaintxt, self test is idempotent. - block8 bk; - memcpy(bk.uc, bf_test_data[i].plaintxt, 8); - bf_e_cblock(bk.uc); - - if (memcmp(bk.uc, bf_test_data[i].cryptxt, 8) != 0) { - if ((err == 0) && (memcmp(bk.uc, bf_test_data[i].badcryptxt, 8) == 0)) { - EMSG(_("E817: Blowfish big/little endian use wrong")); - } - err++; - } - } - - return err > 0 ? FAIL : OK; -} - -// Cipher feedback mode. -static int randbyte_offset = 0; -static int update_offset = 0; -static char_u cfb_buffer[BF_CFB_LEN]; // 64 bytes - -// Initialize with seed "iv[iv_len]". -void bf_cfb_init(char_u *iv, int iv_len) -{ - randbyte_offset = update_offset = 0; - memset(cfb_buffer, 0, BF_CFB_LEN); - - if (iv_len > 0) { - int mi = iv_len > BF_CFB_LEN ? iv_len : BF_CFB_LEN; - int i; - for (i = 0; i < mi; i++) { - cfb_buffer[i % BF_CFB_LEN] ^= iv[i % iv_len]; - } - } -} - -#define BF_CFB_UPDATE(c) \ - { \ - cfb_buffer[update_offset] ^= (char_u)c; \ - if (++update_offset == BF_CFB_LEN) { \ - update_offset = 0; \ - } \ - } - -#define BF_RANBYTE(t) \ - { \ - if ((randbyte_offset & BF_BLOCK_MASK) == 0) { \ - bf_e_cblock(&cfb_buffer[randbyte_offset]); \ - } \ - t = cfb_buffer[randbyte_offset]; \ - if (++randbyte_offset == BF_CFB_LEN) { \ - randbyte_offset = 0; \ - } \ - } - -// Encrypt "from[len]" into "to[len]". -// "from" and "to" can be equal to encrypt in place. -void bf_crypt_encode(char_u *from, size_t len, char_u *to) -{ - size_t i; - for (i = 0; i < len; i++) { - int ztemp = from[i]; - int t; - BF_RANBYTE(t); - BF_CFB_UPDATE(ztemp); - to[i] = t ^ ztemp; - } -} - -/* - * Decrypt "ptr[len]" in place. - */ -void bf_crypt_decode(char_u *ptr, long len) -{ - char_u *p; - for (p = ptr; p < ptr + len; p++) { - int t; - BF_RANBYTE(t); - *p ^= t; - BF_CFB_UPDATE(*p); - } -} - -/* - * Initialize the encryption keys and the random header according to - * the given password. - * in: "passwd" password string with which to modify keys - */ -void bf_crypt_init_keys(char_u *passwd) -{ - char_u *p; - for (p = passwd; *p != NUL; p++) { - BF_CFB_UPDATE(*p); - } -} - -static int save_randbyte_offset; -static int save_update_offset; -static char_u save_cfb_buffer[BF_CFB_LEN]; -static uint32_t save_pax[18]; -static uint32_t save_sbx[4][256]; - -/* - * Save the current crypt state. Can only be used once before - * bf_crypt_restore(). - */ -void bf_crypt_save(void) -{ - save_randbyte_offset = randbyte_offset; - save_update_offset = update_offset; - memmove(save_cfb_buffer, cfb_buffer, BF_CFB_LEN); - memmove(save_pax, pax, 4 * 18); - memmove(save_sbx, sbx, 4 * 4 * 256); -} - -/* - * Restore the current crypt state. Can only be used after - * bf_crypt_save(). - */ -void bf_crypt_restore(void) -{ - randbyte_offset = save_randbyte_offset; - update_offset = save_update_offset; - memmove(cfb_buffer, save_cfb_buffer, BF_CFB_LEN); - memmove(pax, save_pax, 4 * 18); - memmove(sbx, save_sbx, 4 * 4 * 256); -} - -/* - * Run a test to check if the encryption works as expected. - * Give an error and return FAIL when not. - */ -int blowfish_self_test(void) -{ - if (sha256_self_test() == FAIL) { - EMSG(_("E818: sha256 test failed")); - return FAIL; - } - if (bf_self_test() == FAIL) { - EMSG(_("E819: Blowfish test failed")); - return FAIL; - } - return OK; -} diff --git a/src/blowfish.h b/src/blowfish.h deleted file mode 100644 index 288187f433..0000000000 --- a/src/blowfish.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef NEOVIM_BLOWFISH_H -#define NEOVIM_BLOWFISH_H - -void bf_key_init(char_u *password, char_u *salt, int salt_len); -void bf_cfb_init(char_u *iv, int iv_len); -void bf_crypt_encode(char_u *from, size_t len, char_u *to); -void bf_crypt_decode(char_u *ptr, long len); -void bf_crypt_init_keys(char_u *passwd); -void bf_crypt_save(void); -void bf_crypt_restore(void); -int blowfish_self_test(void); - -#endif // NEOVIM_BLOWFISH_H diff --git a/src/buffer.c b/src/buffer.c deleted file mode 100644 index 7cd773b55c..0000000000 --- a/src/buffer.c +++ /dev/null @@ -1,4657 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * buffer.c: functions for dealing with the buffer structure - */ - -/* - * The buffer list is a double linked list of all buffers. - * Each buffer can be in one of these states: - * never loaded: BF_NEVERLOADED is set, only the file name is valid - * not loaded: b_ml.ml_mfp == NULL, no memfile allocated - * hidden: b_nwindows == 0, loaded but not displayed in a window - * normal: loaded and displayed in a window - * - * Instead of storing file names all over the place, each file name is - * stored in the buffer list. It can be referenced by a number. - * - * The current implementation remembers all file names ever used. - */ - -#include - -#include "vim.h" -#include "buffer.h" -#include "charset.h" -#include "diff.h" -#include "digraph.h" -#include "eval.h" -#include "ex_cmds2.h" -#include "ex_cmds.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "ex_getln.h" -#include "fileio.h" -#include "fold.h" -#include "getchar.h" -#include "hashtab.h" -#include "indent.h" -#include "indent_c.h" -#include "main.h" -#include "mark.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "garray.h" -#include "move.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "spell.h" -#include "syntax.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "window.h" -#include "os/os.h" - -// Todo(stefan991): remove this macro -#define INVALID_DEVICE_ID UINT64_MAX - -static char_u *buflist_match(regprog_T *prog, buf_T *buf); -# define HAVE_BUFLIST_MATCH -static char_u *fname_match(regprog_T *prog, char_u *name); -static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, - colnr_T col, int copy_options); -static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer); -static buf_T *buflist_findname_file_info(char_u *ffname, FileInfo *file_info); -static int otherfile_buf(buf_T *buf, char_u *ffname, FileInfo *file_info); -static int buf_same_ino(buf_T *buf, FileInfo *file_info); -static int ti_change(char_u *str, char_u **last); -static int append_arg_number(win_T *wp, char_u *buf, int buflen, int add_file); -static void free_buffer(buf_T *); -static void free_buffer_stuff(buf_T *buf, int free_options); -static void clear_wininfo(buf_T *buf); - -static void insert_sign(buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr); - -static char *msg_loclist = N_("[Location List]"); -static char *msg_qflist = N_("[Quickfix List]"); -static char *e_auabort = N_("E855: Autocommands caused command to abort"); - -/* - * Open current buffer, that is: open the memfile and read the file into - * memory. - * Return FAIL for failure, OK otherwise. - */ -int -open_buffer ( - int read_stdin, /* read file from stdin */ - exarg_T *eap, /* for forced 'ff' and 'fenc' or NULL */ - int flags /* extra flags for readfile() */ -) -{ - int retval = OK; - buf_T *old_curbuf; - long old_tw = curbuf->b_p_tw; - - /* - * The 'readonly' flag is only set when BF_NEVERLOADED is being reset. - * When re-entering the same buffer, it should not change, because the - * user may have reset the flag by hand. - */ - if (readonlymode && curbuf->b_ffname != NULL - && (curbuf->b_flags & BF_NEVERLOADED)) - curbuf->b_p_ro = TRUE; - - if (ml_open(curbuf) == FAIL) { - /* - * There MUST be a memfile, otherwise we can't do anything - * If we can't create one for the current buffer, take another buffer - */ - close_buffer(NULL, curbuf, 0, FALSE); - for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) - if (curbuf->b_ml.ml_mfp != NULL) - break; - /* - * if there is no memfile at all, exit - * This is OK, since there are no changes to lose. - */ - if (curbuf == NULL) { - EMSG(_("E82: Cannot allocate any buffer, exiting...")); - getout(2); - } - EMSG(_("E83: Cannot allocate buffer, using other one...")); - enter_buffer(curbuf); - if (old_tw != curbuf->b_p_tw) - check_colorcolumn(curwin); - return FAIL; - } - - /* The autocommands in readfile() may change the buffer, but only AFTER - * reading the file. */ - old_curbuf = curbuf; - modified_was_set = FALSE; - - /* mark cursor position as being invalid */ - curwin->w_valid = 0; - - if (curbuf->b_ffname != NULL - ) { - retval = readfile(curbuf->b_ffname, curbuf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_NEW); - /* Help buffer is filtered. */ - if (curbuf->b_help) - fix_help_buffer(); - } else if (read_stdin) { - int save_bin = curbuf->b_p_bin; - linenr_T line_count; - - /* - * First read the text in binary mode into the buffer. - * Then read from that same buffer and append at the end. This makes - * it possible to retry when 'fileformat' or 'fileencoding' was - * guessed wrong. - */ - curbuf->b_p_bin = TRUE; - retval = readfile(NULL, NULL, (linenr_T)0, - (linenr_T)0, (linenr_T)MAXLNUM, NULL, - flags | (READ_NEW + READ_STDIN)); - curbuf->b_p_bin = save_bin; - if (retval == OK) { - line_count = curbuf->b_ml.ml_line_count; - retval = readfile(NULL, NULL, (linenr_T)line_count, - (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_BUFFER); - if (retval == OK) { - /* Delete the binary lines. */ - while (--line_count >= 0) - ml_delete((linenr_T)1, FALSE); - } else { - /* Delete the converted lines. */ - while (curbuf->b_ml.ml_line_count > line_count) - ml_delete(line_count, FALSE); - } - /* Put the cursor on the first line. */ - curwin->w_cursor.lnum = 1; - curwin->w_cursor.col = 0; - - /* Set or reset 'modified' before executing autocommands, so that - * it can be changed there. */ - if (!readonlymode && !bufempty()) - changed(); - else if (retval != FAIL) - unchanged(curbuf, FALSE); - apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE, - curbuf, &retval); - } - } - - /* if first time loading this buffer, init b_chartab[] */ - if (curbuf->b_flags & BF_NEVERLOADED) { - (void)buf_init_chartab(curbuf, FALSE); - parse_cino(curbuf); - } - - /* - * Set/reset the Changed flag first, autocmds may change the buffer. - * Apply the automatic commands, before processing the modelines. - * So the modelines have priority over auto commands. - */ - /* When reading stdin, the buffer contents always needs writing, so set - * the changed flag. Unless in readonly mode: "ls | gview -". - * When interrupted and 'cpoptions' contains 'i' set changed flag. */ - if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - || modified_was_set /* ":set modified" used in autocmd */ - || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - ) - changed(); - else if (retval != FAIL && !read_stdin) - unchanged(curbuf, FALSE); - save_file_ff(curbuf); /* keep this fileformat */ - - /* require "!" to overwrite the file, because it wasn't read completely */ - if (aborting()) - curbuf->b_flags |= BF_READERR; - - /* Need to update automatic folding. Do this before the autocommands, - * they may use the fold info. */ - foldUpdateAll(curwin); - - /* need to set w_topline, unless some autocommand already did that. */ - if (!(curwin->w_valid & VALID_TOPLINE)) { - curwin->w_topline = 1; - curwin->w_topfill = 0; - } - apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval); - - if (retval != FAIL) { - /* - * The autocommands may have changed the current buffer. Apply the - * modelines to the correct buffer, if it still exists and is loaded. - */ - if (buf_valid(old_curbuf) && old_curbuf->b_ml.ml_mfp != NULL) { - aco_save_T aco; - - /* Go to the buffer that was opened. */ - aucmd_prepbuf(&aco, old_curbuf); - do_modelines(0); - curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); - - apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf, - &retval); - - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - } - } - - return retval; -} - -/* - * Return TRUE if "buf" points to a valid buffer (in the buffer list). - */ -int buf_valid(buf_T *buf) -{ - buf_T *bp; - - for (bp = firstbuf; bp != NULL; bp = bp->b_next) - if (bp == buf) - return TRUE; - return FALSE; -} - -/* - * Close the link to a buffer. - * "action" is used when there is no longer a window for the buffer. - * It can be: - * 0 buffer becomes hidden - * DOBUF_UNLOAD buffer is unloaded - * DOBUF_DELETE buffer is unloaded and removed from buffer list - * DOBUF_WIPE buffer is unloaded and really deleted - * When doing all but the first one on the current buffer, the caller should - * get a new buffer very soon! - * - * The 'bufhidden' option can force freeing and deleting. - * - * When "abort_if_last" is TRUE then do not close the buffer if autocommands - * cause there to be only one window with this buffer. e.g. when ":quit" is - * supposed to close the window but autocommands close all other windows. - */ -void -close_buffer ( - win_T *win, /* if not NULL, set b_last_cursor */ - buf_T *buf, - int action, - int abort_if_last -) -{ - int is_curbuf; - int nwindows; - int unload_buf = (action != 0); - int del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); - int wipe_buf = (action == DOBUF_WIPE); - - /* - * Force unloading or deleting when 'bufhidden' says so. - * The caller must take care of NOT deleting/freeing when 'bufhidden' is - * "hide" (otherwise we could never free or delete a buffer). - */ - if (buf->b_p_bh[0] == 'd') { /* 'bufhidden' == "delete" */ - del_buf = TRUE; - unload_buf = TRUE; - } else if (buf->b_p_bh[0] == 'w') { /* 'bufhidden' == "wipe" */ - del_buf = TRUE; - unload_buf = TRUE; - wipe_buf = TRUE; - } else if (buf->b_p_bh[0] == 'u') /* 'bufhidden' == "unload" */ - unload_buf = TRUE; - - if (win != NULL) { - /* Set b_last_cursor when closing the last window for the buffer. - * Remember the last cursor position and window options of the buffer. - * This used to be only for the current window, but then options like - * 'foldmethod' may be lost with a ":only" command. */ - if (buf->b_nwindows == 1) - set_last_cursor(win); - buflist_setfpos(buf, win, - win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum, - win->w_cursor.col, TRUE); - } - - /* When the buffer is no longer in a window, trigger BufWinLeave */ - if (buf->b_nwindows == 1) { - buf->b_closing = TRUE; - apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) { - /* Autocommands deleted the buffer. */ -aucmd_abort: - EMSG(_(e_auabort)); - return; - } - buf->b_closing = FALSE; - if (abort_if_last && one_window()) - /* Autocommands made this the only window. */ - goto aucmd_abort; - - /* When the buffer becomes hidden, but is not unloaded, trigger - * BufHidden */ - if (!unload_buf) { - buf->b_closing = TRUE; - apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) - /* Autocommands deleted the buffer. */ - goto aucmd_abort; - buf->b_closing = FALSE; - if (abort_if_last && one_window()) - /* Autocommands made this the only window. */ - goto aucmd_abort; - } - if (aborting()) /* autocmds may abort script processing */ - return; - } - nwindows = buf->b_nwindows; - - /* decrease the link count from windows (unless not in any window) */ - if (buf->b_nwindows > 0) - --buf->b_nwindows; - - /* Return when a window is displaying the buffer or when it's not - * unloaded. */ - if (buf->b_nwindows > 0 || !unload_buf) - return; - - /* Always remove the buffer when there is no file name. */ - if (buf->b_ffname == NULL) - del_buf = TRUE; - - /* - * Free all things allocated for this buffer. - * Also calls the "BufDelete" autocommands when del_buf is TRUE. - */ - /* Remember if we are closing the current buffer. Restore the number of - * windows, so that autocommands in buf_freeall() don't get confused. */ - is_curbuf = (buf == curbuf); - buf->b_nwindows = nwindows; - - buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); - if ( - win_valid(win) && - win->w_buffer == buf) - win->w_buffer = NULL; /* make sure we don't use the buffer now */ - - /* Autocommands may have deleted the buffer. */ - if (!buf_valid(buf)) - return; - if (aborting()) /* autocmds may abort script processing */ - return; - - /* Autocommands may have opened or closed windows for this buffer. - * Decrement the count for the close we do here. */ - if (buf->b_nwindows > 0) - --buf->b_nwindows; - - /* - * It's possible that autocommands change curbuf to the one being deleted. - * This might cause the previous curbuf to be deleted unexpectedly. But - * in some cases it's OK to delete the curbuf, because a new one is - * obtained anyway. Therefore only return if curbuf changed to the - * deleted buffer. - */ - if (buf == curbuf && !is_curbuf) - return; - - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - - /* - * Remove the buffer from the list. - */ - if (wipe_buf) { - free(buf->b_ffname); - free(buf->b_sfname); - if (buf->b_prev == NULL) - firstbuf = buf->b_next; - else - buf->b_prev->b_next = buf->b_next; - if (buf->b_next == NULL) - lastbuf = buf->b_prev; - else - buf->b_next->b_prev = buf->b_prev; - free_buffer(buf); - } else { - if (del_buf) { - /* Free all internal variables and reset option values, to make - * ":bdel" compatible with Vim 5.7. */ - free_buffer_stuff(buf, TRUE); - - /* Make it look like a new buffer. */ - buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; - - /* Init the options when loaded again. */ - buf->b_p_initialized = FALSE; - } - buf_clear_file(buf); - if (del_buf) - buf->b_p_bl = FALSE; - } -} - -/* - * Make buffer not contain a file. - */ -void buf_clear_file(buf_T *buf) -{ - buf->b_ml.ml_line_count = 1; - unchanged(buf, TRUE); - buf->b_p_eol = TRUE; - buf->b_start_eol = TRUE; - buf->b_p_bomb = FALSE; - buf->b_start_bomb = FALSE; - buf->b_ml.ml_mfp = NULL; - buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */ -} - -/* - * buf_freeall() - free all things allocated for a buffer that are related to - * the file. flags: - * BFA_DEL buffer is going to be deleted - * BFA_WIPE buffer is going to be wiped out - * BFA_KEEP_UNDO do not free undo information - */ -void buf_freeall(buf_T *buf, int flags) -{ - int is_curbuf = (buf == curbuf); - - buf->b_closing = TRUE; - apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; - if ((flags & BFA_DEL) && buf->b_p_bl) { - apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; - } - if (flags & BFA_WIPE) { - apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; - } - buf->b_closing = FALSE; - if (aborting()) /* autocmds may abort script processing */ - return; - - /* - * It's possible that autocommands change curbuf to the one being deleted. - * This might cause curbuf to be deleted unexpectedly. But in some cases - * it's OK to delete the curbuf, because a new one is obtained anyway. - * Therefore only return if curbuf changed to the deleted buffer. - */ - if (buf == curbuf && !is_curbuf) - return; - diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */ - /* Remove any ownsyntax, unless exiting. */ - if (firstwin != NULL && curwin->w_buffer == buf) - reset_synblock(curwin); - - /* No folds in an empty buffer. */ - { - win_T *win; - tabpage_T *tp; - - FOR_ALL_TAB_WINDOWS(tp, win) - if (win->w_buffer == buf) - clearFolding(win); - } - - ml_close(buf, TRUE); /* close and delete the memline/memfile */ - buf->b_ml.ml_line_count = 0; /* no lines in buffer */ - if ((flags & BFA_KEEP_UNDO) == 0) { - u_blockfree(buf); /* free the memory allocated for undo */ - u_clearall(buf); /* reset all undo information */ - } - syntax_clear(&buf->b_s); /* reset syntax info */ - buf->b_flags &= ~BF_READERR; /* a read error is no longer relevant */ -} - -/* - * Free a buffer structure and the things it contains related to the buffer - * itself (not the file, that must have been done already). - */ -static void free_buffer(buf_T *buf) -{ - free_buffer_stuff(buf, TRUE); - unref_var_dict(buf->b_vars); - aubuflocal_remove(buf); - if (autocmd_busy) { - // Do not free the buffer structure while autocommands are executing, - // it's still needed. Free it when autocmd_busy is reset. - buf->b_next = au_pending_free_buf; - au_pending_free_buf = buf; - } else { - free(buf); - } -} - -/* - * Free stuff in the buffer for ":bdel" and when wiping out the buffer. - */ -static void -free_buffer_stuff ( - buf_T *buf, - int free_options /* free options as well */ -) -{ - if (free_options) { - clear_wininfo(buf); /* including window-local options */ - free_buf_options(buf, TRUE); - ga_clear(&buf->b_s.b_langp); - } - vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ - hash_init(&buf->b_vars->dv_hashtab); - uc_clear(&buf->b_ucmds); /* clear local user commands */ - buf_delete_signs(buf); /* delete any signs */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ - free(buf->b_start_fenc); - buf->b_start_fenc = NULL; -} - -/* - * Free the b_wininfo list for buffer "buf". - */ -static void clear_wininfo(buf_T *buf) -{ - wininfo_T *wip; - - while (buf->b_wininfo != NULL) { - wip = buf->b_wininfo; - buf->b_wininfo = wip->wi_next; - if (wip->wi_optset) { - clear_winopt(&wip->wi_opt); - deleteFoldRecurse(&wip->wi_folds); - } - free(wip); - } -} - -/* - * Go to another buffer. Handles the result of the ATTENTION dialog. - */ -void goto_buffer(exarg_T *eap, int start, int dir, int count) -{ -# if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION) - buf_T *old_curbuf = curbuf; - - swap_exists_action = SEA_DIALOG; -# endif - (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, - start, dir, count, eap->forceit); -# if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION) - if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') { - cleanup_T cs; - - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a window. */ - enter_cleanup(&cs); - - /* Quitting means closing the split window, nothing else. */ - win_close(curwin, TRUE); - swap_exists_action = SEA_NONE; - swap_exists_did_quit = TRUE; - - /* Restore the error/interrupt/exception state if not discarded by a - * new aborting error, interrupt, or uncaught exception. */ - leave_cleanup(&cs); - } else - handle_swap_exists(old_curbuf); -# endif -} - -#if defined(HAS_SWAP_EXISTS_ACTION) || defined(PROTO) -/* - * Handle the situation of swap_exists_action being set. - * It is allowed for "old_curbuf" to be NULL or invalid. - */ -void handle_swap_exists(buf_T *old_curbuf) -{ - cleanup_T cs; - long old_tw = curbuf->b_p_tw; - - if (swap_exists_action == SEA_QUIT) { - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a buffer. */ - enter_cleanup(&cs); - - /* User selected Quit at ATTENTION prompt. Go back to previous - * buffer. If that buffer is gone or the same as the current one, - * open a new, empty buffer. */ - swap_exists_action = SEA_NONE; /* don't want it again */ - swap_exists_did_quit = TRUE; - close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE); - if (!buf_valid(old_curbuf) || old_curbuf == curbuf) - old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); - if (old_curbuf != NULL) { - enter_buffer(old_curbuf); - if (old_tw != curbuf->b_p_tw) - check_colorcolumn(curwin); - } - /* If "old_curbuf" is NULL we are in big trouble here... */ - - /* Restore the error/interrupt/exception state if not discarded by a - * new aborting error, interrupt, or uncaught exception. */ - leave_cleanup(&cs); - } else if (swap_exists_action == SEA_RECOVER) { - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a buffer. */ - enter_cleanup(&cs); - - /* User selected Recover at ATTENTION prompt. */ - msg_scroll = TRUE; - ml_recover(); - MSG_PUTS("\n"); /* don't overwrite the last message */ - cmdline_row = msg_row; - do_modelines(0); - - /* Restore the error/interrupt/exception state if not discarded by a - * new aborting error, interrupt, or uncaught exception. */ - leave_cleanup(&cs); - } - swap_exists_action = SEA_NONE; -} -#endif - -/* - * do_bufdel() - delete or unload buffer(s) - * - * addr_count == 0: ":bdel" - delete current buffer - * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete - * buffer "end_bnr", then any other arguments. - * addr_count == 2: ":N,N bdel" - delete buffers in range - * - * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or - * DOBUF_DEL (":bdel") - * - * Returns error message or NULL - */ -char_u * -do_bufdel ( - int command, - char_u *arg, /* pointer to extra arguments */ - int addr_count, - int start_bnr, /* first buffer number in a range */ - int end_bnr, /* buffer nr or last buffer nr in a range */ - int forceit -) -{ - int do_current = 0; /* delete current buffer? */ - int deleted = 0; /* number of buffers deleted */ - char_u *errormsg = NULL; /* return value */ - int bnr; /* buffer number */ - char_u *p; - - if (addr_count == 0) { - (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit); - } else { - if (addr_count == 2) { - if (*arg) /* both range and argument is not allowed */ - return (char_u *)_(e_trailing); - bnr = start_bnr; - } else /* addr_count == 1 */ - bnr = end_bnr; - - for (; !got_int; ui_breakcheck()) { - /* - * delete the current buffer last, otherwise when the - * current buffer is deleted, the next buffer becomes - * the current one and will be loaded, which may then - * also be deleted, etc. - */ - if (bnr == curbuf->b_fnum) - do_current = bnr; - else if (do_buffer(command, DOBUF_FIRST, FORWARD, (int)bnr, - forceit) == OK) - ++deleted; - - /* - * find next buffer number to delete/unload - */ - if (addr_count == 2) { - if (++bnr > end_bnr) - break; - } else { /* addr_count == 1 */ - arg = skipwhite(arg); - if (*arg == NUL) - break; - if (!VIM_ISDIGIT(*arg)) { - p = skiptowhite_esc(arg); - bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, - FALSE, FALSE); - if (bnr < 0) /* failed */ - break; - arg = p; - } else - bnr = getdigits(&arg); - } - } - if (!got_int && do_current && do_buffer(command, DOBUF_FIRST, - FORWARD, do_current, forceit) == OK) - ++deleted; - - if (deleted == 0) { - if (command == DOBUF_UNLOAD) - STRCPY(IObuff, _("E515: No buffers were unloaded")); - else if (command == DOBUF_DEL) - STRCPY(IObuff, _("E516: No buffers were deleted")); - else - STRCPY(IObuff, _("E517: No buffers were wiped out")); - errormsg = IObuff; - } else if (deleted >= p_report) { - if (command == DOBUF_UNLOAD) { - if (deleted == 1) - MSG(_("1 buffer unloaded")); - else - smsg((char_u *)_("%d buffers unloaded"), deleted); - } else if (command == DOBUF_DEL) { - if (deleted == 1) - MSG(_("1 buffer deleted")); - else - smsg((char_u *)_("%d buffers deleted"), deleted); - } else { - if (deleted == 1) - MSG(_("1 buffer wiped out")); - else - smsg((char_u *)_("%d buffers wiped out"), deleted); - } - } - } - - - return errormsg; -} - - -static int empty_curbuf(int close_others, int forceit, int action); - -/* - * Make the current buffer empty. - * Used when it is wiped out and it's the last buffer. - */ -static int empty_curbuf(int close_others, int forceit, int action) -{ - int retval; - buf_T *buf = curbuf; - - if (action == DOBUF_UNLOAD) { - EMSG(_("E90: Cannot unload last buffer")); - return FAIL; - } - - if (close_others) { - /* Close any other windows on this buffer, then make it empty. */ - close_windows(buf, TRUE); - } - - setpcmark(); - retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, - forceit ? ECMD_FORCEIT : 0, curwin); - - /* - * do_ecmd() may create a new buffer, then we have to delete - * the old one. But do_ecmd() may have done that already, check - * if the buffer still exists. - */ - if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0) - close_buffer(NULL, buf, action, FALSE); - if (!close_others) - need_fileinfo = FALSE; - return retval; -} -/* - * Implementation of the commands for the buffer list. - * - * action == DOBUF_GOTO go to specified buffer - * action == DOBUF_SPLIT split window and go to specified buffer - * action == DOBUF_UNLOAD unload specified buffer(s) - * action == DOBUF_DEL delete specified buffer(s) from buffer list - * action == DOBUF_WIPE delete specified buffer(s) really - * - * start == DOBUF_CURRENT go to "count" buffer from current buffer - * start == DOBUF_FIRST go to "count" buffer from first buffer - * start == DOBUF_LAST go to "count" buffer from last buffer - * start == DOBUF_MOD go to "count" modified buffer from current buffer - * - * Return FAIL or OK. - */ -int -do_buffer ( - int action, - int start, - int dir, /* FORWARD or BACKWARD */ - int count, /* buffer number or number of buffers */ - int forceit /* TRUE for :...! */ -) -{ - buf_T *buf; - buf_T *bp; - int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL - || action == DOBUF_WIPE); - - switch (start) { - case DOBUF_FIRST: buf = firstbuf; break; - case DOBUF_LAST: buf = lastbuf; break; - default: buf = curbuf; break; - } - if (start == DOBUF_MOD) { /* find next modified buffer */ - while (count-- > 0) { - do { - buf = buf->b_next; - if (buf == NULL) - buf = firstbuf; - } while (buf != curbuf && !bufIsChanged(buf)); - } - if (!bufIsChanged(buf)) { - EMSG(_("E84: No modified buffer found")); - return FAIL; - } - } else if (start == DOBUF_FIRST && count) { /* find specified buffer number */ - while (buf != NULL && buf->b_fnum != count) - buf = buf->b_next; - } else { - bp = NULL; - while (count > 0 || (!unload && !buf->b_p_bl && bp != buf)) { - /* remember the buffer where we start, we come back there when all - * buffers are unlisted. */ - if (bp == NULL) - bp = buf; - if (dir == FORWARD) { - buf = buf->b_next; - if (buf == NULL) - buf = firstbuf; - } else { - buf = buf->b_prev; - if (buf == NULL) - buf = lastbuf; - } - /* don't count unlisted buffers */ - if (unload || buf->b_p_bl) { - --count; - bp = NULL; /* use this buffer as new starting point */ - } - if (bp == buf) { - /* back where we started, didn't find anything. */ - EMSG(_("E85: There is no listed buffer")); - return FAIL; - } - } - } - - if (buf == NULL) { /* could not find it */ - if (start == DOBUF_FIRST) { - /* don't warn when deleting */ - if (!unload) - EMSGN(_("E86: Buffer %" PRId64 " does not exist"), count); - } else if (dir == FORWARD) - EMSG(_("E87: Cannot go beyond last buffer")); - else - EMSG(_("E88: Cannot go before first buffer")); - return FAIL; - } - - - /* - * delete buffer buf from memory and/or the list - */ - if (unload) { - int forward; - - /* When unloading or deleting a buffer that's already unloaded and - * unlisted: fail silently. */ - if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl) - return FAIL; - - if (!forceit && bufIsChanged(buf)) { - if ((p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(buf, FALSE); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! It's not changed - * now. */ - return FAIL; - /* If it's still changed fail silently, the dialog already - * mentioned why it fails. */ - if (bufIsChanged(buf)) - return FAIL; - } else { - EMSGN(_("E89: No write since last change for buffer %" PRId64 - " (add ! to override)"), - buf->b_fnum); - return FAIL; - } - } - - /* - * If deleting the last (listed) buffer, make it empty. - * The last (listed) buffer cannot be unloaded. - */ - for (bp = firstbuf; bp != NULL; bp = bp->b_next) - if (bp->b_p_bl && bp != buf) - break; - if (bp == NULL && buf == curbuf) - return empty_curbuf(TRUE, forceit, action); - - /* - * If the deleted buffer is the current one, close the current window - * (unless it's the only window). Repeat this so long as we end up in - * a window with this buffer. - */ - while (buf == curbuf - && !(curwin->w_closing || curwin->w_buffer->b_closing) - && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { - if (win_close(curwin, FALSE) == FAIL) - break; - } - - /* - * If the buffer to be deleted is not the current one, delete it here. - */ - if (buf != curbuf) { - close_windows(buf, FALSE); - if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) - close_buffer(NULL, buf, action, FALSE); - return OK; - } - - /* - * Deleting the current buffer: Need to find another buffer to go to. - * There should be another, otherwise it would have been handled - * above. However, autocommands may have deleted all buffers. - * First use au_new_curbuf, if it is valid. - * Then prefer the buffer we most recently visited. - * Else try to find one that is loaded, after the current buffer, - * then before the current buffer. - * Finally use any buffer. - */ - buf = NULL; /* selected buffer */ - bp = NULL; /* used when no loaded buffer found */ - if (au_new_curbuf != NULL && buf_valid(au_new_curbuf)) - buf = au_new_curbuf; - else if (curwin->w_jumplistlen > 0) { - int jumpidx; - - jumpidx = curwin->w_jumplistidx - 1; - if (jumpidx < 0) - jumpidx = curwin->w_jumplistlen - 1; - - forward = jumpidx; - while (jumpidx != curwin->w_jumplistidx) { - buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); - if (buf != NULL) { - if (buf == curbuf || !buf->b_p_bl) - buf = NULL; /* skip current and unlisted bufs */ - else if (buf->b_ml.ml_mfp == NULL) { - /* skip unloaded buf, but may keep it for later */ - if (bp == NULL) - bp = buf; - buf = NULL; - } - } - if (buf != NULL) /* found a valid buffer: stop searching */ - break; - /* advance to older entry in jump list */ - if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) - break; - if (--jumpidx < 0) - jumpidx = curwin->w_jumplistlen - 1; - if (jumpidx == forward) /* List exhausted for sure */ - break; - } - } - - if (buf == NULL) { /* No previous buffer, Try 2'nd approach */ - forward = TRUE; - buf = curbuf->b_next; - for (;; ) { - if (buf == NULL) { - if (!forward) /* tried both directions */ - break; - buf = curbuf->b_prev; - forward = FALSE; - continue; - } - /* in non-help buffer, try to skip help buffers, and vv */ - if (buf->b_help == curbuf->b_help && buf->b_p_bl) { - if (buf->b_ml.ml_mfp != NULL) /* found loaded buffer */ - break; - if (bp == NULL) /* remember unloaded buf for later */ - bp = buf; - } - if (forward) - buf = buf->b_next; - else - buf = buf->b_prev; - } - } - if (buf == NULL) /* No loaded buffer, use unloaded one */ - buf = bp; - if (buf == NULL) { /* No loaded buffer, find listed one */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_p_bl && buf != curbuf) - break; - } - if (buf == NULL) { /* Still no buffer, just take one */ - if (curbuf->b_next != NULL) - buf = curbuf->b_next; - else - buf = curbuf->b_prev; - } - } - - if (buf == NULL) { - /* Autocommands must have wiped out all other buffers. Only option - * now is to make the current buffer empty. */ - return empty_curbuf(FALSE, forceit, action); - } - - /* - * make buf current buffer - */ - if (action == DOBUF_SPLIT) { /* split window first */ - /* If 'switchbuf' contains "useopen": jump to first window containing - * "buf" if one exists */ - if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf)) - return OK; - /* If 'switchbuf' contains "usetab": jump to first window in any tab - * page containing "buf" if one exists */ - if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf)) - return OK; - if (win_split(0, 0) == FAIL) - return FAIL; - } - - /* go to current buffer - nothing to do */ - if (buf == curbuf) - return OK; - - /* - * Check if the current buffer may be abandoned. - */ - if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit)) { - if ((p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(curbuf, FALSE); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! */ - return FAIL; - } - if (bufIsChanged(curbuf)) { - EMSG(_(e_nowrtmsg)); - return FAIL; - } - } - - /* Go to the other buffer. */ - set_curbuf(buf, action); - -#if defined(FEAT_LISTCMDS) \ - && (defined(FEAT_SCROLLBIND) || defined(FEAT_CURSORBIND)) - if (action == DOBUF_SPLIT) { - RESET_BINDING(curwin); /* reset 'scrollbind' and 'cursorbind' */ - } -#endif - - if (aborting()) /* autocmds may abort script processing */ - return FAIL; - - return OK; -} - - -/* - * Set current buffer to "buf". Executes autocommands and closes current - * buffer. "action" tells how to close the current buffer: - * DOBUF_GOTO free or hide it - * DOBUF_SPLIT nothing - * DOBUF_UNLOAD unload it - * DOBUF_DEL delete it - * DOBUF_WIPE wipe it out - */ -void set_curbuf(buf_T *buf, int action) -{ - buf_T *prevbuf; - int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL - || action == DOBUF_WIPE); - long old_tw = curbuf->b_p_tw; - - setpcmark(); - if (!cmdmod.keepalt) - curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */ - buflist_altfpos(curwin); /* remember curpos */ - - /* Don't restart Select mode after switching to another buffer. */ - VIsual_reselect = FALSE; - - /* close_windows() or apply_autocmds() may change curbuf */ - prevbuf = curbuf; - - apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (buf_valid(prevbuf) && !aborting()) { - if (prevbuf == curwin->w_buffer) - reset_synblock(curwin); - if (unload) - close_windows(prevbuf, FALSE); - if (buf_valid(prevbuf) && !aborting()) { - win_T *previouswin = curwin; - if (prevbuf == curbuf) - u_sync(FALSE); - close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, - unload ? action : (action == DOBUF_GOTO - && !P_HID(prevbuf) - && !bufIsChanged( - prevbuf)) ? DOBUF_UNLOAD : 0, FALSE); - if (curwin != previouswin && win_valid(previouswin)) - /* autocommands changed curwin, Grr! */ - curwin = previouswin; - } - } - /* An autocommand may have deleted "buf", already entered it (e.g., when - * it did ":bunload") or aborted the script processing! - * If curwin->w_buffer is null, enter_buffer() will make it valid again */ - if ((buf_valid(buf) && buf != curbuf - && !aborting() - ) || curwin->w_buffer == NULL - ) { - enter_buffer(buf); - if (old_tw != curbuf->b_p_tw) - check_colorcolumn(curwin); - } -} - -/* - * Enter a new current buffer. - * Old curbuf must have been abandoned already! This also means "curbuf" may - * be pointing to freed memory. - */ -void enter_buffer(buf_T *buf) -{ - /* Copy buffer and window local option values. Not for a help buffer. */ - buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); - if (!buf->b_help) - get_winopts(buf); - else - /* Remove all folds in the window. */ - clearFolding(curwin); - foldUpdateAll(curwin); /* update folds (later). */ - - /* Get the buffer in the current window. */ - curwin->w_buffer = buf; - curbuf = buf; - ++curbuf->b_nwindows; - - if (curwin->w_p_diff) - diff_buf_add(curbuf); - - curwin->w_s = &(buf->b_s); - - /* Cursor on first line by default. */ - curwin->w_cursor.lnum = 1; - curwin->w_cursor.col = 0; - curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - curwin->w_topline_was_set = FALSE; - - /* mark cursor position as being invalid */ - curwin->w_valid = 0; - - /* Make sure the buffer is loaded. */ - if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */ - /* If there is no filetype, allow for detecting one. Esp. useful for - * ":ball" used in a autocommand. If there already is a filetype we - * might prefer to keep it. */ - if (*curbuf->b_p_ft == NUL) - did_filetype = FALSE; - - open_buffer(FALSE, NULL, 0); - } else { - if (!msg_silent) - need_fileinfo = TRUE; /* display file info after redraw */ - (void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */ - curwin->w_topline = 1; - curwin->w_topfill = 0; - apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); - } - - /* If autocommands did not change the cursor position, restore cursor lnum - * and possibly cursor col. */ - if (curwin->w_cursor.lnum == 1 && inindent(0)) - buflist_getfpos(); - - check_arg_idx(curwin); /* check for valid arg_idx */ - maketitle(); - /* when autocmds didn't change it */ - if (curwin->w_topline == 1 && !curwin->w_topline_was_set) - scroll_cursor_halfway(FALSE); /* redisplay at correct position */ - - - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - - if (curbuf->b_kmap_state & KEYMAP_INIT) - (void)keymap_init(); - /* May need to set the spell language. Can only do this after the buffer - * has been properly setup. */ - if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) - (void)did_set_spelllang(curwin); - - redraw_later(NOT_VALID); -} - -/* - * Change to the directory of the current buffer. - */ -void do_autochdir(void) -{ - if (p_acd) { - if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK) { - shorten_fnames(TRUE); - } - } -} - -/* - * functions for dealing with the buffer list - */ - -/* - * Add a file name to the buffer list. Return a pointer to the buffer. - * If the same file name already exists return a pointer to that buffer. - * If it does not exist, or if fname == NULL, a new entry is created. - * If (flags & BLN_CURBUF) is TRUE, may use current buffer. - * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list. - * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer. - * This is the ONLY way to create a new buffer. - */ -static int top_file_num = 1; /* highest file number */ - -buf_T * -buflist_new ( - char_u *ffname, /* full path of fname or relative */ - char_u *sfname, /* short fname or NULL */ - linenr_T lnum, /* preferred cursor line */ - int flags /* BLN_ defines */ -) -{ - buf_T *buf; - - fname_expand(curbuf, &ffname, &sfname); /* will allocate ffname */ - - /* - * If file name already exists in the list, update the entry. - */ - /* We can use inode numbers when the file exists. Works better - * for hard links. */ - FileInfo file_info; - if (sfname == NULL || !os_get_file_info((char *)sfname, &file_info)) { - file_info.stat.st_dev = INVALID_DEVICE_ID; - } - if (ffname != NULL && !(flags & BLN_DUMMY) - && (buf = buflist_findname_file_info(ffname, &file_info)) != NULL) { - free(ffname); - if (lnum != 0) - buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE); - /* copy the options now, if 'cpo' doesn't have 's' and not done - * already */ - buf_copy_options(buf, 0); - if ((flags & BLN_LISTED) && !buf->b_p_bl) { - buf->b_p_bl = TRUE; - if (!(flags & BLN_DUMMY)) { - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { - return NULL; - } - } - } - return buf; - } - - /* - * If the current buffer has no name and no contents, use the current - * buffer. Otherwise: Need to allocate a new buffer structure. - * - * This is the ONLY place where a new buffer structure is allocated! - * (A spell file buffer is allocated in spell.c, but that's not a normal - * buffer.) - */ - buf = NULL; - if ((flags & BLN_CURBUF) - && curbuf != NULL - && curbuf->b_ffname == NULL - && curbuf->b_nwindows <= 1 - && (curbuf->b_ml.ml_mfp == NULL || bufempty())) { - buf = curbuf; - /* It's like this buffer is deleted. Watch out for autocommands that - * change curbuf! If that happens, allocate a new buffer anyway. */ - if (curbuf->b_p_bl) - apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); - if (buf == curbuf) - apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf); - if (aborting()) /* autocmds may abort script processing */ - return NULL; - if (buf == curbuf) { - /* Make sure 'bufhidden' and 'buftype' are empty */ - clear_string_option(&buf->b_p_bh); - clear_string_option(&buf->b_p_bt); - } - } - if (buf != curbuf || curbuf == NULL) { - buf = xcalloc(1, sizeof(buf_T)); - /* init b: variables */ - buf->b_vars = dict_alloc(); - init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); - } - - if (ffname != NULL) { - buf->b_ffname = ffname; - buf->b_sfname = vim_strsave(sfname); - } - - clear_wininfo(buf); - buf->b_wininfo = xcalloc(1, sizeof(wininfo_T)); - - if (ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) { - free(buf->b_ffname); - buf->b_ffname = NULL; - free(buf->b_sfname); - buf->b_sfname = NULL; - if (buf != curbuf) - free_buffer(buf); - return NULL; - } - - if (buf == curbuf) { - /* free all things allocated for this buffer */ - buf_freeall(buf, 0); - if (buf != curbuf) /* autocommands deleted the buffer! */ - return NULL; - if (aborting()) /* autocmds may abort script processing */ - return NULL; - /* buf->b_nwindows = 0; why was this here? */ - free_buffer_stuff(buf, FALSE); /* delete local variables et al. */ - - /* Init the options. */ - buf->b_p_initialized = FALSE; - buf_copy_options(buf, BCO_ENTER); - - /* need to reload lmaps and set b:keymap_name */ - curbuf->b_kmap_state |= KEYMAP_INIT; - } else { - /* - * put new buffer at the end of the buffer list - */ - buf->b_next = NULL; - if (firstbuf == NULL) { /* buffer list is empty */ - buf->b_prev = NULL; - firstbuf = buf; - } else { /* append new buffer at end of list */ - lastbuf->b_next = buf; - buf->b_prev = lastbuf; - } - lastbuf = buf; - - buf->b_fnum = top_file_num++; - if (top_file_num < 0) { /* wrap around (may cause duplicates) */ - EMSG(_("W14: Warning: List of file names overflow")); - if (emsg_silent == 0) { - out_flush(); - ui_delay(3000L, TRUE); /* make sure it is noticed */ - } - top_file_num = 1; - } - - /* - * Always copy the options from the current buffer. - */ - buf_copy_options(buf, BCO_ALWAYS); - } - - buf->b_wininfo->wi_fpos.lnum = lnum; - buf->b_wininfo->wi_win = curwin; - - hash_init(&buf->b_s.b_keywtab); - hash_init(&buf->b_s.b_keywtab_ic); - - buf->b_fname = buf->b_sfname; - if (file_info.stat.st_dev == INVALID_DEVICE_ID) - buf->b_dev_valid = FALSE; - else { - buf->b_dev_valid = TRUE; - buf->b_dev = file_info.stat.st_dev; - buf->b_ino = file_info.stat.st_ino; - } - buf->b_u_synced = TRUE; - buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; - if (flags & BLN_DUMMY) - buf->b_flags |= BF_DUMMY; - buf_clear_file(buf); - clrallmarks(buf); /* clear marks */ - fmarks_check_names(buf); /* check file marks for this file */ - buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */ - if (!(flags & BLN_DUMMY)) { - apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { - return NULL; - } - if (flags & BLN_LISTED) { - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { - return NULL; - } - } - if (aborting()) /* autocmds may abort script processing */ - return NULL; - } - - return buf; -} - -/* - * Free the memory for the options of a buffer. - * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and - * 'fileencoding'. - */ -void free_buf_options(buf_T *buf, int free_p_ff) -{ - if (free_p_ff) { - clear_string_option(&buf->b_p_fenc); - clear_string_option(&buf->b_p_ff); - clear_string_option(&buf->b_p_bh); - clear_string_option(&buf->b_p_bt); - } - clear_string_option(&buf->b_p_def); - clear_string_option(&buf->b_p_inc); - clear_string_option(&buf->b_p_inex); - clear_string_option(&buf->b_p_inde); - clear_string_option(&buf->b_p_indk); - clear_string_option(&buf->b_p_cm); - clear_string_option(&buf->b_p_fex); - clear_string_option(&buf->b_p_key); - clear_string_option(&buf->b_p_kp); - clear_string_option(&buf->b_p_mps); - clear_string_option(&buf->b_p_fo); - clear_string_option(&buf->b_p_flp); - clear_string_option(&buf->b_p_isk); - clear_string_option(&buf->b_p_keymap); - ga_clear(&buf->b_kmap_ga); - clear_string_option(&buf->b_p_com); - clear_string_option(&buf->b_p_cms); - clear_string_option(&buf->b_p_nf); - clear_string_option(&buf->b_p_syn); - clear_string_option(&buf->b_s.b_p_spc); - clear_string_option(&buf->b_s.b_p_spf); - vim_regfree(buf->b_s.b_cap_prog); - buf->b_s.b_cap_prog = NULL; - clear_string_option(&buf->b_s.b_p_spl); - clear_string_option(&buf->b_p_sua); - clear_string_option(&buf->b_p_ft); - clear_string_option(&buf->b_p_cink); - clear_string_option(&buf->b_p_cino); - clear_string_option(&buf->b_p_cinw); - clear_string_option(&buf->b_p_cpt); - clear_string_option(&buf->b_p_cfu); - clear_string_option(&buf->b_p_ofu); - clear_string_option(&buf->b_p_gp); - clear_string_option(&buf->b_p_mp); - clear_string_option(&buf->b_p_efm); - clear_string_option(&buf->b_p_ep); - clear_string_option(&buf->b_p_path); - clear_string_option(&buf->b_p_tags); - clear_string_option(&buf->b_p_dict); - clear_string_option(&buf->b_p_tsr); - clear_string_option(&buf->b_p_qe); - buf->b_p_ar = -1; - buf->b_p_ul = NO_LOCAL_UNDOLEVEL; -} - -/* - * get alternate file n - * set linenr to lnum or altfpos.lnum if lnum == 0 - * also set cursor column to altfpos.col if 'startofline' is not set. - * if (options & GETF_SETMARK) call setpcmark() - * if (options & GETF_ALT) we are jumping to an alternate file. - * if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping - * - * return FAIL for failure, OK for success - */ -int buflist_getfile(int n, linenr_T lnum, int options, int forceit) -{ - buf_T *buf; - win_T *wp = NULL; - pos_T *fpos; - colnr_T col; - - buf = buflist_findnr(n); - if (buf == NULL) { - if ((options & GETF_ALT) && n == 0) - EMSG(_(e_noalt)); - else - EMSGN(_("E92: Buffer %" PRId64 " not found"), n); - return FAIL; - } - - /* if alternate file is the current buffer, nothing to do */ - if (buf == curbuf) - return OK; - - if (text_locked()) { - text_locked_msg(); - return FAIL; - } - if (curbuf_locked()) - return FAIL; - - /* altfpos may be changed by getfile(), get it now */ - if (lnum == 0) { - fpos = buflist_findfpos(buf); - lnum = fpos->lnum; - col = fpos->col; - } else - col = 0; - - if (options & GETF_SWITCH) { - /* If 'switchbuf' contains "useopen": jump to first window containing - * "buf" if one exists */ - if (swb_flags & SWB_USEOPEN) - wp = buf_jump_open_win(buf); - /* If 'switchbuf' contains "usetab": jump to first window in any tab - * page containing "buf" if one exists */ - if (wp == NULL && (swb_flags & SWB_USETAB)) - wp = buf_jump_open_tab(buf); - /* If 'switchbuf' contains "split" or "newtab" and the current buffer - * isn't empty: open new window */ - if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) { - if (swb_flags & SWB_NEWTAB) /* Open in a new tab */ - tabpage_new(); - else if (win_split(0, 0) == FAIL) /* Open in a new window */ - return FAIL; - RESET_BINDING(curwin); - } - } - - ++RedrawingDisabled; - if (getfile(buf->b_fnum, NULL, NULL, (options & GETF_SETMARK), - lnum, forceit) <= 0) { - --RedrawingDisabled; - - /* cursor is at to BOL and w_cursor.lnum is checked due to getfile() */ - if (!p_sol && col != 0) { - curwin->w_cursor.col = col; - check_cursor_col(); - curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - } - return OK; - } - --RedrawingDisabled; - return FAIL; -} - -/* - * go to the last know line number for the current buffer - */ -void buflist_getfpos(void) -{ - pos_T *fpos; - - fpos = buflist_findfpos(curbuf); - - curwin->w_cursor.lnum = fpos->lnum; - check_cursor_lnum(); - - if (p_sol) - curwin->w_cursor.col = 0; - else { - curwin->w_cursor.col = fpos->col; - check_cursor_col(); - curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - } -} - -/* - * Find file in buffer list by name (it has to be for the current window). - * Returns NULL if not found. - */ -buf_T *buflist_findname_exp(char_u *fname) -{ - char_u *ffname; - buf_T *buf = NULL; - - /* First make the name into a full path name */ - ffname = FullName_save(fname, -#ifdef UNIX - TRUE /* force expansion, get rid of symbolic links */ -#else - FALSE -#endif - ); - if (ffname != NULL) { - buf = buflist_findname(ffname); - free(ffname); - } - return buf; -} - -/* - * Find file in buffer list by name (it has to be for the current window). - * "ffname" must have a full path. - * Skips dummy buffers. - * Returns NULL if not found. - */ -buf_T *buflist_findname(char_u *ffname) -{ - FileInfo file_info; - if (!os_get_file_info((char *)ffname, &file_info)) { - file_info.stat.st_dev = INVALID_DEVICE_ID; - } - return buflist_findname_file_info(ffname, &file_info); -} - -/* - * Same as buflist_findname(), but pass the FileInfo structure to avoid - * getting it twice for the same file. - * Returns NULL if not found. - */ -static buf_T *buflist_findname_file_info(char_u *ffname, FileInfo *file_info) -{ - buf_T *buf; - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if ((buf->b_flags & BF_DUMMY) == 0 - && !otherfile_buf(buf, ffname, file_info)) { - return buf; - } - } - return NULL; -} - -/* - * Find file in buffer list by a regexp pattern. - * Return fnum of the found buffer. - * Return < 0 for error. - */ -int -buflist_findpat ( - char_u *pattern, - char_u *pattern_end, /* pointer to first char after pattern */ - int unlisted, /* find unlisted buffers */ - int diffmode, /* find diff-mode buffers only */ - int curtab_only /* find buffers in current tab only */ -) -{ - buf_T *buf; - regprog_T *prog; - int match = -1; - int find_listed; - char_u *pat; - char_u *patend; - int attempt; - char_u *p; - int toggledollar; - - if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) { - if (*pattern == '%') - match = curbuf->b_fnum; - else - match = curwin->w_alt_fnum; - if (diffmode && !diff_mode_buf(buflist_findnr(match))) - match = -1; - } - /* - * Try four ways of matching a listed buffer: - * attempt == 0: without '^' or '$' (at any position) - * attempt == 1: with '^' at start (only at position 0) - * attempt == 2: with '$' at end (only match at end) - * attempt == 3: with '^' at start and '$' at end (only full match) - * Repeat this for finding an unlisted buffer if there was no matching - * listed buffer. - */ - else { - pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE); - if (pat == NULL) - return -1; - patend = pat + STRLEN(pat) - 1; - toggledollar = (patend > pat && *patend == '$'); - - /* First try finding a listed buffer. If not found and "unlisted" - * is TRUE, try finding an unlisted buffer. */ - find_listed = TRUE; - for (;; ) { - for (attempt = 0; attempt <= 3; ++attempt) { - /* may add '^' and '$' */ - if (toggledollar) - *patend = (attempt < 2) ? NUL : '$'; /* add/remove '$' */ - p = pat; - if (*p == '^' && !(attempt & 1)) /* add/remove '^' */ - ++p; - prog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); - if (prog == NULL) { - free(pat); - return -1; - } - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_p_bl == find_listed - && (!diffmode || diff_mode_buf(buf)) - && buflist_match(prog, buf) != NULL) { - if (curtab_only) { - /* Ignore the match if the buffer is not open in - * the current tab. */ - win_T *wp; - - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer == buf) - break; - if (wp == NULL) - continue; - } - if (match >= 0) { /* already found a match */ - match = -2; - break; - } - match = buf->b_fnum; /* remember first match */ - } - - vim_regfree(prog); - if (match >= 0) /* found one match */ - break; - } - - /* Only search for unlisted buffers if there was no match with - * a listed buffer. */ - if (!unlisted || !find_listed || match != -1) - break; - find_listed = FALSE; - } - - free(pat); - } - - if (match == -2) - EMSG2(_("E93: More than one match for %s"), pattern); - else if (match < 0) - EMSG2(_("E94: No matching buffer for %s"), pattern); - return match; -} - - -/* - * Find all buffer names that match. - * For command line expansion of ":buf" and ":sbuf". - * Return OK if matches found, FAIL otherwise. - */ -int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) -{ - int count = 0; - buf_T *buf; - int round; - char_u *p; - int attempt; - regprog_T *prog; - char_u *patc; - - *num_file = 0; /* return values in case of FAIL */ - *file = NULL; - - /* Make a copy of "pat" and change "^" to "\(^\|[\/]\)". */ - if (*pat == '^') { - patc = xmalloc(STRLEN(pat) + 11); - STRCPY(patc, "\\(^\\|[\\/]\\)"); - STRCPY(patc + 11, pat + 1); - } else - patc = pat; - - /* - * attempt == 0: try match with '\<', match at start of word - * attempt == 1: try match without '\<', match anywhere - */ - for (attempt = 0; attempt <= 1; ++attempt) { - if (attempt > 0 && patc == pat) - break; /* there was no anchor, no need to try again */ - prog = vim_regcomp(patc + attempt * 11, RE_MAGIC); - if (prog == NULL) { - if (patc != pat) - free(patc); - return FAIL; - } - - /* - * round == 1: Count the matches. - * round == 2: Build the array to keep the matches. - */ - for (round = 1; round <= 2; ++round) { - count = 0; - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (!buf->b_p_bl) /* skip unlisted buffers */ - continue; - p = buflist_match(prog, buf); - if (p != NULL) { - if (round == 1) - ++count; - else { - if (options & WILD_HOME_REPLACE) - p = home_replace_save(buf, p); - else - p = vim_strsave(p); - (*file)[count++] = p; - } - } - } - if (count == 0) /* no match found, break here */ - break; - if (round == 1) { - *file = xmalloc(count * sizeof(**file)); - } - } - vim_regfree(prog); - if (count) /* match(es) found, break here */ - break; - } - - if (patc != pat) - free(patc); - - *num_file = count; - return count == 0 ? FAIL : OK; -} - - -#ifdef HAVE_BUFLIST_MATCH -/* - * Check for a match on the file name for buffer "buf" with regprog "prog". - */ -static char_u *buflist_match(regprog_T *prog, buf_T *buf) -{ - char_u *match; - - /* First try the short file name, then the long file name. */ - match = fname_match(prog, buf->b_sfname); - if (match == NULL) - match = fname_match(prog, buf->b_ffname); - - return match; -} - -/* - * Try matching the regexp in "prog" with file name "name". - * Return "name" when there is a match, NULL when not. - */ -static char_u *fname_match(regprog_T *prog, char_u *name) -{ - char_u *match = NULL; - char_u *p; - regmatch_T regmatch; - - if (name != NULL) { - regmatch.regprog = prog; - regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */ - if (vim_regexec(®match, name, (colnr_T)0)) - match = name; - else { - /* Replace $(HOME) with '~' and try matching again. */ - p = home_replace_save(NULL, name); - if (p != NULL && vim_regexec(®match, p, (colnr_T)0)) - match = name; - free(p); - } - } - - return match; -} -#endif - -/* - * find file in buffer list by number - */ -buf_T *buflist_findnr(int nr) -{ - buf_T *buf; - - if (nr == 0) - nr = curwin->w_alt_fnum; - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_fnum == nr) - return buf; - return NULL; -} - -/* - * Get name of file 'n' in the buffer list. - * When the file has no name an empty string is returned. - * home_replace() is used to shorten the file name (used for marks). - * Returns a pointer to allocated memory, of NULL when failed. - */ -char_u * -buflist_nr2name ( - int n, - int fullname, - int helptail /* for help buffers return tail only */ -) -{ - buf_T *buf; - - buf = buflist_findnr(n); - if (buf == NULL) - return NULL; - return home_replace_save(helptail ? buf : NULL, - fullname ? buf->b_ffname : buf->b_fname); -} - -/* - * Set the "lnum" and "col" for the buffer "buf" and the current window. - * When "copy_options" is TRUE save the local window option values. - * When "lnum" is 0 only do the options. - */ -static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options) -{ - wininfo_T *wip; - - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) - if (wip->wi_win == win) - break; - if (wip == NULL) { - /* allocate a new entry */ - wip = xcalloc(1, sizeof(wininfo_T)); - wip->wi_win = win; - if (lnum == 0) /* set lnum even when it's 0 */ - lnum = 1; - } else { - /* remove the entry from the list */ - if (wip->wi_prev) - wip->wi_prev->wi_next = wip->wi_next; - else - buf->b_wininfo = wip->wi_next; - if (wip->wi_next) - wip->wi_next->wi_prev = wip->wi_prev; - if (copy_options && wip->wi_optset) { - clear_winopt(&wip->wi_opt); - deleteFoldRecurse(&wip->wi_folds); - } - } - if (lnum != 0) { - wip->wi_fpos.lnum = lnum; - wip->wi_fpos.col = col; - } - if (copy_options) { - /* Save the window-specific option values. */ - copy_winopt(&win->w_onebuf_opt, &wip->wi_opt); - wip->wi_fold_manual = win->w_fold_manual; - cloneFoldGrowArray(&win->w_folds, &wip->wi_folds); - wip->wi_optset = TRUE; - } - - /* insert the entry in front of the list */ - wip->wi_next = buf->b_wininfo; - buf->b_wininfo = wip; - wip->wi_prev = NULL; - if (wip->wi_next) - wip->wi_next->wi_prev = wip; - - return; -} - -static int wininfo_other_tab_diff(wininfo_T *wip); - -/* - * Return TRUE when "wip" has 'diff' set and the diff is only for another tab - * page. That's because a diff is local to a tab page. - */ -static int wininfo_other_tab_diff(wininfo_T *wip) -{ - win_T *wp; - - if (wip->wi_opt.wo_diff) { - for (wp = firstwin; wp != NULL; wp = wp->w_next) - /* return FALSE when it's a window in the current tab page, thus - * the buffer was in diff mode here */ - if (wip->wi_win == wp) - return FALSE; - return TRUE; - } - return FALSE; -} - -/* - * Find info for the current window in buffer "buf". - * If not found, return the info for the most recently used window. - * When "skip_diff_buffer" is TRUE avoid windows with 'diff' set that is in - * another tab page. - * Returns NULL when there isn't any info. - */ -static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer) -{ - wininfo_T *wip; - - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) - if (wip->wi_win == curwin - && (!skip_diff_buffer || !wininfo_other_tab_diff(wip)) - ) - break; - - /* If no wininfo for curwin, use the first in the list (that doesn't have - * 'diff' set and is in another tab page). */ - if (wip == NULL) { - if (skip_diff_buffer) { - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) - if (!wininfo_other_tab_diff(wip)) - break; - } else - wip = buf->b_wininfo; - } - return wip; -} - -/* - * Reset the local window options to the values last used in this window. - * If the buffer wasn't used in this window before, use the values from - * the most recently used window. If the values were never set, use the - * global values for the window. - */ -void get_winopts(buf_T *buf) -{ - wininfo_T *wip; - - clear_winopt(&curwin->w_onebuf_opt); - clearFolding(curwin); - - wip = find_wininfo(buf, TRUE); - if (wip != NULL && wip->wi_optset) { - copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt); - curwin->w_fold_manual = wip->wi_fold_manual; - curwin->w_foldinvalid = TRUE; - cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds); - } else - copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt); - - /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */ - if (p_fdls >= 0) - curwin->w_p_fdl = p_fdls; - check_colorcolumn(curwin); -} - -/* - * Find the position (lnum and col) for the buffer 'buf' for the current - * window. - * Returns a pointer to no_position if no position is found. - */ -pos_T *buflist_findfpos(buf_T *buf) -{ - wininfo_T *wip; - static pos_T no_position = INIT_POS_T(1, 0, 0); - - wip = find_wininfo(buf, FALSE); - if (wip != NULL) - return &(wip->wi_fpos); - else - return &no_position; -} - -/* - * Find the lnum for the buffer 'buf' for the current window. - */ -linenr_T buflist_findlnum(buf_T *buf) -{ - return buflist_findfpos(buf)->lnum; -} - -/* - * List all know file names (for :files and :buffers command). - */ -void buflist_list(exarg_T *eap) -{ - buf_T *buf; - int len; - int i; - - for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) { - /* skip unlisted buffers, unless ! was used */ - if (!buf->b_p_bl && !eap->forceit) - continue; - msg_putchar('\n'); - if (buf_spname(buf) != NULL) - vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1); - else - home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); - - len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", - buf->b_fnum, - buf->b_p_bl ? ' ' : 'u', - buf == curbuf ? '%' : - (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), - buf->b_ml.ml_mfp == NULL ? ' ' : - (buf->b_nwindows == 0 ? 'h' : 'a'), - !buf->b_p_ma ? '-' : (buf->b_p_ro ? '=' : ' '), - (buf->b_flags & BF_READERR) ? 'x' - : (bufIsChanged(buf) ? '+' : ' '), - NameBuff); - - /* put "line 999" in column 40 or after the file name */ - i = 40 - vim_strsize(IObuff); - do { - IObuff[len++] = ' '; - } while (--i > 0 && len < IOSIZE - 18); - vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), - _("line %" PRId64), - buf == curbuf ? (int64_t)curwin->w_cursor.lnum - : (int64_t)buflist_findlnum(buf)); - msg_outtrans(IObuff); - out_flush(); /* output one line at a time */ - ui_breakcheck(); - } -} - -/* - * Get file name and line number for file 'fnum'. - * Used by DoOneCmd() for translating '%' and '#'. - * Used by insert_reg() and cmdline_paste() for '#' register. - * Return FAIL if not found, OK for success. - */ -int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum) -{ - buf_T *buf; - - buf = buflist_findnr(fnum); - if (buf == NULL || buf->b_fname == NULL) - return FAIL; - - *fname = buf->b_fname; - *lnum = buflist_findlnum(buf); - - return OK; -} - -/* - * Set the file name for "buf"' to 'ffname', short file name to 'sfname'. - * The file name with the full path is also remembered, for when :cd is used. - * Returns FAIL for failure (file name already in use by other buffer) - * OK otherwise. - */ -int -setfname ( - buf_T *buf, - char_u *ffname, - char_u *sfname, - int message /* give message when buffer already exists */ -) -{ - buf_T *obuf = NULL; - FileInfo file_info; - - if (ffname == NULL || *ffname == NUL) { - /* Removing the name. */ - free(buf->b_ffname); - free(buf->b_sfname); - buf->b_ffname = NULL; - buf->b_sfname = NULL; - file_info.stat.st_dev = INVALID_DEVICE_ID; - } else { - fname_expand(buf, &ffname, &sfname); /* will allocate ffname */ - if (ffname == NULL) /* out of memory */ - return FAIL; - - /* - * if the file name is already used in another buffer: - * - if the buffer is loaded, fail - * - if the buffer is not loaded, delete it from the list - */ - if (!os_get_file_info((char *)ffname, &file_info)) { - file_info.stat.st_dev = INVALID_DEVICE_ID; - } - if (!(buf->b_flags & BF_DUMMY)) { - obuf = buflist_findname_file_info(ffname, &file_info); - } - if (obuf != NULL && obuf != buf) { - if (obuf->b_ml.ml_mfp != NULL) { /* it's loaded, fail */ - if (message) - EMSG(_("E95: Buffer with this name already exists")); - free(ffname); - return FAIL; - } - /* delete from the list */ - close_buffer(NULL, obuf, DOBUF_WIPE, FALSE); - } - sfname = vim_strsave(sfname); - if (ffname == NULL || sfname == NULL) { - free(sfname); - free(ffname); - return FAIL; - } -#ifdef USE_FNAME_CASE - fname_case(sfname, 0); /* set correct case for short file name */ -#endif - free(buf->b_ffname); - free(buf->b_sfname); - buf->b_ffname = ffname; - buf->b_sfname = sfname; - } - buf->b_fname = buf->b_sfname; - if (file_info.stat.st_dev == INVALID_DEVICE_ID) { - buf->b_dev_valid = FALSE; - } else { - buf->b_dev_valid = TRUE; - buf->b_dev = file_info.stat.st_dev; - buf->b_ino = file_info.stat.st_ino; - } - - buf_name_changed(buf); - return OK; -} - -/* - * Crude way of changing the name of a buffer. Use with care! - * The name should be relative to the current directory. - */ -void buf_set_name(int fnum, char_u *name) -{ - buf_T *buf; - - buf = buflist_findnr(fnum); - if (buf != NULL) { - free(buf->b_sfname); - free(buf->b_ffname); - buf->b_ffname = vim_strsave(name); - buf->b_sfname = NULL; - /* Allocate ffname and expand into full path. Also resolves .lnk - * files on Win32. */ - fname_expand(buf, &buf->b_ffname, &buf->b_sfname); - buf->b_fname = buf->b_sfname; - } -} - -/* - * Take care of what needs to be done when the name of buffer "buf" has - * changed. - */ -void buf_name_changed(buf_T *buf) -{ - /* - * If the file name changed, also change the name of the swapfile - */ - if (buf->b_ml.ml_mfp != NULL) - ml_setname(buf); - - if (curwin->w_buffer == buf) - check_arg_idx(curwin); /* check file name for arg list */ - maketitle(); /* set window title */ - status_redraw_all(); /* status lines need to be redrawn */ - fmarks_check_names(buf); /* check named file marks */ - ml_timestamp(buf); /* reset timestamp */ -} - -/* - * set alternate file name for current window - * - * Used by do_one_cmd(), do_write() and do_ecmd(). - * Return the buffer. - */ -buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum) -{ - buf_T *buf; - - /* Create a buffer. 'buflisted' is not set if it's a new buffer */ - buf = buflist_new(ffname, sfname, lnum, 0); - if (buf != NULL && !cmdmod.keepalt) - curwin->w_alt_fnum = buf->b_fnum; - return buf; -} - -/* - * Get alternate file name for current window. - * Return NULL if there isn't any, and give error message if requested. - */ -char_u * -getaltfname ( - int errmsg /* give error message */ -) -{ - char_u *fname; - linenr_T dummy; - - if (buflist_name_nr(0, &fname, &dummy) == FAIL) { - if (errmsg) - EMSG(_(e_noalt)); - return NULL; - } - return fname; -} - -/* - * Add a file name to the buflist and return its number. - * Uses same flags as buflist_new(), except BLN_DUMMY. - * - * used by qf_init(), main() and doarglist() - */ -int buflist_add(char_u *fname, int flags) -{ - buf_T *buf; - - buf = buflist_new(fname, NULL, (linenr_T)0, flags); - if (buf != NULL) - return buf->b_fnum; - return 0; -} - -#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) -/* - * Adjust slashes in file names. Called after 'shellslash' was set. - */ -void buflist_slash_adjust(void) -{ - buf_T *bp; - - for (bp = firstbuf; bp != NULL; bp = bp->b_next) { - if (bp->b_ffname != NULL) - slash_adjust(bp->b_ffname); - if (bp->b_sfname != NULL) - slash_adjust(bp->b_sfname); - } -} - -#endif - -/* - * Set alternate cursor position for the current buffer and window "win". - * Also save the local window option values. - */ -void buflist_altfpos(win_T *win) -{ - buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE); -} - -/* - * Return TRUE if 'ffname' is not the same file as current file. - * Fname must have a full path (expanded by path_get_absolute_path()). - */ -int otherfile(char_u *ffname) -{ - return otherfile_buf(curbuf, ffname, NULL); -} - -static int otherfile_buf(buf_T *buf, char_u *ffname, FileInfo *file_info_p) -{ - /* no name is different */ - if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) { - return TRUE; - } - if (fnamecmp(ffname, buf->b_ffname) == 0) { - return FALSE; - } - { - FileInfo file_info; - - /* If no struct stat given, get it now */ - if (file_info_p == NULL) { - if (!buf->b_dev_valid || !os_get_file_info((char *)ffname, &file_info)) { - file_info.stat.st_dev = INVALID_DEVICE_ID; - } - file_info_p = &file_info; - } - /* Use dev/ino to check if the files are the same, even when the names - * are different (possible with links). Still need to compare the - * name above, for when the file doesn't exist yet. - * Problem: The dev/ino changes when a file is deleted (and created - * again) and remains the same when renamed/moved. We don't want to - * stat() each buffer each time, that would be too slow. Get the - * dev/ino again when they appear to match, but not when they appear - * to be different: Could skip a buffer when it's actually the same - * file. */ - if (buf_same_ino(buf, file_info_p)) { - buf_setino(buf); - if (buf_same_ino(buf, file_info_p)) - return FALSE; - } - } - return TRUE; -} - -/* - * Set inode and device number for a buffer. - * Must always be called when b_fname is changed!. - */ -void buf_setino(buf_T *buf) -{ - FileInfo file_info; - if (buf->b_fname != NULL - && os_get_file_info((char *)buf->b_fname, &file_info)) { - buf->b_dev_valid = TRUE; - buf->b_dev = file_info.stat.st_dev; - buf->b_ino = file_info.stat.st_ino; - } else { - buf->b_dev_valid = FALSE; - } -} - -/* - * Return TRUE if dev/ino in buffer "buf" matches with "stp". - */ -static int buf_same_ino(buf_T *buf, FileInfo *file_info) -{ - return buf->b_dev_valid - && file_info->stat.st_dev == buf->b_dev - && file_info->stat.st_ino == buf->b_ino; -} - -/* - * Print info about the current buffer. - */ -void -fileinfo ( - int fullname, /* when non-zero print full path */ - int shorthelp, - int dont_truncate -) -{ - char_u *name; - int n; - char_u *p; - char_u *buffer; - size_t len; - - buffer = xmalloc(IOSIZE); - - if (fullname > 1) { /* 2 CTRL-G: include buffer number */ - vim_snprintf((char *)buffer, IOSIZE, "buf %d: ", curbuf->b_fnum); - p = buffer + STRLEN(buffer); - } else - p = buffer; - - *p++ = '"'; - if (buf_spname(curbuf) != NULL) - vim_strncpy(p, buf_spname(curbuf), IOSIZE - (p - buffer) - 1); - else { - if (!fullname && curbuf->b_fname != NULL) - name = curbuf->b_fname; - else - name = curbuf->b_ffname; - home_replace(shorthelp ? curbuf : NULL, name, p, - (int)(IOSIZE - (p - buffer)), TRUE); - } - - vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", - curbufIsChanged() ? (shortmess(SHM_MOD) - ? " [+]" : _(" [Modified]")) : " ", - (curbuf->b_flags & BF_NOTEDITED) - && !bt_dontwrite(curbuf) - ? _("[Not edited]") : "", - (curbuf->b_flags & BF_NEW) - && !bt_dontwrite(curbuf) - ? _("[New file]") : "", - (curbuf->b_flags & BF_READERR) ? _("[Read errors]") : "", - curbuf->b_p_ro ? (shortmess(SHM_RO) ? _("[RO]") - : _("[readonly]")) : "", - (curbufIsChanged() || (curbuf->b_flags & BF_WRITE_MASK) - || curbuf->b_p_ro) ? - " " : ""); - /* With 32 bit longs and more than 21,474,836 lines multiplying by 100 - * causes an overflow, thus for large numbers divide instead. */ - if (curwin->w_cursor.lnum > 1000000L) - n = (int)(((long)curwin->w_cursor.lnum) / - ((long)curbuf->b_ml.ml_line_count / 100L)); - else - n = (int)(((long)curwin->w_cursor.lnum * 100L) / - (long)curbuf->b_ml.ml_line_count); - if (curbuf->b_ml.ml_flags & ML_EMPTY) { - vim_snprintf_add((char *)buffer, IOSIZE, "%s", _(no_lines_msg)); - } else if (p_ru) { - /* Current line and column are already on the screen -- webb */ - if (curbuf->b_ml.ml_line_count == 1) - vim_snprintf_add((char *)buffer, IOSIZE, _("1 line --%d%%--"), n); - else - vim_snprintf_add((char *)buffer, IOSIZE, _("%" PRId64 " lines --%d%%--"), - (int64_t)curbuf->b_ml.ml_line_count, n); - } else { - vim_snprintf_add((char *)buffer, IOSIZE, - _("line %" PRId64 " of %" PRId64 " --%d%%-- col "), - (int64_t)curwin->w_cursor.lnum, - (int64_t)curbuf->b_ml.ml_line_count, - n); - validate_virtcol(); - len = STRLEN(buffer); - col_print(buffer + len, IOSIZE - len, - (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); - } - - (void)append_arg_number(curwin, buffer, IOSIZE, !shortmess(SHM_FILE)); - - if (dont_truncate) { - /* Temporarily set msg_scroll to avoid the message being truncated. - * First call msg_start() to get the message in the right place. */ - msg_start(); - n = msg_scroll; - msg_scroll = TRUE; - msg(buffer); - msg_scroll = n; - } else { - p = msg_trunc_attr(buffer, FALSE, 0); - if (restart_edit != 0 || (msg_scrolled && !need_wait_return)) - /* Need to repeat the message after redrawing when: - * - When restart_edit is set (otherwise there will be a delay - * before redrawing). - * - When the screen was scrolled but there is no wait-return - * prompt. */ - set_keep_msg(p, 0); - } - - free(buffer); -} - -void col_print(char_u *buf, size_t buflen, int col, int vcol) -{ - if (col == vcol) - vim_snprintf((char *)buf, buflen, "%d", col); - else - vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol); -} - -/* - * put file name in title bar of window and in icon title - */ - -static char_u *lasttitle = NULL; -static char_u *lasticon = NULL; - -void maketitle(void) -{ - char_u *p; - char_u *t_str = NULL; - char_u *i_name; - char_u *i_str = NULL; - int maxlen = 0; - int len; - int mustset; - char_u buf[IOSIZE]; - int off; - - if (!redrawing()) { - /* Postpone updating the title when 'lazyredraw' is set. */ - need_maketitle = TRUE; - return; - } - - need_maketitle = FALSE; - if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL) - return; - - if (p_title) { - if (p_titlelen > 0) { - maxlen = p_titlelen * Columns / 100; - if (maxlen < 10) - maxlen = 10; - } - - t_str = buf; - if (*p_titlestring != NUL) { - if (stl_syntax & STL_IN_TITLE) { - int use_sandbox = FALSE; - int save_called_emsg = called_emsg; - - use_sandbox = was_set_insecurely((char_u *)"titlestring", 0); - called_emsg = FALSE; - build_stl_str_hl(curwin, t_str, sizeof(buf), - p_titlestring, use_sandbox, - 0, maxlen, NULL, NULL); - if (called_emsg) - set_string_option_direct((char_u *)"titlestring", -1, - (char_u *)"", OPT_FREE, SID_ERROR); - called_emsg |= save_called_emsg; - } else - t_str = p_titlestring; - } else { - /* format: "fname + (path) (1 of 2) - VIM" */ - -#define SPACE_FOR_FNAME (IOSIZE - 100) -#define SPACE_FOR_DIR (IOSIZE - 20) -#define SPACE_FOR_ARGNR (IOSIZE - 10) /* at least room for " - VIM" */ - if (curbuf->b_fname == NULL) - vim_strncpy(buf, (char_u *)_("[No Name]"), SPACE_FOR_FNAME); - else { - p = transstr(path_tail(curbuf->b_fname)); - vim_strncpy(buf, p, SPACE_FOR_FNAME); - free(p); - } - - switch (bufIsChanged(curbuf) - + (curbuf->b_p_ro * 2) - + (!curbuf->b_p_ma * 4)) { - case 1: STRCAT(buf, " +"); break; - case 2: STRCAT(buf, " ="); break; - case 3: STRCAT(buf, " =+"); break; - case 4: - case 6: STRCAT(buf, " -"); break; - case 5: - case 7: STRCAT(buf, " -+"); break; - } - - if (curbuf->b_fname != NULL) { - /* Get path of file, replace home dir with ~ */ - off = (int)STRLEN(buf); - buf[off++] = ' '; - buf[off++] = '('; - home_replace(curbuf, curbuf->b_ffname, - buf + off, SPACE_FOR_DIR - off, TRUE); -#ifdef BACKSLASH_IN_FILENAME - /* avoid "c:/name" to be reduced to "c" */ - if (isalpha(buf[off]) && buf[off + 1] == ':') - off += 2; -#endif - /* remove the file name */ - p = path_tail_with_sep(buf + off); - if (p == buf + off) - /* must be a help buffer */ - vim_strncpy(buf + off, (char_u *)_("help"), - (size_t)(SPACE_FOR_DIR - off - 1)); - else - *p = NUL; - - /* Translate unprintable chars and concatenate. Keep some - * room for the server name. When there is no room (very long - * file name) use (...). */ - if (off < SPACE_FOR_DIR) { - p = transstr(buf + off); - vim_strncpy(buf + off, p, (size_t)(SPACE_FOR_DIR - off)); - free(p); - } else { - vim_strncpy(buf + off, (char_u *)"...", - (size_t)(SPACE_FOR_ARGNR - off)); - } - STRCAT(buf, ")"); - } - - append_arg_number(curwin, buf, SPACE_FOR_ARGNR, FALSE); - - STRCAT(buf, " - VIM"); - - if (maxlen > 0) { - /* make it shorter by removing a bit in the middle */ - if (vim_strsize(buf) > maxlen) - trunc_string(buf, buf, maxlen, IOSIZE); - } - } - } - mustset = ti_change(t_str, &lasttitle); - - if (p_icon) { - i_str = buf; - if (*p_iconstring != NUL) { - if (stl_syntax & STL_IN_ICON) { - int use_sandbox = FALSE; - int save_called_emsg = called_emsg; - - use_sandbox = was_set_insecurely((char_u *)"iconstring", 0); - called_emsg = FALSE; - build_stl_str_hl(curwin, i_str, sizeof(buf), - p_iconstring, use_sandbox, - 0, 0, NULL, NULL); - if (called_emsg) - set_string_option_direct((char_u *)"iconstring", -1, - (char_u *)"", OPT_FREE, SID_ERROR); - called_emsg |= save_called_emsg; - } else - i_str = p_iconstring; - } else { - if (buf_spname(curbuf) != NULL) - i_name = buf_spname(curbuf); - else /* use file name only in icon */ - i_name = path_tail(curbuf->b_ffname); - *i_str = NUL; - /* Truncate name at 100 bytes. */ - len = (int)STRLEN(i_name); - if (len > 100) { - len -= 100; - if (has_mbyte) - len += (*mb_tail_off)(i_name, i_name + len) + 1; - i_name += len; - } - STRCPY(i_str, i_name); - trans_characters(i_str, IOSIZE); - } - } - - mustset |= ti_change(i_str, &lasticon); - - if (mustset) - resettitle(); -} - -/* - * Used for title and icon: Check if "str" differs from "*last". Set "*last" - * from "str" if it does. - * Return TRUE when "*last" changed. - */ -static int ti_change(char_u *str, char_u **last) -{ - if ((str == NULL) != (*last == NULL) - || (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) { - free(*last); - if (str == NULL) - *last = NULL; - else - *last = vim_strsave(str); - return TRUE; - } - return FALSE; -} - -/* - * Put current window title back (used after calling a shell) - */ -void resettitle(void) -{ - mch_settitle(lasttitle, lasticon); -} - -# if defined(EXITFREE) || defined(PROTO) -void free_titles(void) -{ - free(lasttitle); - free(lasticon); -} - -# endif - - -/* - * Build a string from the status line items in "fmt". - * Return length of string in screen cells. - * - * Normally works for window "wp", except when working for 'tabline' then it - * is "curwin". - * - * Items are drawn interspersed with the text that surrounds it - * Specials: %-(xxx%) => group, %= => middle marker, %< => truncation - * Item: %-. All but are optional - * - * If maxwidth is not zero, the string will be filled at any middle marker - * or truncated if too long, fillchar is used for all whitespace. - */ -int -build_stl_str_hl ( - win_T *wp, - char_u *out, /* buffer to write into != NameBuff */ - size_t outlen, /* length of out[] */ - char_u *fmt, - int use_sandbox, /* "fmt" was set insecurely, use sandbox */ - int fillchar, - int maxwidth, - struct stl_hlrec *hltab, /* return: HL attributes (can be NULL) */ - struct stl_hlrec *tabtab /* return: tab page nrs (can be NULL) */ -) -{ - char_u *p; - char_u *s; - char_u *t; - int byteval; - win_T *o_curwin; - buf_T *o_curbuf; - int empty_line; - colnr_T virtcol; - long l; - long n; - int prevchar_isflag; - int prevchar_isitem; - int itemisflag; - int fillable; - char_u *str; - long num; - int width; - int itemcnt; - int curitem; - int groupitem[STL_MAX_ITEM]; - int groupdepth; - struct stl_item { - char_u *start; - int minwid; - int maxwid; - enum { - Normal, - Empty, - Group, - Middle, - Highlight, - TabPage, - Trunc - } type; - } item[STL_MAX_ITEM]; - int minwid; - int maxwid; - int zeropad; - char_u base; - char_u opt; -#define TMPLEN 70 - char_u tmp[TMPLEN]; - char_u *usefmt = fmt; - struct stl_hlrec *sp; - - /* - * When the format starts with "%!" then evaluate it as an expression and - * use the result as the actual format string. - */ - if (fmt[0] == '%' && fmt[1] == '!') { - usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox); - if (usefmt == NULL) - usefmt = fmt; - } - - if (fillchar == 0) - fillchar = ' '; - /* Can't handle a multi-byte fill character yet. */ - else if (mb_char2len(fillchar) > 1) - fillchar = '-'; - - /* Get line & check if empty (cursorpos will show "0-1"). Note that - * p will become invalid when getting another buffer line. */ - p = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE); - empty_line = (*p == NUL); - - /* Get the byte value now, in case we need it below. This is more - * efficient than making a copy of the line. */ - if (wp->w_cursor.col > (colnr_T)STRLEN(p)) - byteval = 0; - else - byteval = (*mb_ptr2char)(p + wp->w_cursor.col); - - groupdepth = 0; - p = out; - curitem = 0; - prevchar_isflag = TRUE; - prevchar_isitem = FALSE; - for (s = usefmt; *s; ) { - if (curitem == STL_MAX_ITEM) { - /* There are too many items. Add the error code to the statusline - * to give the user a hint about what went wrong. */ - if (p + 6 < out + outlen) { - memmove(p, " E541", (size_t)5); - p += 5; - } - break; - } - - if (*s != NUL && *s != '%') - prevchar_isflag = prevchar_isitem = FALSE; - - /* - * Handle up to the next '%' or the end. - */ - while (*s != NUL && *s != '%' && p + 1 < out + outlen) - *p++ = *s++; - if (*s == NUL || p + 1 >= out + outlen) - break; - - /* - * Handle one '%' item. - */ - s++; - if (*s == NUL) /* ignore trailing % */ - break; - if (*s == '%') { - if (p + 1 >= out + outlen) - break; - *p++ = *s++; - prevchar_isflag = prevchar_isitem = FALSE; - continue; - } - if (*s == STL_MIDDLEMARK) { - s++; - if (groupdepth > 0) - continue; - item[curitem].type = Middle; - item[curitem++].start = p; - continue; - } - if (*s == STL_TRUNCMARK) { - s++; - item[curitem].type = Trunc; - item[curitem++].start = p; - continue; - } - if (*s == ')') { - s++; - if (groupdepth < 1) - continue; - groupdepth--; - - t = item[groupitem[groupdepth]].start; - *p = NUL; - l = vim_strsize(t); - if (curitem > groupitem[groupdepth] + 1 - && item[groupitem[groupdepth]].minwid == 0) { - /* remove group if all items are empty */ - for (n = groupitem[groupdepth] + 1; n < curitem; n++) - if (item[n].type == Normal) - break; - if (n == curitem) { - p = t; - l = 0; - } - } - if (l > item[groupitem[groupdepth]].maxwid) { - /* truncate, remove n bytes of text at the start */ - if (has_mbyte) { - /* Find the first character that should be included. */ - n = 0; - while (l >= item[groupitem[groupdepth]].maxwid) { - l -= ptr2cells(t + n); - n += (*mb_ptr2len)(t + n); - } - } else - n = (long)(p - t) - item[groupitem[groupdepth]].maxwid + 1; - - *t = '<'; - memmove(t + 1, t + n, (size_t)(p - (t + n))); - p = p - n + 1; - /* Fill up space left over by half a double-wide char. */ - while (++l < item[groupitem[groupdepth]].minwid) - *p++ = fillchar; - - /* correct the start of the items for the truncation */ - for (l = groupitem[groupdepth] + 1; l < curitem; l++) { - item[l].start -= n; - if (item[l].start < t) - item[l].start = t; - } - } else if (abs(item[groupitem[groupdepth]].minwid) > l) { - /* fill */ - n = item[groupitem[groupdepth]].minwid; - if (n < 0) { - /* fill by appending characters */ - n = 0 - n; - while (l++ < n && p + 1 < out + outlen) - *p++ = fillchar; - } else { - /* fill by inserting characters */ - memmove(t + n - l, t, (size_t)(p - t)); - l = n - l; - if (p + l >= out + outlen) - l = (long)((out + outlen) - p - 1); - p += l; - for (n = groupitem[groupdepth] + 1; n < curitem; n++) - item[n].start += l; - for (; l > 0; l--) - *t++ = fillchar; - } - } - continue; - } - minwid = 0; - maxwid = 9999; - zeropad = FALSE; - l = 1; - if (*s == '0') { - s++; - zeropad = TRUE; - } - if (*s == '-') { - s++; - l = -1; - } - if (VIM_ISDIGIT(*s)) { - minwid = (int)getdigits(&s); - if (minwid < 0) /* overflow */ - minwid = 0; - } - if (*s == STL_USER_HL) { - item[curitem].type = Highlight; - item[curitem].start = p; - item[curitem].minwid = minwid > 9 ? 1 : minwid; - s++; - curitem++; - continue; - } - if (*s == STL_TABPAGENR || *s == STL_TABCLOSENR) { - if (*s == STL_TABCLOSENR) { - if (minwid == 0) { - /* %X ends the close label, go back to the previously - * define tab label nr. */ - for (n = curitem - 1; n >= 0; --n) - if (item[n].type == TabPage && item[n].minwid >= 0) { - minwid = item[n].minwid; - break; - } - } else - /* close nrs are stored as negative values */ - minwid = -minwid; - } - item[curitem].type = TabPage; - item[curitem].start = p; - item[curitem].minwid = minwid; - s++; - curitem++; - continue; - } - if (*s == '.') { - s++; - if (VIM_ISDIGIT(*s)) { - maxwid = (int)getdigits(&s); - if (maxwid <= 0) /* overflow */ - maxwid = 50; - } - } - minwid = (minwid > 50 ? 50 : minwid) * l; - if (*s == '(') { - groupitem[groupdepth++] = curitem; - item[curitem].type = Group; - item[curitem].start = p; - item[curitem].minwid = minwid; - item[curitem].maxwid = maxwid; - s++; - curitem++; - continue; - } - if (vim_strchr(STL_ALL, *s) == NULL) { - s++; - continue; - } - opt = *s++; - - /* OK - now for the real work */ - base = 'D'; - itemisflag = FALSE; - fillable = TRUE; - num = -1; - str = NULL; - switch (opt) { - case STL_FILEPATH: - case STL_FULLPATH: - case STL_FILENAME: - fillable = FALSE; /* don't change ' ' to fillchar */ - if (buf_spname(wp->w_buffer) != NULL) - vim_strncpy(NameBuff, buf_spname(wp->w_buffer), MAXPATHL - 1); - else { - t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname - : wp->w_buffer->b_fname; - home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, TRUE); - } - trans_characters(NameBuff, MAXPATHL); - if (opt != STL_FILENAME) - str = NameBuff; - else - str = path_tail(NameBuff); - break; - - case STL_VIM_EXPR: /* '{' */ - itemisflag = TRUE; - t = p; - while (*s != '}' && *s != NUL && p + 1 < out + outlen) - *p++ = *s++; - if (*s != '}') /* missing '}' or out of space */ - break; - s++; - *p = 0; - p = t; - - vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum); - set_internal_string_var((char_u *)"actual_curbuf", tmp); - - o_curbuf = curbuf; - o_curwin = curwin; - curwin = wp; - curbuf = wp->w_buffer; - - str = eval_to_string_safe(p, &t, use_sandbox); - - curwin = o_curwin; - curbuf = o_curbuf; - do_unlet((char_u *)"g:actual_curbuf", TRUE); - - if (str != NULL && *str != 0) { - if (*skipdigits(str) == NUL) { - num = atoi((char *)str); - free(str); - str = NULL; - itemisflag = FALSE; - } - } - break; - - case STL_LINE: - num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) - ? 0L : (long)(wp->w_cursor.lnum); - break; - - case STL_NUMLINES: - num = wp->w_buffer->b_ml.ml_line_count; - break; - - case STL_COLUMN: - num = !(State & INSERT) && empty_line - ? 0 : (int)wp->w_cursor.col + 1; - break; - - case STL_VIRTCOL: - case STL_VIRTCOL_ALT: - /* In list mode virtcol needs to be recomputed */ - virtcol = wp->w_virtcol; - if (wp->w_p_list && lcs_tab1 == NUL) { - wp->w_p_list = FALSE; - getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); - wp->w_p_list = TRUE; - } - ++virtcol; - /* Don't display %V if it's the same as %c. */ - if (opt == STL_VIRTCOL_ALT - && (virtcol == (colnr_T)(!(State & INSERT) && empty_line - ? 0 : (int)wp->w_cursor.col + 1))) - break; - num = (long)virtcol; - break; - - case STL_PERCENTAGE: - num = (int)(((long)wp->w_cursor.lnum * 100L) / - (long)wp->w_buffer->b_ml.ml_line_count); - break; - - case STL_ALTPERCENT: - str = tmp; - get_rel_pos(wp, str, TMPLEN); - break; - - case STL_ARGLISTSTAT: - fillable = FALSE; - tmp[0] = 0; - if (append_arg_number(wp, tmp, (int)sizeof(tmp), FALSE)) - str = tmp; - break; - - case STL_KEYMAP: - fillable = FALSE; - if (get_keymap_str(wp, tmp, TMPLEN)) - str = tmp; - break; - case STL_PAGENUM: - num = printer_page_num; - break; - - case STL_BUFNO: - num = wp->w_buffer->b_fnum; - break; - - case STL_OFFSET_X: - base = 'X'; - case STL_OFFSET: - l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL); - num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ? - 0L : l + 1 + (!(State & INSERT) && empty_line ? - 0 : (int)wp->w_cursor.col); - break; - - case STL_BYTEVAL_X: - base = 'X'; - case STL_BYTEVAL: - num = byteval; - if (num == NL) - num = 0; - else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC) - num = NL; - break; - - case STL_ROFLAG: - case STL_ROFLAG_ALT: - itemisflag = TRUE; - if (wp->w_buffer->b_p_ro) - str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]")); - break; - - case STL_HELPFLAG: - case STL_HELPFLAG_ALT: - itemisflag = TRUE; - if (wp->w_buffer->b_help) - str = (char_u *)((opt == STL_HELPFLAG_ALT) ? ",HLP" - : _("[Help]")); - break; - - case STL_FILETYPE: - if (*wp->w_buffer->b_p_ft != NUL - && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) { - vim_snprintf((char *)tmp, sizeof(tmp), "[%s]", - wp->w_buffer->b_p_ft); - str = tmp; - } - break; - - case STL_FILETYPE_ALT: - itemisflag = TRUE; - if (*wp->w_buffer->b_p_ft != NUL - && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) { - vim_snprintf((char *)tmp, sizeof(tmp), ",%s", - wp->w_buffer->b_p_ft); - for (t = tmp; *t != 0; t++) - *t = TOUPPER_LOC(*t); - str = tmp; - } - break; - - case STL_PREVIEWFLAG: - case STL_PREVIEWFLAG_ALT: - itemisflag = TRUE; - if (wp->w_p_pvw) - str = (char_u *)((opt == STL_PREVIEWFLAG_ALT) ? ",PRV" - : _("[Preview]")); - break; - - case STL_QUICKFIX: - if (bt_quickfix(wp->w_buffer)) - str = (char_u *)(wp->w_llist_ref - ? _(msg_loclist) - : _(msg_qflist)); - break; - - case STL_MODIFIED: - case STL_MODIFIED_ALT: - itemisflag = TRUE; - switch ((opt == STL_MODIFIED_ALT) - + bufIsChanged(wp->w_buffer) * 2 - + (!wp->w_buffer->b_p_ma) * 4) { - case 2: str = (char_u *)"[+]"; break; - case 3: str = (char_u *)",+"; break; - case 4: str = (char_u *)"[-]"; break; - case 5: str = (char_u *)",-"; break; - case 6: str = (char_u *)"[+-]"; break; - case 7: str = (char_u *)",+-"; break; - } - break; - - case STL_HIGHLIGHT: - t = s; - while (*s != '#' && *s != NUL) - ++s; - if (*s == '#') { - item[curitem].type = Highlight; - item[curitem].start = p; - item[curitem].minwid = -syn_namen2id(t, (int)(s - t)); - curitem++; - } - if (*s != NUL) - ++s; - continue; - } - - item[curitem].start = p; - item[curitem].type = Normal; - if (str != NULL && *str) { - t = str; - if (itemisflag) { - if ((t[0] && t[1]) - && ((!prevchar_isitem && *t == ',') - || (prevchar_isflag && *t == ' '))) - t++; - prevchar_isflag = TRUE; - } - l = vim_strsize(t); - if (l > 0) - prevchar_isitem = TRUE; - if (l > maxwid) { - while (l >= maxwid) - if (has_mbyte) { - l -= ptr2cells(t); - t += (*mb_ptr2len)(t); - } else - l -= byte2cells(*t++); - if (p + 1 >= out + outlen) - break; - *p++ = '<'; - } - if (minwid > 0) { - for (; l < minwid && p + 1 < out + outlen; l++) { - /* Don't put a "-" in front of a digit. */ - if (l + 1 == minwid && fillchar == '-' && VIM_ISDIGIT(*t)) - *p++ = ' '; - else - *p++ = fillchar; - } - minwid = 0; - } else - minwid *= -1; - while (*t && p + 1 < out + outlen) { - *p++ = *t++; - /* Change a space by fillchar, unless fillchar is '-' and a - * digit follows. */ - if (fillable && p[-1] == ' ' - && (!VIM_ISDIGIT(*t) || fillchar != '-')) - p[-1] = fillchar; - } - for (; l < minwid && p + 1 < out + outlen; l++) - *p++ = fillchar; - } else if (num >= 0) { - int nbase = (base == 'D' ? 10 : (base == 'O' ? 8 : 16)); - char_u nstr[20]; - - if (p + 20 >= out + outlen) - break; /* not sufficient space */ - prevchar_isitem = TRUE; - t = nstr; - if (opt == STL_VIRTCOL_ALT) { - *t++ = '-'; - minwid--; - } - *t++ = '%'; - if (zeropad) - *t++ = '0'; - *t++ = '*'; - *t++ = nbase == 16 ? base : (char_u)(nbase == 8 ? 'o' : 'd'); - *t = 0; - - for (n = num, l = 1; n >= nbase; n /= nbase) - l++; - if (opt == STL_VIRTCOL_ALT) - l++; - if (l > maxwid) { - l += 2; - n = l - maxwid; - while (l-- > maxwid) - num /= nbase; - *t++ = '>'; - *t++ = '%'; - *t = t[-3]; - *++t = 0; - vim_snprintf((char *)p, outlen - (p - out), (char *)nstr, - 0, num, n); - } else - vim_snprintf((char *)p, outlen - (p - out), (char *)nstr, - minwid, num); - p += STRLEN(p); - } else - item[curitem].type = Empty; - - if (opt == STL_VIM_EXPR) - free(str); - - if (num >= 0 || (!itemisflag && str && *str)) - prevchar_isflag = FALSE; /* Item not NULL, but not a flag */ - curitem++; - } - *p = NUL; - itemcnt = curitem; - - if (usefmt != fmt) - free(usefmt); - - width = vim_strsize(out); - if (maxwidth > 0 && width > maxwidth) { - /* Result is too long, must truncate somewhere. */ - l = 0; - if (itemcnt == 0) - s = out; - else { - for (; l < itemcnt; l++) - if (item[l].type == Trunc) { - /* Truncate at %< item. */ - s = item[l].start; - break; - } - if (l == itemcnt) { - /* No %< item, truncate first item. */ - s = item[0].start; - l = 0; - } - } - - if (width - vim_strsize(s) >= maxwidth) { - /* Truncation mark is beyond max length */ - if (has_mbyte) { - s = out; - width = 0; - for (;; ) { - width += ptr2cells(s); - if (width >= maxwidth) - break; - s += (*mb_ptr2len)(s); - } - /* Fill up for half a double-wide character. */ - while (++width < maxwidth) - *s++ = fillchar; - } else - s = out + maxwidth - 1; - for (l = 0; l < itemcnt; l++) - if (item[l].start > s) - break; - itemcnt = l; - *s++ = '>'; - *s = 0; - } else { - if (has_mbyte) { - n = 0; - while (width >= maxwidth) { - width -= ptr2cells(s + n); - n += (*mb_ptr2len)(s + n); - } - } else - n = width - maxwidth + 1; - p = s + n; - STRMOVE(s + 1, p); - *s = '<'; - - /* Fill up for half a double-wide character. */ - while (++width < maxwidth) { - s = s + STRLEN(s); - *s++ = fillchar; - *s = NUL; - } - - --n; /* count the '<' */ - for (; l < itemcnt; l++) { - if (item[l].start - n >= s) - item[l].start -= n; - else - item[l].start = s; - } - } - width = maxwidth; - } else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 < - outlen) { - /* Apply STL_MIDDLE if any */ - for (l = 0; l < itemcnt; l++) - if (item[l].type == Middle) - break; - if (l < itemcnt) { - p = item[l].start + maxwidth - width; - STRMOVE(p, item[l].start); - for (s = item[l].start; s < p; s++) - *s = fillchar; - for (l++; l < itemcnt; l++) - item[l].start += maxwidth - width; - width = maxwidth; - } - } - - /* Store the info about highlighting. */ - if (hltab != NULL) { - sp = hltab; - for (l = 0; l < itemcnt; l++) { - if (item[l].type == Highlight) { - sp->start = item[l].start; - sp->userhl = item[l].minwid; - sp++; - } - } - sp->start = NULL; - sp->userhl = 0; - } - - /* Store the info about tab pages labels. */ - if (tabtab != NULL) { - sp = tabtab; - for (l = 0; l < itemcnt; l++) { - if (item[l].type == TabPage) { - sp->start = item[l].start; - sp->userhl = item[l].minwid; - sp++; - } - } - sp->start = NULL; - sp->userhl = 0; - } - - return width; -} - -#if defined(FEAT_STL_OPT) || defined(FEAT_CMDL_INFO) \ - || defined(FEAT_GUI_TABLINE) || defined(PROTO) -/* - * Get relative cursor position in window into "buf[buflen]", in the form 99%, - * using "Top", "Bot" or "All" when appropriate. - */ -void get_rel_pos(win_T *wp, char_u *buf, int buflen) -{ - long above; /* number of lines above window */ - long below; /* number of lines below window */ - - above = wp->w_topline - 1; - above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; - below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1; - if (below <= 0) - vim_strncpy(buf, (char_u *)(above == 0 ? _("All") : _("Bot")), - (size_t)(buflen - 1)); - else if (above <= 0) - vim_strncpy(buf, (char_u *)_("Top"), (size_t)(buflen - 1)); - else - vim_snprintf((char *)buf, (size_t)buflen, "%2d%%", above > 1000000L - ? (int)(above / ((above + below) / 100L)) - : (int)(above * 100L / (above + below))); -} -#endif - -/* - * Append (file 2 of 8) to "buf[buflen]", if editing more than one file. - * Return TRUE if it was appended. - */ -static int -append_arg_number ( - win_T *wp, - char_u *buf, - int buflen, - int add_file /* Add "file" before the arg number */ -) -{ - char_u *p; - - if (ARGCOUNT <= 1) /* nothing to do */ - return FALSE; - - p = buf + STRLEN(buf); /* go to the end of the buffer */ - if (p - buf + 35 >= buflen) /* getting too long */ - return FALSE; - *p++ = ' '; - *p++ = '('; - if (add_file) { - STRCPY(p, "file "); - p += 5; - } - vim_snprintf((char *)p, (size_t)(buflen - (p - buf)), - wp->w_arg_idx_invalid ? "(%d) of %d)" - : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); - return TRUE; -} - -/* - * Make "ffname" a full file name, set "sfname" to "ffname" if not NULL. - * "ffname" becomes a pointer to allocated memory (or NULL). - */ -void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) -{ - if (*ffname == NULL) /* if no file name given, nothing to do */ - return; - if (*sfname == NULL) /* if no short file name given, use ffname */ - *sfname = *ffname; - *ffname = fix_fname(*ffname); /* expand to full path */ - -#ifdef FEAT_SHORTCUT - if (!buf->b_p_bin) { - char_u *rfname; - - /* If the file name is a shortcut file, use the file it links to. */ - rfname = mch_resolve_shortcut(*ffname); - if (rfname != NULL) { - free(*ffname); - *ffname = rfname; - *sfname = rfname; - } - } -#endif -} - -/* - * Get the file name for an argument list entry. - */ -char_u *alist_name(aentry_T *aep) -{ - buf_T *bp; - - /* Use the name from the associated buffer if it exists. */ - bp = buflist_findnr(aep->ae_fnum); - if (bp == NULL || bp->b_fname == NULL) - return aep->ae_fname; - return bp->b_fname; -} - -/* - * do_arg_all(): Open up to 'count' windows, one for each argument. - */ -void -do_arg_all ( - int count, - int forceit, /* hide buffers in current windows */ - int keep_tabs /* keep current tabs, for ":tab drop file" */ -) -{ - int i; - win_T *wp, *wpnext; - char_u *opened; /* Array of weight for which args are open: - * 0: not opened - * 1: opened in other tab - * 2: opened in curtab - * 3: opened in curtab and curwin - */ - int opened_len; /* length of opened[] */ - int use_firstwin = FALSE; /* use first window for arglist */ - int split_ret = OK; - int p_ea_save; - alist_T *alist; /* argument list to be used */ - buf_T *buf; - tabpage_T *tpnext; - int had_tab = cmdmod.tab; - win_T *old_curwin, *last_curwin; - tabpage_T *old_curtab, *last_curtab; - win_T *new_curwin = NULL; - tabpage_T *new_curtab = NULL; - - if (ARGCOUNT <= 0) { - /* Don't give an error message. We don't want it when the ":all" - * command is in the .vimrc. */ - return; - } - setpcmark(); - - opened_len = ARGCOUNT; - opened = xcalloc(opened_len, 1); - - /* Autocommands may do anything to the argument list. Make sure it's not - * freed while we are working here by "locking" it. We still have to - * watch out for its size to be changed. */ - alist = curwin->w_alist; - ++alist->al_refcount; - - old_curwin = curwin; - old_curtab = curtab; - - - /* - * Try closing all windows that are not in the argument list. - * Also close windows that are not full width; - * When 'hidden' or "forceit" set the buffer becomes hidden. - * Windows that have a changed buffer and can't be hidden won't be closed. - * When the ":tab" modifier was used do this for all tab pages. - */ - if (had_tab > 0) - goto_tabpage_tp(first_tabpage, TRUE, TRUE); - for (;; ) { - tpnext = curtab->tp_next; - for (wp = firstwin; wp != NULL; wp = wpnext) { - wpnext = wp->w_next; - buf = wp->w_buffer; - if (buf->b_ffname == NULL - || (!keep_tabs && buf->b_nwindows > 1) - || wp->w_width != Columns - ) - i = opened_len; - else { - /* check if the buffer in this window is in the arglist */ - for (i = 0; i < opened_len; ++i) { - if (i < alist->al_ga.ga_len - && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum - || path_full_compare(alist_name(&AARGLIST(alist)[i]), - buf->b_ffname, TRUE) & kEqualFiles)) { - int weight = 1; - - if (old_curtab == curtab) { - ++weight; - if (old_curwin == wp) - ++weight; - } - - if (weight > (int)opened[i]) { - opened[i] = (char_u)weight; - if (i == 0) { - if (new_curwin != NULL) - new_curwin->w_arg_idx = opened_len; - new_curwin = wp; - new_curtab = curtab; - } - } else if (keep_tabs) - i = opened_len; - - if (wp->w_alist != alist) { - /* Use the current argument list for all windows - * containing a file from it. */ - alist_unlink(wp->w_alist); - wp->w_alist = alist; - ++wp->w_alist->al_refcount; - } - break; - } - } - } - wp->w_arg_idx = i; - - if (i == opened_len && !keep_tabs) { /* close this window */ - if (P_HID(buf) || forceit || buf->b_nwindows > 1 - || !bufIsChanged(buf)) { - /* If the buffer was changed, and we would like to hide it, - * try autowriting. */ - if (!P_HID(buf) && buf->b_nwindows <= 1 - && bufIsChanged(buf)) { - (void)autowrite(buf, FALSE); - /* check if autocommands removed the window */ - if (!win_valid(wp) || !buf_valid(buf)) { - wpnext = firstwin; /* start all over... */ - continue; - } - } - /* don't close last window */ - if (firstwin == lastwin - && (first_tabpage->tp_next == NULL || !had_tab)) - use_firstwin = TRUE; - else { - win_close(wp, !P_HID(buf) && !bufIsChanged(buf)); - /* check if autocommands removed the next window */ - if (!win_valid(wpnext)) - wpnext = firstwin; /* start all over... */ - } - } - } - } - - /* Without the ":tab" modifier only do the current tab page. */ - if (had_tab == 0 || tpnext == NULL) - break; - - /* check if autocommands removed the next tab page */ - if (!valid_tabpage(tpnext)) - tpnext = first_tabpage; /* start all over...*/ - goto_tabpage_tp(tpnext, TRUE, TRUE); - } - - /* - * Open a window for files in the argument list that don't have one. - * ARGCOUNT may change while doing this, because of autocommands. - */ - if (count > opened_len || count <= 0) - count = opened_len; - - /* Don't execute Win/Buf Enter/Leave autocommands here. */ - ++autocmd_no_enter; - ++autocmd_no_leave; - last_curwin = curwin; - last_curtab = curtab; - win_enter(lastwin, FALSE); - /* ":drop all" should re-use an empty window to avoid "--remote-tab" - * leaving an empty tab page when executed locally. */ - if (keep_tabs && bufempty() && curbuf->b_nwindows == 1 - && curbuf->b_ffname == NULL && !curbuf->b_changed) - use_firstwin = TRUE; - - for (i = 0; i < count && i < opened_len && !got_int; ++i) { - if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) - arg_had_last = TRUE; - if (opened[i] > 0) { - /* Move the already present window to below the current window */ - if (curwin->w_arg_idx != i) { - for (wpnext = firstwin; wpnext != NULL; wpnext = wpnext->w_next) { - if (wpnext->w_arg_idx == i) { - if (keep_tabs) { - new_curwin = wpnext; - new_curtab = curtab; - } else - win_move_after(wpnext, curwin); - break; - } - } - } - } else if (split_ret == OK) { - if (!use_firstwin) { /* split current window */ - p_ea_save = p_ea; - p_ea = TRUE; /* use space from all windows */ - split_ret = win_split(0, WSP_ROOM | WSP_BELOW); - p_ea = p_ea_save; - if (split_ret == FAIL) - continue; - } else /* first window: do autocmd for leaving this buffer */ - --autocmd_no_leave; - - /* - * edit file "i" - */ - curwin->w_arg_idx = i; - if (i == 0) { - new_curwin = curwin; - new_curtab = curtab; - } - (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, - ECMD_ONE, - ((P_HID(curwin->w_buffer) - || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) - + ECMD_OLDBUF, curwin); - if (use_firstwin) - ++autocmd_no_leave; - use_firstwin = FALSE; - } - ui_breakcheck(); - - /* When ":tab" was used open a new tab for a new window repeatedly. */ - if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) - cmdmod.tab = 9999; - } - - /* Remove the "lock" on the argument list. */ - alist_unlink(alist); - - --autocmd_no_enter; - /* restore last referenced tabpage's curwin */ - if (last_curtab != new_curtab) { - if (valid_tabpage(last_curtab)) - goto_tabpage_tp(last_curtab, TRUE, TRUE); - if (win_valid(last_curwin)) - win_enter(last_curwin, FALSE); - } - /* to window with first arg */ - if (valid_tabpage(new_curtab)) - goto_tabpage_tp(new_curtab, TRUE, TRUE); - if (win_valid(new_curwin)) - win_enter(new_curwin, FALSE); - - --autocmd_no_leave; - free(opened); -} - -/* - * Open a window for a number of buffers. - */ -void ex_buffer_all(exarg_T *eap) -{ - buf_T *buf; - win_T *wp, *wpnext; - int split_ret = OK; - int p_ea_save; - int open_wins = 0; - int r; - int count; /* Maximum number of windows to open. */ - int all; /* When TRUE also load inactive buffers. */ - int had_tab = cmdmod.tab; - tabpage_T *tpnext; - - if (eap->addr_count == 0) /* make as many windows as possible */ - count = 9999; - else - count = eap->line2; /* make as many windows as specified */ - if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide) - all = FALSE; - else - all = TRUE; - - setpcmark(); - - - /* - * Close superfluous windows (two windows for the same buffer). - * Also close windows that are not full-width. - */ - if (had_tab > 0) - goto_tabpage_tp(first_tabpage, TRUE, TRUE); - for (;; ) { - tpnext = curtab->tp_next; - for (wp = firstwin; wp != NULL; wp = wpnext) { - wpnext = wp->w_next; - if ((wp->w_buffer->b_nwindows > 1 - || ((cmdmod.split & WSP_VERT) - ? wp->w_height + wp->w_status_height < Rows - p_ch - - tabline_height() - : wp->w_width != Columns) - || (had_tab > 0 && wp != firstwin) - ) && firstwin != lastwin - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { - win_close(wp, FALSE); - wpnext = firstwin; /* just in case an autocommand does - something strange with windows */ - tpnext = first_tabpage; /* start all over...*/ - open_wins = 0; - } else - ++open_wins; - } - - /* Without the ":tab" modifier only do the current tab page. */ - if (had_tab == 0 || tpnext == NULL) - break; - goto_tabpage_tp(tpnext, TRUE, TRUE); - } - - /* - * Go through the buffer list. When a buffer doesn't have a window yet, - * open one. Otherwise move the window to the right position. - * Watch out for autocommands that delete buffers or windows! - */ - /* Don't execute Win/Buf Enter/Leave autocommands here. */ - ++autocmd_no_enter; - win_enter(lastwin, FALSE); - ++autocmd_no_leave; - for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) { - /* Check if this buffer needs a window */ - if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl) - continue; - - if (had_tab != 0) { - /* With the ":tab" modifier don't move the window. */ - if (buf->b_nwindows > 0) - wp = lastwin; /* buffer has a window, skip it */ - else - wp = NULL; - } else { - /* Check if this buffer already has a window */ - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer == buf) - break; - /* If the buffer already has a window, move it */ - if (wp != NULL) - win_move_after(wp, curwin); - } - - if (wp == NULL && split_ret == OK) { - /* Split the window and put the buffer in it */ - p_ea_save = p_ea; - p_ea = TRUE; /* use space from all windows */ - split_ret = win_split(0, WSP_ROOM | WSP_BELOW); - ++open_wins; - p_ea = p_ea_save; - if (split_ret == FAIL) - continue; - - /* Open the buffer in this window. */ -#if defined(HAS_SWAP_EXISTS_ACTION) - swap_exists_action = SEA_DIALOG; -#endif - set_curbuf(buf, DOBUF_GOTO); - if (!buf_valid(buf)) { /* autocommands deleted the buffer!!! */ -#if defined(HAS_SWAP_EXISTS_ACTION) - swap_exists_action = SEA_NONE; -# endif - break; - } -#if defined(HAS_SWAP_EXISTS_ACTION) - if (swap_exists_action == SEA_QUIT) { - cleanup_T cs; - - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a window. */ - enter_cleanup(&cs); - - /* User selected Quit at ATTENTION prompt; close this window. */ - win_close(curwin, TRUE); - --open_wins; - swap_exists_action = SEA_NONE; - swap_exists_did_quit = TRUE; - - /* Restore the error/interrupt/exception state if not - * discarded by a new aborting error, interrupt, or uncaught - * exception. */ - leave_cleanup(&cs); - } else - handle_swap_exists(NULL); -#endif - } - - ui_breakcheck(); - if (got_int) { - (void)vgetc(); /* only break the file loading, not the rest */ - break; - } - /* Autocommands deleted the buffer or aborted script processing!!! */ - if (aborting()) - break; - /* When ":tab" was used open a new tab for a new window repeatedly. */ - if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) - cmdmod.tab = 9999; - } - --autocmd_no_enter; - win_enter(firstwin, FALSE); /* back to first window */ - --autocmd_no_leave; - - /* - * Close superfluous windows. - */ - for (wp = lastwin; open_wins > count; ) { - r = (P_HID(wp->w_buffer) || !bufIsChanged(wp->w_buffer) - || autowrite(wp->w_buffer, FALSE) == OK); - if (!win_valid(wp)) { - /* BufWrite Autocommands made the window invalid, start over */ - wp = lastwin; - } else if (r) { - win_close(wp, !P_HID(wp->w_buffer)); - --open_wins; - wp = lastwin; - } else { - wp = wp->w_prev; - if (wp == NULL) - break; - } - } -} - - -static int chk_modeline(linenr_T, int); - -/* - * do_modelines() - process mode lines for the current file - * - * "flags" can be: - * OPT_WINONLY only set options local to window - * OPT_NOWIN don't set options local to window - * - * Returns immediately if the "ml" option isn't set. - */ -void do_modelines(int flags) -{ - linenr_T lnum; - int nmlines; - static int entered = 0; - - if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0) - return; - - /* Disallow recursive entry here. Can happen when executing a modeline - * triggers an autocommand, which reloads modelines with a ":do". */ - if (entered) - return; - - ++entered; - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines; - ++lnum) - if (chk_modeline(lnum, flags) == FAIL) - nmlines = 0; - - for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines - && lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum) - if (chk_modeline(lnum, flags) == FAIL) - nmlines = 0; - --entered; -} - -#include "version_defs.h" /* for version number */ - -/* - * chk_modeline() - check a single line for a mode string - * Return FAIL if an error encountered. - */ -static int -chk_modeline ( - linenr_T lnum, - int flags /* Same as for do_modelines(). */ -) -{ - char_u *s; - char_u *e; - char_u *linecopy; /* local copy of any modeline found */ - int prev; - int vers; - int end; - int retval = OK; - char_u *save_sourcing_name; - linenr_T save_sourcing_lnum; - scid_T save_SID; - - prev = -1; - for (s = ml_get(lnum); *s != NUL; ++s) { - if (prev == -1 || vim_isspace(prev)) { - if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0) - || STRNCMP(s, "vi:", (size_t)3) == 0) - break; - /* Accept both "vim" and "Vim". */ - if ((s[0] == 'v' || s[0] == 'V') && s[1] == 'i' && s[2] == 'm') { - if (s[3] == '<' || s[3] == '=' || s[3] == '>') - e = s + 4; - else - e = s + 3; - vers = getdigits(&e); - if (*e == ':' - && (s[0] != 'V' - || STRNCMP(skipwhite(e + 1), "set", 3) == 0) - && (s[3] == ':' - || (VIM_VERSION_100 >= vers && isdigit(s[3])) - || (VIM_VERSION_100 < vers && s[3] == '<') - || (VIM_VERSION_100 > vers && s[3] == '>') - || (VIM_VERSION_100 == vers && s[3] == '='))) - break; - } - } - prev = *s; - } - - if (*s) { - do /* skip over "ex:", "vi:" or "vim:" */ - ++s; - while (s[-1] != ':'); - - s = linecopy = vim_strsave(s); /* copy the line, it will change */ - if (linecopy == NULL) - return FAIL; - - save_sourcing_lnum = sourcing_lnum; - save_sourcing_name = sourcing_name; - sourcing_lnum = lnum; /* prepare for emsg() */ - sourcing_name = (char_u *)"modelines"; - - end = FALSE; - while (end == FALSE) { - s = skipwhite(s); - if (*s == NUL) - break; - - /* - * Find end of set command: ':' or end of line. - * Skip over "\:", replacing it with ":". - */ - for (e = s; *e != ':' && *e != NUL; ++e) - if (e[0] == '\\' && e[1] == ':') - STRMOVE(e, e + 1); - if (*e == NUL) - end = TRUE; - - /* - * If there is a "set" command, require a terminating ':' and - * ignore the stuff after the ':'. - * "vi:set opt opt opt: foo" -- foo not interpreted - * "vi:opt opt opt: foo" -- foo interpreted - * Accept "se" for compatibility with Elvis. - */ - if (STRNCMP(s, "set ", (size_t)4) == 0 - || STRNCMP(s, "se ", (size_t)3) == 0) { - if (*e != ':') /* no terminating ':'? */ - break; - end = TRUE; - s = vim_strchr(s, ' ') + 1; - } - *e = NUL; /* truncate the set command */ - - if (*s != NUL) { /* skip over an empty "::" */ - save_SID = current_SID; - current_SID = SID_MODELINE; - retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags); - current_SID = save_SID; - if (retval == FAIL) /* stop if error found */ - break; - } - s = e + 1; /* advance to next part */ - } - - sourcing_lnum = save_sourcing_lnum; - sourcing_name = save_sourcing_name; - - free(linecopy); - } - return retval; -} - -int read_viminfo_bufferlist(vir_T *virp, int writing) -{ - char_u *tab; - linenr_T lnum; - colnr_T col; - buf_T *buf; - char_u *sfname; - char_u *xline; - - /* Handle long line and escaped characters. */ - xline = viminfo_readstring(virp, 1, FALSE); - - /* don't read in if there are files on the command-line or if writing: */ - if (xline != NULL && !writing && ARGCOUNT == 0 - && find_viminfo_parameter('%') != NULL) { - /* Format is: Tab Tab . - * Watch out for a Tab in the file name, work from the end. */ - lnum = 0; - col = 0; - tab = vim_strrchr(xline, '\t'); - if (tab != NULL) { - *tab++ = '\0'; - col = (colnr_T)atoi((char *)tab); - tab = vim_strrchr(xline, '\t'); - if (tab != NULL) { - *tab++ = '\0'; - lnum = atol((char *)tab); - } - } - - /* Expand "~/" in the file name at "line + 1" to a full path. - * Then try shortening it by comparing with the current directory */ - expand_env(xline, NameBuff, MAXPATHL); - sfname = path_shorten_fname_if_possible(NameBuff); - - buf = buflist_new(NameBuff, sfname, (linenr_T)0, BLN_LISTED); - if (buf != NULL) { /* just in case... */ - buf->b_last_cursor.lnum = lnum; - buf->b_last_cursor.col = col; - buflist_setfpos(buf, curwin, lnum, col, FALSE); - } - } - free(xline); - - return viminfo_readline(virp); -} - -void write_viminfo_bufferlist(FILE *fp) -{ - buf_T *buf; - win_T *win; - tabpage_T *tp; - char_u *line; - int max_buffers; - - if (find_viminfo_parameter('%') == NULL) - return; - - /* Without a number -1 is returned: do all buffers. */ - max_buffers = get_viminfo_parameter('%'); - - /* Allocate room for the file name, lnum and col. */ -#define LINE_BUF_LEN (MAXPATHL + 40) - line = xmalloc(LINE_BUF_LEN); - - FOR_ALL_TAB_WINDOWS(tp, win) - set_last_cursor(win); - - fputs(_("\n# Buffer list:\n"), fp); - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (buf->b_fname == NULL - || !buf->b_p_bl - || bt_quickfix(buf) - || removable(buf->b_ffname)) - continue; - - if (max_buffers-- == 0) - break; - putc('%', fp); - home_replace(NULL, buf->b_ffname, line, MAXPATHL, TRUE); - vim_snprintf_add((char *)line, LINE_BUF_LEN, "\t%" PRId64 "\t%d", - (int64_t)buf->b_last_cursor.lnum, - buf->b_last_cursor.col); - viminfo_writestring(fp, line); - } - free(line); -} - - -/* - * Return special buffer name. - * Returns NULL when the buffer has a normal file name. - */ -char_u *buf_spname(buf_T *buf) -{ - if (bt_quickfix(buf)) { - win_T *win; - tabpage_T *tp; - - /* - * For location list window, w_llist_ref points to the location list. - * For quickfix window, w_llist_ref is NULL. - */ - if (find_win_for_buf(buf, &win, &tp) == OK && win->w_llist_ref != NULL) - return (char_u *)_(msg_loclist); - else - return (char_u *)_(msg_qflist); - } - /* There is no _file_ when 'buftype' is "nofile", b_sfname - * contains the name as specified by the user */ - if (bt_nofile(buf)) { - if (buf->b_sfname != NULL) - return buf->b_sfname; - return (char_u *)_("[Scratch]"); - } - if (buf->b_fname == NULL) - return (char_u *)_("[No Name]"); - return NULL; -} - -/* - * Find a window for buffer "buf". - * If found OK is returned and "wp" and "tp" are set to the window and tabpage. - * If not found FAIL is returned. - */ -int find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) -{ - FOR_ALL_TAB_WINDOWS(*tp, *wp) - if ((*wp)->w_buffer == buf) - goto win_found; - return FAIL; -win_found: - return OK; -} - -/* - * Insert the sign into the signlist. - */ -static void insert_sign( - buf_T *buf, /* buffer to store sign in */ - signlist_T *prev, /* previous sign entry */ - signlist_T *next, /* next sign entry */ - int id, /* sign ID */ - linenr_T lnum, /* line number which gets the mark */ - int typenr /* typenr of sign we are adding */ - ) -{ - signlist_T *newsign = xmalloc(sizeof(signlist_T)); - newsign->id = id; - newsign->lnum = lnum; - newsign->typenr = typenr; - newsign->next = next; - - if (prev == NULL) { - /* When adding first sign need to redraw the windows to create the - * column for signs. */ - if (buf->b_signlist == NULL) { - redraw_buf_later(buf, NOT_VALID); - changed_cline_bef_curs(); - } - - /* first sign in signlist */ - buf->b_signlist = newsign; - } - else { - prev->next = newsign; - } -} - -/* - * Add the sign into the signlist. Find the right spot to do it though. - */ -void buf_addsign( - buf_T *buf, /* buffer to store sign in */ - int id, /* sign ID */ - linenr_T lnum, /* line number which gets the mark */ - int typenr /* typenr of sign we are adding */ - ) -{ - signlist_T *sign; /* a sign in the signlist */ - signlist_T *prev; /* the previous sign */ - - prev = NULL; - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (lnum == sign->lnum && id == sign->id) { - sign->typenr = typenr; - return; - } - else if (id < 0 /* keep signs sorted by lnum */ - && lnum < sign->lnum) - { - insert_sign(buf, prev, sign, id, lnum, typenr); - return; - } - prev = sign; - } - insert_sign(buf, prev, sign, id, lnum, typenr); - - return; -} - -linenr_T buf_change_sign_type( - buf_T *buf, /* buffer to store sign in */ - int markId, /* sign ID */ - int typenr /* typenr of sign we are adding */ - ) -{ - signlist_T *sign; /* a sign in the signlist */ - - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->id == markId) { - sign->typenr = typenr; - return sign->lnum; - } - } - - return (linenr_T)0; -} - -int buf_getsigntype( - buf_T *buf, - linenr_T lnum, - int type /* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */ - ) -{ - signlist_T *sign; /* a sign in a b_signlist */ - - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->lnum == lnum - && (type == SIGN_ANY - || (type == SIGN_TEXT - && sign_get_text(sign->typenr) != NULL) - || (type == SIGN_LINEHL - && sign_get_attr(sign->typenr, TRUE) != 0))) { - return sign->typenr; - } - } - return 0; -} - - -linenr_T buf_delsign( - buf_T *buf, /* buffer sign is stored in */ - int id /* sign id */ - ) -{ - signlist_T **lastp; /* pointer to pointer to current sign */ - signlist_T *sign; /* a sign in a b_signlist */ - signlist_T *next; /* the next sign in a b_signlist */ - linenr_T lnum; /* line number whose sign was deleted */ - - lastp = &buf->b_signlist; - lnum = 0; - for (sign = buf->b_signlist; sign != NULL; sign = next) { - next = sign->next; - if (sign->id == id) { - *lastp = next; - lnum = sign->lnum; - free(sign); - break; - } else { - lastp = &sign->next; - } - } - - /* When deleted the last sign needs to redraw the windows to remove the - * sign column. */ - if (buf->b_signlist == NULL) { - redraw_buf_later(buf, NOT_VALID); - changed_cline_bef_curs(); - } - - return lnum; -} - - -/* - * Find the line number of the sign with the requested id. If the sign does - * not exist, return 0 as the line number. This will still let the correct file - * get loaded. - */ -int buf_findsign( - buf_T *buf, /* buffer to store sign in */ - int id /* sign ID */ - ) -{ - signlist_T *sign; /* a sign in the signlist */ - - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->id == id) { - return sign->lnum; - } - } - - return 0; -} - -int buf_findsign_id( - buf_T *buf, /* buffer whose sign we are searching for */ - linenr_T lnum /* line number of sign */ - ) -{ - signlist_T *sign; /* a sign in the signlist */ - - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->lnum == lnum) { - return sign->id; - } - } - - return 0; -} - - -/* - * Delete signs in buffer "buf". - */ -void buf_delete_signs(buf_T *buf) -{ - signlist_T *next; - - while (buf->b_signlist != NULL) { - next = buf->b_signlist->next; - free(buf->b_signlist); - buf->b_signlist = next; - } -} - -/* - * Delete all signs in all buffers. - */ -void buf_delete_all_signs() -{ - buf_T *buf; /* buffer we are checking for signs */ - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (buf->b_signlist != NULL) { - /* Need to redraw the windows to remove the sign column. */ - redraw_buf_later(buf, NOT_VALID); - buf_delete_signs(buf); - } - } -} - -/* - * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. - */ -void sign_list_placed(buf_T *rbuf) -{ - buf_T *buf; - signlist_T *p; - char lbuf[BUFSIZ]; - - MSG_PUTS_TITLE(_("\n--- Signs ---")); - msg_putchar('\n'); - if (rbuf == NULL) { - buf = firstbuf; - } else { - buf = rbuf; - } - while (buf != NULL && !got_int) { - if (buf->b_signlist != NULL) { - vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname); - MSG_PUTS_ATTR(lbuf, hl_attr(HLF_D)); - msg_putchar('\n'); - } - for (p = buf->b_signlist; p != NULL && !got_int; p = p->next) { - vim_snprintf(lbuf, BUFSIZ, _(" line=%" PRId64 " id=%d name=%s"), - (int64_t)p->lnum, p->id, sign_typenr2name(p->typenr)); - MSG_PUTS(lbuf); - msg_putchar('\n'); - } - if (rbuf != NULL) { - break; - } - buf = buf->b_next; - } -} - -/* - * Adjust a placed sign for inserted/deleted lines. - */ -void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) -{ - signlist_T *sign; /* a sign in a b_signlist */ - - for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->lnum >= line1 && sign->lnum <= line2) { - if (amount == MAXLNUM) { - sign->lnum = line1; - } else { - sign->lnum += amount; - } - } - else if (sign->lnum > line2) - sign->lnum += amount_after; - } -} - -/* - * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. - */ -void set_buflisted(int on) -{ - if (on != curbuf->b_p_bl) { - curbuf->b_p_bl = on; - if (on) - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf); - else - apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); - } -} - -/* - * Read the file for "buf" again and check if the contents changed. - * Return TRUE if it changed or this could not be checked. - */ -int buf_contents_changed(buf_T *buf) -{ - buf_T *newbuf; - int differ = TRUE; - linenr_T lnum; - aco_save_T aco; - exarg_T ea; - - /* Allocate a buffer without putting it in the buffer list. */ - newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); - if (newbuf == NULL) - return TRUE; - - /* Force the 'fileencoding' and 'fileformat' to be equal. */ - prep_exarg(&ea, buf); - - /* set curwin/curbuf to buf and save a few things */ - aucmd_prepbuf(&aco, newbuf); - - if (ml_open(curbuf) == OK - && readfile(buf->b_ffname, buf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, - &ea, READ_NEW | READ_DUMMY) == OK) { - /* compare the two files line by line */ - if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) { - differ = FALSE; - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) - if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0) { - differ = TRUE; - break; - } - } - } - free(ea.cmd); - - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - - if (curbuf != newbuf) /* safety check */ - wipe_buffer(newbuf, FALSE); - - return differ; -} - -/* - * Wipe out a buffer and decrement the last buffer number if it was used for - * this buffer. Call this to wipe out a temp buffer that does not contain any - * marks. - */ -void -wipe_buffer ( - buf_T *buf, - int aucmd /* When TRUE trigger autocommands. */ -) -{ - if (buf->b_fnum == top_file_num - 1) - --top_file_num; - - if (!aucmd) /* Don't trigger BufDelete autocommands here. */ - block_autocmds(); - close_buffer(NULL, buf, DOBUF_WIPE, FALSE); - if (!aucmd) - unblock_autocmds(); -} diff --git a/src/buffer.h b/src/buffer.h deleted file mode 100644 index 8c85d0363a..0000000000 --- a/src/buffer.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef NEOVIM_BUFFER_H -#define NEOVIM_BUFFER_H -/* buffer.c */ -int open_buffer(int read_stdin, exarg_T *eap, int flags); -int buf_valid(buf_T *buf); -void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last); -void buf_clear_file(buf_T *buf); -void buf_freeall(buf_T *buf, int flags); -void goto_buffer(exarg_T *eap, int start, int dir, int count); -void handle_swap_exists(buf_T *old_curbuf); -char_u *do_bufdel(int command, char_u *arg, int addr_count, - int start_bnr, int end_bnr, - int forceit); -int do_buffer(int action, int start, int dir, int count, int forceit); -void set_curbuf(buf_T *buf, int action); -void enter_buffer(buf_T *buf); -void do_autochdir(void); -buf_T *buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, - int flags); -void free_buf_options(buf_T *buf, int free_p_ff); -int buflist_getfile(int n, linenr_T lnum, int options, int forceit); -void buflist_getfpos(void); -buf_T *buflist_findname_exp(char_u *fname); -buf_T *buflist_findname(char_u *ffname); -int buflist_findpat(char_u *pattern, char_u *pattern_end, int unlisted, - int diffmode, - int curtab_only); -int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, - int options); -buf_T *buflist_findnr(int nr); -char_u *buflist_nr2name(int n, int fullname, int helptail); -void get_winopts(buf_T *buf); -pos_T *buflist_findfpos(buf_T *buf); -linenr_T buflist_findlnum(buf_T *buf); -void buflist_list(exarg_T *eap); -int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum); -int setfname(buf_T *buf, char_u *ffname, char_u *sfname, int message); -void buf_set_name(int fnum, char_u *name); -void buf_name_changed(buf_T *buf); -buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum); -char_u *getaltfname(int errmsg); -int buflist_add(char_u *fname, int flags); -void buflist_slash_adjust(void); -void buflist_altfpos(win_T *win); -int otherfile(char_u *ffname); -void buf_setino(buf_T *buf); -void fileinfo(int fullname, int shorthelp, int dont_truncate); -void col_print(char_u *buf, size_t buflen, int col, int vcol); -void maketitle(void); -void resettitle(void); -void free_titles(void); -int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, - int use_sandbox, int fillchar, int maxwidth, - struct stl_hlrec *hltab, - struct stl_hlrec *tabtab); -void get_rel_pos(win_T *wp, char_u *buf, int buflen); -void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname); -char_u *alist_name(aentry_T *aep); -void do_arg_all(int count, int forceit, int keep_tabs); -void ex_buffer_all(exarg_T *eap); -void do_modelines(int flags); -int read_viminfo_bufferlist(vir_T *virp, int writing); -void write_viminfo_bufferlist(FILE *fp); -char_u *buf_spname(buf_T *buf); -int find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp); -void buf_addsign(buf_T *buf, int id, linenr_T lnum, int typenr); -linenr_T buf_change_sign_type(buf_T *buf, int markId, int typenr); -int buf_getsigntype(buf_T *buf, linenr_T lnum, int type); -linenr_T buf_delsign(buf_T *buf, int id); -int buf_findsign(buf_T *buf, int id); -int buf_findsign_id(buf_T *buf, linenr_T lnum); -void buf_delete_signs(buf_T *buf); -void buf_delete_all_signs(void); -void sign_list_placed(buf_T *rbuf); -void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, - long amount_after); -void set_buflisted(int on); -int buf_contents_changed(buf_T *buf); -void wipe_buffer(buf_T *buf, int aucmd); - -#endif /* NEOVIM_BUFFER_H */ diff --git a/src/buffer_defs.h b/src/buffer_defs.h deleted file mode 100644 index d78cf0651f..0000000000 --- a/src/buffer_defs.h +++ /dev/null @@ -1,1081 +0,0 @@ -#ifndef NEOVIM_BUFFER_DEFS_H -#define NEOVIM_BUFFER_DEFS_H - -// for garray_T -#include "garray.h" -// for pos_T and lpos_T -#include "pos.h" -// for the number window-local and buffer-local options -#include "option_defs.h" -// for jump list and tag stack sizes in a buffer and mark types -#include "mark_defs.h" -// for u_header_T -#include "undo_defs.h" -// for hashtab_T -#include "hashtab.h" -// for dict_T -#include "eval_defs.h" - -typedef struct window_S win_T; -typedef struct wininfo_S wininfo_T; -typedef struct frame_S frame_T; -typedef int scid_T; /* script ID */ -typedef struct file_buffer buf_T; /* forward declaration */ -typedef struct memfile memfile_T; - -// for struct memline (it needs memfile_T) -#include "memline_defs.h" - -// for struct memfile, bhdr_T, blocknr_T... (it needs buf_T) -#include "memfile_defs.h" - -/* - * This is here because regexp_defs.h needs win_T and buf_T. regprog_T is - * used below. - */ -#include "regexp_defs.h" - -// for synstate_T (needs reg_extmatch_T, win_T and buf_T) -#include "syntax_defs.h" - -// for signlist_T -#include "sign_defs.h" - -/* - * The taggy struct is used to store the information about a :tag command. - */ -typedef struct taggy { - char_u *tagname; /* tag name */ - fmark_T fmark; /* cursor position BEFORE ":tag" */ - int cur_match; /* match number */ - int cur_fnum; /* buffer number used for cur_match */ -} taggy_T; - -typedef struct buffblock buffblock_T; -typedef struct buffheader buffheader_T; - -/* - * structure used to store one block of the stuff/redo/recording buffers - */ -struct buffblock { - buffblock_T *b_next; // pointer to next buffblock - char_u b_str[1]; // contents (actually longer) -}; - -/* - * header used for the stuff buffer and the redo buffer - */ -struct buffheader { - buffblock_T bh_first; // first (dummy) block of list - buffblock_T *bh_curr; // buffblock for appending - int bh_index; // index for reading - int bh_space; // space in bh_curr for appending -}; - -/* - * Structure that contains all options that are local to a window. - * Used twice in a window: for the current buffer and for all buffers. - * Also used in wininfo_T. - */ -typedef struct { - int wo_arab; -# define w_p_arab w_onebuf_opt.wo_arab /* 'arabic' */ - int wo_diff; -# define w_p_diff w_onebuf_opt.wo_diff /* 'diff' */ - long wo_fdc; -# define w_p_fdc w_onebuf_opt.wo_fdc /* 'foldcolumn' */ - int wo_fdc_save; -# define w_p_fdc_save w_onebuf_opt.wo_fdc_save /* 'foldenable' saved for diff mode */ - int wo_fen; -# define w_p_fen w_onebuf_opt.wo_fen /* 'foldenable' */ - int wo_fen_save; -# define w_p_fen_save w_onebuf_opt.wo_fen_save /* 'foldenable' saved for diff mode */ - char_u *wo_fdi; -# define w_p_fdi w_onebuf_opt.wo_fdi /* 'foldignore' */ - long wo_fdl; -# define w_p_fdl w_onebuf_opt.wo_fdl /* 'foldlevel' */ - int wo_fdl_save; -# define w_p_fdl_save w_onebuf_opt.wo_fdl_save /* 'foldlevel' state saved for diff mode */ - char_u *wo_fdm; -# define w_p_fdm w_onebuf_opt.wo_fdm /* 'foldmethod' */ - char_u *wo_fdm_save; -# define w_p_fdm_save w_onebuf_opt.wo_fdm_save /* 'fdm' saved for diff mode */ - long wo_fml; -# define w_p_fml w_onebuf_opt.wo_fml /* 'foldminlines' */ - long wo_fdn; -# define w_p_fdn w_onebuf_opt.wo_fdn /* 'foldnestmax' */ - char_u *wo_fde; -# define w_p_fde w_onebuf_opt.wo_fde /* 'foldexpr' */ - char_u *wo_fdt; -# define w_p_fdt w_onebuf_opt.wo_fdt /* 'foldtext' */ - char_u *wo_fmr; -# define w_p_fmr w_onebuf_opt.wo_fmr /* 'foldmarker' */ - int wo_lbr; -# define w_p_lbr w_onebuf_opt.wo_lbr /* 'linebreak' */ - int wo_list; -#define w_p_list w_onebuf_opt.wo_list /* 'list' */ - int wo_nu; -#define w_p_nu w_onebuf_opt.wo_nu /* 'number' */ - int wo_rnu; -#define w_p_rnu w_onebuf_opt.wo_rnu /* 'relativenumber' */ - long wo_nuw; -# define w_p_nuw w_onebuf_opt.wo_nuw /* 'numberwidth' */ - int wo_wfh; -# define w_p_wfh w_onebuf_opt.wo_wfh /* 'winfixheight' */ - int wo_wfw; -# define w_p_wfw w_onebuf_opt.wo_wfw /* 'winfixwidth' */ - int wo_pvw; -# define w_p_pvw w_onebuf_opt.wo_pvw /* 'previewwindow' */ - int wo_rl; -# define w_p_rl w_onebuf_opt.wo_rl /* 'rightleft' */ - char_u *wo_rlc; -# define w_p_rlc w_onebuf_opt.wo_rlc /* 'rightleftcmd' */ - long wo_scr; -#define w_p_scr w_onebuf_opt.wo_scr /* 'scroll' */ - int wo_spell; -# define w_p_spell w_onebuf_opt.wo_spell /* 'spell' */ - int wo_cuc; -# define w_p_cuc w_onebuf_opt.wo_cuc /* 'cursorcolumn' */ - int wo_cul; -# define w_p_cul w_onebuf_opt.wo_cul /* 'cursorline' */ - char_u *wo_cc; -# define w_p_cc w_onebuf_opt.wo_cc /* 'colorcolumn' */ - char_u *wo_stl; -#define w_p_stl w_onebuf_opt.wo_stl /* 'statusline' */ - int wo_scb; -# define w_p_scb w_onebuf_opt.wo_scb /* 'scrollbind' */ - int wo_diff_saved; /* options were saved for starting diff mode */ -# define w_p_diff_saved w_onebuf_opt.wo_diff_saved - int wo_scb_save; /* 'scrollbind' saved for diff mode*/ -# define w_p_scb_save w_onebuf_opt.wo_scb_save - int wo_wrap; -#define w_p_wrap w_onebuf_opt.wo_wrap /* 'wrap' */ - int wo_wrap_save; /* 'wrap' state saved for diff mode*/ -# define w_p_wrap_save w_onebuf_opt.wo_wrap_save - char_u *wo_cocu; /* 'concealcursor' */ -# define w_p_cocu w_onebuf_opt.wo_cocu - long wo_cole; /* 'conceallevel' */ -# define w_p_cole w_onebuf_opt.wo_cole - int wo_crb; -# define w_p_crb w_onebuf_opt.wo_crb /* 'cursorbind' */ - int wo_crb_save; /* 'cursorbind' state saved for diff mode*/ -# define w_p_crb_save w_onebuf_opt.wo_crb_save - - int wo_scriptID[WV_COUNT]; /* SIDs for window-local options */ -# define w_p_scriptID w_onebuf_opt.wo_scriptID -} winopt_T; - -/* - * Window info stored with a buffer. - * - * Two types of info are kept for a buffer which are associated with a - * specific window: - * 1. Each window can have a different line number associated with a buffer. - * 2. The window-local options for a buffer work in a similar way. - * The window-info is kept in a list at b_wininfo. It is kept in - * most-recently-used order. - */ -struct wininfo_S { - wininfo_T *wi_next; /* next entry or NULL for last entry */ - wininfo_T *wi_prev; /* previous entry or NULL for first entry */ - win_T *wi_win; /* pointer to window that did set wi_fpos */ - pos_T wi_fpos; /* last cursor position in the file */ - int wi_optset; /* TRUE when wi_opt has useful values */ - winopt_T wi_opt; /* local window options */ - int wi_fold_manual; /* copy of w_fold_manual */ - garray_T wi_folds; /* clone of w_folds */ -}; - -/* - * Argument list: Array of file names. - * Used for the global argument list and the argument lists local to a window. - * - * TODO: move struct arglist to another header - */ -typedef struct arglist { - garray_T al_ga; /* growarray with the array of file names */ - int al_refcount; /* number of windows using this arglist */ -} alist_T; - -/* - * For each argument remember the file name as it was given, and the buffer - * number that contains the expanded file name (required for when ":cd" is - * used. - * - * TODO: move aentry_T to another header - */ -typedef struct argentry { - char_u *ae_fname; /* file name as specified */ - int ae_fnum; /* buffer number with expanded file name */ -} aentry_T; - -# define ALIST(win) (win)->w_alist -#define GARGLIST ((aentry_T *)global_alist.al_ga.ga_data) -#define ARGLIST ((aentry_T *)ALIST(curwin)->al_ga.ga_data) -#define WARGLIST(wp) ((aentry_T *)ALIST(wp)->al_ga.ga_data) -#define AARGLIST(al) ((aentry_T *)((al)->al_ga.ga_data)) -#define GARGCOUNT (global_alist.al_ga.ga_len) -#define ARGCOUNT (ALIST(curwin)->al_ga.ga_len) -#define WARGCOUNT(wp) (ALIST(wp)->al_ga.ga_len) - -#ifdef USE_ICONV -# ifdef HAVE_ICONV_H -# include -# else -# include -typedef void *iconv_t; -# endif -#endif - -/* - * Used for the typeahead buffer: typebuf. - */ -typedef struct { - char_u *tb_buf; /* buffer for typed characters */ - char_u *tb_noremap; /* mapping flags for characters in tb_buf[] */ - int tb_buflen; /* size of tb_buf[] */ - int tb_off; /* current position in tb_buf[] */ - int tb_len; /* number of valid bytes in tb_buf[] */ - int tb_maplen; /* nr of mapped bytes in tb_buf[] */ - int tb_silent; /* nr of silently mapped bytes in tb_buf[] */ - int tb_no_abbr_cnt; /* nr of bytes without abbrev. in tb_buf[] */ - int tb_change_cnt; /* nr of time tb_buf was changed; never zero */ -} typebuf_T; - -/* Struct to hold the saved typeahead for save_typeahead(). */ -typedef struct { - typebuf_T save_typebuf; - int typebuf_valid; /* TRUE when save_typebuf valid */ - int old_char; - int old_mod_mask; - buffheader_T save_readbuf1; - buffheader_T save_readbuf2; -#ifdef USE_INPUT_BUF - char_u *save_inputbuf; -#endif -} tasave_T; - -/* - * Used for conversion of terminal I/O and script files. - */ -typedef struct { - int vc_type; /* zero or one of the CONV_ values */ - int vc_factor; /* max. expansion factor */ -# ifdef USE_ICONV - iconv_t vc_fd; /* for CONV_ICONV */ -# endif - int vc_fail; /* fail for invalid char, don't use '?' */ -} vimconv_T; - -/* - * Structure used for reading from the viminfo file. - */ -typedef struct { - char_u *vir_line; /* text of the current line */ - FILE *vir_fd; /* file descriptor */ - vimconv_T vir_conv; /* encoding conversion */ -} vir_T; - -#define CONV_NONE 0 -#define CONV_TO_UTF8 1 -#define CONV_9_TO_UTF8 2 -#define CONV_TO_LATIN1 3 -#define CONV_TO_LATIN9 4 -#define CONV_ICONV 5 - -/* - * Structure used for mappings and abbreviations. - */ -typedef struct mapblock mapblock_T; -struct mapblock { - mapblock_T *m_next; /* next mapblock in list */ - char_u *m_keys; /* mapped from, lhs */ - char_u *m_str; /* mapped to, rhs */ - char_u *m_orig_str; /* rhs as entered by the user */ - int m_keylen; /* strlen(m_keys) */ - int m_mode; /* valid mode */ - int m_noremap; /* if non-zero no re-mapping for m_str */ - char m_silent; /* used, don't echo commands */ - char m_nowait; /* used */ - char m_expr; /* used, m_str is an expression */ - scid_T m_script_ID; /* ID of script where map was defined */ -}; - -/* - * Used for highlighting in the status line. - */ -struct stl_hlrec { - char_u *start; - int userhl; /* 0: no HL, 1-9: User HL, < 0 for syn ID */ -}; - -/* values for b_syn_spell: what to do with toplevel text */ -#define SYNSPL_DEFAULT 0 /* spell check if @Spell not defined */ -#define SYNSPL_TOP 1 /* spell check toplevel text */ -#define SYNSPL_NOTOP 2 /* don't spell check toplevel text */ - -/* avoid #ifdefs for when b_spell is not available */ -# define B_SPELL(buf) ((buf)->b_spell) - -typedef struct qf_info_S qf_info_T; - -/* - * Used for :syntime: timing of executing a syntax pattern. - */ -typedef struct { - proftime_T total; /* total time used */ - proftime_T slowest; /* time of slowest call */ - long count; /* nr of times used */ - long match; /* nr of times matched */ -} syn_time_T; - -/* - * These are items normally related to a buffer. But when using ":ownsyntax" - * a window may have its own instance. - */ -typedef struct { - hashtab_T b_keywtab; /* syntax keywords hash table */ - hashtab_T b_keywtab_ic; /* idem, ignore case */ - int b_syn_error; /* TRUE when error occurred in HL */ - int b_syn_ic; /* ignore case for :syn cmds */ - int b_syn_spell; /* SYNSPL_ values */ - garray_T b_syn_patterns; /* table for syntax patterns */ - garray_T b_syn_clusters; /* table for syntax clusters */ - int b_spell_cluster_id; /* @Spell cluster ID or 0 */ - int b_nospell_cluster_id; /* @NoSpell cluster ID or 0 */ - int b_syn_containedin; /* TRUE when there is an item with a - "containedin" argument */ - int b_syn_sync_flags; /* flags about how to sync */ - short b_syn_sync_id; /* group to sync on */ - long b_syn_sync_minlines; /* minimal sync lines offset */ - long b_syn_sync_maxlines; /* maximal sync lines offset */ - long b_syn_sync_linebreaks; /* offset for multi-line pattern */ - char_u *b_syn_linecont_pat; /* line continuation pattern */ - regprog_T *b_syn_linecont_prog; /* line continuation program */ - syn_time_T b_syn_linecont_time; - int b_syn_linecont_ic; /* ignore-case flag for above */ - int b_syn_topgrp; /* for ":syntax include" */ - int b_syn_conceal; /* auto-conceal for :syn cmds */ - int b_syn_folditems; /* number of patterns with the HL_FOLD - flag set */ - /* - * b_sst_array[] contains the state stack for a number of lines, for the - * start of that line (col == 0). This avoids having to recompute the - * syntax state too often. - * b_sst_array[] is allocated to hold the state for all displayed lines, - * and states for 1 out of about 20 other lines. - * b_sst_array pointer to an array of synstate_T - * b_sst_len number of entries in b_sst_array[] - * b_sst_first pointer to first used entry in b_sst_array[] or NULL - * b_sst_firstfree pointer to first free entry in b_sst_array[] or NULL - * b_sst_freecount number of free entries in b_sst_array[] - * b_sst_check_lnum entries after this lnum need to be checked for - * validity (MAXLNUM means no check needed) - */ - synstate_T *b_sst_array; - int b_sst_len; - synstate_T *b_sst_first; - synstate_T *b_sst_firstfree; - int b_sst_freecount; - linenr_T b_sst_check_lnum; - uint16_t b_sst_lasttick; /* last display tick */ - - /* for spell checking */ - garray_T b_langp; /* list of pointers to slang_T, see spell.c */ - char_u b_spell_ismw[256]; /* flags: is midword char */ - char_u *b_spell_ismw_mb; /* multi-byte midword chars */ - char_u *b_p_spc; /* 'spellcapcheck' */ - regprog_T *b_cap_prog; /* program for 'spellcapcheck' */ - char_u *b_p_spf; /* 'spellfile' */ - char_u *b_p_spl; /* 'spelllang' */ - int b_cjk; /* all CJK letters as OK */ -} synblock_T; - - -/* - * buffer: structure that holds information about one file - * - * Several windows can share a single Buffer - * A buffer is unallocated if there is no memfile for it. - * A buffer is new if the associated file has never been loaded yet. - */ - -struct file_buffer { - memline_T b_ml; /* associated memline (also contains line - count) */ - - buf_T *b_next; /* links in list of buffers */ - buf_T *b_prev; - - int b_nwindows; /* nr of windows open on this buffer */ - - int b_flags; /* various BF_ flags */ - int b_closing; /* buffer is being closed, don't let - autocommands close it too. */ - - /* - * b_ffname has the full path of the file (NULL for no name). - * b_sfname is the name as the user typed it (or NULL). - * b_fname is the same as b_sfname, unless ":cd" has been done, - * then it is the same as b_ffname (NULL for no name). - */ - char_u *b_ffname; /* full path file name */ - char_u *b_sfname; /* short file name */ - char_u *b_fname; /* current file name */ - - int b_dev_valid; /* TRUE when b_dev has a valid number */ - uint64_t b_dev; /* device number */ - uint64_t b_ino; /* inode number */ - - int b_fnum; /* buffer number for this file. */ - - int b_changed; /* 'modified': Set to TRUE if something in the - file has been changed and not written out. */ - int b_changedtick; /* incremented for each change, also for undo */ - - int b_saving; /* Set to TRUE if we are in the middle of - saving the buffer. */ - - /* - * Changes to a buffer require updating of the display. To minimize the - * work, remember changes made and update everything at once. - */ - int b_mod_set; /* TRUE when there are changes since the last - time the display was updated */ - linenr_T b_mod_top; /* topmost lnum that was changed */ - linenr_T b_mod_bot; /* lnum below last changed line, AFTER the - change */ - long b_mod_xlines; /* number of extra buffer lines inserted; - negative when lines were deleted */ - - wininfo_T *b_wininfo; /* list of last used info for each window */ - - long b_mtime; /* last change time of original file */ - long b_mtime_read; /* last change time when reading */ - off_t b_orig_size; /* size of original file in bytes */ - int b_orig_mode; /* mode of original file */ - - pos_T b_namedm[NMARKS]; /* current named marks (mark.c) */ - - /* These variables are set when VIsual_active becomes FALSE */ - visualinfo_T b_visual; - int b_visual_mode_eval; /* b_visual.vi_mode for visualmode() */ - - pos_T b_last_cursor; /* cursor position when last unloading this - buffer */ - pos_T b_last_insert; /* where Insert mode was left */ - pos_T b_last_change; /* position of last change: '. mark */ - - /* - * the changelist contains old change positions - */ - pos_T b_changelist[JUMPLISTSIZE]; - int b_changelistlen; /* number of active entries */ - int b_new_change; /* set by u_savecommon() */ - - /* - * Character table, only used in charset.c for 'iskeyword' - * 32 bytes of 8 bits: 1 bit per character 0-255. - */ - char_u b_chartab[32]; - - /* Table used for mappings local to a buffer. */ - mapblock_T *(b_maphash[256]); - - /* First abbreviation local to a buffer. */ - mapblock_T *b_first_abbr; - /* User commands local to the buffer. */ - garray_T b_ucmds; - /* - * start and end of an operator, also used for '[ and '] - */ - pos_T b_op_start; - pos_T b_op_start_orig; // used for Insstart_orig - pos_T b_op_end; - - int b_marks_read; /* Have we read viminfo marks yet? */ - - /* - * The following only used in undo.c. - */ - u_header_T *b_u_oldhead; /* pointer to oldest header */ - u_header_T *b_u_newhead; /* pointer to newest header; may not be valid - if b_u_curhead is not NULL */ - u_header_T *b_u_curhead; /* pointer to current header */ - int b_u_numhead; /* current number of headers */ - int b_u_synced; /* entry lists are synced */ - long b_u_seq_last; /* last used undo sequence number */ - long b_u_save_nr_last; /* counter for last file write */ - long b_u_seq_cur; /* hu_seq of header below which we are now */ - time_t b_u_time_cur; /* uh_time of header below which we are now */ - long b_u_save_nr_cur; /* file write nr after which we are now */ - - /* - * variables for "U" command in undo.c - */ - char_u *b_u_line_ptr; /* saved line for "U" command */ - linenr_T b_u_line_lnum; /* line number of line in u_line */ - colnr_T b_u_line_colnr; /* optional column number */ - - int b_scanned; /* ^N/^P have scanned this buffer */ - - /* flags for use of ":lmap" and IM control */ - long b_p_iminsert; /* input mode for insert */ - long b_p_imsearch; /* input mode for search */ -#define B_IMODE_USE_INSERT -1 /* Use b_p_iminsert value for search */ -#define B_IMODE_NONE 0 /* Input via none */ -#define B_IMODE_LMAP 1 /* Input via langmap */ -#ifndef USE_IM_CONTROL -# define B_IMODE_LAST 1 -#else -# define B_IMODE_IM 2 /* Input via input method */ -# define B_IMODE_LAST 2 -#endif - - short b_kmap_state; /* using "lmap" mappings */ -# define KEYMAP_INIT 1 /* 'keymap' was set, call keymap_init() */ -# define KEYMAP_LOADED 2 /* 'keymap' mappings have been loaded */ - garray_T b_kmap_ga; /* the keymap table */ - - /* - * Options local to a buffer. - * They are here because their value depends on the type of file - * or contents of the file being edited. - */ - int b_p_initialized; /* set when options initialized */ - - int b_p_scriptID[BV_COUNT]; /* SIDs for buffer-local options */ - - int b_p_ai; /* 'autoindent' */ - int b_p_ai_nopaste; /* b_p_ai saved for paste mode */ - int b_p_ci; /* 'copyindent' */ - int b_p_bin; /* 'binary' */ - int b_p_bomb; /* 'bomb' */ - char_u *b_p_bh; /* 'bufhidden' */ - char_u *b_p_bt; /* 'buftype' */ - int b_p_bl; /* 'buflisted' */ - int b_p_cin; /* 'cindent' */ - char_u *b_p_cino; /* 'cinoptions' */ - char_u *b_p_cink; /* 'cinkeys' */ - char_u *b_p_cinw; /* 'cinwords' */ - char_u *b_p_com; /* 'comments' */ - char_u *b_p_cms; /* 'commentstring' */ - char_u *b_p_cpt; /* 'complete' */ - char_u *b_p_cfu; /* 'completefunc' */ - char_u *b_p_ofu; /* 'omnifunc' */ - int b_p_eol; /* 'endofline' */ - int b_p_et; /* 'expandtab' */ - int b_p_et_nobin; /* b_p_et saved for binary mode */ - char_u *b_p_fenc; /* 'fileencoding' */ - char_u *b_p_ff; /* 'fileformat' */ - char_u *b_p_ft; /* 'filetype' */ - char_u *b_p_fo; /* 'formatoptions' */ - char_u *b_p_flp; /* 'formatlistpat' */ - int b_p_inf; /* 'infercase' */ - char_u *b_p_isk; /* 'iskeyword' */ - char_u *b_p_def; /* 'define' local value */ - char_u *b_p_inc; /* 'include' */ - char_u *b_p_inex; /* 'includeexpr' */ - long_u b_p_inex_flags; /* flags for 'includeexpr' */ - char_u *b_p_inde; /* 'indentexpr' */ - long_u b_p_inde_flags; /* flags for 'indentexpr' */ - char_u *b_p_indk; /* 'indentkeys' */ - char_u *b_p_fex; /* 'formatexpr' */ - long_u b_p_fex_flags; /* flags for 'formatexpr' */ - char_u *b_p_key; /* 'key' */ - char_u *b_p_kp; /* 'keywordprg' */ - int b_p_lisp; /* 'lisp' */ - char_u *b_p_mps; /* 'matchpairs' */ - int b_p_ml; /* 'modeline' */ - int b_p_ml_nobin; /* b_p_ml saved for binary mode */ - int b_p_ma; /* 'modifiable' */ - char_u *b_p_nf; /* 'nrformats' */ - int b_p_pi; /* 'preserveindent' */ - char_u *b_p_qe; /* 'quoteescape' */ - int b_p_ro; /* 'readonly' */ - long b_p_sw; /* 'shiftwidth' */ - int b_p_si; /* 'smartindent' */ - long b_p_sts; /* 'softtabstop' */ - long b_p_sts_nopaste; /* b_p_sts saved for paste mode */ - char_u *b_p_sua; /* 'suffixesadd' */ - int b_p_swf; /* 'swapfile' */ - long b_p_smc; /* 'synmaxcol' */ - char_u *b_p_syn; /* 'syntax' */ - long b_p_ts; /* 'tabstop' */ - long b_p_tw; /* 'textwidth' */ - long b_p_tw_nobin; /* b_p_tw saved for binary mode */ - long b_p_tw_nopaste; /* b_p_tw saved for paste mode */ - long b_p_wm; /* 'wrapmargin' */ - long b_p_wm_nobin; /* b_p_wm saved for binary mode */ - long b_p_wm_nopaste; /* b_p_wm saved for paste mode */ - char_u *b_p_keymap; /* 'keymap' */ - - /* local values for options which are normally global */ - char_u *b_p_gp; /* 'grepprg' local value */ - char_u *b_p_mp; /* 'makeprg' local value */ - char_u *b_p_efm; /* 'errorformat' local value */ - char_u *b_p_ep; /* 'equalprg' local value */ - char_u *b_p_path; /* 'path' local value */ - int b_p_ar; /* 'autoread' local value */ - char_u *b_p_tags; /* 'tags' local value */ - char_u *b_p_dict; /* 'dictionary' local value */ - char_u *b_p_tsr; /* 'thesaurus' local value */ - long b_p_ul; /* 'undolevels' local value */ - int b_p_udf; /* 'undofile' */ - - /* end of buffer options */ - - /* values set from b_p_cino */ - int b_ind_level; - int b_ind_open_imag; - int b_ind_no_brace; - int b_ind_first_open; - int b_ind_open_extra; - int b_ind_close_extra; - int b_ind_open_left_imag; - int b_ind_jump_label; - int b_ind_case; - int b_ind_case_code; - int b_ind_case_break; - int b_ind_param; - int b_ind_func_type; - int b_ind_comment; - int b_ind_in_comment; - int b_ind_in_comment2; - int b_ind_cpp_baseclass; - int b_ind_continuation; - int b_ind_unclosed; - int b_ind_unclosed2; - int b_ind_unclosed_noignore; - int b_ind_unclosed_wrapped; - int b_ind_unclosed_whiteok; - int b_ind_matching_paren; - int b_ind_paren_prev; - int b_ind_maxparen; - int b_ind_maxcomment; - int b_ind_scopedecl; - int b_ind_scopedecl_code; - int b_ind_java; - int b_ind_js; - int b_ind_keep_case_label; - int b_ind_hash_comment; - int b_ind_cpp_namespace; - int b_ind_if_for_while; - - linenr_T b_no_eol_lnum; /* non-zero lnum when last line of next binary - * write should not have an end-of-line */ - - int b_start_eol; /* last line had eol when it was read */ - int b_start_ffc; /* first char of 'ff' when edit started */ - char_u *b_start_fenc; /* 'fileencoding' when edit started or NULL */ - int b_bad_char; /* "++bad=" argument when edit started or 0 */ - int b_start_bomb; /* 'bomb' when it was read */ - - dictitem_T b_bufvar; /* variable for "b:" Dictionary */ - dict_T *b_vars; /* internal variables, local to buffer */ - - char_u *b_p_cm; /* 'cryptmethod' */ - - /* When a buffer is created, it starts without a swap file. b_may_swap is - * then set to indicate that a swap file may be opened later. It is reset - * if a swap file could not be opened. - */ - int b_may_swap; - int b_did_warn; /* Set to 1 if user has been warned on first - change of a read-only file */ - - /* Two special kinds of buffers: - * help buffer - used for help files, won't use a swap file. - * spell buffer - used for spell info, never displayed and doesn't have a - * file name. - */ - int b_help; /* TRUE for help file buffer (when set b_p_bt - is "help") */ - int b_spell; /* TRUE for a spell file buffer, most fields - are not used! Use the B_SPELL macro to - access b_spell without #ifdef. */ - - synblock_T b_s; /* Info related to syntax highlighting. w_s - * normally points to this, but some windows - * may use a different synblock_T. */ - - signlist_T *b_signlist; /* list of signs to draw */ -}; - -/* - * Stuff for diff mode. - */ -# define DB_COUNT 4 /* up to four buffers can be diff'ed */ - -/* - * Each diffblock defines where a block of lines starts in each of the buffers - * and how many lines it occupies in that buffer. When the lines are missing - * in the buffer the df_count[] is zero. This is all counted in - * buffer lines. - * There is always at least one unchanged line in between the diffs. - * Otherwise it would have been included in the diff above or below it. - * df_lnum[] + df_count[] is the lnum below the change. When in one buffer - * lines have been inserted, in the other buffer df_lnum[] is the line below - * the insertion and df_count[] is zero. When appending lines at the end of - * the buffer, df_lnum[] is one beyond the end! - * This is using a linked list, because the number of differences is expected - * to be reasonable small. The list is sorted on lnum. - */ -typedef struct diffblock_S diff_T; -struct diffblock_S { - diff_T *df_next; - linenr_T df_lnum[DB_COUNT]; /* line number in buffer */ - linenr_T df_count[DB_COUNT]; /* nr of inserted/changed lines */ -}; - -#define SNAP_HELP_IDX 0 -# define SNAP_AUCMD_IDX 1 -# define SNAP_COUNT 2 - -/* - * Tab pages point to the top frame of each tab page. - * Note: Most values are NOT valid for the current tab page! Use "curwin", - * "firstwin", etc. for that. "tp_topframe" is always valid and can be - * compared against "topframe" to find the current tab page. - */ -typedef struct tabpage_S tabpage_T; -struct tabpage_S { - tabpage_T *tp_next; /* next tabpage or NULL */ - frame_T *tp_topframe; /* topframe for the windows */ - win_T *tp_curwin; /* current window in this Tab page */ - win_T *tp_prevwin; /* previous window in this Tab page */ - win_T *tp_firstwin; /* first window in this Tab page */ - win_T *tp_lastwin; /* last window in this Tab page */ - long tp_old_Rows; /* Rows when Tab page was left */ - long tp_old_Columns; /* Columns when Tab page was left */ - long tp_ch_used; /* value of 'cmdheight' when frame size - was set */ - diff_T *tp_first_diff; - buf_T *(tp_diffbuf[DB_COUNT]); - int tp_diff_invalid; /* list of diffs is outdated */ - frame_T *(tp_snapshot[SNAP_COUNT]); /* window layout snapshots */ - dictitem_T tp_winvar; /* variable for "t:" Dictionary */ - dict_T *tp_vars; /* internal variables, local to tab page */ -}; - -/* - * Structure to cache info for displayed lines in w_lines[]. - * Each logical line has one entry. - * The entry tells how the logical line is currently displayed in the window. - * This is updated when displaying the window. - * When the display is changed (e.g., when clearing the screen) w_lines_valid - * is changed to exclude invalid entries. - * When making changes to the buffer, wl_valid is reset to indicate wl_size - * may not reflect what is actually in the buffer. When wl_valid is FALSE, - * the entries can only be used to count the number of displayed lines used. - * wl_lnum and wl_lastlnum are invalid too. - */ -typedef struct w_line { - linenr_T wl_lnum; /* buffer line number for logical line */ - uint16_t wl_size; /* height in screen lines */ - char wl_valid; /* TRUE values are valid for text in buffer */ - char wl_folded; /* TRUE when this is a range of folded lines */ - linenr_T wl_lastlnum; /* last buffer line number for logical line */ -} wline_T; - -/* - * Windows are kept in a tree of frames. Each frame has a column (FR_COL) - * or row (FR_ROW) layout or is a leaf, which has a window. - */ -struct frame_S { - char fr_layout; /* FR_LEAF, FR_COL or FR_ROW */ - int fr_width; - int fr_newwidth; /* new width used in win_equal_rec() */ - int fr_height; - int fr_newheight; /* new height used in win_equal_rec() */ - frame_T *fr_parent; /* containing frame or NULL */ - frame_T *fr_next; /* frame right or below in same parent, NULL - for first */ - frame_T *fr_prev; /* frame left or above in same parent, NULL - for last */ - /* fr_child and fr_win are mutually exclusive */ - frame_T *fr_child; /* first contained frame */ - win_T *fr_win; /* window that fills this frame */ -}; - -#define FR_LEAF 0 /* frame is a leaf */ -#define FR_ROW 1 /* frame with a row of windows */ -#define FR_COL 2 /* frame with a column of windows */ - -/* - * Struct used for highlighting 'hlsearch' matches, matches defined by - * ":match" and matches defined by match functions. - * For 'hlsearch' there is one pattern for all windows. For ":match" and the - * match functions there is a different pattern for each window. - */ -typedef struct { - regmmatch_T rm; /* points to the regexp program; contains last found - match (may continue in next line) */ - buf_T *buf; /* the buffer to search for a match */ - linenr_T lnum; /* the line to search for a match */ - int attr; /* attributes to be used for a match */ - int attr_cur; /* attributes currently active in win_line() */ - linenr_T first_lnum; /* first lnum to search for multi-line pat */ - colnr_T startcol; /* in win_line() points to char where HL starts */ - colnr_T endcol; /* in win_line() points to char where HL ends */ - proftime_T tm; /* for a time limit */ -} match_T; - -/* - * matchitem_T provides a linked list for storing match items for ":match" and - * the match functions. - */ -typedef struct matchitem matchitem_T; -struct matchitem { - matchitem_T *next; - int id; /* match ID */ - int priority; /* match priority */ - char_u *pattern; /* pattern to highlight */ - int hlg_id; /* highlight group ID */ - regmmatch_T match; /* regexp program for pattern */ - match_T hl; /* struct for doing the actual highlighting */ -}; - -/* - * Structure which contains all information that belongs to a window - * - * All row numbers are relative to the start of the window, except w_winrow. - */ -struct window_S { - buf_T *w_buffer; /* buffer we are a window into (used - often, keep it the first item!) */ - - synblock_T *w_s; /* for :ownsyntax */ - - win_T *w_prev; /* link to previous window */ - win_T *w_next; /* link to next window */ - int w_closing; /* window is being closed, don't let - autocommands close it too. */ - - frame_T *w_frame; /* frame containing this window */ - - pos_T w_cursor; /* cursor position in buffer */ - - colnr_T w_curswant; /* The column we'd like to be at. This is - used to try to stay in the same column - for up/down cursor motions. */ - - int w_set_curswant; /* If set, then update w_curswant the next - time through cursupdate() to the - current virtual column */ - - /* - * the next six are used to update the visual part - */ - char w_old_visual_mode; /* last known VIsual_mode */ - linenr_T w_old_cursor_lnum; /* last known end of visual part */ - colnr_T w_old_cursor_fcol; /* first column for block visual part */ - colnr_T w_old_cursor_lcol; /* last column for block visual part */ - linenr_T w_old_visual_lnum; /* last known start of visual part */ - colnr_T w_old_visual_col; /* last known start of visual part */ - colnr_T w_old_curswant; /* last known value of Curswant */ - - /* - * "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for - * displaying the buffer. - */ - linenr_T w_topline; /* buffer line number of the line at the - top of the window */ - char w_topline_was_set; /* flag set to TRUE when topline is set, - e.g. by winrestview() */ - int w_topfill; /* number of filler lines above w_topline */ - int w_old_topfill; /* w_topfill at last redraw */ - int w_botfill; /* TRUE when filler lines are actually - below w_topline (at end of file) */ - int w_old_botfill; /* w_botfill at last redraw */ - colnr_T w_leftcol; /* window column number of the left most - character in the window; used when - 'wrap' is off */ - colnr_T w_skipcol; /* starting column when a single line - doesn't fit in the window */ - - /* - * Layout of the window in the screen. - * May need to add "msg_scrolled" to "w_winrow" in rare situations. - */ - int w_winrow; /* first row of window in screen */ - int w_height; /* number of rows in window, excluding - status/command line(s) */ - int w_status_height; /* number of status lines (0 or 1) */ - int w_wincol; /* Leftmost column of window in screen. - use W_WINCOL() */ - int w_width; /* Width of window, excluding separation. - use W_WIDTH() */ - int w_vsep_width; /* Number of separator columns (0 or 1). - use W_VSEP_WIDTH() */ - - /* - * === start of cached values ==== - */ - /* - * Recomputing is minimized by storing the result of computations. - * Use functions in screen.c to check if they are valid and to update. - * w_valid is a bitfield of flags, which indicate if specific values are - * valid or need to be recomputed. See screen.c for values. - */ - int w_valid; - pos_T w_valid_cursor; /* last known position of w_cursor, used - to adjust w_valid */ - colnr_T w_valid_leftcol; /* last known w_leftcol */ - - /* - * w_cline_height is the number of physical lines taken by the buffer line - * that the cursor is on. We use this to avoid extra calls to plines(). - */ - int w_cline_height; /* current size of cursor line */ - int w_cline_folded; /* cursor line is folded */ - - int w_cline_row; /* starting row of the cursor line */ - - colnr_T w_virtcol; /* column number of the cursor in the - buffer line, as opposed to the column - number we're at on the screen. This - makes a difference on lines which span - more than one screen line or when - w_leftcol is non-zero */ - - /* - * w_wrow and w_wcol specify the cursor position in the window. - * This is related to positions in the window, not in the display or - * buffer, thus w_wrow is relative to w_winrow. - */ - int w_wrow, w_wcol; /* cursor position in window */ - - linenr_T w_botline; /* number of the line below the bottom of - the screen */ - int w_empty_rows; /* number of ~ rows in window */ - int w_filler_rows; /* number of filler rows at the end of the - window */ - - /* - * Info about the lines currently in the window is remembered to avoid - * recomputing it every time. The allocated size of w_lines[] is Rows. - * Only the w_lines_valid entries are actually valid. - * When the display is up-to-date w_lines[0].wl_lnum is equal to w_topline - * and w_lines[w_lines_valid - 1].wl_lnum is equal to w_botline. - * Between changing text and updating the display w_lines[] represents - * what is currently displayed. wl_valid is reset to indicated this. - * This is used for efficient redrawing. - */ - int w_lines_valid; /* number of valid entries */ - wline_T *w_lines; - - garray_T w_folds; /* array of nested folds */ - char w_fold_manual; /* when TRUE: some folds are opened/closed - manually */ - char w_foldinvalid; /* when TRUE: folding needs to be - recomputed */ - int w_nrwidth; /* width of 'number' and 'relativenumber' - column being used */ - - /* - * === end of cached values === - */ - - int w_redr_type; /* type of redraw to be performed on win */ - int w_upd_rows; /* number of window lines to update when - w_redr_type is REDRAW_TOP */ - linenr_T w_redraw_top; /* when != 0: first line needing redraw */ - linenr_T w_redraw_bot; /* when != 0: last line needing redraw */ - int w_redr_status; /* if TRUE status line must be redrawn */ - - /* remember what is shown in the ruler for this window (if 'ruler' set) */ - pos_T w_ru_cursor; /* cursor position shown in ruler */ - colnr_T w_ru_virtcol; /* virtcol shown in ruler */ - linenr_T w_ru_topline; /* topline shown in ruler */ - linenr_T w_ru_line_count; /* line count used for ruler */ - int w_ru_topfill; /* topfill shown in ruler */ - char w_ru_empty; /* TRUE if ruler shows 0-1 (empty line) */ - - int w_alt_fnum; /* alternate file (for # and CTRL-^) */ - - alist_T *w_alist; /* pointer to arglist for this window */ - int w_arg_idx; /* current index in argument list (can be - out of range!) */ - int w_arg_idx_invalid; /* editing another file than w_arg_idx */ - - char_u *w_localdir; /* absolute path of local directory or - NULL */ - /* - * Options local to a window. - * They are local because they influence the layout of the window or - * depend on the window layout. - * There are two values: w_onebuf_opt is local to the buffer currently in - * this window, w_allbuf_opt is for all buffers in this window. - */ - winopt_T w_onebuf_opt; - winopt_T w_allbuf_opt; - - /* A few options have local flags for P_INSECURE. */ - long_u w_p_stl_flags; /* flags for 'statusline' */ - long_u w_p_fde_flags; /* flags for 'foldexpr' */ - long_u w_p_fdt_flags; /* flags for 'foldtext' */ - int *w_p_cc_cols; /* array of columns to highlight or NULL */ - - /* transform a pointer to a "onebuf" option into a "allbuf" option */ -#define GLOBAL_WO(p) ((char *)p + sizeof(winopt_T)) - - long w_scbind_pos; - - dictitem_T w_winvar; /* variable for "w:" Dictionary */ - dict_T *w_vars; /* internal variables, local to window */ - - int w_farsi; /* for the window dependent Farsi functions */ - - /* - * The w_prev_pcmark field is used to check whether we really did jump to - * a new line after setting the w_pcmark. If not, then we revert to - * using the previous w_pcmark. - */ - pos_T w_pcmark; /* previous context mark */ - pos_T w_prev_pcmark; /* previous w_pcmark */ - - /* - * the jumplist contains old cursor positions - */ - xfmark_T w_jumplist[JUMPLISTSIZE]; - int w_jumplistlen; /* number of active entries */ - int w_jumplistidx; /* current position */ - - int w_changelistidx; /* current position in b_changelist */ - - matchitem_T *w_match_head; /* head of match list */ - int w_next_match_id; /* next match ID */ - - /* - * the tagstack grows from 0 upwards: - * entry 0: older - * entry 1: newer - * entry 2: newest - */ - taggy_T w_tagstack[TAGSTACKSIZE]; /* the tag stack */ - int w_tagstackidx; /* idx just below active entry */ - int w_tagstacklen; /* number of tags on stack */ - - /* - * w_fraction is the fractional row of the cursor within the window, from - * 0 at the top row to FRACTION_MULT at the last row. - * w_prev_fraction_row was the actual cursor row when w_fraction was last - * calculated. - */ - int w_fraction; - int w_prev_fraction_row; - - linenr_T w_nrwidth_line_count; /* line count when ml_nrwidth_width - * was computed. */ - int w_nrwidth_width; /* nr of chars to print line count. */ - - qf_info_T *w_llist; /* Location list for this window */ - /* - * Location list reference used in the location list window. - * In a non-location list window, w_llist_ref is NULL. - */ - qf_info_T *w_llist_ref; -}; - -#endif // NEOVIM_BUFFER_DEFS_H diff --git a/src/charset.c b/src/charset.c deleted file mode 100644 index 5217eb2c0a..0000000000 --- a/src/charset.c +++ /dev/null @@ -1,1918 +0,0 @@ -/// @file charset.c -/// -/// Code related to character sets. - -#include -#include -#include // for towupper() and towlower() - -#include "vim.h" -#include "charset.h" -#include "farsi.h" -#include "main.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "misc1.h" -#include "misc2.h" -#include "garray.h" -#include "move.h" -#include "os_unix.h" - -static int win_chartabsize(win_T *wp, char_u *p, colnr_T col); - -static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, - int *headp); - -static unsigned nr2hex(unsigned c); - -static int chartab_initialized = FALSE; - -// b_chartab[] is an array of 32 bytes, each bit representing one of the -// characters 0-255. -#define SET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] |= (1 << ((c) & 0x7)) -#define RESET_CHARTAB(buf, c) \ - (buf)->b_chartab[(unsigned)(c) >> 3] &= ~(1 << ((c) & 0x7)) -#define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 3] & (1 << ((c) & 0x7))) - -/// Fill chartab[]. Also fills curbuf->b_chartab[] with flags for keyword -/// characters for current buffer. -/// -/// Depends on the option settings 'iskeyword', 'isident', 'isfname', -/// 'isprint' and 'encoding'. -/// -/// The index in chartab[] depends on 'encoding': -/// - For non-multi-byte index with the byte (same as the character). -/// - For DBCS index with the first byte. -/// - For UTF-8 index with the character (when first byte is up to 0x80 it is -/// the same as the character, if the first byte is 0x80 and above it depends -/// on further bytes). -/// -/// The contents of chartab[]: -/// - The lower two bits, masked by CT_CELL_MASK, give the number of display -/// cells the character occupies (1 or 2). Not valid for UTF-8 above 0x80. -/// - CT_PRINT_CHAR bit is set when the character is printable (no need to -/// translate the character before displaying it). Note that only DBCS -/// characters can have 2 display cells and still be printable. -/// - CT_FNAME_CHAR bit is set when the character can be in a file name. -/// - CT_ID_CHAR bit is set when the character can be in an identifier. -/// -/// @return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has -/// an error, OK otherwise. -int init_chartab(void) -{ - return buf_init_chartab(curbuf, TRUE); -} - -/// Helper for init_chartab -/// -/// @param global FALSE: only set buf->b_chartab[] -/// -/// @return FAIL if 'iskeyword', 'isident', 'isfname' or 'isprint' option has -/// an error, OK otherwise. -int buf_init_chartab(buf_T *buf, int global) -{ - int c; - int c2; - char_u *p; - int i; - int tilde; - int do_isalpha; - - if (global) { - // Set the default size for printable characters: - // From to '~' is 1 (printable), others are 2 (not printable). - // This also inits all 'isident' and 'isfname' flags to FALSE. - // - // EBCDIC: all chars below ' ' are not printable, all others are - // printable. - c = 0; - - while (c < ' ') { - chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2; - } - - while (c <= '~') { - chartab[c++] = 1 + CT_PRINT_CHAR; - } - - if (p_altkeymap) { - while (c < YE) { - chartab[c++] = 1 + CT_PRINT_CHAR; - } - } - - while (c < 256) { - if (enc_utf8 && (c >= 0xa0)) { - // UTF-8: bytes 0xa0 - 0xff are printable (latin1) - chartab[c++] = CT_PRINT_CHAR + 1; - } else if ((enc_dbcs == DBCS_JPNU) && (c == 0x8e)) { - // euc-jp characters starting with 0x8e are single width - chartab[c++] = CT_PRINT_CHAR + 1; - } else if ((enc_dbcs != 0) && (MB_BYTE2LEN(c) == 2)) { - // other double-byte chars can be printable AND double-width - chartab[c++] = CT_PRINT_CHAR + 2; - } else { - // the rest is unprintable by default - chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2; - } - } - - // Assume that every multi-byte char is a filename character. - for (c = 1; c < 256; ++c) { - if (((enc_dbcs != 0) && (MB_BYTE2LEN(c) > 1)) - || ((enc_dbcs == DBCS_JPNU) && (c == 0x8e)) - || (enc_utf8 && (c >= 0xa0))) { - chartab[c] |= CT_FNAME_CHAR; - } - } - } - - // Init word char flags all to FALSE - memset(buf->b_chartab, 0, (size_t)32); - - if (enc_dbcs != 0) { - for (c = 0; c < 256; ++c) { - // double-byte characters are probably word characters - if (MB_BYTE2LEN(c) == 2) { - SET_CHARTAB(buf, c); - } - } - } - - // In lisp mode the '-' character is included in keywords. - if (buf->b_p_lisp) { - SET_CHARTAB(buf, '-'); - } - - // Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint' - // options Each option is a list of characters, character numbers or - // ranges, separated by commas, e.g.: "200-210,x,#-178,-" - for (i = global ? 0 : 3; i <= 3; ++i) { - if (i == 0) { - // first round: 'isident' - p = p_isi; - } else if (i == 1) { - // second round: 'isprint' - p = p_isp; - } else if (i == 2) { - // third round: 'isfname' - p = p_isf; - } else { // i == 3 - // fourth round: 'iskeyword' - p = buf->b_p_isk; - } - - while (*p) { - tilde = FALSE; - do_isalpha = FALSE; - - if ((*p == '^') && (p[1] != NUL)) { - tilde = TRUE; - ++p; - } - - if (VIM_ISDIGIT(*p)) { - c = getdigits(&p); - } else if (has_mbyte) { - c = mb_ptr2char_adv(&p); - } else { - c = *p++; - } - c2 = -1; - - if ((*p == '-') && (p[1] != NUL)) { - ++p; - - if (VIM_ISDIGIT(*p)) { - c2 = getdigits(&p); - } else if (has_mbyte) { - c2 = mb_ptr2char_adv(&p); - } else { - c2 = *p++; - } - } - - if ((c <= 0) - || (c >= 256) - || ((c2 < c) && (c2 != -1)) - || (c2 >= 256) - || !((*p == NUL) || (*p == ','))) { - return FAIL; - } - - if (c2 == -1) { // not a range - // A single '@' (not "@-@"): - // Decide on letters being ID/printable/keyword chars with - // standard function isalpha(). This takes care of locale for - // single-byte characters). - if (c == '@') { - do_isalpha = TRUE; - c = 1; - c2 = 255; - } else { - c2 = c; - } - } - - while (c <= c2) { - // Use the MB_ functions here, because isalpha() doesn't - // work properly when 'encoding' is "latin1" and the locale is - // "C". - if (!do_isalpha - || vim_islower(c) - || vim_isupper(c) - || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) { - if (i == 0) { - // (re)set ID flag - if (tilde) { - chartab[c] &= ~CT_ID_CHAR; - } else { - chartab[c] |= CT_ID_CHAR; - } - } else if (i == 1) { - // (re)set printable - // For double-byte we keep the cell width, so - // that we can detect it from the first byte. - if (((c < ' ') - || (c > '~') - || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))) - && !(enc_dbcs && (MB_BYTE2LEN(c) == 2))) { - if (tilde) { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) - + ((dy_flags & DY_UHEX) ? 4 : 2); - chartab[c] &= ~CT_PRINT_CHAR; - } else { - chartab[c] = (chartab[c] & ~CT_CELL_MASK) + 1; - chartab[c] |= CT_PRINT_CHAR; - } - } - } else if (i == 2) { - // (re)set fname flag - if (tilde) { - chartab[c] &= ~CT_FNAME_CHAR; - } else { - chartab[c] |= CT_FNAME_CHAR; - } - } else { // i == 3 - // (re)set keyword flag - if (tilde) { - RESET_CHARTAB(buf, c); - } else { - SET_CHARTAB(buf, c); - } - } - } - ++c; - } - - c = *p; - p = skip_to_option_part(p); - - if ((c == ',') && (*p == NUL)) { - // Trailing comma is not allowed. - return FAIL; - } - } - } - chartab_initialized = TRUE; - return OK; -} - -/// Translate any special characters in buf[bufsize] in-place. -/// -/// The result is a string with only printable characters, but if there is not -/// enough room, not all characters will be translated. -/// -/// @param buf -/// @param bufsize -void trans_characters(char_u *buf, int bufsize) -{ - int len; // length of string needing translation - int room; // room in buffer after string - char_u *trs; // translated character - int trs_len; // length of trs[] - - len = (int)STRLEN(buf); - room = bufsize - len; - - while (*buf != 0) { - // Assume a multi-byte character doesn't need translation. - if (has_mbyte && ((trs_len = (*mb_ptr2len)(buf)) > 1)) { - len -= trs_len; - } else { - trs = transchar_byte(*buf); - trs_len = (int)STRLEN(trs); - - if (trs_len > 1) { - room -= trs_len - 1; - if (room <= 0) { - return; - } - memmove(buf + trs_len, buf + 1, (size_t)len); - } - memmove(buf, trs, (size_t)trs_len); - --len; - } - buf += trs_len; - } -} - -#if defined(FEAT_EVAL) \ - || defined(FEAT_TITLE) \ - || defined(FEAT_INS_EXPAND) \ - || defined(PROTO) - -/// Translate a string into allocated memory, replacing special chars with -/// printable chars. Returns NULL when out of memory. -/// -/// @param s -/// -/// @return translated string -char_u *transstr(char_u *s) -{ - char_u *res; - char_u *p; - int l, c; - char_u hexbuf[11]; - - if (has_mbyte) { - // Compute the length of the result, taking account of unprintable - // multi-byte characters. - size_t len = 0; - p = s; - - while (*p != NUL) { - if ((l = (*mb_ptr2len)(p)) > 1) { - c = (*mb_ptr2char)(p); - p += l; - - if (vim_isprintc(c)) { - len += l; - } else { - transchar_hex(hexbuf, c); - len += STRLEN(hexbuf); - } - } else { - l = byte2cells(*p++); - - if (l > 0) { - len += l; - } else { - // illegal byte sequence - len += 4; - } - } - } - res = xmallocz(len); - } else { - res = xmallocz(vim_strsize(s)); - } - - *res = NUL; - p = s; - - while (*p != NUL) { - if (has_mbyte && ((l = (*mb_ptr2len)(p)) > 1)) { - c = (*mb_ptr2char)(p); - - if (vim_isprintc(c)) { - // append printable multi-byte char - STRNCAT(res, p, l); - } else { - transchar_hex(res + STRLEN(res), c); - } - p += l; - } else { - STRCAT(res, transchar_byte(*p++)); - } - } - - return res; -} - -#endif // if defined(FEAT_EVAL) || defined(FEAT_TITLE) - // || defined(FEAT_INS_EXPAND) || defined(PROTO) - -/// Convert the string "str[orglen]" to do ignore-case comparing. Uses the -/// current locale. -/// -/// When "buf" is NULL returns an allocated string (NULL for out-of-memory). -/// Otherwise puts the result in "buf[buflen]". -/// -/// @param str -/// @param orglen -/// @param buf -/// @param buflen -/// -/// @return converted string. -char_u* str_foldcase(char_u *str, int orglen, char_u *buf, int buflen) -{ - garray_T ga; - int i; - int len = orglen; - -#define GA_CHAR(i) ((char_u *)ga.ga_data)[i] -#define GA_PTR(i) ((char_u *)ga.ga_data + i) -#define STR_CHAR(i) (buf == NULL ? GA_CHAR(i) : buf[i]) -#define STR_PTR(i) (buf == NULL ? GA_PTR(i) : buf + i) - - // Copy "str" into "buf" or allocated memory, unmodified. - if (buf == NULL) { - ga_init(&ga, 1, 10); - - ga_grow(&ga, len + 1); - memmove(ga.ga_data, str, (size_t)len); - ga.ga_len = len; - } else { - if (len >= buflen) { - // Ugly! - len = buflen - 1; - } - memmove(buf, str, (size_t)len); - } - - if (buf == NULL) { - GA_CHAR(len) = NUL; - } else { - buf[len] = NUL; - } - - // Make each character lower case. - i = 0; - while (STR_CHAR(i) != NUL) { - if (enc_utf8 || (has_mbyte && (MB_BYTE2LEN(STR_CHAR(i)) > 1))) { - if (enc_utf8) { - int c = utf_ptr2char(STR_PTR(i)); - int olen = utf_ptr2len(STR_PTR(i)); - int lc = utf_tolower(c); - - // Only replace the character when it is not an invalid - // sequence (ASCII character or more than one byte) and - // utf_tolower() doesn't return the original character. - if (((c < 0x80) || (olen > 1)) && (c != lc)) { - int nlen = utf_char2len(lc); - - // If the byte length changes need to shift the following - // characters forward or backward. - if (olen != nlen) { - if (nlen > olen) { - if (buf == NULL) { - ga_grow(&ga, nlen - olen + 1); - } else { - if (len + nlen - olen >= buflen) { - // out of memory, keep old char - lc = c; - nlen = olen; - } - } - } - - if (olen != nlen) { - if (buf == NULL) { - STRMOVE(GA_PTR(i) + nlen, GA_PTR(i) + olen); - ga.ga_len += nlen - olen; - } else { - STRMOVE(buf + i + nlen, buf + i + olen); - len += nlen - olen; - } - } - } - (void)utf_char2bytes(lc, STR_PTR(i)); - } - } - - // skip to next multi-byte char - i += (*mb_ptr2len)(STR_PTR(i)); - } else { - if (buf == NULL) { - GA_CHAR(i) = TOLOWER_LOC(GA_CHAR(i)); - } else { - buf[i] = TOLOWER_LOC(buf[i]); - } - ++i; - } - } - - if (buf == NULL) { - return (char_u *)ga.ga_data; - } - return buf; -} - -// Catch 22: chartab[] can't be initialized before the options are -// initialized, and initializing options may cause transchar() to be called! -// When chartab_initialized == FALSE don't use chartab[]. -// Does NOT work for multi-byte characters, c must be <= 255. -// Also doesn't work for the first byte of a multi-byte, "c" must be a -// character! -static char_u transchar_buf[7]; - -/// Translates a character -/// -/// @param c -/// -/// @return translated character. -char_u* transchar(int c) -{ - int i = 0; - if (IS_SPECIAL(c)) { - // special key code, display as ~@ char - transchar_buf[0] = '~'; - transchar_buf[1] = '@'; - i = 2; - c = K_SECOND(c); - } - - if ((!chartab_initialized && (((c >= ' ') && (c <= '~')) || F_ischar(c))) - || ((c < 256) && vim_isprintc_strict(c))) { - // printable character - transchar_buf[i] = c; - transchar_buf[i + 1] = NUL; - } else { - transchar_nonprint(transchar_buf + i, c); - } - return transchar_buf; -} - -/// Like transchar(), but called with a byte instead of a character. Checks -/// for an illegal UTF-8 byte. -/// -/// @param c -/// -/// @return pointer to translated character in transchar_buf. -char_u* transchar_byte(int c) -{ - if (enc_utf8 && (c >= 0x80)) { - transchar_nonprint(transchar_buf, c); - return transchar_buf; - } - return transchar(c); -} - -/// Convert non-printable character to two or more printable characters in -/// "buf[]". "buf" needs to be able to hold five bytes. -/// Does NOT work for multi-byte characters, c must be <= 255. -/// -/// @param buf -/// @param c -void transchar_nonprint(char_u *buf, int c) -{ - if (c == NL) { - // we use newline in place of a NUL - c = NUL; - } else if ((c == CAR) && (get_fileformat(curbuf) == EOL_MAC)) { - // we use CR in place of NL in this case - c = NL; - } - - if (dy_flags & DY_UHEX) { - // 'display' has "uhex" - transchar_hex(buf, c); - } else if (c <= 0x7f) { - // 0x00 - 0x1f and 0x7f - buf[0] = '^'; - // DEL displayed as ^? - buf[1] = c ^ 0x40; - - buf[2] = NUL; - } else if (enc_utf8 && (c >= 0x80)) { - transchar_hex(buf, c); - } else if ((c >= ' ' + 0x80) && (c <= '~' + 0x80)) { - // 0xa0 - 0xfe - buf[0] = '|'; - buf[1] = c - 0x80; - buf[2] = NUL; - } else { - // 0x80 - 0x9f and 0xff - // TODO: EBCDIC I don't know what to do with this chars, so I display - // them as '~?' for now - buf[0] = '~'; - buf[1] = (c - 0x80) ^ 0x40; - // 0xff displayed as ~? - buf[2] = NUL; - } -} - -/// Convert a non-printable character to hex. -/// -/// @param buf -/// @param c -void transchar_hex(char_u *buf, int c) -{ - int i = 0; - - buf[0] = '<'; - if (c > 255) { - buf[++i] = nr2hex((unsigned)c >> 12); - buf[++i] = nr2hex((unsigned)c >> 8); - } - buf[++i] = nr2hex((unsigned)c >> 4); - buf[++i] = nr2hex((unsigned)c); - buf[++i] = '>'; - buf[++i] = NUL; -} - -/// Convert the lower 4 bits of byte "c" to its hex character. -/// Lower case letters are used to avoid the confusion of being 0xf1 or -/// function key 1. -/// -/// @param c -/// -/// @return the hex character. -static unsigned nr2hex(unsigned c) -{ - if ((c & 0xf) <= 9) { - return (c & 0xf) + '0'; - } - return (c & 0xf) - 10 + 'a'; -} - -/// Return number of display cells occupied by byte "b". -/// -/// Caller must make sure 0 <= b <= 255. -/// For multi-byte mode "b" must be the first byte of a character. -/// A TAB is counted as two cells: "^I". -/// For UTF-8 mode this will return 0 for bytes >= 0x80, because the number of -/// cells depends on further bytes. -/// -/// @param b -/// -/// @reeturn Number of display cells. -int byte2cells(int b) -{ - if (enc_utf8 && (b >= 0x80)) { - return 0; - } - return chartab[b] & CT_CELL_MASK; -} - -/// Return number of display cells occupied by character "c". -/// -/// "c" can be a special key (negative number) in which case 3 or 4 is returned. -/// A TAB is counted as two cells: "^I" or four: "<09>". -/// -/// @param c -/// -/// @return Number of display cells. -int char2cells(int c) -{ - if (IS_SPECIAL(c)) { - return char2cells(K_SECOND(c)) + 2; - } - - if (c >= 0x80) { - // UTF-8: above 0x80 need to check the value - if (enc_utf8) { - return utf_char2cells(c); - } - - // DBCS: double-byte means double-width, except for euc-jp with first - // byte 0x8e - if ((enc_dbcs != 0) && (c >= 0x100)) { - if ((enc_dbcs == DBCS_JPNU) && (((unsigned)c >> 8) == 0x8e)) { - return 1; - } - return 2; - } - } - return chartab[c & 0xff] & CT_CELL_MASK; -} - -/// Return number of display cells occupied by character at "*p". -/// A TAB is counted as two cells: "^I" or four: "<09>". -/// -/// @param p -/// -/// @return number of display cells. -int ptr2cells(char_u *p) -{ - // For UTF-8 we need to look at more bytes if the first byte is >= 0x80. - if (enc_utf8 && (*p >= 0x80)) { - return utf_ptr2cells(p); - } - - // For DBCS we can tell the cell count from the first byte. - return chartab[*p] & CT_CELL_MASK; -} - -/// Return the number of character cells string "s" will take on the screen, -/// counting TABs as two characters: "^I". -/// -/// 's' must be non-null. -/// -/// @param s -/// -/// @return number of character cells. -int vim_strsize(char_u *s) -{ - return vim_strnsize(s, (int)MAXCOL); -} - -/// Return the number of character cells string "s[len]" will take on the -/// screen, counting TABs as two characters: "^I". -/// -/// 's' must be non-null. -/// -/// @param s -/// @param len -/// -/// @return Number of character cells. -int vim_strnsize(char_u *s, int len) -{ - assert(s != NULL); - int size = 0; - while (*s != NUL && --len >= 0) { - if (has_mbyte) { - int l = (*mb_ptr2len)(s); - size += ptr2cells(s); - s += l; - len -= l - 1; - } else { - size += byte2cells(*s++); - } - } - return size; -} - -/// Return the number of characters 'c' will take on the screen, taking -/// into account the size of a tab. -/// Use a define to make it fast, this is used very often!!! -/// Also see getvcol() below. -/// -/// @param p -/// @param col -/// -/// @return Number of characters. -#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ - if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \ - int ts; \ - ts = (buf)->b_p_ts; \ - return (int)(ts - (col % ts)); \ - } else { \ - return ptr2cells(p); \ - } - -#if defined(FEAT_VREPLACE) \ - || defined(FEAT_EX_EXTRA) \ - || defined(FEAT_GUI) \ - || defined(FEAT_VIRTUALEDIT) \ - || defined(PROTO) -int chartabsize(char_u *p, colnr_T col) -{ - RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, p, col) -} - -#endif /* if defined(FEAT_VREPLACE) || defined(FEAT_EX_EXTRA) || - defined(FEAT_GUI) || defined(FEAT_VIRTUALEDIT) || defined(PROTO) */ - -static int win_chartabsize(win_T *wp, char_u *p, colnr_T col) -{ - RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, p, col) -} - -/// Return the number of characters the string 's' will take on the screen, -/// taking into account the size of a tab. -/// -/// @param s -/// -/// @return Number of characters the string will take on the screen. -int linetabsize(char_u *s) -{ - return linetabsize_col(0, s); -} - -/// Like linetabsize(), but starting at column "startcol". -/// -/// @param startcol -/// @param s -/// -/// @return Number of characters the string will take on the screen. -int linetabsize_col(int startcol, char_u *s) -{ - colnr_T col = startcol; - - while (*s != NUL) { - col += lbr_chartabsize_adv(&s, col); - } - return (int)col; -} - -/// Like linetabsize(), but for a given window instead of the current one. -/// -/// @param wp -/// @param p -/// @param len -/// -/// @return Number of characters the string will take on the screen. -int win_linetabsize(win_T *wp, char_u *p, colnr_T len) -{ - colnr_T col = 0; - char_u *s; - - for (s = p; *s != NUL && (len == MAXCOL || s < p + len); mb_ptr_adv(s)) { - col += win_lbr_chartabsize(wp, s, col, NULL); - } - return (int)col; -} - -/// Return TRUE if 'c' is a normal identifier character: -/// -/// Letters and characters from the 'isident' option. -/// -/// @param c -/// -/// @return TRUE if 'c' is a normal identifier character. -int vim_isIDc(int c) -{ - return c > 0 && c < 0x100 && (chartab[c] & CT_ID_CHAR); -} - -/// return TRUE if 'c' is a keyword character: Letters and characters from -/// 'iskeyword' option for current buffer. -/// -/// For multi-byte characters mb_get_class() is used (builtin rules). -/// -/// @param c -/// -/// @return TRUE if 'c' is a keyword character. -int vim_iswordc(int c) -{ - return vim_iswordc_buf(c, curbuf); -} - -int vim_iswordc_buf(int c, buf_T *buf) -{ - if (c >= 0x100) { - if (enc_dbcs != 0) { - return dbcs_class((unsigned)c >> 8, (unsigned)(c & 0xff)) >= 2; - } - - if (enc_utf8) { - return utf_class(c) >= 2; - } - } - return c > 0 && c < 0x100 && GET_CHARTAB(buf, c) != 0; -} - -/// Just like vim_iswordc() but uses a pointer to the (multi-byte) character. -/// -/// @param p -/// -/// @return TRUE if 'p' points to a keyword character. -int vim_iswordp(char_u *p) -{ - if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) { - return mb_get_class(p) >= 2; - } - return GET_CHARTAB(curbuf, *p) != 0; -} - -int vim_iswordp_buf(char_u *p, buf_T *buf) -{ - if (has_mbyte && (MB_BYTE2LEN(*p) > 1)) { - return mb_get_class(p) >= 2; - } - return GET_CHARTAB(buf, *p) != 0; -} - -/// return TRUE if 'c' is a valid file-name character -/// Assume characters above 0x100 are valid (multi-byte). -/// -/// @param c -/// -/// @return TRUE if 'c' is a valid file name character. -int vim_isfilec(int c) -{ - return c >= 0x100 || (c > 0 && (chartab[c] & CT_FNAME_CHAR)); -} - -/// return TRUE if 'c' is a valid file-name character or a wildcard character -/// Assume characters above 0x100 are valid (multi-byte). -/// Explicitly interpret ']' as a wildcard character as mch_has_wildcard("]") -/// returns false. -/// -/// @param c -/// -/// @return TRUE if 'c' is a valid file-name character or wildcard character. -int vim_isfilec_or_wc(int c) -{ - char_u buf[2]; - buf[0] = (char_u)c; - buf[1] = NUL; - return vim_isfilec(c) || c == ']' || mch_has_wildcard(buf); -} - -/// return TRUE if 'c' is a printable character -/// Assume characters above 0x100 are printable (multi-byte), except for -/// Unicode. -/// -/// @param c -/// -/// @return TRUE if 'c' a printable character. -int vim_isprintc(int c) -{ - if (enc_utf8 && (c >= 0x100)) { - return utf_printable(c); - } - return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR)); -} - -/// Strict version of vim_isprintc(c), don't return TRUE if "c" is the head -/// byte of a double-byte character. -/// -/// @param c -/// -/// @return TRUE if 'c' is a printable character. -int vim_isprintc_strict(int c) -{ - if ((enc_dbcs != 0) && (c < 0x100) && (MB_BYTE2LEN(c) > 1)) { - return FALSE; - } - - if (enc_utf8 && (c >= 0x100)) { - return utf_printable(c); - } - return c >= 0x100 || (c > 0 && (chartab[c] & CT_PRINT_CHAR)); -} - -/// like chartabsize(), but also check for line breaks on the screen -/// -/// @param s -/// @param col -/// -/// @return The number of characters taken up on the screen. -int lbr_chartabsize(unsigned char *s, colnr_T col) -{ - if (!curwin->w_p_lbr && (*p_sbr == NUL)) { - if (curwin->w_p_wrap) { - return win_nolbr_chartabsize(curwin, s, col, NULL); - } - RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col) - } - return win_lbr_chartabsize(curwin, s, col, NULL); -} - -/// Call lbr_chartabsize() and advance the pointer. -/// -/// @param s -/// @param col -/// -/// @return The number of characters take up on the screen. -int lbr_chartabsize_adv(char_u **s, colnr_T col) -{ - int retval; - - retval = lbr_chartabsize(*s, col); - mb_ptr_adv(*s); - return retval; -} - -/// This function is used very often, keep it fast!!!! -/// -/// If "headp" not NULL, set *headp to the size of what we for 'showbreak' -/// string at start of line. Warning: *headp is only set if it's a non-zero -/// value, init to 0 before calling. -/// -/// @param wp -/// @param s -/// @param col -/// @param headp -/// -/// @return The number of characters taken up on the screen. -int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) -{ - colnr_T col2; - colnr_T colmax; - int added; - int mb_added = 0; - int numberextra; - char_u *ps; - int tab_corr = (*s == TAB); - int n; - - // No 'linebreak' and 'showbreak': return quickly. - if (!wp->w_p_lbr && (*p_sbr == NUL)) { - if (wp->w_p_wrap) { - return win_nolbr_chartabsize(wp, s, col, headp); - } - RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, col) - } - - // First get normal size, without 'linebreak' - int size = win_chartabsize(wp, s, col); - int c = *s; - - // If 'linebreak' set check at a blank before a non-blank if the line - // needs a break here - if (wp->w_p_lbr - && vim_isbreak(c) - && !vim_isbreak(s[1]) - && !wp->w_p_list - && wp->w_p_wrap - && (wp->w_width != 0)) { - // Count all characters from first non-blank after a blank up to next - // non-blank after a blank. - numberextra = win_col_off(wp); - col2 = col; - colmax = (colnr_T)(W_WIDTH(wp) - numberextra); - - if (col >= colmax) { - n = colmax + win_col_off2(wp); - - if (n > 0) { - colmax += (((col - colmax) / n) + 1) * n; - } - } - - for (;;) { - ps = s; - mb_ptr_adv(s); - c = *s; - - if (!((c != NUL) - && (vim_isbreak(c) - || (!vim_isbreak(c) - && ((col2 == col) || !vim_isbreak(*ps)))))) { - break; - } - - col2 += win_chartabsize(wp, s, col2); - - if (col2 >= colmax) { /* doesn't fit */ - size = colmax - col; - tab_corr = FALSE; - break; - } - } - } else if (has_mbyte - && (size == 2) - && (MB_BYTE2LEN(*s) > 1) - && wp->w_p_wrap - && in_win_border(wp, col)) { - // Count the ">" in the last column. - ++size; - mb_added = 1; - } - - // May have to add something for 'showbreak' string at start of line - // Set *headp to the size of what we add. - added = 0; - - if ((*p_sbr != NUL) && wp->w_p_wrap && (col != 0)) { - numberextra = win_col_off(wp); - col += numberextra + mb_added; - - if (col >= (colnr_T)W_WIDTH(wp)) { - col -= W_WIDTH(wp); - numberextra = W_WIDTH(wp) - (numberextra - win_col_off2(wp)); - if (numberextra > 0) { - col = col % numberextra; - } - } - - if ((col == 0) || (col + size > (colnr_T)W_WIDTH(wp))) { - added = vim_strsize(p_sbr); - if (tab_corr) { - size += (added / wp->w_buffer->b_p_ts) * wp->w_buffer->b_p_ts; - } else { - size += added; - } - - if (col != 0) { - added = 0; - } - } - } - - if (headp != NULL) { - *headp = added + mb_added; - } - return size; -} - -/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and -/// 'wrap' is on. This means we need to check for a double-byte character that -/// doesn't fit at the end of the screen line. -/// -/// @param wp -/// @param s -/// @param col -/// @param headp -/// -/// @return The number of characters take up on the screen. -static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) -{ - int n; - - if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) { - n = wp->w_buffer->b_p_ts; - return (int)(n - (col % n)); - } - n = ptr2cells(s); - - // Add one cell for a double-width character in the last column of the - // window, displayed with a ">". - if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) { - if (headp != NULL) { - *headp = 1; - } - return 3; - } - return n; -} - -/// Return TRUE if virtual column "vcol" is in the rightmost column of window -/// "wp". -/// -/// @param wp -/// @param vcol -/// -/// @return TRUE if the virtual column is in the rightmost column. -int in_win_border(win_T *wp, colnr_T vcol) -{ - int width1; // width of first line (after line number) - int width2; // width of further lines - - if (wp->w_width == 0) { - // there is no border - return FALSE; - } - width1 = W_WIDTH(wp) - win_col_off(wp); - - if ((int)vcol < width1 - 1) { - return FALSE; - } - - if ((int)vcol == width1 - 1) { - return TRUE; - } - width2 = width1 + win_col_off2(wp); - - if (width2 <= 0) { - return FALSE; - } - return (vcol - width1) % width2 == width2 - 1; -} - -/// Get virtual column number of pos. -/// start: on the first position of this character (TAB, ctrl) -/// cursor: where the cursor is on this character (first char, except for TAB) -/// end: on the last position of this character (TAB, ctrl) -/// -/// This is used very often, keep it fast! -/// -/// @param wp -/// @param pos -/// @param start -/// @param cursor -/// @param end -void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, - colnr_T *end) -{ - colnr_T vcol; - char_u *ptr; // points to current char - char_u *posptr; // points to char at pos->col - int incr; - int head; - int ts = wp->w_buffer->b_p_ts; - int c; - - vcol = 0; - ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); - - if (pos->col == MAXCOL) { - // continue until the NUL - posptr = NULL; - } else { - posptr = ptr + pos->col; - } - - // This function is used very often, do some speed optimizations. - // When 'list', 'linebreak' and 'showbreak' are not set use a simple loop. - // Also use this when 'list' is set but tabs take their normal size. - if ((!wp->w_p_list || (lcs_tab1 != NUL)) - && !wp->w_p_lbr - && (*p_sbr == NUL)) { - for (;;) { - head = 0; - c = *ptr; - - // make sure we don't go past the end of the line - if (c == NUL) { - // NUL at end of line only takes one column - incr = 1; - break; - } - - // A tab gets expanded, depending on the current column - if (c == TAB) { - incr = ts - (vcol % ts); - } else { - if (has_mbyte) { - // For utf-8, if the byte is >= 0x80, need to look at - // further bytes to find the cell width. - if (enc_utf8 && (c >= 0x80)) { - incr = utf_ptr2cells(ptr); - } else { - incr = CHARSIZE(c); - } - - // If a double-cell char doesn't fit at the end of a line - // it wraps to the next line, it's like this char is three - // cells wide. - if ((incr == 2) - && wp->w_p_wrap - && (MB_BYTE2LEN(*ptr) > 1) - && in_win_border(wp, vcol)) { - ++incr; - head = 1; - } - } else { - incr = CHARSIZE(c); - } - } - - if ((posptr != NULL) && (ptr >= posptr)) { - // character at pos->col - break; - } - - vcol += incr; - mb_ptr_adv(ptr); - } - } else { - for (;;) { - // A tab gets expanded, depending on the current column - head = 0; - incr = win_lbr_chartabsize(wp, ptr, vcol, &head); - - // make sure we don't go past the end of the line - if (*ptr == NUL) { - // NUL at end of line only takes one column - incr = 1; - break; - } - - if ((posptr != NULL) && (ptr >= posptr)) { - // character at pos->col - break; - } - - vcol += incr; - mb_ptr_adv(ptr); - } - } - - if (start != NULL) { - *start = vcol + head; - } - - if (end != NULL) { - *end = vcol + incr - 1; - } - - if (cursor != NULL) { - if ((*ptr == TAB) - && (State & NORMAL) - && !wp->w_p_list - && !virtual_active() - && !(VIsual_active && ((*p_sel == 'e') || ltoreq(*pos, VIsual)))) { - // cursor at end - *cursor = vcol + incr - 1; - } else { - // cursor at start - *cursor = vcol + head; - } - } -} - -/// Get virtual cursor column in the current window, pretending 'list' is off. -/// -/// @param posp -/// -/// @retujrn The virtual cursor column. -colnr_T getvcol_nolist(pos_T *posp) -{ - int list_save = curwin->w_p_list; - colnr_T vcol; - - curwin->w_p_list = FALSE; - getvcol(curwin, posp, NULL, &vcol, NULL); - curwin->w_p_list = list_save; - return vcol; -} - -/// Get virtual column in virtual mode. -/// -/// @param wp -/// @param pos -/// @param start -/// @param cursor -/// @param end -void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, - colnr_T *end) -{ - colnr_T col; - colnr_T coladd; - colnr_T endadd; - char_u *ptr; - - if (virtual_active()) { - // For virtual mode, only want one value - getvcol(wp, pos, &col, NULL, NULL); - - coladd = pos->coladd; - endadd = 0; - - // Cannot put the cursor on part of a wide character. - ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); - - if (pos->col < (colnr_T)STRLEN(ptr)) { - int c = (*mb_ptr2char)(ptr + pos->col); - if ((c != TAB) && vim_isprintc(c)) { - endadd = (colnr_T)(char2cells(c) - 1); - if (coladd > endadd) { - // past end of line - endadd = 0; - } else { - coladd = 0; - } - } - } - col += coladd; - - if (start != NULL) { - *start = col; - } - - if (cursor != NULL) { - *cursor = col; - } - - if (end != NULL) { - *end = col + endadd; - } - } else { - getvcol(wp, pos, start, cursor, end); - } -} - -/// Get the leftmost and rightmost virtual column of pos1 and pos2. -/// Used for Visual block mode. -/// -/// @param wp -/// @param pos1 -/// @param pos2 -/// @param left -/// @param right -void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, - colnr_T *right) -{ - colnr_T from1; - colnr_T from2; - colnr_T to1; - colnr_T to2; - - if (ltp(pos1, pos2)) { - getvvcol(wp, pos1, &from1, NULL, &to1); - getvvcol(wp, pos2, &from2, NULL, &to2); - } else { - getvvcol(wp, pos2, &from1, NULL, &to1); - getvvcol(wp, pos1, &from2, NULL, &to2); - } - - if (from2 < from1) { - *left = from2; - } else { - *left = from1; - } - - if (to2 > to1) { - if ((*p_sel == 'e') && (from2 - 1 >= to1)) { - *right = from2 - 1; - } else { - *right = to2; - } - } else { - *right = to1; - } -} - -/// skipwhite: skip over ' ' and '\t'. -/// -/// @param q -/// -/// @return Pointer to character after the skipped whitespace. -char_u* skipwhite(char_u *q) -{ - char_u *p = q; - while (vim_iswhite(*p)) { - // skip to next non-white - p++; - } - return p; -} - -/// skip over digits -/// -/// @param q -/// -/// @return Pointer to the character after the skipped digits. -char_u* skipdigits(char_u *q) -{ - char_u *p = q; - while (VIM_ISDIGIT(*p)) { - // skip to next non-digit - p++; - } - return p; -} - -/// skip over digits and hex characters -/// -/// @param q -/// -/// @return Pointer to the character after the skipped digits and hex -/// characters. -char_u* skiphex(char_u *q) -{ - char_u *p = q; - while (vim_isxdigit(*p)) { - // skip to next non-digit - p++; - } - return p; -} - -/// skip to digit (or NUL after the string) -/// -/// @param q -/// -/// @return Pointer to the digit or (NUL after the string). -char_u* skiptodigit(char_u *q) -{ - char_u *p = q; - while (*p != NUL && !VIM_ISDIGIT(*p)) { - // skip to next digit - p++; - } - return p; -} - -/// skip to hex character (or NUL after the string) -/// -/// @param q -/// -/// @return Pointer to the hex character or (NUL after the string). -char_u* skiptohex(char_u *q) -{ - char_u *p = q; - while (*p != NUL && !vim_isxdigit(*p)) { - // skip to next digit - p++; - } - return p; -} - -/// Variant of isdigit() that can handle characters > 0x100. -/// We don't use isdigit() here, because on some systems it also considers -/// superscript 1 to be a digit. -/// Use the VIM_ISDIGIT() macro for simple arguments. -/// -/// @param c -/// -/// @return TRUE if the character is a digit. -int vim_isdigit(int c) -{ - return c >= '0' && c <= '9'; -} - -/// Variant of isxdigit() that can handle characters > 0x100. -/// We don't use isxdigit() here, because on some systems it also considers -/// superscript 1 to be a digit. -/// -/// @param c -/// -/// @return TRUE if the character is a digit. -int vim_isxdigit(int c) -{ - return (c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f') - || (c >= 'A' && c <= 'F'); -} - -// Vim's own character class functions. These exist because many library -// islower()/toupper() etc. do not work properly: they crash when used with -// invalid values or can't handle latin1 when the locale is C. -// Speed is most important here. -#define LATIN1LOWER 'l' -#define LATIN1UPPER 'U' - -static char_u latin1flags[257] = - " " - " UUUUUUUUUUUUUUUUUUUUUUUUUU llllllllllllllllllllllllll " - " " - "UUUUUUUUUUUUUUUUUUUUUUU UUUUUUUllllllllllllllllllllllll llllllll"; -static char_u latin1upper[257] = - " !\"#$%&'()*+,-./0123456789:;<=>" - "?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~" - "\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e" - "\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e" - "\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae" - "\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe" - "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce" - "\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde" - "\xdf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce" - "\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xf7\xd8\xd9\xda\xdb\xdc\xdd\xde\xff"; -static char_u latin1lower[257] = - " !\"#$%&'()*+,-./0123456789:;<=>" - "?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - "\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e" - "\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e" - "\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae" - "\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe" - "\xbf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee" - "\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xd7\xf8\xf9\xfa\xfb\xfc\xfd\xfe" - "\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee" - "\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; - -int vim_islower(int c) -{ - if (c <= '@') { - return FALSE; - } - - if (c >= 0x80) { - if (enc_utf8) { - return utf_islower(c); - } - - if (c >= 0x100) { - if (has_mbyte) { - return iswlower(c); - } - - // islower() can't handle these chars and may crash - return FALSE; - } - - if (enc_latin1like) { - return (latin1flags[c] & LATIN1LOWER) == LATIN1LOWER; - } - } - return islower(c); -} - -int vim_isupper(int c) -{ - if (c <= '@') { - return FALSE; - } - - if (c >= 0x80) { - if (enc_utf8) { - return utf_isupper(c); - } - - if (c >= 0x100) { - if (has_mbyte) { - return iswupper(c); - } - - // islower() can't handle these chars and may crash - return FALSE; - } - - if (enc_latin1like) { - return (latin1flags[c] & LATIN1UPPER) == LATIN1UPPER; - } - } - return isupper(c); -} - -int vim_toupper(int c) -{ - if (c <= '@') { - return c; - } - - if (c >= 0x80) { - if (enc_utf8) { - return utf_toupper(c); - } - - if (c >= 0x100) { - if (has_mbyte) { - return towupper(c); - } - - // toupper() can't handle these chars and may crash - return c; - } - - if (enc_latin1like) { - return latin1upper[c]; - } - } - return TOUPPER_LOC(c); -} - -int vim_tolower(int c) -{ - if (c <= '@') { - return c; - } - - if (c >= 0x80) { - if (enc_utf8) { - return utf_tolower(c); - } - - if (c >= 0x100) { - if (has_mbyte) { - return towlower(c); - } - - // tolower() can't handle these chars and may crash - return c; - } - - if (enc_latin1like) { - return latin1lower[c]; - } - } - return TOLOWER_LOC(c); -} - -/// skiptowhite: skip over text until ' ' or '\t' or NUL. -/// -/// @param p -/// -/// @return Pointer to the next whitespace or NUL character. -char_u* skiptowhite(char_u *p) -{ - while (*p != ' ' && *p != '\t' && *p != NUL) { - p++; - } - return p; -} - -/// skiptowhite_esc: Like skiptowhite(), but also skip escaped chars -/// -/// @param p -/// -/// @return Pointer to the next whitespace character. -char_u* skiptowhite_esc(char_u *p) { - while (*p != ' ' && *p != '\t' && *p != NUL) { - if (((*p == '\\') || (*p == Ctrl_V)) && (*(p + 1) != NUL)) { - ++p; - } - ++p; - } - return p; -} - -/// Getdigits: Get a number from a string and skip over it. -/// -/// Note: the argument is a pointer to a char_u pointer! -/// -/// @param pp -/// -/// @return Number from the string. -long getdigits(char_u **pp) -{ - char_u *p = *pp; - long retval = atol((char *)p); - - if (*p == '-') { - // skip negative sign - ++p; - } - // skip to next non-digit - p = skipdigits(p); - *pp = p; - return retval; -} - -/// Return TRUE if "lbuf" is empty or only contains blanks. -/// -/// @param lbuf -/// -/// @return TRUE if `lbuf` is empty or only contains blanks. -int vim_isblankline(char_u *lbuf) -{ - char_u *p = skipwhite(lbuf); - return *p == NUL || *p == '\r' || *p == '\n'; -} - -/// Convert a string into a long and/or unsigned long, taking care of -/// hexadecimal and octal numbers. Accepts a '-' sign. -/// If "hexp" is not NULL, returns a flag to indicate the type of the number: -/// 0 decimal -/// '0' octal -/// 'X' hex -/// 'x' hex -/// If "len" is not NULL, the length of the number in characters is returned. -/// If "nptr" is not NULL, the signed result is returned in it. -/// If "unptr" is not NULL, the unsigned result is returned in it. -/// If "dooct" is non-zero recognize octal numbers, when > 1 always assume -/// octal number. -/// If "dohex" is non-zero recognize hex numbers, when > 1 always assume -/// hex number. -/// -/// @param start -/// @param hexp Returns type of number 0 = decimal, 'x' or 'X' is hex, -// '0' = octal -/// @param len Returns the detected length of number. -/// @param dooct recognize octal number -/// @param dohex recognize hex number -/// @param nptr Returns the signed result. -/// @param unptr Returns the unsigned result. -void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, int dohex, - long *nptr, unsigned long *unptr) -{ - char_u *ptr = start; - int hex = 0; // default is decimal - int negative = FALSE; - unsigned long un = 0; - int n; - - if (ptr[0] == '-') { - negative = TRUE; - ++ptr; - } - - // Recognize hex and octal. - if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')) { - hex = ptr[1]; - - if (dohex - && ((hex == 'X') || (hex == 'x')) - && vim_isxdigit(ptr[2])) { - // hexadecimal - ptr += 2; - } else { - // default is decimal - hex = 0; - - if (dooct) { - // Don't interpret "0", "08" or "0129" as octal. - for (n = 1; VIM_ISDIGIT(ptr[n]); ++n) { - if (ptr[n] > '7') { - // can't be octal - hex = 0; - break; - } - - if (ptr[n] >= '0') { - // assume octal - hex = '0'; - } - } - } - } - } - - // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - if ((hex == '0') || (dooct > 1)) { - // octal - while ('0' <= *ptr && *ptr <= '7') { - un = 8 * un + (unsigned long)(*ptr - '0'); - ptr++; - } - } else if ((hex != 0) || (dohex > 1)) { - // hex - while (vim_isxdigit(*ptr)) { - un = 16 * un + (unsigned long)hex2nr(*ptr); - ptr++; - } - } else { - // decimal - while (VIM_ISDIGIT(*ptr)) { - un = 10 * un + (unsigned long)(*ptr - '0'); - ptr++; - } - } - - if (hexp != NULL) { - *hexp = hex; - } - - if (len != NULL) { - *len = (int)(ptr - start); - } - - if (nptr != NULL) { - if (negative) { - // account for leading '-' for decimal numbers - *nptr = -(long)un; - } else { - *nptr = (long)un; - } - } - - if (unptr != NULL) { - *unptr = un; - } -} - -/// Return the value of a single hex character. -/// Only valid when the argument is '0' - '9', 'A' - 'F' or 'a' - 'f'. -/// -/// @param c -/// -/// @return The value of the hex character. -int hex2nr(int c) -{ - if ((c >= 'a') && (c <= 'f')) { - return c - 'a' + 10; - } - - if ((c >= 'A') && (c <= 'F')) { - return c - 'A' + 10; - } - return c - '0'; -} - -#if defined(FEAT_TERMRESPONSE) \ - || (defined(FEAT_GUI_GTK) && defined(FEAT_WINDOWS)) \ - || defined(PROTO) - -/// Convert two hex characters to a byte. -/// Return -1 if one of the characters is not hex. -/// -/// @param p -/// -/// @return The two hex characters converted to a byte or -1 if one of the -/// character is not hex. -int hexhex2nr(char_u *p) -{ - if (!vim_isxdigit(p[0]) || !vim_isxdigit(p[1])) { - return -1; - } - return (hex2nr(p[0]) << 4) + hex2nr(p[1]); -} - -#endif // if defined(FEAT_TERMRESPONSE) || (defined(FEAT_GUI_GTK) - // && defined(FEAT_WINDOWS)) || defined(PROTO) - -/// Return TRUE if "str" starts with a backslash that should be removed. -/// For WIN32 this is only done when the character after the -/// backslash is not a normal file name character. -/// '$' is a valid file name character, we don't remove the backslash before -/// it. This means it is not possible to use an environment variable after a -/// backslash. "C:\$VIM\doc" is taken literally, only "$VIM\doc" works. -/// Although "\ name" is valid, the backslash in "Program\ files" must be -/// removed. Assume a file name doesn't start with a space. -/// For multi-byte names, never remove a backslash before a non-ascii -/// character, assume that all multi-byte characters are valid file name -/// characters. -/// -/// @param str -/// -/// @return TRUE if `str` starts with a backslash that should be removed. -int rem_backslash(char_u *str) -{ -#ifdef BACKSLASH_IN_FILENAME - return str[0] == '\\' - && str[1] < 0x80 - && (str[1] == ' ' - || (str[1] != NUL - && str[1] != '*' - && str[1] != '?' - && !vim_isfilec(str[1]))); - -#else // ifdef BACKSLASH_IN_FILENAME - return str[0] == '\\' && str[1] != NUL; -#endif // ifdef BACKSLASH_IN_FILENAME -} - -/// Halve the number of backslashes in a file name argument. -/// -/// @param p -void backslash_halve(char_u *p) -{ - for (; *p; ++p) { - if (rem_backslash(p)) { - STRMOVE(p, p + 1); - } - } -} - -/// backslash_halve() plus save the result in allocated memory. -/// -/// @param p -/// -/// @return String with the number of backslashes halved. -char_u* backslash_halve_save(char_u *p) -{ - char_u *res = vim_strsave(p); - if (res == NULL) { - return p; - } - backslash_halve(res); - return res; -} diff --git a/src/charset.h b/src/charset.h deleted file mode 100644 index 169c83ccc1..0000000000 --- a/src/charset.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef NEOVIM_CHARSET_H -#define NEOVIM_CHARSET_H - -int init_chartab(void); -int buf_init_chartab(buf_T *buf, int global); -void trans_characters(char_u *buf, int bufsize); -char_u *transstr(char_u *s); -char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen); -char_u *transchar(int c); -char_u *transchar_byte(int c); -void transchar_nonprint(char_u *buf, int c); -void transchar_hex(char_u *buf, int c); -int byte2cells(int b); -int char2cells(int c); -int ptr2cells(char_u *p); -int vim_strsize(char_u *s); -int vim_strnsize(char_u *s, int len); -int chartabsize(char_u *p, colnr_T col); -int linetabsize(char_u *s); -int linetabsize_col(int startcol, char_u *s); -int win_linetabsize(win_T *wp, char_u *p, colnr_T len); -int vim_isIDc(int c); -int vim_iswordc(int c); -int vim_iswordc_buf(int c, buf_T *buf); -int vim_iswordp(char_u *p); -int vim_iswordp_buf(char_u *p, buf_T *buf); -int vim_isfilec(int c); -int vim_isfilec_or_wc(int c); -int vim_isprintc(int c); -int vim_isprintc_strict(int c); -int lbr_chartabsize(unsigned char *s, colnr_T col); -int lbr_chartabsize_adv(char_u **s, colnr_T col); -int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp); -int in_win_border(win_T *wp, colnr_T vcol); -void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, - colnr_T *end); -colnr_T getvcol_nolist(pos_T *posp); -void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, - colnr_T *end); -void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, - colnr_T *right); -char_u *skipwhite(char_u *q); -char_u *skipdigits(char_u *q); -char_u *skiphex(char_u *q); -char_u *skiptodigit(char_u *q); -char_u *skiptohex(char_u *q); -int vim_isdigit(int c); -int vim_isxdigit(int c); -int vim_islower(int c); -int vim_isupper(int c); -int vim_toupper(int c); -int vim_tolower(int c); -char_u *skiptowhite(char_u *p); -char_u *skiptowhite_esc(char_u *p); -long getdigits(char_u **pp); -int vim_isblankline(char_u *lbuf); -void vim_str2nr(char_u *start, int *hexp, int *len, int dooct, - int dohex, long *nptr, - unsigned long *unptr); -int hex2nr(int c); -int hexhex2nr(char_u *p); -int rem_backslash(char_u *str); -void backslash_halve(char_u *p); -char_u *backslash_halve_save(char_u *p); - -#endif // NEOVIM_CHARSET_H diff --git a/src/crypt.c b/src/crypt.c deleted file mode 100644 index 72d4a89c63..0000000000 --- a/src/crypt.c +++ /dev/null @@ -1,236 +0,0 @@ -// Optional encryption support. -// Mohsin Ahmed, mosh@sasi.com, 98-09-24 -// Based on zip/crypt sources. -// -// NOTE FOR USA: Since 2000 exporting this code from the USA is allowed to -// most countries. There are a few exceptions, but that still should not be a -// problem since this code was originally created in Europe and India. -// -// Blowfish addition originally made by Mohsin Ahmed, -// http://www.cs.albany.edu/~mosh 2010-03-14 -// Based on blowfish by Bruce Schneier (http://www.schneier.com/blowfish.html) -// and sha256 by Christophe Devine. - -#include "vim.h" -#include "misc2.h" -#include "blowfish.h" -#include "ex_getln.h" -#include "message.h" -#include "option.h" - -static void make_crc_tab(void); - -static uint32_t crc_32_tab[256]; - -// Fills the CRC table. -static void make_crc_tab(void) -{ - uint32_t s; - uint32_t t; - uint32_t v; - static bool done = false; - - if (done) { - return; - } - - for (t = 0; t < 256; t++) { - v = t; - - for (s = 0; s < 8; s++) { - v = (v >> 1) ^ ((v & 1) * (uint32_t)0xedb88320L); - } - crc_32_tab[t] = v; - } - done = true; -} - -#define CRC32(c, b) (crc_32_tab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) - -static uint32_t keys[3]; // keys defining the pseudo-random sequence - -// Returns the next byte in the pseudo-random sequence. -#define DECRYPT_BYTE_ZIP(t) { \ - uint16_t temp; \ - temp = (uint16_t)keys[2] | 2; \ - t = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); \ -} - -// Updates the encryption keys with the next byte of plain text. -#define UPDATE_KEYS_ZIP(c) { \ - keys[0] = CRC32(keys[0], (c)); \ - keys[1] += keys[0] & 0xff; \ - keys[1] = keys[1] * 134775813L + 1; \ - keys[2] = CRC32(keys[2], (int)(keys[1] >> 24)); \ -} - -static int crypt_busy = 0; -static uint32_t saved_keys[3]; -static int saved_crypt_method; - -int crypt_method_from_string(char_u *s) -{ - return *s == 'b' ? 1 : 0; -} - -int get_crypt_method(buf_T *buf) -{ - return crypt_method_from_string(*buf->b_p_cm == NUL ? p_cm : buf->b_p_cm); -} - -void set_crypt_method(buf_T *buf, int method) -{ - free_string_option(buf->b_p_cm); - buf->b_p_cm = vim_strsave((char_u *)(method == 0 ? "zip" : "blowfish")); -} - -void crypt_push_state(void) -{ - if (crypt_busy == 1) { - // Save the state - if (use_crypt_method == 0) { - saved_keys[0] = keys[0]; - saved_keys[1] = keys[1]; - saved_keys[2] = keys[2]; - } else { - bf_crypt_save(); - } - saved_crypt_method = use_crypt_method; - } else if (crypt_busy > 1) { - EMSG2(_(e_intern2), "crypt_push_state()"); - } - crypt_busy++; -} - -void crypt_pop_state(void) -{ - crypt_busy--; - - if (crypt_busy == 1) { - use_crypt_method = saved_crypt_method; - - if (use_crypt_method == 0) { - keys[0] = saved_keys[0]; - keys[1] = saved_keys[1]; - keys[2] = saved_keys[2]; - } else { - bf_crypt_restore(); - } - } -} - -void crypt_encode(char_u *from, size_t len, char_u *to) -{ - size_t i; - int ztemp; - int t; - - if (use_crypt_method == 0) { - for (i = 0; i < len; i++) { - ztemp = from[i]; - DECRYPT_BYTE_ZIP(t); - UPDATE_KEYS_ZIP(ztemp); - to[i] = t ^ ztemp; - } - } else { - bf_crypt_encode(from, len, to); - } -} - -void crypt_decode(char_u *ptr, long len) -{ - char_u *p; - - if (use_crypt_method == 0) { - for (p = ptr; p < ptr + len; p++) { - uint16_t temp; - - temp = (uint16_t)keys[2] | 2; - temp = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); - UPDATE_KEYS_ZIP(*p ^= temp); - } - } else { - bf_crypt_decode(ptr, len); - } -} - -void crypt_init_keys(char_u *passwd) -{ - if ((passwd != NULL) && (*passwd != NUL)) { - if (use_crypt_method == 0) { - char_u *p; - - make_crc_tab(); - keys[0] = 305419896L; - keys[1] = 591751049L; - keys[2] = 878082192L; - - for (p = passwd; *p != NUL; p++) { - UPDATE_KEYS_ZIP((int)*p); - } - } else { - bf_crypt_init_keys(passwd); - } - } -} - -void free_crypt_key(char_u *key) -{ - char_u *p; - - if (key != NULL) { - for (p = key; *p != NUL; p++) { - *p = 0; - } - free(key); - } -} - -char_u *get_crypt_key(int store, int twice) -{ - char_u *p1; - char_u *p2 = NULL; - int round; - - for (round = 0;; round++) { - cmdline_star = TRUE; - cmdline_row = msg_row; - char_u *prompt = (round == 0) - ? (char_u *) _("Enter encryption key: ") - : (char_u *) _("Enter same key again: "); - p1 = getcmdline_prompt(NUL, prompt, 0, EXPAND_NOTHING, NULL); - cmdline_star = FALSE; - if (p1 == NULL) { - break; - } - - if (round == twice) { - if ((p2 != NULL) && (STRCMP(p1, p2) != 0)) { - MSG(_("Keys don't match!")); - free_crypt_key(p1); - free_crypt_key(p2); - p2 = NULL; - round = -1; // Do it again - continue; - } - - if (store) { - set_option_value((char_u *) "key", 0L, p1, OPT_LOCAL); - free_crypt_key(p1); - p1 = curbuf->b_p_key; - } - break; - } - p2 = p1; - } - - // Since the user typed this, no need to wait for return. - if (msg_didout) { - msg_putchar('\n'); - } - need_wait_return = FALSE; - msg_didout = FALSE; - - free_crypt_key(p2); - return p1; -} diff --git a/src/crypt.h b/src/crypt.h deleted file mode 100644 index 8794360dd5..0000000000 --- a/src/crypt.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef NEOVIM_CRYPT_H -#define NEOVIM_CRYPT_H - -/// Returns the crypt method string as a number. -/// -/// @param s Pointer to the crypt method string. -/// -/// @return An integer value of the crypt method: -/// 0 for "zip", the old method. Also for any non-valid value. -/// 1 for "blowfish". -int crypt_method_from_string(char_u *s); - -/// Returns the crypt method of the buffer "buf" as a number. -/// -/// @param buf Pointer to the buffer. -/// -/// @return An integer value of the crypt method: -/// 0 for "zip", the old method. Also for any non-valid value. -/// 1 for "blowfish". -int get_crypt_method(buf_T *buf); - -/// Sets the crypt method for buffer "buf" to "method" using the -/// int value as returned by crypt_method_from_string(). -/// -/// @param buf Pointer to the buffer. -/// @param method Crypt method. -void set_crypt_method(buf_T *buf, int method); - -/// Prepares for initializing the encryption. If already doing encryption, -/// then save the state. -/// -/// This function must always be called symmetrically with crypt_pop_state(). -void crypt_push_state(void); - -/// Ends encryption. If already doing encryption before crypt_push_state(), -/// then restore the saved state. -/// -/// This function must always be called symmetrically with crypt_push_state(). -void crypt_pop_state(void); - -/// Encrypts "from[len]" into "to[len]". -/// For in-place encryption, "from" and "len" must be the same. -/// -/// @param from Pointer to the source string. -/// @param len Length of the strings. -/// @param to Pointer to the destination string. -void crypt_encode(char_u *from, size_t len, char_u *to); - -/// Decrypts "ptr[len]" in-place. -/// -/// @param ptr Pointer to the string. -/// @param len Length of the string. -void crypt_decode(char_u *ptr, long len); - -/// Initializes the encryption keys and the random header according to -/// the given password. -/// -/// If "password" is NULL or empty, the function doesn't do anything. -/// -/// @param passwd The password string with which to modify keys. -void crypt_init_keys(char_u *passwd); - -/// Frees an allocated crypt key and clears the text to make sure -/// nothing stays in memory. -/// -/// @param key The crypt key to be freed. -void free_crypt_key(char_u *key); - -/// Asks the user for the crypt key. -/// -/// When "store" is TRUE, the new key is stored in the 'key' option -/// and the 'key' option value is returned, which MUST NOT be freed -/// manually, but using free_crypt_key(). -/// When "store" is FALSE, the typed key is returned in allocated memory. -/// -/// @param store Determines, whether the new crypt key is stored. -/// @param twice Ask for the key twice. -/// -/// @return The crypt key. On failure, NULL is returned. -char_u *get_crypt_key(int store, int twice); - -#endif // NEOVIM_CRYPT_H diff --git a/src/cursor_shape.c b/src/cursor_shape.c deleted file mode 100644 index 19db088817..0000000000 --- a/src/cursor_shape.c +++ /dev/null @@ -1,209 +0,0 @@ -#include "vim.h" -#include "cursor_shape.h" -#include "misc2.h" -#include "ex_getln.h" -#include "charset.h" -#include "syntax.h" - -/* - * Handling of cursor and mouse pointer shapes in various modes. - */ - -static cursorentry_T shape_table[SHAPE_IDX_COUNT] = -{ - /* The values will be filled in from the 'guicursor' and 'mouseshape' - * defaults when Vim starts. - * Adjust the SHAPE_IDX_ defines when making changes! */ - {0, 0, 0, 700L, 400L, 250L, 0, 0, "n", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "v", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "i", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "r", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "c", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ci", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "cr", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "o", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 700L, 400L, 250L, 0, 0, "ve", SHAPE_CURSOR+SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "e", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "s", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "sd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vs", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "vd", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "m", SHAPE_MOUSE}, - {0, 0, 0, 0L, 0L, 0L, 0, 0, "ml", SHAPE_MOUSE}, - {0, 0, 0, 100L, 100L, 100L, 0, 0, "sm", SHAPE_CURSOR}, -}; - -/* - * Parse the 'guicursor' option ("what" is SHAPE_CURSOR) or 'mouseshape' - * ("what" is SHAPE_MOUSE). - * Returns error message for an illegal option, NULL otherwise. - */ -char_u *parse_shape_opt(int what) -{ - char_u *modep; - char_u *colonp; - char_u *commap; - char_u *slashp; - char_u *p, *endp; - int idx = 0; /* init for GCC */ - int all_idx; - int len; - int i; - long n; - int found_ve = FALSE; /* found "ve" flag */ - int round; - - /* - * First round: check for errors; second round: do it for real. - */ - for (round = 1; round <= 2; ++round) { - /* - * Repeat for all comma separated parts. - */ - modep = p_guicursor; - while (*modep != NUL) { - colonp = vim_strchr(modep, ':'); - if (colonp == NULL) - return (char_u *)N_("E545: Missing colon"); - if (colonp == modep) - return (char_u *)N_("E546: Illegal mode"); - commap = vim_strchr(modep, ','); - - /* - * Repeat for all mode's before the colon. - * For the 'a' mode, we loop to handle all the modes. - */ - all_idx = -1; - while (modep < colonp || all_idx >= 0) { - if (all_idx < 0) { - /* Find the mode. */ - if (modep[1] == '-' || modep[1] == ':') - len = 1; - else - len = 2; - if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') - all_idx = SHAPE_IDX_COUNT - 1; - else { - for (idx = 0; idx < SHAPE_IDX_COUNT; ++idx) - if (STRNICMP(modep, shape_table[idx].name, len) - == 0) - break; - if (idx == SHAPE_IDX_COUNT - || (shape_table[idx].used_for & what) == 0) - return (char_u *)N_("E546: Illegal mode"); - if (len == 2 && modep[0] == 'v' && modep[1] == 'e') - found_ve = TRUE; - } - modep += len + 1; - } - - if (all_idx >= 0) - idx = all_idx--; - else if (round == 2) { - { - /* Set the defaults, for the missing parts */ - shape_table[idx].shape = SHAPE_BLOCK; - shape_table[idx].blinkwait = 700L; - shape_table[idx].blinkon = 400L; - shape_table[idx].blinkoff = 250L; - } - } - - /* Parse the part after the colon */ - for (p = colonp + 1; *p && *p != ','; ) { - { - /* - * First handle the ones with a number argument. - */ - i = *p; - len = 0; - if (STRNICMP(p, "ver", 3) == 0) - len = 3; - else if (STRNICMP(p, "hor", 3) == 0) - len = 3; - else if (STRNICMP(p, "blinkwait", 9) == 0) - len = 9; - else if (STRNICMP(p, "blinkon", 7) == 0) - len = 7; - else if (STRNICMP(p, "blinkoff", 8) == 0) - len = 8; - if (len != 0) { - p += len; - if (!VIM_ISDIGIT(*p)) - return (char_u *)N_("E548: digit expected"); - n = getdigits(&p); - if (len == 3) { /* "ver" or "hor" */ - if (n == 0) - return (char_u *)N_("E549: Illegal percentage"); - if (round == 2) { - if (TOLOWER_ASC(i) == 'v') - shape_table[idx].shape = SHAPE_VER; - else - shape_table[idx].shape = SHAPE_HOR; - shape_table[idx].percentage = n; - } - } else if (round == 2) { - if (len == 9) - shape_table[idx].blinkwait = n; - else if (len == 7) - shape_table[idx].blinkon = n; - else - shape_table[idx].blinkoff = n; - } - } else if (STRNICMP(p, "block", 5) == 0) { - if (round == 2) - shape_table[idx].shape = SHAPE_BLOCK; - p += 5; - } else { /* must be a highlight group name then */ - endp = vim_strchr(p, '-'); - if (commap == NULL) { /* last part */ - if (endp == NULL) - endp = p + STRLEN(p); /* find end of part */ - } else if (endp > commap || endp == NULL) - endp = commap; - slashp = vim_strchr(p, '/'); - if (slashp != NULL && slashp < endp) { - /* "group/langmap_group" */ - i = syn_check_group(p, (int)(slashp - p)); - p = slashp + 1; - } - if (round == 2) { - shape_table[idx].id = syn_check_group(p, - (int)(endp - p)); - shape_table[idx].id_lm = shape_table[idx].id; - if (slashp != NULL && slashp < endp) - shape_table[idx].id = i; - } - p = endp; - } - } /* if (what != SHAPE_MOUSE) */ - - if (*p == '-') - ++p; - } - } - modep = p; - if (*modep == ',') - ++modep; - } - } - - /* If the 's' flag is not given, use the 'v' cursor for 's' */ - if (!found_ve) { - { - shape_table[SHAPE_IDX_VE].shape = shape_table[SHAPE_IDX_V].shape; - shape_table[SHAPE_IDX_VE].percentage = - shape_table[SHAPE_IDX_V].percentage; - shape_table[SHAPE_IDX_VE].blinkwait = - shape_table[SHAPE_IDX_V].blinkwait; - shape_table[SHAPE_IDX_VE].blinkon = - shape_table[SHAPE_IDX_V].blinkon; - shape_table[SHAPE_IDX_VE].blinkoff = - shape_table[SHAPE_IDX_V].blinkoff; - shape_table[SHAPE_IDX_VE].id = shape_table[SHAPE_IDX_V].id; - shape_table[SHAPE_IDX_VE].id_lm = shape_table[SHAPE_IDX_V].id_lm; - } - } - - return NULL; -} diff --git a/src/cursor_shape.h b/src/cursor_shape.h deleted file mode 100644 index 33a120e1c4..0000000000 --- a/src/cursor_shape.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef NEOVIM_CURSOR_SHAPE_H -#define NEOVIM_CURSOR_SHAPE_H - -/* - * struct to store values from 'guicursor' and 'mouseshape' - */ -/* Indexes in shape_table[] */ -#define SHAPE_IDX_N 0 /* Normal mode */ -#define SHAPE_IDX_V 1 /* Visual mode */ -#define SHAPE_IDX_I 2 /* Insert mode */ -#define SHAPE_IDX_R 3 /* Replace mode */ -#define SHAPE_IDX_C 4 /* Command line Normal mode */ -#define SHAPE_IDX_CI 5 /* Command line Insert mode */ -#define SHAPE_IDX_CR 6 /* Command line Replace mode */ -#define SHAPE_IDX_O 7 /* Operator-pending mode */ -#define SHAPE_IDX_VE 8 /* Visual mode with 'selection' exclusive */ -#define SHAPE_IDX_CLINE 9 /* On command line */ -#define SHAPE_IDX_STATUS 10 /* A status line */ -#define SHAPE_IDX_SDRAG 11 /* dragging a status line */ -#define SHAPE_IDX_VSEP 12 /* A vertical separator line */ -#define SHAPE_IDX_VDRAG 13 /* dragging a vertical separator line */ -#define SHAPE_IDX_MORE 14 /* Hit-return or More */ -#define SHAPE_IDX_MOREL 15 /* Hit-return or More in last line */ -#define SHAPE_IDX_SM 16 /* showing matching paren */ -#define SHAPE_IDX_COUNT 17 - -#define SHAPE_BLOCK 0 /* block cursor */ -#define SHAPE_HOR 1 /* horizontal bar cursor */ -#define SHAPE_VER 2 /* vertical bar cursor */ - -#define MSHAPE_NUMBERED 1000 /* offset for shapes identified by number */ -#define MSHAPE_HIDE 1 /* hide mouse pointer */ - -#define SHAPE_MOUSE 1 /* used for mouse pointer shape */ -#define SHAPE_CURSOR 2 /* used for text cursor shape */ - -typedef struct cursor_entry { - int shape; /* one of the SHAPE_ defines */ - int mshape; /* one of the MSHAPE defines */ - int percentage; /* percentage of cell for bar */ - long blinkwait; /* blinking, wait time before blinking starts */ - long blinkon; /* blinking, on time */ - long blinkoff; /* blinking, off time */ - int id; /* highlight group ID */ - int id_lm; /* highlight group ID for :lmap mode */ - char *name; /* mode name (fixed) */ - char used_for; /* SHAPE_MOUSE and/or SHAPE_CURSOR */ -} cursorentry_T; - -char_u *parse_shape_opt(int what); - -#endif /* NEOVIM_CURSOR_SHAPE_H */ diff --git a/src/diff.c b/src/diff.c deleted file mode 100644 index e360edc665..0000000000 --- a/src/diff.c +++ /dev/null @@ -1,2611 +0,0 @@ -/// @file diff.c -/// -/// Code for diff'ing two, three or four buffers. - -#include "vim.h" -#include "diff.h" -#include "buffer.h" -#include "charset.h" -#include "eval.h" -#include "ex_cmds.h" -#include "ex_docmd.h" -#include "fileio.h" -#include "fold.h" -#include "mark.h" -#include "mbyte.h" -#include "memline.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "memory.h" -#include "move.h" -#include "normal.h" -#include "option.h" -#include "path.h" -#include "screen.h" -#include "undo.h" -#include "window.h" -#include "os/os.h" -#include "os/shell.h" - -static int diff_busy = FALSE; // ex_diffgetput() is busy - -// Flags obtained from the 'diffopt' option -#define DIFF_FILLER 1 // display filler lines -#define DIFF_ICASE 2 // ignore case -#define DIFF_IWHITE 4 // ignore change in white space -#define DIFF_HORIZONTAL 8 // horizontal splits -#define DIFF_VERTICAL 16 // vertical splits -static int diff_flags = DIFF_FILLER; - -#define LBUFLEN 50 // length of line in diff file - -// TRUE when "diff -a" works, FALSE when it doesn't work, MAYBE when not -// checked yet -static int diff_a_works = MAYBE; - -static int diff_buf_idx(buf_T *buf); -static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp); -static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, - linenr_T line2, long amount, - long amount_after); -static void diff_check_unchanged(tabpage_T *tp, diff_T *dp); -static int diff_check_sanity(tabpage_T *tp, diff_T *dp); -static void diff_redraw(int dofold); -static int diff_write(buf_T *buf, char_u *fname); -static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff); -static int diff_equal_entry(diff_T *dp, int idx1, int idx2); -static int diff_cmp(char_u *s1, char_u *s2); -static void diff_fold_update(diff_T *dp, int skip_idx); -static void diff_read(int idx_orig, int idx_new, char_u *fname); -static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, - int idx_new); -static diff_T* diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp); - -#ifndef USE_CR -# define tag_fgets vim_fgets -#endif // ifndef USE_CR - -/// Called when deleting or unloading a buffer: No longer make a diff with it. -/// -/// @param buf -void diff_buf_delete(buf_T *buf) -{ - tabpage_T *tp; - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - int i = diff_buf_idx_tp(buf, tp); - - if (i != DB_COUNT) { - tp->tp_diffbuf[i] = NULL; - tp->tp_diff_invalid = TRUE; - - if (tp == curtab) { - diff_redraw(TRUE); - } - } - } -} - -/// Check if the current buffer should be added to or removed from the list of -/// diff buffers. -/// -/// @param win -void diff_buf_adjust(win_T *win) -{ - - if (!win->w_p_diff) { - // When there is no window showing a diff for this buffer, remove - // it from the diffs. - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { - if ((wp->w_buffer == win->w_buffer) && wp->w_p_diff) { - break; - } - } - - if (wp == NULL) { - int i = diff_buf_idx(win->w_buffer); - if (i != DB_COUNT) { - curtab->tp_diffbuf[i] = NULL; - curtab->tp_diff_invalid = TRUE; - diff_redraw(TRUE); - } - } - } else { - diff_buf_add(win->w_buffer); - } -} - -/// Add a buffer to make diffs for. -/// -/// Call this when a new buffer is being edited in the current window where -/// 'diff' is set. -/// Marks the current buffer as being part of the diff and requiring updating. -/// This must be done before any autocmd, because a command may use info -/// about the screen contents. -/// -/// @param buf The buffer to add. -void diff_buf_add(buf_T *buf) -{ - if (diff_buf_idx(buf) != DB_COUNT) { - // It's already there. - return; - } - - int i; - for (i = 0; i < DB_COUNT; ++i) { - if (curtab->tp_diffbuf[i] == NULL) { - curtab->tp_diffbuf[i] = buf; - curtab->tp_diff_invalid = TRUE; - diff_redraw(TRUE); - return; - } - } - - EMSGN(_("E96: Can not diff more than %" PRId64 " buffers"), DB_COUNT); -} - -/// Find buffer "buf" in the list of diff buffers for the current tab page. -/// -/// @param buf The buffer to find. -/// -/// @return Its index or DB_COUNT if not found. -static int diff_buf_idx(buf_T *buf) -{ - int idx; - for (idx = 0; idx < DB_COUNT; ++idx) { - if (curtab->tp_diffbuf[idx] == buf) { - break; - } - } - return idx; -} - -/// Find buffer "buf" in the list of diff buffers for tab page "tp". -/// -/// @param buf -/// @param tp -/// -/// @return its index or DB_COUNT if not found. -static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp) -{ - int idx; - for (idx = 0; idx < DB_COUNT; ++idx) { - if (tp->tp_diffbuf[idx] == buf) { - break; - } - } - return idx; -} - -/// Mark the diff info involving buffer "buf" as invalid, it will be updated -/// when info is requested. -/// -/// @param buf -void diff_invalidate(buf_T *buf) -{ - tabpage_T *tp; - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - int i = diff_buf_idx_tp(buf, tp); - if (i != DB_COUNT) { - tp->tp_diff_invalid = TRUE; - if (tp == curtab) { - diff_redraw(TRUE); - } - } - } -} - -/// Called by mark_adjust(): update line numbers in "curbuf". -/// -/// @param line1 -/// @param line2 -/// @param amount -/// @param amount_after -void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount, - long amount_after) -{ - // Handle all tab pages that use the current buffer in a diff. - tabpage_T *tp; - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - int idx = diff_buf_idx_tp(curbuf, tp); - if (idx != DB_COUNT) { - diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after); - } - } -} - -/// Update line numbers in tab page "tp" for "curbuf" with index "idx". -/// -/// This attempts to update the changes as much as possible: -/// When inserting/deleting lines outside of existing change blocks, create a -/// new change block and update the line numbers in following blocks. -/// When inserting/deleting lines in existing change blocks, update them. -/// -/// @param tp -/// @param idx -/// @param line1 -/// @param line2 -/// @param amount -/// @amount_after -static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, - linenr_T line2, long amount, long amount_after) -{ - int inserted; - int deleted; - if (line2 == MAXLNUM) { - // mark_adjust(99, MAXLNUM, 9, 0): insert lines - inserted = amount; - deleted = 0; - } else if (amount_after > 0) { - // mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines - inserted = amount_after; - deleted = 0; - } else { - // mark_adjust(98, 99, MAXLNUM, -2): delete lines - inserted = 0; - deleted = -amount_after; - } - - diff_T *dprev = NULL; - diff_T *dp = tp->tp_first_diff; - - linenr_T last; - linenr_T lnum_deleted = line1; // lnum of remaining deletion - int n; - int off; - for (;;) { - // If the change is after the previous diff block and before the next - // diff block, thus not touching an existing change, create a new diff - // block. Don't do this when ex_diffgetput() is busy. - if (((dp == NULL) - || (dp->df_lnum[idx] - 1 > line2) - || ((line2 == MAXLNUM) && (dp->df_lnum[idx] > line1))) - && ((dprev == NULL) - || (dprev->df_lnum[idx] + dprev->df_count[idx] < line1)) - && !diff_busy) { - diff_T *dnext = diff_alloc_new(tp, dprev, dp); - - dnext->df_lnum[idx] = line1; - dnext->df_count[idx] = inserted; - int i; - for (i = 0; i < DB_COUNT; ++i) { - if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { - if (dprev == NULL) { - dnext->df_lnum[i] = line1; - } else { - dnext->df_lnum[i] = line1 - + (dprev->df_lnum[i] + dprev->df_count[i]) - - (dprev->df_lnum[idx] + dprev->df_count[idx]); - } - dnext->df_count[i] = deleted; - } - } - } - - // if at end of the list, quit - if (dp == NULL) { - break; - } - - // - // Check for these situations: - // 1 2 3 - // 1 2 3 - // line1 2 3 4 5 - // 2 3 4 5 - // 2 3 4 5 - // line2 2 3 4 5 - // 3 5 6 - // 3 5 6 - - // compute last line of this change - last = dp->df_lnum[idx] + dp->df_count[idx] - 1; - - // 1. change completely above line1: nothing to do - if (last >= line1 - 1) { - // 6. change below line2: only adjust for amount_after; also when - // "deleted" became zero when deleted all lines between two diffs. - if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) { - if (amount_after == 0) { - // nothing left to change - break; - } - dp->df_lnum[idx] += amount_after; - } else { - int check_unchanged = FALSE; - - // 2. 3. 4. 5.: inserted/deleted lines touching this diff. - if (deleted > 0) { - if (dp->df_lnum[idx] >= line1) { - off = dp->df_lnum[idx] - lnum_deleted; - - if (last <= line2) { - // 4. delete all lines of diff - if ((dp->df_next != NULL) - && (dp->df_next->df_lnum[idx] - 1 <= line2)) { - // delete continues in next diff, only do - // lines until that one - n = dp->df_next->df_lnum[idx] - lnum_deleted; - deleted -= n; - n -= dp->df_count[idx]; - lnum_deleted = dp->df_next->df_lnum[idx]; - } else { - n = deleted - dp->df_count[idx]; - } - dp->df_count[idx] = 0; - } else { - // 5. delete lines at or just before top of diff - n = off; - dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; - check_unchanged = TRUE; - } - dp->df_lnum[idx] = line1; - } else { - off = 0; - - if (last < line2) { - // 2. delete at end of of diff - dp->df_count[idx] -= last - lnum_deleted + 1; - - if ((dp->df_next != NULL) - && (dp->df_next->df_lnum[idx] - 1 <= line2)) { - // delete continues in next diff, only do - // lines until that one - n = dp->df_next->df_lnum[idx] - 1 - last; - deleted -= dp->df_next->df_lnum[idx] - lnum_deleted; - lnum_deleted = dp->df_next->df_lnum[idx]; - } else { - n = line2 - last; - } - check_unchanged = TRUE; - } else { - // 3. delete lines inside the diff - n = 0; - dp->df_count[idx] -= deleted; - } - } - - int i; - for (i = 0; i < DB_COUNT; ++i) { - if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { - dp->df_lnum[i] -= off; - dp->df_count[i] += n; - } - } - } else { - if (dp->df_lnum[idx] <= line1) { - // inserted lines somewhere in this diff - dp->df_count[idx] += inserted; - check_unchanged = TRUE; - } else { - // inserted lines somewhere above this diff - dp->df_lnum[idx] += inserted; - } - } - - if (check_unchanged) { - // Check if inserted lines are equal, may reduce the size of the - // diff. - // - // TODO: also check for equal lines in the middle and perhaps split - // the block. - diff_check_unchanged(tp, dp); - } - } - } - - // check if this block touches the previous one, may merge them. - if ((dprev != NULL) - && (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) { - int i; - for (i = 0; i < DB_COUNT; ++i) { - if (tp->tp_diffbuf[i] != NULL) { - dprev->df_count[i] += dp->df_count[i]; - } - } - dprev->df_next = dp->df_next; - free(dp); - dp = dprev->df_next; - } else { - // Advance to next entry. - dprev = dp; - dp = dp->df_next; - } - } - - dprev = NULL; - dp = tp->tp_first_diff; - - while (dp != NULL) { - // All counts are zero, remove this entry. - int i; - for (i = 0; i < DB_COUNT; ++i) { - if ((tp->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) { - break; - } - } - - if (i == DB_COUNT) { - diff_T *dnext = dp->df_next; - free(dp); - dp = dnext; - - if (dprev == NULL) { - tp->tp_first_diff = dnext; - } else { - dprev->df_next = dnext; - } - } else { - // Advance to next entry. - dprev = dp; - dp = dp->df_next; - } - } - - if (tp == curtab) { - diff_redraw(TRUE); - - // Need to recompute the scroll binding, may remove or add filler - // lines (e.g., when adding lines above w_topline). But it's slow when - // making many changes, postpone until redrawing. - diff_need_scrollbind = TRUE; - } -} - -/// Allocate a new diff block and link it between "dprev" and "dp". -/// -/// @param tp -/// @param dprev -/// @param dp -/// -/// @return The new diff block. -static diff_T* diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) -{ - diff_T *dnew = xmalloc(sizeof(*dnew)); - - dnew->df_next = dp; - if (dprev == NULL) { - tp->tp_first_diff = dnew; - } else { - dprev->df_next = dnew; - } - - return dnew; -} - -/// Check if the diff block "dp" can be made smaller for lines at the start and -/// end that are equal. Called after inserting lines. -/// -/// This may result in a change where all buffers have zero lines, the caller -/// must take care of removing it. -/// -/// @param tp -/// @param dp -static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) -{ - // Find the first buffers, use it as the original, compare the other - // buffer lines against this one. - int i_org; - for (i_org = 0; i_org < DB_COUNT; ++i_org) { - if (tp->tp_diffbuf[i_org] != NULL) { - break; - } - } - - // safety check - if (i_org == DB_COUNT) { - return; - } - - if (diff_check_sanity(tp, dp) == FAIL) { - return; - } - - // First check lines at the top, then at the bottom. - int off_org = 0; - int off_new = 0; - int dir = FORWARD; - for (;;) { - // Repeat until a line is found which is different or the number of - // lines has become zero. - while (dp->df_count[i_org] > 0) { - // Copy the line, the next ml_get() will invalidate it. - if (dir == BACKWARD) { - off_org = dp->df_count[i_org] - 1; - } - char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org], - dp->df_lnum[i_org] + off_org, - FALSE)); - - if (line_org == NULL) { - return; - } - - int i_new; - for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) { - if (tp->tp_diffbuf[i_new] == NULL) { - continue; - } - - if (dir == BACKWARD) { - off_new = dp->df_count[i_new] - 1; - } - - // if other buffer doesn't have this line, it was inserted - if ((off_new < 0) || (off_new >= dp->df_count[i_new])) { - break; - } - - if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new], - dp->df_lnum[i_new] + off_new, - FALSE)) != 0) { - break; - } - } - free(line_org); - - // Stop when a line isn't equal in all diff buffers. - if (i_new != DB_COUNT) { - break; - } - - // Line matched in all buffers, remove it from the diff. - for (i_new = i_org; i_new < DB_COUNT; ++i_new) { - if (tp->tp_diffbuf[i_new] != NULL) { - if (dir == FORWARD) { - dp->df_lnum[i_new]++; - } - dp->df_count[i_new]--; - } - } - } - - if (dir == BACKWARD) { - break; - } - dir = BACKWARD; - } -} - -/// Check if a diff block doesn't contain invalid line numbers. -/// This can happen when the diff program returns invalid results. -/// -/// @param tp -/// @param dp -/// -/// @return OK if the diff block doesn't contain invalid line numbers. -static int diff_check_sanity(tabpage_T *tp, diff_T *dp) -{ - int i; - for (i = 0; i < DB_COUNT; ++i) { - if (tp->tp_diffbuf[i] != NULL) { - if (dp->df_lnum[i] + dp->df_count[i] - 1 - > tp->tp_diffbuf[i]->b_ml.ml_line_count) { - return FAIL; - } - } - } - return OK; -} - -/// Mark all diff buffers in the current tab page for redraw. -/// -/// @param dofold Also recompute the folds -static void diff_redraw(int dofold) -{ - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { - if (wp->w_p_diff) { - redraw_win_later(wp, SOME_VALID); - if (dofold && foldmethodIsDiff(wp)) { - foldUpdateAll(wp); - } - - /* A change may have made filler lines invalid, need to take care - * of that for other windows. */ - int n = diff_check(wp, wp->w_topline); - - if (((wp != curwin) && (wp->w_topfill > 0)) || (n > 0)) { - if (wp->w_topfill > n) { - wp->w_topfill = (n < 0 ? 0 : n); - } else if ((n > 0) && (n > wp->w_topfill)) { - wp->w_topfill = n; - } - } - } - } -} - -/// Write buffer "buf" to file "name". -/// -/// Always use 'fileformat' set to "unix". -/// -/// @param buf -/// @param fname -/// -/// @return FAIL for failure -static int diff_write(buf_T *buf, char_u *fname) -{ - char_u *save_ff = buf->b_p_ff; - buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); - int r = buf_write(buf, fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, - NULL, FALSE, FALSE, FALSE, TRUE); - free_string_option(buf->b_p_ff); - buf->b_p_ff = save_ff; - return r; -} - -/// Completely update the diffs for the buffers involved. -/// -/// This uses the ordinary "diff" command. -/// The buffers are written to a file, also for unmodified buffers (the file -/// could have been produced by autocommands, e.g. the netrw plugin). -/// -/// @param eap can be NULL -void ex_diffupdate(exarg_T *eap) -{ - // Delete all diffblocks. - diff_clear(curtab); - curtab->tp_diff_invalid = FALSE; - - // Use the first buffer as the original text. - int idx_orig; - for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) { - if (curtab->tp_diffbuf[idx_orig] != NULL) { - break; - } - } - - if (idx_orig == DB_COUNT) { - return; - } - - // Only need to do something when there is another buffer. - int idx_new; - for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) { - if (curtab->tp_diffbuf[idx_new] != NULL) { - break; - } - } - - if (idx_new == DB_COUNT) { - return; - } - - // We need three temp file names. - char_u *tmp_orig = vim_tempname('o'); - char_u *tmp_new = vim_tempname('n'); - char_u *tmp_diff = vim_tempname('d'); - - if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) { - goto theend; - } - - // Do a quick test if "diff" really works. Otherwise it looks like there - // are no differences. Can't use the return value, it's non-zero when - // there are differences. - // May try twice, first with "-a" and then without. - int io_error = FALSE; - int ok = FALSE; - for (;;) { - ok = FALSE; - FILE *fd = mch_fopen((char *)tmp_orig, "w"); - - if (fd == NULL) { - io_error = TRUE; - } else { - if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) { - io_error = TRUE; - } - fclose(fd); - fd = mch_fopen((char *)tmp_new, "w"); - - if (fd == NULL) { - io_error = TRUE; - } else { - if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) { - io_error = TRUE; - } - fclose(fd); - diff_file(tmp_orig, tmp_new, tmp_diff); - fd = mch_fopen((char *)tmp_diff, "r"); - - if (fd == NULL) { - io_error = TRUE; - } else { - char_u linebuf[LBUFLEN]; - - for (;;) { - // There must be a line that contains "1c1". - if (tag_fgets(linebuf, LBUFLEN, fd)) { - break; - } - - if (STRNCMP(linebuf, "1c1", 3) == 0) { - ok = TRUE; - } - } - fclose(fd); - } - os_remove((char *)tmp_diff); - os_remove((char *)tmp_new); - } - os_remove((char *)tmp_orig); - } - - // When using 'diffexpr' break here. - if (*p_dex != NUL) { - break; - } - - // If we checked if "-a" works already, break here. - if (diff_a_works != MAYBE) { - break; - } - diff_a_works = ok; - - // If "-a" works break here, otherwise retry without "-a". - if (ok) { - break; - } - } - - if (!ok) { - if (io_error) { - EMSG(_("E810: Cannot read or write temp files")); - } - EMSG(_("E97: Cannot create diffs")); - diff_a_works = MAYBE; - goto theend; - } - - // :diffupdate! - if ((eap != NULL) && eap->forceit) { - for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new) { - buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf_valid(buf)) { - buf_check_timestamp(buf, FALSE); - } - } - } - - // Write the first buffer to a tempfile. - buf_T *buf = curtab->tp_diffbuf[idx_orig]; - if (diff_write(buf, tmp_orig) == FAIL) { - goto theend; - } - - // Make a difference between the first buffer and every other. - for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) { - buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf == NULL) { - continue; - } - - if (diff_write(buf, tmp_new) == FAIL) { - continue; - } - diff_file(tmp_orig, tmp_new, tmp_diff); - - // Read the diff output and add each entry to the diff list. - diff_read(idx_orig, idx_new, tmp_diff); - os_remove((char *)tmp_diff); - os_remove((char *)tmp_new); - } - os_remove((char *)tmp_orig); - - // force updating cursor position on screen - curwin->w_valid_cursor.lnum = 0; - - diff_redraw(TRUE); - -theend: - free(tmp_orig); - free(tmp_new); - free(tmp_diff); -} - -/// Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". -/// -/// @param tmp_orig -/// @param tmp_new -/// @param tmp_diff -static void diff_file(char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff) -{ - if (*p_dex != NUL) { - // Use 'diffexpr' to generate the diff file. - eval_diff(tmp_orig, tmp_new, tmp_diff); - } else { - size_t len = STRLEN(tmp_orig) + STRLEN(tmp_new) + STRLEN(tmp_diff) - + STRLEN(p_srr) + 27; - char_u *cmd = xmalloc(len); - - /* We don't want $DIFF_OPTIONS to get in the way. */ - if (os_getenv("DIFF_OPTIONS")) { - vim_setenv((char_u *)"DIFF_OPTIONS", (char_u *)""); - } - - /* Build the diff command and execute it. Always use -a, binary - * differences are of no use. Ignore errors, diff returns - * non-zero when differences have been found. */ - vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s", - diff_a_works == FALSE ? "" : "-a ", - "", - (diff_flags & DIFF_IWHITE) ? "-b " : "", - (diff_flags & DIFF_ICASE) ? "-i " : "", - tmp_orig, tmp_new); - append_redir(cmd, (int)len, p_srr, tmp_diff); - block_autocmds(); /* Avoid ShellCmdPost stuff */ - (void)call_shell( - cmd, - kShellOptFilter | kShellOptSilent | kShellOptDoOut, - NULL - ); - unblock_autocmds(); - free(cmd); - } -} - -/// Create a new version of a file from the current buffer and a diff file. -/// -/// The buffer is written to a file, also for unmodified buffers (the file -/// could have been produced by autocommands, e.g. the netrw plugin). -/// -/// @param eap -void ex_diffpatch(exarg_T *eap) -{ - char_u *buf = NULL; - win_T *old_curwin = curwin; - char_u *newname = NULL; // name of patched file buffer - -#ifdef UNIX - char_u dirbuf[MAXPATHL]; - char_u *fullname = NULL; -#endif // ifdef UNIX - // We need two temp file names. - // Name of original temp file. - char_u *tmp_orig = vim_tempname('o'); - // Name of patched temp file. - char_u *tmp_new = vim_tempname('n'); - - if ((tmp_orig == NULL) || (tmp_new == NULL)) { - goto theend; - } - - // Write the current buffer to "tmp_orig". - if (buf_write(curbuf, tmp_orig, NULL, - (linenr_T)1, curbuf->b_ml.ml_line_count, - NULL, FALSE, FALSE, FALSE, TRUE) == FAIL) { - goto theend; - } - -#ifdef UNIX - // Get the absolute path of the patchfile, changing directory below. - fullname = FullName_save(eap->arg, FALSE); -#endif // ifdef UNIX - -#ifdef UNIX - size_t buflen = STRLEN(tmp_orig) - + (fullname != NULL ? STRLEN(fullname) : STRLEN(eap->arg)) - + STRLEN(tmp_new) + 16; -#else - size_t buflen = STRLEN(tmp_orig) + (STRLEN(eap->arg)) + STRLEN(tmp_new) + 16; -#endif // ifdef UNIX - - buf = xmalloc(buflen); - -#ifdef UNIX - - // Temporarily chdir to /tmp, to avoid patching files in the current - // directory when the patch file contains more than one patch. When we - // have our own temp dir use that instead, it will be cleaned up when we - // exit (any .rej files created). Don't change directory if we can't - // return to the current. - if ((os_dirname(dirbuf, MAXPATHL) != OK) - || (os_chdir((char *)dirbuf) != 0)) { - dirbuf[0] = NUL; - } else { -# ifdef TEMPDIRNAMES - if (vim_tempdir != NULL) { - ignored = os_chdir((char *)vim_tempdir); - } else { - ignored = os_chdir("/tmp"); - } -# else - ignored = os_chdir("/tmp"); -# endif // ifdef TEMPDIRNAMES - shorten_fnames(TRUE); - } -#endif // ifdef UNIX - - if (*p_pex != NUL) { - // Use 'patchexpr' to generate the new file. -#ifdef UNIX - eval_patch(tmp_orig, fullname != NULL ? fullname : eap->arg, tmp_new); -#else - eval_patch(tmp_orig, eap->arg, tmp_new); -#endif // ifdef UNIX - } else { - // Build the patch command and execute it. Ignore errors. Switch to - // cooked mode to allow the user to respond to prompts. -#ifdef UNIX - vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"", - tmp_new, tmp_orig, fullname != NULL ? fullname : eap->arg); -#else - vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"", - tmp_new, tmp_orig, eap->arg); -#endif // ifdef UNIX - // Avoid ShellCmdPost stuff - block_autocmds(); - (void)call_shell(buf, kShellOptFilter | kShellOptCooked, NULL); - unblock_autocmds(); - } - -#ifdef UNIX - if (dirbuf[0] != NUL) { - if (os_chdir((char *)dirbuf) != 0) { - EMSG(_(e_prev_dir)); - } - shorten_fnames(TRUE); - } -#endif // ifdef UNIX - - // patch probably has written over the screen - redraw_later(CLEAR); - - // Delete any .orig or .rej file created. - STRCPY(buf, tmp_new); - STRCAT(buf, ".orig"); - os_remove((char *)buf); - STRCPY(buf, tmp_new); - STRCAT(buf, ".rej"); - os_remove((char *)buf); - - // Only continue if the output file was created. - off_t file_size; - bool file_size_success = os_get_file_size((char *)tmp_new, &file_size); - if (!file_size_success || file_size == 0) { - EMSG(_("E816: Cannot read patch output")); - } else { - if (curbuf->b_fname != NULL) { - newname = vim_strnsave(curbuf->b_fname, - (int)(STRLEN(curbuf->b_fname) + 4)); - - if (newname != NULL) { - STRCAT(newname, ".new"); - } - } - - // don't use a new tab page, each tab page has its own diffs - cmdmod.tab = 0; - - if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) { - // Pretend it was a ":split fname" command - eap->cmdidx = CMD_split; - eap->arg = tmp_new; - do_exedit(eap, old_curwin); - - // check that split worked and editing tmp_new - if ((curwin != old_curwin) && win_valid(old_curwin)) { - // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); - diff_win_options(old_curwin, TRUE); - - if (newname != NULL) { - // do a ":file filename.new" on the patched buffer - eap->arg = newname; - ex_file(eap); - - // Do filetype detection with the new name. - if (au_has_group((char_u *)"filetypedetect")) { - do_cmdline_cmd((char_u *)":doau filetypedetect BufRead"); - } - } - } - } - } - -theend: - if (tmp_orig != NULL) { - os_remove((char *)tmp_orig); - } - free(tmp_orig); - - if (tmp_new != NULL) { - os_remove((char *)tmp_new); - } - free(tmp_new); - free(newname); - free(buf); -#ifdef UNIX - free(fullname); -#endif // ifdef UNIX -} - -/// Split the window and edit another file, setting options to show the diffs. -/// -/// @param eap -void ex_diffsplit(exarg_T *eap) -{ - win_T *old_curwin = curwin; - - // don't use a new tab page, each tab page has its own diffs - cmdmod.tab = 0; - - if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) { - // Pretend it was a ":split fname" command - eap->cmdidx = CMD_split; - curwin->w_p_diff = TRUE; - do_exedit(eap, old_curwin); - - // split must have worked - if (curwin != old_curwin) { - // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); - diff_win_options(old_curwin, TRUE); - } - } -} - -// Set options to show diffs for the current window. -void ex_diffthis(exarg_T *eap) -{ - // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); -} - -/// Set options in window "wp" for diff mode. -/// -/// @param addbuf Add buffer to diff. -void diff_win_options(win_T *wp, int addbuf) -{ - win_T *old_curwin = curwin; - - // close the manually opened folds - curwin = wp; - newFoldLevel(); - curwin = old_curwin; - - wp->w_p_diff = TRUE; - - // Use 'scrollbind' and 'cursorbind' when available - if (!wp->w_p_diff_saved) { - wp->w_p_scb_save = wp->w_p_scb; - } - wp->w_p_scb = TRUE; - - if (!wp->w_p_diff_saved) { - wp->w_p_crb_save = wp->w_p_crb; - } - wp->w_p_crb = TRUE; - - if (!wp->w_p_diff_saved) { - wp->w_p_wrap_save = wp->w_p_wrap; - } - wp->w_p_wrap = FALSE; - curwin = wp; - curbuf = curwin->w_buffer; - - if (!wp->w_p_diff_saved) { - wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm); - } - set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff", - OPT_LOCAL | OPT_FREE, 0); - curwin = old_curwin; - curbuf = curwin->w_buffer; - - if (!wp->w_p_diff_saved) { - wp->w_p_fdc_save = wp->w_p_fdc; - wp->w_p_fen_save = wp->w_p_fen; - wp->w_p_fdl_save = wp->w_p_fdl; - } - wp->w_p_fdc = diff_foldcolumn; - wp->w_p_fen = TRUE; - wp->w_p_fdl = 0; - foldUpdateAll(wp); - - // make sure topline is not halfway a fold - changed_window_setting_win(wp); - if (vim_strchr(p_sbo, 'h') == NULL) { - do_cmdline_cmd((char_u *)"set sbo+=hor"); - } - - // Saved the current values, to be restored in ex_diffoff(). - wp->w_p_diff_saved = TRUE; - - if (addbuf) { - diff_buf_add(wp->w_buffer); - } - redraw_win_later(wp, NOT_VALID); -} - -/// Set options not to show diffs. For the current window or all windows. -/// Only in the current tab page. -/// -/// @param eap -void ex_diffoff(exarg_T *eap) -{ - win_T *old_curwin = curwin; - int diffwin = FALSE; - - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { - if (eap->forceit ? wp->w_p_diff : (wp == curwin)) { - // Set 'diff', 'scrollbind' off and 'wrap' on. If option values - // were saved in diff_win_options() restore them. - wp->w_p_diff = FALSE; - - if (wp->w_p_scb) { - wp->w_p_scb = wp->w_p_diff_saved ? wp->w_p_scb_save : FALSE; - } - - if (wp->w_p_crb) { - wp->w_p_crb = wp->w_p_diff_saved ? wp->w_p_crb_save : FALSE; - } - - if (!wp->w_p_wrap) { - wp->w_p_wrap = wp->w_p_diff_saved ? wp->w_p_wrap_save : TRUE; - } - curwin = wp; - curbuf = curwin->w_buffer; - - if (wp->w_p_diff_saved) { - free_string_option(wp->w_p_fdm); - wp->w_p_fdm = wp->w_p_fdm_save; - wp->w_p_fdm_save = empty_option; - } else { - set_string_option_direct((char_u *)"fdm", -1, - (char_u *)"manual", OPT_LOCAL | OPT_FREE, 0); - } - curwin = old_curwin; - curbuf = curwin->w_buffer; - - if (wp->w_p_fdc == diff_foldcolumn) { - wp->w_p_fdc = wp->w_p_diff_saved ? wp->w_p_fdc_save : 0; - } - - if ((wp->w_p_fdl == 0) - && wp->w_p_diff_saved) { - wp->w_p_fdl = wp->w_p_fdl_save; - } - - if (wp->w_p_fen) { - // Only restore 'foldenable' when 'foldmethod' is not - // "manual", otherwise we continue to show the diff folds. - if (foldmethodIsManual(wp) || !wp->w_p_diff_saved) { - wp->w_p_fen = FALSE; - } else { - wp->w_p_fen = wp->w_p_fen_save; - } - } - - foldUpdateAll(wp); - - // make sure topline is not halfway a fold - changed_window_setting_win(wp); - - // Note: 'sbo' is not restored, it's a global option. - diff_buf_adjust(wp); - - wp->w_p_diff_saved = FALSE; - } - diffwin |= wp->w_p_diff; - } - - // Remove "hor" from from 'scrollopt' if there are no diff windows left. - if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) { - do_cmdline_cmd((char_u *)"set sbo-=hor"); - } -} - -/// Read the diff output and add each entry to the diff list. -/// -/// @param idx_orig idx of original file -/// @param idx_new idx of new file -/// @param fname name of diff output file -static void diff_read(int idx_orig, int idx_new, char_u *fname) -{ - FILE *fd; - diff_T *dprev = NULL; - diff_T *dp = curtab->tp_first_diff; - diff_T *dn, *dpl; - long f1, l1, f2, l2; - char_u linebuf[LBUFLEN]; // only need to hold the diff line - int difftype; - char_u *p; - long off; - int i; - linenr_T lnum_orig, lnum_new; - long count_orig, count_new; - int notset = TRUE; // block "*dp" not set yet - - fd = mch_fopen((char *)fname, "r"); - - if (fd == NULL) { - EMSG(_("E98: Cannot read diff output")); - return; - } - - for (;;) { - if (tag_fgets(linebuf, LBUFLEN, fd)) { - // end of file - break; - } - - if (!isdigit(*linebuf)) { - // not the start of a diff block - continue; - } - - // This line must be one of three formats: - // {first}[,{last}]c{first}[,{last}] - // {first}a{first}[,{last}] - // {first}[,{last}]d{first} - p = linebuf; - f1 = getdigits(&p); - - if (*p == ',') { - ++p; - l1 = getdigits(&p); - } else { - l1 = f1; - } - - if ((*p != 'a') && (*p != 'c') && (*p != 'd')) { - // invalid diff format - continue; - } - difftype = *p++; - f2 = getdigits(&p); - - if (*p == ',') { - ++p; - l2 = getdigits(&p); - } else { - l2 = f2; - } - - if ((l1 < f1) || (l2 < f2)) { - // invalid line range - continue; - } - - if (difftype == 'a') { - lnum_orig = f1 + 1; - count_orig = 0; - } else { - lnum_orig = f1; - count_orig = l1 - f1 + 1; - } - - if (difftype == 'd') { - lnum_new = f2 + 1; - count_new = 0; - } else { - lnum_new = f2; - count_new = l2 - f2 + 1; - } - - // Go over blocks before the change, for which orig and new are equal. - // Copy blocks from orig to new. - while (dp != NULL - && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { - if (notset) { - diff_copy_entry(dprev, dp, idx_orig, idx_new); - } - dprev = dp; - dp = dp->df_next; - notset = TRUE; - } - - if ((dp != NULL) - && (lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) - && (lnum_orig + count_orig >= dp->df_lnum[idx_orig])) { - // New block overlaps with existing block(s). - // First find last block that overlaps. - for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { - if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig]) { - break; - } - } - - // If the newly found block starts before the old one, set the - // start back a number of lines. - off = dp->df_lnum[idx_orig] - lnum_orig; - - if (off > 0) { - for (i = idx_orig; i < idx_new; ++i) { - if (curtab->tp_diffbuf[i] != NULL) { - dp->df_lnum[i] -= off; - } - } - dp->df_lnum[idx_new] = lnum_new; - dp->df_count[idx_new] = count_new; - } else if (notset) { - // new block inside existing one, adjust new block - dp->df_lnum[idx_new] = lnum_new + off; - dp->df_count[idx_new] = count_new - off; - } else { - // second overlap of new block with existing block - dp->df_count[idx_new] += count_new - count_orig - + dpl->df_lnum[idx_orig] + - dpl->df_count[idx_orig] - - (dp->df_lnum[idx_orig] + - dp->df_count[idx_orig]); - } - - // Adjust the size of the block to include all the lines to the - // end of the existing block or the new diff, whatever ends last. - off = (lnum_orig + count_orig) - - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); - - if (off < 0) { - // new change ends in existing block, adjust the end if not - // done already - if (notset) { - dp->df_count[idx_new] += -off; - } - off = 0; - } - - for (i = idx_orig; i < idx_new; ++i) { - if (curtab->tp_diffbuf[i] != NULL) { - dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] - - dp->df_lnum[i] + off; - } - } - - // Delete the diff blocks that have been merged into one. - dn = dp->df_next; - dp->df_next = dpl->df_next; - - while (dn != dp->df_next) { - dpl = dn->df_next; - free(dn); - dn = dpl; - } - } else { - // Allocate a new diffblock. - dp = diff_alloc_new(curtab, dprev, dp); - - dp->df_lnum[idx_orig] = lnum_orig; - dp->df_count[idx_orig] = count_orig; - dp->df_lnum[idx_new] = lnum_new; - dp->df_count[idx_new] = count_new; - - // Set values for other buffers, these must be equal to the - // original buffer, otherwise there would have been a change - // already. - for (i = idx_orig + 1; i < idx_new; ++i) { - if (curtab->tp_diffbuf[i] != NULL) { - diff_copy_entry(dprev, dp, idx_orig, i); - } - } - } - notset = FALSE; // "*dp" has been set - } - - // for remaining diff blocks orig and new are equal - while (dp != NULL) { - if (notset) { - diff_copy_entry(dprev, dp, idx_orig, idx_new); - } - dprev = dp; - dp = dp->df_next; - notset = TRUE; - } - - fclose(fd); -} - -/// Copy an entry at "dp" from "idx_orig" to "idx_new". -/// -/// @param dprev -/// @param dp -/// @param idx_orig -/// @param idx_new -static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, - int idx_new) -{ - long off; - - if (dprev == NULL) { - off = 0; - } else { - off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig]) - - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]); - } - dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off; - dp->df_count[idx_new] = dp->df_count[idx_orig]; -} - -/// Clear the list of diffblocks for tab page "tp". -/// -/// @param tp -void diff_clear(tabpage_T *tp) -{ - diff_T *p; - diff_T *next_p; - for (p = tp->tp_first_diff; p != NULL; p = next_p) { - next_p = p->df_next; - free(p); - } - tp->tp_first_diff = NULL; -} - -/// Check diff status for line "lnum" in buffer "buf": -/// -/// Returns 0 for nothing special -/// Returns -1 for a line that should be highlighted as changed. -/// Returns -2 for a line that should be highlighted as added/deleted. -/// Returns > 0 for inserting that many filler lines above it (never happens -/// when 'diffopt' doesn't contain "filler"). -/// This should only be used for windows where 'diff' is set. -/// -/// @param wp -/// @param lnum -/// -/// @return diff status. -int diff_check(win_T *wp, linenr_T lnum) -{ - int idx; // index in tp_diffbuf[] for this buffer - diff_T *dp; - int maxcount; - int i; - buf_T *buf = wp->w_buffer; - int cmp; - - if (curtab->tp_diff_invalid) { - // update after a big change - ex_diffupdate(NULL); - } - - // no diffs at all - if ((curtab->tp_first_diff == NULL) || !wp->w_p_diff) { - return 0; - } - - // safety check: "lnum" must be a buffer line - if ((lnum < 1) || (lnum > buf->b_ml.ml_line_count + 1)) { - return 0; - } - - idx = diff_buf_idx(buf); - - if (idx == DB_COUNT) { - // no diffs for buffer "buf" - return 0; - } - - // A closed fold never has filler lines. - if (hasFoldingWin(wp, lnum, NULL, NULL, TRUE, NULL)) { - return 0; - } - - // search for a change that includes "lnum" in the list of diffblocks. - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { - if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) { - break; - } - } - - if ((dp == NULL) || (lnum < dp->df_lnum[idx])) { - return 0; - } - - if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) { - int zero = FALSE; - - // Changed or inserted line. If the other buffers have a count of - // zero, the lines were inserted. If the other buffers have the same - // count, check if the lines are identical. - cmp = FALSE; - - for (i = 0; i < DB_COUNT; ++i) { - if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) { - if (dp->df_count[i] == 0) { - zero = TRUE; - } else { - if (dp->df_count[i] != dp->df_count[idx]) { - // nr of lines changed. - return -1; - } - cmp = TRUE; - } - } - } - - if (cmp) { - // Compare all lines. If they are equal the lines were inserted - // in some buffers, deleted in others, but not changed. - for (i = 0; i < DB_COUNT; ++i) { - if ((i != idx) - && (curtab->tp_diffbuf[i] != NULL) - && (dp->df_count[i] != 0)) { - if (!diff_equal_entry(dp, idx, i)) { - return -1; - } - } - } - } - - // If there is no buffer with zero lines then there is no difference - // any longer. Happens when making a change (or undo) that removes - // the difference. Can't remove the entry here, we might be halfway - // updating the window. Just report the text as unchanged. Other - // windows might still show the change though. - if (zero == FALSE) { - return 0; - } - return -2; - } - - // If 'diffopt' doesn't contain "filler", return 0. - if (!(diff_flags & DIFF_FILLER)) { - return 0; - } - - // Insert filler lines above the line just below the change. Will return - // 0 when this buf had the max count. - maxcount = 0; - for (i = 0; i < DB_COUNT; ++i) { - if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) { - maxcount = dp->df_count[i]; - } - } - return maxcount - dp->df_count[idx]; -} - -/// Compare two entries in diff "*dp" and return TRUE if they are equal. -/// -/// @param dp -/// @param idx1 First entry in diff "*dp" -/// @param idx2 Second entry in diff "*dp" -/// -/// @return return TRUE if two entires are equal. -static int diff_equal_entry(diff_T *dp, int idx1, int idx2) -{ - if (dp->df_count[idx1] != dp->df_count[idx2]) { - return FALSE; - } - - if (diff_check_sanity(curtab, dp) == FAIL) { - return FALSE; - } - - int i; - for (i = 0; i < dp->df_count[idx1]; ++i) { - char_u *line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1], - dp->df_lnum[idx1] + i, FALSE)); - - if (line == NULL) { - return FALSE; - } - int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2], - dp->df_lnum[idx2] + i, FALSE)); - free(line); - - if (cmp != 0) { - return FALSE; - } - } - return TRUE; -} - -/// Compare strings "s1" and "s2" according to 'diffopt'. -/// Return non-zero when they are different. -/// -/// @param s1 The first string -/// @param s2 The second string -/// -/// @return on-zero if the two strings are different. -static int diff_cmp(char_u *s1, char_u *s2) -{ - if ((diff_flags & (DIFF_ICASE | DIFF_IWHITE)) == 0) { - return STRCMP(s1, s2); - } - - if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { - return MB_STRICMP(s1, s2); - } - - // Ignore white space changes and possibly ignore case. - char_u *p1 = s1; - char_u *p2 = s2; - - while (*p1 != NUL && *p2 != NUL) { - if (vim_iswhite(*p1) && vim_iswhite(*p2)) { - p1 = skipwhite(p1); - p2 = skipwhite(p2); - } else { - int l = (*mb_ptr2len)(p1); - if (l != (*mb_ptr2len)(p2)) { - break; - } - - if (l > 1) { - if ((STRNCMP(p1, p2, l) != 0) - && (!enc_utf8 - || !(diff_flags & DIFF_ICASE) - || (utf_fold(utf_ptr2char(p1)) - != utf_fold(utf_ptr2char(p2))))) { - break; - } - p1 += l; - p2 += l; - } else { - if ((*p1 != *p2) - && (!(diff_flags & DIFF_ICASE) - || (TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2)))) { - break; - } - ++p1; - ++p2; - } - } - } - - // Ignore trailing white space. - p1 = skipwhite(p1); - p2 = skipwhite(p2); - - if ((*p1 != NUL) || (*p2 != NUL)) { - return 1; - } - return 0; -} - -/// Return the number of filler lines above "lnum". -/// -/// @param wp -/// @param lnum -/// -/// @return Number of filler lines above lnum -int diff_check_fill(win_T *wp, linenr_T lnum) -{ - // be quick when there are no filler lines - if (!(diff_flags & DIFF_FILLER)) { - return 0; - } - int n = diff_check(wp, lnum); - - if (n <= 0) { - return 0; - } - return n; -} - -/// Set the topline of "towin" to match the position in "fromwin", so that they -/// show the same diff'ed lines. -/// -/// @param fromwin -/// @param towin -void diff_set_topline(win_T *fromwin, win_T *towin) -{ - buf_T *frombuf = fromwin->w_buffer; - linenr_T lnum = fromwin->w_topline; - diff_T *dp; - int max_count; - int i; - - int fromidx = diff_buf_idx(frombuf); - if (fromidx == DB_COUNT) { - // safety check - return; - } - - if (curtab->tp_diff_invalid) { - // update after a big change - ex_diffupdate(NULL); - } - towin->w_topfill = 0; - - // search for a change that includes "lnum" in the list of diffblocks. - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { - if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) { - break; - } - } - - if (dp == NULL) { - // After last change, compute topline relative to end of file; no - // filler lines. - towin->w_topline = towin->w_buffer->b_ml.ml_line_count - - (frombuf->b_ml.ml_line_count - lnum); - } else { - // Find index for "towin". - int toidx = diff_buf_idx(towin->w_buffer); - - if (toidx == DB_COUNT) { - // safety check - return; - } - towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]); - - if (lnum >= dp->df_lnum[fromidx]) { - // Inside a change: compute filler lines. With three or more - // buffers we need to know the largest count. - max_count = 0; - - for (i = 0; i < DB_COUNT; ++i) { - if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) { - max_count = dp->df_count[i]; - } - } - - if (dp->df_count[toidx] == dp->df_count[fromidx]) { - // same number of lines: use same filler count - towin->w_topfill = fromwin->w_topfill; - } else if (dp->df_count[toidx] > dp->df_count[fromidx]) { - if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) { - // more lines in towin and fromwin doesn't show diff - // lines, only filler lines - if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) { - // towin also only shows filler lines - towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx]; - towin->w_topfill = fromwin->w_topfill; - } else { - // towin still has some diff lines to show - towin->w_topline = dp->df_lnum[toidx] - + max_count - fromwin->w_topfill; - } - } - } else if (towin->w_topline >= dp->df_lnum[toidx] - + dp->df_count[toidx]) { - // less lines in towin and no diff lines to show: compute - // filler lines - towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx]; - - if (diff_flags & DIFF_FILLER) { - if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) { - // fromwin is also out of diff lines - towin->w_topfill = fromwin->w_topfill; - } else { - // fromwin has some diff lines - towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum; - } - } - } - } - } - - // safety check (if diff info gets outdated strange things may happen) - towin->w_botfill = FALSE; - - if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) { - towin->w_topline = towin->w_buffer->b_ml.ml_line_count; - towin->w_botfill = TRUE; - } - - if (towin->w_topline < 1) { - towin->w_topline = 1; - towin->w_topfill = 0; - } - - // When w_topline changes need to recompute w_botline and cursor position - invalidate_botline_win(towin); - changed_line_abv_curs_win(towin); - - check_topfill(towin, FALSE); - (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline, - NULL, TRUE, NULL); -} - -/// This is called when 'diffopt' is changed. -/// -/// @return -int diffopt_changed(void) -{ - int diff_context_new = 6; - int diff_flags_new = 0; - int diff_foldcolumn_new = 2; - - char_u *p = p_dip; - while (*p != NUL) { - if (STRNCMP(p, "filler", 6) == 0) { - p += 6; - diff_flags_new |= DIFF_FILLER; - } else if ((STRNCMP(p, "context:", 8) == 0) && VIM_ISDIGIT(p[8])) { - p += 8; - diff_context_new = getdigits(&p); - } else if (STRNCMP(p, "icase", 5) == 0) { - p += 5; - diff_flags_new |= DIFF_ICASE; - } else if (STRNCMP(p, "iwhite", 6) == 0) { - p += 6; - diff_flags_new |= DIFF_IWHITE; - } else if (STRNCMP(p, "horizontal", 10) == 0) { - p += 10; - diff_flags_new |= DIFF_HORIZONTAL; - } else if (STRNCMP(p, "vertical", 8) == 0) { - p += 8; - diff_flags_new |= DIFF_VERTICAL; - } else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && VIM_ISDIGIT(p[11])) { - p += 11; - diff_foldcolumn_new = getdigits(&p); - } - - if ((*p != ',') && (*p != NUL)) { - return FAIL; - } - - if (*p == ',') { - ++p; - } - } - - // Can't have both "horizontal" and "vertical". - if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) { - return FAIL; - } - - // If "icase" or "iwhite" was added or removed, need to update the diff. - if (diff_flags != diff_flags_new) { - tabpage_T *tp; - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - tp->tp_diff_invalid = TRUE; - } - } - - diff_flags = diff_flags_new; - diff_context = diff_context_new; - diff_foldcolumn = diff_foldcolumn_new; - - diff_redraw(TRUE); - - // recompute the scroll binding with the new option value, may - // remove or add filler lines - check_scrollbind((linenr_T)0, 0L); - return OK; -} - -/// Return TRUE if 'diffopt' contains "horizontal". -/// -/// @return TRUE if 'diffopt' contains "horizontal" -int diffopt_horizontal(void) -{ - return (diff_flags & DIFF_HORIZONTAL) != 0; -} - -/// Find the difference within a changed line. -/// -/// @param startp first char of the change -/// @param endp last char of the change -/// -/// @returns TRUE if the line was added, no other buffer has it. -int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) -{ - char_u *line_new; - int si_org; - int si_new; - int ei_org; - int ei_new; - int added = TRUE; - - // Make a copy of the line, the next ml_get() will invalidate it. - char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); - if (line_org == NULL) { - return FALSE; - } - - int idx = diff_buf_idx(wp->w_buffer); - if (idx == DB_COUNT) { - // cannot happen - free(line_org); - return FALSE; - } - - // search for a change that includes "lnum" in the list of diffblocks. - diff_T *dp; - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { - if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) { - break; - } - } - - if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) { - free(line_org); - return FALSE; - } - - int off = lnum - dp->df_lnum[idx]; - int i; - for (i = 0; i < DB_COUNT; ++i) { - if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) { - // Skip lines that are not in the other change (filler lines). - if (off >= dp->df_count[i]) { - continue; - } - added = FALSE; - line_new = ml_get_buf(curtab->tp_diffbuf[i], - dp->df_lnum[i] + off, FALSE); - - // Search for start of difference - si_org = si_new = 0; - - while (line_org[si_org] != NUL) { - if ((diff_flags & DIFF_IWHITE) - && vim_iswhite(line_org[si_org]) - && vim_iswhite(line_new[si_new])) { - si_org = (int)(skipwhite(line_org + si_org) - line_org); - si_new = (int)(skipwhite(line_new + si_new) - line_new); - } else { - if (line_org[si_org] != line_new[si_new]) { - break; - } - ++si_org; - ++si_new; - } - } - - if (has_mbyte) { - // Move back to first byte of character in both lines (may - // have "nn^" in line_org and "n^ in line_new). - si_org -= (*mb_head_off)(line_org, line_org + si_org); - si_new -= (*mb_head_off)(line_new, line_new + si_new); - } - - if (*startp > si_org) { - *startp = si_org; - } - - // Search for end of difference, if any. - if ((line_org[si_org] != NUL) || (line_new[si_new] != NUL)) { - ei_org = (int)STRLEN(line_org); - ei_new = (int)STRLEN(line_new); - - while (ei_org >= *startp - && ei_new >= si_new - && ei_org >= 0 - && ei_new >= 0) { - if ((diff_flags & DIFF_IWHITE) - && vim_iswhite(line_org[ei_org]) - && vim_iswhite(line_new[ei_new])) { - while (ei_org >= *startp && vim_iswhite(line_org[ei_org])) { - ei_org--; - } - - while (ei_new >= si_new && vim_iswhite(line_new[ei_new])) { - ei_new--; - } - } else { - if (line_org[ei_org] != line_new[ei_new]) { - break; - } - ei_org--; - ei_new--; - } - } - - if (*endp < ei_org) { - *endp = ei_org; - } - } - } - } - - free(line_org); - return added; -} - -/// Return TRUE if line "lnum" is not close to a diff block, this line should -/// be in a fold. -/// -/// @param wp -/// @param lnum -/// -/// @return FALSE if there are no diff blocks at all in this window. -int diff_infold(win_T *wp, linenr_T lnum) -{ - int other = FALSE; - diff_T *dp; - - // Return if 'diff' isn't set. - if (!wp->w_p_diff) { - return FALSE; - } - - int idx = -1; - int i; - for (i = 0; i < DB_COUNT; ++i) { - if (curtab->tp_diffbuf[i] == wp->w_buffer) { - idx = i; - } else if (curtab->tp_diffbuf[i] != NULL) { - other = TRUE; - } - } - - // return here if there are no diffs in the window - if ((idx == -1) || !other) { - return FALSE; - } - - if (curtab->tp_diff_invalid) { - // update after a big change - ex_diffupdate(NULL); - } - - // Return if there are no diff blocks. All lines will be folded. - if (curtab->tp_first_diff == NULL) { - return TRUE; - } - - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { - // If this change is below the line there can't be any further match. - if (dp->df_lnum[idx] - diff_context > lnum) { - break; - } - - // If this change ends before the line we have a match. - if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) { - return FALSE; - } - } - return TRUE; -} - -/// "dp" and "do" commands. -/// -/// @param put -void nv_diffgetput(int put) -{ - exarg_T ea; - ea.arg = (char_u *)""; - if (put) { - ea.cmdidx = CMD_diffput; - } else { - ea.cmdidx = CMD_diffget; - } - ea.addr_count = 0; - ea.line1 = curwin->w_cursor.lnum; - ea.line2 = curwin->w_cursor.lnum; - ex_diffgetput(&ea); -} - -/// ":diffget" and ":diffput" -/// -/// @param eap -void ex_diffgetput(exarg_T *eap) -{ - linenr_T lnum; - int count; - linenr_T off = 0; - diff_T *dp; - diff_T *dprev; - diff_T *dfree; - int i; - int added; - char_u *p; - aco_save_T aco; - buf_T *buf; - int start_skip, end_skip; - int new_count; - int buf_empty; - int found_not_ma = FALSE; - int idx_other; - int idx_from; - int idx_to; - - // Find the current buffer in the list of diff buffers. - int idx_cur = diff_buf_idx(curbuf); - if (idx_cur == DB_COUNT) { - EMSG(_("E99: Current buffer is not in diff mode")); - return; - } - - if (*eap->arg == NUL) { - // No argument: Find the other buffer in the list of diff buffers. - for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) { - if ((curtab->tp_diffbuf[idx_other] != curbuf) - && (curtab->tp_diffbuf[idx_other] != NULL)) { - if ((eap->cmdidx != CMD_diffput) - || curtab->tp_diffbuf[idx_other]->b_p_ma) { - break; - } - found_not_ma = TRUE; - } - } - - if (idx_other == DB_COUNT) { - if (found_not_ma) { - EMSG(_("E793: No other buffer in diff mode is modifiable")); - } else { - EMSG(_("E100: No other buffer in diff mode")); - } - return; - } - - // Check that there isn't a third buffer in the list - for (i = idx_other + 1; i < DB_COUNT; ++i) { - if ((curtab->tp_diffbuf[i] != curbuf) - && (curtab->tp_diffbuf[i] != NULL) - && ((eap->cmdidx != CMD_diffput) || curtab->tp_diffbuf[i]->b_p_ma)) { - EMSG(_("E101: More than two buffers in diff mode, don't know " - "which one to use")); - return; - } - } - } else { - // Buffer number or pattern given. Ignore trailing white space. - p = eap->arg + STRLEN(eap->arg); - while (p > eap->arg && vim_iswhite(p[-1])) { - p--; - } - - for (i = 0; vim_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) { - } - - if (eap->arg + i == p) { - // digits only - i = atol((char *)eap->arg); - } else { - i = buflist_findpat(eap->arg, p, FALSE, TRUE, FALSE); - - if (i < 0) { - // error message already given - return; - } - } - buf = buflist_findnr(i); - - if (buf == NULL) { - EMSG2(_("E102: Can't find buffer \"%s\""), eap->arg); - return; - } - - if (buf == curbuf) { - // nothing to do - return; - } - idx_other = diff_buf_idx(buf); - - if (idx_other == DB_COUNT) { - EMSG2(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg); - return; - } - } - - diff_busy = TRUE; - - // When no range given include the line above or below the cursor. - if (eap->addr_count == 0) { - // Make it possible that ":diffget" on the last line gets line below - // the cursor line when there is no difference above the cursor. - if ((eap->cmdidx == CMD_diffget) - && (eap->line1 == curbuf->b_ml.ml_line_count) - && (diff_check(curwin, eap->line1) == 0) - && ((eap->line1 == 1) || (diff_check(curwin, eap->line1 - 1) == 0))) { - ++eap->line2; - } else if (eap->line1 > 0) { - --eap->line1; - } - } - - if (eap->cmdidx == CMD_diffget) { - idx_from = idx_other; - idx_to = idx_cur; - } else { - idx_from = idx_cur; - idx_to = idx_other; - - // Need to make the other buffer the current buffer to be able to make - // changes in it. - - // set curwin/curbuf to buf and save a few things - aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]); - } - - // May give the warning for a changed buffer here, which can trigger the - // FileChangedRO autocommand, which may do nasty things and mess - // everything up. - if (!curbuf->b_changed) { - change_warning(0); - if (diff_buf_idx(curbuf) != idx_to) { - EMSG(_("E787: Buffer changed unexpectedly")); - return; - } - } - - dprev = NULL; - - for (dp = curtab->tp_first_diff; dp != NULL;) { - if (dp->df_lnum[idx_cur] > eap->line2 + off) { - // past the range that was specified - break; - } - dfree = NULL; - lnum = dp->df_lnum[idx_to]; - count = dp->df_count[idx_to]; - - if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off) - && (u_save(lnum - 1, lnum + count) != FAIL)) { - // Inside the specified range and saving for undo worked. - start_skip = 0; - end_skip = 0; - - if (eap->addr_count > 0) { - // A range was specified: check if lines need to be skipped. - start_skip = eap->line1 + off - dp->df_lnum[idx_cur]; - if (start_skip > 0) { - // range starts below start of current diff block - if (start_skip > count) { - lnum += count; - count = 0; - } else { - count -= start_skip; - lnum += start_skip; - } - } else { - start_skip = 0; - } - - end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1 - - (eap->line2 + off); - - if (end_skip > 0) { - // range ends above end of current/from diff block - if (idx_cur == idx_from) { - // :diffput - i = dp->df_count[idx_cur] - start_skip - end_skip; - - if (count > i) { - count = i; - } - } else { - // :diffget - count -= end_skip; - end_skip = dp->df_count[idx_from] - start_skip - count; - - if (end_skip < 0) { - end_skip = 0; - } - } - } else { - end_skip = 0; - } - } - - buf_empty = FALSE; - added = 0; - - for (i = 0; i < count; ++i) { - // remember deleting the last line of the buffer - buf_empty = curbuf->b_ml.ml_line_count == 1; - ml_delete(lnum, FALSE); - added--; - } - - for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) { - linenr_T nr = dp->df_lnum[idx_from] + start_skip + i; - if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) { - break; - } - p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, FALSE)); - if (p != NULL) { - ml_append(lnum + i - 1, p, 0, FALSE); - free(p); - added++; - if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) { - // Added the first line into an empty buffer, need to - // delete the dummy empty line. - buf_empty = FALSE; - ml_delete((linenr_T)2, FALSE); - } - } - } - new_count = dp->df_count[idx_to] + added; - dp->df_count[idx_to] = new_count; - - if ((start_skip == 0) && (end_skip == 0)) { - // Check if there are any other buffers and if the diff is - // equal in them. - for (i = 0; i < DB_COUNT; ++i) { - if ((curtab->tp_diffbuf[i] != NULL) - && (i != idx_from) - && (i != idx_to) - && !diff_equal_entry(dp, idx_from, i)) { - break; - } - } - - if (i == DB_COUNT) { - // delete the diff entry, the buffers are now equal here - dfree = dp; - dp = dp->df_next; - - if (dprev == NULL) { - curtab->tp_first_diff = dp; - } else { - dprev->df_next = dp; - } - } - } - - // Adjust marks. This will change the following entries! - if (added != 0) { - mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added); - if (curwin->w_cursor.lnum >= lnum) { - // Adjust the cursor position if it's in/after the changed - // lines. - if (curwin->w_cursor.lnum >= lnum + count) { - curwin->w_cursor.lnum += added; - } else if (added < 0) { - curwin->w_cursor.lnum = lnum; - } - } - } - changed_lines(lnum, 0, lnum + count, (long)added); - - if (dfree != NULL) { - // Diff is deleted, update folds in other windows. - diff_fold_update(dfree, idx_to); - free(dfree); - } else { - // mark_adjust() may have changed the count in a wrong way - dp->df_count[idx_to] = new_count; - } - - // When changing the current buffer, keep track of line numbers - if (idx_cur == idx_to) { - off += added; - } - } - - // If before the range or not deleted, go to next diff. - if (dfree == NULL) { - dprev = dp; - dp = dp->df_next; - } - } - - // restore curwin/curbuf and a few other things - if (eap->cmdidx != CMD_diffget) { - // Syncing undo only works for the current buffer, but we change - // another buffer. Sync undo if the command was typed. This isn't - // 100% right when ":diffput" is used in a function or mapping. - if (KeyTyped) { - u_sync(FALSE); - } - aucmd_restbuf(&aco); - } - - diff_busy = FALSE; - - // Check that the cursor is on a valid character and update it's position. - // When there were filler lines the topline has become invalid. - check_cursor(); - changed_line_abv_curs(); - - // Also need to redraw the other buffers. - diff_redraw(FALSE); -} - -/// Update folds for all diff buffers for entry "dp". -/// -/// Skip buffer with index "skip_idx". -/// When there are no diffs, all folds are removed. -/// -/// @param dp -/// @param skip_idx -static void diff_fold_update(diff_T *dp, int skip_idx) -{ - win_T *wp; - for (wp = firstwin; wp != NULL; wp = wp->w_next) { - int i; - for (i = 0; i < DB_COUNT; ++i) { - if ((curtab->tp_diffbuf[i] == wp->w_buffer) && (i != skip_idx)) { - foldUpdate(wp, dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i]); - } - } - } -} - -/// Checks if the buffer is in diff-mode. -/// -/// @param buf The buffer to check. -/// -/// @return TRUE if buffer "buf" is in diff-mode. -int diff_mode_buf(buf_T *buf) -{ - tabpage_T *tp; - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - if (diff_buf_idx_tp(buf, tp) != DB_COUNT) { - return TRUE; - } - } - return FALSE; -} - -/// Move "count" times in direction "dir" to the next diff block. -/// -/// @param dir -/// @param count -/// -/// @return FAIL if there isn't such a diff block. -int diff_move_to(int dir, long count) -{ - linenr_T lnum = curwin->w_cursor.lnum; - int idx = diff_buf_idx(curbuf); - if ((idx == DB_COUNT) || (curtab->tp_first_diff == NULL)) { - return FAIL; - } - - if (curtab->tp_diff_invalid) { - // update after a big change - ex_diffupdate(NULL); - } - - if (curtab->tp_first_diff == NULL) { - // no diffs today - return FAIL; - } - - while (--count >= 0) { - // Check if already before first diff. - if ((dir == BACKWARD) && (lnum <= curtab->tp_first_diff->df_lnum[idx])) { - break; - } - - diff_T *dp; - for (dp = curtab->tp_first_diff;; dp = dp->df_next) { - if (dp == NULL) { - break; - } - - if (((dir == FORWARD) && (lnum < dp->df_lnum[idx])) - || ((dir == BACKWARD) - && ((dp->df_next == NULL) - || (lnum <= dp->df_next->df_lnum[idx])))) { - lnum = dp->df_lnum[idx]; - break; - } - } - } - - // don't end up past the end of the file - if (lnum > curbuf->b_ml.ml_line_count) { - lnum = curbuf->b_ml.ml_line_count; - } - - // When the cursor didn't move at all we fail. - if (lnum == curwin->w_cursor.lnum) { - return FAIL; - } - - setpcmark(); - curwin->w_cursor.lnum = lnum; - curwin->w_cursor.col = 0; - - return OK; -} - -/// Finds the corresponding line in a diff. -/// -/// @param buf1 -/// @param lnum1 -/// @param buf2 -/// @param lnum3 -/// -/// @return The corresponding line. -linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2, - linenr_T lnum3) -{ - int idx1; - int idx2; - diff_T *dp; - int baseline = 0; - linenr_T lnum2; - - idx1 = diff_buf_idx(buf1); - idx2 = diff_buf_idx(buf2); - - if ((idx1 == DB_COUNT) - || (idx2 == DB_COUNT) - || (curtab->tp_first_diff == NULL)) { - return lnum1; - } - - if (curtab->tp_diff_invalid) { - // update after a big change - ex_diffupdate(NULL); - } - - if (curtab->tp_first_diff == NULL) { - // no diffs today - return lnum1; - } - - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { - if (dp->df_lnum[idx1] > lnum1) { - lnum2 = lnum1 - baseline; - - // don't end up past the end of the file - if (lnum2 > buf2->b_ml.ml_line_count) { - lnum2 = buf2->b_ml.ml_line_count; - } - - return lnum2; - } else if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) { - // Inside the diffblock - baseline = lnum1 - dp->df_lnum[idx1]; - - if (baseline > dp->df_count[idx2]) { - baseline = dp->df_count[idx2]; - } - - return dp->df_lnum[idx2] + baseline; - } else if ((dp->df_lnum[idx1] == lnum1) - && (dp->df_count[idx1] == 0) - && (dp->df_lnum[idx2] <= lnum3) - && ((dp->df_lnum[idx2] + dp->df_count[idx2]) > lnum3)) { - // Special case: if the cursor is just after a zero-count - // block (i.e. all filler) and the target cursor is already - // inside the corresponding block, leave the target cursor - // unmoved. This makes repeated CTRL-W W operations work - // as expected. - return lnum3; - } - baseline = (dp->df_lnum[idx1] + dp->df_count[idx1]) - - (dp->df_lnum[idx2] + dp->df_count[idx2]); - } - - // If we get here then the cursor is after the last diff - lnum2 = lnum1 - baseline; - - // don't end up past the end of the file - if (lnum2 > buf2->b_ml.ml_line_count) { - lnum2 = buf2->b_ml.ml_line_count; - } - - return lnum2; -} - -/// For line "lnum" in the current window find the equivalent lnum in window -/// "wp", compensating for inserted/deleted lines. -linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) -{ - diff_T *dp; - int idx; - int i; - linenr_T n; - - idx = diff_buf_idx(curbuf); - - if (idx == DB_COUNT) { - // safety check - return (linenr_T)0; - } - - if (curtab->tp_diff_invalid) { - // update after a big change - ex_diffupdate(NULL); - } - - // search for a change that includes "lnum" in the list of diffblocks. - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { - if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) { - break; - } - } - - // When after the last change, compute relative to the last line number. - if (dp == NULL) { - return wp->w_buffer->b_ml.ml_line_count - - (curbuf->b_ml.ml_line_count - lnum); - } - - // Find index for "wp". - i = diff_buf_idx(wp->w_buffer); - - if (i == DB_COUNT) { - // safety check - return (linenr_T)0; - } - - n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); - if (n > dp->df_lnum[i] + dp->df_count[i]) { - n = dp->df_lnum[i] + dp->df_count[i]; - } - return n; -} diff --git a/src/diff.h b/src/diff.h deleted file mode 100644 index 223fe8add2..0000000000 --- a/src/diff.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef NEOVIM_DIFF_H -#define NEOVIM_DIFF_H - -void diff_buf_delete(buf_T *buf); -void diff_buf_adjust(win_T *win); -void diff_buf_add(buf_T *buf); -void diff_invalidate(buf_T *buf); -void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount, - long amount_after); -void ex_diffupdate(exarg_T *eap); -void ex_diffpatch(exarg_T *eap); -void ex_diffsplit(exarg_T *eap); -void ex_diffthis(exarg_T *eap); -void diff_win_options(win_T *wp, int addbuf); -void ex_diffoff(exarg_T *eap); -void diff_clear(tabpage_T *tp); -int diff_check(win_T *wp, linenr_T lnum); -int diff_check_fill(win_T *wp, linenr_T lnum); -void diff_set_topline(win_T *fromwin, win_T *towin); -int diffopt_changed(void); -int diffopt_horizontal(void); -int diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp); -int diff_infold(win_T *wp, linenr_T lnum); -void nv_diffgetput(int put); -void ex_diffgetput(exarg_T *eap); -int diff_mode_buf(buf_T *buf); -int diff_move_to(int dir, long count); -linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, - buf_T *buf2, - linenr_T lnum3); -linenr_T diff_lnum_win(linenr_T lnum, win_T *wp); - -#endif // NEOVIM_DIFF_H diff --git a/src/digraph.c b/src/digraph.c deleted file mode 100644 index 7ab8bd181d..0000000000 --- a/src/digraph.c +++ /dev/null @@ -1,1881 +0,0 @@ -/// @file digraph.c -/// -/// code for digraphs - -#include "vim.h" -#include "digraph.h" -#include "charset.h" -#include "ex_cmds2.h" -#include "ex_docmd.h" -#include "ex_getln.h" -#include "getchar.h" -#include "mbyte.h" -#include "message.h" -#include "misc2.h" -#include "memory.h" -#include "garray.h" -#include "normal.h" -#include "screen.h" -#include "ui.h" - -typedef int result_T; - -typedef struct digraph { - char_u char1; - char_u char2; - result_T result; -} digr_T; - -static int getexactdigraph(int char1, int char2, int meta_char); -static void printdigraph(digr_T *dp); - -// digraphs added by the user -static garray_T user_digraphs = {0, 0, (int)sizeof(digr_T), 10, NULL}; - -/// Note: Characters marked with XX are not included literally, because some -/// compilers cannot handle them (Amiga SAS/C is the most picky one). -static digr_T digraphdefault[] = - -// digraphs for Unicode from RFC1345 -// (also work for ISO-8859-1 aka latin1) -{ - { 'N', 'U', 0x0a }, // LF for NUL - { 'S', 'H', 0x01 }, - { 'S', 'X', 0x02 }, - { 'E', 'X', 0x03 }, - { 'E', 'T', 0x04 }, - { 'E', 'Q', 0x05 }, - { 'A', 'K', 0x06 }, - { 'B', 'L', 0x07 }, - { 'B', 'S', 0x08 }, - { 'H', 'T', 0x09 }, - { 'L', 'F', 0x0a }, - { 'V', 'T', 0x0b }, - { 'F', 'F', 0x0c }, - { 'C', 'R', 0x0d }, - { 'S', 'O', 0x0e }, - { 'S', 'I', 0x0f }, - { 'D', 'L', 0x10 }, - { 'D', '1', 0x11 }, - { 'D', '2', 0x12 }, - { 'D', '3', 0x13 }, - { 'D', '4', 0x14 }, - { 'N', 'K', 0x15 }, - { 'S', 'Y', 0x16 }, - { 'E', 'B', 0x17 }, - { 'C', 'N', 0x18 }, - { 'E', 'M', 0x19 }, - { 'S', 'B', 0x1a }, - { 'E', 'C', 0x1b }, - { 'F', 'S', 0x1c }, - { 'G', 'S', 0x1d }, - { 'R', 'S', 0x1e }, - { 'U', 'S', 0x1f }, - { 'S', 'P', 0x20 }, - { 'N', 'b', 0x23 }, - { 'D', 'O', 0x24 }, - { 'A', 't', 0x40 }, - { '<', '(', 0x5b }, - { '/', '/', 0x5c }, - { ')', '>', 0x5d }, - { '\'', '>', 0x5e }, - { '\'', '!', 0x60 }, - { '(', '!', 0x7b }, - { '!', '!', 0x7c }, - { '!', ')', 0x7d }, - { '\'', '?', 0x7e }, - { 'D', 'T', 0x7f }, - { 'P', 'A', 0x80 }, - { 'H', 'O', 0x81 }, - { 'B', 'H', 0x82 }, - { 'N', 'H', 0x83 }, - { 'I', 'N', 0x84 }, - { 'N', 'L', 0x85 }, - { 'S', 'A', 0x86 }, - { 'E', 'S', 0x87 }, - { 'H', 'S', 0x88 }, - { 'H', 'J', 0x89 }, - { 'V', 'S', 0x8a }, - { 'P', 'D', 0x8b }, - { 'P', 'U', 0x8c }, - { 'R', 'I', 0x8d }, - { 'S', '2', 0x8e }, - { 'S', '3', 0x8f }, - { 'D', 'C', 0x90 }, - { 'P', '1', 0x91 }, - { 'P', '2', 0x92 }, - { 'T', 'S', 0x93 }, - { 'C', 'C', 0x94 }, - { 'M', 'W', 0x95 }, - { 'S', 'G', 0x96 }, - { 'E', 'G', 0x97 }, - { 'S', 'S', 0x98 }, - { 'G', 'C', 0x99 }, - { 'S', 'C', 0x9a }, - { 'C', 'I', 0x9b }, - { 'S', 'T', 0x9c }, - { 'O', 'C', 0x9d }, - { 'P', 'M', 0x9e }, - { 'A', 'C', 0x9f }, - { 'N', 'S', 0xa0 }, - { '!', 'I', 0xa1 }, - { 'C', 't', 0xa2 }, - { 'P', 'd', 0xa3 }, - { 'C', 'u', 0xa4 }, - { 'Y', 'e', 0xa5 }, - { 'B', 'B', 0xa6 }, - { 'S', 'E', 0xa7 }, - { '\'', ':', 0xa8 }, - { 'C', 'o', 0xa9 }, - { '-', 'a', 0xaa }, - { '<', '<', 0xab }, - { 'N', 'O', 0xac }, - { '-', '-', 0xad }, - { 'R', 'g', 0xae }, - { '\'', 'm', 0xaf }, - { 'D', 'G', 0xb0 }, - { '+', '-', 0xb1 }, - { '2', 'S', 0xb2 }, - { '3', 'S', 0xb3 }, - { '\'', '\'', 0xb4 }, - { 'M', 'y', 0xb5 }, - { 'P', 'I', 0xb6 }, - { '.', 'M', 0xb7 }, - { '\'', ',', 0xb8 }, - { '1', 'S', 0xb9 }, - { '-', 'o', 0xba }, - { '>', '>', 0xbb }, - { '1', '4', 0xbc }, - { '1', '2', 0xbd }, - { '3', '4', 0xbe }, - { '?', 'I', 0xbf }, - { 'A', '!', 0xc0 }, - { 'A', '\'', 0xc1 }, - { 'A', '>', 0xc2 }, - { 'A', '?', 0xc3 }, - { 'A', ':', 0xc4 }, - { 'A', 'A', 0xc5 }, - { 'A', 'E', 0xc6 }, - { 'C', ',', 0xc7 }, - { 'E', '!', 0xc8 }, - { 'E', '\'', 0xc9 }, - { 'E', '>', 0xca }, - { 'E', ':', 0xcb }, - { 'I', '!', 0xcc }, - { 'I', '\'', 0xcd }, - { 'I', '>', 0xce }, - { 'I', ':', 0xcf }, - { 'D', '-', 0xd0 }, - { 'N', '?', 0xd1 }, - { 'O', '!', 0xd2 }, - { 'O', '\'', 0xd3 }, - { 'O', '>', 0xd4 }, - { 'O', '?', 0xd5 }, - { 'O', ':', 0xd6 }, - { '*', 'X', 0xd7 }, - { 'O', '/', 0xd8 }, - { 'U', '!', 0xd9 }, - { 'U', '\'', 0xda }, - { 'U', '>', 0xdb }, - { 'U', ':', 0xdc }, - { 'Y', '\'', 0xdd }, - { 'T', 'H', 0xde }, - { 's', 's', 0xdf }, - { 'a', '!', 0xe0 }, - { 'a', '\'', 0xe1 }, - { 'a', '>', 0xe2 }, - { 'a', '?', 0xe3 }, - { 'a', ':', 0xe4 }, - { 'a', 'a', 0xe5 }, - { 'a', 'e', 0xe6 }, - { 'c', ',', 0xe7 }, - { 'e', '!', 0xe8 }, - { 'e', '\'', 0xe9 }, - { 'e', '>', 0xea }, - { 'e', ':', 0xeb }, - { 'i', '!', 0xec }, - { 'i', '\'', 0xed }, - { 'i', '>', 0xee }, - { 'i', ':', 0xef }, - { 'd', '-', 0xf0 }, - { 'n', '?', 0xf1 }, - { 'o', '!', 0xf2 }, - { 'o', '\'', 0xf3 }, - { 'o', '>', 0xf4 }, - { 'o', '?', 0xf5 }, - { 'o', ':', 0xf6 }, - { '-', ':', 0xf7 }, - { 'o', '/', 0xf8 }, - { 'u', '!', 0xf9 }, - { 'u', '\'', 0xfa }, - { 'u', '>', 0xfb }, - { 'u', ':', 0xfc }, - { 'y', '\'', 0xfd }, - { 't', 'h', 0xfe }, - { 'y', ':', 0xff }, - - { 'A', '-', 0x0100 }, - { 'a', '-', 0x0101 }, - { 'A', '(', 0x0102 }, - { 'a', '(', 0x0103 }, - { 'A', ';', 0x0104 }, - { 'a', ';', 0x0105 }, - { 'C', '\'', 0x0106 }, - { 'c', '\'', 0x0107 }, - { 'C', '>', 0x0108 }, - { 'c', '>', 0x0109 }, - { 'C', '.', 0x010a }, - { 'c', '.', 0x010b }, - { 'C', '<', 0x010c }, - { 'c', '<', 0x010d }, - { 'D', '<', 0x010e }, - { 'd', '<', 0x010f }, - { 'D', '/', 0x0110 }, - { 'd', '/', 0x0111 }, - { 'E', '-', 0x0112 }, - { 'e', '-', 0x0113 }, - { 'E', '(', 0x0114 }, - { 'e', '(', 0x0115 }, - { 'E', '.', 0x0116 }, - { 'e', '.', 0x0117 }, - { 'E', ';', 0x0118 }, - { 'e', ';', 0x0119 }, - { 'E', '<', 0x011a }, - { 'e', '<', 0x011b }, - { 'G', '>', 0x011c }, - { 'g', '>', 0x011d }, - { 'G', '(', 0x011e }, - { 'g', '(', 0x011f }, - { 'G', '.', 0x0120 }, - { 'g', '.', 0x0121 }, - { 'G', ',', 0x0122 }, - { 'g', ',', 0x0123 }, - { 'H', '>', 0x0124 }, - { 'h', '>', 0x0125 }, - { 'H', '/', 0x0126 }, - { 'h', '/', 0x0127 }, - { 'I', '?', 0x0128 }, - { 'i', '?', 0x0129 }, - { 'I', '-', 0x012a }, - { 'i', '-', 0x012b }, - { 'I', '(', 0x012c }, - { 'i', '(', 0x012d }, - { 'I', ';', 0x012e }, - { 'i', ';', 0x012f }, - { 'I', '.', 0x0130 }, - { 'i', '.', 0x0131 }, - { 'I', 'J', 0x0132 }, - { 'i', 'j', 0x0133 }, - { 'J', '>', 0x0134 }, - { 'j', '>', 0x0135 }, - { 'K', ',', 0x0136 }, - { 'k', ',', 0x0137 }, - { 'k', 'k', 0x0138 }, - { 'L', '\'', 0x0139 }, - { 'l', '\'', 0x013a }, - { 'L', ',', 0x013b }, - { 'l', ',', 0x013c }, - { 'L', '<', 0x013d }, - { 'l', '<', 0x013e }, - { 'L', '.', 0x013f }, - { 'l', '.', 0x0140 }, - { 'L', '/', 0x0141 }, - { 'l', '/', 0x0142 }, - { 'N', '\'', 0x0143 }, - { 'n', '\'', 0x0144 }, - { 'N', ',', 0x0145 }, - { 'n', ',', 0x0146 }, - { 'N', '<', 0x0147 }, - { 'n', '<', 0x0148 }, - { '\'', 'n', 0x0149 }, - { 'N', 'G', 0x014a }, - { 'n', 'g', 0x014b }, - { 'O', '-', 0x014c }, - { 'o', '-', 0x014d }, - { 'O', '(', 0x014e }, - { 'o', '(', 0x014f }, - { 'O', '"', 0x0150 }, - { 'o', '"', 0x0151 }, - { 'O', 'E', 0x0152 }, - { 'o', 'e', 0x0153 }, - { 'R', '\'', 0x0154 }, - { 'r', '\'', 0x0155 }, - { 'R', ',', 0x0156 }, - { 'r', ',', 0x0157 }, - { 'R', '<', 0x0158 }, - { 'r', '<', 0x0159 }, - { 'S', '\'', 0x015a }, - { 's', '\'', 0x015b }, - { 'S', '>', 0x015c }, - { 's', '>', 0x015d }, - { 'S', ',', 0x015e }, - { 's', ',', 0x015f }, - { 'S', '<', 0x0160 }, - { 's', '<', 0x0161 }, - { 'T', ',', 0x0162 }, - { 't', ',', 0x0163 }, - { 'T', '<', 0x0164 }, - { 't', '<', 0x0165 }, - { 'T', '/', 0x0166 }, - { 't', '/', 0x0167 }, - { 'U', '?', 0x0168 }, - { 'u', '?', 0x0169 }, - { 'U', '-', 0x016a }, - { 'u', '-', 0x016b }, - { 'U', '(', 0x016c }, - { 'u', '(', 0x016d }, - { 'U', '0', 0x016e }, - { 'u', '0', 0x016f }, - { 'U', '"', 0x0170 }, - { 'u', '"', 0x0171 }, - { 'U', ';', 0x0172 }, - { 'u', ';', 0x0173 }, - { 'W', '>', 0x0174 }, - { 'w', '>', 0x0175 }, - { 'Y', '>', 0x0176 }, - { 'y', '>', 0x0177 }, - { 'Y', ':', 0x0178 }, - { 'Z', '\'', 0x0179 }, - { 'z', '\'', 0x017a }, - { 'Z', '.', 0x017b }, - { 'z', '.', 0x017c }, - { 'Z', '<', 0x017d }, - { 'z', '<', 0x017e }, - { 'O', '9', 0x01a0 }, - { 'o', '9', 0x01a1 }, - { 'O', 'I', 0x01a2 }, - { 'o', 'i', 0x01a3 }, - { 'y', 'r', 0x01a6 }, - { 'U', '9', 0x01af }, - { 'u', '9', 0x01b0 }, - { 'Z', '/', 0x01b5 }, - { 'z', '/', 0x01b6 }, - { 'E', 'D', 0x01b7 }, - { 'A', '<', 0x01cd }, - { 'a', '<', 0x01ce }, - { 'I', '<', 0x01cf }, - { 'i', '<', 0x01d0 }, - { 'O', '<', 0x01d1 }, - { 'o', '<', 0x01d2 }, - { 'U', '<', 0x01d3 }, - { 'u', '<', 0x01d4 }, - { 'A', '1', 0x01de }, - { 'a', '1', 0x01df }, - { 'A', '7', 0x01e0 }, - { 'a', '7', 0x01e1 }, - { 'A', '3', 0x01e2 }, - { 'a', '3', 0x01e3 }, - { 'G', '/', 0x01e4 }, - { 'g', '/', 0x01e5 }, - { 'G', '<', 0x01e6 }, - { 'g', '<', 0x01e7 }, - { 'K', '<', 0x01e8 }, - { 'k', '<', 0x01e9 }, - { 'O', ';', 0x01ea }, - { 'o', ';', 0x01eb }, - { 'O', '1', 0x01ec }, - { 'o', '1', 0x01ed }, - { 'E', 'Z', 0x01ee }, - { 'e', 'z', 0x01ef }, - { 'j', '<', 0x01f0 }, - { 'G', '\'', 0x01f4 }, - { 'g', '\'', 0x01f5 }, - { ';', 'S', 0x02bf }, - { '\'', '<', 0x02c7 }, - { '\'', '(', 0x02d8 }, - { '\'', '.', 0x02d9 }, - { '\'', '0', 0x02da }, - { '\'', ';', 0x02db }, - { '\'', '"', 0x02dd }, - { 'A', '%', 0x0386 }, - { 'E', '%', 0x0388 }, - { 'Y', '%', 0x0389 }, - { 'I', '%', 0x038a }, - { 'O', '%', 0x038c }, - { 'U', '%', 0x038e }, - { 'W', '%', 0x038f }, - { 'i', '3', 0x0390 }, - { 'A', '*', 0x0391 }, - { 'B', '*', 0x0392 }, - { 'G', '*', 0x0393 }, - { 'D', '*', 0x0394 }, - { 'E', '*', 0x0395 }, - { 'Z', '*', 0x0396 }, - { 'Y', '*', 0x0397 }, - { 'H', '*', 0x0398 }, - { 'I', '*', 0x0399 }, - { 'K', '*', 0x039a }, - { 'L', '*', 0x039b }, - { 'M', '*', 0x039c }, - { 'N', '*', 0x039d }, - { 'C', '*', 0x039e }, - { 'O', '*', 0x039f }, - { 'P', '*', 0x03a0 }, - { 'R', '*', 0x03a1 }, - { 'S', '*', 0x03a3 }, - { 'T', '*', 0x03a4 }, - { 'U', '*', 0x03a5 }, - { 'F', '*', 0x03a6 }, - { 'X', '*', 0x03a7 }, - { 'Q', '*', 0x03a8 }, - { 'W', '*', 0x03a9 }, - { 'J', '*', 0x03aa }, - { 'V', '*', 0x03ab }, - { 'a', '%', 0x03ac }, - { 'e', '%', 0x03ad }, - { 'y', '%', 0x03ae }, - { 'i', '%', 0x03af }, - { 'u', '3', 0x03b0 }, - { 'a', '*', 0x03b1 }, - { 'b', '*', 0x03b2 }, - { 'g', '*', 0x03b3 }, - { 'd', '*', 0x03b4 }, - { 'e', '*', 0x03b5 }, - { 'z', '*', 0x03b6 }, - { 'y', '*', 0x03b7 }, - { 'h', '*', 0x03b8 }, - { 'i', '*', 0x03b9 }, - { 'k', '*', 0x03ba }, - { 'l', '*', 0x03bb }, - { 'm', '*', 0x03bc }, - { 'n', '*', 0x03bd }, - { 'c', '*', 0x03be }, - { 'o', '*', 0x03bf }, - { 'p', '*', 0x03c0 }, - { 'r', '*', 0x03c1 }, - { '*', 's', 0x03c2 }, - { 's', '*', 0x03c3 }, - { 't', '*', 0x03c4 }, - { 'u', '*', 0x03c5 }, - { 'f', '*', 0x03c6 }, - { 'x', '*', 0x03c7 }, - { 'q', '*', 0x03c8 }, - { 'w', '*', 0x03c9 }, - { 'j', '*', 0x03ca }, - { 'v', '*', 0x03cb }, - { 'o', '%', 0x03cc }, - { 'u', '%', 0x03cd }, - { 'w', '%', 0x03ce }, - { '\'', 'G', 0x03d8 }, - { ',', 'G', 0x03d9 }, - { 'T', '3', 0x03da }, - { 't', '3', 0x03db }, - { 'M', '3', 0x03dc }, - { 'm', '3', 0x03dd }, - { 'K', '3', 0x03de }, - { 'k', '3', 0x03df }, - { 'P', '3', 0x03e0 }, - { 'p', '3', 0x03e1 }, - { '\'', '%', 0x03f4 }, - { 'j', '3', 0x03f5 }, - { 'I', 'O', 0x0401 }, - { 'D', '%', 0x0402 }, - { 'G', '%', 0x0403 }, - { 'I', 'E', 0x0404 }, - { 'D', 'S', 0x0405 }, - { 'I', 'I', 0x0406 }, - { 'Y', 'I', 0x0407 }, - { 'J', '%', 0x0408 }, - { 'L', 'J', 0x0409 }, - { 'N', 'J', 0x040a }, - { 'T', 's', 0x040b }, - { 'K', 'J', 0x040c }, - { 'V', '%', 0x040e }, - { 'D', 'Z', 0x040f }, - { 'A', '=', 0x0410 }, - { 'B', '=', 0x0411 }, - { 'V', '=', 0x0412 }, - { 'G', '=', 0x0413 }, - { 'D', '=', 0x0414 }, - { 'E', '=', 0x0415 }, - { 'Z', '%', 0x0416 }, - { 'Z', '=', 0x0417 }, - { 'I', '=', 0x0418 }, - { 'J', '=', 0x0419 }, - { 'K', '=', 0x041a }, - { 'L', '=', 0x041b }, - { 'M', '=', 0x041c }, - { 'N', '=', 0x041d }, - { 'O', '=', 0x041e }, - { 'P', '=', 0x041f }, - { 'R', '=', 0x0420 }, - { 'S', '=', 0x0421 }, - { 'T', '=', 0x0422 }, - { 'U', '=', 0x0423 }, - { 'F', '=', 0x0424 }, - { 'H', '=', 0x0425 }, - { 'C', '=', 0x0426 }, - { 'C', '%', 0x0427 }, - { 'S', '%', 0x0428 }, - { 'S', 'c', 0x0429 }, - { '=', '"', 0x042a }, - { 'Y', '=', 0x042b }, - { '%', '"', 0x042c }, - { 'J', 'E', 0x042d }, - { 'J', 'U', 0x042e }, - { 'J', 'A', 0x042f }, - { 'a', '=', 0x0430 }, - { 'b', '=', 0x0431 }, - { 'v', '=', 0x0432 }, - { 'g', '=', 0x0433 }, - { 'd', '=', 0x0434 }, - { 'e', '=', 0x0435 }, - { 'z', '%', 0x0436 }, - { 'z', '=', 0x0437 }, - { 'i', '=', 0x0438 }, - { 'j', '=', 0x0439 }, - { 'k', '=', 0x043a }, - { 'l', '=', 0x043b }, - { 'm', '=', 0x043c }, - { 'n', '=', 0x043d }, - { 'o', '=', 0x043e }, - { 'p', '=', 0x043f }, - { 'r', '=', 0x0440 }, - { 's', '=', 0x0441 }, - { 't', '=', 0x0442 }, - { 'u', '=', 0x0443 }, - { 'f', '=', 0x0444 }, - { 'h', '=', 0x0445 }, - { 'c', '=', 0x0446 }, - { 'c', '%', 0x0447 }, - { 's', '%', 0x0448 }, - { 's', 'c', 0x0449 }, - { '=', '\'', 0x044a }, - { 'y', '=', 0x044b }, - { '%', '\'', 0x044c }, - { 'j', 'e', 0x044d }, - { 'j', 'u', 0x044e }, - { 'j', 'a', 0x044f }, - { 'i', 'o', 0x0451 }, - { 'd', '%', 0x0452 }, - { 'g', '%', 0x0453 }, - { 'i', 'e', 0x0454 }, - { 'd', 's', 0x0455 }, - { 'i', 'i', 0x0456 }, - { 'y', 'i', 0x0457 }, - { 'j', '%', 0x0458 }, - { 'l', 'j', 0x0459 }, - { 'n', 'j', 0x045a }, - { 't', 's', 0x045b }, - { 'k', 'j', 0x045c }, - { 'v', '%', 0x045e }, - { 'd', 'z', 0x045f }, - { 'Y', '3', 0x0462 }, - { 'y', '3', 0x0463 }, - { 'O', '3', 0x046a }, - { 'o', '3', 0x046b }, - { 'F', '3', 0x0472 }, - { 'f', '3', 0x0473 }, - { 'V', '3', 0x0474 }, - { 'v', '3', 0x0475 }, - { 'C', '3', 0x0480 }, - { 'c', '3', 0x0481 }, - { 'G', '3', 0x0490 }, - { 'g', '3', 0x0491 }, - { 'A', '+', 0x05d0 }, - { 'B', '+', 0x05d1 }, - { 'G', '+', 0x05d2 }, - { 'D', '+', 0x05d3 }, - { 'H', '+', 0x05d4 }, - { 'W', '+', 0x05d5 }, - { 'Z', '+', 0x05d6 }, - { 'X', '+', 0x05d7 }, - { 'T', 'j', 0x05d8 }, - { 'J', '+', 0x05d9 }, - { 'K', '%', 0x05da }, - { 'K', '+', 0x05db }, - { 'L', '+', 0x05dc }, - { 'M', '%', 0x05dd }, - { 'M', '+', 0x05de }, - { 'N', '%', 0x05df }, - { 'N', '+', 0x05e0 }, - { 'S', '+', 0x05e1 }, - { 'E', '+', 0x05e2 }, - { 'P', '%', 0x05e3 }, - { 'P', '+', 0x05e4 }, - { 'Z', 'j', 0x05e5 }, - { 'Z', 'J', 0x05e6 }, - { 'Q', '+', 0x05e7 }, - { 'R', '+', 0x05e8 }, - { 'S', 'h', 0x05e9 }, - { 'T', '+', 0x05ea }, - { ',', '+', 0x060c }, - { ';', '+', 0x061b }, - { '?', '+', 0x061f }, - { 'H', '\'', 0x0621 }, - { 'a', 'M', 0x0622 }, - { 'a', 'H', 0x0623 }, - { 'w', 'H', 0x0624 }, - { 'a', 'h', 0x0625 }, - { 'y', 'H', 0x0626 }, - { 'a', '+', 0x0627 }, - { 'b', '+', 0x0628 }, - { 't', 'm', 0x0629 }, - { 't', '+', 0x062a }, - { 't', 'k', 0x062b }, - { 'g', '+', 0x062c }, - { 'h', 'k', 0x062d }, - { 'x', '+', 0x062e }, - { 'd', '+', 0x062f }, - { 'd', 'k', 0x0630 }, - { 'r', '+', 0x0631 }, - { 'z', '+', 0x0632 }, - { 's', '+', 0x0633 }, - { 's', 'n', 0x0634 }, - { 'c', '+', 0x0635 }, - { 'd', 'd', 0x0636 }, - { 't', 'j', 0x0637 }, - { 'z', 'H', 0x0638 }, - { 'e', '+', 0x0639 }, - { 'i', '+', 0x063a }, - { '+', '+', 0x0640 }, - { 'f', '+', 0x0641 }, - { 'q', '+', 0x0642 }, - { 'k', '+', 0x0643 }, - { 'l', '+', 0x0644 }, - { 'm', '+', 0x0645 }, - { 'n', '+', 0x0646 }, - { 'h', '+', 0x0647 }, - { 'w', '+', 0x0648 }, - { 'j', '+', 0x0649 }, - { 'y', '+', 0x064a }, - { ':', '+', 0x064b }, - { '"', '+', 0x064c }, - { '=', '+', 0x064d }, - { '/', '+', 0x064e }, - { '\'', '+', 0x064f }, - { '1', '+', 0x0650 }, - { '3', '+', 0x0651 }, - { '0', '+', 0x0652 }, - { 'a', 'S', 0x0670 }, - { 'p', '+', 0x067e }, - { 'v', '+', 0x06a4 }, - { 'g', 'f', 0x06af }, - { '0', 'a', 0x06f0 }, - { '1', 'a', 0x06f1 }, - { '2', 'a', 0x06f2 }, - { '3', 'a', 0x06f3 }, - { '4', 'a', 0x06f4 }, - { '5', 'a', 0x06f5 }, - { '6', 'a', 0x06f6 }, - { '7', 'a', 0x06f7 }, - { '8', 'a', 0x06f8 }, - { '9', 'a', 0x06f9 }, - { 'B', '.', 0x1e02 }, - { 'b', '.', 0x1e03 }, - { 'B', '_', 0x1e06 }, - { 'b', '_', 0x1e07 }, - { 'D', '.', 0x1e0a }, - { 'd', '.', 0x1e0b }, - { 'D', '_', 0x1e0e }, - { 'd', '_', 0x1e0f }, - { 'D', ',', 0x1e10 }, - { 'd', ',', 0x1e11 }, - { 'F', '.', 0x1e1e }, - { 'f', '.', 0x1e1f }, - { 'G', '-', 0x1e20 }, - { 'g', '-', 0x1e21 }, - { 'H', '.', 0x1e22 }, - { 'h', '.', 0x1e23 }, - { 'H', ':', 0x1e26 }, - { 'h', ':', 0x1e27 }, - { 'H', ',', 0x1e28 }, - { 'h', ',', 0x1e29 }, - { 'K', '\'', 0x1e30 }, - { 'k', '\'', 0x1e31 }, - { 'K', '_', 0x1e34 }, - { 'k', '_', 0x1e35 }, - { 'L', '_', 0x1e3a }, - { 'l', '_', 0x1e3b }, - { 'M', '\'', 0x1e3e }, - { 'm', '\'', 0x1e3f }, - { 'M', '.', 0x1e40 }, - { 'm', '.', 0x1e41 }, - { 'N', '.', 0x1e44 }, - { 'n', '.', 0x1e45 }, - { 'N', '_', 0x1e48 }, - { 'n', '_', 0x1e49 }, - { 'P', '\'', 0x1e54 }, - { 'p', '\'', 0x1e55 }, - { 'P', '.', 0x1e56 }, - { 'p', '.', 0x1e57 }, - { 'R', '.', 0x1e58 }, - { 'r', '.', 0x1e59 }, - { 'R', '_', 0x1e5e }, - { 'r', '_', 0x1e5f }, - { 'S', '.', 0x1e60 }, - { 's', '.', 0x1e61 }, - { 'T', '.', 0x1e6a }, - { 't', '.', 0x1e6b }, - { 'T', '_', 0x1e6e }, - { 't', '_', 0x1e6f }, - { 'V', '?', 0x1e7c }, - { 'v', '?', 0x1e7d }, - { 'W', '!', 0x1e80 }, - { 'w', '!', 0x1e81 }, - { 'W', '\'', 0x1e82 }, - { 'w', '\'', 0x1e83 }, - { 'W', ':', 0x1e84 }, - { 'w', ':', 0x1e85 }, - { 'W', '.', 0x1e86 }, - { 'w', '.', 0x1e87 }, - { 'X', '.', 0x1e8a }, - { 'x', '.', 0x1e8b }, - { 'X', ':', 0x1e8c }, - { 'x', ':', 0x1e8d }, - { 'Y', '.', 0x1e8e }, - { 'y', '.', 0x1e8f }, - { 'Z', '>', 0x1e90 }, - { 'z', '>', 0x1e91 }, - { 'Z', '_', 0x1e94 }, - { 'z', '_', 0x1e95 }, - { 'h', '_', 0x1e96 }, - { 't', ':', 0x1e97 }, - { 'w', '0', 0x1e98 }, - { 'y', '0', 0x1e99 }, - { 'A', '2', 0x1ea2 }, - { 'a', '2', 0x1ea3 }, - { 'E', '2', 0x1eba }, - { 'e', '2', 0x1ebb }, - { 'E', '?', 0x1ebc }, - { 'e', '?', 0x1ebd }, - { 'I', '2', 0x1ec8 }, - { 'i', '2', 0x1ec9 }, - { 'O', '2', 0x1ece }, - { 'o', '2', 0x1ecf }, - { 'U', '2', 0x1ee6 }, - { 'u', '2', 0x1ee7 }, - { 'Y', '!', 0x1ef2 }, - { 'y', '!', 0x1ef3 }, - { 'Y', '2', 0x1ef6 }, - { 'y', '2', 0x1ef7 }, - { 'Y', '?', 0x1ef8 }, - { 'y', '?', 0x1ef9 }, - { ';', '\'', 0x1f00 }, - { ',', '\'', 0x1f01 }, - { ';', '!', 0x1f02 }, - { ',', '!', 0x1f03 }, - { '?', ';', 0x1f04 }, - { '?', ',', 0x1f05 }, - { '!', ':', 0x1f06 }, - { '?', ':', 0x1f07 }, - { '1', 'N', 0x2002 }, - { '1', 'M', 0x2003 }, - { '3', 'M', 0x2004 }, - { '4', 'M', 0x2005 }, - { '6', 'M', 0x2006 }, - { '1', 'T', 0x2009 }, - { '1', 'H', 0x200a }, - { '-', '1', 0x2010 }, - { '-', 'N', 0x2013 }, - { '-', 'M', 0x2014 }, - { '-', '3', 0x2015 }, - { '!', '2', 0x2016 }, - { '=', '2', 0x2017 }, - { '\'', '6', 0x2018 }, - { '\'', '9', 0x2019 }, - { '.', '9', 0x201a }, - { '9', '\'', 0x201b }, - { '"', '6', 0x201c }, - { '"', '9', 0x201d }, - { ':', '9', 0x201e }, - { '9', '"', 0x201f }, - { '/', '-', 0x2020 }, - { '/', '=', 0x2021 }, - { '.', '.', 0x2025 }, - { '%', '0', 0x2030 }, - { '1', '\'', 0x2032 }, - { '2', '\'', 0x2033 }, - { '3', '\'', 0x2034 }, - { '1', '"', 0x2035 }, - { '2', '"', 0x2036 }, - { '3', '"', 0x2037 }, - { 'C', 'a', 0x2038 }, - { '<', '1', 0x2039 }, - { '>', '1', 0x203a }, - { ':', 'X', 0x203b }, - { '\'', '-', 0x203e }, - { '/', 'f', 0x2044 }, - { '0', 'S', 0x2070 }, - { '4', 'S', 0x2074 }, - { '5', 'S', 0x2075 }, - { '6', 'S', 0x2076 }, - { '7', 'S', 0x2077 }, - { '8', 'S', 0x2078 }, - { '9', 'S', 0x2079 }, - { '+', 'S', 0x207a }, - { '-', 'S', 0x207b }, - { '=', 'S', 0x207c }, - { '(', 'S', 0x207d }, - { ')', 'S', 0x207e }, - { 'n', 'S', 0x207f }, - { '0', 's', 0x2080 }, - { '1', 's', 0x2081 }, - { '2', 's', 0x2082 }, - { '3', 's', 0x2083 }, - { '4', 's', 0x2084 }, - { '5', 's', 0x2085 }, - { '6', 's', 0x2086 }, - { '7', 's', 0x2087 }, - { '8', 's', 0x2088 }, - { '9', 's', 0x2089 }, - { '+', 's', 0x208a }, - { '-', 's', 0x208b }, - { '=', 's', 0x208c }, - { '(', 's', 0x208d }, - { ')', 's', 0x208e }, - { 'L', 'i', 0x20a4 }, - { 'P', 't', 0x20a7 }, - { 'W', '=', 0x20a9 }, - { '=', 'e', 0x20ac }, // euro - { 'E', 'u', 0x20ac }, // euro - { 'o', 'C', 0x2103 }, - { 'c', 'o', 0x2105 }, - { 'o', 'F', 0x2109 }, - { 'N', '0', 0x2116 }, - { 'P', 'O', 0x2117 }, - { 'R', 'x', 0x211e }, - { 'S', 'M', 0x2120 }, - { 'T', 'M', 0x2122 }, - { 'O', 'm', 0x2126 }, - { 'A', 'O', 0x212b }, - { '1', '3', 0x2153 }, - { '2', '3', 0x2154 }, - { '1', '5', 0x2155 }, - { '2', '5', 0x2156 }, - { '3', '5', 0x2157 }, - { '4', '5', 0x2158 }, - { '1', '6', 0x2159 }, - { '5', '6', 0x215a }, - { '1', '8', 0x215b }, - { '3', '8', 0x215c }, - { '5', '8', 0x215d }, - { '7', '8', 0x215e }, - { '1', 'R', 0x2160 }, - { '2', 'R', 0x2161 }, - { '3', 'R', 0x2162 }, - { '4', 'R', 0x2163 }, - { '5', 'R', 0x2164 }, - { '6', 'R', 0x2165 }, - { '7', 'R', 0x2166 }, - { '8', 'R', 0x2167 }, - { '9', 'R', 0x2168 }, - { 'a', 'R', 0x2169 }, - { 'b', 'R', 0x216a }, - { 'c', 'R', 0x216b }, - { '1', 'r', 0x2170 }, - { '2', 'r', 0x2171 }, - { '3', 'r', 0x2172 }, - { '4', 'r', 0x2173 }, - { '5', 'r', 0x2174 }, - { '6', 'r', 0x2175 }, - { '7', 'r', 0x2176 }, - { '8', 'r', 0x2177 }, - { '9', 'r', 0x2178 }, - { 'a', 'r', 0x2179 }, - { 'b', 'r', 0x217a }, - { 'c', 'r', 0x217b }, - { '<', '-', 0x2190 }, - { '-', '!', 0x2191 }, - { '-', '>', 0x2192 }, - { '-', 'v', 0x2193 }, - { '<', '>', 0x2194 }, - { 'U', 'D', 0x2195 }, - { '<', '=', 0x21d0 }, - { '=', '>', 0x21d2 }, - { '=', '=', 0x21d4 }, - { 'F', 'A', 0x2200 }, - { 'd', 'P', 0x2202 }, - { 'T', 'E', 0x2203 }, - { '/', '0', 0x2205 }, - { 'D', 'E', 0x2206 }, - { 'N', 'B', 0x2207 }, - { '(', '-', 0x2208 }, - { '-', ')', 0x220b }, - { '*', 'P', 0x220f }, - { '+', 'Z', 0x2211 }, - { '-', '2', 0x2212 }, - { '-', '+', 0x2213 }, - { '*', '-', 0x2217 }, - { 'O', 'b', 0x2218 }, - { 'S', 'b', 0x2219 }, - { 'R', 'T', 0x221a }, - { '0', '(', 0x221d }, - { '0', '0', 0x221e }, - { '-', 'L', 0x221f }, - { '-', 'V', 0x2220 }, - { 'P', 'P', 0x2225 }, - { 'A', 'N', 0x2227 }, - { 'O', 'R', 0x2228 }, - { '(', 'U', 0x2229 }, - { ')', 'U', 0x222a }, - { 'I', 'n', 0x222b }, - { 'D', 'I', 0x222c }, - { 'I', 'o', 0x222e }, - { '.', ':', 0x2234 }, - { ':', '.', 0x2235 }, - { ':', 'R', 0x2236 }, - { ':', ':', 0x2237 }, - { '?', '1', 0x223c }, - { 'C', 'G', 0x223e }, - { '?', '-', 0x2243 }, - { '?', '=', 0x2245 }, - { '?', '2', 0x2248 }, - { '=', '?', 0x224c }, - { 'H', 'I', 0x2253 }, - { '!', '=', 0x2260 }, - { '=', '3', 0x2261 }, - { '=', '<', 0x2264 }, - { '>', '=', 0x2265 }, - { '<', '*', 0x226a }, - { '*', '>', 0x226b }, - { '!', '<', 0x226e }, - { '!', '>', 0x226f }, - { '(', 'C', 0x2282 }, - { ')', 'C', 0x2283 }, - { '(', '_', 0x2286 }, - { ')', '_', 0x2287 }, - { '0', '.', 0x2299 }, - { '0', '2', 0x229a }, - { '-', 'T', 0x22a5 }, - { '.', 'P', 0x22c5 }, - { ':', '3', 0x22ee }, - { '.', '3', 0x22ef }, - { 'E', 'h', 0x2302 }, - { '<', '7', 0x2308 }, - { '>', '7', 0x2309 }, - { '7', '<', 0x230a }, - { '7', '>', 0x230b }, - { 'N', 'I', 0x2310 }, - { '(', 'A', 0x2312 }, - { 'T', 'R', 0x2315 }, - { 'I', 'u', 0x2320 }, - { 'I', 'l', 0x2321 }, - { '<', '/', 0x2329 }, - { '/', '>', 0x232a }, - { 'V', 's', 0x2423 }, - { '1', 'h', 0x2440 }, - { '3', 'h', 0x2441 }, - { '2', 'h', 0x2442 }, - { '4', 'h', 0x2443 }, - { '1', 'j', 0x2446 }, - { '2', 'j', 0x2447 }, - { '3', 'j', 0x2448 }, - { '4', 'j', 0x2449 }, - { '1', '.', 0x2488 }, - { '2', '.', 0x2489 }, - { '3', '.', 0x248a }, - { '4', '.', 0x248b }, - { '5', '.', 0x248c }, - { '6', '.', 0x248d }, - { '7', '.', 0x248e }, - { '8', '.', 0x248f }, - { '9', '.', 0x2490 }, - { 'h', 'h', 0x2500 }, - { 'H', 'H', 0x2501 }, - { 'v', 'v', 0x2502 }, - { 'V', 'V', 0x2503 }, - { '3', '-', 0x2504 }, - { '3', '_', 0x2505 }, - { '3', '!', 0x2506 }, - { '3', '/', 0x2507 }, - { '4', '-', 0x2508 }, - { '4', '_', 0x2509 }, - { '4', '!', 0x250a }, - { '4', '/', 0x250b }, - { 'd', 'r', 0x250c }, - { 'd', 'R', 0x250d }, - { 'D', 'r', 0x250e }, - { 'D', 'R', 0x250f }, - { 'd', 'l', 0x2510 }, - { 'd', 'L', 0x2511 }, - { 'D', 'l', 0x2512 }, - { 'L', 'D', 0x2513 }, - { 'u', 'r', 0x2514 }, - { 'u', 'R', 0x2515 }, - { 'U', 'r', 0x2516 }, - { 'U', 'R', 0x2517 }, - { 'u', 'l', 0x2518 }, - { 'u', 'L', 0x2519 }, - { 'U', 'l', 0x251a }, - { 'U', 'L', 0x251b }, - { 'v', 'r', 0x251c }, - { 'v', 'R', 0x251d }, - { 'V', 'r', 0x2520 }, - { 'V', 'R', 0x2523 }, - { 'v', 'l', 0x2524 }, - { 'v', 'L', 0x2525 }, - { 'V', 'l', 0x2528 }, - { 'V', 'L', 0x252b }, - { 'd', 'h', 0x252c }, - { 'd', 'H', 0x252f }, - { 'D', 'h', 0x2530 }, - { 'D', 'H', 0x2533 }, - { 'u', 'h', 0x2534 }, - { 'u', 'H', 0x2537 }, - { 'U', 'h', 0x2538 }, - { 'U', 'H', 0x253b }, - { 'v', 'h', 0x253c }, - { 'v', 'H', 0x253f }, - { 'V', 'h', 0x2542 }, - { 'V', 'H', 0x254b }, - { 'F', 'D', 0x2571 }, - { 'B', 'D', 0x2572 }, - { 'T', 'B', 0x2580 }, - { 'L', 'B', 0x2584 }, - { 'F', 'B', 0x2588 }, - { 'l', 'B', 0x258c }, - { 'R', 'B', 0x2590 }, - { '.', 'S', 0x2591 }, - { ':', 'S', 0x2592 }, - { '?', 'S', 0x2593 }, - { 'f', 'S', 0x25a0 }, - { 'O', 'S', 0x25a1 }, - { 'R', 'O', 0x25a2 }, - { 'R', 'r', 0x25a3 }, - { 'R', 'F', 0x25a4 }, - { 'R', 'Y', 0x25a5 }, - { 'R', 'H', 0x25a6 }, - { 'R', 'Z', 0x25a7 }, - { 'R', 'K', 0x25a8 }, - { 'R', 'X', 0x25a9 }, - { 's', 'B', 0x25aa }, - { 'S', 'R', 0x25ac }, - { 'O', 'r', 0x25ad }, - { 'U', 'T', 0x25b2 }, - { 'u', 'T', 0x25b3 }, - { 'P', 'R', 0x25b6 }, - { 'T', 'r', 0x25b7 }, - { 'D', 't', 0x25bc }, - { 'd', 'T', 0x25bd }, - { 'P', 'L', 0x25c0 }, - { 'T', 'l', 0x25c1 }, - { 'D', 'b', 0x25c6 }, - { 'D', 'w', 0x25c7 }, - { 'L', 'Z', 0x25ca }, - { '0', 'm', 0x25cb }, - { '0', 'o', 0x25ce }, - { '0', 'M', 0x25cf }, - { '0', 'L', 0x25d0 }, - { '0', 'R', 0x25d1 }, - { 'S', 'n', 0x25d8 }, - { 'I', 'c', 0x25d9 }, - { 'F', 'd', 0x25e2 }, - { 'B', 'd', 0x25e3 }, - { '*', '2', 0x2605 }, - { '*', '1', 0x2606 }, - { '<', 'H', 0x261c }, - { '>', 'H', 0x261e }, - { '0', 'u', 0x263a }, - { '0', 'U', 0x263b }, - { 'S', 'U', 0x263c }, - { 'F', 'm', 0x2640 }, - { 'M', 'l', 0x2642 }, - { 'c', 'S', 0x2660 }, - { 'c', 'H', 0x2661 }, - { 'c', 'D', 0x2662 }, - { 'c', 'C', 0x2663 }, - { 'M', 'd', 0x2669 }, - { 'M', '8', 0x266a }, - { 'M', '2', 0x266b }, - { 'M', 'b', 0x266d }, - { 'M', 'x', 0x266e }, - { 'M', 'X', 0x266f }, - { 'O', 'K', 0x2713 }, - { 'X', 'X', 0x2717 }, - { '-', 'X', 0x2720 }, - { 'I', 'S', 0x3000 }, - { ',', '_', 0x3001 }, - { '.', '_', 0x3002 }, - { '+', '"', 0x3003 }, - { '+', '_', 0x3004 }, - { '*', '_', 0x3005 }, - { ';', '_', 0x3006 }, - { '0', '_', 0x3007 }, - { '<', '+', 0x300a }, - { '>', '+', 0x300b }, - { '<', '\'', 0x300c }, - { '>', '\'', 0x300d }, - { '<', '"', 0x300e }, - { '>', '"', 0x300f }, - { '(', '"', 0x3010 }, - { ')', '"', 0x3011 }, - { '=', 'T', 0x3012 }, - { '=', '_', 0x3013 }, - { '(', '\'', 0x3014 }, - { ')', '\'', 0x3015 }, - { '(', 'I', 0x3016 }, - { ')', 'I', 0x3017 }, - { '-', '?', 0x301c }, - { 'A', '5', 0x3041 }, - { 'a', '5', 0x3042 }, - { 'I', '5', 0x3043 }, - { 'i', '5', 0x3044 }, - { 'U', '5', 0x3045 }, - { 'u', '5', 0x3046 }, - { 'E', '5', 0x3047 }, - { 'e', '5', 0x3048 }, - { 'O', '5', 0x3049 }, - { 'o', '5', 0x304a }, - { 'k', 'a', 0x304b }, - { 'g', 'a', 0x304c }, - { 'k', 'i', 0x304d }, - { 'g', 'i', 0x304e }, - { 'k', 'u', 0x304f }, - { 'g', 'u', 0x3050 }, - { 'k', 'e', 0x3051 }, - { 'g', 'e', 0x3052 }, - { 'k', 'o', 0x3053 }, - { 'g', 'o', 0x3054 }, - { 's', 'a', 0x3055 }, - { 'z', 'a', 0x3056 }, - { 's', 'i', 0x3057 }, - { 'z', 'i', 0x3058 }, - { 's', 'u', 0x3059 }, - { 'z', 'u', 0x305a }, - { 's', 'e', 0x305b }, - { 'z', 'e', 0x305c }, - { 's', 'o', 0x305d }, - { 'z', 'o', 0x305e }, - { 't', 'a', 0x305f }, - { 'd', 'a', 0x3060 }, - { 't', 'i', 0x3061 }, - { 'd', 'i', 0x3062 }, - { 't', 'U', 0x3063 }, - { 't', 'u', 0x3064 }, - { 'd', 'u', 0x3065 }, - { 't', 'e', 0x3066 }, - { 'd', 'e', 0x3067 }, - { 't', 'o', 0x3068 }, - { 'd', 'o', 0x3069 }, - { 'n', 'a', 0x306a }, - { 'n', 'i', 0x306b }, - { 'n', 'u', 0x306c }, - { 'n', 'e', 0x306d }, - { 'n', 'o', 0x306e }, - { 'h', 'a', 0x306f }, - { 'b', 'a', 0x3070 }, - { 'p', 'a', 0x3071 }, - { 'h', 'i', 0x3072 }, - { 'b', 'i', 0x3073 }, - { 'p', 'i', 0x3074 }, - { 'h', 'u', 0x3075 }, - { 'b', 'u', 0x3076 }, - { 'p', 'u', 0x3077 }, - { 'h', 'e', 0x3078 }, - { 'b', 'e', 0x3079 }, - { 'p', 'e', 0x307a }, - { 'h', 'o', 0x307b }, - { 'b', 'o', 0x307c }, - { 'p', 'o', 0x307d }, - { 'm', 'a', 0x307e }, - { 'm', 'i', 0x307f }, - { 'm', 'u', 0x3080 }, - { 'm', 'e', 0x3081 }, - { 'm', 'o', 0x3082 }, - { 'y', 'A', 0x3083 }, - { 'y', 'a', 0x3084 }, - { 'y', 'U', 0x3085 }, - { 'y', 'u', 0x3086 }, - { 'y', 'O', 0x3087 }, - { 'y', 'o', 0x3088 }, - { 'r', 'a', 0x3089 }, - { 'r', 'i', 0x308a }, - { 'r', 'u', 0x308b }, - { 'r', 'e', 0x308c }, - { 'r', 'o', 0x308d }, - { 'w', 'A', 0x308e }, - { 'w', 'a', 0x308f }, - { 'w', 'i', 0x3090 }, - { 'w', 'e', 0x3091 }, - { 'w', 'o', 0x3092 }, - { 'n', '5', 0x3093 }, - { 'v', 'u', 0x3094 }, - { '"', '5', 0x309b }, - { '0', '5', 0x309c }, - { '*', '5', 0x309d }, - { '+', '5', 0x309e }, - { 'a', '6', 0x30a1 }, - { 'A', '6', 0x30a2 }, - { 'i', '6', 0x30a3 }, - { 'I', '6', 0x30a4 }, - { 'u', '6', 0x30a5 }, - { 'U', '6', 0x30a6 }, - { 'e', '6', 0x30a7 }, - { 'E', '6', 0x30a8 }, - { 'o', '6', 0x30a9 }, - { 'O', '6', 0x30aa }, - { 'K', 'a', 0x30ab }, - { 'G', 'a', 0x30ac }, - { 'K', 'i', 0x30ad }, - { 'G', 'i', 0x30ae }, - { 'K', 'u', 0x30af }, - { 'G', 'u', 0x30b0 }, - { 'K', 'e', 0x30b1 }, - { 'G', 'e', 0x30b2 }, - { 'K', 'o', 0x30b3 }, - { 'G', 'o', 0x30b4 }, - { 'S', 'a', 0x30b5 }, - { 'Z', 'a', 0x30b6 }, - { 'S', 'i', 0x30b7 }, - { 'Z', 'i', 0x30b8 }, - { 'S', 'u', 0x30b9 }, - { 'Z', 'u', 0x30ba }, - { 'S', 'e', 0x30bb }, - { 'Z', 'e', 0x30bc }, - { 'S', 'o', 0x30bd }, - { 'Z', 'o', 0x30be }, - { 'T', 'a', 0x30bf }, - { 'D', 'a', 0x30c0 }, - { 'T', 'i', 0x30c1 }, - { 'D', 'i', 0x30c2 }, - { 'T', 'U', 0x30c3 }, - { 'T', 'u', 0x30c4 }, - { 'D', 'u', 0x30c5 }, - { 'T', 'e', 0x30c6 }, - { 'D', 'e', 0x30c7 }, - { 'T', 'o', 0x30c8 }, - { 'D', 'o', 0x30c9 }, - { 'N', 'a', 0x30ca }, - { 'N', 'i', 0x30cb }, - { 'N', 'u', 0x30cc }, - { 'N', 'e', 0x30cd }, - { 'N', 'o', 0x30ce }, - { 'H', 'a', 0x30cf }, - { 'B', 'a', 0x30d0 }, - { 'P', 'a', 0x30d1 }, - { 'H', 'i', 0x30d2 }, - { 'B', 'i', 0x30d3 }, - { 'P', 'i', 0x30d4 }, - { 'H', 'u', 0x30d5 }, - { 'B', 'u', 0x30d6 }, - { 'P', 'u', 0x30d7 }, - { 'H', 'e', 0x30d8 }, - { 'B', 'e', 0x30d9 }, - { 'P', 'e', 0x30da }, - { 'H', 'o', 0x30db }, - { 'B', 'o', 0x30dc }, - { 'P', 'o', 0x30dd }, - { 'M', 'a', 0x30de }, - { 'M', 'i', 0x30df }, - { 'M', 'u', 0x30e0 }, - { 'M', 'e', 0x30e1 }, - { 'M', 'o', 0x30e2 }, - { 'Y', 'A', 0x30e3 }, - { 'Y', 'a', 0x30e4 }, - { 'Y', 'U', 0x30e5 }, - { 'Y', 'u', 0x30e6 }, - { 'Y', 'O', 0x30e7 }, - { 'Y', 'o', 0x30e8 }, - { 'R', 'a', 0x30e9 }, - { 'R', 'i', 0x30ea }, - { 'R', 'u', 0x30eb }, - { 'R', 'e', 0x30ec }, - { 'R', 'o', 0x30ed }, - { 'W', 'A', 0x30ee }, - { 'W', 'a', 0x30ef }, - { 'W', 'i', 0x30f0 }, - { 'W', 'e', 0x30f1 }, - { 'W', 'o', 0x30f2 }, - { 'N', '6', 0x30f3 }, - { 'V', 'u', 0x30f4 }, - { 'K', 'A', 0x30f5 }, - { 'K', 'E', 0x30f6 }, - { 'V', 'a', 0x30f7 }, - { 'V', 'i', 0x30f8 }, - { 'V', 'e', 0x30f9 }, - { 'V', 'o', 0x30fa }, - { '.', '6', 0x30fb }, - { '-', '6', 0x30fc }, - { '*', '6', 0x30fd }, - { '+', '6', 0x30fe }, - { 'b', '4', 0x3105 }, - { 'p', '4', 0x3106 }, - { 'm', '4', 0x3107 }, - { 'f', '4', 0x3108 }, - { 'd', '4', 0x3109 }, - { 't', '4', 0x310a }, - { 'n', '4', 0x310b }, - { 'l', '4', 0x310c }, - { 'g', '4', 0x310d }, - { 'k', '4', 0x310e }, - { 'h', '4', 0x310f }, - { 'j', '4', 0x3110 }, - { 'q', '4', 0x3111 }, - { 'x', '4', 0x3112 }, - { 'z', 'h', 0x3113 }, - { 'c', 'h', 0x3114 }, - { 's', 'h', 0x3115 }, - { 'r', '4', 0x3116 }, - { 'z', '4', 0x3117 }, - { 'c', '4', 0x3118 }, - { 's', '4', 0x3119 }, - { 'a', '4', 0x311a }, - { 'o', '4', 0x311b }, - { 'e', '4', 0x311c }, - { 'a', 'i', 0x311e }, - { 'e', 'i', 0x311f }, - { 'a', 'u', 0x3120 }, - { 'o', 'u', 0x3121 }, - { 'a', 'n', 0x3122 }, - { 'e', 'n', 0x3123 }, - { 'a', 'N', 0x3124 }, - { 'e', 'N', 0x3125 }, - { 'e', 'r', 0x3126 }, - { 'i', '4', 0x3127 }, - { 'u', '4', 0x3128 }, - { 'i', 'u', 0x3129 }, - { 'v', '4', 0x312a }, - { 'n', 'G', 0x312b }, - { 'g', 'n', 0x312c }, - { '1', 'c', 0x3220 }, - { '2', 'c', 0x3221 }, - { '3', 'c', 0x3222 }, - { '4', 'c', 0x3223 }, - { '5', 'c', 0x3224 }, - { '6', 'c', 0x3225 }, - { '7', 'c', 0x3226 }, - { '8', 'c', 0x3227 }, - { '9', 'c', 0x3228 }, - - // code points 0xe000 - 0xefff excluded, they have no assigned - // characters, only used in proposals. - { 'f', 'f', 0xfb00 }, - { 'f', 'i', 0xfb01 }, - { 'f', 'l', 0xfb02 }, - { 'f', 't', 0xfb05 }, - { 's', 't', 0xfb06 }, - - // Vim 5.x compatible digraphs that don't conflict with the above - { '~', '!', 161 }, // - { 'c', '|', 162 }, // - { '$', '$', 163 }, // - { 'o', 'x', 164 }, // - currency symbol in ISO 8859-1 - { 'Y', '-', 165 }, // - { '|', '|', 166 }, // - { 'c', 'O', 169 }, // - { '-', ',', 172 }, // - { '-', '=', 175 }, // - { '~', 'o', 176 }, // - { '2', '2', 178 }, // - { '3', '3', 179 }, // - { 'p', 'p', 182 }, // - { '~', '.', 183 }, // - { '1', '1', 185 }, // - { '~', '?', 191 }, // - { 'A', '`', 192 }, // - { 'A', '^', 194 }, // - { 'A', '~', 195 }, // - { 'A', '"', 196 }, // - { 'A', '@', 197 }, // - { 'E', '`', 200 }, // - { 'E', '^', 202 }, // - { 'E', '"', 203 }, // - { 'I', '`', 204 }, // - { 'I', '^', 206 }, // - { 'I', '"', 207 }, // - { 'N', '~', 209 }, // - { 'O', '`', 210 }, // - { 'O', '^', 212 }, // - { 'O', '~', 213 }, // - { '/', '\\', 215 }, // - multiplication symbol in ISO 8859-1 - { 'U', '`', 217 }, // - { 'U', '^', 219 }, // - { 'I', 'p', 222 }, // - { 'a', '`', 224 }, // - { 'a', '^', 226 }, // - { 'a', '~', 227 }, // - { 'a', '"', 228 }, // - { 'a', '@', 229 }, // - { 'e', '`', 232 }, // - { 'e', '^', 234 }, // - { 'e', '"', 235 }, // - { 'i', '`', 236 }, // - { 'i', '^', 238 }, // - { 'n', '~', 241 }, // - { 'o', '`', 242 }, // - { 'o', '^', 244 }, // - { 'o', '~', 245 }, // - { 'u', '`', 249 }, // - { 'u', '^', 251 }, // - { 'y', '"', 255 }, // x XX - - { NUL, NUL, NUL } -}; - -/// handle digraphs after typing a character -/// -/// @param c -/// -/// @return The digraph. -int do_digraph(int c) -{ - static int backspaced; // character before K_BS - static int lastchar; // last typed character - - if (c == -1) { // init values - backspaced = -1; - } else if (p_dg) { - if (backspaced >= 0) { - c = getdigraph(backspaced, c, FALSE); - } - backspaced = -1; - - if (((c == K_BS) || (c == Ctrl_H)) && (lastchar >= 0)) { - backspaced = lastchar; - } - } - lastchar = c; - return c; -} - -/// Get a digraph. Used after typing CTRL-K on the command line or in normal -/// mode. -/// -/// @param cmdline TRUE when called from the cmdline -/// -/// @returns composed character, or NUL when ESC was used. -int get_digraph(int cmdline) -{ - int cc; - no_mapping++; - allow_keys++; - int c = plain_vgetc(); - no_mapping--; - allow_keys--; - - if (c != ESC) { - // ESC cancels CTRL-K - if (IS_SPECIAL(c)) { - // insert special key code - return c; - } - - if (cmdline) { - if ((char2cells(c) == 1) && (cmdline_star == 0)) { - putcmdline(c, TRUE); - } - } else { - add_to_showcmd(c); - } - no_mapping++; - allow_keys++; - cc = plain_vgetc(); - no_mapping--; - allow_keys--; - - if (cc != ESC) { - // ESC cancels CTRL-K - return getdigraph(c, cc, TRUE); - } - } - return NUL; -} - -/// Lookup the pair "char1", "char2" in the digraph tables. -/// -/// @param char1 -/// @param char2 -/// @param meta_char -/// -/// @return If no match, return "char2". If "meta_char" is TRUE and "char1" -// is a space, return "char2" | 0x80. -static int getexactdigraph(int char1, int char2, int meta_char) -{ - int retval = 0; - - if (IS_SPECIAL(char1) || IS_SPECIAL(char2)) { - return char2; - } - - // Search user digraphs first. - digr_T *dp = (digr_T *)user_digraphs.ga_data; - int i; - for (i = 0; i < user_digraphs.ga_len; ++i) { - if (((int) dp->char1 == char1) && ((int) dp->char2 == char2)) { - retval = dp->result; - break; - } - ++dp; - } - - // Search default digraphs. - if (retval == 0) { - dp = digraphdefault; - - for (i = 0; dp->char1 != 0; ++i) { - if (((int) dp->char1 == char1) && ((int) dp->char2 == char2)) { - retval = dp->result; - break; - } - ++dp; - } - } - - if ((retval != 0) && !enc_utf8) { - char_u buf[6], *to; - vimconv_T vc; - - // Convert the Unicode digraph to 'encoding'. - i = utf_char2bytes(retval, buf); - retval = 0; - vc.vc_type = CONV_NONE; - - if (convert_setup(&vc, (char_u *)"utf-8", p_enc) == OK) { - vc.vc_fail = TRUE; - to = string_convert(&vc, buf, &i); - - if (to != NULL) { - retval = (*mb_ptr2char)(to); - free(to); - } - (void)convert_setup(&vc, NULL, NULL); - } - } - - // Ignore multi-byte characters when not in multi-byte mode. - if (!has_mbyte && (retval > 0xff)) { - retval = 0; - } - - if (retval == 0) { - // digraph deleted or not found - if ((char1 == ' ') && meta_char) { - // --> meta-char - return char2 | 0x80; - } - return char2; - } - return retval; -} - -/// Get digraph. -/// Allow for both char1-char2 and char2-char1 -/// -/// @param char1 -/// @param char2 -/// @param meta_char -/// -/// @return The digraph. -int getdigraph(int char1, int char2, int meta_char) -{ - int retval; - - if (((retval = getexactdigraph(char1, char2, meta_char)) == char2) - && (char1 != char2) - && ((retval = getexactdigraph(char2, char1, meta_char)) == char1)) { - return char2; - } - return retval; -} - -/// Add the digraphs in the argument to the digraph table. -/// format: {c1}{c2} char {c1}{c2} char ... -/// -/// @param str -void putdigraph(char_u *str) -{ - int char1, char2, n; - int i; - digr_T *dp; - - while (*str != NUL) { - str = skipwhite(str); - - if (*str == NUL) { - return; - } - char1 = *str++; - char2 = *str++; - - if (char2 == 0) { - EMSG(_(e_invarg)); - return; - } - - if ((char1 == ESC) || (char2 == ESC)) { - EMSG(_("E104: Escape not allowed in digraph")); - return; - } - str = skipwhite(str); - - if (!VIM_ISDIGIT(*str)) { - EMSG(_(e_number_exp)); - return; - } - n = getdigits(&str); - - // If the digraph already exists, replace the result. - dp = (digr_T *)user_digraphs.ga_data; - - for (i = 0; i < user_digraphs.ga_len; ++i) { - if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) { - dp->result = n; - break; - } - ++dp; - } - - // Add a new digraph to the table. - if (i == user_digraphs.ga_len) { - ga_grow(&user_digraphs, 1); - dp = (digr_T *)user_digraphs.ga_data + user_digraphs.ga_len; - dp->char1 = char1; - dp->char2 = char2; - dp->result = n; - ++user_digraphs.ga_len; - } - } -} - -void listdigraphs(void) -{ - int i; - digr_T *dp; - - msg_putchar('\n'); - - dp = digraphdefault; - - for (i = 0; dp->char1 != NUL && !got_int; ++i) { - digr_T tmp; - - // May need to convert the result to 'encoding'. - tmp.char1 = dp->char1; - tmp.char2 = dp->char2; - tmp.result = getexactdigraph(tmp.char1, tmp.char2, FALSE); - - if ((tmp.result != 0) - && (tmp.result != tmp.char2) - && (has_mbyte || (tmp.result <= 255))) { - printdigraph(&tmp); - } - dp++; - ui_breakcheck(); - } - - dp = (digr_T *)user_digraphs.ga_data; - for (i = 0; i < user_digraphs.ga_len && !got_int; ++i) { - printdigraph(dp); - ui_breakcheck(); - dp++; - } - // clear screen, because some digraphs may be wrong, in which case we messed - // up ScreenLines - must_redraw = CLEAR; -} - -static void printdigraph(digr_T *dp) -{ - char_u buf[30]; - char_u *p; - - int list_width; - - if ((dy_flags & DY_UHEX) || has_mbyte) { - list_width = 13; - } else { - list_width = 11; - } - - if (dp->result != 0) { - if (msg_col > Columns - list_width) { - msg_putchar('\n'); - } - - if (msg_col) { - while (msg_col % list_width != 0) { - msg_putchar(' '); - } - } - - p = buf; - *p++ = dp->char1; - *p++ = dp->char2; - *p++ = ' '; - - if (has_mbyte) { - // add a space to draw a composing char on - if (enc_utf8 && utf_iscomposing(dp->result)) { - *p++ = ' '; - } - p += (*mb_char2bytes)(dp->result, p); - } else { - *p++ = (char_u)dp->result; - } - - if (char2cells(dp->result) == 1) { - *p++ = ' '; - } - vim_snprintf((char *)p, sizeof(buf) - (p - buf), " %3d", dp->result); - msg_outtrans(buf); - } -} - -/// structure used for b_kmap_ga.ga_data -typedef struct { - char_u *from; - char_u *to; -} kmap_T; - -#define KMAP_MAXLEN 20 // maximum length of "from" or "to" - -static void keymap_unload(void); - -/// Set up key mapping tables for the 'keymap' option. -/// -/// @return NULL if OK, an error message for failure. This only needs to be -/// used when setting the option, not later when the value has already -/// been checked. -char_u* keymap_init(void) -{ - curbuf->b_kmap_state &= ~KEYMAP_INIT; - - if (*curbuf->b_p_keymap == NUL) { - // Stop any active keymap and clear the table. Also remove - // b:keymap_name, as no keymap is active now. - keymap_unload(); - do_cmdline_cmd((char_u *)"unlet! b:keymap_name"); - } else { - char_u *buf; - size_t buflen; - - // Source the keymap file. It will contain a ":loadkeymap" command - // which will call ex_loadkeymap() below. - buflen = STRLEN(curbuf->b_p_keymap) + STRLEN(p_enc) + 14; - buf = alloc((unsigned)buflen); - - if (buf == NULL) { - return e_outofmem; - } - - // try finding "keymap/'keymap'_'encoding'.vim" in 'runtimepath' - vim_snprintf((char *)buf, buflen, "keymap/%s_%s.vim", - curbuf->b_p_keymap, p_enc); - - if (source_runtime(buf, FALSE) == FAIL) { - // try finding "keymap/'keymap'.vim" in 'runtimepath' - vim_snprintf((char *)buf, buflen, "keymap/%s.vim", - curbuf->b_p_keymap); - - if (source_runtime(buf, FALSE) == FAIL) { - free(buf); - return (char_u *)N_("E544: Keymap file not found"); - } - } - free(buf); - } - - return NULL; -} - -/// ":loadkeymap" command: load the following lines as the keymap. -/// -/// @param eap -void ex_loadkeymap(exarg_T *eap) -{ - char_u *line; - char_u *p; - char_u *s; - kmap_T *kp; - -#define KMAP_LLEN 200 // max length of "to" and "from" together - char_u buf[KMAP_LLEN + 11]; - int i; - char_u *save_cpo = p_cpo; - - if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { - EMSG(_("E105: Using :loadkeymap not in a sourced file")); - return; - } - - // Stop any active keymap and clear the table. - keymap_unload(); - - curbuf->b_kmap_state = 0; - ga_init(&curbuf->b_kmap_ga, (int)sizeof(kmap_T), 20); - - // Set 'cpoptions' to "C" to avoid line continuation. - p_cpo = (char_u *)"C"; - - // Get each line of the sourced file, break at the end. - for (;;) { - line = eap->getline(0, eap->cookie, 0); - - if (line == NULL) { - break; - } - - p = skipwhite(line); - - if ((*p != '"') && (*p != NUL)) { - ga_grow(&curbuf->b_kmap_ga, 1); - kp = (kmap_T *)curbuf->b_kmap_ga.ga_data + curbuf->b_kmap_ga.ga_len; - s = skiptowhite(p); - kp->from = vim_strnsave(p, (int)(s - p)); - p = skipwhite(s); - s = skiptowhite(p); - kp->to = vim_strnsave(p, (int)(s - p)); - - if ((kp->from == NULL) - || (kp->to == NULL) - || (STRLEN(kp->from) + STRLEN(kp->to) >= KMAP_LLEN) - || (*kp->from == NUL) - || (*kp->to == NUL)) { - if ((kp->to != NULL) && (*kp->to == NUL)) { - EMSG(_("E791: Empty keymap entry")); - } - free(kp->from); - free(kp->to); - } else { - ++curbuf->b_kmap_ga.ga_len; - } - } - free(line); - } - - // setup ":lnoremap" to map the keys - for (i = 0; i < curbuf->b_kmap_ga.ga_len; ++i) { - vim_snprintf((char *)buf, sizeof(buf), " %s %s", - ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from, - ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to); - (void)do_map(2, buf, LANGMAP, FALSE); - } - - p_cpo = save_cpo; - - curbuf->b_kmap_state |= KEYMAP_LOADED; - status_redraw_curbuf(); -} - -/// Stop using 'keymap'. -static void keymap_unload(void) -{ - char_u buf[KMAP_MAXLEN + 10]; - int i; - char_u *save_cpo = p_cpo; - kmap_T *kp; - - if (!(curbuf->b_kmap_state & KEYMAP_LOADED)) { - return; - } - - // Set 'cpoptions' to "C" to avoid line continuation. - p_cpo = (char_u *)"C"; - - // clear the ":lmap"s - kp = (kmap_T *)curbuf->b_kmap_ga.ga_data; - - for (i = 0; i < curbuf->b_kmap_ga.ga_len; ++i) { - vim_snprintf((char *)buf, sizeof(buf), " %s", kp[i].from); - (void)do_map(1, buf, LANGMAP, FALSE); - free(kp[i].from); - free(kp[i].to); - } - - p_cpo = save_cpo; - - ga_clear(&curbuf->b_kmap_ga); - curbuf->b_kmap_state &= ~KEYMAP_LOADED; - status_redraw_curbuf(); -} diff --git a/src/digraph.h b/src/digraph.h deleted file mode 100644 index 796066bb68..0000000000 --- a/src/digraph.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef NEOVIM_DIGRAPH_H -#define NEOVIM_DIGRAPH_H - -int do_digraph(int c); -int get_digraph(int cmdline); -int getdigraph(int char1, int char2, int meta_char); -void putdigraph(char_u *str); -void listdigraphs(void); -char_u *keymap_init(void); -void ex_loadkeymap(exarg_T *eap); - -#endif // NEOVIM_DIGRAPH_H diff --git a/src/edit.c b/src/edit.c deleted file mode 100644 index 146a8bba70..0000000000 --- a/src/edit.c +++ /dev/null @@ -1,8444 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * edit.c: functions for Insert mode - */ - -#include - -#include "vim.h" -#include "edit.h" -#include "buffer.h" -#include "charset.h" -#include "digraph.h" -#include "eval.h" -#include "ex_docmd.h" -#include "ex_getln.h" -#include "farsi.h" -#include "fileio.h" -#include "fold.h" -#include "getchar.h" -#include "indent.h" -#include "indent_c.h" -#include "main.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "keymap.h" -#include "move.h" -#include "normal.h" -#include "ops.h" -#include "option.h" -#include "path.h" -#include "popupmnu.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "search.h" -#include "spell.h" -#include "syntax.h" -#include "tag.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "window.h" -#include "os/event.h" - -/* - * definitions used for CTRL-X submode - */ -#define CTRL_X_WANT_IDENT 0x100 - -#define CTRL_X_NOT_DEFINED_YET 1 -#define CTRL_X_SCROLL 2 -#define CTRL_X_WHOLE_LINE 3 -#define CTRL_X_FILES 4 -#define CTRL_X_TAGS (5 + CTRL_X_WANT_IDENT) -#define CTRL_X_PATH_PATTERNS (6 + CTRL_X_WANT_IDENT) -#define CTRL_X_PATH_DEFINES (7 + CTRL_X_WANT_IDENT) -#define CTRL_X_FINISHED 8 -#define CTRL_X_DICTIONARY (9 + CTRL_X_WANT_IDENT) -#define CTRL_X_THESAURUS (10 + CTRL_X_WANT_IDENT) -#define CTRL_X_CMDLINE 11 -#define CTRL_X_FUNCTION 12 -#define CTRL_X_OMNI 13 -#define CTRL_X_SPELL 14 -#define CTRL_X_LOCAL_MSG 15 /* only used in "ctrl_x_msgs" */ - -#define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] - -static char *ctrl_x_msgs[] = -{ - N_(" Keyword completion (^N^P)"), /* ctrl_x_mode == 0, ^P/^N compl. */ - N_(" ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"), - NULL, - N_(" Whole line completion (^L^N^P)"), - N_(" File name completion (^F^N^P)"), - N_(" Tag completion (^]^N^P)"), - N_(" Path pattern completion (^N^P)"), - N_(" Definition completion (^D^N^P)"), - NULL, - N_(" Dictionary completion (^K^N^P)"), - N_(" Thesaurus completion (^T^N^P)"), - N_(" Command-line completion (^V^N^P)"), - N_(" User defined completion (^U^N^P)"), - N_(" Omni completion (^O^N^P)"), - N_(" Spelling suggestion (s^N^P)"), - N_(" Keyword Local completion (^N^P)"), -}; - -static char e_hitend[] = N_("Hit end of paragraph"); -static char e_complwin[] = N_("E839: Completion function changed window"); -static char e_compldel[] = N_("E840: Completion function deleted text"); - -/* - * Structure used to store one match for insert completion. - */ -typedef struct compl_S compl_T; -struct compl_S { - compl_T *cp_next; - compl_T *cp_prev; - char_u *cp_str; /* matched text */ - char cp_icase; /* TRUE or FALSE: ignore case */ - char_u *(cp_text[CPT_COUNT]); /* text for the menu */ - char_u *cp_fname; /* file containing the match, allocated when - * cp_flags has FREE_FNAME */ - int cp_flags; /* ORIGINAL_TEXT, CONT_S_IPOS or FREE_FNAME */ - int cp_number; /* sequence number */ -}; - -#define ORIGINAL_TEXT (1) /* the original text when the expansion begun */ -#define FREE_FNAME (2) - -/* - * All the current matches are stored in a list. - * "compl_first_match" points to the start of the list. - * "compl_curr_match" points to the currently selected entry. - * "compl_shown_match" is different from compl_curr_match during - * ins_compl_get_exp(). - */ -static compl_T *compl_first_match = NULL; -static compl_T *compl_curr_match = NULL; -static compl_T *compl_shown_match = NULL; - -/* After using a cursor key selects a match in the popup menu, - * otherwise it inserts a line break. */ -static int compl_enter_selects = FALSE; - -/* When "compl_leader" is not NULL only matches that start with this string - * are used. */ -static char_u *compl_leader = NULL; - -static int compl_get_longest = FALSE; /* put longest common string - in compl_leader */ - -static int compl_used_match; /* Selected one of the matches. When - FALSE the match was edited or using - the longest common string. */ - -static int compl_was_interrupted = FALSE; /* didn't finish finding - completions. */ - -static int compl_restarting = FALSE; /* don't insert match */ - -/* When the first completion is done "compl_started" is set. When it's - * FALSE the word to be completed must be located. */ -static int compl_started = FALSE; - -/* Set when doing something for completion that may call edit() recursively, - * which is not allowed. */ -static int compl_busy = FALSE; - -static int compl_matches = 0; -static char_u *compl_pattern = NULL; -static int compl_direction = FORWARD; -static int compl_shows_dir = FORWARD; -static int compl_pending = 0; /* > 1 for postponed CTRL-N */ -static pos_T compl_startpos; -static colnr_T compl_col = 0; /* column where the text starts - * that is being completed */ -static char_u *compl_orig_text = NULL; /* text as it was before - * completion started */ -static int compl_cont_mode = 0; -static expand_T compl_xp; - -static int compl_opt_refresh_always = FALSE; - -static void ins_ctrl_x(void); -static int has_compl_option(int dict_opt); -static int ins_compl_accept_char(int c); -static int ins_compl_add(char_u *str, int len, int icase, char_u *fname, - char_u **cptext, int cdir, int flags, - int adup); -static int ins_compl_equal(compl_T *match, char_u *str, int len); -static void ins_compl_longest_match(compl_T *match); -static void ins_compl_add_matches(int num_matches, char_u **matches, - int icase); -static int ins_compl_make_cyclic(void); -static void ins_compl_upd_pum(void); -static void ins_compl_del_pum(void); -static int pum_wanted(void); -static int pum_enough_matches(void); -static void ins_compl_dictionaries(char_u *dict, char_u *pat, int flags, - int thesaurus); -static void ins_compl_files(int count, char_u **files, int thesaurus, - int flags, regmatch_T *regmatch, char_u * - buf, - int *dir); -static char_u *find_line_end(char_u *ptr); -static void ins_compl_free(void); -static void ins_compl_clear(void); -static int ins_compl_bs(void); -static int ins_compl_need_restart(void); -static void ins_compl_new_leader(void); -static void ins_compl_addleader(int c); -static int ins_compl_len(void); -static void ins_compl_restart(void); -static void ins_compl_set_original_text(char_u *str); -static void ins_compl_addfrommatch(void); -static int ins_compl_prep(int c); -static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg); -static buf_T *ins_compl_next_buf(buf_T *buf, int flag); -static void ins_compl_add_list(list_T *list); -static void ins_compl_add_dict(dict_T *dict); -static int ins_compl_get_exp(pos_T *ini); -static void ins_compl_delete(void); -static void ins_compl_insert(void); -static int ins_compl_next(int allow_get_expansion, int count, - int insert_match); -static int ins_compl_key2dir(int c); -static int ins_compl_pum_key(int c); -static int ins_compl_key2count(int c); -static int ins_compl_use_match(int c); -static int ins_complete(int c); -static unsigned quote_meta(char_u *dest, char_u *str, int len); - -#define BACKSPACE_CHAR 1 -#define BACKSPACE_WORD 2 -#define BACKSPACE_WORD_NOT_SPACE 3 -#define BACKSPACE_LINE 4 - -static void ins_redraw(int ready); -static void ins_ctrl_v(void); -static void undisplay_dollar(void); -static void insert_special(int, int, int); -static void internal_format(int textwidth, int second_indent, int flags, - int format_only, - int c); -static void check_auto_format(int); -static void redo_literal(int c); -static void start_arrow(pos_T *end_insert_pos); -static void check_spell_redraw(void); -static void spell_back_to_badword(void); -static int spell_bad_len = 0; /* length of located bad word */ -static void stop_insert(pos_T *end_insert_pos, int esc, int nomove); -static int echeck_abbr(int); -static int replace_pop(void); -static void replace_join(int off); -static void replace_pop_ins(void); -static void mb_replace_pop_ins(int cc); -static void replace_flush(void); -static void replace_do_bs(int limit_col); -static int del_char_after_col(int limit_col); -static int cindent_on(void); -static void ins_reg(void); -static void ins_ctrl_g(void); -static void ins_ctrl_hat(void); -static int ins_esc(long *count, int cmdchar, int nomove); -static void ins_ctrl_(void); -static int ins_start_select(int c); -static void ins_insert(int replaceState); -static void ins_ctrl_o(void); -static void ins_shift(int c, int lastc); -static void ins_del(void); -static int ins_bs(int c, int mode, int *inserted_space_p); -static void ins_mouse(int c); -static void ins_mousescroll(int dir); -static void ins_left(void); -static void ins_home(int c); -static void ins_end(int c); -static void ins_s_left(void); -static void ins_right(void); -static void ins_s_right(void); -static void ins_up(int startcol); -static void ins_pageup(void); -static void ins_down(int startcol); -static void ins_pagedown(void); -static int ins_tab(void); -static int ins_eol(int c); -static int ins_digraph(void); -static int ins_ctrl_ey(int tc); -static void ins_try_si(int c); -static colnr_T get_nolist_virtcol(void); -static char_u *do_insert_char_pre(int c); - -static colnr_T Insstart_textlen; /* length of line when insert started */ -static colnr_T Insstart_blank_vcol; /* vcol for first inserted blank */ -static bool update_Insstart_orig = true; /* set Insstart_orig to Insstart */ - -static char_u *last_insert = NULL; /* the text of the previous insert, - K_SPECIAL and CSI are escaped */ -static int last_insert_skip; /* nr of chars in front of previous insert */ -static int new_insert_skip; /* nr of chars in front of current insert */ -static int did_restart_edit; /* "restart_edit" when calling edit() */ - -static int can_cindent; /* may do cindenting on this line */ - -static int old_indent = 0; /* for ^^D command in insert mode */ - -static int revins_on; /* reverse insert mode on */ -static int revins_chars; /* how much to skip after edit */ -static int revins_legal; /* was the last char 'legal'? */ -static int revins_scol; /* start column of revins session */ - -static int ins_need_undo; /* call u_save() before inserting a - char. Set when edit() is called. - after that arrow_used is used. */ - -static int did_add_space = FALSE; /* auto_format() added an extra space - under the cursor */ - -/* - * edit(): Start inserting text. - * - * "cmdchar" can be: - * 'i' normal insert command - * 'a' normal append command - * 'R' replace command - * 'r' "r" command: insert one . Note: count can be > 1, for redo, - * but still only one is inserted. The is not used for redo. - * 'g' "gI" command. - * 'V' "gR" command for Virtual Replace mode. - * 'v' "gr" command for single character Virtual Replace mode. - * - * This function is not called recursively. For CTRL-O commands, it returns - * and lets the caller handle the Normal-mode command. - * - * Return TRUE if a CTRL-O command caused the return (insert mode pending). - */ -int -edit ( - int cmdchar, - int startln, /* if set, insert at start of line */ - long count -) -{ - int c = 0; - char_u *ptr; - int lastc; - int mincol; - static linenr_T o_lnum = 0; - int i; - int did_backspace = TRUE; /* previous char was backspace */ - int line_is_white = FALSE; /* line is empty before insert */ - linenr_T old_topline = 0; /* topline before insertion */ - int old_topfill = -1; - int inserted_space = FALSE; /* just inserted a space */ - int replaceState = REPLACE; - int nomove = FALSE; /* don't move cursor on return */ - - /* Remember whether editing was restarted after CTRL-O. */ - did_restart_edit = restart_edit; - - /* sleep before redrawing, needed for "CTRL-O :" that results in an - * error message */ - check_for_delay(TRUE); - - // set Insstart_orig to Insstart - update_Insstart_orig = true; - -#ifdef HAVE_SANDBOX - /* Don't allow inserting in the sandbox. */ - if (sandbox != 0) { - EMSG(_(e_sandbox)); - return FALSE; - } -#endif - /* Don't allow changes in the buffer while editing the cmdline. The - * caller of getcmdline() may get confused. */ - if (textlock != 0) { - EMSG(_(e_secure)); - return FALSE; - } - - /* Don't allow recursive insert mode when busy with completion. */ - if (compl_started || compl_busy || pum_visible()) { - EMSG(_(e_secure)); - return FALSE; - } - ins_compl_clear(); /* clear stuff for CTRL-X mode */ - - /* - * Trigger InsertEnter autocommands. Do not do this for "r" or "grx". - */ - if (cmdchar != 'r' && cmdchar != 'v') { - pos_T save_cursor = curwin->w_cursor; - - if (cmdchar == 'R') - ptr = (char_u *)"r"; - else if (cmdchar == 'V') - ptr = (char_u *)"v"; - else - ptr = (char_u *)"i"; - set_vim_var_string(VV_INSERTMODE, ptr, 1); - set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */ - apply_autocmds(EVENT_INSERTENTER, NULL, NULL, FALSE, curbuf); - - /* Make sure the cursor didn't move. Do call check_cursor_col() in - * case the text was modified. Since Insert mode was not started yet - * a call to check_cursor_col() may move the cursor, especially with - * the "A" command, thus set State to avoid that. Also check that the - * line number is still valid (lines may have been deleted). - * Do not restore if v:char was set to a non-empty string. */ - if (!equalpos(curwin->w_cursor, save_cursor) - && *get_vim_var_str(VV_CHAR) == NUL - && save_cursor.lnum <= curbuf->b_ml.ml_line_count) { - int save_state = State; - - curwin->w_cursor = save_cursor; - State = INSERT; - check_cursor_col(); - State = save_state; - } - } - - /* Check if the cursor line needs redrawing before changing State. If - * 'concealcursor' is "n" it needs to be redrawn without concealing. */ - conceal_check_cursur_line(); - - /* - * When doing a paste with the middle mouse button, Insstart is set to - * where the paste started. - */ - if (where_paste_started.lnum != 0) - Insstart = where_paste_started; - else { - Insstart = curwin->w_cursor; - if (startln) - Insstart.col = 0; - } - Insstart_textlen = (colnr_T)linetabsize(ml_get_curline()); - Insstart_blank_vcol = MAXCOL; - if (!did_ai) - ai_col = 0; - - if (cmdchar != NUL && restart_edit == 0) { - ResetRedobuff(); - AppendNumberToRedobuff(count); - if (cmdchar == 'V' || cmdchar == 'v') { - /* "gR" or "gr" command */ - AppendCharToRedobuff('g'); - AppendCharToRedobuff((cmdchar == 'v') ? 'r' : 'R'); - } else { - AppendCharToRedobuff(cmdchar); - if (cmdchar == 'g') /* "gI" command */ - AppendCharToRedobuff('I'); - else if (cmdchar == 'r') /* "r" command */ - count = 1; /* insert only one */ - } - } - - if (cmdchar == 'R') { - if (p_fkmap && p_ri) { - beep_flush(); - EMSG(farsi_text_3); /* encoded in Farsi */ - State = INSERT; - } else - State = REPLACE; - } else if (cmdchar == 'V' || cmdchar == 'v') { - State = VREPLACE; - replaceState = VREPLACE; - orig_line_count = curbuf->b_ml.ml_line_count; - vr_lines_changed = 1; - } else - State = INSERT; - - stop_insert_mode = FALSE; - - /* - * Need to recompute the cursor position, it might move when the cursor is - * on a TAB or special character. - */ - curs_columns(TRUE); - - /* - * Enable langmap or IME, indicated by 'iminsert'. - * Note that IME may enabled/disabled without us noticing here, thus the - * 'iminsert' value may not reflect what is actually used. It is updated - * when hitting . - */ - if (curbuf->b_p_iminsert == B_IMODE_LMAP) - State |= LANGMAP; -#ifdef USE_IM_CONTROL - im_set_active(curbuf->b_p_iminsert == B_IMODE_IM); -#endif - - setmouse(); - clear_showcmd(); - /* there is no reverse replace mode */ - revins_on = (State == INSERT && p_ri); - if (revins_on) - undisplay_dollar(); - revins_chars = 0; - revins_legal = 0; - revins_scol = -1; - - /* - * Handle restarting Insert mode. - * Don't do this for "CTRL-O ." (repeat an insert): we get here with - * restart_edit non-zero, and something in the stuff buffer. - */ - if (restart_edit != 0 && stuff_empty()) { - /* - * After a paste we consider text typed to be part of the insert for - * the pasted text. You can backspace over the pasted text too. - */ - if (where_paste_started.lnum) - arrow_used = FALSE; - else - arrow_used = TRUE; - restart_edit = 0; - - /* - * If the cursor was after the end-of-line before the CTRL-O and it is - * now at the end-of-line, put it after the end-of-line (this is not - * correct in very rare cases). - * Also do this if curswant is greater than the current virtual - * column. Eg after "^O$" or "^O80|". - */ - validate_virtcol(); - update_curswant(); - if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum) - || curwin->w_curswant > curwin->w_virtcol) - && *(ptr = ml_get_curline() + curwin->w_cursor.col) != NUL) { - if (ptr[1] == NUL) - ++curwin->w_cursor.col; - else if (has_mbyte) { - i = (*mb_ptr2len)(ptr); - if (ptr[i] == NUL) - curwin->w_cursor.col += i; - } - } - ins_at_eol = FALSE; - } else - arrow_used = FALSE; - - /* we are in insert mode now, don't need to start it anymore */ - need_start_insertmode = FALSE; - - /* Need to save the line for undo before inserting the first char. */ - ins_need_undo = TRUE; - - where_paste_started.lnum = 0; - can_cindent = TRUE; - /* The cursor line is not in a closed fold, unless 'insertmode' is set or - * restarting. */ - if (!p_im && did_restart_edit == 0) - foldOpenCursor(); - - /* - * If 'showmode' is set, show the current (insert/replace/..) mode. - * A warning message for changing a readonly file is given here, before - * actually changing anything. It's put after the mode, if any. - */ - i = 0; - if (p_smd && msg_silent == 0) - i = showmode(); - - if (!p_im && did_restart_edit == 0) - change_warning(i == 0 ? 0 : i + 1); - - ui_cursor_shape(); /* may show different cursor shape */ - do_digraph(-1); /* clear digraphs */ - - /* - * Get the current length of the redo buffer, those characters have to be - * skipped if we want to get to the inserted characters. - */ - ptr = get_inserted(); - if (ptr == NULL) - new_insert_skip = 0; - else { - new_insert_skip = (int)STRLEN(ptr); - free(ptr); - } - - old_indent = 0; - - /* - * Main loop in Insert mode: repeat until Insert mode is left. - */ - for (;; ) { - if (!revins_legal) - revins_scol = -1; /* reset on illegal motions */ - else - revins_legal = 0; - if (arrow_used) /* don't repeat insert when arrow key used */ - count = 0; - - if (update_Insstart_orig) { - Insstart_orig = Insstart; - } - - if (stop_insert_mode) { - /* ":stopinsert" used or 'insertmode' reset */ - count = 0; - goto doESCkey; - } - - /* set curwin->w_curswant for next K_DOWN or K_UP */ - if (!arrow_used) - curwin->w_set_curswant = TRUE; - - /* If there is no typeahead may check for timestamps (e.g., for when a - * menu invoked a shell command). */ - if (stuff_empty()) { - did_check_timestamps = FALSE; - if (need_check_timestamps) - check_timestamps(FALSE); - } - - /* - * When emsg() was called msg_scroll will have been set. - */ - msg_scroll = FALSE; - - - /* Open fold at the cursor line, according to 'foldopen'. */ - if (fdo_flags & FDO_INSERT) - foldOpenCursor(); - /* Close folds where the cursor isn't, according to 'foldclose' */ - if (!char_avail()) - foldCheckClose(); - - /* - * If we inserted a character at the last position of the last line in - * the window, scroll the window one line up. This avoids an extra - * redraw. - * This is detected when the cursor column is smaller after inserting - * something. - * Don't do this when the topline changed already, it has - * already been adjusted (by insertchar() calling open_line())). - */ - if (curbuf->b_mod_set - && curwin->w_p_wrap - && !did_backspace - && curwin->w_topline == old_topline - && curwin->w_topfill == old_topfill - ) { - mincol = curwin->w_wcol; - validate_cursor_col(); - - if ((int)curwin->w_wcol < mincol - curbuf->b_p_ts - && curwin->w_wrow == W_WINROW(curwin) - + curwin->w_height - 1 - p_so - && (curwin->w_cursor.lnum != curwin->w_topline - || curwin->w_topfill > 0 - )) { - if (curwin->w_topfill > 0) - --curwin->w_topfill; - else if (hasFolding(curwin->w_topline, NULL, &old_topline)) - set_topline(curwin, old_topline + 1); - else - set_topline(curwin, curwin->w_topline + 1); - } - } - - /* May need to adjust w_topline to show the cursor. */ - update_topline(); - - did_backspace = FALSE; - - validate_cursor(); /* may set must_redraw */ - - /* - * Redraw the display when no characters are waiting. - * Also shows mode, ruler and positions cursor. - */ - ins_redraw(TRUE); - - if (curwin->w_p_scb) - do_check_scrollbind(TRUE); - - if (curwin->w_p_crb) - do_check_cursorbind(); - update_curswant(); - old_topline = curwin->w_topline; - old_topfill = curwin->w_topfill; - -#ifdef USE_ON_FLY_SCROLL - dont_scroll = FALSE; /* allow scrolling here */ -#endif - - /* - * Get a character for Insert mode. Ignore K_IGNORE. - */ - lastc = c; /* remember previous char for CTRL-D */ - do { - c = safe_vgetc(); - } while (c == K_IGNORE); - - /* Don't want K_CURSORHOLD for the second key, e.g., after CTRL-V. */ - did_cursorhold = TRUE; - - if (p_hkmap && KeyTyped) - c = hkmap(c); /* Hebrew mode mapping */ - if (p_fkmap && KeyTyped) - c = fkmap(c); /* Farsi mode mapping */ - - /* - * Special handling of keys while the popup menu is visible or wanted - * and the cursor is still in the completed word. Only when there is - * a match, skip this when no matches were found. - */ - if (compl_started - && pum_wanted() - && curwin->w_cursor.col >= compl_col - && (compl_shown_match == NULL - || compl_shown_match != compl_shown_match->cp_next)) { - /* BS: Delete one character from "compl_leader". */ - if ((c == K_BS || c == Ctrl_H) - && curwin->w_cursor.col > compl_col - && (c = ins_compl_bs()) == NUL) - continue; - - /* When no match was selected or it was edited. */ - if (!compl_used_match) { - /* CTRL-L: Add one character from the current match to - * "compl_leader". Except when at the original match and - * there is nothing to add, CTRL-L works like CTRL-P then. */ - if (c == Ctrl_L - && (ctrl_x_mode != CTRL_X_WHOLE_LINE - || (int)STRLEN(compl_shown_match->cp_str) - > curwin->w_cursor.col - compl_col)) { - ins_compl_addfrommatch(); - continue; - } - - /* A non-white character that fits in with the current - * completion: Add to "compl_leader". */ - if (ins_compl_accept_char(c)) { - /* Trigger InsertCharPre. */ - char_u *str = do_insert_char_pre(c); - char_u *p; - - if (str != NULL) { - for (p = str; *p != NUL; mb_ptr_adv(p)) - ins_compl_addleader(PTR2CHAR(p)); - free(str); - } else - ins_compl_addleader(c); - continue; - } - - /* Pressing CTRL-Y selects the current match. When - * compl_enter_selects is set the Enter key does the same. */ - if (c == Ctrl_Y || (compl_enter_selects - && (c == CAR || c == K_KENTER || c == NL))) { - ins_compl_delete(); - ins_compl_insert(); - } - } - } - - /* Prepare for or stop CTRL-X mode. This doesn't do completion, but - * it does fix up the text when finishing completion. */ - compl_get_longest = FALSE; - if (ins_compl_prep(c)) - continue; - - /* CTRL-\ CTRL-N goes to Normal mode, - * CTRL-\ CTRL-G goes to mode selected with 'insertmode', - * CTRL-\ CTRL-O is like CTRL-O but without moving the cursor. */ - if (c == Ctrl_BSL) { - /* may need to redraw when no more chars available now */ - ins_redraw(FALSE); - ++no_mapping; - ++allow_keys; - c = plain_vgetc(); - --no_mapping; - --allow_keys; - if (c != Ctrl_N && c != Ctrl_G && c != Ctrl_O) { - /* it's something else */ - vungetc(c); - c = Ctrl_BSL; - } else if (c == Ctrl_G && p_im) - continue; - else { - if (c == Ctrl_O) { - ins_ctrl_o(); - ins_at_eol = FALSE; /* cursor keeps its column */ - nomove = TRUE; - } - count = 0; - goto doESCkey; - } - } - - c = do_digraph(c); - - if ((c == Ctrl_V || c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) - goto docomplete; - if (c == Ctrl_V || c == Ctrl_Q) { - ins_ctrl_v(); - c = Ctrl_V; /* pretend CTRL-V is last typed character */ - continue; - } - - if (cindent_on() - && ctrl_x_mode == 0 - ) { - /* A key name preceded by a bang means this key is not to be - * inserted. Skip ahead to the re-indenting below. - * A key name preceded by a star means that indenting has to be - * done before inserting the key. */ - line_is_white = inindent(0); - if (in_cinkeys(c, '!', line_is_white)) - goto force_cindent; - if (can_cindent && in_cinkeys(c, '*', line_is_white) - && stop_arrow() == OK) - do_c_expr_indent(); - } - - if (curwin->w_p_rl) - switch (c) { - case K_LEFT: c = K_RIGHT; break; - case K_S_LEFT: c = K_S_RIGHT; break; - case K_C_LEFT: c = K_C_RIGHT; break; - case K_RIGHT: c = K_LEFT; break; - case K_S_RIGHT: c = K_S_LEFT; break; - case K_C_RIGHT: c = K_C_LEFT; break; - } - - /* - * If 'keymodel' contains "startsel", may start selection. If it - * does, a CTRL-O and c will be stuffed, we need to get these - * characters. - */ - if (ins_start_select(c)) - continue; - - /* - * The big switch to handle a character in insert mode. - */ - switch (c) { - case ESC: /* End input mode */ - if (echeck_abbr(ESC + ABBR_OFF)) - break; - /*FALLTHROUGH*/ - - case Ctrl_C: /* End input mode */ - if (c == Ctrl_C && cmdwin_type != 0) { - /* Close the cmdline window. */ - cmdwin_result = K_IGNORE; - got_int = FALSE; /* don't stop executing autocommands et al. */ - nomove = TRUE; - goto doESCkey; - } - -#ifdef UNIX -do_intr: -#endif - /* when 'insertmode' set, and not halfway a mapping, don't leave - * Insert mode */ - if (goto_im()) { - if (got_int) { - (void)vgetc(); /* flush all buffers */ - got_int = FALSE; - } else - vim_beep(); - break; - } -doESCkey: - /* - * This is the ONLY return from edit()! - */ - /* Always update o_lnum, so that a "CTRL-O ." that adds a line - * still puts the cursor back after the inserted text. */ - if (ins_at_eol && gchar_cursor() == NUL) - o_lnum = curwin->w_cursor.lnum; - - if (ins_esc(&count, cmdchar, nomove)) { - if (cmdchar != 'r' && cmdchar != 'v') - apply_autocmds(EVENT_INSERTLEAVE, NULL, NULL, - FALSE, curbuf); - did_cursorhold = FALSE; - return c == Ctrl_O; - } - continue; - - case Ctrl_Z: /* suspend when 'insertmode' set */ - if (!p_im) - goto normalchar; /* insert CTRL-Z as normal char */ - stuffReadbuff((char_u *)":st\r"); - c = Ctrl_O; - /*FALLTHROUGH*/ - - case Ctrl_O: /* execute one command */ - if (ctrl_x_mode == CTRL_X_OMNI) - goto docomplete; - if (echeck_abbr(Ctrl_O + ABBR_OFF)) - break; - ins_ctrl_o(); - - /* don't move the cursor left when 'virtualedit' has "onemore". */ - if (ve_flags & VE_ONEMORE) { - ins_at_eol = FALSE; - nomove = TRUE; - } - count = 0; - goto doESCkey; - - case K_INS: /* toggle insert/replace mode */ - case K_KINS: - ins_insert(replaceState); - break; - - case K_SELECT: /* end of Select mode mapping - ignore */ - break; - - - case K_HELP: /* Help key works like */ - case K_F1: - case K_XF1: - stuffcharReadbuff(K_HELP); - if (p_im) - need_start_insertmode = TRUE; - goto doESCkey; - - - case K_ZERO: /* Insert the previously inserted text. */ - case NUL: - case Ctrl_A: - /* For ^@ the trailing ESC will end the insert, unless there is an - * error. */ - if (stuff_inserted(NUL, 1L, (c == Ctrl_A)) == FAIL - && c != Ctrl_A && !p_im) - goto doESCkey; /* quit insert mode */ - inserted_space = FALSE; - break; - - case Ctrl_R: /* insert the contents of a register */ - ins_reg(); - auto_format(FALSE, TRUE); - inserted_space = FALSE; - break; - - case Ctrl_G: /* commands starting with CTRL-G */ - ins_ctrl_g(); - break; - - case Ctrl_HAT: /* switch input mode and/or langmap */ - ins_ctrl_hat(); - break; - - case Ctrl__: /* switch between languages */ - if (!p_ari) - goto normalchar; - ins_ctrl_(); - break; - - case Ctrl_D: /* Make indent one shiftwidth smaller. */ - if (ctrl_x_mode == CTRL_X_PATH_DEFINES) - goto docomplete; - /* FALLTHROUGH */ - - case Ctrl_T: /* Make indent one shiftwidth greater. */ - if (c == Ctrl_T && ctrl_x_mode == CTRL_X_THESAURUS) { - if (has_compl_option(FALSE)) - goto docomplete; - break; - } - ins_shift(c, lastc); - auto_format(FALSE, TRUE); - inserted_space = FALSE; - break; - - case K_DEL: /* delete character under the cursor */ - case K_KDEL: - ins_del(); - auto_format(FALSE, TRUE); - break; - - case K_BS: /* delete character before the cursor */ - case Ctrl_H: - did_backspace = ins_bs(c, BACKSPACE_CHAR, &inserted_space); - auto_format(FALSE, TRUE); - break; - - case Ctrl_W: /* delete word before the cursor */ - did_backspace = ins_bs(c, BACKSPACE_WORD, &inserted_space); - auto_format(FALSE, TRUE); - break; - - case Ctrl_U: /* delete all inserted text in current line */ - /* CTRL-X CTRL-U completes with 'completefunc'. */ - if (ctrl_x_mode == CTRL_X_FUNCTION) - goto docomplete; - did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space); - auto_format(FALSE, TRUE); - inserted_space = FALSE; - break; - - case K_LEFTMOUSE: /* mouse keys */ - case K_LEFTMOUSE_NM: - case K_LEFTDRAG: - case K_LEFTRELEASE: - case K_LEFTRELEASE_NM: - case K_MIDDLEMOUSE: - case K_MIDDLEDRAG: - case K_MIDDLERELEASE: - case K_RIGHTMOUSE: - case K_RIGHTDRAG: - case K_RIGHTRELEASE: - case K_X1MOUSE: - case K_X1DRAG: - case K_X1RELEASE: - case K_X2MOUSE: - case K_X2DRAG: - case K_X2RELEASE: - ins_mouse(c); - break; - - case K_MOUSEDOWN: /* Default action for scroll wheel up: scroll up */ - ins_mousescroll(MSCR_DOWN); - break; - - case K_MOUSEUP: /* Default action for scroll wheel down: scroll down */ - ins_mousescroll(MSCR_UP); - break; - - case K_MOUSELEFT: /* Scroll wheel left */ - ins_mousescroll(MSCR_LEFT); - break; - - case K_MOUSERIGHT: /* Scroll wheel right */ - ins_mousescroll(MSCR_RIGHT); - break; - - case K_IGNORE: /* Something mapped to nothing */ - break; - - case K_CURSORHOLD: /* Didn't type something for a while. */ - apply_autocmds(EVENT_CURSORHOLDI, NULL, NULL, FALSE, curbuf); - did_cursorhold = TRUE; - break; - - case K_EVENT: - event_process(); - break; - - case K_HOME: /* */ - case K_KHOME: - case K_S_HOME: - case K_C_HOME: - ins_home(c); - break; - - case K_END: /* */ - case K_KEND: - case K_S_END: - case K_C_END: - ins_end(c); - break; - - case K_LEFT: /* */ - if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) - ins_s_left(); - else - ins_left(); - break; - - case K_S_LEFT: /* */ - case K_C_LEFT: - ins_s_left(); - break; - - case K_RIGHT: /* */ - if (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL)) - ins_s_right(); - else - ins_right(); - break; - - case K_S_RIGHT: /* */ - case K_C_RIGHT: - ins_s_right(); - break; - - case K_UP: /* */ - if (pum_visible()) - goto docomplete; - if (mod_mask & MOD_MASK_SHIFT) - ins_pageup(); - else - ins_up(FALSE); - break; - - case K_S_UP: /* */ - case K_PAGEUP: - case K_KPAGEUP: - if (pum_visible()) - goto docomplete; - ins_pageup(); - break; - - case K_DOWN: /* */ - if (pum_visible()) - goto docomplete; - if (mod_mask & MOD_MASK_SHIFT) - ins_pagedown(); - else - ins_down(FALSE); - break; - - case K_S_DOWN: /* */ - case K_PAGEDOWN: - case K_KPAGEDOWN: - if (pum_visible()) - goto docomplete; - ins_pagedown(); - break; - - - case K_S_TAB: /* When not mapped, use like a normal TAB */ - c = TAB; - /* FALLTHROUGH */ - - case TAB: /* TAB or Complete patterns along path */ - if (ctrl_x_mode == CTRL_X_PATH_PATTERNS) - goto docomplete; - inserted_space = FALSE; - if (ins_tab()) - goto normalchar; /* insert TAB as a normal char */ - auto_format(FALSE, TRUE); - break; - - case K_KENTER: /* */ - c = CAR; - /* FALLTHROUGH */ - case CAR: - case NL: - /* In a quickfix window a jumps to the error under the - * cursor. */ - if (bt_quickfix(curbuf) && c == CAR) { - if (curwin->w_llist_ref == NULL) /* quickfix window */ - do_cmdline_cmd((char_u *)".cc"); - else /* location list window */ - do_cmdline_cmd((char_u *)".ll"); - break; - } - if (cmdwin_type != 0) { - /* Execute the command in the cmdline window. */ - cmdwin_result = CAR; - goto doESCkey; - } - if (ins_eol(c) && !p_im) - goto doESCkey; /* out of memory */ - auto_format(FALSE, FALSE); - inserted_space = FALSE; - break; - - case Ctrl_K: /* digraph or keyword completion */ - if (ctrl_x_mode == CTRL_X_DICTIONARY) { - if (has_compl_option(TRUE)) - goto docomplete; - break; - } - c = ins_digraph(); - if (c == NUL) - break; - goto normalchar; - - case Ctrl_X: /* Enter CTRL-X mode */ - ins_ctrl_x(); - break; - - case Ctrl_RSB: /* Tag name completion after ^X */ - if (ctrl_x_mode != CTRL_X_TAGS) - goto normalchar; - goto docomplete; - - case Ctrl_F: /* File name completion after ^X */ - if (ctrl_x_mode != CTRL_X_FILES) - goto normalchar; - goto docomplete; - - case 's': /* Spelling completion after ^X */ - case Ctrl_S: - if (ctrl_x_mode != CTRL_X_SPELL) - goto normalchar; - goto docomplete; - - case Ctrl_L: /* Whole line completion after ^X */ - if (ctrl_x_mode != CTRL_X_WHOLE_LINE) { - /* CTRL-L with 'insertmode' set: Leave Insert mode */ - if (p_im) { - if (echeck_abbr(Ctrl_L + ABBR_OFF)) - break; - goto doESCkey; - } - goto normalchar; - } - /* FALLTHROUGH */ - - case Ctrl_P: /* Do previous/next pattern completion */ - case Ctrl_N: - /* if 'complete' is empty then plain ^P is no longer special, - * but it is under other ^X modes */ - if (*curbuf->b_p_cpt == NUL - && ctrl_x_mode != 0 - && !(compl_cont_status & CONT_LOCAL)) - goto normalchar; - -docomplete: - compl_busy = TRUE; - if (ins_complete(c) == FAIL) - compl_cont_status = 0; - compl_busy = FALSE; - break; - - case Ctrl_Y: /* copy from previous line or scroll down */ - case Ctrl_E: /* copy from next line or scroll up */ - c = ins_ctrl_ey(c); - break; - - default: -#ifdef UNIX - if (c == intr_char) /* special interrupt char */ - goto do_intr; -#endif - -normalchar: - /* - * Insert a normal character. - */ - if (!p_paste) { - /* Trigger InsertCharPre. */ - char_u *str = do_insert_char_pre(c); - char_u *p; - - if (str != NULL) { - if (*str != NUL && stop_arrow() != FAIL) { - /* Insert the new value of v:char literally. */ - for (p = str; *p != NUL; mb_ptr_adv(p)) { - c = PTR2CHAR(p); - if (c == CAR || c == K_KENTER || c == NL) - ins_eol(c); - else - ins_char(c); - } - AppendToRedobuffLit(str, -1); - } - free(str); - c = NUL; - } - - /* If the new value is already inserted or an empty string - * then don't insert any character. */ - if (c == NUL) - break; - } - /* Try to perform smart-indenting. */ - ins_try_si(c); - - if (c == ' ') { - inserted_space = TRUE; - if (inindent(0)) - can_cindent = FALSE; - if (Insstart_blank_vcol == MAXCOL - && curwin->w_cursor.lnum == Insstart.lnum) - Insstart_blank_vcol = get_nolist_virtcol(); - } - - /* Insert a normal character and check for abbreviations on a - * special character. Let CTRL-] expand abbreviations without - * inserting it. */ - if (vim_iswordc(c) || (!echeck_abbr( - /* Add ABBR_OFF for characters above 0x100, this is - * what check_abbr() expects. */ - (has_mbyte && c >= 0x100) ? (c + ABBR_OFF) : - c) && c != Ctrl_RSB)) { - insert_special(c, FALSE, FALSE); - revins_legal++; - revins_chars++; - } - - auto_format(FALSE, TRUE); - - /* When inserting a character the cursor line must never be in a - * closed fold. */ - foldOpenCursor(); - break; - } /* end of switch (c) */ - - /* If typed something may trigger CursorHoldI again. */ - if (c != K_CURSORHOLD) - did_cursorhold = FALSE; - - /* If the cursor was moved we didn't just insert a space */ - if (arrow_used) - inserted_space = FALSE; - - if (can_cindent && cindent_on() - && ctrl_x_mode == 0 - ) { -force_cindent: - /* - * Indent now if a key was typed that is in 'cinkeys'. - */ - if (in_cinkeys(c, ' ', line_is_white)) { - if (stop_arrow() == OK) - /* re-indent the current line */ - do_c_expr_indent(); - } - } - - } /* for (;;) */ - /* NOTREACHED */ -} - -/* - * Redraw for Insert mode. - * This is postponed until getting the next character to make '$' in the 'cpo' - * option work correctly. - * Only redraw when there are no characters available. This speeds up - * inserting sequences of characters (e.g., for CTRL-R). - */ -static void -ins_redraw ( - int ready /* not busy with something */ -) -{ - linenr_T conceal_old_cursor_line = 0; - linenr_T conceal_new_cursor_line = 0; - int conceal_update_lines = FALSE; - - if (char_avail()) - return; - - /* Trigger CursorMoved if the cursor moved. Not when the popup menu is - * visible, the command might delete it. */ - if (ready && ( - has_cursormovedI() - || - curwin->w_p_cole > 0 - ) - && !equalpos(last_cursormoved, curwin->w_cursor) - && !pum_visible() - ) { - /* Need to update the screen first, to make sure syntax - * highlighting is correct after making a change (e.g., inserting - * a "(". The autocommand may also require a redraw, so it's done - * again below, unfortunately. */ - if (syntax_present(curwin) && must_redraw) - update_screen(0); - if (has_cursormovedI()) - apply_autocmds(EVENT_CURSORMOVEDI, NULL, NULL, FALSE, curbuf); - if (curwin->w_p_cole > 0) { - conceal_old_cursor_line = last_cursormoved.lnum; - conceal_new_cursor_line = curwin->w_cursor.lnum; - conceal_update_lines = TRUE; - } - last_cursormoved = curwin->w_cursor; - } - - /* Trigger TextChangedI if b_changedtick differs. */ - if (ready && has_textchangedI() - && last_changedtick != curbuf->b_changedtick - && !pum_visible() - ) { - if (last_changedtick_buf == curbuf) - apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, FALSE, curbuf); - last_changedtick_buf = curbuf; - last_changedtick = curbuf->b_changedtick; - } - - if (must_redraw) - update_screen(0); - else if (clear_cmdline || redraw_cmdline) - showmode(); /* clear cmdline and show mode */ - if ((conceal_update_lines - && (conceal_old_cursor_line != conceal_new_cursor_line - || conceal_cursor_line(curwin))) - || need_cursor_line_redraw) { - if (conceal_old_cursor_line != conceal_new_cursor_line) - update_single_line(curwin, conceal_old_cursor_line); - update_single_line(curwin, conceal_new_cursor_line == 0 - ? curwin->w_cursor.lnum : conceal_new_cursor_line); - curwin->w_valid &= ~VALID_CROW; - } - showruler(FALSE); - setcursor(); - emsg_on_display = FALSE; /* may remove error message now */ -} - -/* - * Handle a CTRL-V or CTRL-Q typed in Insert mode. - */ -static void ins_ctrl_v(void) -{ - int c; - int did_putchar = FALSE; - - /* may need to redraw when no more chars available now */ - ins_redraw(FALSE); - - if (redrawing() && !char_avail()) { - edit_putchar('^', TRUE); - did_putchar = TRUE; - } - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ - - add_to_showcmd_c(Ctrl_V); - - c = get_literal(); - if (did_putchar) - /* when the line fits in 'columns' the '^' is at the start of the next - * line and will not removed by the redraw */ - edit_unputchar(); - clear_showcmd(); - insert_special(c, FALSE, TRUE); - revins_chars++; - revins_legal++; -} - -/* - * Put a character directly onto the screen. It's not stored in a buffer. - * Used while handling CTRL-K, CTRL-V, etc. in Insert mode. - */ -static int pc_status; -#define PC_STATUS_UNSET 0 /* pc_bytes was not set */ -#define PC_STATUS_RIGHT 1 /* right halve of double-wide char */ -#define PC_STATUS_LEFT 2 /* left halve of double-wide char */ -#define PC_STATUS_SET 3 /* pc_bytes was filled */ -static char_u pc_bytes[MB_MAXBYTES + 1]; /* saved bytes */ -static int pc_attr; -static int pc_row; -static int pc_col; - -void edit_putchar(int c, int highlight) -{ - int attr; - - if (ScreenLines != NULL) { - update_topline(); /* just in case w_topline isn't valid */ - validate_cursor(); - if (highlight) - attr = hl_attr(HLF_8); - else - attr = 0; - pc_row = W_WINROW(curwin) + curwin->w_wrow; - pc_col = W_WINCOL(curwin); - pc_status = PC_STATUS_UNSET; - if (curwin->w_p_rl) { - pc_col += W_WIDTH(curwin) - 1 - curwin->w_wcol; - if (has_mbyte) { - int fix_col = mb_fix_col(pc_col, pc_row); - - if (fix_col != pc_col) { - screen_putchar(' ', pc_row, fix_col, attr); - --curwin->w_wcol; - pc_status = PC_STATUS_RIGHT; - } - } - } else { - pc_col += curwin->w_wcol; - if (mb_lefthalve(pc_row, pc_col)) - pc_status = PC_STATUS_LEFT; - } - - /* save the character to be able to put it back */ - if (pc_status == PC_STATUS_UNSET) { - screen_getbytes(pc_row, pc_col, pc_bytes, &pc_attr); - pc_status = PC_STATUS_SET; - } - screen_putchar(c, pc_row, pc_col, attr); - } -} - -/* - * Undo the previous edit_putchar(). - */ -void edit_unputchar(void) -{ - if (pc_status != PC_STATUS_UNSET && pc_row >= msg_scrolled) { - if (pc_status == PC_STATUS_RIGHT) - ++curwin->w_wcol; - if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) - redrawWinline(curwin->w_cursor.lnum, FALSE); - else - screen_puts(pc_bytes, pc_row - msg_scrolled, pc_col, pc_attr); - } -} - -/* - * Called when p_dollar is set: display a '$' at the end of the changed text - * Only works when cursor is in the line that changes. - */ -void display_dollar(colnr_T col) -{ - colnr_T save_col; - - if (!redrawing()) - return; - - cursor_off(); - save_col = curwin->w_cursor.col; - curwin->w_cursor.col = col; - if (has_mbyte) { - char_u *p; - - /* If on the last byte of a multi-byte move to the first byte. */ - p = ml_get_curline(); - curwin->w_cursor.col -= (*mb_head_off)(p, p + col); - } - curs_columns(FALSE); /* recompute w_wrow and w_wcol */ - if (curwin->w_wcol < W_WIDTH(curwin)) { - edit_putchar('$', FALSE); - dollar_vcol = curwin->w_virtcol; - } - curwin->w_cursor.col = save_col; -} - -/* - * Call this function before moving the cursor from the normal insert position - * in insert mode. - */ -static void undisplay_dollar(void) -{ - if (dollar_vcol >= 0) { - dollar_vcol = -1; - redrawWinline(curwin->w_cursor.lnum, FALSE); - } -} - -/* - * Insert an indent (for or CTRL-T) or delete an indent (for CTRL-D). - * Keep the cursor on the same character. - * type == INDENT_INC increase indent (for CTRL-T or ) - * type == INDENT_DEC decrease indent (for CTRL-D) - * type == INDENT_SET set indent to "amount" - * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec). - */ -void -change_indent ( - int type, - int amount, - int round, - int replaced, /* replaced character, put on replace stack */ - int call_changed_bytes /* call changed_bytes() */ -) -{ - int vcol; - int last_vcol; - int insstart_less; /* reduction for Insstart.col */ - int new_cursor_col; - int i; - char_u *ptr; - int save_p_list; - int start_col; - colnr_T vc; - colnr_T orig_col = 0; /* init for GCC */ - char_u *new_line, *orig_line = NULL; /* init for GCC */ - - /* VREPLACE mode needs to know what the line was like before changing */ - if (State & VREPLACE_FLAG) { - orig_line = vim_strsave(ml_get_curline()); /* Deal with NULL below */ - orig_col = curwin->w_cursor.col; - } - - /* for the following tricks we don't want list mode */ - save_p_list = curwin->w_p_list; - curwin->w_p_list = FALSE; - vc = getvcol_nolist(&curwin->w_cursor); - vcol = vc; - - /* - * For Replace mode we need to fix the replace stack later, which is only - * possible when the cursor is in the indent. Remember the number of - * characters before the cursor if it's possible. - */ - start_col = curwin->w_cursor.col; - - /* determine offset from first non-blank */ - new_cursor_col = curwin->w_cursor.col; - beginline(BL_WHITE); - new_cursor_col -= curwin->w_cursor.col; - - insstart_less = curwin->w_cursor.col; - - /* - * If the cursor is in the indent, compute how many screen columns the - * cursor is to the left of the first non-blank. - */ - if (new_cursor_col < 0) - vcol = get_indent() - vcol; - - if (new_cursor_col > 0) /* can't fix replace stack */ - start_col = -1; - - /* - * Set the new indent. The cursor will be put on the first non-blank. - */ - if (type == INDENT_SET) - (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); - else { - int save_State = State; - - /* Avoid being called recursively. */ - if (State & VREPLACE_FLAG) - State = INSERT; - shift_line(type == INDENT_DEC, round, 1, call_changed_bytes); - State = save_State; - } - insstart_less -= curwin->w_cursor.col; - - /* - * Try to put cursor on same character. - * If the cursor is at or after the first non-blank in the line, - * compute the cursor column relative to the column of the first - * non-blank character. - * If we are not in insert mode, leave the cursor on the first non-blank. - * If the cursor is before the first non-blank, position it relative - * to the first non-blank, counted in screen columns. - */ - if (new_cursor_col >= 0) { - /* - * When changing the indent while the cursor is touching it, reset - * Insstart_col to 0. - */ - if (new_cursor_col == 0) - insstart_less = MAXCOL; - new_cursor_col += curwin->w_cursor.col; - } else if (!(State & INSERT)) - new_cursor_col = curwin->w_cursor.col; - else { - /* - * Compute the screen column where the cursor should be. - */ - vcol = get_indent() - vcol; - curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol); - - /* - * Advance the cursor until we reach the right screen column. - */ - vcol = last_vcol = 0; - new_cursor_col = -1; - ptr = ml_get_curline(); - while (vcol <= (int)curwin->w_virtcol) { - last_vcol = vcol; - if (has_mbyte && new_cursor_col >= 0) - new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); - else - ++new_cursor_col; - vcol += lbr_chartabsize(ptr + new_cursor_col, (colnr_T)vcol); - } - vcol = last_vcol; - - /* - * May need to insert spaces to be able to position the cursor on - * the right screen column. - */ - if (vcol != (int)curwin->w_virtcol) { - curwin->w_cursor.col = (colnr_T)new_cursor_col; - i = (int)curwin->w_virtcol - vcol; - ptr = xmallocz(i); - memset(ptr, ' ', i); - new_cursor_col += i; - ins_str(ptr); - free(ptr); - } - - /* - * When changing the indent while the cursor is in it, reset - * Insstart_col to 0. - */ - insstart_less = MAXCOL; - } - - curwin->w_p_list = save_p_list; - - if (new_cursor_col <= 0) - curwin->w_cursor.col = 0; - else - curwin->w_cursor.col = (colnr_T)new_cursor_col; - curwin->w_set_curswant = TRUE; - changed_cline_bef_curs(); - - /* - * May have to adjust the start of the insert. - */ - if (State & INSERT) { - if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) { - if ((int)Insstart.col <= insstart_less) - Insstart.col = 0; - else - Insstart.col -= insstart_less; - } - if ((int)ai_col <= insstart_less) - ai_col = 0; - else - ai_col -= insstart_less; - } - - /* - * For REPLACE mode, may have to fix the replace stack, if it's possible. - * If the number of characters before the cursor decreased, need to pop a - * few characters from the replace stack. - * If the number of characters before the cursor increased, need to push a - * few NULs onto the replace stack. - */ - if (REPLACE_NORMAL(State) && start_col >= 0) { - while (start_col > (int)curwin->w_cursor.col) { - replace_join(0); /* remove a NUL from the replace stack */ - --start_col; - } - while (start_col < (int)curwin->w_cursor.col || replaced) { - replace_push(NUL); - if (replaced) { - replace_push(replaced); - replaced = NUL; - } - ++start_col; - } - } - - /* - * For VREPLACE mode, we also have to fix the replace stack. In this case - * it is always possible because we backspace over the whole line and then - * put it back again the way we wanted it. - */ - if (State & VREPLACE_FLAG) { - /* If orig_line didn't allocate, just return. At least we did the job, - * even if you can't backspace. */ - if (orig_line == NULL) - return; - - /* Save new line */ - new_line = vim_strsave(ml_get_curline()); - if (new_line == NULL) - return; - - /* We only put back the new line up to the cursor */ - new_line[curwin->w_cursor.col] = NUL; - - /* Put back original line */ - ml_replace(curwin->w_cursor.lnum, orig_line, FALSE); - curwin->w_cursor.col = orig_col; - - /* Backspace from cursor to start of line */ - backspace_until_column(0); - - /* Insert new stuff into line again */ - ins_bytes(new_line); - - free(new_line); - } -} - -/* - * Truncate the space at the end of a line. This is to be used only in an - * insert mode. It handles fixing the replace stack for REPLACE and VREPLACE - * modes. - */ -void truncate_spaces(char_u *line) -{ - int i; - - /* find start of trailing white space */ - for (i = (int)STRLEN(line) - 1; i >= 0 && vim_iswhite(line[i]); i--) { - if (State & REPLACE_FLAG) - replace_join(0); /* remove a NUL from the replace stack */ - } - line[i + 1] = NUL; -} - -#if defined(FEAT_VREPLACE) || defined(FEAT_INS_EXPAND) \ - || defined(FEAT_COMMENTS) || defined(PROTO) -/* - * Backspace the cursor until the given column. Handles REPLACE and VREPLACE - * modes correctly. May also be used when not in insert mode at all. - * Will attempt not to go before "col" even when there is a composing - * character. - */ -void backspace_until_column(int col) -{ - while ((int)curwin->w_cursor.col > col) { - curwin->w_cursor.col--; - if (State & REPLACE_FLAG) - replace_do_bs(col); - else if (!del_char_after_col(col)) - break; - } -} -#endif - -/* - * Like del_char(), but make sure not to go before column "limit_col". - * Only matters when there are composing characters. - * Return TRUE when something was deleted. - */ -static int del_char_after_col(int limit_col) -{ - if (enc_utf8 && limit_col >= 0) { - colnr_T ecol = curwin->w_cursor.col + 1; - - /* Make sure the cursor is at the start of a character, but - * skip forward again when going too far back because of a - * composing character. */ - mb_adjust_cursor(); - while (curwin->w_cursor.col < (colnr_T)limit_col) { - int l = utf_ptr2len(ml_get_cursor()); - - if (l == 0) /* end of line */ - break; - curwin->w_cursor.col += l; - } - if (*ml_get_cursor() == NUL || curwin->w_cursor.col == ecol) - return FALSE; - del_bytes((long)((int)ecol - curwin->w_cursor.col), FALSE, TRUE); - } else - (void)del_char(FALSE); - return TRUE; -} - -/* - * CTRL-X pressed in Insert mode. - */ -static void ins_ctrl_x(void) -{ - /* CTRL-X after CTRL-X CTRL-V doesn't do anything, so that CTRL-X - * CTRL-V works like CTRL-N */ - if (ctrl_x_mode != CTRL_X_CMDLINE) { - /* if the next ^X<> won't ADD nothing, then reset - * compl_cont_status */ - if (compl_cont_status & CONT_N_ADDS) - compl_cont_status |= CONT_INTRPT; - else - compl_cont_status = 0; - /* We're not sure which CTRL-X mode it will be yet */ - ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - edit_submode_pre = NULL; - showmode(); - } -} - -/* - * Return TRUE if the 'dict' or 'tsr' option can be used. - */ -static int has_compl_option(int dict_opt) -{ - if (dict_opt ? (*curbuf->b_p_dict == NUL && *p_dict == NUL - && !curwin->w_p_spell - ) - : (*curbuf->b_p_tsr == NUL && *p_tsr == NUL)) { - ctrl_x_mode = 0; - edit_submode = NULL; - msg_attr(dict_opt ? (char_u *)_("'dictionary' option is empty") - : (char_u *)_("'thesaurus' option is empty"), - hl_attr(HLF_E)); - if (emsg_silent == 0) { - vim_beep(); - setcursor(); - out_flush(); - ui_delay(2000L, FALSE); - } - return FALSE; - } - return TRUE; -} - -/* - * Is the character 'c' a valid key to go to or keep us in CTRL-X mode? - * This depends on the current mode. - */ -int vim_is_ctrl_x_key(int c) -{ - /* Always allow ^R - let it's results then be checked */ - if (c == Ctrl_R) - return TRUE; - - /* Accept and if the popup menu is visible. */ - if (ins_compl_pum_key(c)) - return TRUE; - - switch (ctrl_x_mode) { - case 0: /* Not in any CTRL-X mode */ - return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X; - case CTRL_X_NOT_DEFINED_YET: - return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E - || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB - || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P - || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V - || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O - || c == Ctrl_S || c == Ctrl_K || c == 's'; - case CTRL_X_SCROLL: - return c == Ctrl_Y || c == Ctrl_E; - case CTRL_X_WHOLE_LINE: - return c == Ctrl_L || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_FILES: - return c == Ctrl_F || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_DICTIONARY: - return c == Ctrl_K || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_THESAURUS: - return c == Ctrl_T || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_TAGS: - return c == Ctrl_RSB || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_PATH_PATTERNS: - return c == Ctrl_P || c == Ctrl_N; - case CTRL_X_PATH_DEFINES: - return c == Ctrl_D || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_CMDLINE: - return c == Ctrl_V || c == Ctrl_Q || c == Ctrl_P || c == Ctrl_N - || c == Ctrl_X; - case CTRL_X_FUNCTION: - return c == Ctrl_U || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_OMNI: - return c == Ctrl_O || c == Ctrl_P || c == Ctrl_N; - case CTRL_X_SPELL: - return c == Ctrl_S || c == Ctrl_P || c == Ctrl_N; - } - EMSG(_(e_internal)); - return FALSE; -} - -/* - * Return TRUE when character "c" is part of the item currently being - * completed. Used to decide whether to abandon complete mode when the menu - * is visible. - */ -static int ins_compl_accept_char(int c) -{ - if (ctrl_x_mode & CTRL_X_WANT_IDENT) - /* When expanding an identifier only accept identifier chars. */ - return vim_isIDc(c); - - switch (ctrl_x_mode) { - case CTRL_X_FILES: - /* When expanding file name only accept file name chars. But not - * path separators, so that "proto/" expands files in - * "proto", not "proto/" as a whole */ - return vim_isfilec(c) && !vim_ispathsep(c); - - case CTRL_X_CMDLINE: - case CTRL_X_OMNI: - /* Command line and Omni completion can work with just about any - * printable character, but do stop at white space. */ - return vim_isprintc(c) && !vim_iswhite(c); - - case CTRL_X_WHOLE_LINE: - /* For while line completion a space can be part of the line. */ - return vim_isprintc(c); - } - return vim_iswordc(c); -} - -/* - * This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the - * case of the originally typed text is used, and the case of the completed - * text is inferred, ie this tries to work out what case you probably wanted - * the rest of the word to be in -- webb - */ -int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags) -{ - char_u *p; - int i, c; - int actual_len; /* Take multi-byte characters */ - int actual_compl_length; /* into account. */ - int min_len; - int *wca; /* Wide character array. */ - int has_lower = FALSE; - int was_letter = FALSE; - - if (p_ic && curbuf->b_p_inf && len > 0) { - /* Infer case of completed part. */ - - /* Find actual length of completion. */ - if (has_mbyte) { - p = str; - actual_len = 0; - while (*p != NUL) { - mb_ptr_adv(p); - ++actual_len; - } - } else - actual_len = len; - - /* Find actual length of original text. */ - if (has_mbyte) { - p = compl_orig_text; - actual_compl_length = 0; - while (*p != NUL) { - mb_ptr_adv(p); - ++actual_compl_length; - } - } else - actual_compl_length = compl_length; - - /* "actual_len" may be smaller than "actual_compl_length" when using - * thesaurus, only use the minimum when comparing. */ - min_len = actual_len < actual_compl_length - ? actual_len : actual_compl_length; - - /* Allocate wide character array for the completion and fill it. */ - wca = xmalloc(actual_len * sizeof(*wca)); - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); - - /* Rule 1: Were any chars converted to lower? */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) { - has_lower = TRUE; - if (vim_isupper(wca[i])) { - /* Rule 1 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = vim_tolower(wca[i]); - break; - } - } - } - - /* - * Rule 2: No lower case, 2nd consecutive letter converted to - * upper case. - */ - if (!has_lower) { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (was_letter && vim_isupper(c) && vim_islower(wca[i])) { - /* Rule 2 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = vim_toupper(wca[i]); - break; - } - was_letter = vim_islower(c) || vim_isupper(c); - } - } - - /* Copy the original case of the part we typed. */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) - wca[i] = vim_tolower(wca[i]); - else if (vim_isupper(c)) - wca[i] = vim_toupper(wca[i]); - } - - /* - * Generate encoding specific output from wide character array. - * Multi-byte characters can occupy up to five bytes more than - * ASCII characters, and we also need one byte for NUL, so stay - * six bytes away from the edge of IObuff. - */ - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; - - free(wca); - - return ins_compl_add(IObuff, len, icase, fname, NULL, dir, - flags, FALSE); - } - return ins_compl_add(str, len, icase, fname, NULL, dir, flags, FALSE); -} - -/* - * Add a match to the list of matches. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -static int -ins_compl_add ( - char_u *str, - int len, - int icase, - char_u *fname, - char_u **cptext, /* extra text for popup menu or NULL */ - int cdir, - int flags, - int adup /* accept duplicate match */ -) -{ - compl_T *match; - int dir = (cdir == 0 ? compl_direction : cdir); - - ui_breakcheck(); - if (got_int) - return FAIL; - if (len < 0) - len = (int)STRLEN(str); - - /* - * If the same match is already present, don't add it. - */ - if (compl_first_match != NULL && !adup) { - match = compl_first_match; - do { - if ( !(match->cp_flags & ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) - return NOTDONE; - match = match->cp_next; - } while (match != NULL && match != compl_first_match); - } - - /* Remove any popup menu before changing the list of matches. */ - ins_compl_del_pum(); - - /* - * Allocate a new match structure. - * Copy the values to the new match structure. - */ - match = xcalloc(1, sizeof(compl_T)); - match->cp_number = -1; - if (flags & ORIGINAL_TEXT) - match->cp_number = 0; - if ((match->cp_str = vim_strnsave(str, len)) == NULL) { - free(match); - return FAIL; - } - match->cp_icase = icase; - - /* match-fname is: - * - compl_curr_match->cp_fname if it is a string equal to fname. - * - a copy of fname, FREE_FNAME is set to free later THE allocated mem. - * - NULL otherwise. --Acevedo */ - if (fname != NULL - && compl_curr_match != NULL - && compl_curr_match->cp_fname != NULL - && STRCMP(fname, compl_curr_match->cp_fname) == 0) - match->cp_fname = compl_curr_match->cp_fname; - else if (fname != NULL) { - match->cp_fname = vim_strsave(fname); - flags |= FREE_FNAME; - } else - match->cp_fname = NULL; - match->cp_flags = flags; - - if (cptext != NULL) { - int i; - - for (i = 0; i < CPT_COUNT; ++i) - if (cptext[i] != NULL && *cptext[i] != NUL) - match->cp_text[i] = vim_strsave(cptext[i]); - } - - /* - * Link the new match structure in the list of matches. - */ - if (compl_first_match == NULL) - match->cp_next = match->cp_prev = NULL; - else if (dir == FORWARD) { - match->cp_next = compl_curr_match->cp_next; - match->cp_prev = compl_curr_match; - } else { /* BACKWARD */ - match->cp_next = compl_curr_match; - match->cp_prev = compl_curr_match->cp_prev; - } - if (match->cp_next) - match->cp_next->cp_prev = match; - if (match->cp_prev) - match->cp_prev->cp_next = match; - else /* if there's nothing before, it is the first match */ - compl_first_match = match; - compl_curr_match = match; - - /* - * Find the longest common string if still doing that. - */ - if (compl_get_longest && (flags & ORIGINAL_TEXT) == 0) - ins_compl_longest_match(match); - - return OK; -} - -/* - * Return TRUE if "str[len]" matches with match->cp_str, considering - * match->cp_icase. - */ -static int ins_compl_equal(compl_T *match, char_u *str, int len) -{ - if (match->cp_icase) - return STRNICMP(match->cp_str, str, (size_t)len) == 0; - return STRNCMP(match->cp_str, str, (size_t)len) == 0; -} - -/* - * Reduce the longest common string for match "match". - */ -static void ins_compl_longest_match(compl_T *match) -{ - char_u *p, *s; - int c1, c2; - int had_match; - - if (compl_leader == NULL) { - /* First match, use it as a whole. */ - compl_leader = vim_strsave(match->cp_str); - if (compl_leader != NULL) { - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) - ins_compl_delete(); - compl_used_match = FALSE; - } - } else { - /* Reduce the text if this match differs from compl_leader. */ - p = compl_leader; - s = match->cp_str; - while (*p != NUL) { - if (has_mbyte) { - c1 = mb_ptr2char(p); - c2 = mb_ptr2char(s); - } else { - c1 = *p; - c2 = *s; - } - if (match->cp_icase ? (vim_tolower(c1) != vim_tolower(c2)) - : (c1 != c2)) - break; - if (has_mbyte) { - mb_ptr_adv(p); - mb_ptr_adv(s); - } else { - ++p; - ++s; - } - } - - if (*p != NUL) { - /* Leader was shortened, need to change the inserted text. */ - *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - ins_redraw(FALSE); - - /* When the match isn't there (to avoid matching itself) remove it - * again after redrawing. */ - if (!had_match) - ins_compl_delete(); - } - - compl_used_match = FALSE; - } -} - -/* - * Add an array of matches to the list of matches. - * Frees matches[]. - */ -static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) -{ - int i; - int add_r = OK; - int dir = compl_direction; - - for (i = 0; i < num_matches && add_r != FAIL; i++) - if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, dir, 0, FALSE)) == OK) - /* if dir was BACKWARD then honor it just once */ - dir = FORWARD; - FreeWild(num_matches, matches); -} - -/* Make the completion list cyclic. - * Return the number of matches (excluding the original). - */ -static int ins_compl_make_cyclic(void) -{ - compl_T *match; - int count = 0; - - if (compl_first_match != NULL) { - /* - * Find the end of the list. - */ - match = compl_first_match; - /* there's always an entry for the compl_orig_text, it doesn't count. */ - while (match->cp_next != NULL && match->cp_next != compl_first_match) { - match = match->cp_next; - ++count; - } - match->cp_next = compl_first_match; - compl_first_match->cp_prev = match; - } - return count; -} - -/* - * Start completion for the complete() function. - * "startcol" is where the matched text starts (1 is first column). - * "list" is the list of matches. - */ -void set_completion(colnr_T startcol, list_T *list) -{ - /* If already doing completions stop it. */ - if (ctrl_x_mode != 0) - ins_compl_prep(' '); - ins_compl_clear(); - - if (stop_arrow() == FAIL) - return; - - compl_direction = FORWARD; - if (startcol > curwin->w_cursor.col) - startcol = curwin->w_cursor.col; - compl_col = startcol; - compl_length = (int)curwin->w_cursor.col - (int)startcol; - /* compl_pattern doesn't need to be set */ - compl_orig_text = vim_strnsave(ml_get_curline() + compl_col, compl_length); - if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, - -1, p_ic, NULL, NULL, 0, ORIGINAL_TEXT, FALSE) != OK) - return; - - /* Handle like dictionary completion. */ - ctrl_x_mode = CTRL_X_WHOLE_LINE; - - ins_compl_add_list(list); - compl_matches = ins_compl_make_cyclic(); - compl_started = TRUE; - compl_used_match = TRUE; - compl_cont_status = 0; - - compl_curr_match = compl_first_match; - ins_complete(Ctrl_N); - out_flush(); -} - - -/* "compl_match_array" points the currently displayed list of entries in the - * popup menu. It is NULL when there is no popup menu. */ -static pumitem_T *compl_match_array = NULL; -static int compl_match_arraysize; - -/* - * Update the screen and when there is any scrolling remove the popup menu. - */ -static void ins_compl_upd_pum(void) -{ - int h; - - if (compl_match_array != NULL) { - h = curwin->w_cline_height; - update_screen(0); - if (h != curwin->w_cline_height) - ins_compl_del_pum(); - } -} - -/* - * Remove any popup menu. - */ -static void ins_compl_del_pum(void) -{ - if (compl_match_array != NULL) { - pum_undisplay(); - free(compl_match_array); - compl_match_array = NULL; - } -} - -/* - * Return TRUE if the popup menu should be displayed. - */ -static int pum_wanted(void) -{ - /* 'completeopt' must contain "menu" or "menuone" */ - if (vim_strchr(p_cot, 'm') == NULL) - return FALSE; - - /* The display looks bad on a B&W display. */ - if (t_colors < 8 - ) - return FALSE; - return TRUE; -} - -/* - * Return TRUE if there are two or more matches to be shown in the popup menu. - * One if 'completopt' contains "menuone". - */ -static int pum_enough_matches(void) -{ - compl_T *compl; - int i; - - /* Don't display the popup menu if there are no matches or there is only - * one (ignoring the original text). */ - compl = compl_first_match; - i = 0; - do { - if (compl == NULL - || ((compl->cp_flags & ORIGINAL_TEXT) == 0 && ++i == 2)) - break; - compl = compl->cp_next; - } while (compl != compl_first_match); - - if (strstr((char *)p_cot, "menuone") != NULL) - return i >= 1; - return i >= 2; -} - -/* - * Show the popup menu for the list of matches. - * Also adjusts "compl_shown_match" to an entry that is actually displayed. - */ -void ins_compl_show_pum(void) -{ - compl_T *compl; - compl_T *shown_compl = NULL; - int did_find_shown_match = FALSE; - int shown_match_ok = FALSE; - int i; - int cur = -1; - colnr_T col; - int lead_len = 0; - - if (!pum_wanted() || !pum_enough_matches()) - return; - - /* Dirty hard-coded hack: remove any matchparen highlighting. */ - do_cmdline_cmd((char_u *)"if exists('g:loaded_matchparen')|3match none|endif"); - - /* Update the screen before drawing the popup menu over it. */ - update_screen(0); - - if (compl_match_array == NULL) { - /* Need to build the popup menu list. */ - compl_match_arraysize = 0; - compl = compl_first_match; - if (compl_leader != NULL) - lead_len = (int)STRLEN(compl_leader); - do { - if ((compl->cp_flags & ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) - ++compl_match_arraysize; - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - if (compl_match_arraysize == 0) - return; - - assert(compl_match_arraysize >= 0); - compl_match_array = xcalloc(compl_match_arraysize, sizeof(pumitem_T)); - /* If the current match is the original text don't find the first - * match after it, don't highlight anything. */ - if (compl_shown_match->cp_flags & ORIGINAL_TEXT) - shown_match_ok = TRUE; - - i = 0; - compl = compl_first_match; - do { - if ((compl->cp_flags & ORIGINAL_TEXT) == 0 - && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) { - if (!shown_match_ok) { - if (compl == compl_shown_match || did_find_shown_match) { - /* This item is the shown match or this is the - * first displayed item after the shown match. */ - compl_shown_match = compl; - did_find_shown_match = TRUE; - shown_match_ok = TRUE; - } else - /* Remember this displayed match for when the - * shown match is just below it. */ - shown_compl = compl; - cur = i; - } - - if (compl->cp_text[CPT_ABBR] != NULL) - compl_match_array[i].pum_text = - compl->cp_text[CPT_ABBR]; - else - compl_match_array[i].pum_text = compl->cp_str; - compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; - if (compl->cp_text[CPT_MENU] != NULL) - compl_match_array[i++].pum_extra = - compl->cp_text[CPT_MENU]; - else - compl_match_array[i++].pum_extra = compl->cp_fname; - } - - if (compl == compl_shown_match) { - did_find_shown_match = TRUE; - - /* When the original text is the shown match don't set - * compl_shown_match. */ - if (compl->cp_flags & ORIGINAL_TEXT) - shown_match_ok = TRUE; - - if (!shown_match_ok && shown_compl != NULL) { - /* The shown match isn't displayed, set it to the - * previously displayed match. */ - compl_shown_match = shown_compl; - shown_match_ok = TRUE; - } - } - compl = compl->cp_next; - } while (compl != NULL && compl != compl_first_match); - - if (!shown_match_ok) /* no displayed match at all */ - cur = -1; - } else { - /* popup menu already exists, only need to find the current item.*/ - for (i = 0; i < compl_match_arraysize; ++i) - if (compl_match_array[i].pum_text == compl_shown_match->cp_str - || compl_match_array[i].pum_text - == compl_shown_match->cp_text[CPT_ABBR]) { - cur = i; - break; - } - } - - if (compl_match_array != NULL) { - /* Compute the screen column of the start of the completed text. - * Use the cursor to get all wrapping and other settings right. */ - col = curwin->w_cursor.col; - curwin->w_cursor.col = compl_col; - pum_display(compl_match_array, compl_match_arraysize, cur); - curwin->w_cursor.col = col; - } -} - -#define DICT_FIRST (1) /* use just first element in "dict" */ -#define DICT_EXACT (2) /* "dict" is the exact name of a file */ - -/* - * Add any identifiers that match the given pattern in the list of dictionary - * files "dict_start" to the list of completions. - */ -static void -ins_compl_dictionaries ( - char_u *dict_start, - char_u *pat, - int flags, /* DICT_FIRST and/or DICT_EXACT */ - int thesaurus /* Thesaurus completion */ -) -{ - char_u *dict = dict_start; - char_u *ptr; - char_u *buf; - regmatch_T regmatch; - char_u **files; - int count; - int save_p_scs; - int dir = compl_direction; - - if (*dict == NUL) { - /* When 'dictionary' is empty and spell checking is enabled use - * "spell". */ - if (!thesaurus && curwin->w_p_spell) - dict = (char_u *)"spell"; - else - return; - } - - buf = xmalloc(LSIZE); - regmatch.regprog = NULL; /* so that we can goto theend */ - - /* If 'infercase' is set, don't use 'smartcase' here */ - save_p_scs = p_scs; - if (curbuf->b_p_inf) - p_scs = FALSE; - - /* When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern - * to only match at the start of a line. Otherwise just match the - * pattern. Also need to double backslashes. */ - if (ctrl_x_mode == CTRL_X_WHOLE_LINE) { - char_u *pat_esc = vim_strsave_escaped(pat, (char_u *)"\\"); - - if (pat_esc == NULL) - goto theend; - size_t len = STRLEN(pat_esc) + 10; - ptr = xmalloc(len); - vim_snprintf((char *)ptr, len, "^\\s*\\zs\\V%s", pat_esc); - regmatch.regprog = vim_regcomp(ptr, RE_MAGIC); - free(pat_esc); - free(ptr); - } else { - regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) - goto theend; - } - - /* ignore case depends on 'ignorecase', 'smartcase' and "pat" */ - regmatch.rm_ic = ignorecase(pat); - while (*dict != NUL && !got_int && !compl_interrupted) { - /* copy one dictionary file name into buf */ - if (flags == DICT_EXACT) { - count = 1; - files = &dict; - } else { - /* Expand wildcards in the dictionary name, but do not allow - * backticks (for security, the 'dict' option may have been set in - * a modeline). */ - copy_option_part(&dict, buf, LSIZE, ","); - if (!thesaurus && STRCMP(buf, "spell") == 0) - count = -1; - else if (vim_strchr(buf, '`') != NULL - || expand_wildcards(1, &buf, &count, &files, - EW_FILE|EW_SILENT) != OK) - count = 0; - } - - if (count == -1) { - /* Complete from active spelling. Skip "\<" in the pattern, we - * don't use it as a RE. */ - if (pat[0] == '\\' && pat[1] == '<') - ptr = pat + 2; - else - ptr = pat; - spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); - } else if (count > 0) { /* avoid warning for using "files" uninit */ - ins_compl_files(count, files, thesaurus, flags, - ®match, buf, &dir); - if (flags != DICT_EXACT) - FreeWild(count, files); - } - if (flags != 0) - break; - } - -theend: - p_scs = save_p_scs; - vim_regfree(regmatch.regprog); - free(buf); -} - -static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, regmatch_T *regmatch, char_u *buf, int *dir) -{ - char_u *ptr; - int i; - FILE *fp; - int add_r; - - for (i = 0; i < count && !got_int && !compl_interrupted; i++) { - fp = mch_fopen((char *)files[i], "r"); /* open dictionary file */ - if (flags != DICT_EXACT) { - vim_snprintf((char *)IObuff, IOSIZE, - _("Scanning dictionary: %s"), (char *)files[i]); - (void)msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); - } - - if (fp != NULL) { - /* - * Read dictionary file line by line. - * Check each line for a match. - */ - while (!got_int && !compl_interrupted - && !vim_fgets(buf, LSIZE, fp)) { - ptr = buf; - while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) { - ptr = regmatch->startp[0]; - if (ctrl_x_mode == CTRL_X_WHOLE_LINE) - ptr = find_line_end(ptr); - else - ptr = find_word_end(ptr); - add_r = ins_compl_add_infercase(regmatch->startp[0], - (int)(ptr - regmatch->startp[0]), - p_ic, files[i], *dir, 0); - if (thesaurus) { - char_u *wstart; - - /* - * Add the other matches on the line - */ - ptr = buf; - while (!got_int) { - /* Find start of the next word. Skip white - * space and punctuation. */ - ptr = find_word_start(ptr); - if (*ptr == NUL || *ptr == NL) - break; - wstart = ptr; - - /* Find end of the word. */ - if (has_mbyte) - /* Japanese words may have characters in - * different classes, only separate words - * with single-byte non-word characters. */ - while (*ptr != NUL) { - int l = (*mb_ptr2len)(ptr); - - if (l < 2 && !vim_iswordc(*ptr)) - break; - ptr += l; - } - else - ptr = find_word_end(ptr); - - /* Add the word. Skip the regexp match. */ - if (wstart != regmatch->startp[0]) - add_r = ins_compl_add_infercase(wstart, - (int)(ptr - wstart), - p_ic, files[i], *dir, 0); - } - } - if (add_r == OK) - /* if dir was BACKWARD then honor it just once */ - *dir = FORWARD; - else if (add_r == FAIL) - break; - /* avoid expensive call to vim_regexec() when at end - * of line */ - if (*ptr == '\n' || got_int) - break; - } - line_breakcheck(); - ins_compl_check_keys(50); - } - fclose(fp); - } - } -} - -/* - * Find the start of the next word. - * Returns a pointer to the first char of the word. Also stops at a NUL. - */ -char_u *find_word_start(char_u *ptr) -{ - if (has_mbyte) - while (*ptr != NUL && *ptr != '\n' && mb_get_class(ptr) <= 1) - ptr += (*mb_ptr2len)(ptr); - else - while (*ptr != NUL && *ptr != '\n' && !vim_iswordc(*ptr)) - ++ptr; - return ptr; -} - -/* - * Find the end of the word. Assumes it starts inside a word. - * Returns a pointer to just after the word. - */ -char_u *find_word_end(char_u *ptr) -{ - int start_class; - - if (has_mbyte) { - start_class = mb_get_class(ptr); - if (start_class > 1) - while (*ptr != NUL) { - ptr += (*mb_ptr2len)(ptr); - if (mb_get_class(ptr) != start_class) - break; - } - } else - while (vim_iswordc(*ptr)) - ++ptr; - return ptr; -} - -/* - * Find the end of the line, omitting CR and NL at the end. - * Returns a pointer to just after the line. - */ -static char_u *find_line_end(char_u *ptr) -{ - char_u *s; - - s = ptr + STRLEN(ptr); - while (s > ptr && (s[-1] == CAR || s[-1] == NL)) - --s; - return s; -} - -/* - * Free the list of completions - */ -static void ins_compl_free(void) -{ - compl_T *match; - int i; - - free(compl_pattern); - compl_pattern = NULL; - free(compl_leader); - compl_leader = NULL; - - if (compl_first_match == NULL) - return; - - ins_compl_del_pum(); - pum_clear(); - - compl_curr_match = compl_first_match; - do { - match = compl_curr_match; - compl_curr_match = compl_curr_match->cp_next; - free(match->cp_str); - /* several entries may use the same fname, free it just once. */ - if (match->cp_flags & FREE_FNAME) - free(match->cp_fname); - for (i = 0; i < CPT_COUNT; ++i) - free(match->cp_text[i]); - free(match); - } while (compl_curr_match != NULL && compl_curr_match != compl_first_match); - compl_first_match = compl_curr_match = NULL; - compl_shown_match = NULL; -} - -static void ins_compl_clear(void) -{ - compl_cont_status = 0; - compl_started = FALSE; - compl_matches = 0; - free(compl_pattern); - compl_pattern = NULL; - free(compl_leader); - compl_leader = NULL; - edit_submode_extra = NULL; - free(compl_orig_text); - compl_orig_text = NULL; - compl_enter_selects = FALSE; -} - -/* - * Return TRUE when Insert completion is active. - */ -int ins_compl_active(void) -{ - return compl_started; -} - -/* - * Delete one character before the cursor and show the subset of the matches - * that match the word that is now before the cursor. - * Returns the character to be used, NUL if the work is done and another char - * to be got from the user. - */ -static int ins_compl_bs(void) -{ - char_u *line; - char_u *p; - - line = ml_get_curline(); - p = line + curwin->w_cursor.col; - mb_ptr_back(line, p); - - /* Stop completion when the whole word was deleted. For Omni completion - * allow the word to be deleted, we won't match everything. */ - if ((int)(p - line) - (int)compl_col < 0 - || ((int)(p - line) - (int)compl_col == 0 - && (ctrl_x_mode & CTRL_X_OMNI) == 0)) - return K_BS; - - /* Deleted more than what was used to find matches or didn't finish - * finding all matches: need to look for matches all over again. */ - if (curwin->w_cursor.col <= compl_col + compl_length - || ins_compl_need_restart()) - ins_compl_restart(); - - free(compl_leader); - compl_leader = vim_strnsave(line + compl_col, (int)(p - line) - compl_col); - if (compl_leader != NULL) { - ins_compl_new_leader(); - if (compl_shown_match != NULL) - /* Make sure current match is not a hidden item. */ - compl_curr_match = compl_shown_match; - return NUL; - } - return K_BS; -} - -/* - * Return TRUE when we need to find matches again, ins_compl_restart() is to - * be called. - */ -static int ins_compl_need_restart(void) -{ - /* Return TRUE if we didn't complete finding matches or when the - * 'completefunc' returned "always" in the "refresh" dictionary item. */ - return compl_was_interrupted - || ((ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == CTRL_X_OMNI) - && compl_opt_refresh_always); -} - -/* - * Called after changing "compl_leader". - * Show the popup menu with a different set of matches. - * May also search for matches again if the previous search was interrupted. - */ -static void ins_compl_new_leader(void) -{ - ins_compl_del_pum(); - ins_compl_delete(); - ins_bytes(compl_leader + ins_compl_len()); - compl_used_match = FALSE; - - if (compl_started) - ins_compl_set_original_text(compl_leader); - else { - spell_bad_len = 0; /* need to redetect bad word */ - /* - * Matches were cleared, need to search for them now. First display - * the changed text before the cursor. Set "compl_restarting" to - * avoid that the first match is inserted. - */ - update_screen(0); - compl_restarting = TRUE; - if (ins_complete(Ctrl_N) == FAIL) - compl_cont_status = 0; - compl_restarting = FALSE; - } - - compl_enter_selects = !compl_used_match; - - /* Show the popup menu with a different set of matches. */ - ins_compl_show_pum(); - - /* Don't let Enter select the original text when there is no popup menu. */ - if (compl_match_array == NULL) - compl_enter_selects = FALSE; -} - -/* - * Return the length of the completion, from the completion start column to - * the cursor column. Making sure it never goes below zero. - */ -static int ins_compl_len(void) -{ - int off = (int)curwin->w_cursor.col - (int)compl_col; - - if (off < 0) - return 0; - return off; -} - -/* - * Append one character to the match leader. May reduce the number of - * matches. - */ -static void ins_compl_addleader(int c) -{ - int cc; - - if (has_mbyte && (cc = (*mb_char2len)(c)) > 1) { - char_u buf[MB_MAXBYTES + 1]; - - (*mb_char2bytes)(c, buf); - buf[cc] = NUL; - ins_char_bytes(buf, cc); - if (compl_opt_refresh_always) - AppendToRedobuff(buf); - } else { - ins_char(c); - if (compl_opt_refresh_always) - AppendCharToRedobuff(c); - } - - /* If we didn't complete finding matches we must search again. */ - if (ins_compl_need_restart()) - ins_compl_restart(); - - /* When 'always' is set, don't reset compl_leader. While completing, - * cursor doesn't point original position, changing compl_leader would - * break redo. */ - if (!compl_opt_refresh_always) { - free(compl_leader); - compl_leader = vim_strnsave(ml_get_curline() + compl_col, - (int)(curwin->w_cursor.col - compl_col)); - if (compl_leader != NULL) - ins_compl_new_leader(); - } -} - -/* - * Setup for finding completions again without leaving CTRL-X mode. Used when - * BS or a key was typed while still searching for matches. - */ -static void ins_compl_restart(void) -{ - ins_compl_free(); - compl_started = FALSE; - compl_matches = 0; - compl_cont_status = 0; - compl_cont_mode = 0; -} - -/* - * Set the first match, the original text. - */ -static void ins_compl_set_original_text(char_u *str) -{ - char_u *p; - - /* Replace the original text entry. */ - if (compl_first_match->cp_flags & ORIGINAL_TEXT) { /* safety check */ - p = vim_strsave(str); - if (p != NULL) { - free(compl_first_match->cp_str); - compl_first_match->cp_str = p; - } - } -} - -/* - * Append one character to the match leader. May reduce the number of - * matches. - */ -static void ins_compl_addfrommatch(void) -{ - char_u *p; - int len = (int)curwin->w_cursor.col - (int)compl_col; - int c; - compl_T *cp; - - p = compl_shown_match->cp_str; - if ((int)STRLEN(p) <= len) { /* the match is too short */ - /* When still at the original match use the first entry that matches - * the leader. */ - if (compl_shown_match->cp_flags & ORIGINAL_TEXT) { - p = NULL; - for (cp = compl_shown_match->cp_next; cp != NULL - && cp != compl_first_match; cp = cp->cp_next) { - if (compl_leader == NULL - || ins_compl_equal(cp, compl_leader, - (int)STRLEN(compl_leader))) { - p = cp->cp_str; - break; - } - } - if (p == NULL || (int)STRLEN(p) <= len) - return; - } else - return; - } - p += len; - c = PTR2CHAR(p); - ins_compl_addleader(c); -} - -/* - * Prepare for Insert mode completion, or stop it. - * Called just after typing a character in Insert mode. - * Returns TRUE when the character is not to be inserted; - */ -static int ins_compl_prep(int c) -{ - char_u *ptr; - int want_cindent; - int retval = FALSE; - - /* Forget any previous 'special' messages if this is actually - * a ^X mode key - bar ^R, in which case we wait to see what it gives us. - */ - if (c != Ctrl_R && vim_is_ctrl_x_key(c)) - edit_submode_extra = NULL; - - /* Ignore end of Select mode mapping and mouse scroll buttons. */ - if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSELEFT || c == K_MOUSERIGHT) - return retval; - - /* Set "compl_get_longest" when finding the first matches. */ - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET - || (ctrl_x_mode == 0 && !compl_started)) { - compl_get_longest = (vim_strchr(p_cot, 'l') != NULL); - compl_used_match = TRUE; - } - - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET) { - /* - * We have just typed CTRL-X and aren't quite sure which CTRL-X mode - * it will be yet. Now we decide. - */ - switch (c) { - case Ctrl_E: - case Ctrl_Y: - ctrl_x_mode = CTRL_X_SCROLL; - if (!(State & REPLACE_FLAG)) - edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); - else - edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); - edit_submode_pre = NULL; - showmode(); - break; - case Ctrl_L: - ctrl_x_mode = CTRL_X_WHOLE_LINE; - break; - case Ctrl_F: - ctrl_x_mode = CTRL_X_FILES; - break; - case Ctrl_K: - ctrl_x_mode = CTRL_X_DICTIONARY; - break; - case Ctrl_R: - /* Simply allow ^R to happen without affecting ^X mode */ - break; - case Ctrl_T: - ctrl_x_mode = CTRL_X_THESAURUS; - break; - case Ctrl_U: - ctrl_x_mode = CTRL_X_FUNCTION; - break; - case Ctrl_O: - ctrl_x_mode = CTRL_X_OMNI; - break; - case 's': - case Ctrl_S: - ctrl_x_mode = CTRL_X_SPELL; - ++emsg_off; /* Avoid getting the E756 error twice. */ - spell_back_to_badword(); - --emsg_off; - break; - case Ctrl_RSB: - ctrl_x_mode = CTRL_X_TAGS; - break; - case Ctrl_I: - case K_S_TAB: - ctrl_x_mode = CTRL_X_PATH_PATTERNS; - break; - case Ctrl_D: - ctrl_x_mode = CTRL_X_PATH_DEFINES; - break; - case Ctrl_V: - case Ctrl_Q: - ctrl_x_mode = CTRL_X_CMDLINE; - break; - case Ctrl_P: - case Ctrl_N: - /* ^X^P means LOCAL expansion if nothing interrupted (eg we - * just started ^X mode, or there were enough ^X's to cancel - * the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below) - * do normal expansion when interrupting a different mode (say - * ^X^F^X^P or ^P^X^X^P, see below) - * nothing changes if interrupting mode 0, (eg, the flag - * doesn't change when going to ADDING mode -- Acevedo */ - if (!(compl_cont_status & CONT_INTRPT)) - compl_cont_status |= CONT_LOCAL; - else if (compl_cont_mode != 0) - compl_cont_status &= ~CONT_LOCAL; - /* FALLTHROUGH */ - default: - /* If we have typed at least 2 ^X's... for modes != 0, we set - * compl_cont_status = 0 (eg, as if we had just started ^X - * mode). - * For mode 0, we set "compl_cont_mode" to an impossible - * value, in both cases ^X^X can be used to restart the same - * mode (avoiding ADDING mode). - * Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start - * 'complete' and local ^P expansions respectively. - * In mode 0 an extra ^X is needed since ^X^P goes to ADDING - * mode -- Acevedo */ - if (c == Ctrl_X) { - if (compl_cont_mode != 0) - compl_cont_status = 0; - else - compl_cont_mode = CTRL_X_NOT_DEFINED_YET; - } - ctrl_x_mode = 0; - edit_submode = NULL; - showmode(); - break; - } - } else if (ctrl_x_mode != 0) { - /* We're already in CTRL-X mode, do we stay in it? */ - if (!vim_is_ctrl_x_key(c)) { - if (ctrl_x_mode == CTRL_X_SCROLL) - ctrl_x_mode = 0; - else - ctrl_x_mode = CTRL_X_FINISHED; - edit_submode = NULL; - } - showmode(); - } - - if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) { - /* Show error message from attempted keyword completion (probably - * 'Pattern not found') until another key is hit, then go back to - * showing what mode we are in. */ - showmode(); - if ((ctrl_x_mode == 0 && c != Ctrl_N && c != Ctrl_P && c != Ctrl_R - && !ins_compl_pum_key(c)) - || ctrl_x_mode == CTRL_X_FINISHED) { - /* Get here when we have finished typing a sequence of ^N and - * ^P or other completion characters in CTRL-X mode. Free up - * memory that was used, and make sure we can redo the insert. */ - if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) { - /* - * If any of the original typed text has been changed, eg when - * ignorecase is set, we must add back-spaces to the redo - * buffer. We add as few as necessary to delete just the part - * of the original text that has changed. - * When using the longest match, edited the match or used - * CTRL-E then don't use the current match. - */ - if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) - ptr = compl_curr_match->cp_str; - else - ptr = NULL; - ins_compl_fixRedoBufForLeader(ptr); - } - - want_cindent = (can_cindent && cindent_on()); - /* - * When completing whole lines: fix indent for 'cindent'. - * Otherwise, break line if it's too long. - */ - if (compl_cont_mode == CTRL_X_WHOLE_LINE) { - /* re-indent the current line */ - if (want_cindent) { - do_c_expr_indent(); - want_cindent = FALSE; /* don't do it again */ - } - } else { - int prev_col = curwin->w_cursor.col; - - /* put the cursor on the last char, for 'tw' formatting */ - if (prev_col > 0) - dec_cursor(); - if (stop_arrow() == OK) - insertchar(NUL, 0, -1); - if (prev_col > 0 - && ml_get_curline()[curwin->w_cursor.col] != NUL) - inc_cursor(); - } - - /* If the popup menu is displayed pressing CTRL-Y means accepting - * the selection without inserting anything. When - * compl_enter_selects is set the Enter key does the same. */ - if ((c == Ctrl_Y || (compl_enter_selects - && (c == CAR || c == K_KENTER || c == NL))) - && pum_visible()) - retval = TRUE; - - /* CTRL-E means completion is Ended, go back to the typed text. */ - if (c == Ctrl_E) { - ins_compl_delete(); - if (compl_leader != NULL) - ins_bytes(compl_leader + ins_compl_len()); - else if (compl_first_match != NULL) - ins_bytes(compl_orig_text + ins_compl_len()); - retval = TRUE; - } - - auto_format(FALSE, TRUE); - - ins_compl_free(); - compl_started = FALSE; - compl_matches = 0; - msg_clr_cmdline(); /* necessary for "noshowmode" */ - ctrl_x_mode = 0; - compl_enter_selects = FALSE; - if (edit_submode != NULL) { - edit_submode = NULL; - showmode(); - } - - /* - * Indent now if a key was typed that is in 'cinkeys'. - */ - if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) - do_c_expr_indent(); - /* Trigger the CompleteDone event to give scripts a chance to act - * upon the completion. */ - apply_autocmds(EVENT_COMPLETEDONE, NULL, NULL, FALSE, curbuf); - } - } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) - /* Trigger the CompleteDone event to give scripts a chance to act - * upon the (possibly failed) completion. */ - apply_autocmds(EVENT_COMPLETEDONE, NULL, NULL, FALSE, curbuf); - - /* reset continue_* if we left expansion-mode, if we stay they'll be - * (re)set properly in ins_complete() */ - if (!vim_is_ctrl_x_key(c)) { - compl_cont_status = 0; - compl_cont_mode = 0; - } - - return retval; -} - -/* - * Fix the redo buffer for the completion leader replacing some of the typed - * text. This inserts backspaces and appends the changed text. - * "ptr" is the known leader text or NUL. - */ -static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) -{ - int len; - char_u *p; - char_u *ptr = ptr_arg; - - if (ptr == NULL) { - if (compl_leader != NULL) - ptr = compl_leader; - else - return; /* nothing to do */ - } - if (compl_orig_text != NULL) { - p = compl_orig_text; - for (len = 0; p[len] != NUL && p[len] == ptr[len]; ++len) - ; - if (len > 0) - len -= (*mb_head_off)(p, p + len); - for (p += len; *p != NUL; mb_ptr_adv(p)) - AppendCharToRedobuff(K_BS); - } else - len = 0; - if (ptr != NULL) - AppendToRedobuffLit(ptr + len, -1); -} - -/* - * Loops through the list of windows, loaded-buffers or non-loaded-buffers - * (depending on flag) starting from buf and looking for a non-scanned - * buffer (other than curbuf). curbuf is special, if it is called with - * buf=curbuf then it has to be the first call for a given flag/expansion. - * - * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo - */ -static buf_T *ins_compl_next_buf(buf_T *buf, int flag) -{ - static win_T *wp; - - if (flag == 'w') { /* just windows */ - if (buf == curbuf) /* first call for this flag/expansion */ - wp = curwin; - while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin - && wp->w_buffer->b_scanned) - ; - buf = wp->w_buffer; - } else - /* 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' - * (unlisted buffers) - * When completing whole lines skip unloaded buffers. */ - while ((buf = (buf->b_next != NULL ? buf->b_next : firstbuf)) != curbuf - && ((flag == 'U' - ? buf->b_p_bl - : (!buf->b_p_bl - || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) - || buf->b_scanned)) - ; - return buf; -} - -static void expand_by_function(int type, char_u *base); - -/* - * Execute user defined complete function 'completefunc' or 'omnifunc', and - * get matches in "matches". - */ -static void -expand_by_function ( - int type, /* CTRL_X_OMNI or CTRL_X_FUNCTION */ - char_u *base -) -{ - list_T *matchlist = NULL; - dict_T *matchdict = NULL; - char_u *args[2]; - char_u *funcname; - pos_T pos; - win_T *curwin_save; - buf_T *curbuf_save; - typval_T rettv; - - funcname = (type == CTRL_X_FUNCTION) ? curbuf->b_p_cfu : curbuf->b_p_ofu; - if (*funcname == NUL) - return; - - /* Call 'completefunc' to obtain the list of matches. */ - args[0] = (char_u *)"0"; - args[1] = base; - - pos = curwin->w_cursor; - curwin_save = curwin; - curbuf_save = curbuf; - - /* Call a function, which returns a list or dict. */ - if (call_vim_function(funcname, 2, args, FALSE, FALSE, &rettv) == OK) { - switch (rettv.v_type) { - case VAR_LIST: - matchlist = rettv.vval.v_list; - break; - case VAR_DICT: - matchdict = rettv.vval.v_dict; - break; - default: - /* TODO: Give error message? */ - clear_tv(&rettv); - break; - } - } - - if (curwin_save != curwin || curbuf_save != curbuf) { - EMSG(_(e_complwin)); - goto theend; - } - curwin->w_cursor = pos; /* restore the cursor position */ - check_cursor(); - if (!equalpos(curwin->w_cursor, pos)) { - EMSG(_(e_compldel)); - goto theend; - } - - if (matchlist != NULL) - ins_compl_add_list(matchlist); - else if (matchdict != NULL) - ins_compl_add_dict(matchdict); - -theend: - if (matchdict != NULL) - dict_unref(matchdict); - if (matchlist != NULL) - list_unref(matchlist); -} - -/* - * Add completions from a list. - */ -static void ins_compl_add_list(list_T *list) -{ - listitem_T *li; - int dir = compl_direction; - - /* Go through the List with matches and add each of them. */ - for (li = list->lv_first; li != NULL; li = li->li_next) { - if (ins_compl_add_tv(&li->li_tv, dir) == OK) - /* if dir was BACKWARD then honor it just once */ - dir = FORWARD; - else if (did_emsg) - break; - } -} - -/* - * Add completions from a dict. - */ -static void ins_compl_add_dict(dict_T *dict) -{ - dictitem_T *di_refresh; - dictitem_T *di_words; - - /* Check for optional "refresh" item. */ - compl_opt_refresh_always = FALSE; - di_refresh = dict_find(dict, (char_u *)"refresh", 7); - if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - char_u *v = di_refresh->di_tv.vval.v_string; - - if (v != NULL && STRCMP(v, (char_u *)"always") == 0) - compl_opt_refresh_always = TRUE; - } - - /* Add completions from a "words" list. */ - di_words = dict_find(dict, (char_u *)"words", 5); - if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) - ins_compl_add_list(di_words->di_tv.vval.v_list); -} - -/* - * Add a match to the list of matches from a typeval_T. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -int ins_compl_add_tv(typval_T *tv, int dir) -{ - char_u *word; - int icase = FALSE; - int adup = FALSE; - int aempty = FALSE; - char_u *(cptext[CPT_COUNT]); - - if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - word = get_dict_string(tv->vval.v_dict, (char_u *)"word", FALSE); - cptext[CPT_ABBR] = get_dict_string(tv->vval.v_dict, - (char_u *)"abbr", FALSE); - cptext[CPT_MENU] = get_dict_string(tv->vval.v_dict, - (char_u *)"menu", FALSE); - cptext[CPT_KIND] = get_dict_string(tv->vval.v_dict, - (char_u *)"kind", FALSE); - cptext[CPT_INFO] = get_dict_string(tv->vval.v_dict, - (char_u *)"info", FALSE); - if (get_dict_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL) - icase = get_dict_number(tv->vval.v_dict, (char_u *)"icase"); - if (get_dict_string(tv->vval.v_dict, (char_u *)"dup", FALSE) != NULL) - adup = get_dict_number(tv->vval.v_dict, (char_u *)"dup"); - if (get_dict_string(tv->vval.v_dict, (char_u *)"empty", FALSE) != NULL) - aempty = get_dict_number(tv->vval.v_dict, (char_u *)"empty"); - } else { - word = get_tv_string_chk(tv); - memset(cptext, 0, sizeof(cptext)); - } - if (word == NULL || (!aempty && *word == NUL)) - return FAIL; - return ins_compl_add(word, -1, icase, NULL, cptext, dir, 0, adup); -} - -/* - * Get the next expansion(s), using "compl_pattern". - * The search starts at position "ini" in curbuf and in the direction - * compl_direction. - * When "compl_started" is FALSE start at that position, otherwise continue - * where we stopped searching before. - * This may return before finding all the matches. - * Return the total number of matches or -1 if still unknown -- Acevedo - */ -static int ins_compl_get_exp(pos_T *ini) -{ - static pos_T first_match_pos; - static pos_T last_match_pos; - static char_u *e_cpt = (char_u *)""; /* curr. entry in 'complete' */ - static int found_all = FALSE; /* Found all matches of a - certain type. */ - static buf_T *ins_buf = NULL; /* buffer being scanned */ - - pos_T *pos; - char_u **matches; - int save_p_scs; - int save_p_ws; - int save_p_ic; - int i; - int num_matches; - int len; - int found_new_match; - int type = ctrl_x_mode; - char_u *ptr; - char_u *dict = NULL; - int dict_f = 0; - compl_T *old_match; - int set_match_pos; - - if (!compl_started) { - for (ins_buf = firstbuf; ins_buf != NULL; ins_buf = ins_buf->b_next) - ins_buf->b_scanned = 0; - found_all = FALSE; - ins_buf = curbuf; - e_cpt = (compl_cont_status & CONT_LOCAL) - ? (char_u *)"." : curbuf->b_p_cpt; - last_match_pos = first_match_pos = *ini; - } - - old_match = compl_curr_match; /* remember the last current match */ - pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; - /* For ^N/^P loop over all the flags/windows/buffers in 'complete' */ - for (;; ) { - found_new_match = FAIL; - set_match_pos = FALSE; - - /* For ^N/^P pick a new entry from e_cpt if compl_started is off, - * or if found_all says this entry is done. For ^X^L only use the - * entries from 'complete' that look in loaded buffers. */ - if ((ctrl_x_mode == 0 || ctrl_x_mode == CTRL_X_WHOLE_LINE) - && (!compl_started || found_all)) { - found_all = FALSE; - while (*e_cpt == ',' || *e_cpt == ' ') - e_cpt++; - if (*e_cpt == '.' && !curbuf->b_scanned) { - ins_buf = curbuf; - first_match_pos = *ini; - /* So that ^N can match word immediately after cursor */ - if (ctrl_x_mode == 0) - dec(&first_match_pos); - last_match_pos = first_match_pos; - type = 0; - - /* Remember the first match so that the loop stops when we - * wrap and come back there a second time. */ - set_match_pos = TRUE; - } else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL - && (ins_buf = - ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { - /* Scan a buffer, but not the current one. */ - if (ins_buf->b_ml.ml_mfp != NULL) { /* loaded buffer */ - compl_started = TRUE; - first_match_pos.col = last_match_pos.col = 0; - first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; - last_match_pos.lnum = 0; - type = 0; - } else { /* unloaded buffer, scan like dictionary */ - found_all = TRUE; - if (ins_buf->b_fname == NULL) - continue; - type = CTRL_X_DICTIONARY; - dict = ins_buf->b_fname; - dict_f = DICT_EXACT; - } - vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), - ins_buf->b_fname == NULL - ? buf_spname(ins_buf) - : ins_buf->b_sfname == NULL - ? ins_buf->b_fname - : ins_buf->b_sfname); - (void)msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); - } else if (*e_cpt == NUL) - break; - else { - if (ctrl_x_mode == CTRL_X_WHOLE_LINE) - type = -1; - else if (*e_cpt == 'k' || *e_cpt == 's') { - if (*e_cpt == 'k') - type = CTRL_X_DICTIONARY; - else - type = CTRL_X_THESAURUS; - if (*++e_cpt != ',' && *e_cpt != NUL) { - dict = e_cpt; - dict_f = DICT_FIRST; - } - } else if (*e_cpt == 'i') - type = CTRL_X_PATH_PATTERNS; - else if (*e_cpt == 'd') - type = CTRL_X_PATH_DEFINES; - else if (*e_cpt == ']' || *e_cpt == 't') { - type = CTRL_X_TAGS; - vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags.")); - (void)msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R)); - } else - type = -1; - - /* in any case e_cpt is advanced to the next entry */ - (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); - - found_all = TRUE; - if (type == -1) - continue; - } - } - - switch (type) { - case -1: - break; - case CTRL_X_PATH_PATTERNS: - case CTRL_X_PATH_DEFINES: - find_pattern_in_path(compl_pattern, compl_direction, - (int)STRLEN(compl_pattern), FALSE, FALSE, - (type == CTRL_X_PATH_DEFINES - && !(compl_cont_status & CONT_SOL)) - ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND, - (linenr_T)1, (linenr_T)MAXLNUM); - break; - - case CTRL_X_DICTIONARY: - case CTRL_X_THESAURUS: - ins_compl_dictionaries( - dict != NULL ? dict - : (type == CTRL_X_THESAURUS - ? (*curbuf->b_p_tsr == NUL - ? p_tsr - : curbuf->b_p_tsr) - : (*curbuf->b_p_dict == NUL - ? p_dict - : curbuf->b_p_dict)), - compl_pattern, - dict != NULL ? dict_f - : 0, type == CTRL_X_THESAURUS); - dict = NULL; - break; - - case CTRL_X_TAGS: - /* set p_ic according to p_ic, p_scs and pat for find_tags(). */ - save_p_ic = p_ic; - p_ic = ignorecase(compl_pattern); - - /* Find up to TAG_MANY matches. Avoids that an enormous number - * of matches is found when compl_pattern is empty */ - if (find_tags(compl_pattern, &num_matches, &matches, - TAG_REGEXP | TAG_NAMES | TAG_NOIC | - TAG_INS_COMP | (ctrl_x_mode ? TAG_VERBOSE : 0), - TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) { - ins_compl_add_matches(num_matches, matches, p_ic); - } - p_ic = save_p_ic; - break; - - case CTRL_X_FILES: - if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, - EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) { - - /* May change home directory back to "~". */ - tilde_replace(compl_pattern, num_matches, matches); - ins_compl_add_matches(num_matches, matches, p_fic || p_wic); - } - break; - - case CTRL_X_CMDLINE: - if (expand_cmdline(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), - &num_matches, &matches) == EXPAND_OK) - ins_compl_add_matches(num_matches, matches, FALSE); - break; - - case CTRL_X_FUNCTION: - case CTRL_X_OMNI: - expand_by_function(type, compl_pattern); - break; - - case CTRL_X_SPELL: - num_matches = expand_spelling(first_match_pos.lnum, - compl_pattern, &matches); - if (num_matches > 0) - ins_compl_add_matches(num_matches, matches, p_ic); - break; - - default: /* normal ^P/^N and ^X^L */ - /* - * If 'infercase' is set, don't use 'smartcase' here - */ - save_p_scs = p_scs; - if (ins_buf->b_p_inf) - p_scs = FALSE; - - /* Buffers other than curbuf are scanned from the beginning or the - * end but never from the middle, thus setting nowrapscan in this - * buffers is a good idea, on the other hand, we always set - * wrapscan for curbuf to avoid missing matches -- Acevedo,Webb */ - save_p_ws = p_ws; - if (ins_buf != curbuf) - p_ws = FALSE; - else if (*e_cpt == '.') - p_ws = TRUE; - for (;; ) { - int flags = 0; - - ++msg_silent; /* Don't want messages for wrapscan. */ - - /* ctrl_x_mode == CTRL_X_WHOLE_LINE || word-wise search that - * has added a word that was at the beginning of the line */ - if ( ctrl_x_mode == CTRL_X_WHOLE_LINE - || (compl_cont_status & CONT_SOL)) - found_new_match = search_for_exact_line(ins_buf, pos, - compl_direction, compl_pattern); - else - found_new_match = searchit(NULL, ins_buf, pos, - compl_direction, - compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, - RE_LAST, (linenr_T)0, NULL); - --msg_silent; - if (!compl_started || set_match_pos) { - /* set "compl_started" even on fail */ - compl_started = TRUE; - first_match_pos = *pos; - last_match_pos = *pos; - set_match_pos = FALSE; - } else if (first_match_pos.lnum == last_match_pos.lnum - && first_match_pos.col == last_match_pos.col) - found_new_match = FAIL; - if (found_new_match == FAIL) { - if (ins_buf == curbuf) - found_all = TRUE; - break; - } - - /* when ADDING, the text before the cursor matches, skip it */ - if ( (compl_cont_status & CONT_ADDING) && ins_buf == curbuf - && ini->lnum == pos->lnum - && ini->col == pos->col) - continue; - ptr = ml_get_buf(ins_buf, pos->lnum, FALSE) + pos->col; - if (ctrl_x_mode == CTRL_X_WHOLE_LINE) { - if (compl_cont_status & CONT_ADDING) { - if (pos->lnum >= ins_buf->b_ml.ml_line_count) - continue; - ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); - if (!p_paste) - ptr = skipwhite(ptr); - } - len = (int)STRLEN(ptr); - } else { - char_u *tmp_ptr = ptr; - - if (compl_cont_status & CONT_ADDING) { - tmp_ptr += compl_length; - /* Skip if already inside a word. */ - if (vim_iswordp(tmp_ptr)) - continue; - /* Find start of next word. */ - tmp_ptr = find_word_start(tmp_ptr); - } - /* Find end of this word. */ - tmp_ptr = find_word_end(tmp_ptr); - len = (int)(tmp_ptr - ptr); - - if ((compl_cont_status & CONT_ADDING) - && len == compl_length) { - if (pos->lnum < ins_buf->b_ml.ml_line_count) { - /* Try next line, if any. the new word will be - * "join" as if the normal command "J" was used. - * IOSIZE is always greater than - * compl_length, so the next STRNCPY always - * works -- Acevedo */ - STRNCPY(IObuff, ptr, len); - ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); - tmp_ptr = ptr = skipwhite(ptr); - /* Find start of next word. */ - tmp_ptr = find_word_start(tmp_ptr); - /* Find end of next word. */ - tmp_ptr = find_word_end(tmp_ptr); - if (tmp_ptr > ptr) { - if (*ptr != ')' && IObuff[len - 1] != TAB) { - if (IObuff[len - 1] != ' ') - IObuff[len++] = ' '; - /* IObuf =~ "\k.* ", thus len >= 2 */ - if (p_js - && (IObuff[len - 2] == '.' - || (vim_strchr(p_cpo, CPO_JOINSP) - == NULL - && (IObuff[len - 2] == '?' - || IObuff[len - 2] == '!')))) - IObuff[len++] = ' '; - } - /* copy as much as possible of the new word */ - if (tmp_ptr - ptr >= IOSIZE - len) - tmp_ptr = ptr + IOSIZE - len - 1; - STRNCPY(IObuff + len, ptr, tmp_ptr - ptr); - len += (int)(tmp_ptr - ptr); - flags |= CONT_S_IPOS; - } - IObuff[len] = NUL; - ptr = IObuff; - } - if (len == compl_length) - continue; - } - } - if (ins_compl_add_infercase(ptr, len, p_ic, - ins_buf == curbuf ? NULL : ins_buf->b_sfname, - 0, flags) != NOTDONE) { - found_new_match = OK; - break; - } - } - p_scs = save_p_scs; - p_ws = save_p_ws; - } - - /* check if compl_curr_match has changed, (e.g. other type of - * expansion added something) */ - if (type != 0 && compl_curr_match != old_match) - found_new_match = OK; - - /* break the loop for specialized modes (use 'complete' just for the - * generic ctrl_x_mode == 0) or when we've found a new match */ - if ((ctrl_x_mode != 0 && ctrl_x_mode != CTRL_X_WHOLE_LINE) - || found_new_match != FAIL) { - if (got_int) - break; - /* Fill the popup menu as soon as possible. */ - if (type != -1) - ins_compl_check_keys(0); - - if ((ctrl_x_mode != 0 && ctrl_x_mode != CTRL_X_WHOLE_LINE) - || compl_interrupted) - break; - compl_started = TRUE; - } else { - /* Mark a buffer scanned when it has been scanned completely */ - if (type == 0 || type == CTRL_X_PATH_PATTERNS) - ins_buf->b_scanned = TRUE; - - compl_started = FALSE; - } - } - compl_started = TRUE; - - if ((ctrl_x_mode == 0 || ctrl_x_mode == CTRL_X_WHOLE_LINE) - && *e_cpt == NUL) /* Got to end of 'complete' */ - found_new_match = FAIL; - - i = -1; /* total of matches, unknown */ - if (found_new_match == FAIL - || (ctrl_x_mode != 0 && ctrl_x_mode != CTRL_X_WHOLE_LINE)) - i = ins_compl_make_cyclic(); - - /* If several matches were added (FORWARD) or the search failed and has - * just been made cyclic then we have to move compl_curr_match to the next - * or previous entry (if any) -- Acevedo */ - compl_curr_match = compl_direction == FORWARD ? old_match->cp_next - : old_match->cp_prev; - if (compl_curr_match == NULL) - compl_curr_match = old_match; - return i; -} - -/* Delete the old text being completed. */ -static void ins_compl_delete(void) -{ - int i; - - /* - * In insert mode: Delete the typed part. - * In replace mode: Put the old characters back, if any. - */ - i = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0); - backspace_until_column(i); - changed_cline_bef_curs(); -} - -/* Insert the new text being completed. */ -static void ins_compl_insert(void) -{ - ins_bytes(compl_shown_match->cp_str + ins_compl_len()); - if (compl_shown_match->cp_flags & ORIGINAL_TEXT) - compl_used_match = FALSE; - else - compl_used_match = TRUE; -} - -/* - * Fill in the next completion in the current direction. - * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to - * get more completions. If it is FALSE, then we just do nothing when there - * are no more completions in a given direction. The latter case is used when - * we are still in the middle of finding completions, to allow browsing - * through the ones found so far. - * Return the total number of matches, or -1 if still unknown -- webb. - * - * compl_curr_match is currently being used by ins_compl_get_exp(), so we use - * compl_shown_match here. - * - * Note that this function may be called recursively once only. First with - * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn - * calls this function with "allow_get_expansion" FALSE. - */ -static int -ins_compl_next ( - int allow_get_expansion, - int count, /* repeat completion this many times; should - be at least 1 */ - int insert_match /* Insert the newly selected match */ -) -{ - int num_matches = -1; - int i; - int todo = count; - compl_T *found_compl = NULL; - int found_end = FALSE; - int advance; - - /* When user complete function return -1 for findstart which is next - * time of 'always', compl_shown_match become NULL. */ - if (compl_shown_match == NULL) - return -1; - - if (compl_leader != NULL - && (compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0) { - /* Set "compl_shown_match" to the actually shown match, it may differ - * when "compl_leader" is used to omit some of the matches. */ - while (!ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader)) - && compl_shown_match->cp_next != NULL - && compl_shown_match->cp_next != compl_first_match) - compl_shown_match = compl_shown_match->cp_next; - - /* If we didn't find it searching forward, and compl_shows_dir is - * backward, find the last match. */ - if (compl_shows_dir == BACKWARD - && !ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader)) - && (compl_shown_match->cp_next == NULL - || compl_shown_match->cp_next == compl_first_match)) { - while (!ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader)) - && compl_shown_match->cp_prev != NULL - && compl_shown_match->cp_prev != compl_first_match) - compl_shown_match = compl_shown_match->cp_prev; - } - } - - if (allow_get_expansion && insert_match - && (!(compl_get_longest || compl_restarting) || compl_used_match)) - /* Delete old text to be replaced */ - ins_compl_delete(); - - /* When finding the longest common text we stick at the original text, - * don't let CTRL-N or CTRL-P move to the first match. */ - advance = count != 1 || !allow_get_expansion || !compl_get_longest; - - /* When restarting the search don't insert the first match either. */ - if (compl_restarting) { - advance = FALSE; - compl_restarting = FALSE; - } - - /* Repeat this for when or is typed. But don't wrap - * around. */ - while (--todo >= 0) { - if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; - found_end = (compl_first_match != NULL - && (compl_shown_match->cp_next == compl_first_match - || compl_shown_match == compl_first_match)); - } else if (compl_shows_dir == BACKWARD - && compl_shown_match->cp_prev != NULL) { - found_end = (compl_shown_match == compl_first_match); - compl_shown_match = compl_shown_match->cp_prev; - found_end |= (compl_shown_match == compl_first_match); - } else { - if (!allow_get_expansion) { - if (advance) { - if (compl_shows_dir == BACKWARD) - compl_pending -= todo + 1; - else - compl_pending += todo + 1; - } - return -1; - } - - if (advance) { - if (compl_shows_dir == BACKWARD) - --compl_pending; - else - ++compl_pending; - } - - /* Find matches. */ - num_matches = ins_compl_get_exp(&compl_startpos); - - /* handle any pending completions */ - while (compl_pending != 0 && compl_direction == compl_shows_dir - && advance) { - if (compl_pending > 0 && compl_shown_match->cp_next != NULL) { - compl_shown_match = compl_shown_match->cp_next; - --compl_pending; - } - if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) { - compl_shown_match = compl_shown_match->cp_prev; - ++compl_pending; - } else - break; - } - found_end = FALSE; - } - if ((compl_shown_match->cp_flags & ORIGINAL_TEXT) == 0 - && compl_leader != NULL - && !ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader))) - ++todo; - else - /* Remember a matching item. */ - found_compl = compl_shown_match; - - /* Stop at the end of the list when we found a usable match. */ - if (found_end) { - if (found_compl != NULL) { - compl_shown_match = found_compl; - break; - } - todo = 1; /* use first usable match after wrapping around */ - } - } - - /* Insert the text of the new completion, or the compl_leader. */ - if (insert_match) { - if (!compl_get_longest || compl_used_match) - ins_compl_insert(); - else - ins_bytes(compl_leader + ins_compl_len()); - } else - compl_used_match = FALSE; - - if (!allow_get_expansion) { - /* may undisplay the popup menu first */ - ins_compl_upd_pum(); - - /* redraw to show the user what was inserted */ - update_screen(0); - - /* display the updated popup menu */ - ins_compl_show_pum(); - - /* Delete old text to be replaced, since we're still searching and - * don't want to match ourselves! */ - ins_compl_delete(); - } - - /* Enter will select a match when the match wasn't inserted and the popup - * menu is visible. */ - compl_enter_selects = !insert_match && compl_match_array != NULL; - - /* - * Show the file name for the match (if any) - * Truncate the file name to avoid a wait for return. - */ - if (compl_shown_match->cp_fname != NULL) { - STRCPY(IObuff, "match in file "); - i = (vim_strsize(compl_shown_match->cp_fname) + 16) - sc_col; - if (i <= 0) - i = 0; - else - STRCAT(IObuff, "<"); - STRCAT(IObuff, compl_shown_match->cp_fname + i); - msg(IObuff); - redraw_cmdline = FALSE; /* don't overwrite! */ - } - - return num_matches; -} - -/* - * Call this while finding completions, to check whether the user has hit a key - * that should change the currently displayed completion, or exit completion - * mode. Also, when compl_pending is not zero, show a completion as soon as - * possible. -- webb - * "frequency" specifies out of how many calls we actually check. - */ -void ins_compl_check_keys(int frequency) -{ - static int count = 0; - - int c; - - /* Don't check when reading keys from a script. That would break the test - * scripts */ - if (using_script()) - return; - - /* Only do this at regular intervals */ - if (++count < frequency) - return; - count = 0; - - /* Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key() - * can't do its work correctly. */ - c = vpeekc_any(); - if (c != NUL) { - if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R) { - c = safe_vgetc(); /* Eat the character */ - compl_shows_dir = ins_compl_key2dir(c); - (void)ins_compl_next(FALSE, ins_compl_key2count(c), - c != K_UP && c != K_DOWN); - } else { - /* Need to get the character to have KeyTyped set. We'll put it - * back with vungetc() below. But skip K_IGNORE. */ - c = safe_vgetc(); - if (c != K_IGNORE) { - /* Don't interrupt completion when the character wasn't typed, - * e.g., when doing @q to replay keys. */ - if (c != Ctrl_R && KeyTyped) - compl_interrupted = TRUE; - - vungetc(c); - } - } - } - if (compl_pending != 0 && !got_int) { - int todo = compl_pending > 0 ? compl_pending : -compl_pending; - - compl_pending = 0; - (void)ins_compl_next(FALSE, todo, TRUE); - } -} - -/* - * Decide the direction of Insert mode complete from the key typed. - * Returns BACKWARD or FORWARD. - */ -static int ins_compl_key2dir(int c) -{ - if (c == Ctrl_P || c == Ctrl_L - || (pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP - || c == K_S_UP || c == K_UP))) - return BACKWARD; - return FORWARD; -} - -/* - * Return TRUE for keys that are used for completion only when the popup menu - * is visible. - */ -static int ins_compl_pum_key(int c) -{ - return pum_visible() && (c == K_PAGEUP || c == K_KPAGEUP || c == K_S_UP - || c == K_PAGEDOWN || c == K_KPAGEDOWN || c == - K_S_DOWN - || c == K_UP || c == K_DOWN); -} - -/* - * Decide the number of completions to move forward. - * Returns 1 for most keys, height of the popup menu for page-up/down keys. - */ -static int ins_compl_key2count(int c) -{ - int h; - - if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) { - h = pum_get_height(); - if (h > 3) - h -= 2; /* keep some context */ - return h; - } - return 1; -} - -/* - * Return TRUE if completion with "c" should insert the match, FALSE if only - * to change the currently selected completion. - */ -static int ins_compl_use_match(int c) -{ - switch (c) { - case K_UP: - case K_DOWN: - case K_PAGEDOWN: - case K_KPAGEDOWN: - case K_S_DOWN: - case K_PAGEUP: - case K_KPAGEUP: - case K_S_UP: - return FALSE; - } - return TRUE; -} - -/* - * Do Insert mode completion. - * Called when character "c" was typed, which has a meaning for completion. - * Returns OK if completion was done, FAIL if something failed (out of mem). - */ -static int ins_complete(int c) -{ - char_u *line; - int startcol = 0; /* column where searched text starts */ - colnr_T curs_col; /* cursor column */ - int n; - int save_w_wrow; - - compl_direction = ins_compl_key2dir(c); - if (!compl_started) { - /* First time we hit ^N or ^P (in a row, I mean) */ - - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - if (stop_arrow() == FAIL) - return FAIL; - - line = ml_get(curwin->w_cursor.lnum); - curs_col = curwin->w_cursor.col; - compl_pending = 0; - - /* If this same ctrl_x_mode has been interrupted use the text from - * "compl_startpos" to the cursor as a pattern to add a new word - * instead of expand the one before the cursor, in word-wise if - * "compl_startpos" is not in the same line as the cursor then fix it - * (the line has been split because it was longer than 'tw'). if SOL - * is set then skip the previous pattern, a word at the beginning of - * the line has been inserted, we'll look for that -- Acevedo. */ - if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT - && compl_cont_mode == ctrl_x_mode) { - /* - * it is a continued search - */ - compl_cont_status &= ~CONT_INTRPT; /* remove INTRPT */ - if (ctrl_x_mode == 0 || ctrl_x_mode == CTRL_X_PATH_PATTERNS - || ctrl_x_mode == CTRL_X_PATH_DEFINES) { - if (compl_startpos.lnum != curwin->w_cursor.lnum) { - /* line (probably) wrapped, set compl_startpos to the - * first non_blank in the line, if it is not a wordchar - * include it to get a better pattern, but then we don't - * want the "\\<" prefix, check it bellow */ - compl_col = (colnr_T)(skipwhite(line) - line); - compl_startpos.col = compl_col; - compl_startpos.lnum = curwin->w_cursor.lnum; - compl_cont_status &= ~CONT_SOL; /* clear SOL if present */ - } else { - /* S_IPOS was set when we inserted a word that was at the - * beginning of the line, which means that we'll go to SOL - * mode but first we need to redefine compl_startpos */ - if (compl_cont_status & CONT_S_IPOS) { - compl_cont_status |= CONT_SOL; - compl_startpos.col = (colnr_T)(skipwhite( - line + compl_length - + compl_startpos.col) - line); - } - compl_col = compl_startpos.col; - } - compl_length = curwin->w_cursor.col - (int)compl_col; - /* IObuff is used to add a "word from the next line" would we - * have enough space? just being paranoid */ -#define MIN_SPACE 75 - if (compl_length > (IOSIZE - MIN_SPACE)) { - compl_cont_status &= ~CONT_SOL; - compl_length = (IOSIZE - MIN_SPACE); - compl_col = curwin->w_cursor.col - compl_length; - } - compl_cont_status |= CONT_ADDING | CONT_N_ADDS; - if (compl_length < 1) - compl_cont_status &= CONT_LOCAL; - } else if (ctrl_x_mode == CTRL_X_WHOLE_LINE) - compl_cont_status = CONT_ADDING | CONT_N_ADDS; - else - compl_cont_status = 0; - } else - compl_cont_status &= CONT_LOCAL; - - if (!(compl_cont_status & CONT_ADDING)) { /* normal expansion */ - compl_cont_mode = ctrl_x_mode; - if (ctrl_x_mode != 0) /* Remove LOCAL if ctrl_x_mode != 0 */ - compl_cont_status = 0; - compl_cont_status |= CONT_N_ADDS; - compl_startpos = curwin->w_cursor; - startcol = (int)curs_col; - compl_col = 0; - } - - /* Work out completion pattern and original text -- webb */ - if (ctrl_x_mode == 0 || (ctrl_x_mode & CTRL_X_WANT_IDENT)) { - if ((compl_cont_status & CONT_SOL) - || ctrl_x_mode == CTRL_X_PATH_DEFINES) { - if (!(compl_cont_status & CONT_ADDING)) { - while (--startcol >= 0 && vim_isIDc(line[startcol])) - ; - compl_col += ++startcol; - compl_length = curs_col - startcol; - } - if (p_ic) - compl_pattern = str_foldcase(line + compl_col, - compl_length, NULL, 0); - else - compl_pattern = vim_strnsave(line + compl_col, - compl_length); - if (compl_pattern == NULL) - return FAIL; - } else if (compl_cont_status & CONT_ADDING) { - char_u *prefix = (char_u *)"\\<"; - - /* we need up to 2 extra chars for the prefix */ - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); - if (!vim_iswordp(line + compl_col) - || (compl_col > 0 - && ( - vim_iswordp(mb_prevptr(line, line + compl_col)) - ))) - prefix = (char_u *)""; - STRCPY((char *)compl_pattern, prefix); - (void)quote_meta(compl_pattern + STRLEN(prefix), - line + compl_col, compl_length); - } else if (--startcol < 0 || - !vim_iswordp(mb_prevptr(line, line + startcol + 1)) - ) { - /* Match any word of at least two chars */ - compl_pattern = vim_strsave((char_u *)"\\<\\k\\k"); - if (compl_pattern == NULL) - return FAIL; - compl_col += curs_col; - compl_length = 0; - } else { - /* Search the point of change class of multibyte character - * or not a word single byte character backward. */ - if (has_mbyte) { - int base_class; - int head_off; - - startcol -= (*mb_head_off)(line, line + startcol); - base_class = mb_get_class(line + startcol); - while (--startcol >= 0) { - head_off = (*mb_head_off)(line, line + startcol); - if (base_class != mb_get_class(line + startcol - - head_off)) - break; - startcol -= head_off; - } - } else - while (--startcol >= 0 && vim_iswordc(line[startcol])) - ; - compl_col += ++startcol; - compl_length = (int)curs_col - startcol; - if (compl_length == 1) { - /* Only match word with at least two chars -- webb - * there's no need to call quote_meta, - * xmalloc(7) is enough -- Acevedo - */ - compl_pattern = xmalloc(7); - STRCPY((char *)compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, 1); - STRCAT((char *)compl_pattern, "\\k"); - } else { - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); - STRCPY((char *)compl_pattern, "\\<"); - (void)quote_meta(compl_pattern + 2, line + compl_col, - compl_length); - } - } - } else if (ctrl_x_mode == CTRL_X_WHOLE_LINE) { - compl_col = (colnr_T)(skipwhite(line) - line); - compl_length = (int)curs_col - (int)compl_col; - if (compl_length < 0) /* cursor in indent: empty pattern */ - compl_length = 0; - if (p_ic) - compl_pattern = str_foldcase(line + compl_col, compl_length, - NULL, 0); - else - compl_pattern = vim_strnsave(line + compl_col, compl_length); - if (compl_pattern == NULL) - return FAIL; - } else if (ctrl_x_mode == CTRL_X_FILES) { - /* Go back to just before the first filename character. */ - if (startcol > 0) { - char_u *p = line + startcol; - - mb_ptr_back(line, p); - while (p > line && vim_isfilec(PTR2CHAR(p))) - mb_ptr_back(line, p); - if (p == line && vim_isfilec(PTR2CHAR(p))) - startcol = 0; - else - startcol = (int)(p - line) + 1; - } - - compl_col += startcol; - compl_length = (int)curs_col - startcol; - compl_pattern = addstar(line + compl_col, compl_length, - EXPAND_FILES); - if (compl_pattern == NULL) - return FAIL; - } else if (ctrl_x_mode == CTRL_X_CMDLINE) { - compl_pattern = vim_strnsave(line, curs_col); - if (compl_pattern == NULL) - return FAIL; - set_cmd_context(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), curs_col); - if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL - || compl_xp.xp_context == EXPAND_NOTHING) - /* No completion possible, use an empty pattern to get a - * "pattern not found" message. */ - compl_col = curs_col; - else - compl_col = (int)(compl_xp.xp_pattern - compl_pattern); - compl_length = curs_col - compl_col; - } else if (ctrl_x_mode == CTRL_X_FUNCTION || ctrl_x_mode == - CTRL_X_OMNI) { - /* - * Call user defined function 'completefunc' with "a:findstart" - * set to 1 to obtain the length of text to use for completion. - */ - char_u *args[2]; - int col; - char_u *funcname; - pos_T pos; - win_T *curwin_save; - buf_T *curbuf_save; - - /* Call 'completefunc' or 'omnifunc' and get pattern length as a - * string */ - funcname = ctrl_x_mode == CTRL_X_FUNCTION - ? curbuf->b_p_cfu : curbuf->b_p_ofu; - if (*funcname == NUL) { - EMSG2(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION - ? "completefunc" : "omnifunc"); - return FAIL; - } - - args[0] = (char_u *)"1"; - args[1] = NULL; - pos = curwin->w_cursor; - curwin_save = curwin; - curbuf_save = curbuf; - col = call_func_retnr(funcname, 2, args, FALSE); - if (curwin_save != curwin || curbuf_save != curbuf) { - EMSG(_(e_complwin)); - return FAIL; - } - curwin->w_cursor = pos; /* restore the cursor position */ - check_cursor(); - if (!equalpos(curwin->w_cursor, pos)) { - EMSG(_(e_compldel)); - return FAIL; - } - - /* Return value -2 means the user complete function wants to - * cancel the complete without an error. - * Return value -3 does the same as -2 and leaves CTRL-X mode.*/ - if (col == -2) - return FAIL; - if (col == -3) { - ctrl_x_mode = 0; - edit_submode = NULL; - msg_clr_cmdline(); - return FAIL; - } - - /* - * Reset extended parameters of completion, when start new - * completion. - */ - compl_opt_refresh_always = FALSE; - - if (col < 0) - col = curs_col; - compl_col = col; - if (compl_col > curs_col) - compl_col = curs_col; - - /* Setup variables for completion. Need to obtain "line" again, - * it may have become invalid. */ - line = ml_get(curwin->w_cursor.lnum); - compl_length = curs_col - compl_col; - compl_pattern = vim_strnsave(line + compl_col, compl_length); - if (compl_pattern == NULL) - return FAIL; - } else if (ctrl_x_mode == CTRL_X_SPELL) { - if (spell_bad_len > 0) - compl_col = curs_col - spell_bad_len; - else - compl_col = spell_word_start(startcol); - if (compl_col >= (colnr_T)startcol) { - compl_length = 0; - compl_col = curs_col; - } else { - spell_expand_check_cap(compl_col); - compl_length = (int)curs_col - compl_col; - } - /* Need to obtain "line" again, it may have become invalid. */ - line = ml_get(curwin->w_cursor.lnum); - compl_pattern = vim_strnsave(line + compl_col, compl_length); - if (compl_pattern == NULL) - return FAIL; - } else { - EMSG2(_(e_intern2), "ins_complete()"); - return FAIL; - } - - if (compl_cont_status & CONT_ADDING) { - edit_submode_pre = (char_u *)_(" Adding"); - if (ctrl_x_mode == CTRL_X_WHOLE_LINE) { - /* Insert a new line, keep indentation but ignore 'comments' */ - char_u *old = curbuf->b_p_com; - - curbuf->b_p_com = (char_u *)""; - compl_startpos.lnum = curwin->w_cursor.lnum; - compl_startpos.col = compl_col; - ins_eol('\r'); - curbuf->b_p_com = old; - compl_length = 0; - compl_col = curwin->w_cursor.col; - } - } else { - edit_submode_pre = NULL; - compl_startpos.col = compl_col; - } - - if (compl_cont_status & CONT_LOCAL) - edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); - else - edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); - - /* If any of the original typed text has been changed we need to fix - * the redo buffer. */ - ins_compl_fixRedoBufForLeader(NULL); - - /* Always add completion for the original text. */ - free(compl_orig_text); - compl_orig_text = vim_strnsave(line + compl_col, compl_length); - if (compl_orig_text == NULL || ins_compl_add(compl_orig_text, - -1, p_ic, NULL, NULL, 0, ORIGINAL_TEXT, FALSE) != OK) { - free(compl_pattern); - compl_pattern = NULL; - free(compl_orig_text); - compl_orig_text = NULL; - return FAIL; - } - - /* showmode might reset the internal line pointers, so it must - * be called before line = ml_get(), or when this address is no - * longer needed. -- Acevedo. - */ - edit_submode_extra = (char_u *)_("-- Searching..."); - edit_submode_highl = HLF_COUNT; - showmode(); - edit_submode_extra = NULL; - out_flush(); - } - - compl_shown_match = compl_curr_match; - compl_shows_dir = compl_direction; - - /* - * Find next match (and following matches). - */ - save_w_wrow = curwin->w_wrow; - n = ins_compl_next(TRUE, ins_compl_key2count(c), ins_compl_use_match(c)); - - /* may undisplay the popup menu */ - ins_compl_upd_pum(); - - if (n > 1) /* all matches have been found */ - compl_matches = n; - compl_curr_match = compl_shown_match; - compl_direction = compl_shows_dir; - - /* Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert - * mode. */ - if (got_int && !global_busy) { - (void)vgetc(); - got_int = FALSE; - } - - /* we found no match if the list has only the "compl_orig_text"-entry */ - if (compl_first_match == compl_first_match->cp_next) { - edit_submode_extra = (compl_cont_status & CONT_ADDING) - && compl_length > 1 - ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); - edit_submode_highl = HLF_E; - /* remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode, - * because we couldn't expand anything at first place, but if we used - * ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word - * (such as M in M'exico) if not tried already. -- Acevedo */ - if ( compl_length > 1 - || (compl_cont_status & CONT_ADDING) - || (ctrl_x_mode != 0 - && ctrl_x_mode != CTRL_X_PATH_PATTERNS - && ctrl_x_mode != CTRL_X_PATH_DEFINES)) - compl_cont_status &= ~CONT_N_ADDS; - } - - if (compl_curr_match->cp_flags & CONT_S_IPOS) - compl_cont_status |= CONT_S_IPOS; - else - compl_cont_status &= ~CONT_S_IPOS; - - if (edit_submode_extra == NULL) { - if (compl_curr_match->cp_flags & ORIGINAL_TEXT) { - edit_submode_extra = (char_u *)_("Back at original"); - edit_submode_highl = HLF_W; - } else if (compl_cont_status & CONT_S_IPOS) { - edit_submode_extra = (char_u *)_("Word from other line"); - edit_submode_highl = HLF_COUNT; - } else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) { - edit_submode_extra = (char_u *)_("The only match"); - edit_submode_highl = HLF_COUNT; - } else { - /* Update completion sequence number when needed. */ - if (compl_curr_match->cp_number == -1) { - int number = 0; - compl_T *match; - - if (compl_direction == FORWARD) { - /* search backwards for the first valid (!= -1) number. - * This should normally succeed already at the first loop - * cycle, so it's fast! */ - for (match = compl_curr_match->cp_prev; match != NULL - && match != compl_first_match; - match = match->cp_prev) - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - if (match != NULL) - /* go up and assign all numbers which are not assigned - * yet */ - for (match = match->cp_next; - match != NULL && match->cp_number == -1; - match = match->cp_next) - match->cp_number = ++number; - } else { /* BACKWARD */ - /* search forwards (upwards) for the first valid (!= -1) - * number. This should normally succeed already at the - * first loop cycle, so it's fast! */ - for (match = compl_curr_match->cp_next; match != NULL - && match != compl_first_match; - match = match->cp_next) - if (match->cp_number != -1) { - number = match->cp_number; - break; - } - if (match != NULL) - /* go down and assign all numbers which are not - * assigned yet */ - for (match = match->cp_prev; match - && match->cp_number == -1; - match = match->cp_prev) - match->cp_number = ++number; - } - } - - /* The match should always have a sequence number now, this is - * just a safety check. */ - if (compl_curr_match->cp_number != -1) { - /* Space for 10 text chars. + 2x10-digit no.s = 31. - * Translations may need more than twice that. */ - static char_u match_ref[81]; - - if (compl_matches > 0) - vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d of %d"), - compl_curr_match->cp_number, compl_matches); - else - vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d"), - compl_curr_match->cp_number); - edit_submode_extra = match_ref; - edit_submode_highl = HLF_R; - if (dollar_vcol >= 0) - curs_columns(FALSE); - } - } - } - - /* Show a message about what (completion) mode we're in. */ - showmode(); - if (edit_submode_extra != NULL) { - if (!p_smd) - msg_attr(edit_submode_extra, - edit_submode_highl < HLF_COUNT - ? hl_attr(edit_submode_highl) : 0); - } else - msg_clr_cmdline(); /* necessary for "noshowmode" */ - - /* Show the popup menu, unless we got interrupted. */ - if (!compl_interrupted) { - /* RedrawingDisabled may be set when invoked through complete(). */ - n = RedrawingDisabled; - RedrawingDisabled = 0; - - /* If the cursor moved we need to remove the pum first. */ - setcursor(); - if (save_w_wrow != curwin->w_wrow) - ins_compl_del_pum(); - - ins_compl_show_pum(); - setcursor(); - RedrawingDisabled = n; - } - compl_was_interrupted = compl_interrupted; - compl_interrupted = FALSE; - - return OK; -} - -/* - * Looks in the first "len" chars. of "src" for search-metachars. - * If dest is not NULL the chars. are copied there quoting (with - * a backslash) the metachars, and dest would be NUL terminated. - * Returns the length (needed) of dest - */ -static unsigned quote_meta(char_u *dest, char_u *src, int len) -{ - unsigned m = (unsigned)len + 1; /* one extra for the NUL */ - - for (; --len >= 0; src++) { - switch (*src) { - case '.': - case '*': - case '[': - if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) - break; - case '~': - if (!p_magic) /* quote these only if magic is set */ - break; - case '\\': - if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) - break; - case '^': /* currently it's not needed. */ - case '$': - m++; - if (dest != NULL) - *dest++ = '\\'; - break; - } - if (dest != NULL) - *dest++ = *src; - /* Copy remaining bytes of a multibyte character. */ - if (has_mbyte) { - int i, mb_len; - - mb_len = (*mb_ptr2len)(src) - 1; - if (mb_len > 0 && len >= mb_len) - for (i = 0; i < mb_len; ++i) { - --len; - ++src; - if (dest != NULL) - *dest++ = *src; - } - } - } - if (dest != NULL) - *dest = NUL; - - return m; -} - -/* - * Next character is interpreted literally. - * A one, two or three digit decimal number is interpreted as its byte value. - * If one or two digits are entered, the next character is given to vungetc(). - * For Unicode a character > 255 may be returned. - */ -int get_literal(void) -{ - int cc; - int nc; - int i; - int hex = FALSE; - int octal = FALSE; - int unicode = 0; - - if (got_int) - return Ctrl_C; - -#ifdef USE_ON_FLY_SCROLL - dont_scroll = TRUE; /* disallow scrolling here */ -#endif - ++no_mapping; /* don't map the next key hits */ - cc = 0; - i = 0; - for (;; ) { - nc = plain_vgetc(); - if (!(State & CMDLINE) - && MB_BYTE2LEN_CHECK(nc) == 1 - ) - add_to_showcmd(nc); - if (nc == 'x' || nc == 'X') - hex = TRUE; - else if (nc == 'o' || nc == 'O') - octal = TRUE; - else if (nc == 'u' || nc == 'U') - unicode = nc; - else { - if (hex - || unicode != 0 - ) { - if (!vim_isxdigit(nc)) - break; - cc = cc * 16 + hex2nr(nc); - } else if (octal) { - if (nc < '0' || nc > '7') - break; - cc = cc * 8 + nc - '0'; - } else { - if (!VIM_ISDIGIT(nc)) - break; - cc = cc * 10 + nc - '0'; - } - - ++i; - } - - if (cc > 255 - && unicode == 0 - ) - cc = 255; /* limit range to 0-255 */ - nc = 0; - - if (hex) { /* hex: up to two chars */ - if (i >= 2) - break; - } else if (unicode) { /* Unicode: up to four or eight chars */ - if ((unicode == 'u' && i >= 4) || (unicode == 'U' && i >= 8)) - break; - } else if (i >= 3) /* decimal or octal: up to three chars */ - break; - } - if (i == 0) { /* no number entered */ - if (nc == K_ZERO) { /* NUL is stored as NL */ - cc = '\n'; - nc = 0; - } else { - cc = nc; - nc = 0; - } - } - - if (cc == 0) /* NUL is stored as NL */ - cc = '\n'; - if (enc_dbcs && (cc & 0xff) == 0) - cc = '?'; /* don't accept an illegal DBCS char, the NUL in the - second byte will cause trouble! */ - - --no_mapping; - if (nc) - vungetc(nc); - got_int = FALSE; /* CTRL-C typed after CTRL-V is not an interrupt */ - return cc; -} - -/* - * Insert character, taking care of special keys and mod_mask - */ -static void -insert_special ( - int c, - int allow_modmask, - int ctrlv /* c was typed after CTRL-V */ -) -{ - char_u *p; - int len; - - /* - * Special function key, translate into "". Up to the last '>' is - * inserted with ins_str(), so as not to replace characters in replace - * mode. - * Only use mod_mask for special keys, to avoid things like , - * unless 'allow_modmask' is TRUE. - */ - if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) { - p = get_special_key_name(c, mod_mask); - len = (int)STRLEN(p); - c = p[len - 1]; - if (len > 2) { - if (stop_arrow() == FAIL) - return; - p[len - 1] = NUL; - ins_str(p); - AppendToRedobuffLit(p, -1); - ctrlv = FALSE; - } - } - if (stop_arrow() == OK) - insertchar(c, ctrlv ? INSCHAR_CTRLV : 0, -1); -} - -/* - * Special characters in this context are those that need processing other - * than the simple insertion that can be performed here. This includes ESC - * which terminates the insert, and CR/NL which need special processing to - * open up a new line. This routine tries to optimize insertions performed by - * the "redo", "undo" or "put" commands, so it needs to know when it should - * stop and defer processing to the "normal" mechanism. - * '0' and '^' are special, because they can be followed by CTRL-D. - */ -# define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^') - -# define WHITECHAR(cc) (vim_iswhite(cc) && \ - (!enc_utf8 || \ - !utf_iscomposing(utf_ptr2char(ml_get_cursor() + 1)))) - -/* - * "flags": INSCHAR_FORMAT - force formatting - * INSCHAR_CTRLV - char typed just after CTRL-V - * INSCHAR_NO_FEX - don't use 'formatexpr' - * - * NOTE: passes the flags value straight through to internal_format() which, - * beside INSCHAR_FORMAT (above), is also looking for these: - * INSCHAR_DO_COM - format comments - * INSCHAR_COM_LIST - format comments with num list or 2nd line indent - */ -void -insertchar ( - int c, /* character to insert or NUL */ - int flags, /* INSCHAR_FORMAT, etc. */ - int second_indent /* indent for second line if >= 0 */ -) -{ - int textwidth; - char_u *p; - int fo_ins_blank; - - textwidth = comp_textwidth(flags & INSCHAR_FORMAT); - fo_ins_blank = has_format_option(FO_INS_BLANK); - - /* - * Try to break the line in two or more pieces when: - * - Always do this if we have been called to do formatting only. - * - Always do this when 'formatoptions' has the 'a' flag and the line - * ends in white space. - * - Otherwise: - * - Don't do this if inserting a blank - * - Don't do this if an existing character is being replaced, unless - * we're in VREPLACE mode. - * - Do this if the cursor is not on the line where insert started - * or - 'formatoptions' doesn't have 'l' or the line was not too long - * before the insert. - * - 'formatoptions' doesn't have 'b' or a blank was inserted at or - * before 'textwidth' - */ - if (textwidth > 0 - && ((flags & INSCHAR_FORMAT) - || (!vim_iswhite(c) - && !((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG) - && *ml_get_cursor() != NUL) - && (curwin->w_cursor.lnum != Insstart.lnum - || ((!has_format_option(FO_INS_LONG) - || Insstart_textlen <= (colnr_T)textwidth) - && (!fo_ins_blank - || Insstart_blank_vcol <= (colnr_T)textwidth - )))))) { - /* Format with 'formatexpr' when it's set. Use internal formatting - * when 'formatexpr' isn't set or it returns non-zero. */ - int do_internal = TRUE; - - if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0) { - do_internal = (fex_format(curwin->w_cursor.lnum, 1L, c) != 0); - /* It may be required to save for undo again, e.g. when setline() - * was called. */ - ins_need_undo = TRUE; - } - if (do_internal) - internal_format(textwidth, second_indent, flags, c == NUL, c); - } - - if (c == NUL) /* only formatting was wanted */ - return; - - /* Check whether this character should end a comment. */ - if (did_ai && (int)c == end_comment_pending) { - char_u *line; - char_u lead_end[COM_MAX_LEN]; /* end-comment string */ - int middle_len, end_len; - int i; - - /* - * Need to remove existing (middle) comment leader and insert end - * comment leader. First, check what comment leader we can find. - */ - i = get_leader_len(line = ml_get_curline(), &p, FALSE, TRUE); - if (i > 0 && vim_strchr(p, COM_MIDDLE) != NULL) { /* Just checking */ - /* Skip middle-comment string */ - while (*p && p[-1] != ':') /* find end of middle flags */ - ++p; - middle_len = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); - /* Don't count trailing white space for middle_len */ - while (middle_len > 0 && vim_iswhite(lead_end[middle_len - 1])) - --middle_len; - - /* Find the end-comment string */ - while (*p && p[-1] != ':') /* find end of end flags */ - ++p; - end_len = copy_option_part(&p, lead_end, COM_MAX_LEN, ","); - - /* Skip white space before the cursor */ - i = curwin->w_cursor.col; - while (--i >= 0 && vim_iswhite(line[i])) - ; - i++; - - /* Skip to before the middle leader */ - i -= middle_len; - - /* Check some expected things before we go on */ - if (i >= 0 && lead_end[end_len - 1] == end_comment_pending) { - /* Backspace over all the stuff we want to replace */ - backspace_until_column(i); - - /* - * Insert the end-comment string, except for the last - * character, which will get inserted as normal later. - */ - ins_bytes_len(lead_end, end_len - 1); - } - } - } - end_comment_pending = NUL; - - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - - /* - * If there's any pending input, grab up to INPUT_BUFLEN at once. - * This speeds up normal text input considerably. - * Don't do this when 'cindent' or 'indentexpr' is set, because we might - * need to re-indent at a ':', or any other character (but not what - * 'paste' is set).. - * Don't do this when there an InsertCharPre autocommand is defined, - * because we need to fire the event for every character. - */ -#ifdef USE_ON_FLY_SCROLL - dont_scroll = FALSE; /* allow scrolling here */ -#endif - - if ( !ISSPECIAL(c) - && (!has_mbyte || (*mb_char2len)(c) == 1) - && vpeekc() != NUL - && !(State & REPLACE_FLAG) - && !cindent_on() - && !p_ri - && !has_insertcharpre() - ) { -#define INPUT_BUFLEN 100 - char_u buf[INPUT_BUFLEN + 1]; - int i; - colnr_T virtcol = 0; - - buf[0] = c; - i = 1; - if (textwidth > 0) - virtcol = get_nolist_virtcol(); - /* - * Stop the string when: - * - no more chars available - * - finding a special character (command key) - * - buffer is full - * - running into the 'textwidth' boundary - * - need to check for abbreviation: A non-word char after a word-char - */ - while ( (c = vpeekc()) != NUL - && !ISSPECIAL(c) - && (!has_mbyte || MB_BYTE2LEN_CHECK(c) == 1) - && i < INPUT_BUFLEN - && (textwidth == 0 - || (virtcol += byte2cells(buf[i - 1])) < (colnr_T)textwidth) - && !(!no_abbr && !vim_iswordc(c) && vim_iswordc(buf[i - 1]))) { - c = vgetc(); - if (p_hkmap && KeyTyped) - c = hkmap(c); /* Hebrew mode mapping */ - if (p_fkmap && KeyTyped) - c = fkmap(c); /* Farsi mode mapping */ - buf[i++] = c; - } - - do_digraph(-1); /* clear digraphs */ - do_digraph(buf[i-1]); /* may be the start of a digraph */ - buf[i] = NUL; - ins_str(buf); - if (flags & INSCHAR_CTRLV) { - redo_literal(*buf); - i = 1; - } else - i = 0; - if (buf[i] != NUL) - AppendToRedobuffLit(buf + i, -1); - } else { - int cc; - - if (has_mbyte && (cc = (*mb_char2len)(c)) > 1) { - char_u buf[MB_MAXBYTES + 1]; - - (*mb_char2bytes)(c, buf); - buf[cc] = NUL; - ins_char_bytes(buf, cc); - AppendCharToRedobuff(c); - } else { - ins_char(c); - if (flags & INSCHAR_CTRLV) - redo_literal(c); - else - AppendCharToRedobuff(c); - } - } -} - -/* - * Format text at the current insert position. - * - * If the INSCHAR_COM_LIST flag is present, then the value of second_indent - * will be the comment leader length sent to open_line(). - */ -static void -internal_format ( - int textwidth, - int second_indent, - int flags, - int format_only, - int c /* character to be inserted (can be NUL) */ -) -{ - int cc; - int save_char = NUL; - int haveto_redraw = FALSE; - int fo_ins_blank = has_format_option(FO_INS_BLANK); - int fo_multibyte = has_format_option(FO_MBYTE_BREAK); - int fo_white_par = has_format_option(FO_WHITE_PAR); - int first_line = TRUE; - colnr_T leader_len; - int no_leader = FALSE; - int do_comments = (flags & INSCHAR_DO_COM); - - /* - * When 'ai' is off we don't want a space under the cursor to be - * deleted. Replace it with an 'x' temporarily. - */ - if (!curbuf->b_p_ai - && !(State & VREPLACE_FLAG) - ) { - cc = gchar_cursor(); - if (vim_iswhite(cc)) { - save_char = cc; - pchar_cursor('x'); - } - } - - /* - * Repeat breaking lines, until the current line is not too long. - */ - while (!got_int) { - int startcol; /* Cursor column at entry */ - int wantcol; /* column at textwidth border */ - int foundcol; /* column for start of spaces */ - int end_foundcol = 0; /* column for start of word */ - colnr_T len; - colnr_T virtcol; - int orig_col = 0; - char_u *saved_text = NULL; - colnr_T col; - colnr_T end_col; - - virtcol = get_nolist_virtcol() - + char2cells(c != NUL ? c : gchar_cursor()); - if (virtcol <= (colnr_T)textwidth) - break; - - if (no_leader) - do_comments = FALSE; - else if (!(flags & INSCHAR_FORMAT) - && has_format_option(FO_WRAP_COMS)) - do_comments = TRUE; - - /* Don't break until after the comment leader */ - if (do_comments) - leader_len = get_leader_len(ml_get_curline(), NULL, FALSE, TRUE); - else - leader_len = 0; - - /* If the line doesn't start with a comment leader, then don't - * start one in a following broken line. Avoids that a %word - * moved to the start of the next line causes all following lines - * to start with %. */ - if (leader_len == 0) - no_leader = TRUE; - if (!(flags & INSCHAR_FORMAT) - && leader_len == 0 - && !has_format_option(FO_WRAP)) - - break; - if ((startcol = curwin->w_cursor.col) == 0) - break; - - /* find column of textwidth border */ - coladvance((colnr_T)textwidth); - wantcol = curwin->w_cursor.col; - - curwin->w_cursor.col = startcol; - foundcol = 0; - - /* - * Find position to break at. - * Stop at first entered white when 'formatoptions' has 'v' - */ - while ((!fo_ins_blank && !has_format_option(FO_INS_VI)) - || (flags & INSCHAR_FORMAT) - || curwin->w_cursor.lnum != Insstart.lnum - || curwin->w_cursor.col >= Insstart.col) { - if (curwin->w_cursor.col == startcol && c != NUL) - cc = c; - else - cc = gchar_cursor(); - if (WHITECHAR(cc)) { - /* remember position of blank just before text */ - end_col = curwin->w_cursor.col; - - /* find start of sequence of blanks */ - while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) { - dec_cursor(); - cc = gchar_cursor(); - } - if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) - break; /* only spaces in front of text */ - /* Don't break until after the comment leader */ - if (curwin->w_cursor.col < leader_len) - break; - if (has_format_option(FO_ONE_LETTER)) { - /* do not break after one-letter words */ - if (curwin->w_cursor.col == 0) - break; /* one-letter word at begin */ - /* do not break "#a b" when 'tw' is 2 */ - if (curwin->w_cursor.col <= leader_len) - break; - col = curwin->w_cursor.col; - dec_cursor(); - cc = gchar_cursor(); - - if (WHITECHAR(cc)) - continue; /* one-letter, continue */ - curwin->w_cursor.col = col; - } - - inc_cursor(); - - end_foundcol = end_col + 1; - foundcol = curwin->w_cursor.col; - if (curwin->w_cursor.col <= (colnr_T)wantcol) - break; - } else if (cc >= 0x100 && fo_multibyte) { - /* Break after or before a multi-byte character. */ - if (curwin->w_cursor.col != startcol) { - /* Don't break until after the comment leader */ - if (curwin->w_cursor.col < leader_len) - break; - col = curwin->w_cursor.col; - inc_cursor(); - /* Don't change end_foundcol if already set. */ - if (foundcol != curwin->w_cursor.col) { - foundcol = curwin->w_cursor.col; - end_foundcol = foundcol; - if (curwin->w_cursor.col <= (colnr_T)wantcol) - break; - } - curwin->w_cursor.col = col; - } - - if (curwin->w_cursor.col == 0) - break; - - col = curwin->w_cursor.col; - - dec_cursor(); - cc = gchar_cursor(); - - if (WHITECHAR(cc)) - continue; /* break with space */ - /* Don't break until after the comment leader */ - if (curwin->w_cursor.col < leader_len) - break; - - curwin->w_cursor.col = col; - - foundcol = curwin->w_cursor.col; - end_foundcol = foundcol; - if (curwin->w_cursor.col <= (colnr_T)wantcol) - break; - } - if (curwin->w_cursor.col == 0) - break; - dec_cursor(); - } - - if (foundcol == 0) { /* no spaces, cannot break line */ - curwin->w_cursor.col = startcol; - break; - } - - /* Going to break the line, remove any "$" now. */ - undisplay_dollar(); - - /* - * Offset between cursor position and line break is used by replace - * stack functions. VREPLACE does not use this, and backspaces - * over the text instead. - */ - if (State & VREPLACE_FLAG) - orig_col = startcol; /* Will start backspacing from here */ - else - replace_offset = startcol - end_foundcol; - - /* - * adjust startcol for spaces that will be deleted and - * characters that will remain on top line - */ - curwin->w_cursor.col = foundcol; - while ((cc = gchar_cursor(), WHITECHAR(cc)) - && (!fo_white_par || curwin->w_cursor.col < startcol)) - inc_cursor(); - startcol -= curwin->w_cursor.col; - if (startcol < 0) - startcol = 0; - - if (State & VREPLACE_FLAG) { - /* - * In VREPLACE mode, we will backspace over the text to be - * wrapped, so save a copy now to put on the next line. - */ - saved_text = vim_strsave(ml_get_cursor()); - curwin->w_cursor.col = orig_col; - if (saved_text == NULL) - break; /* Can't do it, out of memory */ - saved_text[startcol] = NUL; - - /* Backspace over characters that will move to the next line */ - if (!fo_white_par) - backspace_until_column(foundcol); - } else { - /* put cursor after pos. to break line */ - if (!fo_white_par) - curwin->w_cursor.col = foundcol; - } - - /* - * Split the line just before the margin. - * Only insert/delete lines, but don't really redraw the window. - */ - open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX - + (fo_white_par ? OPENLINE_KEEPTRAIL : 0) - + (do_comments ? OPENLINE_DO_COM : 0) - + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0) - , ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent)); - if (!(flags & INSCHAR_COM_LIST)) - old_indent = 0; - - replace_offset = 0; - if (first_line) { - if (!(flags & INSCHAR_COM_LIST)) { - /* - * This section is for auto-wrap of numeric lists. When not - * in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST - * flag will be set and open_line() will handle it (as seen - * above). The code here (and in get_number_indent()) will - * recognize comments if needed... - */ - if (second_indent < 0 && has_format_option(FO_Q_NUMBER)) - second_indent = - get_number_indent(curwin->w_cursor.lnum - 1); - if (second_indent >= 0) { - if (State & VREPLACE_FLAG) - change_indent(INDENT_SET, second_indent, - FALSE, NUL, TRUE); - else if (leader_len > 0 && second_indent - leader_len > 0) { - int i; - int padding = second_indent - leader_len; - - /* We started at the first_line of a numbered list - * that has a comment. the open_line() function has - * inserted the proper comment leader and positioned - * the cursor at the end of the split line. Now we - * add the additional whitespace needed after the - * comment leader for the numbered list. */ - for (i = 0; i < padding; i++) - ins_str((char_u *)" "); - changed_bytes(curwin->w_cursor.lnum, leader_len); - } else { - (void)set_indent(second_indent, SIN_CHANGED); - } - } - } - first_line = FALSE; - } - - if (State & VREPLACE_FLAG) { - /* - * In VREPLACE mode we have backspaced over the text to be - * moved, now we re-insert it into the new line. - */ - ins_bytes(saved_text); - free(saved_text); - } else { - /* - * Check if cursor is not past the NUL off the line, cindent - * may have added or removed indent. - */ - curwin->w_cursor.col += startcol; - len = (colnr_T)STRLEN(ml_get_curline()); - if (curwin->w_cursor.col > len) - curwin->w_cursor.col = len; - } - - haveto_redraw = TRUE; - can_cindent = TRUE; - /* moved the cursor, don't autoindent or cindent now */ - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - line_breakcheck(); - } - - if (save_char != NUL) /* put back space after cursor */ - pchar_cursor(save_char); - - if (!format_only && haveto_redraw) { - update_topline(); - redraw_curbuf_later(VALID); - } -} - -/* - * Called after inserting or deleting text: When 'formatoptions' includes the - * 'a' flag format from the current line until the end of the paragraph. - * Keep the cursor at the same position relative to the text. - * The caller must have saved the cursor line for undo, following ones will be - * saved here. - */ -void -auto_format ( - int trailblank, /* when TRUE also format with trailing blank */ - int prev_line /* may start in previous line */ -) -{ - pos_T pos; - colnr_T len; - char_u *old; - char_u *new, *pnew; - int wasatend; - int cc; - - if (!has_format_option(FO_AUTO)) - return; - - pos = curwin->w_cursor; - old = ml_get_curline(); - - /* may remove added space */ - check_auto_format(FALSE); - - /* Don't format in Insert mode when the cursor is on a trailing blank, the - * user might insert normal text next. Also skip formatting when "1" is - * in 'formatoptions' and there is a single character before the cursor. - * Otherwise the line would be broken and when typing another non-white - * next they are not joined back together. */ - wasatend = (pos.col == (colnr_T)STRLEN(old)); - if (*old != NUL && !trailblank && wasatend) { - dec_cursor(); - cc = gchar_cursor(); - if (!WHITECHAR(cc) && curwin->w_cursor.col > 0 - && has_format_option(FO_ONE_LETTER)) - dec_cursor(); - cc = gchar_cursor(); - if (WHITECHAR(cc)) { - curwin->w_cursor = pos; - return; - } - curwin->w_cursor = pos; - } - - /* With the 'c' flag in 'formatoptions' and 't' missing: only format - * comments. */ - if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP) - && get_leader_len(old, NULL, FALSE, TRUE) == 0) - return; - - /* - * May start formatting in a previous line, so that after "x" a word is - * moved to the previous line if it fits there now. Only when this is not - * the start of a paragraph. - */ - if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) { - --curwin->w_cursor.lnum; - if (u_save_cursor() == FAIL) - return; - } - - /* - * Do the formatting and restore the cursor position. "saved_cursor" will - * be adjusted for the text formatting. - */ - saved_cursor = pos; - format_lines((linenr_T)-1, FALSE); - curwin->w_cursor = saved_cursor; - saved_cursor.lnum = 0; - - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - /* "cannot happen" */ - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance((colnr_T)MAXCOL); - } else - check_cursor_col(); - - /* Insert mode: If the cursor is now after the end of the line while it - * previously wasn't, the line was broken. Because of the rule above we - * need to add a space when 'w' is in 'formatoptions' to keep a paragraph - * formatted. */ - if (!wasatend && has_format_option(FO_WHITE_PAR)) { - new = ml_get_curline(); - len = (colnr_T)STRLEN(new); - if (curwin->w_cursor.col == len) { - pnew = vim_strnsave(new, len + 2); - pnew[len] = ' '; - pnew[len + 1] = NUL; - ml_replace(curwin->w_cursor.lnum, pnew, FALSE); - /* remove the space later */ - did_add_space = TRUE; - } else - /* may remove added space */ - check_auto_format(FALSE); - } - - check_cursor(); -} - -/* - * When an extra space was added to continue a paragraph for auto-formatting, - * delete it now. The space must be under the cursor, just after the insert - * position. - */ -static void -check_auto_format ( - int end_insert /* TRUE when ending Insert mode */ -) -{ - int c = ' '; - int cc; - - if (did_add_space) { - cc = gchar_cursor(); - if (!WHITECHAR(cc)) - /* Somehow the space was removed already. */ - did_add_space = FALSE; - else { - if (!end_insert) { - inc_cursor(); - c = gchar_cursor(); - dec_cursor(); - } - if (c != NUL) { - /* The space is no longer at the end of the line, delete it. */ - del_char(FALSE); - did_add_space = FALSE; - } - } - } -} - -/* - * Find out textwidth to be used for formatting: - * if 'textwidth' option is set, use it - * else if 'wrapmargin' option is set, use W_WIDTH(curwin) - 'wrapmargin' - * if invalid value, use 0. - * Set default to window width (maximum 79) for "gq" operator. - */ -int -comp_textwidth ( - int ff /* force formatting (for "gq" command) */ -) -{ - int textwidth; - - textwidth = curbuf->b_p_tw; - if (textwidth == 0 && curbuf->b_p_wm) { - /* The width is the window width minus 'wrapmargin' minus all the - * things that add to the margin. */ - textwidth = W_WIDTH(curwin) - curbuf->b_p_wm; - if (cmdwin_type != 0) - textwidth -= 1; - textwidth -= curwin->w_p_fdc; - - if (curwin->w_buffer->b_signlist != NULL) { - textwidth -= 1; - } - - if (curwin->w_p_nu || curwin->w_p_rnu) - textwidth -= 8; - } - if (textwidth < 0) - textwidth = 0; - if (ff && textwidth == 0) { - textwidth = W_WIDTH(curwin) - 1; - if (textwidth > 79) - textwidth = 79; - } - return textwidth; -} - -/* - * Put a character in the redo buffer, for when just after a CTRL-V. - */ -static void redo_literal(int c) -{ - char_u buf[10]; - - /* Only digits need special treatment. Translate them into a string of - * three digits. */ - if (VIM_ISDIGIT(c)) { - vim_snprintf((char *)buf, sizeof(buf), "%03d", c); - AppendToRedobuff(buf); - } else - AppendCharToRedobuff(c); -} - -/* - * start_arrow() is called when an arrow key is used in insert mode. - * For undo/redo it resembles hitting the key. - */ -static void -start_arrow ( - pos_T *end_insert_pos /* can be NULL */ -) -{ - if (!arrow_used) { /* something has been inserted */ - AppendToRedobuff(ESC_STR); - stop_insert(end_insert_pos, FALSE, FALSE); - arrow_used = TRUE; /* this means we stopped the current insert */ - } - check_spell_redraw(); -} - -/* - * If we skipped highlighting word at cursor, do it now. - * It may be skipped again, thus reset spell_redraw_lnum first. - */ -static void check_spell_redraw(void) -{ - if (spell_redraw_lnum != 0) { - linenr_T lnum = spell_redraw_lnum; - - spell_redraw_lnum = 0; - redrawWinline(lnum, FALSE); - } -} - -/* - * Called when starting CTRL_X_SPELL mode: Move backwards to a previous badly - * spelled word, if there is one. - */ -static void spell_back_to_badword(void) -{ - pos_T tpos = curwin->w_cursor; - - spell_bad_len = spell_move_to(curwin, BACKWARD, TRUE, TRUE, NULL); - if (curwin->w_cursor.col != tpos.col) - start_arrow(&tpos); -} - -/* - * stop_arrow() is called before a change is made in insert mode. - * If an arrow key has been used, start a new insertion. - * Returns FAIL if undo is impossible, shouldn't insert then. - */ -int stop_arrow(void) -{ - if (arrow_used) { - if (u_save_cursor() == OK) { - arrow_used = FALSE; - ins_need_undo = FALSE; - } - Insstart = curwin->w_cursor; /* new insertion starts here */ - Insstart_textlen = (colnr_T)linetabsize(ml_get_curline()); - ai_col = 0; - if (State & VREPLACE_FLAG) { - orig_line_count = curbuf->b_ml.ml_line_count; - vr_lines_changed = 1; - } - ResetRedobuff(); - AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */ - new_insert_skip = 2; - } else if (ins_need_undo) { - if (u_save_cursor() == OK) - ins_need_undo = FALSE; - } - - /* Always open fold at the cursor line when inserting something. */ - foldOpenCursor(); - - return arrow_used || ins_need_undo ? FAIL : OK; -} - -/* - * Do a few things to stop inserting. - * "end_insert_pos" is where insert ended. It is NULL when we already jumped - * to another window/buffer. - */ -static void -stop_insert ( - pos_T *end_insert_pos, - int esc, /* called by ins_esc() */ - int nomove /* , don't move cursor */ -) -{ - int cc; - char_u *ptr; - - stop_redo_ins(); - replace_flush(); /* abandon replace stack */ - - /* - * Save the inserted text for later redo with ^@ and CTRL-A. - * Don't do it when "restart_edit" was set and nothing was inserted, - * otherwise CTRL-O w and then will clear "last_insert". - */ - ptr = get_inserted(); - if (did_restart_edit == 0 || (ptr != NULL - && (int)STRLEN(ptr) > new_insert_skip)) { - free(last_insert); - last_insert = ptr; - last_insert_skip = new_insert_skip; - } else - free(ptr); - - if (!arrow_used && end_insert_pos != NULL) { - /* Auto-format now. It may seem strange to do this when stopping an - * insertion (or moving the cursor), but it's required when appending - * a line and having it end in a space. But only do it when something - * was actually inserted, otherwise undo won't work. */ - if (!ins_need_undo && has_format_option(FO_AUTO)) { - pos_T tpos = curwin->w_cursor; - - /* When the cursor is at the end of the line after a space the - * formatting will move it to the following word. Avoid that by - * moving the cursor onto the space. */ - cc = 'x'; - if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL) { - dec_cursor(); - cc = gchar_cursor(); - if (!vim_iswhite(cc)) - curwin->w_cursor = tpos; - } - - auto_format(TRUE, FALSE); - - if (vim_iswhite(cc)) { - if (gchar_cursor() != NUL) - inc_cursor(); - /* If the cursor is still at the same character, also keep - * the "coladd". */ - if (gchar_cursor() == NUL - && curwin->w_cursor.lnum == tpos.lnum - && curwin->w_cursor.col == tpos.col) - curwin->w_cursor.coladd = tpos.coladd; - } - } - - /* If a space was inserted for auto-formatting, remove it now. */ - check_auto_format(TRUE); - - /* If we just did an auto-indent, remove the white space from the end - * of the line, and put the cursor back. - * Do this when ESC was used or moving the cursor up/down. - * Check for the old position still being valid, just in case the text - * got changed unexpectedly. */ - if (!nomove && did_ai && (esc || (vim_strchr(p_cpo, CPO_INDENT) == NULL - && curwin->w_cursor.lnum != - end_insert_pos->lnum)) - && end_insert_pos->lnum <= curbuf->b_ml.ml_line_count) { - pos_T tpos = curwin->w_cursor; - - curwin->w_cursor = *end_insert_pos; - check_cursor_col(); /* make sure it is not past the line */ - for (;; ) { - if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) - --curwin->w_cursor.col; - cc = gchar_cursor(); - if (!vim_iswhite(cc)) - break; - if (del_char(TRUE) == FAIL) - break; /* should not happen */ - } - if (curwin->w_cursor.lnum != tpos.lnum) - curwin->w_cursor = tpos; - else if (cc != NUL) - ++curwin->w_cursor.col; /* put cursor back on the NUL */ - - /* may have started Visual mode, adjust the position for - * deleted characters. */ - if (VIsual_active && VIsual.lnum == curwin->w_cursor.lnum) { - int len = (int)STRLEN(ml_get_curline()); - - if (VIsual.col > len) { - VIsual.col = len; - VIsual.coladd = 0; - } - } - } - } - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - - /* Set '[ and '] to the inserted text. When end_insert_pos is NULL we are - * now in a different buffer. */ - if (end_insert_pos != NULL) { - curbuf->b_op_start = Insstart; - curbuf->b_op_start_orig = Insstart_orig; - curbuf->b_op_end = *end_insert_pos; - } -} - -/* - * Set the last inserted text to a single character. - * Used for the replace command. - */ -void set_last_insert(int c) -{ - char_u *s; - - free(last_insert); - last_insert = xmalloc(MB_MAXBYTES * 3 + 5); - s = last_insert; - /* Use the CTRL-V only when entering a special char */ - if (c < ' ' || c == DEL) - *s++ = Ctrl_V; - s = add_char2buf(c, s); - *s++ = ESC; - *s++ = NUL; - last_insert_skip = 0; -} - -#if defined(EXITFREE) || defined(PROTO) -void free_last_insert(void) -{ - free(last_insert); - last_insert = NULL; - free(compl_orig_text); - compl_orig_text = NULL; -} - -#endif - -/* - * Add character "c" to buffer "s". Escape the special meaning of K_SPECIAL - * and CSI. Handle multi-byte characters. - * Returns a pointer to after the added bytes. - */ -char_u *add_char2buf(int c, char_u *s) -{ - char_u temp[MB_MAXBYTES + 1]; - int i; - int len; - - len = (*mb_char2bytes)(c, temp); - for (i = 0; i < len; ++i) { - c = temp[i]; - /* Need to escape K_SPECIAL and CSI like in the typeahead buffer. */ - if (c == K_SPECIAL) { - *s++ = K_SPECIAL; - *s++ = KS_SPECIAL; - *s++ = KE_FILLER; - } else - *s++ = c; - } - return s; -} - -/* - * move cursor to start of line - * if flags & BL_WHITE move to first non-white - * if flags & BL_SOL move to first non-white if startofline is set, - * otherwise keep "curswant" column - * if flags & BL_FIX don't leave the cursor on a NUL. - */ -void beginline(int flags) -{ - if ((flags & BL_SOL) && !p_sol) - coladvance(curwin->w_curswant); - else { - curwin->w_cursor.col = 0; - curwin->w_cursor.coladd = 0; - - if (flags & (BL_WHITE | BL_SOL)) { - char_u *ptr; - - for (ptr = ml_get_curline(); vim_iswhite(*ptr) - && !((flags & BL_FIX) && ptr[1] == NUL); ++ptr) - ++curwin->w_cursor.col; - } - curwin->w_set_curswant = TRUE; - } -} - -/* - * oneright oneleft cursor_down cursor_up - * - * Move one char {right,left,down,up}. - * Doesn't move onto the NUL past the end of the line, unless it is allowed. - * Return OK when successful, FAIL when we hit a line of file boundary. - */ - -int oneright(void) -{ - char_u *ptr; - int l; - - if (virtual_active()) { - pos_T prevpos = curwin->w_cursor; - - /* Adjust for multi-wide char (excluding TAB) */ - ptr = ml_get_cursor(); - coladvance(getviscol() + ((*ptr != TAB && vim_isprintc( - (*mb_ptr2char)(ptr) - )) - ? ptr2cells(ptr) : 1)); - curwin->w_set_curswant = TRUE; - /* Return OK if the cursor moved, FAIL otherwise (at window edge). */ - return (prevpos.col != curwin->w_cursor.col - || prevpos.coladd != curwin->w_cursor.coladd) ? OK : FAIL; - } - - ptr = ml_get_cursor(); - if (*ptr == NUL) - return FAIL; /* already at the very end */ - - if (has_mbyte) - l = (*mb_ptr2len)(ptr); - else - l = 1; - - /* move "l" bytes right, but don't end up on the NUL, unless 'virtualedit' - * contains "onemore". */ - if (ptr[l] == NUL - && (ve_flags & VE_ONEMORE) == 0 - ) - return FAIL; - curwin->w_cursor.col += l; - - curwin->w_set_curswant = TRUE; - return OK; -} - -int oneleft(void) -{ - if (virtual_active()) { - int width; - int v = getviscol(); - - if (v == 0) - return FAIL; - - /* We might get stuck on 'showbreak', skip over it. */ - width = 1; - for (;; ) { - coladvance(v - width); - /* getviscol() is slow, skip it when 'showbreak' is empty and - * there are no multi-byte characters */ - if ((*p_sbr == NUL - && !has_mbyte - ) || getviscol() < v) - break; - ++width; - } - - if (curwin->w_cursor.coladd == 1) { - char_u *ptr; - - /* Adjust for multi-wide char (not a TAB) */ - ptr = ml_get_cursor(); - if (*ptr != TAB && vim_isprintc( - (*mb_ptr2char)(ptr) - ) && ptr2cells(ptr) > 1) - curwin->w_cursor.coladd = 0; - } - - curwin->w_set_curswant = TRUE; - return OK; - } - - if (curwin->w_cursor.col == 0) - return FAIL; - - curwin->w_set_curswant = TRUE; - --curwin->w_cursor.col; - - /* if the character on the left of the current cursor is a multi-byte - * character, move to its first byte */ - if (has_mbyte) - mb_adjust_cursor(); - return OK; -} - -int -cursor_up ( - long n, - int upd_topline /* When TRUE: update topline */ -) -{ - linenr_T lnum; - - if (n > 0) { - lnum = curwin->w_cursor.lnum; - /* This fails if the cursor is already in the first line or the count - * is larger than the line number and '-' is in 'cpoptions' */ - if (lnum <= 1 || (n >= lnum && vim_strchr(p_cpo, CPO_MINUS) != NULL)) - return FAIL; - if (n >= lnum) - lnum = 1; - else if (hasAnyFolding(curwin)) { - /* - * Count each sequence of folded lines as one logical line. - */ - /* go to the start of the current fold */ - (void)hasFolding(lnum, &lnum, NULL); - - while (n--) { - /* move up one line */ - --lnum; - if (lnum <= 1) - break; - /* If we entered a fold, move to the beginning, unless in - * Insert mode or when 'foldopen' contains "all": it will open - * in a moment. */ - if (n > 0 || !((State & INSERT) || (fdo_flags & FDO_ALL))) - (void)hasFolding(lnum, &lnum, NULL); - } - if (lnum < 1) - lnum = 1; - } else - lnum -= n; - curwin->w_cursor.lnum = lnum; - } - - /* try to advance to the column we want to be at */ - coladvance(curwin->w_curswant); - - if (upd_topline) - update_topline(); /* make sure curwin->w_topline is valid */ - - return OK; -} - -/* - * Cursor down a number of logical lines. - */ -int -cursor_down ( - long n, - int upd_topline /* When TRUE: update topline */ -) -{ - linenr_T lnum; - - if (n > 0) { - lnum = curwin->w_cursor.lnum; - /* Move to last line of fold, will fail if it's the end-of-file. */ - (void)hasFolding(lnum, NULL, &lnum); - /* This fails if the cursor is already in the last line or would move - * beyond the last line and '-' is in 'cpoptions' */ - if (lnum >= curbuf->b_ml.ml_line_count - || (lnum + n > curbuf->b_ml.ml_line_count - && vim_strchr(p_cpo, CPO_MINUS) != NULL)) - return FAIL; - if (lnum + n >= curbuf->b_ml.ml_line_count) - lnum = curbuf->b_ml.ml_line_count; - else if (hasAnyFolding(curwin)) { - linenr_T last; - - /* count each sequence of folded lines as one logical line */ - while (n--) { - if (hasFolding(lnum, NULL, &last)) - lnum = last + 1; - else - ++lnum; - if (lnum >= curbuf->b_ml.ml_line_count) - break; - } - if (lnum > curbuf->b_ml.ml_line_count) - lnum = curbuf->b_ml.ml_line_count; - } else - lnum += n; - curwin->w_cursor.lnum = lnum; - } - - /* try to advance to the column we want to be at */ - coladvance(curwin->w_curswant); - - if (upd_topline) - update_topline(); /* make sure curwin->w_topline is valid */ - - return OK; -} - -/* - * Stuff the last inserted text in the read buffer. - * Last_insert actually is a copy of the redo buffer, so we - * first have to remove the command. - */ -int -stuff_inserted ( - int c, /* Command character to be inserted */ - long count, /* Repeat this many times */ - int no_esc /* Don't add an ESC at the end */ -) -{ - char_u *esc_ptr; - char_u *ptr; - char_u *last_ptr; - char_u last = NUL; - - ptr = get_last_insert(); - if (ptr == NULL) { - EMSG(_(e_noinstext)); - return FAIL; - } - - /* may want to stuff the command character, to start Insert mode */ - if (c != NUL) - stuffcharReadbuff(c); - if ((esc_ptr = (char_u *)vim_strrchr(ptr, ESC)) != NULL) - *esc_ptr = NUL; /* remove the ESC */ - - /* when the last char is either "0" or "^" it will be quoted if no ESC - * comes after it OR if it will inserted more than once and "ptr" - * starts with ^D. -- Acevedo - */ - last_ptr = (esc_ptr ? esc_ptr : ptr + STRLEN(ptr)) - 1; - if (last_ptr >= ptr && (*last_ptr == '0' || *last_ptr == '^') - && (no_esc || (*ptr == Ctrl_D && count > 1))) { - last = *last_ptr; - *last_ptr = NUL; - } - - do { - stuffReadbuff(ptr); - /* a trailing "0" is inserted as "048", "^" as "^" */ - if (last) - stuffReadbuff((char_u *)(last == '0' - ? IF_EB("\026\060\064\070", CTRL_V_STR "xf0") - : IF_EB("\026^", CTRL_V_STR "^"))); - } while (--count > 0); - - if (last) - *last_ptr = last; - - if (esc_ptr != NULL) - *esc_ptr = ESC; /* put the ESC back */ - - /* may want to stuff a trailing ESC, to get out of Insert mode */ - if (!no_esc) - stuffcharReadbuff(ESC); - - return OK; -} - -char_u *get_last_insert(void) -{ - if (last_insert == NULL) - return NULL; - return last_insert + last_insert_skip; -} - -/* - * Get last inserted string, and remove trailing . - * Returns pointer to allocated memory (must be freed) or NULL. - */ -char_u *get_last_insert_save(void) -{ - char_u *s; - int len; - - if (last_insert == NULL) - return NULL; - s = vim_strsave(last_insert + last_insert_skip); - if (s != NULL) { - len = (int)STRLEN(s); - if (len > 0 && s[len - 1] == ESC) /* remove trailing ESC */ - s[len - 1] = NUL; - } - return s; -} - -/* - * Check the word in front of the cursor for an abbreviation. - * Called when the non-id character "c" has been entered. - * When an abbreviation is recognized it is removed from the text and - * the replacement string is inserted in typebuf.tb_buf[], followed by "c". - */ -static int echeck_abbr(int c) -{ - /* Don't check for abbreviation in paste mode, when disabled and just - * after moving around with cursor keys. */ - if (p_paste || no_abbr || arrow_used) - return FALSE; - - return check_abbr(c, ml_get_curline(), curwin->w_cursor.col, - curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); -} - -/* - * replace-stack functions - * - * When replacing characters, the replaced characters are remembered for each - * new character. This is used to re-insert the old text when backspacing. - * - * There is a NUL headed list of characters for each character that is - * currently in the file after the insertion point. When BS is used, one NUL - * headed list is put back for the deleted character. - * - * For a newline, there are two NUL headed lists. One contains the characters - * that the NL replaced. The extra one stores the characters after the cursor - * that were deleted (always white space). - * - * Replace_offset is normally 0, in which case replace_push will add a new - * character at the end of the stack. If replace_offset is not 0, that many - * characters will be left on the stack above the newly inserted character. - */ - -static char_u *replace_stack = NULL; -static ssize_t replace_stack_nr = 0; /* next entry in replace stack */ -static ssize_t replace_stack_len = 0; /* max. number of entries */ - -void -replace_push ( - int c /* character that is replaced (NUL is none) */ -) -{ - if (replace_stack_nr < replace_offset) /* nothing to do */ - return; - - // TODO(philix): use xrealloc in replace_push() - if (replace_stack_len <= replace_stack_nr) { - replace_stack_len += 50; - void *aux = xmalloc(replace_stack_len); - if (replace_stack != NULL) { - memmove(aux, replace_stack, replace_stack_nr); - free(replace_stack); - } - replace_stack = aux; - } - char_u *p = replace_stack + replace_stack_nr - replace_offset; - if (replace_offset) - memmove(p + 1, p, replace_offset); - *p = (char_u)c; - ++replace_stack_nr; -} - -/* - * Push a character onto the replace stack. Handles a multi-byte character in - * reverse byte order, so that the first byte is popped off first. - * Return the number of bytes done (includes composing characters). - */ -int replace_push_mb(char_u *p) -{ - int l = (*mb_ptr2len)(p); - int j; - - for (j = l - 1; j >= 0; --j) - replace_push(p[j]); - return l; -} - -/* - * Pop one item from the replace stack. - * return -1 if stack empty - * return replaced character or NUL otherwise - */ -static int replace_pop(void) -{ - if (replace_stack_nr == 0) - return -1; - return (int)replace_stack[--replace_stack_nr]; -} - -/* - * Join the top two items on the replace stack. This removes to "off"'th NUL - * encountered. - */ -static void -replace_join ( - int off /* offset for which NUL to remove */ -) -{ - int i; - - for (i = replace_stack_nr; --i >= 0; ) - if (replace_stack[i] == NUL && off-- <= 0) { - --replace_stack_nr; - memmove(replace_stack + i, replace_stack + i + 1, - (size_t)(replace_stack_nr - i)); - return; - } -} - -/* - * Pop bytes from the replace stack until a NUL is found, and insert them - * before the cursor. Can only be used in REPLACE or VREPLACE mode. - */ -static void replace_pop_ins(void) -{ - int cc; - int oldState = State; - - State = NORMAL; /* don't want REPLACE here */ - while ((cc = replace_pop()) > 0) { - mb_replace_pop_ins(cc); - dec_cursor(); - } - State = oldState; -} - -/* - * Insert bytes popped from the replace stack. "cc" is the first byte. If it - * indicates a multi-byte char, pop the other bytes too. - */ -static void mb_replace_pop_ins(int cc) -{ - int n; - char_u buf[MB_MAXBYTES + 1]; - int i; - int c; - - if (has_mbyte && (n = MB_BYTE2LEN(cc)) > 1) { - buf[0] = cc; - for (i = 1; i < n; ++i) - buf[i] = replace_pop(); - ins_bytes_len(buf, n); - } else - ins_char(cc); - - if (enc_utf8) - /* Handle composing chars. */ - for (;; ) { - c = replace_pop(); - if (c == -1) /* stack empty */ - break; - if ((n = MB_BYTE2LEN(c)) == 1) { - /* Not a multi-byte char, put it back. */ - replace_push(c); - break; - } else { - buf[0] = c; - for (i = 1; i < n; ++i) - buf[i] = replace_pop(); - if (utf_iscomposing(utf_ptr2char(buf))) - ins_bytes_len(buf, n); - else { - /* Not a composing char, put it back. */ - for (i = n - 1; i >= 0; --i) - replace_push(buf[i]); - break; - } - } - } -} - -/* - * make the replace stack empty - * (called when exiting replace mode) - */ -static void replace_flush(void) -{ - free(replace_stack); - replace_stack = NULL; - replace_stack_len = 0; - replace_stack_nr = 0; -} - -/* - * Handle doing a BS for one character. - * cc < 0: replace stack empty, just move cursor - * cc == 0: character was inserted, delete it - * cc > 0: character was replaced, put cc (first byte of original char) back - * and check for more characters to be put back - * When "limit_col" is >= 0, don't delete before this column. Matters when - * using composing characters, use del_char_after_col() instead of del_char(). - */ -static void replace_do_bs(int limit_col) -{ - int cc; - int orig_len = 0; - int ins_len; - int orig_vcols = 0; - colnr_T start_vcol; - char_u *p; - int i; - int vcol; - - cc = replace_pop(); - if (cc > 0) { - if (State & VREPLACE_FLAG) { - /* Get the number of screen cells used by the character we are - * going to delete. */ - getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL); - orig_vcols = chartabsize(ml_get_cursor(), start_vcol); - } - if (has_mbyte) { - (void)del_char_after_col(limit_col); - if (State & VREPLACE_FLAG) - orig_len = (int)STRLEN(ml_get_cursor()); - replace_push(cc); - } else { - pchar_cursor(cc); - if (State & VREPLACE_FLAG) - orig_len = (int)STRLEN(ml_get_cursor()) - 1; - } - replace_pop_ins(); - - if (State & VREPLACE_FLAG) { - /* Get the number of screen cells used by the inserted characters */ - p = ml_get_cursor(); - ins_len = (int)STRLEN(p) - orig_len; - vcol = start_vcol; - for (i = 0; i < ins_len; ++i) { - vcol += chartabsize(p + i, vcol); - i += (*mb_ptr2len)(p) - 1; - } - vcol -= start_vcol; - - /* Delete spaces that were inserted after the cursor to keep the - * text aligned. */ - curwin->w_cursor.col += ins_len; - while (vcol > orig_vcols && gchar_cursor() == ' ') { - del_char(FALSE); - ++orig_vcols; - } - curwin->w_cursor.col -= ins_len; - } - - /* mark the buffer as changed and prepare for displaying */ - changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); - } else if (cc == 0) - (void)del_char_after_col(limit_col); -} - -/* - * Return TRUE if C-indenting is on. - */ -static int cindent_on(void) { - return !p_paste && (curbuf->b_p_cin - || *curbuf->b_p_inde != NUL - ); -} - -/* - * Re-indent the current line, based on the current contents of it and the - * surrounding lines. Fixing the cursor position seems really easy -- I'm very - * confused what all the part that handles Control-T is doing that I'm not. - * "get_the_indent" should be get_c_indent, get_expr_indent or get_lisp_indent. - */ - -void fixthisline(get_the_indent) -int (*get_the_indent)(void); -{ - change_indent(INDENT_SET, get_the_indent(), FALSE, 0, TRUE); - if (linewhite(curwin->w_cursor.lnum)) - did_ai = TRUE; /* delete the indent if the line stays empty */ -} - -void fix_indent(void) { - if (p_paste) - return; - if (curbuf->b_p_lisp && curbuf->b_p_ai) - fixthisline(get_lisp_indent); - else if (cindent_on()) - do_c_expr_indent(); -} - -/* - * return TRUE if 'cinkeys' contains the key "keytyped", - * when == '*': Only if key is preceded with '*' (indent before insert) - * when == '!': Only if key is preceded with '!' (don't insert) - * when == ' ': Only if key is not preceded with '*'(indent afterwards) - * - * "keytyped" can have a few special values: - * KEY_OPEN_FORW - * KEY_OPEN_BACK - * KEY_COMPLETE just finished completion. - * - * If line_is_empty is TRUE accept keys with '0' before them. - */ -int in_cinkeys(int keytyped, int when, int line_is_empty) -{ - char_u *look; - int try_match; - int try_match_word; - char_u *p; - char_u *line; - int icase; - int i; - - if (keytyped == NUL) - /* Can happen with CTRL-Y and CTRL-E on a short line. */ - return FALSE; - - if (*curbuf->b_p_inde != NUL) - look = curbuf->b_p_indk; /* 'indentexpr' set: use 'indentkeys' */ - else - look = curbuf->b_p_cink; /* 'indentexpr' empty: use 'cinkeys' */ - while (*look) { - /* - * Find out if we want to try a match with this key, depending on - * 'when' and a '*' or '!' before the key. - */ - switch (when) { - case '*': try_match = (*look == '*'); break; - case '!': try_match = (*look == '!'); break; - default: try_match = (*look != '*'); break; - } - if (*look == '*' || *look == '!') - ++look; - - /* - * If there is a '0', only accept a match if the line is empty. - * But may still match when typing last char of a word. - */ - if (*look == '0') { - try_match_word = try_match; - if (!line_is_empty) - try_match = FALSE; - ++look; - } else - try_match_word = FALSE; - - /* - * does it look like a control character? - */ - if (*look == '^' - && look[1] >= '?' && look[1] <= '_' - ) { - if (try_match && keytyped == Ctrl_chr(look[1])) - return TRUE; - look += 2; - } - /* - * 'o' means "o" command, open forward. - * 'O' means "O" command, open backward. - */ - else if (*look == 'o') { - if (try_match && keytyped == KEY_OPEN_FORW) - return TRUE; - ++look; - } else if (*look == 'O') { - if (try_match && keytyped == KEY_OPEN_BACK) - return TRUE; - ++look; - } - /* - * 'e' means to check for "else" at start of line and just before the - * cursor. - */ - else if (*look == 'e') { - if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) { - p = ml_get_curline(); - if (skipwhite(p) == p + curwin->w_cursor.col - 4 && - STRNCMP(p + curwin->w_cursor.col - 4, "else", 4) == 0) - return TRUE; - } - ++look; - } - /* - * ':' only causes an indent if it is at the end of a label or case - * statement, or when it was before typing the ':' (to fix - * class::method for C++). - */ - else if (*look == ':') { - if (try_match && keytyped == ':') { - p = ml_get_curline(); - if (cin_iscase(p, FALSE) || cin_isscopedecl(p) || cin_islabel()) - return TRUE; - /* Need to get the line again after cin_islabel(). */ - p = ml_get_curline(); - if (curwin->w_cursor.col > 2 - && p[curwin->w_cursor.col - 1] == ':' - && p[curwin->w_cursor.col - 2] == ':') { - p[curwin->w_cursor.col - 1] = ' '; - i = (cin_iscase(p, FALSE) || cin_isscopedecl(p) - || cin_islabel()); - p = ml_get_curline(); - p[curwin->w_cursor.col - 1] = ':'; - if (i) - return TRUE; - } - } - ++look; - } - /* - * Is it a key in <>, maybe? - */ - else if (*look == '<') { - if (try_match) { - /* - * make up some named keys , , , <0>, <>>, <<>, <*>, - * <:> and so that people can re-indent on o, O, e, 0, <, - * >, *, : and ! keys if they really really want to. - */ - if (vim_strchr((char_u *)"<>!*oOe0:", look[1]) != NULL - && keytyped == look[1]) - return TRUE; - - if (keytyped == get_special_key_code(look + 1)) - return TRUE; - } - while (*look && *look != '>') - look++; - while (*look == '>') - look++; - } - /* - * Is it a word: "=word"? - */ - else if (*look == '=' && look[1] != ',' && look[1] != NUL) { - ++look; - if (*look == '~') { - icase = TRUE; - ++look; - } else - icase = FALSE; - p = vim_strchr(look, ','); - if (p == NULL) - p = look + STRLEN(look); - if ((try_match || try_match_word) - && curwin->w_cursor.col >= (colnr_T)(p - look)) { - int match = FALSE; - - if (keytyped == KEY_COMPLETE) { - char_u *s; - - /* Just completed a word, check if it starts with "look". - * search back for the start of a word. */ - line = ml_get_curline(); - if (has_mbyte) { - char_u *n; - - for (s = line + curwin->w_cursor.col; s > line; s = n) { - n = mb_prevptr(line, s); - if (!vim_iswordp(n)) - break; - } - } else - for (s = line + curwin->w_cursor.col; s > line; --s) - if (!vim_iswordc(s[-1])) - break; - if (s + (p - look) <= line + curwin->w_cursor.col - && (icase - ? MB_STRNICMP(s, look, p - look) - : STRNCMP(s, look, p - look)) == 0) - match = TRUE; - } else - /* TODO: multi-byte */ - if (keytyped == (int)p[-1] || (icase && keytyped < 256 - && TOLOWER_LOC(keytyped) == - TOLOWER_LOC((int)p[-1]))) { - line = ml_get_cursor(); - if ((curwin->w_cursor.col == (colnr_T)(p - look) - || !vim_iswordc(line[-(p - look) - 1])) - && (icase - ? MB_STRNICMP(line - (p - look), look, p - look) - : STRNCMP(line - (p - look), look, p - look)) - == 0) - match = TRUE; - } - if (match && try_match_word && !try_match) { - /* "0=word": Check if there are only blanks before the - * word. */ - line = ml_get_curline(); - if ((int)(skipwhite(line) - line) != - (int)(curwin->w_cursor.col - (p - look))) - match = FALSE; - } - if (match) - return TRUE; - } - look = p; - } - /* - * ok, it's a boring generic character. - */ - else { - if (try_match && *look == keytyped) - return TRUE; - ++look; - } - - /* - * Skip over ", ". - */ - look = skip_to_option_part(look); - } - return FALSE; -} - -/* - * Map Hebrew keyboard when in hkmap mode. - */ -int hkmap(int c) -{ - if (p_hkmapp) { /* phonetic mapping, by Ilya Dogolazky */ - enum {hALEF=0, BET, GIMEL, DALET, HEI, VAV, ZAIN, HET, TET, IUD, - KAFsofit, hKAF, LAMED, MEMsofit, MEM, NUNsofit, NUN, SAMEH, AIN, - PEIsofit, PEI, ZADIsofit, ZADI, KOF, RESH, hSHIN, TAV}; - static char_u map[26] = - {(char_u)hALEF /*a*/, (char_u)BET /*b*/, (char_u)hKAF /*c*/, - (char_u)DALET /*d*/, (char_u)-1 /*e*/, (char_u)PEIsofit /*f*/, - (char_u)GIMEL /*g*/, (char_u)HEI /*h*/, (char_u)IUD /*i*/, - (char_u)HET /*j*/, (char_u)KOF /*k*/, (char_u)LAMED /*l*/, - (char_u)MEM /*m*/, (char_u)NUN /*n*/, (char_u)SAMEH /*o*/, - (char_u)PEI /*p*/, (char_u)-1 /*q*/, (char_u)RESH /*r*/, - (char_u)ZAIN /*s*/, (char_u)TAV /*t*/, (char_u)TET /*u*/, - (char_u)VAV /*v*/, (char_u)hSHIN /*w*/, (char_u)-1 /*x*/, - (char_u)AIN /*y*/, (char_u)ZADI /*z*/}; - - if (c == 'N' || c == 'M' || c == 'P' || c == 'C' || c == 'Z') - return (int)(map[CharOrd(c)] - 1 + p_aleph); - /* '-1'='sofit' */ - else if (c == 'x') - return 'X'; - else if (c == 'q') - return '\''; /* {geresh}={'} */ - else if (c == 246) - return ' '; /* \"o --> ' ' for a german keyboard */ - else if (c == 228) - return ' '; /* \"a --> ' ' -- / -- */ - else if (c == 252) - return ' '; /* \"u --> ' ' -- / -- */ - /* NOTE: islower() does not do the right thing for us on Linux so we - * do this the same was as 5.7 and previous, so it works correctly on - * all systems. Specifically, the e.g. Delete and Arrow keys are - * munged and won't work if e.g. searching for Hebrew text. - */ - else if (c >= 'a' && c <= 'z') - return (int)(map[CharOrdLow(c)] + p_aleph); - else - return c; - } else { - switch (c) { - case '`': return ';'; - case '/': return '.'; - case '\'': return ','; - case 'q': return '/'; - case 'w': return '\''; - - /* Hebrew letters - set offset from 'a' */ - case ',': c = '{'; break; - case '.': c = 'v'; break; - case ';': c = 't'; break; - default: { - static char str[] = "zqbcxlsjphmkwonu ydafe rig"; - - if (c < 'a' || c > 'z') - return c; - c = str[CharOrdLow(c)]; - break; - } - } - - return (int)(CharOrdLow(c) + p_aleph); - } -} - -static void ins_reg(void) -{ - int need_redraw = FALSE; - int regname; - int literally = 0; - int vis_active = VIsual_active; - - /* - * If we are going to wait for a character, show a '"'. - */ - pc_status = PC_STATUS_UNSET; - if (redrawing() && !char_avail()) { - /* may need to redraw when no more chars available now */ - ins_redraw(FALSE); - - edit_putchar('"', TRUE); - add_to_showcmd_c(Ctrl_R); - } - -#ifdef USE_ON_FLY_SCROLL - dont_scroll = TRUE; /* disallow scrolling here */ -#endif - - /* - * Don't map the register name. This also prevents the mode message to be - * deleted when ESC is hit. - */ - ++no_mapping; - regname = plain_vgetc(); - LANGMAP_ADJUST(regname, TRUE); - if (regname == Ctrl_R || regname == Ctrl_O || regname == Ctrl_P) { - /* Get a third key for literal register insertion */ - literally = regname; - add_to_showcmd_c(literally); - regname = plain_vgetc(); - LANGMAP_ADJUST(regname, TRUE); - } - --no_mapping; - - /* Don't call u_sync() while typing the expression or giving an error - * message for it. Only call it explicitly. */ - ++no_u_sync; - if (regname == '=') { -# ifdef USE_IM_CONTROL - int im_on = im_get_status(); -# endif - /* Sync undo when evaluating the expression calls setline() or - * append(), so that it can be undone separately. */ - u_sync_once = 2; - - regname = get_expr_register(); -# ifdef USE_IM_CONTROL - /* Restore the Input Method. */ - if (im_on) - im_set_active(TRUE); -# endif - } - if (regname == NUL || !valid_yank_reg(regname, FALSE)) { - vim_beep(); - need_redraw = TRUE; /* remove the '"' */ - } else { - if (literally == Ctrl_O || literally == Ctrl_P) { - /* Append the command to the redo buffer. */ - AppendCharToRedobuff(Ctrl_R); - AppendCharToRedobuff(literally); - AppendCharToRedobuff(regname); - - do_put(regname, BACKWARD, 1L, - (literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND); - } else if (insert_reg(regname, literally) == FAIL) { - vim_beep(); - need_redraw = TRUE; /* remove the '"' */ - } else if (stop_insert_mode) - /* When the '=' register was used and a function was invoked that - * did ":stopinsert" then stuff_empty() returns FALSE but we won't - * insert anything, need to remove the '"' */ - need_redraw = TRUE; - - } - --no_u_sync; - if (u_sync_once == 1) - ins_need_undo = TRUE; - u_sync_once = 0; - clear_showcmd(); - - /* If the inserted register is empty, we need to remove the '"' */ - if (need_redraw || stuff_empty()) - edit_unputchar(); - - /* Disallow starting Visual mode here, would get a weird mode. */ - if (!vis_active && VIsual_active) - end_visual_mode(); -} - -/* - * CTRL-G commands in Insert mode. - */ -static void ins_ctrl_g(void) -{ - int c; - - /* Right after CTRL-X the cursor will be after the ruler. */ - setcursor(); - - /* - * Don't map the second key. This also prevents the mode message to be - * deleted when ESC is hit. - */ - ++no_mapping; - c = plain_vgetc(); - --no_mapping; - switch (c) { - /* CTRL-G k and CTRL-G : cursor up to Insstart.col */ - case K_UP: - case Ctrl_K: - case 'k': ins_up(TRUE); - break; - - /* CTRL-G j and CTRL-G : cursor down to Insstart.col */ - case K_DOWN: - case Ctrl_J: - case 'j': ins_down(TRUE); - break; - - /* CTRL-G u: start new undoable edit */ - case 'u': u_sync(TRUE); - ins_need_undo = TRUE; - - /* Need to reset Insstart, esp. because a BS that joins - * a line to the previous one must save for undo. */ - update_Insstart_orig = false; - Insstart = curwin->w_cursor; - break; - - /* Unknown CTRL-G command, reserved for future expansion. */ - default: vim_beep(); - } -} - -/* - * CTRL-^ in Insert mode. - */ -static void ins_ctrl_hat(void) -{ - if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE)) { - /* ":lmap" mappings exists, Toggle use of ":lmap" mappings. */ - if (State & LANGMAP) { - curbuf->b_p_iminsert = B_IMODE_NONE; - State &= ~LANGMAP; - } else { - curbuf->b_p_iminsert = B_IMODE_LMAP; - State |= LANGMAP; -#ifdef USE_IM_CONTROL - im_set_active(FALSE); -#endif - } - } -#ifdef USE_IM_CONTROL - else { - /* There are no ":lmap" mappings, toggle IM */ - if (im_get_status()) { - curbuf->b_p_iminsert = B_IMODE_NONE; - im_set_active(FALSE); - } else { - curbuf->b_p_iminsert = B_IMODE_IM; - State &= ~LANGMAP; - im_set_active(TRUE); - } - } -#endif - set_iminsert_global(); - showmode(); - /* Show/unshow value of 'keymap' in status lines. */ - status_redraw_curbuf(); -} - -/* - * Handle ESC in insert mode. - * Returns TRUE when leaving insert mode, FALSE when going to repeat the - * insert. - */ -static int -ins_esc ( - long *count, - int cmdchar, - int nomove /* don't move cursor */ -) -{ - int temp; - static int disabled_redraw = FALSE; - - check_spell_redraw(); - - temp = curwin->w_cursor.col; - if (disabled_redraw) { - --RedrawingDisabled; - disabled_redraw = FALSE; - } - if (!arrow_used) { - /* - * Don't append the ESC for "r" and "grx". - * When 'insertmode' is set only CTRL-L stops Insert mode. Needed for - * when "count" is non-zero. - */ - if (cmdchar != 'r' && cmdchar != 'v') - AppendToRedobuff(p_im ? (char_u *)"\014" : ESC_STR); - - /* - * Repeating insert may take a long time. Check for - * interrupt now and then. - */ - if (*count > 0) { - line_breakcheck(); - if (got_int) - *count = 0; - } - - if (--*count > 0) { /* repeat what was typed */ - /* Vi repeats the insert without replacing characters. */ - if (vim_strchr(p_cpo, CPO_REPLCNT) != NULL) - State &= ~REPLACE_FLAG; - - (void)start_redo_ins(); - if (cmdchar == 'r' || cmdchar == 'v') - stuffReadbuff(ESC_STR); /* no ESC in redo buffer */ - ++RedrawingDisabled; - disabled_redraw = TRUE; - return FALSE; /* repeat the insert */ - } - stop_insert(&curwin->w_cursor, TRUE, nomove); - undisplay_dollar(); - } - - /* When an autoindent was removed, curswant stays after the - * indent */ - if (restart_edit == NUL && (colnr_T)temp == curwin->w_cursor.col) - curwin->w_set_curswant = TRUE; - - /* Remember the last Insert position in the '^ mark. */ - if (!cmdmod.keepjumps) - curbuf->b_last_insert = curwin->w_cursor; - - /* - * The cursor should end up on the last inserted character. - * Don't do it for CTRL-O, unless past the end of the line. - */ - if (!nomove - && (curwin->w_cursor.col != 0 - || curwin->w_cursor.coladd > 0 - ) - && (restart_edit == NUL - || (gchar_cursor() == NUL - && !VIsual_active - )) - && !revins_on - ) { - if (curwin->w_cursor.coladd > 0 || ve_flags == VE_ALL) { - oneleft(); - if (restart_edit != NUL) - ++curwin->w_cursor.coladd; - } else { - --curwin->w_cursor.col; - /* Correct cursor for multi-byte character. */ - if (has_mbyte) - mb_adjust_cursor(); - } - } - -#ifdef USE_IM_CONTROL - /* Disable IM to allow typing English directly for Normal mode commands. - * When ":lmap" is enabled don't change 'iminsert' (IM can be enabled as - * well). */ - if (!(State & LANGMAP)) - im_save_status(&curbuf->b_p_iminsert); - im_set_active(FALSE); -#endif - - State = NORMAL; - /* need to position cursor again (e.g. when on a TAB ) */ - changed_cline_bef_curs(); - - setmouse(); - ui_cursor_shape(); /* may show different cursor shape */ - - /* - * When recording or for CTRL-O, need to display the new mode. - * Otherwise remove the mode message. - */ - if (Recording || restart_edit != NUL) - showmode(); - else if (p_smd) - MSG(""); - - return TRUE; /* exit Insert mode */ -} - -/* - * Toggle language: hkmap and revins_on. - * Move to end of reverse inserted text. - */ -static void ins_ctrl_(void) -{ - if (revins_on && revins_chars && revins_scol >= 0) { - while (gchar_cursor() != NUL && revins_chars--) - ++curwin->w_cursor.col; - } - p_ri = !p_ri; - revins_on = (State == INSERT && p_ri); - if (revins_on) { - revins_scol = curwin->w_cursor.col; - revins_legal++; - revins_chars = 0; - undisplay_dollar(); - } else - revins_scol = -1; - if (p_altkeymap) { - /* - * to be consistent also for redo command, using '.' - * set arrow_used to true and stop it - causing to redo - * characters entered in one mode (normal/reverse insert). - */ - arrow_used = TRUE; - (void)stop_arrow(); - p_fkmap = curwin->w_p_rl ^ p_ri; - if (p_fkmap && p_ri) - State = INSERT; - } else - p_hkmap = curwin->w_p_rl ^ p_ri; /* be consistent! */ - showmode(); -} - -/* - * If 'keymodel' contains "startsel", may start selection. - * Returns TRUE when a CTRL-O and other keys stuffed. - */ -static int ins_start_select(int c) -{ - if (km_startsel) - switch (c) { - case K_KHOME: - case K_KEND: - case K_PAGEUP: - case K_KPAGEUP: - case K_PAGEDOWN: - case K_KPAGEDOWN: - if (!(mod_mask & MOD_MASK_SHIFT)) - break; - /* FALLTHROUGH */ - case K_S_LEFT: - case K_S_RIGHT: - case K_S_UP: - case K_S_DOWN: - case K_S_END: - case K_S_HOME: - /* Start selection right away, the cursor can move with - * CTRL-O when beyond the end of the line. */ - start_selection(); - - /* Execute the key in (insert) Select mode. */ - stuffcharReadbuff(Ctrl_O); - if (mod_mask) { - char_u buf[4]; - - buf[0] = K_SPECIAL; - buf[1] = KS_MODIFIER; - buf[2] = mod_mask; - buf[3] = NUL; - stuffReadbuff(buf); - } - stuffcharReadbuff(c); - return TRUE; - } - return FALSE; -} - -/* - * key in Insert mode: toggle insert/replace mode. - */ -static void ins_insert(int replaceState) -{ - if (p_fkmap && p_ri) { - beep_flush(); - EMSG(farsi_text_3); /* encoded in Farsi */ - return; - } - - set_vim_var_string(VV_INSERTMODE, - (char_u *)((State & REPLACE_FLAG) ? "i" : - replaceState == VREPLACE ? "v" : - "r"), 1); - apply_autocmds(EVENT_INSERTCHANGE, NULL, NULL, FALSE, curbuf); - if (State & REPLACE_FLAG) - State = INSERT | (State & LANGMAP); - else - State = replaceState | (State & LANGMAP); - AppendCharToRedobuff(K_INS); - showmode(); - ui_cursor_shape(); /* may show different cursor shape */ -} - -/* - * Pressed CTRL-O in Insert mode. - */ -static void ins_ctrl_o(void) -{ - if (State & VREPLACE_FLAG) - restart_edit = 'V'; - else if (State & REPLACE_FLAG) - restart_edit = 'R'; - else - restart_edit = 'I'; - if (virtual_active()) - ins_at_eol = FALSE; /* cursor always keeps its column */ - else - ins_at_eol = (gchar_cursor() == NUL); -} - -/* - * If the cursor is on an indent, ^T/^D insert/delete one - * shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>". - * Always round the indent to 'shiftwidth', this is compatible - * with vi. But vi only supports ^T and ^D after an - * autoindent, we support it everywhere. - */ -static void ins_shift(int c, int lastc) -{ - if (stop_arrow() == FAIL) - return; - AppendCharToRedobuff(c); - - /* - * 0^D and ^^D: remove all indent. - */ - if (c == Ctrl_D && (lastc == '0' || lastc == '^') - && curwin->w_cursor.col > 0) { - --curwin->w_cursor.col; - (void)del_char(FALSE); /* delete the '^' or '0' */ - /* In Replace mode, restore the characters that '^' or '0' replaced. */ - if (State & REPLACE_FLAG) - replace_pop_ins(); - if (lastc == '^') - old_indent = get_indent(); /* remember curr. indent */ - change_indent(INDENT_SET, 0, TRUE, 0, TRUE); - } else - change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, TRUE, 0, TRUE); - - if (did_ai && *skipwhite(ml_get_curline()) != NUL) - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - can_cindent = FALSE; /* no cindenting after ^D or ^T */ -} - -static void ins_del(void) -{ - int temp; - - if (stop_arrow() == FAIL) - return; - if (gchar_cursor() == NUL) { /* delete newline */ - temp = curwin->w_cursor.col; - if (!can_bs(BS_EOL) /* only if "eol" included */ - || do_join(2, FALSE, TRUE, FALSE) == FAIL) - vim_beep(); - else - curwin->w_cursor.col = temp; - } else if (del_char(FALSE) == FAIL) /* delete char under cursor */ - vim_beep(); - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - AppendCharToRedobuff(K_DEL); -} - -static void ins_bs_one(colnr_T *vcolp); - -/* - * Delete one character for ins_bs(). - */ -static void ins_bs_one(colnr_T *vcolp) -{ - dec_cursor(); - getvcol(curwin, &curwin->w_cursor, vcolp, NULL, NULL); - if (State & REPLACE_FLAG) { - /* Don't delete characters before the insert point when in - * Replace mode */ - if (curwin->w_cursor.lnum != Insstart.lnum - || curwin->w_cursor.col >= Insstart.col) - replace_do_bs(-1); - } else - (void)del_char(FALSE); -} - -/* - * Handle Backspace, delete-word and delete-line in Insert mode. - * Return TRUE when backspace was actually used. - */ -static int ins_bs(int c, int mode, int *inserted_space_p) -{ - linenr_T lnum; - int cc; - int temp = 0; /* init for GCC */ - colnr_T save_col; - colnr_T mincol; - int did_backspace = FALSE; - int in_indent; - int oldState; - int cpc[MAX_MCO]; /* composing characters */ - - /* - * can't delete anything in an empty file - * can't backup past first character in buffer - * can't backup past starting point unless 'backspace' > 1 - * can backup to a previous line if 'backspace' == 0 - */ - if ( bufempty() - || ( - !revins_on && - ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) - || (!can_bs(BS_START) - && (arrow_used - || (curwin->w_cursor.lnum == Insstart_orig.lnum - && curwin->w_cursor.col <= Insstart_orig.col))) - || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 - && curwin->w_cursor.col <= ai_col) - || (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) { - vim_beep(); - return FALSE; - } - - if (stop_arrow() == FAIL) - return FALSE; - in_indent = inindent(0); - if (in_indent) - can_cindent = FALSE; - end_comment_pending = NUL; /* After BS, don't auto-end comment */ - if (revins_on) /* put cursor after last inserted char */ - inc_cursor(); - - /* Virtualedit: - * BACKSPACE_CHAR eats a virtual space - * BACKSPACE_WORD eats all coladd - * BACKSPACE_LINE eats all coladd and keeps going - */ - if (curwin->w_cursor.coladd > 0) { - if (mode == BACKSPACE_CHAR) { - --curwin->w_cursor.coladd; - return TRUE; - } - if (mode == BACKSPACE_WORD) { - curwin->w_cursor.coladd = 0; - return TRUE; - } - curwin->w_cursor.coladd = 0; - } - - /* - * delete newline! - */ - if (curwin->w_cursor.col == 0) { - lnum = Insstart_orig.lnum; - if (curwin->w_cursor.lnum == lnum || revins_on) { - if (u_save((linenr_T)(curwin->w_cursor.lnum - 2), - (linenr_T)(curwin->w_cursor.lnum + 1)) == FAIL) { - return FALSE; - } - --Insstart_orig.lnum; - Insstart_orig.col = MAXCOL; - } - /* - * In replace mode: - * cc < 0: NL was inserted, delete it - * cc >= 0: NL was replaced, put original characters back - */ - cc = -1; - if (State & REPLACE_FLAG) - cc = replace_pop(); /* returns -1 if NL was inserted */ - /* - * In replace mode, in the line we started replacing, we only move the - * cursor. - */ - if ((State & REPLACE_FLAG) && curwin->w_cursor.lnum <= lnum) { - dec_cursor(); - } else { - if (!(State & VREPLACE_FLAG) - || curwin->w_cursor.lnum > orig_line_count) { - temp = gchar_cursor(); /* remember current char */ - --curwin->w_cursor.lnum; - - /* When "aw" is in 'formatoptions' we must delete the space at - * the end of the line, otherwise the line will be broken - * again when auto-formatting. */ - if (has_format_option(FO_AUTO) - && has_format_option(FO_WHITE_PAR)) { - char_u *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, - TRUE); - int len; - - len = (int)STRLEN(ptr); - if (len > 0 && ptr[len - 1] == ' ') - ptr[len - 1] = NUL; - } - - (void)do_join(2, FALSE, FALSE, FALSE); - if (temp == NUL && gchar_cursor() != NUL) - inc_cursor(); - } else - dec_cursor(); - - /* - * In REPLACE mode we have to put back the text that was replaced - * by the NL. On the replace stack is first a NUL-terminated - * sequence of characters that were deleted and then the - * characters that NL replaced. - */ - if (State & REPLACE_FLAG) { - /* - * Do the next ins_char() in NORMAL state, to - * prevent ins_char() from replacing characters and - * avoiding showmatch(). - */ - oldState = State; - State = NORMAL; - /* - * restore characters (blanks) deleted after cursor - */ - while (cc > 0) { - save_col = curwin->w_cursor.col; - mb_replace_pop_ins(cc); - curwin->w_cursor.col = save_col; - cc = replace_pop(); - } - /* restore the characters that NL replaced */ - replace_pop_ins(); - State = oldState; - } - } - did_ai = FALSE; - } else { - /* - * Delete character(s) before the cursor. - */ - if (revins_on) /* put cursor on last inserted char */ - dec_cursor(); - mincol = 0; - /* keep indent */ - if (mode == BACKSPACE_LINE - && (curbuf->b_p_ai - || cindent_on() - ) - && !revins_on - ) { - save_col = curwin->w_cursor.col; - beginline(BL_WHITE); - if (curwin->w_cursor.col < save_col) - mincol = curwin->w_cursor.col; - curwin->w_cursor.col = save_col; - } - - /* - * Handle deleting one 'shiftwidth' or 'softtabstop'. - */ - if ( mode == BACKSPACE_CHAR - && ((p_sta && in_indent) - || (get_sts_value() != 0 - && curwin->w_cursor.col > 0 - && (*(ml_get_cursor() - 1) == TAB - || (*(ml_get_cursor() - 1) == ' ' - && (!*inserted_space_p - || arrow_used)))))) { - int ts; - colnr_T vcol; - colnr_T want_vcol; - colnr_T start_vcol; - - *inserted_space_p = FALSE; - if (p_sta && in_indent) - ts = (int)get_sw_value(curbuf); - else - ts = (int)get_sts_value(); - /* Compute the virtual column where we want to be. Since - * 'showbreak' may get in the way, need to get the last column of - * the previous character. */ - getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); - start_vcol = vcol; - dec_cursor(); - getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol); - inc_cursor(); - want_vcol = (want_vcol / ts) * ts; - - /* delete characters until we are at or before want_vcol */ - while (vcol > want_vcol - && (cc = *(ml_get_cursor() - 1), vim_iswhite(cc))) - ins_bs_one(&vcol); - - /* insert extra spaces until we are at want_vcol */ - while (vcol < want_vcol) { - /* Remember the first char we inserted */ - if (curwin->w_cursor.lnum == Insstart_orig.lnum - && curwin->w_cursor.col < Insstart_orig.col) { - Insstart_orig.col = curwin->w_cursor.col; - } - - if (State & VREPLACE_FLAG) - ins_char(' '); - else { - ins_str((char_u *)" "); - if ((State & REPLACE_FLAG)) - replace_push(NUL); - } - getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); - } - - /* If we are now back where we started delete one character. Can - * happen when using 'sts' and 'linebreak'. */ - if (vcol >= start_vcol) - ins_bs_one(&vcol); - } - /* - * Delete upto starting point, start of line or previous word. - */ - else do { - if (!revins_on) /* put cursor on char to be deleted */ - dec_cursor(); - - /* start of word? */ - if (mode == BACKSPACE_WORD && !vim_isspace(gchar_cursor())) { - mode = BACKSPACE_WORD_NOT_SPACE; - temp = vim_iswordc(gchar_cursor()); - } - /* end of word? */ - else if (mode == BACKSPACE_WORD_NOT_SPACE - && (vim_isspace(cc = gchar_cursor()) - || vim_iswordc(cc) != temp)) { - if (!revins_on) - inc_cursor(); - else if (State & REPLACE_FLAG) - dec_cursor(); - break; - } - if (State & REPLACE_FLAG) - replace_do_bs(-1); - else { - if (enc_utf8 && p_deco) - (void)utfc_ptr2char(ml_get_cursor(), cpc); - (void)del_char(FALSE); - /* - * If there are combining characters and 'delcombine' is set - * move the cursor back. Don't back up before the base - * character. - */ - if (enc_utf8 && p_deco && cpc[0] != NUL) - inc_cursor(); - if (revins_chars) { - revins_chars--; - revins_legal++; - } - if (revins_on && gchar_cursor() == NUL) - break; - } - /* Just a single backspace?: */ - if (mode == BACKSPACE_CHAR) - break; - } while ( - revins_on || - (curwin->w_cursor.col > mincol - && (curwin->w_cursor.lnum != Insstart_orig.lnum - || curwin->w_cursor.col != Insstart_orig.col))); - did_backspace = TRUE; - } - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - if (curwin->w_cursor.col <= 1) - did_ai = FALSE; - /* - * It's a little strange to put backspaces into the redo - * buffer, but it makes auto-indent a lot easier to deal - * with. - */ - AppendCharToRedobuff(c); - - /* If deleted before the insertion point, adjust it */ - if (curwin->w_cursor.lnum == Insstart_orig.lnum - && curwin->w_cursor.col < Insstart_orig.col) { - Insstart_orig.col = curwin->w_cursor.col; - } - - /* vi behaviour: the cursor moves backward but the character that - * was there remains visible - * Vim behaviour: the cursor moves backward and the character that - * was there is erased from the screen. - * We can emulate the vi behaviour by pretending there is a dollar - * displayed even when there isn't. - * --pkv Sun Jan 19 01:56:40 EST 2003 */ - if (vim_strchr(p_cpo, CPO_BACKSPACE) != NULL && dollar_vcol == -1) - dollar_vcol = curwin->w_virtcol; - - /* When deleting a char the cursor line must never be in a closed fold. - * E.g., when 'foldmethod' is indent and deleting the first non-white - * char before a Tab. */ - if (did_backspace) - foldOpenCursor(); - - return did_backspace; -} - -static void ins_mouse(int c) -{ - pos_T tpos; - win_T *old_curwin = curwin; - - if (!mouse_has(MOUSE_INSERT)) - return; - - undisplay_dollar(); - tpos = curwin->w_cursor; - if (do_mouse(NULL, c, BACKWARD, 1L, 0)) { - win_T *new_curwin = curwin; - - if (curwin != old_curwin && win_valid(old_curwin)) { - /* Mouse took us to another window. We need to go back to the - * previous one to stop insert there properly. */ - curwin = old_curwin; - curbuf = curwin->w_buffer; - } - start_arrow(curwin == old_curwin ? &tpos : NULL); - if (curwin != new_curwin && win_valid(new_curwin)) { - curwin = new_curwin; - curbuf = curwin->w_buffer; - } - can_cindent = TRUE; - } - - /* redraw status lines (in case another window became active) */ - redraw_statuslines(); -} - -static void ins_mousescroll(int dir) -{ - pos_T tpos; - win_T *old_curwin = curwin; - int did_scroll = FALSE; - - tpos = curwin->w_cursor; - - if (mouse_row >= 0 && mouse_col >= 0) { - int row, col; - - row = mouse_row; - col = mouse_col; - - /* find the window at the pointer coordinates */ - curwin = mouse_find_win(&row, &col); - curbuf = curwin->w_buffer; - } - if (curwin == old_curwin) - undisplay_dollar(); - - /* Don't scroll the window in which completion is being done. */ - if (!pum_visible() - || curwin != old_curwin - ) { - if (dir == MSCR_DOWN || dir == MSCR_UP) { - if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) - scroll_redraw(dir, - (long)(curwin->w_botline - curwin->w_topline)); - else - scroll_redraw(dir, 3L); - } - did_scroll = TRUE; - } - - curwin->w_redr_status = TRUE; - - curwin = old_curwin; - curbuf = curwin->w_buffer; - - /* The popup menu may overlay the window, need to redraw it. - * TODO: Would be more efficient to only redraw the windows that are - * overlapped by the popup menu. */ - if (pum_visible() && did_scroll) { - redraw_all_later(NOT_VALID); - ins_compl_show_pum(); - } - - if (!equalpos(curwin->w_cursor, tpos)) { - start_arrow(&tpos); - can_cindent = TRUE; - } -} - - - -static void ins_left(void) -{ - pos_T tpos; - - if ((fdo_flags & FDO_HOR) && KeyTyped) - foldOpenCursor(); - undisplay_dollar(); - tpos = curwin->w_cursor; - if (oneleft() == OK) { - start_arrow(&tpos); - /* If exit reversed string, position is fixed */ - if (revins_scol != -1 && (int)curwin->w_cursor.col >= revins_scol) - revins_legal++; - revins_chars++; - } - /* - * if 'whichwrap' set for cursor in insert mode may go to - * previous line - */ - else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) { - start_arrow(&tpos); - --(curwin->w_cursor.lnum); - coladvance((colnr_T)MAXCOL); - curwin->w_set_curswant = TRUE; /* so we stay at the end */ - } else - vim_beep(); -} - -static void ins_home(int c) -{ - pos_T tpos; - - if ((fdo_flags & FDO_HOR) && KeyTyped) - foldOpenCursor(); - undisplay_dollar(); - tpos = curwin->w_cursor; - if (c == K_C_HOME) - curwin->w_cursor.lnum = 1; - curwin->w_cursor.col = 0; - curwin->w_cursor.coladd = 0; - curwin->w_curswant = 0; - start_arrow(&tpos); -} - -static void ins_end(int c) -{ - pos_T tpos; - - if ((fdo_flags & FDO_HOR) && KeyTyped) - foldOpenCursor(); - undisplay_dollar(); - tpos = curwin->w_cursor; - if (c == K_C_END) - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance((colnr_T)MAXCOL); - curwin->w_curswant = MAXCOL; - - start_arrow(&tpos); -} - -static void ins_s_left(void) -{ - if ((fdo_flags & FDO_HOR) && KeyTyped) - foldOpenCursor(); - undisplay_dollar(); - if (curwin->w_cursor.lnum > 1 || curwin->w_cursor.col > 0) { - start_arrow(&curwin->w_cursor); - (void)bck_word(1L, FALSE, FALSE); - curwin->w_set_curswant = TRUE; - } else - vim_beep(); -} - -static void ins_right(void) -{ - if ((fdo_flags & FDO_HOR) && KeyTyped) - foldOpenCursor(); - undisplay_dollar(); - if (gchar_cursor() != NUL - || virtual_active() - ) { - start_arrow(&curwin->w_cursor); - curwin->w_set_curswant = TRUE; - if (virtual_active()) - oneright(); - else { - if (has_mbyte) - curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor()); - else - ++curwin->w_cursor.col; - } - - revins_legal++; - if (revins_chars) - revins_chars--; - } - /* if 'whichwrap' set for cursor in insert mode, may move the - * cursor to the next line */ - else if (vim_strchr(p_ww, ']') != NULL - && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - start_arrow(&curwin->w_cursor); - curwin->w_set_curswant = TRUE; - ++curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; - } else - vim_beep(); -} - -static void ins_s_right(void) -{ - if ((fdo_flags & FDO_HOR) && KeyTyped) - foldOpenCursor(); - undisplay_dollar(); - if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count - || gchar_cursor() != NUL) { - start_arrow(&curwin->w_cursor); - (void)fwd_word(1L, FALSE, 0); - curwin->w_set_curswant = TRUE; - } else - vim_beep(); -} - -static void -ins_up ( - int startcol /* when TRUE move to Insstart.col */ -) -{ - pos_T tpos; - linenr_T old_topline = curwin->w_topline; - int old_topfill = curwin->w_topfill; - - undisplay_dollar(); - tpos = curwin->w_cursor; - if (cursor_up(1L, TRUE) == OK) { - if (startcol) - coladvance(getvcol_nolist(&Insstart)); - if (old_topline != curwin->w_topline - || old_topfill != curwin->w_topfill - ) - redraw_later(VALID); - start_arrow(&tpos); - can_cindent = TRUE; - } else - vim_beep(); -} - -static void ins_pageup(void) -{ - pos_T tpos; - - undisplay_dollar(); - - if (mod_mask & MOD_MASK_CTRL) { - /* : tab page back */ - if (first_tabpage->tp_next != NULL) { - start_arrow(&curwin->w_cursor); - goto_tabpage(-1); - } - return; - } - - tpos = curwin->w_cursor; - if (onepage(BACKWARD, 1L) == OK) { - start_arrow(&tpos); - can_cindent = TRUE; - } else - vim_beep(); -} - -static void -ins_down ( - int startcol /* when TRUE move to Insstart.col */ -) -{ - pos_T tpos; - linenr_T old_topline = curwin->w_topline; - int old_topfill = curwin->w_topfill; - - undisplay_dollar(); - tpos = curwin->w_cursor; - if (cursor_down(1L, TRUE) == OK) { - if (startcol) - coladvance(getvcol_nolist(&Insstart)); - if (old_topline != curwin->w_topline - || old_topfill != curwin->w_topfill - ) - redraw_later(VALID); - start_arrow(&tpos); - can_cindent = TRUE; - } else - vim_beep(); -} - -static void ins_pagedown(void) -{ - pos_T tpos; - - undisplay_dollar(); - - if (mod_mask & MOD_MASK_CTRL) { - /* : tab page forward */ - if (first_tabpage->tp_next != NULL) { - start_arrow(&curwin->w_cursor); - goto_tabpage(0); - } - return; - } - - tpos = curwin->w_cursor; - if (onepage(FORWARD, 1L) == OK) { - start_arrow(&tpos); - can_cindent = TRUE; - } else - vim_beep(); -} - -/* - * Handle TAB in Insert or Replace mode. - * Return TRUE when the TAB needs to be inserted like a normal character. - */ -static int ins_tab(void) -{ - int ind; - int i; - int temp; - - if (Insstart_blank_vcol == MAXCOL && curwin->w_cursor.lnum == Insstart.lnum) - Insstart_blank_vcol = get_nolist_virtcol(); - if (echeck_abbr(TAB + ABBR_OFF)) - return FALSE; - - ind = inindent(0); - if (ind) - can_cindent = FALSE; - - /* - * When nothing special, insert TAB like a normal character - */ - if (!curbuf->b_p_et - && !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf)) - && get_sts_value() == 0) - return TRUE; - - if (stop_arrow() == FAIL) - return TRUE; - - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - AppendToRedobuff((char_u *)"\t"); - - if (p_sta && ind) /* insert tab in indent, use 'shiftwidth' */ - temp = (int)get_sw_value(curbuf); - else if (curbuf->b_p_sts != 0) /* use 'softtabstop' when set */ - temp = (int)get_sts_value(); - else /* otherwise use 'tabstop' */ - temp = (int)curbuf->b_p_ts; - temp -= get_nolist_virtcol() % temp; - - /* - * Insert the first space with ins_char(). It will delete one char in - * replace mode. Insert the rest with ins_str(); it will not delete any - * chars. For VREPLACE mode, we use ins_char() for all characters. - */ - ins_char(' '); - while (--temp > 0) { - if (State & VREPLACE_FLAG) - ins_char(' '); - else { - ins_str((char_u *)" "); - if (State & REPLACE_FLAG) /* no char replaced */ - replace_push(NUL); - } - } - - /* - * When 'expandtab' not set: Replace spaces by TABs where possible. - */ - if (!curbuf->b_p_et && (get_sts_value() || (p_sta && ind))) { - char_u *ptr; - char_u *saved_line = NULL; /* init for GCC */ - pos_T pos; - pos_T fpos; - pos_T *cursor; - colnr_T want_vcol, vcol; - int change_col = -1; - int save_list = curwin->w_p_list; - - /* - * Get the current line. For VREPLACE mode, don't make real changes - * yet, just work on a copy of the line. - */ - if (State & VREPLACE_FLAG) { - pos = curwin->w_cursor; - cursor = &pos; - saved_line = vim_strsave(ml_get_curline()); - if (saved_line == NULL) - return FALSE; - ptr = saved_line + pos.col; - } else { - ptr = ml_get_cursor(); - cursor = &curwin->w_cursor; - } - - /* When 'L' is not in 'cpoptions' a tab always takes up 'ts' spaces. */ - if (vim_strchr(p_cpo, CPO_LISTWM) == NULL) - curwin->w_p_list = FALSE; - - /* Find first white before the cursor */ - fpos = curwin->w_cursor; - while (fpos.col > 0 && vim_iswhite(ptr[-1])) { - --fpos.col; - --ptr; - } - - /* In Replace mode, don't change characters before the insert point. */ - if ((State & REPLACE_FLAG) - && fpos.lnum == Insstart.lnum - && fpos.col < Insstart.col) { - ptr += Insstart.col - fpos.col; - fpos.col = Insstart.col; - } - - /* compute virtual column numbers of first white and cursor */ - getvcol(curwin, &fpos, &vcol, NULL, NULL); - getvcol(curwin, cursor, &want_vcol, NULL, NULL); - - /* Use as many TABs as possible. Beware of 'showbreak' and - * 'linebreak' adding extra virtual columns. */ - while (vim_iswhite(*ptr)) { - i = lbr_chartabsize((char_u *)"\t", vcol); - if (vcol + i > want_vcol) - break; - if (*ptr != TAB) { - *ptr = TAB; - if (change_col < 0) { - change_col = fpos.col; /* Column of first change */ - /* May have to adjust Insstart */ - if (fpos.lnum == Insstart.lnum && fpos.col < Insstart.col) - Insstart.col = fpos.col; - } - } - ++fpos.col; - ++ptr; - vcol += i; - } - - if (change_col >= 0) { - int repl_off = 0; - - /* Skip over the spaces we need. */ - while (vcol < want_vcol && *ptr == ' ') { - vcol += lbr_chartabsize(ptr, vcol); - ++ptr; - ++repl_off; - } - if (vcol > want_vcol) { - /* Must have a char with 'showbreak' just before it. */ - --ptr; - --repl_off; - } - fpos.col += repl_off; - - /* Delete following spaces. */ - i = cursor->col - fpos.col; - if (i > 0) { - STRMOVE(ptr, ptr + i); - /* correct replace stack. */ - if ((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG) - ) - for (temp = i; --temp >= 0; ) - replace_join(repl_off); - } - cursor->col -= i; - - /* - * In VREPLACE mode, we haven't changed anything yet. Do it now by - * backspacing over the changed spacing and then inserting the new - * spacing. - */ - if (State & VREPLACE_FLAG) { - /* Backspace from real cursor to change_col */ - backspace_until_column(change_col); - - /* Insert each char in saved_line from changed_col to - * ptr-cursor */ - ins_bytes_len(saved_line + change_col, - cursor->col - change_col); - } - } - - if (State & VREPLACE_FLAG) - free(saved_line); - curwin->w_p_list = save_list; - } - - return FALSE; -} - -/* - * Handle CR or NL in insert mode. - * Return TRUE when out of memory or can't undo. - */ -static int ins_eol(int c) -{ - int i; - - if (echeck_abbr(c + ABBR_OFF)) - return FALSE; - if (stop_arrow() == FAIL) - return TRUE; - undisplay_dollar(); - - /* - * Strange Vi behaviour: In Replace mode, typing a NL will not delete the - * character under the cursor. Only push a NUL on the replace stack, - * nothing to put back when the NL is deleted. - */ - if ((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG) - ) - replace_push(NUL); - - /* - * In VREPLACE mode, a NL replaces the rest of the line, and starts - * replacing the next line, so we push all of the characters left on the - * line onto the replace stack. This is not done here though, it is done - * in open_line(). - */ - - /* Put cursor on NUL if on the last char and coladd is 1 (happens after - * CTRL-O). */ - if (virtual_active() && curwin->w_cursor.coladd > 0) - coladvance(getviscol()); - - if (p_altkeymap && p_fkmap) - fkmap(NL); - /* NL in reverse insert will always start in the end of - * current line. */ - if (revins_on) - curwin->w_cursor.col += (colnr_T)STRLEN(ml_get_cursor()); - - AppendToRedobuff(NL_STR); - i = open_line(FORWARD, - has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : - 0, old_indent); - old_indent = 0; - can_cindent = TRUE; - /* When inserting a line the cursor line must never be in a closed fold. */ - foldOpenCursor(); - - return !i; -} - -/* - * Handle digraph in insert mode. - * Returns character still to be inserted, or NUL when nothing remaining to be - * done. - */ -static int ins_digraph(void) -{ - int c; - int cc; - int did_putchar = FALSE; - - pc_status = PC_STATUS_UNSET; - if (redrawing() && !char_avail()) { - /* may need to redraw when no more chars available now */ - ins_redraw(FALSE); - - edit_putchar('?', TRUE); - did_putchar = TRUE; - add_to_showcmd_c(Ctrl_K); - } - -#ifdef USE_ON_FLY_SCROLL - dont_scroll = TRUE; /* disallow scrolling here */ -#endif - - /* don't map the digraph chars. This also prevents the - * mode message to be deleted when ESC is hit */ - ++no_mapping; - ++allow_keys; - c = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the next - * line and will not be removed by the redraw */ - edit_unputchar(); - - if (IS_SPECIAL(c) || mod_mask) { /* special key */ - clear_showcmd(); - insert_special(c, TRUE, FALSE); - return NUL; - } - if (c != ESC) { - did_putchar = FALSE; - if (redrawing() && !char_avail()) { - /* may need to redraw when no more chars available now */ - ins_redraw(FALSE); - - if (char2cells(c) == 1) { - ins_redraw(FALSE); - edit_putchar(c, TRUE); - did_putchar = TRUE; - } - add_to_showcmd_c(c); - } - ++no_mapping; - ++allow_keys; - cc = plain_vgetc(); - --no_mapping; - --allow_keys; - if (did_putchar) - /* when the line fits in 'columns' the '?' is at the start of the - * next line and will not be removed by a redraw */ - edit_unputchar(); - if (cc != ESC) { - AppendToRedobuff((char_u *)CTRL_V_STR); - c = getdigraph(c, cc, TRUE); - clear_showcmd(); - return c; - } - } - clear_showcmd(); - return NUL; -} - -/* - * Handle CTRL-E and CTRL-Y in Insert mode: copy char from other line. - * Returns the char to be inserted, or NUL if none found. - */ -int ins_copychar(linenr_T lnum) -{ - int c; - int temp; - char_u *ptr, *prev_ptr; - - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { - vim_beep(); - return NUL; - } - - /* try to advance to the cursor column */ - temp = 0; - ptr = ml_get(lnum); - prev_ptr = ptr; - validate_virtcol(); - while ((colnr_T)temp < curwin->w_virtcol && *ptr != NUL) { - prev_ptr = ptr; - temp += lbr_chartabsize_adv(&ptr, (colnr_T)temp); - } - if ((colnr_T)temp > curwin->w_virtcol) - ptr = prev_ptr; - - c = (*mb_ptr2char)(ptr); - if (c == NUL) - vim_beep(); - return c; -} - -/* - * CTRL-Y or CTRL-E typed in Insert mode. - */ -static int ins_ctrl_ey(int tc) -{ - int c = tc; - - if (ctrl_x_mode == CTRL_X_SCROLL) { - if (c == Ctrl_Y) - scrolldown_clamp(); - else - scrollup_clamp(); - redraw_later(VALID); - } else { - c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1)); - if (c != NUL) { - long tw_save; - - /* The character must be taken literally, insert like it - * was typed after a CTRL-V, and pretend 'textwidth' - * wasn't set. Digits, 'o' and 'x' are special after a - * CTRL-V, don't use it for these. */ - if (c < 256 && !isalnum(c)) - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ - tw_save = curbuf->b_p_tw; - curbuf->b_p_tw = -1; - insert_special(c, TRUE, FALSE); - curbuf->b_p_tw = tw_save; - revins_chars++; - revins_legal++; - c = Ctrl_V; /* pretend CTRL-V is last character */ - auto_format(FALSE, TRUE); - } - } - return c; -} - -/* - * Try to do some very smart auto-indenting. - * Used when inserting a "normal" character. - */ -static void ins_try_si(int c) -{ - pos_T *pos, old_pos; - char_u *ptr; - int i; - int temp; - - /* - * do some very smart indenting when entering '{' or '}' - */ - if (((did_si || can_si_back) && c == '{') || (can_si && c == '}')) { - /* - * for '}' set indent equal to indent of line containing matching '{' - */ - if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) { - old_pos = curwin->w_cursor; - /* - * If the matching '{' has a ')' immediately before it (ignoring - * white-space), then line up with the start of the line - * containing the matching '(' if there is one. This handles the - * case where an "if (..\n..) {" statement continues over multiple - * lines -- webb - */ - ptr = ml_get(pos->lnum); - i = pos->col; - if (i > 0) /* skip blanks before '{' */ - while (--i > 0 && vim_iswhite(ptr[i])) - ; - curwin->w_cursor.lnum = pos->lnum; - curwin->w_cursor.col = i; - if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) - curwin->w_cursor = *pos; - i = get_indent(); - curwin->w_cursor = old_pos; - if (State & VREPLACE_FLAG) - change_indent(INDENT_SET, i, FALSE, NUL, TRUE); - else - (void)set_indent(i, SIN_CHANGED); - } else if (curwin->w_cursor.col > 0) { - /* - * when inserting '{' after "O" reduce indent, but not - * more than indent of previous line - */ - temp = TRUE; - if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) { - old_pos = curwin->w_cursor; - i = get_indent(); - while (curwin->w_cursor.lnum > 1) { - ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum))); - - /* ignore empty lines and lines starting with '#'. */ - if (*ptr != '#' && *ptr != NUL) - break; - } - if (get_indent() >= i) - temp = FALSE; - curwin->w_cursor = old_pos; - } - if (temp) - shift_line(TRUE, FALSE, 1, TRUE); - } - } - - /* - * set indent of '#' always to 0 - */ - if (curwin->w_cursor.col > 0 && can_si && c == '#') { - /* remember current indent for next line */ - old_indent = get_indent(); - (void)set_indent(0, SIN_CHANGED); - } - - /* Adjust ai_col, the char at this position can be deleted. */ - if (ai_col > curwin->w_cursor.col) - ai_col = curwin->w_cursor.col; -} - -/* - * Get the value that w_virtcol would have when 'list' is off. - * Unless 'cpo' contains the 'L' flag. - */ -static colnr_T get_nolist_virtcol(void) -{ - if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) - return getvcol_nolist(&curwin->w_cursor); - validate_virtcol(); - return curwin->w_virtcol; -} - -/* - * Handle the InsertCharPre autocommand. - * "c" is the character that was typed. - * Return a pointer to allocated memory with the replacement string. - * Return NULL to continue inserting "c". - */ -static char_u *do_insert_char_pre(int c) -{ - char_u *res; - char_u buf[MB_MAXBYTES + 1]; - - /* Return quickly when there is nothing to do. */ - if (!has_insertcharpre()) - return NULL; - - if (has_mbyte) - buf[(*mb_char2bytes)(c, buf)] = NUL; - else { - buf[0] = c; - buf[1] = NUL; - } - - /* Lock the text to avoid weird things from happening. */ - ++textlock; - set_vim_var_string(VV_CHAR, buf, -1); /* set v:char */ - - res = NULL; - if (apply_autocmds(EVENT_INSERTCHARPRE, NULL, NULL, FALSE, curbuf)) { - /* Get the value of v:char. It may be empty or more than one - * character. Only use it when changed, otherwise continue with the - * original character to avoid breaking autoindent. */ - if (STRCMP(buf, get_vim_var_str(VV_CHAR)) != 0) - res = vim_strsave(get_vim_var_str(VV_CHAR)); - } - - set_vim_var_string(VV_CHAR, NULL, -1); /* clear v:char */ - --textlock; - - return res; -} diff --git a/src/edit.h b/src/edit.h deleted file mode 100644 index 3ab0f1ba59..0000000000 --- a/src/edit.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef NEOVIM_EDIT_H -#define NEOVIM_EDIT_H - -#include "vim.h" - -/* - * Array indexes used for cptext argument of ins_compl_add(). - */ -#define CPT_ABBR 0 /* "abbr" */ -#define CPT_MENU 1 /* "menu" */ -#define CPT_KIND 2 /* "kind" */ -#define CPT_INFO 3 /* "info" */ -#define CPT_COUNT 4 /* Number of entries */ - -int edit(int cmdchar, int startln, long count); -void edit_putchar(int c, int highlight); -void edit_unputchar(void); -void display_dollar(colnr_T col); -void change_indent(int type, int amount, int round, int replaced, - int call_changed_bytes); -void truncate_spaces(char_u *line); -void backspace_until_column(int col); -int vim_is_ctrl_x_key(int c); -int ins_compl_add_infercase(char_u *str, int len, int icase, - char_u *fname, int dir, - int flags); -void set_completion(colnr_T startcol, list_T *list); -void ins_compl_show_pum(void); -char_u *find_word_start(char_u *ptr); -char_u *find_word_end(char_u *ptr); -int ins_compl_active(void); -int ins_compl_add_tv(typval_T *tv, int dir); -void ins_compl_check_keys(int frequency); -int get_literal(void); -void insertchar(int c, int flags, int second_indent); -void auto_format(int trailblank, int prev_line); -int comp_textwidth(int ff); -int stop_arrow(void); -void set_last_insert(int c); -void free_last_insert(void); -char_u *add_char2buf(int c, char_u *s); -void beginline(int flags); -int oneright(void); -int oneleft(void); -int cursor_up(long n, int upd_topline); -int cursor_down(long n, int upd_topline); -int stuff_inserted(int c, long count, int no_esc); -char_u *get_last_insert(void); -char_u *get_last_insert_save(void); -void replace_push(int c); -int replace_push_mb(char_u *p); -void fixthisline(int (*get_the_indent)(void)); -void fix_indent(void); -int in_cinkeys(int keytyped, int when, int line_is_empty); -int hkmap(int c); -int ins_copychar(linenr_T lnum); - -#endif /* NEOVIM_EDIT_H */ diff --git a/src/eval.c b/src/eval.c deleted file mode 100644 index 98ab1ea2fe..0000000000 --- a/src/eval.c +++ /dev/null @@ -1,19819 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * eval.c: Expression evaluation. - */ - -#include -#include - -#include "vim.h" -#include "eval.h" -#include "buffer.h" -#include "charset.h" -#include "diff.h" -#include "edit.h" -#include "ex_cmds.h" -#include "ex_cmds2.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "ex_getln.h" -#include "fileio.h" -#include "fold.h" -#include "getchar.h" -#include "hashtab.h" -#include "if_cscope.h" -#include "indent_c.h" -#include "indent.h" -#include "mark.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "keymap.h" -#include "file_search.h" -#include "garray.h" -#include "move.h" -#include "normal.h" -#include "ops.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "popupmnu.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "search.h" -#include "sha256.h" -#include "spell.h" -#include "syntax.h" -#include "tag.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "version.h" -#include "window.h" -#include "os/os.h" -#include "os/job.h" -#include "os/rstream.h" -#include "os/rstream_defs.h" -#include "os/time.h" - -#if defined(FEAT_FLOAT) -# include -#endif - -#define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ - -#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not - be freed. */ - -/* - * In a hashtab item "hi_key" points to "di_key" in a dictitem. - * This avoids adding a pointer to the hashtab item. - * DI2HIKEY() converts a dictitem pointer to a hashitem key pointer. - * HIKEY2DI() converts a hashitem key pointer to a dictitem pointer. - * HI2DI() converts a hashitem pointer to a dictitem pointer. - */ -static dictitem_T dumdi; -#define DI2HIKEY(di) ((di)->di_key) -#define HIKEY2DI(p) ((dictitem_T *)(p - (dumdi.di_key - (char_u *)&dumdi))) -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) - -/* - * Structure returned by get_lval() and used by set_var_lval(). - * For a plain name: - * "name" points to the variable name. - * "exp_name" is NULL. - * "tv" is NULL - * For a magic braces name: - * "name" points to the expanded variable name. - * "exp_name" is non-NULL, to be freed later. - * "tv" is NULL - * For an index in a list: - * "name" points to the (expanded) variable name. - * "exp_name" NULL or non-NULL, to be freed later. - * "tv" points to the (first) list item value - * "li" points to the (first) list item - * "range", "n1", "n2" and "empty2" indicate what items are used. - * For an existing Dict item: - * "name" points to the (expanded) variable name. - * "exp_name" NULL or non-NULL, to be freed later. - * "tv" points to the dict item value - * "newkey" is NULL - * For a non-existing Dict item: - * "name" points to the (expanded) variable name. - * "exp_name" NULL or non-NULL, to be freed later. - * "tv" points to the Dictionary typval_T - * "newkey" is the key for the new item. - */ -typedef struct lval_S { - char_u *ll_name; /* start of variable name (can be NULL) */ - char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */ - typval_T *ll_tv; /* Typeval of item being used. If "newkey" - isn't NULL it's the Dict to which to add - the item. */ - listitem_T *ll_li; /* The list item or NULL. */ - list_T *ll_list; /* The list or NULL. */ - int ll_range; /* TRUE when a [i:j] range was used */ - long ll_n1; /* First index for list */ - long ll_n2; /* Second index for list range */ - int ll_empty2; /* Second index is empty: [i:] */ - dict_T *ll_dict; /* The Dictionary or NULL */ - dictitem_T *ll_di; /* The dictitem or NULL */ - char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ -} lval_T; - - -static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_listidx = N_("E684: list index out of range: %" PRId64); -static char *e_undefvar = N_("E121: Undefined variable: %s"); -static char *e_missbrac = N_("E111: Missing ']'"); -static char *e_listarg = N_("E686: Argument of %s must be a List"); -static char *e_listdictarg = N_( - "E712: Argument of %s must be a List or Dictionary"); -static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); -static char *e_listreq = N_("E714: List required"); -static char *e_dictreq = N_("E715: Dictionary required"); -static char *e_toomanyarg = N_("E118: Too many arguments for function: %s"); -static char *e_dictkey = N_("E716: Key not present in Dictionary: %s"); -static char *e_funcexts = N_( - "E122: Function %s already exists, add ! to replace it"); -static char *e_funcdict = N_("E717: Dictionary entry already exists"); -static char *e_funcref = N_("E718: Funcref required"); -static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_letwrong = N_("E734: Wrong variable type for %s="); -static char *e_nofunc = N_("E130: Unknown function: %s"); -static char *e_illvar = N_("E461: Illegal variable name: %s"); -static char *e_float_as_string = N_("E806: using Float as a String"); - -static dictitem_T globvars_var; /* variable used for g: */ -#define globvarht globvardict.dv_hashtab - -/* - * Old Vim variables such as "v:version" are also available without the "v:". - * Also in functions. We need a special hashtable for them. - */ -static hashtab_T compat_hashtab; - -/* - * When recursively copying lists and dicts we need to remember which ones we - * have done to avoid endless recursiveness. This unique ID is used for that. - * The last bit is used for previous_funccal, ignored when comparing. - */ -static int current_copyID = 0; -#define COPYID_INC 2 -#define COPYID_MASK (~0x1) - -/* - * Array to hold the hashtab with variables local to each sourced script. - * Each item holds a variable (nameless) that points to the dict_T. - */ -typedef struct { - dictitem_T sv_var; - dict_T sv_dict; -} scriptvar_T; - -static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL}; -#define SCRIPT_SV(id) (((scriptvar_T **)ga_scripts.ga_data)[(id) - 1]) -#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab) - -static int echo_attr = 0; /* attributes used for ":echo" */ - -/* Values for trans_function_name() argument: */ -#define TFN_INT 1 /* internal function name OK */ -#define TFN_QUIET 2 /* no error messages */ -#define TFN_NO_AUTOLOAD 4 /* do not use script autoloading */ - -/* Values for get_lval() flags argument: */ -#define GLV_QUIET TFN_QUIET /* no error messages */ -#define GLV_NO_AUTOLOAD TFN_NO_AUTOLOAD /* do not use script autoloading */ - -/* - * Structure to hold info for a user function. - */ -typedef struct ufunc ufunc_T; - -struct ufunc { - int uf_varargs; /* variable nr of arguments */ - int uf_flags; - int uf_calls; /* nr of active calls */ - garray_T uf_args; /* arguments */ - garray_T uf_lines; /* function lines */ - int uf_profiling; /* TRUE when func is being profiled */ - /* profiling the function as a whole */ - int uf_tm_count; /* nr of calls */ - proftime_T uf_tm_total; /* time spent in function + children */ - proftime_T uf_tm_self; /* time spent in function itself */ - proftime_T uf_tm_children; /* time spent in children this call */ - /* profiling the function per line */ - int *uf_tml_count; /* nr of times line was executed */ - proftime_T *uf_tml_total; /* time spent in a line + children */ - proftime_T *uf_tml_self; /* time spent in a line itself */ - proftime_T uf_tml_start; /* start time for current line */ - proftime_T uf_tml_children; /* time spent in children for this line */ - proftime_T uf_tml_wait; /* start wait time for current line */ - int uf_tml_idx; /* index of line being timed; -1 if none */ - int uf_tml_execed; /* line being timed was executed */ - scid_T uf_script_ID; /* ID of script where function was defined, - used for s: variables */ - int uf_refcount; /* for numbered function: reference count */ - char_u uf_name[1]; /* name of function (actually longer); can - start with 123_ ( is K_SPECIAL - KS_EXTRA KE_SNR) */ -}; - -/* function flags */ -#define FC_ABORT 1 /* abort function on error */ -#define FC_RANGE 2 /* function accepts range */ -#define FC_DICT 4 /* Dict function, uses "self" */ - -/* - * All user-defined functions are found in this hashtable. - */ -static hashtab_T func_hashtab; - -/* The names of packages that once were loaded are remembered. */ -static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; - -/* list heads for garbage collection */ -static dict_T *first_dict = NULL; /* list of all dicts */ -static list_T *first_list = NULL; /* list of all lists */ - -/* From user function to hashitem and back. */ -static ufunc_T dumuf; -#define UF2HIKEY(fp) ((fp)->uf_name) -#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) -#define HI2UF(hi) HIKEY2UF((hi)->hi_key) - -#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] -#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] - -#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */ -#define VAR_SHORT_LEN 20 /* short variable name length */ -#define FIXVAR_CNT 12 /* number of fixed variables */ - -/* structure to hold info for a function that is currently being executed. */ -typedef struct funccall_S funccall_T; - -struct funccall_S { - ufunc_T *func; /* function being called */ - int linenr; /* next line to be executed */ - int returned; /* ":return" used */ - struct /* fixed variables for arguments */ - { - dictitem_T var; /* variable (without room for name) */ - char_u room[VAR_SHORT_LEN]; /* room for the name */ - } fixvar[FIXVAR_CNT]; - dict_T l_vars; /* l: local function variables */ - dictitem_T l_vars_var; /* variable for l: scope */ - dict_T l_avars; /* a: argument variables */ - dictitem_T l_avars_var; /* variable for a: scope */ - list_T l_varlist; /* list for a:000 */ - listitem_T l_listitems[MAX_FUNC_ARGS]; /* listitems for a:000 */ - typval_T *rettv; /* return value */ - linenr_T breakpoint; /* next line with breakpoint or zero */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of executed function */ - proftime_T prof_child; /* time spent in a child */ - funccall_T *caller; /* calling function or NULL */ -}; - -/* - * Info used by a ":for" loop. - */ -typedef struct { - int fi_semicolon; /* TRUE if ending in '; var]' */ - int fi_varcount; /* nr of variables in the list */ - listwatch_T fi_lw; /* keep an eye on the item used. */ - list_T *fi_list; /* list being used */ -} forinfo_T; - -/* - * Struct used by trans_function_name() - */ -typedef struct { - dict_T *fd_dict; /* Dictionary used */ - char_u *fd_newkey; /* new key in "dict" in allocated memory */ - dictitem_T *fd_di; /* Dictionary item used */ -} funcdict_T; - - -/* - * Array to hold the value of v: variables. - * The value is in a dictitem, so that it can also be used in the v: scope. - * The reason to use this table anyway is for very quick access to the - * variables with the VV_ defines. - */ -#include "version_defs.h" - -/* values for vv_flags: */ -#define VV_COMPAT 1 /* compatible, also used without "v:" */ -#define VV_RO 2 /* read-only */ -#define VV_RO_SBX 4 /* read-only in the sandbox */ - -#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}, {0} - -static struct vimvar { - char *vv_name; /* name of variable, without v: */ - dictitem_T vv_di; /* value and name for key */ - char vv_filler[16]; /* space for LONGEST name below!!! */ - char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */ -} vimvars[VV_LEN] = -{ - /* - * The order here must match the VV_ defines in vim.h! - * Initializing a union does not work, leave tv.vval empty to get zero's. - */ - {VV_NAME("count", VAR_NUMBER), VV_COMPAT+VV_RO}, - {VV_NAME("count1", VAR_NUMBER), VV_RO}, - {VV_NAME("prevcount", VAR_NUMBER), VV_RO}, - {VV_NAME("errmsg", VAR_STRING), VV_COMPAT}, - {VV_NAME("warningmsg", VAR_STRING), 0}, - {VV_NAME("statusmsg", VAR_STRING), 0}, - {VV_NAME("shell_error", VAR_NUMBER), VV_COMPAT+VV_RO}, - {VV_NAME("this_session", VAR_STRING), VV_COMPAT}, - {VV_NAME("version", VAR_NUMBER), VV_COMPAT+VV_RO}, - {VV_NAME("lnum", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("termresponse", VAR_STRING), VV_RO}, - {VV_NAME("fname", VAR_STRING), VV_RO}, - {VV_NAME("lang", VAR_STRING), VV_RO}, - {VV_NAME("lc_time", VAR_STRING), VV_RO}, - {VV_NAME("ctype", VAR_STRING), VV_RO}, - {VV_NAME("charconvert_from", VAR_STRING), VV_RO}, - {VV_NAME("charconvert_to", VAR_STRING), VV_RO}, - {VV_NAME("fname_in", VAR_STRING), VV_RO}, - {VV_NAME("fname_out", VAR_STRING), VV_RO}, - {VV_NAME("fname_new", VAR_STRING), VV_RO}, - {VV_NAME("fname_diff", VAR_STRING), VV_RO}, - {VV_NAME("cmdarg", VAR_STRING), VV_RO}, - {VV_NAME("foldstart", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("foldend", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("folddashes", VAR_STRING), VV_RO_SBX}, - {VV_NAME("foldlevel", VAR_NUMBER), VV_RO_SBX}, - {VV_NAME("progname", VAR_STRING), VV_RO}, - {VV_NAME("servername", VAR_STRING), VV_RO}, - {VV_NAME("dying", VAR_NUMBER), VV_RO}, - {VV_NAME("exception", VAR_STRING), VV_RO}, - {VV_NAME("throwpoint", VAR_STRING), VV_RO}, - {VV_NAME("register", VAR_STRING), VV_RO}, - {VV_NAME("cmdbang", VAR_NUMBER), VV_RO}, - {VV_NAME("insertmode", VAR_STRING), VV_RO}, - {VV_NAME("val", VAR_UNKNOWN), VV_RO}, - {VV_NAME("key", VAR_UNKNOWN), VV_RO}, - {VV_NAME("profiling", VAR_NUMBER), VV_RO}, - {VV_NAME("fcs_reason", VAR_STRING), VV_RO}, - {VV_NAME("fcs_choice", VAR_STRING), 0}, - {VV_NAME("beval_bufnr", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_winnr", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_lnum", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_col", VAR_NUMBER), VV_RO}, - {VV_NAME("beval_text", VAR_STRING), VV_RO}, - {VV_NAME("scrollstart", VAR_STRING), 0}, - {VV_NAME("swapname", VAR_STRING), VV_RO}, - {VV_NAME("swapchoice", VAR_STRING), 0}, - {VV_NAME("swapcommand", VAR_STRING), VV_RO}, - {VV_NAME("char", VAR_STRING), 0}, - {VV_NAME("mouse_win", VAR_NUMBER), 0}, - {VV_NAME("mouse_lnum", VAR_NUMBER), 0}, - {VV_NAME("mouse_col", VAR_NUMBER), 0}, - {VV_NAME("operator", VAR_STRING), VV_RO}, - {VV_NAME("searchforward", VAR_NUMBER), 0}, - {VV_NAME("hlsearch", VAR_NUMBER), 0}, - {VV_NAME("oldfiles", VAR_LIST), 0}, - {VV_NAME("windowid", VAR_NUMBER), VV_RO}, - {VV_NAME("progpath", VAR_STRING), VV_RO}, - {VV_NAME("job_data", VAR_LIST), 0} -}; - -/* shorthand */ -#define vv_type vv_di.di_tv.v_type -#define vv_nr vv_di.di_tv.vval.v_number -#define vv_float vv_di.di_tv.vval.v_float -#define vv_str vv_di.di_tv.vval.v_string -#define vv_list vv_di.di_tv.vval.v_list -#define vv_tv vv_di.di_tv - -static dictitem_T vimvars_var; /* variable used for v: */ -#define vimvarht vimvardict.dv_hashtab - -static void on_job_stdout(RStream *rstream, void *data, bool eof); -static void on_job_stderr(RStream *rstream, void *data, bool eof); -static void on_job_exit(Job *job, void *data); -static void on_job_data(RStream *rstream, void *data, bool eof, char *type); -static void apply_job_autocmds(Job *job, char *name, char *type, char *str); -static void prepare_vimvar(int idx, typval_T *save_tv); -static void restore_vimvar(int idx, typval_T *save_tv); -static int ex_let_vars(char_u *arg, typval_T *tv, int copy, - int semicolon, int var_count, - char_u *nextchars); -static char_u *skip_var_list(char_u *arg, int *var_count, - int *semicolon); -static char_u *skip_var_one(char_u *arg); -static void list_hashtable_vars(hashtab_T *ht, char_u *prefix, - int empty, - int *first); -static void list_glob_vars(int *first); -static void list_buf_vars(int *first); -static void list_win_vars(int *first); -static void list_tab_vars(int *first); -static void list_vim_vars(int *first); -static void list_script_vars(int *first); -static void list_func_vars(int *first); -static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first); -static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, - char_u *endchars, char_u *op); -static int check_changedtick(char_u *arg); -static char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, - int unlet, int skip, int flags, - int fne_flags); -static void clear_lval(lval_T *lp); -static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, - int copy, - char_u *op); -static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op); -static void list_fix_watch(list_T *l, listitem_T *item); -static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep); -static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit); -static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock); -static void item_lock(typval_T *tv, int deep, int lock); -static int tv_islocked(typval_T *tv); - -static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, - int evaluate); -static int eval1(char_u **arg, typval_T *rettv, int evaluate); -static int eval2(char_u **arg, typval_T *rettv, int evaluate); -static int eval3(char_u **arg, typval_T *rettv, int evaluate); -static int eval4(char_u **arg, typval_T *rettv, int evaluate); -static int eval5(char_u **arg, typval_T *rettv, int evaluate); -static int eval6(char_u **arg, typval_T *rettv, int evaluate, - int want_string); -static int eval7(char_u **arg, typval_T *rettv, int evaluate, - int want_string); - -static int eval_index(char_u **arg, typval_T *rettv, int evaluate, - int verbose); -static int get_option_tv(char_u **arg, typval_T *rettv, int evaluate); -static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate); -static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate); -static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate); -static void rettv_list_alloc(typval_T *rettv); -static long list_len(list_T *l); -static int list_equal(list_T *l1, list_T *l2, int ic, int recursive); -static int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive); -static int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); -static long list_find_nr(list_T *l, long idx, int *errorp); -static long list_idx_of_item(list_T *l, listitem_T *item); -static void list_append_number(list_T *l, varnumber_T n); -static int list_extend(list_T *l1, list_T *l2, listitem_T *bef); -static int list_concat(list_T *l1, list_T *l2, typval_T *tv); -static list_T *list_copy(list_T *orig, int deep, int copyID); -static char_u *list2string(typval_T *tv, int copyID); -static int list_join_inner(garray_T *gap, list_T *l, char_u *sep, - int echo_style, int copyID, - garray_T *join_gap); -static int list_join(garray_T *gap, list_T *l, char_u *sep, int echo, - int copyID); -static int free_unref_items(int copyID); -static int rettv_dict_alloc(typval_T *rettv); -static dictitem_T *dictitem_copy(dictitem_T *org); -static void dictitem_remove(dict_T *dict, dictitem_T *item); -static dict_T *dict_copy(dict_T *orig, int deep, int copyID); -static long dict_len(dict_T *d); -static char_u *dict2string(typval_T *tv, int copyID); -static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); -static char_u *echo_string(typval_T *tv, char_u **tofree, - char_u *numbuf, - int copyID); -static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, - int copyID); -static char_u *string_quote(char_u *str, int function); -static int string2float(char_u *text, float_T *value); -static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); -static int find_internal_func(char_u *name); -static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload); -static int get_func_tv(char_u *name, int len, typval_T *rettv, - char_u **arg, linenr_T firstline, linenr_T lastline, - int *doesrange, int evaluate, - dict_T *selfdict); -static int call_func(char_u *funcname, int len, typval_T *rettv, - int argcount, typval_T *argvars, - linenr_T firstline, linenr_T lastline, - int *doesrange, int evaluate, - dict_T *selfdict); -static void emsg_funcname(char *ermsg, char_u *name); -static int non_zero_arg(typval_T *argvars); - -static void f_abs(typval_T *argvars, typval_T *rettv); -static void f_acos(typval_T *argvars, typval_T *rettv); -static void f_add(typval_T *argvars, typval_T *rettv); -static void f_and(typval_T *argvars, typval_T *rettv); -static void f_append(typval_T *argvars, typval_T *rettv); -static void f_argc(typval_T *argvars, typval_T *rettv); -static void f_argidx(typval_T *argvars, typval_T *rettv); -static void f_argv(typval_T *argvars, typval_T *rettv); -static void f_asin(typval_T *argvars, typval_T *rettv); -static void f_atan(typval_T *argvars, typval_T *rettv); -static void f_atan2(typval_T *argvars, typval_T *rettv); -static void f_browse(typval_T *argvars, typval_T *rettv); -static void f_browsedir(typval_T *argvars, typval_T *rettv); -static void f_bufexists(typval_T *argvars, typval_T *rettv); -static void f_buflisted(typval_T *argvars, typval_T *rettv); -static void f_bufloaded(typval_T *argvars, typval_T *rettv); -static void f_bufname(typval_T *argvars, typval_T *rettv); -static void f_bufnr(typval_T *argvars, typval_T *rettv); -static void f_bufwinnr(typval_T *argvars, typval_T *rettv); -static void f_byte2line(typval_T *argvars, typval_T *rettv); -static void byteidx(typval_T *argvars, typval_T *rettv, int comp); -static void f_byteidx(typval_T *argvars, typval_T *rettv); -static void f_byteidxcomp(typval_T *argvars, typval_T *rettv); -static void f_call(typval_T *argvars, typval_T *rettv); -static void f_ceil(typval_T *argvars, typval_T *rettv); -static void f_changenr(typval_T *argvars, typval_T *rettv); -static void f_char2nr(typval_T *argvars, typval_T *rettv); -static void f_cindent(typval_T *argvars, typval_T *rettv); -static void f_clearmatches(typval_T *argvars, typval_T *rettv); -static void f_col(typval_T *argvars, typval_T *rettv); -static void f_complete(typval_T *argvars, typval_T *rettv); -static void f_complete_add(typval_T *argvars, typval_T *rettv); -static void f_complete_check(typval_T *argvars, typval_T *rettv); -static void f_confirm(typval_T *argvars, typval_T *rettv); -static void f_copy(typval_T *argvars, typval_T *rettv); -static void f_cos(typval_T *argvars, typval_T *rettv); -static void f_cosh(typval_T *argvars, typval_T *rettv); -static void f_count(typval_T *argvars, typval_T *rettv); -static void f_cscope_connection(typval_T *argvars, typval_T *rettv); -static void f_cursor(typval_T *argsvars, typval_T *rettv); -static void f_deepcopy(typval_T *argvars, typval_T *rettv); -static void f_delete(typval_T *argvars, typval_T *rettv); -static void f_did_filetype(typval_T *argvars, typval_T *rettv); -static void f_diff_filler(typval_T *argvars, typval_T *rettv); -static void f_diff_hlID(typval_T *argvars, typval_T *rettv); -static void f_empty(typval_T *argvars, typval_T *rettv); -static void f_escape(typval_T *argvars, typval_T *rettv); -static void f_eval(typval_T *argvars, typval_T *rettv); -static void f_eventhandler(typval_T *argvars, typval_T *rettv); -static void f_executable(typval_T *argvars, typval_T *rettv); -static void f_exists(typval_T *argvars, typval_T *rettv); -static void f_exp(typval_T *argvars, typval_T *rettv); -static void f_expand(typval_T *argvars, typval_T *rettv); -static void f_extend(typval_T *argvars, typval_T *rettv); -static void f_feedkeys(typval_T *argvars, typval_T *rettv); -static void f_filereadable(typval_T *argvars, typval_T *rettv); -static void f_filewritable(typval_T *argvars, typval_T *rettv); -static void f_filter(typval_T *argvars, typval_T *rettv); -static void f_finddir(typval_T *argvars, typval_T *rettv); -static void f_findfile(typval_T *argvars, typval_T *rettv); -static void f_float2nr(typval_T *argvars, typval_T *rettv); -static void f_floor(typval_T *argvars, typval_T *rettv); -static void f_fmod(typval_T *argvars, typval_T *rettv); -static void f_fnameescape(typval_T *argvars, typval_T *rettv); -static void f_fnamemodify(typval_T *argvars, typval_T *rettv); -static void f_foldclosed(typval_T *argvars, typval_T *rettv); -static void f_foldclosedend(typval_T *argvars, typval_T *rettv); -static void f_foldlevel(typval_T *argvars, typval_T *rettv); -static void f_foldtext(typval_T *argvars, typval_T *rettv); -static void f_foldtextresult(typval_T *argvars, typval_T *rettv); -static void f_foreground(typval_T *argvars, typval_T *rettv); -static void f_function(typval_T *argvars, typval_T *rettv); -static void f_garbagecollect(typval_T *argvars, typval_T *rettv); -static void f_get(typval_T *argvars, typval_T *rettv); -static void f_getbufline(typval_T *argvars, typval_T *rettv); -static void f_getbufvar(typval_T *argvars, typval_T *rettv); -static void f_getchar(typval_T *argvars, typval_T *rettv); -static void f_getcharmod(typval_T *argvars, typval_T *rettv); -static void f_getcmdline(typval_T *argvars, typval_T *rettv); -static void f_getcmdpos(typval_T *argvars, typval_T *rettv); -static void f_getcmdtype(typval_T *argvars, typval_T *rettv); -static void f_getcwd(typval_T *argvars, typval_T *rettv); -static void f_getfontname(typval_T *argvars, typval_T *rettv); -static void f_getfperm(typval_T *argvars, typval_T *rettv); -static void f_getfsize(typval_T *argvars, typval_T *rettv); -static void f_getftime(typval_T *argvars, typval_T *rettv); -static void f_getftype(typval_T *argvars, typval_T *rettv); -static void f_getline(typval_T *argvars, typval_T *rettv); -static void f_getmatches(typval_T *argvars, typval_T *rettv); -static void f_getpid(typval_T *argvars, typval_T *rettv); -static void f_getpos(typval_T *argvars, typval_T *rettv); -static void f_getqflist(typval_T *argvars, typval_T *rettv); -static void f_getreg(typval_T *argvars, typval_T *rettv); -static void f_getregtype(typval_T *argvars, typval_T *rettv); -static void f_gettabvar(typval_T *argvars, typval_T *rettv); -static void f_gettabwinvar(typval_T *argvars, typval_T *rettv); -static void f_getwinposx(typval_T *argvars, typval_T *rettv); -static void f_getwinposy(typval_T *argvars, typval_T *rettv); -static void f_getwinvar(typval_T *argvars, typval_T *rettv); -static void f_glob(typval_T *argvars, typval_T *rettv); -static void f_globpath(typval_T *argvars, typval_T *rettv); -static void f_has(typval_T *argvars, typval_T *rettv); -static void f_has_key(typval_T *argvars, typval_T *rettv); -static void f_haslocaldir(typval_T *argvars, typval_T *rettv); -static void f_hasmapto(typval_T *argvars, typval_T *rettv); -static void f_histadd(typval_T *argvars, typval_T *rettv); -static void f_histdel(typval_T *argvars, typval_T *rettv); -static void f_histget(typval_T *argvars, typval_T *rettv); -static void f_histnr(typval_T *argvars, typval_T *rettv); -static void f_hlID(typval_T *argvars, typval_T *rettv); -static void f_hlexists(typval_T *argvars, typval_T *rettv); -static void f_hostname(typval_T *argvars, typval_T *rettv); -static void f_iconv(typval_T *argvars, typval_T *rettv); -static void f_indent(typval_T *argvars, typval_T *rettv); -static void f_index(typval_T *argvars, typval_T *rettv); -static void f_input(typval_T *argvars, typval_T *rettv); -static void f_inputdialog(typval_T *argvars, typval_T *rettv); -static void f_inputlist(typval_T *argvars, typval_T *rettv); -static void f_inputrestore(typval_T *argvars, typval_T *rettv); -static void f_inputsave(typval_T *argvars, typval_T *rettv); -static void f_inputsecret(typval_T *argvars, typval_T *rettv); -static void f_insert(typval_T *argvars, typval_T *rettv); -static void f_invert(typval_T *argvars, typval_T *rettv); -static void f_isdirectory(typval_T *argvars, typval_T *rettv); -static void f_islocked(typval_T *argvars, typval_T *rettv); -static void f_items(typval_T *argvars, typval_T *rettv); -static void f_job_start(typval_T *argvars, typval_T *rettv); -static void f_job_stop(typval_T *argvars, typval_T *rettv); -static void f_job_write(typval_T *argvars, typval_T *rettv); -static void f_join(typval_T *argvars, typval_T *rettv); -static void f_keys(typval_T *argvars, typval_T *rettv); -static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); -static void f_len(typval_T *argvars, typval_T *rettv); -static void f_libcall(typval_T *argvars, typval_T *rettv); -static void f_libcallnr(typval_T *argvars, typval_T *rettv); -static void f_line(typval_T *argvars, typval_T *rettv); -static void f_line2byte(typval_T *argvars, typval_T *rettv); -static void f_lispindent(typval_T *argvars, typval_T *rettv); -static void f_localtime(typval_T *argvars, typval_T *rettv); -static void f_log(typval_T *argvars, typval_T *rettv); -static void f_log10(typval_T *argvars, typval_T *rettv); -static void f_map(typval_T *argvars, typval_T *rettv); -static void f_maparg(typval_T *argvars, typval_T *rettv); -static void f_mapcheck(typval_T *argvars, typval_T *rettv); -static void f_match(typval_T *argvars, typval_T *rettv); -static void f_matchadd(typval_T *argvars, typval_T *rettv); -static void f_matcharg(typval_T *argvars, typval_T *rettv); -static void f_matchdelete(typval_T *argvars, typval_T *rettv); -static void f_matchend(typval_T *argvars, typval_T *rettv); -static void f_matchlist(typval_T *argvars, typval_T *rettv); -static void f_matchstr(typval_T *argvars, typval_T *rettv); -static void f_max(typval_T *argvars, typval_T *rettv); -static void f_min(typval_T *argvars, typval_T *rettv); -static void f_mkdir(typval_T *argvars, typval_T *rettv); -static void f_mode(typval_T *argvars, typval_T *rettv); -static void f_nextnonblank(typval_T *argvars, typval_T *rettv); -static void f_nr2char(typval_T *argvars, typval_T *rettv); -static void f_or(typval_T *argvars, typval_T *rettv); -static void f_pathshorten(typval_T *argvars, typval_T *rettv); -static void f_pow(typval_T *argvars, typval_T *rettv); -static void f_prevnonblank(typval_T *argvars, typval_T *rettv); -static void f_printf(typval_T *argvars, typval_T *rettv); -static void f_pumvisible(typval_T *argvars, typval_T *rettv); -static void f_range(typval_T *argvars, typval_T *rettv); -static void f_readfile(typval_T *argvars, typval_T *rettv); -static void f_reltime(typval_T *argvars, typval_T *rettv); -static void f_reltimestr(typval_T *argvars, typval_T *rettv); -static void f_remote_expr(typval_T *argvars, typval_T *rettv); -static void f_remote_foreground(typval_T *argvars, typval_T *rettv); -static void f_remote_peek(typval_T *argvars, typval_T *rettv); -static void f_remote_read(typval_T *argvars, typval_T *rettv); -static void f_remote_send(typval_T *argvars, typval_T *rettv); -static void f_remove(typval_T *argvars, typval_T *rettv); -static void f_rename(typval_T *argvars, typval_T *rettv); -static void f_repeat(typval_T *argvars, typval_T *rettv); -static void f_resolve(typval_T *argvars, typval_T *rettv); -static void f_reverse(typval_T *argvars, typval_T *rettv); -static void f_round(typval_T *argvars, typval_T *rettv); -static void f_screenattr(typval_T *argvars, typval_T *rettv); -static void f_screenchar(typval_T *argvars, typval_T *rettv); -static void f_screencol(typval_T *argvars, typval_T *rettv); -static void f_screenrow(typval_T *argvars, typval_T *rettv); -static void f_search(typval_T *argvars, typval_T *rettv); -static void f_searchdecl(typval_T *argvars, typval_T *rettv); -static void f_searchpair(typval_T *argvars, typval_T *rettv); -static void f_searchpairpos(typval_T *argvars, typval_T *rettv); -static void f_searchpos(typval_T *argvars, typval_T *rettv); -static void f_server2client(typval_T *argvars, typval_T *rettv); -static void f_serverlist(typval_T *argvars, typval_T *rettv); -static void f_setbufvar(typval_T *argvars, typval_T *rettv); -static void f_setcmdpos(typval_T *argvars, typval_T *rettv); -static void f_setline(typval_T *argvars, typval_T *rettv); -static void f_setloclist(typval_T *argvars, typval_T *rettv); -static void f_setmatches(typval_T *argvars, typval_T *rettv); -static void f_setpos(typval_T *argvars, typval_T *rettv); -static void f_setqflist(typval_T *argvars, typval_T *rettv); -static void f_setreg(typval_T *argvars, typval_T *rettv); -static void f_settabvar(typval_T *argvars, typval_T *rettv); -static void f_settabwinvar(typval_T *argvars, typval_T *rettv); -static void f_setwinvar(typval_T *argvars, typval_T *rettv); -static void f_sha256(typval_T *argvars, typval_T *rettv); -static void f_shellescape(typval_T *argvars, typval_T *rettv); -static void f_shiftwidth(typval_T *argvars, typval_T *rettv); -static void f_simplify(typval_T *argvars, typval_T *rettv); -static void f_sin(typval_T *argvars, typval_T *rettv); -static void f_sinh(typval_T *argvars, typval_T *rettv); -static void f_sort(typval_T *argvars, typval_T *rettv); -static void f_soundfold(typval_T *argvars, typval_T *rettv); -static void f_spellbadword(typval_T *argvars, typval_T *rettv); -static void f_spellsuggest(typval_T *argvars, typval_T *rettv); -static void f_split(typval_T *argvars, typval_T *rettv); -static void f_sqrt(typval_T *argvars, typval_T *rettv); -static void f_str2float(typval_T *argvars, typval_T *rettv); -static void f_str2nr(typval_T *argvars, typval_T *rettv); -static void f_strchars(typval_T *argvars, typval_T *rettv); -static void f_strftime(typval_T *argvars, typval_T *rettv); -static void f_stridx(typval_T *argvars, typval_T *rettv); -static void f_string(typval_T *argvars, typval_T *rettv); -static void f_strlen(typval_T *argvars, typval_T *rettv); -static void f_strpart(typval_T *argvars, typval_T *rettv); -static void f_strridx(typval_T *argvars, typval_T *rettv); -static void f_strtrans(typval_T *argvars, typval_T *rettv); -static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv); -static void f_strwidth(typval_T *argvars, typval_T *rettv); -static void f_submatch(typval_T *argvars, typval_T *rettv); -static void f_substitute(typval_T *argvars, typval_T *rettv); -static void f_synID(typval_T *argvars, typval_T *rettv); -static void f_synIDattr(typval_T *argvars, typval_T *rettv); -static void f_synIDtrans(typval_T *argvars, typval_T *rettv); -static void f_synstack(typval_T *argvars, typval_T *rettv); -static void f_synconcealed(typval_T *argvars, typval_T *rettv); -static void f_system(typval_T *argvars, typval_T *rettv); -static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv); -static void f_tabpagenr(typval_T *argvars, typval_T *rettv); -static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv); -static void f_taglist(typval_T *argvars, typval_T *rettv); -static void f_tagfiles(typval_T *argvars, typval_T *rettv); -static void f_tempname(typval_T *argvars, typval_T *rettv); -static void f_test(typval_T *argvars, typval_T *rettv); -static void f_tan(typval_T *argvars, typval_T *rettv); -static void f_tanh(typval_T *argvars, typval_T *rettv); -static void f_tolower(typval_T *argvars, typval_T *rettv); -static void f_toupper(typval_T *argvars, typval_T *rettv); -static void f_tr(typval_T *argvars, typval_T *rettv); -static void f_trunc(typval_T *argvars, typval_T *rettv); -static void f_type(typval_T *argvars, typval_T *rettv); -static void f_undofile(typval_T *argvars, typval_T *rettv); -static void f_undotree(typval_T *argvars, typval_T *rettv); -static void f_uniq(typval_T *argvars, typval_T *rettv); -static void f_values(typval_T *argvars, typval_T *rettv); -static void f_virtcol(typval_T *argvars, typval_T *rettv); -static void f_visualmode(typval_T *argvars, typval_T *rettv); -static void f_wildmenumode(typval_T *argvars, typval_T *rettv); -static void f_winbufnr(typval_T *argvars, typval_T *rettv); -static void f_wincol(typval_T *argvars, typval_T *rettv); -static void f_winheight(typval_T *argvars, typval_T *rettv); -static void f_winline(typval_T *argvars, typval_T *rettv); -static void f_winnr(typval_T *argvars, typval_T *rettv); -static void f_winrestcmd(typval_T *argvars, typval_T *rettv); -static void f_winrestview(typval_T *argvars, typval_T *rettv); -static void f_winsaveview(typval_T *argvars, typval_T *rettv); -static void f_winwidth(typval_T *argvars, typval_T *rettv); -static void f_writefile(typval_T *argvars, typval_T *rettv); -static void f_xor(typval_T *argvars, typval_T *rettv); - -static int list2fpos(typval_T *arg, pos_T *posp, int *fnump); -static pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum); -static int get_env_len(char_u **arg); -static int get_id_len(char_u **arg); -static int get_name_len(char_u **arg, char_u **alias, int evaluate, - int verbose); -static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u * - *expr_end, - int flags); -#define FNE_INCL_BR 1 /* find_name_end(): include [] in name */ -#define FNE_CHECK_START 2 /* find_name_end(): check name starts with - valid character */ -static char_u * -make_expanded_name(char_u *in_start, char_u *expr_start, char_u * - expr_end, - char_u *in_end); -static int eval_isnamec(int c); -static int eval_isnamec1(int c); -static int get_var_tv(char_u *name, int len, typval_T *rettv, - int verbose, - int no_autoload); -static int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, - int verbose); -static void init_tv(typval_T *varp); -static long get_tv_number(typval_T *varp); -static linenr_T get_tv_lnum(typval_T *argvars); -static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf); -static char_u *get_tv_string(typval_T *varp); -static char_u *get_tv_string_buf(typval_T *varp, char_u *buf); -static char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf); -static dictitem_T *find_var(char_u *name, hashtab_T **htp, - int no_autoload); -static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, - char_u *varname, - int no_autoload); -static hashtab_T *find_var_ht(char_u *name, char_u **varname); -static void vars_clear_ext(hashtab_T *ht, int free_val); -static void delete_var(hashtab_T *ht, hashitem_T *hi); -static void list_one_var(dictitem_T *v, char_u *prefix, int *first); -static void list_one_var_a(char_u *prefix, char_u *name, int type, - char_u *string, - int *first); -static void set_var(char_u *name, typval_T *varp, int copy); -static int var_check_ro(int flags, char_u *name); -static int var_check_fixed(int flags, char_u *name); -static int var_check_func_name(char_u *name, int new_var); -static int valid_varname(char_u *varname); -static int tv_check_lock(int lock, char_u *name); -static int item_copy(typval_T *from, typval_T *to, int deep, int copyID); -static char_u *find_option_end(char_u **arg, int *opt_flags); -static char_u *trans_function_name(char_u **pp, int skip, int flags, - funcdict_T *fd); -static int eval_fname_script(char_u *p); -static int eval_fname_sid(char_u *p); -static void list_func_head(ufunc_T *fp, int indent); -static ufunc_T *find_func(char_u *name); -static int function_exists(char_u *name); -static bool builtin_function(char_u *name, int len); -static void func_do_profile(ufunc_T *fp); -static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, - char *title, - int prefer_self); -static void prof_func_line(FILE *fd, int count, proftime_T *total, - proftime_T *self, - int prefer_self); -static int -prof_total_cmp(const void *s1, const void *s2); -static int -prof_self_cmp(const void *s1, const void *s2); -static int script_autoload(char_u *name, int reload); -static char_u *autoload_name(char_u *name); -static void cat_func_name(char_u *buf, ufunc_T *fp); -static void func_free(ufunc_T *fp); -static void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, - typval_T *rettv, linenr_T firstline, - linenr_T lastline, - dict_T *selfdict); -static int can_free_funccal(funccall_T *fc, int copyID); -static void free_funccal(funccall_T *fc, int free_val); -static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, - varnumber_T nr); -static win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp); -static void getwinvar(typval_T *argvars, typval_T *rettv, int off); -static int searchpair_cmn(typval_T *argvars, pos_T *match_pos); -static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp); -static void setwinvar(typval_T *argvars, typval_T *rettv, int off); - - - -/* - * Initialize the global and v: variables. - */ -void eval_init(void) -{ - int i; - struct vimvar *p; - - init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); - init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE); - vimvardict.dv_lock = VAR_FIXED; - hash_init(&compat_hashtab); - hash_init(&func_hashtab); - - for (i = 0; i < VV_LEN; ++i) { - p = &vimvars[i]; - STRCPY(p->vv_di.di_key, p->vv_name); - if (p->vv_flags & VV_RO) - p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - else if (p->vv_flags & VV_RO_SBX) - p->vv_di.di_flags = DI_FLAGS_RO_SBX | DI_FLAGS_FIX; - else - p->vv_di.di_flags = DI_FLAGS_FIX; - - /* add to v: scope dict, unless the value is not always available */ - if (p->vv_type != VAR_UNKNOWN) - hash_add(&vimvarht, p->vv_di.di_key); - if (p->vv_flags & VV_COMPAT) - /* add to compat scope dict */ - hash_add(&compat_hashtab, p->vv_di.di_key); - } - set_vim_var_nr(VV_SEARCHFORWARD, 1L); - set_vim_var_nr(VV_HLSEARCH, 1L); - set_reg_var(0); /* default for v:register is not 0 but '"' */ - -} - -#if defined(EXITFREE) || defined(PROTO) -void eval_clear(void) -{ - int i; - struct vimvar *p; - - for (i = 0; i < VV_LEN; ++i) { - p = &vimvars[i]; - if (p->vv_di.di_tv.v_type == VAR_STRING) { - free(p->vv_str); - p->vv_str = NULL; - } else if (p->vv_di.di_tv.v_type == VAR_LIST) { - list_unref(p->vv_list); - p->vv_list = NULL; - } - } - hash_clear(&vimvarht); - hash_init(&vimvarht); /* garbage_collect() will access it */ - hash_clear(&compat_hashtab); - - free_scriptnames(); - free_locales(); - - /* global variables */ - vars_clear(&globvarht); - - /* autoloaded script names */ - ga_clear_strings(&ga_loaded); - - /* Script-local variables. First clear all the variables and in a second - * loop free the scriptvar_T, because a variable in one script might hold - * a reference to the whole scope of another script. */ - for (i = 1; i <= ga_scripts.ga_len; ++i) - vars_clear(&SCRIPT_VARS(i)); - for (i = 1; i <= ga_scripts.ga_len; ++i) - free(SCRIPT_SV(i)); - ga_clear(&ga_scripts); - - /* unreferenced lists and dicts */ - (void)garbage_collect(); - - /* functions */ - free_all_functions(); - hash_clear(&func_hashtab); -} - -#endif - -/* - * Return the name of the executed function. - */ -char_u *func_name(void *cookie) -{ - return ((funccall_T *)cookie)->func->uf_name; -} - -/* - * Return the address holding the next breakpoint line for a funccall cookie. - */ -linenr_T *func_breakpoint(void *cookie) -{ - return &((funccall_T *)cookie)->breakpoint; -} - -/* - * Return the address holding the debug tick for a funccall cookie. - */ -int *func_dbg_tick(void *cookie) -{ - return &((funccall_T *)cookie)->dbg_tick; -} - -/* - * Return the nesting level for a funccall cookie. - */ -int func_level(void *cookie) -{ - return ((funccall_T *)cookie)->level; -} - -/* pointer to funccal for currently active function */ -funccall_T *current_funccal = NULL; - -/* pointer to list of previously used funccal, still around because some - * item in it is still being used. */ -funccall_T *previous_funccal = NULL; - -/* - * Return TRUE when a function was ended by a ":return" command. - */ -int current_func_returned(void) -{ - return current_funccal->returned; -} - -/* - * Set an internal variable to a string value. Creates the variable if it does - * not already exist. - */ -void set_internal_string_var(char_u *name, char_u *value) -{ - char_u *val = vim_strsave(value); - typval_T *tvp = xcalloc(1, sizeof(typval_T)); - - tvp->v_type = VAR_STRING; - tvp->vval.v_string = val; - set_var(name, tvp, FALSE); - free_tv(tvp); -} - -static lval_T *redir_lval = NULL; -static garray_T redir_ga; /* only valid when redir_lval is not NULL */ -static char_u *redir_endp = NULL; -static char_u *redir_varname = NULL; - -/* - * Start recording command output to a variable - * Returns OK if successfully completed the setup. FAIL otherwise. - */ -int -var_redir_start ( - char_u *name, - int append /* append to an existing variable */ -) -{ - int save_emsg; - int err; - typval_T tv; - - /* Catch a bad name early. */ - if (!eval_isnamec1(*name)) { - EMSG(_(e_invarg)); - return FAIL; - } - - /* Make a copy of the name, it is used in redir_lval until redir ends. */ - redir_varname = vim_strsave(name); - if (redir_varname == NULL) - return FAIL; - - redir_lval = xcalloc(1, sizeof(lval_T)); - - /* The output is stored in growarray "redir_ga" until redirection ends. */ - ga_init(&redir_ga, (int)sizeof(char), 500); - - /* Parse the variable name (can be a dict or list entry). */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, - FNE_CHECK_START); - if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != - NUL) { - clear_lval(redir_lval); - if (redir_endp != NULL && *redir_endp != NUL) - /* Trailing characters are present after the variable name */ - EMSG(_(e_trailing)); - else - EMSG(_(e_invarg)); - redir_endp = NULL; /* don't store a value, only cleanup */ - var_redir_stop(); - return FAIL; - } - - /* check if we can write to the variable: set it to or append an empty - * string */ - save_emsg = did_emsg; - did_emsg = FALSE; - tv.v_type = VAR_STRING; - tv.vval.v_string = (char_u *)""; - if (append) - set_var_lval(redir_lval, redir_endp, &tv, TRUE, (char_u *)"."); - else - set_var_lval(redir_lval, redir_endp, &tv, TRUE, (char_u *)"="); - clear_lval(redir_lval); - err = did_emsg; - did_emsg |= save_emsg; - if (err) { - redir_endp = NULL; /* don't store a value, only cleanup */ - var_redir_stop(); - return FAIL; - } - - return OK; -} - -/* - * Append "value[value_len]" to the variable set by var_redir_start(). - * The actual appending is postponed until redirection ends, because the value - * appended may in fact be the string we write to, changing it may cause freed - * memory to be used: - * :redir => foo - * :let foo - * :redir END - */ -void var_redir_str(char_u *value, int value_len) -{ - int len; - - if (redir_lval == NULL) - return; - - if (value_len == -1) - len = (int)STRLEN(value); /* Append the entire string */ - else - len = value_len; /* Append only "value_len" characters */ - - ga_grow(&redir_ga, len); - memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len); - redir_ga.ga_len += len; -} - -/* - * Stop redirecting command output to a variable. - * Frees the allocated memory. - */ -void var_redir_stop(void) -{ - typval_T tv; - - if (redir_lval != NULL) { - /* If there was no error: assign the text to the variable. */ - if (redir_endp != NULL) { - ga_append(&redir_ga, NUL); /* Append the trailing NUL. */ - tv.v_type = VAR_STRING; - tv.vval.v_string = redir_ga.ga_data; - /* Call get_lval() again, if it's inside a Dict or List it may - * have changed. */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, - FALSE, FALSE, 0, FNE_CHECK_START); - if (redir_endp != NULL && redir_lval->ll_name != NULL) - set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)"."); - clear_lval(redir_lval); - } - - /* free the collected output */ - free(redir_ga.ga_data); - redir_ga.ga_data = NULL; - - free(redir_lval); - redir_lval = NULL; - } - free(redir_varname); - redir_varname = NULL; -} - -int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to) -{ - int err = FALSE; - - set_vim_var_string(VV_CC_FROM, enc_from, -1); - set_vim_var_string(VV_CC_TO, enc_to, -1); - set_vim_var_string(VV_FNAME_IN, fname_from, -1); - set_vim_var_string(VV_FNAME_OUT, fname_to, -1); - if (eval_to_bool(p_ccv, &err, NULL, FALSE)) - err = TRUE; - set_vim_var_string(VV_CC_FROM, NULL, -1); - set_vim_var_string(VV_CC_TO, NULL, -1); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); - - if (err) - return FAIL; - return OK; -} - -int eval_printexpr(char_u *fname, char_u *args) -{ - int err = FALSE; - - set_vim_var_string(VV_FNAME_IN, fname, -1); - set_vim_var_string(VV_CMDARG, args, -1); - if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) - err = TRUE; - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_CMDARG, NULL, -1); - - if (err) { - os_remove((char *)fname); - return FAIL; - } - return OK; -} - -void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile) -{ - int err = FALSE; - - set_vim_var_string(VV_FNAME_IN, origfile, -1); - set_vim_var_string(VV_FNAME_NEW, newfile, -1); - set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_dex, &err, NULL, FALSE); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_NEW, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); -} - -void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile) -{ - int err; - - set_vim_var_string(VV_FNAME_IN, origfile, -1); - set_vim_var_string(VV_FNAME_DIFF, difffile, -1); - set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_pex, &err, NULL, FALSE); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_DIFF, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); -} - -/* - * Top level evaluation function, returning a boolean. - * Sets "error" to TRUE if there was an error. - * Return TRUE or FALSE. - */ -int -eval_to_bool ( - char_u *arg, - int *error, - char_u **nextcmd, - int skip /* only parse, don't execute */ -) -{ - typval_T tv; - int retval = FALSE; - - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) - *error = TRUE; - else { - *error = FALSE; - if (!skip) { - retval = (get_tv_number_chk(&tv, error) != 0); - clear_tv(&tv); - } - } - if (skip) - --emsg_skip; - - return retval; -} - -/* - * Top level evaluation function, returning a string. If "skip" is TRUE, - * only parsing to "nextcmd" is done, without reporting errors. Return - * pointer to allocated memory, or NULL for failure or when "skip" is TRUE. - */ -char_u * -eval_to_string_skip ( - char_u *arg, - char_u **nextcmd, - int skip /* only parse, don't execute */ -) -{ - typval_T tv; - char_u *retval; - - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip) - retval = NULL; - else { - retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); - } - if (skip) - --emsg_skip; - - return retval; -} - -/* - * Skip over an expression at "*pp". - * Return FAIL for an error, OK otherwise. - */ -int skip_expr(char_u **pp) -{ - typval_T rettv; - - *pp = skipwhite(*pp); - return eval1(pp, &rettv, FALSE); -} - -/* - * Top level evaluation function, returning a string. - * When "convert" is TRUE convert a List into a sequence of lines and convert - * a Float to a String. - * Return pointer to allocated memory, or NULL for failure. - */ -char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) -{ - typval_T tv; - char_u *retval; - garray_T ga; - char_u numbuf[NUMBUFLEN]; - - if (eval0(arg, &tv, nextcmd, TRUE) == FAIL) - retval = NULL; - else { - if (convert && tv.v_type == VAR_LIST) { - ga_init(&ga, (int)sizeof(char), 80); - if (tv.vval.v_list != NULL) { - list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, 0); - if (tv.vval.v_list->lv_len > 0) - ga_append(&ga, NL); - } - ga_append(&ga, NUL); - retval = (char_u *)ga.ga_data; - } else if (convert && tv.v_type == VAR_FLOAT) { - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float); - retval = vim_strsave(numbuf); - } else - retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); - } - - return retval; -} - -/* - * Call eval_to_string() without using current local variables and using - * textlock. When "use_sandbox" is TRUE use the sandbox. - */ -char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox) -{ - char_u *retval; - void *save_funccalp; - - save_funccalp = save_funccal(); - if (use_sandbox) - ++sandbox; - ++textlock; - retval = eval_to_string(arg, nextcmd, FALSE); - if (use_sandbox) - --sandbox; - --textlock; - restore_funccal(save_funccalp); - return retval; -} - -/* - * Top level evaluation function, returning a number. - * Evaluates "expr" silently. - * Returns -1 for an error. - */ -int eval_to_number(char_u *expr) -{ - typval_T rettv; - int retval; - char_u *p = skipwhite(expr); - - ++emsg_off; - - if (eval1(&p, &rettv, TRUE) == FAIL) - retval = -1; - else { - retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); - } - --emsg_off; - - return retval; -} - - -/* - * Prepare v: variable "idx" to be used. - * Save the current typeval in "save_tv". - * When not used yet add the variable to the v: hashtable. - */ -static void prepare_vimvar(int idx, typval_T *save_tv) -{ - *save_tv = vimvars[idx].vv_tv; - if (vimvars[idx].vv_type == VAR_UNKNOWN) - hash_add(&vimvarht, vimvars[idx].vv_di.di_key); -} - -/* - * Restore v: variable "idx" to typeval "save_tv". - * When no longer defined, remove the variable from the v: hashtable. - */ -static void restore_vimvar(int idx, typval_T *save_tv) -{ - hashitem_T *hi; - - vimvars[idx].vv_tv = *save_tv; - if (vimvars[idx].vv_type == VAR_UNKNOWN) { - hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "restore_vimvar()"); - else - hash_remove(&vimvarht, hi); - } -} - -/* - * Evaluate an expression to a list with suggestions. - * For the "expr:" part of 'spellsuggest'. - * Returns NULL when there is an error. - */ -list_T *eval_spell_expr(char_u *badword, char_u *expr) -{ - typval_T save_val; - typval_T rettv; - list_T *list = NULL; - char_u *p = skipwhite(expr); - - /* Set "v:val" to the bad word. */ - prepare_vimvar(VV_VAL, &save_val); - vimvars[VV_VAL].vv_type = VAR_STRING; - vimvars[VV_VAL].vv_str = badword; - if (p_verbose == 0) - ++emsg_off; - - if (eval1(&p, &rettv, TRUE) == OK) { - if (rettv.v_type != VAR_LIST) - clear_tv(&rettv); - else - list = rettv.vval.v_list; - } - - if (p_verbose == 0) - --emsg_off; - restore_vimvar(VV_VAL, &save_val); - - return list; -} - -/* - * "list" is supposed to contain two items: a word and a number. Return the - * word in "pp" and the number as the return value. - * Return -1 if anything isn't right. - * Used to get the good word and score from the eval_spell_expr() result. - */ -int get_spellword(list_T *list, char_u **pp) -{ - listitem_T *li; - - li = list->lv_first; - if (li == NULL) - return -1; - *pp = get_tv_string(&li->li_tv); - - li = li->li_next; - if (li == NULL) - return -1; - return get_tv_number(&li->li_tv); -} - -/* - * Top level evaluation function. - * Returns an allocated typval_T with the result. - * Returns NULL when there is an error. - */ -typval_T *eval_expr(char_u *arg, char_u **nextcmd) -{ - typval_T *tv; - - tv = (typval_T *)alloc(sizeof(typval_T)); - if (tv != NULL && eval0(arg, tv, nextcmd, TRUE) == FAIL) { - free(tv); - tv = NULL; - } - - return tv; -} - - -/* - * Call some vimL function and return the result in "*rettv". - * Uses argv[argc] for the function arguments. Only Number and String - * arguments are currently supported. - * Returns OK or FAIL. - */ -int -call_vim_function ( - char_u *func, - int argc, - char_u **argv, - int safe, /* use the sandbox */ - int str_arg_only, /* all arguments are strings */ - typval_T *rettv -) -{ - typval_T *argvars; - long n; - int len; - int i; - int doesrange; - void *save_funccalp = NULL; - int ret; - - argvars = (typval_T *)alloc((unsigned)((argc + 1) * sizeof(typval_T))); - if (argvars == NULL) - return FAIL; - - for (i = 0; i < argc; i++) { - /* Pass a NULL or empty argument as an empty string */ - if (argv[i] == NULL || *argv[i] == NUL) { - argvars[i].v_type = VAR_STRING; - argvars[i].vval.v_string = (char_u *)""; - continue; - } - - if (str_arg_only) - len = 0; - else - /* Recognize a number argument, the others must be strings. */ - vim_str2nr(argv[i], NULL, &len, TRUE, TRUE, &n, NULL); - if (len != 0 && len == (int)STRLEN(argv[i])) { - argvars[i].v_type = VAR_NUMBER; - argvars[i].vval.v_number = n; - } else { - argvars[i].v_type = VAR_STRING; - argvars[i].vval.v_string = argv[i]; - } - } - - if (safe) { - save_funccalp = save_funccal(); - ++sandbox; - } - - rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, TRUE, NULL); - if (safe) { - --sandbox; - restore_funccal(save_funccalp); - } - free(argvars); - - if (ret == FAIL) - clear_tv(rettv); - - return ret; -} - -/* - * Call vimL function "func" and return the result as a number. - * Returns -1 when calling the function fails. - * Uses argv[argc] for the function arguments. - */ -long -call_func_retnr ( - char_u *func, - int argc, - char_u **argv, - int safe /* use the sandbox */ -) -{ - typval_T rettv; - long retval; - - /* All arguments are passed as strings, no conversion to number. */ - if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) - return -1; - - retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); - return retval; -} - -#if (defined(FEAT_USR_CMDS) && defined(FEAT_CMDL_COMPL)) \ - || defined(FEAT_COMPL_FUNC) || defined(PROTO) - -/* - * Call vimL function "func" and return the result as a string. - * Returns NULL when calling the function fails. - * Uses argv[argc] for the function arguments. - */ -void * -call_func_retstr ( - char_u *func, - int argc, - char_u **argv, - int safe /* use the sandbox */ -) -{ - typval_T rettv; - char_u *retval; - - /* All arguments are passed as strings, no conversion to number. */ - if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) - return NULL; - - retval = vim_strsave(get_tv_string(&rettv)); - clear_tv(&rettv); - return retval; -} - -/* - * Call vimL function "func" and return the result as a List. - * Uses argv[argc] for the function arguments. - * Returns NULL when there is something wrong. - */ -void * -call_func_retlist ( - char_u *func, - int argc, - char_u **argv, - int safe /* use the sandbox */ -) -{ - typval_T rettv; - - /* All arguments are passed as strings, no conversion to number. */ - if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) - return NULL; - - if (rettv.v_type != VAR_LIST) { - clear_tv(&rettv); - return NULL; - } - - return rettv.vval.v_list; -} -#endif - -/* - * Save the current function call pointer, and set it to NULL. - * Used when executing autocommands and for ":source". - */ -void *save_funccal(void) -{ - funccall_T *fc = current_funccal; - - current_funccal = NULL; - return (void *)fc; -} - -void restore_funccal(void *vfc) -{ - funccall_T *fc = (funccall_T *)vfc; - - current_funccal = fc; -} - -/* - * Prepare profiling for entering a child or something else that is not - * counted for the script/function itself. - * Should always be called in pair with prof_child_exit(). - */ -void prof_child_enter(tm) -proftime_T *tm; /* place to store waittime */ -{ - funccall_T *fc = current_funccal; - - if (fc != NULL && fc->func->uf_profiling) - profile_start(&fc->prof_child); - script_prof_save(tm); -} - -/* - * Take care of time spent in a child. - * Should always be called after prof_child_enter(). - */ -void prof_child_exit(tm) -proftime_T *tm; /* where waittime was stored */ -{ - funccall_T *fc = current_funccal; - - if (fc != NULL && fc->func->uf_profiling) { - profile_end(&fc->prof_child); - profile_sub_wait(tm, &fc->prof_child); /* don't count waiting time */ - profile_add(&fc->func->uf_tm_children, &fc->prof_child); - profile_add(&fc->func->uf_tml_children, &fc->prof_child); - } - script_prof_restore(tm); -} - - -/* - * Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding - * it in "*cp". Doesn't give error messages. - */ -int eval_foldexpr(char_u *arg, int *cp) -{ - typval_T tv; - int retval; - char_u *s; - int use_sandbox = was_set_insecurely((char_u *)"foldexpr", - OPT_LOCAL); - - ++emsg_off; - if (use_sandbox) - ++sandbox; - ++textlock; - *cp = NUL; - if (eval0(arg, &tv, NULL, TRUE) == FAIL) - retval = 0; - else { - /* If the result is a number, just return the number. */ - if (tv.v_type == VAR_NUMBER) - retval = tv.vval.v_number; - else if (tv.v_type != VAR_STRING || tv.vval.v_string == NULL) - retval = 0; - else { - /* If the result is a string, check if there is a non-digit before - * the number. */ - s = tv.vval.v_string; - if (!VIM_ISDIGIT(*s) && *s != '-') - *cp = *s++; - retval = atol((char *)s); - } - clear_tv(&tv); - } - --emsg_off; - if (use_sandbox) - --sandbox; - --textlock; - - return retval; -} - -/* - * ":let" list all variable values - * ":let var1 var2" list variable values - * ":let var = expr" assignment command. - * ":let var += expr" assignment command. - * ":let var -= expr" assignment command. - * ":let var .= expr" assignment command. - * ":let [var1, var2] = expr" unpack list. - */ -void ex_let(exarg_T *eap) -{ - char_u *arg = eap->arg; - char_u *expr = NULL; - typval_T rettv; - int i; - int var_count = 0; - int semicolon = 0; - char_u op[2]; - char_u *argend; - int first = TRUE; - - argend = skip_var_list(arg, &var_count, &semicolon); - if (argend == NULL) - return; - if (argend > arg && argend[-1] == '.') /* for var.='str' */ - --argend; - expr = skipwhite(argend); - if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL - && expr[1] == '=')) { - // ":let" without "=": list variables - if (*arg == '[') - EMSG(_(e_invarg)); - else if (!ends_excmd(*arg)) - /* ":let var1 var2" */ - arg = list_arg_vars(eap, arg, &first); - else if (!eap->skip) { - /* ":let" */ - list_glob_vars(&first); - list_buf_vars(&first); - list_win_vars(&first); - list_tab_vars(&first); - list_script_vars(&first); - list_func_vars(&first); - list_vim_vars(&first); - } - eap->nextcmd = check_nextcmd(arg); - } else { - op[0] = '='; - op[1] = NUL; - if (*expr != '=') { - if (vim_strchr((char_u *)"+-.", *expr) != NULL) { - op[0] = *expr; // +=, -=, .= - } - expr = skipwhite(expr + 2); - } else { - expr = skipwhite(expr + 1); - } - - if (eap->skip) - ++emsg_skip; - i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); - if (eap->skip) { - if (i != FAIL) - clear_tv(&rettv); - --emsg_skip; - } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, - op); - clear_tv(&rettv); - } - } -} - -/* - * Assign the typevalue "tv" to the variable or variables at "arg_start". - * Handles both "var" with any type and "[var, var; var]" with a list type. - * When "nextchars" is not NULL it points to a string with characters that - * must appear after the variable(s). Use "+", "-" or "." for add, subtract - * or concatenate. - * Returns OK or FAIL; - */ -static int -ex_let_vars ( - char_u *arg_start, - typval_T *tv, - int copy, /* copy values from "tv", don't move */ - int semicolon, /* from skip_var_list() */ - int var_count, /* from skip_var_list() */ - char_u *nextchars -) -{ - char_u *arg = arg_start; - list_T *l; - int i; - listitem_T *item; - typval_T ltv; - - if (*arg != '[') { - /* - * ":let var = expr" or ":for var in list" - */ - if (ex_let_one(arg, tv, copy, nextchars, nextchars) == NULL) - return FAIL; - return OK; - } - - /* - * ":let [v1, v2] = list" or ":for [v1, v2] in listlist" - */ - if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL) { - EMSG(_(e_listreq)); - return FAIL; - } - - i = list_len(l); - if (semicolon == 0 && var_count < i) { - EMSG(_("E687: Less targets than List items")); - return FAIL; - } - if (var_count - semicolon > i) { - EMSG(_("E688: More targets than List items")); - return FAIL; - } - - item = l->lv_first; - while (*arg != ']') { - arg = skipwhite(arg + 1); - arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars); - item = item->li_next; - if (arg == NULL) - return FAIL; - - arg = skipwhite(arg); - if (*arg == ';') { - /* Put the rest of the list (may be empty) in the var after ';'. - * Create a new list for this. */ - l = list_alloc(); - if (l == NULL) - return FAIL; - while (item != NULL) { - list_append_tv(l, &item->li_tv); - item = item->li_next; - } - - ltv.v_type = VAR_LIST; - ltv.v_lock = 0; - ltv.vval.v_list = l; - l->lv_refcount = 1; - - arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, - (char_u *)"]", nextchars); - clear_tv(<v); - if (arg == NULL) - return FAIL; - break; - } else if (*arg != ',' && *arg != ']') { - EMSG2(_(e_intern2), "ex_let_vars()"); - return FAIL; - } - } - - return OK; -} - -/* - * Skip over assignable variable "var" or list of variables "[var, var]". - * Used for ":let varvar = expr" and ":for varvar in expr". - * For "[var, var]" increment "*var_count" for each variable. - * for "[var, var; var]" set "semicolon". - * Return NULL for an error. - */ -static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) -{ - char_u *p, *s; - - if (*arg == '[') { - /* "[var, var]": find the matching ']'. */ - p = arg; - for (;; ) { - p = skipwhite(p + 1); /* skip whites after '[', ';' or ',' */ - s = skip_var_one(p); - if (s == p) { - EMSG2(_(e_invarg2), p); - return NULL; - } - ++*var_count; - - p = skipwhite(s); - if (*p == ']') - break; - else if (*p == ';') { - if (*semicolon == 1) { - EMSG(_("Double ; in list of variables")); - return NULL; - } - *semicolon = 1; - } else if (*p != ',') { - EMSG2(_(e_invarg2), p); - return NULL; - } - } - return p + 1; - } else - return skip_var_one(arg); -} - -/* - * Skip one (assignable) variable name, including @r, $VAR, &option, d.key, - * l[idx]. - */ -static char_u *skip_var_one(char_u *arg) -{ - if (*arg == '@' && arg[1] != NUL) - return arg + 2; - return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg, - NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); -} - -/* - * List variables for hashtab "ht" with prefix "prefix". - * If "empty" is TRUE also list NULL strings as empty strings. - */ -static void list_hashtable_vars(hashtab_T *ht, char_u *prefix, int empty, int *first) -{ - hashitem_T *hi; - dictitem_T *di; - int todo; - - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - di = HI2DI(hi); - if (empty || di->di_tv.v_type != VAR_STRING - || di->di_tv.vval.v_string != NULL) - list_one_var(di, prefix, first); - } - } -} - -/* - * List global variables. - */ -static void list_glob_vars(int *first) -{ - list_hashtable_vars(&globvarht, (char_u *)"", TRUE, first); -} - -/* - * List buffer variables. - */ -static void list_buf_vars(int *first) -{ - char_u numbuf[NUMBUFLEN]; - - list_hashtable_vars(&curbuf->b_vars->dv_hashtab, (char_u *)"b:", - TRUE, first); - - sprintf((char *)numbuf, "%" PRId64, (int64_t)curbuf->b_changedtick); - list_one_var_a((char_u *)"b:", (char_u *)"changedtick", VAR_NUMBER, - numbuf, first); -} - -/* - * List window variables. - */ -static void list_win_vars(int *first) -{ - list_hashtable_vars(&curwin->w_vars->dv_hashtab, - (char_u *)"w:", TRUE, first); -} - -/* - * List tab page variables. - */ -static void list_tab_vars(int *first) -{ - list_hashtable_vars(&curtab->tp_vars->dv_hashtab, - (char_u *)"t:", TRUE, first); -} - -/* - * List Vim variables. - */ -static void list_vim_vars(int *first) -{ - list_hashtable_vars(&vimvarht, (char_u *)"v:", FALSE, first); -} - -/* - * List script-local variables, if there is a script. - */ -static void list_script_vars(int *first) -{ - if (current_SID > 0 && current_SID <= ga_scripts.ga_len) - list_hashtable_vars(&SCRIPT_VARS(current_SID), - (char_u *)"s:", FALSE, first); -} - -/* - * List function variables, if there is a function. - */ -static void list_func_vars(int *first) -{ - if (current_funccal != NULL) - list_hashtable_vars(¤t_funccal->l_vars.dv_hashtab, - (char_u *)"l:", FALSE, first); -} - -/* - * List variables in "arg". - */ -static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first) -{ - int error = FALSE; - int len; - char_u *name; - char_u *name_start; - char_u *arg_subsc; - char_u *tofree; - typval_T tv; - - while (!ends_excmd(*arg) && !got_int) { - if (error || eap->skip) { - arg = find_name_end(arg, NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); - if (!vim_iswhite(*arg) && !ends_excmd(*arg)) { - emsg_severe = TRUE; - EMSG(_(e_trailing)); - break; - } - } else { - /* get_name_len() takes care of expanding curly braces */ - name_start = name = arg; - len = get_name_len(&arg, &tofree, TRUE, TRUE); - if (len <= 0) { - /* This is mainly to keep test 49 working: when expanding - * curly braces fails overrule the exception error message. */ - if (len < 0 && !aborting()) { - emsg_severe = TRUE; - EMSG2(_(e_invarg2), arg); - break; - } - error = TRUE; - } else { - if (tofree != NULL) - name = tofree; - if (get_var_tv(name, len, &tv, TRUE, FALSE) == FAIL) - error = TRUE; - else { - /* handle d.key, l[idx], f(expr) */ - arg_subsc = arg; - if (handle_subscript(&arg, &tv, TRUE, TRUE) == FAIL) - error = TRUE; - else { - if (arg == arg_subsc && len == 2 && name[1] == ':') { - switch (*name) { - case 'g': list_glob_vars(first); break; - case 'b': list_buf_vars(first); break; - case 'w': list_win_vars(first); break; - case 't': list_tab_vars(first); break; - case 'v': list_vim_vars(first); break; - case 's': list_script_vars(first); break; - case 'l': list_func_vars(first); break; - default: - EMSG2(_("E738: Can't list variables for %s"), name); - } - } else { - char_u numbuf[NUMBUFLEN]; - char_u *tf; - int c; - char_u *s; - - s = echo_string(&tv, &tf, numbuf, 0); - c = *arg; - *arg = NUL; - list_one_var_a((char_u *)"", - arg == arg_subsc ? name : name_start, - tv.v_type, - s == NULL ? (char_u *)"" : s, - first); - *arg = c; - free(tf); - } - clear_tv(&tv); - } - } - } - - free(tofree); - } - - arg = skipwhite(arg); - } - - return arg; -} - -/* - * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. - * Returns a pointer to the char just after the var name. - * Returns NULL if there is an error. - */ -static char_u * -ex_let_one ( - char_u *arg, /* points to variable name */ - typval_T *tv, /* value to assign to variable */ - int copy, /* copy value from "tv" */ - char_u *endchars, /* valid chars after variable name or NULL */ - char_u *op /* "+", "-", "." or NULL*/ -) -{ - int c1; - char_u *name; - char_u *p; - char_u *arg_end = NULL; - int len; - int opt_flags; - char_u *tofree = NULL; - - /* - * ":let $VAR = expr": Set environment variable. - */ - if (*arg == '$') { - /* Find the end of the name. */ - ++arg; - name = arg; - len = get_env_len(&arg); - if (len == 0) - EMSG2(_(e_invarg2), name - 1); - else { - if (op != NULL && (*op == '+' || *op == '-')) - EMSG2(_(e_letwrong), op); - else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg)) == NULL) - EMSG(_(e_letunexp)); - else if (!check_secure()) { - c1 = name[len]; - name[len] = NUL; - p = get_tv_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - int mustfree = FALSE; - char_u *s = vim_getenv(name, &mustfree); - - if (s != NULL) { - p = tofree = concat_str(s, p); - if (mustfree) - free(s); - } - } - if (p != NULL) { - vim_setenv(name, p); - if (STRICMP(name, "HOME") == 0) - init_homedir(); - else if (didset_vim && STRICMP(name, "VIM") == 0) - didset_vim = FALSE; - else if (didset_vimruntime - && STRICMP(name, "VIMRUNTIME") == 0) - didset_vimruntime = FALSE; - arg_end = arg; - } - name[len] = c1; - free(tofree); - } - } - } - /* - * ":let &option = expr": Set option value. - * ":let &l:option = expr": Set local option value. - * ":let &g:option = expr": Set global option value. - */ - else if (*arg == '&') { - /* Find the end of the name. */ - p = find_option_end(&arg, &opt_flags); - if (p == NULL || (endchars != NULL - && vim_strchr(endchars, *skipwhite(p)) == NULL)) - EMSG(_(e_letunexp)); - else { - long n; - int opt_type; - long numval; - char_u *stringval = NULL; - char_u *s; - - c1 = *p; - *p = NUL; - - n = get_tv_number(tv); - s = get_tv_string_chk(tv); /* != NULL if number or string */ - if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, - &stringval, opt_flags); - if ((opt_type == 1 && *op == '.') - || (opt_type == 0 && *op != '.')) - EMSG2(_(e_letwrong), op); - else { - if (opt_type == 1) { /* number */ - if (*op == '+') - n = numval + n; - else - n = numval - n; - } else if (opt_type == 0 && stringval != NULL) { /* string */ - s = concat_str(stringval, s); - free(stringval); - stringval = s; - } - } - } - if (s != NULL) { - set_option_value(arg, n, s, opt_flags); - arg_end = p; - } - *p = c1; - free(stringval); - } - } - /* - * ":let @r = expr": Set register contents. - */ - else if (*arg == '@') { - ++arg; - if (op != NULL && (*op == '+' || *op == '-')) - EMSG2(_(e_letwrong), op); - else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) - EMSG(_(e_letunexp)); - else { - char_u *ptofree = NULL; - char_u *s; - - p = get_tv_string_chk(tv); - if (p != NULL && op != NULL && *op == '.') { - s = get_reg_contents(*arg == '@' ? '"' : *arg, TRUE, TRUE); - if (s != NULL) { - p = ptofree = concat_str(s, p); - free(s); - } - } - if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE); - arg_end = arg + 1; - } - free(ptofree); - } - } - /* - * ":let var = expr": Set internal variable. - * ":let {expr} = expr": Idem, name made with curly braces - */ - else if (eval_isnamec1(*arg) || *arg == '{') { - lval_T lv; - - p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START); - if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) - EMSG(_(e_letunexp)); - else { - set_var_lval(&lv, p, tv, copy, op); - arg_end = p; - } - } - clear_lval(&lv); - } else - EMSG2(_(e_invarg2), arg); - - return arg_end; -} - -/* - * If "arg" is equal to "b:changedtick" give an error and return TRUE. - */ -static int check_changedtick(char_u *arg) -{ - if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13])) { - EMSG2(_(e_readonlyvar), arg); - return TRUE; - } - return FALSE; -} - -/* - * Get an lval: variable, Dict item or List item that can be assigned a value - * to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", - * "name.key", "name.key[expr]" etc. - * Indexing only works if "name" is an existing List or Dictionary. - * "name" points to the start of the name. - * If "rettv" is not NULL it points to the value to be assigned. - * "unlet" is TRUE for ":unlet": slightly different behavior when something is - * wrong; must end in space or cmd separator. - * - * flags: - * GLV_QUIET: do not give error messages - * GLV_NO_AUTOLOAD: do not use script autoloading - * - * Returns a pointer to just after the name, including indexes. - * When an evaluation error occurs "lp->ll_name" is NULL; - * Returns NULL for a parsing error. Still need to free items in "lp"! - */ -static char_u * -get_lval ( - char_u *name, - typval_T *rettv, - lval_T *lp, - int unlet, - int skip, - int flags, /* GLV_ values */ - int fne_flags /* flags for find_name_end() */ -) -{ - char_u *p; - char_u *expr_start, *expr_end; - int cc; - dictitem_T *v; - typval_T var1; - typval_T var2; - int empty1 = FALSE; - listitem_T *ni; - char_u *key = NULL; - int len; - hashtab_T *ht; - int quiet = flags & GLV_QUIET; - - /* Clear everything in "lp". */ - memset(lp, 0, sizeof(lval_T)); - - if (skip) { - /* When skipping just find the end of the name. */ - lp->ll_name = name; - return find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); - } - - /* Find the end of the name. */ - p = find_name_end(name, &expr_start, &expr_end, fne_flags); - if (expr_start != NULL) { - /* Don't expand the name when we already know there is an error. */ - if (unlet && !vim_iswhite(*p) && !ends_excmd(*p) - && *p != '[' && *p != '.') { - EMSG(_(e_trailing)); - return NULL; - } - - lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); - if (lp->ll_exp_name == NULL) { - /* Report an invalid expression in braces, unless the - * expression evaluation has been cancelled due to an - * aborting error, an interrupt, or an exception. */ - if (!aborting() && !quiet) { - emsg_severe = TRUE; - EMSG2(_(e_invarg2), name); - return NULL; - } - } - lp->ll_name = lp->ll_exp_name; - } else - lp->ll_name = name; - - /* Without [idx] or .key we are done. */ - if ((*p != '[' && *p != '.') || lp->ll_name == NULL) - return p; - - cc = *p; - *p = NUL; - v = find_var(lp->ll_name, &ht, flags & GLV_NO_AUTOLOAD); - if (v == NULL && !quiet) - EMSG2(_(e_undefvar), lp->ll_name); - *p = cc; - if (v == NULL) - return NULL; - - /* - * Loop until no more [idx] or .key is following. - */ - lp->ll_tv = &v->di_tv; - while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) { - if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) - && !(lp->ll_tv->v_type == VAR_DICT - && lp->ll_tv->vval.v_dict != NULL)) { - if (!quiet) - EMSG(_("E689: Can only index a List or Dictionary")); - return NULL; - } - if (lp->ll_range) { - if (!quiet) - EMSG(_("E708: [:] must come last")); - return NULL; - } - - len = -1; - if (*p == '.') { - key = p + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) - ; - if (len == 0) { - if (!quiet) - EMSG(_(e_emptykey)); - return NULL; - } - p = key + len; - } else { - /* Get the index [expr] or the first index [expr: ]. */ - p = skipwhite(p + 1); - if (*p == ':') - empty1 = TRUE; - else { - empty1 = FALSE; - if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ - return NULL; - if (get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); - return NULL; - } - } - - /* Optionally get the second index [ :expr]. */ - if (*p == ':') { - if (lp->ll_tv->v_type == VAR_DICT) { - if (!quiet) - EMSG(_(e_dictrange)); - if (!empty1) - clear_tv(&var1); - return NULL; - } - if (rettv != NULL && (rettv->v_type != VAR_LIST - || rettv->vval.v_list == NULL)) { - if (!quiet) - EMSG(_("E709: [:] requires a List value")); - if (!empty1) - clear_tv(&var1); - return NULL; - } - p = skipwhite(p + 1); - if (*p == ']') - lp->ll_empty2 = TRUE; - else { - lp->ll_empty2 = FALSE; - if (eval1(&p, &var2, TRUE) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); - return NULL; - } - if (get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); - return NULL; - } - } - lp->ll_range = TRUE; - } else - lp->ll_range = FALSE; - - if (*p != ']') { - if (!quiet) - EMSG(_(e_missbrac)); - if (!empty1) - clear_tv(&var1); - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); - return NULL; - } - - /* Skip to past ']'. */ - ++p; - } - - if (lp->ll_tv->v_type == VAR_DICT) { - if (len == -1) { - /* "[key]": get key from "var1" */ - key = get_tv_string(&var1); /* is number or string */ - if (*key == NUL) { - if (!quiet) - EMSG(_(e_emptykey)); - clear_tv(&var1); - return NULL; - } - } - lp->ll_list = NULL; - lp->ll_dict = lp->ll_tv->vval.v_dict; - lp->ll_di = dict_find(lp->ll_dict, key, len); - - /* When assigning to a scope dictionary check that a function and - * variable name is valid (only variable name unless it is l: or - * g: dictionary). Disallow overwriting a builtin function. */ - if (rettv != NULL && lp->ll_dict->dv_scope != 0) { - int prevval; - int wrong; - - if (len != -1) { - prevval = key[len]; - key[len] = NUL; - } else - prevval = 0; /* avoid compiler warning */ - wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC - && var_check_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key); - if (len != -1) - key[len] = prevval; - if (wrong) - return NULL; - } - - if (lp->ll_di == NULL) { - /* Can't add "v:" variable. */ - if (lp->ll_dict == &vimvardict) { - EMSG2(_(e_illvar), name); - return NULL; - } - - /* Key does not exist in dict: may need to add it. */ - if (*p == '[' || *p == '.' || unlet) { - if (!quiet) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); - return NULL; - } - if (len == -1) - lp->ll_newkey = vim_strsave(key); - else - lp->ll_newkey = vim_strnsave(key, len); - if (len == -1) - clear_tv(&var1); - if (lp->ll_newkey == NULL) - p = NULL; - break; - } - /* existing variable, need to check if it can be changed */ - else if (var_check_ro(lp->ll_di->di_flags, name)) - return NULL; - - if (len == -1) - clear_tv(&var1); - lp->ll_tv = &lp->ll_di->di_tv; - } else { - /* - * Get the number and item for the only or first index of the List. - */ - if (empty1) - lp->ll_n1 = 0; - else { - lp->ll_n1 = get_tv_number(&var1); /* is number or string */ - clear_tv(&var1); - } - lp->ll_dict = NULL; - lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); - if (lp->ll_li == NULL) { - if (lp->ll_n1 < 0) { - lp->ll_n1 = 0; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); - } - } - if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); - if (!quiet) - EMSGN(_(e_listidx), lp->ll_n1); - return NULL; - } - - /* - * May need to find the item or absolute index for the second - * index of a range. - * When no index given: "lp->ll_empty2" is TRUE. - * Otherwise "lp->ll_n2" is set to the second index. - */ - if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); /* is number or string */ - clear_tv(&var2); - if (lp->ll_n2 < 0) { - ni = list_find(lp->ll_list, lp->ll_n2); - if (ni == NULL) { - if (!quiet) - EMSGN(_(e_listidx), lp->ll_n2); - return NULL; - } - lp->ll_n2 = list_idx_of_item(lp->ll_list, ni); - } - - /* Check that lp->ll_n2 isn't before lp->ll_n1. */ - if (lp->ll_n1 < 0) - lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li); - if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) - EMSGN(_(e_listidx), lp->ll_n2); - return NULL; - } - } - - lp->ll_tv = &lp->ll_li->li_tv; - } - } - - return p; -} - -/* - * Clear lval "lp" that was filled by get_lval(). - */ -static void clear_lval(lval_T *lp) -{ - free(lp->ll_exp_name); - free(lp->ll_newkey); -} - -/* - * Set a variable that was parsed by get_lval() to "rettv". - * "endp" points to just after the parsed name. - * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=". - */ -static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op) -{ - int cc; - listitem_T *ri; - dictitem_T *di; - - if (lp->ll_tv == NULL) { - if (!check_changedtick(lp->ll_name)) { - cc = *endp; - *endp = NUL; - if (op != NULL && *op != '=') { - typval_T tv; - - /* handle +=, -= and .= */ - if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), - &tv, TRUE, FALSE) == OK) { - if (tv_op(&tv, rettv, op) == OK) - set_var(lp->ll_name, &tv, FALSE); - clear_tv(&tv); - } - } else - set_var(lp->ll_name, rettv, copy); - *endp = cc; - } - } else if (tv_check_lock(lp->ll_newkey == NULL - ? lp->ll_tv->v_lock - : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name)) - ; - else if (lp->ll_range) { - /* - * Assign the List values to the list items. - */ - for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { - if (op != NULL && *op != '=') - tv_op(&lp->ll_li->li_tv, &ri->li_tv, op); - else { - clear_tv(&lp->ll_li->li_tv); - copy_tv(&ri->li_tv, &lp->ll_li->li_tv); - } - ri = ri->li_next; - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) - break; - if (lp->ll_li->li_next == NULL) { - /* Need to add an empty item. */ - list_append_number(lp->ll_list, 0); - } - lp->ll_li = lp->ll_li->li_next; - ++lp->ll_n1; - } - if (ri != NULL) - EMSG(_("E710: List value has more items than target")); - else if (lp->ll_empty2 - ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL) - : lp->ll_n1 != lp->ll_n2) - EMSG(_("E711: List value has not enough items")); - } else { - /* - * Assign to a List or Dictionary item. - */ - if (lp->ll_newkey != NULL) { - if (op != NULL && *op != '=') { - EMSG2(_(e_letwrong), op); - return; - } - - /* Need to add an item to the Dictionary. */ - di = dictitem_alloc(lp->ll_newkey); - if (di == NULL) - return; - if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { - free(di); - return; - } - lp->ll_tv = &di->di_tv; - } else if (op != NULL && *op != '=') { - tv_op(lp->ll_tv, rettv, op); - return; - } else - clear_tv(lp->ll_tv); - - /* - * Assign the value to the variable or list item. - */ - if (copy) - copy_tv(rettv, lp->ll_tv); - else { - *lp->ll_tv = *rettv; - lp->ll_tv->v_lock = 0; - init_tv(rettv); - } - } -} - -/* - * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2" - * Returns OK or FAIL. - */ -static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) -{ - long n; - char_u numbuf[NUMBUFLEN]; - char_u *s; - - /* Can't do anything with a Funcref or a Dict on the right. */ - if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { - switch (tv1->v_type) { - case VAR_DICT: - case VAR_FUNC: - break; - - case VAR_LIST: - if (*op != '+' || tv2->v_type != VAR_LIST) - break; - /* List += List */ - if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) - list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); - return OK; - - case VAR_NUMBER: - case VAR_STRING: - if (tv2->v_type == VAR_LIST) - break; - if (*op == '+' || *op == '-') { - /* nr += nr or nr -= nr*/ - n = get_tv_number(tv1); - if (tv2->v_type == VAR_FLOAT) { - float_T f = n; - - if (*op == '+') - f += tv2->vval.v_float; - else - f -= tv2->vval.v_float; - clear_tv(tv1); - tv1->v_type = VAR_FLOAT; - tv1->vval.v_float = f; - } else { - if (*op == '+') - n += get_tv_number(tv2); - else - n -= get_tv_number(tv2); - clear_tv(tv1); - tv1->v_type = VAR_NUMBER; - tv1->vval.v_number = n; - } - } else { - if (tv2->v_type == VAR_FLOAT) - break; - - /* str .= str */ - s = get_tv_string(tv1); - s = concat_str(s, get_tv_string_buf(tv2, numbuf)); - clear_tv(tv1); - tv1->v_type = VAR_STRING; - tv1->vval.v_string = s; - } - return OK; - - case VAR_FLOAT: - { - float_T f; - - if (*op == '.' || (tv2->v_type != VAR_FLOAT - && tv2->v_type != VAR_NUMBER - && tv2->v_type != VAR_STRING)) - break; - if (tv2->v_type == VAR_FLOAT) - f = tv2->vval.v_float; - else - f = get_tv_number(tv2); - if (*op == '+') - tv1->vval.v_float += f; - else - tv1->vval.v_float -= f; - } - return OK; - } - } - - EMSG2(_(e_letwrong), op); - return FAIL; -} - -/* - * Add a watcher to a list. - */ -void list_add_watch(list_T *l, listwatch_T *lw) -{ - lw->lw_next = l->lv_watch; - l->lv_watch = lw; -} - -/* - * Remove a watcher from a list. - * No warning when it isn't found... - */ -void list_rem_watch(list_T *l, listwatch_T *lwrem) -{ - listwatch_T *lw, **lwp; - - lwp = &l->lv_watch; - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - if (lw == lwrem) { - *lwp = lw->lw_next; - break; - } - lwp = &lw->lw_next; - } -} - -/* - * Just before removing an item from a list: advance watchers to the next - * item. - */ -static void list_fix_watch(list_T *l, listitem_T *item) -{ - listwatch_T *lw; - - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) - if (lw->lw_item == item) - lw->lw_item = item->li_next; -} - -/* - * Evaluate the expression used in a ":for var in expr" command. - * "arg" points to "var". - * Set "*errp" to TRUE for an error, FALSE otherwise; - * Return a pointer that holds the info. Null when there is an error. - */ -void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) -{ - forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); - char_u *expr; - typval_T tv; - list_T *l; - - *errp = TRUE; /* default: there is an error */ - - expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); - if (expr == NULL) - return fi; - - expr = skipwhite(expr); - if (expr[0] != 'i' || expr[1] != 'n' || !vim_iswhite(expr[2])) { - EMSG(_("E690: Missing \"in\" after :for")); - return fi; - } - - if (skip) - ++emsg_skip; - if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { - *errp = FALSE; - if (!skip) { - l = tv.vval.v_list; - if (tv.v_type != VAR_LIST || l == NULL) { - EMSG(_(e_listreq)); - clear_tv(&tv); - } else { - /* No need to increment the refcount, it's already set for the - * list being used in "tv". */ - fi->fi_list = l; - list_add_watch(l, &fi->fi_lw); - fi->fi_lw.lw_item = l->lv_first; - } - } - } - if (skip) - --emsg_skip; - - return fi; -} - -/* - * Use the first item in a ":for" list. Advance to the next. - * Assign the values to the variable (list). "arg" points to the first one. - * Return TRUE when a valid item was found, FALSE when at end of list or - * something wrong. - */ -int next_for_item(void *fi_void, char_u *arg) -{ - forinfo_T *fi = (forinfo_T *)fi_void; - int result; - listitem_T *item; - - item = fi->fi_lw.lw_item; - if (item == NULL) - result = FALSE; - else { - fi->fi_lw.lw_item = item->li_next; - result = (ex_let_vars(arg, &item->li_tv, TRUE, - fi->fi_semicolon, fi->fi_varcount, NULL) == OK); - } - return result; -} - -/* - * Free the structure used to store info used by ":for". - */ -void free_for_info(void *fi_void) -{ - forinfo_T *fi = (forinfo_T *)fi_void; - - if (fi != NULL && fi->fi_list != NULL) { - list_rem_watch(fi->fi_list, &fi->fi_lw); - list_unref(fi->fi_list); - } - free(fi); -} - - -void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) -{ - int got_eq = FALSE; - int c; - char_u *p; - - if (cmdidx == CMD_let) { - xp->xp_context = EXPAND_USER_VARS; - if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL) { - /* ":let var1 var2 ...": find last space. */ - for (p = arg + STRLEN(arg); p >= arg; ) { - xp->xp_pattern = p; - mb_ptr_back(arg, p); - if (vim_iswhite(*p)) - break; - } - return; - } - } else - xp->xp_context = cmdidx == CMD_call ? EXPAND_FUNCTIONS - : EXPAND_EXPRESSION; - while ((xp->xp_pattern = vim_strpbrk(arg, - (char_u *)"\"'+-*/%.=!?~|&$([<>,#")) != NULL) { - c = *xp->xp_pattern; - if (c == '&') { - c = xp->xp_pattern[1]; - if (c == '&') { - ++xp->xp_pattern; - xp->xp_context = cmdidx != CMD_let || got_eq - ? EXPAND_EXPRESSION : EXPAND_NOTHING; - } else if (c != ' ') { - xp->xp_context = EXPAND_SETTINGS; - if ((c == 'l' || c == 'g') && xp->xp_pattern[2] == ':') - xp->xp_pattern += 2; - - } - } else if (c == '$') { - /* environment variable */ - xp->xp_context = EXPAND_ENV_VARS; - } else if (c == '=') { - got_eq = TRUE; - xp->xp_context = EXPAND_EXPRESSION; - } else if (c == '<' - && xp->xp_context == EXPAND_FUNCTIONS - && vim_strchr(xp->xp_pattern, '(') == NULL) { - /* Function name can start with "" */ - break; - } else if (cmdidx != CMD_let || got_eq) { - if (c == '"') { /* string */ - while ((c = *++xp->xp_pattern) != NUL && c != '"') - if (c == '\\' && xp->xp_pattern[1] != NUL) - ++xp->xp_pattern; - xp->xp_context = EXPAND_NOTHING; - } else if (c == '\'') { /* literal string */ - /* Trick: '' is like stopping and starting a literal string. */ - while ((c = *++xp->xp_pattern) != NUL && c != '\'') - /* skip */; - xp->xp_context = EXPAND_NOTHING; - } else if (c == '|') { - if (xp->xp_pattern[1] == '|') { - ++xp->xp_pattern; - xp->xp_context = EXPAND_EXPRESSION; - } else - xp->xp_context = EXPAND_COMMANDS; - } else - xp->xp_context = EXPAND_EXPRESSION; - } else - /* Doesn't look like something valid, expand as an expression - * anyway. */ - xp->xp_context = EXPAND_EXPRESSION; - arg = xp->xp_pattern; - if (*arg != NUL) - while ((c = *++arg) != NUL && (c == ' ' || c == '\t')) - /* skip */; - } - xp->xp_pattern = arg; -} - - -/* - * ":1,25call func(arg1, arg2)" function call. - */ -void ex_call(exarg_T *eap) -{ - char_u *arg = eap->arg; - char_u *startarg; - char_u *name; - char_u *tofree; - int len; - typval_T rettv; - linenr_T lnum; - int doesrange; - int failed = FALSE; - funcdict_T fudi; - - if (eap->skip) { - /* trans_function_name() doesn't work well when skipping, use eval0() - * instead to skip to any following command, e.g. for: - * :if 0 | call dict.foo().bar() | endif */ - ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL) - clear_tv(&rettv); - --emsg_skip; - return; - } - - tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi); - if (fudi.fd_newkey != NULL) { - /* Still need to give an error message for missing key. */ - EMSG2(_(e_dictkey), fudi.fd_newkey); - free(fudi.fd_newkey); - } - if (tofree == NULL) - return; - - /* Increase refcount on dictionary, it could get deleted when evaluating - * the arguments. */ - if (fudi.fd_dict != NULL) - ++fudi.fd_dict->dv_refcount; - - /* If it is the name of a variable of type VAR_FUNC use its contents. */ - len = (int)STRLEN(tofree); - name = deref_func_name(tofree, &len, FALSE); - - /* Skip white space to allow ":call func ()". Not good, but required for - * backward compatibility. */ - startarg = skipwhite(arg); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - - if (*startarg != '(') { - EMSG2(_("E107: Missing parentheses: %s"), eap->arg); - goto end; - } - - /* - * When skipping, evaluate the function once, to find the end of the - * arguments. - * When the function takes a range, this is discovered after the first - * call, and the loop is broken. - */ - if (eap->skip) { - ++emsg_skip; - lnum = eap->line2; /* do it once, also with an invalid range */ - } else - lnum = eap->line1; - for (; lnum <= eap->line2; ++lnum) { - if (!eap->skip && eap->addr_count > 0) { - curwin->w_cursor.lnum = lnum; - curwin->w_cursor.col = 0; - curwin->w_cursor.coladd = 0; - } - arg = startarg; - if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg, - eap->line1, eap->line2, &doesrange, - !eap->skip, fudi.fd_dict) == FAIL) { - failed = TRUE; - break; - } - - /* Handle a function returning a Funcref, Dictionary or List. */ - if (handle_subscript(&arg, &rettv, !eap->skip, TRUE) == FAIL) { - failed = TRUE; - break; - } - - clear_tv(&rettv); - if (doesrange || eap->skip) - break; - - /* Stop when immediately aborting on error, or when an interrupt - * occurred or an exception was thrown but not caught. - * get_func_tv() returned OK, so that the check for trailing - * characters below is executed. */ - if (aborting()) - break; - } - if (eap->skip) - --emsg_skip; - - if (!failed) { - /* Check for trailing illegal characters and a following command. */ - if (!ends_excmd(*arg)) { - emsg_severe = TRUE; - EMSG(_(e_trailing)); - } else - eap->nextcmd = check_nextcmd(arg); - } - -end: - dict_unref(fudi.fd_dict); - free(tofree); -} - -/* - * ":unlet[!] var1 ... " command. - */ -void ex_unlet(exarg_T *eap) -{ - ex_unletlock(eap, eap->arg, 0); -} - -/* - * ":lockvar" and ":unlockvar" commands - */ -void ex_lockvar(exarg_T *eap) -{ - char_u *arg = eap->arg; - int deep = 2; - - if (eap->forceit) - deep = -1; - else if (vim_isdigit(*arg)) { - deep = getdigits(&arg); - arg = skipwhite(arg); - } - - ex_unletlock(eap, arg, deep); -} - -/* - * ":unlet", ":lockvar" and ":unlockvar" are quite similar. - */ -static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) -{ - char_u *arg = argstart; - char_u *name_end; - int error = FALSE; - lval_T lv; - - do { - /* Parse the name and find the end. */ - name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0, - FNE_CHECK_START); - if (lv.ll_name == NULL) - error = TRUE; /* error but continue parsing */ - if (name_end == NULL || (!vim_iswhite(*name_end) - && !ends_excmd(*name_end))) { - if (name_end != NULL) { - emsg_severe = TRUE; - EMSG(_(e_trailing)); - } - if (!(eap->skip || error)) - clear_lval(&lv); - break; - } - - if (!error && !eap->skip) { - if (eap->cmdidx == CMD_unlet) { - if (do_unlet_var(&lv, name_end, eap->forceit) == FAIL) - error = TRUE; - } else { - if (do_lock_var(&lv, name_end, deep, - eap->cmdidx == CMD_lockvar) == FAIL) - error = TRUE; - } - } - - if (!eap->skip) - clear_lval(&lv); - - arg = skipwhite(name_end); - } while (!ends_excmd(*arg)); - - eap->nextcmd = check_nextcmd(arg); -} - -static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) -{ - int ret = OK; - int cc; - - if (lp->ll_tv == NULL) { - cc = *name_end; - *name_end = NUL; - - /* Normal name or expanded name. */ - if (check_changedtick(lp->ll_name)) - ret = FAIL; - else if (do_unlet(lp->ll_name, forceit) == FAIL) - ret = FAIL; - *name_end = cc; - } else if (tv_check_lock(lp->ll_tv->v_lock, lp->ll_name)) - return FAIL; - else if (lp->ll_range) { - listitem_T *li; - - /* Delete a range of List items. */ - while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - li = lp->ll_li->li_next; - listitem_remove(lp->ll_list, lp->ll_li); - lp->ll_li = li; - ++lp->ll_n1; - } - } else { - if (lp->ll_list != NULL) - /* unlet a List item. */ - listitem_remove(lp->ll_list, lp->ll_li); - else - /* unlet a Dictionary item. */ - dictitem_remove(lp->ll_dict, lp->ll_di); - } - - return ret; -} - -/* - * "unlet" a variable. Return OK if it existed, FAIL if not. - * When "forceit" is TRUE don't complain if the variable doesn't exist. - */ -int do_unlet(char_u *name, int forceit) -{ - hashtab_T *ht; - hashitem_T *hi; - char_u *varname; - dictitem_T *di; - - ht = find_var_ht(name, &varname); - if (ht != NULL && *varname != NUL) { - hi = hash_find(ht, varname); - if (!HASHITEM_EMPTY(hi)) { - di = HI2DI(hi); - if (var_check_fixed(di->di_flags, name) - || var_check_ro(di->di_flags, name)) - return FAIL; - delete_var(ht, hi); - return OK; - } - } - if (forceit) - return OK; - EMSG2(_("E108: No such variable: \"%s\""), name); - return FAIL; -} - -/* - * Lock or unlock variable indicated by "lp". - * "deep" is the levels to go (-1 for unlimited); - * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". - */ -static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) -{ - int ret = OK; - int cc; - dictitem_T *di; - - if (deep == 0) /* nothing to do */ - return OK; - - if (lp->ll_tv == NULL) { - cc = *name_end; - *name_end = NUL; - - /* Normal name or expanded name. */ - if (check_changedtick(lp->ll_name)) - ret = FAIL; - else { - di = find_var(lp->ll_name, NULL, TRUE); - if (di == NULL) - ret = FAIL; - else { - if (lock) - di->di_flags |= DI_FLAGS_LOCK; - else - di->di_flags &= ~DI_FLAGS_LOCK; - item_lock(&di->di_tv, deep, lock); - } - } - *name_end = cc; - } else if (lp->ll_range) { - listitem_T *li = lp->ll_li; - - /* (un)lock a range of List items. */ - while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - item_lock(&li->li_tv, deep, lock); - li = li->li_next; - ++lp->ll_n1; - } - } else if (lp->ll_list != NULL) - /* (un)lock a List item. */ - item_lock(&lp->ll_li->li_tv, deep, lock); - else - /* un(lock) a Dictionary item. */ - item_lock(&lp->ll_di->di_tv, deep, lock); - - return ret; -} - -/* - * Lock or unlock an item. "deep" is nr of levels to go. - */ -static void item_lock(typval_T *tv, int deep, int lock) -{ - static int recurse = 0; - list_T *l; - listitem_T *li; - dict_T *d; - hashitem_T *hi; - int todo; - - if (recurse >= DICT_MAXNEST) { - EMSG(_("E743: variable nested too deep for (un)lock")); - return; - } - if (deep == 0) - return; - ++recurse; - - /* lock/unlock the item itself */ - if (lock) - tv->v_lock |= VAR_LOCKED; - else - tv->v_lock &= ~VAR_LOCKED; - - switch (tv->v_type) { - case VAR_LIST: - if ((l = tv->vval.v_list) != NULL) { - if (lock) - l->lv_lock |= VAR_LOCKED; - else - l->lv_lock &= ~VAR_LOCKED; - if (deep < 0 || deep > 1) - /* recursive: lock/unlock the items the List contains */ - for (li = l->lv_first; li != NULL; li = li->li_next) - item_lock(&li->li_tv, deep - 1, lock); - } - break; - case VAR_DICT: - if ((d = tv->vval.v_dict) != NULL) { - if (lock) - d->dv_lock |= VAR_LOCKED; - else - d->dv_lock &= ~VAR_LOCKED; - if (deep < 0 || deep > 1) { - /* recursive: lock/unlock the items the List contains */ - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } - } - } - } - --recurse; -} - -/* - * Return TRUE if typeval "tv" is locked: Either that value is locked itself - * or it refers to a List or Dictionary that is locked. - */ -static int tv_islocked(typval_T *tv) -{ - return (tv->v_lock & VAR_LOCKED) - || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) - || (tv->v_type == VAR_DICT - && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED)); -} - -/* - * Delete all "menutrans_" variables. - */ -void del_menutrans_vars(void) -{ - hashitem_T *hi; - int todo; - - hash_lock(&globvarht); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) - delete_var(&globvarht, hi); - } - } - hash_unlock(&globvarht); -} - -/* - * Local string buffer for the next two functions to store a variable name - * with its prefix. Allocated in cat_prefix_varname(), freed later in - * get_user_var_name(). - */ - -static char_u *cat_prefix_varname(int prefix, char_u *name); - -static char_u *varnamebuf = NULL; -static int varnamebuflen = 0; - -/* - * Function to concatenate a prefix and a variable name. - */ -static char_u *cat_prefix_varname(int prefix, char_u *name) -{ - int len; - - len = (int)STRLEN(name) + 3; - if (len > varnamebuflen) { - free(varnamebuf); - len += 10; /* some additional space */ - varnamebuf = alloc(len); - if (varnamebuf == NULL) { - varnamebuflen = 0; - return NULL; - } - varnamebuflen = len; - } - *varnamebuf = prefix; - varnamebuf[1] = ':'; - STRCPY(varnamebuf + 2, name); - return varnamebuf; -} - -/* - * Function given to ExpandGeneric() to obtain the list of user defined - * (global/buffer/window/built-in) variable names. - */ -char_u *get_user_var_name(expand_T *xp, int idx) -{ - static long_u gdone; - static long_u bdone; - static long_u wdone; - static long_u tdone; - static int vidx; - static hashitem_T *hi; - hashtab_T *ht; - - if (idx == 0) { - gdone = bdone = wdone = vidx = 0; - tdone = 0; - } - - /* Global variables */ - if (gdone < globvarht.ht_used) { - if (gdone++ == 0) - hi = globvarht.ht_array; - else - ++hi; - while (HASHITEM_EMPTY(hi)) - ++hi; - if (STRNCMP("g:", xp->xp_pattern, 2) == 0) - return cat_prefix_varname('g', hi->hi_key); - return hi->hi_key; - } - - /* b: variables */ - ht = &curbuf->b_vars->dv_hashtab; - if (bdone < ht->ht_used) { - if (bdone++ == 0) - hi = ht->ht_array; - else - ++hi; - while (HASHITEM_EMPTY(hi)) - ++hi; - return cat_prefix_varname('b', hi->hi_key); - } - if (bdone == ht->ht_used) { - ++bdone; - return (char_u *)"b:changedtick"; - } - - /* w: variables */ - ht = &curwin->w_vars->dv_hashtab; - if (wdone < ht->ht_used) { - if (wdone++ == 0) - hi = ht->ht_array; - else - ++hi; - while (HASHITEM_EMPTY(hi)) - ++hi; - return cat_prefix_varname('w', hi->hi_key); - } - - /* t: variables */ - ht = &curtab->tp_vars->dv_hashtab; - if (tdone < ht->ht_used) { - if (tdone++ == 0) - hi = ht->ht_array; - else - ++hi; - while (HASHITEM_EMPTY(hi)) - ++hi; - return cat_prefix_varname('t', hi->hi_key); - } - - /* v: variables */ - if (vidx < VV_LEN) - return cat_prefix_varname('v', (char_u *)vimvars[vidx++].vv_name); - - free(varnamebuf); - varnamebuf = NULL; - varnamebuflen = 0; - return NULL; -} - - -/* - * types for expressions. - */ -typedef enum { - TYPE_UNKNOWN = 0 - , TYPE_EQUAL /* == */ - , TYPE_NEQUAL /* != */ - , TYPE_GREATER /* > */ - , TYPE_GEQUAL /* >= */ - , TYPE_SMALLER /* < */ - , TYPE_SEQUAL /* <= */ - , TYPE_MATCH /* =~ */ - , TYPE_NOMATCH /* !~ */ -} exptype_T; - -/* - * The "evaluate" argument: When FALSE, the argument is only parsed but not - * executed. The function may return OK, but the rettv will be of type - * VAR_UNKNOWN. The function still returns FAIL for a syntax error. - */ - -/* - * Handle zero level expression. - * This calls eval1() and handles error message and nextcmd. - * Put the result in "rettv" when returning OK and "evaluate" is TRUE. - * Note: "rettv.v_lock" is not set. - * Return OK or FAIL. - */ -static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) -{ - int ret; - char_u *p; - - p = skipwhite(arg); - ret = eval1(&p, rettv, evaluate); - if (ret == FAIL || !ends_excmd(*p)) { - if (ret != FAIL) - clear_tv(rettv); - /* - * Report the invalid expression unless the expression evaluation has - * been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting()) - EMSG2(_(e_invexpr2), arg); - ret = FAIL; - } - if (nextcmd != NULL) - *nextcmd = check_nextcmd(p); - - return ret; -} - -/* - * Handle top level expression: - * expr2 ? expr1 : expr1 - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Note: "rettv.v_lock" is not set. - * - * Return OK or FAIL. - */ -static int eval1(char_u **arg, typval_T *rettv, int evaluate) -{ - int result; - typval_T var2; - - /* - * Get the first variable. - */ - if (eval2(arg, rettv, evaluate) == FAIL) - return FAIL; - - if ((*arg)[0] == '?') { - result = FALSE; - if (evaluate) { - int error = FALSE; - - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) - return FAIL; - } - - /* - * Get the second variable. - */ - *arg = skipwhite(*arg + 1); - if (eval1(arg, rettv, evaluate && result) == FAIL) /* recursive! */ - return FAIL; - - /* - * Check for the ":". - */ - if ((*arg)[0] != ':') { - EMSG(_("E109: Missing ':' after '?'")); - if (evaluate && result) - clear_tv(rettv); - return FAIL; - } - - /* - * Get the third variable. - */ - *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, evaluate && !result) == FAIL) { /* recursive! */ - if (evaluate && result) - clear_tv(rettv); - return FAIL; - } - if (evaluate && !result) - *rettv = var2; - } - - return OK; -} - -/* - * Handle first level expression: - * expr2 || expr2 || expr2 logical OR - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ -static int eval2(char_u **arg, typval_T *rettv, int evaluate) -{ - typval_T var2; - long result; - int first; - int error = FALSE; - - /* - * Get the first variable. - */ - if (eval3(arg, rettv, evaluate) == FAIL) - return FAIL; - - /* - * Repeat until there is no following "||". - */ - first = TRUE; - result = FALSE; - while ((*arg)[0] == '|' && (*arg)[1] == '|') { - if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) - return FAIL; - first = FALSE; - } - - /* - * Get the second variable. - */ - *arg = skipwhite(*arg + 2); - if (eval3(arg, &var2, evaluate && !result) == FAIL) - return FAIL; - - /* - * Compute the result. - */ - if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) - result = TRUE; - clear_tv(&var2); - if (error) - return FAIL; - } - if (evaluate) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = result; - } - } - - return OK; -} - -/* - * Handle second level expression: - * expr3 && expr3 && expr3 logical AND - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ -static int eval3(char_u **arg, typval_T *rettv, int evaluate) -{ - typval_T var2; - long result; - int first; - int error = FALSE; - - /* - * Get the first variable. - */ - if (eval4(arg, rettv, evaluate) == FAIL) - return FAIL; - - /* - * Repeat until there is no following "&&". - */ - first = TRUE; - result = TRUE; - while ((*arg)[0] == '&' && (*arg)[1] == '&') { - if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) - result = FALSE; - clear_tv(rettv); - if (error) - return FAIL; - first = FALSE; - } - - /* - * Get the second variable. - */ - *arg = skipwhite(*arg + 2); - if (eval4(arg, &var2, evaluate && result) == FAIL) - return FAIL; - - /* - * Compute the result. - */ - if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) - result = FALSE; - clear_tv(&var2); - if (error) - return FAIL; - } - if (evaluate) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = result; - } - } - - return OK; -} - -/* - * Handle third level expression: - * var1 == var2 - * var1 =~ var2 - * var1 != var2 - * var1 !~ var2 - * var1 > var2 - * var1 >= var2 - * var1 < var2 - * var1 <= var2 - * var1 is var2 - * var1 isnot var2 - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ -static int eval4(char_u **arg, typval_T *rettv, int evaluate) -{ - typval_T var2; - char_u *p; - int i; - exptype_T type = TYPE_UNKNOWN; - int type_is = FALSE; /* TRUE for "is" and "isnot" */ - int len = 2; - long n1, n2; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - regmatch_T regmatch; - int ic; - char_u *save_cpo; - - /* - * Get the first variable. - */ - if (eval5(arg, rettv, evaluate) == FAIL) - return FAIL; - - p = *arg; - switch (p[0]) { - case '=': if (p[1] == '=') - type = TYPE_EQUAL; - else if (p[1] == '~') - type = TYPE_MATCH; - break; - case '!': if (p[1] == '=') - type = TYPE_NEQUAL; - else if (p[1] == '~') - type = TYPE_NOMATCH; - break; - case '>': if (p[1] != '=') { - type = TYPE_GREATER; - len = 1; - } else - type = TYPE_GEQUAL; - break; - case '<': if (p[1] != '=') { - type = TYPE_SMALLER; - len = 1; - } else - type = TYPE_SEQUAL; - break; - case 'i': if (p[1] == 's') { - if (p[2] == 'n' && p[3] == 'o' && p[4] == 't') - len = 5; - if (!vim_isIDc(p[len])) { - type = len == 2 ? TYPE_EQUAL : TYPE_NEQUAL; - type_is = TRUE; - } - } - break; - } - - /* - * If there is a comparative operator, use it. - */ - if (type != TYPE_UNKNOWN) { - /* extra question mark appended: ignore case */ - if (p[len] == '?') { - ic = TRUE; - ++len; - } - /* extra '#' appended: match case */ - else if (p[len] == '#') { - ic = FALSE; - ++len; - } - /* nothing appended: use 'ignorecase' */ - else - ic = p_ic; - - /* - * Get the second variable. - */ - *arg = skipwhite(p + len); - if (eval5(arg, &var2, evaluate) == FAIL) { - clear_tv(rettv); - return FAIL; - } - - if (evaluate) { - if (type_is && rettv->v_type != var2.v_type) { - /* For "is" a different type always means FALSE, for "notis" - * it means TRUE. */ - n1 = (type == TYPE_NEQUAL); - } else if (rettv->v_type == VAR_LIST || var2.v_type == VAR_LIST) { - if (type_is) { - n1 = (rettv->v_type == var2.v_type - && rettv->vval.v_list == var2.vval.v_list); - if (type == TYPE_NEQUAL) - n1 = !n1; - } else if (rettv->v_type != var2.v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) { - if (rettv->v_type != var2.v_type) - EMSG(_("E691: Can only compare List with List")); - else - EMSG(_("E692: Invalid operation for Lists")); - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } else { - /* Compare two Lists for being equal or unequal. */ - n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, - ic, FALSE); - if (type == TYPE_NEQUAL) - n1 = !n1; - } - } else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) { - if (type_is) { - n1 = (rettv->v_type == var2.v_type - && rettv->vval.v_dict == var2.vval.v_dict); - if (type == TYPE_NEQUAL) - n1 = !n1; - } else if (rettv->v_type != var2.v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) { - if (rettv->v_type != var2.v_type) - EMSG(_("E735: Can only compare Dictionary with Dictionary")); - else - EMSG(_("E736: Invalid operation for Dictionary")); - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } else { - /* Compare two Dictionaries for being equal or unequal. */ - n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, - ic, FALSE); - if (type == TYPE_NEQUAL) - n1 = !n1; - } - } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC) { - if (rettv->v_type != var2.v_type - || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) { - if (rettv->v_type != var2.v_type) - EMSG(_("E693: Can only compare Funcref with Funcref")); - else - EMSG(_("E694: Invalid operation for Funcrefs")); - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } else { - /* Compare two Funcrefs for being equal or unequal. */ - if (rettv->vval.v_string == NULL - || var2.vval.v_string == NULL) - n1 = FALSE; - else - n1 = STRCMP(rettv->vval.v_string, - var2.vval.v_string) == 0; - if (type == TYPE_NEQUAL) - n1 = !n1; - } - } - /* - * If one of the two variables is a float, compare as a float. - * When using "=~" or "!~", always compare as string. - */ - else if ((rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) - && type != TYPE_MATCH && type != TYPE_NOMATCH) { - float_T f1, f2; - - if (rettv->v_type == VAR_FLOAT) - f1 = rettv->vval.v_float; - else - f1 = get_tv_number(rettv); - if (var2.v_type == VAR_FLOAT) - f2 = var2.vval.v_float; - else - f2 = get_tv_number(&var2); - n1 = FALSE; - switch (type) { - case TYPE_EQUAL: n1 = (f1 == f2); break; - case TYPE_NEQUAL: n1 = (f1 != f2); break; - case TYPE_GREATER: n1 = (f1 > f2); break; - case TYPE_GEQUAL: n1 = (f1 >= f2); break; - case TYPE_SMALLER: n1 = (f1 < f2); break; - case TYPE_SEQUAL: n1 = (f1 <= f2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ - } - } - /* - * If one of the two variables is a number, compare as a number. - * When using "=~" or "!~", always compare as string. - */ - else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER) - && type != TYPE_MATCH && type != TYPE_NOMATCH) { - n1 = get_tv_number(rettv); - n2 = get_tv_number(&var2); - switch (type) { - case TYPE_EQUAL: n1 = (n1 == n2); break; - case TYPE_NEQUAL: n1 = (n1 != n2); break; - case TYPE_GREATER: n1 = (n1 > n2); break; - case TYPE_GEQUAL: n1 = (n1 >= n2); break; - case TYPE_SMALLER: n1 = (n1 < n2); break; - case TYPE_SEQUAL: n1 = (n1 <= n2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ - } - } else { - s1 = get_tv_string_buf(rettv, buf1); - s2 = get_tv_string_buf(&var2, buf2); - if (type != TYPE_MATCH && type != TYPE_NOMATCH) - i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2); - else - i = 0; - n1 = FALSE; - switch (type) { - case TYPE_EQUAL: n1 = (i == 0); break; - case TYPE_NEQUAL: n1 = (i != 0); break; - case TYPE_GREATER: n1 = (i > 0); break; - case TYPE_GEQUAL: n1 = (i >= 0); break; - case TYPE_SMALLER: n1 = (i < 0); break; - case TYPE_SEQUAL: n1 = (i <= 0); break; - - case TYPE_MATCH: - case TYPE_NOMATCH: - /* avoid 'l' flag in 'cpoptions' */ - save_cpo = p_cpo; - p_cpo = (char_u *)""; - regmatch.regprog = vim_regcomp(s2, - RE_MAGIC + RE_STRING); - regmatch.rm_ic = ic; - if (regmatch.regprog != NULL) { - n1 = vim_regexec_nl(®match, s1, (colnr_T)0); - vim_regfree(regmatch.regprog); - if (type == TYPE_NOMATCH) - n1 = !n1; - } - p_cpo = save_cpo; - break; - - case TYPE_UNKNOWN: break; /* avoid gcc warning */ - } - } - clear_tv(rettv); - clear_tv(&var2); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; - } - } - - return OK; -} - -/* - * Handle fourth level expression: - * + number addition - * - number subtraction - * . string concatenation - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ -static int eval5(char_u **arg, typval_T *rettv, int evaluate) -{ - typval_T var2; - typval_T var3; - int op; - long n1, n2; - float_T f1 = 0, f2 = 0; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - char_u *p; - - /* - * Get the first variable. - */ - if (eval6(arg, rettv, evaluate, FALSE) == FAIL) - return FAIL; - - /* - * Repeat computing, until no '+', '-' or '.' is following. - */ - for (;; ) { - op = **arg; - if (op != '+' && op != '-' && op != '.') - break; - - if ((op != '+' || rettv->v_type != VAR_LIST) - && (op == '.' || rettv->v_type != VAR_FLOAT) - ) { - /* For "list + ...", an illegal use of the first operand as - * a number cannot be determined before evaluating the 2nd - * operand: if this is also a list, all is ok. - * For "something . ...", "something - ..." or "non-list + ...", - * we know that the first operand needs to be a string or number - * without evaluating the 2nd operand. So check before to avoid - * side effects after an error. */ - if (evaluate && get_tv_string_chk(rettv) == NULL) { - clear_tv(rettv); - return FAIL; - } - } - - /* - * Get the second variable. - */ - *arg = skipwhite(*arg + 1); - if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { - clear_tv(rettv); - return FAIL; - } - - if (evaluate) { - /* - * Compute the result. - */ - if (op == '.') { - s1 = get_tv_string_buf(rettv, buf1); /* already checked */ - s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { /* type error ? */ - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } - p = concat_str(s1, s2); - clear_tv(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; - } else if (op == '+' && rettv->v_type == VAR_LIST - && var2.v_type == VAR_LIST) { - /* concatenate Lists */ - if (list_concat(rettv->vval.v_list, var2.vval.v_list, - &var3) == FAIL) { - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } - clear_tv(rettv); - *rettv = var3; - } else { - int error = FALSE; - - if (rettv->v_type == VAR_FLOAT) { - f1 = rettv->vval.v_float; - n1 = 0; - } else { - n1 = get_tv_number_chk(rettv, &error); - if (error) { - /* This can only happen for "list + non-list". For - * "non-list + ..." or "something - ...", we returned - * before evaluating the 2nd operand. */ - clear_tv(rettv); - return FAIL; - } - if (var2.v_type == VAR_FLOAT) - f1 = n1; - } - if (var2.v_type == VAR_FLOAT) { - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = get_tv_number_chk(&var2, &error); - if (error) { - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } - if (rettv->v_type == VAR_FLOAT) - f2 = n2; - } - clear_tv(rettv); - - /* If there is a float on either side the result is a float. */ - if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { - if (op == '+') - f1 = f1 + f2; - else - f1 = f1 - f2; - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f1; - } else { - if (op == '+') - n1 = n1 + n2; - else - n1 = n1 - n2; - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; - } - } - clear_tv(&var2); - } - } - return OK; -} - -/* - * Handle fifth level expression: - * * number multiplication - * / number division - * % number modulo - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ -static int -eval6 ( - char_u **arg, - typval_T *rettv, - int evaluate, - int want_string /* after "." operator */ -) -{ - typval_T var2; - int op; - long n1, n2; - int use_float = FALSE; - float_T f1 = 0, f2; - int error = FALSE; - - /* - * Get the first variable. - */ - if (eval7(arg, rettv, evaluate, want_string) == FAIL) - return FAIL; - - /* - * Repeat computing, until no '*', '/' or '%' is following. - */ - for (;; ) { - op = **arg; - if (op != '*' && op != '/' && op != '%') - break; - - if (evaluate) { - if (rettv->v_type == VAR_FLOAT) { - f1 = rettv->vval.v_float; - use_float = TRUE; - n1 = 0; - } else - n1 = get_tv_number_chk(rettv, &error); - clear_tv(rettv); - if (error) - return FAIL; - } else - n1 = 0; - - /* - * Get the second variable. - */ - *arg = skipwhite(*arg + 1); - if (eval7(arg, &var2, evaluate, FALSE) == FAIL) - return FAIL; - - if (evaluate) { - if (var2.v_type == VAR_FLOAT) { - if (!use_float) { - f1 = n1; - use_float = TRUE; - } - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = get_tv_number_chk(&var2, &error); - clear_tv(&var2); - if (error) - return FAIL; - if (use_float) - f2 = n2; - } - - /* - * Compute the result. - * When either side is a float the result is a float. - */ - if (use_float) { - if (op == '*') - f1 = f1 * f2; - else if (op == '/') { - /* We rely on the floating point library to handle divide - * by zero to result in "inf" and not a crash. */ - f1 = f2 != 0 ? f1 / f2 : INFINITY; - } else { - EMSG(_("E804: Cannot use '%' with Float")); - return FAIL; - } - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f1; - } else { - if (op == '*') - n1 = n1 * n2; - else if (op == '/') { - if (n2 == 0) { /* give an error message? */ - if (n1 == 0) - n1 = -0x7fffffffL - 1L; /* similar to NaN */ - else if (n1 < 0) - n1 = -0x7fffffffL; - else - n1 = 0x7fffffffL; - } else - n1 = n1 / n2; - } else { - if (n2 == 0) /* give an error message? */ - n1 = 0; - else - n1 = n1 % n2; - } - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; - } - } - } - - return OK; -} - -/* - * Handle sixth level expression: - * number number constant - * "string" string constant - * 'string' literal string constant - * &option-name option value - * @r register contents - * identifier variable value - * function() function call - * $VAR environment variable - * (expression) nested expression - * [expr, expr] List - * {key: val, key: val} Dictionary - * - * Also handle: - * ! in front logical NOT - * - in front unary minus - * + in front unary plus (ignored) - * trailing [] subscript in String or List - * trailing .name entry in Dictionary - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ -static int -eval7 ( - char_u **arg, - typval_T *rettv, - int evaluate, - int want_string /* after "." operator */ -) -{ - long n; - int len; - char_u *s; - char_u *start_leader, *end_leader; - int ret = OK; - char_u *alias; - - /* - * Initialise variable so that clear_tv() can't mistake this for a - * string and free a string that isn't there. - */ - rettv->v_type = VAR_UNKNOWN; - - /* - * Skip '!' and '-' characters. They are handled later. - */ - start_leader = *arg; - while (**arg == '!' || **arg == '-' || **arg == '+') - *arg = skipwhite(*arg + 1); - end_leader = *arg; - - switch (**arg) { - /* - * Number constant. - */ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - char_u *p = skipdigits(*arg + 1); - int get_float = FALSE; - - /* We accept a float when the format matches - * "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?". This is very - * strict to avoid backwards compatibility problems. - * Don't look for a float after the "." operator, so that - * ":let vers = 1.2.3" doesn't fail. */ - if (!want_string && p[0] == '.' && vim_isdigit(p[1])) { - get_float = TRUE; - p = skipdigits(p + 2); - if (*p == 'e' || *p == 'E') { - ++p; - if (*p == '-' || *p == '+') - ++p; - if (!vim_isdigit(*p)) - get_float = FALSE; - else - p = skipdigits(p + 1); - } - if (ASCII_ISALPHA(*p) || *p == '.') - get_float = FALSE; - } - if (get_float) { - float_T f; - - *arg += string2float(*arg, &f); - if (evaluate) { - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f; - } - } else { - vim_str2nr(*arg, NULL, &len, TRUE, TRUE, &n, NULL); - *arg += len; - if (evaluate) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n; - } - } - break; - } - - /* - * String constant: "string". - */ - case '"': ret = get_string_tv(arg, rettv, evaluate); - break; - - /* - * Literal string constant: 'str''ing'. - */ - case '\'': ret = get_lit_string_tv(arg, rettv, evaluate); - break; - - /* - * List: [expr, expr] - */ - case '[': ret = get_list_tv(arg, rettv, evaluate); - break; - - /* - * Dictionary: {key: val, key: val} - */ - case '{': ret = get_dict_tv(arg, rettv, evaluate); - break; - - /* - * Option value: &name - */ - case '&': ret = get_option_tv(arg, rettv, evaluate); - break; - - /* - * Environment variable: $VAR. - */ - case '$': ret = get_env_tv(arg, rettv, evaluate); - break; - - /* - * Register contents: @r. - */ - case '@': ++*arg; - if (evaluate) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_reg_contents(**arg, TRUE, TRUE); - } - if (**arg != NUL) - ++*arg; - break; - - /* - * nested expression: (expression). - */ - case '(': *arg = skipwhite(*arg + 1); - ret = eval1(arg, rettv, evaluate); /* recursive! */ - if (**arg == ')') - ++*arg; - else if (ret == OK) { - EMSG(_("E110: Missing ')'")); - clear_tv(rettv); - ret = FAIL; - } - break; - - default: ret = NOTDONE; - break; - } - - if (ret == NOTDONE) { - /* - * Must be a variable or function name. - * Can also be a curly-braces kind of name: {expr}. - */ - s = *arg; - len = get_name_len(arg, &alias, evaluate, TRUE); - if (alias != NULL) - s = alias; - - if (len <= 0) - ret = FAIL; - else { - if (**arg == '(') { /* recursive! */ - /* If "s" is the name of a variable of type VAR_FUNC - * use its contents. */ - s = deref_func_name(s, &len, !evaluate); - - /* Invoke the function. */ - ret = get_func_tv(s, len, rettv, arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, NULL); - - /* If evaluate is FALSE rettv->v_type was not set in - * get_func_tv, but it's needed in handle_subscript() to parse - * what follows. So set it here. */ - if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = (char_u *)""; - rettv->v_type = VAR_FUNC; - } - - /* Stop the expression evaluation when immediately - * aborting on error, or when an interrupt occurred or - * an exception was thrown but not caught. */ - if (aborting()) { - if (ret == OK) - clear_tv(rettv); - ret = FAIL; - } - } else if (evaluate) - ret = get_var_tv(s, len, rettv, TRUE, FALSE); - else - ret = OK; - } - free(alias); - } - - *arg = skipwhite(*arg); - - /* Handle following '[', '(' and '.' for expr[expr], expr.name, - * expr(expr). */ - if (ret == OK) - ret = handle_subscript(arg, rettv, evaluate, TRUE); - - /* - * Apply logical NOT and unary '-', from right to left, ignore '+'. - */ - if (ret == OK && evaluate && end_leader > start_leader) { - int error = FALSE; - int val = 0; - float_T f = 0.0; - - if (rettv->v_type == VAR_FLOAT) - f = rettv->vval.v_float; - else - val = get_tv_number_chk(rettv, &error); - if (error) { - clear_tv(rettv); - ret = FAIL; - } else { - while (end_leader > start_leader) { - --end_leader; - if (*end_leader == '!') { - if (rettv->v_type == VAR_FLOAT) - f = !f; - else - val = !val; - } else if (*end_leader == '-') { - if (rettv->v_type == VAR_FLOAT) - f = -f; - else - val = -val; - } - } - if (rettv->v_type == VAR_FLOAT) { - clear_tv(rettv); - rettv->vval.v_float = f; - } else { - clear_tv(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = val; - } - } - } - - return ret; -} - -/* - * Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". - * "*arg" points to the '[' or '.'. - * Returns FAIL or OK. "*arg" is advanced to after the ']'. - */ -static int -eval_index ( - char_u **arg, - typval_T *rettv, - int evaluate, - int verbose /* give error messages */ -) -{ - int empty1 = FALSE, empty2 = FALSE; - typval_T var1, var2; - long n1, n2 = 0; - long len = -1; - int range = FALSE; - char_u *s; - char_u *key = NULL; - - if (rettv->v_type == VAR_FUNC) { - if (verbose) - EMSG(_("E695: Cannot index a Funcref")); - return FAIL; - } else if (rettv->v_type == VAR_FLOAT) { - if (verbose) - EMSG(_(e_float_as_string)); - return FAIL; - } - - if (**arg == '.') { - /* - * dict.name - */ - key = *arg + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) - ; - if (len == 0) - return FAIL; - *arg = skipwhite(key + len); - } else { - /* - * something[idx] - * - * Get the (first) variable from inside the []. - */ - *arg = skipwhite(*arg + 1); - if (**arg == ':') - empty1 = TRUE; - else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */ - return FAIL; - else if (evaluate && get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); - return FAIL; - } - - /* - * Get the second variable from inside the [:]. - */ - if (**arg == ':') { - range = TRUE; - *arg = skipwhite(*arg + 1); - if (**arg == ']') - empty2 = TRUE; - else if (eval1(arg, &var2, evaluate) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); - return FAIL; - } else if (evaluate && get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); - return FAIL; - } - } - - /* Check for the ']'. */ - if (**arg != ']') { - if (verbose) - EMSG(_(e_missbrac)); - clear_tv(&var1); - if (range) - clear_tv(&var2); - return FAIL; - } - *arg = skipwhite(*arg + 1); /* skip the ']' */ - } - - if (evaluate) { - n1 = 0; - if (!empty1 && rettv->v_type != VAR_DICT) { - n1 = get_tv_number(&var1); - clear_tv(&var1); - } - if (range) { - if (empty2) - n2 = -1; - else { - n2 = get_tv_number(&var2); - clear_tv(&var2); - } - } - - switch (rettv->v_type) { - case VAR_NUMBER: - case VAR_STRING: - s = get_tv_string(rettv); - len = (long)STRLEN(s); - if (range) { - /* The resulting variable is a substring. If the indexes - * are out of range the result is empty. */ - if (n1 < 0) { - n1 = len + n1; - if (n1 < 0) - n1 = 0; - } - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len; - if (n1 >= len || n2 < 0 || n1 > n2) - s = NULL; - else - s = vim_strnsave(s + n1, (int)(n2 - n1 + 1)); - } else { - /* The resulting variable is a string of a single - * character. If the index is too big or negative the - * result is empty. */ - if (n1 >= len || n1 < 0) - s = NULL; - else - s = vim_strnsave(s + n1, 1); - } - clear_tv(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = s; - break; - - case VAR_LIST: - len = list_len(rettv->vval.v_list); - if (n1 < 0) - n1 = len + n1; - if (!empty1 && (n1 < 0 || n1 >= len)) { - /* For a range we allow invalid values and return an empty - * list. A list index out of range is an error. */ - if (!range) { - if (verbose) - EMSGN(_(e_listidx), n1); - return FAIL; - } - n1 = len; - } - if (range) { - list_T *l; - listitem_T *item; - - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len - 1; - if (!empty2 && (n2 < 0 || n2 + 1 < n1)) - n2 = -1; - l = list_alloc(); - if (l == NULL) - return FAIL; - item = list_find(rettv->vval.v_list, n1); - while (n1++ <= n2) { - list_append_tv(l, &item->li_tv); - item = item->li_next; - } - clear_tv(rettv); - rettv->v_type = VAR_LIST; - rettv->vval.v_list = l; - ++l->lv_refcount; - } else { - copy_tv(&list_find(rettv->vval.v_list, n1)->li_tv, &var1); - clear_tv(rettv); - *rettv = var1; - } - break; - - case VAR_DICT: - if (range) { - if (verbose) - EMSG(_(e_dictrange)); - if (len == -1) - clear_tv(&var1); - return FAIL; - } - { - dictitem_T *item; - - if (len == -1) { - key = get_tv_string(&var1); - if (*key == NUL) { - if (verbose) - EMSG(_(e_emptykey)); - clear_tv(&var1); - return FAIL; - } - } - - item = dict_find(rettv->vval.v_dict, key, (int)len); - - if (item == NULL && verbose) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); - if (item == NULL) - return FAIL; - - copy_tv(&item->di_tv, &var1); - clear_tv(rettv); - *rettv = var1; - } - break; - } - } - - return OK; -} - -/* - * Get an option value. - * "arg" points to the '&' or '+' before the option name. - * "arg" is advanced to character after the option name. - * Return OK or FAIL. - */ -static int -get_option_tv ( - char_u **arg, - typval_T *rettv, /* when NULL, only check if option exists */ - int evaluate -) -{ - char_u *option_end; - long numval; - char_u *stringval; - int opt_type; - int c; - int working = (**arg == '+'); /* has("+option") */ - int ret = OK; - int opt_flags; - - /* - * Isolate the option name and find its value. - */ - option_end = find_option_end(arg, &opt_flags); - if (option_end == NULL) { - if (rettv != NULL) - EMSG2(_("E112: Option name missing: %s"), *arg); - return FAIL; - } - - if (!evaluate) { - *arg = option_end; - return OK; - } - - c = *option_end; - *option_end = NUL; - opt_type = get_option_value(*arg, &numval, - rettv == NULL ? NULL : &stringval, opt_flags); - - if (opt_type == -3) { /* invalid name */ - if (rettv != NULL) - EMSG2(_("E113: Unknown option: %s"), *arg); - ret = FAIL; - } else if (rettv != NULL) { - if (opt_type == -2) { /* hidden string option */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - } else if (opt_type == -1) { /* hidden number option */ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - } else if (opt_type == 1) { /* number option */ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = numval; - } else { /* string option */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = stringval; - } - } else if (working && (opt_type == -2 || opt_type == -1)) - ret = FAIL; - - *option_end = c; /* put back for error messages */ - *arg = option_end; - - return ret; -} - -/* - * Allocate a variable for a string constant. - * Return OK or FAIL. - */ -static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) -{ - char_u *p; - char_u *name; - int extra = 0; - - /* - * Find the end of the string, skipping backslashed characters. - */ - for (p = *arg + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) { - if (*p == '\\' && p[1] != NUL) { - ++p; - /* A "\" form occupies at least 4 characters, and produces up - * to 6 characters: reserve space for 2 extra */ - if (*p == '<') - extra += 2; - } - } - - if (*p != '"') { - EMSG2(_("E114: Missing quote: %s"), *arg); - return FAIL; - } - - /* If only parsing, set *arg and return here */ - if (!evaluate) { - *arg = p + 1; - return OK; - } - - /* - * Copy the string into allocated memory, handling backslashed - * characters. - */ - name = alloc((unsigned)(p - *arg + extra)); - if (name == NULL) - return FAIL; - rettv->v_type = VAR_STRING; - rettv->vval.v_string = name; - - for (p = *arg + 1; *p != NUL && *p != '"'; ) { - if (*p == '\\') { - switch (*++p) { - case 'b': *name++ = BS; ++p; break; - case 'e': *name++ = ESC; ++p; break; - case 'f': *name++ = FF; ++p; break; - case 'n': *name++ = NL; ++p; break; - case 'r': *name++ = CAR; ++p; break; - case 't': *name++ = TAB; ++p; break; - - case 'X': /* hex: "\x1", "\x12" */ - case 'x': - case 'u': /* Unicode: "\u0023" */ - case 'U': - if (vim_isxdigit(p[1])) { - int n, nr; - int c = toupper(*p); - - if (c == 'X') - n = 2; - else - n = 4; - nr = 0; - while (--n >= 0 && vim_isxdigit(p[1])) { - ++p; - nr = (nr << 4) + hex2nr(*p); - } - ++p; - /* For "\u" store the number according to - * 'encoding'. */ - if (c != 'X') - name += (*mb_char2bytes)(nr, name); - else - *name++ = nr; - } - break; - - /* octal: "\1", "\12", "\123" */ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': *name = *p++ - '0'; - if (*p >= '0' && *p <= '7') { - *name = (*name << 3) + *p++ - '0'; - if (*p >= '0' && *p <= '7') - *name = (*name << 3) + *p++ - '0'; - } - ++name; - break; - - /* Special key, e.g.: "\" */ - case '<': extra = trans_special(&p, name, TRUE); - if (extra != 0) { - name += extra; - break; - } - /* FALLTHROUGH */ - - default: MB_COPY_CHAR(p, name); - break; - } - } else - MB_COPY_CHAR(p, name); - - } - *name = NUL; - *arg = p + 1; - - return OK; -} - -/* - * Allocate a variable for a 'str''ing' constant. - * Return OK or FAIL. - */ -static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) -{ - char_u *p; - char_u *str; - int reduce = 0; - - /* - * Find the end of the string, skipping ''. - */ - for (p = *arg + 1; *p != NUL; mb_ptr_adv(p)) { - if (*p == '\'') { - if (p[1] != '\'') - break; - ++reduce; - ++p; - } - } - - if (*p != '\'') { - EMSG2(_("E115: Missing quote: %s"), *arg); - return FAIL; - } - - /* If only parsing return after setting "*arg" */ - if (!evaluate) { - *arg = p + 1; - return OK; - } - - /* - * Copy the string into allocated memory, handling '' to ' reduction. - */ - str = alloc((unsigned)((p - *arg) - reduce)); - if (str == NULL) - return FAIL; - rettv->v_type = VAR_STRING; - rettv->vval.v_string = str; - - for (p = *arg + 1; *p != NUL; ) { - if (*p == '\'') { - if (p[1] != '\'') - break; - ++p; - } - MB_COPY_CHAR(p, str); - } - *str = NUL; - *arg = p + 1; - - return OK; -} - -/* - * Allocate a variable for a List and fill it from "*arg". - * Return OK or FAIL. - */ -static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) -{ - list_T *l = NULL; - typval_T tv; - listitem_T *item; - - if (evaluate) { - l = list_alloc(); - if (l == NULL) - return FAIL; - } - - *arg = skipwhite(*arg + 1); - while (**arg != ']' && **arg != NUL) { - if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ - goto failret; - if (evaluate) { - item = listitem_alloc(); - if (item != NULL) { - item->li_tv = tv; - item->li_tv.v_lock = 0; - list_append(l, item); - } else - clear_tv(&tv); - } - - if (**arg == ']') - break; - if (**arg != ',') { - EMSG2(_("E696: Missing comma in List: %s"), *arg); - goto failret; - } - *arg = skipwhite(*arg + 1); - } - - if (**arg != ']') { - EMSG2(_("E697: Missing end of List ']': %s"), *arg); -failret: - if (evaluate) - list_free(l, TRUE); - return FAIL; - } - - *arg = skipwhite(*arg + 1); - if (evaluate) { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = l; - ++l->lv_refcount; - } - - return OK; -} - -/* - * Allocate an empty header for a list. - * Caller should take care of the reference count. - */ -list_T *list_alloc(void) -{ - list_T *list = xcalloc(1, sizeof(list_T)); - - /* Prepend the list to the list of lists for garbage collection. */ - if (first_list != NULL) - first_list->lv_used_prev = list; - list->lv_used_prev = NULL; - list->lv_used_next = first_list; - first_list = list; - return list; -} - -/* - * Allocate an empty list for a return value. - */ -static void rettv_list_alloc(typval_T *rettv) -{ - list_T *l = list_alloc(); - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - ++l->lv_refcount; -} - -/* - * Unreference a list: decrement the reference count and free it when it - * becomes zero. - */ -void list_unref(list_T *l) -{ - if (l != NULL && --l->lv_refcount <= 0) - list_free(l, TRUE); -} - -/* - * Free a list, including all items it points to. - * Ignores the reference count. - */ -void -list_free ( - list_T *l, - int recurse /* Free Lists and Dictionaries recursively. */ -) -{ - listitem_T *item; - - /* Remove the list from the list of lists for garbage collection. */ - if (l->lv_used_prev == NULL) - first_list = l->lv_used_next; - else - l->lv_used_prev->lv_used_next = l->lv_used_next; - if (l->lv_used_next != NULL) - l->lv_used_next->lv_used_prev = l->lv_used_prev; - - for (item = l->lv_first; item != NULL; item = l->lv_first) { - /* Remove the item before deleting it. */ - l->lv_first = item->li_next; - if (recurse || (item->li_tv.v_type != VAR_LIST - && item->li_tv.v_type != VAR_DICT)) - clear_tv(&item->li_tv); - free(item); - } - free(l); -} - -/* - * Allocate a list item. - */ -listitem_T *listitem_alloc(void) -{ - return (listitem_T *)alloc(sizeof(listitem_T)); -} - -/* - * Free a list item. Also clears the value. Does not notify watchers. - */ -void listitem_free(listitem_T *item) -{ - clear_tv(&item->li_tv); - free(item); -} - -/* - * Remove a list item from a List and free it. Also clears the value. - */ -void listitem_remove(list_T *l, listitem_T *item) -{ - list_remove(l, item, item); - listitem_free(item); -} - -/* - * Get the number of items in a list. - */ -static long list_len(list_T *l) -{ - if (l == NULL) - return 0L; - return l->lv_len; -} - -/* - * Return TRUE when two lists have exactly the same values. - */ -static int -list_equal ( - list_T *l1, - list_T *l2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - listitem_T *item1, *item2; - - if (l1 == NULL || l2 == NULL) - return FALSE; - if (l1 == l2) - return TRUE; - if (list_len(l1) != list_len(l2)) - return FALSE; - - for (item1 = l1->lv_first, item2 = l2->lv_first; - item1 != NULL && item2 != NULL; - item1 = item1->li_next, item2 = item2->li_next) - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) - return FALSE; - return item1 == NULL && item2 == NULL; -} - -#if defined(PROTO) -/* - * Return the dictitem that an entry in a hashtable points to. - */ -dictitem_T *dict_lookup(hashitem_T *hi) -{ - return HI2DI(hi); -} -#endif - -dictitem_T * dict_lookup(hashitem_T *hi) -{ - return HI2DI(hi); -} - -/* - * Return TRUE when two dictionaries have exactly the same key/values. - */ -static int -dict_equal ( - dict_T *d1, - dict_T *d2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - hashitem_T *hi; - dictitem_T *item2; - int todo; - - if (d1 == NULL || d2 == NULL) - return FALSE; - if (d1 == d2) - return TRUE; - if (dict_len(d1) != dict_len(d2)) - return FALSE; - - todo = (int)d1->dv_hashtab.ht_used; - for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - item2 = dict_find(d2, hi->hi_key, -1); - if (item2 == NULL) - return FALSE; - if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive)) - return FALSE; - --todo; - } - } - return TRUE; -} - -static int tv_equal_recurse_limit; - -/* - * Return TRUE if "tv1" and "tv2" have the same value. - * Compares the items just like "==" would compare them, but strings and - * numbers are different. Floats and numbers are also different. - */ -static int -tv_equal ( - typval_T *tv1, - typval_T *tv2, - int ic, /* ignore case */ - int recursive /* TRUE when used recursively */ -) -{ - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - char_u *s1, *s2; - static int recursive_cnt = 0; /* catch recursive loops */ - int r; - - if (tv1->v_type != tv2->v_type) - return FALSE; - - /* Catch lists and dicts that have an endless loop by limiting - * recursiveness to a limit. We guess they are equal then. - * A fixed limit has the problem of still taking an awful long time. - * Reduce the limit every time running into it. That should work fine for - * deeply linked structures that are not recursively linked and catch - * recursiveness quickly. */ - if (!recursive) - tv_equal_recurse_limit = 1000; - if (recursive_cnt >= tv_equal_recurse_limit) { - --tv_equal_recurse_limit; - return TRUE; - } - - switch (tv1->v_type) { - case VAR_LIST: - ++recursive_cnt; - r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE); - --recursive_cnt; - return r; - - case VAR_DICT: - ++recursive_cnt; - r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE); - --recursive_cnt; - return r; - - case VAR_FUNC: - return tv1->vval.v_string != NULL - && tv2->vval.v_string != NULL - && STRCMP(tv1->vval.v_string, tv2->vval.v_string) == 0; - - case VAR_NUMBER: - return tv1->vval.v_number == tv2->vval.v_number; - - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - - case VAR_STRING: - s1 = get_tv_string_buf(tv1, buf1); - s2 = get_tv_string_buf(tv2, buf2); - return (ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0; - } - - EMSG2(_(e_intern2), "tv_equal()"); - return TRUE; -} - -/* - * Locate item with index "n" in list "l" and return it. - * A negative index is counted from the end; -1 is the last item. - * Returns NULL when "n" is out of range. - */ -listitem_T *list_find(list_T *l, long n) -{ - listitem_T *item; - long idx; - - if (l == NULL) - return NULL; - - /* Negative index is relative to the end. */ - if (n < 0) - n = l->lv_len + n; - - /* Check for index out of range. */ - if (n < 0 || n >= l->lv_len) - return NULL; - - /* When there is a cached index may start search from there. */ - if (l->lv_idx_item != NULL) { - if (n < l->lv_idx / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else if (n > (l->lv_idx + l->lv_len) / 2) { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } else { - /* closest to the cached index */ - item = l->lv_idx_item; - idx = l->lv_idx; - } - } else { - if (n < l->lv_len / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } - } - - while (n > idx) { - /* search forward */ - item = item->li_next; - ++idx; - } - while (n < idx) { - /* search backward */ - item = item->li_prev; - --idx; - } - - /* cache the used index */ - l->lv_idx = idx; - l->lv_idx_item = item; - - return item; -} - -/* - * Get list item "l[idx]" as a number. - */ -static long -list_find_nr ( - list_T *l, - long idx, - int *errorp /* set to TRUE when something wrong */ -) -{ - listitem_T *li; - - li = list_find(l, idx); - if (li == NULL) { - if (errorp != NULL) - *errorp = TRUE; - return -1L; - } - return get_tv_number_chk(&li->li_tv, errorp); -} - -/* - * Get list item "l[idx - 1]" as a string. Returns NULL for failure. - */ -char_u *list_find_str(list_T *l, long idx) -{ - listitem_T *li; - - li = list_find(l, idx - 1); - if (li == NULL) { - EMSGN(_(e_listidx), idx); - return NULL; - } - return get_tv_string(&li->li_tv); -} - -/* - * Locate "item" list "l" and return its index. - * Returns -1 when "item" is not in the list. - */ -static long list_idx_of_item(list_T *l, listitem_T *item) -{ - long idx = 0; - listitem_T *li; - - if (l == NULL) - return -1; - idx = 0; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) - ++idx; - if (li == NULL) - return -1; - return idx; -} - -/* - * Append item "item" to the end of list "l". - */ -void list_append(list_T *l, listitem_T *item) -{ - if (l->lv_last == NULL) { - /* empty list */ - l->lv_first = item; - l->lv_last = item; - item->li_prev = NULL; - } else { - l->lv_last->li_next = item; - item->li_prev = l->lv_last; - l->lv_last = item; - } - ++l->lv_len; - item->li_next = NULL; -} - -/* - * Append typval_T "tv" to the end of list "l". - */ -void list_append_tv(list_T *l, typval_T *tv) -{ - listitem_T *li = listitem_alloc(); - copy_tv(tv, &li->li_tv); - list_append(l, li); -} - -/* - * Add a dictionary to a list. Used by getqflist(). - */ -void list_append_dict(list_T *list, dict_T *dict) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_dict = dict; - list_append(list, li); - ++dict->dv_refcount; -} - -/* - * Make a copy of "str" and append it as an item to list "l". - * When "len" >= 0 use "str[len]". - */ -void list_append_string(list_T *l, char_u *str, int len) -{ - listitem_T *li = listitem_alloc(); - - list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - - if (str == NULL) { - li->li_tv.vval.v_string = NULL; - } else { - li->li_tv.vval.v_string = (len >= 0) ? vim_strnsave(str, len) - : vim_strsave(str); - } -} - -/* - * Append "n" to list "l". - */ -static void list_append_number(list_T *l, varnumber_T n) -{ - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_number = n; - list_append(l, li); -} - -/* - * Insert typval_T "tv" in list "l" before "item". - * If "item" is NULL append at the end. - * Return FAIL when out of memory. - */ -int list_insert_tv(list_T *l, typval_T *tv, listitem_T *item) -{ - listitem_T *ni = listitem_alloc(); - - if (ni == NULL) - return FAIL; - copy_tv(tv, &ni->li_tv); - list_insert(l, ni, item); - return OK; -} - -void list_insert(list_T *l, listitem_T *ni, listitem_T *item) -{ - if (item == NULL) - /* Append new item at end of list. */ - list_append(l, ni); - else { - /* Insert new item before existing item. */ - ni->li_prev = item->li_prev; - ni->li_next = item; - if (item->li_prev == NULL) { - l->lv_first = ni; - ++l->lv_idx; - } else { - item->li_prev->li_next = ni; - l->lv_idx_item = NULL; - } - item->li_prev = ni; - ++l->lv_len; - } -} - -/* - * Extend "l1" with "l2". - * If "bef" is NULL append at the end, otherwise insert before this item. - * Returns FAIL when out of memory. - */ -static int list_extend(list_T *l1, list_T *l2, listitem_T *bef) -{ - listitem_T *item; - int todo = l2->lv_len; - - /* We also quit the loop when we have inserted the original item count of - * the list, avoid a hang when we extend a list with itself. */ - for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next) - if (list_insert_tv(l1, &item->li_tv, bef) == FAIL) - return FAIL; - return OK; -} - -/* - * Concatenate lists "l1" and "l2" into a new list, stored in "tv". - * Return FAIL when out of memory. - */ -static int list_concat(list_T *l1, list_T *l2, typval_T *tv) -{ - list_T *l; - - if (l1 == NULL || l2 == NULL) - return FAIL; - - /* make a copy of the first list. */ - l = list_copy(l1, FALSE, 0); - if (l == NULL) - return FAIL; - tv->v_type = VAR_LIST; - tv->vval.v_list = l; - - /* append all items from the second list */ - return list_extend(l, l2, NULL); -} - -/* - * Make a copy of list "orig". Shallow if "deep" is FALSE. - * The refcount of the new list is set to 1. - * See item_copy() for "copyID". - * Returns NULL when out of memory. - */ -static list_T *list_copy(list_T *orig, int deep, int copyID) -{ - list_T *copy; - listitem_T *item; - listitem_T *ni; - - if (orig == NULL) - return NULL; - - copy = list_alloc(); - if (copy != NULL) { - if (copyID != 0) { - /* Do this before adding the items, because one of the items may - * refer back to this list. */ - orig->lv_copyID = copyID; - orig->lv_copylist = copy; - } - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { - ni = listitem_alloc(); - if (ni == NULL) - break; - if (deep) { - if (item_copy(&item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { - free(ni); - break; - } - } else - copy_tv(&item->li_tv, &ni->li_tv); - list_append(copy, ni); - } - ++copy->lv_refcount; - if (item != NULL) { - list_unref(copy); - copy = NULL; - } - } - - return copy; -} - -/* - * Remove items "item" to "item2" from list "l". - * Does not free the listitem or the value! - */ -void list_remove(list_T *l, listitem_T *item, listitem_T *item2) -{ - listitem_T *ip; - - /* notify watchers */ - for (ip = item; ip != NULL; ip = ip->li_next) { - --l->lv_len; - list_fix_watch(l, ip); - if (ip == item2) - break; - } - - if (item2->li_next == NULL) - l->lv_last = item->li_prev; - else - item2->li_next->li_prev = item->li_prev; - if (item->li_prev == NULL) - l->lv_first = item2->li_next; - else - item->li_prev->li_next = item2->li_next; - l->lv_idx_item = NULL; -} - -/* - * Return an allocated string with the string representation of a list. - * May return NULL. - */ -static char_u *list2string(typval_T *tv, int copyID) -{ - garray_T ga; - - if (tv->vval.v_list == NULL) - return NULL; - ga_init(&ga, (int)sizeof(char), 80); - ga_append(&ga, '['); - if (list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE, copyID) == FAIL) { - free(ga.ga_data); - return NULL; - } - ga_append(&ga, ']'); - ga_append(&ga, NUL); - return (char_u *)ga.ga_data; -} - -typedef struct join_S { - char_u *s; - char_u *tofree; -} join_T; - -static int -list_join_inner ( - garray_T *gap, /* to store the result in */ - list_T *l, - char_u *sep, - int echo_style, - int copyID, - garray_T *join_gap /* to keep each list item string */ -) -{ - int i; - join_T *p; - int len; - int sumlen = 0; - int first = TRUE; - char_u *tofree; - char_u numbuf[NUMBUFLEN]; - listitem_T *item; - char_u *s; - - /* Stringify each item in the list. */ - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { - if (echo_style) - s = echo_string(&item->li_tv, &tofree, numbuf, copyID); - else - s = tv2string(&item->li_tv, &tofree, numbuf, copyID); - if (s == NULL) - return FAIL; - - len = (int)STRLEN(s); - sumlen += len; - - ga_grow(join_gap, 1); - p = ((join_T *)join_gap->ga_data) + (join_gap->ga_len++); - if (tofree != NULL || s != numbuf) { - p->s = s; - p->tofree = tofree; - } else { - p->s = vim_strnsave(s, len); - p->tofree = p->s; - } - - line_breakcheck(); - } - - /* Allocate result buffer with its total size, avoid re-allocation and - * multiple copy operations. Add 2 for a tailing ']' and NUL. */ - if (join_gap->ga_len >= 2) - sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1); - ga_grow(gap, sumlen + 2); - - for (i = 0; i < join_gap->ga_len && !got_int; ++i) { - if (first) - first = FALSE; - else - ga_concat(gap, sep); - p = ((join_T *)join_gap->ga_data) + i; - - if (p->s != NULL) - ga_concat(gap, p->s); - line_breakcheck(); - } - - return OK; -} - -/* - * Join list "l" into a string in "*gap", using separator "sep". - * When "echo_style" is TRUE use String as echoed, otherwise as inside a List. - * Return FAIL or OK. - */ -static int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int copyID) -{ - garray_T join_ga; - int retval; - join_T *p; - int i; - - ga_init(&join_ga, (int)sizeof(join_T), l->lv_len); - retval = list_join_inner(gap, l, sep, echo_style, copyID, &join_ga); - - /* Dispose each item in join_ga. */ - if (join_ga.ga_data != NULL) { - p = (join_T *)join_ga.ga_data; - for (i = 0; i < join_ga.ga_len; ++i) { - free(p->tofree); - ++p; - } - ga_clear(&join_ga); - } - - return retval; -} - -/* - * Garbage collection for lists and dictionaries. - * - * We use reference counts to be able to free most items right away when they - * are no longer used. But for composite items it's possible that it becomes - * unused while the reference count is > 0: When there is a recursive - * reference. Example: - * :let l = [1, 2, 3] - * :let d = {9: l} - * :let l[1] = d - * - * Since this is quite unusual we handle this with garbage collection: every - * once in a while find out which lists and dicts are not referenced from any - * variable. - * - * Here is a good reference text about garbage collection (refers to Python - * but it applies to all reference-counting mechanisms): - * http://python.ca/nas/python/gc/ - */ - -/* - * Do garbage collection for lists and dicts. - * Return TRUE if some memory was freed. - */ -int garbage_collect(void) -{ - int copyID; - buf_T *buf; - win_T *wp; - int i; - funccall_T *fc, **pfc; - int did_free; - int did_free_funccal = FALSE; - tabpage_T *tp; - - /* Only do this once. */ - want_garbage_collect = FALSE; - may_garbage_collect = FALSE; - garbage_collect_at_exit = FALSE; - - /* We advance by two because we add one for items referenced through - * previous_funccal. */ - current_copyID += COPYID_INC; - copyID = current_copyID; - - /* - * 1. Go through all accessible variables and mark all lists and dicts - * with copyID. - */ - - /* Don't free variables in the previous_funccal list unless they are only - * referenced through previous_funccal. This must be first, because if - * the item is referenced elsewhere the funccal must not be freed. */ - for (fc = previous_funccal; fc != NULL; fc = fc->caller) { - set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1); - set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1); - } - - /* script-local variables */ - for (i = 1; i <= ga_scripts.ga_len; ++i) - set_ref_in_ht(&SCRIPT_VARS(i), copyID); - - /* buffer-local variables */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - set_ref_in_item(&buf->b_bufvar.di_tv, copyID); - - /* window-local variables */ - FOR_ALL_TAB_WINDOWS(tp, wp) - set_ref_in_item(&wp->w_winvar.di_tv, copyID); - if (aucmd_win != NULL) - set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID); - - /* tabpage-local variables */ - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) - set_ref_in_item(&tp->tp_winvar.di_tv, copyID); - - /* global variables */ - set_ref_in_ht(&globvarht, copyID); - - /* function-local variables */ - for (fc = current_funccal; fc != NULL; fc = fc->caller) { - set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID); - set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID); - } - - /* v: vars */ - set_ref_in_ht(&vimvarht, copyID); - - - - - /* - * 2. Free lists and dictionaries that are not referenced. - */ - did_free = free_unref_items(copyID); - - /* - * 3. Check if any funccal can be freed now. - */ - for (pfc = &previous_funccal; *pfc != NULL; ) { - if (can_free_funccal(*pfc, copyID)) { - fc = *pfc; - *pfc = fc->caller; - free_funccal(fc, TRUE); - did_free = TRUE; - did_free_funccal = TRUE; - } else - pfc = &(*pfc)->caller; - } - if (did_free_funccal) - /* When a funccal was freed some more items might be garbage - * collected, so run again. */ - (void)garbage_collect(); - - return did_free; -} - -/* - * Free lists and dictionaries that are no longer referenced. - */ -static int free_unref_items(int copyID) -{ - dict_T *dd; - list_T *ll; - int did_free = FALSE; - - /* - * Go through the list of dicts and free items without the copyID. - */ - for (dd = first_dict; dd != NULL; ) - if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { - /* Free the Dictionary and ordinary items it contains, but don't - * recurse into Lists and Dictionaries, they will be in the list - * of dicts or list of lists. */ - dict_free(dd, FALSE); - did_free = TRUE; - - /* restart, next dict may also have been freed */ - dd = first_dict; - } else - dd = dd->dv_used_next; - - /* - * Go through the list of lists and free items without the copyID. - * But don't free a list that has a watcher (used in a for loop), these - * are not referenced anywhere. - */ - for (ll = first_list; ll != NULL; ) - if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && ll->lv_watch == NULL) { - /* Free the List and ordinary items it contains, but don't recurse - * into Lists and Dictionaries, they will be in the list of dicts - * or list of lists. */ - list_free(ll, FALSE); - did_free = TRUE; - - /* restart, next list may also have been freed */ - ll = first_list; - } else - ll = ll->lv_used_next; - - return did_free; -} - -/* - * Mark all lists and dicts referenced through hashtab "ht" with "copyID". - */ -void set_ref_in_ht(hashtab_T *ht, int copyID) -{ - int todo; - hashitem_T *hi; - - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) - if (!HASHITEM_EMPTY(hi)) { - --todo; - set_ref_in_item(&HI2DI(hi)->di_tv, copyID); - } -} - -/* - * Mark all lists and dicts referenced through list "l" with "copyID". - */ -void set_ref_in_list(list_T *l, int copyID) -{ - listitem_T *li; - - for (li = l->lv_first; li != NULL; li = li->li_next) - set_ref_in_item(&li->li_tv, copyID); -} - -/* - * Mark all lists and dicts referenced through typval "tv" with "copyID". - */ -void set_ref_in_item(typval_T *tv, int copyID) -{ - dict_T *dd; - list_T *ll; - - switch (tv->v_type) { - case VAR_DICT: - dd = tv->vval.v_dict; - if (dd != NULL && dd->dv_copyID != copyID) { - /* Didn't see this dict yet. */ - dd->dv_copyID = copyID; - set_ref_in_ht(&dd->dv_hashtab, copyID); - } - break; - - case VAR_LIST: - ll = tv->vval.v_list; - if (ll != NULL && ll->lv_copyID != copyID) { - /* Didn't see this list yet. */ - ll->lv_copyID = copyID; - set_ref_in_list(ll, copyID); - } - break; - } - return; -} - -/* - * Allocate an empty header for a dictionary. - */ -dict_T *dict_alloc(void) -{ - dict_T *d = xmalloc(sizeof(dict_T)); - - /* Add the dict to the list of dicts for garbage collection. */ - if (first_dict != NULL) - first_dict->dv_used_prev = d; - d->dv_used_next = first_dict; - d->dv_used_prev = NULL; - first_dict = d; - - hash_init(&d->dv_hashtab); - d->dv_lock = 0; - d->dv_scope = 0; - d->dv_refcount = 0; - d->dv_copyID = 0; - - return d; -} - -/* - * Allocate an empty dict for a return value. - * Returns OK or FAIL. - */ -static int rettv_dict_alloc(typval_T *rettv) -{ - dict_T *d = dict_alloc(); - - if (d == NULL) - return FAIL; - - rettv->vval.v_dict = d; - rettv->v_type = VAR_DICT; - ++d->dv_refcount; - return OK; -} - - -/* - * Unreference a Dictionary: decrement the reference count and free it when it - * becomes zero. - */ -void dict_unref(dict_T *d) -{ - if (d != NULL && --d->dv_refcount <= 0) - dict_free(d, TRUE); -} - -/* - * Free a Dictionary, including all items it contains. - * Ignores the reference count. - */ -void -dict_free ( - dict_T *d, - int recurse /* Free Lists and Dictionaries recursively. */ -) -{ - int todo; - hashitem_T *hi; - dictitem_T *di; - - /* Remove the dict from the list of dicts for garbage collection. */ - if (d->dv_used_prev == NULL) - first_dict = d->dv_used_next; - else - d->dv_used_prev->dv_used_next = d->dv_used_next; - if (d->dv_used_next != NULL) - d->dv_used_next->dv_used_prev = d->dv_used_prev; - - /* Lock the hashtab, we don't want it to resize while freeing items. */ - hash_lock(&d->dv_hashtab); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - /* Remove the item before deleting it, just in case there is - * something recursive causing trouble. */ - di = HI2DI(hi); - hash_remove(&d->dv_hashtab, hi); - if (recurse || (di->di_tv.v_type != VAR_LIST - && di->di_tv.v_type != VAR_DICT)) - clear_tv(&di->di_tv); - free(di); - --todo; - } - } - hash_clear(&d->dv_hashtab); - free(d); -} - -/* - * Allocate a Dictionary item. - * The "key" is copied to the new item. - * Note that the value of the item "di_tv" still needs to be initialized! - * Returns NULL when out of memory. - */ -dictitem_T *dictitem_alloc(char_u *key) -{ - dictitem_T *di; - - di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + STRLEN(key))); - if (di != NULL) { - STRCPY(di->di_key, key); - di->di_flags = 0; - } - return di; -} - -/* - * Make a copy of a Dictionary item. - */ -static dictitem_T *dictitem_copy(dictitem_T *org) -{ - dictitem_T *di; - - di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) - + STRLEN(org->di_key))); - if (di != NULL) { - STRCPY(di->di_key, org->di_key); - di->di_flags = 0; - copy_tv(&org->di_tv, &di->di_tv); - } - return di; -} - -/* - * Remove item "item" from Dictionary "dict" and free it. - */ -static void dictitem_remove(dict_T *dict, dictitem_T *item) -{ - hashitem_T *hi; - - hi = hash_find(&dict->dv_hashtab, item->di_key); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "dictitem_remove()"); - else - hash_remove(&dict->dv_hashtab, hi); - dictitem_free(item); -} - -/* - * Free a dict item. Also clears the value. - */ -void dictitem_free(dictitem_T *item) -{ - clear_tv(&item->di_tv); - free(item); -} - -/* - * Make a copy of dict "d". Shallow if "deep" is FALSE. - * The refcount of the new dict is set to 1. - * See item_copy() for "copyID". - * Returns NULL when out of memory. - */ -static dict_T *dict_copy(dict_T *orig, int deep, int copyID) -{ - dict_T *copy; - dictitem_T *di; - int todo; - hashitem_T *hi; - - if (orig == NULL) - return NULL; - - copy = dict_alloc(); - if (copy != NULL) { - if (copyID != 0) { - orig->dv_copyID = copyID; - orig->dv_copydict = copy; - } - todo = (int)orig->dv_hashtab.ht_used; - for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - di = dictitem_alloc(hi->hi_key); - if (di == NULL) - break; - if (deep) { - if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep, - copyID) == FAIL) { - free(di); - break; - } - } else - copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); - if (dict_add(copy, di) == FAIL) { - dictitem_free(di); - break; - } - } - } - - ++copy->dv_refcount; - if (todo > 0) { - dict_unref(copy); - copy = NULL; - } - } - - return copy; -} - -/* - * Add item "item" to Dictionary "d". - * Returns FAIL when out of memory and when key already exists. - */ -int dict_add(dict_T *d, dictitem_T *item) -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - -/* - * Add a number or string entry to dictionary "d". - * When "str" is NULL use number "nr", otherwise use "str". - * Returns FAIL when out of memory and when key already exists. - */ -int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) -{ - dictitem_T *item; - - item = dictitem_alloc((char_u *)key); - if (item == NULL) - return FAIL; - item->di_tv.v_lock = 0; - if (str == NULL) { - item->di_tv.v_type = VAR_NUMBER; - item->di_tv.vval.v_number = nr; - } else { - item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = vim_strsave(str); - } - if (dict_add(d, item) == FAIL) { - dictitem_free(item); - return FAIL; - } - return OK; -} - -/* - * Add a list entry to dictionary "d". - * Returns FAIL when out of memory and when key already exists. - */ -int dict_add_list(dict_T *d, char *key, list_T *list) -{ - dictitem_T *item; - - item = dictitem_alloc((char_u *)key); - if (item == NULL) - return FAIL; - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_LIST; - item->di_tv.vval.v_list = list; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); - return FAIL; - } - ++list->lv_refcount; - return OK; -} - -/* - * Get the number of items in a Dictionary. - */ -static long dict_len(dict_T *d) -{ - if (d == NULL) - return 0L; - return (long)d->dv_hashtab.ht_used; -} - -/* - * Find item "key[len]" in Dictionary "d". - * If "len" is negative use strlen(key). - * Returns NULL when not found. - */ -dictitem_T *dict_find(dict_T *d, char_u *key, int len) -{ -#define AKEYLEN 200 - char_u buf[AKEYLEN]; - char_u *akey; - char_u *tofree = NULL; - hashitem_T *hi; - - if (len < 0) - akey = key; - else if (len >= AKEYLEN) { - tofree = akey = vim_strnsave(key, len); - if (akey == NULL) - return NULL; - } else { - /* Avoid a malloc/free by using buf[]. */ - vim_strncpy(buf, key, len); - akey = buf; - } - - hi = hash_find(&d->dv_hashtab, akey); - free(tofree); - if (HASHITEM_EMPTY(hi)) - return NULL; - return HI2DI(hi); -} - -/* - * Get a string item from a dictionary. - * When "save" is TRUE allocate memory for it. - * Returns NULL if the entry doesn't exist or out of memory. - */ -char_u *get_dict_string(dict_T *d, char_u *key, int save) -{ - dictitem_T *di; - char_u *s; - - di = dict_find(d, key, -1); - if (di == NULL) - return NULL; - s = get_tv_string(&di->di_tv); - if (save && s != NULL) - s = vim_strsave(s); - return s; -} - -/* - * Get a number item from a dictionary. - * Returns 0 if the entry doesn't exist or out of memory. - */ -long get_dict_number(dict_T *d, char_u *key) -{ - dictitem_T *di; - - di = dict_find(d, key, -1); - if (di == NULL) - return 0; - return get_tv_number(&di->di_tv); -} - -/* - * Return an allocated string with the string representation of a Dictionary. - * May return NULL. - */ -static char_u *dict2string(typval_T *tv, int copyID) -{ - garray_T ga; - int first = TRUE; - char_u *tofree; - char_u numbuf[NUMBUFLEN]; - hashitem_T *hi; - char_u *s; - dict_T *d; - int todo; - - if ((d = tv->vval.v_dict) == NULL) - return NULL; - ga_init(&ga, (int)sizeof(char), 80); - ga_append(&ga, '{'); - - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - if (first) - first = FALSE; - else - ga_concat(&ga, (char_u *)", "); - - tofree = string_quote(hi->hi_key, FALSE); - if (tofree != NULL) { - ga_concat(&ga, tofree); - free(tofree); - } - ga_concat(&ga, (char_u *)": "); - s = tv2string(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID); - if (s != NULL) - ga_concat(&ga, s); - free(tofree); - if (s == NULL) - break; - } - } - if (todo > 0) { - free(ga.ga_data); - return NULL; - } - - ga_append(&ga, '}'); - ga_append(&ga, NUL); - return (char_u *)ga.ga_data; -} - -/* - * Allocate a variable for a Dictionary and fill it from "*arg". - * Return OK or FAIL. Returns NOTDONE for {expr}. - */ -static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) -{ - dict_T *d = NULL; - typval_T tvkey; - typval_T tv; - char_u *key = NULL; - dictitem_T *item; - char_u *start = skipwhite(*arg + 1); - char_u buf[NUMBUFLEN]; - - /* - * First check if it's not a curly-braces thing: {expr}. - * Must do this without evaluating, otherwise a function may be called - * twice. Unfortunately this means we need to call eval1() twice for the - * first item. - * But {} is an empty Dictionary. - */ - if (*start != '}') { - if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */ - return FAIL; - if (*start == '}') - return NOTDONE; - } - - if (evaluate) { - d = dict_alloc(); - if (d == NULL) - return FAIL; - } - tvkey.v_type = VAR_UNKNOWN; - tv.v_type = VAR_UNKNOWN; - - *arg = skipwhite(*arg + 1); - while (**arg != '}' && **arg != NUL) { - if (eval1(arg, &tvkey, evaluate) == FAIL) /* recursive! */ - goto failret; - if (**arg != ':') { - EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); - clear_tv(&tvkey); - goto failret; - } - if (evaluate) { - key = get_tv_string_buf_chk(&tvkey, buf); - if (key == NULL || *key == NUL) { - /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */ - if (key != NULL) - EMSG(_(e_emptykey)); - clear_tv(&tvkey); - goto failret; - } - } - - *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) { /* recursive! */ - if (evaluate) - clear_tv(&tvkey); - goto failret; - } - if (evaluate) { - item = dict_find(d, key, -1); - if (item != NULL) { - EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); - clear_tv(&tvkey); - clear_tv(&tv); - goto failret; - } - item = dictitem_alloc(key); - clear_tv(&tvkey); - if (item != NULL) { - item->di_tv = tv; - item->di_tv.v_lock = 0; - if (dict_add(d, item) == FAIL) - dictitem_free(item); - } - } - - if (**arg == '}') - break; - if (**arg != ',') { - EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg); - goto failret; - } - *arg = skipwhite(*arg + 1); - } - - if (**arg != '}') { - EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); -failret: - if (evaluate) - dict_free(d, TRUE); - return FAIL; - } - - *arg = skipwhite(*arg + 1); - if (evaluate) { - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = d; - ++d->dv_refcount; - } - - return OK; -} - -/* - * Return a string with the string representation of a variable. - * If the memory is allocated "tofree" is set to it, otherwise NULL. - * "numbuf" is used for a number. - * Does not put quotes around strings, as ":echo" displays values. - * When "copyID" is not NULL replace recursive lists and dicts with "...". - * May return NULL. - */ -static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID) -{ - static int recurse = 0; - char_u *r = NULL; - - if (recurse >= DICT_MAXNEST) { - EMSG(_("E724: variable nested too deep for displaying")); - *tofree = NULL; - return NULL; - } - ++recurse; - - switch (tv->v_type) { - case VAR_FUNC: - *tofree = NULL; - r = tv->vval.v_string; - break; - - case VAR_LIST: - if (tv->vval.v_list == NULL) { - *tofree = NULL; - r = NULL; - } else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID) { - *tofree = NULL; - r = (char_u *)"[...]"; - } else { - tv->vval.v_list->lv_copyID = copyID; - *tofree = list2string(tv, copyID); - r = *tofree; - } - break; - - case VAR_DICT: - if (tv->vval.v_dict == NULL) { - *tofree = NULL; - r = NULL; - } else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID) { - *tofree = NULL; - r = (char_u *)"{...}"; - } else { - tv->vval.v_dict->dv_copyID = copyID; - *tofree = dict2string(tv, copyID); - r = *tofree; - } - break; - - case VAR_STRING: - case VAR_NUMBER: - *tofree = NULL; - r = get_tv_string_buf(tv, numbuf); - break; - - case VAR_FLOAT: - *tofree = NULL; - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float); - r = numbuf; - break; - - default: - EMSG2(_(e_intern2), "echo_string()"); - *tofree = NULL; - } - - --recurse; - return r; -} - -/* - * Return a string with the string representation of a variable. - * If the memory is allocated "tofree" is set to it, otherwise NULL. - * "numbuf" is used for a number. - * Puts quotes around strings, so that they can be parsed back by eval(). - * May return NULL. - */ -static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID) -{ - switch (tv->v_type) { - case VAR_FUNC: - *tofree = string_quote(tv->vval.v_string, TRUE); - return *tofree; - case VAR_STRING: - *tofree = string_quote(tv->vval.v_string, FALSE); - return *tofree; - case VAR_FLOAT: - *tofree = NULL; - vim_snprintf((char *)numbuf, NUMBUFLEN - 1, "%g", tv->vval.v_float); - return numbuf; - case VAR_NUMBER: - case VAR_LIST: - case VAR_DICT: - break; - default: - EMSG2(_(e_intern2), "tv2string()"); - } - return echo_string(tv, tofree, numbuf, copyID); -} - -/* - * Return string "str" in ' quotes, doubling ' characters. - * If "str" is NULL an empty string is assumed. - * If "function" is TRUE make it function('string'). - */ -static char_u *string_quote(char_u *str, int function) -{ - unsigned len; - char_u *p, *r, *s; - - len = (function ? 13 : 3); - if (str != NULL) { - len += (unsigned)STRLEN(str); - for (p = str; *p != NUL; mb_ptr_adv(p)) - if (*p == '\'') - ++len; - } - s = r = alloc(len); - if (r != NULL) { - if (function) { - STRCPY(r, "function('"); - r += 10; - } else - *r++ = '\''; - if (str != NULL) - for (p = str; *p != NUL; ) { - if (*p == '\'') - *r++ = '\''; - MB_COPY_CHAR(p, r); - } - *r++ = '\''; - if (function) - *r++ = ')'; - *r++ = NUL; - } - return s; -} - -/* - * Convert the string "text" to a floating point number. - * This uses strtod(). setlocale(LC_NUMERIC, "C") has been used to make sure - * this always uses a decimal point. - * Returns the length of the text that was consumed. - */ -static int -string2float ( - char_u *text, - float_T *value /* result stored here */ -) -{ - char *s = (char *)text; - float_T f; - - f = strtod(s, &s); - *value = f; - return (int)((char_u *)s - text); -} - -/// Get the value of an environment variable. -/// -/// If the environment variable was not set, silently assume it is empty. -/// -/// @param arg Points to the '$'. It is advanced to after the name. -/// @return FAIL if the name is invalid. -/// -static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) -{ - char_u *name; - char_u *string = NULL; - int mustfree = FALSE; - int len; - int cc; - - ++*arg; - name = *arg; - len = get_env_len(arg); - - if (evaluate) { - if (len == 0) { - return FAIL; // Can't be an environment variable. - } - cc = name[len]; - name[len] = NUL; - // First try vim_getenv(), fast for normal environment vars. - string = vim_getenv(name, &mustfree); - if (string != NULL && *string != NUL) { - if (!mustfree) { - string = vim_strsave(string); - } - } else { - if (mustfree) { - free(string); - } - - // Next try expanding things like $VIM and ${HOME}. - string = expand_env_save(name - 1); - if (string != NULL && *string == '$') { - free(string); - string = NULL; - } - } - name[len] = cc; - rettv->v_type = VAR_STRING; - rettv->vval.v_string = string; - } - - return OK; -} - -/* - * Array with names and number of arguments of all internal functions - * MUST BE KEPT SORTED IN strcmp() ORDER FOR BINARY SEARCH! - */ -static struct fst { - char *f_name; /* function name */ - char f_min_argc; /* minimal number of arguments */ - char f_max_argc; /* maximal number of arguments */ - void (*f_func)(typval_T *args, typval_T *rvar); - /* implementation of function */ -} functions[] = -{ - {"abs", 1, 1, f_abs}, - {"acos", 1, 1, f_acos}, /* WJMc */ - {"add", 2, 2, f_add}, - {"and", 2, 2, f_and}, - {"append", 2, 2, f_append}, - {"argc", 0, 0, f_argc}, - {"argidx", 0, 0, f_argidx}, - {"argv", 0, 1, f_argv}, - {"asin", 1, 1, f_asin}, /* WJMc */ - {"atan", 1, 1, f_atan}, - {"atan2", 2, 2, f_atan2}, - {"browse", 4, 4, f_browse}, - {"browsedir", 2, 2, f_browsedir}, - {"bufexists", 1, 1, f_bufexists}, - {"buffer_exists", 1, 1, f_bufexists}, /* obsolete */ - {"buffer_name", 1, 1, f_bufname}, /* obsolete */ - {"buffer_number", 1, 1, f_bufnr}, /* obsolete */ - {"buflisted", 1, 1, f_buflisted}, - {"bufloaded", 1, 1, f_bufloaded}, - {"bufname", 1, 1, f_bufname}, - {"bufnr", 1, 2, f_bufnr}, - {"bufwinnr", 1, 1, f_bufwinnr}, - {"byte2line", 1, 1, f_byte2line}, - {"byteidx", 2, 2, f_byteidx}, - {"byteidxcomp", 2, 2, f_byteidxcomp}, - {"call", 2, 3, f_call}, - {"ceil", 1, 1, f_ceil}, - {"changenr", 0, 0, f_changenr}, - {"char2nr", 1, 2, f_char2nr}, - {"cindent", 1, 1, f_cindent}, - {"clearmatches", 0, 0, f_clearmatches}, - {"col", 1, 1, f_col}, - {"complete", 2, 2, f_complete}, - {"complete_add", 1, 1, f_complete_add}, - {"complete_check", 0, 0, f_complete_check}, - {"confirm", 1, 4, f_confirm}, - {"copy", 1, 1, f_copy}, - {"cos", 1, 1, f_cos}, - {"cosh", 1, 1, f_cosh}, - {"count", 2, 4, f_count}, - {"cscope_connection",0,3, f_cscope_connection}, - {"cursor", 1, 3, f_cursor}, - {"deepcopy", 1, 2, f_deepcopy}, - {"delete", 1, 1, f_delete}, - {"did_filetype", 0, 0, f_did_filetype}, - {"diff_filler", 1, 1, f_diff_filler}, - {"diff_hlID", 2, 2, f_diff_hlID}, - {"empty", 1, 1, f_empty}, - {"escape", 2, 2, f_escape}, - {"eval", 1, 1, f_eval}, - {"eventhandler", 0, 0, f_eventhandler}, - {"executable", 1, 1, f_executable}, - {"exists", 1, 1, f_exists}, - {"exp", 1, 1, f_exp}, - {"expand", 1, 3, f_expand}, - {"extend", 2, 3, f_extend}, - {"feedkeys", 1, 2, f_feedkeys}, - {"file_readable", 1, 1, f_filereadable}, /* obsolete */ - {"filereadable", 1, 1, f_filereadable}, - {"filewritable", 1, 1, f_filewritable}, - {"filter", 2, 2, f_filter}, - {"finddir", 1, 3, f_finddir}, - {"findfile", 1, 3, f_findfile}, - {"float2nr", 1, 1, f_float2nr}, - {"floor", 1, 1, f_floor}, - {"fmod", 2, 2, f_fmod}, - {"fnameescape", 1, 1, f_fnameescape}, - {"fnamemodify", 2, 2, f_fnamemodify}, - {"foldclosed", 1, 1, f_foldclosed}, - {"foldclosedend", 1, 1, f_foldclosedend}, - {"foldlevel", 1, 1, f_foldlevel}, - {"foldtext", 0, 0, f_foldtext}, - {"foldtextresult", 1, 1, f_foldtextresult}, - {"foreground", 0, 0, f_foreground}, - {"function", 1, 1, f_function}, - {"garbagecollect", 0, 1, f_garbagecollect}, - {"get", 2, 3, f_get}, - {"getbufline", 2, 3, f_getbufline}, - {"getbufvar", 2, 3, f_getbufvar}, - {"getchar", 0, 1, f_getchar}, - {"getcharmod", 0, 0, f_getcharmod}, - {"getcmdline", 0, 0, f_getcmdline}, - {"getcmdpos", 0, 0, f_getcmdpos}, - {"getcmdtype", 0, 0, f_getcmdtype}, - {"getcwd", 0, 0, f_getcwd}, - {"getfontname", 0, 1, f_getfontname}, - {"getfperm", 1, 1, f_getfperm}, - {"getfsize", 1, 1, f_getfsize}, - {"getftime", 1, 1, f_getftime}, - {"getftype", 1, 1, f_getftype}, - {"getline", 1, 2, f_getline}, - {"getloclist", 1, 1, f_getqflist}, - {"getmatches", 0, 0, f_getmatches}, - {"getpid", 0, 0, f_getpid}, - {"getpos", 1, 1, f_getpos}, - {"getqflist", 0, 0, f_getqflist}, - {"getreg", 0, 2, f_getreg}, - {"getregtype", 0, 1, f_getregtype}, - {"gettabvar", 2, 3, f_gettabvar}, - {"gettabwinvar", 3, 4, f_gettabwinvar}, - {"getwinposx", 0, 0, f_getwinposx}, - {"getwinposy", 0, 0, f_getwinposy}, - {"getwinvar", 2, 3, f_getwinvar}, - {"glob", 1, 3, f_glob}, - {"globpath", 2, 3, f_globpath}, - {"has", 1, 1, f_has}, - {"has_key", 2, 2, f_has_key}, - {"haslocaldir", 0, 0, f_haslocaldir}, - {"hasmapto", 1, 3, f_hasmapto}, - {"highlightID", 1, 1, f_hlID}, /* obsolete */ - {"highlight_exists",1, 1, f_hlexists}, /* obsolete */ - {"histadd", 2, 2, f_histadd}, - {"histdel", 1, 2, f_histdel}, - {"histget", 1, 2, f_histget}, - {"histnr", 1, 1, f_histnr}, - {"hlID", 1, 1, f_hlID}, - {"hlexists", 1, 1, f_hlexists}, - {"hostname", 0, 0, f_hostname}, - {"iconv", 3, 3, f_iconv}, - {"indent", 1, 1, f_indent}, - {"index", 2, 4, f_index}, - {"input", 1, 3, f_input}, - {"inputdialog", 1, 3, f_inputdialog}, - {"inputlist", 1, 1, f_inputlist}, - {"inputrestore", 0, 0, f_inputrestore}, - {"inputsave", 0, 0, f_inputsave}, - {"inputsecret", 1, 2, f_inputsecret}, - {"insert", 2, 3, f_insert}, - {"invert", 1, 1, f_invert}, - {"isdirectory", 1, 1, f_isdirectory}, - {"islocked", 1, 1, f_islocked}, - {"items", 1, 1, f_items}, - {"jobstart", 2, 3, f_job_start}, - {"jobstop", 1, 1, f_job_stop}, - {"jobwrite", 2, 2, f_job_write}, - {"join", 1, 2, f_join}, - {"keys", 1, 1, f_keys}, - {"last_buffer_nr", 0, 0, f_last_buffer_nr}, /* obsolete */ - {"len", 1, 1, f_len}, - {"libcall", 3, 3, f_libcall}, - {"libcallnr", 3, 3, f_libcallnr}, - {"line", 1, 1, f_line}, - {"line2byte", 1, 1, f_line2byte}, - {"lispindent", 1, 1, f_lispindent}, - {"localtime", 0, 0, f_localtime}, - {"log", 1, 1, f_log}, - {"log10", 1, 1, f_log10}, - {"map", 2, 2, f_map}, - {"maparg", 1, 4, f_maparg}, - {"mapcheck", 1, 3, f_mapcheck}, - {"match", 2, 4, f_match}, - {"matchadd", 2, 4, f_matchadd}, - {"matcharg", 1, 1, f_matcharg}, - {"matchdelete", 1, 1, f_matchdelete}, - {"matchend", 2, 4, f_matchend}, - {"matchlist", 2, 4, f_matchlist}, - {"matchstr", 2, 4, f_matchstr}, - {"max", 1, 1, f_max}, - {"min", 1, 1, f_min}, - {"mkdir", 1, 3, f_mkdir}, - {"mode", 0, 1, f_mode}, - {"nextnonblank", 1, 1, f_nextnonblank}, - {"nr2char", 1, 2, f_nr2char}, - {"or", 2, 2, f_or}, - {"pathshorten", 1, 1, f_pathshorten}, - {"pow", 2, 2, f_pow}, - {"prevnonblank", 1, 1, f_prevnonblank}, - {"printf", 2, 19, f_printf}, - {"pumvisible", 0, 0, f_pumvisible}, - {"range", 1, 3, f_range}, - {"readfile", 1, 3, f_readfile}, - {"reltime", 0, 2, f_reltime}, - {"reltimestr", 1, 1, f_reltimestr}, - {"remote_expr", 2, 3, f_remote_expr}, - {"remote_foreground", 1, 1, f_remote_foreground}, - {"remote_peek", 1, 2, f_remote_peek}, - {"remote_read", 1, 1, f_remote_read}, - {"remote_send", 2, 3, f_remote_send}, - {"remove", 2, 3, f_remove}, - {"rename", 2, 2, f_rename}, - {"repeat", 2, 2, f_repeat}, - {"resolve", 1, 1, f_resolve}, - {"reverse", 1, 1, f_reverse}, - {"round", 1, 1, f_round}, - {"screenattr", 2, 2, f_screenattr}, - {"screenchar", 2, 2, f_screenchar}, - {"screencol", 0, 0, f_screencol}, - {"screenrow", 0, 0, f_screenrow}, - {"search", 1, 4, f_search}, - {"searchdecl", 1, 3, f_searchdecl}, - {"searchpair", 3, 7, f_searchpair}, - {"searchpairpos", 3, 7, f_searchpairpos}, - {"searchpos", 1, 4, f_searchpos}, - {"server2client", 2, 2, f_server2client}, - {"serverlist", 0, 0, f_serverlist}, - {"setbufvar", 3, 3, f_setbufvar}, - {"setcmdpos", 1, 1, f_setcmdpos}, - {"setline", 2, 2, f_setline}, - {"setloclist", 2, 3, f_setloclist}, - {"setmatches", 1, 1, f_setmatches}, - {"setpos", 2, 2, f_setpos}, - {"setqflist", 1, 2, f_setqflist}, - {"setreg", 2, 3, f_setreg}, - {"settabvar", 3, 3, f_settabvar}, - {"settabwinvar", 4, 4, f_settabwinvar}, - {"setwinvar", 3, 3, f_setwinvar}, - {"sha256", 1, 1, f_sha256}, - {"shellescape", 1, 2, f_shellescape}, - {"shiftwidth", 0, 0, f_shiftwidth}, - {"simplify", 1, 1, f_simplify}, - {"sin", 1, 1, f_sin}, - {"sinh", 1, 1, f_sinh}, - {"sort", 1, 3, f_sort}, - {"soundfold", 1, 1, f_soundfold}, - {"spellbadword", 0, 1, f_spellbadword}, - {"spellsuggest", 1, 3, f_spellsuggest}, - {"split", 1, 3, f_split}, - {"sqrt", 1, 1, f_sqrt}, - {"str2float", 1, 1, f_str2float}, - {"str2nr", 1, 2, f_str2nr}, - {"strchars", 1, 1, f_strchars}, - {"strdisplaywidth", 1, 2, f_strdisplaywidth}, - {"strftime", 1, 2, f_strftime}, - {"stridx", 2, 3, f_stridx}, - {"string", 1, 1, f_string}, - {"strlen", 1, 1, f_strlen}, - {"strpart", 2, 3, f_strpart}, - {"strridx", 2, 3, f_strridx}, - {"strtrans", 1, 1, f_strtrans}, - {"strwidth", 1, 1, f_strwidth}, - {"submatch", 1, 2, f_submatch}, - {"substitute", 4, 4, f_substitute}, - {"synID", 3, 3, f_synID}, - {"synIDattr", 2, 3, f_synIDattr}, - {"synIDtrans", 1, 1, f_synIDtrans}, - {"synconcealed", 2, 2, f_synconcealed}, - {"synstack", 2, 2, f_synstack}, - {"system", 1, 2, f_system}, - {"tabpagebuflist", 0, 1, f_tabpagebuflist}, - {"tabpagenr", 0, 1, f_tabpagenr}, - {"tabpagewinnr", 1, 2, f_tabpagewinnr}, - {"tagfiles", 0, 0, f_tagfiles}, - {"taglist", 1, 1, f_taglist}, - {"tan", 1, 1, f_tan}, - {"tanh", 1, 1, f_tanh}, - {"tempname", 0, 0, f_tempname}, - {"test", 1, 1, f_test}, - {"tolower", 1, 1, f_tolower}, - {"toupper", 1, 1, f_toupper}, - {"tr", 3, 3, f_tr}, - {"trunc", 1, 1, f_trunc}, - {"type", 1, 1, f_type}, - {"undofile", 1, 1, f_undofile}, - {"undotree", 0, 0, f_undotree}, - {"uniq", 1, 3, f_uniq}, - {"values", 1, 1, f_values}, - {"virtcol", 1, 1, f_virtcol}, - {"visualmode", 0, 1, f_visualmode}, - {"wildmenumode", 0, 0, f_wildmenumode}, - {"winbufnr", 1, 1, f_winbufnr}, - {"wincol", 0, 0, f_wincol}, - {"winheight", 1, 1, f_winheight}, - {"winline", 0, 0, f_winline}, - {"winnr", 0, 1, f_winnr}, - {"winrestcmd", 0, 0, f_winrestcmd}, - {"winrestview", 1, 1, f_winrestview}, - {"winsaveview", 0, 0, f_winsaveview}, - {"winwidth", 1, 1, f_winwidth}, - {"writefile", 2, 3, f_writefile}, - {"xor", 2, 2, f_xor}, -}; - - -/* - * Function given to ExpandGeneric() to obtain the list of internal - * or user defined function names. - */ -char_u *get_function_name(expand_T *xp, int idx) -{ - static int intidx = -1; - char_u *name; - - if (idx == 0) - intidx = -1; - if (intidx < 0) { - name = get_user_func_name(xp, idx); - if (name != NULL) - return name; - } - if (++intidx < (int)(sizeof(functions) / sizeof(struct fst))) { - STRCPY(IObuff, functions[intidx].f_name); - STRCAT(IObuff, "("); - if (functions[intidx].f_max_argc == 0) - STRCAT(IObuff, ")"); - return IObuff; - } - - return NULL; -} - -/* - * Function given to ExpandGeneric() to obtain the list of internal or - * user defined variable or function names. - */ -char_u *get_expr_name(expand_T *xp, int idx) -{ - static int intidx = -1; - char_u *name; - - if (idx == 0) - intidx = -1; - if (intidx < 0) { - name = get_function_name(xp, idx); - if (name != NULL) - return name; - } - return get_user_var_name(xp, ++intidx); -} - - - - -/* - * Find internal function in table above. - * Return index, or -1 if not found - */ -static int -find_internal_func ( - char_u *name /* name of the function */ -) -{ - int first = 0; - int last = (int)(sizeof(functions) / sizeof(struct fst)) - 1; - - /* - * Find the function name in the table. Binary search. - */ - while (first <= last) { - int x = first + ((unsigned)(last - first)) / 2; - int cmp = STRCMP(name, functions[x].f_name); - if (cmp < 0) - last = x - 1; - else if (cmp > 0) - first = x + 1; - else - return x; - } - return -1; -} - -/* - * Check if "name" is a variable of type VAR_FUNC. If so, return the function - * name it contains, otherwise return "name". - */ -static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload) -{ - dictitem_T *v; - int cc; - - cc = name[*lenp]; - name[*lenp] = NUL; - v = find_var(name, NULL, no_autoload); - name[*lenp] = cc; - if (v != NULL && v->di_tv.v_type == VAR_FUNC) { - if (v->di_tv.vval.v_string == NULL) { - *lenp = 0; - return (char_u *)""; /* just in case */ - } - *lenp = (int)STRLEN(v->di_tv.vval.v_string); - return v->di_tv.vval.v_string; - } - - return name; -} - -/* - * Allocate a variable for the result of a function. - * Return OK or FAIL. - */ -static int -get_func_tv ( - char_u *name, /* name of the function */ - int len, /* length of "name" */ - typval_T *rettv, - char_u **arg, /* argument, pointing to the '(' */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - int *doesrange, /* return: function handled range */ - int evaluate, - dict_T *selfdict /* Dictionary for "self" */ -) -{ - char_u *argp; - int ret = OK; - typval_T argvars[MAX_FUNC_ARGS + 1]; /* vars for arguments */ - int argcount = 0; /* number of arguments found */ - - /* - * Get the arguments. - */ - argp = *arg; - while (argcount < MAX_FUNC_ARGS) { - argp = skipwhite(argp + 1); /* skip the '(' or ',' */ - if (*argp == ')' || *argp == ',' || *argp == NUL) - break; - if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) { - ret = FAIL; - break; - } - ++argcount; - if (*argp != ',') - break; - } - if (*argp == ')') - ++argp; - else - ret = FAIL; - - if (ret == OK) - ret = call_func(name, len, rettv, argcount, argvars, - firstline, lastline, doesrange, evaluate, selfdict); - else if (!aborting()) { - if (argcount == MAX_FUNC_ARGS) - emsg_funcname(N_("E740: Too many arguments for function %s"), name); - else - emsg_funcname(N_("E116: Invalid arguments for function %s"), name); - } - - while (--argcount >= 0) - clear_tv(&argvars[argcount]); - - *arg = skipwhite(argp); - return ret; -} - - -/* - * Call a function with its resolved parameters - * Return FAIL when the function can't be called, OK otherwise. - * Also returns OK when an error was encountered while executing the function. - */ -static int -call_func ( - char_u *funcname, /* name of the function */ - int len, /* length of "name" */ - typval_T *rettv, /* return value goes here */ - int argcount, /* number of "argvars" */ - typval_T *argvars, /* vars for arguments, must have "argcount" - PLUS ONE elements! */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - int *doesrange, /* return: function handled range */ - int evaluate, - dict_T *selfdict /* Dictionary for "self" */ -) -{ - int ret = FAIL; -#define ERROR_UNKNOWN 0 -#define ERROR_TOOMANY 1 -#define ERROR_TOOFEW 2 -#define ERROR_SCRIPT 3 -#define ERROR_DICT 4 -#define ERROR_NONE 5 -#define ERROR_OTHER 6 - int error = ERROR_NONE; - int i; - int llen; - ufunc_T *fp; -#define FLEN_FIXED 40 - char_u fname_buf[FLEN_FIXED + 1]; - char_u *fname; - char_u *name; - - /* Make a copy of the name, if it comes from a funcref variable it could - * be changed or deleted in the called function. */ - name = vim_strnsave(funcname, len); - if (name == NULL) - return ret; - - /* - * In a script change name() and s:name() to K_SNR 123_name(). - * Change 123_name() to K_SNR 123_name(). - * Use fname_buf[] when it fits, otherwise allocate memory (slow). - */ - llen = eval_fname_script(name); - if (llen > 0) { - fname_buf[0] = K_SPECIAL; - fname_buf[1] = KS_EXTRA; - fname_buf[2] = (int)KE_SNR; - i = 3; - if (eval_fname_sid(name)) { /* "" or "s:" */ - if (current_SID <= 0) - error = ERROR_SCRIPT; - else { - sprintf((char *)fname_buf + 3, "%" PRId64 "_", (int64_t)current_SID); - i = (int)STRLEN(fname_buf); - } - } - if (i + STRLEN(name + llen) < FLEN_FIXED) { - STRCPY(fname_buf + i, name + llen); - fname = fname_buf; - } else { - fname = alloc((unsigned)(i + STRLEN(name + llen) + 1)); - if (fname == NULL) - error = ERROR_OTHER; - else { - memmove(fname, fname_buf, (size_t)i); - STRCPY(fname + i, name + llen); - } - } - } else - fname = name; - - *doesrange = FALSE; - - - /* execute the function if no errors detected and executing */ - if (evaluate && error == ERROR_NONE) { - char_u *rfname = fname; - - /* Ignore "g:" before a function name. */ - if (fname[0] == 'g' && fname[1] == ':') { - rfname = fname + 2; - } - - rettv->v_type = VAR_NUMBER; /* default rettv is number zero */ - rettv->vval.v_number = 0; - error = ERROR_UNKNOWN; - - if (!builtin_function(rfname, -1)) { - /* - * User defined function. - */ - fp = find_func(rfname); - - /* Trigger FuncUndefined event, may load the function. */ - if (fp == NULL - && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, TRUE, NULL) - && !aborting()) { - /* executed an autocommand, search for the function again */ - fp = find_func(rfname); - } - /* Try loading a package. */ - if (fp == NULL && script_autoload(rfname, TRUE) && !aborting()) { - /* loaded a package, search for the function again */ - fp = find_func(rfname); - } - - if (fp != NULL) { - if (fp->uf_flags & FC_RANGE) - *doesrange = TRUE; - if (argcount < fp->uf_args.ga_len) - error = ERROR_TOOFEW; - else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) - error = ERROR_TOOMANY; - else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) - error = ERROR_DICT; - else { - /* - * Call the user function. - * Save and restore search patterns, script variables and - * redo buffer. - */ - save_search_patterns(); - saveRedobuff(); - ++fp->uf_calls; - call_user_func(fp, argcount, argvars, rettv, - firstline, lastline, - (fp->uf_flags & FC_DICT) ? selfdict : NULL); - if (--fp->uf_calls <= 0 && isdigit(*fp->uf_name) - && fp->uf_refcount <= 0) - /* Function was unreferenced while being used, free it - * now. */ - func_free(fp); - restoreRedobuff(); - restore_search_patterns(); - error = ERROR_NONE; - } - } - } else { - /* - * Find the function name in the table, call its implementation. - */ - i = find_internal_func(fname); - if (i >= 0) { - if (argcount < functions[i].f_min_argc) - error = ERROR_TOOFEW; - else if (argcount > functions[i].f_max_argc) - error = ERROR_TOOMANY; - else { - argvars[argcount].v_type = VAR_UNKNOWN; - functions[i].f_func(argvars, rettv); - error = ERROR_NONE; - } - } - } - /* - * The function call (or "FuncUndefined" autocommand sequence) might - * have been aborted by an error, an interrupt, or an explicitly thrown - * exception that has not been caught so far. This situation can be - * tested for by calling aborting(). For an error in an internal - * function or for the "E132" error in call_user_func(), however, the - * throw point at which the "force_abort" flag (temporarily reset by - * emsg()) is normally updated has not been reached yet. We need to - * update that flag first to make aborting() reliable. - */ - update_force_abort(); - } - if (error == ERROR_NONE) - ret = OK; - - /* - * Report an error unless the argument evaluation or function call has been - * cancelled due to an aborting error, an interrupt, or an exception. - */ - if (!aborting()) { - switch (error) { - case ERROR_UNKNOWN: - emsg_funcname(N_("E117: Unknown function: %s"), name); - break; - case ERROR_TOOMANY: - emsg_funcname(e_toomanyarg, name); - break; - case ERROR_TOOFEW: - emsg_funcname(N_("E119: Not enough arguments for function: %s"), - name); - break; - case ERROR_SCRIPT: - emsg_funcname(N_("E120: Using not in a script context: %s"), - name); - break; - case ERROR_DICT: - emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), - name); - break; - } - } - - if (fname != name && fname != fname_buf) - free(fname); - free(name); - - return ret; -} - -/* - * Give an error message with a function name. Handle things. - * "ermsg" is to be passed without translation, use N_() instead of _(). - */ -static void emsg_funcname(char *ermsg, char_u *name) -{ - char_u *p; - - if (*name == K_SPECIAL) - p = concat_str((char_u *)"", name + 3); - else - p = name; - EMSG2(_(ermsg), p); - if (p != name) - free(p); -} - -/* - * Return TRUE for a non-zero Number and a non-empty String. - */ -static int non_zero_arg(typval_T *argvars) -{ - return (argvars[0].v_type == VAR_NUMBER - && argvars[0].vval.v_number != 0) - || (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && *argvars[0].vval.v_string != NUL); -} - -/********************************************* - * Implementation of the built-in functions - */ - -static int get_float_arg(typval_T *argvars, float_T *f); - -/* - * Get the float value of "argvars[0]" into "f". - * Returns FAIL when the argument is not a Number or Float. - */ -static int get_float_arg(typval_T *argvars, float_T *f) -{ - if (argvars[0].v_type == VAR_FLOAT) { - *f = argvars[0].vval.v_float; - return OK; - } - if (argvars[0].v_type == VAR_NUMBER) { - *f = (float_T)argvars[0].vval.v_number; - return OK; - } - EMSG(_("E808: Number or Float required")); - return FAIL; -} - -/* - * "abs(expr)" function - */ -static void f_abs(typval_T *argvars, typval_T *rettv) -{ - if (argvars[0].v_type == VAR_FLOAT) { - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = fabs(argvars[0].vval.v_float); - } else { - varnumber_T n; - int error = FALSE; - - n = get_tv_number_chk(&argvars[0], &error); - if (error) - rettv->vval.v_number = -1; - else if (n > 0) - rettv->vval.v_number = n; - else - rettv->vval.v_number = -n; - } -} - -/* - * "acos()" function - */ -static void f_acos(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = acos(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "add(list, item)" function - */ -static void f_add(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - - rettv->vval.v_number = 1; /* Default: Failed */ - if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("add() argument"))) { - list_append_tv(l, &argvars[1]); - copy_tv(&argvars[0], rettv); - } - } else - EMSG(_(e_listreq)); -} - -/* - * "and(expr, expr)" function - */ -static void f_and(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - & get_tv_number_chk(&argvars[1], NULL); -} - -/* - * "append(lnum, string/list)" function - */ -static void f_append(typval_T *argvars, typval_T *rettv) -{ - long lnum; - char_u *line; - list_T *l = NULL; - listitem_T *li = NULL; - typval_T *tv; - long added = 0; - - /* When coming here from Insert mode, sync undo, so that this can be - * undone separately from what was previously inserted. */ - if (u_sync_once == 2) { - u_sync_once = 1; /* notify that u_sync() was called */ - u_sync(TRUE); - } - - lnum = get_tv_lnum(argvars); - if (lnum >= 0 - && lnum <= curbuf->b_ml.ml_line_count - && u_save(lnum, lnum + 1) == OK) { - if (argvars[1].v_type == VAR_LIST) { - l = argvars[1].vval.v_list; - if (l == NULL) - return; - li = l->lv_first; - } - for (;; ) { - if (l == NULL) - tv = &argvars[1]; /* append a string */ - else if (li == NULL) - break; /* end of list */ - else - tv = &li->li_tv; /* append item from list */ - line = get_tv_string_chk(tv); - if (line == NULL) { /* type error */ - rettv->vval.v_number = 1; /* Failed */ - break; - } - ml_append(lnum + added, line, (colnr_T)0, FALSE); - ++added; - if (l == NULL) - break; - li = li->li_next; - } - - appended_lines_mark(lnum, added); - if (curwin->w_cursor.lnum > lnum) - curwin->w_cursor.lnum += added; - } else - rettv->vval.v_number = 1; /* Failed */ -} - -/* - * "argc()" function - */ -static void f_argc(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = ARGCOUNT; -} - -/* - * "argidx()" function - */ -static void f_argidx(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = curwin->w_arg_idx; -} - -/* - * "argv(nr)" function - */ -static void f_argv(typval_T *argvars, typval_T *rettv) -{ - int idx; - - if (argvars[0].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[0], NULL); - if (idx >= 0 && idx < ARGCOUNT) - rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx])); - else - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; - } else { - rettv_list_alloc(rettv); - for (idx = 0; idx < ARGCOUNT; ++idx) { - list_append_string(rettv->vval.v_list, alist_name(&ARGLIST[idx]), -1); - } - } -} - -/* - * "asin()" function - */ -static void f_asin(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = asin(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "atan()" function - */ -static void f_atan(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = atan(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "atan2()" function - */ -static void f_atan2(typval_T *argvars, typval_T *rettv) -{ - float_T fx, fy; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = atan2(fx, fy); - else - rettv->vval.v_float = 0.0; -} - -/* - * "browse(save, title, initdir, default)" function - */ -static void f_browse(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; -} - -/* - * "browsedir(title, initdir)" function - */ -static void f_browsedir(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; -} - -static buf_T *find_buffer(typval_T *avar); - -/* - * Find a buffer by number or exact name. - */ -static buf_T *find_buffer(typval_T *avar) -{ - buf_T *buf = NULL; - - if (avar->v_type == VAR_NUMBER) - buf = buflist_findnr((int)avar->vval.v_number); - else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) { - buf = buflist_findname_exp(avar->vval.v_string); - if (buf == NULL) { - /* No full path name match, try a match with a URL or a "nofile" - * buffer, these don't use the full path. */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_fname != NULL - && (path_with_url(buf->b_fname) - || bt_nofile(buf) - ) - && STRCMP(buf->b_fname, avar->vval.v_string) == 0) - break; - } - } - return buf; -} - -/* - * "bufexists(expr)" function - */ -static void f_bufexists(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL); -} - -/* - * "buflisted(expr)" function - */ -static void f_buflisted(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - - buf = find_buffer(&argvars[0]); - rettv->vval.v_number = (buf != NULL && buf->b_p_bl); -} - -/* - * "bufloaded(expr)" function - */ -static void f_bufloaded(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - - buf = find_buffer(&argvars[0]); - rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); -} - -static buf_T *get_buf_tv(typval_T *tv, int curtab_only); - -/* - * Get buffer by number or pattern. - */ -static buf_T *get_buf_tv(typval_T *tv, int curtab_only) -{ - char_u *name = tv->vval.v_string; - int save_magic; - char_u *save_cpo; - buf_T *buf; - - if (tv->v_type == VAR_NUMBER) - return buflist_findnr((int)tv->vval.v_number); - if (tv->v_type != VAR_STRING) - return NULL; - if (name == NULL || *name == NUL) - return curbuf; - if (name[0] == '$' && name[1] == NUL) - return lastbuf; - - /* Ignore 'magic' and 'cpoptions' here to make scripts portable */ - save_magic = p_magic; - p_magic = TRUE; - save_cpo = p_cpo; - p_cpo = (char_u *)""; - - buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name), - TRUE, FALSE, curtab_only)); - - p_magic = save_magic; - p_cpo = save_cpo; - - /* If not found, try expanding the name, like done for bufexists(). */ - if (buf == NULL) - buf = find_buffer(tv); - - return buf; -} - -/* - * "bufname(expr)" function - */ -static void f_bufname(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - rettv->v_type = VAR_STRING; - if (buf != NULL && buf->b_fname != NULL) - rettv->vval.v_string = vim_strsave(buf->b_fname); - else - rettv->vval.v_string = NULL; - --emsg_off; -} - -/* - * "bufnr(expr)" function - */ -static void f_bufnr(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - int error = FALSE; - char_u *name; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - --emsg_off; - - /* If the buffer isn't found and the second argument is not zero create a - * new buffer. */ - if (buf == NULL - && argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error) != 0 - && !error - && (name = get_tv_string_chk(&argvars[0])) != NULL - && !error) - buf = buflist_new(name, NULL, (linenr_T)1, 0); - - if (buf != NULL) - rettv->vval.v_number = buf->b_fnum; - else - rettv->vval.v_number = -1; -} - -/* - * "bufwinnr(nr)" function - */ -static void f_bufwinnr(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - int winnr = 0; - buf_T *buf; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], TRUE); - for (wp = firstwin; wp; wp = wp->w_next) { - ++winnr; - if (wp->w_buffer == buf) - break; - } - rettv->vval.v_number = (wp != NULL ? winnr : -1); - --emsg_off; -} - -/* - * "byte2line(byte)" function - */ -static void f_byte2line(typval_T *argvars, typval_T *rettv) -{ - long boff = 0; - - boff = get_tv_number(&argvars[0]) - 1; /* boff gets -1 on type error */ - if (boff < 0) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = ml_find_line_or_offset(curbuf, - (linenr_T)0, &boff); -} - -static void byteidx(typval_T *argvars, typval_T *rettv, int comp) -{ - char_u *t; - char_u *str; - long idx; - - str = get_tv_string_chk(&argvars[0]); - idx = get_tv_number_chk(&argvars[1], NULL); - rettv->vval.v_number = -1; - if (str == NULL || idx < 0) - return; - - t = str; - for (; idx > 0; idx--) { - if (*t == NUL) /* EOL reached */ - return; - if (enc_utf8 && comp) - t += utf_ptr2len(t); - else - t += (*mb_ptr2len)(t); - } - rettv->vval.v_number = (varnumber_T)(t - str); -} - -/* - * "byteidx()" function - */ -static void f_byteidx(typval_T *argvars, typval_T *rettv) -{ - byteidx(argvars, rettv, FALSE); -} - -/* - * "byteidxcomp()" function - */ -static void f_byteidxcomp(typval_T *argvars, typval_T *rettv) -{ - byteidx(argvars, rettv, TRUE); -} - -int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv) -{ - listitem_T *item; - typval_T argv[MAX_FUNC_ARGS + 1]; - int argc = 0; - int dummy; - int r = 0; - - for (item = args->vval.v_list->lv_first; item != NULL; - item = item->li_next) { - if (argc == MAX_FUNC_ARGS) { - EMSG(_("E699: Too many arguments")); - break; - } - /* Make a copy of each argument. This is needed to be able to set - * v_lock to VAR_FIXED in the copy without changing the original list. - */ - copy_tv(&item->li_tv, &argv[argc++]); - } - - if (item == NULL) - r = call_func(name, (int)STRLEN(name), rettv, argc, argv, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, TRUE, selfdict); - - /* Free the arguments. */ - while (argc > 0) - clear_tv(&argv[--argc]); - - return r; -} - -/* - * "call(func, arglist)" function - */ -static void f_call(typval_T *argvars, typval_T *rettv) -{ - char_u *func; - dict_T *selfdict = NULL; - - if (argvars[1].v_type != VAR_LIST) { - EMSG(_(e_listreq)); - return; - } - if (argvars[1].vval.v_list == NULL) - return; - - if (argvars[0].v_type == VAR_FUNC) - func = argvars[0].vval.v_string; - else - func = get_tv_string(&argvars[0]); - if (*func == NUL) - return; /* type error or empty name */ - - if (argvars[2].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - selfdict = argvars[2].vval.v_dict; - } - - (void)func_call(func, &argvars[1], selfdict, rettv); -} - -/* - * "ceil({float})" function - */ -static void f_ceil(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = ceil(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "changenr()" function - */ -static void f_changenr(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = curbuf->b_u_seq_cur; -} - -/* - * "char2nr(string)" function - */ -static void f_char2nr(typval_T *argvars, typval_T *rettv) -{ - if (has_mbyte) { - int utf8 = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = get_tv_number_chk(&argvars[1], NULL); - - if (utf8) - rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0])); - else - rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0])); - } else - rettv->vval.v_number = get_tv_string(&argvars[0])[0]; -} - -/* - * "cindent(lnum)" function - */ -static void f_cindent(typval_T *argvars, typval_T *rettv) -{ - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_c_indent(); - curwin->w_cursor = pos; - } else - rettv->vval.v_number = -1; -} - -/* - * "clearmatches()" function - */ -static void f_clearmatches(typval_T *argvars, typval_T *rettv) -{ - clear_matches(curwin); -} - -/* - * "col(string)" function - */ -static void f_col(typval_T *argvars, typval_T *rettv) -{ - colnr_T col = 0; - pos_T *fp; - int fnum = curbuf->b_fnum; - - fp = var2fpos(&argvars[0], FALSE, &fnum); - if (fp != NULL && fnum == curbuf->b_fnum) { - if (fp->col == MAXCOL) { - /* '> can be MAXCOL, get the length of the line then */ - if (fp->lnum <= curbuf->b_ml.ml_line_count) - col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1; - else - col = MAXCOL; - } else { - col = fp->col + 1; - /* col(".") when the cursor is on the NUL at the end of the line - * because of "coladd" can be seen as an extra column. */ - if (virtual_active() && fp == &curwin->w_cursor) { - char_u *p = ml_get_cursor(); - - if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p, - curwin->w_virtcol - curwin->w_cursor.coladd)) { - int l; - - if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) - col += l; - } - } - } - } - rettv->vval.v_number = col; -} - -/* - * "complete()" function - */ -static void f_complete(typval_T *argvars, typval_T *rettv) -{ - int startcol; - - if ((State & INSERT) == 0) { - EMSG(_("E785: complete() can only be used in Insert mode")); - return; - } - - /* Check for undo allowed here, because if something was already inserted - * the line was already saved for undo and this check isn't done. */ - if (!undo_allowed()) - return; - - if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) { - EMSG(_(e_invarg)); - return; - } - - startcol = get_tv_number_chk(&argvars[0], NULL); - if (startcol <= 0) - return; - - set_completion(startcol - 1, argvars[1].vval.v_list); -} - -/* - * "complete_add()" function - */ -static void f_complete_add(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0); -} - -/* - * "complete_check()" function - */ -static void f_complete_check(typval_T *argvars, typval_T *rettv) -{ - int saved = RedrawingDisabled; - - RedrawingDisabled = 0; - ins_compl_check_keys(0); - rettv->vval.v_number = compl_interrupted; - RedrawingDisabled = saved; -} - -/* - * "confirm(message, buttons[, default [, type]])" function - */ -static void f_confirm(typval_T *argvars, typval_T *rettv) -{ - char_u *message; - char_u *buttons = NULL; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - int def = 1; - int type = VIM_GENERIC; - char_u *typestr; - int error = FALSE; - - message = get_tv_string_chk(&argvars[0]); - if (message == NULL) - error = TRUE; - if (argvars[1].v_type != VAR_UNKNOWN) { - buttons = get_tv_string_buf_chk(&argvars[1], buf); - if (buttons == NULL) - error = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) { - def = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - typestr = get_tv_string_buf_chk(&argvars[3], buf2); - if (typestr == NULL) - error = TRUE; - else { - switch (TOUPPER_ASC(*typestr)) { - case 'E': type = VIM_ERROR; break; - case 'Q': type = VIM_QUESTION; break; - case 'I': type = VIM_INFO; break; - case 'W': type = VIM_WARNING; break; - case 'G': type = VIM_GENERIC; break; - } - } - } - } - } - - if (buttons == NULL || *buttons == NUL) - buttons = (char_u *)_("&Ok"); - - if (!error) - rettv->vval.v_number = do_dialog(type, NULL, message, buttons, - def, NULL, FALSE); -} - -/* - * "copy()" function - */ -static void f_copy(typval_T *argvars, typval_T *rettv) -{ - item_copy(&argvars[0], rettv, FALSE, 0); -} - -/* - * "cos()" function - */ -static void f_cos(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cos(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "cosh()" function - */ -static void f_cosh(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cosh(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "count()" function - */ -static void f_count(typval_T *argvars, typval_T *rettv) -{ - long n = 0; - int ic = FALSE; - - if (argvars[0].v_type == VAR_LIST) { - listitem_T *li; - list_T *l; - long idx; - - if ((l = argvars[0].vval.v_list) != NULL) { - li = l->lv_first; - if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; - - ic = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[3], &error); - if (!error) { - li = list_find(l, idx); - if (li == NULL) - EMSGN(_(e_listidx), idx); - } - } - if (error) - li = NULL; - } - - for (; li != NULL; li = li->li_next) - if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) - ++n; - } - } else if (argvars[0].v_type == VAR_DICT) { - int todo; - dict_T *d; - hashitem_T *hi; - - if ((d = argvars[0].vval.v_dict) != NULL) { - int error = FALSE; - - if (argvars[2].v_type != VAR_UNKNOWN) { - ic = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - EMSG(_(e_invarg)); - } - - todo = error ? 0 : (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) - ++n; - } - } - } - } else - EMSG2(_(e_listdictarg), "count()"); - rettv->vval.v_number = n; -} - -/* - * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function - * - * Checks the existence of a cscope connection. - */ -static void f_cscope_connection(typval_T *argvars, typval_T *rettv) -{ - int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; - char_u buf[NUMBUFLEN]; - - if (argvars[0].v_type != VAR_UNKNOWN - && argvars[1].v_type != VAR_UNKNOWN) { - num = (int)get_tv_number(&argvars[0]); - dbpath = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) - prepend = get_tv_string_buf(&argvars[2], buf); - } - - rettv->vval.v_number = cs_connection(num, dbpath, prepend); -} - -/* - * "cursor(lnum, col)" function - * - * Moves the cursor to the specified line and column. - * Returns 0 when the position could be set, -1 otherwise. - */ -static void f_cursor(typval_T *argvars, typval_T *rettv) -{ - long line, col; - long coladd = 0; - - rettv->vval.v_number = -1; - if (argvars[1].v_type == VAR_UNKNOWN) { - pos_T pos; - - if (list2fpos(argvars, &pos, NULL) == FAIL) - return; - line = pos.lnum; - col = pos.col; - coladd = pos.coladd; - } else { - line = get_tv_lnum(argvars); - col = get_tv_number_chk(&argvars[1], NULL); - if (argvars[2].v_type != VAR_UNKNOWN) - coladd = get_tv_number_chk(&argvars[2], NULL); - } - if (line < 0 || col < 0 - || coladd < 0 - ) - return; /* type error; errmsg already given */ - if (line > 0) - curwin->w_cursor.lnum = line; - if (col > 0) - curwin->w_cursor.col = col - 1; - curwin->w_cursor.coladd = coladd; - - /* Make sure the cursor is in a valid position. */ - check_cursor(); - /* Correct cursor for multi-byte character. */ - if (has_mbyte) - mb_adjust_cursor(); - - curwin->w_set_curswant = TRUE; - rettv->vval.v_number = 0; -} - -/* - * "deepcopy()" function - */ -static void f_deepcopy(typval_T *argvars, typval_T *rettv) -{ - int noref = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - noref = get_tv_number_chk(&argvars[1], NULL); - if (noref < 0 || noref > 1) - EMSG(_(e_invarg)); - else { - current_copyID += COPYID_INC; - item_copy(&argvars[0], rettv, TRUE, noref == 0 ? current_copyID : 0); - } -} - -/* - * "delete()" function - */ -static void f_delete(typval_T *argvars, typval_T *rettv) -{ - if (check_restricted() || check_secure()) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = os_remove((char *)get_tv_string(&argvars[0])); -} - -/* - * "did_filetype()" function - */ -static void f_did_filetype(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = did_filetype; -} - -/* - * "diff_filler()" function - */ -static void f_diff_filler(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars)); -} - -/* - * "diff_hlID()" function - */ -static void f_diff_hlID(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum = get_tv_lnum(argvars); - static linenr_T prev_lnum = 0; - static int changedtick = 0; - static int fnum = 0; - static int change_start = 0; - static int change_end = 0; - static hlf_T hlID = (hlf_T)0; - int filler_lines; - int col; - - if (lnum < 0) /* ignore type error in {lnum} arg */ - lnum = 0; - if (lnum != prev_lnum - || changedtick != curbuf->b_changedtick - || fnum != curbuf->b_fnum) { - /* New line, buffer, change: need to get the values. */ - filler_lines = diff_check(curwin, lnum); - if (filler_lines < 0) { - if (filler_lines == -1) { - change_start = MAXCOL; - change_end = -1; - if (diff_find_change(curwin, lnum, &change_start, &change_end)) - hlID = HLF_ADD; /* added line */ - else - hlID = HLF_CHD; /* changed line */ - } else - hlID = HLF_ADD; /* added line */ - } else - hlID = (hlf_T)0; - prev_lnum = lnum; - changedtick = curbuf->b_changedtick; - fnum = curbuf->b_fnum; - } - - if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */ - if (col >= change_start && col <= change_end) - hlID = HLF_TXD; /* changed text */ - else - hlID = HLF_CHD; /* changed line */ - } - rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; -} - -/* - * "empty({expr})" function - */ -static void f_empty(typval_T *argvars, typval_T *rettv) -{ - int n; - - switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_FUNC: - n = argvars[0].vval.v_string == NULL - || *argvars[0].vval.v_string == NUL; - break; - case VAR_NUMBER: - n = argvars[0].vval.v_number == 0; - break; - case VAR_FLOAT: - n = argvars[0].vval.v_float == 0.0; - break; - case VAR_LIST: - n = argvars[0].vval.v_list == NULL - || argvars[0].vval.v_list->lv_first == NULL; - break; - case VAR_DICT: - n = argvars[0].vval.v_dict == NULL - || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; - break; - default: - EMSG2(_(e_intern2), "f_empty()"); - n = 0; - } - - rettv->vval.v_number = n; -} - -/* - * "escape({string}, {chars})" function - */ -static void f_escape(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - - rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); - rettv->v_type = VAR_STRING; -} - -/* - * "eval()" function - */ -static void f_eval(typval_T *argvars, typval_T *rettv) -{ - char_u *s; - - s = get_tv_string_chk(&argvars[0]); - if (s != NULL) - s = skipwhite(s); - - if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - } else if (*s != NUL) - EMSG(_(e_trailing)); -} - -/* - * "eventhandler()" function - */ -static void f_eventhandler(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = vgetc_busy; -} - -/* - * "executable()" function - */ -static void f_executable(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = os_can_exe(get_tv_string(&argvars[0])); -} - -/* - * "exists()" function - */ -static void f_exists(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - char_u *name; - int n = FALSE; - int len = 0; - - p = get_tv_string(&argvars[0]); - if (*p == '$') { /* environment variable */ - /* first try "normal" environment variables (fast) */ - if (os_getenv((char *)(p + 1)) != NULL) - n = TRUE; - else { - /* try expanding things like $VIM and ${HOME} */ - p = expand_env_save(p); - if (p != NULL && *p != '$') - n = TRUE; - free(p); - } - } else if (*p == '&' || *p == '+') { /* option */ - n = (get_option_tv(&p, NULL, TRUE) == OK); - if (*skipwhite(p) != NUL) - n = FALSE; /* trailing garbage */ - } else if (*p == '*') { /* internal or user defined function */ - n = function_exists(p + 1); - } else if (*p == ':') { - n = cmd_exists(p + 1); - } else if (*p == '#') { - if (p[1] == '#') - n = autocmd_supported(p + 2); - else - n = au_exists(p + 1); - } else { /* internal variable */ - char_u *tofree; - typval_T tv; - - /* get_name_len() takes care of expanding curly braces */ - name = p; - len = get_name_len(&p, &tofree, TRUE, FALSE); - if (len > 0) { - if (tofree != NULL) - name = tofree; - n = (get_var_tv(name, len, &tv, FALSE, TRUE) == OK); - if (n) { - /* handle d.key, l[idx], f(expr) */ - n = (handle_subscript(&p, &tv, TRUE, FALSE) == OK); - if (n) - clear_tv(&tv); - } - } - if (*p != NUL) - n = FALSE; - - free(tofree); - } - - rettv->vval.v_number = n; -} - -/* - * "exp()" function - */ -static void f_exp(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = exp(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "expand()" function - */ -static void f_expand(typval_T *argvars, typval_T *rettv) -{ - char_u *s; - int len; - char_u *errormsg; - int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; - expand_T xpc; - int error = FALSE; - char_u *result; - - rettv->v_type = VAR_STRING; - if (argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error) - && !error) { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - } - - s = get_tv_string(&argvars[0]); - if (*s == '%' || *s == '#' || *s == '<') { - ++emsg_off; - result = eval_vars(s, s, &len, NULL, &errormsg, NULL); - --emsg_off; - if (rettv->v_type == VAR_LIST) { - rettv_list_alloc(rettv); - if (result != NULL) { - list_append_string(rettv->vval.v_list, result, -1); - } - } else - rettv->vval.v_string = result; - } else { - /* When the optional second argument is non-zero, don't remove matches - * for 'wildignore' and don't put matches for 'suffixes' at the end. */ - if (argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error)) - options |= WILD_KEEP_ALL; - if (!error) { - ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; - if (p_wic) - options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, - options, WILD_ALL); - else { - rettv_list_alloc(rettv); - ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); - for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); - } - ExpandCleanup(&xpc); - } - } else - rettv->vval.v_string = NULL; - } -} - -/* - * Go over all entries in "d2" and add them to "d1". - * When "action" is "error" then a duplicate key is an error. - * When "action" is "force" then a duplicate key is overwritten. - * Otherwise duplicate keys are ignored ("action" is "keep"). - */ -void dict_extend(dict_T *d1, dict_T *d2, char_u *action) -{ - dictitem_T *di1; - hashitem_T *hi2; - int todo; - - todo = (int)d2->dv_hashtab.ht_used; - for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { - if (!HASHITEM_EMPTY(hi2)) { - --todo; - di1 = dict_find(d1, hi2->hi_key, -1); - if (d1->dv_scope != 0) { - /* Disallow replacing a builtin function in l: and g:. - * Check the key to be valid when adding to any - * scope. */ - if (d1->dv_scope == VAR_DEF_SCOPE - && HI2DI(hi2)->di_tv.v_type == VAR_FUNC - && var_check_func_name(hi2->hi_key, - di1 == NULL)) - break; - if (!valid_varname(hi2->hi_key)) - break; - } - if (di1 == NULL) { - di1 = dictitem_copy(HI2DI(hi2)); - if (di1 != NULL && dict_add(d1, di1) == FAIL) - dictitem_free(di1); - } else if (*action == 'e') { - EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); - break; - } else if (*action == 'f' && HI2DI(hi2) != di1) { - clear_tv(&di1->di_tv); - copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); - } - } - } -} - -/* - * "extend(list, list [, idx])" function - * "extend(dict, dict [, action])" function - */ -static void f_extend(typval_T *argvars, typval_T *rettv) -{ - char *arg_errmsg = N_("extend() argument"); - - if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { - list_T *l1, *l2; - listitem_T *item; - long before; - int error = FALSE; - - l1 = argvars[0].vval.v_list; - l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, (char_u *)_(arg_errmsg)) - && l2 != NULL) { - if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ - - if (before == l1->lv_len) - item = NULL; - else { - item = list_find(l1, before); - if (item == NULL) { - EMSGN(_(e_listidx), before); - return; - } - } - } else - item = NULL; - list_extend(l1, l2, item); - - copy_tv(&argvars[0], rettv); - } - } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == - VAR_DICT) { - dict_T *d1, *d2; - char_u *action; - int i; - - d1 = argvars[0].vval.v_dict; - d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, (char_u *)_(arg_errmsg)) - && d2 != NULL) { - /* Check the third argument. */ - if (argvars[2].v_type != VAR_UNKNOWN) { - static char *(av[]) = {"keep", "force", "error"}; - - action = get_tv_string_chk(&argvars[2]); - if (action == NULL) - return; /* type error; errmsg already given */ - for (i = 0; i < 3; ++i) - if (STRCMP(action, av[i]) == 0) - break; - if (i == 3) { - EMSG2(_(e_invarg2), action); - return; - } - } else - action = (char_u *)"force"; - - dict_extend(d1, d2, action); - - copy_tv(&argvars[0], rettv); - } - } else - EMSG2(_(e_listdictarg), "extend()"); -} - -/* - * "feedkeys()" function - */ -static void f_feedkeys(typval_T *argvars, typval_T *rettv) -{ - int remap = TRUE; - char_u *keys, *flags; - char_u nbuf[NUMBUFLEN]; - int typed = FALSE; - char_u *keys_esc; - - /* This is not allowed in the sandbox. If the commands would still be - * executed in the sandbox it would be OK, but it probably happens later, - * when "sandbox" is no longer set. */ - if (check_secure()) - return; - - keys = get_tv_string(&argvars[0]); - if (*keys != NUL) { - if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); - for (; *flags != NUL; ++flags) { - switch (*flags) { - case 'n': remap = FALSE; break; - case 'm': remap = TRUE; break; - case 't': typed = TRUE; break; - } - } - } - - /* Need to escape K_SPECIAL and CSI before putting the string in the - * typeahead buffer. */ - keys_esc = vim_strsave_escape_csi(keys); - if (keys_esc != NULL) { - ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), - typebuf.tb_len, !typed, FALSE); - free(keys_esc); - if (vgetc_busy) - typebuf_was_filled = TRUE; - } - } -} - -/* - * "filereadable()" function - */ -static void f_filereadable(typval_T *argvars, typval_T *rettv) -{ - int fd; - char_u *p; - int n; - -#ifndef O_NONBLOCK -# define O_NONBLOCK 0 -#endif - p = get_tv_string(&argvars[0]); - if (*p && !os_isdir(p) && (fd = mch_open((char *)p, - O_RDONLY | O_NONBLOCK, 0)) >= 0) { - n = TRUE; - close(fd); - } else - n = FALSE; - - rettv->vval.v_number = n; -} - -/* - * Return 0 for not writable, 1 for writable file, 2 for a dir which we have - * rights to write into. - */ -static void f_filewritable(typval_T *argvars, typval_T *rettv) -{ - char *filename = (char *)get_tv_string(&argvars[0]); - rettv->vval.v_number = os_file_is_writable(filename); -} - -static void findfilendir(typval_T *argvars, typval_T *rettv, - int find_what); - -static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) -{ - char_u *fname; - char_u *fresult = NULL; - char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; - char_u *p; - char_u pathbuf[NUMBUFLEN]; - int count = 1; - int first = TRUE; - int error = FALSE; - - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; - - fname = get_tv_string(&argvars[0]); - - if (argvars[1].v_type != VAR_UNKNOWN) { - p = get_tv_string_buf_chk(&argvars[1], pathbuf); - if (p == NULL) - error = TRUE; - else { - if (*p != NUL) - path = p; - - if (argvars[2].v_type != VAR_UNKNOWN) - count = get_tv_number_chk(&argvars[2], &error); - } - } - - if (count < 0) { - rettv_list_alloc(rettv); - } - - if (*fname != NUL && !error) { - do { - if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) - free(fresult); - fresult = find_file_in_path_option(first ? fname : NULL, - first ? (int)STRLEN(fname) : 0, - 0, first, path, - find_what, - curbuf->b_ffname, - find_what == FINDFILE_DIR - ? (char_u *)"" : curbuf->b_p_sua); - first = FALSE; - - if (fresult != NULL && rettv->v_type == VAR_LIST) - list_append_string(rettv->vval.v_list, fresult, -1); - - } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); - } - - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = fresult; -} - -static void filter_map(typval_T *argvars, typval_T *rettv, int map); -static int filter_map_one(typval_T *tv, char_u *expr, int map, int *remp); - -/* - * Implementation of map() and filter(). - */ -static void filter_map(typval_T *argvars, typval_T *rettv, int map) -{ - char_u buf[NUMBUFLEN]; - char_u *expr; - listitem_T *li, *nli; - list_T *l = NULL; - dictitem_T *di; - hashtab_T *ht; - hashitem_T *hi; - dict_T *d = NULL; - typval_T save_val; - typval_T save_key; - int rem; - int todo; - char_u *ermsg = (char_u *)(map ? "map()" : "filter()"); - char *arg_errmsg = (map ? N_("map() argument") - : N_("filter() argument")); - int save_did_emsg; - int idx = 0; - - if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) == NULL - || tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) - return; - } else if (argvars[0].v_type == VAR_DICT) { - if ((d = argvars[0].vval.v_dict) == NULL - || tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) - return; - } else { - EMSG2(_(e_listdictarg), ermsg); - return; - } - - expr = get_tv_string_buf_chk(&argvars[1], buf); - /* On type errors, the preceding call has already displayed an error - * message. Avoid a misleading error message for an empty string that - * was not passed as argument. */ - if (expr != NULL) { - prepare_vimvar(VV_VAL, &save_val); - expr = skipwhite(expr); - - /* We reset "did_emsg" to be able to detect whether an error - * occurred during evaluation of the expression. */ - save_did_emsg = did_emsg; - did_emsg = FALSE; - - prepare_vimvar(VV_KEY, &save_key); - if (argvars[0].v_type == VAR_DICT) { - vimvars[VV_KEY].vv_type = VAR_STRING; - - ht = &d->dv_hashtab; - hash_lock(ht); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - di = HI2DI(hi); - if (tv_check_lock(di->di_tv.v_lock, - (char_u *)_(arg_errmsg))) - break; - vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); - if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL - || did_emsg) - break; - if (!map && rem) - dictitem_remove(d, di); - clear_tv(&vimvars[VV_KEY].vv_tv); - } - } - hash_unlock(ht); - } else { - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - for (li = l->lv_first; li != NULL; li = nli) { - if (tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg))) - break; - nli = li->li_next; - vimvars[VV_KEY].vv_nr = idx; - if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL - || did_emsg) - break; - if (!map && rem) - listitem_remove(l, li); - ++idx; - } - } - - restore_vimvar(VV_KEY, &save_key); - restore_vimvar(VV_VAL, &save_val); - - did_emsg |= save_did_emsg; - } - - copy_tv(&argvars[0], rettv); -} - -static int filter_map_one(typval_T *tv, char_u *expr, int map, int *remp) -{ - typval_T rettv; - char_u *s; - int retval = FAIL; - - copy_tv(tv, &vimvars[VV_VAL].vv_tv); - s = expr; - if (eval1(&s, &rettv, TRUE) == FAIL) - goto theend; - if (*s != NUL) { /* check for trailing chars after expr */ - EMSG2(_(e_invexpr2), s); - goto theend; - } - if (map) { - /* map(): replace the list item value */ - clear_tv(tv); - rettv.v_lock = 0; - *tv = rettv; - } else { - int error = FALSE; - - /* filter(): when expr is zero remove the item */ - *remp = (get_tv_number_chk(&rettv, &error) == 0); - clear_tv(&rettv); - /* On type error, nothing has been removed; return FAIL to stop the - * loop. The error message was given by get_tv_number_chk(). */ - if (error) - goto theend; - } - retval = OK; -theend: - clear_tv(&vimvars[VV_VAL].vv_tv); - return retval; -} - -/* - * "filter()" function - */ -static void f_filter(typval_T *argvars, typval_T *rettv) -{ - filter_map(argvars, rettv, FALSE); -} - -/* - * "finddir({fname}[, {path}[, {count}]])" function - */ -static void f_finddir(typval_T *argvars, typval_T *rettv) -{ - findfilendir(argvars, rettv, FINDFILE_DIR); -} - -/* - * "findfile({fname}[, {path}[, {count}]])" function - */ -static void f_findfile(typval_T *argvars, typval_T *rettv) -{ - findfilendir(argvars, rettv, FINDFILE_FILE); -} - -/* - * "float2nr({float})" function - */ -static void f_float2nr(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - if (get_float_arg(argvars, &f) == OK) { - if (f < -0x7fffffff) - rettv->vval.v_number = -0x7fffffff; - else if (f > 0x7fffffff) - rettv->vval.v_number = 0x7fffffff; - else - rettv->vval.v_number = (varnumber_T)f; - } -} - -/* - * "floor({float})" function - */ -static void f_floor(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = floor(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "fmod()" function - */ -static void f_fmod(typval_T *argvars, typval_T *rettv) -{ - float_T fx, fy; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = fmod(fx, fy); - else - rettv->vval.v_float = 0.0; -} - -/* - * "fnameescape({string})" function - */ -static void f_fnameescape(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_string = vim_strsave_fnameescape( - get_tv_string(&argvars[0]), FALSE); - rettv->v_type = VAR_STRING; -} - -/* - * "fnamemodify({fname}, {mods})" function - */ -static void f_fnamemodify(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - char_u *mods; - int usedlen = 0; - int len; - char_u *fbuf = NULL; - char_u buf[NUMBUFLEN]; - - fname = get_tv_string_chk(&argvars[0]); - mods = get_tv_string_buf_chk(&argvars[1], buf); - if (fname == NULL || mods == NULL) - fname = NULL; - else { - len = (int)STRLEN(fname); - (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); - } - - rettv->v_type = VAR_STRING; - if (fname == NULL) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = vim_strnsave(fname, len); - free(fbuf); -} - -static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end); - -/* - * "foldclosed()" function - */ -static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) -{ - linenr_T lnum; - linenr_T first, last; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { - if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) { - if (end) - rettv->vval.v_number = (varnumber_T)last; - else - rettv->vval.v_number = (varnumber_T)first; - return; - } - } - rettv->vval.v_number = -1; -} - -/* - * "foldclosed()" function - */ -static void f_foldclosed(typval_T *argvars, typval_T *rettv) -{ - foldclosed_both(argvars, rettv, FALSE); -} - -/* - * "foldclosedend()" function - */ -static void f_foldclosedend(typval_T *argvars, typval_T *rettv) -{ - foldclosed_both(argvars, rettv, TRUE); -} - -/* - * "foldlevel()" function - */ -static void f_foldlevel(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - rettv->vval.v_number = foldLevel(lnum); -} - -/* - * "foldtext()" function - */ -static void f_foldtext(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - char_u *s; - char_u *r; - int len; - char *txt; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if ((linenr_T)vimvars[VV_FOLDSTART].vv_nr > 0 - && (linenr_T)vimvars[VV_FOLDEND].vv_nr - <= curbuf->b_ml.ml_line_count - && vimvars[VV_FOLDDASHES].vv_str != NULL) { - /* Find first non-empty line in the fold. */ - lnum = (linenr_T)vimvars[VV_FOLDSTART].vv_nr; - while (lnum < (linenr_T)vimvars[VV_FOLDEND].vv_nr) { - if (!linewhite(lnum)) - break; - ++lnum; - } - - /* Find interesting text in this line. */ - s = skipwhite(ml_get(lnum)); - /* skip C comment-start */ - if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) { - s = skipwhite(s + 2); - if (*skipwhite(s) == NUL - && lnum + 1 < (linenr_T)vimvars[VV_FOLDEND].vv_nr) { - s = skipwhite(ml_get(lnum + 1)); - if (*s == '*') - s = skipwhite(s + 1); - } - } - txt = _("+-%s%3ld lines: "); - r = alloc((unsigned)(STRLEN(txt) - + STRLEN(vimvars[VV_FOLDDASHES].vv_str) /* for %s */ - + 20 /* for %3ld */ - + STRLEN(s))); /* concatenated */ - if (r != NULL) { - sprintf((char *)r, txt, vimvars[VV_FOLDDASHES].vv_str, - (long)((linenr_T)vimvars[VV_FOLDEND].vv_nr - - (linenr_T)vimvars[VV_FOLDSTART].vv_nr + 1)); - len = (int)STRLEN(r); - STRCAT(r, s); - /* remove 'foldmarker' and 'commentstring' */ - foldtext_cleanup(r + len); - rettv->vval.v_string = r; - } - } -} - -/* - * "foldtextresult(lnum)" function - */ -static void f_foldtextresult(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - char_u *text; - char_u buf[51]; - foldinfo_T foldinfo; - int fold_count; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - lnum = get_tv_lnum(argvars); - /* treat illegal types and illegal string values for {lnum} the same */ - if (lnum < 0) - lnum = 0; - fold_count = foldedCount(curwin, lnum, &foldinfo); - if (fold_count > 0) { - text = get_foldtext(curwin, lnum, lnum + fold_count - 1, - &foldinfo, buf); - if (text == buf) - text = vim_strsave(text); - rettv->vval.v_string = text; - } -} - -/* - * "foreground()" function - */ -static void f_foreground(typval_T *argvars, typval_T *rettv) -{ -} - -/* - * "function()" function - */ -static void f_function(typval_T *argvars, typval_T *rettv) -{ - char_u *s; - - s = get_tv_string(&argvars[0]); - if (s == NULL || *s == NUL || VIM_ISDIGIT(*s)) - EMSG2(_(e_invarg2), s); - /* Don't check an autoload name for existence here. */ - else if (vim_strchr(s, AUTOLOAD_CHAR) == NULL && !function_exists(s)) - EMSG2(_("E700: Unknown function: %s"), s); - else { - if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "", 5) == 0) { - char sid_buf[25]; - int off = *s == 's' ? 2 : 5; - - /* Expand s: and into nr_, so that the function can - * also be called from another script. Using trans_function_name() - * would also work, but some plugins depend on the name being - * printable text. */ - sprintf(sid_buf, "%" PRId64 "_", (int64_t)current_SID); - rettv->vval.v_string = - alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1)); - if (rettv->vval.v_string != NULL) { - STRCPY(rettv->vval.v_string, sid_buf); - STRCAT(rettv->vval.v_string, s + off); - } - } else - rettv->vval.v_string = vim_strsave(s); - rettv->v_type = VAR_FUNC; - } -} - -/* - * "garbagecollect()" function - */ -static void f_garbagecollect(typval_T *argvars, typval_T *rettv) -{ - /* This is postponed until we are back at the toplevel, because we may be - * using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". */ - want_garbage_collect = TRUE; - - if (argvars[0].v_type != VAR_UNKNOWN && get_tv_number(&argvars[0]) == 1) - garbage_collect_at_exit = TRUE; -} - -/* - * "get()" function - */ -static void f_get(typval_T *argvars, typval_T *rettv) -{ - listitem_T *li; - list_T *l; - dictitem_T *di; - dict_T *d; - typval_T *tv = NULL; - - if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) != NULL) { - int error = FALSE; - - li = list_find(l, get_tv_number_chk(&argvars[1], &error)); - if (!error && li != NULL) - tv = &li->li_tv; - } - } else if (argvars[0].v_type == VAR_DICT) { - if ((d = argvars[0].vval.v_dict) != NULL) { - di = dict_find(d, get_tv_string(&argvars[1]), -1); - if (di != NULL) - tv = &di->di_tv; - } - } else - EMSG2(_(e_listdictarg), "get()"); - - if (tv == NULL) { - if (argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); - } else - copy_tv(tv, rettv); -} - -static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, - int retlist, - typval_T *rettv); - -/* - * Get line or list of lines from buffer "buf" into "rettv". - * Return a range (from start to end) of lines in rettv from the specified - * buffer. - * If 'retlist' is TRUE, then the lines are returned as a Vim List. - */ -static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) -{ - char_u *p; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (retlist) { - rettv_list_alloc(rettv); - } - - if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) - return; - - if (!retlist) { - if (start >= 1 && start <= buf->b_ml.ml_line_count) - p = ml_get_buf(buf, start, FALSE); - else - p = (char_u *)""; - rettv->vval.v_string = vim_strsave(p); - } else { - if (end < start) - return; - - if (start < 1) - start = 1; - if (end > buf->b_ml.ml_line_count) - end = buf->b_ml.ml_line_count; - while (start <= end) { - list_append_string( - rettv->vval.v_list, ml_get_buf(buf, start++, FALSE), -1); - } - } -} - -/* - * "getbufline()" function - */ -static void f_getbufline(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - linenr_T end; - buf_T *buf; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - --emsg_off; - - lnum = get_tv_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type == VAR_UNKNOWN) - end = lnum; - else - end = get_tv_lnum_buf(&argvars[2], buf); - - get_buffer_lines(buf, lnum, end, TRUE, rettv); -} - -/* - * "getbufvar()" function - */ -static void f_getbufvar(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - buf_T *save_curbuf; - char_u *varname; - dictitem_T *v; - int done = FALSE; - - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - varname = get_tv_string_chk(&argvars[1]); - ++emsg_off; - buf = get_buf_tv(&argvars[0], FALSE); - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (buf != NULL && varname != NULL) { - /* set curbuf to be our buf, temporarily */ - save_curbuf = curbuf; - curbuf = buf; - - if (*varname == '&') { /* buffer-local-option */ - if (get_option_tv(&varname, rettv, TRUE) == OK) - done = TRUE; - } else if (STRCMP(varname, "changedtick") == 0) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = curbuf->b_changedtick; - done = TRUE; - } else { - /* Look up the variable. */ - /* Let getbufvar({nr}, "") return the "b:" dictionary. */ - v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, - 'b', varname, FALSE); - if (v != NULL) { - copy_tv(&v->di_tv, rettv); - done = TRUE; - } - } - - /* restore previous notion of curbuf */ - curbuf = save_curbuf; - } - - if (!done && argvars[2].v_type != VAR_UNKNOWN) - /* use the default value */ - copy_tv(&argvars[2], rettv); - - --emsg_off; -} - -/* - * "getchar()" function - */ -static void f_getchar(typval_T *argvars, typval_T *rettv) -{ - varnumber_T n; - int error = FALSE; - - /* Position the cursor. Needed after a message that ends in a space. */ - windgoto(msg_row, msg_col); - - ++no_mapping; - ++allow_keys; - for (;; ) { - if (argvars[0].v_type == VAR_UNKNOWN) - /* getchar(): blocking wait. */ - n = safe_vgetc(); - else if (get_tv_number_chk(&argvars[0], &error) == 1) - /* getchar(1): only check if char avail */ - n = vpeekc(); - else if (error || vpeekc() == NUL) - /* illegal argument or getchar(0) and no char avail: return zero */ - n = 0; - else - /* getchar(0) and char avail: return char */ - n = safe_vgetc(); - if (n == K_IGNORE) - continue; - break; - } - --no_mapping; - --allow_keys; - - vimvars[VV_MOUSE_WIN].vv_nr = 0; - vimvars[VV_MOUSE_LNUM].vv_nr = 0; - vimvars[VV_MOUSE_COL].vv_nr = 0; - - rettv->vval.v_number = n; - if (IS_SPECIAL(n) || mod_mask != 0) { - char_u temp[10]; /* modifier: 3, mbyte-char: 6, NUL: 1 */ - int i = 0; - - /* Turn a special key into three bytes, plus modifier. */ - if (mod_mask != 0) { - temp[i++] = K_SPECIAL; - temp[i++] = KS_MODIFIER; - temp[i++] = mod_mask; - } - if (IS_SPECIAL(n)) { - temp[i++] = K_SPECIAL; - temp[i++] = K_SECOND(n); - temp[i++] = K_THIRD(n); - } else if (has_mbyte) - i += (*mb_char2bytes)(n, temp + i); - else - temp[i++] = n; - temp[i++] = NUL; - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(temp); - - if (is_mouse_key(n)) { - int row = mouse_row; - int col = mouse_col; - win_T *win; - linenr_T lnum; - win_T *wp; - int winnr = 1; - - if (row >= 0 && col >= 0) { - /* Find the window at the mouse coordinates and compute the - * text position. */ - win = mouse_find_win(&row, &col); - (void)mouse_comp_pos(win, &row, &col, &lnum); - for (wp = firstwin; wp != win; wp = wp->w_next) - ++winnr; - vimvars[VV_MOUSE_WIN].vv_nr = winnr; - vimvars[VV_MOUSE_LNUM].vv_nr = lnum; - vimvars[VV_MOUSE_COL].vv_nr = col + 1; - } - } - } -} - -/* - * "getcharmod()" function - */ -static void f_getcharmod(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = mod_mask; -} - -/* - * "getcmdline()" function - */ -static void f_getcmdline(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_cmdline_str(); -} - -/* - * "getcmdpos()" function - */ -static void f_getcmdpos(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = get_cmdline_pos() + 1; -} - -/* - * "getcmdtype()" function - */ -static void f_getcmdtype(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = alloc(2); - if (rettv->vval.v_string != NULL) { - rettv->vval.v_string[0] = get_cmdline_type(); - rettv->vval.v_string[1] = NUL; - } -} - -/* - * "getcwd()" function - */ -static void f_getcwd(typval_T *argvars, typval_T *rettv) -{ - char_u *cwd; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - cwd = alloc(MAXPATHL); - if (cwd != NULL) { - if (os_dirname(cwd, MAXPATHL) != FAIL) { - rettv->vval.v_string = vim_strsave(cwd); -#ifdef BACKSLASH_IN_FILENAME - if (rettv->vval.v_string != NULL) - slash_adjust(rettv->vval.v_string); -#endif - } - free(cwd); - } -} - -/* - * "getfontname()" function - */ -static void f_getfontname(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -} - -/* - * "getfperm({fname})" function - */ -static void f_getfperm(typval_T *argvars, typval_T *rettv) -{ - char_u *perm = NULL; - char_u flags[] = "rwx"; - - char_u *filename = get_tv_string(&argvars[0]); - int32_t file_perm = os_getperm(filename); - if (file_perm >= 0) { - perm = vim_strsave((char_u *)"---------"); - if (perm != NULL) { - for (int i = 0; i < 9; i++) { - if (file_perm & (1 << (8 - i))) - perm[i] = flags[i % 3]; - } - } - } - rettv->v_type = VAR_STRING; - rettv->vval.v_string = perm; -} - -/* - * "getfsize({fname})" function - */ -static void f_getfsize(typval_T *argvars, typval_T *rettv) -{ - char *fname = (char *)get_tv_string(&argvars[0]); - - rettv->v_type = VAR_NUMBER; - - off_t file_size; - if (os_get_file_size(fname, &file_size)) { - if (os_isdir((char_u *)fname)) - rettv->vval.v_number = 0; - else { - rettv->vval.v_number = (varnumber_T)file_size; - - /* non-perfect check for overflow */ - if ((off_t)rettv->vval.v_number != file_size) { - rettv->vval.v_number = -2; - } - } - } else { - rettv->vval.v_number = -1; - } -} - -/* - * "getftime({fname})" function - */ -static void f_getftime(typval_T *argvars, typval_T *rettv) -{ - char *fname = (char *)get_tv_string(&argvars[0]); - - FileInfo file_info; - if (os_get_file_info(fname, &file_info)) { - rettv->vval.v_number = (varnumber_T)file_info.stat.st_mtim.tv_sec; - } else { - rettv->vval.v_number = -1; - } -} - -/* - * "getftype({fname})" function - */ -static void f_getftype(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - char_u *type = NULL; - char *t; - - fname = get_tv_string(&argvars[0]); - - rettv->v_type = VAR_STRING; - FileInfo file_info; - if (os_get_file_info_link((char *)fname, &file_info)) { - uint64_t mode = file_info.stat.st_mode; -#ifdef S_ISREG - if (S_ISREG(mode)) - t = "file"; - else if (S_ISDIR(mode)) - t = "dir"; -# ifdef S_ISLNK - else if (S_ISLNK(mode)) - t = "link"; -# endif -# ifdef S_ISBLK - else if (S_ISBLK(mode)) - t = "bdev"; -# endif -# ifdef S_ISCHR - else if (S_ISCHR(mode)) - t = "cdev"; -# endif -# ifdef S_ISFIFO - else if (S_ISFIFO(mode)) - t = "fifo"; -# endif -# ifdef S_ISSOCK - else if (S_ISSOCK(mode)) - t = "fifo"; -# endif - else - t = "other"; -#else -# ifdef S_IFMT - switch (mode & S_IFMT) { - case S_IFREG: t = "file"; break; - case S_IFDIR: t = "dir"; break; -# ifdef S_IFLNK - case S_IFLNK: t = "link"; break; -# endif -# ifdef S_IFBLK - case S_IFBLK: t = "bdev"; break; -# endif -# ifdef S_IFCHR - case S_IFCHR: t = "cdev"; break; -# endif -# ifdef S_IFIFO - case S_IFIFO: t = "fifo"; break; -# endif -# ifdef S_IFSOCK - case S_IFSOCK: t = "socket"; break; -# endif - default: t = "other"; - } -# else - if (os_isdir(fname)) - t = "dir"; - else - t = "file"; -# endif -#endif - type = vim_strsave((char_u *)t); - } - rettv->vval.v_string = type; -} - -/* - * "getline(lnum, [end])" function - */ -static void f_getline(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - linenr_T end; - int retlist; - - lnum = get_tv_lnum(argvars); - if (argvars[1].v_type == VAR_UNKNOWN) { - end = 0; - retlist = FALSE; - } else { - end = get_tv_lnum(&argvars[1]); - retlist = TRUE; - } - - get_buffer_lines(curbuf, lnum, end, retlist, rettv); -} - -/* - * "getmatches()" function - */ -static void f_getmatches(typval_T *argvars, typval_T *rettv) -{ - matchitem_T *cur = curwin->w_match_head; - - rettv_list_alloc(rettv); - while (cur != NULL) { - dict_T *dict = dict_alloc(); - dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); - dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); - dict_add_nr_str(dict, "id", (long)cur->id, NULL); - list_append_dict(rettv->vval.v_list, dict); - cur = cur->next; - } -} - -/* - * "getpid()" function - */ -static void f_getpid(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = os_get_pid(); -} - -/* - * "getpos(string)" function - */ -static void f_getpos(typval_T *argvars, typval_T *rettv) -{ - pos_T *fp; - list_T *l; - int fnum = -1; - - rettv_list_alloc(rettv); - l = rettv->vval.v_list; - fp = var2fpos(&argvars[0], TRUE, &fnum); - list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); - list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0); - list_append_number(l, - (fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0); - list_append_number(l, - (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); -} - -/* - * "getqflist()" and "getloclist()" functions - */ -static void f_getqflist(typval_T *argvars, typval_T *rettv) -{ - rettv_list_alloc(rettv); - win_T *wp = NULL; - if (argvars[0].v_type != VAR_UNKNOWN) { /* getloclist() */ - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) { - return; - } - } - (void)get_errorlist(wp, rettv->vval.v_list); -} - -/* - * "getreg()" function - */ -static void f_getreg(typval_T *argvars, typval_T *rettv) -{ - char_u *strregname; - int regname; - int arg2 = FALSE; - int error = FALSE; - - if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); - error = strregname == NULL; - if (argvars[1].v_type != VAR_UNKNOWN) - arg2 = get_tv_number_chk(&argvars[1], &error); - } else - strregname = vimvars[VV_REG].vv_str; - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) - regname = '"'; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = error ? NULL : - get_reg_contents(regname, TRUE, arg2); -} - -/* - * "getregtype()" function - */ -static void f_getregtype(typval_T *argvars, typval_T *rettv) -{ - char_u *strregname; - int regname; - char_u buf[NUMBUFLEN + 2]; - long reglen = 0; - - if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); - if (strregname == NULL) { /* type error; errmsg already given */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - return; - } - } else - /* Default to v:register */ - strregname = vimvars[VV_REG].vv_str; - - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) - regname = '"'; - - buf[0] = NUL; - buf[1] = NUL; - switch (get_reg_type(regname, ®len)) { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; - case MBLOCK: - buf[0] = Ctrl_V; - sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1)); - break; - } - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); -} - -/* - * "gettabvar()" function - */ -static void f_gettabvar(typval_T *argvars, typval_T *rettv) -{ - tabpage_T *tp; - dictitem_T *v; - char_u *varname; - int done = FALSE; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - varname = get_tv_string_chk(&argvars[1]); - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - if (tp != NULL && varname != NULL) { - /* look up the variable */ - v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 0, varname, FALSE); - if (v != NULL) { - copy_tv(&v->di_tv, rettv); - done = TRUE; - } - } - - if (!done && argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); -} - -/* - * "gettabwinvar()" function - */ -static void f_gettabwinvar(typval_T *argvars, typval_T *rettv) -{ - getwinvar(argvars, rettv, 1); -} - -/* - * "getwinposx()" function - */ -static void f_getwinposx(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = -1; -} - -/* - * "getwinposy()" function - */ -static void f_getwinposy(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = -1; -} - -/* - * Find window specified by "vp" in tabpage "tp". - */ -static win_T * -find_win_by_nr ( - typval_T *vp, - tabpage_T *tp /* NULL for current tab page */ -) -{ - win_T *wp; - int nr; - - nr = get_tv_number_chk(vp, NULL); - - if (nr < 0) - return NULL; - if (nr == 0) - return curwin; - - for (wp = (tp == NULL || tp == curtab) ? firstwin : tp->tp_firstwin; - wp != NULL; wp = wp->w_next) - if (--nr <= 0) - break; - return wp; -} - -/* - * "getwinvar()" function - */ -static void f_getwinvar(typval_T *argvars, typval_T *rettv) -{ - getwinvar(argvars, rettv, 0); -} - -/* - * getwinvar() and gettabwinvar() - */ -static void -getwinvar ( - typval_T *argvars, - typval_T *rettv, - int off /* 1 for gettabwinvar() */ -) -{ - win_T *win, *oldcurwin; - char_u *varname; - dictitem_T *v; - tabpage_T *tp = NULL; - tabpage_T *oldtabpage = NULL; - int done = FALSE; - - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else - tp = curtab; - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - ++emsg_off; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (win != NULL && varname != NULL) { - /* Set curwin to be our win, temporarily. Also set the tabpage, - * otherwise the window is not valid. */ - switch_win(&oldcurwin, &oldtabpage, win, tp, TRUE); - - if (*varname == '&') { /* window-local-option */ - if (get_option_tv(&varname, rettv, 1) == OK) - done = TRUE; - } else { - /* Look up the variable. */ - /* Let getwinvar({nr}, "") return the "w:" dictionary. */ - v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, FALSE); - if (v != NULL) { - copy_tv(&v->di_tv, rettv); - done = TRUE; - } - } - - /* restore previous notion of curwin */ - restore_win(oldcurwin, oldtabpage, TRUE); - } - - if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) - /* use the default return value */ - copy_tv(&argvars[off + 2], rettv); - - --emsg_off; -} - -/* - * "glob()" function - */ -static void f_glob(typval_T *argvars, typval_T *rettv) -{ - int options = WILD_SILENT|WILD_USE_NL; - expand_T xpc; - int error = FALSE; - - /* When the optional second argument is non-zero, don't remove matches - * for 'wildignore' and don't put matches for 'suffixes' at the end. */ - rettv->v_type = VAR_STRING; - if (argvars[1].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[1], &error)) - options |= WILD_KEEP_ALL; - if (argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error)) { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - } - } - if (!error) { - ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; - if (p_wic) - options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), - NULL, options, WILD_ALL); - else { - rettv_list_alloc(rettv); - ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); - for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); - } - ExpandCleanup(&xpc); - } - } else - rettv->vval.v_string = NULL; -} - -/* - * "globpath()" function - */ -static void f_globpath(typval_T *argvars, typval_T *rettv) -{ - int flags = 0; - char_u buf1[NUMBUFLEN]; - char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); - int error = FALSE; - - /* When the optional second argument is non-zero, don't remove matches - * for 'wildignore' and don't put matches for 'suffixes' at the end. */ - if (argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error)) - flags |= WILD_KEEP_ALL; - rettv->v_type = VAR_STRING; - if (file == NULL || error) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = globpath(get_tv_string(&argvars[0]), file, - flags); -} - -/* - * "has()" function - */ -static void f_has(typval_T *argvars, typval_T *rettv) -{ - int i; - char_u *name; - int n = FALSE; - static char *(has_list[]) = - { -#ifdef UNIX - "unix", -#endif -#if defined(WIN64) || defined(_WIN64) - "win64", -#endif -#ifndef CASE_INSENSITIVE_FILENAME - "fname_case", -#endif -#ifdef HAVE_ACL - "acl", -#endif - "arabic", - "autocmd", -#if defined(SOME_BUILTIN_TCAPS) || defined(ALL_BUILTIN_TCAPS) - "builtin_terms", -# ifdef ALL_BUILTIN_TCAPS - "all_builtin_terms", -# endif -#endif -#if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \ - || defined(FEAT_GUI_W32) \ - || defined(FEAT_GUI_MOTIF)) - "browsefilter", -#endif - "byte_offset", - "cindent", - "cmdline_compl", - "cmdline_hist", - "comments", - "conceal", - "cryptv", - "cscope", - "cursorbind", - "cursorshape", -#ifdef DEBUG - "debug", -#endif - "dialog_con", - "diff", - "digraphs", - "eval", /* always present, of course! */ - "ex_extra", - "extra_search", - "farsi", - "file_in_path", - "filterpipe", - "find_in_path", - "float", - "folding", -#if defined(UNIX) - "fork", -#endif - "gettext", -#if defined(HAVE_ICONV_H) && defined(USE_ICONV) - "iconv", -#endif - "insert_expand", - "jumplist", - "keymap", - "langmap", -#ifdef FEAT_LIBCALL - "libcall", -#endif - "linebreak", - "lispindent", - "listcmds", - "localmap", - "menu", - "mksession", - "modify_fname", - "mouse", -#if defined(UNIX) - "mouse_dec", -# ifdef FEAT_MOUSE_JSB - "mouse_jsbterm", -# endif - "mouse_netterm", - "mouse_sgr", - "mouse_urxvt", - "mouse_xterm", -#endif - "multi_byte", - "multi_lang", - "path_extra", - "persistent_undo", - "postscript", - "printer", - "profile", - "reltime", - "quickfix", - "rightleft", - "scrollbind", - "showcmd", - "cmdline_info", - "signs", - "smartindent", -#ifdef STARTUPTIME - "startuptime", -#endif - "statusline", - "spell", - "syntax", -#if !defined(UNIX) - "system", -#endif - "tag_binary", - "tag_old_static", -#ifdef FEAT_TAG_ANYWHITE - "tag_any_white", -#endif -#ifdef TERMINFO - "terminfo", -#endif - "termresponse", - "textobjects", -#ifdef HAVE_TGETENT - "tgetent", -#endif - "title", - "user-commands", /* was accidentally included in 5.4 */ - "user_commands", - "viminfo", - "vertsplit", - "virtualedit", - "visual", - "visualextra", - "vreplace", - "wildignore", - "wildmenu", - "windows", - "winaltkeys", - "writebackup", - "neovim", - NULL - }; - - name = get_tv_string(&argvars[0]); - for (i = 0; has_list[i] != NULL; ++i) - if (STRICMP(name, has_list[i]) == 0) { - n = TRUE; - break; - } - - if (n == FALSE) { - if (STRNICMP(name, "patch", 5) == 0) { - if (name[5] == '-' - && STRLEN(name) > 11 - && vim_isdigit(name[6]) - && vim_isdigit(name[8]) - && vim_isdigit(name[10])) { - int major = atoi((char *)name + 6); - int minor = atoi((char *)name + 8); - - // Expect "patch-9.9.01234". - n = (major < VIM_VERSION_MAJOR - || (major == VIM_VERSION_MAJOR - && (minor < VIM_VERSION_MINOR - || (minor == VIM_VERSION_MINOR - && has_patch(atoi((char *)name + 10)))))); - } else { - n = has_patch(atoi((char *)name + 5)); - } - } else if (STRICMP(name, "vim_starting") == 0) { - n = (starting != 0); - } else if (STRICMP(name, "multi_byte_encoding") == 0) { - n = has_mbyte; -#if defined(USE_ICONV) && defined(DYNAMIC_ICONV) - } else if (STRICMP(name, "iconv") == 0) { - n = iconv_enabled(FALSE); -#endif - } else if (STRICMP(name, "syntax_items") == 0) { - n = syntax_present(curwin); - } - } - - rettv->vval.v_number = n; -} - -/* - * "has_key()" function - */ -static void f_has_key(typval_T *argvars, typval_T *rettv) -{ - if (argvars[0].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - if (argvars[0].vval.v_dict == NULL) - return; - - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - get_tv_string(&argvars[1]), -1) != NULL; -} - -/* - * "haslocaldir()" function - */ -static void f_haslocaldir(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = (curwin->w_localdir != NULL); -} - -/* - * "hasmapto()" function - */ -static void f_hasmapto(typval_T *argvars, typval_T *rettv) -{ - char_u *name; - char_u *mode; - char_u buf[NUMBUFLEN]; - int abbr = FALSE; - - name = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - mode = (char_u *)"nvo"; - else { - mode = get_tv_string_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) - abbr = get_tv_number(&argvars[2]); - } - - if (map_to_exists(name, mode, abbr)) - rettv->vval.v_number = TRUE; - else - rettv->vval.v_number = FALSE; -} - -/* - * "histadd()" function - */ -static void f_histadd(typval_T *argvars, typval_T *rettv) -{ - int histype; - char_u *str; - char_u buf[NUMBUFLEN]; - - rettv->vval.v_number = FALSE; - if (check_restricted() || check_secure()) - return; - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - histype = str != NULL ? get_histtype(str) : -1; - if (histype >= 0) { - str = get_tv_string_buf(&argvars[1], buf); - if (*str != NUL) { - init_history(); - add_to_history(histype, str, FALSE, NUL); - rettv->vval.v_number = TRUE; - return; - } - } -} - -/* - * "histdel()" function - */ -static void f_histdel(typval_T *argvars, typval_T *rettv) -{ - int n; - char_u buf[NUMBUFLEN]; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) - n = 0; - else if (argvars[1].v_type == VAR_UNKNOWN) - /* only one argument: clear entire history */ - n = clr_history(get_histtype(str)); - else if (argvars[1].v_type == VAR_NUMBER) - /* index given: remove that entry */ - n = del_history_idx(get_histtype(str), - (int)get_tv_number(&argvars[1])); - else - /* string given: remove all matching entries */ - n = del_history_entry(get_histtype(str), - get_tv_string_buf(&argvars[1], buf)); - rettv->vval.v_number = n; -} - -/* - * "histget()" function - */ -static void f_histget(typval_T *argvars, typval_T *rettv) -{ - int type; - int idx; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); /* NULL on type error */ - if (str == NULL) - rettv->vval.v_string = NULL; - else { - type = get_histtype(str); - if (argvars[1].v_type == VAR_UNKNOWN) - idx = get_history_idx(type); - else - idx = (int)get_tv_number_chk(&argvars[1], NULL); - /* -1 on type error */ - rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); - } - rettv->v_type = VAR_STRING; -} - -/* - * "histnr()" function - */ -static void f_histnr(typval_T *argvars, typval_T *rettv) -{ - int i; - - char_u *history = get_tv_string_chk(&argvars[0]); - - i = history == NULL ? HIST_CMD - 1 : get_histtype(history); - if (i >= HIST_CMD && i < HIST_COUNT) - i = get_history_idx(i); - else - i = -1; - rettv->vval.v_number = i; -} - -/* - * "highlightID(name)" function - */ -static void f_hlID(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0])); -} - -/* - * "highlight_exists()" function - */ -static void f_hlexists(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = highlight_exists(get_tv_string(&argvars[0])); -} - -/* - * "hostname()" function - */ -static void f_hostname(typval_T *argvars, typval_T *rettv) -{ - char hostname[256]; - - os_get_hostname(hostname, 256); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave((char_u *)hostname); -} - -/* - * iconv() function - */ -static void f_iconv(typval_T *argvars, typval_T *rettv) -{ - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *from, *to, *str; - vimconv_T vimconv; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - str = get_tv_string(&argvars[0]); - from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1))); - to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2))); - vimconv.vc_type = CONV_NONE; - convert_setup(&vimconv, from, to); - - /* If the encodings are equal, no conversion needed. */ - if (vimconv.vc_type == CONV_NONE) - rettv->vval.v_string = vim_strsave(str); - else - rettv->vval.v_string = string_convert(&vimconv, str, NULL); - - convert_setup(&vimconv, NULL, NULL); - free(from); - free(to); -} - -/* - * "indent()" function - */ -static void f_indent(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) - rettv->vval.v_number = get_indent_lnum(lnum); - else - rettv->vval.v_number = -1; -} - -/* - * "index()" function - */ -static void f_index(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *item; - long idx = 0; - int ic = FALSE; - - rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) { - EMSG(_(e_listreq)); - return; - } - l = argvars[0].vval.v_list; - if (l != NULL) { - item = l->lv_first; - if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; - - /* Start at specified item. Use the cached index that list_find() - * sets, so that a negative number also works. */ - item = list_find(l, get_tv_number_chk(&argvars[2], &error)); - idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) - ic = get_tv_number_chk(&argvars[3], &error); - if (error) - item = NULL; - } - - for (; item != NULL; item = item->li_next, ++idx) - if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE)) { - rettv->vval.v_number = idx; - break; - } - } -} - -static int inputsecret_flag = 0; - -static void get_user_input(typval_T *argvars, typval_T *rettv, - int inputdialog); - -/* - * This function is used by f_input() and f_inputdialog() functions. The third - * argument to f_input() specifies the type of completion to use at the - * prompt. The third argument to f_inputdialog() specifies the value to return - * when the user cancels the prompt. - */ -static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) -{ - char_u *prompt = get_tv_string_chk(&argvars[0]); - char_u *p = NULL; - int c; - char_u buf[NUMBUFLEN]; - int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; - int xp_type = EXPAND_NOTHING; - char_u *xp_arg = NULL; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - -#ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. */ - if (no_console_input()) - return; -#endif - - cmd_silent = FALSE; /* Want to see the prompt. */ - if (prompt != NULL) { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) - p = prompt; - else { - ++p; - c = *p; - *p = NUL; - msg_start(); - msg_clr_eos(); - msg_puts_attr(prompt, echo_attr); - msg_didout = FALSE; - msg_starthere(); - *p = c; - } - cmdline_row = msg_row; - - if (argvars[1].v_type != VAR_UNKNOWN) { - defstr = get_tv_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) - stuffReadbuffSpec(defstr); - - if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { - char_u *xp_name; - int xp_namelen; - long argt; - - /* input() with a third argument: completion */ - rettv->vval.v_string = NULL; - - xp_name = get_tv_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) - return; - - xp_namelen = (int)STRLEN(xp_name); - - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) - return; - } - } - - if (defstr != NULL) { - int save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, - xp_type, xp_arg); - ex_normal_busy = save_ex_normal_busy; - } - if (inputdialog && rettv->vval.v_string == NULL - && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(get_tv_string_buf( - &argvars[2], buf)); - - free(xp_arg); - - /* since the user typed this, no need to wait for return */ - need_wait_return = FALSE; - msg_didout = FALSE; - } - cmd_silent = cmd_silent_save; -} - -/* - * "input()" function - * Also handles inputsecret() when inputsecret is set. - */ -static void f_input(typval_T *argvars, typval_T *rettv) -{ - get_user_input(argvars, rettv, FALSE); -} - -/* - * "inputdialog()" function - */ -static void f_inputdialog(typval_T *argvars, typval_T *rettv) -{ - get_user_input(argvars, rettv, TRUE); -} - -/* - * "inputlist()" function - */ -static void f_inputlist(typval_T *argvars, typval_T *rettv) -{ - listitem_T *li; - int selected; - int mouse_used; - -#ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. */ - if (no_console_input()) - return; -#endif - if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) { - EMSG2(_(e_listarg), "inputlist()"); - return; - } - - msg_start(); - msg_row = Rows - 1; /* for when 'cmdheight' > 1 */ - lines_left = Rows; /* avoid more prompt */ - msg_scroll = TRUE; - msg_clr_eos(); - - for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts(get_tv_string(&li->li_tv)); - msg_putchar('\n'); - } - - /* Ask for choice. */ - selected = prompt_for_number(&mouse_used); - if (mouse_used) - selected -= lines_left; - - rettv->vval.v_number = selected; -} - - -static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL}; - -/* - * "inputrestore()" function - */ -static void f_inputrestore(typval_T *argvars, typval_T *rettv) -{ - if (ga_userinput.ga_len > 0) { - --ga_userinput.ga_len; - restore_typeahead((tasave_T *)(ga_userinput.ga_data) - + ga_userinput.ga_len); - /* default return is zero == OK */ - } else if (p_verbose > 1) { - verb_msg((char_u *)_("called inputrestore() more often than inputsave()")); - rettv->vval.v_number = 1; /* Failed */ - } -} - -/* - * "inputsave()" function - */ -static void f_inputsave(typval_T *argvars, typval_T *rettv) -{ - /* Add an entry to the stack of typeahead storage. */ - ga_grow(&ga_userinput, 1); - save_typeahead((tasave_T *)(ga_userinput.ga_data) - + ga_userinput.ga_len); - ++ga_userinput.ga_len; -} - -/* - * "inputsecret()" function - */ -static void f_inputsecret(typval_T *argvars, typval_T *rettv) -{ - ++cmdline_star; - ++inputsecret_flag; - f_input(argvars, rettv); - --cmdline_star; - --inputsecret_flag; -} - -/* - * "insert()" function - */ -static void f_insert(typval_T *argvars, typval_T *rettv) -{ - long before = 0; - listitem_T *item; - list_T *l; - int error = FALSE; - - if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listarg), "insert()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("insert() argument"))) { - if (argvars[2].v_type != VAR_UNKNOWN) - before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ - - if (before == l->lv_len) - item = NULL; - else { - item = list_find(l, before); - if (item == NULL) { - EMSGN(_(e_listidx), before); - l = NULL; - } - } - if (l != NULL) { - list_insert_tv(l, &argvars[1], item); - copy_tv(&argvars[0], rettv); - } - } -} - -/* - * "invert(expr)" function - */ -static void f_invert(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL); -} - -/* - * "isdirectory()" function - */ -static void f_isdirectory(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = os_isdir(get_tv_string(&argvars[0])); -} - -/* - * "islocked()" function - */ -static void f_islocked(typval_T *argvars, typval_T *rettv) -{ - lval_T lv; - char_u *end; - dictitem_T *di; - - rettv->vval.v_number = -1; - end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, FALSE, FALSE, - GLV_NO_AUTOLOAD, FNE_CHECK_START); - if (end != NULL && lv.ll_name != NULL) { - if (*end != NUL) - EMSG(_(e_trailing)); - else { - if (lv.ll_tv == NULL) { - if (check_changedtick(lv.ll_name)) - rettv->vval.v_number = 1; /* always locked */ - else { - di = find_var(lv.ll_name, NULL, TRUE); - if (di != NULL) { - /* Consider a variable locked when: - * 1. the variable itself is locked - * 2. the value of the variable is locked. - * 3. the List or Dict value is locked. - */ - rettv->vval.v_number = ((di->di_flags & DI_FLAGS_LOCK) - || tv_islocked(&di->di_tv)); - } - } - } else if (lv.ll_range) - EMSG(_("E786: Range not allowed")); - else if (lv.ll_newkey != NULL) - EMSG2(_(e_dictkey), lv.ll_newkey); - else if (lv.ll_list != NULL) - /* List item. */ - rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv); - else - /* Dictionary item. */ - rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv); - } - } - - clear_lval(&lv); -} - -static void dict_list(typval_T *argvars, typval_T *rettv, int what); - -/* - * Turn a dict into a list: - * "what" == 0: list of keys - * "what" == 1: list of values - * "what" == 2: list of items - */ -static void dict_list(typval_T *argvars, typval_T *rettv, int what) -{ - list_T *l2; - dictitem_T *di; - hashitem_T *hi; - listitem_T *li; - listitem_T *li2; - dict_T *d; - int todo; - - if (argvars[0].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - if ((d = argvars[0].vval.v_dict) == NULL) - return; - - rettv_list_alloc(rettv); - - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - di = HI2DI(hi); - - li = listitem_alloc(); - if (li == NULL) - break; - list_append(rettv->vval.v_list, li); - - if (what == 0) { - /* keys() */ - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - /* values() */ - copy_tv(&di->di_tv, &li->li_tv); - } else { - /* items() */ - l2 = list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - if (l2 == NULL) - break; - ++l2->lv_refcount; - - li2 = listitem_alloc(); - if (li2 == NULL) - break; - list_append(l2, li2); - li2->li_tv.v_type = VAR_STRING; - li2->li_tv.v_lock = 0; - li2->li_tv.vval.v_string = vim_strsave(di->di_key); - - li2 = listitem_alloc(); - if (li2 == NULL) - break; - list_append(l2, li2); - copy_tv(&di->di_tv, &li2->li_tv); - } - } - } -} - -/* - * "items(dict)" function - */ -static void f_items(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 2); -} - -// "jobstart()" function -static void f_job_start(typval_T *argvars, typval_T *rettv) -{ - list_T *args = NULL; - listitem_T *arg; - int i, argvl, argsl; - char **argv = NULL; - - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_STRING - || argvars[1].v_type != VAR_STRING - || (argvars[2].v_type != VAR_LIST - && argvars[2].v_type != VAR_UNKNOWN)) { - // Wrong argument types - EMSG(_(e_invarg)); - return; - } - - argsl = 0; - if (argvars[2].v_type == VAR_LIST) { - args = argvars[2].vval.v_list; - argsl = args->lv_len; - // Assert that all list items are strings - for (arg = args->lv_first; arg != NULL; arg = arg->li_next) { - if (arg->li_tv.v_type != VAR_STRING) { - EMSG(_(e_invarg)); - return; - } - } - } - - if (!os_can_exe(get_tv_string(&argvars[1]))) { - // String is not executable - EMSG2(e_jobexe, get_tv_string(&argvars[1])); - return; - } - - // Allocate extra memory for the argument vector and the NULL pointer - argvl = argsl + 2; - argv = xmalloc(sizeof(char_u *) * argvl); - - // Copy program name - argv[0] = xstrdup((char *)argvars[1].vval.v_string); - - i = 1; - // Copy arguments to the vector - if (argsl > 0) { - for (arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup((char *)arg->li_tv.vval.v_string); - } - } - - // The last item of argv must be NULL - argv[i] = NULL; - - rettv->vval.v_number = job_start(argv, - xstrdup((char *)argvars[0].vval.v_string), - on_job_stdout, - on_job_stderr, - on_job_exit); - - if (rettv->vval.v_number <= 0) { - if (rettv->vval.v_number == 0) { - EMSG(_(e_jobtblfull)); - } else { - EMSG(_(e_jobexe)); - } - } -} - -// "jobstop()" function -static void f_job_stop(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER) { - // Only argument is the job id - EMSG(_(e_invarg)); - return; - } - - if (!job_stop(argvars[0].vval.v_number)) { - // Probably an invalid job id - EMSG(_(e_invjob)); - return; - } - - rettv->vval.v_number = 1; -} - -// "jobwrite()" function -static void f_job_write(typval_T *argvars, typval_T *rettv) -{ - bool res; - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type != VAR_STRING) { - // First argument is the job id and second is the string to write to - // the job's stdin - EMSG(_(e_invarg)); - return; - } - - res = job_write(argvars[0].vval.v_number, - xstrdup((char *)argvars[1].vval.v_string), - strlen((char *)argvars[1].vval.v_string)); - - if (!res) { - // Invalid job id - EMSG(_(e_invjob)); - } - - rettv->vval.v_number = 1; -} - -/* - * "join()" function - */ -static void f_join(typval_T *argvars, typval_T *rettv) -{ - garray_T ga; - char_u *sep; - - if (argvars[0].v_type != VAR_LIST) { - EMSG(_(e_listreq)); - return; - } - if (argvars[0].vval.v_list == NULL) - return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = get_tv_string_chk(&argvars[1]); - - rettv->v_type = VAR_STRING; - - if (sep != NULL) { - ga_init(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, sep, TRUE, 0); - ga_append(&ga, NUL); - rettv->vval.v_string = (char_u *)ga.ga_data; - } else - rettv->vval.v_string = NULL; -} - -/* - * "keys()" function - */ -static void f_keys(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 0); -} - -/* - * "last_buffer_nr()" function. - */ -static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv) -{ - int n = 0; - buf_T *buf; - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (n < buf->b_fnum) - n = buf->b_fnum; - - rettv->vval.v_number = n; -} - -/* - * "len()" function - */ -static void f_len(typval_T *argvars, typval_T *rettv) -{ - switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_NUMBER: - rettv->vval.v_number = (varnumber_T)STRLEN( - get_tv_string(&argvars[0])); - break; - case VAR_LIST: - rettv->vval.v_number = list_len(argvars[0].vval.v_list); - break; - case VAR_DICT: - rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); - break; - default: - EMSG(_("E701: Invalid type for len()")); - break; - } -} - -static void libcall_common(typval_T *argvars, typval_T *rettv, int type); - -static void libcall_common(typval_T *argvars, typval_T *rettv, int type) -{ -#ifdef FEAT_LIBCALL - char_u *string_in; - char_u **string_result; - int nr_result; -#endif - - rettv->v_type = type; - if (type != VAR_NUMBER) - rettv->vval.v_string = NULL; - - if (check_restricted() || check_secure()) - return; - -#ifdef FEAT_LIBCALL - /* The first two args must be strings, otherwise its meaningless */ - if (argvars[0].v_type == VAR_STRING && argvars[1].v_type == VAR_STRING) { - string_in = NULL; - if (argvars[2].v_type == VAR_STRING) - string_in = argvars[2].vval.v_string; - if (type == VAR_NUMBER) - string_result = NULL; - else - string_result = &rettv->vval.v_string; - if (mch_libcall(argvars[0].vval.v_string, - argvars[1].vval.v_string, - string_in, - argvars[2].vval.v_number, - string_result, - &nr_result) == OK - && type == VAR_NUMBER) - rettv->vval.v_number = nr_result; - } -#endif -} - -/* - * "libcall()" function - */ -static void f_libcall(typval_T *argvars, typval_T *rettv) -{ - libcall_common(argvars, rettv, VAR_STRING); -} - -/* - * "libcallnr()" function - */ -static void f_libcallnr(typval_T *argvars, typval_T *rettv) -{ - libcall_common(argvars, rettv, VAR_NUMBER); -} - -/* - * "line(string)" function - */ -static void f_line(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum = 0; - pos_T *fp; - int fnum; - - fp = var2fpos(&argvars[0], TRUE, &fnum); - if (fp != NULL) - lnum = fp->lnum; - rettv->vval.v_number = lnum; -} - -/* - * "line2byte(lnum)" function - */ -static void f_line2byte(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL); - if (rettv->vval.v_number >= 0) - ++rettv->vval.v_number; -} - -/* - * "lispindent(lnum)" function - */ -static void f_lispindent(typval_T *argvars, typval_T *rettv) -{ - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = lnum; - rettv->vval.v_number = get_lisp_indent(); - curwin->w_cursor = pos; - } else - rettv->vval.v_number = -1; -} - -/* - * "localtime()" function - */ -static void f_localtime(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = (varnumber_T)time(NULL); -} - -static void get_maparg(typval_T *argvars, typval_T *rettv, int exact); - -static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) -{ - char_u *keys; - char_u *which; - char_u buf[NUMBUFLEN]; - char_u *keys_buf = NULL; - char_u *rhs; - int mode; - int abbr = FALSE; - int get_dict = FALSE; - mapblock_T *mp; - int buffer_local; - - /* return empty string for failure */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - keys = get_tv_string(&argvars[0]); - if (*keys == NUL) - return; - - if (argvars[1].v_type != VAR_UNKNOWN) { - which = get_tv_string_buf_chk(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); - if (argvars[3].v_type != VAR_UNKNOWN) - get_dict = get_tv_number(&argvars[3]); - } - } else - which = (char_u *)""; - if (which == NULL) - return; - - mode = get_map_mode(&which, 0); - - keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); - rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); - free(keys_buf); - - if (!get_dict) { - /* Return a string. */ - if (rhs != NULL) - rettv->vval.v_string = str2special_save(rhs, FALSE); - - } else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL) { - /* Return a dictionary. */ - char_u *lhs = str2special_save(mp->m_keys, TRUE); - char_u *mapmode = map_mode_to_chars(mp->m_mode); - dict_T *dict = rettv->vval.v_dict; - - dict_add_nr_str(dict, "lhs", 0L, lhs); - dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str); - dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L, NULL); - dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL); - dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL); - dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL); - dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL); - dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL); - dict_add_nr_str(dict, "mode", 0L, mapmode); - - free(lhs); - free(mapmode); - } -} - -/* - * "log()" function - */ -static void f_log(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "log10()" function - */ -static void f_log10(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log10(f); - else - rettv->vval.v_float = 0.0; -} - - -/* - * "map()" function - */ -static void f_map(typval_T *argvars, typval_T *rettv) -{ - filter_map(argvars, rettv, TRUE); -} - -/* - * "maparg()" function - */ -static void f_maparg(typval_T *argvars, typval_T *rettv) -{ - get_maparg(argvars, rettv, TRUE); -} - -/* - * "mapcheck()" function - */ -static void f_mapcheck(typval_T *argvars, typval_T *rettv) -{ - get_maparg(argvars, rettv, FALSE); -} - -static void find_some_match(typval_T *argvars, typval_T *rettv, int start); - -static void find_some_match(typval_T *argvars, typval_T *rettv, int type) -{ - char_u *str = NULL; - long len = 0; - char_u *expr = NULL; - char_u *pat; - regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; - char_u strbuf[NUMBUFLEN]; - char_u *save_cpo; - long start = 0; - long nth = 1; - colnr_T startcol = 0; - int match = 0; - list_T *l = NULL; - listitem_T *li = NULL; - long idx = 0; - char_u *tofree = NULL; - - /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ - save_cpo = p_cpo; - p_cpo = (char_u *)""; - - rettv->vval.v_number = -1; - if (type == 3) { - /* return empty list when there are no matches */ - rettv_list_alloc(rettv); - } else if (type == 2) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - } - - if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) == NULL) - goto theend; - li = l->lv_first; - } else { - expr = str = get_tv_string(&argvars[0]); - len = (long)STRLEN(str); - } - - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - goto theend; - - if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; - - start = get_tv_number_chk(&argvars[2], &error); - if (error) - goto theend; - if (l != NULL) { - li = list_find(l, start); - if (li == NULL) - goto theend; - idx = l->lv_idx; /* use the cached index */ - } else { - if (start < 0) - start = 0; - if (start > len) - goto theend; - /* When "count" argument is there ignore matches before "start", - * otherwise skip part of the string. Differs when pattern is "^" - * or "\<". */ - if (argvars[3].v_type != VAR_UNKNOWN) - startcol = start; - else { - str += start; - len -= start; - } - } - - if (argvars[3].v_type != VAR_UNKNOWN) - nth = get_tv_number_chk(&argvars[3], &error); - if (error) - goto theend; - } - - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - if (regmatch.regprog != NULL) { - regmatch.rm_ic = p_ic; - - for (;; ) { - if (l != NULL) { - if (li == NULL) { - match = FALSE; - break; - } - free(tofree); - str = echo_string(&li->li_tv, &tofree, strbuf, 0); - if (str == NULL) - break; - } - - match = vim_regexec_nl(®match, str, (colnr_T)startcol); - - if (match && --nth <= 0) - break; - if (l == NULL && !match) - break; - - /* Advance to just after the match. */ - if (l != NULL) { - li = li->li_next; - ++idx; - } else { - startcol = (colnr_T)(regmatch.startp[0] - + (*mb_ptr2len)(regmatch.startp[0]) - str); - if (startcol > (colnr_T)len || str + startcol <= regmatch.startp[0]) { - match = FALSE; - break; - } - } - } - - if (match) { - if (type == 3) { - int i; - - /* return list with matched string and submatches */ - for (i = 0; i < NSUBEXP; ++i) { - if (regmatch.endp[i] == NULL) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); - } else { - list_append_string(rettv->vval.v_list, - regmatch.startp[i], - (int)(regmatch.endp[i] - regmatch.startp[i])); - } - } - } else if (type == 2) { - /* return matched string */ - if (l != NULL) - copy_tv(&li->li_tv, rettv); - else - rettv->vval.v_string = vim_strnsave(regmatch.startp[0], - (int)(regmatch.endp[0] - regmatch.startp[0])); - } else if (l != NULL) - rettv->vval.v_number = idx; - else { - if (type != 0) - rettv->vval.v_number = - (varnumber_T)(regmatch.startp[0] - str); - else - rettv->vval.v_number = - (varnumber_T)(regmatch.endp[0] - str); - rettv->vval.v_number += (varnumber_T)(str - expr); - } - } - vim_regfree(regmatch.regprog); - } - -theend: - free(tofree); - p_cpo = save_cpo; -} - -/* - * "match()" function - */ -static void f_match(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 1); -} - -/* - * "matchadd()" function - */ -static void f_matchadd(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ - char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ - int prio = 10; /* default priority */ - int id = -1; - int error = FALSE; - - rettv->vval.v_number = -1; - - if (grp == NULL || pat == NULL) - return; - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) - id = get_tv_number_chk(&argvars[3], &error); - } - if (error == TRUE) - return; - if (id >= 1 && id <= 3) { - EMSGN("E798: ID is reserved for \":match\": %" PRId64, id); - return; - } - - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id); -} - -/* - * "matcharg()" function - */ -static void f_matcharg(typval_T *argvars, typval_T *rettv) -{ - rettv_list_alloc(rettv); - - int id = get_tv_number(&argvars[0]); - - if (id >= 1 && id <= 3) { - matchitem_T *m; - - if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { - list_append_string(rettv->vval.v_list, syn_id2name(m->hlg_id), -1); - list_append_string(rettv->vval.v_list, m->pattern, -1); - } else { - list_append_string(rettv->vval.v_list, NULL, -1); - list_append_string(rettv->vval.v_list, NULL, -1); - } - } -} - -/* - * "matchdelete()" function - */ -static void f_matchdelete(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = match_delete(curwin, - (int)get_tv_number(&argvars[0]), TRUE); -} - -/* - * "matchend()" function - */ -static void f_matchend(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 0); -} - -/* - * "matchlist()" function - */ -static void f_matchlist(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 3); -} - -/* - * "matchstr()" function - */ -static void f_matchstr(typval_T *argvars, typval_T *rettv) -{ - find_some_match(argvars, rettv, 2); -} - -static void max_min(typval_T *argvars, typval_T *rettv, int domax); - -static void max_min(typval_T *argvars, typval_T *rettv, int domax) -{ - long n = 0; - long i; - int error = FALSE; - - if (argvars[0].v_type == VAR_LIST) { - list_T *l; - listitem_T *li; - - l = argvars[0].vval.v_list; - if (l != NULL) { - li = l->lv_first; - if (li != NULL) { - n = get_tv_number_chk(&li->li_tv, &error); - for (;; ) { - li = li->li_next; - if (li == NULL) - break; - i = get_tv_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) - n = i; - } - } - } - } else if (argvars[0].v_type == VAR_DICT) { - dict_T *d; - int first = TRUE; - hashitem_T *hi; - int todo; - - d = argvars[0].vval.v_dict; - if (d != NULL) { - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error); - if (first) { - n = i; - first = FALSE; - } else if (domax ? i > n : i < n) - n = i; - } - } - } - } else - EMSG(_(e_listdictarg)); - rettv->vval.v_number = error ? 0 : n; -} - -/* - * "max()" function - */ -static void f_max(typval_T *argvars, typval_T *rettv) -{ - max_min(argvars, rettv, TRUE); -} - -/* - * "min()" function - */ -static void f_min(typval_T *argvars, typval_T *rettv) -{ - max_min(argvars, rettv, FALSE); -} - -static int mkdir_recurse(char_u *dir, int prot); - -/* - * Create the directory in which "dir" is located, and higher levels when - * needed. - */ -static int mkdir_recurse(char_u *dir, int prot) -{ - char_u *p; - char_u *updir; - int r = FAIL; - - /* Get end of directory name in "dir". - * We're done when it's "/" or "c:/". */ - p = path_tail_with_sep(dir); - if (p <= get_past_head(dir)) - return OK; - - /* If the directory exists we're done. Otherwise: create it.*/ - updir = vim_strnsave(dir, (int)(p - dir)); - if (updir == NULL) - return FAIL; - if (os_isdir(updir)) - r = OK; - else if (mkdir_recurse(updir, prot) == OK) - r = vim_mkdir_emsg(updir, prot); - free(updir); - return r; -} - -/* - * "mkdir()" function - */ -static void f_mkdir(typval_T *argvars, typval_T *rettv) -{ - char_u *dir; - char_u buf[NUMBUFLEN]; - int prot = 0755; - - rettv->vval.v_number = FAIL; - if (check_restricted() || check_secure()) - return; - - dir = get_tv_string_buf(&argvars[0], buf); - if (*dir == NUL) - rettv->vval.v_number = FAIL; - else { - if (*path_tail(dir) == NUL) - /* remove trailing slashes */ - *path_tail_with_sep(dir) = NUL; - - if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) - prot = get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) - mkdir_recurse(dir, prot); - } - rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot); - } -} - -/* - * "mode()" function - */ -static void f_mode(typval_T *argvars, typval_T *rettv) -{ - char_u buf[3]; - - buf[1] = NUL; - buf[2] = NUL; - - if (VIsual_active) { - if (VIsual_select) - buf[0] = VIsual_mode + 's' - 'v'; - else - buf[0] = VIsual_mode; - } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE - || State == CONFIRM) { - buf[0] = 'r'; - if (State == ASKMORE) - buf[1] = 'm'; - else if (State == CONFIRM) - buf[1] = '?'; - } else if (State == EXTERNCMD) - buf[0] = '!'; - else if (State & INSERT) { - if (State & VREPLACE_FLAG) { - buf[0] = 'R'; - buf[1] = 'v'; - } else if (State & REPLACE_FLAG) - buf[0] = 'R'; - else - buf[0] = 'i'; - } else if (State & CMDLINE) { - buf[0] = 'c'; - if (exmode_active) - buf[1] = 'v'; - } else if (exmode_active) { - buf[0] = 'c'; - buf[1] = 'e'; - } else { - buf[0] = 'n'; - if (finish_op) - buf[1] = 'o'; - } - - /* Clear out the minor mode when the argument is not a non-zero number or - * non-empty string. */ - if (!non_zero_arg(&argvars[0])) - buf[1] = NUL; - - rettv->vval.v_string = vim_strsave(buf); - rettv->v_type = VAR_STRING; -} - - -/* - * "nextnonblank()" function - */ -static void f_nextnonblank(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - for (lnum = get_tv_lnum(argvars);; ++lnum) { - if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) { - lnum = 0; - break; - } - if (*skipwhite(ml_get(lnum)) != NUL) - break; - } - rettv->vval.v_number = lnum; -} - -/* - * "nr2char()" function - */ -static void f_nr2char(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - - if (has_mbyte) { - int utf8 = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = get_tv_number_chk(&argvars[1], NULL); - if (utf8) - buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - else - buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - } else { - buf[0] = (char_u)get_tv_number(&argvars[0]); - buf[1] = NUL; - } - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); -} - -/* - * "or(expr, expr)" function - */ -static void f_or(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - | get_tv_number_chk(&argvars[1], NULL); -} - -/* - * "pathshorten()" function - */ -static void f_pathshorten(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - rettv->v_type = VAR_STRING; - p = get_tv_string_chk(&argvars[0]); - if (p == NULL) - rettv->vval.v_string = NULL; - else { - p = vim_strsave(p); - rettv->vval.v_string = p; - if (p != NULL) - shorten_dir(p); - } -} - -/* - * "pow()" function - */ -static void f_pow(typval_T *argvars, typval_T *rettv) -{ - float_T fx, fy; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) - rettv->vval.v_float = pow(fx, fy); - else - rettv->vval.v_float = 0.0; -} - -/* - * "prevnonblank()" function - */ -static void f_prevnonblank(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) - lnum = 0; - else - while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) - --lnum; - rettv->vval.v_number = lnum; -} - -/* This dummy va_list is here because: - * - passing a NULL pointer doesn't work when va_list isn't a pointer - * - locally in the function results in a "used before set" warning - * - using va_start() to initialize it gives "function with fixed args" error */ -static va_list ap; - -/* - * "printf()" function - */ -static void f_printf(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - { - char_u buf[NUMBUFLEN]; - int len; - char_u *s; - int saved_did_emsg = did_emsg; - char *fmt; - - /* Get the required length, allocate the buffer and do it for real. */ - did_emsg = FALSE; - fmt = (char *)get_tv_string_buf(&argvars[0], buf); - len = vim_vsnprintf(NULL, 0, fmt, ap, argvars + 1); - if (!did_emsg) { - s = alloc(len + 1); - if (s != NULL) { - rettv->vval.v_string = s; - (void)vim_vsnprintf((char *)s, len + 1, fmt, ap, argvars + 1); - } - } - did_emsg |= saved_did_emsg; - } -} - -/* - * "pumvisible()" function - */ -static void f_pumvisible(typval_T *argvars, typval_T *rettv) -{ - if (pum_visible()) - rettv->vval.v_number = 1; -} - - - -/* - * "range()" function - */ -static void f_range(typval_T *argvars, typval_T *rettv) -{ - long start; - long end; - long stride = 1; - long i; - int error = FALSE; - - start = get_tv_number_chk(&argvars[0], &error); - if (argvars[1].v_type == VAR_UNKNOWN) { - end = start - 1; - start = 0; - } else { - end = get_tv_number_chk(&argvars[1], &error); - if (argvars[2].v_type != VAR_UNKNOWN) - stride = get_tv_number_chk(&argvars[2], &error); - } - - if (error) - return; /* type error; errmsg already given */ - if (stride == 0) - EMSG(_("E726: Stride is zero")); - else if (stride > 0 ? end + 1 < start : end - 1 > start) - EMSG(_("E727: Start past end")); - else { - rettv_list_alloc(rettv); - for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { - list_append_number(rettv->vval.v_list, (varnumber_T)i); - } - } -} - -/* - * "readfile()" function - */ -static void f_readfile(typval_T *argvars, typval_T *rettv) -{ - int binary = FALSE; - int failed = FALSE; - char_u *fname; - FILE *fd; - char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ - int io_size = sizeof(buf); - int readlen; /* size of last fread() */ - char_u *prev = NULL; /* previously read bytes, if any */ - long prevlen = 0; /* length of data in prev */ - long prevsize = 0; /* size of prev buffer */ - long maxline = MAXLNUM; - long cnt = 0; - char_u *p; /* position in buf */ - char_u *start; /* start of current line */ - - if (argvars[1].v_type != VAR_UNKNOWN) { - if (STRCMP(get_tv_string(&argvars[1]), "b") == 0) - binary = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - maxline = get_tv_number(&argvars[2]); - } - - rettv_list_alloc(rettv); - - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[0]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) { - EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("") : fname); - return; - } - - while (cnt < maxline || maxline < 0) { - readlen = (int)fread(buf, 1, io_size, fd); - - /* This for loop processes what was read, but is also entered at end - * of file so that either: - * - an incomplete line gets written - * - a "binary" file gets an empty line at the end if it ends in a - * newline. */ - for (p = buf, start = buf; - p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); - ++p) { - if (*p == '\n' || readlen <= 0) { - listitem_T *li; - char_u *s = NULL; - long_u len = p - start; - - /* Finished a line. Remove CRs before NL. */ - if (readlen > 0 && !binary) { - while (len > 0 && start[len - 1] == '\r') - --len; - /* removal may cross back to the "prev" string */ - if (len == 0) - while (prevlen > 0 && prev[prevlen - 1] == '\r') - --prevlen; - } - if (prevlen == 0) - s = vim_strnsave(start, (int)len); - else { - /* Change "prev" buffer to be the right size. This way - * the bytes are only copied once, and very long lines are - * allocated only once. */ - if ((s = xrealloc(prev, prevlen + len + 1)) != NULL) { - memmove(s + prevlen, start, len); - s[prevlen + len] = NUL; - prev = NULL; /* the list will own the string */ - prevlen = prevsize = 0; - } - } - if (s == NULL) { - do_outofmem_msg((long_u) prevlen + len + 1); - failed = TRUE; - break; - } - - if ((li = listitem_alloc()) == NULL) { - free(s); - failed = TRUE; - break; - } - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - list_append(rettv->vval.v_list, li); - - start = p + 1; /* step over newline */ - if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) - break; - } else if (*p == NUL) - *p = '\n'; - /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this - * when finding the BF and check the previous two bytes. */ - else if (*p == 0xbf && enc_utf8 && !binary) { - /* Find the two bytes before the 0xbf. If p is at buf, or buf - * + 1, these may be in the "prev" string. */ - char_u back1 = p >= buf + 1 ? p[-1] - : prevlen >= 1 ? prev[prevlen - 1] : NUL; - char_u back2 = p >= buf + 2 ? p[-2] - : p == buf + 1 && prevlen >= 1 ? prev[prevlen - 1] - : prevlen >= 2 ? prev[prevlen - 2] : NUL; - - if (back2 == 0xef && back1 == 0xbb) { - char_u *dest = p - 2; - - /* Usually a BOM is at the beginning of a file, and so at - * the beginning of a line; then we can just step over it. - */ - if (start == dest) - start = p + 1; - else { - /* have to shuffle buf to close gap */ - int adjust_prevlen = 0; - - if (dest < buf) { - adjust_prevlen = (int)(buf - dest); /* must be 1 or 2 */ - dest = buf; - } - if (readlen > p - buf + 1) - memmove(dest, p + 1, readlen - (p - buf) - 1); - readlen -= 3 - adjust_prevlen; - prevlen -= adjust_prevlen; - p = dest - 1; - } - } - } - } /* for */ - - if (failed || (cnt >= maxline && maxline >= 0) || readlen <= 0) - break; - if (start < p) { - /* There's part of a line in buf, store it in "prev". */ - if (p - start + prevlen >= prevsize) { - /* need bigger "prev" buffer */ - char_u *newprev; - - /* A common use case is ordinary text files and "prev" gets a - * fragment of a line, so the first allocation is made - * small, to avoid repeatedly 'allocing' large and - * 'reallocing' small. */ - if (prevsize == 0) - prevsize = (long)(p - start); - else { - long grow50pc = (prevsize * 3) / 2; - long growmin = (long)((p - start) * 2 + prevlen); - prevsize = grow50pc > growmin ? grow50pc : growmin; - } - newprev = prev == NULL ? alloc(prevsize) - : xrealloc(prev, prevsize); - if (newprev == NULL) { - do_outofmem_msg((long_u)prevsize); - failed = TRUE; - break; - } - prev = newprev; - } - /* Add the line part to end of "prev". */ - memmove(prev + prevlen, start, p - start); - prevlen += (long)(p - start); - } - } /* while */ - - /* - * For a negative line count use only the lines at the end of the file, - * free the rest. - */ - if (!failed && maxline < 0) - while (cnt > -maxline) { - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - --cnt; - } - - if (failed) { - list_free(rettv->vval.v_list, TRUE); - /* readfile doc says an empty list is returned on error */ - rettv->vval.v_list = list_alloc(); - } - - free(prev); - fclose(fd); -} - -static int list2proftime(typval_T *arg, proftime_T *tm); - -/* - * Convert a List to proftime_T. - * Return FAIL when there is something wrong. - */ -static int list2proftime(arg, tm) -typval_T *arg; -proftime_T *tm; -{ - long n1, n2; - int error = FALSE; - - if (arg->v_type != VAR_LIST || arg->vval.v_list == NULL - || arg->vval.v_list->lv_len != 2) - return FAIL; - n1 = list_find_nr(arg->vval.v_list, 0L, &error); - n2 = list_find_nr(arg->vval.v_list, 1L, &error); - tm->tv_sec = n1; - tm->tv_usec = n2; - return error ? FAIL : OK; -} - -/* - * "reltime()" function - */ -static void f_reltime(typval_T *argvars, typval_T *rettv) -{ - proftime_T res; - proftime_T start; - - if (argvars[0].v_type == VAR_UNKNOWN) { - /* No arguments: get current time. */ - profile_start(&res); - } else if (argvars[1].v_type == VAR_UNKNOWN) { - if (list2proftime(&argvars[0], &res) == FAIL) - return; - profile_end(&res); - } else { - /* Two arguments: compute the difference. */ - if (list2proftime(&argvars[0], &start) == FAIL - || list2proftime(&argvars[1], &res) == FAIL) - return; - profile_sub(&res, &start); - } - - rettv_list_alloc(rettv); - long n1 = res.tv_sec; - long n2 = res.tv_usec; - list_append_number(rettv->vval.v_list, (varnumber_T)n1); - list_append_number(rettv->vval.v_list, (varnumber_T)n2); -} - -/* - * "reltimestr()" function - */ -static void f_reltimestr(typval_T *argvars, typval_T *rettv) -{ - proftime_T tm; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (list2proftime(&argvars[0], &tm) == OK) - rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm)); -} - - - -/* - * "remote_expr()" function - */ -static void f_remote_expr(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -} - -/* - * "remote_foreground()" function - */ -static void f_remote_foreground(typval_T *argvars, typval_T *rettv) -{ -} - -static void f_remote_peek(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = -1; -} - -static void f_remote_read(typval_T *argvars, typval_T *rettv) -{ - char_u *r = NULL; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = r; -} - -/* - * "remote_send()" function - */ -static void f_remote_send(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; -} - -/* - * "remove()" function - */ -static void f_remove(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *item, *item2; - listitem_T *li; - long idx; - long end; - char_u *key; - dict_T *d; - dictitem_T *di; - char *arg_errmsg = N_("remove() argument"); - - if (argvars[0].v_type == VAR_DICT) { - if (argvars[2].v_type != VAR_UNKNOWN) - EMSG2(_(e_toomanyarg), "remove()"); - else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))) { - key = get_tv_string_chk(&argvars[1]); - if (key != NULL) { - di = dict_find(d, key, -1); - if (di == NULL) - EMSG2(_(e_dictkey), key); - else { - *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - } - } - } - } else if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listdictarg), "remove()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))) { - int error = FALSE; - - idx = get_tv_number_chk(&argvars[1], &error); - if (error) - ; /* type error: do nothing, errmsg already given */ - else if ((item = list_find(l, idx)) == NULL) - EMSGN(_(e_listidx), idx); - else { - if (argvars[2].v_type == VAR_UNKNOWN) { - /* Remove one item, return its value. */ - list_remove(l, item, item); - *rettv = item->li_tv; - free(item); - } else { - /* Remove range of items, return list with values. */ - end = get_tv_number_chk(&argvars[2], &error); - if (error) - ; /* type error: do nothing */ - else if ((item2 = list_find(l, end)) == NULL) - EMSGN(_(e_listidx), end); - else { - int cnt = 0; - - for (li = item; li != NULL; li = li->li_next) { - ++cnt; - if (li == item2) - break; - } - if (li == NULL) /* didn't find "item2" after "item" */ - EMSG(_(e_invrange)); - else { - list_remove(l, item, item2); - rettv_list_alloc(rettv); - l = rettv->vval.v_list; - l->lv_first = item; - l->lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - l->lv_len = cnt; - } - } - } - } - } -} - -/* - * "rename({from}, {to})" function - */ -static void f_rename(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - - if (check_restricted() || check_secure()) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); -} - -/* - * "repeat()" function - */ -static void f_repeat(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - int n; - int slen; - int len; - char_u *r; - int i; - - n = get_tv_number(&argvars[1]); - if (argvars[0].v_type == VAR_LIST) { - rettv_list_alloc(rettv); - if (argvars[0].vval.v_list != NULL) { - while (n-- > 0) { - if (list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL) - == FAIL) { - break; - } - } - } - } else { - p = get_tv_string(&argvars[0]); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - slen = (int)STRLEN(p); - len = slen * n; - if (len <= 0) - return; - - r = alloc(len + 1); - if (r != NULL) { - for (i = 0; i < n; i++) - memmove(r + i * slen, p, (size_t)slen); - r[len] = NUL; - } - - rettv->vval.v_string = r; - } -} - -/* - * "resolve()" function - */ -static void f_resolve(typval_T *argvars, typval_T *rettv) -{ - char_u *p; -#ifdef HAVE_READLINK - char_u *buf = NULL; -#endif - - p = get_tv_string(&argvars[0]); -#ifdef FEAT_SHORTCUT - { - char_u *v = NULL; - - v = mch_resolve_shortcut(p); - if (v != NULL) - rettv->vval.v_string = v; - else - rettv->vval.v_string = vim_strsave(p); - } -#else -# ifdef HAVE_READLINK - { - char_u *cpy; - int len; - char_u *remain = NULL; - char_u *q; - int is_relative_to_current = FALSE; - int has_trailing_pathsep = FALSE; - int limit = 100; - - p = vim_strsave(p); - - if (p[0] == '.' && (vim_ispathsep(p[1]) - || (p[1] == '.' && (vim_ispathsep(p[2]))))) - is_relative_to_current = TRUE; - - len = STRLEN(p); - if (len > 0 && after_pathsep(p, p + len)) { - has_trailing_pathsep = TRUE; - p[len - 1] = NUL; /* the trailing slash breaks readlink() */ - } - - q = path_next_component(p); - if (*q != NUL) { - /* Separate the first path component in "p", and keep the - * remainder (beginning with the path separator). */ - remain = vim_strsave(q - 1); - q[-1] = NUL; - } - - buf = alloc(MAXPATHL + 1); - if (buf == NULL) - goto fail; - - for (;; ) { - for (;; ) { - len = readlink((char *)p, (char *)buf, MAXPATHL); - if (len <= 0) - break; - buf[len] = NUL; - - if (limit-- == 0) { - free(p); - free(remain); - EMSG(_("E655: Too many symbolic links (cycle?)")); - rettv->vval.v_string = NULL; - goto fail; - } - - /* Ensure that the result will have a trailing path separator - * if the argument has one. */ - if (remain == NULL && has_trailing_pathsep) - add_pathsep(buf); - - /* Separate the first path component in the link value and - * concatenate the remainders. */ - q = path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); - if (*q != NUL) { - if (remain == NULL) - remain = vim_strsave(q - 1); - else { - cpy = concat_str(q - 1, remain); - if (cpy != NULL) { - free(remain); - remain = cpy; - } - } - q[-1] = NUL; - } - - q = path_tail(p); - if (q > p && *q == NUL) { - /* Ignore trailing path separator. */ - q[-1] = NUL; - q = path_tail(p); - } - if (q > p && !path_is_absolute_path(buf)) { - /* symlink is relative to directory of argument */ - cpy = alloc((unsigned)(STRLEN(p) + STRLEN(buf) + 1)); - if (cpy != NULL) { - STRCPY(cpy, p); - STRCPY(path_tail(cpy), buf); - free(p); - p = cpy; - } - } else { - free(p); - p = vim_strsave(buf); - } - } - - if (remain == NULL) - break; - - /* Append the first path component of "remain" to "p". */ - q = path_next_component(remain + 1); - len = q - remain - (*q != NUL); - cpy = vim_strnsave(p, STRLEN(p) + len); - if (cpy != NULL) { - STRNCAT(cpy, remain, len); - free(p); - p = cpy; - } - /* Shorten "remain". */ - if (*q != NUL) - STRMOVE(remain, q - 1); - else { - free(remain); - remain = NULL; - } - } - - /* If the result is a relative path name, make it explicitly relative to - * the current directory if and only if the argument had this form. */ - if (!vim_ispathsep(*p)) { - if (is_relative_to_current - && *p != NUL - && !(p[0] == '.' - && (p[1] == NUL - || vim_ispathsep(p[1]) - || (p[1] == '.' - && (p[2] == NUL - || vim_ispathsep(p[2])))))) { - /* Prepend "./". */ - cpy = concat_str((char_u *)"./", p); - if (cpy != NULL) { - free(p); - p = cpy; - } - } else if (!is_relative_to_current) { - /* Strip leading "./". */ - q = p; - while (q[0] == '.' && vim_ispathsep(q[1])) - q += 2; - if (q > p) - STRMOVE(p, p + 2); - } - } - - /* Ensure that the result will have no trailing path separator - * if the argument had none. But keep "/" or "//". */ - if (!has_trailing_pathsep) { - q = p + STRLEN(p); - if (after_pathsep(p, q)) - *path_tail_with_sep(p) = NUL; - } - - rettv->vval.v_string = p; - } -# else - rettv->vval.v_string = vim_strsave(p); -# endif -#endif - - simplify_filename(rettv->vval.v_string); - -#ifdef HAVE_READLINK -fail: - free(buf); -#endif - rettv->v_type = VAR_STRING; -} - -/* - * "reverse({list})" function - */ -static void f_reverse(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *li, *ni; - - if (argvars[0].v_type != VAR_LIST) - EMSG2(_(e_listarg), "reverse()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, (char_u *)_("reverse() argument"))) { - li = l->lv_last; - l->lv_first = l->lv_last = NULL; - l->lv_len = 0; - while (li != NULL) { - ni = li->li_prev; - list_append(l, li); - li = ni; - } - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - ++l->lv_refcount; - l->lv_idx = l->lv_len - l->lv_idx - 1; - } -} - -#define SP_NOMOVE 0x01 /* don't move cursor */ -#define SP_REPEAT 0x02 /* repeat to find outer pair */ -#define SP_RETCOUNT 0x04 /* return matchcount */ -#define SP_SETPCMARK 0x08 /* set previous context mark */ -#define SP_START 0x10 /* accept match at start position */ -#define SP_SUBPAT 0x20 /* return nr of matching sub-pattern */ -#define SP_END 0x40 /* leave cursor at end of match */ - -static int get_search_arg(typval_T *varp, int *flagsp); - -/* - * Get flags for a search function. - * Possibly sets "p_ws". - * Returns BACKWARD, FORWARD or zero (for an error). - */ -static int get_search_arg(typval_T *varp, int *flagsp) -{ - int dir = FORWARD; - char_u *flags; - char_u nbuf[NUMBUFLEN]; - int mask; - - if (varp->v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf_chk(varp, nbuf); - if (flags == NULL) - return 0; /* type error; errmsg already given */ - while (*flags != NUL) { - switch (*flags) { - case 'b': dir = BACKWARD; break; - case 'w': p_ws = TRUE; break; - case 'W': p_ws = FALSE; break; - default: mask = 0; - if (flagsp != NULL) - switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; - } - if (mask == 0) { - EMSG2(_(e_invarg2), flags); - dir = 0; - } else - *flagsp |= mask; - } - if (dir == 0) - break; - ++flags; - } - } - return dir; -} - -/* - * Shared by search() and searchpos() functions - */ -static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) -{ - int flags; - char_u *pat; - pos_T pos; - pos_T save_cursor; - int save_p_ws = p_ws; - int dir; - int retval = 0; /* default: FAIL */ - long lnum_stop = 0; - proftime_T tm; - long time_limit = 0; - int options = SEARCH_KEEP; - int subpatnum; - - pat = get_tv_string(&argvars[0]); - dir = get_search_arg(&argvars[1], flagsp); /* may set p_ws */ - if (dir == 0) - goto theend; - flags = *flagsp; - if (flags & SP_START) - options |= SEARCH_START; - if (flags & SP_END) - options |= SEARCH_END; - - /* Optional arguments: line number to stop searching and timeout. */ - if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[2], NULL); - if (lnum_stop < 0) - goto theend; - if (argvars[3].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[3], NULL); - if (time_limit < 0) - goto theend; - } - } - - /* Set the time limit, if there is one. */ - profile_setlimit(time_limit, &tm); - - /* - * This function does not accept SP_REPEAT and SP_RETCOUNT flags. - * Check to make sure only those flags are set. - * Also, Only the SP_NOMOVE or the SP_SETPCMARK flag can be set. Both - * flags cannot be set. Check for that condition also. - */ - if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0) - || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); - goto theend; - } - - pos = save_cursor = curwin->w_cursor; - subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, (linenr_T)lnum_stop, &tm); - if (subpatnum != FAIL) { - if (flags & SP_SUBPAT) - retval = subpatnum; - else - retval = pos.lnum; - if (flags & SP_SETPCMARK) - setpcmark(); - curwin->w_cursor = pos; - if (match_pos != NULL) { - /* Store the match cursor position */ - match_pos->lnum = pos.lnum; - match_pos->col = pos.col + 1; - } - /* "/$" will put the cursor after the end of the line, may need to - * correct that here */ - check_cursor(); - } - - /* If 'n' flag is used: restore cursor position. */ - if (flags & SP_NOMOVE) - curwin->w_cursor = save_cursor; - else - curwin->w_set_curswant = TRUE; -theend: - p_ws = save_p_ws; - - return retval; -} - - -/* - * round() is not in C90, use ceil() or floor() instead. - */ -float_T vim_round(float_T f) -{ - return f > 0 ? floor(f + 0.5) : ceil(f - 0.5); -} - -/* - * "round({float})" function - */ -static void f_round(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = vim_round(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "screenattr()" function - */ -static void f_screenattr(typval_T *argvars, typval_T *rettv) -{ - int row; - int col; - int c; - - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; - if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) - c = -1; - else - c = ScreenAttrs[LineOffset[row] + col]; - rettv->vval.v_number = c; -} - -/* - * "screenchar()" function - */ -static void f_screenchar(typval_T *argvars, typval_T *rettv) -{ - int row; - int col; - int off; - int c; - - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; - if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) - c = -1; - else { - off = LineOffset[row] + col; - if (enc_utf8 && ScreenLinesUC[off] != 0) - c = ScreenLinesUC[off]; - else - c = ScreenLines[off]; - } - rettv->vval.v_number = c; -} - -/* - * "screencol()" function - * - * First column is 1 to be consistent with virtcol(). - */ -static void f_screencol(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = screen_screencol() + 1; -} - -/* - * "screenrow()" function - */ -static void f_screenrow(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = screen_screenrow() + 1; -} - -/* - * "search()" function - */ -static void f_search(typval_T *argvars, typval_T *rettv) -{ - int flags = 0; - - rettv->vval.v_number = search_cmn(argvars, NULL, &flags); -} - -/* - * "searchdecl()" function - */ -static void f_searchdecl(typval_T *argvars, typval_T *rettv) -{ - int locally = 1; - int thisblock = 0; - int error = FALSE; - char_u *name; - - rettv->vval.v_number = 1; /* default: FAIL */ - - name = get_tv_string_chk(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN) { - locally = get_tv_number_chk(&argvars[1], &error) == 0; - if (!error && argvars[2].v_type != VAR_UNKNOWN) - thisblock = get_tv_number_chk(&argvars[2], &error) != 0; - } - if (!error && name != NULL) - rettv->vval.v_number = find_decl(name, (int)STRLEN(name), - locally, thisblock, SEARCH_KEEP) == FAIL; -} - -/* - * Used by searchpair() and searchpairpos() - */ -static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) -{ - char_u *spat, *mpat, *epat; - char_u *skip; - int save_p_ws = p_ws; - int dir; - int flags = 0; - char_u nbuf1[NUMBUFLEN]; - char_u nbuf2[NUMBUFLEN]; - char_u nbuf3[NUMBUFLEN]; - int retval = 0; /* default: FAIL */ - long lnum_stop = 0; - long time_limit = 0; - - /* Get the three pattern arguments: start, middle, end. */ - spat = get_tv_string_chk(&argvars[0]); - mpat = get_tv_string_buf_chk(&argvars[1], nbuf1); - epat = get_tv_string_buf_chk(&argvars[2], nbuf2); - if (spat == NULL || mpat == NULL || epat == NULL) - goto theend; /* type error */ - - /* Handle the optional fourth argument: flags */ - dir = get_search_arg(&argvars[3], &flags); /* may set p_ws */ - if (dir == 0) - goto theend; - - /* Don't accept SP_END or SP_SUBPAT. - * Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. - */ - if ((flags & (SP_END | SP_SUBPAT)) != 0 - || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[3])); - goto theend; - } - - /* Using 'r' implies 'W', otherwise it doesn't work. */ - if (flags & SP_REPEAT) - p_ws = FALSE; - - /* Optional fifth argument: skip expression */ - if (argvars[3].v_type == VAR_UNKNOWN - || argvars[4].v_type == VAR_UNKNOWN) - skip = (char_u *)""; - else { - skip = get_tv_string_buf_chk(&argvars[4], nbuf3); - if (argvars[5].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[5], NULL); - if (lnum_stop < 0) - goto theend; - if (argvars[6].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[6], NULL); - if (time_limit < 0) - goto theend; - } - } - } - if (skip == NULL) - goto theend; /* type error */ - - retval = do_searchpair(spat, mpat, epat, dir, skip, flags, - match_pos, lnum_stop, time_limit); - -theend: - p_ws = save_p_ws; - - return retval; -} - -/* - * "searchpair()" function - */ -static void f_searchpair(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = searchpair_cmn(argvars, NULL); -} - -/* - * "searchpairpos()" function - */ -static void f_searchpairpos(typval_T *argvars, typval_T *rettv) -{ - pos_T match_pos; - int lnum = 0; - int col = 0; - - rettv_list_alloc(rettv); - - if (searchpair_cmn(argvars, &match_pos) > 0) { - lnum = match_pos.lnum; - col = match_pos.col; - } - - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); -} - -/* - * Search for a start/middle/end thing. - * Used by searchpair(), see its documentation for the details. - * Returns 0 or -1 for no match, - */ -long -do_searchpair ( - char_u *spat, /* start pattern */ - char_u *mpat, /* middle pattern */ - char_u *epat, /* end pattern */ - int dir, /* BACKWARD or FORWARD */ - char_u *skip, /* skip expression */ - int flags, /* SP_SETPCMARK and other SP_ values */ - pos_T *match_pos, - linenr_T lnum_stop, /* stop at this line if not zero */ - long time_limit /* stop after this many msec */ -) -{ - char_u *save_cpo; - char_u *pat, *pat2 = NULL, *pat3 = NULL; - long retval = 0; - pos_T pos; - pos_T firstpos; - pos_T foundpos; - pos_T save_cursor; - pos_T save_pos; - int n; - int r; - int nest = 1; - int err; - int options = SEARCH_KEEP; - proftime_T tm; - - /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ - save_cpo = p_cpo; - p_cpo = empty_option; - - /* Set the time limit, if there is one. */ - profile_setlimit(time_limit, &tm); - - /* Make two search patterns: start/end (pat2, for in nested pairs) and - * start/middle/end (pat3, for the top pair). */ - pat2 = alloc((unsigned)(STRLEN(spat) + STRLEN(epat) + 15)); - pat3 = alloc((unsigned)(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 23)); - if (pat2 == NULL || pat3 == NULL) - goto theend; - sprintf((char *)pat2, "\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); - if (*mpat == NUL) - STRCPY(pat3, pat2); - else - sprintf((char *)pat3, "\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", - spat, epat, mpat); - if (flags & SP_START) - options |= SEARCH_START; - - save_cursor = curwin->w_cursor; - pos = curwin->w_cursor; - clearpos(&firstpos); - clearpos(&foundpos); - pat = pat3; - for (;; ) { - n = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, lnum_stop, &tm); - if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) - /* didn't find it or found the first match again: FAIL */ - break; - - if (firstpos.lnum == 0) - firstpos = pos; - if (equalpos(pos, foundpos)) { - /* Found the same position again. Can happen with a pattern that - * has "\zs" at the end and searching backwards. Advance one - * character and try again. */ - if (dir == BACKWARD) - decl(&pos); - else - incl(&pos); - } - foundpos = pos; - - /* clear the start flag to avoid getting stuck here */ - options &= ~SEARCH_START; - - /* If the skip pattern matches, ignore this match. */ - if (*skip != NUL) { - save_pos = curwin->w_cursor; - curwin->w_cursor = pos; - r = eval_to_bool(skip, &err, NULL, FALSE); - curwin->w_cursor = save_pos; - if (err) { - /* Evaluating {skip} caused an error, break here. */ - curwin->w_cursor = save_cursor; - retval = -1; - break; - } - if (r) - continue; - } - - if ((dir == BACKWARD && n == 3) || (dir == FORWARD && n == 2)) { - /* Found end when searching backwards or start when searching - * forward: nested pair. */ - ++nest; - pat = pat2; /* nested, don't search for middle */ - } else { - /* Found end when searching forward or start when searching - * backward: end of (nested) pair; or found middle in outer pair. */ - if (--nest == 1) - pat = pat3; /* outer level, search for middle */ - } - - if (nest == 0) { - /* Found the match: return matchcount or line number. */ - if (flags & SP_RETCOUNT) - ++retval; - else - retval = pos.lnum; - if (flags & SP_SETPCMARK) - setpcmark(); - curwin->w_cursor = pos; - if (!(flags & SP_REPEAT)) - break; - nest = 1; /* search for next unmatched */ - } - } - - if (match_pos != NULL) { - /* Store the match cursor position */ - match_pos->lnum = curwin->w_cursor.lnum; - match_pos->col = curwin->w_cursor.col + 1; - } - - /* If 'n' flag is used or search failed: restore cursor position. */ - if ((flags & SP_NOMOVE) || retval == 0) - curwin->w_cursor = save_cursor; - -theend: - free(pat2); - free(pat3); - if (p_cpo == empty_option) - p_cpo = save_cpo; - else - /* Darn, evaluating the {skip} expression changed the value. */ - free_string_option(save_cpo); - - return retval; -} - -/* - * "searchpos()" function - */ -static void f_searchpos(typval_T *argvars, typval_T *rettv) -{ - pos_T match_pos; - int lnum = 0; - int col = 0; - int n; - int flags = 0; - - rettv_list_alloc(rettv); - - n = search_cmn(argvars, &match_pos, &flags); - if (n > 0) { - lnum = match_pos.lnum; - col = match_pos.col; - } - - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); - if (flags & SP_SUBPAT) - list_append_number(rettv->vval.v_list, (varnumber_T)n); -} - - -static void f_server2client(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = -1; -} - -static void f_serverlist(typval_T *argvars, typval_T *rettv) -{ - char_u *r = NULL; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = r; -} - -/* - * "setbufvar()" function - */ -static void f_setbufvar(typval_T *argvars, typval_T *rettv) -{ - buf_T *buf; - aco_save_T aco; - char_u *varname, *bufvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - - if (check_restricted() || check_secure()) - return; - (void)get_tv_number(&argvars[0]); /* issue errmsg if type error */ - varname = get_tv_string_chk(&argvars[1]); - buf = get_buf_tv(&argvars[0], FALSE); - varp = &argvars[2]; - - if (buf != NULL && varname != NULL && varp != NULL) { - /* set curbuf to be our buf, temporarily */ - aucmd_prepbuf(&aco, buf); - - if (*varname == '&') { - long numval; - char_u *strval; - int error = FALSE; - - ++varname; - numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) - set_option_value(varname, numval, strval, OPT_LOCAL); - } else { - bufvarname = alloc((unsigned)STRLEN(varname) + 3); - if (bufvarname != NULL) { - STRCPY(bufvarname, "b:"); - STRCPY(bufvarname + 2, varname); - set_var(bufvarname, varp, TRUE); - free(bufvarname); - } - } - - /* reset notion of buffer */ - aucmd_restbuf(&aco); - } -} - -/* - * "setcmdpos()" function - */ -static void f_setcmdpos(typval_T *argvars, typval_T *rettv) -{ - int pos = (int)get_tv_number(&argvars[0]) - 1; - - if (pos >= 0) - rettv->vval.v_number = set_cmdline_pos(pos); -} - -/* - * "setline()" function - */ -static void f_setline(typval_T *argvars, typval_T *rettv) -{ - linenr_T lnum; - char_u *line = NULL; - list_T *l = NULL; - listitem_T *li = NULL; - long added = 0; - linenr_T lcount = curbuf->b_ml.ml_line_count; - - lnum = get_tv_lnum(&argvars[0]); - if (argvars[1].v_type == VAR_LIST) { - l = argvars[1].vval.v_list; - li = l->lv_first; - } else - line = get_tv_string_chk(&argvars[1]); - - /* default result is zero == OK */ - for (;; ) { - if (l != NULL) { - /* list argument, get next string */ - if (li == NULL) - break; - line = get_tv_string_chk(&li->li_tv); - li = li->li_next; - } - - rettv->vval.v_number = 1; /* FAIL */ - if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) - break; - - /* When coming here from Insert mode, sync undo, so that this can be - * undone separately from what was previously inserted. */ - if (u_sync_once == 2) { - u_sync_once = 1; /* notify that u_sync() was called */ - u_sync(TRUE); - } - - if (lnum <= curbuf->b_ml.ml_line_count) { - /* existing line, replace it */ - if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) { - changed_bytes(lnum, 0); - if (lnum == curwin->w_cursor.lnum) - check_cursor_col(); - rettv->vval.v_number = 0; /* OK */ - } - } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { - /* lnum is one past the last line, append the line */ - ++added; - if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) - rettv->vval.v_number = 0; /* OK */ - } - - if (l == NULL) /* only one string argument */ - break; - ++lnum; - } - - if (added > 0) - appended_lines_mark(lcount, added); -} - -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, - typval_T *action_arg, - typval_T *rettv); - -/* - * Used by "setqflist()" and "setloclist()" functions - */ -static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv) -{ - char_u *act; - int action = ' '; - - rettv->vval.v_number = -1; - - if (list_arg->v_type != VAR_LIST) - EMSG(_(e_listreq)); - else { - list_T *l = list_arg->vval.v_list; - - if (action_arg->v_type == VAR_STRING) { - act = get_tv_string_chk(action_arg); - if (act == NULL) - return; /* type error; errmsg already given */ - if (*act == 'a' || *act == 'r') - action = *act; - } - - if (l != NULL && set_errorlist(wp, l, action, - (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK) - rettv->vval.v_number = 0; - } -} - -/* - * "setloclist()" function - */ -static void f_setloclist(typval_T *argvars, typval_T *rettv) -{ - win_T *win; - - rettv->vval.v_number = -1; - - win = find_win_by_nr(&argvars[0], NULL); - if (win != NULL) - set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); -} - -/* - * "setmatches()" function - */ -static void f_setmatches(typval_T *argvars, typval_T *rettv) -{ - list_T *l; - listitem_T *li; - dict_T *d; - - rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) { - EMSG(_(e_listreq)); - return; - } - if ((l = argvars[0].vval.v_list) != NULL) { - - /* To some extent make sure that we are dealing with a list from - * "getmatches()". */ - li = l->lv_first; - while (li != NULL) { - if (li->li_tv.v_type != VAR_DICT - || (d = li->li_tv.vval.v_dict) == NULL) { - EMSG(_(e_invarg)); - return; - } - if (!(dict_find(d, (char_u *)"group", -1) != NULL - && dict_find(d, (char_u *)"pattern", -1) != NULL - && dict_find(d, (char_u *)"priority", -1) != NULL - && dict_find(d, (char_u *)"id", -1) != NULL)) { - EMSG(_(e_invarg)); - return; - } - li = li->li_next; - } - - clear_matches(curwin); - li = l->lv_first; - while (li != NULL) { - d = li->li_tv.vval.v_dict; - match_add(curwin, get_dict_string(d, (char_u *)"group", FALSE), - get_dict_string(d, (char_u *)"pattern", FALSE), - (int)get_dict_number(d, (char_u *)"priority"), - (int)get_dict_number(d, (char_u *)"id")); - li = li->li_next; - } - rettv->vval.v_number = 0; - } -} - -/* - * "setpos()" function - */ -static void f_setpos(typval_T *argvars, typval_T *rettv) -{ - pos_T pos; - int fnum; - char_u *name; - - rettv->vval.v_number = -1; - name = get_tv_string_chk(argvars); - if (name != NULL) { - if (list2fpos(&argvars[1], &pos, &fnum) == OK) { - if (--pos.col < 0) - pos.col = 0; - if (name[0] == '.' && name[1] == NUL) { - /* set cursor */ - if (fnum == curbuf->b_fnum) { - curwin->w_cursor = pos; - check_cursor(); - rettv->vval.v_number = 0; - } else - EMSG(_(e_invarg)); - } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { - /* set mark */ - if (setmark_pos(name[1], &pos, fnum) == OK) - rettv->vval.v_number = 0; - } else - EMSG(_(e_invarg)); - } - } -} - -/* - * "setqflist()" function - */ -static void f_setqflist(typval_T *argvars, typval_T *rettv) -{ - set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv); -} - -/* - * "setreg()" function - */ -static void f_setreg(typval_T *argvars, typval_T *rettv) -{ - int regname; - char_u *strregname; - char_u *stropt; - char_u *strval; - int append; - char_u yank_type; - long block_len; - - block_len = -1; - yank_type = MAUTO; - append = FALSE; - - strregname = get_tv_string_chk(argvars); - rettv->vval.v_number = 1; /* FAIL is default */ - - if (strregname == NULL) - return; /* type error; errmsg already given */ - regname = *strregname; - if (regname == 0 || regname == '@') - regname = '"'; - else if (regname == '=') - return; - - if (argvars[2].v_type != VAR_UNKNOWN) { - stropt = get_tv_string_chk(&argvars[2]); - if (stropt == NULL) - return; /* type error */ - for (; *stropt != NUL; ++stropt) - switch (*stropt) { - case 'a': case 'A': /* append */ - append = TRUE; - break; - case 'v': case 'c': /* character-wise selection */ - yank_type = MCHAR; - break; - case 'V': case 'l': /* line-wise selection */ - yank_type = MLINE; - break; - case 'b': case Ctrl_V: /* block-wise selection */ - yank_type = MBLOCK; - if (VIM_ISDIGIT(stropt[1])) { - ++stropt; - block_len = getdigits(&stropt) - 1; - --stropt; - } - break; - } - } - - strval = get_tv_string_chk(&argvars[1]); - if (strval != NULL) - write_reg_contents_ex(regname, strval, -1, - append, yank_type, block_len); - rettv->vval.v_number = 0; -} - -/* - * "settabvar()" function - */ -static void f_settabvar(typval_T *argvars, typval_T *rettv) -{ - tabpage_T *save_curtab; - tabpage_T *tp; - char_u *varname, *tabvarname; - typval_T *varp; - - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) - return; - - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - varname = get_tv_string_chk(&argvars[1]); - varp = &argvars[2]; - - if (varname != NULL && varp != NULL - && tp != NULL - ) { - save_curtab = curtab; - goto_tabpage_tp(tp, FALSE, FALSE); - - tabvarname = alloc((unsigned)STRLEN(varname) + 3); - if (tabvarname != NULL) { - STRCPY(tabvarname, "t:"); - STRCPY(tabvarname + 2, varname); - set_var(tabvarname, varp, TRUE); - free(tabvarname); - } - - /* Restore current tabpage */ - if (valid_tabpage(save_curtab)) - goto_tabpage_tp(save_curtab, FALSE, FALSE); - } -} - -/* - * "settabwinvar()" function - */ -static void f_settabwinvar(typval_T *argvars, typval_T *rettv) -{ - setwinvar(argvars, rettv, 1); -} - -/* - * "setwinvar()" function - */ -static void f_setwinvar(typval_T *argvars, typval_T *rettv) -{ - setwinvar(argvars, rettv, 0); -} - -/* - * "setwinvar()" and "settabwinvar()" functions - */ - -static void setwinvar(typval_T *argvars, typval_T *rettv, int off) -{ - win_T *win; - win_T *save_curwin; - tabpage_T *save_curtab; - char_u *varname, *winvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - tabpage_T *tp = NULL; - - if (check_restricted() || check_secure()) - return; - - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else - tp = curtab; - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - varp = &argvars[off + 2]; - - if (win != NULL && varname != NULL && varp != NULL) { - if (switch_win(&save_curwin, &save_curtab, win, tp, TRUE) == FAIL) - return; - - if (*varname == '&') { - long numval; - char_u *strval; - int error = FALSE; - - ++varname; - numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); - if (!error && strval != NULL) - set_option_value(varname, numval, strval, OPT_LOCAL); - } else { - winvarname = alloc((unsigned)STRLEN(varname) + 3); - if (winvarname != NULL) { - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); - set_var(winvarname, varp, TRUE); - free(winvarname); - } - } - - restore_win(save_curwin, save_curtab, TRUE); - } -} - -/* - * "sha256({string})" function - */ -static void f_sha256(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - p = get_tv_string(&argvars[0]); - rettv->vval.v_string = vim_strsave( - sha256_bytes(p, (int)STRLEN(p), NULL, 0)); - rettv->v_type = VAR_STRING; -} - -/* - * "shellescape({string})" function - */ -static void f_shellescape(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_string = vim_strsave_shellescape( - get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), true); - rettv->v_type = VAR_STRING; -} - -/* - * shiftwidth() function - */ -static void f_shiftwidth(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = get_sw_value(curbuf); -} - -/* - * "simplify()" function - */ -static void f_simplify(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - p = get_tv_string(&argvars[0]); - rettv->vval.v_string = vim_strsave(p); - simplify_filename(rettv->vval.v_string); /* simplify in place */ - rettv->v_type = VAR_STRING; -} - -/* - * "sin()" function - */ -static void f_sin(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sin(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "sinh()" function - */ -static void f_sinh(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sinh(f); - else - rettv->vval.v_float = 0.0; -} - -static int -item_compare(const void *s1, const void *s2); -static int -item_compare2(const void *s1, const void *s2); - -static int item_compare_ic; -static char_u *item_compare_func; -static dict_T *item_compare_selfdict; -static int item_compare_func_err; -static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort); -#define ITEM_COMPARE_FAIL 999 - -/* - * Compare functions for f_sort() and f_uniq() below. - */ -static int item_compare(const void *s1, const void *s2) -{ - char_u *p1, *p2; - char_u *tofree1, *tofree2; - int res; - char_u numbuf1[NUMBUFLEN]; - char_u numbuf2[NUMBUFLEN]; - - p1 = tv2string(&(*(listitem_T **)s1)->li_tv, &tofree1, numbuf1, 0); - p2 = tv2string(&(*(listitem_T **)s2)->li_tv, &tofree2, numbuf2, 0); - if (p1 == NULL) - p1 = (char_u *)""; - if (p2 == NULL) - p2 = (char_u *)""; - if (item_compare_ic) - res = STRICMP(p1, p2); - else - res = STRCMP(p1, p2); - free(tofree1); - free(tofree2); - return res; -} - -static int item_compare2(const void *s1, const void *s2) -{ - int res; - typval_T rettv; - typval_T argv[3]; - int dummy; - - /* shortcut after failure in previous call; compare all items equal */ - if (item_compare_func_err) - return 0; - - /* copy the values. This is needed to be able to set v_lock to VAR_FIXED - * in the copy without changing the original list items. */ - copy_tv(&(*(listitem_T **)s1)->li_tv, &argv[0]); - copy_tv(&(*(listitem_T **)s2)->li_tv, &argv[1]); - - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - res = call_func(item_compare_func, (int)STRLEN(item_compare_func), - &rettv, 2, argv, 0L, 0L, &dummy, TRUE, - item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); - - if (res == FAIL) - res = ITEM_COMPARE_FAIL; - else - res = get_tv_number_chk(&rettv, &item_compare_func_err); - if (item_compare_func_err) - res = ITEM_COMPARE_FAIL; /* return value has wrong type */ - clear_tv(&rettv); - return res; -} - -/* - * "sort({list})" function - */ -static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) -{ - list_T *l; - listitem_T *li; - listitem_T **ptrs; - long len; - long i; - - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); - } else { - l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, - (char_u *)(sort ? _("sort() argument") : _("uniq() argument")))) { - return; - } - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - ++l->lv_refcount; - - len = list_len(l); - if (len <= 1) - return; /* short list sorts pretty quickly */ - - item_compare_ic = FALSE; - item_compare_func = NULL; - item_compare_selfdict = NULL; - - if (argvars[1].v_type != VAR_UNKNOWN) { - /* optional second argument: {func} */ - if (argvars[1].v_type == VAR_FUNC) { - item_compare_func = argvars[1].vval.v_string; - } else { - int error = FALSE; - - i = get_tv_number_chk(&argvars[1], &error); - if (error) - return; /* type error; errmsg already given */ - if (i == 1) - item_compare_ic = TRUE; - else - item_compare_func = get_tv_string(&argvars[1]); - } - - if (argvars[2].v_type != VAR_UNKNOWN) { - /* optional third argument: {dict} */ - if (argvars[2].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - item_compare_selfdict = argvars[2].vval.v_dict; - } - } - - /* Make an array with each entry pointing to an item in the List. */ - ptrs = xmalloc((size_t)(len * sizeof (listitem_T *))); - - i = 0; - if (sort) { - // sort(): ptrs will be the list to sort. - for (li = l->lv_first; li != NULL; li = li->li_next) { - ptrs[i++] = li; - } - - item_compare_func_err = FALSE; - // Test the compare function. - if (item_compare_func != NULL - && item_compare2(&ptrs[0], &ptrs[1]) == ITEM_COMPARE_FAIL) { - EMSG(_("E702: Sort compare function failed")); - } else { - // Sort the array with item pointers. - qsort(ptrs, (size_t)len, sizeof (listitem_T *), - item_compare_func == NULL ? item_compare : item_compare2); - - if (!item_compare_func_err) { - // Clear the list and append the items in the sorted order. - l->lv_first = NULL; - l->lv_last = NULL; - l->lv_idx_item = NULL; - l->lv_len = 0; - - for (i = 0; i < len; i++) { - list_append(l, ptrs[i]); - } - } - } - } else { - int (*item_compare_func_ptr)(const void *, const void *); - - // f_uniq(): ptrs will be a stack of items to remove. - item_compare_func_err = FALSE; - item_compare_func_ptr = item_compare_func ? item_compare2 : item_compare; - - for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { - if (item_compare_func_ptr(&li, &li->li_next) == 0) { - ptrs[i++] = li; - } - if (item_compare_func_err) { - EMSG(_("E882: Uniq compare function failed")); - break; - } - } - - if (!item_compare_func_err) { - while (--i >= 0) { - li = ptrs[i]->li_next; - ptrs[i]->li_next = li->li_next; - if (li->li_next != NULL) { - li->li_next->li_prev = ptrs[i]; - } else { - l->lv_last = ptrs[i]; - } - list_fix_watch(l, li); - listitem_free(li); - l->lv_len--; - } - } - } - - free(ptrs); - } -} - -/// "sort"({list})" function -static void f_sort(typval_T *argvars, typval_T *rettv) -{ - do_sort_uniq(argvars, rettv, true); -} - -/// "uniq({list})" function -static void f_uniq(typval_T *argvars, typval_T *rettv) -{ - do_sort_uniq(argvars, rettv, false); -} - -/* - * "soundfold({word})" function - */ -static void f_soundfold(typval_T *argvars, typval_T *rettv) -{ - char_u *s; - - rettv->v_type = VAR_STRING; - s = get_tv_string(&argvars[0]); - rettv->vval.v_string = eval_soundfold(s); -} - -/* - * "spellbadword()" function - */ -static void f_spellbadword(typval_T *argvars, typval_T *rettv) -{ - char_u *word = (char_u *)""; - hlf_T attr = HLF_COUNT; - int len = 0; - - rettv_list_alloc(rettv); - - if (argvars[0].v_type == VAR_UNKNOWN) { - /* Find the start and length of the badly spelled word. */ - len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr); - if (len != 0) - word = ml_get_cursor(); - } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { - char_u *str = get_tv_string_chk(&argvars[0]); - int capcol = -1; - - if (str != NULL) { - /* Check the argument for spelling. */ - while (*str != NUL) { - len = spell_check(curwin, str, &attr, &capcol, FALSE); - if (attr != HLF_COUNT) { - word = str; - break; - } - str += len; - } - } - } - - list_append_string(rettv->vval.v_list, word, len); - list_append_string(rettv->vval.v_list, (char_u *)( - attr == HLF_SPB ? "bad" : - attr == HLF_SPR ? "rare" : - attr == HLF_SPL ? "local" : - attr == HLF_SPC ? "caps" : - ""), -1); -} - -/* - * "spellsuggest()" function - */ -static void f_spellsuggest(typval_T *argvars, typval_T *rettv) -{ - char_u *str; - int typeerr = FALSE; - int maxcount; - garray_T ga; - int i; - listitem_T *li; - int need_capital = FALSE; - - rettv_list_alloc(rettv); - - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - str = get_tv_string(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = get_tv_number_chk(&argvars[1], &typeerr); - if (maxcount <= 0) - return; - if (argvars[2].v_type != VAR_UNKNOWN) { - need_capital = get_tv_number_chk(&argvars[2], &typeerr); - if (typeerr) - return; - } - } else - maxcount = 25; - - spell_suggest_list(&ga, str, maxcount, need_capital, FALSE); - - for (i = 0; i < ga.ga_len; ++i) { - str = ((char_u **)ga.ga_data)[i]; - - li = listitem_alloc(); - if (li == NULL) - free(str); - else { - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = str; - list_append(rettv->vval.v_list, li); - } - } - ga_clear(&ga); - } -} - -static void f_split(typval_T *argvars, typval_T *rettv) -{ - char_u *str; - char_u *end; - char_u *pat = NULL; - regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; - char_u *save_cpo; - int match; - colnr_T col = 0; - int keepempty = FALSE; - int typeerr = FALSE; - - /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ - save_cpo = p_cpo; - p_cpo = (char_u *)""; - - str = get_tv_string(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN) { - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - typeerr = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - keepempty = get_tv_number_chk(&argvars[2], &typeerr); - } - if (pat == NULL || *pat == NUL) - pat = (char_u *)"[\\x01- ]\\+"; - - rettv_list_alloc(rettv); - - if (typeerr) - return; - - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - if (regmatch.regprog != NULL) { - regmatch.rm_ic = FALSE; - while (*str != NUL || keepempty) { - if (*str == NUL) - match = FALSE; /* empty item at the end */ - else - match = vim_regexec_nl(®match, str, col); - if (match) - end = regmatch.startp[0]; - else - end = str + STRLEN(str); - if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 - && *str != NUL && match && end < - regmatch.endp[0])) { - list_append_string(rettv->vval.v_list, str, (int)(end - str)); - } - if (!match) - break; - /* Advance to just after the match. */ - if (regmatch.endp[0] > str) - col = 0; - else { - /* Don't get stuck at the same match. */ - col = (*mb_ptr2len)(regmatch.endp[0]); - } - str = regmatch.endp[0]; - } - - vim_regfree(regmatch.regprog); - } - - p_cpo = save_cpo; -} - -/* - * "sqrt()" function - */ -static void f_sqrt(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sqrt(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "str2float()" function - */ -static void f_str2float(typval_T *argvars, typval_T *rettv) -{ - char_u *p = skipwhite(get_tv_string(&argvars[0])); - - if (*p == '+') - p = skipwhite(p + 1); - (void)string2float(p, &rettv->vval.v_float); - rettv->v_type = VAR_FLOAT; -} - -/* - * "str2nr()" function - */ -static void f_str2nr(typval_T *argvars, typval_T *rettv) -{ - int base = 10; - char_u *p; - long n; - - if (argvars[1].v_type != VAR_UNKNOWN) { - base = get_tv_number(&argvars[1]); - if (base != 8 && base != 10 && base != 16) { - EMSG(_(e_invarg)); - return; - } - } - - p = skipwhite(get_tv_string(&argvars[0])); - if (*p == '+') - p = skipwhite(p + 1); - vim_str2nr(p, NULL, NULL, base == 8 ? 2 : 0, base == 16 ? 2 : 0, &n, NULL); - rettv->vval.v_number = n; -} - -/* - * "strftime({format}[, {time}])" function - */ -static void f_strftime(typval_T *argvars, typval_T *rettv) -{ - char_u result_buf[256]; - time_t seconds; - char_u *p; - - rettv->v_type = VAR_STRING; - - p = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - seconds = time(NULL); - else - seconds = (time_t)get_tv_number(&argvars[1]); - - struct tm curtime; - struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime); - /* MSVC returns NULL for an invalid value of seconds. */ - if (curtime_ptr == NULL) - rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)")); - else { - vimconv_T conv; - char_u *enc; - - conv.vc_type = CONV_NONE; - enc = enc_locale(); - convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) - p = string_convert(&conv, p, NULL); - if (p != NULL) - (void)strftime((char *)result_buf, sizeof(result_buf), - (char *)p, curtime_ptr); - else - result_buf[0] = NUL; - - if (conv.vc_type != CONV_NONE) - free(p); - convert_setup(&conv, enc, p_enc); - if (conv.vc_type != CONV_NONE) - rettv->vval.v_string = string_convert(&conv, result_buf, NULL); - else - rettv->vval.v_string = vim_strsave(result_buf); - - /* Release conversion descriptors */ - convert_setup(&conv, NULL, NULL); - free(enc); - } -} - -/* - * "stridx()" function - */ -static void f_stridx(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *save_haystack; - char_u *pos; - int start_idx; - - needle = get_tv_string_chk(&argvars[1]); - save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf); - rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ - - if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; - - start_idx = get_tv_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) - return; - if (start_idx >= 0) - haystack += start_idx; - } - - pos = (char_u *)strstr((char *)haystack, (char *)needle); - if (pos != NULL) - rettv->vval.v_number = (varnumber_T)(pos - save_haystack); -} - -/* - * "string()" function - */ -static void f_string(typval_T *argvars, typval_T *rettv) -{ - char_u *tofree; - char_u numbuf[NUMBUFLEN]; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf, 0); - /* Make a copy if we have a value but it's not in allocated memory. */ - if (rettv->vval.v_string != NULL && tofree == NULL) - rettv->vval.v_string = vim_strsave(rettv->vval.v_string); -} - -/* - * "strlen()" function - */ -static void f_strlen(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = (varnumber_T)(STRLEN( - get_tv_string(&argvars[0]))); -} - -/* - * "strchars()" function - */ -static void f_strchars(typval_T *argvars, typval_T *rettv) -{ - char_u *s = get_tv_string(&argvars[0]); - varnumber_T len = 0; - - while (*s != NUL) { - mb_cptr2char_adv(&s); - ++len; - } - rettv->vval.v_number = len; -} - -/* - * "strdisplaywidth()" function - */ -static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv) -{ - char_u *s = get_tv_string(&argvars[0]); - int col = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) - col = get_tv_number(&argvars[1]); - - rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col); -} - -/* - * "strwidth()" function - */ -static void f_strwidth(typval_T *argvars, typval_T *rettv) -{ - char_u *s = get_tv_string(&argvars[0]); - - rettv->vval.v_number = (varnumber_T)( - mb_string2cells(s, -1) - ); -} - -/* - * "strpart()" function - */ -static void f_strpart(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - int n; - int len; - int slen; - int error = FALSE; - - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); - - n = get_tv_number_chk(&argvars[1], &error); - if (error) - len = 0; - else if (argvars[2].v_type != VAR_UNKNOWN) - len = get_tv_number(&argvars[2]); - else - len = slen - n; /* default len: all bytes that are available. */ - - /* - * Only return the overlap between the specified part and the actual - * string. - */ - if (n < 0) { - len += n; - n = 0; - } else if (n > slen) - n = slen; - if (len < 0) - len = 0; - else if (n + len > slen) - len = slen - n; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + n, len); -} - -/* - * "strridx()" function - */ -static void f_strridx(typval_T *argvars, typval_T *rettv) -{ - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *rest; - char_u *lastmatch = NULL; - int haystack_len, end_idx; - - needle = get_tv_string_chk(&argvars[1]); - haystack = get_tv_string_buf_chk(&argvars[0], buf); - - rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ - - haystack_len = (int)STRLEN(haystack); - if (argvars[2].v_type != VAR_UNKNOWN) { - /* Third argument: upper limit for index */ - end_idx = get_tv_number_chk(&argvars[2], NULL); - if (end_idx < 0) - return; /* can never find a match */ - } else - end_idx = haystack_len; - - if (*needle == NUL) { - /* Empty string matches past the end. */ - lastmatch = haystack + end_idx; - } else { - for (rest = haystack; *rest != NUL; ++rest) { - rest = (char_u *)strstr((char *)rest, (char *)needle); - if (rest == NULL || rest > haystack + end_idx) - break; - lastmatch = rest; - } - } - - if (lastmatch == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); -} - -/* - * "strtrans()" function - */ -static void f_strtrans(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = transstr(get_tv_string(&argvars[0])); -} - -/* - * "submatch()" function - */ -static void f_submatch(typval_T *argvars, typval_T *rettv) -{ - int error = FALSE; - int no = (int)get_tv_number_chk(&argvars[0], &error); - if (error) { - return; - } - - int retList = 0; - - if (argvars[1].v_type != VAR_UNKNOWN) { - retList = get_tv_number_chk(&argvars[1], &error); - if (error) { - return; - } - } - - if (retList == 0) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = reg_submatch(no); - } else { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = reg_submatch_list(no); - } -} - -/* - * "substitute()" function - */ -static void f_substitute(typval_T *argvars, typval_T *rettv) -{ - char_u patbuf[NUMBUFLEN]; - char_u subbuf[NUMBUFLEN]; - char_u flagsbuf[NUMBUFLEN]; - - char_u *str = get_tv_string_chk(&argvars[0]); - char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf); - char_u *sub = get_tv_string_buf_chk(&argvars[2], subbuf); - char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf); - - rettv->v_type = VAR_STRING; - if (str == NULL || pat == NULL || sub == NULL || flg == NULL) - rettv->vval.v_string = NULL; - else - rettv->vval.v_string = do_string_sub(str, pat, sub, flg); -} - -/* - * "synID(lnum, col, trans)" function - */ -static void f_synID(typval_T *argvars, typval_T *rettv) -{ - int id = 0; - long lnum; - long col; - int trans; - int transerr = FALSE; - - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - trans = get_tv_number_chk(&argvars[2], &transerr); - - if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && col < (long)STRLEN(ml_get(lnum))) - id = syn_get_id(curwin, lnum, (colnr_T)col, trans, NULL, FALSE); - - rettv->vval.v_number = id; -} - -/* - * "synIDattr(id, what [, mode])" function - */ -static void f_synIDattr(typval_T *argvars, typval_T *rettv) -{ - char_u *p = NULL; - int id; - char_u *what; - char_u *mode; - char_u modebuf[NUMBUFLEN]; - int modec; - - id = get_tv_number(&argvars[0]); - what = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) { - mode = get_tv_string_buf(&argvars[2], modebuf); - modec = TOLOWER_ASC(mode[0]); - if (modec != 't' && modec != 'c' && modec != 'g') - modec = 0; /* replace invalid with current */ - } else { - if (t_colors > 1) - modec = 'c'; - else - modec = 't'; - } - - - switch (TOLOWER_ASC(what[0])) { - case 'b': - if (TOLOWER_ASC(what[1]) == 'g') /* bg[#] */ - p = highlight_color(id, what, modec); - else /* bold */ - p = highlight_has_attr(id, HL_BOLD, modec); - break; - - case 'f': /* fg[#] or font */ - p = highlight_color(id, what, modec); - break; - - case 'i': - if (TOLOWER_ASC(what[1]) == 'n') /* inverse */ - p = highlight_has_attr(id, HL_INVERSE, modec); - else /* italic */ - p = highlight_has_attr(id, HL_ITALIC, modec); - break; - - case 'n': /* name */ - p = get_highlight_name(NULL, id - 1); - break; - - case 'r': /* reverse */ - p = highlight_has_attr(id, HL_INVERSE, modec); - break; - - case 's': - if (TOLOWER_ASC(what[1]) == 'p') /* sp[#] */ - p = highlight_color(id, what, modec); - else /* standout */ - p = highlight_has_attr(id, HL_STANDOUT, modec); - break; - - case 'u': - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') - /* underline */ - p = highlight_has_attr(id, HL_UNDERLINE, modec); - else - /* undercurl */ - p = highlight_has_attr(id, HL_UNDERCURL, modec); - break; - } - - if (p != NULL) - p = vim_strsave(p); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; -} - -/* - * "synIDtrans(id)" function - */ -static void f_synIDtrans(typval_T *argvars, typval_T *rettv) -{ - int id; - - id = get_tv_number(&argvars[0]); - - if (id > 0) - id = syn_get_final_id(id); - else - id = 0; - - rettv->vval.v_number = id; -} - -/* - * "synconcealed(lnum, col)" function - */ -static void f_synconcealed(typval_T *argvars, typval_T *rettv) -{ - long lnum; - long col; - int syntax_flags = 0; - int cchar; - int matchid = 0; - char_u str[NUMBUFLEN]; - - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - - memset(str, NUL, sizeof(str)); - - rettv_list_alloc(rettv); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); - syntax_flags = get_syntax_info(&matchid); - - // get the conceal character - if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { - cchar = syn_get_sub_char(); - if (cchar == NUL && curwin->w_p_cole == 1 && lcs_conceal != NUL) { - cchar = lcs_conceal; - } - if (cchar != NUL) { - if (has_mbyte) - (*mb_char2bytes)(cchar, str); - else - str[0] = cchar; - } - } - } - - list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); - // -1 to auto-determine strlen - list_append_string(rettv->vval.v_list, str, -1); - list_append_number(rettv->vval.v_list, matchid); -} - -/* - * "synstack(lnum, col)" function - */ -static void f_synstack(typval_T *argvars, typval_T *rettv) -{ - long lnum; - long col; - - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - - if (lnum >= 1 - && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 - && col <= (long)STRLEN(ml_get(lnum))) { - rettv_list_alloc(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE); - - int id; - int i = 0; - while ((id = syn_get_stack_item(i++)) >= 0) { - list_append_number(rettv->vval.v_list, id); - } - } -} - -/* - * "system()" function - */ -static void f_system(typval_T *argvars, typval_T *rettv) -{ - char_u *res = NULL; - char_u *p; - char_u *infile = NULL; - char_u buf[NUMBUFLEN]; - int err = FALSE; - FILE *fd; - - if (check_restricted() || check_secure()) - goto done; - - if (argvars[1].v_type != VAR_UNKNOWN) { - /* - * Write the string to a temp file, to be used for input of the shell - * command. - */ - if ((infile = vim_tempname('i')) == NULL) { - EMSG(_(e_notmp)); - goto done; - } - - fd = mch_fopen((char *)infile, WRITEBIN); - if (fd == NULL) { - EMSG2(_(e_notopen), infile); - goto done; - } - p = get_tv_string_buf_chk(&argvars[1], buf); - if (p == NULL) { - fclose(fd); - goto done; /* type error; errmsg already given */ - } - if (fwrite(p, STRLEN(p), 1, fd) != 1) - err = TRUE; - if (fclose(fd) != 0) - err = TRUE; - if (err) { - EMSG(_("E677: Error writing temp file")); - goto done; - } - } - - res = get_cmd_output(get_tv_string(&argvars[0]), infile, - kShellOptSilent | kShellOptCooked); - -#ifdef USE_CR - /* translate into */ - if (res != NULL) { - char_u *s; - - for (s = res; *s; ++s) { - if (*s == CAR) - *s = NL; - } - } -#else -# ifdef USE_CRNL - /* translate into */ - if (res != NULL) { - char_u *s, *d; - - d = res; - for (s = res; *s; ++s) { - if (s[0] == CAR && s[1] == NL) - ++s; - *d++ = *s; - } - *d = NUL; - } -# endif -#endif - -done: - if (infile != NULL) { - os_remove((char *)infile); - free(infile); - } - rettv->v_type = VAR_STRING; - rettv->vval.v_string = res; -} - -/* - * "tabpagebuflist()" function - */ -static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv) -{ - tabpage_T *tp; - win_T *wp = NULL; - - if (argvars[0].v_type == VAR_UNKNOWN) - wp = firstwin; - else { - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp != NULL) - wp = (tp == curtab) ? firstwin : tp->tp_firstwin; - } - if (wp != NULL) { - rettv_list_alloc(rettv); - while (wp != NULL) { - list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); - wp = wp->w_next; - } - } -} - - -/* - * "tabpagenr()" function - */ -static void f_tabpagenr(typval_T *argvars, typval_T *rettv) -{ - int nr = 1; - char_u *arg; - - if (argvars[0].v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(&argvars[0]); - nr = 0; - if (arg != NULL) { - if (STRCMP(arg, "$") == 0) - nr = tabpage_index(NULL) - 1; - else - EMSG2(_(e_invexpr2), arg); - } - } else - nr = tabpage_index(curtab); - rettv->vval.v_number = nr; -} - - -static int get_winnr(tabpage_T *tp, typval_T *argvar); - -/* - * Common code for tabpagewinnr() and winnr(). - */ -static int get_winnr(tabpage_T *tp, typval_T *argvar) -{ - win_T *twin; - int nr = 1; - win_T *wp; - char_u *arg; - - twin = (tp == curtab) ? curwin : tp->tp_curwin; - if (argvar->v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(argvar); - if (arg == NULL) - nr = 0; /* type error; errmsg already given */ - else if (STRCMP(arg, "$") == 0) - twin = (tp == curtab) ? lastwin : tp->tp_lastwin; - else if (STRCMP(arg, "#") == 0) { - twin = (tp == curtab) ? prevwin : tp->tp_prevwin; - if (twin == NULL) - nr = 0; - } else { - EMSG2(_(e_invexpr2), arg); - nr = 0; - } - } - - if (nr > 0) - for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin; - wp != twin; wp = wp->w_next) { - if (wp == NULL) { - /* didn't find it in this tabpage */ - nr = 0; - break; - } - ++nr; - } - return nr; -} - -/* - * "tabpagewinnr()" function - */ -static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv) -{ - int nr = 1; - tabpage_T *tp; - - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp == NULL) - nr = 0; - else - nr = get_winnr(tp, &argvars[1]); - rettv->vval.v_number = nr; -} - - -/* - * "tagfiles()" function - */ -static void f_tagfiles(typval_T *argvars, typval_T *rettv) -{ - char_u *fname; - tagname_T tn; - - rettv_list_alloc(rettv); - fname = alloc(MAXPATHL); - - int first = TRUE; - while (get_tagfname(&tn, first, fname) == OK) { - list_append_string(rettv->vval.v_list, fname, -1); - first = FALSE; - } - - tagname_free(&tn); - free(fname); -} - -/* - * "taglist()" function - */ -static void f_taglist(typval_T *argvars, typval_T *rettv) -{ - char_u *tag_pattern; - - tag_pattern = get_tv_string(&argvars[0]); - - rettv->vval.v_number = FALSE; - if (*tag_pattern == NUL) - return; - - rettv_list_alloc(rettv); - (void)get_tags(rettv->vval.v_list, tag_pattern); -} - -/* - * "tempname()" function - */ -static void f_tempname(typval_T *argvars, typval_T *rettv) -{ - static int x = 'A'; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_tempname(x); - - /* Advance 'x' to use A-Z and 0-9, so that there are at least 34 different - * names. Skip 'I' and 'O', they are used for shell redirection. */ - do { - if (x == 'Z') - x = '0'; - else if (x == '9') - x = 'A'; - else { - ++x; - } - } while (x == 'I' || x == 'O'); -} - -/* - * "test(list)" function: Just checking the walls... - */ -static void f_test(typval_T *argvars, typval_T *rettv) -{ - /* Used for unit testing. Change the code below to your liking. */ -} - -/* - * "tan()" function - */ -static void f_tan(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tan(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "tanh()" function - */ -static void f_tanh(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tanh(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "tolower(string)" function - */ -static void f_tolower(typval_T *argvars, typval_T *rettv) -{ - char_u *p; - - p = vim_strsave(get_tv_string(&argvars[0])); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; - - if (p != NULL) - while (*p != NUL) { - int l; - - if (enc_utf8) { - int c, lc; - - c = utf_ptr2char(p); - lc = utf_tolower(c); - l = utf_ptr2len(p); - /* TODO: reallocate string when byte count changes. */ - if (utf_char2len(lc) == l) - utf_char2bytes(lc, p); - p += l; - } else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) - p += l; /* skip multi-byte character */ - else { - *p = TOLOWER_LOC(*p); /* note that tolower() can be a macro */ - ++p; - } - } -} - -/* - * "toupper(string)" function - */ -static void f_toupper(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = strup_save(get_tv_string(&argvars[0])); -} - -/* - * "tr(string, fromstr, tostr)" function - */ -static void f_tr(typval_T *argvars, typval_T *rettv) -{ - char_u *in_str; - char_u *fromstr; - char_u *tostr; - char_u *p; - int inlen; - int fromlen; - int tolen; - int idx; - char_u *cpstr; - int cplen; - int first = TRUE; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - garray_T ga; - - in_str = get_tv_string(&argvars[0]); - fromstr = get_tv_string_buf_chk(&argvars[1], buf); - tostr = get_tv_string_buf_chk(&argvars[2], buf2); - - /* Default return value: empty string. */ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (fromstr == NULL || tostr == NULL) - return; /* type error; errmsg already given */ - ga_init(&ga, (int)sizeof(char), 80); - - if (!has_mbyte) - /* not multi-byte: fromstr and tostr must be the same length */ - if (STRLEN(fromstr) != STRLEN(tostr)) { -error: - EMSG2(_(e_invarg2), fromstr); - ga_clear(&ga); - return; - } - - /* fromstr and tostr have to contain the same number of chars */ - while (*in_str != NUL) { - if (has_mbyte) { - inlen = (*mb_ptr2len)(in_str); - cpstr = in_str; - cplen = inlen; - idx = 0; - for (p = fromstr; *p != NUL; p += fromlen) { - fromlen = (*mb_ptr2len)(p); - if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { - for (p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); - if (idx-- == 0) { - cplen = tolen; - cpstr = p; - break; - } - } - if (*p == NUL) /* tostr is shorter than fromstr */ - goto error; - break; - } - ++idx; - } - - if (first && cpstr == in_str) { - /* Check that fromstr and tostr have the same number of - * (multi-byte) characters. Done only once when a character - * of in_str doesn't appear in fromstr. */ - first = FALSE; - for (p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); - --idx; - } - if (idx != 0) - goto error; - } - - ga_grow(&ga, cplen); - memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen); - ga.ga_len += cplen; - - in_str += inlen; - } else { - /* When not using multi-byte chars we can do it faster. */ - p = vim_strchr(fromstr, *in_str); - if (p != NULL) - ga_append(&ga, tostr[p - fromstr]); - else - ga_append(&ga, *in_str); - ++in_str; - } - } - - /* add a terminating NUL */ - ga_grow(&ga, 1); - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; -} - -/* - * "trunc({float})" function - */ -static void f_trunc(typval_T *argvars, typval_T *rettv) -{ - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - /* trunc() is not in C90, use floor() or ceil() instead. */ - rettv->vval.v_float = f > 0 ? floor(f) : ceil(f); - else - rettv->vval.v_float = 0.0; -} - -/* - * "type(expr)" function - */ -static void f_type(typval_T *argvars, typval_T *rettv) -{ - int n; - - switch (argvars[0].v_type) { - case VAR_NUMBER: n = 0; break; - case VAR_STRING: n = 1; break; - case VAR_FUNC: n = 2; break; - case VAR_LIST: n = 3; break; - case VAR_DICT: n = 4; break; - case VAR_FLOAT: n = 5; break; - default: EMSG2(_(e_intern2), "f_type()"); n = 0; break; - } - rettv->vval.v_number = n; -} - -/* - * "undofile(name)" function - */ -static void f_undofile(typval_T *argvars, typval_T *rettv) -{ - rettv->v_type = VAR_STRING; - { - char_u *fname = get_tv_string(&argvars[0]); - - if (*fname == NUL) { - /* If there is no file name there will be no undo file. */ - rettv->vval.v_string = NULL; - } else { - char_u *ffname = FullName_save(fname, FALSE); - - if (ffname != NULL) - rettv->vval.v_string = u_get_undo_file_name(ffname, FALSE); - free(ffname); - } - } -} - -/* - * "undotree()" function - */ -static void f_undotree(typval_T *argvars, typval_T *rettv) -{ - if (rettv_dict_alloc(rettv) == OK) { - dict_T *dict = rettv->vval.v_dict; - list_T *list; - - dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); - dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); - dict_add_nr_str(dict, "save_last", - (long)curbuf->b_u_save_nr_last, NULL); - dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); - dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); - dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); - - list = list_alloc(); - if (list != NULL) { - u_eval_tree(curbuf->b_u_oldhead, list); - dict_add_list(dict, "entries", list); - } - } -} - -/* - * "values(dict)" function - */ -static void f_values(typval_T *argvars, typval_T *rettv) -{ - dict_list(argvars, rettv, 1); -} - -/* - * "virtcol(string)" function - */ -static void f_virtcol(typval_T *argvars, typval_T *rettv) -{ - colnr_T vcol = 0; - pos_T *fp; - int fnum = curbuf->b_fnum; - - fp = var2fpos(&argvars[0], FALSE, &fnum); - if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count - && fnum == curbuf->b_fnum) { - getvvcol(curwin, fp, NULL, NULL, &vcol); - ++vcol; - } - - rettv->vval.v_number = vcol; -} - -/* - * "visualmode()" function - */ -static void f_visualmode(typval_T *argvars, typval_T *rettv) -{ - char_u str[2]; - - rettv->v_type = VAR_STRING; - str[0] = curbuf->b_visual_mode_eval; - str[1] = NUL; - rettv->vval.v_string = vim_strsave(str); - - /* A non-zero number or non-empty string argument: reset mode. */ - if (non_zero_arg(&argvars[0])) - curbuf->b_visual_mode_eval = NUL; -} - -/* - * "wildmenumode()" function - */ -static void f_wildmenumode(typval_T *argvars, typval_T *rettv) -{ - if (wild_menu_showing) - rettv->vval.v_number = 1; -} - -/* - * "winbufnr(nr)" function - */ -static void f_winbufnr(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = wp->w_buffer->b_fnum; -} - -/* - * "wincol()" function - */ -static void f_wincol(typval_T *argvars, typval_T *rettv) -{ - validate_cursor(); - rettv->vval.v_number = curwin->w_wcol + 1; -} - -/* - * "winheight(nr)" function - */ -static void f_winheight(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = wp->w_height; -} - -/* - * "winline()" function - */ -static void f_winline(typval_T *argvars, typval_T *rettv) -{ - validate_cursor(); - rettv->vval.v_number = curwin->w_wrow + 1; -} - -/* - * "winnr()" function - */ -static void f_winnr(typval_T *argvars, typval_T *rettv) -{ - int nr = 1; - - nr = get_winnr(curtab, &argvars[0]); - rettv->vval.v_number = nr; -} - -/* - * "winrestcmd()" function - */ -static void f_winrestcmd(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - int winnr = 1; - garray_T ga; - char_u buf[50]; - - ga_init(&ga, (int)sizeof(char), 70); - for (wp = firstwin; wp != NULL; wp = wp->w_next) { - sprintf((char *)buf, "%dresize %d|", winnr, wp->w_height); - ga_concat(&ga, buf); - sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width); - ga_concat(&ga, buf); - ++winnr; - } - ga_append(&ga, NUL); - - rettv->vval.v_string = ga.ga_data; - rettv->v_type = VAR_STRING; -} - -/* - * "winrestview()" function - */ -static void f_winrestview(typval_T *argvars, typval_T *rettv) -{ - dict_T *dict; - - if (argvars[0].v_type != VAR_DICT - || (dict = argvars[0].vval.v_dict) == NULL) - EMSG(_(e_invarg)); - else { - curwin->w_cursor.lnum = get_dict_number(dict, (char_u *)"lnum"); - curwin->w_cursor.col = get_dict_number(dict, (char_u *)"col"); - curwin->w_cursor.coladd = get_dict_number(dict, (char_u *)"coladd"); - curwin->w_curswant = get_dict_number(dict, (char_u *)"curswant"); - curwin->w_set_curswant = FALSE; - - set_topline(curwin, get_dict_number(dict, (char_u *)"topline")); - curwin->w_topfill = get_dict_number(dict, (char_u *)"topfill"); - curwin->w_leftcol = get_dict_number(dict, (char_u *)"leftcol"); - curwin->w_skipcol = get_dict_number(dict, (char_u *)"skipcol"); - - check_cursor(); - win_new_height(curwin, curwin->w_height); - win_new_width(curwin, W_WIDTH(curwin)); - changed_window_setting(); - - if (curwin->w_topline == 0) - curwin->w_topline = 1; - if (curwin->w_topline > curbuf->b_ml.ml_line_count) - curwin->w_topline = curbuf->b_ml.ml_line_count; - check_topfill(curwin, TRUE); - } -} - -/* - * "winsaveview()" function - */ -static void f_winsaveview(typval_T *argvars, typval_T *rettv) -{ - dict_T *dict; - - if (rettv_dict_alloc(rettv) == FAIL) - return; - dict = rettv->vval.v_dict; - - dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); - dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL); - dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL); - update_curswant(); - dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL); - - dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL); - dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL); - dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL); - dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); -} - -/* - * "winwidth(nr)" function - */ -static void f_winwidth(typval_T *argvars, typval_T *rettv) -{ - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) - rettv->vval.v_number = -1; - else - rettv->vval.v_number = wp->w_width; -} - -/* - * "writefile()" function - */ -static void f_writefile(typval_T *argvars, typval_T *rettv) -{ - int binary = FALSE; - char_u *fname; - FILE *fd; - listitem_T *li; - char_u *s; - int ret = 0; - int c; - - if (check_restricted() || check_secure()) - return; - - if (argvars[0].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "writefile()"); - return; - } - if (argvars[0].vval.v_list == NULL) - return; - - if (argvars[2].v_type != VAR_UNKNOWN - && STRCMP(get_tv_string(&argvars[2]), "b") == 0) - binary = TRUE; - - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[1]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, WRITEBIN)) == NULL) { - EMSG2(_(e_notcreate), *fname == NUL ? (char_u *)_("") : fname); - ret = -1; - } else { - for (li = argvars[0].vval.v_list->lv_first; li != NULL; - li = li->li_next) { - for (s = get_tv_string(&li->li_tv); *s != NUL; ++s) { - if (*s == '\n') - c = putc(NUL, fd); - else - c = putc(*s, fd); - if (c == EOF) { - ret = -1; - break; - } - } - if (!binary || li->li_next != NULL) - if (putc('\n', fd) == EOF) { - ret = -1; - break; - } - if (ret < 0) { - EMSG(_(e_write)); - break; - } - } - fclose(fd); - } - - rettv->vval.v_number = ret; -} - -/* - * "xor(expr, expr)" function - */ -static void f_xor(typval_T *argvars, typval_T *rettv) -{ - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - ^ get_tv_number_chk(&argvars[1], NULL); -} - - -/* - * Translate a String variable into a position. - * Returns NULL when there is an error. - */ -static pos_T * -var2fpos ( - typval_T *varp, - int dollar_lnum, /* TRUE when $ is last line */ - int *fnum /* set to fnum for '0, 'A, etc. */ -) -{ - char_u *name; - static pos_T pos; - pos_T *pp; - - /* Argument can be [lnum, col, coladd]. */ - if (varp->v_type == VAR_LIST) { - list_T *l; - int len; - int error = FALSE; - listitem_T *li; - - l = varp->vval.v_list; - if (l == NULL) - return NULL; - - /* Get the line number */ - pos.lnum = list_find_nr(l, 0L, &error); - if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) - return NULL; /* invalid line number */ - - /* Get the column number */ - pos.col = list_find_nr(l, 1L, &error); - if (error) - return NULL; - len = (long)STRLEN(ml_get(pos.lnum)); - - /* We accept "$" for the column number: last column. */ - li = list_find(l, 1L); - if (li != NULL && li->li_tv.v_type == VAR_STRING - && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) - pos.col = len + 1; - - /* Accept a position up to the NUL after the line. */ - if (pos.col == 0 || (int)pos.col > len + 1) - return NULL; /* invalid column number */ - --pos.col; - - /* Get the virtual offset. Defaults to zero. */ - pos.coladd = list_find_nr(l, 2L, &error); - if (error) - pos.coladd = 0; - - return &pos; - } - - name = get_tv_string_chk(varp); - if (name == NULL) - return NULL; - if (name[0] == '.') /* cursor */ - return &curwin->w_cursor; - if (name[0] == 'v' && name[1] == NUL) { /* Visual start */ - if (VIsual_active) - return &VIsual; - return &curwin->w_cursor; - } - if (name[0] == '\'') { /* mark */ - pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); - if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) - return NULL; - return pp; - } - - pos.coladd = 0; - - if (name[0] == 'w' && dollar_lnum) { - pos.col = 0; - if (name[1] == '0') { /* "w0": first visible line */ - update_topline(); - pos.lnum = curwin->w_topline; - return &pos; - } else if (name[1] == '$') { /* "w$": last visible line */ - validate_botline(); - pos.lnum = curwin->w_botline - 1; - return &pos; - } - } else if (name[0] == '$') { /* last column or line */ - if (dollar_lnum) { - pos.lnum = curbuf->b_ml.ml_line_count; - pos.col = 0; - } else { - pos.lnum = curwin->w_cursor.lnum; - pos.col = (colnr_T)STRLEN(ml_get_curline()); - } - return &pos; - } - return NULL; -} - -/* - * Convert list in "arg" into a position and optional file number. - * When "fnump" is NULL there is no file number, only 3 items. - * Note that the column is passed on as-is, the caller may want to decrement - * it to use 1 for the first column. - * Return FAIL when conversion is not possible, doesn't check the position for - * validity. - */ -static int list2fpos(typval_T *arg, pos_T *posp, int *fnump) -{ - list_T *l = arg->vval.v_list; - long i = 0; - long n; - - /* List must be: [fnum, lnum, col, coladd], where "fnum" is only there - * when "fnump" isn't NULL and "coladd" is optional. */ - if (arg->v_type != VAR_LIST - || l == NULL - || l->lv_len < (fnump == NULL ? 2 : 3) - || l->lv_len > (fnump == NULL ? 3 : 4)) - return FAIL; - - if (fnump != NULL) { - n = list_find_nr(l, i++, NULL); /* fnum */ - if (n < 0) - return FAIL; - if (n == 0) - n = curbuf->b_fnum; /* current buffer */ - *fnump = n; - } - - n = list_find_nr(l, i++, NULL); /* lnum */ - if (n < 0) - return FAIL; - posp->lnum = n; - - n = list_find_nr(l, i++, NULL); /* col */ - if (n < 0) - return FAIL; - posp->col = n; - - n = list_find_nr(l, i, NULL); - if (n < 0) - posp->coladd = 0; - else - posp->coladd = n; - - return OK; -} - -/* - * Get the length of an environment variable name. - * Advance "arg" to the first character after the name. - * Return 0 for error. - */ -static int get_env_len(char_u **arg) -{ - char_u *p; - int len; - - for (p = *arg; vim_isIDc(*p); ++p) - ; - if (p == *arg) /* no name found */ - return 0; - - len = (int)(p - *arg); - *arg = p; - return len; -} - -/* - * Get the length of the name of a function or internal variable. - * "arg" is advanced to the first non-white character after the name. - * Return 0 if something is wrong. - */ -static int get_id_len(char_u **arg) -{ - char_u *p; - int len; - - /* Find the end of the name. */ - for (p = *arg; eval_isnamec(*p); ++p) - ; - if (p == *arg) /* no name found */ - return 0; - - len = (int)(p - *arg); - *arg = skipwhite(p); - - return len; -} - -/* - * Get the length of the name of a variable or function. - * Only the name is recognized, does not handle ".key" or "[idx]". - * "arg" is advanced to the first non-white character after the name. - * Return -1 if curly braces expansion failed. - * Return 0 if something else is wrong. - * If the name contains 'magic' {}'s, expand them and return the - * expanded name in an allocated string via 'alias' - caller must free. - */ -static int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose) -{ - int len; - char_u *p; - char_u *expr_start; - char_u *expr_end; - - *alias = NULL; /* default to no alias */ - - if ((*arg)[0] == K_SPECIAL && (*arg)[1] == KS_EXTRA - && (*arg)[2] == (int)KE_SNR) { - /* hard coded , already translated */ - *arg += 3; - return get_id_len(arg) + 3; - } - len = eval_fname_script(*arg); - if (len > 0) { - /* literal "", "s:" or "" */ - *arg += len; - } - - /* - * Find the end of the name; check for {} construction. - */ - p = find_name_end(*arg, &expr_start, &expr_end, - len > 0 ? 0 : FNE_CHECK_START); - if (expr_start != NULL) { - char_u *temp_string; - - if (!evaluate) { - len += (int)(p - *arg); - *arg = skipwhite(p); - return len; - } - - /* - * Include any etc in the expanded string: - * Thus the -len here. - */ - temp_string = make_expanded_name(*arg - len, expr_start, expr_end, p); - if (temp_string == NULL) - return -1; - *alias = temp_string; - *arg = skipwhite(p); - return (int)STRLEN(temp_string); - } - - len += get_id_len(arg); - if (len == 0 && verbose) - EMSG2(_(e_invexpr2), *arg); - - return len; -} - -/* - * Find the end of a variable or function name, taking care of magic braces. - * If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the - * start and end of the first magic braces item. - * "flags" can have FNE_INCL_BR and FNE_CHECK_START. - * Return a pointer to just after the name. Equal to "arg" if there is no - * valid name. - */ -static char_u *find_name_end(char_u *arg, char_u **expr_start, char_u **expr_end, int flags) -{ - int mb_nest = 0; - int br_nest = 0; - char_u *p; - - if (expr_start != NULL) { - *expr_start = NULL; - *expr_end = NULL; - } - - /* Quick check for valid starting character. */ - if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{') - return arg; - - for (p = arg; *p != NUL - && (eval_isnamec(*p) - || *p == '{' - || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.')) - || mb_nest != 0 - || br_nest != 0); mb_ptr_adv(p)) { - if (*p == '\'') { - /* skip over 'string' to avoid counting [ and ] inside it. */ - for (p = p + 1; *p != NUL && *p != '\''; mb_ptr_adv(p)) - ; - if (*p == NUL) - break; - } else if (*p == '"') { - /* skip over "str\"ing" to avoid counting [ and ] inside it. */ - for (p = p + 1; *p != NUL && *p != '"'; mb_ptr_adv(p)) - if (*p == '\\' && p[1] != NUL) - ++p; - if (*p == NUL) - break; - } - - if (mb_nest == 0) { - if (*p == '[') - ++br_nest; - else if (*p == ']') - --br_nest; - } - - if (br_nest == 0) { - if (*p == '{') { - mb_nest++; - if (expr_start != NULL && *expr_start == NULL) - *expr_start = p; - } else if (*p == '}') { - mb_nest--; - if (expr_start != NULL && mb_nest == 0 && *expr_end == NULL) - *expr_end = p; - } - } - } - - return p; -} - -/* - * Expands out the 'magic' {}'s in a variable/function name. - * Note that this can call itself recursively, to deal with - * constructs like foo{bar}{baz}{bam} - * The four pointer arguments point to "foo{expre}ss{ion}bar" - * "in_start" ^ - * "expr_start" ^ - * "expr_end" ^ - * "in_end" ^ - * - * Returns a new allocated string, which the caller must free. - * Returns NULL for failure. - */ -static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end) -{ - char_u c1; - char_u *retval = NULL; - char_u *temp_result; - char_u *nextcmd = NULL; - - if (expr_end == NULL || in_end == NULL) - return NULL; - *expr_start = NUL; - *expr_end = NUL; - c1 = *in_end; - *in_end = NUL; - - temp_result = eval_to_string(expr_start + 1, &nextcmd, FALSE); - if (temp_result != NULL && nextcmd == NULL) { - retval = alloc((unsigned)(STRLEN(temp_result) + (expr_start - in_start) - + (in_end - expr_end) + 1)); - if (retval != NULL) { - STRCPY(retval, in_start); - STRCAT(retval, temp_result); - STRCAT(retval, expr_end + 1); - } - } - free(temp_result); - - *in_end = c1; /* put char back for error messages */ - *expr_start = '{'; - *expr_end = '}'; - - if (retval != NULL) { - temp_result = find_name_end(retval, &expr_start, &expr_end, 0); - if (expr_start != NULL) { - /* Further expansion! */ - temp_result = make_expanded_name(retval, expr_start, - expr_end, temp_result); - free(retval); - retval = temp_result; - } - } - - return retval; -} - -/* - * Return TRUE if character "c" can be used in a variable or function name. - * Does not include '{' or '}' for magic braces. - */ -static int eval_isnamec(int c) -{ - return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR; -} - -/* - * Return TRUE if character "c" can be used as the first character in a - * variable or function name (excluding '{' and '}'). - */ -static int eval_isnamec1(int c) -{ - return ASCII_ISALPHA(c) || c == '_'; -} - -/* - * Set number v: variable to "val". - */ -void set_vim_var_nr(int idx, long val) -{ - vimvars[idx].vv_nr = val; -} - -/* - * Get number v: variable value. - */ -long get_vim_var_nr(int idx) -{ - return vimvars[idx].vv_nr; -} - -/* - * Get string v: variable value. Uses a static buffer, can only be used once. - */ -char_u *get_vim_var_str(int idx) -{ - return get_tv_string(&vimvars[idx].vv_tv); -} - -/* - * Get List v: variable value. Caller must take care of reference count when - * needed. - */ -list_T *get_vim_var_list(int idx) -{ - return vimvars[idx].vv_list; -} - -/* - * Set v:char to character "c". - */ -void set_vim_var_char(int c) -{ - char_u buf[MB_MAXBYTES + 1]; - - if (has_mbyte) - buf[(*mb_char2bytes)(c, buf)] = NUL; - else { - buf[0] = c; - buf[1] = NUL; - } - set_vim_var_string(VV_CHAR, buf, -1); -} - -/* - * Set v:count to "count" and v:count1 to "count1". - * When "set_prevcount" is TRUE first set v:prevcount from v:count. - */ -void set_vcount(long count, long count1, int set_prevcount) -{ - if (set_prevcount) - vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr; - vimvars[VV_COUNT].vv_nr = count; - vimvars[VV_COUNT1].vv_nr = count1; -} - -/* - * Set string v: variable to a copy of "val". - */ -void -set_vim_var_string ( - int idx, - char_u *val, - int len /* length of "val" to use or -1 (whole string) */ -) -{ - /* Need to do this (at least) once, since we can't initialize a union. - * Will always be invoked when "v:progname" is set. */ - vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - - free(vimvars[idx].vv_str); - if (val == NULL) - vimvars[idx].vv_str = NULL; - else if (len == -1) - vimvars[idx].vv_str = vim_strsave(val); - else - vimvars[idx].vv_str = vim_strnsave(val, len); -} - -/* - * Set List v: variable to "val". - */ -void set_vim_var_list(int idx, list_T *val) -{ - list_unref(vimvars[idx].vv_list); - vimvars[idx].vv_list = val; - if (val != NULL) - ++val->lv_refcount; -} - -/* - * Set v:register if needed. - */ -void set_reg_var(int c) -{ - char_u regname; - - if (c == 0 || c == ' ') - regname = '"'; - else - regname = c; - /* Avoid free/alloc when the value is already right. */ - if (vimvars[VV_REG].vv_str == NULL || vimvars[VV_REG].vv_str[0] != c) - set_vim_var_string(VV_REG, ®name, 1); -} - -/* - * Get or set v:exception. If "oldval" == NULL, return the current value. - * Otherwise, restore the value to "oldval" and return NULL. - * Must always be called in pairs to save and restore v:exception! Does not - * take care of memory allocations. - */ -char_u *v_exception(char_u *oldval) -{ - if (oldval == NULL) - return vimvars[VV_EXCEPTION].vv_str; - - vimvars[VV_EXCEPTION].vv_str = oldval; - return NULL; -} - -/* - * Get or set v:throwpoint. If "oldval" == NULL, return the current value. - * Otherwise, restore the value to "oldval" and return NULL. - * Must always be called in pairs to save and restore v:throwpoint! Does not - * take care of memory allocations. - */ -char_u *v_throwpoint(char_u *oldval) -{ - if (oldval == NULL) - return vimvars[VV_THROWPOINT].vv_str; - - vimvars[VV_THROWPOINT].vv_str = oldval; - return NULL; -} - -/* - * Set v:cmdarg. - * If "eap" != NULL, use "eap" to generate the value and return the old value. - * If "oldarg" != NULL, restore the value to "oldarg" and return NULL. - * Must always be called in pairs! - */ -char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) -{ - char_u *oldval; - char_u *newval; - unsigned len; - - oldval = vimvars[VV_CMDARG].vv_str; - if (eap == NULL) { - free(oldval); - vimvars[VV_CMDARG].vv_str = oldarg; - return NULL; - } - - if (eap->force_bin == FORCE_BIN) - len = 6; - else if (eap->force_bin == FORCE_NOBIN) - len = 8; - else - len = 0; - - if (eap->read_edit) - len += 7; - - if (eap->force_ff != 0) - len += (unsigned)STRLEN(eap->cmd + eap->force_ff) + 6; - if (eap->force_enc != 0) - len += (unsigned)STRLEN(eap->cmd + eap->force_enc) + 7; - if (eap->bad_char != 0) - len += 7 + 4; /* " ++bad=" + "keep" or "drop" */ - - newval = alloc(len + 1); - if (newval == NULL) - return NULL; - - if (eap->force_bin == FORCE_BIN) - sprintf((char *)newval, " ++bin"); - else if (eap->force_bin == FORCE_NOBIN) - sprintf((char *)newval, " ++nobin"); - else - *newval = NUL; - - if (eap->read_edit) - STRCAT(newval, " ++edit"); - - if (eap->force_ff != 0) - sprintf((char *)newval + STRLEN(newval), " ++ff=%s", - eap->cmd + eap->force_ff); - if (eap->force_enc != 0) - sprintf((char *)newval + STRLEN(newval), " ++enc=%s", - eap->cmd + eap->force_enc); - if (eap->bad_char == BAD_KEEP) - STRCPY(newval + STRLEN(newval), " ++bad=keep"); - else if (eap->bad_char == BAD_DROP) - STRCPY(newval + STRLEN(newval), " ++bad=drop"); - else if (eap->bad_char != 0) - sprintf((char *)newval + STRLEN(newval), " ++bad=%c", eap->bad_char); - vimvars[VV_CMDARG].vv_str = newval; - return oldval; -} - -/* - * Get the value of internal variable "name". - * Return OK or FAIL. - */ -static int -get_var_tv ( - char_u *name, - int len, /* length of "name" */ - typval_T *rettv, /* NULL when only checking existence */ - int verbose, /* may give error message */ - int no_autoload /* do not use script autoloading */ -) -{ - int ret = OK; - typval_T *tv = NULL; - typval_T atv; - dictitem_T *v; - int cc; - - /* truncate the name, so that we can use strcmp() */ - cc = name[len]; - name[len] = NUL; - - /* - * Check for "b:changedtick". - */ - if (STRCMP(name, "b:changedtick") == 0) { - atv.v_type = VAR_NUMBER; - atv.vval.v_number = curbuf->b_changedtick; - tv = &atv; - } - /* - * Check for user-defined variables. - */ - else { - v = find_var(name, NULL, no_autoload); - if (v != NULL) - tv = &v->di_tv; - } - - if (tv == NULL) { - if (rettv != NULL && verbose) - EMSG2(_(e_undefvar), name); - ret = FAIL; - } else if (rettv != NULL) - copy_tv(tv, rettv); - - name[len] = cc; - - return ret; -} - -/* - * Handle expr[expr], expr[expr:expr] subscript and .name lookup. - * Also handle function call with Funcref variable: func(expr) - * Can all be combined: dict.func(expr)[idx]['func'](expr) - */ -static int -handle_subscript ( - char_u **arg, - typval_T *rettv, - int evaluate, /* do more than finding the end */ - int verbose /* give error messages */ -) -{ - int ret = OK; - dict_T *selfdict = NULL; - char_u *s; - int len; - typval_T functv; - - while (ret == OK - && (**arg == '[' - || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC))) - && !vim_iswhite(*(*arg - 1))) { - if (**arg == '(') { - /* need to copy the funcref so that we can clear rettv */ - if (evaluate) { - functv = *rettv; - rettv->v_type = VAR_UNKNOWN; - - /* Invoke the function. Recursive! */ - s = functv.vval.v_string; - } else - s = (char_u *)""; - ret = get_func_tv(s, (int)STRLEN(s), rettv, arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, selfdict); - - /* Clear the funcref afterwards, so that deleting it while - * evaluating the arguments is possible (see test55). */ - if (evaluate) - clear_tv(&functv); - - /* Stop the expression evaluation when immediately aborting on - * error, or when an interrupt occurred or an exception was thrown - * but not caught. */ - if (aborting()) { - if (ret == OK) - clear_tv(rettv); - ret = FAIL; - } - dict_unref(selfdict); - selfdict = NULL; - } else { /* **arg == '[' || **arg == '.' */ - dict_unref(selfdict); - if (rettv->v_type == VAR_DICT) { - selfdict = rettv->vval.v_dict; - if (selfdict != NULL) - ++selfdict->dv_refcount; - } else - selfdict = NULL; - if (eval_index(arg, rettv, evaluate, verbose) == FAIL) { - clear_tv(rettv); - ret = FAIL; - } - } - } - dict_unref(selfdict); - return ret; -} - -/* - * Free the memory for a variable type-value. - */ -void free_tv(typval_T *varp) -{ - if (varp != NULL) { - switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - /*FALLTHROUGH*/ - case VAR_STRING: - free(varp->vval.v_string); - break; - case VAR_LIST: - list_unref(varp->vval.v_list); - break; - case VAR_DICT: - dict_unref(varp->vval.v_dict); - break; - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - default: - EMSG2(_(e_intern2), "free_tv()"); - break; - } - free(varp); - } -} - -/* - * Free the memory for a variable value and set the value to NULL or 0. - */ -void clear_tv(typval_T *varp) -{ - if (varp != NULL) { - switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - /*FALLTHROUGH*/ - case VAR_STRING: - free(varp->vval.v_string); - varp->vval.v_string = NULL; - break; - case VAR_LIST: - list_unref(varp->vval.v_list); - varp->vval.v_list = NULL; - break; - case VAR_DICT: - dict_unref(varp->vval.v_dict); - varp->vval.v_dict = NULL; - break; - case VAR_NUMBER: - varp->vval.v_number = 0; - break; - case VAR_FLOAT: - varp->vval.v_float = 0.0; - break; - case VAR_UNKNOWN: - break; - default: - EMSG2(_(e_intern2), "clear_tv()"); - } - varp->v_lock = 0; - } -} - -/* - * Set the value of a variable to NULL without freeing items. - */ -static void init_tv(typval_T *varp) -{ - if (varp != NULL) - memset(varp, 0, sizeof(typval_T)); -} - -/* - * Get the number value of a variable. - * If it is a String variable, uses vim_str2nr(). - * For incompatible types, return 0. - * get_tv_number_chk() is similar to get_tv_number(), but informs the - * caller of incompatible types: it sets *denote to TRUE if "denote" - * is not NULL or returns -1 otherwise. - */ -static long get_tv_number(typval_T *varp) -{ - int error = FALSE; - - return get_tv_number_chk(varp, &error); /* return 0L on error */ -} - -long get_tv_number_chk(typval_T *varp, int *denote) -{ - long n = 0L; - - switch (varp->v_type) { - case VAR_NUMBER: - return (long)(varp->vval.v_number); - case VAR_FLOAT: - EMSG(_("E805: Using a Float as a Number")); - break; - case VAR_FUNC: - EMSG(_("E703: Using a Funcref as a Number")); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) - vim_str2nr(varp->vval.v_string, NULL, NULL, - TRUE, TRUE, &n, NULL); - return n; - case VAR_LIST: - EMSG(_("E745: Using a List as a Number")); - break; - case VAR_DICT: - EMSG(_("E728: Using a Dictionary as a Number")); - break; - default: - EMSG2(_(e_intern2), "get_tv_number()"); - break; - } - if (denote == NULL) /* useful for values that must be unsigned */ - n = -1; - else - *denote = TRUE; - return n; -} - -/* - * Get the lnum from the first argument. - * Also accepts ".", "$", etc., but that only works for the current buffer. - * Returns -1 on error. - */ -static linenr_T get_tv_lnum(typval_T *argvars) -{ - typval_T rettv; - linenr_T lnum; - - lnum = get_tv_number_chk(&argvars[0], NULL); - if (lnum == 0) { /* no valid number, try using line() */ - rettv.v_type = VAR_NUMBER; - f_line(argvars, &rettv); - lnum = rettv.vval.v_number; - clear_tv(&rettv); - } - return lnum; -} - -/* - * Get the lnum from the first argument. - * Also accepts "$", then "buf" is used. - * Returns 0 on error. - */ -static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) -{ - if (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && argvars[0].vval.v_string[0] == '$' - && buf != NULL) - return buf->b_ml.ml_line_count; - return get_tv_number_chk(&argvars[0], NULL); -} - -/* - * Get the string value of a variable. - * If it is a Number variable, the number is converted into a string. - * get_tv_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! - * get_tv_string_buf() uses a given buffer. - * If the String variable has never been set, return an empty string. - * Never returns NULL; - * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return - * NULL on error. - */ -static char_u *get_tv_string(typval_T *varp) -{ - static char_u mybuf[NUMBUFLEN]; - - return get_tv_string_buf(varp, mybuf); -} - -static char_u *get_tv_string_buf(typval_T *varp, char_u *buf) -{ - char_u *res = get_tv_string_buf_chk(varp, buf); - - return res != NULL ? res : (char_u *)""; -} - -char_u *get_tv_string_chk(typval_T *varp) -{ - static char_u mybuf[NUMBUFLEN]; - - return get_tv_string_buf_chk(varp, mybuf); -} - -static char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf) -{ - switch (varp->v_type) { - case VAR_NUMBER: - sprintf((char *)buf, "%" PRId64, (int64_t)varp->vval.v_number); - return buf; - case VAR_FUNC: - EMSG(_("E729: using Funcref as a String")); - break; - case VAR_LIST: - EMSG(_("E730: using List as a String")); - break; - case VAR_DICT: - EMSG(_("E731: using Dictionary as a String")); - break; - case VAR_FLOAT: - EMSG(_(e_float_as_string)); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) - return varp->vval.v_string; - return (char_u *)""; - default: - EMSG2(_(e_intern2), "get_tv_string_buf()"); - break; - } - return NULL; -} - -/* - * Find variable "name" in the list of variables. - * Return a pointer to it if found, NULL if not found. - * Careful: "a:0" variables don't have a name. - * When "htp" is not NULL we are writing to the variable, set "htp" to the - * hashtab_T used. - */ -static dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload) -{ - char_u *varname; - hashtab_T *ht; - - ht = find_var_ht(name, &varname); - if (htp != NULL) - *htp = ht; - if (ht == NULL) - return NULL; - return find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL); -} - -/* - * Find variable "varname" in hashtab "ht" with name "htname". - * Returns NULL if not found. - */ -static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload) -{ - hashitem_T *hi; - - if (*varname == NUL) { - /* Must be something like "s:", otherwise "ht" would be NULL. */ - switch (htname) { - case 's': return &SCRIPT_SV(current_SID)->sv_var; - case 'g': return &globvars_var; - case 'v': return &vimvars_var; - case 'b': return &curbuf->b_bufvar; - case 'w': return &curwin->w_winvar; - case 't': return &curtab->tp_winvar; - case 'l': return current_funccal == NULL - ? NULL : ¤t_funccal->l_vars_var; - case 'a': return current_funccal == NULL - ? NULL : ¤t_funccal->l_avars_var; - } - return NULL; - } - - hi = hash_find(ht, varname); - if (HASHITEM_EMPTY(hi)) { - /* For global variables we may try auto-loading the script. If it - * worked find the variable again. Don't auto-load a script if it was - * loaded already, otherwise it would be loaded every time when - * checking if a function name is a Funcref variable. */ - if (ht == &globvarht && !no_autoload) { - /* Note: script_autoload() may make "hi" invalid. It must either - * be obtained again or not used. */ - if (!script_autoload(varname, FALSE) || aborting()) - return NULL; - hi = hash_find(ht, varname); - } - if (HASHITEM_EMPTY(hi)) - return NULL; - } - return HI2DI(hi); -} - -/* - * Find the hashtab used for a variable name. - * Set "varname" to the start of name without ':'. - */ -static hashtab_T *find_var_ht(char_u *name, char_u **varname) -{ - hashitem_T *hi; - - if (name[1] != ':') { - /* The name must not start with a colon or #. */ - if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) - return NULL; - *varname = name; - - /* "version" is "v:version" in all scopes */ - hi = hash_find(&compat_hashtab, name); - if (!HASHITEM_EMPTY(hi)) - return &compat_hashtab; - - if (current_funccal == NULL) - return &globvarht; /* global variable */ - return ¤t_funccal->l_vars.dv_hashtab; /* l: variable */ - } - *varname = name + 2; - if (*name == 'g') /* global variable */ - return &globvarht; - /* There must be no ':' or '#' in the rest of the name, unless g: is used - */ - if (vim_strchr(name + 2, ':') != NULL - || vim_strchr(name + 2, AUTOLOAD_CHAR) != NULL) - return NULL; - if (*name == 'b') /* buffer variable */ - return &curbuf->b_vars->dv_hashtab; - if (*name == 'w') /* window variable */ - return &curwin->w_vars->dv_hashtab; - if (*name == 't') /* tab page variable */ - return &curtab->tp_vars->dv_hashtab; - if (*name == 'v') /* v: variable */ - return &vimvarht; - if (*name == 'a' && current_funccal != NULL) /* function argument */ - return ¤t_funccal->l_avars.dv_hashtab; - if (*name == 'l' && current_funccal != NULL) /* local function variable */ - return ¤t_funccal->l_vars.dv_hashtab; - if (*name == 's' /* script variable */ - && current_SID > 0 && current_SID <= ga_scripts.ga_len) - return &SCRIPT_VARS(current_SID); - return NULL; -} - -/* - * Get the string value of a (global/local) variable. - * Note: see get_tv_string() for how long the pointer remains valid. - * Returns NULL when it doesn't exist. - */ -char_u *get_var_value(char_u *name) -{ - dictitem_T *v; - - v = find_var(name, NULL, FALSE); - if (v == NULL) - return NULL; - return get_tv_string(&v->di_tv); -} - -/* - * Allocate a new hashtab for a sourced script. It will be used while - * sourcing this script and when executing functions defined in the script. - */ -void new_script_vars(scid_T id) -{ - int i; - hashtab_T *ht; - scriptvar_T *sv; - - ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)); - { - /* Re-allocating ga_data means that an ht_array pointing to - * ht_smallarray becomes invalid. We can recognize this: ht_mask is - * at its init value. Also reset "v_dict", it's always the same. */ - for (i = 1; i <= ga_scripts.ga_len; ++i) { - ht = &SCRIPT_VARS(i); - if (ht->ht_mask == HT_INIT_SIZE - 1) - ht->ht_array = ht->ht_smallarray; - sv = SCRIPT_SV(i); - sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict; - } - - while (ga_scripts.ga_len < id) { - sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T)); - init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE); - ++ga_scripts.ga_len; - } - } -} - -/* - * Initialize dictionary "dict" as a scope and set variable "dict_var" to - * point to it. - */ -void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope) -{ - hash_init(&dict->dv_hashtab); - dict->dv_lock = 0; - dict->dv_scope = scope; - dict->dv_refcount = DO_NOT_FREE_CNT; - dict->dv_copyID = 0; - dict_var->di_tv.vval.v_dict = dict; - dict_var->di_tv.v_type = VAR_DICT; - dict_var->di_tv.v_lock = VAR_FIXED; - dict_var->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - dict_var->di_key[0] = NUL; -} - -/* - * Unreference a dictionary initialized by init_var_dict(). - */ -void unref_var_dict(dict_T *dict) -{ - /* Now the dict needs to be freed if no one else is using it, go back to - * normal reference counting. */ - dict->dv_refcount -= DO_NOT_FREE_CNT - 1; - dict_unref(dict); -} - -/* - * Clean up a list of internal variables. - * Frees all allocated variables and the value they contain. - * Clears hashtab "ht", does not free it. - */ -void vars_clear(hashtab_T *ht) -{ - vars_clear_ext(ht, TRUE); -} - -/* - * Like vars_clear(), but only free the value if "free_val" is TRUE. - */ -static void vars_clear_ext(hashtab_T *ht, int free_val) -{ - int todo; - hashitem_T *hi; - dictitem_T *v; - - hash_lock(ht); - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - /* Free the variable. Don't remove it from the hashtab, - * ht_array might change then. hash_clear() takes care of it - * later. */ - v = HI2DI(hi); - if (free_val) - clear_tv(&v->di_tv); - if ((v->di_flags & DI_FLAGS_FIX) == 0) - free(v); - } - } - hash_clear(ht); - ht->ht_used = 0; -} - -/* - * Delete a variable from hashtab "ht" at item "hi". - * Clear the variable value and free the dictitem. - */ -static void delete_var(hashtab_T *ht, hashitem_T *hi) -{ - dictitem_T *di = HI2DI(hi); - - hash_remove(ht, hi); - clear_tv(&di->di_tv); - free(di); -} - -/* - * List the value of one internal variable. - */ -static void list_one_var(dictitem_T *v, char_u *prefix, int *first) -{ - char_u *tofree; - char_u *s; - char_u numbuf[NUMBUFLEN]; - - current_copyID += COPYID_INC; - s = echo_string(&v->di_tv, &tofree, numbuf, current_copyID); - list_one_var_a(prefix, v->di_key, v->di_tv.v_type, - s == NULL ? (char_u *)"" : s, first); - free(tofree); -} - -static void -list_one_var_a ( - char_u *prefix, - char_u *name, - int type, - char_u *string, - int *first /* when TRUE clear rest of screen and set to FALSE */ -) -{ - /* don't use msg() or msg_attr() to avoid overwriting "v:statusmsg" */ - msg_start(); - msg_puts(prefix); - if (name != NULL) /* "a:" vars don't have a name stored */ - msg_puts(name); - msg_putchar(' '); - msg_advance(22); - if (type == VAR_NUMBER) - msg_putchar('#'); - else if (type == VAR_FUNC) - msg_putchar('*'); - else if (type == VAR_LIST) { - msg_putchar('['); - if (*string == '[') - ++string; - } else if (type == VAR_DICT) { - msg_putchar('{'); - if (*string == '{') - ++string; - } else - msg_putchar(' '); - - msg_outtrans(string); - - if (type == VAR_FUNC) - msg_puts((char_u *)"()"); - if (*first) { - msg_clr_eos(); - *first = FALSE; - } -} - -/* - * Set variable "name" to value in "tv". - * If the variable already exists, the value is updated. - * Otherwise the variable is created. - */ -static void -set_var ( - char_u *name, - typval_T *tv, - int copy /* make copy of value in "tv" */ -) -{ - dictitem_T *v; - char_u *varname; - hashtab_T *ht; - - ht = find_var_ht(name, &varname); - if (ht == NULL || *varname == NUL) { - EMSG2(_(e_illvar), name); - return; - } - v = find_var_in_ht(ht, 0, varname, TRUE); - - if (tv->v_type == VAR_FUNC && var_check_func_name(name, v == NULL)) - return; - - if (v != NULL) { - /* existing variable, need to clear the value */ - if (var_check_ro(v->di_flags, name) - || tv_check_lock(v->di_tv.v_lock, name)) - return; - if (v->di_tv.v_type != tv->v_type - && !((v->di_tv.v_type == VAR_STRING - || v->di_tv.v_type == VAR_NUMBER) - && (tv->v_type == VAR_STRING - || tv->v_type == VAR_NUMBER)) - && !((v->di_tv.v_type == VAR_NUMBER - || v->di_tv.v_type == VAR_FLOAT) - && (tv->v_type == VAR_NUMBER - || tv->v_type == VAR_FLOAT)) - ) { - EMSG2(_("E706: Variable type mismatch for: %s"), name); - return; - } - - /* - * Handle setting internal v: variables separately: we don't change - * the type. - */ - if (ht == &vimvarht) { - if (v->di_tv.v_type == VAR_STRING) { - free(v->di_tv.vval.v_string); - if (copy || tv->v_type != VAR_STRING) - v->di_tv.vval.v_string = vim_strsave(get_tv_string(tv)); - else { - /* Take over the string to avoid an extra alloc/free. */ - v->di_tv.vval.v_string = tv->vval.v_string; - tv->vval.v_string = NULL; - } - } else if (v->di_tv.v_type != VAR_NUMBER) - EMSG2(_(e_intern2), "set_var()"); - else { - v->di_tv.vval.v_number = get_tv_number(tv); - if (STRCMP(varname, "searchforward") == 0) - set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); - else if (STRCMP(varname, "hlsearch") == 0) { - no_hlsearch = !v->di_tv.vval.v_number; - redraw_all_later(SOME_VALID); - } - } - return; - } - - clear_tv(&v->di_tv); - } else { /* add a new variable */ - /* Can't add "v:" variable. */ - if (ht == &vimvarht) { - EMSG2(_(e_illvar), name); - return; - } - - /* Make sure the variable name is valid. */ - if (!valid_varname(varname)) - return; - - v = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) - + STRLEN(varname))); - if (v == NULL) - return; - STRCPY(v->di_key, varname); - if (hash_add(ht, DI2HIKEY(v)) == FAIL) { - free(v); - return; - } - v->di_flags = 0; - } - - if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) - copy_tv(tv, &v->di_tv); - else { - v->di_tv = *tv; - v->di_tv.v_lock = 0; - init_tv(tv); - } -} - -/* - * Return TRUE if di_flags "flags" indicates variable "name" is read-only. - * Also give an error message. - */ -static int var_check_ro(int flags, char_u *name) -{ - if (flags & DI_FLAGS_RO) { - EMSG2(_(e_readonlyvar), name); - return TRUE; - } - if ((flags & DI_FLAGS_RO_SBX) && sandbox) { - EMSG2(_(e_readonlysbx), name); - return TRUE; - } - return FALSE; -} - -/* - * Return TRUE if di_flags "flags" indicates variable "name" is fixed. - * Also give an error message. - */ -static int var_check_fixed(int flags, char_u *name) -{ - if (flags & DI_FLAGS_FIX) { - EMSG2(_("E795: Cannot delete variable %s"), name); - return TRUE; - } - return FALSE; -} - -/* - * Check if a funcref is assigned to a valid variable name. - * Return TRUE and give an error if not. - */ -static int -var_check_func_name ( - char_u *name, /* points to start of variable name */ - int new_var /* TRUE when creating the variable */ -) -{ - if (!(vim_strchr((char_u *)"wbs", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') - ? name[2] : name[0])) { - EMSG2(_("E704: Funcref variable name must start with a capital: %s"), - name); - return TRUE; - } - /* Don't allow hiding a function. When "v" is not NULL we might be - * assigning another function to the same var, the type is checked - * below. */ - if (new_var && function_exists(name)) { - EMSG2(_("E705: Variable name conflicts with existing function: %s"), - name); - return TRUE; - } - return FALSE; -} - -/* - * Check if a variable name is valid. - * Return FALSE and give an error if not. - */ -static int valid_varname(char_u *varname) -{ - char_u *p; - - for (p = varname; *p != NUL; ++p) - if (!eval_isnamec1(*p) && (p == varname || !VIM_ISDIGIT(*p)) - && *p != AUTOLOAD_CHAR) { - EMSG2(_(e_illvar), varname); - return FALSE; - } - return TRUE; -} - -/* - * Return TRUE if typeval "tv" is set to be locked (immutable). - * Also give an error message, using "name". - */ -static int tv_check_lock(int lock, char_u *name) -{ - if (lock & VAR_LOCKED) { - EMSG2(_("E741: Value is locked: %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; - } - if (lock & VAR_FIXED) { - EMSG2(_("E742: Cannot change value of %s"), - name == NULL ? (char_u *)_("Unknown") : name); - return TRUE; - } - return FALSE; -} - -/* - * Copy the values from typval_T "from" to typval_T "to". - * When needed allocates string or increases reference count. - * Does not make a copy of a list or dict but copies the reference! - * It is OK for "from" and "to" to point to the same item. This is used to - * make a copy later. - */ -void copy_tv(typval_T *from, typval_T *to) -{ - to->v_type = from->v_type; - to->v_lock = 0; - switch (from->v_type) { - case VAR_NUMBER: - to->vval.v_number = from->vval.v_number; - break; - case VAR_FLOAT: - to->vval.v_float = from->vval.v_float; - break; - case VAR_STRING: - case VAR_FUNC: - if (from->vval.v_string == NULL) - to->vval.v_string = NULL; - else { - to->vval.v_string = vim_strsave(from->vval.v_string); - if (from->v_type == VAR_FUNC) - func_ref(to->vval.v_string); - } - break; - case VAR_LIST: - if (from->vval.v_list == NULL) - to->vval.v_list = NULL; - else { - to->vval.v_list = from->vval.v_list; - ++to->vval.v_list->lv_refcount; - } - break; - case VAR_DICT: - if (from->vval.v_dict == NULL) - to->vval.v_dict = NULL; - else { - to->vval.v_dict = from->vval.v_dict; - ++to->vval.v_dict->dv_refcount; - } - break; - default: - EMSG2(_(e_intern2), "copy_tv()"); - break; - } -} - -/* - * Make a copy of an item. - * Lists and Dictionaries are also copied. A deep copy if "deep" is set. - * For deepcopy() "copyID" is zero for a full copy or the ID for when a - * reference to an already copied list/dict can be used. - * Returns FAIL or OK. - */ -static int item_copy(typval_T *from, typval_T *to, int deep, int copyID) -{ - static int recurse = 0; - int ret = OK; - - if (recurse >= DICT_MAXNEST) { - EMSG(_("E698: variable nested too deep for making a copy")); - return FAIL; - } - ++recurse; - - switch (from->v_type) { - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_STRING: - case VAR_FUNC: - copy_tv(from, to); - break; - case VAR_LIST: - to->v_type = VAR_LIST; - to->v_lock = 0; - if (from->vval.v_list == NULL) - to->vval.v_list = NULL; - else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) { - /* use the copy made earlier */ - to->vval.v_list = from->vval.v_list->lv_copylist; - ++to->vval.v_list->lv_refcount; - } else - to->vval.v_list = list_copy(from->vval.v_list, deep, copyID); - if (to->vval.v_list == NULL) - ret = FAIL; - break; - case VAR_DICT: - to->v_type = VAR_DICT; - to->v_lock = 0; - if (from->vval.v_dict == NULL) - to->vval.v_dict = NULL; - else if (copyID != 0 && from->vval.v_dict->dv_copyID == copyID) { - /* use the copy made earlier */ - to->vval.v_dict = from->vval.v_dict->dv_copydict; - ++to->vval.v_dict->dv_refcount; - } else - to->vval.v_dict = dict_copy(from->vval.v_dict, deep, copyID); - if (to->vval.v_dict == NULL) - ret = FAIL; - break; - default: - EMSG2(_(e_intern2), "item_copy()"); - ret = FAIL; - } - --recurse; - return ret; -} - -/* - * ":echo expr1 ..." print each argument separated with a space, add a - * newline at the end. - * ":echon expr1 ..." print each argument plain. - */ -void ex_echo(exarg_T *eap) -{ - char_u *arg = eap->arg; - typval_T rettv; - char_u *tofree; - char_u *p; - int needclr = TRUE; - int atstart = TRUE; - char_u numbuf[NUMBUFLEN]; - - if (eap->skip) - ++emsg_skip; - while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) { - /* If eval1() causes an error message the text from the command may - * still need to be cleared. E.g., "echo 22,44". */ - need_clr_eos = needclr; - - p = arg; - if (eval1(&arg, &rettv, !eap->skip) == FAIL) { - /* - * Report the invalid expression unless the expression evaluation - * has been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting()) - EMSG2(_(e_invexpr2), p); - need_clr_eos = FALSE; - break; - } - need_clr_eos = FALSE; - - if (!eap->skip) { - if (atstart) { - atstart = FALSE; - /* Call msg_start() after eval1(), evaluating the expression - * may cause a message to appear. */ - if (eap->cmdidx == CMD_echo) { - /* Mark the saved text as finishing the line, so that what - * follows is displayed on a new line when scrolling back - * at the more prompt. */ - msg_sb_eol(); - msg_start(); - } - } else if (eap->cmdidx == CMD_echo) - msg_puts_attr((char_u *)" ", echo_attr); - current_copyID += COPYID_INC; - p = echo_string(&rettv, &tofree, numbuf, current_copyID); - if (p != NULL) - for (; *p != NUL && !got_int; ++p) { - if (*p == '\n' || *p == '\r' || *p == TAB) { - if (*p != TAB && needclr) { - /* remove any text still there from the command */ - msg_clr_eos(); - needclr = FALSE; - } - msg_putchar_attr(*p, echo_attr); - } else { - if (has_mbyte) { - int i = (*mb_ptr2len)(p); - - (void)msg_outtrans_len_attr(p, i, echo_attr); - p += i - 1; - } else - (void)msg_outtrans_len_attr(p, 1, echo_attr); - } - } - free(tofree); - } - clear_tv(&rettv); - arg = skipwhite(arg); - } - eap->nextcmd = check_nextcmd(arg); - - if (eap->skip) - --emsg_skip; - else { - /* remove text that may still be there from the command */ - if (needclr) - msg_clr_eos(); - if (eap->cmdidx == CMD_echo) - msg_end(); - } -} - -/* - * ":echohl {name}". - */ -void ex_echohl(exarg_T *eap) -{ - int id; - - id = syn_name2id(eap->arg); - if (id == 0) - echo_attr = 0; - else - echo_attr = syn_id2attr(id); -} - -/* - * ":execute expr1 ..." execute the result of an expression. - * ":echomsg expr1 ..." Print a message - * ":echoerr expr1 ..." Print an error - * Each gets spaces around each argument and a newline at the end for - * echo commands - */ -void ex_execute(exarg_T *eap) -{ - char_u *arg = eap->arg; - typval_T rettv; - int ret = OK; - char_u *p; - garray_T ga; - int len; - int save_did_emsg; - - ga_init(&ga, 1, 80); - - if (eap->skip) - ++emsg_skip; - while (*arg != NUL && *arg != '|' && *arg != '\n') { - p = arg; - if (eval1(&arg, &rettv, !eap->skip) == FAIL) { - /* - * Report the invalid expression unless the expression evaluation - * has been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting()) - EMSG2(_(e_invexpr2), p); - ret = FAIL; - break; - } - - if (!eap->skip) { - p = get_tv_string(&rettv); - len = (int)STRLEN(p); - ga_grow(&ga, len + 2); - if (ga.ga_len) - ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; - STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); - ga.ga_len += len; - } - - clear_tv(&rettv); - arg = skipwhite(arg); - } - - if (ret != FAIL && ga.ga_data != NULL) { - if (eap->cmdidx == CMD_echomsg) { - MSG_ATTR(ga.ga_data, echo_attr); - out_flush(); - } else if (eap->cmdidx == CMD_echoerr) { - /* We don't want to abort following commands, restore did_emsg. */ - save_did_emsg = did_emsg; - EMSG((char_u *)ga.ga_data); - if (!force_abort) - did_emsg = save_did_emsg; - } else if (eap->cmdidx == CMD_execute) - do_cmdline((char_u *)ga.ga_data, - eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); - } - - ga_clear(&ga); - - if (eap->skip) - --emsg_skip; - - eap->nextcmd = check_nextcmd(arg); -} - -/* - * Skip over the name of an option: "&option", "&g:option" or "&l:option". - * "arg" points to the "&" or '+' when called, to "option" when returning. - * Returns NULL when no option name found. Otherwise pointer to the char - * after the option name. - */ -static char_u *find_option_end(char_u **arg, int *opt_flags) -{ - char_u *p = *arg; - - ++p; - if (*p == 'g' && p[1] == ':') { - *opt_flags = OPT_GLOBAL; - p += 2; - } else if (*p == 'l' && p[1] == ':') { - *opt_flags = OPT_LOCAL; - p += 2; - } else - *opt_flags = 0; - - if (!ASCII_ISALPHA(*p)) - return NULL; - *arg = p; - - if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) - p += 4; /* termcap option */ - else - while (ASCII_ISALPHA(*p)) - ++p; - return p; -} - -/* - * ":function" - */ -void ex_function(exarg_T *eap) -{ - char_u *theline; - int i; - int j; - int c; - int saved_did_emsg; - int saved_wait_return = need_wait_return; - char_u *name = NULL; - char_u *p; - char_u *arg; - char_u *line_arg = NULL; - garray_T newargs; - garray_T newlines; - int varargs = FALSE; - int mustend = FALSE; - int flags = 0; - ufunc_T *fp; - int indent; - int nesting; - char_u *skip_until = NULL; - dictitem_T *v; - funcdict_T fudi; - static int func_nr = 0; /* number for nameless function */ - int paren; - hashtab_T *ht; - int todo; - hashitem_T *hi; - int sourcing_lnum_off; - - /* - * ":function" without argument: list functions. - */ - if (ends_excmd(*eap->arg)) { - if (!eap->skip) { - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - fp = HI2UF(hi); - if (!isdigit(*fp->uf_name)) - list_func_head(fp, FALSE); - } - } - } - eap->nextcmd = check_nextcmd(eap->arg); - return; - } - - /* - * ":function /pat": list functions matching pattern. - */ - if (*eap->arg == '/') { - p = skip_regexp(eap->arg + 1, '/', TRUE, NULL); - if (!eap->skip) { - regmatch_T regmatch; - - c = *p; - *p = NUL; - regmatch.regprog = vim_regcomp(eap->arg + 1, RE_MAGIC); - *p = c; - if (regmatch.regprog != NULL) { - regmatch.rm_ic = p_ic; - - todo = (int)func_hashtab.ht_used; - for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - fp = HI2UF(hi); - if (!isdigit(*fp->uf_name) - && vim_regexec(®match, fp->uf_name, 0)) - list_func_head(fp, FALSE); - } - } - vim_regfree(regmatch.regprog); - } - } - if (*p == '/') - ++p; - eap->nextcmd = check_nextcmd(p); - return; - } - - // Get the function name. There are these situations: - // func function name - // "name" == func, "fudi.fd_dict" == NULL - // dict.func new dictionary entry - // "name" == NULL, "fudi.fd_dict" set, - // "fudi.fd_di" == NULL, "fudi.fd_newkey" == func - // dict.func existing dict entry with a Funcref - // "name" == func, "fudi.fd_dict" set, - // "fudi.fd_di" set, "fudi.fd_newkey" == NULL - // dict.func existing dict entry that's not a Funcref - // "name" == NULL, "fudi.fd_dict" set, - // "fudi.fd_di" set, "fudi.fd_newkey" == NULL - // s:func script-local function name - // g:func global function name, same as "func" - p = eap->arg; - name = trans_function_name(&p, eap->skip, 0, &fudi); - paren = (vim_strchr(p, '(') != NULL); - if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) { - /* - * Return on an invalid expression in braces, unless the expression - * evaluation has been cancelled due to an aborting error, an - * interrupt, or an exception. - */ - if (!aborting()) { - if (!eap->skip && fudi.fd_newkey != NULL) - EMSG2(_(e_dictkey), fudi.fd_newkey); - free(fudi.fd_newkey); - return; - } else - eap->skip = TRUE; - } - - /* An error in a function call during evaluation of an expression in magic - * braces should not cause the function not to be defined. */ - saved_did_emsg = did_emsg; - did_emsg = FALSE; - - /* - * ":function func" with only function name: list function. - */ - if (!paren) { - if (!ends_excmd(*skipwhite(p))) { - EMSG(_(e_trailing)); - goto ret_free; - } - eap->nextcmd = check_nextcmd(p); - if (eap->nextcmd != NULL) - *p = NUL; - if (!eap->skip && !got_int) { - fp = find_func(name); - if (fp != NULL) { - list_func_head(fp, TRUE); - for (j = 0; j < fp->uf_lines.ga_len && !got_int; ++j) { - if (FUNCLINE(fp, j) == NULL) - continue; - msg_putchar('\n'); - msg_outnum((long)(j + 1)); - if (j < 9) - msg_putchar(' '); - if (j < 99) - msg_putchar(' '); - msg_prt_line(FUNCLINE(fp, j), FALSE); - out_flush(); /* show a line at a time */ - ui_breakcheck(); - } - if (!got_int) { - msg_putchar('\n'); - msg_puts((char_u *)" endfunction"); - } - } else - emsg_funcname(N_("E123: Undefined function: %s"), name); - } - goto ret_free; - } - - /* - * ":function name(arg1, arg2)" Define function. - */ - p = skipwhite(p); - if (*p != '(') { - if (!eap->skip) { - EMSG2(_("E124: Missing '(': %s"), eap->arg); - goto ret_free; - } - /* attempt to continue by skipping some text */ - if (vim_strchr(p, '(') != NULL) - p = vim_strchr(p, '('); - } - p = skipwhite(p + 1); - - ga_init(&newargs, (int)sizeof(char_u *), 3); - ga_init(&newlines, (int)sizeof(char_u *), 3); - - if (!eap->skip) { - /* Check the name of the function. Unless it's a dictionary function - * (that we are overwriting). */ - if (name != NULL) - arg = name; - else - arg = fudi.fd_newkey; - if (arg != NULL && (fudi.fd_di == NULL - || fudi.fd_di->di_tv.v_type != VAR_FUNC)) { - if (*arg == K_SPECIAL) - j = 3; - else - j = 0; - while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) - : eval_isnamec(arg[j]))) - ++j; - if (arg[j] != NUL) - emsg_funcname((char *)e_invarg2, arg); - } - /* Disallow using the g: dict. */ - if (fudi.fd_dict != NULL && fudi.fd_dict->dv_scope == VAR_DEF_SCOPE) - EMSG(_("E862: Cannot use g: here")); - } - - /* - * Isolate the arguments: "arg1, arg2, ...)" - */ - while (*p != ')') { - if (p[0] == '.' && p[1] == '.' && p[2] == '.') { - varargs = TRUE; - p += 3; - mustend = TRUE; - } else { - arg = p; - while (ASCII_ISALNUM(*p) || *p == '_') - ++p; - if (arg == p || isdigit(*arg) - || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0) - || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) { - if (!eap->skip) - EMSG2(_("E125: Illegal argument: %s"), arg); - break; - } - ga_grow(&newargs, 1); - c = *p; - *p = NUL; - arg = vim_strsave(arg); - if (arg == NULL) - goto erret; - - /* Check for duplicate argument name. */ - for (i = 0; i < newargs.ga_len; ++i) - if (STRCMP(((char_u **)(newargs.ga_data))[i], arg) == 0) { - EMSG2(_("E853: Duplicate argument name: %s"), arg); - free(arg); - goto erret; - } - - ((char_u **)(newargs.ga_data))[newargs.ga_len] = arg; - *p = c; - newargs.ga_len++; - if (*p == ',') - ++p; - else - mustend = TRUE; - } - p = skipwhite(p); - if (mustend && *p != ')') { - if (!eap->skip) - EMSG2(_(e_invarg2), eap->arg); - break; - } - } - ++p; /* skip the ')' */ - - /* find extra arguments "range", "dict" and "abort" */ - for (;; ) { - p = skipwhite(p); - if (STRNCMP(p, "range", 5) == 0) { - flags |= FC_RANGE; - p += 5; - } else if (STRNCMP(p, "dict", 4) == 0) { - flags |= FC_DICT; - p += 4; - } else if (STRNCMP(p, "abort", 5) == 0) { - flags |= FC_ABORT; - p += 5; - } else - break; - } - - /* When there is a line break use what follows for the function body. - * Makes 'exe "func Test()\n...\nendfunc"' work. */ - if (*p == '\n') - line_arg = p + 1; - else if (*p != NUL && *p != '"' && !eap->skip && !did_emsg) - EMSG(_(e_trailing)); - - /* - * Read the body of the function, until ":endfunction" is found. - */ - if (KeyTyped) { - /* Check if the function already exists, don't let the user type the - * whole function before telling him it doesn't work! For a script we - * need to skip the body to be able to find what follows. */ - if (!eap->skip && !eap->forceit) { - if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL) - EMSG(_(e_funcdict)); - else if (name != NULL && find_func(name) != NULL) - emsg_funcname(e_funcexts, name); - } - - if (!eap->skip && did_emsg) - goto erret; - - msg_putchar('\n'); /* don't overwrite the function name */ - cmdline_row = msg_row; - } - - indent = 2; - nesting = 0; - for (;; ) { - if (KeyTyped) { - msg_scroll = TRUE; - saved_wait_return = FALSE; - } - need_wait_return = FALSE; - sourcing_lnum_off = sourcing_lnum; - - if (line_arg != NULL) { - /* Use eap->arg, split up in parts by line breaks. */ - theline = line_arg; - p = vim_strchr(theline, '\n'); - if (p == NULL) - line_arg += STRLEN(line_arg); - else { - *p = NUL; - line_arg = p + 1; - } - } else if (eap->getline == NULL) - theline = getcmdline(':', 0L, indent); - else - theline = eap->getline(':', eap->cookie, indent); - if (KeyTyped) - lines_left = Rows - 1; - if (theline == NULL) { - EMSG(_("E126: Missing :endfunction")); - goto erret; - } - - /* Detect line continuation: sourcing_lnum increased more than one. */ - if (sourcing_lnum > sourcing_lnum_off + 1) - sourcing_lnum_off = sourcing_lnum - sourcing_lnum_off - 1; - else - sourcing_lnum_off = 0; - - if (skip_until != NULL) { - /* between ":append" and "." and between ":python < 2 && STRNCMP(p, "end", 3) == 0) - indent -= 2; - else if (STRNCMP(p, "if", 2) == 0 - || STRNCMP(p, "wh", 2) == 0 - || STRNCMP(p, "for", 3) == 0 - || STRNCMP(p, "try", 3) == 0) - indent += 2; - - /* Check for defining a function inside this function. */ - if (checkforcmd(&p, "function", 2)) { - if (*p == '!') - p = skipwhite(p + 1); - p += eval_fname_script(p); - if (ASCII_ISALPHA(*p)) { - free(trans_function_name(&p, TRUE, 0, NULL)); - if (*skipwhite(p) == '(') { - ++nesting; - indent += 2; - } - } - } - - /* Check for ":append" or ":insert". */ - p = skip_range(p, NULL); - if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p')) - || (p[0] == 'i' - && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n' - && (!ASCII_ISALPHA(p[2]) || - (p[2] == 's')))))) - skip_until = vim_strsave((char_u *)"."); - - /* Check for ":python < 0) - ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL; - - /* Check for end of eap->arg. */ - if (line_arg != NULL && *line_arg == NUL) - line_arg = NULL; - } - - /* Don't define the function when skipping commands or when an error was - * detected. */ - if (eap->skip || did_emsg) - goto erret; - - /* - * If there are no errors, add the function - */ - if (fudi.fd_dict == NULL) { - v = find_var(name, &ht, FALSE); - if (v != NULL && v->di_tv.v_type == VAR_FUNC) { - emsg_funcname(N_("E707: Function name conflicts with variable: %s"), - name); - goto erret; - } - - fp = find_func(name); - if (fp != NULL) { - if (!eap->forceit) { - emsg_funcname(e_funcexts, name); - goto erret; - } - if (fp->uf_calls > 0) { - emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), - name); - goto erret; - } - /* redefine existing function */ - ga_clear_strings(&(fp->uf_args)); - ga_clear_strings(&(fp->uf_lines)); - free(name); - name = NULL; - } - } else { - char numbuf[20]; - - fp = NULL; - if (fudi.fd_newkey == NULL && !eap->forceit) { - EMSG(_(e_funcdict)); - goto erret; - } - if (fudi.fd_di == NULL) { - /* Can't add a function to a locked dictionary */ - if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg)) - goto erret; - } - /* Can't change an existing function if it is locked */ - else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg)) - goto erret; - - /* Give the function a sequential number. Can only be used with a - * Funcref! */ - free(name); - sprintf(numbuf, "%d", ++func_nr); - name = vim_strsave((char_u *)numbuf); - if (name == NULL) - goto erret; - } - - if (fp == NULL) { - if (fudi.fd_dict == NULL && vim_strchr(name, AUTOLOAD_CHAR) != NULL) { - int slen, plen; - char_u *scriptname; - - /* Check that the autoload name matches the script name. */ - j = FAIL; - if (sourcing_name != NULL) { - scriptname = autoload_name(name); - if (scriptname != NULL) { - p = vim_strchr(scriptname, '/'); - plen = (int)STRLEN(p); - slen = (int)STRLEN(sourcing_name); - if (slen > plen && fnamecmp(p, - sourcing_name + slen - plen) == 0) - j = OK; - free(scriptname); - } - } - if (j == FAIL) { - EMSG2(_( - "E746: Function name does not match script file name: %s"), - name); - goto erret; - } - } - - fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + STRLEN(name))); - if (fp == NULL) - goto erret; - - if (fudi.fd_dict != NULL) { - if (fudi.fd_di == NULL) { - /* add new dict entry */ - fudi.fd_di = dictitem_alloc(fudi.fd_newkey); - if (fudi.fd_di == NULL) { - free(fp); - goto erret; - } - if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { - free(fudi.fd_di); - free(fp); - goto erret; - } - } else - /* overwrite existing dict entry */ - clear_tv(&fudi.fd_di->di_tv); - fudi.fd_di->di_tv.v_type = VAR_FUNC; - fudi.fd_di->di_tv.v_lock = 0; - fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); - fp->uf_refcount = 1; - - /* behave like "dict" was used */ - flags |= FC_DICT; - } - - /* insert the new function in the function list */ - STRCPY(fp->uf_name, name); - hash_add(&func_hashtab, UF2HIKEY(fp)); - } - fp->uf_args = newargs; - fp->uf_lines = newlines; - fp->uf_tml_count = NULL; - fp->uf_tml_total = NULL; - fp->uf_tml_self = NULL; - fp->uf_profiling = FALSE; - if (prof_def_func()) - func_do_profile(fp); - fp->uf_varargs = varargs; - fp->uf_flags = flags; - fp->uf_calls = 0; - fp->uf_script_ID = current_SID; - goto ret_free; - -erret: - ga_clear_strings(&newargs); - ga_clear_strings(&newlines); -ret_free: - free(skip_until); - free(fudi.fd_newkey); - free(name); - did_emsg |= saved_did_emsg; - need_wait_return |= saved_wait_return; -} - -/* - * Get a function name, translating "" and "". - * Also handles a Funcref in a List or Dictionary. - * Returns the function name in allocated memory, or NULL for failure. - * flags: - * TFN_INT: internal function name OK - * TFN_QUIET: be quiet - * TFN_NO_AUTOLOAD: do not use script autoloading - * Advances "pp" to just after the function name (if no error). - */ -static char_u * -trans_function_name ( - char_u **pp, - int skip, /* only find the end, don't evaluate */ - int flags, - funcdict_T *fdp /* return: info about dictionary used */ -) -{ - char_u *name = NULL; - char_u *start; - char_u *end; - int lead; - char_u sid_buf[20]; - int len; - lval_T lv; - - if (fdp != NULL) - memset(fdp, 0, sizeof(funcdict_T)); - start = *pp; - - /* Check for hard coded : already translated function ID (from a user - * command). */ - if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA - && (*pp)[2] == (int)KE_SNR) { - *pp += 3; - len = get_id_len(pp) + 3; - return vim_strnsave(start, len); - } - - /* A name starting with "" or "" is local to a script. But - * don't skip over "s:", get_lval() needs it for "s:dict.func". */ - lead = eval_fname_script(start); - if (lead > 2) - start += lead; - - /* Note that TFN_ flags use the same values as GLV_ flags. */ - end = get_lval(start, NULL, &lv, FALSE, skip, flags, - lead > 2 ? 0 : FNE_CHECK_START); - if (end == start) { - if (!skip) - EMSG(_("E129: Function name required")); - goto theend; - } - if (end == NULL || (lv.ll_tv != NULL && (lead > 2 || lv.ll_range))) { - /* - * Report an invalid expression in braces, unless the expression - * evaluation has been cancelled due to an aborting error, an - * interrupt, or an exception. - */ - if (!aborting()) { - if (end != NULL) - EMSG2(_(e_invarg2), start); - } else - *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR); - goto theend; - } - - if (lv.ll_tv != NULL) { - if (fdp != NULL) { - fdp->fd_dict = lv.ll_dict; - fdp->fd_newkey = lv.ll_newkey; - lv.ll_newkey = NULL; - fdp->fd_di = lv.ll_di; - } - if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { - name = vim_strsave(lv.ll_tv->vval.v_string); - *pp = end; - } else { - if (!skip && !(flags & TFN_QUIET) && (fdp == NULL - || lv.ll_dict == NULL || - fdp->fd_newkey == NULL)) - EMSG(_(e_funcref)); - else - *pp = end; - name = NULL; - } - goto theend; - } - - if (lv.ll_name == NULL) { - /* Error found, but continue after the function name. */ - *pp = end; - goto theend; - } - - /* Check if the name is a Funcref. If so, use the value. */ - if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name(lv.ll_exp_name, &len, flags & TFN_NO_AUTOLOAD); - if (name == lv.ll_exp_name) - name = NULL; - } else { - len = (int)(end - *pp); - name = deref_func_name(*pp, &len, flags & TFN_NO_AUTOLOAD); - if (name == *pp) - name = NULL; - } - if (name != NULL) { - name = vim_strsave(name); - *pp = end; - if (strncmp((char *)name, "", 5) == 0) { - // Change "" to the byte sequence. - name[0] = K_SPECIAL; - name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; - memmove(name + 3, name + 5, strlen((char *)name + 5) + 1); - } - goto theend; - } - - if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); - if (lead <= 2 && lv.ll_name == lv.ll_exp_name - && STRNCMP(lv.ll_name, "s:", 2) == 0) { - /* When there was "s:" already or the name expanded to get a - * leading "s:" then remove it. */ - lv.ll_name += 2; - len -= 2; - lead = 2; - } - } else { - // Skip over "s:" and "g:". - if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) { - lv.ll_name += 2; - } - len = (int)(end - lv.ll_name); - } - - /* - * Copy the function name to allocated memory. - * Accept name() inside a script, translate into 123_name(). - * Accept 123_name() outside a script. - */ - if (skip) - lead = 0; /* do nothing */ - else if (lead > 0) { - lead = 3; - if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) - || eval_fname_sid(*pp)) { - /* It's "s:" or "" */ - if (current_SID <= 0) { - EMSG(_(e_usingsid)); - goto theend; - } - sprintf((char *)sid_buf, "%" PRId64 "_", (int64_t)current_SID); - lead += (int)STRLEN(sid_buf); - } - } else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len)) { - EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"), - start); - goto theend; - } - - if (!skip && !(flags & TFN_QUIET)) { - char_u *cp = vim_strchr(lv.ll_name, ':'); - - if (cp != NULL && cp < end) { - EMSG2(_("E884: Function name cannot contain a colon: %s"), start); - goto theend; - } - } - - name = alloc((unsigned)(len + lead + 1)); - if (name != NULL) { - if (lead > 0) { - name[0] = K_SPECIAL; - name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; - if (lead > 3) /* If it's "" */ - STRCPY(name + 3, sid_buf); - } - memmove(name + lead, lv.ll_name, (size_t)len); - name[lead + len] = NUL; - } - *pp = end; - -theend: - clear_lval(&lv); - return name; -} - -/* - * Return 5 if "p" starts with "" or "" (ignoring case). - * Return 2 if "p" starts with "s:". - * Return 0 otherwise. - */ -static int eval_fname_script(char_u *p) -{ - if (p[0] == '<' && (STRNICMP(p + 1, "SID>", 4) == 0 - || STRNICMP(p + 1, "SNR>", 4) == 0)) - return 5; - if (p[0] == 's' && p[1] == ':') - return 2; - return 0; -} - -/* - * Return TRUE if "p" starts with "" or "s:". - * Only works if eval_fname_script() returned non-zero for "p"! - */ -static int eval_fname_sid(char_u *p) -{ - return *p == 's' || TOUPPER_ASC(p[2]) == 'I'; -} - -/* - * List the head of the function: "name(arg1, arg2)". - */ -static void list_func_head(ufunc_T *fp, int indent) -{ - int j; - - msg_start(); - if (indent) - MSG_PUTS(" "); - MSG_PUTS("function "); - if (fp->uf_name[0] == K_SPECIAL) { - MSG_PUTS_ATTR("", hl_attr(HLF_8)); - msg_puts(fp->uf_name + 3); - } else - msg_puts(fp->uf_name); - msg_putchar('('); - for (j = 0; j < fp->uf_args.ga_len; ++j) { - if (j) - MSG_PUTS(", "); - msg_puts(FUNCARG(fp, j)); - } - if (fp->uf_varargs) { - if (j) - MSG_PUTS(", "); - MSG_PUTS("..."); - } - msg_putchar(')'); - if (fp->uf_flags & FC_ABORT) - MSG_PUTS(" abort"); - if (fp->uf_flags & FC_RANGE) - MSG_PUTS(" range"); - if (fp->uf_flags & FC_DICT) - MSG_PUTS(" dict"); - msg_clr_eos(); - if (p_verbose > 0) - last_set_msg(fp->uf_script_ID); -} - -/* - * Find a function by name, return pointer to it in ufuncs. - * Return NULL for unknown function. - */ -static ufunc_T *find_func(char_u *name) -{ - hashitem_T *hi; - - hi = hash_find(&func_hashtab, name); - if (!HASHITEM_EMPTY(hi)) - return HI2UF(hi); - return NULL; -} - -#if defined(EXITFREE) || defined(PROTO) -void free_all_functions(void) -{ - hashitem_T *hi; - - /* Need to start all over every time, because func_free() may change the - * hash table. */ - while (func_hashtab.ht_used > 0) - for (hi = func_hashtab.ht_array;; ++hi) - if (!HASHITEM_EMPTY(hi)) { - func_free(HI2UF(hi)); - break; - } -} - -#endif - -int translated_function_exists(char_u *name) -{ - if (builtin_function(name, -1)) { - return find_internal_func(name) >= 0; - } - return find_func(name) != NULL; -} - -/* - * Return TRUE if a function "name" exists. - */ -static int function_exists(char_u *name) -{ - char_u *nm = name; - char_u *p; - int n = FALSE; - - p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET|TFN_NO_AUTOLOAD, - NULL); - nm = skipwhite(nm); - - /* Only accept "funcname", "funcname ", "funcname (..." and - * "funcname(...", not "funcname!...". */ - if (p != NULL && (*nm == NUL || *nm == '(')) - n = translated_function_exists(p); - free(p); - return n; -} - -char_u *get_expanded_name(char_u *name, int check) -{ - char_u *nm = name; - char_u *p; - - p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL); - - if (p != NULL && *nm == NUL) - if (!check || translated_function_exists(p)) - return p; - - free(p); - return NULL; -} - -/// Return TRUE if "name" looks like a builtin function name: starts with a -/// lower case letter and doesn't contain AUTOLOAD_CHAR. -/// "len" is the length of "name", or -1 for NUL terminated. -static bool builtin_function(char_u *name, int len) -{ - if (!ASCII_ISLOWER(name[0])) { - return FALSE; - } - - char_u *p = vim_strchr(name, AUTOLOAD_CHAR); - - return p == NULL - || (len > 0 && p > name + len); -} - -/* - * Start profiling function "fp". - */ -static void func_do_profile(ufunc_T *fp) -{ - int len = fp->uf_lines.ga_len; - - if (len == 0) - len = 1; /* avoid getting error for allocating zero bytes */ - fp->uf_tm_count = 0; - profile_zero(&fp->uf_tm_self); - profile_zero(&fp->uf_tm_total); - - if (fp->uf_tml_count == NULL) { - fp->uf_tml_count = xcalloc(len, sizeof(int)); - } - - if (fp->uf_tml_total == NULL) { - fp->uf_tml_total = xcalloc(len, sizeof(proftime_T)); - } - - if (fp->uf_tml_self == NULL) { - fp->uf_tml_self = xcalloc(len, sizeof(proftime_T)); - } - - fp->uf_tml_idx = -1; - if (fp->uf_tml_count == NULL || fp->uf_tml_total == NULL - || fp->uf_tml_self == NULL) - return; /* out of memory */ - - fp->uf_profiling = TRUE; -} - -/* - * Dump the profiling results for all functions in file "fd". - */ -void func_dump_profile(FILE *fd) -{ - hashitem_T *hi; - int todo; - ufunc_T *fp; - int i; - ufunc_T **sorttab; - int st_len = 0; - - todo = (int)func_hashtab.ht_used; - if (todo == 0) - return; /* nothing to dump */ - - sorttab = (ufunc_T **)alloc((unsigned)(sizeof(ufunc_T) * todo)); - - for (hi = func_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - fp = HI2UF(hi); - if (fp->uf_profiling) { - if (sorttab != NULL) - sorttab[st_len++] = fp; - - if (fp->uf_name[0] == K_SPECIAL) - fprintf(fd, "FUNCTION %s()\n", fp->uf_name + 3); - else - fprintf(fd, "FUNCTION %s()\n", fp->uf_name); - if (fp->uf_tm_count == 1) - fprintf(fd, "Called 1 time\n"); - else - fprintf(fd, "Called %d times\n", fp->uf_tm_count); - fprintf(fd, "Total time: %s\n", profile_msg(&fp->uf_tm_total)); - fprintf(fd, " Self time: %s\n", profile_msg(&fp->uf_tm_self)); - fprintf(fd, "\n"); - fprintf(fd, "count total (s) self (s)\n"); - - for (i = 0; i < fp->uf_lines.ga_len; ++i) { - if (FUNCLINE(fp, i) == NULL) - continue; - prof_func_line(fd, fp->uf_tml_count[i], - &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE); - fprintf(fd, "%s\n", FUNCLINE(fp, i)); - } - fprintf(fd, "\n"); - } - } - } - - if (sorttab != NULL && st_len > 0) { - qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), - prof_total_cmp); - prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE); - qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *), - prof_self_cmp); - prof_sort_list(fd, sorttab, st_len, "SELF", TRUE); - } - - free(sorttab); -} - -static void -prof_sort_list ( - FILE *fd, - ufunc_T **sorttab, - int st_len, - char *title, - int prefer_self /* when equal print only self time */ -) -{ - int i; - ufunc_T *fp; - - fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title); - fprintf(fd, "count total (s) self (s) function\n"); - for (i = 0; i < 20 && i < st_len; ++i) { - fp = sorttab[i]; - prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self, - prefer_self); - if (fp->uf_name[0] == K_SPECIAL) - fprintf(fd, " %s()\n", fp->uf_name + 3); - else - fprintf(fd, " %s()\n", fp->uf_name); - } - fprintf(fd, "\n"); -} - -/* - * Print the count and times for one function or function line. - */ -static void prof_func_line(fd, count, total, self, prefer_self) -FILE *fd; -int count; -proftime_T *total; -proftime_T *self; -int prefer_self; /* when equal print only self time */ -{ - if (count > 0) { - fprintf(fd, "%5d ", count); - if (prefer_self && profile_equal(total, self)) - fprintf(fd, " "); - else - fprintf(fd, "%s ", profile_msg(total)); - if (!prefer_self && profile_equal(total, self)) - fprintf(fd, " "); - else - fprintf(fd, "%s ", profile_msg(self)); - } else - fprintf(fd, " "); -} - -/* - * Compare function for total time sorting. - */ -static int prof_total_cmp(const void *s1, const void *s2) -{ - ufunc_T *p1, *p2; - - p1 = *(ufunc_T **)s1; - p2 = *(ufunc_T **)s2; - return profile_cmp(&p1->uf_tm_total, &p2->uf_tm_total); -} - -/* - * Compare function for self time sorting. - */ -static int prof_self_cmp(const void *s1, const void *s2) -{ - ufunc_T *p1, *p2; - - p1 = *(ufunc_T **)s1; - p2 = *(ufunc_T **)s2; - return profile_cmp(&p1->uf_tm_self, &p2->uf_tm_self); -} - - -/* - * If "name" has a package name try autoloading the script for it. - * Return TRUE if a package was loaded. - */ -static int -script_autoload ( - char_u *name, - int reload /* load script again when already loaded */ -) -{ - char_u *p; - char_u *scriptname, *tofree; - int ret = FALSE; - int i; - - /* If there is no '#' after name[0] there is no package name. */ - p = vim_strchr(name, AUTOLOAD_CHAR); - if (p == NULL || p == name) - return FALSE; - - tofree = scriptname = autoload_name(name); - - /* Find the name in the list of previously loaded package names. Skip - * "autoload/", it's always the same. */ - for (i = 0; i < ga_loaded.ga_len; ++i) - if (STRCMP(((char_u **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) - break; - if (!reload && i < ga_loaded.ga_len) - ret = FALSE; /* was loaded already */ - else { - /* Remember the name if it wasn't loaded already. */ - if (i == ga_loaded.ga_len) { - ga_grow(&ga_loaded, 1); - ((char_u **)ga_loaded.ga_data)[ga_loaded.ga_len++] = scriptname; - tofree = NULL; - } - - /* Try loading the package from $VIMRUNTIME/autoload/.vim */ - if (source_runtime(scriptname, FALSE) == OK) - ret = TRUE; - } - - free(tofree); - return ret; -} - -/* - * Return the autoload script name for a function or variable name. - * Returns NULL when out of memory. - */ -static char_u *autoload_name(char_u *name) -{ - char_u *p; - char_u *scriptname; - - /* Get the script file name: replace '#' with '/', append ".vim". */ - scriptname = alloc((unsigned)(STRLEN(name) + 14)); - if (scriptname == NULL) - return FALSE; - STRCPY(scriptname, "autoload/"); - STRCAT(scriptname, name); - *vim_strrchr(scriptname, AUTOLOAD_CHAR) = NUL; - STRCAT(scriptname, ".vim"); - while ((p = vim_strchr(scriptname, AUTOLOAD_CHAR)) != NULL) - *p = '/'; - return scriptname; -} - - -/* - * Function given to ExpandGeneric() to obtain the list of user defined - * function names. - */ -char_u *get_user_func_name(expand_T *xp, int idx) -{ - static long_u done; - static hashitem_T *hi; - ufunc_T *fp; - - if (idx == 0) { - done = 0; - hi = func_hashtab.ht_array; - } - if (done < func_hashtab.ht_used) { - if (done++ > 0) - ++hi; - while (HASHITEM_EMPTY(hi)) - ++hi; - fp = HI2UF(hi); - - if (fp->uf_flags & FC_DICT) - return (char_u *)""; /* don't show dict functions */ - - if (STRLEN(fp->uf_name) + 4 >= IOSIZE) - return fp->uf_name; /* prevents overflow */ - - cat_func_name(IObuff, fp); - if (xp->xp_context != EXPAND_USER_FUNC) { - STRCAT(IObuff, "("); - if (!fp->uf_varargs && fp->uf_args.ga_len == 0) - STRCAT(IObuff, ")"); - } - return IObuff; - } - return NULL; -} - - -/* - * Copy the function name of "fp" to buffer "buf". - * "buf" must be able to hold the function name plus three bytes. - * Takes care of script-local function names. - */ -static void cat_func_name(char_u *buf, ufunc_T *fp) -{ - if (fp->uf_name[0] == K_SPECIAL) { - STRCPY(buf, ""); - STRCAT(buf, fp->uf_name + 3); - } else - STRCPY(buf, fp->uf_name); -} - -/* - * ":delfunction {name}" - */ -void ex_delfunction(exarg_T *eap) -{ - ufunc_T *fp = NULL; - char_u *p; - char_u *name; - funcdict_T fudi; - - p = eap->arg; - name = trans_function_name(&p, eap->skip, 0, &fudi); - free(fudi.fd_newkey); - if (name == NULL) { - if (fudi.fd_dict != NULL && !eap->skip) - EMSG(_(e_funcref)); - return; - } - if (!ends_excmd(*skipwhite(p))) { - free(name); - EMSG(_(e_trailing)); - return; - } - eap->nextcmd = check_nextcmd(p); - if (eap->nextcmd != NULL) - *p = NUL; - - if (!eap->skip) - fp = find_func(name); - free(name); - - if (!eap->skip) { - if (fp == NULL) { - EMSG2(_(e_nofunc), eap->arg); - return; - } - if (fp->uf_calls > 0) { - EMSG2(_("E131: Cannot delete function %s: It is in use"), eap->arg); - return; - } - - if (fudi.fd_dict != NULL) { - /* Delete the dict item that refers to the function, it will - * invoke func_unref() and possibly delete the function. */ - dictitem_remove(fudi.fd_dict, fudi.fd_di); - } else - func_free(fp); - } -} - -/* - * Free a function and remove it from the list of functions. - */ -static void func_free(ufunc_T *fp) -{ - hashitem_T *hi; - - /* clear this function */ - ga_clear_strings(&(fp->uf_args)); - ga_clear_strings(&(fp->uf_lines)); - free(fp->uf_tml_count); - free(fp->uf_tml_total); - free(fp->uf_tml_self); - - /* remove the function from the function hashtable */ - hi = hash_find(&func_hashtab, UF2HIKEY(fp)); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "func_free()"); - else - hash_remove(&func_hashtab, hi); - - free(fp); -} - -/* - * Unreference a Function: decrement the reference count and free it when it - * becomes zero. Only for numbered functions. - */ -void func_unref(char_u *name) -{ - ufunc_T *fp; - - if (name != NULL && isdigit(*name)) { - fp = find_func(name); - if (fp == NULL) - EMSG2(_(e_intern2), "func_unref()"); - else if (--fp->uf_refcount <= 0) { - /* Only delete it when it's not being used. Otherwise it's done - * when "uf_calls" becomes zero. */ - if (fp->uf_calls == 0) - func_free(fp); - } - } -} - -/* - * Count a reference to a Function. - */ -void func_ref(char_u *name) -{ - ufunc_T *fp; - - if (name != NULL && isdigit(*name)) { - fp = find_func(name); - if (fp == NULL) - EMSG2(_(e_intern2), "func_ref()"); - else - ++fp->uf_refcount; - } -} - -/* - * Call a user function. - */ -static void -call_user_func ( - ufunc_T *fp, /* pointer to function */ - int argcount, /* nr of args */ - typval_T *argvars, /* arguments */ - typval_T *rettv, /* return value */ - linenr_T firstline, /* first line of range */ - linenr_T lastline, /* last line of range */ - dict_T *selfdict /* Dictionary for "self" */ -) -{ - char_u *save_sourcing_name; - linenr_T save_sourcing_lnum; - scid_T save_current_SID; - funccall_T *fc; - int save_did_emsg; - static int depth = 0; - dictitem_T *v; - int fixvar_idx = 0; /* index in fixvar[] */ - int i; - int ai; - char_u numbuf[NUMBUFLEN]; - char_u *name; - proftime_T wait_start; - proftime_T call_start; - - /* If depth of calling is getting too high, don't execute the function */ - if (depth >= p_mfd) { - EMSG(_("E132: Function call depth is higher than 'maxfuncdepth'")); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = -1; - return; - } - ++depth; - - line_breakcheck(); /* check for CTRL-C hit */ - - fc = (funccall_T *)alloc(sizeof(funccall_T)); - fc->caller = current_funccal; - current_funccal = fc; - fc->func = fp; - fc->rettv = rettv; - rettv->vval.v_number = 0; - fc->linenr = 0; - fc->returned = FALSE; - fc->level = ex_nesting_level; - /* Check if this function has a breakpoint. */ - fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0); - fc->dbg_tick = debug_tick; - - /* - * Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables - * with names up to VAR_SHORT_LEN long. This avoids having to alloc/free - * each argument variable and saves a lot of time. - */ - /* - * Init l: variables. - */ - init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); - if (selfdict != NULL) { - /* Set l:self to "selfdict". Use "name" to avoid a warning from - * some compiler that checks the destination size. */ - v = &fc->fixvar[fixvar_idx++].var; - name = v->di_key; - STRCPY(name, "self"); - v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); - v->di_tv.v_type = VAR_DICT; - v->di_tv.v_lock = 0; - v->di_tv.vval.v_dict = selfdict; - ++selfdict->dv_refcount; - } - - /* - * Init a: variables. - * Set a:0 to "argcount". - * Set a:000 to a list with room for the "..." arguments. - */ - init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", - (varnumber_T)(argcount - fp->uf_args.ga_len)); - /* Use "name" to avoid a warning from some compiler that checks the - * destination size. */ - v = &fc->fixvar[fixvar_idx++].var; - name = v->di_key; - STRCPY(name, "000"); - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); - v->di_tv.v_type = VAR_LIST; - v->di_tv.v_lock = VAR_FIXED; - v->di_tv.vval.v_list = &fc->l_varlist; - memset(&fc->l_varlist, 0, sizeof(list_T)); - fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; - fc->l_varlist.lv_lock = VAR_FIXED; - - /* - * Set a:firstline to "firstline" and a:lastline to "lastline". - * Set a:name to named arguments. - * Set a:N to the "..." arguments. - */ - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline", - (varnumber_T)firstline); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", - (varnumber_T)lastline); - for (i = 0; i < argcount; ++i) { - ai = i - fp->uf_args.ga_len; - if (ai < 0) - /* named argument a:name */ - name = FUNCARG(fp, i); - else { - /* "..." argument a:1, a:2, etc. */ - sprintf((char *)numbuf, "%d", ai + 1); - name = numbuf; - } - if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) { - v = &fc->fixvar[fixvar_idx++].var; - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - } else { - v = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) - + STRLEN(name))); - if (v == NULL) - break; - v->di_flags = DI_FLAGS_RO; - } - STRCPY(v->di_key, name); - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); - - /* Note: the values are copied directly to avoid alloc/free. - * "argvars" must have VAR_FIXED for v_lock. */ - v->di_tv = argvars[i]; - v->di_tv.v_lock = VAR_FIXED; - - if (ai >= 0 && ai < MAX_FUNC_ARGS) { - list_append(&fc->l_varlist, &fc->l_listitems[ai]); - fc->l_listitems[ai].li_tv = argvars[i]; - fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; - } - } - - /* Don't redraw while executing the function. */ - ++RedrawingDisabled; - save_sourcing_name = sourcing_name; - save_sourcing_lnum = sourcing_lnum; - sourcing_lnum = 1; - sourcing_name = alloc((unsigned)((save_sourcing_name == NULL ? 0 - : STRLEN(save_sourcing_name)) + - STRLEN(fp->uf_name) + 13)); - if (sourcing_name != NULL) { - if (save_sourcing_name != NULL - && STRNCMP(save_sourcing_name, "function ", 9) == 0) - sprintf((char *)sourcing_name, "%s..", save_sourcing_name); - else - STRCPY(sourcing_name, "function "); - cat_func_name(sourcing_name + STRLEN(sourcing_name), fp); - - if (p_verbose >= 12) { - ++no_wait_return; - verbose_enter_scroll(); - - smsg((char_u *)_("calling %s"), sourcing_name); - if (p_verbose >= 14) { - char_u buf[MSG_BUF_LEN]; - char_u numbuf2[NUMBUFLEN]; - char_u *tofree; - char_u *s; - - msg_puts((char_u *)"("); - for (i = 0; i < argcount; ++i) { - if (i > 0) - msg_puts((char_u *)", "); - if (argvars[i].v_type == VAR_NUMBER) - msg_outnum((long)argvars[i].vval.v_number); - else { - s = tv2string(&argvars[i], &tofree, numbuf2, 0); - if (s != NULL) { - if (vim_strsize(s) > MSG_BUF_CLEN) { - trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); - s = buf; - } - msg_puts(s); - free(tofree); - } - } - } - msg_puts((char_u *)")"); - } - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - verbose_leave_scroll(); - --no_wait_return; - } - } - if (do_profiling == PROF_YES) { - if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL)) - func_do_profile(fp); - if (fp->uf_profiling - || (fc->caller != NULL && fc->caller->func->uf_profiling)) { - ++fp->uf_tm_count; - profile_start(&call_start); - profile_zero(&fp->uf_tm_children); - } - script_prof_save(&wait_start); - } - - save_current_SID = current_SID; - current_SID = fp->uf_script_ID; - save_did_emsg = did_emsg; - did_emsg = FALSE; - - /* call do_cmdline() to execute the lines */ - do_cmdline(NULL, get_func_line, (void *)fc, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); - - --RedrawingDisabled; - - /* when the function was aborted because of an error, return -1 */ - if ((did_emsg && - (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { - clear_tv(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = -1; - } - - if (do_profiling == PROF_YES && (fp->uf_profiling - || (fc->caller != NULL && - fc->caller->func->uf_profiling))) { - profile_end(&call_start); - profile_sub_wait(&wait_start, &call_start); - profile_add(&fp->uf_tm_total, &call_start); - profile_self(&fp->uf_tm_self, &call_start, &fp->uf_tm_children); - if (fc->caller != NULL && fc->caller->func->uf_profiling) { - profile_add(&fc->caller->func->uf_tm_children, &call_start); - profile_add(&fc->caller->func->uf_tml_children, &call_start); - } - } - - /* when being verbose, mention the return value */ - if (p_verbose >= 12) { - ++no_wait_return; - verbose_enter_scroll(); - - if (aborting()) - smsg((char_u *)_("%s aborted"), sourcing_name); - else if (fc->rettv->v_type == VAR_NUMBER) - smsg((char_u *)_("%s returning #%" PRId64 ""), - sourcing_name, (int64_t)fc->rettv->vval.v_number); - else { - char_u buf[MSG_BUF_LEN]; - char_u numbuf2[NUMBUFLEN]; - char_u *tofree; - char_u *s; - - /* The value may be very long. Skip the middle part, so that we - * have some idea how it starts and ends. smsg() would always - * truncate it at the end. */ - s = tv2string(fc->rettv, &tofree, numbuf2, 0); - if (s != NULL) { - if (vim_strsize(s) > MSG_BUF_CLEN) { - trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); - s = buf; - } - smsg((char_u *)_("%s returning %s"), sourcing_name, s); - free(tofree); - } - } - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - verbose_leave_scroll(); - --no_wait_return; - } - - free(sourcing_name); - sourcing_name = save_sourcing_name; - sourcing_lnum = save_sourcing_lnum; - current_SID = save_current_SID; - if (do_profiling == PROF_YES) - script_prof_restore(&wait_start); - - if (p_verbose >= 12 && sourcing_name != NULL) { - ++no_wait_return; - verbose_enter_scroll(); - - smsg((char_u *)_("continuing in %s"), sourcing_name); - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - verbose_leave_scroll(); - --no_wait_return; - } - - did_emsg |= save_did_emsg; - current_funccal = fc->caller; - --depth; - - /* If the a:000 list and the l: and a: dicts are not referenced we can - * free the funccall_T and what's in it. */ - if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) { - free_funccal(fc, FALSE); - } else { - hashitem_T *hi; - listitem_T *li; - int todo; - - /* "fc" is still in use. This can happen when returning "a:000" or - * assigning "l:" to a global variable. - * Link "fc" in the list for garbage collection later. */ - fc->caller = previous_funccal; - previous_funccal = fc; - - /* Make a copy of the a: variables, since we didn't do that above. */ - todo = (int)fc->l_avars.dv_hashtab.ht_used; - for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - v = HI2DI(hi); - copy_tv(&v->di_tv, &v->di_tv); - } - } - - /* Make a copy of the a:000 items, since we didn't do that above. */ - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) - copy_tv(&li->li_tv, &li->li_tv); - } -} - -/* - * Return TRUE if items in "fc" do not have "copyID". That means they are not - * referenced from anywhere that is in use. - */ -static int can_free_funccal(funccall_T *fc, int copyID) -{ - return fc->l_varlist.lv_copyID != copyID - && fc->l_vars.dv_copyID != copyID - && fc->l_avars.dv_copyID != copyID; -} - -/* - * Free "fc" and what it contains. - */ -static void -free_funccal ( - funccall_T *fc, - int free_val /* a: vars were allocated */ -) -{ - listitem_T *li; - - /* The a: variables typevals may not have been allocated, only free the - * allocated variables. */ - vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); - - /* free all l: variables */ - vars_clear(&fc->l_vars.dv_hashtab); - - /* Free the a:000 variables if they were allocated. */ - if (free_val) - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) - clear_tv(&li->li_tv); - - free(fc); -} - -/* - * Add a number variable "name" to dict "dp" with value "nr". - */ -static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) -{ - STRCPY(v->di_key, name); - v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&dp->dv_hashtab, DI2HIKEY(v)); - v->di_tv.v_type = VAR_NUMBER; - v->di_tv.v_lock = VAR_FIXED; - v->di_tv.vval.v_number = nr; -} - -/* - * ":return [expr]" - */ -void ex_return(exarg_T *eap) -{ - char_u *arg = eap->arg; - typval_T rettv; - int returning = FALSE; - - if (current_funccal == NULL) { - EMSG(_("E133: :return not inside a function")); - return; - } - - if (eap->skip) - ++emsg_skip; - - eap->nextcmd = NULL; - if ((*arg != NUL && *arg != '|' && *arg != '\n') - && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) { - if (!eap->skip) - returning = do_return(eap, FALSE, TRUE, &rettv); - else - clear_tv(&rettv); - } - /* It's safer to return also on error. */ - else if (!eap->skip) { - /* - * Return unless the expression evaluation has been cancelled due to an - * aborting error, an interrupt, or an exception. - */ - if (!aborting()) - returning = do_return(eap, FALSE, TRUE, NULL); - } - - /* When skipping or the return gets pending, advance to the next command - * in this line (!returning). Otherwise, ignore the rest of the line. - * Following lines will be ignored by get_func_line(). */ - if (returning) - eap->nextcmd = NULL; - else if (eap->nextcmd == NULL) /* no argument */ - eap->nextcmd = check_nextcmd(arg); - - if (eap->skip) - --emsg_skip; -} - -/* - * Return from a function. Possibly makes the return pending. Also called - * for a pending return at the ":endtry" or after returning from an extra - * do_cmdline(). "reanimate" is used in the latter case. "is_cmd" is set - * when called due to a ":return" command. "rettv" may point to a typval_T - * with the return rettv. Returns TRUE when the return can be carried out, - * FALSE when the return gets pending. - */ -int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) -{ - int idx; - struct condstack *cstack = eap->cstack; - - if (reanimate) - /* Undo the return. */ - current_funccal->returned = FALSE; - - /* - * Cleanup (and inactivate) conditionals, but stop when a try conditional - * not in its finally clause (which then is to be executed next) is found. - * In this case, make the ":return" pending for execution at the ":endtry". - * Otherwise, return normally. - */ - idx = cleanup_conditionals(eap->cstack, 0, TRUE); - if (idx >= 0) { - cstack->cs_pending[idx] = CSTP_RETURN; - - if (!is_cmd && !reanimate) - /* A pending return again gets pending. "rettv" points to an - * allocated variable with the rettv of the original ":return"'s - * argument if present or is NULL else. */ - cstack->cs_rettv[idx] = rettv; - else { - /* When undoing a return in order to make it pending, get the stored - * return rettv. */ - if (reanimate) - rettv = current_funccal->rettv; - - if (rettv != NULL) { - /* Store the value of the pending return. */ - cstack->cs_rettv[idx] = xcalloc(1, sizeof(typval_T)); - *(typval_T *)cstack->cs_rettv[idx] = *(typval_T *)rettv; - } else - cstack->cs_rettv[idx] = NULL; - - if (reanimate) { - /* The pending return value could be overwritten by a ":return" - * without argument in a finally clause; reset the default - * return value. */ - current_funccal->rettv->v_type = VAR_NUMBER; - current_funccal->rettv->vval.v_number = 0; - } - } - report_make_pending(CSTP_RETURN, rettv); - } else { - current_funccal->returned = TRUE; - - /* If the return is carried out now, store the return value. For - * a return immediately after reanimation, the value is already - * there. */ - if (!reanimate && rettv != NULL) { - clear_tv(current_funccal->rettv); - *current_funccal->rettv = *(typval_T *)rettv; - if (!is_cmd) - free(rettv); - } - } - - return idx < 0; -} - -/* - * Free the variable with a pending return value. - */ -void discard_pending_return(void *rettv) -{ - free_tv((typval_T *)rettv); -} - -/* - * Generate a return command for producing the value of "rettv". The result - * is an allocated string. Used by report_pending() for verbose messages. - */ -char_u *get_return_cmd(void *rettv) -{ - char_u *s = NULL; - char_u *tofree = NULL; - char_u numbuf[NUMBUFLEN]; - - if (rettv != NULL) - s = echo_string((typval_T *)rettv, &tofree, numbuf, 0); - if (s == NULL) - s = (char_u *)""; - - STRCPY(IObuff, ":return "); - STRNCPY(IObuff + 8, s, IOSIZE - 8); - if (STRLEN(s) + 8 >= IOSIZE) - STRCPY(IObuff + IOSIZE - 4, "..."); - free(tofree); - return vim_strsave(IObuff); -} - -/* - * Get next function line. - * Called by do_cmdline() to get the next line. - * Returns allocated string, or NULL for end of function. - */ -char_u *get_func_line(int c, void *cookie, int indent) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - char_u *retval; - garray_T *gap; /* growarray with function lines */ - - /* If breakpoints have been added/deleted need to check for it. */ - if (fcp->dbg_tick != debug_tick) { - fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, - sourcing_lnum); - fcp->dbg_tick = debug_tick; - } - if (do_profiling == PROF_YES) - func_line_end(cookie); - - gap = &fp->uf_lines; - if (((fp->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) - || fcp->returned) - retval = NULL; - else { - /* Skip NULL lines (continuation lines). */ - while (fcp->linenr < gap->ga_len - && ((char_u **)(gap->ga_data))[fcp->linenr] == NULL) - ++fcp->linenr; - if (fcp->linenr >= gap->ga_len) - retval = NULL; - else { - retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); - sourcing_lnum = fcp->linenr; - if (do_profiling == PROF_YES) - func_line_start(cookie); - } - } - - /* Did we encounter a breakpoint? */ - if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) { - dbg_breakpoint(fp->uf_name, sourcing_lnum); - /* Find next breakpoint. */ - fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, - sourcing_lnum); - fcp->dbg_tick = debug_tick; - } - - return retval; -} - -/* - * Called when starting to read a function line. - * "sourcing_lnum" must be correct! - * When skipping lines it may not actually be executed, but we won't find out - * until later and we need to store the time now. - */ -void func_line_start(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - - if (fp->uf_profiling && sourcing_lnum >= 1 - && sourcing_lnum <= fp->uf_lines.ga_len) { - fp->uf_tml_idx = sourcing_lnum - 1; - /* Skip continuation lines. */ - while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) - --fp->uf_tml_idx; - fp->uf_tml_execed = FALSE; - profile_start(&fp->uf_tml_start); - profile_zero(&fp->uf_tml_children); - profile_get_wait(&fp->uf_tml_wait); - } -} - -/* - * Called when actually executing a function line. - */ -void func_line_exec(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - - if (fp->uf_profiling && fp->uf_tml_idx >= 0) - fp->uf_tml_execed = TRUE; -} - -/* - * Called when done with a function line. - */ -void func_line_end(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - ufunc_T *fp = fcp->func; - - if (fp->uf_profiling && fp->uf_tml_idx >= 0) { - if (fp->uf_tml_execed) { - ++fp->uf_tml_count[fp->uf_tml_idx]; - profile_end(&fp->uf_tml_start); - profile_sub_wait(&fp->uf_tml_wait, &fp->uf_tml_start); - profile_add(&fp->uf_tml_total[fp->uf_tml_idx], &fp->uf_tml_start); - profile_self(&fp->uf_tml_self[fp->uf_tml_idx], &fp->uf_tml_start, - &fp->uf_tml_children); - } - fp->uf_tml_idx = -1; - } -} - -/* - * Return TRUE if the currently active function should be ended, because a - * return was encountered or an error occurred. Used inside a ":while". - */ -int func_has_ended(void *cookie) -{ - funccall_T *fcp = (funccall_T *)cookie; - - /* Ignore the "abort" flag if the abortion behavior has been changed due to - * an error inside a try conditional. */ - return ((fcp->func->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) - || fcp->returned; -} - -/* - * return TRUE if cookie indicates a function which "abort"s on errors. - */ -int func_has_abort(void *cookie) -{ - return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT; -} - -typedef enum { - VAR_FLAVOUR_DEFAULT, /* doesn't start with uppercase */ - VAR_FLAVOUR_SESSION, /* starts with uppercase, some lower */ - VAR_FLAVOUR_VIMINFO /* all uppercase */ -} var_flavour_T; - -static var_flavour_T var_flavour(char_u *varname); - -static var_flavour_T var_flavour(char_u *varname) -{ - char_u *p = varname; - - if (ASCII_ISUPPER(*p)) { - while (*(++p)) - if (ASCII_ISLOWER(*p)) - return VAR_FLAVOUR_SESSION; - return VAR_FLAVOUR_VIMINFO; - } else - return VAR_FLAVOUR_DEFAULT; -} - -/* - * Restore global vars that start with a capital from the viminfo file - */ -int read_viminfo_varlist(vir_T *virp, int writing) -{ - char_u *tab; - int type = VAR_NUMBER; - typval_T tv; - - if (!writing && (find_viminfo_parameter('!') != NULL)) { - tab = vim_strchr(virp->vir_line + 1, '\t'); - if (tab != NULL) { - *tab++ = NUL; /* isolate the variable name */ - switch (*tab) { - case 'S': type = VAR_STRING; break; - case 'F': type = VAR_FLOAT; break; - case 'D': type = VAR_DICT; break; - case 'L': type = VAR_LIST; break; - } - - tab = vim_strchr(tab, '\t'); - if (tab != NULL) { - tv.v_type = type; - if (type == VAR_STRING || type == VAR_DICT || type == VAR_LIST) - tv.vval.v_string = viminfo_readstring(virp, - (int)(tab - virp->vir_line + 1), TRUE); - else if (type == VAR_FLOAT) - (void)string2float(tab + 1, &tv.vval.v_float); - else - tv.vval.v_number = atol((char *)tab + 1); - if (type == VAR_DICT || type == VAR_LIST) { - typval_T *etv = eval_expr(tv.vval.v_string, NULL); - - if (etv == NULL) - /* Failed to parse back the dict or list, use it as a - * string. */ - tv.v_type = VAR_STRING; - else { - free(tv.vval.v_string); - tv = *etv; - free(etv); - } - } - - set_var(virp->vir_line + 1, &tv, FALSE); - - if (tv.v_type == VAR_STRING) - free(tv.vval.v_string); - else if (tv.v_type == VAR_DICT || tv.v_type == VAR_LIST) - clear_tv(&tv); - } - } - } - - return viminfo_readline(virp); -} - -/* - * Write global vars that start with a capital to the viminfo file - */ -void write_viminfo_varlist(FILE *fp) -{ - hashitem_T *hi; - dictitem_T *this_var; - int todo; - char *s; - char_u *p; - char_u *tofree; - char_u numbuf[NUMBUFLEN]; - - if (find_viminfo_parameter('!') == NULL) - return; - - fputs(_("\n# global variables:\n"), fp); - - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - this_var = HI2DI(hi); - if (var_flavour(this_var->di_key) == VAR_FLAVOUR_VIMINFO) { - switch (this_var->di_tv.v_type) { - case VAR_STRING: s = "STR"; break; - case VAR_NUMBER: s = "NUM"; break; - case VAR_FLOAT: s = "FLO"; break; - case VAR_DICT: s = "DIC"; break; - case VAR_LIST: s = "LIS"; break; - default: continue; - } - fprintf(fp, "!%s\t%s\t", this_var->di_key, s); - p = echo_string(&this_var->di_tv, &tofree, numbuf, 0); - if (p != NULL) - viminfo_writestring(fp, p); - free(tofree); - } - } - } -} - -int store_session_globals(FILE *fd) -{ - hashitem_T *hi; - dictitem_T *this_var; - int todo; - char_u *p, *t; - - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - this_var = HI2DI(hi); - if ((this_var->di_tv.v_type == VAR_NUMBER - || this_var->di_tv.v_type == VAR_STRING) - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - /* Escape special characters with a backslash. Turn a LF and - * CR into \n and \r. */ - p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); - if (p == NULL) /* out of memory */ - break; - for (t = p; *t != NUL; ++t) - if (*t == '\n') - *t = 'n'; - else if (*t == '\r') - *t = 'r'; - if ((fprintf(fd, "let %s = %c%s%c", - this_var->di_key, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ', - p, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ') < 0) - || put_eol(fd) == FAIL) { - free(p); - return FAIL; - } - free(p); - } else if (this_var->di_tv.v_type == VAR_FLOAT - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - float_T f = this_var->di_tv.vval.v_float; - int sign = ' '; - - if (f < 0) { - f = -f; - sign = '-'; - } - if ((fprintf(fd, "let %s = %c%f", - this_var->di_key, sign, f) < 0) - || put_eol(fd) == FAIL) - return FAIL; - } - } - } - return OK; -} - -/* - * Display script name where an item was last set. - * Should only be invoked when 'verbose' is non-zero. - */ -void last_set_msg(scid_T scriptID) -{ - char_u *p; - - if (scriptID != 0) { - p = home_replace_save(NULL, get_scriptname(scriptID)); - if (p != NULL) { - verbose_enter(); - MSG_PUTS(_("\n\tLast set from ")); - MSG_PUTS(p); - free(p); - verbose_leave(); - } - } -} - -/* - * List v:oldfiles in a nice way. - */ -void ex_oldfiles(exarg_T *eap) -{ - list_T *l = vimvars[VV_OLDFILES].vv_list; - listitem_T *li; - int nr = 0; - - if (l == NULL) - msg((char_u *)_("No old files")); - else { - msg_start(); - msg_scroll = TRUE; - for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { - msg_outnum((long)++nr); - MSG_PUTS(": "); - msg_outtrans(get_tv_string(&li->li_tv)); - msg_putchar('\n'); - out_flush(); /* output one line at a time */ - ui_breakcheck(); - } - /* Assume "got_int" was set to truncate the listing. */ - got_int = FALSE; - - } -} - - - - - -/* - * Adjust a filename, according to a string of modifiers. - * *fnamep must be NUL terminated when called. When returning, the length is - * determined by *fnamelen. - * Returns VALID_ flags or -1 for failure. - * When there is an error, *fnamep is set to NULL. - */ -int -modify_fname ( - char_u *src, /* string with modifiers */ - int *usedlen, /* characters after src that are used */ - char_u **fnamep, /* file name so far */ - char_u **bufp, /* buffer for allocated file name or NULL */ - int *fnamelen /* length of fnamep */ -) -{ - int valid = 0; - char_u *tail; - char_u *s, *p, *pbuf; - char_u dirname[MAXPATHL]; - int c; - int has_fullname = 0; - -repeat: - /* ":p" - full path/file_name */ - if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') { - has_fullname = 1; - - valid |= VALID_PATH; - *usedlen += 2; - - /* Expand "~/path" for all systems and "~user/path" for Unix */ - if ((*fnamep)[0] == '~' -#if !defined(UNIX) - && ((*fnamep)[1] == '/' -# ifdef BACKSLASH_IN_FILENAME - || (*fnamep)[1] == '\\' -# endif - || (*fnamep)[1] == NUL) - -#endif - ) { - *fnamep = expand_env_save(*fnamep); - free(*bufp); /* free any allocated file name */ - *bufp = *fnamep; - if (*fnamep == NULL) - return -1; - } - - /* When "/." or "/.." is used: force expansion to get rid of it. */ - for (p = *fnamep; *p != NUL; mb_ptr_adv(p)) { - if (vim_ispathsep(*p) - && p[1] == '.' - && (p[2] == NUL - || vim_ispathsep(p[2]) - || (p[2] == '.' - && (p[3] == NUL || vim_ispathsep(p[3]))))) - break; - } - - /* FullName_save() is slow, don't use it when not needed. */ - if (*p != NUL || !vim_isAbsName(*fnamep)) { - *fnamep = FullName_save(*fnamep, *p != NUL); - free(*bufp); /* free any allocated file name */ - *bufp = *fnamep; - if (*fnamep == NULL) - return -1; - } - - /* Append a path separator to a directory. */ - if (os_isdir(*fnamep)) { - /* Make room for one or two extra characters. */ - *fnamep = vim_strnsave(*fnamep, (int)STRLEN(*fnamep) + 2); - free(*bufp); /* free any allocated file name */ - *bufp = *fnamep; - if (*fnamep == NULL) - return -1; - add_pathsep(*fnamep); - } - } - - /* ":." - path relative to the current directory */ - /* ":~" - path relative to the home directory */ - /* ":8" - shortname path - postponed till after */ - while (src[*usedlen] == ':' - && ((c = src[*usedlen + 1]) == '.' || c == '~' || c == '8')) { - *usedlen += 2; - if (c == '8') { - continue; - } - pbuf = NULL; - /* Need full path first (use expand_env() to remove a "~/") */ - if (!has_fullname) { - if (c == '.' && **fnamep == '~') - p = pbuf = expand_env_save(*fnamep); - else - p = pbuf = FullName_save(*fnamep, FALSE); - } else - p = *fnamep; - - has_fullname = 0; - - if (p != NULL) { - if (c == '.') { - os_dirname(dirname, MAXPATHL); - s = path_shorten_fname(p, dirname); - if (s != NULL) { - *fnamep = s; - if (pbuf != NULL) { - free(*bufp); /* free any allocated file name */ - *bufp = pbuf; - pbuf = NULL; - } - } - } else { - home_replace(NULL, p, dirname, MAXPATHL, TRUE); - /* Only replace it when it starts with '~' */ - if (*dirname == '~') { - s = vim_strsave(dirname); - if (s != NULL) { - *fnamep = s; - free(*bufp); - *bufp = s; - } - } - } - free(pbuf); - } - } - - tail = path_tail(*fnamep); - *fnamelen = (int)STRLEN(*fnamep); - - /* ":h" - head, remove "/file_name", can be repeated */ - /* Don't remove the first "/" or "c:\" */ - while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') { - valid |= VALID_HEAD; - *usedlen += 2; - s = get_past_head(*fnamep); - while (tail > s && after_pathsep(s, tail)) - mb_ptr_back(*fnamep, tail); - *fnamelen = (int)(tail - *fnamep); - if (*fnamelen == 0) { - /* Result is empty. Turn it into "." to make ":cd %:h" work. */ - p = vim_strsave((char_u *)"."); - if (p == NULL) - return -1; - free(*bufp); - *bufp = *fnamep = tail = p; - *fnamelen = 1; - } else { - while (tail > s && !after_pathsep(s, tail)) - mb_ptr_back(*fnamep, tail); - } - } - - /* ":8" - shortname */ - if (src[*usedlen] == ':' && src[*usedlen + 1] == '8') { - *usedlen += 2; - } - - - /* ":t" - tail, just the basename */ - if (src[*usedlen] == ':' && src[*usedlen + 1] == 't') { - *usedlen += 2; - *fnamelen -= (int)(tail - *fnamep); - *fnamep = tail; - } - - /* ":e" - extension, can be repeated */ - /* ":r" - root, without extension, can be repeated */ - while (src[*usedlen] == ':' - && (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) { - /* find a '.' in the tail: - * - for second :e: before the current fname - * - otherwise: The last '.' - */ - if (src[*usedlen + 1] == 'e' && *fnamep > tail) - s = *fnamep - 2; - else - s = *fnamep + *fnamelen - 1; - for (; s > tail; --s) - if (s[0] == '.') - break; - if (src[*usedlen + 1] == 'e') { /* :e */ - if (s > tail) { - *fnamelen += (int)(*fnamep - (s + 1)); - *fnamep = s + 1; - } else if (*fnamep <= tail) - *fnamelen = 0; - } else { /* :r */ - if (s > tail) /* remove one extension */ - *fnamelen = (int)(s - *fnamep); - } - *usedlen += 2; - } - - /* ":s?pat?foo?" - substitute */ - /* ":gs?pat?foo?" - global substitute */ - if (src[*usedlen] == ':' - && (src[*usedlen + 1] == 's' - || (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) { - char_u *str; - char_u *pat; - char_u *sub; - int sep; - char_u *flags; - int didit = FALSE; - - flags = (char_u *)""; - s = src + *usedlen + 2; - if (src[*usedlen + 1] == 'g') { - flags = (char_u *)"g"; - ++s; - } - - sep = *s++; - if (sep) { - /* find end of pattern */ - p = vim_strchr(s, sep); - if (p != NULL) { - pat = vim_strnsave(s, (int)(p - s)); - if (pat != NULL) { - s = p + 1; - /* find end of substitution */ - p = vim_strchr(s, sep); - if (p != NULL) { - sub = vim_strnsave(s, (int)(p - s)); - str = vim_strnsave(*fnamep, *fnamelen); - if (sub != NULL && str != NULL) { - *usedlen = (int)(p + 1 - src); - s = do_string_sub(str, pat, sub, flags); - if (s != NULL) { - *fnamep = s; - *fnamelen = (int)STRLEN(s); - free(*bufp); - *bufp = s; - didit = TRUE; - } - } - free(sub); - free(str); - } - free(pat); - } - } - /* after using ":s", repeat all the modifiers */ - if (didit) - goto repeat; - } - } - - if (src[*usedlen] == ':' && src[*usedlen + 1] == 'S') { - p = vim_strsave_shellescape(*fnamep, false, false); - free(*bufp); - *bufp = *fnamep = p; - *fnamelen = (int)STRLEN(p); - *usedlen += 2; - } - - return valid; -} - -/* - * Perform a substitution on "str" with pattern "pat" and substitute "sub". - * "flags" can be "g" to do a global substitute. - * Returns an allocated string, NULL for error. - */ -char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags) -{ - int sublen; - regmatch_T regmatch; - int i; - int do_all; - char_u *tail; - garray_T ga; - char_u *ret; - char_u *save_cpo; - char_u *zero_width = NULL; - - /* Make 'cpoptions' empty, so that the 'l' flag doesn't work here */ - save_cpo = p_cpo; - p_cpo = empty_option; - - ga_init(&ga, 1, 200); - - do_all = (flags[0] == 'g'); - - regmatch.rm_ic = p_ic; - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - if (regmatch.regprog != NULL) { - tail = str; - while (vim_regexec_nl(®match, str, (colnr_T)(tail - str))) { - /* Skip empty match except for first match. */ - if (regmatch.startp[0] == regmatch.endp[0]) { - if (zero_width == regmatch.startp[0]) { - /* avoid getting stuck on a match with an empty string */ - *((char_u *)ga.ga_data + ga.ga_len) = *tail++; - ++ga.ga_len; - continue; - } - zero_width = regmatch.startp[0]; - } - - /* - * Get some space for a temporary buffer to do the substitution - * into. It will contain: - * - The text up to where the match is. - * - The substituted text. - * - The text after the match. - */ - sublen = vim_regsub(®match, sub, tail, FALSE, TRUE, FALSE); - ga_grow(&ga, (int)(STRLEN(tail) + sublen - - (regmatch.endp[0] - regmatch.startp[0]))); - - /* copy the text up to where the match is */ - i = (int)(regmatch.startp[0] - tail); - memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); - /* add the substituted text */ - (void)vim_regsub(®match, sub, (char_u *)ga.ga_data - + ga.ga_len + i, TRUE, TRUE, FALSE); - ga.ga_len += i + sublen - 1; - tail = regmatch.endp[0]; - if (*tail == NUL) - break; - if (!do_all) - break; - } - - if (ga.ga_data != NULL) - STRCPY((char *)ga.ga_data + ga.ga_len, tail); - - vim_regfree(regmatch.regprog); - } - - ret = vim_strsave(ga.ga_data == NULL ? str : (char_u *)ga.ga_data); - ga_clear(&ga); - if (p_cpo == empty_option) - p_cpo = save_cpo; - else - /* Darn, evaluating {sub} expression changed the value. */ - free_string_option(save_cpo); - - return ret; -} - -static void on_job_stdout(RStream *rstream, void *data, bool eof) -{ - if (!eof) { - on_job_data(rstream, data, eof, "stdout"); - } -} - -static void on_job_stderr(RStream *rstream, void *data, bool eof) -{ - if (!eof) { - on_job_data(rstream, data, eof, "stderr"); - } -} - -static void on_job_exit(Job *job, void *data) -{ - apply_job_autocmds(job, data, "exit", NULL); -} - -static void on_job_data(RStream *rstream, void *data, bool eof, char *type) -{ - Job *job = data; - uint32_t read_count = rstream_available(rstream); - char *str = xmalloc(read_count + 1); - - rstream_read(rstream, str, read_count); - str[read_count] = NUL; - apply_job_autocmds(job, job_data(job), type, str); -} - -static void apply_job_autocmds(Job *job, char *name, char *type, char *str) -{ - list_T *list; - - // Create the list which will be set to v:job_data - list = list_alloc(); - list_append_number(list, job_id(job)); - list_append_string(list, (uint8_t *)type, -1); - - if (str) { - listitem_T *str_slot = listitem_alloc(); - str_slot->li_tv.v_type = VAR_STRING; - str_slot->li_tv.v_lock = 0; - str_slot->li_tv.vval.v_string = (uint8_t *)str; - list_append(list, str_slot); - } - - // Update v:job_data for the autocommands - set_vim_var_list(VV_JOB_DATA, list); - // Call JobActivity autocommands - apply_autocmds(EVENT_JOBACTIVITY, (uint8_t *)name, NULL, TRUE, NULL); -} - diff --git a/src/eval.h b/src/eval.h deleted file mode 100644 index 20d6bfe119..0000000000 --- a/src/eval.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef NEOVIM_EVAL_H -#define NEOVIM_EVAL_H -/* eval.c */ -void eval_init(void); -void eval_clear(void); -char_u *func_name(void *cookie); -linenr_T *func_breakpoint(void *cookie); -int *func_dbg_tick(void *cookie); -int func_level(void *cookie); -int current_func_returned(void); -void set_internal_string_var(char_u *name, char_u *value); -int var_redir_start(char_u *name, int append); -void var_redir_str(char_u *value, int value_len); -void var_redir_stop(void); -int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u * - fname_from, - char_u *fname_to); -int eval_printexpr(char_u *fname, char_u *args); -void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile); -void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile); -int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip); -char_u *eval_to_string_skip(char_u *arg, char_u **nextcmd, int skip); -int skip_expr(char_u **pp); -char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert); -char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, - int use_sandbox); -int eval_to_number(char_u *expr); -list_T *eval_spell_expr(char_u *badword, char_u *expr); -int get_spellword(list_T *list, char_u **pp); -typval_T *eval_expr(char_u *arg, char_u **nextcmd); -int call_vim_function(char_u *func, int argc, char_u **argv, int safe, - int str_arg_only, - typval_T *rettv); -long call_func_retnr(char_u *func, int argc, char_u **argv, int safe); -void *call_func_retstr(char_u *func, int argc, char_u **argv, int safe); -void *call_func_retlist(char_u *func, int argc, char_u **argv, int safe); -void *save_funccal(void); -void restore_funccal(void *vfc); -void prof_child_enter(proftime_T *tm); -void prof_child_exit(proftime_T *tm); -int eval_foldexpr(char_u *arg, int *cp); -void ex_let(exarg_T *eap); -void list_add_watch(list_T *l, listwatch_T *lw); -void list_rem_watch(list_T *l, listwatch_T *lwrem); -void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip); -int next_for_item(void *fi_void, char_u *arg); -void free_for_info(void *fi_void); -void set_context_for_expression(expand_T *xp, char_u *arg, - cmdidx_T cmdidx); -void ex_call(exarg_T *eap); -void ex_unlet(exarg_T *eap); -void ex_lockvar(exarg_T *eap); -int do_unlet(char_u *name, int forceit); -void del_menutrans_vars(void); -char_u *get_user_var_name(expand_T *xp, int idx); -list_T *list_alloc(void); -void list_unref(list_T *l); -void list_free(list_T *l, int recurse); -listitem_T *listitem_alloc(void); -void listitem_free(listitem_T *item); -void listitem_remove(list_T *l, listitem_T *item); -dictitem_T *dict_lookup(hashitem_T *hi); -listitem_T *list_find(list_T *l, long n); -char_u *list_find_str(list_T *l, long idx); -void list_append(list_T *l, listitem_T *item); -void list_append_tv(list_T *l, typval_T *tv); -void list_append_dict(list_T *list, dict_T *dict); -void list_append_string(list_T *l, char_u *str, int len); -int list_insert_tv(list_T *l, typval_T *tv, listitem_T *item); -void list_remove(list_T *l, listitem_T *item, listitem_T *item2); -void list_insert(list_T *l, listitem_T *ni, listitem_T *item); -int garbage_collect(void); -void set_ref_in_ht(hashtab_T *ht, int copyID); -void set_ref_in_list(list_T *l, int copyID); -void set_ref_in_item(typval_T *tv, int copyID); -dict_T *dict_alloc(void); -void dict_unref(dict_T *d); -void dict_free(dict_T *d, int recurse); -dictitem_T *dictitem_alloc(char_u *key); -void dictitem_free(dictitem_T *item); -int dict_add(dict_T *d, dictitem_T *item); -int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str); -int dict_add_list(dict_T *d, char *key, list_T *list); -dictitem_T *dict_find(dict_T *d, char_u *key, int len); -char_u *get_dict_string(dict_T *d, char_u *key, int save); -long get_dict_number(dict_T *d, char_u *key); -char_u *get_function_name(expand_T *xp, int idx); -char_u *get_expr_name(expand_T *xp, int idx); -int func_call(char_u *name, typval_T *args, dict_T *selfdict, - typval_T *rettv); -void dict_extend(dict_T *d1, dict_T *d2, char_u *action); -float_T vim_round(float_T f); -long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, - char_u *skip, int flags, pos_T *match_pos, - linenr_T lnum_stop, - long time_limit); -void set_vim_var_nr(int idx, long val); -long get_vim_var_nr(int idx); -char_u *get_vim_var_str(int idx); -list_T *get_vim_var_list(int idx); -void set_vim_var_char(int c); -void set_vcount(long count, long count1, int set_prevcount); -void set_vim_var_string(int idx, char_u *val, int len); -void set_vim_var_list(int idx, list_T *val); -void set_reg_var(int c); -char_u *v_exception(char_u *oldval); -char_u *v_throwpoint(char_u *oldval); -char_u *set_cmdarg(exarg_T *eap, char_u *oldarg); -void free_tv(typval_T *varp); -void clear_tv(typval_T *varp); -long get_tv_number_chk(typval_T *varp, int *denote); -char_u *get_tv_string_chk(typval_T *varp); -char_u *get_var_value(char_u *name); -void new_script_vars(scid_T id); -void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope); -void unref_var_dict(dict_T *dict); -void vars_clear(hashtab_T *ht); -void copy_tv(typval_T *from, typval_T *to); -void ex_echo(exarg_T *eap); -void ex_echohl(exarg_T *eap); -void ex_execute(exarg_T *eap); -void ex_function(exarg_T *eap); -void free_all_functions(void); -int translated_function_exists(char_u *name); -char_u *get_expanded_name(char_u *name, int check); -void func_dump_profile(FILE *fd); -char_u *get_user_func_name(expand_T *xp, int idx); -void ex_delfunction(exarg_T *eap); -void func_unref(char_u *name); -void func_ref(char_u *name); -void ex_return(exarg_T *eap); -int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); -void discard_pending_return(void *rettv); -char_u *get_return_cmd(void *rettv); -char_u *get_func_line(int c, void *cookie, int indent); -void func_line_start(void *cookie); -void func_line_exec(void *cookie); -void func_line_end(void *cookie); -int func_has_ended(void *cookie); -int func_has_abort(void *cookie); -int read_viminfo_varlist(vir_T *virp, int writing); -void write_viminfo_varlist(FILE *fp); -int store_session_globals(FILE *fd); -void last_set_msg(scid_T scriptID); -void ex_oldfiles(exarg_T *eap); -int modify_fname(char_u *src, int *usedlen, char_u **fnamep, - char_u **bufp, - int *fnamelen); -char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, - char_u *flags); - -#endif /* NEOVIM_EVAL_H */ diff --git a/src/eval_defs.h b/src/eval_defs.h deleted file mode 100644 index aa086c3278..0000000000 --- a/src/eval_defs.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef NEOVIM_EVAL_DEFS_H -#define NEOVIM_EVAL_DEFS_H - -#include "hashtab.h" - -typedef int varnumber_T; -typedef double float_T; - -typedef struct listvar_S list_T; -typedef struct dictvar_S dict_T; - -/* - * Structure to hold an internal variable without a name. - */ -typedef struct { - char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */ - char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */ - union { - varnumber_T v_number; /* number value */ - float_T v_float; /* floating number value */ - char_u *v_string; /* string value (can be NULL!) */ - list_T *v_list; /* list value (can be NULL!) */ - dict_T *v_dict; /* dict value (can be NULL!) */ - } vval; -} typval_T; - -/* Values for "v_type". */ -#define VAR_UNKNOWN 0 -#define VAR_NUMBER 1 /* "v_number" is used */ -#define VAR_STRING 2 /* "v_string" is used */ -#define VAR_FUNC 3 /* "v_string" is function name */ -#define VAR_LIST 4 /* "v_list" is used */ -#define VAR_DICT 5 /* "v_dict" is used */ -#define VAR_FLOAT 6 /* "v_float" is used */ - -/* Values for "dv_scope". */ -#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ -#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not - allowed to mask existing functions */ - -/* Values for "v_lock". */ -#define VAR_LOCKED 1 /* locked with lock(), can use unlock() */ -#define VAR_FIXED 2 /* locked forever */ - -/* - * Structure to hold an item of a list: an internal variable without a name. - */ -typedef struct listitem_S listitem_T; - -struct listitem_S { - listitem_T *li_next; /* next item in list */ - listitem_T *li_prev; /* previous item in list */ - typval_T li_tv; /* type and value of the variable */ -}; - -/* - * Struct used by those that are using an item in a list. - */ -typedef struct listwatch_S listwatch_T; - -struct listwatch_S { - listitem_T *lw_item; /* item being watched */ - listwatch_T *lw_next; /* next watcher */ -}; - -/* - * Structure to hold info about a list. - */ -struct listvar_S { - listitem_T *lv_first; /* first item, NULL if none */ - listitem_T *lv_last; /* last item, NULL if none */ - int lv_refcount; /* reference count */ - int lv_len; /* number of items */ - listwatch_T *lv_watch; /* first watcher, NULL if none */ - int lv_idx; /* cached index of an item */ - listitem_T *lv_idx_item; /* when not NULL item at index "lv_idx" */ - int lv_copyID; /* ID used by deepcopy() */ - list_T *lv_copylist; /* copied list used by deepcopy() */ - char lv_lock; /* zero, VAR_LOCKED, VAR_FIXED */ - list_T *lv_used_next; /* next list in used lists list */ - list_T *lv_used_prev; /* previous list in used lists list */ -}; - -/* - * Structure to hold an item of a Dictionary. - * Also used for a variable. - * The key is copied into "di_key" to avoid an extra alloc/free for it. - */ -struct dictitem_S { - typval_T di_tv; /* type and value of the variable */ - char_u di_flags; /* flags (only used for variable) */ - char_u di_key[1]; /* key (actually longer!) */ -}; - -typedef struct dictitem_S dictitem_T; - -#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */ -#define DI_FLAGS_RO_SBX 2 /* "di_flags" value: read-only in the sandbox */ -#define DI_FLAGS_FIX 4 /* "di_flags" value: fixed variable, not allocated */ -#define DI_FLAGS_LOCK 8 /* "di_flags" value: locked variable */ - -/* - * Structure to hold info about a Dictionary. - */ -struct dictvar_S { - char dv_lock; /* zero, VAR_LOCKED, VAR_FIXED */ - char dv_scope; /* zero, VAR_SCOPE, VAR_DEF_SCOPE */ - int dv_refcount; /* reference count */ - int dv_copyID; /* ID used by deepcopy() */ - hashtab_T dv_hashtab; /* hashtab that refers to the items */ - dict_T *dv_copydict; /* copied dict used by deepcopy() */ - dict_T *dv_used_next; /* next dict in used dicts list */ - dict_T *dv_used_prev; /* previous dict in used dicts list */ -}; - -#endif // NEOVIM_EVAL_DEFS_H diff --git a/src/ex_cmds.c b/src/ex_cmds.c deleted file mode 100644 index 3613a02cb3..0000000000 --- a/src/ex_cmds.c +++ /dev/null @@ -1,6374 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * ex_cmds.c: some functions for command line commands - */ - -#include -#include - -#include "vim.h" -#include "version_defs.h" -#include "ex_cmds.h" -#include "buffer.h" -#include "charset.h" -#include "diff.h" -#include "digraph.h" -#include "edit.h" -#include "eval.h" -#include "ex_cmds2.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "ex_getln.h" -#include "farsi.h" -#include "fileio.h" -#include "fold.h" -#include "getchar.h" -#include "indent.h" -#include "main.h" -#include "mark.h" -#include "mbyte.h" -#include "memline.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "garray.h" -#include "memory.h" -#include "move.h" -#include "normal.h" -#include "ops.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "search.h" -#include "spell.h" -#include "syntax.h" -#include "tag.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "window.h" -#include "os/os.h" -#include "os/shell.h" - -static int linelen(int *has_tab); -static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, - char_u *cmd, int do_in, - int do_out); -static char_u *viminfo_filename(char_u *); -static void do_viminfo(FILE *fp_in, FILE *fp_out, int flags); -static int viminfo_encoding(vir_T *virp); -static int read_viminfo_up_to_marks(vir_T *virp, int forceit, - int writing); - -static int check_readonly(int *forceit, buf_T *buf); -static void delbuf_msg(char_u *name); -static int help_compare(const void *s1, const void *s2); - -/* - * ":ascii" and "ga". - */ -void do_ascii(exarg_T *eap) -{ - int c; - int cval; - char buf1[20]; - char buf2[20]; - char_u buf3[7]; - int cc[MAX_MCO]; - int ci = 0; - int len; - - if (enc_utf8) - c = utfc_ptr2char(ml_get_cursor(), cc); - else - c = gchar_cursor(); - if (c == NUL) { - MSG("NUL"); - return; - } - - IObuff[0] = NUL; - if (!has_mbyte || (enc_dbcs != 0 && c < 0x100) || c < 0x80) { - if (c == NL) /* NUL is stored as NL */ - c = NUL; - if (c == CAR && get_fileformat(curbuf) == EOL_MAC) - cval = NL; /* NL is stored as CR */ - else - cval = c; - if (vim_isprintc_strict(c) && (c < ' ' - || c > '~' - )) { - transchar_nonprint(buf3, c); - vim_snprintf(buf1, sizeof(buf1), " <%s>", (char *)buf3); - } else - buf1[0] = NUL; - if (c >= 0x80) - vim_snprintf(buf2, sizeof(buf2), " ", - (char *)transchar(c & 0x7f)); - else - buf2[0] = NUL; - vim_snprintf((char *)IObuff, IOSIZE, - _("<%s>%s%s %d, Hex %02x, Octal %03o"), - transchar(c), buf1, buf2, cval, cval, cval); - if (enc_utf8) - c = cc[ci++]; - else - c = 0; - } - - /* Repeat for combining characters. */ - while (has_mbyte && (c >= 0x100 || (enc_utf8 && c >= 0x80))) { - len = (int)STRLEN(IObuff); - /* This assumes every multi-byte char is printable... */ - if (len > 0) - IObuff[len++] = ' '; - IObuff[len++] = '<'; - if (enc_utf8 && utf_iscomposing(c) -# ifdef USE_GUI - && !gui.in_use -# endif - ) - IObuff[len++] = ' '; /* draw composing char on top of a space */ - len += (*mb_char2bytes)(c, IObuff + len); - vim_snprintf((char *)IObuff + len, IOSIZE - len, - c < 0x10000 ? _("> %d, Hex %04x, Octal %o") - : _("> %d, Hex %08x, Octal %o"), c, c, c); - if (ci == MAX_MCO) - break; - if (enc_utf8) - c = cc[ci++]; - else - c = 0; - } - - msg(IObuff); -} - -/* - * ":left", ":center" and ":right": align text. - */ -void ex_align(exarg_T *eap) -{ - pos_T save_curpos; - int len; - int indent = 0; - int new_indent; - int has_tab; - int width; - - if (curwin->w_p_rl) { - /* switch left and right aligning */ - if (eap->cmdidx == CMD_right) - eap->cmdidx = CMD_left; - else if (eap->cmdidx == CMD_left) - eap->cmdidx = CMD_right; - } - - width = atoi((char *)eap->arg); - save_curpos = curwin->w_cursor; - if (eap->cmdidx == CMD_left) { /* width is used for new indent */ - if (width >= 0) - indent = width; - } else { - /* - * if 'textwidth' set, use it - * else if 'wrapmargin' set, use it - * if invalid value, use 80 - */ - if (width <= 0) - width = curbuf->b_p_tw; - if (width == 0 && curbuf->b_p_wm > 0) - width = W_WIDTH(curwin) - curbuf->b_p_wm; - if (width <= 0) - width = 80; - } - - if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) - return; - - for (curwin->w_cursor.lnum = eap->line1; - curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum) { - if (eap->cmdidx == CMD_left) /* left align */ - new_indent = indent; - else { - has_tab = FALSE; /* avoid uninit warnings */ - len = linelen(eap->cmdidx == CMD_right ? &has_tab - : NULL) - get_indent(); - - if (len <= 0) /* skip blank lines */ - continue; - - if (eap->cmdidx == CMD_center) - new_indent = (width - len) / 2; - else { - new_indent = width - len; /* right align */ - - /* - * Make sure that embedded TABs don't make the text go too far - * to the right. - */ - if (has_tab) - while (new_indent > 0) { - (void)set_indent(new_indent, 0); - if (linelen(NULL) <= width) { - /* - * Now try to move the line as much as possible to - * the right. Stop when it moves too far. - */ - do - (void)set_indent(++new_indent, 0); - while (linelen(NULL) <= width); - --new_indent; - break; - } - --new_indent; - } - } - } - if (new_indent < 0) - new_indent = 0; - (void)set_indent(new_indent, 0); /* set indent */ - } - changed_lines(eap->line1, 0, eap->line2 + 1, 0L); - curwin->w_cursor = save_curpos; - beginline(BL_WHITE | BL_FIX); -} - -/* - * Get the length of the current line, excluding trailing white space. - */ -static int linelen(int *has_tab) -{ - char_u *line; - char_u *first; - char_u *last; - int save; - int len; - - /* find the first non-blank character */ - line = ml_get_curline(); - first = skipwhite(line); - - /* find the character after the last non-blank character */ - for (last = first + STRLEN(first); - last > first && vim_iswhite(last[-1]); --last) - ; - save = *last; - *last = NUL; - len = linetabsize(line); /* get line length */ - if (has_tab != NULL) /* check for embedded TAB */ - *has_tab = (vim_strrchr(first, TAB) != NULL); - *last = save; - - return len; -} - -/* Buffer for two lines used during sorting. They are allocated to - * contain the longest line being sorted. */ -static char_u *sortbuf1; -static char_u *sortbuf2; - -static int sort_ic; /* ignore case */ -static int sort_nr; /* sort on number */ -static int sort_rx; /* sort on regex instead of skipping it */ - -static int sort_abort; /* flag to indicate if sorting has been interrupted */ - -/* Struct to store info to be sorted. */ -typedef struct { - linenr_T lnum; /* line number */ - long start_col_nr; /* starting column number or number */ - long end_col_nr; /* ending column number */ -} sorti_T; - -static int -sort_compare(const void *s1, const void *s2); - -static int sort_compare(const void *s1, const void *s2) -{ - sorti_T l1 = *(sorti_T *)s1; - sorti_T l2 = *(sorti_T *)s2; - int result = 0; - - /* If the user interrupts, there's no way to stop qsort() immediately, but - * if we return 0 every time, qsort will assume it's done sorting and - * exit. */ - if (sort_abort) - return 0; - fast_breakcheck(); - if (got_int) - sort_abort = TRUE; - - /* When sorting numbers "start_col_nr" is the number, not the column - * number. */ - if (sort_nr) - result = l1.start_col_nr == l2.start_col_nr ? 0 - : l1.start_col_nr > l2.start_col_nr ? 1 : -1; - else { - /* We need to copy one line into "sortbuf1", because there is no - * guarantee that the first pointer becomes invalid when obtaining the - * second one. */ - STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr, - l1.end_col_nr - l1.start_col_nr + 1); - sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0; - STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr, - l2.end_col_nr - l2.start_col_nr + 1); - sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0; - - result = sort_ic ? STRICMP(sortbuf1, sortbuf2) - : STRCMP(sortbuf1, sortbuf2); - } - - /* If two lines have the same value, preserve the original line order. */ - if (result == 0) - return (int)(l1.lnum - l2.lnum); - return result; -} - -/* - * ":sort". - */ -void ex_sort(exarg_T *eap) -{ - regmatch_T regmatch; - int len; - linenr_T lnum; - long maxlen = 0; - size_t count = (size_t)(eap->line2 - eap->line1 + 1); - size_t i; - char_u *p; - char_u *s; - char_u *s2; - char_u c; /* temporary character storage */ - int unique = FALSE; - long deleted; - colnr_T start_col; - colnr_T end_col; - int sort_oct; /* sort on octal number */ - int sort_hex; /* sort on hex number */ - - /* Sorting one line is really quick! */ - if (count <= 1) - return; - - if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) - return; - sortbuf1 = NULL; - sortbuf2 = NULL; - regmatch.regprog = NULL; - sorti_T *nrs = xmalloc(count * sizeof(sorti_T)); - - sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0; - - for (p = eap->arg; *p != NUL; ++p) { - if (vim_iswhite(*p)) - ; - else if (*p == 'i') - sort_ic = TRUE; - else if (*p == 'r') - sort_rx = TRUE; - else if (*p == 'n') - sort_nr = 2; - else if (*p == 'o') - sort_oct = 2; - else if (*p == 'x') - sort_hex = 2; - else if (*p == 'u') - unique = TRUE; - else if (*p == '"') /* comment start */ - break; - else if (check_nextcmd(p) != NULL) { - eap->nextcmd = check_nextcmd(p); - break; - } else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) { - s = skip_regexp(p + 1, *p, TRUE, NULL); - if (*s != *p) { - EMSG(_(e_invalpat)); - goto sortend; - } - *s = NUL; - /* Use last search pattern if sort pattern is empty. */ - if (s == p + 1) { - if (last_search_pat() == NULL) { - EMSG(_(e_noprevre)); - goto sortend; - } - regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC); - } else - regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC); - if (regmatch.regprog == NULL) - goto sortend; - p = s; /* continue after the regexp */ - regmatch.rm_ic = p_ic; - } else { - EMSG2(_(e_invarg2), p); - goto sortend; - } - } - - /* Can only have one of 'n', 'o' and 'x'. */ - if (sort_nr + sort_oct + sort_hex > 2) { - EMSG(_(e_invarg)); - goto sortend; - } - - /* From here on "sort_nr" is used as a flag for any number sorting. */ - sort_nr += sort_oct + sort_hex; - - /* - * Make an array with all line numbers. This avoids having to copy all - * the lines into allocated memory. - * When sorting on strings "start_col_nr" is the offset in the line, for - * numbers sorting it's the number to sort on. This means the pattern - * matching and number conversion only has to be done once per line. - * Also get the longest line length for allocating "sortbuf". - */ - for (lnum = eap->line1; lnum <= eap->line2; ++lnum) { - s = ml_get(lnum); - len = (int)STRLEN(s); - if (maxlen < len) - maxlen = len; - - start_col = 0; - end_col = len; - if (regmatch.regprog != NULL && vim_regexec(®match, s, 0)) { - if (sort_rx) { - start_col = (colnr_T)(regmatch.startp[0] - s); - end_col = (colnr_T)(regmatch.endp[0] - s); - } else - start_col = (colnr_T)(regmatch.endp[0] - s); - } else if (regmatch.regprog != NULL) - end_col = 0; - - if (sort_nr) { - /* Make sure vim_str2nr doesn't read any digits past the end - * of the match, by temporarily terminating the string there */ - s2 = s + end_col; - c = *s2; - *s2 = NUL; - /* Sorting on number: Store the number itself. */ - p = s + start_col; - if (sort_hex) - s = skiptohex(p); - else - s = skiptodigit(p); - if (s > p && s[-1] == '-') - --s; /* include preceding negative sign */ - if (*s == NUL) - /* empty line should sort before any number */ - nrs[lnum - eap->line1].start_col_nr = -MAXLNUM; - else - vim_str2nr(s, NULL, NULL, sort_oct, sort_hex, - &nrs[lnum - eap->line1].start_col_nr, NULL); - *s2 = c; - } else { - /* Store the column to sort at. */ - nrs[lnum - eap->line1].start_col_nr = start_col; - nrs[lnum - eap->line1].end_col_nr = end_col; - } - - nrs[lnum - eap->line1].lnum = lnum; - - if (regmatch.regprog != NULL) - fast_breakcheck(); - if (got_int) - goto sortend; - } - - /* Allocate a buffer that can hold the longest line. */ - sortbuf1 = xmalloc(maxlen + 1); - sortbuf2 = xmalloc(maxlen + 1); - - /* Sort the array of line numbers. Note: can't be interrupted! */ - qsort((void *)nrs, count, sizeof(sorti_T), sort_compare); - - if (sort_abort) - goto sortend; - - /* Insert the lines in the sorted order below the last one. */ - lnum = eap->line2; - for (i = 0; i < count; ++i) { - s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum); - if (!unique || i == 0 - || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) { - if (ml_append(lnum++, s, (colnr_T)0, FALSE) == FAIL) - break; - if (unique) - STRCPY(sortbuf1, s); - } - fast_breakcheck(); - if (got_int) - goto sortend; - } - - /* delete the original lines if appending worked */ - if (i == count) - for (i = 0; i < count; ++i) - ml_delete(eap->line1, FALSE); - else - count = 0; - - /* Adjust marks for deleted (or added) lines and prepare for displaying. */ - deleted = (long)(count - (lnum - eap->line2)); - if (deleted > 0) - mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted); - else if (deleted < 0) - mark_adjust(eap->line2, MAXLNUM, -deleted, 0L); - changed_lines(eap->line1, 0, eap->line2 + 1, -deleted); - - curwin->w_cursor.lnum = eap->line1; - beginline(BL_WHITE | BL_FIX); - -sortend: - free(nrs); - free(sortbuf1); - free(sortbuf2); - vim_regfree(regmatch.regprog); - if (got_int) - EMSG(_(e_interr)); -} - -/* - * ":retab". - */ -void ex_retab(exarg_T *eap) -{ - linenr_T lnum; - int got_tab = FALSE; - long num_spaces = 0; - long num_tabs; - long len; - long col; - long vcol; - long start_col = 0; /* For start of white-space string */ - long start_vcol = 0; /* For start of white-space string */ - int temp; - long old_len; - char_u *ptr; - char_u *new_line = (char_u *)1; /* init to non-NULL */ - int did_undo; /* called u_save for current line */ - int new_ts; - int save_list; - linenr_T first_line = 0; /* first changed line */ - linenr_T last_line = 0; /* last changed line */ - - save_list = curwin->w_p_list; - curwin->w_p_list = 0; /* don't want list mode here */ - - new_ts = getdigits(&(eap->arg)); - if (new_ts < 0) { - EMSG(_(e_positive)); - return; - } - if (new_ts == 0) - new_ts = curbuf->b_p_ts; - for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) { - ptr = ml_get(lnum); - col = 0; - vcol = 0; - did_undo = FALSE; - for (;; ) { - if (vim_iswhite(ptr[col])) { - if (!got_tab && num_spaces == 0) { - /* First consecutive white-space */ - start_vcol = vcol; - start_col = col; - } - if (ptr[col] == ' ') - num_spaces++; - else - got_tab = TRUE; - } else { - if (got_tab || (eap->forceit && num_spaces > 1)) { - /* Retabulate this string of white-space */ - - /* len is virtual length of white string */ - len = num_spaces = vcol - start_vcol; - num_tabs = 0; - if (!curbuf->b_p_et) { - temp = new_ts - (start_vcol % new_ts); - if (num_spaces >= temp) { - num_spaces -= temp; - num_tabs++; - } - num_tabs += num_spaces / new_ts; - num_spaces -= (num_spaces / new_ts) * new_ts; - } - if (curbuf->b_p_et || got_tab || - (num_spaces + num_tabs < len)) { - if (did_undo == FALSE) { - did_undo = TRUE; - if (u_save((linenr_T)(lnum - 1), - (linenr_T)(lnum + 1)) == FAIL) { - new_line = NULL; /* flag out-of-memory */ - break; - } - } - - /* len is actual number of white characters used */ - len = num_spaces + num_tabs; - old_len = (long)STRLEN(ptr); - new_line = xmalloc(old_len - col + start_col + len + 1); - - if (start_col > 0) - memmove(new_line, ptr, (size_t)start_col); - memmove(new_line + start_col + len, - ptr + col, (size_t)(old_len - col + 1)); - ptr = new_line + start_col; - for (col = 0; col < len; col++) - ptr[col] = (col < num_tabs) ? '\t' : ' '; - ml_replace(lnum, new_line, FALSE); - if (first_line == 0) - first_line = lnum; - last_line = lnum; - ptr = new_line; - col = start_col + len; - } - } - got_tab = FALSE; - num_spaces = 0; - } - if (ptr[col] == NUL) - break; - vcol += chartabsize(ptr + col, (colnr_T)vcol); - if (has_mbyte) - col += (*mb_ptr2len)(ptr + col); - else - ++col; - } - if (new_line == NULL) /* out of memory */ - break; - line_breakcheck(); - } - if (got_int) - EMSG(_(e_interr)); - - if (curbuf->b_p_ts != new_ts) - redraw_curbuf_later(NOT_VALID); - if (first_line != 0) - changed_lines(first_line, 0, last_line + 1, 0L); - - curwin->w_p_list = save_list; /* restore 'list' */ - - curbuf->b_p_ts = new_ts; - coladvance(curwin->w_curswant); - - u_clearline(); -} - -/* - * :move command - move lines line1-line2 to line dest - * - * return FAIL for failure, OK otherwise - */ -int do_move(linenr_T line1, linenr_T line2, linenr_T dest) -{ - char_u *str; - linenr_T l; - linenr_T extra; /* Num lines added before line1 */ - linenr_T num_lines; /* Num lines moved */ - linenr_T last_line; /* Last line in file after adding new text */ - - if (dest >= line1 && dest < line2) { - EMSG(_("E134: Move lines into themselves")); - return FAIL; - } - - num_lines = line2 - line1 + 1; - - /* - * First we copy the old text to its new location -- webb - * Also copy the flag that ":global" command uses. - */ - if (u_save(dest, dest + 1) == FAIL) - return FAIL; - for (extra = 0, l = line1; l <= line2; l++) { - str = vim_strsave(ml_get(l + extra)); - if (str != NULL) { - ml_append(dest + l - line1, str, (colnr_T)0, FALSE); - free(str); - if (dest < line1) - extra++; - } - } - - /* - * Now we must be careful adjusting our marks so that we don't overlap our - * mark_adjust() calls. - * - * We adjust the marks within the old text so that they refer to the - * last lines of the file (temporarily), because we know no other marks - * will be set there since these line numbers did not exist until we added - * our new lines. - * - * Then we adjust the marks on lines between the old and new text positions - * (either forwards or backwards). - * - * And Finally we adjust the marks we put at the end of the file back to - * their final destination at the new text position -- webb - */ - last_line = curbuf->b_ml.ml_line_count; - mark_adjust(line1, line2, last_line - line2, 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines); - if (dest >= line2) { - mark_adjust(line2 + 1, dest, -num_lines, 0L); - curbuf->b_op_start.lnum = dest - num_lines + 1; - curbuf->b_op_end.lnum = dest; - } else { - mark_adjust(dest + 1, line1 - 1, num_lines, 0L); - curbuf->b_op_start.lnum = dest + 1; - curbuf->b_op_end.lnum = dest + num_lines; - } - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - mark_adjust(last_line - num_lines + 1, last_line, - -(last_line - dest - extra), 0L); - changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra); - - /* - * Now we delete the original text -- webb - */ - if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL) - return FAIL; - - for (l = line1; l <= line2; l++) - ml_delete(line1 + extra, TRUE); - - if (!global_busy && num_lines > p_report) { - if (num_lines == 1) - MSG(_("1 line moved")); - else - smsg((char_u *)_("%" PRId64 " lines moved"), (int64_t)num_lines); - } - - /* - * Leave the cursor on the last of the moved lines. - */ - if (dest >= line1) - curwin->w_cursor.lnum = dest; - else - curwin->w_cursor.lnum = dest + (line2 - line1) + 1; - - if (line1 < dest) { - dest += num_lines + 1; - last_line = curbuf->b_ml.ml_line_count; - if (dest > last_line + 1) - dest = last_line + 1; - changed_lines(line1, 0, dest, 0L); - } else - changed_lines(dest + 1, 0, line1 + num_lines, 0L); - - return OK; -} - -/* - * ":copy" - */ -void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) -{ - linenr_T count; - char_u *p; - - count = line2 - line1 + 1; - curbuf->b_op_start.lnum = n + 1; - curbuf->b_op_end.lnum = n + count; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - - /* - * there are three situations: - * 1. destination is above line1 - * 2. destination is between line1 and line2 - * 3. destination is below line2 - * - * n = destination (when starting) - * curwin->w_cursor.lnum = destination (while copying) - * line1 = start of source (while copying) - * line2 = end of source (while copying) - */ - if (u_save(n, n + 1) == FAIL) - return; - - curwin->w_cursor.lnum = n; - while (line1 <= line2) { - /* need to use vim_strsave() because the line will be unlocked within - * ml_append() */ - p = vim_strsave(ml_get(line1)); - if (p != NULL) { - ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE); - free(p); - } - /* situation 2: skip already copied lines */ - if (line1 == n) - line1 = curwin->w_cursor.lnum; - ++line1; - if (curwin->w_cursor.lnum < line1) - ++line1; - if (curwin->w_cursor.lnum < line2) - ++line2; - ++curwin->w_cursor.lnum; - } - - appended_lines_mark(n, count); - - msgmore((long)count); -} - -static char_u *prevcmd = NULL; /* the previous command */ - -#if defined(EXITFREE) || defined(PROTO) -void free_prev_shellcmd(void) -{ - free(prevcmd); -} - -#endif - -/* - * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd" - * Bangs in the argument are replaced with the previously entered command. - * Remember the argument. - */ -void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) -{ - char_u *arg = eap->arg; /* command */ - linenr_T line1 = eap->line1; /* start of range */ - linenr_T line2 = eap->line2; /* end of range */ - char_u *newcmd = NULL; /* the new command */ - int free_newcmd = FALSE; /* need to free() newcmd */ - int ins_prevcmd; - char_u *t; - char_u *p; - char_u *trailarg; - int len; - int scroll_save = msg_scroll; - - /* - * Disallow shell commands for "rvim". - * Disallow shell commands from .exrc and .vimrc in current directory for - * security reasons. - */ - if (check_restricted() || check_secure()) - return; - - if (addr_count == 0) { /* :! */ - msg_scroll = FALSE; /* don't scroll here */ - autowrite_all(); - msg_scroll = scroll_save; - } - - /* - * Try to find an embedded bang, like in :! ! [args] - * (:!! is indicated by the 'forceit' variable) - */ - ins_prevcmd = forceit; - trailarg = arg; - do { - len = (int)STRLEN(trailarg) + 1; - if (newcmd != NULL) - len += (int)STRLEN(newcmd); - if (ins_prevcmd) { - if (prevcmd == NULL) { - EMSG(_(e_noprev)); - free(newcmd); - return; - } - len += (int)STRLEN(prevcmd); - } - t = xmalloc(len); - *t = NUL; - if (newcmd != NULL) - STRCAT(t, newcmd); - if (ins_prevcmd) - STRCAT(t, prevcmd); - p = t + STRLEN(t); - STRCAT(t, trailarg); - free(newcmd); - newcmd = t; - - /* - * Scan the rest of the argument for '!', which is replaced by the - * previous command. "\!" is replaced by "!" (this is vi compatible). - */ - trailarg = NULL; - while (*p) { - if (*p == '!') { - if (p > newcmd && p[-1] == '\\') - STRMOVE(p - 1, p); - else { - trailarg = p; - *trailarg++ = NUL; - ins_prevcmd = TRUE; - break; - } - } - ++p; - } - } while (trailarg != NULL); - - free(prevcmd); - prevcmd = newcmd; - - if (bangredo) { /* put cmd in redo buffer for ! command */ - /* If % or # appears in the command, it must have been escaped. - * Reescape them, so that redoing them does not substitute them by the - * buffername. */ - char_u *cmd = vim_strsave_escaped(prevcmd, (char_u *)"%#"); - - AppendToRedobuffLit(cmd, -1); - free(cmd); - AppendToRedobuff((char_u *)"\n"); - bangredo = FALSE; - } - /* - * Add quotes around the command, for shells that need them. - */ - if (*p_shq != NUL) { - newcmd = xmalloc(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1); - STRCPY(newcmd, p_shq); - STRCAT(newcmd, prevcmd); - STRCAT(newcmd, p_shq); - free_newcmd = TRUE; - } - if (addr_count == 0) { /* :! */ - /* echo the command */ - msg_start(); - msg_putchar(':'); - msg_putchar('!'); - msg_outtrans(newcmd); - msg_clr_eos(); - windgoto(msg_row, msg_col); - - do_shell(newcmd, 0); - } else { /* :range! */ - /* Careful: This may recursively call do_bang() again! (because of - * autocommands) */ - do_filter(line1, line2, eap, newcmd, do_in, do_out); - apply_autocmds(EVENT_SHELLFILTERPOST, NULL, NULL, FALSE, curbuf); - } - if (free_newcmd) - free(newcmd); -} - -/* - * do_filter: filter lines through a command given by the user - * - * We mostly use temp files and the call_shell() routine here. This would - * normally be done using pipes on a UNIX machine, but this is more portable - * to non-unix machines. The call_shell() routine needs to be able - * to deal with redirection somehow, and should handle things like looking - * at the PATH env. variable, and adding reasonable extensions to the - * command name given by the user. All reasonable versions of call_shell() - * do this. - * Alternatively, if on Unix and redirecting input or output, but not both, - * and the 'shelltemp' option isn't set, use pipes. - * We use input redirection if do_in is TRUE. - * We use output redirection if do_out is TRUE. - */ -static void -do_filter ( - linenr_T line1, - linenr_T line2, - exarg_T *eap, /* for forced 'ff' and 'fenc' */ - char_u *cmd, - int do_in, - int do_out -) -{ - char_u *itmp = NULL; - char_u *otmp = NULL; - linenr_T linecount; - linenr_T read_linecount; - pos_T cursor_save; - char_u *cmd_buf; - buf_T *old_curbuf = curbuf; - int shell_flags = 0; - - if (*cmd == NUL) /* no filter command */ - return; - - - cursor_save = curwin->w_cursor; - linecount = line2 - line1 + 1; - curwin->w_cursor.lnum = line1; - curwin->w_cursor.col = 0; - changed_line_abv_curs(); - invalidate_botline(); - - /* - * When using temp files: - * 1. * Form temp file names - * 2. * Write the lines to a temp file - * 3. Run the filter command on the temp file - * 4. * Read the output of the command into the buffer - * 5. * Delete the original lines to be filtered - * 6. * Remove the temp files - * - * When writing the input with a pipe or when catching the output with a - * pipe only need to do 3. - */ - - if (do_out) - shell_flags |= kShellOptDoOut; - - if (!do_in && do_out && !p_stmp) { - // Use a pipe to fetch stdout of the command, do not use a temp file. - shell_flags |= kShellOptRead; - curwin->w_cursor.lnum = line2; - } else if (do_in && !do_out && !p_stmp) { - // Use a pipe to write stdin of the command, do not use a temp file. - shell_flags |= kShellOptWrite; - curbuf->b_op_start.lnum = line1; - curbuf->b_op_end.lnum = line2; - } else if (do_in && do_out && !p_stmp) { - // Use a pipe to write stdin and fetch stdout of the command, do not - // use a temp file. - shell_flags |= kShellOptRead | kShellOptWrite; - curbuf->b_op_start.lnum = line1; - curbuf->b_op_end.lnum = line2; - curwin->w_cursor.lnum = line2; - } else if ((do_in && (itmp = vim_tempname('i')) == NULL) - || (do_out && (otmp = vim_tempname('o')) == NULL)) { - EMSG(_(e_notmp)); - goto filterend; - } - - /* - * The writing and reading of temp files will not be shown. - * Vi also doesn't do this and the messages are not very informative. - */ - ++no_wait_return; /* don't call wait_return() while busy */ - if (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap, - FALSE, FALSE, FALSE, TRUE) == FAIL) { - msg_putchar('\n'); /* keep message from buf_write() */ - --no_wait_return; - if (!aborting()) - (void)EMSG2(_(e_notcreate), itmp); /* will call wait_return */ - goto filterend; - } - if (curbuf != old_curbuf) - goto filterend; - - if (!do_out) - msg_putchar('\n'); - - /* Create the shell command in allocated memory. */ - cmd_buf = make_filter_cmd(cmd, itmp, otmp); - - windgoto((int)Rows - 1, 0); - cursor_on(); - - /* - * When not redirecting the output the command can write anything to the - * screen. If 'shellredir' is equal to ">", screen may be messed up by - * stderr output of external command. Clear the screen later. - * If do_in is FALSE, this could be something like ":r !cat", which may - * also mess up the screen, clear it later. - */ - if (!do_out || STRCMP(p_srr, ">") == 0 || !do_in) - redraw_later_clear(); - - if (do_out) { - if (u_save((linenr_T)(line2), (linenr_T)(line2 + 1)) == FAIL) { - free(cmd_buf); - goto error; - } - redraw_curbuf_later(VALID); - } - read_linecount = curbuf->b_ml.ml_line_count; - - /* - * When call_shell() fails wait_return() is called to give the user a - * chance to read the error messages. Otherwise errors are ignored, so you - * can see the error messages from the command that appear on stdout; use - * 'u' to fix the text - * Switch to cooked mode when not redirecting stdin, avoids that something - * like ":r !cat" hangs. - * Pass on the kShellDoOut flag when the output is being redirected. - */ - if (call_shell( - cmd_buf, - kShellOptFilter | kShellOptCooked | shell_flags, - NULL - )) { - redraw_later_clear(); - wait_return(FALSE); - } - free(cmd_buf); - - did_check_timestamps = FALSE; - need_check_timestamps = TRUE; - - /* When interrupting the shell command, it may still have produced some - * useful output. Reset got_int here, so that readfile() won't cancel - * reading. */ - ui_breakcheck(); - got_int = FALSE; - - if (do_out) { - if (otmp != NULL) { - if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, - eap, READ_FILTER) == FAIL) { - if (!aborting()) { - msg_putchar('\n'); - EMSG2(_(e_notread), otmp); - } - goto error; - } - if (curbuf != old_curbuf) - goto filterend; - } - - read_linecount = curbuf->b_ml.ml_line_count - read_linecount; - - if (shell_flags & kShellOptRead) { - curbuf->b_op_start.lnum = line2 + 1; - curbuf->b_op_end.lnum = curwin->w_cursor.lnum; - appended_lines_mark(line2, read_linecount); - } - - if (do_in) { - if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL) { - if (read_linecount >= linecount) - /* move all marks from old lines to new lines */ - mark_adjust(line1, line2, linecount, 0L); - else { - /* move marks from old lines to new lines, delete marks - * that are in deleted lines */ - mark_adjust(line1, line1 + read_linecount - 1, - linecount, 0L); - mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L); - } - } - - /* - * Put cursor on first filtered line for ":range!cmd". - * Adjust '[ and '] (set by buf_write()). - */ - curwin->w_cursor.lnum = line1; - del_lines(linecount, TRUE); - curbuf->b_op_start.lnum -= linecount; /* adjust '[ */ - curbuf->b_op_end.lnum -= linecount; /* adjust '] */ - write_lnum_adjust(-linecount); /* adjust last line - for next write */ - foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum); - } else { - /* - * Put cursor on last new line for ":r !cmd". - */ - linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1; - curwin->w_cursor.lnum = curbuf->b_op_end.lnum; - } - - beginline(BL_WHITE | BL_FIX); /* cursor on first non-blank */ - --no_wait_return; - - if (linecount > p_report) { - if (do_in) { - vim_snprintf((char *)msg_buf, sizeof(msg_buf), - _("%" PRId64 " lines filtered"), (int64_t)linecount); - if (msg(msg_buf) && !msg_scroll) - /* save message to display it after redraw */ - set_keep_msg(msg_buf, 0); - } else - msgmore((long)linecount); - } - } else { -error: - /* put cursor back in same position for ":w !cmd" */ - curwin->w_cursor = cursor_save; - --no_wait_return; - wait_return(FALSE); - } - -filterend: - - if (curbuf != old_curbuf) { - --no_wait_return; - EMSG(_("E135: *Filter* Autocommands must not change current buffer")); - } - if (itmp != NULL) - os_remove((char *)itmp); - if (otmp != NULL) - os_remove((char *)otmp); - free(itmp); - free(otmp); -} - -/* - * Call a shell to execute a command. - * When "cmd" is NULL start an interactive shell. - */ -void -do_shell ( - char_u *cmd, - int flags /* may be SHELL_DOOUT when output is redirected */ -) -{ - buf_T *buf; - int save_nwr; - - /* - * Disallow shell commands for "rvim". - * Disallow shell commands from .exrc and .vimrc in current directory for - * security reasons. - */ - if (check_restricted() || check_secure()) { - msg_end(); - return; - } - - - /* - * For autocommands we want to get the output on the current screen, to - * avoid having to type return below. - */ - msg_putchar('\r'); /* put cursor at start of line */ - if (!autocmd_busy) { - stoptermcap(); - } - msg_putchar('\n'); /* may shift screen one line up */ - - /* warning message before calling the shell */ - if (p_warn - && !autocmd_busy - && msg_silent == 0) - for (buf = firstbuf; buf; buf = buf->b_next) - if (bufIsChanged(buf)) { - MSG_PUTS(_("[No write since last change]\n")); - break; - } - - /* This windgoto is required for when the '\n' resulted in a "delete line - * 1" command to the terminal. */ - if (!swapping_screen()) - windgoto(msg_row, msg_col); - cursor_on(); - (void)call_shell(cmd, kShellOptCooked | flags, NULL); - did_check_timestamps = FALSE; - need_check_timestamps = TRUE; - - /* - * put the message cursor at the end of the screen, avoids wait_return() - * to overwrite the text that the external command showed - */ - if (!swapping_screen()) { - msg_row = Rows - 1; - msg_col = 0; - } - - if (autocmd_busy) { - if (msg_silent == 0) - redraw_later_clear(); - } else { - /* - * For ":sh" there is no need to call wait_return(), just redraw. - * Also for the Win32 GUI (the output is in a console window). - * Otherwise there is probably text on the screen that the user wants - * to read before redrawing, so call wait_return(). - */ - if (cmd == NULL - ) { - if (msg_silent == 0) - redraw_later_clear(); - need_wait_return = FALSE; - } else { - /* - * If we switch screens when starttermcap() is called, we really - * want to wait for "hit return to continue". - */ - save_nwr = no_wait_return; - if (swapping_screen()) - no_wait_return = FALSE; - wait_return(msg_silent == 0); - no_wait_return = save_nwr; - } - - starttermcap(); /* start termcap if not done by wait_return() */ - - /* - * In an Amiga window redrawing is caused by asking the window size. - * If we got an interrupt this will not work. The chance that the - * window size is wrong is very small, but we need to redraw the - * screen. Don't do this if ':' hit in wait_return(). THIS IS UGLY - * but it saves an extra redraw. - */ - } - - /* display any error messages now */ - display_errors(); - - apply_autocmds(EVENT_SHELLCMDPOST, NULL, NULL, FALSE, curbuf); -} - -/* - * Create a shell command from a command string, input redirection file and - * output redirection file. - * Returns an allocated string with the shell command. - */ -char_u * -make_filter_cmd ( - char_u *cmd, /* command */ - char_u *itmp, /* NULL or name of input file */ - char_u *otmp /* NULL or name of output file */ -) -{ - size_t len = STRLEN(cmd) + 3; /* "()" + NUL */ - if (itmp != NULL) - len += STRLEN(itmp) + 9; /* " { < " + " } " */ - if (otmp != NULL) - len += STRLEN(otmp) + STRLEN(p_srr) + 2; /* " " */ - char_u *buf = xmalloc(len); - -#if defined(UNIX) - /* - * Put braces around the command (for concatenated commands) when - * redirecting input and/or output. - */ - if (itmp != NULL || otmp != NULL) - vim_snprintf((char *)buf, len, "(%s)", (char *)cmd); - else - STRCPY(buf, cmd); - if (itmp != NULL) { - STRCAT(buf, " < "); - STRCAT(buf, itmp); - } -#else - /* - * for shells that don't understand braces around commands, at least allow - * the use of commands in a pipe. - */ - STRCPY(buf, cmd); - if (itmp != NULL) { - char_u *p; - - /* - * If there is a pipe, we have to put the '<' in front of it. - * Don't do this when 'shellquote' is not empty, otherwise the - * redirection would be inside the quotes. - */ - if (*p_shq == NUL) { - p = vim_strchr(buf, '|'); - if (p != NULL) - *p = NUL; - } - STRCAT(buf, " < "); - STRCAT(buf, itmp); - if (*p_shq == NUL) { - p = vim_strchr(cmd, '|'); - if (p != NULL) { - STRCAT(buf, " "); /* insert a space before the '|' for DOS */ - STRCAT(buf, p); - } - } - } -#endif - if (otmp != NULL) - append_redir(buf, (int)len, p_srr, otmp); - - return buf; -} - -/* - * Append output redirection for file "fname" to the end of string buffer - * "buf[buflen]" - * Works with the 'shellredir' and 'shellpipe' options. - * The caller should make sure that there is enough room: - * STRLEN(opt) + STRLEN(fname) + 3 - */ -void append_redir(char_u *buf, int buflen, char_u *opt, char_u *fname) -{ - char_u *p; - char_u *end; - - end = buf + STRLEN(buf); - /* find "%s" */ - for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p) { - if (p[1] == 's') /* found %s */ - break; - if (p[1] == '%') /* skip %% */ - ++p; - } - if (p != NULL) { - *end = ' '; /* not really needed? Not with sh, ksh or bash */ - vim_snprintf((char *)end + 1, (size_t)(buflen - (end + 1 - buf)), - (char *)opt, (char *)fname); - } else - vim_snprintf((char *)end, (size_t)(buflen - (end - buf)), - " %s %s", - (char *)opt, (char *)fname); -} - - -static int no_viminfo(void); -static int viminfo_errcnt; - -static int no_viminfo(void) -{ - /* "vim -i NONE" does not read or write a viminfo file */ - return use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0; -} - -/* - * Report an error for reading a viminfo file. - * Count the number of errors. When there are more than 10, return TRUE. - */ -int viminfo_error(char *errnum, char *message, char_u *line) -{ - vim_snprintf((char *)IObuff, IOSIZE, _("%sviminfo: %s in line: "), - errnum, message); - STRNCAT(IObuff, line, IOSIZE - STRLEN(IObuff) - 1); - if (IObuff[STRLEN(IObuff) - 1] == '\n') - IObuff[STRLEN(IObuff) - 1] = NUL; - emsg(IObuff); - if (++viminfo_errcnt >= 10) { - EMSG(_("E136: viminfo: Too many errors, skipping rest of file")); - return TRUE; - } - return FALSE; -} - -/* - * read_viminfo() -- Read the viminfo file. Registers etc. which are already - * set are not over-written unless "flags" includes VIF_FORCEIT. -- webb - */ -int -read_viminfo ( - char_u *file, /* file name or NULL to use default name */ - int flags /* VIF_WANT_INFO et al. */ -) -{ - FILE *fp; - char_u *fname; - - if (no_viminfo()) - return FAIL; - - fname = viminfo_filename(file); /* get file name in allocated buffer */ - if (fname == NULL) - return FAIL; - fp = mch_fopen((char *)fname, READBIN); - - if (p_verbose > 0) { - verbose_enter(); - smsg((char_u *)_("Reading viminfo file \"%s\"%s%s%s"), - fname, - (flags & VIF_WANT_INFO) ? _(" info") : "", - (flags & VIF_WANT_MARKS) ? _(" marks") : "", - (flags & VIF_GET_OLDFILES) ? _(" oldfiles") : "", - fp == NULL ? _(" FAILED") : ""); - verbose_leave(); - } - - free(fname); - if (fp == NULL) - return FAIL; - - viminfo_errcnt = 0; - do_viminfo(fp, NULL, flags); - - fclose(fp); - return OK; -} - -/* - * Write the viminfo file. The old one is read in first so that effectively a - * merge of current info and old info is done. This allows multiple vims to - * run simultaneously, without losing any marks etc. - * If "forceit" is TRUE, then the old file is not read in, and only internal - * info is written to the file. - */ -void write_viminfo(char_u *file, int forceit) -{ - char_u *fname; - FILE *fp_in = NULL; /* input viminfo file, if any */ - FILE *fp_out = NULL; /* output viminfo file */ - char_u *tempname = NULL; /* name of temp viminfo file */ - char_u *wp; -#if defined(UNIX) - mode_t umask_save; -#endif - - if (no_viminfo()) - return; - - fname = viminfo_filename(file); /* may set to default if NULL */ - if (fname == NULL) - return; - - fp_in = mch_fopen((char *)fname, READBIN); - if (fp_in == NULL) { - /* if it does exist, but we can't read it, don't try writing */ - if (os_file_exists(fname)) - goto end; -#if defined(UNIX) - /* - * For Unix we create the .viminfo non-accessible for others, - * because it may contain text from non-accessible documents. - */ - umask_save = umask(077); -#endif - fp_out = mch_fopen((char *)fname, WRITEBIN); -#if defined(UNIX) - (void)umask(umask_save); -#endif - } else { - /* - * There is an existing viminfo file. Create a temporary file to - * write the new viminfo into, in the same directory as the - * existing viminfo file, which will be renamed later. - */ -#ifdef UNIX - /* - * For Unix we check the owner of the file. It's not very nice to - * overwrite a user's viminfo file after a "su root", with a - * viminfo file that the user can't read. - */ - - FileInfo old_info; // FileInfo of existing viminfo file - if (os_get_file_info((char *)fname, &old_info) - && getuid() != ROOT_UID - && !(old_info.stat.st_uid == getuid() - ? (old_info.stat.st_mode & 0200) - : (old_info.stat.st_gid == getgid() - ? (old_info.stat.st_mode & 0020) - : (old_info.stat.st_mode & 0002)))) { - int tt = msg_didany; - - /* avoid a wait_return for this message, it's annoying */ - EMSG2(_("E137: Viminfo file is not writable: %s"), fname); - msg_didany = tt; - fclose(fp_in); - goto end; - } -#endif - - // Make tempname - tempname = modname(fname, (char_u *)".tmp", FALSE); - if (tempname != NULL) { - /* - * Check if tempfile already exists. Never overwrite an - * existing file! - */ - if (os_file_exists(tempname)) { - /* - * Try another name. Change one character, just before - * the extension. - */ - wp = tempname + STRLEN(tempname) - 5; - if (wp < path_tail(tempname)) /* empty file name? */ - wp = path_tail(tempname); - for (*wp = 'z'; os_file_exists(tempname); --*wp) { - /* - * They all exist? Must be something wrong! Don't - * write the viminfo file then. - */ - if (*wp == 'a') { - free(tempname); - tempname = NULL; - break; - } - } - } - } - - if (tempname != NULL) { - int fd; - - /* Use mch_open() to be able to use O_NOFOLLOW and set file - * protection: - * Unix: same as original file, but strip s-bit. Reset umask to - * avoid it getting in the way. - * Others: r&w for user only. */ -# ifdef UNIX - umask_save = umask(0); - fd = mch_open((char *)tempname, - O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, - (int)((old_info.stat.st_mode & 0777) | 0600)); - (void)umask(umask_save); -# else - fd = mch_open((char *)tempname, - O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, 0600); -# endif - if (fd < 0) - fp_out = NULL; - else - fp_out = fdopen(fd, WRITEBIN); - - /* - * If we can't create in the same directory, try creating a - * "normal" temp file. - */ - if (fp_out == NULL) { - free(tempname); - if ((tempname = vim_tempname('o')) != NULL) - fp_out = mch_fopen((char *)tempname, WRITEBIN); - } - -#if defined(UNIX) && defined(HAVE_FCHOWN) - /* - * Make sure the owner can read/write it. This only works for - * root. - */ - if (fp_out != NULL) { - fchown(fileno(fp_out), old_info.stat.st_uid, old_info.stat.st_gid); - } -#endif - } - } - - /* - * Check if the new viminfo file can be written to. - */ - if (fp_out == NULL) { - EMSG2(_("E138: Can't write viminfo file %s!"), - (fp_in == NULL || tempname == NULL) ? fname : tempname); - if (fp_in != NULL) - fclose(fp_in); - goto end; - } - - if (p_verbose > 0) { - verbose_enter(); - smsg((char_u *)_("Writing viminfo file \"%s\""), fname); - verbose_leave(); - } - - viminfo_errcnt = 0; - do_viminfo(fp_in, fp_out, forceit ? 0 : (VIF_WANT_INFO | VIF_WANT_MARKS)); - - fclose(fp_out); /* errors are ignored !? */ - if (fp_in != NULL) { - fclose(fp_in); - - /* - * In case of an error keep the original viminfo file. - * Otherwise rename the newly written file. - */ - if (viminfo_errcnt || vim_rename(tempname, fname) == -1) { - os_remove((char *)tempname); - } - } - -end: - free(fname); - free(tempname); -} - -/* - * Get the viminfo file name to use. - * If "file" is given and not empty, use it (has already been expanded by - * cmdline functions). - * Otherwise use "-i file_name", value from 'viminfo' or the default, and - * expand environment variables. - * Returns an allocated string. NULL when out of memory. - */ -static char_u *viminfo_filename(char_u *file) -{ - if (file == NULL || *file == NUL) { - if (use_viminfo != NULL) - file = use_viminfo; - else if ((file = find_viminfo_parameter('n')) == NULL || *file == NUL) { -#ifdef VIMINFO_FILE2 - /* don't use $HOME when not defined (turned into "c:/"!). */ - if (os_getenv((char_u *)"HOME") == NULL) { - /* don't use $VIM when not available. */ - expand_env((char_u *)"$VIM", NameBuff, MAXPATHL); - if (STRCMP("$VIM", NameBuff) != 0) /* $VIM was expanded */ - file = (char_u *)VIMINFO_FILE2; - else - file = (char_u *)VIMINFO_FILE; - } else -#endif - file = (char_u *)VIMINFO_FILE; - } - expand_env(file, NameBuff, MAXPATHL); - file = NameBuff; - } - return vim_strsave(file); -} - -/* - * do_viminfo() -- Should only be called from read_viminfo() & write_viminfo(). - */ -static void do_viminfo(FILE *fp_in, FILE *fp_out, int flags) -{ - int count = 0; - int eof = FALSE; - vir_T vir; - int merge = FALSE; - - vir.vir_line = xmalloc(LSIZE); - vir.vir_fd = fp_in; - vir.vir_conv.vc_type = CONV_NONE; - - if (fp_in != NULL) { - if (flags & VIF_WANT_INFO) { - eof = read_viminfo_up_to_marks(&vir, - flags & VIF_FORCEIT, fp_out != NULL); - merge = TRUE; - } else if (flags != 0) - /* Skip info, find start of marks */ - while (!(eof = viminfo_readline(&vir)) - && vir.vir_line[0] != '>') - ; - } - if (fp_out != NULL) { - /* Write the info: */ - fprintf(fp_out, _("# This viminfo file was generated by Vim %s.\n"), - VIM_VERSION_MEDIUM); - fputs(_("# You may edit it if you're careful!\n\n"), fp_out); - fputs(_("# Value of 'encoding' when this file was written\n"), fp_out); - fprintf(fp_out, "*encoding=%s\n\n", p_enc); - write_viminfo_search_pattern(fp_out); - write_viminfo_sub_string(fp_out); - write_viminfo_history(fp_out, merge); - write_viminfo_registers(fp_out); - write_viminfo_varlist(fp_out); - write_viminfo_filemarks(fp_out); - write_viminfo_bufferlist(fp_out); - count = write_viminfo_marks(fp_out); - } - if (fp_in != NULL - && (flags & (VIF_WANT_MARKS | VIF_GET_OLDFILES | VIF_FORCEIT))) - copy_viminfo_marks(&vir, fp_out, count, eof, flags); - - free(vir.vir_line); - if (vir.vir_conv.vc_type != CONV_NONE) - convert_setup(&vir.vir_conv, NULL, NULL); -} - -/* - * read_viminfo_up_to_marks() -- Only called from do_viminfo(). Reads in the - * first part of the viminfo file which contains everything but the marks that - * are local to a file. Returns TRUE when end-of-file is reached. -- webb - */ -static int read_viminfo_up_to_marks(vir_T *virp, int forceit, int writing) -{ - int eof; - buf_T *buf; - - prepare_viminfo_history(forceit ? 9999 : 0, writing); - eof = viminfo_readline(virp); - while (!eof && virp->vir_line[0] != '>') { - switch (virp->vir_line[0]) { - /* Characters reserved for future expansion, ignored now */ - case '+': /* "+40 /path/dir file", for running vim without args */ - case '|': /* to be defined */ - case '^': /* to be defined */ - case '<': /* long line - ignored */ - /* A comment or empty line. */ - case NUL: - case '\r': - case '\n': - case '#': - eof = viminfo_readline(virp); - break; - case '*': /* "*encoding=value" */ - eof = viminfo_encoding(virp); - break; - case '!': /* global variable */ - eof = read_viminfo_varlist(virp, writing); - break; - case '%': /* entry for buffer list */ - eof = read_viminfo_bufferlist(virp, writing); - break; - case '"': - eof = read_viminfo_register(virp, forceit); - break; - case '/': /* Search string */ - case '&': /* Substitute search string */ - case '~': /* Last search string, followed by '/' or '&' */ - eof = read_viminfo_search_pattern(virp, forceit); - break; - case '$': - eof = read_viminfo_sub_string(virp, forceit); - break; - case ':': - case '?': - case '=': - case '@': - eof = read_viminfo_history(virp, writing); - break; - case '-': - case '\'': - eof = read_viminfo_filemark(virp, forceit); - break; - default: - if (viminfo_error("E575: ", _("Illegal starting char"), - virp->vir_line)) - eof = TRUE; - else - eof = viminfo_readline(virp); - break; - } - } - - /* Finish reading history items. */ - if (!writing) - finish_viminfo_history(); - - /* Change file names to buffer numbers for fmarks. */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - fmarks_check_names(buf); - - return eof; -} - -/* - * Compare the 'encoding' value in the viminfo file with the current value of - * 'encoding'. If different and the 'c' flag is in 'viminfo', setup for - * conversion of text with iconv() in viminfo_readstring(). - */ -static int viminfo_encoding(vir_T *virp) -{ - char_u *p; - int i; - - if (get_viminfo_parameter('c') != 0) { - p = vim_strchr(virp->vir_line, '='); - if (p != NULL) { - /* remove trailing newline */ - ++p; - for (i = 0; vim_isprintc(p[i]); ++i) - ; - p[i] = NUL; - - convert_setup(&virp->vir_conv, p, p_enc); - } - } - return viminfo_readline(virp); -} - -/* - * Read a line from the viminfo file. - * Returns TRUE for end-of-file; - */ -int viminfo_readline(vir_T *virp) -{ - return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd); -} - -/* - * check string read from viminfo file - * remove '\n' at the end of the line - * - replace CTRL-V CTRL-V with CTRL-V - * - replace CTRL-V 'n' with '\n' - * - * Check for a long line as written by viminfo_writestring(). - * - * Return the string in allocated memory (NULL when out of memory). - */ -char_u * -viminfo_readstring ( - vir_T *virp, - int off, /* offset for virp->vir_line */ - int convert /* convert the string */ -) -{ - char_u *retval; - char_u *s, *d; - - if (virp->vir_line[off] == Ctrl_V && vim_isdigit(virp->vir_line[off + 1])) { - ssize_t len = atol((char *)virp->vir_line + off + 1); - retval = xmalloc(len); - // TODO(philix): change type of vim_fgets() size argument to size_t - (void)vim_fgets(retval, (int)len, virp->vir_fd); - s = retval + 1; /* Skip the leading '<' */ - } else { - retval = vim_strsave(virp->vir_line + off); - if (retval == NULL) - return NULL; - s = retval; - } - - /* Change CTRL-V CTRL-V to CTRL-V and CTRL-V n to \n in-place. */ - d = retval; - while (*s != NUL && *s != '\n') { - if (s[0] == Ctrl_V && s[1] != NUL) { - if (s[1] == 'n') - *d++ = '\n'; - else - *d++ = Ctrl_V; - s += 2; - } else - *d++ = *s++; - } - *d = NUL; - - if (convert && virp->vir_conv.vc_type != CONV_NONE && *retval != NUL) { - d = string_convert(&virp->vir_conv, retval, NULL); - if (d != NULL) { - free(retval); - retval = d; - } - } - - return retval; -} - -/* - * write string to viminfo file - * - replace CTRL-V with CTRL-V CTRL-V - * - replace '\n' with CTRL-V 'n' - * - add a '\n' at the end - * - * For a long line: - * - write " CTRL-V \n " in first line - * - write " < \n " in second line - */ -void viminfo_writestring(FILE *fd, char_u *p) -{ - int c; - char_u *s; - int len = 0; - - for (s = p; *s != NUL; ++s) { - if (*s == Ctrl_V || *s == '\n') - ++len; - ++len; - } - - /* If the string will be too long, write its length and put it in the next - * line. Take into account that some room is needed for what comes before - * the string (e.g., variable name). Add something to the length for the - * '<', NL and trailing NUL. */ - if (len > LSIZE / 2) - fprintf(fd, IF_EB("\026%d\n<", CTRL_V_STR "%d\n<"), len + 3); - - while ((c = *p++) != NUL) { - if (c == Ctrl_V || c == '\n') { - putc(Ctrl_V, fd); - if (c == '\n') - c = 'n'; - } - putc(c, fd); - } - putc('\n', fd); -} - -/* - * Implementation of ":fixdel", also used by get_stty(). - * resulting - * ^? ^H - * not ^? ^? - */ -void do_fixdel(exarg_T *eap) -{ - char_u *p; - - p = find_termcode((char_u *)"kb"); - add_termcode((char_u *)"kD", p != NULL - && *p == DEL ? (char_u *)CTRL_H_STR : DEL_STR, FALSE); -} - -void print_line_no_prefix(linenr_T lnum, int use_number, int list) -{ - char_u numbuf[30]; - - if (curwin->w_p_nu || use_number) { - vim_snprintf((char *)numbuf, sizeof(numbuf), - "%*ld ", number_width(curwin), (long)lnum); - msg_puts_attr(numbuf, hl_attr(HLF_N)); /* Highlight line nrs */ - } - msg_prt_line(ml_get(lnum), list); -} - -/* - * Print a text line. Also in silent mode ("ex -s"). - */ -void print_line(linenr_T lnum, int use_number, int list) -{ - int save_silent = silent_mode; - - msg_start(); - silent_mode = FALSE; - info_message = TRUE; /* use mch_msg(), not mch_errmsg() */ - print_line_no_prefix(lnum, use_number, list); - if (save_silent) { - msg_putchar('\n'); - cursor_on(); /* msg_start() switches it off */ - out_flush(); - silent_mode = save_silent; - } - info_message = FALSE; -} - -int rename_buffer(char_u *new_fname) -{ - char_u *fname, *sfname, *xfname; - buf_T *buf; - - buf = curbuf; - apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf); - /* buffer changed, don't change name now */ - if (buf != curbuf) - return FAIL; - if (aborting()) /* autocmds may abort script processing */ - return FAIL; - /* - * The name of the current buffer will be changed. - * A new (unlisted) buffer entry needs to be made to hold the old file - * name, which will become the alternate file name. - * But don't set the alternate file name if the buffer didn't have a - * name. - */ - fname = curbuf->b_ffname; - sfname = curbuf->b_sfname; - xfname = curbuf->b_fname; - curbuf->b_ffname = NULL; - curbuf->b_sfname = NULL; - if (setfname(curbuf, new_fname, NULL, TRUE) == FAIL) { - curbuf->b_ffname = fname; - curbuf->b_sfname = sfname; - return FAIL; - } - curbuf->b_flags |= BF_NOTEDITED; - if (xfname != NULL && *xfname != NUL) { - buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0); - if (buf != NULL && !cmdmod.keepalt) - curwin->w_alt_fnum = buf->b_fnum; - } - free(fname); - free(sfname); - apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf); - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - return OK; -} - -/* - * ":file[!] [fname]". - */ -void ex_file(exarg_T *eap) -{ - /* ":0file" removes the file name. Check for illegal uses ":3file", - * "0file name", etc. */ - if (eap->addr_count > 0 - && (*eap->arg != NUL - || eap->line2 > 0 - || eap->addr_count > 1)) { - EMSG(_(e_invarg)); - return; - } - - if (*eap->arg != NUL || eap->addr_count == 1) { - if (rename_buffer(eap->arg) == FAIL) - return; - } - /* print full file name if :cd used */ - fileinfo(FALSE, FALSE, eap->forceit); -} - -/* - * ":update". - */ -void ex_update(exarg_T *eap) -{ - if (curbufIsChanged()) - (void)do_write(eap); -} - -/* - * ":write" and ":saveas". - */ -void ex_write(exarg_T *eap) -{ - if (eap->usefilter) /* input lines to shell command */ - do_bang(1, eap, FALSE, TRUE, FALSE); - else - (void)do_write(eap); -} - -/* - * write current buffer to file 'eap->arg' - * if 'eap->append' is TRUE, append to the file - * - * if *eap->arg == NUL write to current file - * - * return FAIL for failure, OK otherwise - */ -int do_write(exarg_T *eap) -{ - int other; - char_u *fname = NULL; /* init to shut up gcc */ - char_u *ffname; - int retval = FAIL; - char_u *free_fname = NULL; - buf_T *alt_buf = NULL; - - if (not_writing()) /* check 'write' option */ - return FAIL; - - ffname = eap->arg; - if (*ffname == NUL) { - if (eap->cmdidx == CMD_saveas) { - EMSG(_(e_argreq)); - goto theend; - } - other = FALSE; - } else { - fname = ffname; - free_fname = fix_fname(ffname); - /* - * When out-of-memory, keep unexpanded file name, because we MUST be - * able to write the file in this situation. - */ - if (free_fname != NULL) - ffname = free_fname; - other = otherfile(ffname); - } - - /* - * If we have a new file, put its name in the list of alternate file names. - */ - if (other) { - if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL - || eap->cmdidx == CMD_saveas) - alt_buf = setaltfname(ffname, fname, (linenr_T)1); - else - alt_buf = buflist_findname(ffname); - if (alt_buf != NULL && alt_buf->b_ml.ml_mfp != NULL) { - /* Overwriting a file that is loaded in another buffer is not a - * good idea. */ - EMSG(_(e_bufloaded)); - goto theend; - } - } - - /* - * Writing to the current file is not allowed in readonly mode - * and a file name is required. - * "nofile" and "nowrite" buffers cannot be written implicitly either. - */ - if (!other && ( - bt_dontwrite_msg(curbuf) || - check_fname() == FAIL || check_readonly(&eap->forceit, curbuf))) - goto theend; - - if (!other) { - ffname = curbuf->b_ffname; - fname = curbuf->b_fname; - /* - * Not writing the whole file is only allowed with '!'. - */ - if ( (eap->line1 != 1 - || eap->line2 != curbuf->b_ml.ml_line_count) - && !eap->forceit - && !eap->append - && !p_wa) { - if (p_confirm || cmdmod.confirm) { - if (vim_dialog_yesno(VIM_QUESTION, NULL, - (char_u *)_("Write partial file?"), 2) != VIM_YES) - goto theend; - eap->forceit = TRUE; - } else { - EMSG(_("E140: Use ! to write partial buffer")); - goto theend; - } - } - } - - if (check_overwrite(eap, curbuf, fname, ffname, other) == OK) { - if (eap->cmdidx == CMD_saveas && alt_buf != NULL) { - buf_T *was_curbuf = curbuf; - - apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, alt_buf); - if (curbuf != was_curbuf || aborting()) { - /* buffer changed, don't change name now */ - retval = FAIL; - goto theend; - } - /* Exchange the file names for the current and the alternate - * buffer. This makes it look like we are now editing the buffer - * under the new name. Must be done before buf_write(), because - * if there is no file name and 'cpo' contains 'F', it will set - * the file name. */ - fname = alt_buf->b_fname; - alt_buf->b_fname = curbuf->b_fname; - curbuf->b_fname = fname; - fname = alt_buf->b_ffname; - alt_buf->b_ffname = curbuf->b_ffname; - curbuf->b_ffname = fname; - fname = alt_buf->b_sfname; - alt_buf->b_sfname = curbuf->b_sfname; - curbuf->b_sfname = fname; - buf_name_changed(curbuf); - apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, alt_buf); - if (!alt_buf->b_p_bl) { - alt_buf->b_p_bl = TRUE; - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, alt_buf); - } - if (curbuf != was_curbuf || aborting()) { - /* buffer changed, don't write the file */ - retval = FAIL; - goto theend; - } - - /* If 'filetype' was empty try detecting it now. */ - if (*curbuf->b_p_ft == NUL) { - if (au_has_group((char_u *)"filetypedetect")) - (void)do_doautocmd((char_u *)"filetypedetect BufRead", - TRUE); - do_modelines(0); - } - - /* Autocommands may have changed buffer names, esp. when - * 'autochdir' is set. */ - fname = curbuf->b_sfname; - } - - retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2, - eap, eap->append, eap->forceit, TRUE, FALSE); - - /* After ":saveas fname" reset 'readonly'. */ - if (eap->cmdidx == CMD_saveas) { - if (retval == OK) { - curbuf->b_p_ro = FALSE; - redraw_tabline = TRUE; - } - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - } - } - -theend: - free(free_fname); - return retval; -} - -/* - * Check if it is allowed to overwrite a file. If b_flags has BF_NOTEDITED, - * BF_NEW or BF_READERR, check for overwriting current file. - * May set eap->forceit if a dialog says it's OK to overwrite. - * Return OK if it's OK, FAIL if it is not. - */ -int -check_overwrite ( - exarg_T *eap, - buf_T *buf, - char_u *fname, /* file name to be used (can differ from - buf->ffname) */ - char_u *ffname, /* full path version of fname */ - int other /* writing under other name */ -) -{ - /* - * write to other file or b_flags set or not writing the whole file: - * overwriting only allowed with '!' - */ - if ( (other - || (buf->b_flags & BF_NOTEDITED) - || ((buf->b_flags & BF_NEW) - && vim_strchr(p_cpo, CPO_OVERNEW) == NULL) - || (buf->b_flags & BF_READERR)) - && !p_wa - && !bt_nofile(buf) - && os_file_exists(ffname)) { - if (!eap->forceit && !eap->append) { -#ifdef UNIX - /* with UNIX it is possible to open a directory */ - if (os_isdir(ffname)) { - EMSG2(_(e_isadir2), ffname); - return FAIL; - } -#endif - if (p_confirm || cmdmod.confirm) { - char_u buff[DIALOG_MSG_SIZE]; - - dialog_msg(buff, _("Overwrite existing file \"%s\"?"), fname); - if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES) - return FAIL; - eap->forceit = TRUE; - } else { - EMSG(_(e_exists)); - return FAIL; - } - } - - /* For ":w! filename" check that no swap file exists for "filename". */ - if (other && !emsg_silent) { - char_u *dir; - char_u *p; - char_u *swapname; - - /* We only try the first entry in 'directory', without checking if - * it's writable. If the "." directory is not writable the write - * will probably fail anyway. - * Use 'shortname' of the current buffer, since there is no buffer - * for the written file. */ - if (*p_dir == NUL) { - dir = xmalloc(5); - STRCPY(dir, "."); - } else { - dir = xmalloc(MAXPATHL); - p = p_dir; - copy_option_part(&p, dir, MAXPATHL, ","); - } - swapname = makeswapname(fname, ffname, curbuf, dir); - free(dir); - if (os_file_exists(swapname)) { - if (p_confirm || cmdmod.confirm) { - char_u buff[DIALOG_MSG_SIZE]; - - dialog_msg(buff, - _("Swap file \"%s\" exists, overwrite anyway?"), - swapname); - if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) - != VIM_YES) { - free(swapname); - return FAIL; - } - eap->forceit = TRUE; - } else { - EMSG2(_("E768: Swap file exists: %s (:silent! overrides)"), - swapname); - free(swapname); - return FAIL; - } - } - free(swapname); - } - } - return OK; -} - -/* - * Handle ":wnext", ":wNext" and ":wprevious" commands. - */ -void ex_wnext(exarg_T *eap) -{ - int i; - - if (eap->cmd[1] == 'n') - i = curwin->w_arg_idx + (int)eap->line2; - else - i = curwin->w_arg_idx - (int)eap->line2; - eap->line1 = 1; - eap->line2 = curbuf->b_ml.ml_line_count; - if (do_write(eap) != FAIL) - do_argfile(eap, i); -} - -/* - * ":wall", ":wqall" and ":xall": Write all changed files (and exit). - */ -void do_wqall(exarg_T *eap) -{ - buf_T *buf; - int error = 0; - int save_forceit = eap->forceit; - - if (eap->cmdidx == CMD_xall || eap->cmdidx == CMD_wqall) - exiting = TRUE; - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (bufIsChanged(buf)) { - /* - * Check if there is a reason the buffer cannot be written: - * 1. if the 'write' option is set - * 2. if there is no file name (even after browsing) - * 3. if the 'readonly' is set (even after a dialog) - * 4. if overwriting is allowed (even after a dialog) - */ - if (not_writing()) { - ++error; - break; - } - if (buf->b_ffname == NULL) { - EMSGN(_("E141: No file name for buffer %" PRId64), buf->b_fnum); - ++error; - } else if (check_readonly(&eap->forceit, buf) - || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname, - FALSE) == FAIL) { - ++error; - } else { - if (buf_write_all(buf, eap->forceit) == FAIL) - ++error; - /* an autocommand may have deleted the buffer */ - if (!buf_valid(buf)) - buf = firstbuf; - } - eap->forceit = save_forceit; /* check_overwrite() may set it */ - } - } - if (exiting) { - if (!error) - getout(0); /* exit Vim */ - not_exiting(); - } -} - -/* - * Check the 'write' option. - * Return TRUE and give a message when it's not st. - */ -int not_writing(void) -{ - if (p_write) - return FALSE; - EMSG(_("E142: File not written: Writing is disabled by 'write' option")); - return TRUE; -} - -/* - * Check if a buffer is read-only (either 'readonly' option is set or file is - * read-only). Ask for overruling in a dialog. Return TRUE and give an error - * message when the buffer is readonly. - */ -static int check_readonly(int *forceit, buf_T *buf) -{ - /* Handle a file being readonly when the 'readonly' option is set or when - * the file exists and permissions are read-only. */ - if (!*forceit && (buf->b_p_ro - || (os_file_exists(buf->b_ffname) - && os_file_is_readonly((char *)buf->b_ffname)))) { - if ((p_confirm || cmdmod.confirm) && buf->b_fname != NULL) { - char_u buff[DIALOG_MSG_SIZE]; - - if (buf->b_p_ro) - dialog_msg(buff, - _( - "'readonly' option is set for \"%s\".\nDo you wish to write anyway?"), - buf->b_fname); - else - dialog_msg(buff, - _( - "File permissions of \"%s\" are read-only.\nIt may still be possible to write it.\nDo you wish to try?"), - buf->b_fname); - - if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) == VIM_YES) { - /* Set forceit, to force the writing of a readonly file */ - *forceit = TRUE; - return FALSE; - } else - return TRUE; - } else if (buf->b_p_ro) - EMSG(_(e_readonly)); - else - EMSG2(_("E505: \"%s\" is read-only (add ! to override)"), - buf->b_fname); - return TRUE; - } - - return FALSE; -} - -/* - * Try to abandon current file and edit a new or existing file. - * 'fnum' is the number of the file, if zero use ffname/sfname. - * - * Return 1 for "normal" error, 2 for "not written" error, 0 for success - * -1 for successfully opening another file. - * 'lnum' is the line number for the cursor in the new file (if non-zero). - */ -int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum, int forceit) -{ - int other; - int retval; - char_u *free_me = NULL; - - if (text_locked()) - return 1; - if (curbuf_locked()) - return 1; - - if (fnum == 0) { - /* make ffname full path, set sfname */ - fname_expand(curbuf, &ffname, &sfname); - other = otherfile(ffname); - free_me = ffname; /* has been allocated, free() later */ - } else - other = (fnum != curbuf->b_fnum); - - if (other) - ++no_wait_return; /* don't wait for autowrite message */ - if (other && !forceit && curbuf->b_nwindows == 1 && !P_HID(curbuf) - && curbufIsChanged() && autowrite(curbuf, forceit) == FAIL) { - if (p_confirm && p_write) - dialog_changed(curbuf, FALSE); - if (curbufIsChanged()) { - if (other) - --no_wait_return; - EMSG(_(e_nowrtmsg)); - retval = 2; /* file has been changed */ - goto theend; - } - } - if (other) - --no_wait_return; - if (setpm) - setpcmark(); - if (!other) { - if (lnum != 0) - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_SOL | BL_FIX); - retval = 0; /* it's in the same file */ - } else if (do_ecmd(fnum, ffname, sfname, NULL, lnum, - (P_HID(curbuf) ? ECMD_HIDE : 0) + (forceit ? ECMD_FORCEIT : 0), - curwin) == OK) - retval = -1; /* opened another file */ - else - retval = 1; /* error encountered */ - -theend: - free(free_me); - return retval; -} - -/* - * start editing a new file - * - * fnum: file number; if zero use ffname/sfname - * ffname: the file name - * - full path if sfname used, - * - any file name if sfname is NULL - * - empty string to re-edit with the same file name (but may be - * in a different directory) - * - NULL to start an empty buffer - * sfname: the short file name (or NULL) - * eap: contains the command to be executed after loading the file and - * forced 'ff' and 'fenc' - * newlnum: if > 0: put cursor on this line number (if possible) - * if ECMD_LASTL: use last position in loaded file - * if ECMD_LAST: use last position in all files - * if ECMD_ONE: use first line - * flags: - * ECMD_HIDE: if TRUE don't free the current buffer - * ECMD_SET_HELP: set b_help flag of (new) buffer before opening file - * ECMD_OLDBUF: use existing buffer if it exists - * ECMD_FORCEIT: ! used for Ex command - * ECMD_ADDBUF: don't edit, just add to buffer list - * oldwin: Should be "curwin" when editing a new buffer in the current - * window, NULL when splitting the window first. When not NULL info - * of the previous buffer for "oldwin" is stored. - * - * return FAIL for failure, OK otherwise - */ -int -do_ecmd ( - int fnum, - char_u *ffname, - char_u *sfname, - exarg_T *eap, /* can be NULL! */ - linenr_T newlnum, - int flags, - win_T *oldwin -) -{ - int other_file; /* TRUE if editing another file */ - int oldbuf; /* TRUE if using existing buffer */ - int auto_buf = FALSE; /* TRUE if autocommands brought us - into the buffer unexpectedly */ - char_u *new_name = NULL; - int did_set_swapcommand = FALSE; - buf_T *buf; - buf_T *old_curbuf = curbuf; - char_u *free_fname = NULL; - int retval = FAIL; - long n; - linenr_T lnum; - linenr_T topline = 0; - int newcol = -1; - int solcol = -1; - pos_T *pos; - char_u *command = NULL; - int did_get_winopts = FALSE; - int readfile_flags = 0; - - if (eap != NULL) - command = eap->do_ecmd_cmd; - - if (fnum != 0) { - if (fnum == curbuf->b_fnum) /* file is already being edited */ - return OK; /* nothing to do */ - other_file = TRUE; - } else { - /* if no short name given, use ffname for short name */ - if (sfname == NULL) - sfname = ffname; -#ifdef USE_FNAME_CASE - if (sfname != NULL) - fname_case(sfname, 0); /* set correct case for sfname */ -#endif - - if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL)) - goto theend; - - if (ffname == NULL) - other_file = TRUE; - /* there is no file name */ - else if (*ffname == NUL && curbuf->b_ffname == NULL) - other_file = FALSE; - else { - if (*ffname == NUL) { /* re-edit with same file name */ - ffname = curbuf->b_ffname; - sfname = curbuf->b_fname; - } - free_fname = fix_fname(ffname); /* may expand to full path name */ - if (free_fname != NULL) - ffname = free_fname; - other_file = otherfile(ffname); - } - } - - /* - * if the file was changed we may not be allowed to abandon it - * - if we are going to re-edit the same file - * - or if we are the only window on this file and if ECMD_HIDE is FALSE - */ - if ( ((!other_file && !(flags & ECMD_OLDBUF)) - || (curbuf->b_nwindows == 1 - && !(flags & (ECMD_HIDE | ECMD_ADDBUF)))) - && check_changed(curbuf, (p_awa ? CCGD_AW : 0) - | (other_file ? 0 : CCGD_MULTWIN) - | ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0) - | (eap == NULL ? 0 : CCGD_EXCMD))) { - if (fnum == 0 && other_file && ffname != NULL) - (void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum); - goto theend; - } - - /* - * End Visual mode before switching to another buffer, so the text can be - * copied into the GUI selection buffer. - */ - reset_VIsual(); - - if ((command != NULL || newlnum > (linenr_T)0) - && *get_vim_var_str(VV_SWAPCOMMAND) == NUL) { - char_u *p; - - /* Set v:swapcommand for the SwapExists autocommands. */ - size_t len = (command != NULL) ? STRLEN(command) + 3 : 30; - p = xmalloc(len); - if (command != NULL) { - vim_snprintf((char *)p, len, ":%s\r", command); - } else { - vim_snprintf((char *)p, len, "%" PRId64 "G", (int64_t)newlnum); - } - set_vim_var_string(VV_SWAPCOMMAND, p, -1); - did_set_swapcommand = TRUE; - free(p); - } - - /* - * If we are starting to edit another file, open a (new) buffer. - * Otherwise we re-use the current buffer. - */ - if (other_file) { - if (!(flags & ECMD_ADDBUF)) { - if (!cmdmod.keepalt) - curwin->w_alt_fnum = curbuf->b_fnum; - if (oldwin != NULL) - buflist_altfpos(oldwin); - } - - if (fnum) - buf = buflist_findnr(fnum); - else { - if (flags & ECMD_ADDBUF) { - linenr_T tlnum = 1L; - - if (command != NULL) { - tlnum = atol((char *)command); - if (tlnum <= 0) - tlnum = 1L; - } - (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED); - goto theend; - } - buf = buflist_new(ffname, sfname, 0L, - BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED)); - // Autocmds may change curwin and curbuf. - if (oldwin != NULL) { - oldwin = curwin; - } - old_curbuf = curbuf; - } - if (buf == NULL) - goto theend; - if (buf->b_ml.ml_mfp == NULL) { /* no memfile yet */ - oldbuf = FALSE; - buf->b_nwindows = 0; - } else { /* existing memfile */ - oldbuf = TRUE; - (void)buf_check_timestamp(buf, FALSE); - /* Check if autocommands made buffer invalid or changed the current - * buffer. */ - if (!buf_valid(buf) - || curbuf != old_curbuf - ) - goto theend; - if (aborting()) /* autocmds may abort script processing */ - goto theend; - } - - /* May jump to last used line number for a loaded buffer or when asked - * for explicitly */ - if ((oldbuf && newlnum == ECMD_LASTL) || newlnum == ECMD_LAST) { - pos = buflist_findfpos(buf); - newlnum = pos->lnum; - solcol = pos->col; - } - - /* - * Make the (new) buffer the one used by the current window. - * If the old buffer becomes unused, free it if ECMD_HIDE is FALSE. - * If the current buffer was empty and has no file name, curbuf - * is returned by buflist_new(). - */ - if (buf != curbuf) { - /* - * Be careful: The autocommands may delete any buffer and change - * the current buffer. - * - If the buffer we are going to edit is deleted, give up. - * - If the current buffer is deleted, prefer to load the new - * buffer when loading a buffer is required. This avoids - * loading another buffer which then must be closed again. - * - If we ended up in the new buffer already, need to skip a few - * things, set auto_buf. - */ - if (buf->b_fname != NULL) - new_name = vim_strsave(buf->b_fname); - au_new_curbuf = buf; - apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (!buf_valid(buf)) { /* new buffer has been deleted */ - delbuf_msg(new_name); /* frees new_name */ - goto theend; - } - if (aborting()) { /* autocmds may abort script processing */ - free(new_name); - goto theend; - } - if (buf == curbuf) /* already in new buffer */ - auto_buf = TRUE; - else { - if (curbuf == old_curbuf) - buf_copy_options(buf, BCO_ENTER); - - /* close the link to the current buffer */ - u_sync(FALSE); - close_buffer(oldwin, curbuf, - (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE); - - /* Autocommands may open a new window and leave oldwin open - * which leads to crashes since the above call sets - * oldwin->w_buffer to NULL. */ - if (curwin != oldwin && oldwin != aucmd_win - && win_valid(oldwin) && oldwin->w_buffer == NULL) - win_close(oldwin, FALSE); - - if (aborting()) { /* autocmds may abort script processing */ - free(new_name); - goto theend; - } - /* Be careful again, like above. */ - if (!buf_valid(buf)) { /* new buffer has been deleted */ - delbuf_msg(new_name); /* frees new_name */ - goto theend; - } - if (buf == curbuf) /* already in new buffer */ - auto_buf = TRUE; - else { - /* - * We could instead free the synblock - * and re-attach to buffer, perhaps. - */ - if (curwin->w_buffer != NULL && - curwin->w_s == &(curwin->w_buffer->b_s)) - curwin->w_s = &(buf->b_s); - - curwin->w_buffer = buf; - curbuf = buf; - ++curbuf->b_nwindows; - - /* Set 'fileformat', 'binary' and 'fenc' when forced. */ - if (!oldbuf && eap != NULL) { - set_file_options(TRUE, eap); - set_forced_fenc(eap); - } - } - - /* May get the window options from the last time this buffer - * was in this window (or another window). If not used - * before, reset the local window options to the global - * values. Also restores old folding stuff. */ - get_winopts(curbuf); - did_get_winopts = TRUE; - - } - free(new_name); - au_new_curbuf = NULL; - } else - ++curbuf->b_nwindows; - - curwin->w_pcmark.lnum = 1; - curwin->w_pcmark.col = 0; - } else { /* !other_file */ - if ( - (flags & ECMD_ADDBUF) || - check_fname() == FAIL) - goto theend; - oldbuf = (flags & ECMD_OLDBUF); - } - - if ((flags & ECMD_SET_HELP) || keep_help_flag) { - char_u *p; - - curbuf->b_help = TRUE; - set_string_option_direct((char_u *)"buftype", -1, - (char_u *)"help", OPT_FREE|OPT_LOCAL, 0); - - /* - * Always set these options after jumping to a help tag, because the - * user may have an autocommand that gets in the way. - * Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and - * latin1 word characters (for translated help files). - * Only set it when needed, buf_init_chartab() is some work. - */ - p = - (char_u *)"!-~,^*,^|,^\",192-255"; - if (STRCMP(curbuf->b_p_isk, p) != 0) { - set_string_option_direct((char_u *)"isk", -1, p, - OPT_FREE|OPT_LOCAL, 0); - check_buf_options(curbuf); - (void)buf_init_chartab(curbuf, FALSE); - } - - curbuf->b_p_ts = 8; /* 'tabstop' is 8 */ - curwin->w_p_list = FALSE; /* no list mode */ - - curbuf->b_p_ma = FALSE; /* not modifiable */ - curbuf->b_p_bin = FALSE; /* reset 'bin' before reading file */ - curwin->w_p_nu = 0; /* no line numbers */ - curwin->w_p_rnu = 0; /* no relative line numbers */ - RESET_BINDING(curwin); /* no scroll or cursor binding */ - curwin->w_p_arab = FALSE; /* no arabic mode */ - curwin->w_p_rl = FALSE; /* help window is left-to-right */ - curwin->w_p_fen = FALSE; /* No folding in the help window */ - curwin->w_p_diff = FALSE; /* No 'diff' */ - curwin->w_p_spell = FALSE; /* No spell checking */ - - buf = curbuf; - set_buflisted(FALSE); - } else { - buf = curbuf; - /* Don't make a buffer listed if it's a help buffer. Useful when - * using CTRL-O to go back to a help file. */ - if (!curbuf->b_help) - set_buflisted(TRUE); - } - - /* If autocommands change buffers under our fingers, forget about - * editing the file. */ - if (buf != curbuf) - goto theend; - if (aborting()) /* autocmds may abort script processing */ - goto theend; - - /* Since we are starting to edit a file, consider the filetype to be - * unset. Helps for when an autocommand changes files and expects syntax - * highlighting to work in the other file. */ - did_filetype = FALSE; - - /* - * other_file oldbuf - * FALSE FALSE re-edit same file, buffer is re-used - * FALSE TRUE re-edit same file, nothing changes - * TRUE FALSE start editing new file, new buffer - * TRUE TRUE start editing in existing buffer (nothing to do) - */ - if (!other_file && !oldbuf) { /* re-use the buffer */ - set_last_cursor(curwin); /* may set b_last_cursor */ - if (newlnum == ECMD_LAST || newlnum == ECMD_LASTL) { - newlnum = curwin->w_cursor.lnum; - solcol = curwin->w_cursor.col; - } - buf = curbuf; - if (buf->b_fname != NULL) - new_name = vim_strsave(buf->b_fname); - else - new_name = NULL; - if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) { - /* Save all the text, so that the reload can be undone. - * Sync first so that this is a separate undo-able action. */ - u_sync(FALSE); - if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, TRUE) - == FAIL) - goto theend; - u_unchanged(curbuf); - buf_freeall(curbuf, BFA_KEEP_UNDO); - - /* tell readfile() not to clear or reload undo info */ - readfile_flags = READ_KEEP_UNDO; - } else - buf_freeall(curbuf, 0); /* free all things for buffer */ - /* If autocommands deleted the buffer we were going to re-edit, give - * up and jump to the end. */ - if (!buf_valid(buf)) { - delbuf_msg(new_name); /* frees new_name */ - goto theend; - } - free(new_name); - - /* If autocommands change buffers under our fingers, forget about - * re-editing the file. Should do the buf_clear_file(), but perhaps - * the autocommands changed the buffer... */ - if (buf != curbuf) - goto theend; - if (aborting()) /* autocmds may abort script processing */ - goto theend; - buf_clear_file(curbuf); - curbuf->b_op_start.lnum = 0; /* clear '[ and '] marks */ - curbuf->b_op_end.lnum = 0; - } - - /* - * If we get here we are sure to start editing - */ - /* don't redraw until the cursor is in the right line */ - ++RedrawingDisabled; - - /* Assume success now */ - retval = OK; - - /* - * Reset cursor position, could be used by autocommands. - */ - check_cursor(); - - /* - * Check if we are editing the w_arg_idx file in the argument list. - */ - check_arg_idx(curwin); - - if (!auto_buf) { - /* - * Set cursor and init window before reading the file and executing - * autocommands. This allows for the autocommands to position the - * cursor. - */ - curwin_init(); - - /* It's possible that all lines in the buffer changed. Need to update - * automatic folding for all windows where it's used. */ - { - win_T *win; - tabpage_T *tp; - - FOR_ALL_TAB_WINDOWS(tp, win) - if (win->w_buffer == curbuf) - foldUpdateAll(win); - } - - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - - /* - * Careful: open_buffer() and apply_autocmds() may change the current - * buffer and window. - */ - lnum = curwin->w_cursor.lnum; - topline = curwin->w_topline; - if (!oldbuf) { /* need to read the file */ -#if defined(HAS_SWAP_EXISTS_ACTION) - swap_exists_action = SEA_DIALOG; -#endif - curbuf->b_flags |= BF_CHECK_RO; /* set/reset 'ro' flag */ - - /* - * Open the buffer and read the file. - */ - if (should_abort(open_buffer(FALSE, eap, readfile_flags))) - retval = FAIL; - -#if defined(HAS_SWAP_EXISTS_ACTION) - if (swap_exists_action == SEA_QUIT) - retval = FAIL; - handle_swap_exists(old_curbuf); -#endif - } else { - /* Read the modelines, but only to set window-local options. Any - * buffer-local options have already been set and may have been - * changed by the user. */ - do_modelines(OPT_WINONLY); - - apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, - &retval); - apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf, - &retval); - } - check_arg_idx(curwin); - - /* - * If autocommands change the cursor position or topline, we should - * keep it. - */ - if (curwin->w_cursor.lnum != lnum) { - newlnum = curwin->w_cursor.lnum; - newcol = curwin->w_cursor.col; - } - if (curwin->w_topline == topline) - topline = 0; - - /* Even when cursor didn't move we need to recompute topline. */ - changed_line_abv_curs(); - - maketitle(); - } - - /* Tell the diff stuff that this buffer is new and/or needs updating. - * Also needed when re-editing the same buffer, because unloading will - * have removed it as a diff buffer. */ - if (curwin->w_p_diff) { - diff_buf_add(curbuf); - diff_invalidate(curbuf); - } - - /* If the window options were changed may need to set the spell language. - * Can only do this after the buffer has been properly setup. */ - if (did_get_winopts && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) - (void)did_set_spelllang(curwin); - - if (command == NULL) { - if (newcol >= 0) { /* position set by autocommands */ - curwin->w_cursor.lnum = newlnum; - curwin->w_cursor.col = newcol; - check_cursor(); - } else if (newlnum > 0) { /* line number from caller or old position */ - curwin->w_cursor.lnum = newlnum; - check_cursor_lnum(); - if (solcol >= 0 && !p_sol) { - /* 'sol' is off: Use last known column. */ - curwin->w_cursor.col = solcol; - check_cursor_col(); - curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - } else - beginline(BL_SOL | BL_FIX); - } else { /* no line number, go to last line in Ex mode */ - if (exmode_active) - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - beginline(BL_WHITE | BL_FIX); - } - } - - /* Check if cursors in other windows on the same buffer are still valid */ - check_lnums(FALSE); - - /* - * Did not read the file, need to show some info about the file. - * Do this after setting the cursor. - */ - if (oldbuf - && !auto_buf - ) { - int msg_scroll_save = msg_scroll; - - /* Obey the 'O' flag in 'cpoptions': overwrite any previous file - * message. */ - if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) - msg_scroll = FALSE; - if (!msg_scroll) /* wait a bit when overwriting an error msg */ - check_for_delay(FALSE); - msg_start(); - msg_scroll = msg_scroll_save; - msg_scrolled_ign = TRUE; - - fileinfo(FALSE, TRUE, FALSE); - - msg_scrolled_ign = FALSE; - } - - if (command != NULL) - do_cmdline(command, NULL, NULL, DOCMD_VERBOSE); - - if (curbuf->b_kmap_state & KEYMAP_INIT) - (void)keymap_init(); - - --RedrawingDisabled; - if (!skip_redraw) { - n = p_so; - if (topline == 0 && command == NULL) - p_so = 999; /* force cursor halfway the window */ - update_topline(); - curwin->w_scbind_pos = curwin->w_topline; - p_so = n; - redraw_curbuf_later(NOT_VALID); /* redraw this buffer later */ - } - - if (p_im) - need_start_insertmode = TRUE; - - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - - -theend: - if (did_set_swapcommand) - set_vim_var_string(VV_SWAPCOMMAND, NULL, -1); - free(free_fname); - return retval; -} - -static void delbuf_msg(char_u *name) -{ - EMSG2(_("E143: Autocommands unexpectedly deleted new buffer %s"), - name == NULL ? (char_u *)"" : name); - free(name); - au_new_curbuf = NULL; -} - -static int append_indent = 0; /* autoindent for first line */ - -/* - * ":insert" and ":append", also used by ":change" - */ -void ex_append(exarg_T *eap) -{ - char_u *theline; - int did_undo = FALSE; - linenr_T lnum = eap->line2; - int indent = 0; - char_u *p; - int vcol; - int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); - - /* the ! flag toggles autoindent */ - if (eap->forceit) - curbuf->b_p_ai = !curbuf->b_p_ai; - - /* First autoindent comes from the line we start on */ - if (eap->cmdidx != CMD_change && curbuf->b_p_ai && lnum > 0) - append_indent = get_indent_lnum(lnum); - - if (eap->cmdidx != CMD_append) - --lnum; - - /* when the buffer is empty append to line 0 and delete the dummy line */ - if (empty && lnum == 1) - lnum = 0; - - State = INSERT; /* behave like in Insert mode */ - if (curbuf->b_p_iminsert == B_IMODE_LMAP) - State |= LANGMAP; - - for (;; ) { - msg_scroll = TRUE; - need_wait_return = FALSE; - if (curbuf->b_p_ai) { - if (append_indent >= 0) { - indent = append_indent; - append_indent = -1; - } else if (lnum > 0) - indent = get_indent_lnum(lnum); - } - ex_keep_indent = FALSE; - if (eap->getline == NULL) { - /* No getline() function, use the lines that follow. This ends - * when there is no more. */ - if (eap->nextcmd == NULL || *eap->nextcmd == NUL) - break; - p = vim_strchr(eap->nextcmd, NL); - if (p == NULL) - p = eap->nextcmd + STRLEN(eap->nextcmd); - theline = vim_strnsave(eap->nextcmd, (int)(p - eap->nextcmd)); - if (*p != NUL) - ++p; - eap->nextcmd = p; - } else - theline = eap->getline( - eap->cstack->cs_looplevel > 0 ? -1 : - NUL, eap->cookie, indent); - lines_left = Rows - 1; - if (theline == NULL) - break; - - /* Using ^ CTRL-D in getexmodeline() makes us repeat the indent. */ - if (ex_keep_indent) - append_indent = indent; - - /* Look for the "." after automatic indent. */ - vcol = 0; - for (p = theline; indent > vcol; ++p) { - if (*p == ' ') - ++vcol; - else if (*p == TAB) - vcol += 8 - vcol % 8; - else - break; - } - if ((p[0] == '.' && p[1] == NUL) - || (!did_undo && u_save(lnum, lnum + 1 + (empty ? 1 : 0)) - == FAIL)) { - free(theline); - break; - } - - /* don't use autoindent if nothing was typed. */ - if (p[0] == NUL) - theline[0] = NUL; - - did_undo = TRUE; - ml_append(lnum, theline, (colnr_T)0, FALSE); - appended_lines_mark(lnum, 1L); - - free(theline); - ++lnum; - - if (empty) { - ml_delete(2L, FALSE); - empty = FALSE; - } - } - State = NORMAL; - - if (eap->forceit) - curbuf->b_p_ai = !curbuf->b_p_ai; - - /* "start" is set to eap->line2+1 unless that position is invalid (when - * eap->line2 pointed to the end of the buffer and nothing was appended) - * "end" is set to lnum when something has been appended, otherwise - * it is the same than "start" -- Acevedo */ - curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ? - eap->line2 + 1 : curbuf->b_ml.ml_line_count; - if (eap->cmdidx != CMD_append) - --curbuf->b_op_start.lnum; - curbuf->b_op_end.lnum = (eap->line2 < lnum) - ? lnum : curbuf->b_op_start.lnum; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_SOL | BL_FIX); - - need_wait_return = FALSE; /* don't use wait_return() now */ - ex_no_reprint = TRUE; -} - -/* - * ":change" - */ -void ex_change(exarg_T *eap) -{ - linenr_T lnum; - - if (eap->line2 >= eap->line1 - && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) - return; - - /* the ! flag toggles autoindent */ - if (eap->forceit ? !curbuf->b_p_ai : curbuf->b_p_ai) - append_indent = get_indent_lnum(eap->line1); - - for (lnum = eap->line2; lnum >= eap->line1; --lnum) { - if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */ - break; - ml_delete(eap->line1, FALSE); - } - - /* make sure the cursor is not beyond the end of the file now */ - check_cursor_lnum(); - deleted_lines_mark(eap->line1, (long)(eap->line2 - lnum)); - - /* ":append" on the line above the deleted lines. */ - eap->line2 = eap->line1; - ex_append(eap); -} - -void ex_z(exarg_T *eap) -{ - char_u *x; - int bigness; - char_u *kind; - int minus = 0; - linenr_T start, end, curs, i; - int j; - linenr_T lnum = eap->line2; - - /* Vi compatible: ":z!" uses display height, without a count uses - * 'scroll' */ - if (eap->forceit) - bigness = curwin->w_height; - else if (firstwin == lastwin) - bigness = curwin->w_p_scr * 2; - else - bigness = curwin->w_height - 3; - if (bigness < 1) - bigness = 1; - - x = eap->arg; - kind = x; - if (*kind == '-' || *kind == '+' || *kind == '=' - || *kind == '^' || *kind == '.') - ++x; - while (*x == '-' || *x == '+') - ++x; - - if (*x != 0) { - if (!VIM_ISDIGIT(*x)) { - EMSG(_("E144: non-numeric argument to :z")); - return; - } else { - bigness = atoi((char *)x); - p_window = bigness; - if (*kind == '=') - bigness += 2; - } - } - - /* the number of '-' and '+' multiplies the distance */ - if (*kind == '-' || *kind == '+') - for (x = kind + 1; *x == *kind; ++x) - ; - - switch (*kind) { - case '-': - start = lnum - bigness * (linenr_T)(x - kind) + 1; - end = start + bigness - 1; - curs = end; - break; - - case '=': - start = lnum - (bigness + 1) / 2 + 1; - end = lnum + (bigness + 1) / 2 - 1; - curs = lnum; - minus = 1; - break; - - case '^': - start = lnum - bigness * 2; - end = lnum - bigness; - curs = lnum - bigness; - break; - - case '.': - start = lnum - (bigness + 1) / 2 + 1; - end = lnum + (bigness + 1) / 2 - 1; - curs = end; - break; - - default: /* '+' */ - start = lnum; - if (*kind == '+') - start += bigness * (linenr_T)(x - kind - 1) + 1; - else if (eap->addr_count == 0) - ++start; - end = start + bigness - 1; - curs = end; - break; - } - - if (start < 1) - start = 1; - - if (end > curbuf->b_ml.ml_line_count) - end = curbuf->b_ml.ml_line_count; - - if (curs > curbuf->b_ml.ml_line_count) - curs = curbuf->b_ml.ml_line_count; - - for (i = start; i <= end; i++) { - if (minus && i == lnum) { - msg_putchar('\n'); - - for (j = 1; j < Columns; j++) - msg_putchar('-'); - } - - print_line(i, eap->flags & EXFLAG_NR, eap->flags & EXFLAG_LIST); - - if (minus && i == lnum) { - msg_putchar('\n'); - - for (j = 1; j < Columns; j++) - msg_putchar('-'); - } - } - - curwin->w_cursor.lnum = curs; - ex_no_reprint = TRUE; -} - -/* - * Check if the restricted flag is set. - * If so, give an error message and return TRUE. - * Otherwise, return FALSE. - */ -int check_restricted(void) -{ - if (restricted) { - EMSG(_("E145: Shell commands not allowed in rvim")); - return TRUE; - } - return FALSE; -} - -/* - * Check if the secure flag is set (.exrc or .vimrc in current directory). - * If so, give an error message and return TRUE. - * Otherwise, return FALSE. - */ -int check_secure(void) -{ - if (secure) { - secure = 2; - EMSG(_(e_curdir)); - return TRUE; - } -#ifdef HAVE_SANDBOX - /* - * In the sandbox more things are not allowed, including the things - * disallowed in secure mode. - */ - if (sandbox != 0) { - EMSG(_(e_sandbox)); - return TRUE; - } -#endif - return FALSE; -} - -static char_u *old_sub = NULL; /* previous substitute pattern */ -static int global_need_beginline; /* call beginline() after ":g" */ - -/* do_sub() - * - * Perform a substitution from line eap->line1 to line eap->line2 using the - * command pointed to by eap->arg which should be of the form: - * - * /pattern/substitution/{flags} - * - * The usual escapes are supported as described in the regexp docs. - */ -void do_sub(exarg_T *eap) -{ - linenr_T lnum; - long i = 0; - regmmatch_T regmatch; - static int do_all = FALSE; /* do multiple substitutions per line */ - static int do_ask = FALSE; /* ask for confirmation */ - static int do_count = FALSE; /* count only */ - static int do_error = TRUE; /* if false, ignore errors */ - static int do_print = FALSE; /* print last line with subs. */ - static int do_list = FALSE; /* list last line with subs. */ - static int do_number = FALSE; /* list last line with line nr*/ - static int do_ic = 0; /* ignore case flag */ - char_u *pat = NULL, *sub = NULL; /* init for GCC */ - int delimiter; - int sublen; - int got_quit = FALSE; - int got_match = FALSE; - int temp; - int which_pat; - char_u *cmd; - int save_State; - linenr_T first_line = 0; /* first changed line */ - linenr_T last_line= 0; /* below last changed line AFTER the - * change */ - linenr_T old_line_count = curbuf->b_ml.ml_line_count; - linenr_T line2; - long nmatch; /* number of lines in match */ - char_u *sub_firstline; /* allocated copy of first sub line */ - int endcolumn = FALSE; /* cursor in last column when done */ - pos_T old_cursor = curwin->w_cursor; - int start_nsubs; - int save_ma = 0; - - cmd = eap->arg; - if (!global_busy) { - sub_nsubs = 0; - sub_nlines = 0; - } - start_nsubs = sub_nsubs; - - if (eap->cmdidx == CMD_tilde) - which_pat = RE_LAST; /* use last used regexp */ - else - which_pat = RE_SUBST; /* use last substitute regexp */ - - /* new pattern and substitution */ - if (eap->cmd[0] == 's' && *cmd != NUL && !vim_iswhite(*cmd) - && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) { - /* don't accept alphanumeric for separator */ - if (isalpha(*cmd)) { - EMSG(_("E146: Regular expressions can't be delimited by letters")); - return; - } - /* - * undocumented vi feature: - * "\/sub/" and "\?sub?" use last used search pattern (almost like - * //sub/r). "\&sub&" use last substitute pattern (like //sub/). - */ - if (*cmd == '\\') { - ++cmd; - if (vim_strchr((char_u *)"/?&", *cmd) == NULL) { - EMSG(_(e_backslash)); - return; - } - if (*cmd != '&') - which_pat = RE_SEARCH; /* use last '/' pattern */ - pat = (char_u *)""; /* empty search pattern */ - delimiter = *cmd++; /* remember delimiter character */ - } else { /* find the end of the regexp */ - if (p_altkeymap && curwin->w_p_rl) - lrF_sub(cmd); - which_pat = RE_LAST; /* use last used regexp */ - delimiter = *cmd++; /* remember delimiter character */ - pat = cmd; /* remember start of search pat */ - cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg); - if (cmd[0] == delimiter) /* end delimiter found */ - *cmd++ = NUL; /* replace it with a NUL */ - } - - /* - * Small incompatibility: vi sees '\n' as end of the command, but in - * Vim we want to use '\n' to find/substitute a NUL. - */ - sub = cmd; /* remember the start of the substitution */ - - while (cmd[0]) { - if (cmd[0] == delimiter) { /* end delimiter found */ - *cmd++ = NUL; /* replace it with a NUL */ - break; - } - if (cmd[0] == '\\' && cmd[1] != 0) /* skip escaped characters */ - ++cmd; - mb_ptr_adv(cmd); - } - - if (!eap->skip) { - /* In POSIX vi ":s/pat/%/" uses the previous subst. string. */ - if (STRCMP(sub, "%") == 0 - && vim_strchr(p_cpo, CPO_SUBPERCENT) != NULL) { - if (old_sub == NULL) { /* there is no previous command */ - EMSG(_(e_nopresub)); - return; - } - sub = old_sub; - } else { - free(old_sub); - old_sub = vim_strsave(sub); - } - } - } else if (!eap->skip) { /* use previous pattern and substitution */ - if (old_sub == NULL) { /* there is no previous command */ - EMSG(_(e_nopresub)); - return; - } - pat = NULL; /* search_regcomp() will use previous pattern */ - sub = old_sub; - - /* Vi compatibility quirk: repeating with ":s" keeps the cursor in the - * last column after using "$". */ - endcolumn = (curwin->w_curswant == MAXCOL); - } - - // Recognize ":%s/\n//" and turn it into a join command, which is much - // more efficient. - // TODO: find a generic solution to make line-joining operations more - // efficient, avoid allocating a string that grows in size. - if (pat != NULL - && strcmp((const char *)pat, "\\n") == 0 - && *sub == NUL - && (*cmd == NUL || (cmd[1] == NUL - && (*cmd == 'g' - || *cmd == 'l' - || *cmd == 'p' - || *cmd == '#')))) { - curwin->w_cursor.lnum = eap->line1; - if (*cmd == 'l') { - eap->flags = EXFLAG_LIST; - } else if (*cmd == '#') { - eap->flags = EXFLAG_NR; - } else if (*cmd == 'p') { - eap->flags = EXFLAG_PRINT; - } - - do_join(eap->line2 - eap->line1 + 1, FALSE, TRUE, FALSE); - sub_nlines = sub_nsubs = eap->line2 - eap->line1 + 1; - do_sub_msg(FALSE); - ex_may_print(eap); - - return; - } - - /* - * Find trailing options. When '&' is used, keep old options. - */ - if (*cmd == '&') - ++cmd; - else { - if (!p_ed) { - if (p_gd) /* default is global on */ - do_all = TRUE; - else - do_all = FALSE; - do_ask = FALSE; - } - do_error = TRUE; - do_print = FALSE; - do_count = FALSE; - do_number = FALSE; - do_ic = 0; - } - while (*cmd) { - /* - * Note that 'g' and 'c' are always inverted, also when p_ed is off. - * 'r' is never inverted. - */ - if (*cmd == 'g') - do_all = !do_all; - else if (*cmd == 'c') - do_ask = !do_ask; - else if (*cmd == 'n') - do_count = TRUE; - else if (*cmd == 'e') - do_error = !do_error; - else if (*cmd == 'r') /* use last used regexp */ - which_pat = RE_LAST; - else if (*cmd == 'p') - do_print = TRUE; - else if (*cmd == '#') { - do_print = TRUE; - do_number = TRUE; - } else if (*cmd == 'l') { - do_print = TRUE; - do_list = TRUE; - } else if (*cmd == 'i') /* ignore case */ - do_ic = 'i'; - else if (*cmd == 'I') /* don't ignore case */ - do_ic = 'I'; - else - break; - ++cmd; - } - if (do_count) - do_ask = FALSE; - - /* - * check for a trailing count - */ - cmd = skipwhite(cmd); - if (VIM_ISDIGIT(*cmd)) { - i = getdigits(&cmd); - if (i <= 0 && !eap->skip && do_error) { - EMSG(_(e_zerocount)); - return; - } - eap->line1 = eap->line2; - eap->line2 += i - 1; - if (eap->line2 > curbuf->b_ml.ml_line_count) - eap->line2 = curbuf->b_ml.ml_line_count; - } - - /* - * check for trailing command or garbage - */ - cmd = skipwhite(cmd); - if (*cmd && *cmd != '"') { /* if not end-of-line or comment */ - eap->nextcmd = check_nextcmd(cmd); - if (eap->nextcmd == NULL) { - EMSG(_(e_trailing)); - return; - } - } - - if (eap->skip) /* not executing commands, only parsing */ - return; - - if (!do_count && !curbuf->b_p_ma) { - /* Substitution is not allowed in non-'modifiable' buffer */ - EMSG(_(e_modifiable)); - return; - } - - if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, - ®match) == FAIL) { - if (do_error) - EMSG(_(e_invcmd)); - return; - } - - /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */ - if (do_ic == 'i') - regmatch.rmm_ic = TRUE; - else if (do_ic == 'I') - regmatch.rmm_ic = FALSE; - - sub_firstline = NULL; - - /* - * ~ in the substitute pattern is replaced with the old pattern. - * We do it here once to avoid it to be replaced over and over again. - * But don't do it when it starts with "\=", then it's an expression. - */ - if (!(sub[0] == '\\' && sub[1] == '=')) - sub = regtilde(sub, p_magic); - - /* - * Check for a match on each line. - */ - line2 = eap->line2; - for (lnum = eap->line1; lnum <= line2 && !(got_quit - || aborting() - ); ++lnum) { - nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); - if (nmatch) { - colnr_T copycol; - colnr_T matchcol; - colnr_T prev_matchcol = MAXCOL; - char_u *new_end, *new_start = NULL; - unsigned new_start_len = 0; - char_u *p1; - int did_sub = FALSE; - int lastone; - int len, copy_len, needed_len; - long nmatch_tl = 0; /* nr of lines matched below lnum */ - int do_again; /* do it again after joining lines */ - int skip_match = FALSE; - linenr_T sub_firstlnum; /* nr of first sub line */ - - /* - * The new text is build up step by step, to avoid too much - * copying. There are these pieces: - * sub_firstline The old text, unmodified. - * copycol Column in the old text where we started - * looking for a match; from here old text still - * needs to be copied to the new text. - * matchcol Column number of the old text where to look - * for the next match. It's just after the - * previous match or one further. - * prev_matchcol Column just after the previous match (if any). - * Mostly equal to matchcol, except for the first - * match and after skipping an empty match. - * regmatch.*pos Where the pattern matched in the old text. - * new_start The new text, all that has been produced so - * far. - * new_end The new text, where to append new text. - * - * lnum The line number where we found the start of - * the match. Can be below the line we searched - * when there is a \n before a \zs in the - * pattern. - * sub_firstlnum The line number in the buffer where to look - * for a match. Can be different from "lnum" - * when the pattern or substitute string contains - * line breaks. - * - * Special situations: - * - When the substitute string contains a line break, the part up - * to the line break is inserted in the text, but the copy of - * the original line is kept. "sub_firstlnum" is adjusted for - * the inserted lines. - * - When the matched pattern contains a line break, the old line - * is taken from the line at the end of the pattern. The lines - * in the match are deleted later, "sub_firstlnum" is adjusted - * accordingly. - * - * The new text is built up in new_start[]. It has some extra - * room to avoid using xmalloc()/free() too often. new_start_len is - * the length of the allocated memory at new_start. - * - * Make a copy of the old line, so it won't be taken away when - * updating the screen or handling a multi-line match. The "old_" - * pointers point into this copy. - */ - sub_firstlnum = lnum; - copycol = 0; - matchcol = 0; - - /* At first match, remember current cursor position. */ - if (!got_match) { - setpcmark(); - got_match = TRUE; - } - - /* - * Loop until nothing more to replace in this line. - * 1. Handle match with empty string. - * 2. If do_ask is set, ask for confirmation. - * 3. substitute the string. - * 4. if do_all is set, find next match - * 5. break if there isn't another match in this line - */ - for (;; ) { - /* Advance "lnum" to the line where the match starts. The - * match does not start in the first line when there is a line - * break before \zs. */ - if (regmatch.startpos[0].lnum > 0) { - lnum += regmatch.startpos[0].lnum; - sub_firstlnum += regmatch.startpos[0].lnum; - nmatch -= regmatch.startpos[0].lnum; - free(sub_firstline); - sub_firstline = NULL; - } - - if (sub_firstline == NULL) { - sub_firstline = vim_strsave(ml_get(sub_firstlnum)); - } - - /* Save the line number of the last change for the final - * cursor position (just like Vi). */ - curwin->w_cursor.lnum = lnum; - do_again = FALSE; - - /* - * 1. Match empty string does not count, except for first - * match. This reproduces the strange vi behaviour. - * This also catches endless loops. - */ - if (matchcol == prev_matchcol - && regmatch.endpos[0].lnum == 0 - && matchcol == regmatch.endpos[0].col) { - if (sub_firstline[matchcol] == NUL) - /* We already were at the end of the line. Don't look - * for a match in this line again. */ - skip_match = TRUE; - else { - /* search for a match at next column */ - if (has_mbyte) - matchcol += mb_ptr2len(sub_firstline + matchcol); - else - ++matchcol; - } - goto skip; - } - - /* Normally we continue searching for a match just after the - * previous match. */ - matchcol = regmatch.endpos[0].col; - prev_matchcol = matchcol; - - /* - * 2. If do_count is set only increase the counter. - * If do_ask is set, ask for confirmation. - */ - if (do_count) { - /* For a multi-line match, put matchcol at the NUL at - * the end of the line and set nmatch to one, so that - * we continue looking for a match on the next line. - * Avoids that ":s/\nB\@=//gc" get stuck. */ - if (nmatch > 1) { - matchcol = (colnr_T)STRLEN(sub_firstline); - nmatch = 1; - skip_match = TRUE; - } - sub_nsubs++; - did_sub = TRUE; - /* Skip the substitution, unless an expression is used, - * then it is evaluated in the sandbox. */ - if (!(sub[0] == '\\' && sub[1] == '=')) - goto skip; - } - - if (do_ask) { - int typed = 0; - - /* change State to CONFIRM, so that the mouse works - * properly */ - save_State = State; - State = CONFIRM; - setmouse(); /* disable mouse in xterm */ - curwin->w_cursor.col = regmatch.startpos[0].col; - - /* When 'cpoptions' contains "u" don't sync undo when - * asking for confirmation. */ - if (vim_strchr(p_cpo, CPO_UNDO) != NULL) - ++no_u_sync; - - /* - * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed. - */ - while (do_ask) { - if (exmode_active) { - char_u *resp; - colnr_T sc, ec; - - print_line_no_prefix(lnum, do_number, do_list); - - getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL); - curwin->w_cursor.col = regmatch.endpos[0].col - 1; - getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec); - if (do_number || curwin->w_p_nu) { - int numw = number_width(curwin) + 1; - sc += numw; - ec += numw; - } - msg_start(); - for (i = 0; i < (long)sc; ++i) - msg_putchar(' '); - for (; i <= (long)ec; ++i) - msg_putchar('^'); - - resp = getexmodeline('?', NULL, 0); - if (resp != NULL) { - typed = *resp; - free(resp); - } - } else { - char_u *orig_line = NULL; - int len_change = 0; - int save_p_fen = curwin->w_p_fen; - - curwin->w_p_fen = FALSE; - /* Invert the matched string. - * Remove the inversion afterwards. */ - temp = RedrawingDisabled; - RedrawingDisabled = 0; - - if (new_start != NULL) { - /* There already was a substitution, we would - * like to show this to the user. We cannot - * really update the line, it would change - * what matches. Temporarily replace the line - * and change it back afterwards. */ - orig_line = vim_strsave(ml_get(lnum)); - if (orig_line != NULL) { - char_u *new_line = concat_str(new_start, - sub_firstline + copycol); - - // Position the cursor relative to the end of the line, the - // previous substitute may have inserted or deleted characters - // before the cursor. - len_change = (int)STRLEN(new_line) - (int)STRLEN(orig_line); - curwin->w_cursor.col += len_change; - ml_replace(lnum, new_line, FALSE); - } - } - - search_match_lines = regmatch.endpos[0].lnum - - regmatch.startpos[0].lnum; - search_match_endcol = regmatch.endpos[0].col - + len_change; - highlight_match = TRUE; - - update_topline(); - validate_cursor(); - update_screen(SOME_VALID); - highlight_match = FALSE; - redraw_later(SOME_VALID); - - curwin->w_p_fen = save_p_fen; - if (msg_row == Rows - 1) - msg_didout = FALSE; /* avoid a scroll-up */ - msg_starthere(); - i = msg_scroll; - msg_scroll = 0; /* truncate msg when - needed */ - msg_no_more = TRUE; - /* write message same highlighting as for - * wait_return */ - smsg_attr(hl_attr(HLF_R), - (char_u *)_("replace with %s (y/n/a/q/l/^E/^Y)?"), sub); - msg_no_more = FALSE; - msg_scroll = i; - showruler(TRUE); - windgoto(msg_row, msg_col); - RedrawingDisabled = temp; - -#ifdef USE_ON_FLY_SCROLL - dont_scroll = FALSE; /* allow scrolling here */ -#endif - ++no_mapping; /* don't map this key */ - ++allow_keys; /* allow special keys */ - typed = plain_vgetc(); - --allow_keys; - --no_mapping; - - /* clear the question */ - msg_didout = FALSE; /* don't scroll up */ - msg_col = 0; - gotocmdline(TRUE); - - /* restore the line */ - if (orig_line != NULL) - ml_replace(lnum, orig_line, FALSE); - } - - need_wait_return = FALSE; /* no hit-return prompt */ - if (typed == 'q' || typed == ESC || typed == Ctrl_C -#ifdef UNIX - || typed == intr_char -#endif - ) { - got_quit = TRUE; - break; - } - if (typed == 'n') - break; - if (typed == 'y') - break; - if (typed == 'l') { - /* last: replace and then stop */ - do_all = FALSE; - line2 = lnum; - break; - } - if (typed == 'a') { - do_ask = FALSE; - break; - } - if (typed == Ctrl_E) - scrollup_clamp(); - else if (typed == Ctrl_Y) - scrolldown_clamp(); - } - State = save_State; - setmouse(); - if (vim_strchr(p_cpo, CPO_UNDO) != NULL) - --no_u_sync; - - if (typed == 'n') { - /* For a multi-line match, put matchcol at the NUL at - * the end of the line and set nmatch to one, so that - * we continue looking for a match on the next line. - * Avoids that ":%s/\nB\@=//gc" and ":%s/\n/,\r/gc" - * get stuck when pressing 'n'. */ - if (nmatch > 1) { - matchcol = (colnr_T)STRLEN(sub_firstline); - skip_match = TRUE; - } - goto skip; - } - if (got_quit) - goto skip; - } - - /* Move the cursor to the start of the match, so that we can - * use "\=col("."). */ - curwin->w_cursor.col = regmatch.startpos[0].col; - - /* - * 3. substitute the string. - */ - if (do_count) { - /* prevent accidentally changing the buffer by a function */ - save_ma = curbuf->b_p_ma; - curbuf->b_p_ma = FALSE; - sandbox++; - } - /* get length of substitution part */ - sublen = vim_regsub_multi(®match, - sub_firstlnum - regmatch.startpos[0].lnum, - sub, sub_firstline, FALSE, p_magic, TRUE); - if (do_count) { - curbuf->b_p_ma = save_ma; - sandbox--; - goto skip; - } - - /* When the match included the "$" of the last line it may - * go beyond the last line of the buffer. */ - if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) { - nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1; - skip_match = TRUE; - } - - /* Need room for: - * - result so far in new_start (not for first sub in line) - * - original text up to match - * - length of substituted part - * - original text after match - */ - if (nmatch == 1) - p1 = sub_firstline; - else { - p1 = ml_get(sub_firstlnum + nmatch - 1); - nmatch_tl += nmatch - 1; - } - copy_len = regmatch.startpos[0].col - copycol; - needed_len = copy_len + ((unsigned)STRLEN(p1) - - regmatch.endpos[0].col) + sublen + 1; - if (new_start == NULL) { - /* - * Get some space for a temporary buffer to do the - * substitution into (and some extra space to avoid - * too many calls to xmalloc()/free()). - */ - new_start_len = needed_len + 50; - new_start = xmalloc(new_start_len); - *new_start = NUL; - new_end = new_start; - } else { - /* - * Check if the temporary buffer is long enough to do the - * substitution into. If not, make it larger (with a bit - * extra to avoid too many calls to xmalloc()/free()). - */ - len = (unsigned)STRLEN(new_start); - needed_len += len; - if (needed_len > (int)new_start_len) { - new_start_len = needed_len + 50; - new_start = xrealloc(new_start, new_start_len); - } - new_end = new_start + len; - } - - /* - * copy the text up to the part that matched - */ - memmove(new_end, sub_firstline + copycol, (size_t)copy_len); - new_end += copy_len; - - (void)vim_regsub_multi(®match, - sub_firstlnum - regmatch.startpos[0].lnum, - sub, new_end, TRUE, p_magic, TRUE); - sub_nsubs++; - did_sub = TRUE; - - /* Move the cursor to the start of the line, to avoid that it - * is beyond the end of the line after the substitution. */ - curwin->w_cursor.col = 0; - - /* For a multi-line match, make a copy of the last matched - * line and continue in that one. */ - if (nmatch > 1) { - sub_firstlnum += nmatch - 1; - free(sub_firstline); - sub_firstline = vim_strsave(ml_get(sub_firstlnum)); - /* When going beyond the last line, stop substituting. */ - if (sub_firstlnum <= line2) - do_again = TRUE; - else - do_all = FALSE; - } - - /* Remember next character to be copied. */ - copycol = regmatch.endpos[0].col; - - if (skip_match) { - /* Already hit end of the buffer, sub_firstlnum is one - * less than what it ought to be. */ - free(sub_firstline); - sub_firstline = vim_strsave((char_u *)""); - copycol = 0; - } - - /* - * Now the trick is to replace CTRL-M chars with a real line - * break. This would make it impossible to insert a CTRL-M in - * the text. The line break can be avoided by preceding the - * CTRL-M with a backslash. To be able to insert a backslash, - * they must be doubled in the string and are halved here. - * That is Vi compatible. - */ - for (p1 = new_end; *p1; ++p1) { - if (p1[0] == '\\' && p1[1] != NUL) /* remove backslash */ - STRMOVE(p1, p1 + 1); - else if (*p1 == CAR) { - if (u_inssub(lnum) == OK) { /* prepare for undo */ - *p1 = NUL; /* truncate up to the CR */ - ml_append(lnum - 1, new_start, - (colnr_T)(p1 - new_start + 1), FALSE); - mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); - if (do_ask) - appended_lines(lnum - 1, 1L); - else { - if (first_line == 0) - first_line = lnum; - last_line = lnum + 1; - } - /* All line numbers increase. */ - ++sub_firstlnum; - ++lnum; - ++line2; - /* move the cursor to the new line, like Vi */ - ++curwin->w_cursor.lnum; - /* copy the rest */ - STRMOVE(new_start, p1 + 1); - p1 = new_start - 1; - } - } else if (has_mbyte) - p1 += (*mb_ptr2len)(p1) - 1; - } - - /* - * 4. If do_all is set, find next match. - * Prevent endless loop with patterns that match empty - * strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g. - * But ":s/\n/#/" is OK. - */ -skip: - /* We already know that we did the last subst when we are at - * the end of the line, except that a pattern like - * "bar\|\nfoo" may match at the NUL. "lnum" can be below - * "line2" when there is a \zs in the pattern after a line - * break. */ - lastone = (skip_match - || got_int - || got_quit - || lnum > line2 - || !(do_all || do_again) - || (sub_firstline[matchcol] == NUL && nmatch <= 1 - && !re_multiline(regmatch.regprog))); - nmatch = -1; - - /* - * Replace the line in the buffer when needed. This is - * skipped when there are more matches. - * The check for nmatch_tl is needed for when multi-line - * matching must replace the lines before trying to do another - * match, otherwise "\@<=" won't work. - * When the match starts below where we start searching also - * need to replace the line first (using \zs after \n). - */ - if (lastone - || nmatch_tl > 0 - || (nmatch = vim_regexec_multi(®match, curwin, - curbuf, sub_firstlnum, - matchcol, NULL)) == 0 - || regmatch.startpos[0].lnum > 0) { - if (new_start != NULL) { - /* - * Copy the rest of the line, that didn't match. - * "matchcol" has to be adjusted, we use the end of - * the line as reference, because the substitute may - * have changed the number of characters. Same for - * "prev_matchcol". - */ - STRCAT(new_start, sub_firstline + copycol); - matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol; - prev_matchcol = (colnr_T)STRLEN(sub_firstline) - - prev_matchcol; - - if (u_savesub(lnum) != OK) - break; - ml_replace(lnum, new_start, TRUE); - - if (nmatch_tl > 0) { - /* - * Matched lines have now been substituted and are - * useless, delete them. The part after the match - * has been appended to new_start, we don't need - * it in the buffer. - */ - ++lnum; - if (u_savedel(lnum, nmatch_tl) != OK) - break; - for (i = 0; i < nmatch_tl; ++i) - ml_delete(lnum, (int)FALSE); - mark_adjust(lnum, lnum + nmatch_tl - 1, - (long)MAXLNUM, -nmatch_tl); - if (do_ask) - deleted_lines(lnum, nmatch_tl); - --lnum; - line2 -= nmatch_tl; /* nr of lines decreases */ - nmatch_tl = 0; - } - - /* When asking, undo is saved each time, must also set - * changed flag each time. */ - if (do_ask) - changed_bytes(lnum, 0); - else { - if (first_line == 0) - first_line = lnum; - last_line = lnum + 1; - } - - sub_firstlnum = lnum; - free(sub_firstline); /* free the temp buffer */ - sub_firstline = new_start; - new_start = NULL; - matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol; - prev_matchcol = (colnr_T)STRLEN(sub_firstline) - - prev_matchcol; - copycol = 0; - } - if (nmatch == -1 && !lastone) - nmatch = vim_regexec_multi(®match, curwin, curbuf, - sub_firstlnum, matchcol, NULL); - - /* - * 5. break if there isn't another match in this line - */ - if (nmatch <= 0) { - /* If the match found didn't start where we were - * searching, do the next search in the line where we - * found the match. */ - if (nmatch == -1) - lnum -= regmatch.startpos[0].lnum; - break; - } - } - - line_breakcheck(); - } - - if (did_sub) - ++sub_nlines; - free(new_start); /* for when substitute was cancelled */ - free(sub_firstline); /* free the copy of the original line */ - sub_firstline = NULL; - } - - line_breakcheck(); - } - - if (first_line != 0) { - /* Need to subtract the number of added lines from "last_line" to get - * the line number before the change (same as adding the number of - * deleted lines). */ - i = curbuf->b_ml.ml_line_count - old_line_count; - changed_lines(first_line, 0, last_line - i, i); - } - - free(sub_firstline); /* may have to free allocated copy of the line */ - - /* ":s/pat//n" doesn't move the cursor */ - if (do_count) - curwin->w_cursor = old_cursor; - - if (sub_nsubs > start_nsubs) { - /* Set the '[ and '] marks. */ - curbuf->b_op_start.lnum = eap->line1; - curbuf->b_op_end.lnum = line2; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; - - if (!global_busy) { - if (!do_ask) { /* when interactive leave cursor on the match */ - if (endcolumn) - coladvance((colnr_T)MAXCOL); - else - beginline(BL_WHITE | BL_FIX); - } - if (!do_sub_msg(do_count) && do_ask) - MSG(""); - } else - global_need_beginline = TRUE; - if (do_print) - print_line(curwin->w_cursor.lnum, do_number, do_list); - } else if (!global_busy) { - if (got_int) /* interrupted */ - EMSG(_(e_interr)); - else if (got_match) /* did find something but nothing substituted */ - MSG(""); - else if (do_error) /* nothing found */ - EMSG2(_(e_patnotf2), get_search_pat()); - } - - if (do_ask && hasAnyFolding(curwin)) - /* Cursor position may require updating */ - changed_window_setting(); - - vim_regfree(regmatch.regprog); -} - -/* - * Give message for number of substitutions. - * Can also be used after a ":global" command. - * Return TRUE if a message was given. - */ -int -do_sub_msg ( - int count_only /* used 'n' flag for ":s" */ -) -{ - /* - * Only report substitutions when: - * - more than 'report' substitutions - * - command was typed by user, or number of changed lines > 'report' - * - giving messages is not disabled by 'lazyredraw' - */ - if (((sub_nsubs > p_report && (KeyTyped || sub_nlines > 1 || p_report < 1)) - || count_only) - && messaging()) { - if (got_int) - STRCPY(msg_buf, _("(Interrupted) ")); - else - *msg_buf = NUL; - if (sub_nsubs == 1) - vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - "%s", count_only ? _("1 match") : _("1 substitution")); - else - vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - count_only ? _("%" PRId64 " matches") - : _("%" PRId64 " substitutions"), - (int64_t)sub_nsubs); - if (sub_nlines == 1) - vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - "%s", _(" on 1 line")); - else - vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - _(" on %" PRId64 " lines"), (int64_t)sub_nlines); - if (msg(msg_buf)) - /* save message to display it after redraw */ - set_keep_msg(msg_buf, 0); - return TRUE; - } - if (got_int) { - EMSG(_(e_interr)); - return TRUE; - } - return FALSE; -} - -/* - * Execute a global command of the form: - * - * g/pattern/X : execute X on all lines where pattern matches - * v/pattern/X : execute X on all lines where pattern does not match - * - * where 'X' is an EX command - * - * The command character (as well as the trailing slash) is optional, and - * is assumed to be 'p' if missing. - * - * This is implemented in two passes: first we scan the file for the pattern and - * set a mark for each line that (not) matches. Secondly we execute the command - * for each line that has a mark. This is required because after deleting - * lines we do not know where to search for the next match. - */ -void ex_global(exarg_T *eap) -{ - linenr_T lnum; /* line number according to old situation */ - int ndone = 0; - int type; /* first char of cmd: 'v' or 'g' */ - char_u *cmd; /* command argument */ - - char_u delim; /* delimiter, normally '/' */ - char_u *pat; - regmmatch_T regmatch; - int match; - int which_pat; - - if (global_busy) { - EMSG(_("E147: Cannot do :global recursive")); /* will increment global_busy */ - return; - } - - if (eap->forceit) /* ":global!" is like ":vglobal" */ - type = 'v'; - else - type = *eap->cmd; - cmd = eap->arg; - which_pat = RE_LAST; /* default: use last used regexp */ - - /* - * undocumented vi feature: - * "\/" and "\?": use previous search pattern. - * "\&": use previous substitute pattern. - */ - if (*cmd == '\\') { - ++cmd; - if (vim_strchr((char_u *)"/?&", *cmd) == NULL) { - EMSG(_(e_backslash)); - return; - } - if (*cmd == '&') - which_pat = RE_SUBST; /* use previous substitute pattern */ - else - which_pat = RE_SEARCH; /* use previous search pattern */ - ++cmd; - pat = (char_u *)""; - } else if (*cmd == NUL) { - EMSG(_("E148: Regular expression missing from global")); - return; - } else { - delim = *cmd; /* get the delimiter */ - if (delim) - ++cmd; /* skip delimiter if there is one */ - pat = cmd; /* remember start of pattern */ - cmd = skip_regexp(cmd, delim, p_magic, &eap->arg); - if (cmd[0] == delim) /* end delimiter found */ - *cmd++ = NUL; /* replace it with a NUL */ - } - - if (p_altkeymap && curwin->w_p_rl) - lrFswap(pat,0); - - if (search_regcomp(pat, RE_BOTH, which_pat, SEARCH_HIS, ®match) == FAIL) { - EMSG(_(e_invcmd)); - return; - } - - /* - * pass 1: set marks for each (not) matching line - */ - for (lnum = eap->line1; lnum <= eap->line2 && !got_int; ++lnum) { - /* a match on this line? */ - match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); - if ((type == 'g' && match) || (type == 'v' && !match)) { - ml_setmarked(lnum); - ndone++; - } - line_breakcheck(); - } - - /* - * pass 2: execute the command for each line that has been marked - */ - if (got_int) - MSG(_(e_interr)); - else if (ndone == 0) { - if (type == 'v') - smsg((char_u *)_("Pattern found in every line: %s"), pat); - else - smsg((char_u *)_("Pattern not found: %s"), pat); - } else - global_exe(cmd); - - ml_clearmarked(); /* clear rest of the marks */ - vim_regfree(regmatch.regprog); -} - -/* - * Execute "cmd" on lines marked with ml_setmarked(). - */ -void global_exe(char_u *cmd) -{ - linenr_T old_lcount; /* b_ml.ml_line_count before the command */ - buf_T *old_buf = curbuf; /* remember what buffer we started in */ - linenr_T lnum; /* line number according to old situation */ - - /* - * Set current position only once for a global command. - * If global_busy is set, setpcmark() will not do anything. - * If there is an error, global_busy will be incremented. - */ - setpcmark(); - - /* When the command writes a message, don't overwrite the command. */ - msg_didout = TRUE; - - sub_nsubs = 0; - sub_nlines = 0; - global_need_beginline = FALSE; - global_busy = 1; - old_lcount = curbuf->b_ml.ml_line_count; - while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1) { - curwin->w_cursor.lnum = lnum; - curwin->w_cursor.col = 0; - if (*cmd == NUL || *cmd == '\n') - do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT); - else - do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT); - ui_breakcheck(); - } - - global_busy = 0; - if (global_need_beginline) - beginline(BL_WHITE | BL_FIX); - else - check_cursor(); /* cursor may be beyond the end of the line */ - - /* the cursor may not have moved in the text but a change in a previous - * line may move it on the screen */ - changed_line_abv_curs(); - - /* If it looks like no message was written, allow overwriting the - * command with the report for number of changes. */ - if (msg_col == 0 && msg_scrolled == 0) - msg_didout = FALSE; - - /* If substitutes done, report number of substitutes, otherwise report - * number of extra or deleted lines. - * Don't report extra or deleted lines in the edge case where the buffer - * we are in after execution is different from the buffer we started in. */ - if (!do_sub_msg(FALSE) && curbuf == old_buf) - msgmore(curbuf->b_ml.ml_line_count - old_lcount); -} - -int read_viminfo_sub_string(vir_T *virp, int force) -{ - if (force) - free(old_sub); - if (force || old_sub == NULL) - old_sub = viminfo_readstring(virp, 1, TRUE); - return viminfo_readline(virp); -} - -void write_viminfo_sub_string(FILE *fp) -{ - if (get_viminfo_parameter('/') != 0 && old_sub != NULL) { - fputs(_("\n# Last Substitute String:\n$"), fp); - viminfo_writestring(fp, old_sub); - } -} - -#if defined(EXITFREE) || defined(PROTO) -void free_old_sub(void) -{ - free(old_sub); -} - -#endif - -/* - * Set up for a tagpreview. - * Return TRUE when it was created. - */ -int -prepare_tagpreview ( - int undo_sync /* sync undo when leaving the window */ -) -{ - win_T *wp; - - - /* - * If there is already a preview window open, use that one. - */ - if (!curwin->w_p_pvw) { - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_p_pvw) - break; - if (wp != NULL) - win_enter(wp, undo_sync); - else { - /* - * There is no preview window open yet. Create one. - */ - if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0) - == FAIL) - return FALSE; - curwin->w_p_pvw = TRUE; - curwin->w_p_wfh = TRUE; - RESET_BINDING(curwin); /* don't take over 'scrollbind' - and 'cursorbind' */ - curwin->w_p_diff = FALSE; /* no 'diff' */ - curwin->w_p_fdc = 0; /* no 'foldcolumn' */ - return TRUE; - } - } - return FALSE; -} - - - -/* - * ":help": open a read-only window on a help file - */ -void ex_help(exarg_T *eap) -{ - char_u *arg; - char_u *tag; - FILE *helpfd; /* file descriptor of help file */ - int n; - int i; - win_T *wp; - int num_matches; - char_u **matches; - char_u *p; - int empty_fnum = 0; - int alt_fnum = 0; - buf_T *buf; - int len; - char_u *lang; - int old_KeyTyped = KeyTyped; - - if (eap != NULL) { - /* - * A ":help" command ends at the first LF, or at a '|' that is - * followed by some text. Set nextcmd to the following command. - */ - for (arg = eap->arg; *arg; ++arg) { - if (*arg == '\n' || *arg == '\r' - || (*arg == '|' && arg[1] != NUL && arg[1] != '|')) { - *arg++ = NUL; - eap->nextcmd = arg; - break; - } - } - arg = eap->arg; - - if (eap->forceit && *arg == NUL && !curbuf->b_help) { - EMSG(_("E478: Don't panic!")); - return; - } - - if (eap->skip) /* not executing commands */ - return; - } else - arg = (char_u *)""; - - /* remove trailing blanks */ - p = arg + STRLEN(arg) - 1; - while (p > arg && vim_iswhite(*p) && p[-1] != '\\') - *p-- = NUL; - - /* Check for a specified language */ - lang = check_help_lang(arg); - - /* When no argument given go to the index. */ - if (*arg == NUL) - arg = (char_u *)"help.txt"; - - /* - * Check if there is a match for the argument. - */ - n = find_help_tags(arg, &num_matches, &matches, - eap != NULL && eap->forceit); - - i = 0; - if (n != FAIL && lang != NULL) - /* Find first item with the requested language. */ - for (i = 0; i < num_matches; ++i) { - len = (int)STRLEN(matches[i]); - if (len > 3 && matches[i][len - 3] == '@' - && STRICMP(matches[i] + len - 2, lang) == 0) - break; - } - if (i >= num_matches || n == FAIL) { - if (lang != NULL) - EMSG3(_("E661: Sorry, no '%s' help for %s"), lang, arg); - else - EMSG2(_("E149: Sorry, no help for %s"), arg); - if (n != FAIL) - FreeWild(num_matches, matches); - return; - } - - /* The first match (in the requested language) is the best match. */ - tag = vim_strsave(matches[i]); - FreeWild(num_matches, matches); - - - /* - * Re-use an existing help window or open a new one. - * Always open a new one for ":tab help". - */ - if (!curwin->w_buffer->b_help - || cmdmod.tab != 0 - ) { - if (cmdmod.tab != 0) - wp = NULL; - else - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer != NULL && wp->w_buffer->b_help) - break; - if (wp != NULL && wp->w_buffer->b_nwindows > 0) - win_enter(wp, TRUE); - else { - /* - * There is no help window yet. - * Try to open the file specified by the "helpfile" option. - */ - if ((helpfd = mch_fopen((char *)p_hf, READBIN)) == NULL) { - smsg((char_u *)_("Sorry, help file \"%s\" not found"), p_hf); - goto erret; - } - fclose(helpfd); - - /* Split off help window; put it at far top if no position - * specified, the current window is vertically split and - * narrow. */ - n = WSP_HELP; - if (cmdmod.split == 0 && curwin->w_width != Columns - && curwin->w_width < 80) - n |= WSP_TOP; - if (win_split(0, n) == FAIL) - goto erret; - - if (curwin->w_height < p_hh) - win_setheight((int)p_hh); - - /* - * Open help file (do_ecmd() will set b_help flag, readfile() will - * set b_p_ro flag). - * Set the alternate file to the previously edited file. - */ - alt_fnum = curbuf->b_fnum; - (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL, - ECMD_HIDE + ECMD_SET_HELP, - NULL /* buffer is still open, don't store info */ - ); - if (!cmdmod.keepalt) - curwin->w_alt_fnum = alt_fnum; - empty_fnum = curbuf->b_fnum; - } - } - - if (!p_im) - restart_edit = 0; /* don't want insert mode in help file */ - - /* Restore KeyTyped, setting 'filetype=help' may reset it. - * It is needed for do_tag top open folds under the cursor. */ - KeyTyped = old_KeyTyped; - - if (tag != NULL) - do_tag(tag, DT_HELP, 1, FALSE, TRUE); - - /* Delete the empty buffer if we're not using it. Careful: autocommands - * may have jumped to another window, check that the buffer is not in a - * window. */ - if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) { - buf = buflist_findnr(empty_fnum); - if (buf != NULL && buf->b_nwindows == 0) - wipe_buffer(buf, TRUE); - } - - /* keep the previous alternate file */ - if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum && !cmdmod.keepalt) - curwin->w_alt_fnum = alt_fnum; - -erret: - free(tag); -} - - -/* - * In an argument search for a language specifiers in the form "@xx". - * Changes the "@" to NUL if found, and returns a pointer to "xx". - * Returns NULL if not found. - */ -char_u *check_help_lang(char_u *arg) -{ - int len = (int)STRLEN(arg); - - if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2]) - && ASCII_ISALPHA(arg[len - 1])) { - arg[len - 3] = NUL; /* remove the '@' */ - return arg + len - 2; - } - return NULL; -} - -/* - * Return a heuristic indicating how well the given string matches. The - * smaller the number, the better the match. This is the order of priorities, - * from best match to worst match: - * - Match with least alpha-numeric characters is better. - * - Match with least total characters is better. - * - Match towards the start is better. - * - Match starting with "+" is worse (feature instead of command) - * Assumption is made that the matched_string passed has already been found to - * match some string for which help is requested. webb. - */ -int -help_heuristic ( - char_u *matched_string, - int offset, /* offset for match */ - int wrong_case /* no matching case */ -) -{ - int num_letters; - char_u *p; - - num_letters = 0; - for (p = matched_string; *p; p++) - if (ASCII_ISALNUM(*p)) - num_letters++; - - /* - * Multiply the number of letters by 100 to give it a much bigger - * weighting than the number of characters. - * If there only is a match while ignoring case, add 5000. - * If the match starts in the middle of a word, add 10000 to put it - * somewhere in the last half. - * If the match is more than 2 chars from the start, multiply by 200 to - * put it after matches at the start. - */ - if (ASCII_ISALNUM(matched_string[offset]) && offset > 0 - && ASCII_ISALNUM(matched_string[offset - 1])) - offset += 10000; - else if (offset > 2) - offset *= 200; - if (wrong_case) - offset += 5000; - /* Features are less interesting than the subjects themselves, but "+" - * alone is not a feature. */ - if (matched_string[0] == '+' && matched_string[1] != NUL) - offset += 100; - return (int)(100 * num_letters + STRLEN(matched_string) + offset); -} - -/* - * Compare functions for qsort() below, that checks the help heuristics number - * that has been put after the tagname by find_tags(). - */ -static int help_compare(const void *s1, const void *s2) -{ - char *p1; - char *p2; - - p1 = *(char **)s1 + strlen(*(char **)s1) + 1; - p2 = *(char **)s2 + strlen(*(char **)s2) + 1; - return strcmp(p1, p2); -} - -/* - * Find all help tags matching "arg", sort them and return in matches[], with - * the number of matches in num_matches. - * The matches will be sorted with a "best" match algorithm. - * When "keep_lang" is TRUE try keeping the language of the current buffer. - */ -int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_lang) -{ - char_u *s, *d; - int i; - static char *(mtable[]) = {"*", "g*", "[*", "]*", ":*", - "/*", "/\\*", "\"*", "**", - "cpo-*", "/\\(\\)", "/\\%(\\)", - "?", ":?", "?", "g?", "g?g?", "g??", "z?", - "/\\?", "/\\z(\\)", "\\=", ":s\\=", - "[count]", "[quotex]", "[range]", - "[pattern]", "\\|", "\\%$", - "s/\\~", "s/\\U", "s/\\L", - "s/\\1", "s/\\2", "s/\\3", "s/\\9"}; - static char *(rtable[]) = {"star", "gstar", "[star", "]star", ":star", - "/star", "/\\\\star", "quotestar", "starstar", - "cpo-star", "/\\\\(\\\\)", "/\\\\%(\\\\)", - "?", ":?", "?", "g?", "g?g?", "g??", "z?", - "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", - "\\[count]", "\\[quotex]", "\\[range]", - "\\[pattern]", "\\\\bar", "/\\\\%\\$", - "s/\\\\\\~", "s/\\\\U", "s/\\\\L", - "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"}; - int flags; - - d = IObuff; /* assume IObuff is long enough! */ - - /* - * Recognize a few exceptions to the rule. Some strings that contain '*' - * with "star". Otherwise '*' is recognized as a wildcard. - */ - for (i = (int)(sizeof(mtable) / sizeof(char *)); --i >= 0; ) - if (STRCMP(arg, mtable[i]) == 0) { - STRCPY(d, rtable[i]); - break; - } - - if (i < 0) { /* no match in table */ - /* Replace "\S" with "/\\S", etc. Otherwise every tag is matched. - * Also replace "\%^" and "\%(", they match every tag too. - * Also "\zs", "\z1", etc. - * Also "\@<", "\@=", "\@<=", etc. - * And also "\_$" and "\_^". */ - if (arg[0] == '\\' - && ((arg[1] != NUL && arg[2] == NUL) - || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL - && arg[2] != NUL))) { - STRCPY(d, "/\\\\"); - STRCPY(d + 3, arg + 1); - /* Check for "/\\_$", should be "/\\_\$" */ - if (d[3] == '_' && d[4] == '$') - STRCPY(d + 4, "\\$"); - } else { - /* Replace: - * "[:...:]" with "\[:...:]" - * "[++...]" with "\[++...]" - * "\{" with "\\{" -- matching "} \}" - */ - if ((arg[0] == '[' && (arg[1] == ':' - || (arg[1] == '+' && arg[2] == '+'))) - || (arg[0] == '\\' && arg[1] == '{')) - *d++ = '\\'; - - for (s = arg; *s; ++s) { - /* - * Replace "|" with "bar" and '"' with "quote" to match the name of - * the tags for these commands. - * Replace "*" with ".*" and "?" with "." to match command line - * completion. - * Insert a backslash before '~', '$' and '.' to avoid their - * special meaning. - */ - if (d - IObuff > IOSIZE - 10) /* getting too long!? */ - break; - switch (*s) { - case '|': STRCPY(d, "bar"); - d += 3; - continue; - case '"': STRCPY(d, "quote"); - d += 5; - continue; - case '*': *d++ = '.'; - break; - case '?': *d++ = '.'; - continue; - case '$': - case '.': - case '~': *d++ = '\\'; - break; - } - - /* - * Replace "^x" by "CTRL-X". Don't do this for "^_" to make - * ":help i_^_CTRL-D" work. - * Insert '-' before and after "CTRL-X" when applicable. - */ - if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1]) - || vim_strchr((char_u *) - "?@[\\]^", - s[1]) != NULL))) { - if (d > IObuff && d[-1] != '_' && d[-1] != '\\') - *d++ = '_'; /* prepend a '_' to make x_CTRL-x */ - STRCPY(d, "CTRL-"); - d += 5; - if (*s < ' ') { - *d++ = *s + '@'; - if (d[-1] == '\\') - *d++ = '\\'; /* double a backslash */ - } else - *d++ = *++s; - if (s[1] != NUL && s[1] != '_') - *d++ = '_'; /* append a '_' */ - continue; - } else if (*s == '^') /* "^" or "CTRL-^" or "^_" */ - *d++ = '\\'; - - /* - * Insert a backslash before a backslash after a slash, for search - * pattern tags: "/\|" --> "/\\|". - */ - else if (s[0] == '\\' && s[1] != '\\' - && *arg == '/' && s == arg + 1) - *d++ = '\\'; - - /* "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in - * "CTRL-\_CTRL-N" */ - if (STRNICMP(s, "CTRL-\\_", 7) == 0) { - STRCPY(d, "CTRL-\\\\"); - d += 7; - s += 6; - } - - *d++ = *s; - - /* - * If tag starts with ', toss everything after a second '. Fixes - * CTRL-] on 'option'. (would include the trailing '.'). - */ - if (*s == '\'' && s > arg && *arg == '\'') - break; - } - *d = NUL; - - if (*IObuff == '`') { - if (d > IObuff + 2 && d[-1] == '`') { - /* remove the backticks from `command` */ - memmove(IObuff, IObuff + 1, STRLEN(IObuff)); - d[-2] = NUL; - } else if (d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') { - /* remove the backticks and comma from `command`, */ - memmove(IObuff, IObuff + 1, STRLEN(IObuff)); - d[-3] = NUL; - } else if (d > IObuff + 4 && d[-3] == '`' - && d[-2] == '\\' && d[-1] == '.') { - /* remove the backticks and dot from `command`\. */ - memmove(IObuff, IObuff + 1, STRLEN(IObuff)); - d[-4] = NUL; - } - } - } - } - - *matches = (char_u **)""; - *num_matches = 0; - flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE; - if (keep_lang) - flags |= TAG_KEEP_LANG; - if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK - && *num_matches > 0) { - /* Sort the matches found on the heuristic number that is after the - * tag name. */ - qsort((void *)*matches, (size_t)*num_matches, - sizeof(char_u *), help_compare); - /* Delete more than TAG_MANY to reduce the size of the listing. */ - while (*num_matches > TAG_MANY) - free((*matches)[--*num_matches]); - } - return OK; -} - -/* - * After reading a help file: May cleanup a help buffer when syntax - * highlighting is not used. - */ -void fix_help_buffer(void) -{ - linenr_T lnum; - char_u *line; - int in_example = FALSE; - int len; - char_u *fname; - char_u *p; - char_u *rt; - int mustfree; - - /* set filetype to "help". */ - set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); - - if (!syntax_present(curwin)) { - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { - line = ml_get_buf(curbuf, lnum, FALSE); - len = (int)STRLEN(line); - if (in_example && len > 0 && !vim_iswhite(line[0])) { - /* End of example: non-white or '<' in first column. */ - if (line[0] == '<') { - /* blank-out a '<' in the first column */ - line = ml_get_buf(curbuf, lnum, TRUE); - line[0] = ' '; - } - in_example = FALSE; - } - if (!in_example && len > 0) { - if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) { - /* blank-out a '>' in the last column (start of example) */ - line = ml_get_buf(curbuf, lnum, TRUE); - line[len - 1] = ' '; - in_example = TRUE; - } else if (line[len - 1] == '~') { - /* blank-out a '~' at the end of line (header marker) */ - line = ml_get_buf(curbuf, lnum, TRUE); - line[len - 1] = ' '; - } - } - } - } - - /* - * In the "help.txt" and "help.abx" file, add the locally added help - * files. This uses the very first line in the help file. - */ - fname = path_tail(curbuf->b_fname); - if (fnamecmp(fname, "help.txt") == 0 - || (fnamencmp(fname, "help.", 5) == 0 - && ASCII_ISALPHA(fname[5]) - && ASCII_ISALPHA(fname[6]) - && TOLOWER_ASC(fname[7]) == 'x' - && fname[8] == NUL) - ) { - for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum) { - line = ml_get_buf(curbuf, lnum, FALSE); - if (strstr((char *)line, "*local-additions*") == NULL) - continue; - - /* Go through all directories in 'runtimepath', skipping - * $VIMRUNTIME. */ - p = p_rtp; - while (*p != NUL) { - copy_option_part(&p, NameBuff, MAXPATHL, ","); - mustfree = FALSE; - rt = vim_getenv((char_u *)"VIMRUNTIME", &mustfree); - if (path_full_compare(rt, NameBuff, FALSE) != kEqualFiles) { - int fcount; - char_u **fnames; - FILE *fd; - char_u *s; - int fi; - vimconv_T vc; - char_u *cp; - - /* Find all "doc/ *.txt" files in this directory. */ - add_pathsep(NameBuff); - STRCAT(NameBuff, "doc/*.??[tx]"); - if (gen_expand_wildcards(1, &NameBuff, &fcount, - &fnames, EW_FILE|EW_SILENT) == OK - && fcount > 0) { - int i1; - int i2; - char_u *f1; - char_u *f2; - char_u *t1; - char_u *e1; - char_u *e2; - - /* If foo.abx is found use it instead of foo.txt in - * the same directory. */ - for (i1 = 0; i1 < fcount; ++i1) { - for (i2 = 0; i2 < fcount; ++i2) { - if (i1 == i2) - continue; - if (fnames[i1] == NULL || fnames[i2] == NULL) - continue; - f1 = fnames[i1]; - f2 = fnames[i2]; - t1 = path_tail(f1); - if (fnamencmp(f1, f2, t1 - f1) != 0) - continue; - e1 = vim_strrchr(t1, '.'); - e2 = vim_strrchr(path_tail(f2), '.'); - if (e1 == NUL || e2 == NUL) - continue; - if (fnamecmp(e1, ".txt") != 0 - && fnamecmp(e1, fname + 4) != 0) { - /* Not .txt and not .abx, remove it. */ - free(fnames[i1]); - fnames[i1] = NULL; - continue; - } - if (fnamencmp(f1, f2, e1 - f1) != 0) - continue; - if (fnamecmp(e1, ".txt") == 0 - && fnamecmp(e2, fname + 4) == 0) { - /* use .abx instead of .txt */ - free(fnames[i1]); - fnames[i1] = NULL; - } - } - } - for (fi = 0; fi < fcount; ++fi) { - if (fnames[fi] == NULL) - continue; - fd = mch_fopen((char *)fnames[fi], "r"); - if (fd != NULL) { - vim_fgets(IObuff, IOSIZE, fd); - if (IObuff[0] == '*' - && (s = vim_strchr(IObuff + 1, '*')) - != NULL) { - int this_utf = MAYBE; - /* Change tag definition to a - * reference and remove /. */ - IObuff[0] = '|'; - *s = '|'; - while (*s != NUL) { - if (*s == '\r' || *s == '\n') - *s = NUL; - /* The text is utf-8 when a byte - * above 127 is found and no - * illegal byte sequence is found. - */ - if (*s >= 0x80 && this_utf != FALSE) { - int l; - - this_utf = TRUE; - l = utf_ptr2len(s); - if (l == 1) - this_utf = FALSE; - s += l - 1; - } - ++s; - } - /* The help file is latin1 or utf-8; - * conversion to the current - * 'encoding' may be required. */ - vc.vc_type = CONV_NONE; - convert_setup(&vc, (char_u *)( - this_utf == TRUE ? "utf-8" - : "latin1"), p_enc); - if (vc.vc_type == CONV_NONE) - /* No conversion needed. */ - cp = IObuff; - else { - /* Do the conversion. If it fails - * use the unconverted text. */ - cp = string_convert(&vc, IObuff, - NULL); - if (cp == NULL) - cp = IObuff; - } - convert_setup(&vc, NULL, NULL); - - ml_append(lnum, cp, (colnr_T)0, FALSE); - if (cp != IObuff) - free(cp); - ++lnum; - } - fclose(fd); - } - } - FreeWild(fcount, fnames); - } - } - if (mustfree) - free(rt); - } - break; - } - } -} - -/* - * ":exusage" - */ -void ex_exusage(exarg_T *eap) -{ - do_cmdline_cmd((char_u *)"help ex-cmd-index"); -} - -/* - * ":viusage" - */ -void ex_viusage(exarg_T *eap) -{ - do_cmdline_cmd((char_u *)"help normal-index"); -} - -static void helptags_one(char_u *dir, char_u *ext, char_u *lang, - int add_help_tags); - -/* - * ":helptags" - */ -void ex_helptags(exarg_T *eap) -{ - garray_T ga; - int i, j; - int len; - char_u lang[2]; - expand_T xpc; - char_u *dirname; - char_u ext[5]; - char_u fname[8]; - int filecount; - char_u **files; - int add_help_tags = FALSE; - - /* Check for ":helptags ++t {dir}". */ - if (STRNCMP(eap->arg, "++t", 3) == 0 && vim_iswhite(eap->arg[3])) { - add_help_tags = TRUE; - eap->arg = skipwhite(eap->arg + 3); - } - - ExpandInit(&xpc); - xpc.xp_context = EXPAND_DIRECTORIES; - dirname = ExpandOne(&xpc, eap->arg, NULL, - WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); - if (dirname == NULL || !os_isdir(dirname)) { - EMSG2(_("E150: Not a directory: %s"), eap->arg); - return; - } - - /* Get a list of all files in the help directory and in subdirectories. */ - STRCPY(NameBuff, dirname); - add_pathsep(NameBuff); - STRCAT(NameBuff, "**"); - if (gen_expand_wildcards(1, &NameBuff, &filecount, &files, - EW_FILE|EW_SILENT) == FAIL - || filecount == 0) { - EMSG2("E151: No match: %s", NameBuff); - free(dirname); - return; - } - - /* Go over all files in the directory to find out what languages are - * present. */ - ga_init(&ga, 1, 10); - for (i = 0; i < filecount; ++i) { - len = (int)STRLEN(files[i]); - if (len > 4) { - if (STRICMP(files[i] + len - 4, ".txt") == 0) { - /* ".txt" -> language "en" */ - lang[0] = 'e'; - lang[1] = 'n'; - } else if (files[i][len - 4] == '.' - && ASCII_ISALPHA(files[i][len - 3]) - && ASCII_ISALPHA(files[i][len - 2]) - && TOLOWER_ASC(files[i][len - 1]) == 'x') { - /* ".abx" -> language "ab" */ - lang[0] = TOLOWER_ASC(files[i][len - 3]); - lang[1] = TOLOWER_ASC(files[i][len - 2]); - } else - continue; - - /* Did we find this language already? */ - for (j = 0; j < ga.ga_len; j += 2) - if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) - break; - if (j == ga.ga_len) { - /* New language, add it. */ - ga_grow(&ga, 2); - ((char_u *)ga.ga_data)[ga.ga_len++] = lang[0]; - ((char_u *)ga.ga_data)[ga.ga_len++] = lang[1]; - } - } - } - - /* - * Loop over the found languages to generate a tags file for each one. - */ - for (j = 0; j < ga.ga_len; j += 2) { - STRCPY(fname, "tags-xx"); - fname[5] = ((char_u *)ga.ga_data)[j]; - fname[6] = ((char_u *)ga.ga_data)[j + 1]; - if (fname[5] == 'e' && fname[6] == 'n') { - /* English is an exception: use ".txt" and "tags". */ - fname[4] = NUL; - STRCPY(ext, ".txt"); - } else { - /* Language "ab" uses ".abx" and "tags-ab". */ - STRCPY(ext, ".xxx"); - ext[1] = fname[5]; - ext[2] = fname[6]; - } - helptags_one(dirname, ext, fname, add_help_tags); - } - - ga_clear(&ga); - FreeWild(filecount, files); - - free(dirname); -} - -static void -helptags_one ( - char_u *dir, /* doc directory */ - char_u *ext, /* suffix, ".txt", ".itx", ".frx", etc. */ - char_u *tagfname, /* "tags" for English, "tags-fr" for French. */ - int add_help_tags /* add "help-tags" tag */ -) -{ - FILE *fd_tags; - FILE *fd; - garray_T ga; - int filecount; - char_u **files; - char_u *p1, *p2; - int fi; - char_u *s; - int i; - char_u *fname; - int dirlen; - int utf8 = MAYBE; - int this_utf8; - int firstline; - int mix = FALSE; /* detected mixed encodings */ - - /* - * Find all *.txt files. - */ - dirlen = (int)STRLEN(dir); - STRCPY(NameBuff, dir); - STRCAT(NameBuff, "/**/*"); - STRCAT(NameBuff, ext); - if (gen_expand_wildcards(1, &NameBuff, &filecount, &files, - EW_FILE|EW_SILENT) == FAIL - || filecount == 0) { - if (!got_int) - EMSG2("E151: No match: %s", NameBuff); - return; - } - - /* - * Open the tags file for writing. - * Do this before scanning through all the files. - */ - STRCPY(NameBuff, dir); - add_pathsep(NameBuff); - STRCAT(NameBuff, tagfname); - fd_tags = mch_fopen((char *)NameBuff, "w"); - if (fd_tags == NULL) { - EMSG2(_("E152: Cannot open %s for writing"), NameBuff); - FreeWild(filecount, files); - return; - } - - /* - * If using the "++t" argument or generating tags for "$VIMRUNTIME/doc" - * add the "help-tags" tag. - */ - ga_init(&ga, (int)sizeof(char_u *), 100); - if (add_help_tags || path_full_compare((char_u *)"$VIMRUNTIME/doc", - dir, FALSE) == kEqualFiles) { - ga_grow(&ga, 1); - s = xmalloc(18 + STRLEN(tagfname)); - sprintf((char *)s, "help-tags\t%s\t1\n", tagfname); - ((char_u **)ga.ga_data)[ga.ga_len] = s; - ++ga.ga_len; - } - - /* - * Go over all the files and extract the tags. - */ - for (fi = 0; fi < filecount && !got_int; ++fi) { - fd = mch_fopen((char *)files[fi], "r"); - if (fd == NULL) { - EMSG2(_("E153: Unable to open %s for reading"), files[fi]); - continue; - } - fname = files[fi] + dirlen + 1; - - firstline = TRUE; - while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) { - if (firstline) { - /* Detect utf-8 file by a non-ASCII char in the first line. */ - this_utf8 = MAYBE; - for (s = IObuff; *s != NUL; ++s) - if (*s >= 0x80) { - int l; - - this_utf8 = TRUE; - l = utf_ptr2len(s); - if (l == 1) { - /* Illegal UTF-8 byte sequence. */ - this_utf8 = FALSE; - break; - } - s += l - 1; - } - if (this_utf8 == MAYBE) /* only ASCII characters found */ - this_utf8 = FALSE; - if (utf8 == MAYBE) /* first file */ - utf8 = this_utf8; - else if (utf8 != this_utf8) { - EMSG2(_( - "E670: Mix of help file encodings within a language: %s"), - files[fi]); - mix = !got_int; - got_int = TRUE; - } - firstline = FALSE; - } - p1 = vim_strchr(IObuff, '*'); /* find first '*' */ - while (p1 != NULL) { - /* Use vim_strbyte() instead of vim_strchr() so that when - * 'encoding' is dbcs it still works, don't find '*' in the - * second byte. */ - p2 = vim_strbyte(p1 + 1, '*'); /* find second '*' */ - if (p2 != NULL && p2 > p1 + 1) { /* skip "*" and "**" */ - for (s = p1 + 1; s < p2; ++s) - if (*s == ' ' || *s == '\t' || *s == '|') - break; - - /* - * Only accept a *tag* when it consists of valid - * characters, there is white space before it and is - * followed by a white character or end-of-line. - */ - if (s == p2 - && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t') - && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL - || s[1] == '\0')) { - *p2 = '\0'; - ++p1; - ga_grow(&ga, 1); - s = xmalloc((p2 - p1) + STRLEN(fname) + 2); - ((char_u **)ga.ga_data)[ga.ga_len] = s; - ++ga.ga_len; - sprintf((char *)s, "%s\t%s", p1, fname); - - /* find next '*' */ - p2 = vim_strchr(p2 + 1, '*'); - } - } - p1 = p2; - } - line_breakcheck(); - } - - fclose(fd); - } - - FreeWild(filecount, files); - - if (!got_int) { - /* - * Sort the tags. - */ - sort_strings((char_u **)ga.ga_data, ga.ga_len); - - /* - * Check for duplicates. - */ - for (i = 1; i < ga.ga_len; ++i) { - p1 = ((char_u **)ga.ga_data)[i - 1]; - p2 = ((char_u **)ga.ga_data)[i]; - while (*p1 == *p2) { - if (*p2 == '\t') { - *p2 = NUL; - vim_snprintf((char *)NameBuff, MAXPATHL, - _("E154: Duplicate tag \"%s\" in file %s/%s"), - ((char_u **)ga.ga_data)[i], dir, p2 + 1); - EMSG(NameBuff); - *p2 = '\t'; - break; - } - ++p1; - ++p2; - } - } - - if (utf8 == TRUE) - fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n"); - - /* - * Write the tags into the file. - */ - for (i = 0; i < ga.ga_len; ++i) { - s = ((char_u **)ga.ga_data)[i]; - if (STRNCMP(s, "help-tags\t", 10) == 0) - /* help-tags entry was added in formatted form */ - fputs((char *)s, fd_tags); - else { - fprintf(fd_tags, "%s\t/*", s); - for (p1 = s; *p1 != '\t'; ++p1) { - /* insert backslash before '\\' and '/' */ - if (*p1 == '\\' || *p1 == '/') - putc('\\', fd_tags); - putc(*p1, fd_tags); - } - fprintf(fd_tags, "*\n"); - } - } - } - if (mix) - got_int = FALSE; /* continue with other languages */ - - for (i = 0; i < ga.ga_len; ++i) - free(((char_u **)ga.ga_data)[i]); - ga_clear(&ga); - fclose(fd_tags); /* there is no check for an error... */ -} - -/* - * Struct to hold the sign properties. - */ -typedef struct sign sign_T; - -struct sign -{ - sign_T *sn_next; /* next sign in list */ - int sn_typenr; /* type number of sign */ - char_u *sn_name; /* name of sign */ - char_u *sn_icon; /* name of pixmap */ - char_u *sn_text; /* text used instead of pixmap */ - int sn_line_hl; /* highlight ID for line */ - int sn_text_hl; /* highlight ID for text */ -}; - -static sign_T *first_sign = NULL; -static int next_sign_typenr = 1; - -static int sign_cmd_idx (char_u *begin_cmd, char_u *end_cmd); -static void sign_list_defined (sign_T *sp); -static void sign_undefine (sign_T *sp, sign_T *sp_prev); - -static char *cmds[] = { - "define", -#define SIGNCMD_DEFINE 0 - "undefine", -#define SIGNCMD_UNDEFINE 1 - "list", -#define SIGNCMD_LIST 2 - "place", -#define SIGNCMD_PLACE 3 - "unplace", -#define SIGNCMD_UNPLACE 4 - "jump", -#define SIGNCMD_JUMP 5 - NULL -#define SIGNCMD_LAST 6 -}; - -/* - * Find index of a ":sign" subcmd from its name. - * "*end_cmd" must be writable. - */ -static int sign_cmd_idx( - char_u *begin_cmd, /* begin of sign subcmd */ - char_u *end_cmd /* just after sign subcmd */ - ) -{ - int idx; - char save = *end_cmd; - - *end_cmd = NUL; - for (idx = 0; ; ++idx) { - if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { - break; - } - } - *end_cmd = save; - return idx; -} - -/* - * ":sign" command - */ -void ex_sign(exarg_T *eap) -{ - char_u *arg = eap->arg; - char_u *p; - int idx; - sign_T *sp; - sign_T *sp_prev; - buf_T *buf; - - /* Parse the subcommand. */ - p = skiptowhite(arg); - idx = sign_cmd_idx(arg, p); - if (idx == SIGNCMD_LAST) - { - EMSG2(_("E160: Unknown sign command: %s"), arg); - return; - } - arg = skipwhite(p); - - if (idx <= SIGNCMD_LIST) - { - /* - * Define, undefine or list signs. - */ - if (idx == SIGNCMD_LIST && *arg == NUL) - { - /* ":sign list": list all defined signs */ - for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next) - sign_list_defined(sp); - } - else if (*arg == NUL) - EMSG(_("E156: Missing sign name")); - else - { - /* Isolate the sign name. If it's a number skip leading zeroes, - * so that "099" and "99" are the same sign. But keep "0". */ - p = skiptowhite(arg); - if (*p != NUL) - *p++ = NUL; - while (arg[0] == '0' && arg[1] != NUL) - ++arg; - - sp_prev = NULL; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - { - if (STRCMP(sp->sn_name, arg) == 0) - break; - sp_prev = sp; - } - if (idx == SIGNCMD_DEFINE) - { - /* ":sign define {name} ...": define a sign */ - if (sp == NULL) - { - sign_T *lp; - int start = next_sign_typenr; - - /* Allocate a new sign. */ - sp = xcalloc(1, sizeof(sign_T)); - - /* Check that next_sign_typenr is not already being used. - * This only happens after wrapping around. Hopefully - * another one got deleted and we can use its number. */ - for (lp = first_sign; lp != NULL; ) - { - if (lp->sn_typenr == next_sign_typenr) - { - ++next_sign_typenr; - if (next_sign_typenr == MAX_TYPENR) - next_sign_typenr = 1; - if (next_sign_typenr == start) - { - free(sp); - EMSG(_("E612: Too many signs defined")); - return; - } - lp = first_sign; /* start all over */ - continue; - } - lp = lp->sn_next; - } - - sp->sn_typenr = next_sign_typenr; - if (++next_sign_typenr == MAX_TYPENR) - next_sign_typenr = 1; /* wrap around */ - - sp->sn_name = vim_strsave(arg); - if (sp->sn_name == NULL) /* out of memory */ - { - free(sp); - return; - } - - /* add the new sign to the list of signs */ - if (sp_prev == NULL) - first_sign = sp; - else - sp_prev->sn_next = sp; - } - - /* set values for a defined sign. */ - for (;;) - { - arg = skipwhite(p); - if (*arg == NUL) - break; - p = skiptowhite_esc(arg); - if (STRNCMP(arg, "icon=", 5) == 0) - { - arg += 5; - free(sp->sn_icon); - sp->sn_icon = vim_strnsave(arg, (int)(p - arg)); - backslash_halve(sp->sn_icon); - } - else if (STRNCMP(arg, "text=", 5) == 0) - { - char_u *s; - int cells; - int len; - - arg += 5; - - /* Count cells and check for non-printable chars */ - if (has_mbyte) - { - cells = 0; - for (s = arg; s < p; s += (*mb_ptr2len)(s)) - { - if (!vim_isprintc((*mb_ptr2char)(s))) - break; - cells += (*mb_ptr2cells)(s); - } - } - else - - { - for (s = arg; s < p; ++s) - if (!vim_isprintc(*s)) - break; - cells = (int)(s - arg); - } - /* Currently must be one or two display cells */ - if (s != p || cells < 1 || cells > 2) - { - *p = NUL; - EMSG2(_("E239: Invalid sign text: %s"), arg); - return; - } - - free(sp->sn_text); - /* Allocate one byte more if we need to pad up - * with a space. */ - len = (int)(p - arg + ((cells == 1) ? 1 : 0)); - sp->sn_text = vim_strnsave(arg, len); - - if (sp->sn_text != NULL && cells == 1) - STRCPY(sp->sn_text + len - 1, " "); - } - else if (STRNCMP(arg, "linehl=", 7) == 0) - { - arg += 7; - sp->sn_line_hl = syn_check_group(arg, (int)(p - arg)); - } - else if (STRNCMP(arg, "texthl=", 7) == 0) - { - arg += 7; - sp->sn_text_hl = syn_check_group(arg, (int)(p - arg)); - } - else - { - EMSG2(_(e_invarg2), arg); - return; - } - } - } - else if (sp == NULL) - EMSG2(_("E155: Unknown sign: %s"), arg); - else if (idx == SIGNCMD_LIST) - /* ":sign list {name}" */ - sign_list_defined(sp); - else - /* ":sign undefine {name}" */ - sign_undefine(sp, sp_prev); - } - } - else - { - int id = -1; - linenr_T lnum = -1; - char_u *sign_name = NULL; - char_u *arg1; - - if (*arg == NUL) - { - if (idx == SIGNCMD_PLACE) - { - /* ":sign place": list placed signs in all buffers */ - sign_list_placed(NULL); - } - else if (idx == SIGNCMD_UNPLACE) - { - /* ":sign unplace": remove placed sign at cursor */ - id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum); - if (id > 0) - { - buf_delsign(curwin->w_buffer, id); - update_debug_sign(curwin->w_buffer, curwin->w_cursor.lnum); - } - else - EMSG(_("E159: Missing sign number")); - } - else - EMSG(_(e_argreq)); - return; - } - - if (idx == SIGNCMD_UNPLACE && arg[0] == '*' && arg[1] == NUL) - { - /* ":sign unplace *": remove all placed signs */ - buf_delete_all_signs(); - return; - } - - /* first arg could be placed sign id */ - arg1 = arg; - if (VIM_ISDIGIT(*arg)) - { - id = getdigits(&arg); - if (!vim_iswhite(*arg) && *arg != NUL) - { - id = -1; - arg = arg1; - } - else - { - arg = skipwhite(arg); - if (idx == SIGNCMD_UNPLACE && *arg == NUL) - { - /* ":sign unplace {id}": remove placed sign by number */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if ((lnum = buf_delsign(buf, id)) != 0) - update_debug_sign(buf, lnum); - return; - } - } - } - - /* - * Check for line={lnum} name={name} and file={fname} or buffer={nr}. - * Leave "arg" pointing to {fname}. - */ - for (;;) - { - if (STRNCMP(arg, "line=", 5) == 0) - { - arg += 5; - lnum = atoi((char *)arg); - arg = skiptowhite(arg); - } - else if (STRNCMP(arg, "*", 1) == 0 && idx == SIGNCMD_UNPLACE) - { - if (id != -1) - { - EMSG(_(e_invarg)); - return; - } - id = -2; - arg = skiptowhite(arg + 1); - } - else if (STRNCMP(arg, "name=", 5) == 0) - { - arg += 5; - sign_name = arg; - arg = skiptowhite(arg); - if (*arg != NUL) - *arg++ = NUL; - while (sign_name[0] == '0' && sign_name[1] != NUL) - ++sign_name; - } - else if (STRNCMP(arg, "file=", 5) == 0) - { - arg += 5; - buf = buflist_findname(arg); - break; - } - else if (STRNCMP(arg, "buffer=", 7) == 0) - { - arg += 7; - buf = buflist_findnr((int)getdigits(&arg)); - if (*skipwhite(arg) != NUL) - EMSG(_(e_trailing)); - break; - } - else - { - EMSG(_(e_invarg)); - return; - } - arg = skipwhite(arg); - } - - if (buf == NULL) - { - EMSG2(_("E158: Invalid buffer name: %s"), arg); - } - else if (id <= 0 && !(idx == SIGNCMD_UNPLACE && id == -2)) - { - if (lnum >= 0 || sign_name != NULL) - EMSG(_(e_invarg)); - else - /* ":sign place file={fname}": list placed signs in one file */ - sign_list_placed(buf); - } - else if (idx == SIGNCMD_JUMP) - { - /* ":sign jump {id} file={fname}" */ - if (lnum >= 0 || sign_name != NULL) - EMSG(_(e_invarg)); - else if ((lnum = buf_findsign(buf, id)) > 0) - { /* goto a sign ... */ - if (buf_jump_open_win(buf) != NULL) - { /* ... in a current window */ - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_WHITE); - } - else - { /* ... not currently in a window */ - char_u *cmd; - - cmd = xmalloc(STRLEN(buf->b_fname) + 25); - sprintf((char *)cmd, "e +%" PRId64 " %s", - (int64_t)lnum, buf->b_fname); - do_cmdline_cmd(cmd); - free(cmd); - } - - foldOpenCursor(); - } - else - EMSGN(_("E157: Invalid sign ID: %" PRId64), id); - } - else if (idx == SIGNCMD_UNPLACE) - { - if (lnum >= 0 || sign_name != NULL) - EMSG(_(e_invarg)); - else if (id == -2) - { - /* ":sign unplace * file={fname}" */ - redraw_buf_later(buf, NOT_VALID); - buf_delete_signs(buf); - } - else - { - /* ":sign unplace {id} file={fname}" */ - lnum = buf_delsign(buf, id); - update_debug_sign(buf, lnum); - } - } - /* idx == SIGNCMD_PLACE */ - else if (sign_name != NULL) - { - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (STRCMP(sp->sn_name, sign_name) == 0) - break; - if (sp == NULL) - { - EMSG2(_("E155: Unknown sign: %s"), sign_name); - return; - } - if (lnum > 0) - /* ":sign place {id} line={lnum} name={name} file={fname}": - * place a sign */ - buf_addsign(buf, id, lnum, sp->sn_typenr); - else - /* ":sign place {id} file={fname}": change sign type */ - lnum = buf_change_sign_type(buf, id, sp->sn_typenr); - update_debug_sign(buf, lnum); - } - else - EMSG(_(e_invarg)); - } -} - -/* - * List one sign. - */ - static void -sign_list_defined(sp) - sign_T *sp; -{ - char_u *p; - - smsg((char_u *)"sign %s", sp->sn_name); - if (sp->sn_icon != NULL) - { - MSG_PUTS(" icon="); - msg_outtrans(sp->sn_icon); - MSG_PUTS(_(" (not supported)")); - } - if (sp->sn_text != NULL) - { - MSG_PUTS(" text="); - msg_outtrans(sp->sn_text); - } - if (sp->sn_line_hl > 0) - { - MSG_PUTS(" linehl="); - p = get_highlight_name(NULL, sp->sn_line_hl - 1); - if (p == NULL) - MSG_PUTS("NONE"); - else - msg_puts(p); - } - if (sp->sn_text_hl > 0) - { - MSG_PUTS(" texthl="); - p = get_highlight_name(NULL, sp->sn_text_hl - 1); - if (p == NULL) - MSG_PUTS("NONE"); - else - msg_puts(p); - } -} - -/* - * Undefine a sign and free its memory. - */ - static void -sign_undefine(sp, sp_prev) - sign_T *sp; - sign_T *sp_prev; -{ - free(sp->sn_name); - free(sp->sn_icon); - free(sp->sn_text); - if (sp_prev == NULL) - first_sign = sp->sn_next; - else - sp_prev->sn_next = sp->sn_next; - free(sp); -} - -/* - * Get highlighting attribute for sign "typenr". - * If "line" is TRUE: line highl, if FALSE: text highl. - */ - int -sign_get_attr(typenr, line) - int typenr; - int line; -{ - sign_T *sp; - - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_typenr == typenr) - { - if (line) - { - if (sp->sn_line_hl > 0) - return syn_id2attr(sp->sn_line_hl); - } - else - { - if (sp->sn_text_hl > 0) - return syn_id2attr(sp->sn_text_hl); - } - break; - } - return 0; -} - -/* - * Get text mark for sign "typenr". - * Returns NULL if there isn't one. - */ - char_u * -sign_get_text(typenr) - int typenr; -{ - sign_T *sp; - - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_typenr == typenr) - return sp->sn_text; - return NULL; -} - - -/* - * Get the name of a sign by its typenr. - */ - char_u * -sign_typenr2name(typenr) - int typenr; -{ - sign_T *sp; - - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_typenr == typenr) - return sp->sn_name; - return (char_u *)_("[Deleted]"); -} - -#if defined(EXITFREE) || defined(PROTO) -/* - * Undefine/free all signs. - */ - void -free_signs() -{ - while (first_sign != NULL) - sign_undefine(first_sign, NULL); -} -#endif - -static enum -{ - EXP_SUBCMD, /* expand :sign sub-commands */ - EXP_DEFINE, /* expand :sign define {name} args */ - EXP_PLACE, /* expand :sign place {id} args */ - EXP_UNPLACE, /* expand :sign unplace" */ - EXP_SIGN_NAMES /* expand with name of placed signs */ -} expand_what; - -/* - * Function given to ExpandGeneric() to obtain the sign command - * expansion. - */ -char_u * get_sign_name(expand_T *xp, int idx) -{ - sign_T *sp; - int current_idx; - - switch (expand_what) - { - case EXP_SUBCMD: - return (char_u *)cmds[idx]; - case EXP_DEFINE: - { - char *define_arg[] = - { - "icon=", "linehl=", "text=", "texthl=", NULL - }; - return (char_u *)define_arg[idx]; - } - case EXP_PLACE: - { - char *place_arg[] = - { - "line=", "name=", "file=", "buffer=", NULL - }; - return (char_u *)place_arg[idx]; - } - case EXP_UNPLACE: - { - char *unplace_arg[] = { "file=", "buffer=", NULL }; - return (char_u *)unplace_arg[idx]; - } - case EXP_SIGN_NAMES: - /* Complete with name of signs already defined */ - current_idx = 0; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (current_idx++ == idx) - return sp->sn_name; - return NULL; - default: - return NULL; - } -} - -/* - * Handle command line completion for :sign command. - */ - void -set_context_in_sign_cmd(xp, arg) - expand_T *xp; - char_u *arg; -{ - char_u *p; - char_u *end_subcmd; - char_u *last; - int cmd_idx; - char_u *begin_subcmd_args; - - /* Default: expand subcommands. */ - xp->xp_context = EXPAND_SIGN; - expand_what = EXP_SUBCMD; - xp->xp_pattern = arg; - - end_subcmd = skiptowhite(arg); - if (*end_subcmd == NUL) - /* expand subcmd name - * :sign {subcmd}*/ - return; - - cmd_idx = sign_cmd_idx(arg, end_subcmd); - - /* :sign {subcmd} {subcmd_args} - * | - * begin_subcmd_args */ - begin_subcmd_args = skipwhite(end_subcmd); - p = skiptowhite(begin_subcmd_args); - if (*p == NUL) - { - /* - * Expand first argument of subcmd when possible. - * For ":jump {id}" and ":unplace {id}", we could - * possibly expand the ids of all signs already placed. - */ - xp->xp_pattern = begin_subcmd_args; - switch (cmd_idx) - { - case SIGNCMD_LIST: - case SIGNCMD_UNDEFINE: - /* :sign list - * :sign undefine */ - expand_what = EXP_SIGN_NAMES; - break; - default: - xp->xp_context = EXPAND_NOTHING; - } - return; - } - - /* expand last argument of subcmd */ - - /* :sign define {name} {args}... - * | - * p */ - - /* Loop until reaching last argument. */ - do - { - p = skipwhite(p); - last = p; - p = skiptowhite(p); - } while (*p != NUL); - - p = vim_strchr(last, '='); - - /* :sign define {name} {args}... {last}= - * | | - * last p */ - if (p == NUL) - { - /* Expand last argument name (before equal sign). */ - xp->xp_pattern = last; - switch (cmd_idx) - { - case SIGNCMD_DEFINE: - expand_what = EXP_DEFINE; - break; - case SIGNCMD_PLACE: - expand_what = EXP_PLACE; - break; - case SIGNCMD_JUMP: - case SIGNCMD_UNPLACE: - expand_what = EXP_UNPLACE; - break; - default: - xp->xp_context = EXPAND_NOTHING; - } - } - else - { - /* Expand last argument value (after equal sign). */ - xp->xp_pattern = p + 1; - switch (cmd_idx) - { - case SIGNCMD_DEFINE: - if (STRNCMP(last, "texthl", p - last) == 0 || - STRNCMP(last, "linehl", p - last) == 0) - xp->xp_context = EXPAND_HIGHLIGHT; - else if (STRNCMP(last, "icon", p - last) == 0) - xp->xp_context = EXPAND_FILES; - else - xp->xp_context = EXPAND_NOTHING; - break; - case SIGNCMD_PLACE: - if (STRNCMP(last, "name", p - last) == 0) - expand_what = EXP_SIGN_NAMES; - else - xp->xp_context = EXPAND_NOTHING; - break; - default: - xp->xp_context = EXPAND_NOTHING; - } - } -} - diff --git a/src/ex_cmds.h b/src/ex_cmds.h deleted file mode 100644 index f2cee3d7a0..0000000000 --- a/src/ex_cmds.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef NEOVIM_EX_CMDS_H -#define NEOVIM_EX_CMDS_H -/* ex_cmds.c */ - -void do_ascii(exarg_T *eap); -void ex_align(exarg_T *eap); -void ex_sort(exarg_T *eap); -void ex_retab(exarg_T *eap); -int do_move(linenr_T line1, linenr_T line2, linenr_T dest); -void ex_copy(linenr_T line1, linenr_T line2, linenr_T n); -void free_prev_shellcmd(void); -void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, - int do_out); -void do_shell(char_u *cmd, int flags); -char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp); -void append_redir(char_u *buf, int buflen, char_u *opt, char_u *fname); -int viminfo_error(char *errnum, char *message, char_u *line); -int read_viminfo(char_u *file, int flags); -void write_viminfo(char_u *file, int forceit); -int viminfo_readline(vir_T *virp); -char_u *viminfo_readstring(vir_T *virp, int off, int convert); -void viminfo_writestring(FILE *fd, char_u *p); -void do_fixdel(exarg_T *eap); -void print_line_no_prefix(linenr_T lnum, int use_number, int list); -void print_line(linenr_T lnum, int use_number, int list); -int rename_buffer(char_u *new_fname); -void ex_file(exarg_T *eap); -void ex_update(exarg_T *eap); -void ex_write(exarg_T *eap); -int do_write(exarg_T *eap); -int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u * - ffname, - int other); -void ex_wnext(exarg_T *eap); -void do_wqall(exarg_T *eap); -int not_writing(void); -int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, - linenr_T lnum, - int forceit); -int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, - linenr_T newlnum, int flags, - win_T *oldwin); -void ex_append(exarg_T *eap); -void ex_change(exarg_T *eap); -void ex_z(exarg_T *eap); -int check_restricted(void); -int check_secure(void); -void do_sub(exarg_T *eap); -int do_sub_msg(int count_only); -void ex_global(exarg_T *eap); -void global_exe(char_u *cmd); -int read_viminfo_sub_string(vir_T *virp, int force); -void write_viminfo_sub_string(FILE *fp); -void free_old_sub(void); -int prepare_tagpreview(int undo_sync); -void ex_help(exarg_T *eap); -char_u *check_help_lang(char_u *arg); -int help_heuristic(char_u *matched_string, int offset, int wrong_case); -int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, - int keep_lang); -void fix_help_buffer(void); -void ex_exusage(exarg_T *eap); -void ex_viusage(exarg_T *eap); -void ex_helptags(exarg_T *eap); -void ex_sign(exarg_T *eap); -int sign_get_attr(int typenr, int line); -char_u *sign_get_text(int typenr); -void *sign_get_image(int typenr); -char_u *sign_typenr2name(int typenr); -void free_signs(void); -char_u *get_sign_name(expand_T *xp, int idx); -void set_context_in_sign_cmd(expand_T *xp, char_u *arg); -void ex_drop(exarg_T *eap); - -#endif /* NEOVIM_EX_CMDS_H */ diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c deleted file mode 100644 index 0837e5386e..0000000000 --- a/src/ex_cmds2.c +++ /dev/null @@ -1,3541 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * ex_cmds2.c: some more functions for command line commands - */ - -#include - -#include "vim.h" -#include "version_defs.h" -#include "ex_cmds2.h" -#include "buffer.h" -#include "charset.h" -#include "eval.h" -#include "ex_cmds.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "ex_getln.h" -#include "fileio.h" -#include "getchar.h" -#include "mark.h" -#include "mbyte.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "garray.h" -#include "memory.h" -#include "move.h" -#include "normal.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "term.h" -#include "undo.h" -#include "window.h" -#include "os/os.h" -#include "os/shell.h" - -static void cmd_source(char_u *fname, exarg_T *eap); - -/* Growarray to store info about already sourced scripts. - * Also store the dev/ino, so that we don't have to stat() each - * script when going through the list. */ -typedef struct scriptitem_S { - char_u *sn_name; - int sn_dev_valid; - uint64_t sn_dev; - uint64_t sn_ino; - int sn_prof_on; /* TRUE when script is/was profiled */ - int sn_pr_force; /* forceit: profile functions in this script */ - proftime_T sn_pr_child; /* time set when going into first child */ - int sn_pr_nest; /* nesting for sn_pr_child */ - /* profiling the script as a whole */ - int sn_pr_count; /* nr of times sourced */ - proftime_T sn_pr_total; /* time spent in script + children */ - proftime_T sn_pr_self; /* time spent in script itself */ - proftime_T sn_pr_start; /* time at script start */ - proftime_T sn_pr_children; /* time in children after script start */ - /* profiling the script per line */ - garray_T sn_prl_ga; /* things stored for every line */ - proftime_T sn_prl_start; /* start time for current line */ - proftime_T sn_prl_children; /* time spent in children for this line */ - proftime_T sn_prl_wait; /* wait start time for current line */ - int sn_prl_idx; /* index of line being timed; -1 if none */ - int sn_prl_execed; /* line being timed was executed */ -} scriptitem_T; - -static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; -#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1]) - -/* Struct used in sn_prl_ga for every line of a script. */ -typedef struct sn_prl_S { - int snp_count; /* nr of times line was executed */ - proftime_T sn_prl_total; /* time spent in a line + children */ - proftime_T sn_prl_self; /* time spent in a line itself */ -} sn_prl_T; - -# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) - -static int debug_greedy = FALSE; /* batch mode debugging: don't save - and restore typeahead. */ - -/* - * do_debug(): Debug mode. - * Repeatedly get Ex commands, until told to continue normal execution. - */ -void do_debug(char_u *cmd) -{ - int save_msg_scroll = msg_scroll; - int save_State = State; - int save_did_emsg = did_emsg; - int save_cmd_silent = cmd_silent; - int save_msg_silent = msg_silent; - int save_emsg_silent = emsg_silent; - int save_redir_off = redir_off; - tasave_T typeaheadbuf; - int typeahead_saved = FALSE; - int save_ignore_script = 0; - int save_ex_normal_busy; - int n; - char_u *cmdline = NULL; - char_u *p; - char *tail = NULL; - static int last_cmd = 0; -#define CMD_CONT 1 -#define CMD_NEXT 2 -#define CMD_STEP 3 -#define CMD_FINISH 4 -#define CMD_QUIT 5 -#define CMD_INTERRUPT 6 - - - /* Make sure we are in raw mode and start termcap mode. Might have side - * effects... */ - settmode(TMODE_RAW); - starttermcap(); - - ++RedrawingDisabled; /* don't redisplay the window */ - ++no_wait_return; /* don't wait for return */ - did_emsg = FALSE; /* don't use error from debugged stuff */ - cmd_silent = FALSE; /* display commands */ - msg_silent = FALSE; /* display messages */ - emsg_silent = FALSE; /* display error messages */ - redir_off = TRUE; /* don't redirect debug commands */ - - State = NORMAL; - - if (!debug_did_msg) - MSG(_("Entering Debug mode. Type \"cont\" to continue.")); - if (sourcing_name != NULL) - msg(sourcing_name); - if (sourcing_lnum != 0) - smsg((char_u *)_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd); - else - smsg((char_u *)_("cmd: %s"), cmd); - - /* - * Repeat getting a command and executing it. - */ - for (;; ) { - msg_scroll = TRUE; - need_wait_return = FALSE; - /* Save the current typeahead buffer and replace it with an empty one. - * This makes sure we get input from the user here and don't interfere - * with the commands being executed. Reset "ex_normal_busy" to avoid - * the side effects of using ":normal". Save the stuff buffer and make - * it empty. Set ignore_script to avoid reading from script input. */ - save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - if (!debug_greedy) { - save_typeahead(&typeaheadbuf); - typeahead_saved = TRUE; - save_ignore_script = ignore_script; - ignore_script = TRUE; - } - - cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); - - if (typeahead_saved) { - restore_typeahead(&typeaheadbuf); - ignore_script = save_ignore_script; - } - ex_normal_busy = save_ex_normal_busy; - - cmdline_row = msg_row; - if (cmdline != NULL) { - /* If this is a debug command, set "last_cmd". - * If not, reset "last_cmd". - * For a blank line use previous command. */ - p = skipwhite(cmdline); - if (*p != NUL) { - switch (*p) { - case 'c': last_cmd = CMD_CONT; - tail = "ont"; - break; - case 'n': last_cmd = CMD_NEXT; - tail = "ext"; - break; - case 's': last_cmd = CMD_STEP; - tail = "tep"; - break; - case 'f': last_cmd = CMD_FINISH; - tail = "inish"; - break; - case 'q': last_cmd = CMD_QUIT; - tail = "uit"; - break; - case 'i': last_cmd = CMD_INTERRUPT; - tail = "nterrupt"; - break; - default: last_cmd = 0; - } - if (last_cmd != 0) { - /* Check that the tail matches. */ - ++p; - while (*p != NUL && *p == *tail) { - ++p; - ++tail; - } - if (ASCII_ISALPHA(*p)) - last_cmd = 0; - } - } - - if (last_cmd != 0) { - /* Execute debug command: decided where to break next and - * return. */ - switch (last_cmd) { - case CMD_CONT: - debug_break_level = -1; - break; - case CMD_NEXT: - debug_break_level = ex_nesting_level; - break; - case CMD_STEP: - debug_break_level = 9999; - break; - case CMD_FINISH: - debug_break_level = ex_nesting_level - 1; - break; - case CMD_QUIT: - got_int = TRUE; - debug_break_level = -1; - break; - case CMD_INTERRUPT: - got_int = TRUE; - debug_break_level = 9999; - /* Do not repeat ">interrupt" cmd, continue stepping. */ - last_cmd = CMD_STEP; - break; - } - break; - } - - /* don't debug this command */ - n = debug_break_level; - debug_break_level = -1; - (void)do_cmdline(cmdline, getexline, NULL, - DOCMD_VERBOSE|DOCMD_EXCRESET); - debug_break_level = n; - - free(cmdline); - } - lines_left = Rows - 1; - } - free(cmdline); - - --RedrawingDisabled; - --no_wait_return; - redraw_all_later(NOT_VALID); - need_wait_return = FALSE; - msg_scroll = save_msg_scroll; - lines_left = Rows - 1; - State = save_State; - did_emsg = save_did_emsg; - cmd_silent = save_cmd_silent; - msg_silent = save_msg_silent; - emsg_silent = save_emsg_silent; - redir_off = save_redir_off; - - /* Only print the message again when typing a command before coming back - * here. */ - debug_did_msg = TRUE; -} - -/* - * ":debug". - */ -void ex_debug(exarg_T *eap) -{ - int debug_break_level_save = debug_break_level; - - debug_break_level = 9999; - do_cmdline_cmd(eap->arg); - debug_break_level = debug_break_level_save; -} - -static char_u *debug_breakpoint_name = NULL; -static linenr_T debug_breakpoint_lnum; - -/* - * When debugging or a breakpoint is set on a skipped command, no debug prompt - * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and - * debug_skipped_name is then set to the source name in the breakpoint case. If - * a skipped command decides itself that a debug prompt should be displayed, it - * can do so by calling dbg_check_skipped(). - */ -static int debug_skipped; -static char_u *debug_skipped_name; - -/* - * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is - * at or below the break level. But only when the line is actually - * executed. Return TRUE and set breakpoint_name for skipped commands that - * decide to execute something themselves. - * Called from do_one_cmd() before executing a command. - */ -void dbg_check_breakpoint(exarg_T *eap) -{ - char_u *p; - - debug_skipped = FALSE; - if (debug_breakpoint_name != NULL) { - if (!eap->skip) { - /* replace K_SNR with "" */ - if (debug_breakpoint_name[0] == K_SPECIAL - && debug_breakpoint_name[1] == KS_EXTRA - && debug_breakpoint_name[2] == (int)KE_SNR) - p = (char_u *)""; - else - p = (char_u *)""; - smsg((char_u *)_("Breakpoint in \"%s%s\" line %" PRId64), - p, - debug_breakpoint_name + (*p == NUL ? 0 : 3), - (int64_t)debug_breakpoint_lnum); - debug_breakpoint_name = NULL; - do_debug(eap->cmd); - } else { - debug_skipped = TRUE; - debug_skipped_name = debug_breakpoint_name; - debug_breakpoint_name = NULL; - } - } else if (ex_nesting_level <= debug_break_level) { - if (!eap->skip) - do_debug(eap->cmd); - else { - debug_skipped = TRUE; - debug_skipped_name = NULL; - } - } -} - -/* - * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was - * set. Return TRUE when the debug mode is entered this time. - */ -int dbg_check_skipped(exarg_T *eap) -{ - int prev_got_int; - - if (debug_skipped) { - /* - * Save the value of got_int and reset it. We don't want a previous - * interruption cause flushing the input buffer. - */ - prev_got_int = got_int; - got_int = FALSE; - debug_breakpoint_name = debug_skipped_name; - /* eap->skip is TRUE */ - eap->skip = FALSE; - (void)dbg_check_breakpoint(eap); - eap->skip = TRUE; - got_int |= prev_got_int; - return TRUE; - } - return FALSE; -} - -/* - * The list of breakpoints: dbg_breakp. - * This is a grow-array of structs. - */ -struct debuggy { - int dbg_nr; /* breakpoint number */ - int dbg_type; /* DBG_FUNC or DBG_FILE */ - char_u *dbg_name; /* function or file name */ - regprog_T *dbg_prog; /* regexp program */ - linenr_T dbg_lnum; /* line number in function or file */ - int dbg_forceit; /* ! used */ -}; - -static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; -#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx]) -#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx]) -static int last_breakp = 0; /* nr of last defined breakpoint */ - -/* Profiling uses file and func names similar to breakpoints. */ -static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL}; -#define DBG_FUNC 1 -#define DBG_FILE 2 - -static int dbg_parsearg(char_u *arg, garray_T *gap); -static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, - garray_T *gap, - int *fp); - -/* - * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them - * in the entry just after the last one in dbg_breakp. Note that "dbg_name" - * is allocated. - * Returns FAIL for failure. - */ -static int -dbg_parsearg ( - char_u *arg, - garray_T *gap /* either &dbg_breakp or &prof_ga */ -) -{ - char_u *p = arg; - char_u *q; - struct debuggy *bp; - int here = FALSE; - - ga_grow(gap, 1); - - bp = &DEBUGGY(gap, gap->ga_len); - - /* Find "func" or "file". */ - if (STRNCMP(p, "func", 4) == 0) - bp->dbg_type = DBG_FUNC; - else if (STRNCMP(p, "file", 4) == 0) - bp->dbg_type = DBG_FILE; - else if ( - gap != &prof_ga && - STRNCMP(p, "here", 4) == 0) { - if (curbuf->b_ffname == NULL) { - EMSG(_(e_noname)); - return FAIL; - } - bp->dbg_type = DBG_FILE; - here = TRUE; - } else { - EMSG2(_(e_invarg2), p); - return FAIL; - } - p = skipwhite(p + 4); - - /* Find optional line number. */ - if (here) - bp->dbg_lnum = curwin->w_cursor.lnum; - else if ( - gap != &prof_ga && - VIM_ISDIGIT(*p)) { - bp->dbg_lnum = getdigits(&p); - p = skipwhite(p); - } else - bp->dbg_lnum = 0; - - /* Find the function or file name. Don't accept a function name with (). */ - if ((!here && *p == NUL) - || (here && *p != NUL) - || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) { - EMSG2(_(e_invarg2), arg); - return FAIL; - } - - if (bp->dbg_type == DBG_FUNC) - bp->dbg_name = vim_strsave(p); - else if (here) - bp->dbg_name = vim_strsave(curbuf->b_ffname); - else { - /* Expand the file name in the same way as do_source(). This means - * doing it twice, so that $DIR/file gets expanded when $DIR is - * "~/dir". */ - q = expand_env_save(p); - if (q == NULL) - return FAIL; - p = expand_env_save(q); - free(q); - if (p == NULL) - return FAIL; - if (*p != '*') { - bp->dbg_name = fix_fname(p); - free(p); - } else - bp->dbg_name = p; - } - - if (bp->dbg_name == NULL) - return FAIL; - return OK; -} - -/* - * ":breakadd". - */ -void ex_breakadd(exarg_T *eap) -{ - struct debuggy *bp; - char_u *pat; - garray_T *gap; - - gap = &dbg_breakp; - if (eap->cmdidx == CMD_profile) - gap = &prof_ga; - - if (dbg_parsearg(eap->arg, gap) == OK) { - bp = &DEBUGGY(gap, gap->ga_len); - bp->dbg_forceit = eap->forceit; - - pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE); - if (pat != NULL) { - bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - free(pat); - } - if (pat == NULL || bp->dbg_prog == NULL) - free(bp->dbg_name); - else { - if (bp->dbg_lnum == 0) /* default line number is 1 */ - bp->dbg_lnum = 1; - if (eap->cmdidx != CMD_profile) { - DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp; - ++debug_tick; - } - ++gap->ga_len; - } - } -} - -/* - * ":debuggreedy". - */ -void ex_debuggreedy(exarg_T *eap) -{ - if (eap->addr_count == 0 || eap->line2 != 0) - debug_greedy = TRUE; - else - debug_greedy = FALSE; -} - -/* - * ":breakdel" and ":profdel". - */ -void ex_breakdel(exarg_T *eap) -{ - struct debuggy *bp, *bpi; - int nr; - int todel = -1; - int del_all = FALSE; - int i; - linenr_T best_lnum = 0; - garray_T *gap; - - gap = &dbg_breakp; - if (eap->cmdidx == CMD_profdel) { - gap = &prof_ga; - } - - if (vim_isdigit(*eap->arg)) { - /* ":breakdel {nr}" */ - nr = atol((char *)eap->arg); - for (i = 0; i < gap->ga_len; ++i) - if (DEBUGGY(gap, i).dbg_nr == nr) { - todel = i; - break; - } - } else if (*eap->arg == '*') { - todel = 0; - del_all = TRUE; - } else { - /* ":breakdel {func|file} [lnum] {name}" */ - if (dbg_parsearg(eap->arg, gap) == FAIL) - return; - bp = &DEBUGGY(gap, gap->ga_len); - for (i = 0; i < gap->ga_len; ++i) { - bpi = &DEBUGGY(gap, i); - if (bp->dbg_type == bpi->dbg_type - && STRCMP(bp->dbg_name, bpi->dbg_name) == 0 - && (bp->dbg_lnum == bpi->dbg_lnum - || (bp->dbg_lnum == 0 - && (best_lnum == 0 - || bpi->dbg_lnum < best_lnum)))) { - todel = i; - best_lnum = bpi->dbg_lnum; - } - } - free(bp->dbg_name); - } - - if (todel < 0) - EMSG2(_("E161: Breakpoint not found: %s"), eap->arg); - else { - while (gap->ga_len > 0) { - free(DEBUGGY(gap, todel).dbg_name); - vim_regfree(DEBUGGY(gap, todel).dbg_prog); - --gap->ga_len; - if (todel < gap->ga_len) - memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1), - (gap->ga_len - todel) * sizeof(struct debuggy)); - if (eap->cmdidx == CMD_breakdel) - ++debug_tick; - if (!del_all) - break; - } - - /* If all breakpoints were removed clear the array. */ - if (gap->ga_len == 0) - ga_clear(gap); - } -} - -/* - * ":breaklist". - */ -void ex_breaklist(exarg_T *eap) -{ - struct debuggy *bp; - int i; - - if (dbg_breakp.ga_len == 0) - MSG(_("No breakpoints defined")); - else - for (i = 0; i < dbg_breakp.ga_len; ++i) { - bp = &BREAKP(i); - if (bp->dbg_type == DBG_FILE) - home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE); - smsg((char_u *)_("%3d %s %s line %" PRId64), - bp->dbg_nr, - bp->dbg_type == DBG_FUNC ? "func" : "file", - bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff, - (int64_t)bp->dbg_lnum); - } -} - -/* - * Find a breakpoint for a function or sourced file. - * Returns line number at which to break; zero when no matching breakpoint. - */ -linenr_T -dbg_find_breakpoint ( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - linenr_T after /* after this line number */ -) -{ - return debuggy_find(file, fname, after, &dbg_breakp, NULL); -} - -/* - * Return TRUE if profiling is on for a function or sourced file. - */ -int -has_profiling ( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - int *fp /* return: forceit */ -) -{ - return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp) - != (linenr_T)0; -} - -/* - * Common code for dbg_find_breakpoint() and has_profiling(). - */ -static linenr_T -debuggy_find ( - int file, /* TRUE for a file, FALSE for a function */ - char_u *fname, /* file or function name */ - linenr_T after, /* after this line number */ - garray_T *gap, /* either &dbg_breakp or &prof_ga */ - int *fp /* if not NULL: return forceit */ -) -{ - struct debuggy *bp; - int i; - linenr_T lnum = 0; - regmatch_T regmatch; - char_u *name = fname; - int prev_got_int; - - /* Return quickly when there are no breakpoints. */ - if (gap->ga_len == 0) - return (linenr_T)0; - - /* Replace K_SNR in function name with "". */ - if (!file && fname[0] == K_SPECIAL) { - name = xmalloc(STRLEN(fname) + 3); - STRCPY(name, ""); - STRCPY(name + 5, fname + 3); - } - - for (i = 0; i < gap->ga_len; ++i) { - /* Skip entries that are not useful or are for a line that is beyond - * an already found breakpoint. */ - bp = &DEBUGGY(gap, i); - if (((bp->dbg_type == DBG_FILE) == file && ( - gap == &prof_ga || - (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) { - regmatch.regprog = bp->dbg_prog; - regmatch.rm_ic = FALSE; - /* - * Save the value of got_int and reset it. We don't want a - * previous interruption cancel matching, only hitting CTRL-C - * while matching should abort it. - */ - prev_got_int = got_int; - got_int = FALSE; - if (vim_regexec(®match, name, (colnr_T)0)) { - lnum = bp->dbg_lnum; - if (fp != NULL) - *fp = bp->dbg_forceit; - } - got_int |= prev_got_int; - } - } - if (name != fname) - free(name); - - return lnum; -} - -/* - * Called when a breakpoint was encountered. - */ -void dbg_breakpoint(char_u *name, linenr_T lnum) -{ - /* We need to check if this line is actually executed in do_one_cmd() */ - debug_breakpoint_name = name; - debug_breakpoint_lnum = lnum; -} - - -/* - * Store the current time in "tm". - */ -void profile_start(tm) -proftime_T *tm; -{ - gettimeofday(tm, NULL); -} - -/* - * Compute the elapsed time from "tm" till now and store in "tm". - */ -void profile_end(tm) -proftime_T *tm; -{ - proftime_T now; - - gettimeofday(&now, NULL); - tm->tv_usec = now.tv_usec - tm->tv_usec; - tm->tv_sec = now.tv_sec - tm->tv_sec; - if (tm->tv_usec < 0) { - tm->tv_usec += 1000000; - --tm->tv_sec; - } -} - -/* - * Subtract the time "tm2" from "tm". - */ -void profile_sub(tm, tm2) -proftime_T *tm, *tm2; -{ - tm->tv_usec -= tm2->tv_usec; - tm->tv_sec -= tm2->tv_sec; - if (tm->tv_usec < 0) { - tm->tv_usec += 1000000; - --tm->tv_sec; - } -} - -/* - * Return a string that represents the time in "tm". - * Uses a static buffer! - */ -char * profile_msg(tm) -proftime_T *tm; -{ - static char buf[50]; - - sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec); - return buf; -} - -/* - * Put the time "msec" past now in "tm". - */ -void profile_setlimit(msec, tm) -long msec; -proftime_T *tm; -{ - if (msec <= 0) /* no limit */ - profile_zero(tm); - else { - long usec; - - gettimeofday(tm, NULL); - usec = (long)tm->tv_usec + (long)msec * 1000; - tm->tv_usec = usec % 1000000L; - tm->tv_sec += usec / 1000000L; - } -} - -/* - * Return TRUE if the current time is past "tm". - */ -int profile_passed_limit(tm) -proftime_T *tm; -{ - proftime_T now; - - if (tm->tv_sec == 0) /* timer was not set */ - return FALSE; - gettimeofday(&now, NULL); - return now.tv_sec > tm->tv_sec - || (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec); -} - -/* - * Set the time in "tm" to zero. - */ -void profile_zero(tm) -proftime_T *tm; -{ - tm->tv_usec = 0; - tm->tv_sec = 0; -} - - -#include - -/* - * Divide the time "tm" by "count" and store in "tm2". - */ -void profile_divide(tm, count, tm2) -proftime_T *tm; -proftime_T *tm2; -int count; -{ - if (count == 0) - profile_zero(tm2); - else { - double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count; - - tm2->tv_sec = floor(usec / 1000000.0); - tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0)); - } -} - -/* - * Functions for profiling. - */ -static void script_do_profile(scriptitem_T *si); -static void script_dump_profile(FILE *fd); -static proftime_T prof_wait_time; - -/* - * Add the time "tm2" to "tm". - */ -void profile_add(tm, tm2) -proftime_T *tm, *tm2; -{ - tm->tv_usec += tm2->tv_usec; - tm->tv_sec += tm2->tv_sec; - if (tm->tv_usec >= 1000000) { - tm->tv_usec -= 1000000; - ++tm->tv_sec; - } -} - -/* - * Add the "self" time from the total time and the children's time. - */ -void profile_self(self, total, children) -proftime_T *self, *total, *children; -{ - /* Check that the result won't be negative. Can happen with recursive - * calls. */ - if (total->tv_sec < children->tv_sec - || (total->tv_sec == children->tv_sec - && total->tv_usec <= children->tv_usec)) - return; - profile_add(self, total); - profile_sub(self, children); -} - -/* - * Get the current waittime. - */ -void profile_get_wait(tm) -proftime_T *tm; -{ - *tm = prof_wait_time; -} - -/* - * Subtract the passed waittime since "tm" from "tma". - */ -void profile_sub_wait(tm, tma) -proftime_T *tm, *tma; -{ - proftime_T tm3 = prof_wait_time; - - profile_sub(&tm3, tm); - profile_sub(tma, &tm3); -} - -/* - * Return TRUE if "tm1" and "tm2" are equal. - */ -int profile_equal(tm1, tm2) -proftime_T *tm1, *tm2; -{ - return tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec; -} - -/* - * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2" - */ -int profile_cmp(tm1, tm2) -const proftime_T *tm1, *tm2; -{ - if (tm1->tv_sec == tm2->tv_sec) - return tm2->tv_usec - tm1->tv_usec; - return tm2->tv_sec - tm1->tv_sec; -} - -static char_u *profile_fname = NULL; -static proftime_T pause_time; - -/* - * ":profile cmd args" - */ -void ex_profile(exarg_T *eap) -{ - char_u *e; - int len; - - e = skiptowhite(eap->arg); - len = (int)(e - eap->arg); - e = skipwhite(e); - - if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) { - free(profile_fname); - profile_fname = vim_strsave(e); - do_profiling = PROF_YES; - profile_zero(&prof_wait_time); - set_vim_var_nr(VV_PROFILING, 1L); - } else if (do_profiling == PROF_NONE) - EMSG(_("E750: First use \":profile start {fname}\"")); - else if (STRCMP(eap->arg, "pause") == 0) { - if (do_profiling == PROF_YES) - profile_start(&pause_time); - do_profiling = PROF_PAUSED; - } else if (STRCMP(eap->arg, "continue") == 0) { - if (do_profiling == PROF_PAUSED) { - profile_end(&pause_time); - profile_add(&prof_wait_time, &pause_time); - } - do_profiling = PROF_YES; - } else { - /* The rest is similar to ":breakadd". */ - ex_breakadd(eap); - } -} - -/* Command line expansion for :profile. */ -static enum { - PEXP_SUBCMD, /* expand :profile sub-commands */ - PEXP_FUNC /* expand :profile func {funcname} */ -} pexpand_what; - -static char *pexpand_cmds[] = { - "start", -#define PROFCMD_START 0 - "pause", -#define PROFCMD_PAUSE 1 - "continue", -#define PROFCMD_CONTINUE 2 - "func", -#define PROFCMD_FUNC 3 - "file", -#define PROFCMD_FILE 4 - NULL -#define PROFCMD_LAST 5 -}; - -/* - * Function given to ExpandGeneric() to obtain the profile command - * specific expansion. - */ -char_u *get_profile_name(expand_T *xp, int idx) -{ - switch (pexpand_what) { - case PEXP_SUBCMD: - return (char_u *)pexpand_cmds[idx]; - /* case PEXP_FUNC: TODO */ - default: - return NULL; - } -} - -/* - * Handle command line completion for :profile command. - */ -void set_context_in_profile_cmd(expand_T *xp, char_u *arg) -{ - char_u *end_subcmd; - - /* Default: expand subcommands. */ - xp->xp_context = EXPAND_PROFILE; - pexpand_what = PEXP_SUBCMD; - xp->xp_pattern = arg; - - end_subcmd = skiptowhite(arg); - if (*end_subcmd == NUL) - return; - - if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0) { - xp->xp_context = EXPAND_FILES; - xp->xp_pattern = skipwhite(end_subcmd); - return; - } - - /* TODO: expand function names after "func" */ - xp->xp_context = EXPAND_NOTHING; -} - -/* - * Dump the profiling info. - */ -void profile_dump(void) -{ - FILE *fd; - - if (profile_fname != NULL) { - fd = mch_fopen((char *)profile_fname, "w"); - if (fd == NULL) - EMSG2(_(e_notopen), profile_fname); - else { - script_dump_profile(fd); - func_dump_profile(fd); - fclose(fd); - } - } -} - -/* - * Start profiling script "fp". - */ -static void script_do_profile(scriptitem_T *si) -{ - si->sn_pr_count = 0; - profile_zero(&si->sn_pr_total); - profile_zero(&si->sn_pr_self); - - ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100); - si->sn_prl_idx = -1; - si->sn_prof_on = TRUE; - si->sn_pr_nest = 0; -} - -/* - * save time when starting to invoke another script or function. - */ -void script_prof_save(tm) -proftime_T *tm; /* place to store wait time */ -{ - scriptitem_T *si; - - if (current_SID > 0 && current_SID <= script_items.ga_len) { - si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on && si->sn_pr_nest++ == 0) - profile_start(&si->sn_pr_child); - } - profile_get_wait(tm); -} - -/* - * Count time spent in children after invoking another script or function. - */ -void script_prof_restore(tm) -proftime_T *tm; -{ - scriptitem_T *si; - - if (current_SID > 0 && current_SID <= script_items.ga_len) { - si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on && --si->sn_pr_nest == 0) { - profile_end(&si->sn_pr_child); - profile_sub_wait(tm, &si->sn_pr_child); /* don't count wait time */ - profile_add(&si->sn_pr_children, &si->sn_pr_child); - profile_add(&si->sn_prl_children, &si->sn_pr_child); - } - } -} - -static proftime_T inchar_time; - -/* - * Called when starting to wait for the user to type a character. - */ -void prof_inchar_enter(void) -{ - profile_start(&inchar_time); -} - -/* - * Called when finished waiting for the user to type a character. - */ -void prof_inchar_exit(void) -{ - profile_end(&inchar_time); - profile_add(&prof_wait_time, &inchar_time); -} - -/* - * Dump the profiling results for all scripts in file "fd". - */ -static void script_dump_profile(FILE *fd) -{ - int id; - scriptitem_T *si; - int i; - FILE *sfd; - sn_prl_T *pp; - - for (id = 1; id <= script_items.ga_len; ++id) { - si = &SCRIPT_ITEM(id); - if (si->sn_prof_on) { - fprintf(fd, "SCRIPT %s\n", si->sn_name); - if (si->sn_pr_count == 1) - fprintf(fd, "Sourced 1 time\n"); - else - fprintf(fd, "Sourced %d times\n", si->sn_pr_count); - fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total)); - fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self)); - fprintf(fd, "\n"); - fprintf(fd, "count total (s) self (s)\n"); - - sfd = mch_fopen((char *)si->sn_name, "r"); - if (sfd == NULL) - fprintf(fd, "Cannot open file!\n"); - else { - for (i = 0; i < si->sn_prl_ga.ga_len; ++i) { - if (vim_fgets(IObuff, IOSIZE, sfd)) - break; - pp = &PRL_ITEM(si, i); - if (pp->snp_count > 0) { - fprintf(fd, "%5d ", pp->snp_count); - if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self)) - fprintf(fd, " "); - else - fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total)); - fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self)); - } else - fprintf(fd, " "); - fprintf(fd, "%s", IObuff); - } - fclose(sfd); - } - fprintf(fd, "\n"); - } - } -} - -/* - * Return TRUE when a function defined in the current script should be - * profiled. - */ -int prof_def_func(void) -{ - if (current_SID > 0) - return SCRIPT_ITEM(current_SID).sn_pr_force; - return FALSE; -} - -/* - * If 'autowrite' option set, try to write the file. - * Careful: autocommands may make "buf" invalid! - * - * return FAIL for failure, OK otherwise - */ -int autowrite(buf_T *buf, int forceit) -{ - int r; - - if (!(p_aw || p_awa) || !p_write - /* never autowrite a "nofile" or "nowrite" buffer */ - || bt_dontwrite(buf) - || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL) - return FAIL; - r = buf_write_all(buf, forceit); - - /* Writing may succeed but the buffer still changed, e.g., when there is a - * conversion error. We do want to return FAIL then. */ - if (buf_valid(buf) && bufIsChanged(buf)) - r = FAIL; - return r; -} - -/* - * flush all buffers, except the ones that are readonly - */ -void autowrite_all(void) -{ - buf_T *buf; - - if (!(p_aw || p_awa) || !p_write) - return; - for (buf = firstbuf; buf; buf = buf->b_next) - if (bufIsChanged(buf) && !buf->b_p_ro) { - (void)buf_write_all(buf, FALSE); - /* an autocommand may have deleted the buffer */ - if (!buf_valid(buf)) - buf = firstbuf; - } -} - -/* - * Return TRUE if buffer was changed and cannot be abandoned. - * For flags use the CCGD_ values. - */ -int check_changed(buf_T *buf, int flags) -{ - int forceit = (flags & CCGD_FORCEIT); - - if ( !forceit - && bufIsChanged(buf) - && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1) - && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL)) { - if ((p_confirm || cmdmod.confirm) && p_write) { - buf_T *buf2; - int count = 0; - - if (flags & CCGD_ALLBUF) - for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next) - if (bufIsChanged(buf2) - && (buf2->b_ffname != NULL - )) - ++count; - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! It's not changed now. */ - return FALSE; - dialog_changed(buf, count > 1); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! It's not changed now. */ - return FALSE; - return bufIsChanged(buf); - } - if (flags & CCGD_EXCMD) - EMSG(_(e_nowrtmsg)); - else - EMSG(_(e_nowrtmsg_nobang)); - return TRUE; - } - return FALSE; -} - - - -/* - * Ask the user what to do when abandoning a changed buffer. - * Must check 'write' option first! - */ -void -dialog_changed ( - buf_T *buf, - int checkall /* may abandon all changed buffers */ -) -{ - char_u buff[DIALOG_MSG_SIZE]; - int ret; - buf_T *buf2; - exarg_T ea; - - dialog_msg(buff, _("Save changes to \"%s\"?"), - (buf->b_fname != NULL) ? - buf->b_fname : (char_u *)_("Untitled")); - if (checkall) - ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1); - else - ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1); - - /* Init ea pseudo-structure, this is needed for the check_overwrite() - * function. */ - ea.append = ea.forceit = FALSE; - - if (ret == VIM_YES) { - if (buf->b_fname != NULL && check_overwrite(&ea, buf, - buf->b_fname, buf->b_ffname, FALSE) == OK) - /* didn't hit Cancel */ - (void)buf_write_all(buf, FALSE); - } else if (ret == VIM_NO) { - unchanged(buf, TRUE); - } else if (ret == VIM_ALL) { - /* - * Write all modified files that can be written. - * Skip readonly buffers, these need to be confirmed - * individually. - */ - for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next) { - if (bufIsChanged(buf2) - && (buf2->b_ffname != NULL - ) - && !buf2->b_p_ro) { - if (buf2->b_fname != NULL && check_overwrite(&ea, buf2, - buf2->b_fname, buf2->b_ffname, FALSE) == OK) - /* didn't hit Cancel */ - (void)buf_write_all(buf2, FALSE); - /* an autocommand may have deleted the buffer */ - if (!buf_valid(buf2)) - buf2 = firstbuf; - } - } - } else if (ret == VIM_DISCARDALL) { - /* - * mark all buffers as unchanged - */ - for (buf2 = firstbuf; buf2 != NULL; buf2 = buf2->b_next) - unchanged(buf2, TRUE); - } -} - -/* - * Return TRUE if the buffer "buf" can be abandoned, either by making it - * hidden, autowriting it or unloading it. - */ -int can_abandon(buf_T *buf, int forceit) -{ - return P_HID(buf) - || !bufIsChanged(buf) - || buf->b_nwindows > 1 - || autowrite(buf, forceit) == OK - || forceit; -} - -static void add_bufnum(int *bufnrs, int *bufnump, int nr); - -/* - * Add a buffer number to "bufnrs", unless it's already there. - */ -static void add_bufnum(int *bufnrs, int *bufnump, int nr) -{ - int i; - - for (i = 0; i < *bufnump; ++i) - if (bufnrs[i] == nr) - return; - bufnrs[*bufnump] = nr; - *bufnump = *bufnump + 1; -} - -/* - * Return TRUE if any buffer was changed and cannot be abandoned. - * That changed buffer becomes the current buffer. - */ -int -check_changed_any ( - int hidden /* Only check hidden buffers */ -) -{ - int ret = FALSE; - buf_T *buf; - int save; - int i; - int bufnum = 0; - int bufcount = 0; - int *bufnrs; - tabpage_T *tp; - win_T *wp; - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - ++bufcount; - - if (bufcount == 0) - return FALSE; - - bufnrs = xmalloc(sizeof(*bufnrs) * bufcount); - - /* curbuf */ - bufnrs[bufnum++] = curbuf->b_fnum; - /* buf in curtab */ - FOR_ALL_WINDOWS(wp) - if (wp->w_buffer != curbuf) - add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); - - /* buf in other tab */ - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) - if (tp != curtab) - for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) - add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); - /* any other buf */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - add_bufnum(bufnrs, &bufnum, buf->b_fnum); - - for (i = 0; i < bufnum; ++i) { - buf = buflist_findnr(bufnrs[i]); - if (buf == NULL) - continue; - if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf)) { - /* Try auto-writing the buffer. If this fails but the buffer no - * longer exists it's not changed, that's OK. */ - if (check_changed(buf, (p_awa ? CCGD_AW : 0) - | CCGD_MULTWIN - | CCGD_ALLBUF) && buf_valid(buf)) - break; /* didn't save - still changes */ - } - } - - if (i >= bufnum) - goto theend; - - ret = TRUE; - exiting = FALSE; - /* - * When ":confirm" used, don't give an error message. - */ - if (!(p_confirm || cmdmod.confirm)) { - /* There must be a wait_return for this message, do_buffer() - * may cause a redraw. But wait_return() is a no-op when vgetc() - * is busy (Quit used from window menu), then make sure we don't - * cause a scroll up. */ - if (vgetc_busy > 0) { - msg_row = cmdline_row; - msg_col = 0; - msg_didout = FALSE; - } - if (EMSG2(_("E162: No write since last change for buffer \"%s\""), - buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname)) { - save = no_wait_return; - no_wait_return = FALSE; - wait_return(FALSE); - no_wait_return = save; - } - } - - /* Try to find a window that contains the buffer. */ - if (buf != curbuf) - FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_buffer == buf) { - goto_tabpage_win(tp, wp); - /* Paranoia: did autocms wipe out the buffer with changes? */ - if (!buf_valid(buf)) { - goto theend; - } - goto buf_found; - } -buf_found: - - /* Open the changed buffer in the current window. */ - if (buf != curbuf) - set_curbuf(buf, DOBUF_GOTO); - -theend: - free(bufnrs); - return ret; -} - -/* - * return FAIL if there is no file name, OK if there is one - * give error message for FAIL - */ -int check_fname(void) -{ - if (curbuf->b_ffname == NULL) { - EMSG(_(e_noname)); - return FAIL; - } - return OK; -} - -/* - * flush the contents of a buffer, unless it has no file name - * - * return FAIL for failure, OK otherwise - */ -int buf_write_all(buf_T *buf, int forceit) -{ - int retval; - buf_T *old_curbuf = curbuf; - - retval = (buf_write(buf, buf->b_ffname, buf->b_fname, - (linenr_T)1, buf->b_ml.ml_line_count, NULL, - FALSE, forceit, TRUE, FALSE)); - if (curbuf != old_curbuf) { - msg_source(hl_attr(HLF_W)); - MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)")); - } - return retval; -} - -/* - * Code to handle the argument list. - */ - -static char_u *do_one_arg(char_u *str); -static int do_arglist(char_u *str, int what, int after); -static void alist_check_arg_idx(void); -static int editing_arg_idx(win_T *win); -static int alist_add_list(int count, char_u **files, int after); -#define AL_SET 1 -#define AL_ADD 2 -#define AL_DEL 3 - -/* - * Isolate one argument, taking backticks. - * Changes the argument in-place, puts a NUL after it. Backticks remain. - * Return a pointer to the start of the next argument. - */ -static char_u *do_one_arg(char_u *str) -{ - char_u *p; - int inbacktick; - - inbacktick = FALSE; - for (p = str; *str; ++str) { - /* When the backslash is used for escaping the special meaning of a - * character we need to keep it until wildcard expansion. */ - if (rem_backslash(str)) { - *p++ = *str++; - *p++ = *str; - } else { - /* An item ends at a space not in backticks */ - if (!inbacktick && vim_isspace(*str)) - break; - if (*str == '`') - inbacktick ^= TRUE; - *p++ = *str; - } - } - str = skipwhite(str); - *p = NUL; - - return str; -} - -/* - * Separate the arguments in "str" and return a list of pointers in the - * growarray "gap". - */ -void get_arglist(garray_T *gap, char_u *str) -{ - ga_init(gap, (int)sizeof(char_u *), 20); - while (*str != NUL) { - ga_grow(gap, 1); - ((char_u **)gap->ga_data)[gap->ga_len++] = str; - - /* Isolate one argument, change it in-place, put a NUL after it. */ - str = do_one_arg(str); - } -} - -/* - * Parse a list of arguments (file names), expand them and return in - * "fnames[fcountp]". When "wig" is TRUE, removes files matching 'wildignore'. - * Return FAIL or OK. - */ -int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, int wig) -{ - garray_T ga; - int i; - - get_arglist(&ga, str); - - if (wig == TRUE) - i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND); - else - i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND); - - ga_clear(&ga); - return i; -} - - -/* - * "what" == AL_SET: Redefine the argument list to 'str'. - * "what" == AL_ADD: add files in 'str' to the argument list after "after". - * "what" == AL_DEL: remove files in 'str' from the argument list. - * - * Return FAIL for failure, OK otherwise. - */ -static int -do_arglist ( - char_u *str, - int what, - int after /* 0 means before first one */ -) -{ - garray_T new_ga; - int exp_count; - char_u **exp_files; - int i; - char_u *p; - int match; - - /* - * Collect all file name arguments in "new_ga". - */ - get_arglist(&new_ga, str); - - if (what == AL_DEL) { - regmatch_T regmatch; - int didone; - - /* - * Delete the items: use each item as a regexp and find a match in the - * argument list. - */ - regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */ - for (i = 0; i < new_ga.ga_len && !got_int; ++i) { - p = ((char_u **)new_ga.ga_data)[i]; - p = file_pat_to_reg_pat(p, NULL, NULL, FALSE); - if (p == NULL) - break; - regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) { - free(p); - break; - } - - didone = FALSE; - for (match = 0; match < ARGCOUNT; ++match) - if (vim_regexec(®match, alist_name(&ARGLIST[match]), - (colnr_T)0)) { - didone = TRUE; - free(ARGLIST[match].ae_fname); - memmove(ARGLIST + match, ARGLIST + match + 1, - (ARGCOUNT - match - 1) * sizeof(aentry_T)); - --ALIST(curwin)->al_ga.ga_len; - if (curwin->w_arg_idx > match) - --curwin->w_arg_idx; - --match; - } - - vim_regfree(regmatch.regprog); - free(p); - if (!didone) - EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]); - } - ga_clear(&new_ga); - } else { - i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data, - &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND); - ga_clear(&new_ga); - if (i == FAIL) - return FAIL; - if (exp_count == 0) { - EMSG(_(e_nomatch)); - return FAIL; - } - - if (what == AL_ADD) { - (void)alist_add_list(exp_count, exp_files, after); - free(exp_files); - } else /* what == AL_SET */ - alist_set(ALIST(curwin), exp_count, exp_files, FALSE, NULL, 0); - } - - alist_check_arg_idx(); - - return OK; -} - -/* - * Check the validity of the arg_idx for each other window. - */ -static void alist_check_arg_idx(void) -{ - win_T *win; - tabpage_T *tp; - - FOR_ALL_TAB_WINDOWS(tp, win) - if (win->w_alist == curwin->w_alist) - check_arg_idx(win); -} - -/* - * Return TRUE if window "win" is editing the file at the current argument - * index. - */ -static int editing_arg_idx(win_T *win) -{ - return !(win->w_arg_idx >= WARGCOUNT(win) - || (win->w_buffer->b_fnum - != WARGLIST(win)[win->w_arg_idx].ae_fnum - && (win->w_buffer->b_ffname == NULL - || !(path_full_compare( - alist_name(&WARGLIST(win)[win->w_arg_idx]), - win->w_buffer->b_ffname, TRUE) & kEqualFiles)))); -} - -/* - * Check if window "win" is editing the w_arg_idx file in its argument list. - */ -void check_arg_idx(win_T *win) -{ - if (WARGCOUNT(win) > 1 && !editing_arg_idx(win)) { - /* We are not editing the current entry in the argument list. - * Set "arg_had_last" if we are editing the last one. */ - win->w_arg_idx_invalid = TRUE; - if (win->w_arg_idx != WARGCOUNT(win) - 1 - && arg_had_last == FALSE - && ALIST(win) == &global_alist - && GARGCOUNT > 0 - && win->w_arg_idx < GARGCOUNT - && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum - || (win->w_buffer->b_ffname != NULL - && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]), - win->w_buffer->b_ffname, TRUE) & kEqualFiles)))) - arg_had_last = TRUE; - } else { - /* We are editing the current entry in the argument list. - * Set "arg_had_last" if it's also the last one */ - win->w_arg_idx_invalid = FALSE; - if (win->w_arg_idx == WARGCOUNT(win) - 1 - && win->w_alist == &global_alist - ) - arg_had_last = TRUE; - } -} - -/* - * ":args", ":argslocal" and ":argsglobal". - */ -void ex_args(exarg_T *eap) -{ - int i; - - if (eap->cmdidx != CMD_args) { - alist_unlink(ALIST(curwin)); - if (eap->cmdidx == CMD_argglobal) - ALIST(curwin) = &global_alist; - else /* eap->cmdidx == CMD_arglocal */ - alist_new(); - } - - if (!ends_excmd(*eap->arg)) { - /* - * ":args file ..": define new argument list, handle like ":next" - * Also for ":argslocal file .." and ":argsglobal file ..". - */ - ex_next(eap); - } else if (eap->cmdidx == CMD_args) { - /* - * ":args": list arguments. - */ - if (ARGCOUNT > 0) { - /* Overwrite the command, for a short list there is no scrolling - * required and no wait_return(). */ - gotocmdline(TRUE); - for (i = 0; i < ARGCOUNT; ++i) { - if (i == curwin->w_arg_idx) - msg_putchar('['); - msg_outtrans(alist_name(&ARGLIST[i])); - if (i == curwin->w_arg_idx) - msg_putchar(']'); - msg_putchar(' '); - } - } - } else if (eap->cmdidx == CMD_arglocal) { - garray_T *gap = &curwin->w_alist->al_ga; - - /* - * ":argslocal": make a local copy of the global argument list. - */ - ga_grow(gap, GARGCOUNT); - for (i = 0; i < GARGCOUNT; ++i) - if (GARGLIST[i].ae_fname != NULL) { - AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname = - vim_strsave(GARGLIST[i].ae_fname); - AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum = - GARGLIST[i].ae_fnum; - ++gap->ga_len; - } - } -} - -/* - * ":previous", ":sprevious", ":Next" and ":sNext". - */ -void ex_previous(exarg_T *eap) -{ - /* If past the last one already, go to the last one. */ - if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) - do_argfile(eap, ARGCOUNT - 1); - else - do_argfile(eap, curwin->w_arg_idx - (int)eap->line2); -} - -/* - * ":rewind", ":first", ":sfirst" and ":srewind". - */ -void ex_rewind(exarg_T *eap) -{ - do_argfile(eap, 0); -} - -/* - * ":last" and ":slast". - */ -void ex_last(exarg_T *eap) -{ - do_argfile(eap, ARGCOUNT - 1); -} - -/* - * ":argument" and ":sargument". - */ -void ex_argument(exarg_T *eap) -{ - int i; - - if (eap->addr_count > 0) - i = eap->line2 - 1; - else - i = curwin->w_arg_idx; - do_argfile(eap, i); -} - -/* - * Edit file "argn" of the argument lists. - */ -void do_argfile(exarg_T *eap, int argn) -{ - int other; - char_u *p; - int old_arg_idx = curwin->w_arg_idx; - - if (argn < 0 || argn >= ARGCOUNT) { - if (ARGCOUNT <= 1) - EMSG(_("E163: There is only one file to edit")); - else if (argn < 0) - EMSG(_("E164: Cannot go before first file")); - else - EMSG(_("E165: Cannot go beyond last file")); - } else { - setpcmark(); - - /* split window or create new tab page first */ - if (*eap->cmd == 's' || cmdmod.tab != 0) { - if (win_split(0, 0) == FAIL) - return; - RESET_BINDING(curwin); - } else { - /* - * if 'hidden' set, only check for changed file when re-editing - * the same buffer - */ - other = TRUE; - if (P_HID(curbuf)) { - p = fix_fname(alist_name(&ARGLIST[argn])); - other = otherfile(p); - free(p); - } - if ((!P_HID(curbuf) || !other) - && check_changed(curbuf, CCGD_AW - | (other ? 0 : CCGD_MULTWIN) - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) - return; - } - - curwin->w_arg_idx = argn; - if (argn == ARGCOUNT - 1 - && curwin->w_alist == &global_alist - ) - arg_had_last = TRUE; - - /* Edit the file; always use the last known line number. - * When it fails (e.g. Abort for already edited file) restore the - * argument index. */ - if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL, - eap, ECMD_LAST, - (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) - + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) - curwin->w_arg_idx = old_arg_idx; - /* like Vi: set the mark where the cursor is in the file. */ - else if (eap->cmdidx != CMD_argdo) - setmark('\''); - } -} - -/* - * ":next", and commands that behave like it. - */ -void ex_next(exarg_T *eap) -{ - int i; - - /* - * check for changed buffer now, if this fails the argument list is not - * redefined. - */ - if ( P_HID(curbuf) - || eap->cmdidx == CMD_snext - || !check_changed(curbuf, CCGD_AW - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) { - if (*eap->arg != NUL) { /* redefine file list */ - if (do_arglist(eap->arg, AL_SET, 0) == FAIL) - return; - i = 0; - } else - i = curwin->w_arg_idx + (int)eap->line2; - do_argfile(eap, i); - } -} - -/* - * ":argedit" - */ -void ex_argedit(exarg_T *eap) -{ - int fnum; - int i; - char_u *s; - - /* Add the argument to the buffer list and get the buffer number. */ - fnum = buflist_add(eap->arg, BLN_LISTED); - - /* Check if this argument is already in the argument list. */ - for (i = 0; i < ARGCOUNT; ++i) - if (ARGLIST[i].ae_fnum == fnum) - break; - if (i == ARGCOUNT) { - /* Can't find it, add it to the argument list. */ - s = vim_strsave(eap->arg); - if (s == NULL) - return; - i = alist_add_list(1, &s, - eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1); - curwin->w_arg_idx = i; - } - - alist_check_arg_idx(); - - /* Edit the argument. */ - do_argfile(eap, i); -} - -/* - * ":argadd" - */ -void ex_argadd(exarg_T *eap) -{ - do_arglist(eap->arg, AL_ADD, - eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1); - maketitle(); -} - -/* - * ":argdelete" - */ -void ex_argdelete(exarg_T *eap) -{ - int i; - int n; - - if (eap->addr_count > 0) { - /* ":1,4argdel": Delete all arguments in the range. */ - if (eap->line2 > ARGCOUNT) - eap->line2 = ARGCOUNT; - n = eap->line2 - eap->line1 + 1; - if (*eap->arg != NUL || n <= 0) - EMSG(_(e_invarg)); - else { - for (i = eap->line1; i <= eap->line2; ++i) - free(ARGLIST[i - 1].ae_fname); - memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2, - (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T))); - ALIST(curwin)->al_ga.ga_len -= n; - if (curwin->w_arg_idx >= eap->line2) - curwin->w_arg_idx -= n; - else if (curwin->w_arg_idx > eap->line1) - curwin->w_arg_idx = eap->line1; - } - } else if (*eap->arg == NUL) - EMSG(_(e_argreq)); - else - do_arglist(eap->arg, AL_DEL, 0); - maketitle(); -} - -/* - * ":argdo", ":windo", ":bufdo", ":tabdo" - */ -void ex_listdo(exarg_T *eap) -{ - int i; - win_T *wp; - tabpage_T *tp; - buf_T *buf; - int next_fnum = 0; - char_u *save_ei = NULL; - char_u *p_shm_save; - - - if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) - /* Don't do syntax HL autocommands. Skipping the syntax file is a - * great speed improvement. */ - save_ei = au_event_disable(",Syntax"); - - if (eap->cmdidx == CMD_windo - || eap->cmdidx == CMD_tabdo - || P_HID(curbuf) - || !check_changed(curbuf, CCGD_AW - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) { - /* start at the first argument/window/buffer */ - i = 0; - wp = firstwin; - tp = first_tabpage; - /* set pcmark now */ - if (eap->cmdidx == CMD_bufdo) - goto_buffer(eap, DOBUF_FIRST, FORWARD, 0); - else - setpcmark(); - listcmd_busy = TRUE; /* avoids setting pcmark below */ - - while (!got_int) { - if (eap->cmdidx == CMD_argdo) { - /* go to argument "i" */ - if (i == ARGCOUNT) - break; - /* Don't call do_argfile() when already there, it will try - * reloading the file. */ - if (curwin->w_arg_idx != i || !editing_arg_idx(curwin)) { - /* Clear 'shm' to avoid that the file message overwrites - * any output from the command. */ - p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); - do_argfile(eap, i); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); - free(p_shm_save); - } - if (curwin->w_arg_idx != i) - break; - ++i; - } else if (eap->cmdidx == CMD_windo) { - /* go to window "wp" */ - if (!win_valid(wp)) - break; - win_goto(wp); - if (curwin != wp) - break; /* something must be wrong */ - wp = curwin->w_next; - } else if (eap->cmdidx == CMD_tabdo) { - /* go to window "tp" */ - if (!valid_tabpage(tp)) - break; - goto_tabpage_tp(tp, TRUE, TRUE); - tp = tp->tp_next; - } else if (eap->cmdidx == CMD_bufdo) { - /* Remember the number of the next listed buffer, in case - * ":bwipe" is used or autocommands do something strange. */ - next_fnum = -1; - for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next) - if (buf->b_p_bl) { - next_fnum = buf->b_fnum; - break; - } - } - - /* execute the command */ - do_cmdline(eap->arg, eap->getline, eap->cookie, - DOCMD_VERBOSE + DOCMD_NOWAIT); - - if (eap->cmdidx == CMD_bufdo) { - /* Done? */ - if (next_fnum < 0) - break; - /* Check if the buffer still exists. */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_fnum == next_fnum) - break; - if (buf == NULL) - break; - - /* Go to the next buffer. Clear 'shm' to avoid that the file - * message overwrites any output from the command. */ - p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); - goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); - free(p_shm_save); - - /* If autocommands took us elsewhere, quit here */ - if (curbuf->b_fnum != next_fnum) - break; - } - - if (eap->cmdidx == CMD_windo) { - validate_cursor(); /* cursor may have moved */ - /* required when 'scrollbind' has been set */ - if (curwin->w_p_scb) - do_check_scrollbind(TRUE); - } - } - listcmd_busy = FALSE; - } - - if (save_ei != NULL) { - au_event_restore(save_ei); - apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, - curbuf->b_fname, TRUE, curbuf); - } -} - -/* - * Add files[count] to the arglist of the current window after arg "after". - * The file names in files[count] must have been allocated and are taken over. - * Files[] itself is not taken over. - * Returns index of first added argument. - */ -static int -alist_add_list ( - int count, - char_u **files, - int after /* where to add: 0 = before first one */ -) -{ - int i; - - ga_grow(&ALIST(curwin)->al_ga, count); - { - if (after < 0) - after = 0; - if (after > ARGCOUNT) - after = ARGCOUNT; - if (after < ARGCOUNT) - memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), - (ARGCOUNT - after) * sizeof(aentry_T)); - for (i = 0; i < count; ++i) { - ARGLIST[after + i].ae_fname = files[i]; - ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED); - } - ALIST(curwin)->al_ga.ga_len += count; - if (curwin->w_arg_idx >= after) - ++curwin->w_arg_idx; - return after; - } -} - - -/* - * ":compiler[!] {name}" - */ -void ex_compiler(exarg_T *eap) -{ - char_u *buf; - char_u *old_cur_comp = NULL; - char_u *p; - - if (*eap->arg == NUL) { - /* List all compiler scripts. */ - do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')"); - /* ) keep the indenter happy... */ - } else { - buf = xmalloc(STRLEN(eap->arg) + 14); - if (eap->forceit) { - /* ":compiler! {name}" sets global options */ - do_cmdline_cmd((char_u *) - "command -nargs=* CompilerSet set "); - } else { - /* ":compiler! {name}" sets local options. - * To remain backwards compatible "current_compiler" is always - * used. A user's compiler plugin may set it, the distributed - * plugin will then skip the settings. Afterwards set - * "b:current_compiler" and restore "current_compiler". - * Explicitly prepend "g:" to make it work in a function. */ - old_cur_comp = get_var_value((char_u *)"g:current_compiler"); - if (old_cur_comp != NULL) - old_cur_comp = vim_strsave(old_cur_comp); - do_cmdline_cmd((char_u *) - "command -nargs=* CompilerSet setlocal "); - } - do_unlet((char_u *)"g:current_compiler", TRUE); - do_unlet((char_u *)"b:current_compiler", TRUE); - - sprintf((char *)buf, "compiler/%s.vim", eap->arg); - if (source_runtime(buf, TRUE) == FAIL) - EMSG2(_("E666: compiler not supported: %s"), eap->arg); - free(buf); - - do_cmdline_cmd((char_u *)":delcommand CompilerSet"); - - /* Set "b:current_compiler" from "current_compiler". */ - p = get_var_value((char_u *)"g:current_compiler"); - if (p != NULL) - set_internal_string_var((char_u *)"b:current_compiler", p); - - /* Restore "current_compiler" for ":compiler {name}". */ - if (!eap->forceit) { - if (old_cur_comp != NULL) { - set_internal_string_var((char_u *)"g:current_compiler", - old_cur_comp); - free(old_cur_comp); - } else - do_unlet((char_u *)"g:current_compiler", TRUE); - } - } -} - -/* - * ":runtime {name}" - */ -void ex_runtime(exarg_T *eap) -{ - source_runtime(eap->arg, eap->forceit); -} - -static void source_callback(char_u *fname, void *cookie); - -static void source_callback(char_u *fname, void *cookie) -{ - (void)do_source(fname, FALSE, DOSO_NONE); -} - -/* - * Source the file "name" from all directories in 'runtimepath'. - * "name" can contain wildcards. - * When "all" is TRUE, source all files, otherwise only the first one. - * return FAIL when no file could be sourced, OK otherwise. - */ -int source_runtime(char_u *name, int all) -{ - return do_in_runtimepath(name, all, source_callback, NULL); -} - -/* - * Find "name" in 'runtimepath'. When found, invoke the callback function for - * it: callback(fname, "cookie") - * When "all" is TRUE repeat for all matches, otherwise only the first one is - * used. - * Returns OK when at least one match found, FAIL otherwise. - * - * If "name" is NULL calls callback for each entry in runtimepath. Cookie is - * passed by reference in this case, setting it to NULL indicates that callback - * has done its job. - */ -int do_in_runtimepath(name, all, callback, cookie) -char_u *name; -int all; -void (*callback)(char_u *fname, void *ck); -void *cookie; -{ - char_u *rtp; - char_u *np; - char_u *buf; - char_u *rtp_copy; - char_u *tail; - int num_files; - char_u **files; - int i; - int did_one = FALSE; - - /* Make a copy of 'runtimepath'. Invoking the callback may change the - * value. */ - rtp_copy = vim_strsave(p_rtp); - buf = xmalloc(MAXPATHL); - if (rtp_copy != NULL) { - if (p_verbose > 1 && name != NULL) { - verbose_enter(); - smsg((char_u *)_("Searching for \"%s\" in \"%s\""), - (char *)name, (char *)p_rtp); - verbose_leave(); - } - - /* Loop over all entries in 'runtimepath'. */ - rtp = rtp_copy; - while (*rtp != NUL && (all || !did_one)) { - /* Copy the path from 'runtimepath' to buf[]. */ - copy_option_part(&rtp, buf, MAXPATHL, ","); - if (name == NULL) { - (*callback)(buf, (void *) &cookie); - if (!did_one) - did_one = (cookie == NULL); - } else if (STRLEN(buf) + STRLEN(name) + 2 < MAXPATHL) { - add_pathsep(buf); - tail = buf + STRLEN(buf); - - /* Loop over all patterns in "name" */ - np = name; - while (*np != NUL && (all || !did_one)) { - /* Append the pattern from "name" to buf[]. */ - copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)), - "\t "); - - if (p_verbose > 2) { - verbose_enter(); - smsg((char_u *)_("Searching for \"%s\""), buf); - verbose_leave(); - } - - /* Expand wildcards, invoke the callback for each match. */ - if (gen_expand_wildcards(1, &buf, &num_files, &files, - EW_FILE) == OK) { - for (i = 0; i < num_files; ++i) { - (*callback)(files[i], cookie); - did_one = TRUE; - if (!all) - break; - } - FreeWild(num_files, files); - } - } - } - } - } - free(buf); - free(rtp_copy); - if (p_verbose > 0 && !did_one && name != NULL) { - verbose_enter(); - smsg((char_u *)_("not found in 'runtimepath': \"%s\""), name); - verbose_leave(); - } - - - return did_one ? OK : FAIL; -} - -/* - * ":options" - */ -void ex_options(exarg_T *eap) -{ - cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); -} - -/* - * ":source {fname}" - */ -void ex_source(exarg_T *eap) -{ - cmd_source(eap->arg, eap); -} - -static void cmd_source(char_u *fname, exarg_T *eap) -{ - if (*fname == NUL) - EMSG(_(e_argreq)); - - else if (eap != NULL && eap->forceit) - /* ":source!": read Normal mode commands - * Need to execute the commands directly. This is required at least - * for: - * - ":g" command busy - * - after ":argdo", ":windo" or ":bufdo" - * - another command follows - * - inside a loop - */ - openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL - || eap->cstack->cs_idx >= 0 - ); - - /* ":source" read ex commands */ - else if (do_source(fname, FALSE, DOSO_NONE) == FAIL) - EMSG2(_(e_notopen), fname); -} - -/* - * ":source" and associated commands. - */ -/* - * Structure used to store info for each sourced file. - * It is shared between do_source() and getsourceline(). - * This is required, because it needs to be handed to do_cmdline() and - * sourcing can be done recursively. - */ -struct source_cookie { - FILE *fp; /* opened file for sourcing */ - char_u *nextline; /* if not NULL: line that was read ahead */ - int finished; /* ":finish" used */ -#if defined(USE_CRNL) || defined(USE_CR) - int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */ - int error; /* TRUE if LF found after CR-LF */ -#endif - linenr_T breakpoint; /* next line with breakpoint or zero */ - char_u *fname; /* name of sourced file */ - int dbg_tick; /* debug_tick when breakpoint was set */ - int level; /* top nesting level of sourced file */ - vimconv_T conv; /* type of conversion */ -}; - -/* - * Return the address holding the next breakpoint line for a source cookie. - */ -linenr_T *source_breakpoint(void *cookie) -{ - return &((struct source_cookie *)cookie)->breakpoint; -} - -/* - * Return the address holding the debug tick for a source cookie. - */ -int *source_dbg_tick(void *cookie) -{ - return &((struct source_cookie *)cookie)->dbg_tick; -} - -/* - * Return the nesting level for a source cookie. - */ -int source_level(void *cookie) -{ - return ((struct source_cookie *)cookie)->level; -} - -static char_u *get_one_sourceline(struct source_cookie *sp); - -#if (defined(WIN32) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC) -# define USE_FOPEN_NOINH -static FILE *fopen_noinh_readbin(char *filename); - -/* - * Special function to open a file without handle inheritance. - * When possible the handle is closed on exec(). - */ -static FILE *fopen_noinh_readbin(char *filename) -{ - int fd_tmp = mch_open(filename, O_RDONLY, 0); - - if (fd_tmp == -1) - return NULL; - -# ifdef HAVE_FD_CLOEXEC - { - int fdflags = fcntl(fd_tmp, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); - } -# endif - - return fdopen(fd_tmp, READBIN); -} -#endif - - -/* - * do_source: Read the file "fname" and execute its lines as EX commands. - * - * This function may be called recursively! - * - * return FAIL if file could not be opened, OK otherwise - */ -int -do_source ( - char_u *fname, - int check_other, /* check for .vimrc and _vimrc */ - int is_vimrc /* DOSO_ value */ -) -{ - struct source_cookie cookie; - char_u *save_sourcing_name; - linenr_T save_sourcing_lnum; - char_u *p; - char_u *fname_exp; - char_u *firstline = NULL; - int retval = FAIL; - scid_T save_current_SID; - static scid_T last_current_SID = 0; - void *save_funccalp; - int save_debug_break_level = debug_break_level; - scriptitem_T *si = NULL; -#ifdef STARTUPTIME - struct timeval tv_rel; - struct timeval tv_start; -#endif - proftime_T wait_start; - - p = expand_env_save(fname); - if (p == NULL) - return retval; - fname_exp = fix_fname(p); - free(p); - if (fname_exp == NULL) - return retval; - if (os_isdir(fname_exp)) { - smsg((char_u *)_("Cannot source a directory: \"%s\""), fname); - goto theend; - } - - /* Apply SourceCmd autocommands, they should get the file and source it. */ - if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL) - && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp, - FALSE, curbuf)) { - retval = aborting() ? FAIL : OK; - goto theend; - } - - /* Apply SourcePre autocommands, they may get the file. */ - apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf); - -#ifdef USE_FOPEN_NOINH - cookie.fp = fopen_noinh_readbin((char *)fname_exp); -#else - cookie.fp = mch_fopen((char *)fname_exp, READBIN); -#endif - if (cookie.fp == NULL && check_other) { - /* - * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa, - * and ".exrc" by "_exrc" or vice versa. - */ - p = path_tail(fname_exp); - if ((*p == '.' || *p == '_') - && (STRICMP(p + 1, "nvimrc") == 0 - || STRICMP(p + 1, "ngvimrc") == 0 - || STRICMP(p + 1, "exrc") == 0)) { - if (*p == '_') - *p = '.'; - else - *p = '_'; -#ifdef USE_FOPEN_NOINH - cookie.fp = fopen_noinh_readbin((char *)fname_exp); -#else - cookie.fp = mch_fopen((char *)fname_exp, READBIN); -#endif - } - } - - if (cookie.fp == NULL) { - if (p_verbose > 0) { - verbose_enter(); - if (sourcing_name == NULL) - smsg((char_u *)_("could not source \"%s\""), fname); - else - smsg((char_u *)_("line %" PRId64 ": could not source \"%s\""), - (int64_t)sourcing_lnum, fname); - verbose_leave(); - } - goto theend; - } - - /* - * The file exists. - * - In verbose mode, give a message. - * - For a vimrc file, may want to set 'compatible', call vimrc_found(). - */ - if (p_verbose > 1) { - verbose_enter(); - if (sourcing_name == NULL) - smsg((char_u *)_("sourcing \"%s\""), fname); - else - smsg((char_u *)_("line %" PRId64 ": sourcing \"%s\""), - (int64_t)sourcing_lnum, fname); - verbose_leave(); - } - if (is_vimrc == DOSO_VIMRC) - vimrc_found(fname_exp, (char_u *)"MYVIMRC"); - else if (is_vimrc == DOSO_GVIMRC) - vimrc_found(fname_exp, (char_u *)"MYGVIMRC"); - -#ifdef USE_CRNL - /* If no automatic file format: Set default to CR-NL. */ - if (*p_ffs == NUL) - cookie.fileformat = EOL_DOS; - else - cookie.fileformat = EOL_UNKNOWN; - cookie.error = FALSE; -#endif - -#ifdef USE_CR - /* If no automatic file format: Set default to CR. */ - if (*p_ffs == NUL) - cookie.fileformat = EOL_MAC; - else - cookie.fileformat = EOL_UNKNOWN; - cookie.error = FALSE; -#endif - - cookie.nextline = NULL; - cookie.finished = FALSE; - - /* - * Check if this script has a breakpoint. - */ - cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0); - cookie.fname = fname_exp; - cookie.dbg_tick = debug_tick; - - cookie.level = ex_nesting_level; - - /* - * Keep the sourcing name/lnum, for recursive calls. - */ - save_sourcing_name = sourcing_name; - sourcing_name = fname_exp; - save_sourcing_lnum = sourcing_lnum; - sourcing_lnum = 0; - - cookie.conv.vc_type = CONV_NONE; /* no conversion */ - - /* Read the first line so we can check for a UTF-8 BOM. */ - firstline = getsourceline(0, (void *)&cookie, 0); - if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef - && firstline[1] == 0xbb && firstline[2] == 0xbf) { - /* Found BOM; setup conversion, skip over BOM and recode the line. */ - convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); - p = string_convert(&cookie.conv, firstline + 3, NULL); - if (p == NULL) - p = vim_strsave(firstline + 3); - if (p != NULL) { - free(firstline); - firstline = p; - } - } - -#ifdef STARTUPTIME - if (time_fd != NULL) - time_push(&tv_rel, &tv_start); -#endif - - if (do_profiling == PROF_YES) - prof_child_enter(&wait_start); /* entering a child now */ - - /* Don't use local function variables, if called from a function. - * Also starts profiling timer for nested script. */ - save_funccalp = save_funccal(); - - /* - * Check if this script was sourced before to finds its SID. - * If it's new, generate a new SID. - */ - save_current_SID = current_SID; - FileInfo file_info; - bool file_info_ok = os_get_file_info((char *)fname_exp, &file_info); - for (current_SID = script_items.ga_len; current_SID > 0; --current_SID) { - si = &SCRIPT_ITEM(current_SID); - // Compare dev/ino when possible, it catches symbolic links. - // Also compare file names, the inode may change when the file was edited. - bool file_id_equal = file_info_ok && si->sn_dev_valid - && si->sn_dev == file_info.stat.st_dev - && si->sn_ino == file_info.stat.st_ino; - if (si->sn_name != NULL - && (file_id_equal || fnamecmp(si->sn_name, fname_exp) == 0)) { - break; - } - } - if (current_SID == 0) { - current_SID = ++last_current_SID; - ga_grow(&script_items, (int)(current_SID - script_items.ga_len)); - while (script_items.ga_len < current_SID) { - ++script_items.ga_len; - SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; - SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; - } - si = &SCRIPT_ITEM(current_SID); - si->sn_name = fname_exp; - fname_exp = NULL; - if (file_info_ok) { - si->sn_dev_valid = TRUE; - si->sn_dev = file_info.stat.st_dev; - si->sn_ino = file_info.stat.st_ino; - } else { - si->sn_dev_valid = FALSE; - } - - /* Allocate the local script variables to use for this script. */ - new_script_vars(current_SID); - } - - if (do_profiling == PROF_YES) { - int forceit; - - /* Check if we do profiling for this script. */ - if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit)) { - script_do_profile(si); - si->sn_pr_force = forceit; - } - if (si->sn_prof_on) { - ++si->sn_pr_count; - profile_start(&si->sn_pr_start); - profile_zero(&si->sn_pr_children); - } - } - - /* - * Call do_cmdline, which will call getsourceline() to get the lines. - */ - do_cmdline(firstline, getsourceline, (void *)&cookie, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); - retval = OK; - - if (do_profiling == PROF_YES) { - /* Get "si" again, "script_items" may have been reallocated. */ - si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on) { - profile_end(&si->sn_pr_start); - profile_sub_wait(&wait_start, &si->sn_pr_start); - profile_add(&si->sn_pr_total, &si->sn_pr_start); - profile_self(&si->sn_pr_self, &si->sn_pr_start, - &si->sn_pr_children); - } - } - - if (got_int) - EMSG(_(e_interr)); - sourcing_name = save_sourcing_name; - sourcing_lnum = save_sourcing_lnum; - if (p_verbose > 1) { - verbose_enter(); - smsg((char_u *)_("finished sourcing %s"), fname); - if (sourcing_name != NULL) - smsg((char_u *)_("continuing in %s"), sourcing_name); - verbose_leave(); - } -#ifdef STARTUPTIME - if (time_fd != NULL) { - vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname); - time_msg((char *)IObuff, &tv_start); - time_pop(&tv_rel); - } -#endif - - /* - * After a "finish" in debug mode, need to break at first command of next - * sourced file. - */ - if (save_debug_break_level > ex_nesting_level - && debug_break_level == ex_nesting_level) - ++debug_break_level; - - current_SID = save_current_SID; - restore_funccal(save_funccalp); - if (do_profiling == PROF_YES) - prof_child_exit(&wait_start); /* leaving a child now */ - fclose(cookie.fp); - free(cookie.nextline); - free(firstline); - convert_setup(&cookie.conv, NULL, NULL); - -theend: - free(fname_exp); - return retval; -} - - -/* - * ":scriptnames" - */ -void ex_scriptnames(exarg_T *eap) -{ - int i; - - for (i = 1; i <= script_items.ga_len && !got_int; ++i) - if (SCRIPT_ITEM(i).sn_name != NULL) { - home_replace(NULL, SCRIPT_ITEM(i).sn_name, - NameBuff, MAXPATHL, TRUE); - smsg((char_u *)"%3d: %s", i, NameBuff); - } -} - -# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) -/* - * Fix slashes in the list of script names for 'shellslash'. - */ -void scriptnames_slash_adjust(void) -{ - int i; - - for (i = 1; i <= script_items.ga_len; ++i) - if (SCRIPT_ITEM(i).sn_name != NULL) - slash_adjust(SCRIPT_ITEM(i).sn_name); -} - -# endif - -/* - * Get a pointer to a script name. Used for ":verbose set". - */ -char_u *get_scriptname(scid_T id) -{ - if (id == SID_MODELINE) - return (char_u *)_("modeline"); - if (id == SID_CMDARG) - return (char_u *)_("--cmd argument"); - if (id == SID_CARG) - return (char_u *)_("-c argument"); - if (id == SID_ENV) - return (char_u *)_("environment variable"); - if (id == SID_ERROR) - return (char_u *)_("error handler"); - return SCRIPT_ITEM(id).sn_name; -} - -# if defined(EXITFREE) || defined(PROTO) -void free_scriptnames(void) -{ - int i; - - for (i = script_items.ga_len; i > 0; --i) - free(SCRIPT_ITEM(i).sn_name); - ga_clear(&script_items); -} - -# endif - - -#if defined(USE_CR) || defined(PROTO) -/* - * Version of fgets() which also works for lines ending in a only - * (Macintosh format). - * For older versions of the Metrowerks library. - * At least CodeWarrior 9 needed this code. - */ -char *fgets_cr(char *s, int n, FILE *stream) -{ - int c = 0; - int char_read = 0; - - while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1) { - c = fgetc(stream); - s[char_read++] = c; - /* If the file is in DOS format, we need to skip a NL after a CR. I - * thought it was the other way around, but this appears to work... */ - if (c == '\n') { - c = fgetc(stream); - if (c != '\r') - ungetc(c, stream); - } - } - - s[char_read] = 0; - if (char_read == 0) - return NULL; - - if (feof(stream) && char_read == 1) - return NULL; - - return s; -} -#endif - -/* - * Get one full line from a sourced file. - * Called by do_cmdline() when it's called from do_source(). - * - * Return a pointer to the line in allocated memory. - * Return NULL for end-of-file or some error. - */ -char_u *getsourceline(int c, void *cookie, int indent) -{ - struct source_cookie *sp = (struct source_cookie *)cookie; - char_u *line; - char_u *p; - - /* If breakpoints have been added/deleted need to check for it. */ - if (sp->dbg_tick < debug_tick) { - sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); - sp->dbg_tick = debug_tick; - } - if (do_profiling == PROF_YES) - script_line_end(); - /* - * Get current line. If there is a read-ahead line, use it, otherwise get - * one now. - */ - if (sp->finished) - line = NULL; - else if (sp->nextline == NULL) - line = get_one_sourceline(sp); - else { - line = sp->nextline; - sp->nextline = NULL; - ++sourcing_lnum; - } - if (line != NULL && do_profiling == PROF_YES) - script_line_start(); - - /* Only concatenate lines starting with a \ when 'cpoptions' doesn't - * contain the 'C' flag. */ - if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL)) { - /* compensate for the one line read-ahead */ - --sourcing_lnum; - - /* Get the next line and concatenate it when it starts with a - * backslash. We always need to read the next line, keep it in - * sp->nextline. */ - sp->nextline = get_one_sourceline(sp); - if (sp->nextline != NULL && *(p = skipwhite(sp->nextline)) == '\\') { - garray_T ga; - - ga_init(&ga, (int)sizeof(char_u), 400); - ga_concat(&ga, line); - ga_concat(&ga, p + 1); - for (;; ) { - free(sp->nextline); - sp->nextline = get_one_sourceline(sp); - if (sp->nextline == NULL) - break; - p = skipwhite(sp->nextline); - if (*p != '\\') - break; - /* Adjust the growsize to the current length to speed up - * concatenating many lines. */ - if (ga.ga_len > 400) { - if (ga.ga_len > 8000) - ga.ga_growsize = 8000; - else - ga.ga_growsize = ga.ga_len; - } - ga_concat(&ga, p + 1); - } - ga_append(&ga, NUL); - free(line); - line = ga.ga_data; - } - } - - if (line != NULL && sp->conv.vc_type != CONV_NONE) { - char_u *s; - - /* Convert the encoding of the script line. */ - s = string_convert(&sp->conv, line, NULL); - if (s != NULL) { - free(line); - line = s; - } - } - - /* Did we encounter a breakpoint? */ - if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) { - dbg_breakpoint(sp->fname, sourcing_lnum); - /* Find next breakpoint. */ - sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); - sp->dbg_tick = debug_tick; - } - - return line; -} - -static char_u *get_one_sourceline(struct source_cookie *sp) -{ - garray_T ga; - int len; - int c; - char_u *buf; -#ifdef USE_CRNL - int has_cr; /* CR-LF found */ -#endif -#ifdef USE_CR - char_u *scan; -#endif - int have_read = FALSE; - - /* use a growarray to store the sourced line */ - ga_init(&ga, 1, 250); - - /* - * Loop until there is a finished line (or end-of-file). - */ - sourcing_lnum++; - for (;; ) { - /* make room to read at least 120 (more) characters */ - ga_grow(&ga, 120); - buf = (char_u *)ga.ga_data; - -#ifdef USE_CR - if (sp->fileformat == EOL_MAC) { - if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, - sp->fp) == NULL) - break; - } else -#endif - if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, - sp->fp) == NULL) - break; - len = ga.ga_len + (int)STRLEN(buf + ga.ga_len); -#ifdef USE_CRNL - /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the - * CTRL-Z by its own, or after a NL. */ - if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n')) - && sp->fileformat == EOL_DOS - && buf[len - 1] == Ctrl_Z) { - buf[len - 1] = NUL; - break; - } -#endif - -#ifdef USE_CR - /* If the read doesn't stop on a new line, and there's - * some CR then we assume a Mac format */ - if (sp->fileformat == EOL_UNKNOWN) { - if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL) - sp->fileformat = EOL_MAC; - else - sp->fileformat = EOL_UNIX; - } - - if (sp->fileformat == EOL_MAC) { - scan = vim_strchr(buf, '\r'); - - if (scan != NULL) { - *scan = '\n'; - if (*(scan + 1) != 0) { - *(scan + 1) = 0; - fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR); - } - } - len = STRLEN(buf); - } -#endif - - have_read = TRUE; - ga.ga_len = len; - - /* If the line was longer than the buffer, read more. */ - if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') - continue; - - if (len >= 1 && buf[len - 1] == '\n') { /* remove trailing NL */ -#ifdef USE_CRNL - has_cr = (len >= 2 && buf[len - 2] == '\r'); - if (sp->fileformat == EOL_UNKNOWN) { - if (has_cr) - sp->fileformat = EOL_DOS; - else - sp->fileformat = EOL_UNIX; - } - - if (sp->fileformat == EOL_DOS) { - if (has_cr) { /* replace trailing CR */ - buf[len - 2] = '\n'; - --len; - --ga.ga_len; - } else { /* lines like ":map xx yy^M" will have failed */ - if (!sp->error) { - msg_source(hl_attr(HLF_W)); - EMSG(_("W15: Warning: Wrong line separator, ^M may be missing")); - } - sp->error = TRUE; - sp->fileformat = EOL_UNIX; - } - } -#endif - /* The '\n' is escaped if there is an odd number of ^V's just - * before it, first set "c" just before the 'V's and then check - * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */ - for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) - ; - if ((len & 1) != (c & 1)) { /* escaped NL, read more */ - sourcing_lnum++; - continue; - } - - buf[len - 1] = NUL; /* remove the NL */ - } - - /* - * Check for ^C here now and then, so recursive :so can be broken. - */ - line_breakcheck(); - break; - } - - if (have_read) - return (char_u *)ga.ga_data; - - free(ga.ga_data); - return NULL; -} - -/* - * Called when starting to read a script line. - * "sourcing_lnum" must be correct! - * When skipping lines it may not actually be executed, but we won't find out - * until later and we need to store the time now. - */ -void script_line_start(void) -{ - scriptitem_T *si; - sn_prl_T *pp; - - if (current_SID <= 0 || current_SID > script_items.ga_len) - return; - si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on && sourcing_lnum >= 1) { - /* Grow the array before starting the timer, so that the time spent - * here isn't counted. */ - ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len)); - si->sn_prl_idx = sourcing_lnum - 1; - while (si->sn_prl_ga.ga_len <= si->sn_prl_idx - && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) { - /* Zero counters for a line that was not used before. */ - pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len); - pp->snp_count = 0; - profile_zero(&pp->sn_prl_total); - profile_zero(&pp->sn_prl_self); - ++si->sn_prl_ga.ga_len; - } - si->sn_prl_execed = FALSE; - profile_start(&si->sn_prl_start); - profile_zero(&si->sn_prl_children); - profile_get_wait(&si->sn_prl_wait); - } -} - -/* - * Called when actually executing a function line. - */ -void script_line_exec(void) -{ - scriptitem_T *si; - - if (current_SID <= 0 || current_SID > script_items.ga_len) - return; - si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on && si->sn_prl_idx >= 0) - si->sn_prl_execed = TRUE; -} - -/* - * Called when done with a function line. - */ -void script_line_end(void) -{ - scriptitem_T *si; - sn_prl_T *pp; - - if (current_SID <= 0 || current_SID > script_items.ga_len) - return; - si = &SCRIPT_ITEM(current_SID); - if (si->sn_prof_on && si->sn_prl_idx >= 0 - && si->sn_prl_idx < si->sn_prl_ga.ga_len) { - if (si->sn_prl_execed) { - pp = &PRL_ITEM(si, si->sn_prl_idx); - ++pp->snp_count; - profile_end(&si->sn_prl_start); - profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start); - profile_add(&pp->sn_prl_total, &si->sn_prl_start); - profile_self(&pp->sn_prl_self, &si->sn_prl_start, - &si->sn_prl_children); - } - si->sn_prl_idx = -1; - } -} - -/* - * ":scriptencoding": Set encoding conversion for a sourced script. - * Without the multi-byte feature it's simply ignored. - */ -void ex_scriptencoding(exarg_T *eap) -{ - struct source_cookie *sp; - char_u *name; - - if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { - EMSG(_("E167: :scriptencoding used outside of a sourced file")); - return; - } - - if (*eap->arg != NUL) { - name = enc_canonize(eap->arg); - if (name == NULL) /* out of memory */ - return; - } else - name = eap->arg; - - /* Setup for conversion from the specified encoding to 'encoding'. */ - sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie); - convert_setup(&sp->conv, name, p_enc); - - if (name != eap->arg) - free(name); -} - -/* - * ":finish": Mark a sourced file as finished. - */ -void ex_finish(exarg_T *eap) -{ - if (getline_equal(eap->getline, eap->cookie, getsourceline)) - do_finish(eap, FALSE); - else - EMSG(_("E168: :finish used outside of a sourced file")); -} - -/* - * Mark a sourced file as finished. Possibly makes the ":finish" pending. - * Also called for a pending finish at the ":endtry" or after returning from - * an extra do_cmdline(). "reanimate" is used in the latter case. - */ -void do_finish(exarg_T *eap, int reanimate) -{ - int idx; - - if (reanimate) - ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = FALSE; - - /* - * Cleanup (and inactivate) conditionals, but stop when a try conditional - * not in its finally clause (which then is to be executed next) is found. - * In this case, make the ":finish" pending for execution at the ":endtry". - * Otherwise, finish normally. - */ - idx = cleanup_conditionals(eap->cstack, 0, TRUE); - if (idx >= 0) { - eap->cstack->cs_pending[idx] = CSTP_FINISH; - report_make_pending(CSTP_FINISH, NULL); - } else - ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = TRUE; -} - - -/* - * Return TRUE when a sourced file had the ":finish" command: Don't give error - * message for missing ":endif". - * Return FALSE when not sourcing a file. - */ -int source_finished(fgetline, cookie) -char_u *(*fgetline)(int, void *, int); -void *cookie; -{ - return getline_equal(fgetline, cookie, getsourceline) - && ((struct source_cookie *)getline_cookie( - fgetline, cookie))->finished; -} - -/* - * ":checktime [buffer]" - */ -void ex_checktime(exarg_T *eap) -{ - buf_T *buf; - int save_no_check_timestamps = no_check_timestamps; - - no_check_timestamps = 0; - if (eap->addr_count == 0) /* default is all buffers */ - check_timestamps(FALSE); - else { - buf = buflist_findnr((int)eap->line2); - if (buf != NULL) /* cannot happen? */ - (void)buf_check_timestamp(buf, FALSE); - } - no_check_timestamps = save_no_check_timestamps; -} - -#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ - && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG)) -# define HAVE_GET_LOCALE_VAL -static char *get_locale_val(int what); - -static char *get_locale_val(int what) -{ - char *loc; - - /* Obtain the locale value from the libraries. For DJGPP this is - * redefined and it doesn't use the arguments. */ - loc = setlocale(what, NULL); - - - return loc; -} -#endif - - - -/* - * Obtain the current messages language. Used to set the default for - * 'helplang'. May return NULL or an empty string. - */ -char_u *get_mess_lang(void) -{ - char_u *p; - -# ifdef HAVE_GET_LOCALE_VAL -# if defined(LC_MESSAGES) - p = (char_u *)get_locale_val(LC_MESSAGES); -# else - /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG - * may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME - * and LC_MONETARY may be set differently for a Japanese working in the - * US. */ - p = (char_u *)get_locale_val(LC_COLLATE); -# endif -# else - p = os_getenv((char_u *)"LC_ALL"); - if (p == NULL || *p == NUL) { - p = os_getenv((char_u *)"LC_MESSAGES"); - if (p == NULL || *p == NUL) - p = os_getenv((char_u *)"LANG"); - } -# endif - return p; -} - -/* Complicated #if; matches with where get_mess_env() is used below. */ -#ifdef HAVE_WORKING_LIBINTL -static char_u *get_mess_env(void); - -/* - * Get the language used for messages from the environment. - */ -static char_u *get_mess_env(void) -{ - char_u *p; - - p = (char_u *)os_getenv("LC_ALL"); - if (p == NULL || *p == NUL) { - p = (char_u *)os_getenv("LC_MESSAGES"); - if (p == NULL || *p == NUL) { - p = (char_u *)os_getenv("LANG"); - if (p != NULL && VIM_ISDIGIT(*p)) - p = NULL; /* ignore something like "1043" */ -# ifdef HAVE_GET_LOCALE_VAL - if (p == NULL || *p == NUL) - p = (char_u *)get_locale_val(LC_CTYPE); -# endif - } - } - return p; -} - -#endif - - -/* - * Set the "v:lang" variable according to the current locale setting. - * Also do "v:lc_time"and "v:ctype". - */ -void set_lang_var(void) -{ - char_u *loc; - -# ifdef HAVE_GET_LOCALE_VAL - loc = (char_u *)get_locale_val(LC_CTYPE); -# else - /* setlocale() not supported: use the default value */ - loc = (char_u *)"C"; -# endif - set_vim_var_string(VV_CTYPE, loc, -1); - - /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall - * back to LC_CTYPE if it's empty. */ -# ifdef HAVE_WORKING_LIBINTL - loc = get_mess_env(); -# else - loc = (char_u *)get_locale_val(LC_MESSAGES); -# endif - set_vim_var_string(VV_LANG, loc, -1); - -# ifdef HAVE_GET_LOCALE_VAL - loc = (char_u *)get_locale_val(LC_TIME); -# endif - set_vim_var_string(VV_LC_TIME, loc, -1); -} - -#ifdef HAVE_WORKING_LIBINTL -/* - * ":language": Set the language (locale). - */ -void ex_language(exarg_T *eap) -{ - char *loc; - char_u *p; - char_u *name; - int what = LC_ALL; - char *whatstr = ""; -#ifdef LC_MESSAGES -# define VIM_LC_MESSAGES LC_MESSAGES -#else -# define VIM_LC_MESSAGES 6789 -#endif - - name = eap->arg; - - /* Check for "messages {name}", "ctype {name}" or "time {name}" argument. - * Allow abbreviation, but require at least 3 characters to avoid - * confusion with a two letter language name "me" or "ct". */ - p = skiptowhite(eap->arg); - if ((*p == NUL || vim_iswhite(*p)) && p - eap->arg >= 3) { - if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) { - what = VIM_LC_MESSAGES; - name = skipwhite(p); - whatstr = "messages "; - } else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0) { - what = LC_CTYPE; - name = skipwhite(p); - whatstr = "ctype "; - } else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0) { - what = LC_TIME; - name = skipwhite(p); - whatstr = "time "; - } - } - - if (*name == NUL) { -#ifdef HAVE_WORKING_LIBINTL - if (what == VIM_LC_MESSAGES) - p = get_mess_env(); - else -#endif - p = (char_u *)setlocale(what, NULL); - if (p == NULL || *p == NUL) - p = (char_u *)"Unknown"; - smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p); - } else { -#ifndef LC_MESSAGES - if (what == VIM_LC_MESSAGES) - loc = ""; - else -#endif - { - loc = setlocale(what, (char *)name); -#if defined(FEAT_FLOAT) && defined(LC_NUMERIC) - /* Make sure strtod() uses a decimal point, not a comma. */ - setlocale(LC_NUMERIC, "C"); -#endif - } - if (loc == NULL) - EMSG2(_("E197: Cannot set language to \"%s\""), name); - else { -#ifdef HAVE_NL_MSG_CAT_CNTR - /* Need to do this for GNU gettext, otherwise cached translations - * will be used again. */ - extern int _nl_msg_cat_cntr; - - ++_nl_msg_cat_cntr; -#endif - /* Reset $LC_ALL, otherwise it would overrule everything. */ - vim_setenv((char_u *)"LC_ALL", (char_u *)""); - - if (what != LC_TIME) { - /* Tell gettext() what to translate to. It apparently doesn't - * use the currently effective locale. Also do this when - * FEAT_GETTEXT isn't defined, so that shell commands use this - * value. */ - if (what == LC_ALL) { - vim_setenv((char_u *)"LANG", name); - - /* Clear $LANGUAGE because GNU gettext uses it. */ - vim_setenv((char_u *)"LANGUAGE", (char_u *)""); - } - if (what != LC_CTYPE) { - char_u *mname; - mname = name; - vim_setenv((char_u *)"LC_MESSAGES", mname); - set_helplang_default(mname); - } - } - - /* Set v:lang, v:lc_time and v:ctype to the final result. */ - set_lang_var(); - maketitle(); - } - } -} - - -static char_u **locales = NULL; /* Array of all available locales */ -static int did_init_locales = FALSE; - -static void init_locales(void); -static char_u **find_locales(void); - -/* - * Lazy initialization of all available locales. - */ -static void init_locales(void) -{ - if (!did_init_locales) { - did_init_locales = TRUE; - locales = find_locales(); - } -} - -/* Return an array of strings for all available locales + NULL for the - * last element. Return NULL in case of error. */ -static char_u **find_locales(void) -{ - garray_T locales_ga; - char_u *loc; - - /* Find all available locales by running command "locale -a". If this - * doesn't work we won't have completion. */ - char_u *locale_a = get_cmd_output((char_u *)"locale -a", - NULL, kShellOptSilent); - if (locale_a == NULL) - return NULL; - ga_init(&locales_ga, sizeof(char_u *), 20); - - /* Transform locale_a string where each locale is separated by "\n" - * into an array of locale strings. */ - loc = (char_u *)strtok((char *)locale_a, "\n"); - - while (loc != NULL) { - ga_grow(&locales_ga, 1); - loc = vim_strsave(loc); - if (loc == NULL) - break; - - ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc; - loc = (char_u *)strtok(NULL, "\n"); - } - free(locale_a); - ga_grow(&locales_ga, 1); - ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; - return (char_u **)locales_ga.ga_data; -} - -# if defined(EXITFREE) || defined(PROTO) -void free_locales(void) -{ - int i; - if (locales != NULL) { - for (i = 0; locales[i] != NULL; i++) - free(locales[i]); - free(locales); - locales = NULL; - } -} - -# endif - -/* - * Function given to ExpandGeneric() to obtain the possible arguments of the - * ":language" command. - */ -char_u *get_lang_arg(expand_T *xp, int idx) -{ - if (idx == 0) - return (char_u *)"messages"; - if (idx == 1) - return (char_u *)"ctype"; - if (idx == 2) - return (char_u *)"time"; - - init_locales(); - if (locales == NULL) - return NULL; - return locales[idx - 3]; -} - -/* - * Function given to ExpandGeneric() to obtain the available locales. - */ -char_u *get_locales(expand_T *xp, int idx) -{ - init_locales(); - if (locales == NULL) - return NULL; - return locales[idx]; -} - -#endif diff --git a/src/ex_cmds2.h b/src/ex_cmds2.h deleted file mode 100644 index 5a84206a0e..0000000000 --- a/src/ex_cmds2.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef NEOVIM_EX_CMDS2_H -#define NEOVIM_EX_CMDS2_H -/* ex_cmds2.c */ -void do_debug(char_u *cmd); -void ex_debug(exarg_T *eap); -void dbg_check_breakpoint(exarg_T *eap); -int dbg_check_skipped(exarg_T *eap); -void ex_breakadd(exarg_T *eap); -void ex_debuggreedy(exarg_T *eap); -void ex_breakdel(exarg_T *eap); -void ex_breaklist(exarg_T *eap); -linenr_T dbg_find_breakpoint(int file, char_u *fname, linenr_T after); -int has_profiling(int file, char_u *fname, int *fp); -void dbg_breakpoint(char_u *name, linenr_T lnum); -void profile_start(proftime_T *tm); -void profile_end(proftime_T *tm); -void profile_sub(proftime_T *tm, proftime_T *tm2); -char *profile_msg(proftime_T *tm); -void profile_setlimit(long msec, proftime_T *tm); -int profile_passed_limit(proftime_T *tm); -void profile_zero(proftime_T *tm); -void profile_divide(proftime_T *tm, int count, proftime_T *tm2); -void profile_add(proftime_T *tm, proftime_T *tm2); -void profile_self(proftime_T *self, proftime_T *total, - proftime_T *children); -void profile_get_wait(proftime_T *tm); -void profile_sub_wait(proftime_T *tm, proftime_T *tma); -int profile_equal(proftime_T *tm1, proftime_T *tm2); -int profile_cmp(const proftime_T *tm1, const proftime_T *tm2); -void ex_profile(exarg_T *eap); -char_u *get_profile_name(expand_T *xp, int idx); -void set_context_in_profile_cmd(expand_T *xp, char_u *arg); -void profile_dump(void); -void script_prof_save(proftime_T *tm); -void script_prof_restore(proftime_T *tm); -void prof_inchar_enter(void); -void prof_inchar_exit(void); -int prof_def_func(void); -int autowrite(buf_T *buf, int forceit); -void autowrite_all(void); -int check_changed(buf_T *buf, int flags); -void dialog_changed(buf_T *buf, int checkall); -int can_abandon(buf_T *buf, int forceit); -int check_changed_any(int hidden); -int check_fname(void); -int buf_write_all(buf_T *buf, int forceit); -void get_arglist(garray_T *gap, char_u *str); -int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, - int wig); -void check_arg_idx(win_T *win); -void ex_args(exarg_T *eap); -void ex_previous(exarg_T *eap); -void ex_rewind(exarg_T *eap); -void ex_last(exarg_T *eap); -void ex_argument(exarg_T *eap); -void do_argfile(exarg_T *eap, int argn); -void ex_next(exarg_T *eap); -void ex_argedit(exarg_T *eap); -void ex_argadd(exarg_T *eap); -void ex_argdelete(exarg_T *eap); -void ex_listdo(exarg_T *eap); -void ex_compiler(exarg_T *eap); -void ex_runtime(exarg_T *eap); -int source_runtime(char_u *name, int all); -int do_in_runtimepath(char_u *name, int all, - void (*callback)(char_u *fname, void *ck), - void *cookie); -void ex_options(exarg_T *eap); -void ex_source(exarg_T *eap); -linenr_T *source_breakpoint(void *cookie); -int *source_dbg_tick(void *cookie); -int source_level(void *cookie); -int do_source(char_u *fname, int check_other, int is_vimrc); -void ex_scriptnames(exarg_T *eap); -void scriptnames_slash_adjust(void); -char_u *get_scriptname(scid_T id); -void free_scriptnames(void); -char *fgets_cr(char *s, int n, FILE *stream); -char_u *getsourceline(int c, void *cookie, int indent); -void script_line_start(void); -void script_line_exec(void); -void script_line_end(void); -void ex_scriptencoding(exarg_T *eap); -void ex_finish(exarg_T *eap); -void do_finish(exarg_T *eap, int reanimate); -int source_finished(char_u *(*fgetline)(int, void *, int), void *cookie); -void ex_checktime(exarg_T *eap); -char_u *get_mess_lang(void); -void set_lang_var(void); -void ex_language(exarg_T *eap); -void free_locales(void); -char_u *get_lang_arg(expand_T *xp, int idx); -char_u *get_locales(expand_T *xp, int idx); - -#endif /* NEOVIM_EX_CMDS2_H */ diff --git a/src/ex_cmds_defs.h b/src/ex_cmds_defs.h deleted file mode 100644 index 6497e98480..0000000000 --- a/src/ex_cmds_defs.h +++ /dev/null @@ -1,1191 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - */ - -#include "normal.h" - -/* - * This file defines the Ex commands. - * When DO_DECLARE_EXCMD is defined, the table with ex command names and - * options results. - * When DO_DECLARE_EXCMD is NOT defined, the enum with all the Ex commands - * results. - * This clever trick was invented by Ron Aaron. - */ - -/* - * When adding an Ex command: - * 1. Add an entry in the table below. Keep it sorted on the shortest - * version of the command name that works. If it doesn't start with a - * lower case letter, add it at the end. - * 2. Add a "case: CMD_xxx" in the big switch in ex_docmd.c. - * 3. Add an entry in the index for Ex commands at ":help ex-cmd-index". - * 4. Add documentation in ../doc/xxx.txt. Add a tag for both the short and - * long name of the command. - */ - -#define RANGE 0x001 /* allow a linespecs */ -#define BANG 0x002 /* allow a ! after the command name */ -#define EXTRA 0x004 /* allow extra args after command name */ -#define XFILE 0x008 /* expand wildcards in extra part */ -#define NOSPC 0x010 /* no spaces allowed in the extra part */ -#define DFLALL 0x020 /* default file range is 1,$ */ -#define WHOLEFOLD 0x040 /* extend range to include whole fold also - when less than two numbers given */ -#define NEEDARG 0x080 /* argument required */ -#define TRLBAR 0x100 /* check for trailing vertical bar */ -#define REGSTR 0x200 /* allow "x for register designation */ -#define COUNT 0x400 /* allow count in argument, after command */ -#define NOTRLCOM 0x800 /* no trailing comment allowed */ -#define ZEROR 0x1000 /* zero line number allowed */ -#define USECTRLV 0x2000 /* do not remove CTRL-V from argument */ -#define NOTADR 0x4000 /* number before command is not an address */ -#define EDITCMD 0x8000 /* allow "+command" argument */ -#define BUFNAME 0x10000L /* accepts buffer name */ -#define BUFUNL 0x20000L /* accepts unlisted buffer too */ -#define ARGOPT 0x40000L /* allow "++opt=val" argument */ -#define SBOXOK 0x80000L /* allowed in the sandbox */ -#define CMDWIN 0x100000L /* allowed in cmdline window */ -#define MODIFY 0x200000L /* forbidden in non-'modifiable' buffer */ -#define EXFLAGS 0x400000L /* allow flags after count in argument */ -#define FILES (XFILE | EXTRA) /* multiple extra files allowed */ -#define WORD1 (EXTRA | NOSPC) /* one extra word allowed */ -#define FILE1 (FILES | NOSPC) /* 1 file allowed, defaults to current file */ - -#ifndef DO_DECLARE_EXCMD -typedef struct exarg exarg_T; -#endif - -/* - * This array maps ex command names to command codes. - * The order in which command names are listed below is significant -- - * ambiguous abbreviations are always resolved to be the first possible match - * (e.g. "r" is taken to mean "read", not "rewind", because "read" comes - * before "rewind"). - * Not supported commands are included to avoid ambiguities. - */ -#ifdef EX -# undef EX /* just in case */ -#endif -#ifdef DO_DECLARE_EXCMD -# define EX(a, b, c, d) {(char_u *)b, c, (long_u)(d)} - -typedef void (*ex_func_T)(exarg_T *eap); - -static struct cmdname { - char_u *cmd_name; /* name of the command */ - ex_func_T cmd_func; /* function for this command */ - long_u cmd_argt; /* flags declared above */ -} -cmdnames[] = -#else -# define EX(a, b, c, d) a -enum CMD_index -#endif -{ - EX(CMD_append, "append", ex_append, - BANG|RANGE|ZEROR|TRLBAR|CMDWIN|MODIFY), - EX(CMD_abbreviate, "abbreviate", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_abclear, "abclear", ex_abclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_aboveleft, "aboveleft", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_all, "all", ex_all, - BANG|RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_amenu, "amenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_anoremenu, "anoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_args, "args", ex_args, - BANG|FILES|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_argadd, "argadd", ex_argadd, - BANG|NEEDARG|RANGE|NOTADR|ZEROR|FILES|TRLBAR), - EX(CMD_argdelete, "argdelete", ex_argdelete, - BANG|RANGE|NOTADR|FILES|TRLBAR), - EX(CMD_argdo, "argdo", ex_listdo, - BANG|NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_argedit, "argedit", ex_argedit, - BANG|NEEDARG|RANGE|NOTADR|FILE1|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_argglobal, "argglobal", ex_args, - BANG|FILES|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_arglocal, "arglocal", ex_args, - BANG|FILES|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_argument, "argument", ex_argument, - BANG|RANGE|NOTADR|COUNT|EXTRA|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_ascii, "ascii", do_ascii, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_autocmd, "autocmd", ex_autocmd, - BANG|EXTRA|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_augroup, "augroup", ex_autocmd, - BANG|WORD1|TRLBAR|CMDWIN), - EX(CMD_aunmenu, "aunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_buffer, "buffer", ex_buffer, - BANG|RANGE|NOTADR|BUFNAME|BUFUNL|COUNT|EXTRA|TRLBAR), - EX(CMD_bNext, "bNext", ex_bprevious, - BANG|RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_ball, "ball", ex_buffer_all, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_badd, "badd", ex_edit, - NEEDARG|FILE1|EDITCMD|TRLBAR|CMDWIN), - EX(CMD_bdelete, "bdelete", ex_bunload, - BANG|RANGE|NOTADR|BUFNAME|COUNT|EXTRA|TRLBAR), - EX(CMD_behave, "behave", ex_behave, - NEEDARG|WORD1|TRLBAR|CMDWIN), - EX(CMD_belowright, "belowright", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_bfirst, "bfirst", ex_brewind, - BANG|RANGE|NOTADR|TRLBAR), - EX(CMD_blast, "blast", ex_blast, - BANG|RANGE|NOTADR|TRLBAR), - EX(CMD_bmodified, "bmodified", ex_bmodified, - BANG|RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_bnext, "bnext", ex_bnext, - BANG|RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_botright, "botright", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_bprevious, "bprevious", ex_bprevious, - BANG|RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_brewind, "brewind", ex_brewind, - BANG|RANGE|NOTADR|TRLBAR), - EX(CMD_break, "break", ex_break, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_breakadd, "breakadd", ex_breakadd, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_breakdel, "breakdel", ex_breakdel, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_breaklist, "breaklist", ex_breaklist, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_browse, "browse", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM|CMDWIN), - EX(CMD_buffers, "buffers", buflist_list, - BANG|TRLBAR|CMDWIN), - EX(CMD_bufdo, "bufdo", ex_listdo, - BANG|NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_bunload, "bunload", ex_bunload, - BANG|RANGE|NOTADR|BUFNAME|COUNT|EXTRA|TRLBAR), - EX(CMD_bwipeout, "bwipeout", ex_bunload, - BANG|RANGE|NOTADR|BUFNAME|BUFUNL|COUNT|EXTRA|TRLBAR), - EX(CMD_change, "change", ex_change, - BANG|WHOLEFOLD|RANGE|COUNT|TRLBAR|CMDWIN|MODIFY), - EX(CMD_cNext, "cNext", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cNfile, "cNfile", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cabbrev, "cabbrev", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cabclear, "cabclear", ex_abclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_caddbuffer, "caddbuffer", ex_cbuffer, - RANGE|NOTADR|WORD1|TRLBAR), - EX(CMD_caddexpr, "caddexpr", ex_cexpr, - NEEDARG|WORD1|NOTRLCOM|TRLBAR), - EX(CMD_caddfile, "caddfile", ex_cfile, - TRLBAR|FILE1), - EX(CMD_call, "call", ex_call, - RANGE|NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_catch, "catch", ex_catch, - EXTRA|SBOXOK|CMDWIN), - EX(CMD_cbuffer, "cbuffer", ex_cbuffer, - BANG|RANGE|NOTADR|WORD1|TRLBAR), - EX(CMD_cc, "cc", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cclose, "cclose", ex_cclose, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_cd, "cd", ex_cd, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_center, "center", ex_align, - TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY), - EX(CMD_cexpr, "cexpr", ex_cexpr, - NEEDARG|WORD1|NOTRLCOM|TRLBAR|BANG), - EX(CMD_cfile, "cfile", ex_cfile, - TRLBAR|FILE1|BANG), - EX(CMD_cfirst, "cfirst", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cgetfile, "cgetfile", ex_cfile, - TRLBAR|FILE1), - EX(CMD_cgetbuffer, "cgetbuffer", ex_cbuffer, - RANGE|NOTADR|WORD1|TRLBAR), - EX(CMD_cgetexpr, "cgetexpr", ex_cexpr, - NEEDARG|WORD1|NOTRLCOM|TRLBAR), - EX(CMD_chdir, "chdir", ex_cd, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_changes, "changes", ex_changes, - TRLBAR|CMDWIN), - EX(CMD_checkpath, "checkpath", ex_checkpath, - TRLBAR|BANG|CMDWIN), - EX(CMD_checktime, "checktime", ex_checktime, - RANGE|NOTADR|BUFNAME|COUNT|EXTRA|TRLBAR), - EX(CMD_clist, "clist", qf_list, - BANG|EXTRA|TRLBAR|CMDWIN), - EX(CMD_clast, "clast", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_close, "close", ex_close, - BANG|TRLBAR|CMDWIN), - EX(CMD_cmap, "cmap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cmapclear, "cmapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_cmenu, "cmenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cnext, "cnext", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cnewer, "cnewer", qf_age, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_cnfile, "cnfile", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cnoremap, "cnoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cnoreabbrev, "cnoreabbrev", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cnoremenu, "cnoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_copy, "copy", ex_copymove, - RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN|MODIFY), - EX(CMD_colder, "colder", qf_age, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_colorscheme, "colorscheme", ex_colorscheme, - WORD1|TRLBAR|CMDWIN), - EX(CMD_command, "command", ex_command, - EXTRA|BANG|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_comclear, "comclear", ex_comclear, - TRLBAR|CMDWIN), - EX(CMD_compiler, "compiler", ex_compiler, - BANG|TRLBAR|WORD1|CMDWIN), - EX(CMD_continue, "continue", ex_continue, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_confirm, "confirm", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM|CMDWIN), - EX(CMD_copen, "copen", ex_copen, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_cprevious, "cprevious", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cpfile, "cpfile", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cquit, "cquit", ex_cquit, - TRLBAR|BANG), - EX(CMD_crewind, "crewind", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_cscope, "cscope", do_cscope, - EXTRA|NOTRLCOM|XFILE), - EX(CMD_cstag, "cstag", do_cstag, - BANG|TRLBAR|WORD1), - EX(CMD_cunmap, "cunmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cunabbrev, "cunabbrev", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cunmenu, "cunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_cwindow, "cwindow", ex_cwindow, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_delete, "delete", ex_operators, - RANGE|WHOLEFOLD|REGSTR|COUNT|TRLBAR|CMDWIN|MODIFY), - EX(CMD_delmarks, "delmarks", ex_delmarks, - BANG|EXTRA|TRLBAR|CMDWIN), - EX(CMD_debug, "debug", ex_debug, - NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_debuggreedy, "debuggreedy", ex_debuggreedy, - RANGE|NOTADR|ZEROR|TRLBAR|CMDWIN), - EX(CMD_delcommand, "delcommand", ex_delcommand, - NEEDARG|WORD1|TRLBAR|CMDWIN), - EX(CMD_delfunction, "delfunction", ex_delfunction, - NEEDARG|WORD1|CMDWIN), - EX(CMD_display, "display", ex_display, - EXTRA|NOTRLCOM|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_diffupdate, "diffupdate", ex_diffupdate, - BANG|TRLBAR), - EX(CMD_diffget, "diffget", ex_diffgetput, - RANGE|EXTRA|TRLBAR|MODIFY), - EX(CMD_diffoff, "diffoff", ex_diffoff, - BANG|TRLBAR), - EX(CMD_diffpatch, "diffpatch", ex_diffpatch, - EXTRA|FILE1|TRLBAR|MODIFY), - EX(CMD_diffput, "diffput", ex_diffgetput, - RANGE|EXTRA|TRLBAR), - EX(CMD_diffsplit, "diffsplit", ex_diffsplit, - EXTRA|FILE1|TRLBAR), - EX(CMD_diffthis, "diffthis", ex_diffthis, - TRLBAR), - EX(CMD_digraphs, "digraphs", ex_digraphs, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_djump, "djump", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA), - EX(CMD_dlist, "dlist", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN), - EX(CMD_doautocmd, "doautocmd", ex_doautocmd, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_doautoall, "doautoall", ex_doautoall, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_drop, "drop", ex_drop, - FILES|EDITCMD|NEEDARG|ARGOPT|TRLBAR), - EX(CMD_dsearch, "dsearch", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN), - EX(CMD_dsplit, "dsplit", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA), - EX(CMD_edit, "edit", ex_edit, - BANG|FILE1|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_earlier, "earlier", ex_later, - TRLBAR|EXTRA|NOSPC|CMDWIN), - EX(CMD_echo, "echo", ex_echo, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_echoerr, "echoerr", ex_execute, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_echohl, "echohl", ex_echohl, - EXTRA|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_echomsg, "echomsg", ex_execute, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_echon, "echon", ex_echo, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_else, "else", ex_else, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_elseif, "elseif", ex_else, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_emenu, "emenu", ex_emenu, - NEEDARG|EXTRA|TRLBAR|NOTRLCOM|RANGE|NOTADR|CMDWIN), - EX(CMD_endif, "endif", ex_endif, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_endfunction, "endfunction", ex_endfunction, - TRLBAR|CMDWIN), - EX(CMD_endfor, "endfor", ex_endwhile, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_endtry, "endtry", ex_endtry, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_endwhile, "endwhile", ex_endwhile, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_enew, "enew", ex_edit, - BANG|TRLBAR), - EX(CMD_ex, "ex", ex_edit, - BANG|FILE1|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_execute, "execute", ex_execute, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_exit, "exit", ex_exit, - RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN), - EX(CMD_exusage, "exusage", ex_exusage, - TRLBAR), - EX(CMD_file, "file", ex_file, - RANGE|NOTADR|ZEROR|BANG|FILE1|TRLBAR), - EX(CMD_files, "files", buflist_list, - BANG|TRLBAR|CMDWIN), - EX(CMD_filetype, "filetype", ex_filetype, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_find, "find", ex_find, - RANGE|NOTADR|BANG|FILE1|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_finally, "finally", ex_finally, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_finish, "finish", ex_finish, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_first, "first", ex_rewind, - EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_fixdel, "fixdel", do_fixdel, - TRLBAR|CMDWIN), - EX(CMD_fold, "fold", ex_fold, - RANGE|WHOLEFOLD|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_foldclose, "foldclose", ex_foldopen, - RANGE|BANG|WHOLEFOLD|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_folddoopen, "folddoopen", ex_folddo, - RANGE|DFLALL|NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_folddoclosed, "folddoclosed", ex_folddo, - RANGE|DFLALL|NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_foldopen, "foldopen", ex_foldopen, - RANGE|BANG|WHOLEFOLD|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_for, "for", ex_while, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_function, "function", ex_function, - EXTRA|BANG|CMDWIN), - EX(CMD_global, "global", ex_global, - RANGE|WHOLEFOLD|BANG|EXTRA|DFLALL|SBOXOK|CMDWIN), - EX(CMD_goto, "goto", ex_goto, - RANGE|NOTADR|COUNT|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_grep, "grep", ex_make, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_grepadd, "grepadd", ex_make, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_gui, "gui", ex_gui, - BANG|FILES|EDITCMD|ARGOPT|TRLBAR|CMDWIN), - EX(CMD_gvim, "gvim", ex_gui, - BANG|FILES|EDITCMD|ARGOPT|TRLBAR|CMDWIN), - EX(CMD_help, "help", ex_help, - BANG|EXTRA|NOTRLCOM), - EX(CMD_helpfind, "helpfind", ex_helpfind, - EXTRA|NOTRLCOM), - EX(CMD_helpgrep, "helpgrep", ex_helpgrep, - EXTRA|NOTRLCOM|NEEDARG), - EX(CMD_helptags, "helptags", ex_helptags, - NEEDARG|FILES|TRLBAR|CMDWIN), - EX(CMD_hardcopy, "hardcopy", ex_hardcopy, - RANGE|COUNT|EXTRA|TRLBAR|DFLALL|BANG), - EX(CMD_highlight, "highlight", ex_highlight, - BANG|EXTRA|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_hide, "hide", ex_hide, - BANG|EXTRA|NOTRLCOM), - EX(CMD_history, "history", ex_history, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_insert, "insert", ex_append, - BANG|RANGE|TRLBAR|CMDWIN|MODIFY), - EX(CMD_iabbrev, "iabbrev", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_iabclear, "iabclear", ex_abclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_if, "if", ex_if, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_ijump, "ijump", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA), - EX(CMD_ilist, "ilist", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN), - EX(CMD_imap, "imap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_imapclear, "imapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_imenu, "imenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_inoremap, "inoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_inoreabbrev, "inoreabbrev", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_inoremenu, "inoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_intro, "intro", ex_intro, - TRLBAR|CMDWIN), - EX(CMD_isearch, "isearch", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA|CMDWIN), - EX(CMD_isplit, "isplit", ex_findpat, - BANG|RANGE|DFLALL|WHOLEFOLD|EXTRA), - EX(CMD_iunmap, "iunmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_iunabbrev, "iunabbrev", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_iunmenu, "iunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_join, "join", ex_join, - BANG|RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|MODIFY), - EX(CMD_jumps, "jumps", ex_jumps, - TRLBAR|CMDWIN), - EX(CMD_k, "k", ex_mark, - RANGE|WORD1|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_keepmarks, "keepmarks", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_keepjumps, "keepjumps", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_keeppatterns, "keeppatterns", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_keepalt, "keepalt", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_list, "list", ex_print, - RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN), - EX(CMD_lNext, "lNext", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_lNfile, "lNfile", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_last, "last", ex_last, - EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_language, "language", ex_language, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_laddexpr, "laddexpr", ex_cexpr, - NEEDARG|WORD1|NOTRLCOM|TRLBAR), - EX(CMD_laddbuffer, "laddbuffer", ex_cbuffer, - RANGE|NOTADR|WORD1|TRLBAR), - EX(CMD_laddfile, "laddfile", ex_cfile, - TRLBAR|FILE1), - EX(CMD_later, "later", ex_later, - TRLBAR|EXTRA|NOSPC|CMDWIN), - EX(CMD_lbuffer, "lbuffer", ex_cbuffer, - BANG|RANGE|NOTADR|WORD1|TRLBAR), - EX(CMD_lcd, "lcd", ex_cd, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_lchdir, "lchdir", ex_cd, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_lclose, "lclose", ex_cclose, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_lcscope, "lcscope", do_cscope, - EXTRA|NOTRLCOM|XFILE), - EX(CMD_left, "left", ex_align, - TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY), - EX(CMD_leftabove, "leftabove", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_let, "let", ex_let, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_lexpr, "lexpr", ex_cexpr, - NEEDARG|WORD1|NOTRLCOM|TRLBAR|BANG), - EX(CMD_lfile, "lfile", ex_cfile, - TRLBAR|FILE1|BANG), - EX(CMD_lfirst, "lfirst", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_lgetfile, "lgetfile", ex_cfile, - TRLBAR|FILE1), - EX(CMD_lgetbuffer, "lgetbuffer", ex_cbuffer, - RANGE|NOTADR|WORD1|TRLBAR), - EX(CMD_lgetexpr, "lgetexpr", ex_cexpr, - NEEDARG|WORD1|NOTRLCOM|TRLBAR), - EX(CMD_lgrep, "lgrep", ex_make, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_lgrepadd, "lgrepadd", ex_make, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_lhelpgrep, "lhelpgrep", ex_helpgrep, - EXTRA|NOTRLCOM|NEEDARG), - EX(CMD_ll, "ll", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_llast, "llast", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_llist, "llist", qf_list, - BANG|EXTRA|TRLBAR|CMDWIN), - EX(CMD_lmap, "lmap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_lmapclear, "lmapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_lmake, "lmake", ex_make, - BANG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_lnoremap, "lnoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_lnext, "lnext", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_lnewer, "lnewer", qf_age, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_lnfile, "lnfile", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_loadview, "loadview", ex_loadview, - FILE1|TRLBAR), - EX(CMD_loadkeymap, "loadkeymap", ex_loadkeymap, - CMDWIN), - EX(CMD_lockmarks, "lockmarks", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_lockvar, "lockvar", ex_lockvar, - BANG|EXTRA|NEEDARG|SBOXOK|CMDWIN), - EX(CMD_lolder, "lolder", qf_age, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_lopen, "lopen", ex_copen, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_lprevious, "lprevious", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_lpfile, "lpfile", ex_cnext, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_lrewind, "lrewind", ex_cc, - RANGE|NOTADR|COUNT|TRLBAR|BANG), - EX(CMD_ltag, "ltag", ex_tag, - NOTADR|TRLBAR|BANG|WORD1), - EX(CMD_lunmap, "lunmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_lvimgrep, "lvimgrep", ex_vimgrep, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_lvimgrepadd, "lvimgrepadd", ex_vimgrep, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_lwindow, "lwindow", ex_cwindow, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_ls, "ls", buflist_list, - BANG|TRLBAR|CMDWIN), - EX(CMD_move, "move", ex_copymove, - RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN|MODIFY), - EX(CMD_mark, "mark", ex_mark, - RANGE|WORD1|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_make, "make", ex_make, - BANG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_map, "map", ex_map, - BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_mapclear, "mapclear", ex_mapclear, - EXTRA|BANG|TRLBAR|CMDWIN), - EX(CMD_marks, "marks", do_marks, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_match, "match", ex_match, - RANGE|NOTADR|EXTRA|CMDWIN), - EX(CMD_menu, "menu", ex_menu, - RANGE|NOTADR|ZEROR|BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_menutranslate, "menutranslate", ex_menutranslate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_messages, "messages", ex_messages, - TRLBAR|CMDWIN), - EX(CMD_mkexrc, "mkexrc", ex_mkrc, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_mksession, "mksession", ex_mkrc, - BANG|FILE1|TRLBAR), - EX(CMD_mkspell, "mkspell", ex_mkspell, - BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_mkvimrc, "mkvimrc", ex_mkrc, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_mkview, "mkview", ex_mkrc, - BANG|FILE1|TRLBAR), - EX(CMD_mode, "mode", ex_mode, - WORD1|TRLBAR|CMDWIN), - EX(CMD_next, "next", ex_next, - RANGE|NOTADR|BANG|FILES|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_nbkey, "nbkey", ex_nbkey, - EXTRA|NOTADR|NEEDARG), - EX(CMD_nbclose, "nbclose", ex_nbclose, - TRLBAR|CMDWIN), - EX(CMD_nbstart, "nbstart", ex_nbstart, - WORD1|TRLBAR|CMDWIN), - EX(CMD_new, "new", ex_splitview, - BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_nmap, "nmap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_nmapclear, "nmapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_nmenu, "nmenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_nnoremap, "nnoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_nnoremenu, "nnoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_noremap, "noremap", ex_map, - BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_noautocmd, "noautocmd", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_nohlsearch, "nohlsearch", ex_nohlsearch, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_noreabbrev, "noreabbrev", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_noremenu, "noremenu", ex_menu, - RANGE|NOTADR|ZEROR|BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_noswapfile, "noswapfile", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_normal, "normal", ex_normal, - RANGE|BANG|EXTRA|NEEDARG|NOTRLCOM|USECTRLV|SBOXOK|CMDWIN), - EX(CMD_number, "number", ex_print, - RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN), - EX(CMD_nunmap, "nunmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_nunmenu, "nunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_open, "open", ex_open, - RANGE|BANG|EXTRA), - EX(CMD_oldfiles, "oldfiles", ex_oldfiles, - BANG|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_omap, "omap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_omapclear, "omapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_omenu, "omenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_only, "only", ex_only, - BANG|TRLBAR), - EX(CMD_onoremap, "onoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_onoremenu, "onoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_options, "options", ex_options, - TRLBAR), - EX(CMD_ounmap, "ounmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_ounmenu, "ounmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_ownsyntax, "ownsyntax", ex_ownsyntax, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_print, "print", ex_print, - RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|SBOXOK), - EX(CMD_pclose, "pclose", ex_pclose, - BANG|TRLBAR), - EX(CMD_pedit, "pedit", ex_pedit, - BANG|FILE1|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_pop, "pop", ex_tag, - RANGE|NOTADR|BANG|COUNT|TRLBAR|ZEROR), - EX(CMD_popup, "popup", ex_popup, - NEEDARG|EXTRA|BANG|TRLBAR|NOTRLCOM|CMDWIN), - EX(CMD_ppop, "ppop", ex_ptag, - RANGE|NOTADR|BANG|COUNT|TRLBAR|ZEROR), - EX(CMD_preserve, "preserve", ex_preserve, - TRLBAR), - EX(CMD_previous, "previous", ex_previous, - EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_promptfind, "promptfind", gui_mch_find_dialog, - EXTRA|NOTRLCOM|CMDWIN), - EX(CMD_promptrepl, "promptrepl", gui_mch_replace_dialog, - EXTRA|NOTRLCOM|CMDWIN), - EX(CMD_profile, "profile", ex_profile, - BANG|EXTRA|TRLBAR|CMDWIN), - EX(CMD_profdel, "profdel", ex_breakdel, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_psearch, "psearch", ex_psearch, - BANG|RANGE|WHOLEFOLD|DFLALL|EXTRA), - EX(CMD_ptag, "ptag", ex_ptag, - RANGE|NOTADR|BANG|WORD1|TRLBAR|ZEROR), - EX(CMD_ptNext, "ptNext", ex_ptag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_ptfirst, "ptfirst", ex_ptag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_ptjump, "ptjump", ex_ptag, - BANG|TRLBAR|WORD1), - EX(CMD_ptlast, "ptlast", ex_ptag, - BANG|TRLBAR), - EX(CMD_ptnext, "ptnext", ex_ptag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_ptprevious, "ptprevious", ex_ptag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_ptrewind, "ptrewind", ex_ptag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_ptselect, "ptselect", ex_ptag, - BANG|TRLBAR|WORD1), - EX(CMD_put, "put", ex_put, - RANGE|WHOLEFOLD|BANG|REGSTR|TRLBAR|ZEROR|CMDWIN|MODIFY), - EX(CMD_pwd, "pwd", ex_pwd, - TRLBAR|CMDWIN), - EX(CMD_quit, "quit", ex_quit, - BANG|TRLBAR|CMDWIN), - EX(CMD_quitall, "quitall", ex_quit_all, - BANG|TRLBAR), - EX(CMD_qall, "qall", ex_quit_all, - BANG|TRLBAR|CMDWIN), - EX(CMD_read, "read", ex_read, - BANG|RANGE|WHOLEFOLD|FILE1|ARGOPT|TRLBAR|ZEROR|CMDWIN|MODIFY), - EX(CMD_recover, "recover", ex_recover, - BANG|FILE1|TRLBAR), - EX(CMD_redo, "redo", ex_redo, - TRLBAR|CMDWIN), - EX(CMD_redir, "redir", ex_redir, - BANG|FILES|TRLBAR|CMDWIN), - EX(CMD_redraw, "redraw", ex_redraw, - BANG|TRLBAR|CMDWIN), - EX(CMD_redrawstatus, "redrawstatus", ex_redrawstatus, - BANG|TRLBAR|CMDWIN), - EX(CMD_registers, "registers", ex_display, - EXTRA|NOTRLCOM|TRLBAR|CMDWIN), - EX(CMD_resize, "resize", ex_resize, - RANGE|NOTADR|TRLBAR|WORD1), - EX(CMD_retab, "retab", ex_retab, - TRLBAR|RANGE|WHOLEFOLD|DFLALL|BANG|WORD1|CMDWIN|MODIFY), - EX(CMD_return, "return", ex_return, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_rewind, "rewind", ex_rewind, - EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_right, "right", ex_align, - TRLBAR|RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY), - EX(CMD_rightbelow, "rightbelow", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_runtime, "runtime", ex_runtime, - BANG|NEEDARG|FILES|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_rundo, "rundo", ex_rundo, - NEEDARG|FILE1), - EX(CMD_rviminfo, "rviminfo", ex_viminfo, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_substitute, "substitute", do_sub, - RANGE|WHOLEFOLD|EXTRA|CMDWIN), - EX(CMD_sNext, "sNext", ex_previous, - EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_sargument, "sargument", ex_argument, - BANG|RANGE|NOTADR|COUNT|EXTRA|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_sall, "sall", ex_all, - BANG|RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_sandbox, "sandbox", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_saveas, "saveas", ex_write, - BANG|DFLALL|FILE1|ARGOPT|CMDWIN|TRLBAR), - EX(CMD_sbuffer, "sbuffer", ex_buffer, - BANG|RANGE|NOTADR|BUFNAME|BUFUNL|COUNT|EXTRA|TRLBAR), - EX(CMD_sbNext, "sbNext", ex_bprevious, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_sball, "sball", ex_buffer_all, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_sbfirst, "sbfirst", ex_brewind, - TRLBAR), - EX(CMD_sblast, "sblast", ex_blast, - TRLBAR), - EX(CMD_sbmodified, "sbmodified", ex_bmodified, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_sbnext, "sbnext", ex_bnext, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_sbprevious, "sbprevious", ex_bprevious, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_sbrewind, "sbrewind", ex_brewind, - TRLBAR), - EX(CMD_scriptnames, "scriptnames", ex_scriptnames, - TRLBAR|CMDWIN), - EX(CMD_scriptencoding, "scriptencoding", ex_scriptencoding, - WORD1|TRLBAR|CMDWIN), - EX(CMD_scscope, "scscope", do_scscope, - EXTRA|NOTRLCOM), - EX(CMD_set, "set", ex_set, - TRLBAR|EXTRA|CMDWIN|SBOXOK), - EX(CMD_setfiletype, "setfiletype", ex_setfiletype, - TRLBAR|EXTRA|NEEDARG|CMDWIN), - EX(CMD_setglobal, "setglobal", ex_set, - TRLBAR|EXTRA|CMDWIN|SBOXOK), - EX(CMD_setlocal, "setlocal", ex_set, - TRLBAR|EXTRA|CMDWIN|SBOXOK), - EX(CMD_sfind, "sfind", ex_splitview, - BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_sfirst, "sfirst", ex_rewind, - EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_simalt, "simalt", ex_simalt, - NEEDARG|WORD1|TRLBAR|CMDWIN), - EX(CMD_sign, "sign", ex_sign, - NEEDARG|RANGE|NOTADR|EXTRA|CMDWIN), - EX(CMD_silent, "silent", ex_wrongmodifier, - NEEDARG|EXTRA|BANG|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_sleep, "sleep", ex_sleep, - RANGE|NOTADR|COUNT|EXTRA|TRLBAR|CMDWIN), - EX(CMD_slast, "slast", ex_last, - EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_smagic, "smagic", ex_submagic, - RANGE|WHOLEFOLD|EXTRA|CMDWIN), - EX(CMD_smap, "smap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_smapclear, "smapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_smenu, "smenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_snext, "snext", ex_next, - RANGE|NOTADR|BANG|FILES|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_snomagic, "snomagic", ex_submagic, - RANGE|WHOLEFOLD|EXTRA|CMDWIN), - EX(CMD_snoremap, "snoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_snoremenu, "snoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_source, "source", ex_source, - BANG|FILE1|TRLBAR|SBOXOK|CMDWIN), - EX(CMD_sort, "sort", ex_sort, - RANGE|DFLALL|WHOLEFOLD|BANG|EXTRA|NOTRLCOM|MODIFY), - EX(CMD_split, "split", ex_splitview, - BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_spellgood, "spellgood", ex_spell, - BANG|RANGE|NOTADR|NEEDARG|EXTRA|TRLBAR), - EX(CMD_spelldump, "spelldump", ex_spelldump, - BANG|TRLBAR), - EX(CMD_spellinfo, "spellinfo", ex_spellinfo, - TRLBAR), - EX(CMD_spellrepall, "spellrepall", ex_spellrepall, - TRLBAR), - EX(CMD_spellundo, "spellundo", ex_spell, - BANG|RANGE|NOTADR|NEEDARG|EXTRA|TRLBAR), - EX(CMD_spellwrong, "spellwrong", ex_spell, - BANG|RANGE|NOTADR|NEEDARG|EXTRA|TRLBAR), - EX(CMD_sprevious, "sprevious", ex_previous, - EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_srewind, "srewind", ex_rewind, - EXTRA|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_stop, "stop", ex_stop, - TRLBAR|BANG|CMDWIN), - EX(CMD_stag, "stag", ex_stag, - RANGE|NOTADR|BANG|WORD1|TRLBAR|ZEROR), - EX(CMD_startinsert, "startinsert", ex_startinsert, - BANG|TRLBAR|CMDWIN), - EX(CMD_startgreplace, "startgreplace", ex_startinsert, - BANG|TRLBAR|CMDWIN), - EX(CMD_startreplace, "startreplace", ex_startinsert, - BANG|TRLBAR|CMDWIN), - EX(CMD_stopinsert, "stopinsert", ex_stopinsert, - BANG|TRLBAR|CMDWIN), - EX(CMD_stjump, "stjump", ex_stag, - BANG|TRLBAR|WORD1), - EX(CMD_stselect, "stselect", ex_stag, - BANG|TRLBAR|WORD1), - EX(CMD_sunhide, "sunhide", ex_buffer_all, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_sunmap, "sunmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_sunmenu, "sunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_suspend, "suspend", ex_stop, - TRLBAR|BANG|CMDWIN), - EX(CMD_sview, "sview", ex_splitview, - BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_swapname, "swapname", ex_swapname, - TRLBAR|CMDWIN), - EX(CMD_syntax, "syntax", ex_syntax, - EXTRA|NOTRLCOM|CMDWIN), - EX(CMD_syntime, "syntime", ex_syntime, - NEEDARG|WORD1|TRLBAR|CMDWIN), - EX(CMD_syncbind, "syncbind", ex_syncbind, - TRLBAR), - EX(CMD_t, "t", ex_copymove, - RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN|MODIFY), - EX(CMD_tNext, "tNext", ex_tag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_tag, "tag", ex_tag, - RANGE|NOTADR|BANG|WORD1|TRLBAR|ZEROR), - EX(CMD_tags, "tags", do_tags, - TRLBAR|CMDWIN), - EX(CMD_tab, "tab", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_tabclose, "tabclose", ex_tabclose, - RANGE|NOTADR|COUNT|BANG|TRLBAR|CMDWIN), - EX(CMD_tabdo, "tabdo", ex_listdo, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_tabedit, "tabedit", ex_splitview, - BANG|FILE1|RANGE|NOTADR|ZEROR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_tabfind, "tabfind", ex_splitview, - BANG|FILE1|RANGE|NOTADR|ZEROR|EDITCMD|ARGOPT|NEEDARG|TRLBAR), - EX(CMD_tabfirst, "tabfirst", ex_tabnext, - TRLBAR), - EX(CMD_tabmove, "tabmove", ex_tabmove, - RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR), - EX(CMD_tablast, "tablast", ex_tabnext, - TRLBAR), - EX(CMD_tabnext, "tabnext", ex_tabnext, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_tabnew, "tabnew", ex_splitview, - BANG|FILE1|RANGE|NOTADR|ZEROR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_tabonly, "tabonly", ex_tabonly, - BANG|TRLBAR|CMDWIN), - EX(CMD_tabprevious, "tabprevious", ex_tabnext, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_tabNext, "tabNext", ex_tabnext, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_tabrewind, "tabrewind", ex_tabnext, - TRLBAR), - EX(CMD_tabs, "tabs", ex_tabs, - TRLBAR|CMDWIN), - EX(CMD_tearoff, "tearoff", ex_tearoff, - NEEDARG|EXTRA|TRLBAR|NOTRLCOM|CMDWIN), - EX(CMD_tfirst, "tfirst", ex_tag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_throw, "throw", ex_throw, - EXTRA|NEEDARG|SBOXOK|CMDWIN), - EX(CMD_tjump, "tjump", ex_tag, - BANG|TRLBAR|WORD1), - EX(CMD_tlast, "tlast", ex_tag, - BANG|TRLBAR), - EX(CMD_tmenu, "tmenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_tnext, "tnext", ex_tag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_topleft, "topleft", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_tprevious, "tprevious", ex_tag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_trewind, "trewind", ex_tag, - RANGE|NOTADR|BANG|TRLBAR|ZEROR), - EX(CMD_try, "try", ex_try, - TRLBAR|SBOXOK|CMDWIN), - EX(CMD_tselect, "tselect", ex_tag, - BANG|TRLBAR|WORD1), - EX(CMD_tunmenu, "tunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_undo, "undo", ex_undo, - RANGE|NOTADR|COUNT|ZEROR|TRLBAR|CMDWIN), - EX(CMD_undojoin, "undojoin", ex_undojoin, - TRLBAR|CMDWIN), - EX(CMD_undolist, "undolist", ex_undolist, - TRLBAR|CMDWIN), - EX(CMD_unabbreviate, "unabbreviate", ex_abbreviate, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_unhide, "unhide", ex_buffer_all, - RANGE|NOTADR|COUNT|TRLBAR), - EX(CMD_unlet, "unlet", ex_unlet, - BANG|EXTRA|NEEDARG|SBOXOK|CMDWIN), - EX(CMD_unlockvar, "unlockvar", ex_lockvar, - BANG|EXTRA|NEEDARG|SBOXOK|CMDWIN), - EX(CMD_unmap, "unmap", ex_unmap, - BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_unmenu, "unmenu", ex_menu, - BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_unsilent, "unsilent", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_update, "update", ex_update, - RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR), - EX(CMD_vglobal, "vglobal", ex_global, - RANGE|WHOLEFOLD|EXTRA|DFLALL|CMDWIN), - EX(CMD_version, "version", ex_version, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_verbose, "verbose", ex_wrongmodifier, - NEEDARG|RANGE|NOTADR|EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_vertical, "vertical", ex_wrongmodifier, - NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_visual, "visual", ex_edit, - BANG|FILE1|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_view, "view", ex_edit, - BANG|FILE1|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_vimgrep, "vimgrep", ex_vimgrep, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_vimgrepadd, "vimgrepadd", ex_vimgrep, - RANGE|NOTADR|BANG|NEEDARG|EXTRA|NOTRLCOM|TRLBAR|XFILE), - EX(CMD_viusage, "viusage", ex_viusage, - TRLBAR), - EX(CMD_vmap, "vmap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_vmapclear, "vmapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_vmenu, "vmenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_vnoremap, "vnoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_vnew, "vnew", ex_splitview, - BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_vnoremenu, "vnoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_vsplit, "vsplit", ex_splitview, - BANG|FILE1|RANGE|NOTADR|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_vunmap, "vunmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_vunmenu, "vunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_write, "write", ex_write, - RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN), - EX(CMD_wNext, "wNext", ex_wnext, - RANGE|WHOLEFOLD|NOTADR|BANG|FILE1|ARGOPT|TRLBAR), - EX(CMD_wall, "wall", do_wqall, - BANG|TRLBAR|CMDWIN), - EX(CMD_while, "while", ex_while, - EXTRA|NOTRLCOM|SBOXOK|CMDWIN), - EX(CMD_winsize, "winsize", ex_winsize, - EXTRA|NEEDARG|TRLBAR), - EX(CMD_wincmd, "wincmd", ex_wincmd, - NEEDARG|WORD1|RANGE|NOTADR), - EX(CMD_windo, "windo", ex_listdo, - BANG|NEEDARG|EXTRA|NOTRLCOM), - EX(CMD_winpos, "winpos", ex_winpos, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_wnext, "wnext", ex_wnext, - RANGE|NOTADR|BANG|FILE1|ARGOPT|TRLBAR), - EX(CMD_wprevious, "wprevious", ex_wnext, - RANGE|NOTADR|BANG|FILE1|ARGOPT|TRLBAR), - EX(CMD_wq, "wq", ex_exit, - RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR), - EX(CMD_wqall, "wqall", do_wqall, - BANG|FILE1|ARGOPT|DFLALL|TRLBAR), - EX(CMD_wsverb, "wsverb", ex_wsverb, - EXTRA|NOTADR|NEEDARG), - EX(CMD_wundo, "wundo", ex_wundo, - BANG|NEEDARG|FILE1), - EX(CMD_wviminfo, "wviminfo", ex_viminfo, - BANG|FILE1|TRLBAR|CMDWIN), - EX(CMD_xit, "xit", ex_exit, - RANGE|WHOLEFOLD|BANG|FILE1|ARGOPT|DFLALL|TRLBAR|CMDWIN), - EX(CMD_xall, "xall", do_wqall, - BANG|TRLBAR), - EX(CMD_xmap, "xmap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_xmapclear, "xmapclear", ex_mapclear, - EXTRA|TRLBAR|CMDWIN), - EX(CMD_xmenu, "xmenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_xnoremap, "xnoremap", ex_map, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_xnoremenu, "xnoremenu", ex_menu, - RANGE|NOTADR|ZEROR|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_xunmap, "xunmap", ex_unmap, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_xunmenu, "xunmenu", ex_menu, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), - EX(CMD_yank, "yank", ex_operators, - RANGE|WHOLEFOLD|REGSTR|COUNT|TRLBAR|CMDWIN), - EX(CMD_z, "z", ex_z, - RANGE|WHOLEFOLD|EXTRA|EXFLAGS|TRLBAR|CMDWIN), - - /* commands that don't start with a lowercase letter */ - EX(CMD_bang, "!", ex_bang, - RANGE|WHOLEFOLD|BANG|FILES|CMDWIN), - EX(CMD_pound, "#", ex_print, - RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN), - EX(CMD_and, "&", do_sub, - RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY), - EX(CMD_star, "*", ex_at, - RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN), - EX(CMD_lshift, "<", ex_operators, - RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|MODIFY), - EX(CMD_equal, "=", ex_equal, - RANGE|TRLBAR|DFLALL|EXFLAGS|CMDWIN), - EX(CMD_rshift, ">", ex_operators, - RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN|MODIFY), - EX(CMD_at, "@", ex_at, - RANGE|WHOLEFOLD|EXTRA|TRLBAR|CMDWIN), - EX(CMD_Next, "Next", ex_previous, - EXTRA|RANGE|NOTADR|COUNT|BANG|EDITCMD|ARGOPT|TRLBAR), - EX(CMD_Print, "Print", ex_print, - RANGE|WHOLEFOLD|COUNT|EXFLAGS|TRLBAR|CMDWIN), - EX(CMD_X, "X", ex_X, - TRLBAR), - EX(CMD_tilde, "~", do_sub, - RANGE|WHOLEFOLD|EXTRA|CMDWIN|MODIFY), - -#ifndef DO_DECLARE_EXCMD - CMD_SIZE, /* MUST be after all real commands! */ - CMD_USER = -1, /* User-defined command */ - CMD_USER_BUF = -2 /* User-defined command local to buffer */ -#endif -}; - -#define USER_CMDIDX(idx) ((int)(idx) < 0) - -#ifndef DO_DECLARE_EXCMD -typedef enum CMD_index cmdidx_T; - -/* - * Arguments used for Ex commands. - */ -struct exarg { - char_u *arg; /* argument of the command */ - char_u *nextcmd; /* next command (NULL if none) */ - char_u *cmd; /* the name of the command (except for :make) */ - char_u **cmdlinep; /* pointer to pointer of allocated cmdline */ - cmdidx_T cmdidx; /* the index for the command */ - long argt; /* flags for the command */ - int skip; /* don't execute the command, only parse it */ - int forceit; /* TRUE if ! present */ - int addr_count; /* the number of addresses given */ - linenr_T line1; /* the first line number */ - linenr_T line2; /* the second line number or count */ - int flags; /* extra flags after count: EXFLAG_ */ - char_u *do_ecmd_cmd; /* +command arg to be used in edited file */ - linenr_T do_ecmd_lnum; /* the line number in an edited file */ - int append; /* TRUE with ":w >>file" command */ - int usefilter; /* TRUE with ":w !command" and ":r!command" */ - int amount; /* number of '>' or '<' for shift command */ - int regname; /* register name (NUL if none) */ - int force_bin; /* 0, FORCE_BIN or FORCE_NOBIN */ - int read_edit; /* ++edit argument */ - int force_ff; /* ++ff= argument (index in cmd[]) */ - int force_enc; /* ++enc= argument (index in cmd[]) */ - int bad_char; /* BAD_KEEP, BAD_DROP or replacement byte */ - int useridx; /* user command index */ - char_u *errmsg; /* returned error message */ - char_u *(*getline)(int, void *, int); - void *cookie; /* argument for getline() */ - struct condstack *cstack; /* condition stack for ":if" etc. */ -}; - -#define FORCE_BIN 1 /* ":edit ++bin file" */ -#define FORCE_NOBIN 2 /* ":edit ++nobin file" */ - -/* Values for "flags" */ -#define EXFLAG_LIST 0x01 /* 'l': list */ -#define EXFLAG_NR 0x02 /* '#': number */ -#define EXFLAG_PRINT 0x04 /* 'p': print */ - -/* - * used for completion on the command line - */ -typedef struct expand { - int xp_context; /* type of expansion */ - char_u *xp_pattern; /* start of item to expand */ - int xp_pattern_len; /* bytes in xp_pattern before cursor */ - char_u *xp_arg; /* completion function */ - int xp_scriptID; /* SID for completion function */ - int xp_backslash; /* one of the XP_BS_ values */ -#ifndef BACKSLASH_IN_FILENAME - int xp_shell; /* TRUE for a shell command, more - characters need to be escaped */ -#endif - int xp_numfiles; /* number of files found by - file name completion */ - char_u **xp_files; /* list of files */ - char_u *xp_line; /* text being completed */ - int xp_col; /* cursor position in line */ -} expand_T; - -/* values for xp_backslash */ -#define XP_BS_NONE 0 /* nothing special for backslashes */ -#define XP_BS_ONE 1 /* uses one backslash before a space */ -#define XP_BS_THREE 2 /* uses three backslashes before a space */ - -/* - * Command modifiers ":vertical", ":browse", ":confirm" and ":hide" set a flag. - * This needs to be saved for recursive commands, put them in a structure for - * easy manipulation. - */ -typedef struct { - int hide; /* TRUE when ":hide" was used */ - int split; /* flags for win_split() */ - int tab; /* > 0 when ":tab" was used */ - int confirm; /* TRUE to invoke yes/no dialog */ - int keepalt; /* TRUE when ":keepalt" was used */ - int keepmarks; /* TRUE when ":keepmarks" was used */ - int keepjumps; /* TRUE when ":keepjumps" was used */ - int lockmarks; /* TRUE when ":lockmarks" was used */ - int keeppatterns; /* TRUE when ":keeppatterns" was used */ - bool noswapfile; /* true when ":noswapfile" was used */ - char_u *save_ei; /* saved value of 'eventignore' */ -} cmdmod_T; - -#endif diff --git a/src/ex_docmd.c b/src/ex_docmd.c deleted file mode 100644 index 87ce252434..0000000000 --- a/src/ex_docmd.c +++ /dev/null @@ -1,9109 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * ex_docmd.c: functions for executing an Ex command line. - */ - -#include - -#include "vim.h" -#include "ex_docmd.h" -#include "blowfish.h" -#include "buffer.h" -#include "charset.h" -#include "diff.h" -#include "digraph.h" -#include "edit.h" -#include "eval.h" -#include "ex_cmds.h" -#include "ex_cmds2.h" -#include "ex_eval.h" -#include "ex_getln.h" -#include "fileio.h" -#include "fold.h" -#include "getchar.h" -#include "hardcopy.h" -#include "if_cscope.h" -#include "main.h" -#include "mark.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "menu.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "keymap.h" -#include "crypt.h" -#include "file_search.h" -#include "garray.h" -#include "move.h" -#include "normal.h" -#include "ops.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "search.h" -#include "spell.h" -#include "syntax.h" -#include "tag.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "version.h" -#include "window.h" -#include "os/os.h" - -static int quitmore = 0; -static int ex_pressedreturn = FALSE; - -typedef struct ucmd { - char_u *uc_name; /* The command name */ - long_u uc_argt; /* The argument type */ - char_u *uc_rep; /* The command's replacement string */ - long uc_def; /* The default value for a range/count */ - int uc_compl; /* completion type */ - scid_T uc_scriptID; /* SID where the command was defined */ - char_u *uc_compl_arg; /* completion argument if any */ -} ucmd_T; - -#define UC_BUFFER 1 /* -buffer: local to current buffer */ - -static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL}; - -#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i]) -#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) - -static void do_ucmd(exarg_T *eap); -static void ex_command(exarg_T *eap); -static void ex_delcommand(exarg_T *eap); -static char_u *get_user_command_name(int idx); - - -static char_u *do_one_cmd(char_u **, int, struct condstack *, - char_u *(*fgetline)(int, void *, int), - void *cookie); -static void append_command(char_u *cmd); -static char_u *find_command(exarg_T *eap, int *full); - -static void ex_abbreviate(exarg_T *eap); -static void ex_map(exarg_T *eap); -static void ex_unmap(exarg_T *eap); -static void ex_mapclear(exarg_T *eap); -static void ex_abclear(exarg_T *eap); -static void ex_autocmd(exarg_T *eap); -static void ex_doautocmd(exarg_T *eap); -static void ex_bunload(exarg_T *eap); -static void ex_buffer(exarg_T *eap); -static void ex_bmodified(exarg_T *eap); -static void ex_bnext(exarg_T *eap); -static void ex_bprevious(exarg_T *eap); -static void ex_brewind(exarg_T *eap); -static void ex_blast(exarg_T *eap); -static char_u *getargcmd(char_u **); -static char_u *skip_cmd_arg(char_u *p, int rembs); -static int getargopt(exarg_T *eap); - -static int check_more(int, int); -static linenr_T get_address(char_u **, int skip, int to_other_file); -static void get_flags(exarg_T *eap); -# define HAVE_EX_SCRIPT_NI -static void ex_script_ni(exarg_T *eap); -static char_u *invalid_range(exarg_T *eap); -static void correct_range(exarg_T *eap); -static char_u *replace_makeprg(exarg_T *eap, char_u *p, - char_u **cmdlinep); -static char_u *repl_cmdline(exarg_T *eap, char_u *src, int srclen, - char_u *repl, - char_u **cmdlinep); -static void ex_highlight(exarg_T *eap); -static void ex_colorscheme(exarg_T *eap); -static void ex_quit(exarg_T *eap); -static void ex_cquit(exarg_T *eap); -static void ex_quit_all(exarg_T *eap); -static void ex_close(exarg_T *eap); -static void ex_win_close(int forceit, win_T *win, tabpage_T *tp); -static void ex_only(exarg_T *eap); -static void ex_resize(exarg_T *eap); -static void ex_stag(exarg_T *eap); -static void ex_tabclose(exarg_T *eap); -static void ex_tabonly(exarg_T *eap); -static void ex_tabnext(exarg_T *eap); -static void ex_tabmove(exarg_T *eap); -static void ex_tabs(exarg_T *eap); -static void ex_pclose(exarg_T *eap); -static void ex_ptag(exarg_T *eap); -static void ex_pedit(exarg_T *eap); -static void ex_hide(exarg_T *eap); -static void ex_stop(exarg_T *eap); -static void ex_exit(exarg_T *eap); -static void ex_print(exarg_T *eap); -static void ex_goto(exarg_T *eap); -static void ex_preserve(exarg_T *eap); -static void ex_recover(exarg_T *eap); -static void ex_mode(exarg_T *eap); -static void ex_wrongmodifier(exarg_T *eap); -static void ex_find(exarg_T *eap); -static void ex_open(exarg_T *eap); -static void ex_edit(exarg_T *eap); -# define ex_drop ex_ni -# define ex_gui ex_nogui -static void ex_nogui(exarg_T *eap); -# define ex_tearoff ex_ni -# define ex_popup ex_ni -# define ex_simalt ex_ni -# define gui_mch_find_dialog ex_ni -# define gui_mch_replace_dialog ex_ni -# define ex_helpfind ex_ni -static void ex_swapname(exarg_T *eap); -static void ex_syncbind(exarg_T *eap); -static void ex_read(exarg_T *eap); -static void ex_pwd(exarg_T *eap); -static void ex_equal(exarg_T *eap); -static void ex_sleep(exarg_T *eap); -static void do_exmap(exarg_T *eap, int isabbrev); -static void ex_winsize(exarg_T *eap); -static void ex_wincmd(exarg_T *eap); -#if defined(FEAT_GUI) || defined(UNIX) || defined(MSWIN) -static void ex_winpos(exarg_T *eap); -#else -# define ex_winpos ex_ni -#endif -static void ex_operators(exarg_T *eap); -static void ex_put(exarg_T *eap); -static void ex_copymove(exarg_T *eap); -static void ex_submagic(exarg_T *eap); -static void ex_join(exarg_T *eap); -static void ex_at(exarg_T *eap); -static void ex_bang(exarg_T *eap); -static void ex_undo(exarg_T *eap); -static void ex_wundo(exarg_T *eap); -static void ex_rundo(exarg_T *eap); -static void ex_redo(exarg_T *eap); -static void ex_later(exarg_T *eap); -static void ex_redir(exarg_T *eap); -static void ex_redraw(exarg_T *eap); -static void ex_redrawstatus(exarg_T *eap); -static void close_redir(void); -static void ex_mkrc(exarg_T *eap); -static void ex_mark(exarg_T *eap); -static char_u *uc_fun_cmd(void); -static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, - expand_T *xp, - int *compl); -static void ex_normal(exarg_T *eap); -static void ex_startinsert(exarg_T *eap); -static void ex_stopinsert(exarg_T *eap); -static void ex_checkpath(exarg_T *eap); -static void ex_findpat(exarg_T *eap); -static void ex_psearch(exarg_T *eap); -static void ex_tag(exarg_T *eap); -static void ex_tag_cmd(exarg_T *eap, char_u *name); -static char_u *arg_all(void); -static int makeopens(FILE *fd, char_u *dirnow); -static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, - int current_arg_idx); -static void ex_loadview(exarg_T *eap); -static char_u *get_view_file(int c); -static int did_lcd; /* whether ":lcd" was produced for a session */ -static void ex_viminfo(exarg_T *eap); -static void ex_behave(exarg_T *eap); -static void ex_filetype(exarg_T *eap); -static void ex_setfiletype(exarg_T *eap); -static void ex_digraphs(exarg_T *eap); -static void ex_set(exarg_T *eap); -static void ex_nohlsearch(exarg_T *eap); -static void ex_match(exarg_T *eap); -static void ex_X(exarg_T *eap); -static void ex_fold(exarg_T *eap); -static void ex_foldopen(exarg_T *eap); -static void ex_folddo(exarg_T *eap); -#ifndef HAVE_WORKING_LIBINTL -# define ex_language ex_ni -#endif -# define ex_wsverb ex_ni -# define ex_nbclose ex_ni -# define ex_nbkey ex_ni -# define ex_nbstart ex_ni - - - - -/* - * Declare cmdnames[]. - */ -#define DO_DECLARE_EXCMD -#include "ex_cmds_defs.h" - -/* - * Table used to quickly search for a command, based on its first character. - */ -static cmdidx_T cmdidxs[27] = -{ - CMD_append, - CMD_buffer, - CMD_change, - CMD_delete, - CMD_edit, - CMD_file, - CMD_global, - CMD_help, - CMD_insert, - CMD_join, - CMD_k, - CMD_list, - CMD_move, - CMD_next, - CMD_open, - CMD_print, - CMD_quit, - CMD_read, - CMD_substitute, - CMD_t, - CMD_undo, - CMD_vglobal, - CMD_write, - CMD_xit, - CMD_yank, - CMD_z, - CMD_bang -}; - -static char_u dollar_command[2] = {'$', 0}; - - -/* Struct for storing a line inside a while/for loop */ -typedef struct { - char_u *line; /* command line */ - linenr_T lnum; /* sourcing_lnum of the line */ -} wcmd_T; - -/* - * Structure used to store info for line position in a while or for loop. - * This is required, because do_one_cmd() may invoke ex_function(), which - * reads more lines that may come from the while/for loop. - */ -struct loop_cookie { - garray_T *lines_gap; /* growarray with line info */ - int current_line; /* last read line from growarray */ - int repeating; /* TRUE when looping a second time */ - /* When "repeating" is FALSE use "getline" and "cookie" to get lines */ - char_u *(*getline)(int, void *, int); - void *cookie; -}; - -static char_u *get_loop_line(int c, void *cookie, int indent); -static void store_loop_line(garray_T *gap, char_u *line); -static void free_cmdlines(garray_T *gap); - -/* Struct to save a few things while debugging. Used in do_cmdline() only. */ -struct dbg_stuff { - int trylevel; - int force_abort; - except_T *caught_stack; - char_u *vv_exception; - char_u *vv_throwpoint; - int did_emsg; - int got_int; - int did_throw; - int need_rethrow; - int check_cstack; - except_T *current_exception; -}; - -static void save_dbg_stuff(struct dbg_stuff *dsp); -static void restore_dbg_stuff(struct dbg_stuff *dsp); - -static void save_dbg_stuff(struct dbg_stuff *dsp) -{ - dsp->trylevel = trylevel; trylevel = 0; - dsp->force_abort = force_abort; force_abort = FALSE; - dsp->caught_stack = caught_stack; caught_stack = NULL; - dsp->vv_exception = v_exception(NULL); - dsp->vv_throwpoint = v_throwpoint(NULL); - - /* Necessary for debugging an inactive ":catch", ":finally", ":endtry" */ - dsp->did_emsg = did_emsg; did_emsg = FALSE; - dsp->got_int = got_int; got_int = FALSE; - dsp->did_throw = did_throw; did_throw = FALSE; - dsp->need_rethrow = need_rethrow; need_rethrow = FALSE; - dsp->check_cstack = check_cstack; check_cstack = FALSE; - dsp->current_exception = current_exception; current_exception = NULL; -} - -static void restore_dbg_stuff(struct dbg_stuff *dsp) -{ - suppress_errthrow = FALSE; - trylevel = dsp->trylevel; - force_abort = dsp->force_abort; - caught_stack = dsp->caught_stack; - (void)v_exception(dsp->vv_exception); - (void)v_throwpoint(dsp->vv_throwpoint); - did_emsg = dsp->did_emsg; - got_int = dsp->got_int; - did_throw = dsp->did_throw; - need_rethrow = dsp->need_rethrow; - check_cstack = dsp->check_cstack; - current_exception = dsp->current_exception; -} - - -/* - * do_exmode(): Repeatedly get commands for the "Ex" mode, until the ":vi" - * command is given. - */ -void -do_exmode ( - int improved /* TRUE for "improved Ex" mode */ -) -{ - int save_msg_scroll; - int prev_msg_row; - linenr_T prev_line; - int changedtick; - - if (improved) - exmode_active = EXMODE_VIM; - else - exmode_active = EXMODE_NORMAL; - State = NORMAL; - - /* When using ":global /pat/ visual" and then "Q" we return to continue - * the :global command. */ - if (global_busy) - return; - - save_msg_scroll = msg_scroll; - ++RedrawingDisabled; /* don't redisplay the window */ - ++no_wait_return; /* don't wait for return */ - - MSG(_("Entering Ex mode. Type \"visual\" to go to Normal mode.")); - while (exmode_active) { - /* Check for a ":normal" command and no more characters left. */ - if (ex_normal_busy > 0 && typebuf.tb_len == 0) { - exmode_active = FALSE; - break; - } - msg_scroll = TRUE; - need_wait_return = FALSE; - ex_pressedreturn = FALSE; - ex_no_reprint = FALSE; - changedtick = curbuf->b_changedtick; - prev_msg_row = msg_row; - prev_line = curwin->w_cursor.lnum; - if (improved) { - cmdline_row = msg_row; - do_cmdline(NULL, getexline, NULL, 0); - } else - do_cmdline(NULL, getexmodeline, NULL, DOCMD_NOWAIT); - lines_left = Rows - 1; - - if ((prev_line != curwin->w_cursor.lnum - || changedtick != curbuf->b_changedtick) && !ex_no_reprint) { - if (curbuf->b_ml.ml_flags & ML_EMPTY) - EMSG(_(e_emptybuf)); - else { - if (ex_pressedreturn) { - /* go up one line, to overwrite the ":" line, so the - * output doesn't contain empty lines. */ - msg_row = prev_msg_row; - if (prev_msg_row == Rows - 1) - msg_row--; - } - msg_col = 0; - print_line_no_prefix(curwin->w_cursor.lnum, FALSE, FALSE); - msg_clr_eos(); - } - } else if (ex_pressedreturn && !ex_no_reprint) { /* must be at EOF */ - if (curbuf->b_ml.ml_flags & ML_EMPTY) - EMSG(_(e_emptybuf)); - else - EMSG(_("E501: At end-of-file")); - } - } - - --RedrawingDisabled; - --no_wait_return; - update_screen(CLEAR); - need_wait_return = FALSE; - msg_scroll = save_msg_scroll; -} - -/* - * Execute a simple command line. Used for translated commands like "*". - */ -int do_cmdline_cmd(char_u *cmd) -{ - return do_cmdline(cmd, NULL, NULL, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); -} - -/* - * do_cmdline(): execute one Ex command line - * - * 1. Execute "cmdline" when it is not NULL. - * If "cmdline" is NULL, or more lines are needed, fgetline() is used. - * 2. Split up in parts separated with '|'. - * - * This function can be called recursively! - * - * flags: - * DOCMD_VERBOSE - The command will be included in the error message. - * DOCMD_NOWAIT - Don't call wait_return() and friends. - * DOCMD_REPEAT - Repeat execution until fgetline() returns NULL. - * DOCMD_KEYTYPED - Don't reset KeyTyped. - * DOCMD_EXCRESET - Reset the exception environment (used for debugging). - * DOCMD_KEEPLINE - Store first typed line (for repeating with "."). - * - * return FAIL if cmdline could not be executed, OK otherwise - */ -int do_cmdline(cmdline, fgetline, cookie, flags) -char_u *cmdline; -char_u *(*fgetline)(int, void *, int); -void *cookie; /* argument for fgetline() */ -int flags; -{ - char_u *next_cmdline; /* next cmd to execute */ - char_u *cmdline_copy = NULL; /* copy of cmd line */ - int used_getline = FALSE; /* used "fgetline" to obtain command */ - static int recursive = 0; /* recursive depth */ - int msg_didout_before_start = 0; - int count = 0; /* line number count */ - int did_inc = FALSE; /* incremented RedrawingDisabled */ - int retval = OK; - struct condstack cstack; /* conditional stack */ - garray_T lines_ga; /* keep lines for ":while"/":for" */ - int current_line = 0; /* active line in lines_ga */ - char_u *fname = NULL; /* function or script name */ - linenr_T *breakpoint = NULL; /* ptr to breakpoint field in cookie */ - int *dbg_tick = NULL; /* ptr to dbg_tick field in cookie */ - struct dbg_stuff debug_saved; /* saved things for debug mode */ - int initial_trylevel; - struct msglist **saved_msg_list = NULL; - struct msglist *private_msg_list; - - /* "fgetline" and "cookie" passed to do_one_cmd() */ - char_u *(*cmd_getline)(int, void *, int); - void *cmd_cookie; - struct loop_cookie cmd_loop_cookie; - void *real_cookie; - int getline_is_func; - static int call_depth = 0; /* recursiveness */ - - /* For every pair of do_cmdline()/do_one_cmd() calls, use an extra memory - * location for storing error messages to be converted to an exception. - * This ensures that the do_errthrow() call in do_one_cmd() does not - * combine the messages stored by an earlier invocation of do_one_cmd() - * with the command name of the later one. This would happen when - * BufWritePost autocommands are executed after a write error. */ - saved_msg_list = msg_list; - msg_list = &private_msg_list; - private_msg_list = NULL; - - /* It's possible to create an endless loop with ":execute", catch that - * here. The value of 200 allows nested function calls, ":source", etc. */ - if (call_depth == 200) { - EMSG(_("E169: Command too recursive")); - /* When converting to an exception, we do not include the command name - * since this is not an error of the specific command. */ - do_errthrow((struct condstack *)NULL, (char_u *)NULL); - msg_list = saved_msg_list; - return FAIL; - } - ++call_depth; - - cstack.cs_idx = -1; - cstack.cs_looplevel = 0; - cstack.cs_trylevel = 0; - cstack.cs_emsg_silent_list = NULL; - cstack.cs_lflags = 0; - ga_init(&lines_ga, (int)sizeof(wcmd_T), 10); - - real_cookie = getline_cookie(fgetline, cookie); - - /* Inside a function use a higher nesting level. */ - getline_is_func = getline_equal(fgetline, cookie, get_func_line); - if (getline_is_func && ex_nesting_level == func_level(real_cookie)) - ++ex_nesting_level; - - /* Get the function or script name and the address where the next breakpoint - * line and the debug tick for a function or script are stored. */ - if (getline_is_func) { - fname = func_name(real_cookie); - breakpoint = func_breakpoint(real_cookie); - dbg_tick = func_dbg_tick(real_cookie); - } else if (getline_equal(fgetline, cookie, getsourceline)) { - fname = sourcing_name; - breakpoint = source_breakpoint(real_cookie); - dbg_tick = source_dbg_tick(real_cookie); - } - - /* - * Initialize "force_abort" and "suppress_errthrow" at the top level. - */ - if (!recursive) { - force_abort = FALSE; - suppress_errthrow = FALSE; - } - - /* - * If requested, store and reset the global values controlling the - * exception handling (used when debugging). Otherwise clear it to avoid - * a bogus compiler warning when the optimizer uses inline functions... - */ - if (flags & DOCMD_EXCRESET) - save_dbg_stuff(&debug_saved); - else - memset(&debug_saved, 0, 1); - - initial_trylevel = trylevel; - - /* - * "did_throw" will be set to TRUE when an exception is being thrown. - */ - did_throw = FALSE; - /* - * "did_emsg" will be set to TRUE when emsg() is used, in which case we - * cancel the whole command line, and any if/endif or loop. - * If force_abort is set, we cancel everything. - */ - did_emsg = FALSE; - - /* - * KeyTyped is only set when calling vgetc(). Reset it here when not - * calling vgetc() (sourced command lines). - */ - if (!(flags & DOCMD_KEYTYPED) - && !getline_equal(fgetline, cookie, getexline)) - KeyTyped = FALSE; - - /* - * Continue executing command lines: - * - when inside an ":if", ":while" or ":for" - * - for multiple commands on one line, separated with '|' - * - when repeating until there are no more lines (for ":source") - */ - next_cmdline = cmdline; - do { - getline_is_func = getline_equal(fgetline, cookie, get_func_line); - - /* stop skipping cmds for an error msg after all endif/while/for */ - if (next_cmdline == NULL - && !force_abort - && cstack.cs_idx < 0 - && !(getline_is_func && func_has_abort(real_cookie)) - ) - did_emsg = FALSE; - - /* - * 1. If repeating a line in a loop, get a line from lines_ga. - * 2. If no line given: Get an allocated line with fgetline(). - * 3. If a line is given: Make a copy, so we can mess with it. - */ - - /* 1. If repeating, get a previous line from lines_ga. */ - if (cstack.cs_looplevel > 0 && current_line < lines_ga.ga_len) { - /* Each '|' separated command is stored separately in lines_ga, to - * be able to jump to it. Don't use next_cmdline now. */ - free(cmdline_copy); - cmdline_copy = NULL; - - /* Check if a function has returned or, unless it has an unclosed - * try conditional, aborted. */ - if (getline_is_func) { - if (do_profiling == PROF_YES) - func_line_end(real_cookie); - if (func_has_ended(real_cookie)) { - retval = FAIL; - break; - } - } else if (do_profiling == PROF_YES - && getline_equal(fgetline, cookie, getsourceline)) - script_line_end(); - - /* Check if a sourced file hit a ":finish" command. */ - if (source_finished(fgetline, cookie)) { - retval = FAIL; - break; - } - - /* If breakpoints have been added/deleted need to check for it. */ - if (breakpoint != NULL && dbg_tick != NULL - && *dbg_tick != debug_tick) { - *breakpoint = dbg_find_breakpoint( - getline_equal(fgetline, cookie, getsourceline), - fname, sourcing_lnum); - *dbg_tick = debug_tick; - } - - next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line; - sourcing_lnum = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum; - - /* Did we encounter a breakpoint? */ - if (breakpoint != NULL && *breakpoint != 0 - && *breakpoint <= sourcing_lnum) { - dbg_breakpoint(fname, sourcing_lnum); - /* Find next breakpoint. */ - *breakpoint = dbg_find_breakpoint( - getline_equal(fgetline, cookie, getsourceline), - fname, sourcing_lnum); - *dbg_tick = debug_tick; - } - if (do_profiling == PROF_YES) { - if (getline_is_func) - func_line_start(real_cookie); - else if (getline_equal(fgetline, cookie, getsourceline)) - script_line_start(); - } - } - - if (cstack.cs_looplevel > 0) { - /* Inside a while/for loop we need to store the lines and use them - * again. Pass a different "fgetline" function to do_one_cmd() - * below, so that it stores lines in or reads them from - * "lines_ga". Makes it possible to define a function inside a - * while/for loop. */ - cmd_getline = get_loop_line; - cmd_cookie = (void *)&cmd_loop_cookie; - cmd_loop_cookie.lines_gap = &lines_ga; - cmd_loop_cookie.current_line = current_line; - cmd_loop_cookie.getline = fgetline; - cmd_loop_cookie.cookie = cookie; - cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len); - } else { - cmd_getline = fgetline; - cmd_cookie = cookie; - } - - /* 2. If no line given, get an allocated line with fgetline(). */ - if (next_cmdline == NULL) { - /* - * Need to set msg_didout for the first line after an ":if", - * otherwise the ":if" will be overwritten. - */ - if (count == 1 && getline_equal(fgetline, cookie, getexline)) - msg_didout = TRUE; - if (fgetline == NULL || (next_cmdline = fgetline(':', cookie, - cstack.cs_idx < - 0 ? 0 : (cstack.cs_idx + 1) * 2 - )) == NULL) { - /* Don't call wait_return for aborted command line. The NULL - * returned for the end of a sourced file or executed function - * doesn't do this. */ - if (KeyTyped && !(flags & DOCMD_REPEAT)) - need_wait_return = FALSE; - retval = FAIL; - break; - } - used_getline = TRUE; - - /* - * Keep the first typed line. Clear it when more lines are typed. - */ - if (flags & DOCMD_KEEPLINE) { - free(repeat_cmdline); - if (count == 0) - repeat_cmdline = vim_strsave(next_cmdline); - else - repeat_cmdline = NULL; - } - } - /* 3. Make a copy of the command so we can mess with it. */ - else if (cmdline_copy == NULL) { - next_cmdline = vim_strsave(next_cmdline); - if (next_cmdline == NULL) { - EMSG(_(e_outofmem)); - retval = FAIL; - break; - } - } - cmdline_copy = next_cmdline; - - /* - * Save the current line when inside a ":while" or ":for", and when - * the command looks like a ":while" or ":for", because we may need it - * later. When there is a '|' and another command, it is stored - * separately, because we need to be able to jump back to it from an - * :endwhile/:endfor. - */ - if (current_line == lines_ga.ga_len - && (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) { - store_loop_line(&lines_ga, next_cmdline); - } - did_endif = FALSE; - - if (count++ == 0) { - /* - * All output from the commands is put below each other, without - * waiting for a return. Don't do this when executing commands - * from a script or when being called recursive (e.g. for ":e - * +command file"). - */ - if (!(flags & DOCMD_NOWAIT) && !recursive) { - msg_didout_before_start = msg_didout; - msg_didany = FALSE; /* no output yet */ - msg_start(); - msg_scroll = TRUE; /* put messages below each other */ - ++no_wait_return; /* don't wait for return until finished */ - ++RedrawingDisabled; - did_inc = TRUE; - } - } - - if (p_verbose >= 15 && sourcing_name != NULL) { - ++no_wait_return; - verbose_enter_scroll(); - - smsg((char_u *)_("line %" PRId64 ": %s"), - (int64_t)sourcing_lnum, cmdline_copy); - if (msg_silent == 0) - msg_puts((char_u *)"\n"); /* don't overwrite this */ - - verbose_leave_scroll(); - --no_wait_return; - } - - /* - * 2. Execute one '|' separated command. - * do_one_cmd() will return NULL if there is no trailing '|'. - * "cmdline_copy" can change, e.g. for '%' and '#' expansion. - */ - ++recursive; - next_cmdline = do_one_cmd(&cmdline_copy, flags & DOCMD_VERBOSE, - &cstack, - cmd_getline, cmd_cookie); - --recursive; - - if (cmd_cookie == (void *)&cmd_loop_cookie) - /* Use "current_line" from "cmd_loop_cookie", it may have been - * incremented when defining a function. */ - current_line = cmd_loop_cookie.current_line; - - if (next_cmdline == NULL) { - free(cmdline_copy); - cmdline_copy = NULL; - /* - * If the command was typed, remember it for the ':' register. - * Do this AFTER executing the command to make :@: work. - */ - if (getline_equal(fgetline, cookie, getexline) - && new_last_cmdline != NULL) { - free(last_cmdline); - last_cmdline = new_last_cmdline; - new_last_cmdline = NULL; - } - } else { - /* need to copy the command after the '|' to cmdline_copy, for the - * next do_one_cmd() */ - STRMOVE(cmdline_copy, next_cmdline); - next_cmdline = cmdline_copy; - } - - - /* reset did_emsg for a function that is not aborted by an error */ - if (did_emsg && !force_abort - && getline_equal(fgetline, cookie, get_func_line) - && !func_has_abort(real_cookie)) - did_emsg = FALSE; - - if (cstack.cs_looplevel > 0) { - ++current_line; - - /* - * An ":endwhile", ":endfor" and ":continue" is handled here. - * If we were executing commands, jump back to the ":while" or - * ":for". - * If we were not executing commands, decrement cs_looplevel. - */ - if (cstack.cs_lflags & (CSL_HAD_CONT | CSL_HAD_ENDLOOP)) { - cstack.cs_lflags &= ~(CSL_HAD_CONT | CSL_HAD_ENDLOOP); - - /* Jump back to the matching ":while" or ":for". Be careful - * not to use a cs_line[] from an entry that isn't a ":while" - * or ":for": It would make "current_line" invalid and can - * cause a crash. */ - if (!did_emsg && !got_int && !did_throw - && cstack.cs_idx >= 0 - && (cstack.cs_flags[cstack.cs_idx] - & (CSF_WHILE | CSF_FOR)) - && cstack.cs_line[cstack.cs_idx] >= 0 - && (cstack.cs_flags[cstack.cs_idx] & CSF_ACTIVE)) { - current_line = cstack.cs_line[cstack.cs_idx]; - /* remember we jumped there */ - cstack.cs_lflags |= CSL_HAD_LOOP; - line_breakcheck(); /* check if CTRL-C typed */ - - /* Check for the next breakpoint at or after the ":while" - * or ":for". */ - if (breakpoint != NULL) { - *breakpoint = dbg_find_breakpoint( - getline_equal(fgetline, cookie, getsourceline), - fname, - ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1); - *dbg_tick = debug_tick; - } - } else { - /* can only get here with ":endwhile" or ":endfor" */ - if (cstack.cs_idx >= 0) - rewind_conditionals(&cstack, cstack.cs_idx - 1, - CSF_WHILE | CSF_FOR, &cstack.cs_looplevel); - } - } - /* - * For a ":while" or ":for" we need to remember the line number. - */ - else if (cstack.cs_lflags & CSL_HAD_LOOP) { - cstack.cs_lflags &= ~CSL_HAD_LOOP; - cstack.cs_line[cstack.cs_idx] = current_line - 1; - } - } - - /* - * When not inside any ":while" loop, clear remembered lines. - */ - if (cstack.cs_looplevel == 0) { - if (lines_ga.ga_len > 0) { - sourcing_lnum = - ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum; - free_cmdlines(&lines_ga); - } - current_line = 0; - } - - /* - * A ":finally" makes did_emsg, got_int, and did_throw pending for - * being restored at the ":endtry". Reset them here and set the - * ACTIVE and FINALLY flags, so that the finally clause gets executed. - * This includes the case where a missing ":endif", ":endwhile" or - * ":endfor" was detected by the ":finally" itself. - */ - if (cstack.cs_lflags & CSL_HAD_FINA) { - cstack.cs_lflags &= ~CSL_HAD_FINA; - report_make_pending(cstack.cs_pending[cstack.cs_idx] - & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW), - did_throw ? (void *)current_exception : NULL); - did_emsg = got_int = did_throw = FALSE; - cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY; - } - - /* Update global "trylevel" for recursive calls to do_cmdline() from - * within this loop. */ - trylevel = initial_trylevel + cstack.cs_trylevel; - - /* - * If the outermost try conditional (across function calls and sourced - * files) is aborted because of an error, an interrupt, or an uncaught - * exception, cancel everything. If it is left normally, reset - * force_abort to get the non-EH compatible abortion behavior for - * the rest of the script. - */ - if (trylevel == 0 && !did_emsg && !got_int && !did_throw) - force_abort = FALSE; - - /* Convert an interrupt to an exception if appropriate. */ - (void)do_intthrow(&cstack); - - } - /* - * Continue executing command lines when: - * - no CTRL-C typed, no aborting error, no exception thrown or try - * conditionals need to be checked for executing finally clauses or - * catching an interrupt exception - * - didn't get an error message or lines are not typed - * - there is a command after '|', inside a :if, :while, :for or :try, or - * looping for ":source" command or function call. - */ - while (!((got_int - || (did_emsg && force_abort) || did_throw - ) - && cstack.cs_trylevel == 0 - ) - && !(did_emsg - /* Keep going when inside try/catch, so that the error can be - * deal with, except when it is a syntax error, it may cause - * the :endtry to be missed. */ - && (cstack.cs_trylevel == 0 || did_emsg_syntax) - && used_getline - && (getline_equal(fgetline, cookie, getexmodeline) - || getline_equal(fgetline, cookie, getexline))) - && (next_cmdline != NULL - || cstack.cs_idx >= 0 - || (flags & DOCMD_REPEAT))); - - free(cmdline_copy); - did_emsg_syntax = FALSE; - free_cmdlines(&lines_ga); - ga_clear(&lines_ga); - - if (cstack.cs_idx >= 0) { - /* - * If a sourced file or executed function ran to its end, report the - * unclosed conditional. - */ - if (!got_int && !did_throw - && ((getline_equal(fgetline, cookie, getsourceline) - && !source_finished(fgetline, cookie)) - || (getline_equal(fgetline, cookie, get_func_line) - && !func_has_ended(real_cookie)))) { - if (cstack.cs_flags[cstack.cs_idx] & CSF_TRY) - EMSG(_(e_endtry)); - else if (cstack.cs_flags[cstack.cs_idx] & CSF_WHILE) - EMSG(_(e_endwhile)); - else if (cstack.cs_flags[cstack.cs_idx] & CSF_FOR) - EMSG(_(e_endfor)); - else - EMSG(_(e_endif)); - } - - /* - * Reset "trylevel" in case of a ":finish" or ":return" or a missing - * ":endtry" in a sourced file or executed function. If the try - * conditional is in its finally clause, ignore anything pending. - * If it is in a catch clause, finish the caught exception. - * Also cleanup any "cs_forinfo" structures. - */ - do { - int idx = cleanup_conditionals(&cstack, 0, TRUE); - - if (idx >= 0) - --idx; /* remove try block not in its finally clause */ - rewind_conditionals(&cstack, idx, CSF_WHILE | CSF_FOR, - &cstack.cs_looplevel); - } while (cstack.cs_idx >= 0); - trylevel = initial_trylevel; - } - - /* If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory - * lack was reported above and the error message is to be converted to an - * exception, do this now after rewinding the cstack. */ - do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line) - ? (char_u *)"endfunction" : (char_u *)NULL); - - if (trylevel == 0) { - /* - * When an exception is being thrown out of the outermost try - * conditional, discard the uncaught exception, disable the conversion - * of interrupts or errors to exceptions, and ensure that no more - * commands are executed. - */ - if (did_throw) { - void *p = NULL; - char_u *saved_sourcing_name; - int saved_sourcing_lnum; - struct msglist *messages = NULL, *next; - - /* - * If the uncaught exception is a user exception, report it as an - * error. If it is an error exception, display the saved error - * message now. For an interrupt exception, do nothing; the - * interrupt message is given elsewhere. - */ - switch (current_exception->type) { - case ET_USER: - vim_snprintf((char *)IObuff, IOSIZE, - _("E605: Exception not caught: %s"), - current_exception->value); - p = vim_strsave(IObuff); - break; - case ET_ERROR: - messages = current_exception->messages; - current_exception->messages = NULL; - break; - case ET_INTERRUPT: - break; - default: - p = vim_strsave((char_u *)_(e_internal)); - } - - saved_sourcing_name = sourcing_name; - saved_sourcing_lnum = sourcing_lnum; - sourcing_name = current_exception->throw_name; - sourcing_lnum = current_exception->throw_lnum; - current_exception->throw_name = NULL; - - discard_current_exception(); /* uses IObuff if 'verbose' */ - suppress_errthrow = TRUE; - force_abort = TRUE; - - if (messages != NULL) { - do { - next = messages->next; - emsg(messages->msg); - free(messages->msg); - free(messages); - messages = next; - } while (messages != NULL); - } else if (p != NULL) { - emsg(p); - free(p); - } - free(sourcing_name); - sourcing_name = saved_sourcing_name; - sourcing_lnum = saved_sourcing_lnum; - } - /* - * On an interrupt or an aborting error not converted to an exception, - * disable the conversion of errors to exceptions. (Interrupts are not - * converted any more, here.) This enables also the interrupt message - * when force_abort is set and did_emsg unset in case of an interrupt - * from a finally clause after an error. - */ - else if (got_int || (did_emsg && force_abort)) - suppress_errthrow = TRUE; - } - - /* - * The current cstack will be freed when do_cmdline() returns. An uncaught - * exception will have to be rethrown in the previous cstack. If a function - * has just returned or a script file was just finished and the previous - * cstack belongs to the same function or, respectively, script file, it - * will have to be checked for finally clauses to be executed due to the - * ":return" or ":finish". This is done in do_one_cmd(). - */ - if (did_throw) - need_rethrow = TRUE; - if ((getline_equal(fgetline, cookie, getsourceline) - && ex_nesting_level > source_level(real_cookie)) - || (getline_equal(fgetline, cookie, get_func_line) - && ex_nesting_level > func_level(real_cookie) + 1)) { - if (!did_throw) - check_cstack = TRUE; - } else { - /* When leaving a function, reduce nesting level. */ - if (getline_equal(fgetline, cookie, get_func_line)) - --ex_nesting_level; - /* - * Go to debug mode when returning from a function in which we are - * single-stepping. - */ - if ((getline_equal(fgetline, cookie, getsourceline) - || getline_equal(fgetline, cookie, get_func_line)) - && ex_nesting_level + 1 <= debug_break_level) - do_debug(getline_equal(fgetline, cookie, getsourceline) - ? (char_u *)_("End of sourced file") - : (char_u *)_("End of function")); - } - - /* - * Restore the exception environment (done after returning from the - * debugger). - */ - if (flags & DOCMD_EXCRESET) - restore_dbg_stuff(&debug_saved); - - msg_list = saved_msg_list; - - /* - * If there was too much output to fit on the command line, ask the user to - * hit return before redrawing the screen. With the ":global" command we do - * this only once after the command is finished. - */ - if (did_inc) { - --RedrawingDisabled; - --no_wait_return; - msg_scroll = FALSE; - - /* - * When just finished an ":if"-":else" which was typed, no need to - * wait for hit-return. Also for an error situation. - */ - if (retval == FAIL - || (did_endif && KeyTyped && !did_emsg) - ) { - need_wait_return = FALSE; - msg_didany = FALSE; /* don't wait when restarting edit */ - } else if (need_wait_return) { - /* - * The msg_start() above clears msg_didout. The wait_return we do - * here should not overwrite the command that may be shown before - * doing that. - */ - msg_didout |= msg_didout_before_start; - wait_return(FALSE); - } - } - - did_endif = FALSE; /* in case do_cmdline used recursively */ - - --call_depth; - return retval; -} - -/* - * Obtain a line when inside a ":while" or ":for" loop. - */ -static char_u *get_loop_line(int c, void *cookie, int indent) -{ - struct loop_cookie *cp = (struct loop_cookie *)cookie; - wcmd_T *wp; - char_u *line; - - if (cp->current_line + 1 >= cp->lines_gap->ga_len) { - if (cp->repeating) - return NULL; /* trying to read past ":endwhile"/":endfor" */ - - /* First time inside the ":while"/":for": get line normally. */ - if (cp->getline == NULL) - line = getcmdline(c, 0L, indent); - else - line = cp->getline(c, cp->cookie, indent); - if (line != NULL) { - store_loop_line(cp->lines_gap, line); - ++cp->current_line; - } - - return line; - } - - KeyTyped = FALSE; - ++cp->current_line; - wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line; - sourcing_lnum = wp->lnum; - return vim_strsave(wp->line); -} - -/* - * Store a line in "gap" so that a ":while" loop can execute it again. - */ -static void store_loop_line(garray_T *gap, char_u *line) -{ - ga_grow(gap, 1); - ((wcmd_T *)(gap->ga_data))[gap->ga_len].line = vim_strsave(line); - ((wcmd_T *)(gap->ga_data))[gap->ga_len].lnum = sourcing_lnum; - ++gap->ga_len; -} - -/* - * Free the lines stored for a ":while" or ":for" loop. - */ -static void free_cmdlines(garray_T *gap) -{ - while (gap->ga_len > 0) { - free(((wcmd_T *)(gap->ga_data))[gap->ga_len - 1].line); - --gap->ga_len; - } -} - -/* - * If "fgetline" is get_loop_line(), return TRUE if the getline it uses equals - * "func". * Otherwise return TRUE when "fgetline" equals "func". - */ -int getline_equal(fgetline, cookie, func) -char_u *(*fgetline)(int, void *, int); -void *cookie; /* argument for fgetline() */ -char_u *(*func)(int, void *, int); -{ - char_u *(*gp)(int, void *, int); - struct loop_cookie *cp; - - /* When "fgetline" is "get_loop_line()" use the "cookie" to find the - * function that's originally used to obtain the lines. This may be - * nested several levels. */ - gp = fgetline; - cp = (struct loop_cookie *)cookie; - while (gp == get_loop_line) { - gp = cp->getline; - cp = cp->cookie; - } - return gp == func; -} - -/* - * If "fgetline" is get_loop_line(), return the cookie used by the original - * getline function. Otherwise return "cookie". - */ -void * getline_cookie(fgetline, cookie) -char_u *(*fgetline)(int, void *, int); -void *cookie; /* argument for fgetline() */ -{ - char_u *(*gp)(int, void *, int); - struct loop_cookie *cp; - - /* When "fgetline" is "get_loop_line()" use the "cookie" to find the - * cookie that's originally used to obtain the lines. This may be nested - * several levels. */ - gp = fgetline; - cp = (struct loop_cookie *)cookie; - while (gp == get_loop_line) { - gp = cp->getline; - cp = cp->cookie; - } - return cp; -} - -/* - * Execute one Ex command. - * - * If 'sourcing' is TRUE, the command will be included in the error message. - * - * 1. skip comment lines and leading space - * 2. handle command modifiers - * 3. parse range - * 4. parse command - * 5. parse arguments - * 6. switch on command name - * - * Note: "fgetline" can be NULL. - * - * This function may be called recursively! - */ -static char_u * do_one_cmd(cmdlinep, sourcing, - cstack, - fgetline, cookie) -char_u **cmdlinep; -int sourcing; -struct condstack *cstack; -char_u *(*fgetline)(int, void *, int); -void *cookie; /*argument for fgetline() */ -{ - char_u *p; - linenr_T lnum; - long n; - char_u *errormsg = NULL; /* error message */ - exarg_T ea; /* Ex command arguments */ - long verbose_save = -1; - int save_msg_scroll = msg_scroll; - int save_msg_silent = -1; - int did_esilent = 0; -#ifdef HAVE_SANDBOX - int did_sandbox = FALSE; -#endif - cmdmod_T save_cmdmod; - int ni; /* set when Not Implemented */ - - memset(&ea, 0, sizeof(ea)); - ea.line1 = 1; - ea.line2 = 1; - ++ex_nesting_level; - - /* When the last file has not been edited :q has to be typed twice. */ - if (quitmore - /* avoid that a function call in 'statusline' does this */ - && !getline_equal(fgetline, cookie, get_func_line) - /* avoid that an autocommand, e.g. QuitPre, does this */ - && !getline_equal(fgetline, cookie, getnextac) - ) - --quitmore; - - /* - * Reset browse, confirm, etc.. They are restored when returning, for - * recursive calls. - */ - save_cmdmod = cmdmod; - memset(&cmdmod, 0, sizeof(cmdmod)); - - /* "#!anything" is handled like a comment. */ - if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') - goto doend; - - /* - * Repeat until no more command modifiers are found. - */ - ea.cmd = *cmdlinep; - for (;; ) { - /* - * 1. skip comment lines and leading white space and colons - */ - while (*ea.cmd == ' ' || *ea.cmd == '\t' || *ea.cmd == ':') - ++ea.cmd; - - /* in ex mode, an empty line works like :+ */ - if (*ea.cmd == NUL && exmode_active - && (getline_equal(fgetline, cookie, getexmodeline) - || getline_equal(fgetline, cookie, getexline)) - && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - ea.cmd = (char_u *)"+"; - ex_pressedreturn = TRUE; - } - - /* ignore comment and empty lines */ - if (*ea.cmd == '"') - goto doend; - if (*ea.cmd == NUL) { - ex_pressedreturn = TRUE; - goto doend; - } - - /* - * 2. handle command modifiers. - */ - p = ea.cmd; - if (VIM_ISDIGIT(*ea.cmd)) - p = skipwhite(skipdigits(ea.cmd)); - switch (*p) { - /* When adding an entry, also modify cmd_exists(). */ - case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3)) - break; - cmdmod.split |= WSP_ABOVE; - continue; - - case 'b': if (checkforcmd(&ea.cmd, "belowright", 3)) { - cmdmod.split |= WSP_BELOW; - continue; - } - if (checkforcmd(&ea.cmd, "browse", 3)) { - continue; - } - if (!checkforcmd(&ea.cmd, "botright", 2)) - break; - cmdmod.split |= WSP_BOT; - continue; - - case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4)) - break; - cmdmod.confirm = TRUE; - continue; - - case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) { - cmdmod.keepmarks = TRUE; - continue; - } - if (checkforcmd(&ea.cmd, "keepalt", 5)) { - cmdmod.keepalt = TRUE; - continue; - } - if (checkforcmd(&ea.cmd, "keeppatterns", 5)) { - cmdmod.keeppatterns = TRUE; - continue; - } - if (!checkforcmd(&ea.cmd, "keepjumps", 5)) - break; - cmdmod.keepjumps = TRUE; - continue; - - /* ":hide" and ":hide | cmd" are not modifiers */ - case 'h': if (p != ea.cmd || !checkforcmd(&p, "hide", 3) - || *p == NUL || ends_excmd(*p)) - break; - ea.cmd = p; - cmdmod.hide = TRUE; - continue; - - case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) { - cmdmod.lockmarks = TRUE; - continue; - } - - if (!checkforcmd(&ea.cmd, "leftabove", 5)) - break; - cmdmod.split |= WSP_ABOVE; - continue; - - case 'n': - if (checkforcmd(&ea.cmd, "noautocmd", 3)) { - if (cmdmod.save_ei == NULL) { - /* Set 'eventignore' to "all". Restore the - * existing option value later. */ - cmdmod.save_ei = vim_strsave(p_ei); - set_string_option_direct( - (char_u *)"ei", -1, (char_u *)"all", OPT_FREE, SID_NONE); - } - continue; - } - if (!checkforcmd(&ea.cmd, "noswapfile", 6)) { - break; - } - cmdmod.noswapfile = true; - continue; - - case 'r': if (!checkforcmd(&ea.cmd, "rightbelow", 6)) - break; - cmdmod.split |= WSP_BELOW; - continue; - - case 's': if (checkforcmd(&ea.cmd, "sandbox", 3)) { -#ifdef HAVE_SANDBOX - if (!did_sandbox) - ++sandbox; - did_sandbox = TRUE; -#endif - continue; - } - if (!checkforcmd(&ea.cmd, "silent", 3)) - break; - if (save_msg_silent == -1) - save_msg_silent = msg_silent; - ++msg_silent; - if (*ea.cmd == '!' && !vim_iswhite(ea.cmd[-1])) { - /* ":silent!", but not "silent !cmd" */ - ea.cmd = skipwhite(ea.cmd + 1); - ++emsg_silent; - ++did_esilent; - } - continue; - - case 't': if (checkforcmd(&p, "tab", 3)) { - if (vim_isdigit(*ea.cmd)) - cmdmod.tab = atoi((char *)ea.cmd) + 1; - else - cmdmod.tab = tabpage_index(curtab) + 1; - ea.cmd = p; - continue; - } - if (!checkforcmd(&ea.cmd, "topleft", 2)) - break; - cmdmod.split |= WSP_TOP; - continue; - - case 'u': if (!checkforcmd(&ea.cmd, "unsilent", 3)) - break; - if (save_msg_silent == -1) - save_msg_silent = msg_silent; - msg_silent = 0; - continue; - - case 'v': if (checkforcmd(&ea.cmd, "vertical", 4)) { - cmdmod.split |= WSP_VERT; - continue; - } - if (!checkforcmd(&p, "verbose", 4)) - break; - if (verbose_save < 0) - verbose_save = p_verbose; - if (vim_isdigit(*ea.cmd)) - p_verbose = atoi((char *)ea.cmd); - else - p_verbose = 1; - ea.cmd = p; - continue; - } - break; - } - - ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0 - && !(cstack->cs_flags[cstack-> - cs_idx] - & CSF_ACTIVE)); - - /* Count this line for profiling if ea.skip is FALSE. */ - if (do_profiling == PROF_YES && !ea.skip) { - if (getline_equal(fgetline, cookie, get_func_line)) - func_line_exec(getline_cookie(fgetline, cookie)); - else if (getline_equal(fgetline, cookie, getsourceline)) - script_line_exec(); - } - - /* May go to debug mode. If this happens and the ">quit" debug command is - * used, throw an interrupt exception and skip the next command. */ - dbg_check_breakpoint(&ea); - if (!ea.skip && got_int) { - ea.skip = TRUE; - (void)do_intthrow(cstack); - } - - /* - * 3. parse a range specifier of the form: addr [,addr] [;addr] .. - * - * where 'addr' is: - * - * % (entire file) - * $ [+-NUM] - * 'x [+-NUM] (where x denotes a currently defined mark) - * . [+-NUM] - * [+-NUM].. - * NUM - * - * The ea.cmd pointer is updated to point to the first character following the - * range spec. If an initial address is found, but no second, the upper bound - * is equal to the lower. - */ - - /* repeat for all ',' or ';' separated addresses */ - for (;; ) { - ea.line1 = ea.line2; - ea.line2 = curwin->w_cursor.lnum; /* default is current line number */ - ea.cmd = skipwhite(ea.cmd); - lnum = get_address(&ea.cmd, ea.skip, ea.addr_count == 0); - if (ea.cmd == NULL) /* error detected */ - goto doend; - if (lnum == MAXLNUM) { - if (*ea.cmd == '%') { /* '%' - all lines */ - ++ea.cmd; - ea.line1 = 1; - ea.line2 = curbuf->b_ml.ml_line_count; - ++ea.addr_count; - } - /* '*' - visual area */ - else if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) { - pos_T *fp; - - ++ea.cmd; - if (!ea.skip) { - fp = getmark('<', FALSE); - if (check_mark(fp) == FAIL) - goto doend; - ea.line1 = fp->lnum; - fp = getmark('>', FALSE); - if (check_mark(fp) == FAIL) - goto doend; - ea.line2 = fp->lnum; - ++ea.addr_count; - } - } - } else - ea.line2 = lnum; - ea.addr_count++; - - if (*ea.cmd == ';') { - if (!ea.skip) - curwin->w_cursor.lnum = ea.line2; - } else if (*ea.cmd != ',') - break; - ++ea.cmd; - } - - /* One address given: set start and end lines */ - if (ea.addr_count == 1) { - ea.line1 = ea.line2; - /* ... but only implicit: really no address given */ - if (lnum == MAXLNUM) - ea.addr_count = 0; - } - - /* Don't leave the cursor on an illegal line (caused by ';') */ - check_cursor_lnum(); - - /* - * 4. parse command - */ - - /* - * Skip ':' and any white space - */ - ea.cmd = skipwhite(ea.cmd); - while (*ea.cmd == ':') - ea.cmd = skipwhite(ea.cmd + 1); - - /* - * If we got a line, but no command, then go to the line. - * If we find a '|' or '\n' we set ea.nextcmd. - */ - if (*ea.cmd == NUL || *ea.cmd == '"' || - (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) { - /* - * strange vi behaviour: - * ":3" jumps to line 3 - * ":3|..." prints line 3 - * ":|" prints current line - */ - if (ea.skip) /* skip this if inside :if */ - goto doend; - if (*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2)) { - ea.cmdidx = CMD_print; - ea.argt = RANGE+COUNT+TRLBAR; - if ((errormsg = invalid_range(&ea)) == NULL) { - correct_range(&ea); - ex_print(&ea); - } - } else if (ea.addr_count != 0) { - if (ea.line2 > curbuf->b_ml.ml_line_count) { - /* With '-' in 'cpoptions' a line number past the file is an - * error, otherwise put it at the end of the file. */ - if (vim_strchr(p_cpo, CPO_MINUS) != NULL) - ea.line2 = -1; - else - ea.line2 = curbuf->b_ml.ml_line_count; - } - - if (ea.line2 < 0) - errormsg = (char_u *)_(e_invrange); - else { - if (ea.line2 == 0) - curwin->w_cursor.lnum = 1; - else - curwin->w_cursor.lnum = ea.line2; - beginline(BL_SOL | BL_FIX); - } - } - goto doend; - } - - /* Find the command and let "p" point to after it. */ - p = find_command(&ea, NULL); - - if (p == NULL) { - if (!ea.skip) - errormsg = (char_u *)_("E464: Ambiguous use of user-defined command"); - goto doend; - } - /* Check for wrong commands. */ - if (*p == '!' && ea.cmd[1] == 0151 && ea.cmd[0] == 78) { - errormsg = uc_fun_cmd(); - goto doend; - } - if (ea.cmdidx == CMD_SIZE) { - if (!ea.skip) { - STRCPY(IObuff, _("E492: Not an editor command")); - if (!sourcing) - append_command(*cmdlinep); - errormsg = IObuff; - did_emsg_syntax = TRUE; - } - goto doend; - } - - ni = ( - !USER_CMDIDX(ea.cmdidx) && - (cmdnames[ea.cmdidx].cmd_func == ex_ni -#ifdef HAVE_EX_SCRIPT_NI - || cmdnames[ea.cmdidx].cmd_func == ex_script_ni -#endif - )); - - - /* forced commands */ - if (*p == '!' && ea.cmdidx != CMD_substitute - && ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic) { - ++p; - ea.forceit = TRUE; - } else - ea.forceit = FALSE; - - /* - * 5. parse arguments - */ - if (!USER_CMDIDX(ea.cmdidx)) - ea.argt = (long)cmdnames[(int)ea.cmdidx].cmd_argt; - - if (!ea.skip) { -#ifdef HAVE_SANDBOX - if (sandbox != 0 && !(ea.argt & SBOXOK)) { - /* Command not allowed in sandbox. */ - errormsg = (char_u *)_(e_sandbox); - goto doend; - } -#endif - if (!curbuf->b_p_ma && (ea.argt & MODIFY)) { - /* Command not allowed in non-'modifiable' buffer */ - errormsg = (char_u *)_(e_modifiable); - goto doend; - } - - if (text_locked() && !(ea.argt & CMDWIN) - && !USER_CMDIDX(ea.cmdidx) - ) { - /* Command not allowed when editing the command line. */ - if (cmdwin_type != 0) - errormsg = (char_u *)_(e_cmdwin); - else - errormsg = (char_u *)_(e_secure); - goto doend; - } - /* Disallow editing another buffer when "curbuf_lock" is set. - * Do allow ":edit" (check for argument later). - * Do allow ":checktime" (it's postponed). */ - if (!(ea.argt & CMDWIN) - && ea.cmdidx != CMD_edit - && ea.cmdidx != CMD_checktime - && !USER_CMDIDX(ea.cmdidx) - && curbuf_locked()) - goto doend; - - if (!ni && !(ea.argt & RANGE) && ea.addr_count > 0) { - /* no range allowed */ - errormsg = (char_u *)_(e_norange); - goto doend; - } - } - - if (!ni && !(ea.argt & BANG) && ea.forceit) { /* no allowed */ - errormsg = (char_u *)_(e_nobang); - goto doend; - } - - /* - * Don't complain about the range if it is not used - * (could happen if line_count is accidentally set to 0). - */ - if (!ea.skip && !ni) { - /* - * If the range is backwards, ask for confirmation and, if given, swap - * ea.line1 & ea.line2 so it's forwards again. - * When global command is busy, don't ask, will fail below. - */ - if (!global_busy && ea.line1 > ea.line2) { - if (msg_silent == 0) { - if (sourcing || exmode_active) { - errormsg = (char_u *)_("E493: Backwards range given"); - goto doend; - } - if (ask_yesno((char_u *) - _("Backwards range given, OK to swap"), FALSE) != 'y') - goto doend; - } - lnum = ea.line1; - ea.line1 = ea.line2; - ea.line2 = lnum; - } - if ((errormsg = invalid_range(&ea)) != NULL) - goto doend; - } - - if ((ea.argt & NOTADR) && ea.addr_count == 0) /* default is 1, not cursor */ - ea.line2 = 1; - - correct_range(&ea); - - if (((ea.argt & WHOLEFOLD) || ea.addr_count >= 2) && !global_busy) { - /* Put the first line at the start of a closed fold, put the last line - * at the end of a closed fold. */ - (void)hasFolding(ea.line1, &ea.line1, NULL); - (void)hasFolding(ea.line2, NULL, &ea.line2); - } - - /* - * For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg' - * option here, so things like % get expanded. - */ - p = replace_makeprg(&ea, p, cmdlinep); - if (p == NULL) - goto doend; - - /* - * Skip to start of argument. - * Don't do this for the ":!" command, because ":!! -l" needs the space. - */ - if (ea.cmdidx == CMD_bang) - ea.arg = p; - else - ea.arg = skipwhite(p); - - /* - * Check for "++opt=val" argument. - * Must be first, allow ":w ++enc=utf8 !cmd" - */ - if (ea.argt & ARGOPT) - while (ea.arg[0] == '+' && ea.arg[1] == '+') - if (getargopt(&ea) == FAIL && !ni) { - errormsg = (char_u *)_(e_invarg); - goto doend; - } - - if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { - if (*ea.arg == '>') { /* append */ - if (*++ea.arg != '>') { /* typed wrong */ - errormsg = (char_u *)_("E494: Use w or w>>"); - goto doend; - } - ea.arg = skipwhite(ea.arg + 1); - ea.append = TRUE; - } else if (*ea.arg == '!' && ea.cmdidx == CMD_write) { /* :w !filter */ - ++ea.arg; - ea.usefilter = TRUE; - } - } - - if (ea.cmdidx == CMD_read) { - if (ea.forceit) { - ea.usefilter = TRUE; /* :r! filter if ea.forceit */ - ea.forceit = FALSE; - } else if (*ea.arg == '!') { /* :r !filter */ - ++ea.arg; - ea.usefilter = TRUE; - } - } - - if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { - ea.amount = 1; - while (*ea.arg == *ea.cmd) { /* count number of '>' or '<' */ - ++ea.arg; - ++ea.amount; - } - ea.arg = skipwhite(ea.arg); - } - - /* - * Check for "+command" argument, before checking for next command. - * Don't do this for ":read !cmd" and ":write !cmd". - */ - if ((ea.argt & EDITCMD) && !ea.usefilter) - ea.do_ecmd_cmd = getargcmd(&ea.arg); - - /* - * Check for '|' to separate commands and '"' to start comments. - * Don't do this for ":read !cmd" and ":write !cmd". - */ - if ((ea.argt & TRLBAR) && !ea.usefilter) - separate_nextcmd(&ea); - - /* - * Check for to end a shell command. - * Also do this for ":read !cmd", ":write !cmd" and ":global". - * Any others? - */ - else if (ea.cmdidx == CMD_bang - || ea.cmdidx == CMD_global - || ea.cmdidx == CMD_vglobal - || ea.usefilter) { - for (p = ea.arg; *p; ++p) { - /* Remove one backslash before a newline, so that it's possible to - * pass a newline to the shell and also a newline that is preceded - * with a backslash. This makes it impossible to end a shell - * command in a backslash, but that doesn't appear useful. - * Halving the number of backslashes is incompatible with previous - * versions. */ - if (*p == '\\' && p[1] == '\n') - STRMOVE(p, p + 1); - else if (*p == '\n') { - ea.nextcmd = p + 1; - *p = NUL; - break; - } - } - } - - if ((ea.argt & DFLALL) && ea.addr_count == 0) { - ea.line1 = 1; - ea.line2 = curbuf->b_ml.ml_line_count; - } - - /* accept numbered register only when no count allowed (:put) */ - if ( (ea.argt & REGSTR) - && *ea.arg != NUL - /* Do not allow register = for user commands */ - && (!USER_CMDIDX(ea.cmdidx) || *ea.arg != '=') - && !((ea.argt & COUNT) && VIM_ISDIGIT(*ea.arg))) { - /* check these explicitly for a more specific error message */ - if (*ea.arg == '*' || *ea.arg == '+') { - errormsg = (char_u *)_(e_invalidreg); - goto doend; - } - if ( - valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put - && USER_CMDIDX(ea.cmdidx))) - ) { - ea.regname = *ea.arg++; - /* for '=' register: accept the rest of the line as an expression */ - if (ea.arg[-1] == '=' && ea.arg[0] != NUL) { - set_expr_line(vim_strsave(ea.arg)); - ea.arg += STRLEN(ea.arg); - } - ea.arg = skipwhite(ea.arg); - } - } - - /* - * Check for a count. When accepting a BUFNAME, don't use "123foo" as a - * count, it's a buffer name. - */ - if ((ea.argt & COUNT) && VIM_ISDIGIT(*ea.arg) - && (!(ea.argt & BUFNAME) || *(p = skipdigits(ea.arg)) == NUL - || vim_iswhite(*p))) { - n = getdigits(&ea.arg); - ea.arg = skipwhite(ea.arg); - if (n <= 0 && !ni && (ea.argt & ZEROR) == 0) { - errormsg = (char_u *)_(e_zerocount); - goto doend; - } - if (ea.argt & NOTADR) { /* e.g. :buffer 2, :sleep 3 */ - ea.line2 = n; - if (ea.addr_count == 0) - ea.addr_count = 1; - } else { - ea.line1 = ea.line2; - ea.line2 += n - 1; - ++ea.addr_count; - /* - * Be vi compatible: no error message for out of range. - */ - if (ea.line2 > curbuf->b_ml.ml_line_count) - ea.line2 = curbuf->b_ml.ml_line_count; - } - } - - /* - * Check for flags: 'l', 'p' and '#'. - */ - if (ea.argt & EXFLAGS) - get_flags(&ea); - /* no arguments allowed */ - if (!ni && !(ea.argt & EXTRA) && *ea.arg != NUL - && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & TRLBAR) == 0)) { - errormsg = (char_u *)_(e_trailing); - goto doend; - } - - if (!ni && (ea.argt & NEEDARG) && *ea.arg == NUL) { - errormsg = (char_u *)_(e_argreq); - goto doend; - } - - /* - * Skip the command when it's not going to be executed. - * The commands like :if, :endif, etc. always need to be executed. - * Also make an exception for commands that handle a trailing command - * themselves. - */ - if (ea.skip) { - switch (ea.cmdidx) { - /* commands that need evaluation */ - case CMD_while: - case CMD_endwhile: - case CMD_for: - case CMD_endfor: - case CMD_if: - case CMD_elseif: - case CMD_else: - case CMD_endif: - case CMD_try: - case CMD_catch: - case CMD_finally: - case CMD_endtry: - case CMD_function: - break; - - /* Commands that handle '|' themselves. Check: A command should - * either have the TRLBAR flag, appear in this list or appear in - * the list at ":help :bar". */ - case CMD_aboveleft: - case CMD_and: - case CMD_belowright: - case CMD_botright: - case CMD_browse: - case CMD_call: - case CMD_confirm: - case CMD_delfunction: - case CMD_djump: - case CMD_dlist: - case CMD_dsearch: - case CMD_dsplit: - case CMD_echo: - case CMD_echoerr: - case CMD_echomsg: - case CMD_echon: - case CMD_execute: - case CMD_help: - case CMD_hide: - case CMD_ijump: - case CMD_ilist: - case CMD_isearch: - case CMD_isplit: - case CMD_keepalt: - case CMD_keepjumps: - case CMD_keepmarks: - case CMD_keeppatterns: - case CMD_leftabove: - case CMD_let: - case CMD_lockmarks: - case CMD_match: - case CMD_noautocmd: - case CMD_noswapfile: - case CMD_psearch: - case CMD_return: - case CMD_rightbelow: - case CMD_silent: - case CMD_smagic: - case CMD_snomagic: - case CMD_substitute: - case CMD_syntax: - case CMD_tab: - case CMD_throw: - case CMD_tilde: - case CMD_topleft: - case CMD_unlet: - case CMD_verbose: - case CMD_vertical: - case CMD_wincmd: - break; - - default: goto doend; - } - } - - if (ea.argt & XFILE) { - if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) - goto doend; - } - - /* - * Accept buffer name. Cannot be used at the same time with a buffer - * number. Don't do this for a user command. - */ - if ((ea.argt & BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 - && !USER_CMDIDX(ea.cmdidx) - ) { - /* - * :bdelete, :bwipeout and :bunload take several arguments, separated - * by spaces: find next space (skipping over escaped characters). - * The others take one argument: ignore trailing spaces. - */ - if (ea.cmdidx == CMD_bdelete || ea.cmdidx == CMD_bwipeout - || ea.cmdidx == CMD_bunload) - p = skiptowhite_esc(ea.arg); - else { - p = ea.arg + STRLEN(ea.arg); - while (p > ea.arg && vim_iswhite(p[-1])) - --p; - } - ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & BUFUNL) != 0, - FALSE, FALSE); - if (ea.line2 < 0) /* failed */ - goto doend; - ea.addr_count = 1; - ea.arg = skipwhite(p); - } - - /* - * 6. switch on command name - * - * The "ea" structure holds the arguments that can be used. - */ - ea.cmdlinep = cmdlinep; - ea.getline = fgetline; - ea.cookie = cookie; - ea.cstack = cstack; - - if (USER_CMDIDX(ea.cmdidx)) { - /* - * Execute a user-defined command. - */ - do_ucmd(&ea); - } else { - /* - * Call the function to execute the command. - */ - ea.errmsg = NULL; - (cmdnames[ea.cmdidx].cmd_func)(&ea); - if (ea.errmsg != NULL) - errormsg = (char_u *)_(ea.errmsg); - } - - /* - * If the command just executed called do_cmdline(), any throw or ":return" - * or ":finish" encountered there must also check the cstack of the still - * active do_cmdline() that called this do_one_cmd(). Rethrow an uncaught - * exception, or reanimate a returned function or finished script file and - * return or finish it again. - */ - if (need_rethrow) - do_throw(cstack); - else if (check_cstack) { - if (source_finished(fgetline, cookie)) - do_finish(&ea, TRUE); - else if (getline_equal(fgetline, cookie, get_func_line) - && current_func_returned()) - do_return(&ea, TRUE, FALSE, NULL); - } - need_rethrow = check_cstack = FALSE; - -doend: - if (curwin->w_cursor.lnum == 0) /* can happen with zero line number */ - curwin->w_cursor.lnum = 1; - - if (errormsg != NULL && *errormsg != NUL && !did_emsg) { - if (sourcing) { - if (errormsg != IObuff) { - STRCPY(IObuff, errormsg); - errormsg = IObuff; - } - append_command(*cmdlinep); - } - emsg(errormsg); - } - do_errthrow(cstack, - (ea.cmdidx != CMD_SIZE - && !USER_CMDIDX(ea.cmdidx) - ) ? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL); - - if (verbose_save >= 0) - p_verbose = verbose_save; - if (cmdmod.save_ei != NULL) { - /* Restore 'eventignore' to the value before ":noautocmd". */ - set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei, - OPT_FREE, SID_NONE); - free_string_option(cmdmod.save_ei); - } - - cmdmod = save_cmdmod; - - if (save_msg_silent != -1) { - /* messages could be enabled for a serious error, need to check if the - * counters don't become negative */ - if (!did_emsg || msg_silent > save_msg_silent) - msg_silent = save_msg_silent; - emsg_silent -= did_esilent; - if (emsg_silent < 0) - emsg_silent = 0; - /* Restore msg_scroll, it's set by file I/O commands, even when no - * message is actually displayed. */ - msg_scroll = save_msg_scroll; - - /* "silent reg" or "silent echo x" inside "redir" leaves msg_col - * somewhere in the line. Put it back in the first column. */ - if (redirecting()) - msg_col = 0; - } - -#ifdef HAVE_SANDBOX - if (did_sandbox) - --sandbox; -#endif - - if (ea.nextcmd && *ea.nextcmd == NUL) /* not really a next command */ - ea.nextcmd = NULL; - - --ex_nesting_level; - - return ea.nextcmd; -} - -/* - * Check for an Ex command with optional tail. - * If there is a match advance "pp" to the argument and return TRUE. - */ -int -checkforcmd ( - char_u **pp, /* start of command */ - char *cmd, /* name of command */ - int len /* required length */ -) -{ - int i; - - for (i = 0; cmd[i] != NUL; ++i) - if (((char_u *)cmd)[i] != (*pp)[i]) - break; - if (i >= len && !isalpha((*pp)[i])) { - *pp = skipwhite(*pp + i); - return TRUE; - } - return FALSE; -} - -/* - * Append "cmd" to the error message in IObuff. - * Takes care of limiting the length and handling 0xa0, which would be - * invisible otherwise. - */ -static void append_command(char_u *cmd) -{ - char_u *s = cmd; - char_u *d; - - STRCAT(IObuff, ": "); - d = IObuff + STRLEN(IObuff); - while (*s != NUL && d - IObuff < IOSIZE - 7) { - if ( - enc_utf8 ? (s[0] == 0xc2 && s[1] == 0xa0) : - *s == 0xa0) { - s += - enc_utf8 ? 2 : - 1; - STRCPY(d, ""); - d += 4; - } else - MB_COPY_CHAR(s, d); - } - *d = NUL; -} - -/* - * Find an Ex command by its name, either built-in or user. - * Start of the name can be found at eap->cmd. - * Returns pointer to char after the command name. - * "full" is set to TRUE if the whole command name matched. - * Returns NULL for an ambiguous user command. - */ -static char_u *find_command(exarg_T *eap, int *full) -{ - int len; - char_u *p; - int i; - - /* - * Isolate the command and search for it in the command table. - * Exceptions: - * - the 'k' command can directly be followed by any character. - * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' - * but :sre[wind] is another command, as are :scrip[tnames], - * :scs[cope], :sim[alt], :sig[ns] and :sil[ent]. - * - the "d" command can directly be followed by 'l' or 'p' flag. - */ - p = eap->cmd; - if (*p == 'k') { - eap->cmdidx = CMD_k; - ++p; - } else if (p[0] == 's' - && ((p[1] == 'c' && p[2] != 's' && p[2] != 'r' - && p[3] != 'i' && p[4] != 'p') - || p[1] == 'g' - || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g') - || p[1] == 'I' - || (p[1] == 'r' && p[2] != 'e'))) { - eap->cmdidx = CMD_substitute; - ++p; - } else { - while (ASCII_ISALPHA(*p)) - ++p; - /* for python 3.x support ":py3", ":python3", ":py3file", etc. */ - if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y') - while (ASCII_ISALNUM(*p)) - ++p; - - /* check for non-alpha command */ - if (p == eap->cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) - ++p; - len = (int)(p - eap->cmd); - if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) { - /* Check for ":dl", ":dell", etc. to ":deletel": that's - * :delete with the 'l' flag. Same for 'p'. */ - for (i = 0; i < len; ++i) - if (eap->cmd[i] != ((char_u *)"delete")[i]) - break; - if (i == len - 1) { - --len; - if (p[-1] == 'l') - eap->flags |= EXFLAG_LIST; - else - eap->flags |= EXFLAG_PRINT; - } - } - - if (ASCII_ISLOWER(*eap->cmd)) - eap->cmdidx = cmdidxs[CharOrdLow(*eap->cmd)]; - else - eap->cmdidx = cmdidxs[26]; - - for (; (int)eap->cmdidx < (int)CMD_SIZE; - eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) - if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, (char *)eap->cmd, - (size_t)len) == 0) { - if (full != NULL - && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) - *full = TRUE; - break; - } - - /* Look for a user defined command as a last resort. Let ":Print" be - * overruled by a user defined command. */ - if ((eap->cmdidx == CMD_SIZE || eap->cmdidx == CMD_Print) - && *eap->cmd >= 'A' && *eap->cmd <= 'Z') { - /* User defined commands may contain digits. */ - while (ASCII_ISALNUM(*p)) - ++p; - p = find_ucmd(eap, p, full, NULL, NULL); - } - if (p == eap->cmd) - eap->cmdidx = CMD_SIZE; - } - - return p; -} - -/* - * Search for a user command that matches "eap->cmd". - * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". - * Return a pointer to just after the command. - * Return NULL if there is no matching command. - */ -static char_u * -find_ucmd ( - exarg_T *eap, - char_u *p, /* end of the command (possibly including count) */ - int *full, /* set to TRUE for a full match */ - expand_T *xp, /* used for completion, NULL otherwise */ - int *compl /* completion flags or NULL */ -) -{ - int len = (int)(p - eap->cmd); - int j, k, matchlen = 0; - ucmd_T *uc; - int found = FALSE; - int possible = FALSE; - char_u *cp, *np; /* Point into typed cmd and test name */ - garray_T *gap; - int amb_local = FALSE; /* Found ambiguous buffer-local command, - only full match global is accepted. */ - - /* - * Look for buffer-local user commands first, then global ones. - */ - gap = &curbuf->b_ucmds; - for (;; ) { - for (j = 0; j < gap->ga_len; ++j) { - uc = USER_CMD_GA(gap, j); - cp = eap->cmd; - np = uc->uc_name; - k = 0; - while (k < len && *np != NUL && *cp++ == *np++) - k++; - if (k == len || (*np == NUL && vim_isdigit(eap->cmd[k]))) { - /* If finding a second match, the command is ambiguous. But - * not if a buffer-local command wasn't a full match and a - * global command is a full match. */ - if (k == len && found && *np != NUL) { - if (gap == &ucmds) - return NULL; - amb_local = TRUE; - } - - if (!found || (k == len && *np == NUL)) { - /* If we matched up to a digit, then there could - * be another command including the digit that we - * should use instead. - */ - if (k == len) - found = TRUE; - else - possible = TRUE; - - if (gap == &ucmds) - eap->cmdidx = CMD_USER; - else - eap->cmdidx = CMD_USER_BUF; - eap->argt = (long)uc->uc_argt; - eap->useridx = j; - - if (compl != NULL) - *compl = uc->uc_compl; - if (xp != NULL) { - xp->xp_arg = uc->uc_compl_arg; - xp->xp_scriptID = uc->uc_scriptID; - } - /* Do not search for further abbreviations - * if this is an exact match. */ - matchlen = k; - if (k == len && *np == NUL) { - if (full != NULL) - *full = TRUE; - amb_local = FALSE; - break; - } - } - } - } - - /* Stop if we found a full match or searched all. */ - if (j < gap->ga_len || gap == &ucmds) - break; - gap = &ucmds; - } - - /* Only found ambiguous matches. */ - if (amb_local) { - if (xp != NULL) - xp->xp_context = EXPAND_UNSUCCESSFUL; - return NULL; - } - - /* The match we found may be followed immediately by a number. Move "p" - * back to point to it. */ - if (found || possible) - return p + (matchlen - len); - return p; -} - -static struct cmdmod { - char *name; - int minlen; - int has_count; /* :123verbose :3tab */ -} cmdmods[] = { - {"aboveleft", 3, FALSE}, - {"belowright", 3, FALSE}, - {"botright", 2, FALSE}, - {"browse", 3, FALSE}, - {"confirm", 4, FALSE}, - {"hide", 3, FALSE}, - {"keepalt", 5, FALSE}, - {"keepjumps", 5, FALSE}, - {"keepmarks", 3, FALSE}, - {"keeppatterns", 5, FALSE}, - {"leftabove", 5, FALSE}, - {"lockmarks", 3, FALSE}, - {"noautocmd", 3, FALSE}, - {"noswapfile", 3, FALSE}, - {"rightbelow", 6, FALSE}, - {"sandbox", 3, FALSE}, - {"silent", 3, FALSE}, - {"tab", 3, TRUE}, - {"topleft", 2, FALSE}, - {"unsilent", 3, FALSE}, - {"verbose", 4, TRUE}, - {"vertical", 4, FALSE}, -}; - -/* - * Return length of a command modifier (including optional count). - * Return zero when it's not a modifier. - */ -int modifier_len(char_u *cmd) -{ - int i, j; - char_u *p = cmd; - - if (VIM_ISDIGIT(*cmd)) - p = skipwhite(skipdigits(cmd)); - for (i = 0; i < (int)(sizeof(cmdmods) / sizeof(struct cmdmod)); ++i) { - for (j = 0; p[j] != NUL; ++j) - if (p[j] != cmdmods[i].name[j]) - break; - if (!ASCII_ISALPHA(p[j]) && j >= cmdmods[i].minlen - && (p == cmd || cmdmods[i].has_count)) - return j + (int)(p - cmd); - } - return 0; -} - -/* - * Return > 0 if an Ex command "name" exists. - * Return 2 if there is an exact match. - * Return 3 if there is an ambiguous match. - */ -int cmd_exists(char_u *name) -{ - exarg_T ea; - int full = FALSE; - int i; - int j; - char_u *p; - - /* Check command modifiers. */ - for (i = 0; i < (int)(sizeof(cmdmods) / sizeof(struct cmdmod)); ++i) { - for (j = 0; name[j] != NUL; ++j) - if (name[j] != cmdmods[i].name[j]) - break; - if (name[j] == NUL && j >= cmdmods[i].minlen) - return cmdmods[i].name[j] == NUL ? 2 : 1; - } - - /* Check built-in commands and user defined commands. - * For ":2match" and ":3match" we need to skip the number. */ - ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; - ea.cmdidx = (cmdidx_T)0; - p = find_command(&ea, &full); - if (p == NULL) - return 3; - if (vim_isdigit(*name) && ea.cmdidx != CMD_match) - return 0; - if (*skipwhite(p) != NUL) - return 0; /* trailing garbage */ - return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1); -} - -/* - * This is all pretty much copied from do_one_cmd(), with all the extra stuff - * we don't need/want deleted. Maybe this could be done better if we didn't - * repeat all this stuff. The only problem is that they may not stay - * perfectly compatible with each other, but then the command line syntax - * probably won't change that much -- webb. - */ -char_u * -set_one_cmd_context ( - expand_T *xp, - char_u *buff /* buffer for command string */ -) -{ - char_u *p; - char_u *cmd, *arg; - int len = 0; - exarg_T ea; - int compl = EXPAND_NOTHING; - int delim; - int forceit = FALSE; - int usefilter = FALSE; /* filter instead of file name */ - - ExpandInit(xp); - xp->xp_pattern = buff; - xp->xp_context = EXPAND_COMMANDS; /* Default until we get past command */ - ea.argt = 0; - - /* - * 2. skip comment lines and leading space, colons or bars - */ - for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++) - ; - xp->xp_pattern = cmd; - - if (*cmd == NUL) - return NULL; - if (*cmd == '"') { /* ignore comment lines */ - xp->xp_context = EXPAND_NOTHING; - return NULL; - } - - /* - * 3. parse a range specifier of the form: addr [,addr] [;addr] .. - */ - cmd = skip_range(cmd, &xp->xp_context); - - /* - * 4. parse command - */ - xp->xp_pattern = cmd; - if (*cmd == NUL) - return NULL; - if (*cmd == '"') { - xp->xp_context = EXPAND_NOTHING; - return NULL; - } - - if (*cmd == '|' || *cmd == '\n') - return cmd + 1; /* There's another command */ - - /* - * Isolate the command and search for it in the command table. - * Exceptions: - * - the 'k' command can directly be followed by any character, but - * do accept "keepmarks", "keepalt" and "keepjumps". - * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' - */ - if (*cmd == 'k' && cmd[1] != 'e') { - ea.cmdidx = CMD_k; - p = cmd + 1; - } else { - p = cmd; - while (ASCII_ISALPHA(*p) || *p == '*') /* Allow * wild card */ - ++p; - /* check for non-alpha command */ - if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) - ++p; - /* for python 3.x: ":py3*" commands completion */ - if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') { - ++p; - while (ASCII_ISALPHA(*p) || *p == '*') - ++p; - } - len = (int)(p - cmd); - - if (len == 0) { - xp->xp_context = EXPAND_UNSUCCESSFUL; - return NULL; - } - for (ea.cmdidx = (cmdidx_T)0; (int)ea.cmdidx < (int)CMD_SIZE; - ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) - if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, - (size_t)len) == 0) - break; - - if (cmd[0] >= 'A' && cmd[0] <= 'Z') - while (ASCII_ISALNUM(*p) || *p == '*') /* Allow * wild card */ - ++p; - } - - /* - * If the cursor is touching the command, and it ends in an alpha-numeric - * character, complete the command name. - */ - if (*p == NUL && ASCII_ISALNUM(p[-1])) - return NULL; - - if (ea.cmdidx == CMD_SIZE) { - if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL) { - ea.cmdidx = CMD_substitute; - p = cmd + 1; - } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') { - ea.cmd = cmd; - p = find_ucmd(&ea, p, NULL, xp, - &compl - ); - if (p == NULL) - ea.cmdidx = CMD_SIZE; /* ambiguous user command */ - } - } - if (ea.cmdidx == CMD_SIZE) { - /* Not still touching the command and it was an illegal one */ - xp->xp_context = EXPAND_UNSUCCESSFUL; - return NULL; - } - - xp->xp_context = EXPAND_NOTHING; /* Default now that we're past command */ - - if (*p == '!') { /* forced commands */ - forceit = TRUE; - ++p; - } - - /* - * 5. parse arguments - */ - if (!USER_CMDIDX(ea.cmdidx)) - ea.argt = (long)cmdnames[(int)ea.cmdidx].cmd_argt; - - arg = skipwhite(p); - - if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { - if (*arg == '>') { /* append */ - if (*++arg == '>') - ++arg; - arg = skipwhite(arg); - } else if (*arg == '!' && ea.cmdidx == CMD_write) { /* :w !filter */ - ++arg; - usefilter = TRUE; - } - } - - if (ea.cmdidx == CMD_read) { - usefilter = forceit; /* :r! filter if forced */ - if (*arg == '!') { /* :r !filter */ - ++arg; - usefilter = TRUE; - } - } - - if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { - while (*arg == *cmd) /* allow any number of '>' or '<' */ - ++arg; - arg = skipwhite(arg); - } - - /* Does command allow "+command"? */ - if ((ea.argt & EDITCMD) && !usefilter && *arg == '+') { - /* Check if we're in the +command */ - p = arg + 1; - arg = skip_cmd_arg(arg, FALSE); - - /* Still touching the command after '+'? */ - if (*arg == NUL) - return p; - - /* Skip space(s) after +command to get to the real argument */ - arg = skipwhite(arg); - } - - /* - * Check for '|' to separate commands and '"' to start comments. - * Don't do this for ":read !cmd" and ":write !cmd". - */ - if ((ea.argt & TRLBAR) && !usefilter) { - p = arg; - /* ":redir @" is not the start of a comment */ - if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') - p += 2; - while (*p) { - if (*p == Ctrl_V) { - if (p[1] != NUL) - ++p; - } else if ( (*p == '"' && !(ea.argt & NOTRLCOM)) - || *p == '|' || *p == '\n') { - if (*(p - 1) != '\\') { - if (*p == '|' || *p == '\n') - return p + 1; - return NULL; /* It's a comment */ - } - } - mb_ptr_adv(p); - } - } - - /* no arguments allowed */ - if (!(ea.argt & EXTRA) && *arg != NUL && - vim_strchr((char_u *)"|\"", *arg) == NULL) - return NULL; - - /* Find start of last argument (argument just before cursor): */ - p = buff; - xp->xp_pattern = p; - len = (int)STRLEN(buff); - while (*p && p < buff + len) { - if (*p == ' ' || *p == TAB) { - /* argument starts after a space */ - xp->xp_pattern = ++p; - } else { - if (*p == '\\' && *(p + 1) != NUL) - ++p; /* skip over escaped character */ - mb_ptr_adv(p); - } - } - - if (ea.argt & XFILE) { - int c; - int in_quote = FALSE; - char_u *bow = NULL; /* Beginning of word */ - - /* - * Allow spaces within back-quotes to count as part of the argument - * being expanded. - */ - xp->xp_pattern = skipwhite(arg); - p = xp->xp_pattern; - while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else - c = *p; - if (c == '\\' && p[1] != NUL) - ++p; - else if (c == '`') { - if (!in_quote) { - xp->xp_pattern = p; - bow = p + 1; - } - in_quote = !in_quote; - } - /* An argument can contain just about everything, except - * characters that end the command and white space. */ - else if (c == '|' || c == '\n' || c == '"' || (vim_iswhite(c) -#ifdef SPACE_IN_FILENAME - && (!(ea.argt & NOSPC) || - usefilter) -#endif - )) { - len = 0; /* avoid getting stuck when space is in 'isfname' */ - while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else - c = *p; - if (c == '`' || vim_isfilec_or_wc(c)) - break; - if (has_mbyte) - len = (*mb_ptr2len)(p); - else - len = 1; - mb_ptr_adv(p); - } - if (in_quote) - bow = p; - else - xp->xp_pattern = p; - p -= len; - } - mb_ptr_adv(p); - } - - /* - * If we are still inside the quotes, and we passed a space, just - * expand from there. - */ - if (bow != NULL && in_quote) - xp->xp_pattern = bow; - xp->xp_context = EXPAND_FILES; - - /* For a shell command more chars need to be escaped. */ - if (usefilter || ea.cmdidx == CMD_bang) { -#ifndef BACKSLASH_IN_FILENAME - xp->xp_shell = TRUE; -#endif - /* When still after the command name expand executables. */ - if (xp->xp_pattern == skipwhite(arg)) - xp->xp_context = EXPAND_SHELLCMD; - } - - /* Check for environment variable */ - if (*xp->xp_pattern == '$' - ) { - for (p = xp->xp_pattern + 1; *p != NUL; ++p) - if (!vim_isIDc(*p)) - break; - if (*p == NUL) { - xp->xp_context = EXPAND_ENV_VARS; - ++xp->xp_pattern; - /* Avoid that the assignment uses EXPAND_FILES again. */ - if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) - compl = EXPAND_ENV_VARS; - } - } - /* Check for user names */ - if (*xp->xp_pattern == '~') { - for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p) - ; - /* Complete ~user only if it partially matches a user name. - * A full match ~user will be replaced by user's home - * directory i.e. something like ~user -> /home/user/ */ - if (*p == NUL && p > xp->xp_pattern + 1 - && match_user(xp->xp_pattern + 1) == 1) { - xp->xp_context = EXPAND_USER; - ++xp->xp_pattern; - } - } - } - - /* - * 6. switch on command name - */ - switch (ea.cmdidx) { - case CMD_find: - case CMD_sfind: - case CMD_tabfind: - if (xp->xp_context == EXPAND_FILES) - xp->xp_context = EXPAND_FILES_IN_PATH; - break; - case CMD_cd: - case CMD_chdir: - case CMD_lcd: - case CMD_lchdir: - if (xp->xp_context == EXPAND_FILES) - xp->xp_context = EXPAND_DIRECTORIES; - break; - case CMD_help: - xp->xp_context = EXPAND_HELP; - xp->xp_pattern = arg; - break; - - /* Command modifiers: return the argument. - * Also for commands with an argument that is a command. */ - case CMD_aboveleft: - case CMD_argdo: - case CMD_belowright: - case CMD_botright: - case CMD_browse: - case CMD_bufdo: - case CMD_confirm: - case CMD_debug: - case CMD_folddoclosed: - case CMD_folddoopen: - case CMD_hide: - case CMD_keepalt: - case CMD_keepjumps: - case CMD_keepmarks: - case CMD_keeppatterns: - case CMD_leftabove: - case CMD_lockmarks: - case CMD_noautocmd: - case CMD_noswapfile: - case CMD_rightbelow: - case CMD_sandbox: - case CMD_silent: - case CMD_tab: - case CMD_tabdo: - case CMD_topleft: - case CMD_verbose: - case CMD_vertical: - case CMD_windo: - return arg; - - case CMD_match: - if (*arg == NUL || !ends_excmd(*arg)) { - /* also complete "None" */ - set_context_in_echohl_cmd(xp, arg); - arg = skipwhite(skiptowhite(arg)); - if (*arg != NUL) { - xp->xp_context = EXPAND_NOTHING; - arg = skip_regexp(arg + 1, *arg, p_magic, NULL); - } - } - return find_nextcmd(arg); - - /* - * All completion for the +cmdline_compl feature goes here. - */ - - case CMD_command: - /* Check for attributes */ - while (*arg == '-') { - arg++; /* Skip "-" */ - p = skiptowhite(arg); - if (*p == NUL) { - /* Cursor is still in the attribute */ - p = vim_strchr(arg, '='); - if (p == NULL) { - /* No "=", so complete attribute names */ - xp->xp_context = EXPAND_USER_CMD_FLAGS; - xp->xp_pattern = arg; - return NULL; - } - - /* For the -complete and -nargs attributes, we complete - * their arguments as well. - */ - if (STRNICMP(arg, "complete", p - arg) == 0) { - xp->xp_context = EXPAND_USER_COMPLETE; - xp->xp_pattern = p + 1; - return NULL; - } else if (STRNICMP(arg, "nargs", p - arg) == 0) { - xp->xp_context = EXPAND_USER_NARGS; - xp->xp_pattern = p + 1; - return NULL; - } - return NULL; - } - arg = skipwhite(p); - } - - /* After the attributes comes the new command name */ - p = skiptowhite(arg); - if (*p == NUL) { - xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; - break; - } - - /* And finally comes a normal command */ - return skipwhite(p); - - case CMD_delcommand: - xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; - break; - - case CMD_global: - case CMD_vglobal: - delim = *arg; /* get the delimiter */ - if (delim) - ++arg; /* skip delimiter if there is one */ - - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; - } - if (arg[0] != NUL) - return arg + 1; - break; - case CMD_and: - case CMD_substitute: - delim = *arg; - if (delim) { - /* skip "from" part */ - ++arg; - arg = skip_regexp(arg, delim, p_magic, NULL); - } - /* skip "to" part */ - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; - } - if (arg[0] != NUL) /* skip delimiter */ - ++arg; - while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) - ++arg; - if (arg[0] != NUL) - return arg; - break; - case CMD_isearch: - case CMD_dsearch: - case CMD_ilist: - case CMD_dlist: - case CMD_ijump: - case CMD_psearch: - case CMD_djump: - case CMD_isplit: - case CMD_dsplit: - arg = skipwhite(skipdigits(arg)); /* skip count */ - if (*arg == '/') { /* Match regexp, not just whole words */ - for (++arg; *arg && *arg != '/'; arg++) - if (*arg == '\\' && arg[1] != NUL) - arg++; - if (*arg) { - arg = skipwhite(arg + 1); - - /* Check for trailing illegal characters */ - if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL) - xp->xp_context = EXPAND_NOTHING; - else - return arg; - } - } - break; - case CMD_autocmd: - return set_context_in_autocmd(xp, arg, FALSE); - - case CMD_doautocmd: - case CMD_doautoall: - return set_context_in_autocmd(xp, arg, TRUE); - case CMD_set: - set_context_in_set_cmd(xp, arg, 0); - break; - case CMD_setglobal: - set_context_in_set_cmd(xp, arg, OPT_GLOBAL); - break; - case CMD_setlocal: - set_context_in_set_cmd(xp, arg, OPT_LOCAL); - break; - case CMD_tag: - case CMD_stag: - case CMD_ptag: - case CMD_ltag: - case CMD_tselect: - case CMD_stselect: - case CMD_ptselect: - case CMD_tjump: - case CMD_stjump: - case CMD_ptjump: - if (*p_wop != NUL) - xp->xp_context = EXPAND_TAGS_LISTFILES; - else - xp->xp_context = EXPAND_TAGS; - xp->xp_pattern = arg; - break; - case CMD_augroup: - xp->xp_context = EXPAND_AUGROUP; - xp->xp_pattern = arg; - break; - case CMD_syntax: - set_context_in_syntax_cmd(xp, arg); - break; - case CMD_let: - case CMD_if: - case CMD_elseif: - case CMD_while: - case CMD_for: - case CMD_echo: - case CMD_echon: - case CMD_execute: - case CMD_echomsg: - case CMD_echoerr: - case CMD_call: - case CMD_return: - set_context_for_expression(xp, arg, ea.cmdidx); - break; - - case CMD_unlet: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; - xp->xp_context = EXPAND_USER_VARS; - xp->xp_pattern = arg; - break; - - case CMD_function: - case CMD_delfunction: - xp->xp_context = EXPAND_USER_FUNC; - xp->xp_pattern = arg; - break; - - case CMD_echohl: - set_context_in_echohl_cmd(xp, arg); - break; - case CMD_highlight: - set_context_in_highlight_cmd(xp, arg); - break; - case CMD_cscope: - case CMD_lcscope: - case CMD_scscope: - set_context_in_cscope_cmd(xp, arg, ea.cmdidx); - break; - case CMD_sign: - set_context_in_sign_cmd(xp, arg); - break; - case CMD_bdelete: - case CMD_bwipeout: - case CMD_bunload: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; - /*FALLTHROUGH*/ - case CMD_buffer: - case CMD_sbuffer: - case CMD_checktime: - xp->xp_context = EXPAND_BUFFERS; - xp->xp_pattern = arg; - break; - case CMD_USER: - case CMD_USER_BUF: - if (compl != EXPAND_NOTHING) { - /* XFILE: file names are handled above */ - if (!(ea.argt & XFILE)) { - if (compl == EXPAND_MENUS) - return set_context_in_menu_cmd(xp, cmd, arg, forceit); - if (compl == EXPAND_COMMANDS) - return arg; - if (compl == EXPAND_MAPPINGS) - return set_context_in_map_cmd(xp, (char_u *)"map", - arg, forceit, FALSE, FALSE, CMD_map); - /* Find start of last argument. */ - p = arg; - while (*p) { - if (*p == ' ') - /* argument starts after a space */ - arg = p + 1; - else if (*p == '\\' && *(p + 1) != NUL) - ++p; /* skip over escaped character */ - mb_ptr_adv(p); - } - xp->xp_pattern = arg; - } - xp->xp_context = compl; - } - break; - case CMD_map: case CMD_noremap: - case CMD_nmap: case CMD_nnoremap: - case CMD_vmap: case CMD_vnoremap: - case CMD_omap: case CMD_onoremap: - case CMD_imap: case CMD_inoremap: - case CMD_cmap: case CMD_cnoremap: - case CMD_lmap: case CMD_lnoremap: - case CMD_smap: case CMD_snoremap: - case CMD_xmap: case CMD_xnoremap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, FALSE, ea.cmdidx); - case CMD_unmap: - case CMD_nunmap: - case CMD_vunmap: - case CMD_ounmap: - case CMD_iunmap: - case CMD_cunmap: - case CMD_lunmap: - case CMD_sunmap: - case CMD_xunmap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, TRUE, ea.cmdidx); - case CMD_abbreviate: case CMD_noreabbrev: - case CMD_cabbrev: case CMD_cnoreabbrev: - case CMD_iabbrev: case CMD_inoreabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, FALSE, ea.cmdidx); - case CMD_unabbreviate: - case CMD_cunabbrev: - case CMD_iunabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, TRUE, ea.cmdidx); - case CMD_menu: case CMD_noremenu: case CMD_unmenu: - case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: - case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: - case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu: - case CMD_omenu: case CMD_onoremenu: case CMD_ounmenu: - case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu: - case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: - case CMD_tmenu: case CMD_tunmenu: - case CMD_popup: case CMD_tearoff: case CMD_emenu: - return set_context_in_menu_cmd(xp, cmd, arg, forceit); - - case CMD_colorscheme: - xp->xp_context = EXPAND_COLORS; - xp->xp_pattern = arg; - break; - - case CMD_compiler: - xp->xp_context = EXPAND_COMPILER; - xp->xp_pattern = arg; - break; - - case CMD_ownsyntax: - xp->xp_context = EXPAND_OWNSYNTAX; - xp->xp_pattern = arg; - break; - - case CMD_setfiletype: - xp->xp_context = EXPAND_FILETYPE; - xp->xp_pattern = arg; - break; - -#ifdef HAVE_WORKING_LIBINTL - case CMD_language: - p = skiptowhite(arg); - if (*p == NUL) { - xp->xp_context = EXPAND_LANGUAGE; - xp->xp_pattern = arg; - } else { - if ( STRNCMP(arg, "messages", p - arg) == 0 - || STRNCMP(arg, "ctype", p - arg) == 0 - || STRNCMP(arg, "time", p - arg) == 0) { - xp->xp_context = EXPAND_LOCALES; - xp->xp_pattern = skipwhite(p); - } else - xp->xp_context = EXPAND_NOTHING; - } - break; -#endif - case CMD_profile: - set_context_in_profile_cmd(xp, arg); - break; - case CMD_behave: - xp->xp_context = EXPAND_BEHAVE; - xp->xp_pattern = arg; - break; - - case CMD_history: - xp->xp_context = EXPAND_HISTORY; - xp->xp_pattern = arg; - break; - case CMD_syntime: - xp->xp_context = EXPAND_SYNTIME; - xp->xp_pattern = arg; - break; - - - default: - break; - } - return NULL; -} - -/* - * skip a range specifier of the form: addr [,addr] [;addr] .. - * - * Backslashed delimiters after / or ? will be skipped, and commands will - * not be expanded between /'s and ?'s or after "'". - * - * Also skip white space and ":" characters. - * Returns the "cmd" pointer advanced to beyond the range. - */ -char_u * -skip_range ( - char_u *cmd, - int *ctx /* pointer to xp_context or NULL */ -) -{ - unsigned delim; - - while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;", *cmd) != NULL) { - if (*cmd == '\'') { - if (*++cmd == NUL && ctx != NULL) - *ctx = EXPAND_NOTHING; - } else if (*cmd == '/' || *cmd == '?') { - delim = *cmd++; - while (*cmd != NUL && *cmd != delim) - if (*cmd++ == '\\' && *cmd != NUL) - ++cmd; - if (*cmd == NUL && ctx != NULL) - *ctx = EXPAND_NOTHING; - } - if (*cmd != NUL) - ++cmd; - } - - /* Skip ":" and white space. */ - while (*cmd == ':') - cmd = skipwhite(cmd + 1); - - return cmd; -} - -/* - * get a single EX address - * - * Set ptr to the next character after the part that was interpreted. - * Set ptr to NULL when an error is encountered. - * - * Return MAXLNUM when no Ex address was found. - */ -static linenr_T -get_address ( - char_u **ptr, - int skip, /* only skip the address, don't use it */ - int to_other_file /* flag: may jump to other file */ -) -{ - int c; - int i; - long n; - char_u *cmd; - pos_T pos; - pos_T *fp; - linenr_T lnum; - - cmd = skipwhite(*ptr); - lnum = MAXLNUM; - do { - switch (*cmd) { - case '.': /* '.' - Cursor position */ - ++cmd; - lnum = curwin->w_cursor.lnum; - break; - - case '$': /* '$' - last line */ - ++cmd; - lnum = curbuf->b_ml.ml_line_count; - break; - - case '\'': /* ''' - mark */ - if (*++cmd == NUL) { - cmd = NULL; - goto error; - } - if (skip) - ++cmd; - else { - /* Only accept a mark in another file when it is - * used by itself: ":'M". */ - fp = getmark(*cmd, to_other_file && cmd[1] == NUL); - ++cmd; - if (fp == (pos_T *)-1) - /* Jumped to another file. */ - lnum = curwin->w_cursor.lnum; - else { - if (check_mark(fp) == FAIL) { - cmd = NULL; - goto error; - } - lnum = fp->lnum; - } - } - break; - - case '/': - case '?': /* '/' or '?' - search */ - c = *cmd++; - if (skip) { /* skip "/pat/" */ - cmd = skip_regexp(cmd, c, (int)p_magic, NULL); - if (*cmd == c) - ++cmd; - } else { - pos = curwin->w_cursor; /* save curwin->w_cursor */ - /* - * When '/' or '?' follows another address, start - * from there. - */ - if (lnum != MAXLNUM) - curwin->w_cursor.lnum = lnum; - /* - * Start a forward search at the end of the line. - * Start a backward search at the start of the line. - * This makes sure we never match in the current - * line, and can match anywhere in the - * next/previous line. - */ - if (c == '/') - curwin->w_cursor.col = MAXCOL; - else - curwin->w_cursor.col = 0; - searchcmdlen = 0; - if (!do_search(NULL, c, cmd, 1L, - SEARCH_HIS | SEARCH_MSG, NULL)) { - curwin->w_cursor = pos; - cmd = NULL; - goto error; - } - lnum = curwin->w_cursor.lnum; - curwin->w_cursor = pos; - /* adjust command string pointer */ - cmd += searchcmdlen; - } - break; - - case '\\': /* "\?", "\/" or "\&", repeat search */ - ++cmd; - if (*cmd == '&') - i = RE_SUBST; - else if (*cmd == '?' || *cmd == '/') - i = RE_SEARCH; - else { - EMSG(_(e_backslash)); - cmd = NULL; - goto error; - } - - if (!skip) { - /* - * When search follows another address, start from - * there. - */ - if (lnum != MAXLNUM) - pos.lnum = lnum; - else - pos.lnum = curwin->w_cursor.lnum; - - /* - * Start the search just like for the above - * do_search(). - */ - if (*cmd != '?') - pos.col = MAXCOL; - else - pos.col = 0; - if (searchit(curwin, curbuf, &pos, - *cmd == '?' ? BACKWARD : FORWARD, - (char_u *)"", 1L, SEARCH_MSG, - i, (linenr_T)0, NULL) != FAIL) - lnum = pos.lnum; - else { - cmd = NULL; - goto error; - } - } - ++cmd; - break; - - default: - if (VIM_ISDIGIT(*cmd)) /* absolute line number */ - lnum = getdigits(&cmd); - } - - for (;; ) { - cmd = skipwhite(cmd); - if (*cmd != '-' && *cmd != '+' && !VIM_ISDIGIT(*cmd)) - break; - - if (lnum == MAXLNUM) - lnum = curwin->w_cursor.lnum; /* "+1" is same as ".+1" */ - if (VIM_ISDIGIT(*cmd)) - i = '+'; /* "number" is same as "+number" */ - else - i = *cmd++; - if (!VIM_ISDIGIT(*cmd)) /* '+' is '+1', but '+0' is not '+1' */ - n = 1; - else - n = getdigits(&cmd); - if (i == '-') - lnum -= n; - else - lnum += n; - } - } while (*cmd == '/' || *cmd == '?'); - -error: - *ptr = cmd; - return lnum; -} - -/* - * Get flags from an Ex command argument. - */ -static void get_flags(exarg_T *eap) -{ - while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL) { - if (*eap->arg == 'l') - eap->flags |= EXFLAG_LIST; - else if (*eap->arg == 'p') - eap->flags |= EXFLAG_PRINT; - else - eap->flags |= EXFLAG_NR; - eap->arg = skipwhite(eap->arg + 1); - } -} - -/* - * Function called for command which is Not Implemented. NI! - */ -void ex_ni(exarg_T *eap) -{ - if (!eap->skip) - eap->errmsg = (char_u *)N_( - "E319: Sorry, the command is not available in this version"); -} - -#ifdef HAVE_EX_SCRIPT_NI -/* - * Function called for script command which is Not Implemented. NI! - * Skips over ":perl <skip) - ex_ni(eap); - else - free(script_get(eap, eap->arg)); -} -#endif - -/* - * Check range in Ex command for validity. - * Return NULL when valid, error message when invalid. - */ -static char_u *invalid_range(exarg_T *eap) -{ - if ( eap->line1 < 0 - || eap->line2 < 0 - || eap->line1 > eap->line2 - || ((eap->argt & RANGE) - && !(eap->argt & NOTADR) - && eap->line2 > curbuf->b_ml.ml_line_count - + (eap->cmdidx == CMD_diffget) - )) - return (char_u *)_(e_invrange); - return NULL; -} - -/* - * Correct the range for zero line number, if required. - */ -static void correct_range(exarg_T *eap) -{ - if (!(eap->argt & ZEROR)) { /* zero in range not allowed */ - if (eap->line1 == 0) - eap->line1 = 1; - if (eap->line2 == 0) - eap->line2 = 1; - } -} - -static char_u *skip_grep_pat(exarg_T *eap); - -/* - * For a ":vimgrep" or ":vimgrepadd" command return a pointer past the - * pattern. Otherwise return eap->arg. - */ -static char_u *skip_grep_pat(exarg_T *eap) -{ - char_u *p = eap->arg; - - if (*p != NUL && (eap->cmdidx == CMD_vimgrep || eap->cmdidx == CMD_lvimgrep - || eap->cmdidx == CMD_vimgrepadd - || eap->cmdidx == CMD_lvimgrepadd - || grep_internal(eap->cmdidx))) { - p = skip_vimgrep_pat(p, NULL, NULL); - if (p == NULL) - p = eap->arg; - } - return p; -} - -/* - * For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option - * in the command line, so that things like % get expanded. - */ -static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) -{ - char_u *new_cmdline; - char_u *program; - char_u *pos; - char_u *ptr; - int len; - int i; - - /* - * Don't do it when ":vimgrep" is used for ":grep". - */ - if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake - || eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep - || eap->cmdidx == CMD_grepadd - || eap->cmdidx == CMD_lgrepadd) - && !grep_internal(eap->cmdidx)) { - if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep - || eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd) { - if (*curbuf->b_p_gp == NUL) - program = p_gp; - else - program = curbuf->b_p_gp; - } else { - if (*curbuf->b_p_mp == NUL) - program = p_mp; - else - program = curbuf->b_p_mp; - } - - p = skipwhite(p); - - if ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) { - /* replace $* by given arguments */ - i = 1; - while ((pos = (char_u *)strstr((char *)pos + 2, "$*")) != NULL) - ++i; - len = (int)STRLEN(p); - new_cmdline = xmalloc(STRLEN(program) + i * (len - 2) + 1); - ptr = new_cmdline; - while ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) { - i = (int)(pos - program); - STRNCPY(ptr, program, i); - STRCPY(ptr += i, p); - ptr += len; - program = pos + 2; - } - STRCPY(ptr, program); - } else { - new_cmdline = xmalloc(STRLEN(program) + STRLEN(p) + 2); - STRCPY(new_cmdline, program); - STRCAT(new_cmdline, " "); - STRCAT(new_cmdline, p); - } - msg_make(p); - - /* 'eap->cmd' is not set here, because it is not used at CMD_make */ - free(*cmdlinep); - *cmdlinep = new_cmdline; - p = new_cmdline; - } - return p; -} - -/* - * Expand file name in Ex command argument. - * Return FAIL for failure, OK otherwise. - */ -int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) -{ - int has_wildcards; /* need to expand wildcards */ - char_u *repl; - int srclen; - char_u *p; - int escaped; - - /* Skip a regexp pattern for ":vimgrep[add] pat file..." */ - p = skip_grep_pat(eap); - - /* - * Decide to expand wildcards *before* replacing '%', '#', etc. If - * the file name contains a wildcard it should not cause expanding. - * (it will be expanded anyway if there is a wildcard before replacing). - */ - has_wildcards = mch_has_wildcard(p); - while (*p != NUL) { - /* Skip over `=expr`, wildcards in it are not expanded. */ - if (p[0] == '`' && p[1] == '=') { - p += 2; - (void)skip_expr(&p); - if (*p == '`') - ++p; - continue; - } - /* - * Quick check if this cannot be the start of a special string. - * Also removes backslash before '%', '#' and '<'. - */ - if (vim_strchr((char_u *)"%#<", *p) == NULL) { - ++p; - continue; - } - - /* - * Try to find a match at this position. - */ - repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum), - errormsgp, &escaped); - if (*errormsgp != NULL) /* error detected */ - return FAIL; - if (repl == NULL) { /* no match found */ - p += srclen; - continue; - } - - /* Wildcards won't be expanded below, the replacement is taken - * literally. But do expand "~/file", "~user/file" and "$HOME/file". */ - if (vim_strchr(repl, '$') != NULL || vim_strchr(repl, '~') != NULL) { - char_u *l = repl; - - repl = expand_env_save(repl); - free(l); - } - - /* Need to escape white space et al. with a backslash. - * Don't do this for: - * - replacement that already has been escaped: "##" - * - shell commands (may have to use quotes instead). - * - non-unix systems when there is a single argument (spaces don't - * separate arguments then). - */ - if (!eap->usefilter - && !escaped - && eap->cmdidx != CMD_bang - && eap->cmdidx != CMD_make - && eap->cmdidx != CMD_lmake - && eap->cmdidx != CMD_grep - && eap->cmdidx != CMD_lgrep - && eap->cmdidx != CMD_grepadd - && eap->cmdidx != CMD_lgrepadd -#ifndef UNIX - && !(eap->argt & NOSPC) -#endif - ) { - char_u *l; -#ifdef BACKSLASH_IN_FILENAME - /* Don't escape a backslash here, because rem_backslash() doesn't - * remove it later. */ - static char_u *nobslash = (char_u *)" \t\"|"; -# define ESCAPE_CHARS nobslash -#else -# define ESCAPE_CHARS escape_chars -#endif - - for (l = repl; *l; ++l) - if (vim_strchr(ESCAPE_CHARS, *l) != NULL) { - l = vim_strsave_escaped(repl, ESCAPE_CHARS); - if (l != NULL) { - free(repl); - repl = l; - } - break; - } - } - - /* For a shell command a '!' must be escaped. */ - if ((eap->usefilter || eap->cmdidx == CMD_bang) - && vim_strpbrk(repl, (char_u *)"!") != NULL) { - char_u *l; - - l = vim_strsave_escaped(repl, (char_u *)"!"); - if (l != NULL) { - free(repl); - repl = l; - } - } - - p = repl_cmdline(eap, p, srclen, repl, cmdlinep); - free(repl); - } - - /* - * One file argument: Expand wildcards. - * Don't do this with ":r !command" or ":w !command". - */ - if ((eap->argt & NOSPC) && !eap->usefilter) { - // Replace environment variables. - if (has_wildcards) { - /* - * May expand environment variables. This - * can be done much faster with expand_env() than with - * something else (e.g., calling a shell). - * After expanding environment variables, check again - * if there are still wildcards present. - */ - if (vim_strchr(eap->arg, '$') != NULL - || vim_strchr(eap->arg, '~') != NULL) { - expand_env_esc(eap->arg, NameBuff, MAXPATHL, - TRUE, TRUE, NULL); - has_wildcards = mch_has_wildcard(NameBuff); - p = NameBuff; - } else - p = NULL; - if (p != NULL) { - (void)repl_cmdline(eap, eap->arg, (int)STRLEN(eap->arg), - p, cmdlinep); - } - } - - // Replace any other wildcards, remove backslashes. -#ifdef UNIX - /* - * Only for Unix we check for more than one file name. - * For other systems spaces are considered to be part - * of the file name. - * Only check here if there is no wildcard, otherwise - * ExpandOne() will check for errors. This allows - * ":e `ls ve*.c`" on Unix. - */ - if (!has_wildcards) - for (p = eap->arg; *p; ++p) { - /* skip escaped characters */ - if (p[1] && (*p == '\\' || *p == Ctrl_V)) - ++p; - else if (vim_iswhite(*p)) { - *errormsgp = (char_u *)_("E172: Only one file name allowed"); - return FAIL; - } - } -#endif - - /* - * Halve the number of backslashes (this is Vi compatible). - * For Unix, when wildcards are expanded, this is - * done by ExpandOne() below. - */ -#ifdef UNIX - if (!has_wildcards) -#endif - backslash_halve(eap->arg); - - if (has_wildcards) { - expand_T xpc; - int options = WILD_LIST_NOTFOUND|WILD_ADD_SLASH; - - ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; - if (p_wic) - options += WILD_ICASE; - p = ExpandOne(&xpc, eap->arg, NULL, - options, WILD_EXPAND_FREE); - if (p == NULL) - return FAIL; - if (p != NULL) { - (void)repl_cmdline(eap, eap->arg, (int)STRLEN(eap->arg), - p, cmdlinep); - free(p); - } - } - } - return OK; -} - -/* - * Replace part of the command line, keeping eap->cmd, eap->arg and - * eap->nextcmd correct. - * "src" points to the part that is to be replaced, of length "srclen". - * "repl" is the replacement string. - * Returns a pointer to the character after the replaced string. - */ -static char_u *repl_cmdline(exarg_T *eap, char_u *src, int srclen, char_u *repl, char_u **cmdlinep) -{ - int len; - int i; - char_u *new_cmdline; - - /* - * The new command line is build in new_cmdline[]. - * First allocate it. - * Careful: a "+cmd" argument may have been NUL terminated. - */ - len = (int)STRLEN(repl); - i = (int)(src - *cmdlinep) + (int)STRLEN(src + srclen) + len + 3; - if (eap->nextcmd != NULL) - i += (int)STRLEN(eap->nextcmd); /* add space for next command */ - new_cmdline = xmalloc(i); - - /* - * Copy the stuff before the expanded part. - * Copy the expanded stuff. - * Copy what came after the expanded part. - * Copy the next commands, if there are any. - */ - i = (int)(src - *cmdlinep); /* length of part before match */ - memmove(new_cmdline, *cmdlinep, (size_t)i); - - memmove(new_cmdline + i, repl, (size_t)len); - i += len; /* remember the end of the string */ - STRCPY(new_cmdline + i, src + srclen); - src = new_cmdline + i; /* remember where to continue */ - - if (eap->nextcmd != NULL) { /* append next command */ - i = (int)STRLEN(new_cmdline) + 1; - STRCPY(new_cmdline + i, eap->nextcmd); - eap->nextcmd = new_cmdline + i; - } - eap->cmd = new_cmdline + (eap->cmd - *cmdlinep); - eap->arg = new_cmdline + (eap->arg - *cmdlinep); - if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) - eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep); - free(*cmdlinep); - *cmdlinep = new_cmdline; - - return src; -} - -/* - * Check for '|' to separate commands and '"' to start comments. - */ -void separate_nextcmd(exarg_T *eap) -{ - char_u *p; - - p = skip_grep_pat(eap); - - for (; *p; mb_ptr_adv(p)) { - if (*p == Ctrl_V) { - if (eap->argt & (USECTRLV | XFILE)) - ++p; /* skip CTRL-V and next char */ - else - /* remove CTRL-V and skip next char */ - STRMOVE(p, p + 1); - if (*p == NUL) /* stop at NUL after CTRL-V */ - break; - } - /* Skip over `=expr` when wildcards are expanded. */ - else if (p[0] == '`' && p[1] == '=' && (eap->argt & XFILE)) { - p += 2; - (void)skip_expr(&p); - } - /* Check for '"': start of comment or '|': next command */ - /* :@" and :*" do not start a comment! - * :redir @" doesn't either. */ - else if ((*p == '"' && !(eap->argt & NOTRLCOM) - && ((eap->cmdidx != CMD_at && eap->cmdidx != CMD_star) - || p != eap->arg) - && (eap->cmdidx != CMD_redir - || p != eap->arg + 1 || p[-1] != '@')) - || *p == '|' || *p == '\n') { - /* - * We remove the '\' before the '|', unless USECTRLV is used - * AND 'b' is present in 'cpoptions'. - */ - if ((vim_strchr(p_cpo, CPO_BAR) == NULL - || !(eap->argt & USECTRLV)) && *(p - 1) == '\\') { - STRMOVE(p - 1, p); /* remove the '\' */ - --p; - } else { - eap->nextcmd = check_nextcmd(p); - *p = NUL; - break; - } - } - } - - if (!(eap->argt & NOTRLCOM)) /* remove trailing spaces */ - del_trailing_spaces(eap->arg); -} - -/* - * get + command from ex argument - */ -static char_u *getargcmd(char_u **argp) -{ - char_u *arg = *argp; - char_u *command = NULL; - - if (*arg == '+') { /* +[command] */ - ++arg; - if (vim_isspace(*arg) || *arg == '\0') - command = dollar_command; - else { - command = arg; - arg = skip_cmd_arg(command, TRUE); - if (*arg != NUL) - *arg++ = NUL; /* terminate command with NUL */ - } - - arg = skipwhite(arg); /* skip over spaces */ - *argp = arg; - } - return command; -} - -/* - * Find end of "+command" argument. Skip over "\ " and "\\". - */ -static char_u * -skip_cmd_arg ( - char_u *p, - int rembs /* TRUE to halve the number of backslashes */ -) -{ - while (*p && !vim_isspace(*p)) { - if (*p == '\\' && p[1] != NUL) { - if (rembs) - STRMOVE(p, p + 1); - else - ++p; - } - mb_ptr_adv(p); - } - return p; -} - -/* - * Get "++opt=arg" argument. - * Return FAIL or OK. - */ -static int getargopt(exarg_T *eap) -{ - char_u *arg = eap->arg + 2; - int *pp = NULL; - int bad_char_idx; - char_u *p; - - /* ":edit ++[no]bin[ary] file" */ - if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0) { - if (*arg == 'n') { - arg += 2; - eap->force_bin = FORCE_NOBIN; - } else - eap->force_bin = FORCE_BIN; - if (!checkforcmd(&arg, "binary", 3)) - return FAIL; - eap->arg = skipwhite(arg); - return OK; - } - - /* ":read ++edit file" */ - if (STRNCMP(arg, "edit", 4) == 0) { - eap->read_edit = TRUE; - eap->arg = skipwhite(arg + 4); - return OK; - } - - if (STRNCMP(arg, "ff", 2) == 0) { - arg += 2; - pp = &eap->force_ff; - } else if (STRNCMP(arg, "fileformat", 10) == 0) { - arg += 10; - pp = &eap->force_ff; - } else if (STRNCMP(arg, "enc", 3) == 0) { - if (STRNCMP(arg, "encoding", 8) == 0) - arg += 8; - else - arg += 3; - pp = &eap->force_enc; - } else if (STRNCMP(arg, "bad", 3) == 0) { - arg += 3; - pp = &bad_char_idx; - } - - if (pp == NULL || *arg != '=') - return FAIL; - - ++arg; - *pp = (int)(arg - eap->cmd); - arg = skip_cmd_arg(arg, FALSE); - eap->arg = skipwhite(arg); - *arg = NUL; - - if (pp == &eap->force_ff) { - if (check_ff_value(eap->cmd + eap->force_ff) == FAIL) - return FAIL; - } else if (pp == &eap->force_enc) { - /* Make 'fileencoding' lower case. */ - for (p = eap->cmd + eap->force_enc; *p != NUL; ++p) - *p = TOLOWER_ASC(*p); - } else { - /* Check ++bad= argument. Must be a single-byte character, "keep" or - * "drop". */ - p = eap->cmd + bad_char_idx; - if (STRICMP(p, "keep") == 0) - eap->bad_char = BAD_KEEP; - else if (STRICMP(p, "drop") == 0) - eap->bad_char = BAD_DROP; - else if (MB_BYTE2LEN(*p) == 1 && p[1] == NUL) - eap->bad_char = *p; - else - return FAIL; - } - - return OK; -} - -/* - * ":abbreviate" and friends. - */ -static void ex_abbreviate(exarg_T *eap) -{ - do_exmap(eap, TRUE); /* almost the same as mapping */ -} - -/* - * ":map" and friends. - */ -static void ex_map(exarg_T *eap) -{ - /* - * If we are sourcing .exrc or .vimrc in current directory we - * print the mappings for security reasons. - */ - if (secure) { - secure = 2; - msg_outtrans(eap->cmd); - msg_putchar('\n'); - } - do_exmap(eap, FALSE); -} - -/* - * ":unmap" and friends. - */ -static void ex_unmap(exarg_T *eap) -{ - do_exmap(eap, FALSE); -} - -/* - * ":mapclear" and friends. - */ -static void ex_mapclear(exarg_T *eap) -{ - map_clear(eap->cmd, eap->arg, eap->forceit, FALSE); -} - -/* - * ":abclear" and friends. - */ -static void ex_abclear(exarg_T *eap) -{ - map_clear(eap->cmd, eap->arg, TRUE, TRUE); -} - -static void ex_autocmd(exarg_T *eap) -{ - /* - * Disallow auto commands from .exrc and .vimrc in current - * directory for security reasons. - */ - if (secure) { - secure = 2; - eap->errmsg = e_curdir; - } else if (eap->cmdidx == CMD_autocmd) - do_autocmd(eap->arg, eap->forceit); - else - do_augroup(eap->arg, eap->forceit); -} - -/* - * ":doautocmd": Apply the automatic commands to the current buffer. - */ -static void ex_doautocmd(exarg_T *eap) -{ - char_u *arg = eap->arg; - int call_do_modelines = check_nomodeline(&arg); - - (void)do_doautocmd(arg, TRUE); - if (call_do_modelines) /* Only when there is no . */ - do_modelines(0); -} - -/* - * :[N]bunload[!] [N] [bufname] unload buffer - * :[N]bdelete[!] [N] [bufname] delete buffer from buffer list - * :[N]bwipeout[!] [N] [bufname] delete buffer really - */ -static void ex_bunload(exarg_T *eap) -{ - eap->errmsg = do_bufdel( - eap->cmdidx == CMD_bdelete ? DOBUF_DEL - : eap->cmdidx == CMD_bwipeout ? DOBUF_WIPE - : DOBUF_UNLOAD, eap->arg, - eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit); -} - -/* - * :[N]buffer [N] to buffer N - * :[N]sbuffer [N] to buffer N - */ -static void ex_buffer(exarg_T *eap) -{ - if (*eap->arg) - eap->errmsg = e_trailing; - else { - if (eap->addr_count == 0) /* default is current buffer */ - goto_buffer(eap, DOBUF_CURRENT, FORWARD, 0); - else - goto_buffer(eap, DOBUF_FIRST, FORWARD, (int)eap->line2); - } -} - -/* - * :[N]bmodified [N] to next mod. buffer - * :[N]sbmodified [N] to next mod. buffer - */ -static void ex_bmodified(exarg_T *eap) -{ - goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2); -} - -/* - * :[N]bnext [N] to next buffer - * :[N]sbnext [N] split and to next buffer - */ -static void ex_bnext(exarg_T *eap) -{ - goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2); -} - -/* - * :[N]bNext [N] to previous buffer - * :[N]bprevious [N] to previous buffer - * :[N]sbNext [N] split and to previous buffer - * :[N]sbprevious [N] split and to previous buffer - */ -static void ex_bprevious(exarg_T *eap) -{ - goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2); -} - -/* - * :brewind to first buffer - * :bfirst to first buffer - * :sbrewind split and to first buffer - * :sbfirst split and to first buffer - */ -static void ex_brewind(exarg_T *eap) -{ - goto_buffer(eap, DOBUF_FIRST, FORWARD, 0); -} - -/* - * :blast to last buffer - * :sblast split and to last buffer - */ -static void ex_blast(exarg_T *eap) -{ - goto_buffer(eap, DOBUF_LAST, BACKWARD, 0); -} - -int ends_excmd(int c) -{ - return c == NUL || c == '|' || c == '"' || c == '\n'; -} - -#if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) || defined(FEAT_EVAL) \ - || defined(PROTO) -/* - * Return the next command, after the first '|' or '\n'. - * Return NULL if not found. - */ -char_u *find_nextcmd(char_u *p) -{ - while (*p != '|' && *p != '\n') { - if (*p == NUL) - return NULL; - ++p; - } - return p + 1; -} -#endif - -/* - * Check if *p is a separator between Ex commands. - * Return NULL if it isn't, (p + 1) if it is. - */ -char_u *check_nextcmd(char_u *p) -{ - p = skipwhite(p); - if (*p == '|' || *p == '\n') - return p + 1; - else - return NULL; -} - -/* - * - if there are more files to edit - * - and this is the last window - * - and forceit not used - * - and not repeated twice on a row - * return FAIL and give error message if 'message' TRUE - * return OK otherwise - */ -static int -check_more ( - int message, /* when FALSE check only, no messages */ - int forceit -) -{ - int n = ARGCOUNT - curwin->w_arg_idx - 1; - - if (!forceit && only_one_window() - && ARGCOUNT > 1 && !arg_had_last && n >= 0 && quitmore == 0) { - if (message) { - if ((p_confirm || cmdmod.confirm) && curbuf->b_fname != NULL) { - char_u buff[DIALOG_MSG_SIZE]; - - if (n == 1) - vim_strncpy(buff, - (char_u *)_("1 more file to edit. Quit anyway?"), - DIALOG_MSG_SIZE - 1); - else - vim_snprintf((char *)buff, DIALOG_MSG_SIZE, - _("%d more files to edit. Quit anyway?"), n); - if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) - return OK; - return FAIL; - } - if (n == 1) - EMSG(_("E173: 1 more file to edit")); - else - EMSGN(_("E173: %" PRId64 " more files to edit"), n); - quitmore = 2; /* next try to quit is allowed */ - } - return FAIL; - } - return OK; -} - -/* - * Function given to ExpandGeneric() to obtain the list of command names. - */ -char_u *get_command_name(expand_T *xp, int idx) -{ - if (idx >= (int)CMD_SIZE) - return get_user_command_name(idx); - return cmdnames[idx].cmd_name; -} - -static int uc_add_command(char_u *name, size_t name_len, char_u *rep, - long argt, long def, int flags, int compl, - char_u *compl_arg, - int force); -static void uc_list(char_u *name, size_t name_len); -static int uc_scan_attr(char_u *attr, size_t len, long *argt, long *def, - int *flags, int *compl, - char_u **compl_arg); -static char_u *uc_split_args(char_u *arg, size_t *lenp); -static size_t uc_check_code(char_u *code, size_t len, char_u *buf, - ucmd_T *cmd, exarg_T *eap, char_u **split_buf, - size_t *split_len); - -static int uc_add_command(char_u *name, size_t name_len, char_u *rep, long argt, long def, int flags, int compl, char_u *compl_arg, int force) -{ - ucmd_T *cmd = NULL; - char_u *p; - int i; - int cmp = 1; - char_u *rep_buf = NULL; - garray_T *gap; - - replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); - if (rep_buf == NULL) { - /* Can't replace termcodes - try using the string as is */ - rep_buf = vim_strsave(rep); - - /* Give up if out of memory */ - if (rep_buf == NULL) - return FAIL; - } - - /* get address of growarray: global or in curbuf */ - if (flags & UC_BUFFER) { - gap = &curbuf->b_ucmds; - if (gap->ga_itemsize == 0) - ga_init(gap, (int)sizeof(ucmd_T), 4); - } else - gap = &ucmds; - - /* Search for the command in the already defined commands. */ - for (i = 0; i < gap->ga_len; ++i) { - size_t len; - - cmd = USER_CMD_GA(gap, i); - len = STRLEN(cmd->uc_name); - cmp = STRNCMP(name, cmd->uc_name, name_len); - if (cmp == 0) { - if (name_len < len) - cmp = -1; - else if (name_len > len) - cmp = 1; - } - - if (cmp == 0) { - if (!force) { - EMSG(_("E174: Command already exists: add ! to replace it")); - goto fail; - } - - free(cmd->uc_rep); - cmd->uc_rep = NULL; - free(cmd->uc_compl_arg); - cmd->uc_compl_arg = NULL; - break; - } - - /* Stop as soon as we pass the name to add */ - if (cmp < 0) - break; - } - - /* Extend the array unless we're replacing an existing command */ - if (cmp != 0) { - ga_grow(gap, 1); - - if ((p = vim_strnsave(name, (int)name_len)) == NULL) - goto fail; - - cmd = USER_CMD_GA(gap, i); - memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T)); - - ++gap->ga_len; - - cmd->uc_name = p; - } - - cmd->uc_rep = rep_buf; - cmd->uc_argt = argt; - cmd->uc_def = def; - cmd->uc_compl = compl; - cmd->uc_scriptID = current_SID; - cmd->uc_compl_arg = compl_arg; - - return OK; - -fail: - free(rep_buf); - free(compl_arg); - return FAIL; -} - -/* - * List of names for completion for ":command" with the EXPAND_ flag. - * Must be alphabetical for completion. - */ -static struct { - int expand; - char *name; -} command_complete[] = -{ - {EXPAND_AUGROUP, "augroup"}, - {EXPAND_BEHAVE, "behave"}, - {EXPAND_BUFFERS, "buffer"}, - {EXPAND_COLORS, "color"}, - {EXPAND_COMMANDS, "command"}, - {EXPAND_COMPILER, "compiler"}, - {EXPAND_CSCOPE, "cscope"}, - {EXPAND_USER_DEFINED, "custom"}, - {EXPAND_USER_LIST, "customlist"}, - {EXPAND_DIRECTORIES, "dir"}, - {EXPAND_ENV_VARS, "environment"}, - {EXPAND_EVENTS, "event"}, - {EXPAND_EXPRESSION, "expression"}, - {EXPAND_FILES, "file"}, - {EXPAND_FILES_IN_PATH, "file_in_path"}, - {EXPAND_FILETYPE, "filetype"}, - {EXPAND_FUNCTIONS, "function"}, - {EXPAND_HELP, "help"}, - {EXPAND_HIGHLIGHT, "highlight"}, - {EXPAND_HISTORY, "history"}, -#ifdef HAVE_WORKING_LIBINTL - {EXPAND_LOCALES, "locale"}, -#endif - {EXPAND_MAPPINGS, "mapping"}, - {EXPAND_MENUS, "menu"}, - {EXPAND_OWNSYNTAX, "syntax"}, - {EXPAND_SYNTIME, "syntime"}, - {EXPAND_SETTINGS, "option"}, - {EXPAND_SHELLCMD, "shellcmd"}, - {EXPAND_SIGN, "sign"}, - {EXPAND_TAGS, "tag"}, - {EXPAND_TAGS_LISTFILES, "tag_listfiles"}, - {EXPAND_USER, "user"}, - {EXPAND_USER_VARS, "var"}, - {0, NULL} -}; - -static void uc_list(char_u *name, size_t name_len) -{ - int i, j; - int found = FALSE; - ucmd_T *cmd; - int len; - long a; - garray_T *gap; - - gap = &curbuf->b_ucmds; - for (;; ) { - for (i = 0; i < gap->ga_len; ++i) { - cmd = USER_CMD_GA(gap, i); - a = (long)cmd->uc_argt; - - /* Skip commands which don't match the requested prefix */ - if (STRNCMP(name, cmd->uc_name, name_len) != 0) - continue; - - /* Put out the title first time */ - if (!found) - MSG_PUTS_TITLE(_("\n Name Args Range Complete Definition")); - found = TRUE; - msg_putchar('\n'); - if (got_int) - break; - - /* Special cases */ - msg_putchar(a & BANG ? '!' : ' '); - msg_putchar(a & REGSTR ? '"' : ' '); - msg_putchar(gap != &ucmds ? 'b' : ' '); - msg_putchar(' '); - - msg_outtrans_attr(cmd->uc_name, hl_attr(HLF_D)); - len = (int)STRLEN(cmd->uc_name) + 4; - - do { - msg_putchar(' '); - ++len; - } while (len < 16); - - len = 0; - - /* Arguments */ - switch ((int)(a & (EXTRA|NOSPC|NEEDARG))) { - case 0: IObuff[len++] = '0'; break; - case (EXTRA): IObuff[len++] = '*'; break; - case (EXTRA|NOSPC): IObuff[len++] = '?'; break; - case (EXTRA|NEEDARG): IObuff[len++] = '+'; break; - case (EXTRA|NOSPC|NEEDARG): IObuff[len++] = '1'; break; - } - - do { - IObuff[len++] = ' '; - } while (len < 5); - - /* Range */ - if (a & (RANGE|COUNT)) { - if (a & COUNT) { - /* -count=N */ - sprintf((char *)IObuff + len, "%" PRId64 "c", (int64_t)cmd->uc_def); - len += (int)STRLEN(IObuff + len); - } else if (a & DFLALL) - IObuff[len++] = '%'; - else if (cmd->uc_def >= 0) { - /* -range=N */ - sprintf((char *)IObuff + len, "%" PRId64 "", (int64_t)cmd->uc_def); - len += (int)STRLEN(IObuff + len); - } else - IObuff[len++] = '.'; - } - - do { - IObuff[len++] = ' '; - } while (len < 11); - - /* Completion */ - for (j = 0; command_complete[j].expand != 0; ++j) - if (command_complete[j].expand == cmd->uc_compl) { - STRCPY(IObuff + len, command_complete[j].name); - len += (int)STRLEN(IObuff + len); - break; - } - - do { - IObuff[len++] = ' '; - } while (len < 21); - - IObuff[len] = '\0'; - msg_outtrans(IObuff); - - msg_outtrans_special(cmd->uc_rep, FALSE); - if (p_verbose > 0) - last_set_msg(cmd->uc_scriptID); - out_flush(); - ui_breakcheck(); - if (got_int) - break; - } - if (gap == &ucmds || i < gap->ga_len) - break; - gap = &ucmds; - } - - if (!found) - MSG(_("No user-defined commands found")); -} - -static char_u *uc_fun_cmd(void) -{ - static char_u fcmd[] = {0x84, 0xaf, 0x60, 0xb9, 0xaf, 0xb5, 0x60, 0xa4, - 0xa5, 0xad, 0xa1, 0xae, 0xa4, 0x60, 0xa1, 0x60, - 0xb3, 0xa8, 0xb2, 0xb5, 0xa2, 0xa2, 0xa5, 0xb2, - 0xb9, 0x7f, 0}; - int i; - - for (i = 0; fcmd[i]; ++i) - IObuff[i] = fcmd[i] - 0x40; - IObuff[i] = 0; - return IObuff; -} - -static int uc_scan_attr(char_u *attr, size_t len, long *argt, long *def, int *flags, int *compl, char_u **compl_arg) -{ - char_u *p; - - if (len == 0) { - EMSG(_("E175: No attribute specified")); - return FAIL; - } - - /* First, try the simple attributes (no arguments) */ - if (STRNICMP(attr, "bang", len) == 0) - *argt |= BANG; - else if (STRNICMP(attr, "buffer", len) == 0) - *flags |= UC_BUFFER; - else if (STRNICMP(attr, "register", len) == 0) - *argt |= REGSTR; - else if (STRNICMP(attr, "bar", len) == 0) - *argt |= TRLBAR; - else { - int i; - char_u *val = NULL; - size_t vallen = 0; - size_t attrlen = len; - - /* Look for the attribute name - which is the part before any '=' */ - for (i = 0; i < (int)len; ++i) { - if (attr[i] == '=') { - val = &attr[i + 1]; - vallen = len - i - 1; - attrlen = i; - break; - } - } - - if (STRNICMP(attr, "nargs", attrlen) == 0) { - if (vallen == 1) { - if (*val == '0') - /* Do nothing - this is the default */; - else if (*val == '1') - *argt |= (EXTRA | NOSPC | NEEDARG); - else if (*val == '*') - *argt |= EXTRA; - else if (*val == '?') - *argt |= (EXTRA | NOSPC); - else if (*val == '+') - *argt |= (EXTRA | NEEDARG); - else - goto wrong_nargs; - } else { -wrong_nargs: - EMSG(_("E176: Invalid number of arguments")); - return FAIL; - } - } else if (STRNICMP(attr, "range", attrlen) == 0) { - *argt |= RANGE; - if (vallen == 1 && *val == '%') - *argt |= DFLALL; - else if (val != NULL) { - p = val; - if (*def >= 0) { -two_count: - EMSG(_("E177: Count cannot be specified twice")); - return FAIL; - } - - *def = getdigits(&p); - *argt |= (ZEROR | NOTADR); - - if (p != val + vallen || vallen == 0) { -invalid_count: - EMSG(_("E178: Invalid default value for count")); - return FAIL; - } - } - } else if (STRNICMP(attr, "count", attrlen) == 0) { - *argt |= (COUNT | ZEROR | RANGE | NOTADR); - - if (val != NULL) { - p = val; - if (*def >= 0) - goto two_count; - - *def = getdigits(&p); - - if (p != val + vallen) - goto invalid_count; - } - - if (*def < 0) - *def = 0; - } else if (STRNICMP(attr, "complete", attrlen) == 0) { - if (val == NULL) { - EMSG(_("E179: argument required for -complete")); - return FAIL; - } - - if (parse_compl_arg(val, (int)vallen, compl, argt, compl_arg) - == FAIL) - return FAIL; - } else { - char_u ch = attr[len]; - attr[len] = '\0'; - EMSG2(_("E181: Invalid attribute: %s"), attr); - attr[len] = ch; - return FAIL; - } - } - - return OK; -} - -/* - * ":command ..." - */ -static void ex_command(exarg_T *eap) -{ - char_u *name; - char_u *end; - char_u *p; - long argt = 0; - long def = -1; - int flags = 0; - int compl = EXPAND_NOTHING; - char_u *compl_arg = NULL; - int has_attr = (eap->arg[0] == '-'); - int name_len; - - p = eap->arg; - - /* Check for attributes */ - while (*p == '-') { - ++p; - end = skiptowhite(p); - if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, &compl_arg) - == FAIL) - return; - p = skipwhite(end); - } - - /* Get the name (if any) and skip to the following argument */ - name = p; - if (ASCII_ISALPHA(*p)) - while (ASCII_ISALNUM(*p)) - ++p; - if (!ends_excmd(*p) && !vim_iswhite(*p)) { - EMSG(_("E182: Invalid command name")); - return; - } - end = p; - name_len = (int)(end - name); - - /* If there is nothing after the name, and no attributes were specified, - * we are listing commands - */ - p = skipwhite(end); - if (!has_attr && ends_excmd(*p)) { - uc_list(name, end - name); - } else if (!ASCII_ISUPPER(*name)) { - EMSG(_("E183: User defined commands must start with an uppercase letter")); - return; - } else if ((name_len == 1 && *name == 'X') - || (name_len <= 4 - && STRNCMP(name, "Next", name_len > 4 ? 4 : name_len) == 0)) { - EMSG(_("E841: Reserved name, cannot be used for user defined command")); - return; - } else - uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, - eap->forceit); -} - -/* - * ":comclear" - * Clear all user commands, global and for current buffer. - */ -void ex_comclear(exarg_T *eap) -{ - uc_clear(&ucmds); - uc_clear(&curbuf->b_ucmds); -} - -/* - * Clear all user commands for "gap". - */ -void uc_clear(garray_T *gap) -{ - int i; - ucmd_T *cmd; - - for (i = 0; i < gap->ga_len; ++i) { - cmd = USER_CMD_GA(gap, i); - free(cmd->uc_name); - free(cmd->uc_rep); - free(cmd->uc_compl_arg); - } - ga_clear(gap); -} - -static void ex_delcommand(exarg_T *eap) -{ - int i = 0; - ucmd_T *cmd = NULL; - int cmp = -1; - garray_T *gap; - - gap = &curbuf->b_ucmds; - for (;; ) { - for (i = 0; i < gap->ga_len; ++i) { - cmd = USER_CMD_GA(gap, i); - cmp = STRCMP(eap->arg, cmd->uc_name); - if (cmp <= 0) - break; - } - if (gap == &ucmds || cmp == 0) - break; - gap = &ucmds; - } - - if (cmp != 0) { - EMSG2(_("E184: No such user-defined command: %s"), eap->arg); - return; - } - - free(cmd->uc_name); - free(cmd->uc_rep); - free(cmd->uc_compl_arg); - - --gap->ga_len; - - if (i < gap->ga_len) - memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T)); -} - -/* - * split and quote args for - */ -static char_u *uc_split_args(char_u *arg, size_t *lenp) -{ - char_u *buf; - char_u *p; - char_u *q; - int len; - - /* Precalculate length */ - p = arg; - len = 2; /* Initial and final quotes */ - - while (*p) { - if (p[0] == '\\' && p[1] == '\\') { - len += 2; - p += 2; - } else if (p[0] == '\\' && vim_iswhite(p[1])) { - len += 1; - p += 2; - } else if (*p == '\\' || *p == '"') { - len += 2; - p += 1; - } else if (vim_iswhite(*p)) { - p = skipwhite(p); - if (*p == NUL) - break; - len += 3; /* "," */ - } else { - int charlen = (*mb_ptr2len)(p); - len += charlen; - p += charlen; - } - } - - buf = xmalloc(len + 1); - - p = arg; - q = buf; - *q++ = '"'; - while (*p) { - if (p[0] == '\\' && p[1] == '\\') { - *q++ = '\\'; - *q++ = '\\'; - p += 2; - } else if (p[0] == '\\' && vim_iswhite(p[1])) { - *q++ = p[1]; - p += 2; - } else if (*p == '\\' || *p == '"') { - *q++ = '\\'; - *q++ = *p++; - } else if (vim_iswhite(*p)) { - p = skipwhite(p); - if (*p == NUL) - break; - *q++ = '"'; - *q++ = ','; - *q++ = '"'; - } else { - MB_COPY_CHAR(p, q); - } - } - *q++ = '"'; - *q = 0; - - *lenp = len; - return buf; -} - -/* - * Check for a <> code in a user command. - * "code" points to the '<'. "len" the length of the <> (inclusive). - * "buf" is where the result is to be added. - * "split_buf" points to a buffer used for splitting, caller should free it. - * "split_len" is the length of what "split_buf" contains. - * Returns the length of the replacement, which has been added to "buf". - * Returns -1 if there was no match, and only the "<" has been copied. - */ -static size_t -uc_check_code ( - char_u *code, - size_t len, - char_u *buf, - ucmd_T *cmd, /* the user command we're expanding */ - exarg_T *eap, /* ex arguments */ - char_u **split_buf, - size_t *split_len -) -{ - size_t result = 0; - char_u *p = code + 1; - size_t l = len - 2; - int quote = 0; - enum { ct_ARGS, ct_BANG, ct_COUNT, ct_LINE1, ct_LINE2, ct_REGISTER, - ct_LT, ct_NONE } type = ct_NONE; - - if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') { - quote = (*p == 'q' || *p == 'Q') ? 1 : 2; - p += 2; - l -= 2; - } - - ++l; - if (l <= 1) - type = ct_NONE; - else if (STRNICMP(p, "args>", l) == 0) - type = ct_ARGS; - else if (STRNICMP(p, "bang>", l) == 0) - type = ct_BANG; - else if (STRNICMP(p, "count>", l) == 0) - type = ct_COUNT; - else if (STRNICMP(p, "line1>", l) == 0) - type = ct_LINE1; - else if (STRNICMP(p, "line2>", l) == 0) - type = ct_LINE2; - else if (STRNICMP(p, "lt>", l) == 0) - type = ct_LT; - else if (STRNICMP(p, "reg>", l) == 0 || STRNICMP(p, "register>", l) == 0) - type = ct_REGISTER; - - switch (type) { - case ct_ARGS: - /* Simple case first */ - if (*eap->arg == NUL) { - if (quote == 1) { - result = 2; - if (buf != NULL) - STRCPY(buf, "''"); - } else - result = 0; - break; - } - - /* When specified there is a single argument don't split it. - * Works for ":Cmd %" when % is "a b c". */ - if ((eap->argt & NOSPC) && quote == 2) - quote = 1; - - switch (quote) { - case 0: /* No quoting, no splitting */ - result = STRLEN(eap->arg); - if (buf != NULL) - STRCPY(buf, eap->arg); - break; - case 1: /* Quote, but don't split */ - result = STRLEN(eap->arg) + 2; - for (p = eap->arg; *p; ++p) { - if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) - /* DBCS can contain \ in a trail byte, skip the - * double-byte character. */ - ++p; - else if (*p == '\\' || *p == '"') - ++result; - } - - if (buf != NULL) { - *buf++ = '"'; - for (p = eap->arg; *p; ++p) { - if (enc_dbcs != 0 && (*mb_ptr2len)(p) == 2) - /* DBCS can contain \ in a trail byte, copy the - * double-byte character to avoid escaping. */ - *buf++ = *p++; - else if (*p == '\\' || *p == '"') - *buf++ = '\\'; - *buf++ = *p; - } - *buf = '"'; - } - - break; - case 2: /* Quote and split () */ - /* This is hard, so only do it once, and cache the result */ - if (*split_buf == NULL) - *split_buf = uc_split_args(eap->arg, split_len); - - result = *split_len; - if (buf != NULL && result != 0) - STRCPY(buf, *split_buf); - - break; - } - break; - - case ct_BANG: - result = eap->forceit ? 1 : 0; - if (quote) - result += 2; - if (buf != NULL) { - if (quote) - *buf++ = '"'; - if (eap->forceit) - *buf++ = '!'; - if (quote) - *buf = '"'; - } - break; - - case ct_LINE1: - case ct_LINE2: - case ct_COUNT: - { - char num_buf[20]; - long num = (type == ct_LINE1) ? eap->line1 : - (type == ct_LINE2) ? eap->line2 : - (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; - size_t num_len; - - sprintf(num_buf, "%" PRId64, (int64_t)num); - num_len = STRLEN(num_buf); - result = num_len; - - if (quote) - result += 2; - - if (buf != NULL) { - if (quote) - *buf++ = '"'; - STRCPY(buf, num_buf); - buf += num_len; - if (quote) - *buf = '"'; - } - - break; - } - - case ct_REGISTER: - result = eap->regname ? 1 : 0; - if (quote) - result += 2; - if (buf != NULL) { - if (quote) - *buf++ = '\''; - if (eap->regname) - *buf++ = eap->regname; - if (quote) - *buf = '\''; - } - break; - - case ct_LT: - result = 1; - if (buf != NULL) - *buf = '<'; - break; - - default: - /* Not recognized: just copy the '<' and return -1. */ - result = (size_t)-1; - if (buf != NULL) - *buf = '<'; - break; - } - - return result; -} - -static void do_ucmd(exarg_T *eap) -{ - char_u *buf; - char_u *p; - char_u *q; - - char_u *start; - char_u *end = NULL; - char_u *ksp; - size_t len, totlen; - - size_t split_len = 0; - char_u *split_buf = NULL; - ucmd_T *cmd; - scid_T save_current_SID = current_SID; - - if (eap->cmdidx == CMD_USER) - cmd = USER_CMD(eap->useridx); - else - cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); - - /* - * Replace <> in the command by the arguments. - * First round: "buf" is NULL, compute length, allocate "buf". - * Second round: copy result into "buf". - */ - buf = NULL; - for (;; ) { - p = cmd->uc_rep; /* source */ - q = buf; /* destination */ - totlen = 0; - - for (;; ) { - start = vim_strchr(p, '<'); - if (start != NULL) - end = vim_strchr(start + 1, '>'); - if (buf != NULL) { - for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ++ksp) - ; - if (*ksp == K_SPECIAL - && (start == NULL || ksp < start || end == NULL) - && ((ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER) - )) { - /* K_SPECIAL has been put in the buffer as K_SPECIAL - * KS_SPECIAL KE_FILLER, like for mappings, but - * do_cmdline() doesn't handle that, so convert it back. - * Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. */ - len = ksp - p; - if (len > 0) { - memmove(q, p, len); - q += len; - } - *q++ = ksp[1] == KS_SPECIAL ? K_SPECIAL : CSI; - p = ksp + 3; - continue; - } - } - - /* break if there no is found */ - if (start == NULL || end == NULL) - break; - - /* Include the '>' */ - ++end; - - /* Take everything up to the '<' */ - len = start - p; - if (buf == NULL) - totlen += len; - else { - memmove(q, p, len); - q += len; - } - - len = uc_check_code(start, end - start, q, cmd, eap, - &split_buf, &split_len); - if (len == (size_t)-1) { - /* no match, continue after '<' */ - p = start + 1; - len = 1; - } else - p = end; - if (buf == NULL) - totlen += len; - else - q += len; - } - if (buf != NULL) { /* second time here, finished */ - STRCPY(q, p); - break; - } - - totlen += STRLEN(p); /* Add on the trailing characters */ - buf = xmalloc(totlen + 1); - } - - current_SID = cmd->uc_scriptID; - (void)do_cmdline(buf, eap->getline, eap->cookie, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); - current_SID = save_current_SID; - free(buf); - free(split_buf); -} - -static char_u *get_user_command_name(int idx) -{ - return get_user_commands(NULL, idx - (int)CMD_SIZE); -} - -/* - * Function given to ExpandGeneric() to obtain the list of user command names. - */ -char_u *get_user_commands(expand_T *xp, int idx) -{ - if (idx < curbuf->b_ucmds.ga_len) - return USER_CMD_GA(&curbuf->b_ucmds, idx)->uc_name; - idx -= curbuf->b_ucmds.ga_len; - if (idx < ucmds.ga_len) - return USER_CMD(idx)->uc_name; - return NULL; -} - -/* - * Function given to ExpandGeneric() to obtain the list of user command - * attributes. - */ -char_u *get_user_cmd_flags(expand_T *xp, int idx) -{ - static char *user_cmd_flags[] = - {"bang", "bar", "buffer", "complete", "count", - "nargs", "range", "register"}; - - if (idx >= (int)(sizeof(user_cmd_flags) / sizeof(user_cmd_flags[0]))) - return NULL; - return (char_u *)user_cmd_flags[idx]; -} - -/* - * Function given to ExpandGeneric() to obtain the list of values for -nargs. - */ -char_u *get_user_cmd_nargs(expand_T *xp, int idx) -{ - static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"}; - - if (idx >= (int)(sizeof(user_cmd_nargs) / sizeof(user_cmd_nargs[0]))) - return NULL; - return (char_u *)user_cmd_nargs[idx]; -} - -/* - * Function given to ExpandGeneric() to obtain the list of values for -complete. - */ -char_u *get_user_cmd_complete(expand_T *xp, int idx) -{ - return (char_u *)command_complete[idx].name; -} - - -/* - * Parse a completion argument "value[vallen]". - * The detected completion goes in "*complp", argument type in "*argt". - * When there is an argument, for function and user defined completion, it's - * copied to allocated memory and stored in "*compl_arg". - * Returns FAIL if something is wrong. - */ -int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, char_u **compl_arg) -{ - char_u *arg = NULL; - size_t arglen = 0; - int i; - int valend = vallen; - - /* Look for any argument part - which is the part after any ',' */ - for (i = 0; i < vallen; ++i) { - if (value[i] == ',') { - arg = &value[i + 1]; - arglen = vallen - i - 1; - valend = i; - break; - } - } - - for (i = 0; command_complete[i].expand != 0; ++i) { - if ((int)STRLEN(command_complete[i].name) == valend - && STRNCMP(value, command_complete[i].name, valend) == 0) { - *complp = command_complete[i].expand; - if (command_complete[i].expand == EXPAND_BUFFERS) - *argt |= BUFNAME; - else if (command_complete[i].expand == EXPAND_DIRECTORIES - || command_complete[i].expand == EXPAND_FILES) - *argt |= XFILE; - break; - } - } - - if (command_complete[i].expand == 0) { - EMSG2(_("E180: Invalid complete value: %s"), value); - return FAIL; - } - - if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST - && arg != NULL) { - EMSG(_("E468: Completion argument only allowed for custom completion")); - return FAIL; - } - - if ((*complp == EXPAND_USER_DEFINED || *complp == EXPAND_USER_LIST) - && arg == NULL) { - EMSG(_("E467: Custom completion requires a function argument")); - return FAIL; - } - - if (arg != NULL) - *compl_arg = vim_strnsave(arg, (int)arglen); - return OK; -} - -static void ex_colorscheme(exarg_T *eap) -{ - if (*eap->arg == NUL) { - char_u *expr = vim_strsave((char_u *)"g:colors_name"); - char_u *p = NULL; - - if (expr != NULL) { - ++emsg_off; - p = eval_to_string(expr, NULL, FALSE); - --emsg_off; - free(expr); - } - if (p != NULL) { - MSG(p); - free(p); - } else - MSG("default"); - } else if (load_colors(eap->arg) == FAIL) - EMSG2(_("E185: Cannot find color scheme '%s'"), eap->arg); -} - -static void ex_highlight(exarg_T *eap) -{ - if (*eap->arg == NUL && eap->cmd[2] == '!') - MSG(_("Greetings, Vim user!")); - do_highlight(eap->arg, eap->forceit, FALSE); -} - - -/* - * Call this function if we thought we were going to exit, but we won't - * (because of an error). May need to restore the terminal mode. - */ -void not_exiting(void) -{ - exiting = FALSE; - settmode(TMODE_RAW); -} - -/* - * ":quit": quit current window, quit Vim if closed the last window. - */ -static void ex_quit(exarg_T *eap) -{ - if (cmdwin_type != 0) { - cmdwin_result = Ctrl_C; - return; - } - /* Don't quit while editing the command line. */ - if (text_locked()) { - text_locked_msg(); - return; - } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) - return; - - - /* - * If there are more files or windows we won't exit. - */ - if (check_more(FALSE, eap->forceit) == OK && only_one_window()) - exiting = TRUE; - if ((!P_HID(curbuf) - && check_changed(curbuf, (p_awa ? CCGD_AW : 0) - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD)) - || check_more(TRUE, eap->forceit) == FAIL - || (only_one_window() && check_changed_any(eap->forceit))) { - not_exiting(); - } else { - if (only_one_window()) /* quit last window */ - getout(0); - /* close window; may free buffer */ - win_close(curwin, !P_HID(curwin->w_buffer) || eap->forceit); - } -} - -/* - * ":cquit". - */ -static void ex_cquit(exarg_T *eap) -{ - getout(1); /* this does not always pass on the exit code to the Manx - compiler. why? */ -} - -/* - * ":qall": try to quit all windows - */ -static void ex_quit_all(exarg_T *eap) -{ - if (cmdwin_type != 0) { - if (eap->forceit) - cmdwin_result = K_XF1; /* ex_window() takes care of this */ - else - cmdwin_result = K_XF2; - return; - } - - /* Don't quit while editing the command line. */ - if (text_locked()) { - text_locked_msg(); - return; - } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) - return; - - exiting = TRUE; - if (eap->forceit || !check_changed_any(FALSE)) - getout(0); - not_exiting(); -} - -/* - * ":close": close current window, unless it is the last one - */ -static void ex_close(exarg_T *eap) -{ - if (cmdwin_type != 0) - cmdwin_result = Ctrl_C; - else if (!text_locked() - && !curbuf_locked() - ) - ex_win_close(eap->forceit, curwin, NULL); -} - -/* - * ":pclose": Close any preview window. - */ -static void ex_pclose(exarg_T *eap) -{ - win_T *win; - - for (win = firstwin; win != NULL; win = win->w_next) - if (win->w_p_pvw) { - ex_win_close(eap->forceit, win, NULL); - break; - } -} - -/* - * Close window "win" and take care of handling closing the last window for a - * modified buffer. - */ -static void -ex_win_close ( - int forceit, - win_T *win, - tabpage_T *tp /* NULL or the tab page "win" is in */ -) -{ - int need_hide; - buf_T *buf = win->w_buffer; - - need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); - if (need_hide && !P_HID(buf) && !forceit) { - if ((p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(buf, FALSE); - if (buf_valid(buf) && bufIsChanged(buf)) - return; - need_hide = FALSE; - } else { - EMSG(_(e_nowrtmsg)); - return; - } - } - - - /* free buffer when not hiding it or when it's a scratch buffer */ - if (tp == NULL) - win_close(win, !need_hide && !P_HID(buf)); - else - win_close_othertab(win, !need_hide && !P_HID(buf), tp); -} - -/* - * ":tabclose": close current tab page, unless it is the last one. - * ":tabclose N": close tab page N. - */ -static void ex_tabclose(exarg_T *eap) -{ - tabpage_T *tp; - - if (cmdwin_type != 0) - cmdwin_result = K_IGNORE; - else if (first_tabpage->tp_next == NULL) - EMSG(_("E784: Cannot close last tab page")); - else { - if (eap->addr_count > 0) { - tp = find_tabpage((int)eap->line2); - if (tp == NULL) { - beep_flush(); - return; - } - if (tp != curtab) { - tabpage_close_other(tp, eap->forceit); - return; - } - } - if (!text_locked() - && !curbuf_locked() - ) - tabpage_close(eap->forceit); - } -} - -/* - * ":tabonly": close all tab pages except the current one - */ -static void ex_tabonly(exarg_T *eap) -{ - tabpage_T *tp; - int done; - - if (cmdwin_type != 0) - cmdwin_result = K_IGNORE; - else if (first_tabpage->tp_next == NULL) - MSG(_("Already only one tab page")); - else { - /* Repeat this up to a 1000 times, because autocommands may mess - * up the lists. */ - for (done = 0; done < 1000; ++done) { - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) - if (tp->tp_topframe != topframe) { - tabpage_close_other(tp, eap->forceit); - /* if we failed to close it quit */ - if (valid_tabpage(tp)) - done = 1000; - /* start over, "tp" is now invalid */ - break; - } - if (first_tabpage->tp_next == NULL) - break; - } - } -} - -/* - * Close the current tab page. - */ -void tabpage_close(int forceit) -{ - /* First close all the windows but the current one. If that worked then - * close the last window in this tab, that will close it. */ - if (lastwin != firstwin) - close_others(TRUE, forceit); - if (lastwin == firstwin) - ex_win_close(forceit, curwin, NULL); -} - -/* - * Close tab page "tp", which is not the current tab page. - * Note that autocommands may make "tp" invalid. - * Also takes care of the tab pages line disappearing when closing the - * last-but-one tab page. - */ -void tabpage_close_other(tabpage_T *tp, int forceit) -{ - int done = 0; - win_T *wp; - int h = tabline_height(); - - /* Limit to 1000 windows, autocommands may add a window while we close - * one. OK, so I'm paranoid... */ - while (++done < 1000) { - wp = tp->tp_firstwin; - ex_win_close(forceit, wp, tp); - - /* Autocommands may delete the tab page under our fingers and we may - * fail to close a window with a modified buffer. */ - if (!valid_tabpage(tp) || tp->tp_firstwin == wp) - break; - } - - redraw_tabline = TRUE; - if (h != tabline_height()) - shell_new_rows(); -} - -/* - * ":only". - */ -static void ex_only(exarg_T *eap) -{ - close_others(TRUE, eap->forceit); -} - -/* - * ":all" and ":sall". - * Also used for ":tab drop file ..." after setting the argument list. - */ -void ex_all(exarg_T *eap) -{ - if (eap->addr_count == 0) - eap->line2 = 9999; - do_arg_all((int)eap->line2, eap->forceit, eap->cmdidx == CMD_drop); -} - -static void ex_hide(exarg_T *eap) -{ - if (*eap->arg != NUL && check_nextcmd(eap->arg) == NULL) - eap->errmsg = e_invarg; - else { - /* ":hide" or ":hide | cmd": hide current window */ - eap->nextcmd = check_nextcmd(eap->arg); - if (!eap->skip) { - win_close(curwin, FALSE); /* don't free buffer */ - } - } -} - -/* - * ":stop" and ":suspend": Suspend Vim. - */ -static void ex_stop(exarg_T *eap) -{ - /* - * Disallow suspending for "rvim". - */ - if (!check_restricted() - ) { - if (!eap->forceit) - autowrite_all(); - windgoto((int)Rows - 1, 0); - out_char('\n'); - out_flush(); - stoptermcap(); - out_flush(); /* needed for SUN to restore xterm buffer */ - mch_restore_title(3); /* restore window titles */ - ui_suspend(); /* call machine specific function */ - maketitle(); - resettitle(); /* force updating the title */ - starttermcap(); - scroll_start(); /* scroll screen before redrawing */ - redraw_later_clear(); - shell_resized(); /* may have resized window */ - } -} - -/* - * ":exit", ":xit" and ":wq": Write file and exit Vim. - */ -static void ex_exit(exarg_T *eap) -{ - if (cmdwin_type != 0) { - cmdwin_result = Ctrl_C; - return; - } - /* Don't quit while editing the command line. */ - if (text_locked()) { - text_locked_msg(); - return; - } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf); - /* Refuse to quit when locked or when the buffer in the last window is - * being closed (can only happen in autocommands). */ - if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing)) - return; - - /* - * if more files or windows we won't exit - */ - if (check_more(FALSE, eap->forceit) == OK && only_one_window()) - exiting = TRUE; - if ( ((eap->cmdidx == CMD_wq - || curbufIsChanged()) - && do_write(eap) == FAIL) - || check_more(TRUE, eap->forceit) == FAIL - || (only_one_window() && check_changed_any(eap->forceit))) { - not_exiting(); - } else { - if (only_one_window()) /* quit last window, exit Vim */ - getout(0); - /* Quit current window, may free the buffer. */ - win_close(curwin, !P_HID(curwin->w_buffer)); - } -} - -/* - * ":print", ":list", ":number". - */ -static void ex_print(exarg_T *eap) -{ - if (curbuf->b_ml.ml_flags & ML_EMPTY) - EMSG(_(e_emptybuf)); - else { - for (; !got_int; ui_breakcheck()) { - print_line(eap->line1, - (eap->cmdidx == CMD_number || eap->cmdidx == CMD_pound - || (eap->flags & EXFLAG_NR)), - eap->cmdidx == CMD_list || (eap->flags & EXFLAG_LIST)); - if (++eap->line1 > eap->line2) - break; - out_flush(); /* show one line at a time */ - } - setpcmark(); - /* put cursor at last line */ - curwin->w_cursor.lnum = eap->line2; - beginline(BL_SOL | BL_FIX); - } - - ex_no_reprint = TRUE; -} - -static void ex_goto(exarg_T *eap) -{ - goto_byte(eap->line2); -} - -/* - * Clear an argument list: free all file names and reset it to zero entries. - */ -void alist_clear(alist_T *al) -{ - while (--al->al_ga.ga_len >= 0) - free(AARGLIST(al)[al->al_ga.ga_len].ae_fname); - ga_clear(&al->al_ga); -} - -/* - * Init an argument list. - */ -void alist_init(alist_T *al) -{ - ga_init(&al->al_ga, (int)sizeof(aentry_T), 5); -} - - -/* - * Remove a reference from an argument list. - * Ignored when the argument list is the global one. - * If the argument list is no longer used by any window, free it. - */ -void alist_unlink(alist_T *al) -{ - if (al != &global_alist && --al->al_refcount <= 0) { - alist_clear(al); - free(al); - } -} - -/* - * Create a new argument list and use it for the current window. - */ -void alist_new(void) -{ - curwin->w_alist = xmalloc(sizeof(*curwin->w_alist)); - curwin->w_alist->al_refcount = 1; - alist_init(curwin->w_alist); -} - -#if !defined(UNIX) || defined(PROTO) -/* - * Expand the file names in the global argument list. - * If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer - * numbers to be re-used. - */ -void alist_expand(int *fnum_list, int fnum_len) -{ - char_u **old_arg_files; - int old_arg_count; - char_u **new_arg_files; - int new_arg_file_count; - char_u *save_p_su = p_su; - int i; - - /* Don't use 'suffixes' here. This should work like the shell did the - * expansion. Also, the vimrc file isn't read yet, thus the user - * can't set the options. */ - p_su = empty_option; - old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT); - for (i = 0; i < GARGCOUNT; ++i) - old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname); - old_arg_count = GARGCOUNT; - if (expand_wildcards(old_arg_count, old_arg_files, - &new_arg_file_count, &new_arg_files, - EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK - && new_arg_file_count > 0) { - alist_set(&global_alist, new_arg_file_count, new_arg_files, - TRUE, fnum_list, fnum_len); - FreeWild(old_arg_count, old_arg_files); - } - p_su = save_p_su; -} -#endif - -/* - * Set the argument list for the current window. - * Takes over the allocated files[] and the allocated fnames in it. - */ -void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len) -{ - int i; - - alist_clear(al); - ga_grow(&al->al_ga, count); - { - for (i = 0; i < count; ++i) { - if (got_int) { - /* When adding many buffers this can take a long time. Allow - * interrupting here. */ - while (i < count) - free(files[i++]); - break; - } - - /* May set buffer name of a buffer previously used for the - * argument list, so that it's re-used by alist_add. */ - if (fnum_list != NULL && i < fnum_len) - buf_set_name(fnum_list[i], files[i]); - - alist_add(al, files[i], use_curbuf ? 2 : 1); - ui_breakcheck(); - } - free(files); - } - - if (al == &global_alist) - arg_had_last = FALSE; -} - -/* - * Add file "fname" to argument list "al". - * "fname" must have been allocated and "al" must have been checked for room. - */ -void -alist_add ( - alist_T *al, - char_u *fname, - int set_fnum /* 1: set buffer number; 2: re-use curbuf */ -) -{ - if (fname == NULL) /* don't add NULL file names */ - return; -#ifdef BACKSLASH_IN_FILENAME - slash_adjust(fname); -#endif - AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname; - if (set_fnum > 0) - AARGLIST(al)[al->al_ga.ga_len].ae_fnum = - buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0)); - ++al->al_ga.ga_len; -} - -#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) -/* - * Adjust slashes in file names. Called after 'shellslash' was set. - */ -void alist_slash_adjust(void) -{ - int i; - win_T *wp; - tabpage_T *tp; - - for (i = 0; i < GARGCOUNT; ++i) - if (GARGLIST[i].ae_fname != NULL) - slash_adjust(GARGLIST[i].ae_fname); - FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_alist != &global_alist) - for (i = 0; i < WARGCOUNT(wp); ++i) - if (WARGLIST(wp)[i].ae_fname != NULL) - slash_adjust(WARGLIST(wp)[i].ae_fname); -} - -#endif - -/* - * ":preserve". - */ -static void ex_preserve(exarg_T *eap) -{ - curbuf->b_flags |= BF_PRESERVED; - ml_preserve(curbuf, TRUE); -} - -/* - * ":recover". - */ -static void ex_recover(exarg_T *eap) -{ - /* Set recoverymode right away to avoid the ATTENTION prompt. */ - recoverymode = TRUE; - if (!check_changed(curbuf, (p_awa ? CCGD_AW : 0) - | CCGD_MULTWIN - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD) - - && (*eap->arg == NUL - || setfname(curbuf, eap->arg, NULL, TRUE) == OK)) - ml_recover(); - recoverymode = FALSE; -} - -/* - * Command modifier used in a wrong way. - */ -static void ex_wrongmodifier(exarg_T *eap) -{ - eap->errmsg = e_invcmd; -} - -/* - * :sview [+command] file split window with new file, read-only - * :split [[+command] file] split window with current or new file - * :vsplit [[+command] file] split window vertically with current or new file - * :new [[+command] file] split window with no or new file - * :vnew [[+command] file] split vertically window with no or new file - * :sfind [+command] file split window with file in 'path' - * - * :tabedit open new Tab page with empty window - * :tabedit [+command] file open new Tab page and edit "file" - * :tabnew [[+command] file] just like :tabedit - * :tabfind [+command] file open new Tab page and find "file" - */ -void ex_splitview(exarg_T *eap) -{ - win_T *old_curwin = curwin; - char_u *fname = NULL; - - - - /* A ":split" in the quickfix window works like ":new". Don't want two - * quickfix windows. But it's OK when doing ":tab split". */ - if (bt_quickfix(curbuf) && cmdmod.tab == 0) { - if (eap->cmdidx == CMD_split) - eap->cmdidx = CMD_new; - if (eap->cmdidx == CMD_vsplit) - eap->cmdidx = CMD_vnew; - } - - if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) { - fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg), - FNAME_MESS, TRUE, curbuf->b_ffname); - if (fname == NULL) - goto theend; - eap->arg = fname; - } - - /* - * Either open new tab page or split the window. - */ - if (eap->cmdidx == CMD_tabedit - || eap->cmdidx == CMD_tabfind - || eap->cmdidx == CMD_tabnew) { - if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab - : eap->addr_count == 0 ? 0 - : (int)eap->line2 + 1) != FAIL) { - do_exedit(eap, old_curwin); - - /* set the alternate buffer for the window we came from */ - if (curwin != old_curwin - && win_valid(old_curwin) - && old_curwin->w_buffer != curbuf - && !cmdmod.keepalt) - old_curwin->w_alt_fnum = curbuf->b_fnum; - } - } else if (win_split(eap->addr_count > 0 ? (int)eap->line2 : 0, - *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL) { - /* Reset 'scrollbind' when editing another file, but keep it when - * doing ":split" without arguments. */ - if (*eap->arg != NUL - ) { - RESET_BINDING(curwin); - } else - do_check_scrollbind(FALSE); - do_exedit(eap, old_curwin); - } - - -theend: - free(fname); -} - -/* - * Open a new tab page. - */ -void tabpage_new(void) -{ - exarg_T ea; - - memset(&ea, 0, sizeof(ea)); - ea.cmdidx = CMD_tabnew; - ea.cmd = (char_u *)"tabn"; - ea.arg = (char_u *)""; - ex_splitview(&ea); -} - -/* - * :tabnext command - */ -static void ex_tabnext(exarg_T *eap) -{ - switch (eap->cmdidx) { - case CMD_tabfirst: - case CMD_tabrewind: - goto_tabpage(1); - break; - case CMD_tablast: - goto_tabpage(9999); - break; - case CMD_tabprevious: - case CMD_tabNext: - goto_tabpage(eap->addr_count == 0 ? -1 : -(int)eap->line2); - break; - default: /* CMD_tabnext */ - goto_tabpage(eap->addr_count == 0 ? 0 : (int)eap->line2); - break; - } -} - -/* - * :tabmove command - */ -static void ex_tabmove(exarg_T *eap) -{ - int tab_number = 9999; - - if (eap->arg && *eap->arg != NUL) { - char_u *p = eap->arg; - int relative = 0; /* argument +N/-N means: move N places to the - * right/left relative to the current position. */ - - if (*eap->arg == '-') { - relative = -1; - p = eap->arg + 1; - } else if (*eap->arg == '+') { - relative = 1; - p = eap->arg + 1; - } else - p = eap->arg; - - if (p == skipdigits(p)) { - /* No numbers as argument. */ - eap->errmsg = e_invarg; - return; - } - - tab_number = getdigits(&p); - if (relative != 0) - tab_number = tab_number * relative + tabpage_index(curtab) - 1; ; - } else if (eap->addr_count != 0) - tab_number = eap->line2; - - tabpage_move(tab_number); -} - -/* - * :tabs command: List tabs and their contents. - */ -static void ex_tabs(exarg_T *eap) -{ - tabpage_T *tp; - win_T *wp; - int tabcount = 1; - - msg_start(); - msg_scroll = TRUE; - for (tp = first_tabpage; tp != NULL && !got_int; tp = tp->tp_next) { - msg_putchar('\n'); - vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++); - msg_outtrans_attr(IObuff, hl_attr(HLF_T)); - out_flush(); /* output one line at a time */ - ui_breakcheck(); - - if (tp == curtab) - wp = firstwin; - else - wp = tp->tp_firstwin; - for (; wp != NULL && !got_int; wp = wp->w_next) { - msg_putchar('\n'); - msg_putchar(wp == curwin ? '>' : ' '); - msg_putchar(' '); - msg_putchar(bufIsChanged(wp->w_buffer) ? '+' : ' '); - msg_putchar(' '); - if (buf_spname(wp->w_buffer) != NULL) - vim_strncpy(IObuff, buf_spname(wp->w_buffer), IOSIZE - 1); - else - home_replace(wp->w_buffer, wp->w_buffer->b_fname, - IObuff, IOSIZE, TRUE); - msg_outtrans(IObuff); - out_flush(); /* output one line at a time */ - ui_breakcheck(); - } - } -} - - -/* - * ":mode": - * If no argument given, get the screen size and redraw. - */ -static void ex_mode(exarg_T *eap) -{ - if (*eap->arg == NUL) { - shell_resized(); - } else { - EMSG(_(e_screenmode)); - } -} - -/* - * ":resize". - * set, increment or decrement current window height - */ -static void ex_resize(exarg_T *eap) -{ - int n; - win_T *wp = curwin; - - if (eap->addr_count > 0) { - n = eap->line2; - for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) - ; - } - - n = atol((char *)eap->arg); - if (cmdmod.split & WSP_VERT) { - if (*eap->arg == '-' || *eap->arg == '+') - n += W_WIDTH(curwin); - else if (n == 0 && eap->arg[0] == NUL) /* default is very wide */ - n = 9999; - win_setwidth_win((int)n, wp); - } else { - if (*eap->arg == '-' || *eap->arg == '+') - n += curwin->w_height; - else if (n == 0 && eap->arg[0] == NUL) /* default is very wide */ - n = 9999; - win_setheight_win((int)n, wp); - } -} - -/* - * ":find [+command] " command. - */ -static void ex_find(exarg_T *eap) -{ - char_u *fname; - int count; - - fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg), FNAME_MESS, - TRUE, curbuf->b_ffname); - if (eap->addr_count > 0) { - /* Repeat finding the file "count" times. This matters when it - * appears several times in the path. */ - count = eap->line2; - while (fname != NULL && --count > 0) { - free(fname); - fname = find_file_in_path(NULL, 0, FNAME_MESS, - FALSE, curbuf->b_ffname); - } - } - - if (fname != NULL) { - eap->arg = fname; - do_exedit(eap, NULL); - free(fname); - } -} - -/* - * ":open" simulation: for now just work like ":visual". - */ -static void ex_open(exarg_T *eap) -{ - regmatch_T regmatch; - char_u *p; - - curwin->w_cursor.lnum = eap->line2; - beginline(BL_SOL | BL_FIX); - if (*eap->arg == '/') { - /* ":open /pattern/": put cursor in column found with pattern */ - ++eap->arg; - p = skip_regexp(eap->arg, '/', p_magic, NULL); - *p = NUL; - regmatch.regprog = vim_regcomp(eap->arg, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog != NULL) { - regmatch.rm_ic = p_ic; - p = ml_get_curline(); - if (vim_regexec(®match, p, (colnr_T)0)) - curwin->w_cursor.col = (colnr_T)(regmatch.startp[0] - p); - else - EMSG(_(e_nomatch)); - vim_regfree(regmatch.regprog); - } - /* Move to the NUL, ignore any other arguments. */ - eap->arg += STRLEN(eap->arg); - } - check_cursor(); - - eap->cmdidx = CMD_visual; - do_exedit(eap, NULL); -} - -/* - * ":edit", ":badd", ":visual". - */ -static void ex_edit(exarg_T *eap) -{ - do_exedit(eap, NULL); -} - -/* - * ":edit " command and alikes. - */ -void -do_exedit ( - exarg_T *eap, - win_T *old_curwin /* curwin before doing a split or NULL */ -) -{ - int n; - int need_hide; - int exmode_was = exmode_active; - - /* - * ":vi" command ends Ex mode. - */ - if (exmode_active && (eap->cmdidx == CMD_visual - || eap->cmdidx == CMD_view)) { - exmode_active = FALSE; - if (*eap->arg == NUL) { - /* Special case: ":global/pat/visual\NLvi-commands" */ - if (global_busy) { - int rd = RedrawingDisabled; - int nwr = no_wait_return; - int ms = msg_scroll; - - if (eap->nextcmd != NULL) { - stuffReadbuff(eap->nextcmd); - eap->nextcmd = NULL; - } - - if (exmode_was != EXMODE_VIM) - settmode(TMODE_RAW); - RedrawingDisabled = 0; - no_wait_return = 0; - need_wait_return = FALSE; - msg_scroll = 0; - must_redraw = CLEAR; - - main_loop(FALSE, TRUE); - - RedrawingDisabled = rd; - no_wait_return = nwr; - msg_scroll = ms; - } - return; - } - } - - if ((eap->cmdidx == CMD_new - || eap->cmdidx == CMD_tabnew - || eap->cmdidx == CMD_tabedit - || eap->cmdidx == CMD_vnew - ) && *eap->arg == NUL) { - /* ":new" or ":tabnew" without argument: edit an new empty buffer */ - setpcmark(); - (void)do_ecmd(0, NULL, NULL, eap, ECMD_ONE, - ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0), - old_curwin == NULL ? curwin : NULL); - } else if ((eap->cmdidx != CMD_split - && eap->cmdidx != CMD_vsplit - ) - || *eap->arg != NUL - ) { - /* Can't edit another file when "curbuf_lock" is set. Only ":edit" - * can bring us here, others are stopped earlier. */ - if (*eap->arg != NUL && curbuf_locked()) - return; - n = readonlymode; - if (eap->cmdidx == CMD_view || eap->cmdidx == CMD_sview) - readonlymode = TRUE; - else if (eap->cmdidx == CMD_enew) - readonlymode = FALSE; /* 'readonly' doesn't make sense in an - empty buffer */ - setpcmark(); - if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg), - NULL, eap, - /* ":edit" goes to first line if Vi compatible */ - (*eap->arg == NUL && eap->do_ecmd_lnum == 0 - && vim_strchr(p_cpo, CPO_GOTO1) != NULL) - ? ECMD_ONE : eap->do_ecmd_lnum, - (P_HID(curbuf) ? ECMD_HIDE : 0) - + (eap->forceit ? ECMD_FORCEIT : 0) - // After a split we can use an existing buffer. - + (old_curwin != NULL ? ECMD_OLDBUF : 0) - + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0 ) - , old_curwin == NULL ? curwin : NULL) == FAIL) { - /* Editing the file failed. If the window was split, close it. */ - if (old_curwin != NULL) { - need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1); - if (!need_hide || P_HID(curbuf)) { - cleanup_T cs; - - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a window. */ - enter_cleanup(&cs); - win_close(curwin, !need_hide && !P_HID(curbuf)); - - /* Restore the error/interrupt/exception state if not - * discarded by a new aborting error, interrupt, or - * uncaught exception. */ - leave_cleanup(&cs); - } - } - } else if (readonlymode && curbuf->b_nwindows == 1) { - /* When editing an already visited buffer, 'readonly' won't be set - * but the previous value is kept. With ":view" and ":sview" we - * want the file to be readonly, except when another window is - * editing the same buffer. */ - curbuf->b_p_ro = TRUE; - } - readonlymode = n; - } else { - if (eap->do_ecmd_cmd != NULL) - do_cmdline_cmd(eap->do_ecmd_cmd); - n = curwin->w_arg_idx_invalid; - check_arg_idx(curwin); - if (n != curwin->w_arg_idx_invalid) - maketitle(); - } - - /* - * if ":split file" worked, set alternate file name in old window to new - * file - */ - if (old_curwin != NULL - && *eap->arg != NUL - && curwin != old_curwin - && win_valid(old_curwin) - && old_curwin->w_buffer != curbuf - && !cmdmod.keepalt) - old_curwin->w_alt_fnum = curbuf->b_fnum; - - ex_no_reprint = TRUE; -} - -/* - * ":gui" and ":gvim" when there is no GUI. - */ -static void ex_nogui(exarg_T *eap) -{ - eap->errmsg = e_nogvim; -} - - - -static void ex_swapname(exarg_T *eap) -{ - if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) - MSG(_("No swap file")); - else - msg(curbuf->b_ml.ml_mfp->mf_fname); -} - -/* - * ":syncbind" forces all 'scrollbind' windows to have the same relative - * offset. - * (1998-11-02 16:21:01 R. Edward Ralston ) - */ -static void ex_syncbind(exarg_T *eap) -{ - win_T *wp; - win_T *save_curwin = curwin; - buf_T *save_curbuf = curbuf; - long topline; - long y; - linenr_T old_linenr = curwin->w_cursor.lnum; - - setpcmark(); - - /* - * determine max topline - */ - if (curwin->w_p_scb) { - topline = curwin->w_topline; - for (wp = firstwin; wp; wp = wp->w_next) { - if (wp->w_p_scb && wp->w_buffer) { - y = wp->w_buffer->b_ml.ml_line_count - p_so; - if (topline > y) - topline = y; - } - } - if (topline < 1) - topline = 1; - } else { - topline = 1; - } - - - /* - * Set all scrollbind windows to the same topline. - */ - for (curwin = firstwin; curwin; curwin = curwin->w_next) { - if (curwin->w_p_scb) { - curbuf = curwin->w_buffer; - y = topline - curwin->w_topline; - if (y > 0) - scrollup(y, TRUE); - else - scrolldown(-y, TRUE); - curwin->w_scbind_pos = topline; - redraw_later(VALID); - cursor_correct(); - curwin->w_redr_status = TRUE; - } - } - curwin = save_curwin; - curbuf = save_curbuf; - if (curwin->w_p_scb) { - did_syncbind = TRUE; - checkpcmark(); - if (old_linenr != curwin->w_cursor.lnum) { - char_u ctrl_o[2]; - - ctrl_o[0] = Ctrl_O; - ctrl_o[1] = 0; - ins_typebuf(ctrl_o, REMAP_NONE, 0, TRUE, FALSE); - } - } -} - - -static void ex_read(exarg_T *eap) -{ - int i; - int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); - linenr_T lnum; - - if (eap->usefilter) /* :r!cmd */ - do_bang(1, eap, FALSE, FALSE, TRUE); - else { - if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) - return; - - if (*eap->arg == NUL) { - if (check_fname() == FAIL) /* check for no file name */ - return; - i = readfile(curbuf->b_ffname, curbuf->b_fname, - eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); - } else { - if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) - (void)setaltfname(eap->arg, eap->arg, (linenr_T)1); - i = readfile(eap->arg, NULL, - eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); - - } - if (i == FAIL) { - if (!aborting()) - EMSG2(_(e_notopen), eap->arg); - } else { - if (empty && exmode_active) { - /* Delete the empty line that remains. Historically ex does - * this but vi doesn't. */ - if (eap->line2 == 0) - lnum = curbuf->b_ml.ml_line_count; - else - lnum = 1; - if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK) { - ml_delete(lnum, FALSE); - if (curwin->w_cursor.lnum > 1 - && curwin->w_cursor.lnum >= lnum) - --curwin->w_cursor.lnum; - deleted_lines_mark(lnum, 1L); - } - } - redraw_curbuf_later(VALID); - } - } -} - -static char_u *prev_dir = NULL; - -#if defined(EXITFREE) || defined(PROTO) -void free_cd_dir(void) -{ - free(prev_dir); - prev_dir = NULL; - - free(globaldir); - globaldir = NULL; -} - -#endif - -/* - * Deal with the side effects of changing the current directory. - * When "local" is TRUE then this was after an ":lcd" command. - */ -void post_chdir(int local) -{ - free(curwin->w_localdir); - curwin->w_localdir = NULL; - if (local) { - /* If still in global directory, need to remember current - * directory as global directory. */ - if (globaldir == NULL && prev_dir != NULL) - globaldir = vim_strsave(prev_dir); - /* Remember this local directory for the window. */ - if (os_dirname(NameBuff, MAXPATHL) == OK) - curwin->w_localdir = vim_strsave(NameBuff); - } else { - /* We are now in the global directory, no need to remember its - * name. */ - free(globaldir); - globaldir = NULL; - } - - shorten_fnames(TRUE); -} - - -/* - * ":cd", ":lcd", ":chdir" and ":lchdir". - */ -void ex_cd(exarg_T *eap) -{ - char_u *new_dir; - char_u *tofree; - - new_dir = eap->arg; -#if !defined(UNIX) - /* for non-UNIX ":cd" means: print current directory */ - if (*new_dir == NUL) - ex_pwd(NULL); - else -#endif - { - if (allbuf_locked()) - return; - if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged() - && !eap->forceit) { - EMSG(_( - "E747: Cannot change directory, buffer is modified (add ! to override)")); - return; - } - - /* ":cd -": Change to previous directory */ - if (STRCMP(new_dir, "-") == 0) { - if (prev_dir == NULL) { - EMSG(_("E186: No previous directory")); - return; - } - new_dir = prev_dir; - } - - /* Save current directory for next ":cd -" */ - tofree = prev_dir; - if (os_dirname(NameBuff, MAXPATHL) == OK) - prev_dir = vim_strsave(NameBuff); - else - prev_dir = NULL; - -#if defined(UNIX) - /* for UNIX ":cd" means: go to home directory */ - if (*new_dir == NUL) { - /* use NameBuff for home directory name */ - expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); - new_dir = NameBuff; - } -#endif - if (new_dir == NULL || vim_chdir(new_dir)) - EMSG(_(e_failed)); - else { - post_chdir(eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir); - - /* Echo the new current directory if the command was typed. */ - if (KeyTyped || p_verbose >= 5) - ex_pwd(eap); - } - free(tofree); - } -} - -/* - * ":pwd". - */ -static void ex_pwd(exarg_T *eap) -{ - if (os_dirname(NameBuff, MAXPATHL) == OK) { -#ifdef BACKSLASH_IN_FILENAME - slash_adjust(NameBuff); -#endif - msg(NameBuff); - } else - EMSG(_("E187: Unknown")); -} - -/* - * ":=". - */ -static void ex_equal(exarg_T *eap) -{ - smsg((char_u *)"%" PRId64, (int64_t)eap->line2); - ex_may_print(eap); -} - -static void ex_sleep(exarg_T *eap) -{ - int n; - long len; - - if (cursor_valid()) { - n = W_WINROW(curwin) + curwin->w_wrow - msg_scrolled; - if (n >= 0) - windgoto((int)n, W_WINCOL(curwin) + curwin->w_wcol); - } - - len = eap->line2; - switch (*eap->arg) { - case 'm': break; - case NUL: len *= 1000L; break; - default: EMSG2(_(e_invarg2), eap->arg); return; - } - do_sleep(len); -} - -/* - * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second. - */ -void do_sleep(long msec) -{ - long done; - - cursor_on(); - out_flush(); - for (done = 0; !got_int && done < msec; done += 1000L) { - ui_delay(msec - done > 1000L ? 1000L : msec - done, TRUE); - ui_breakcheck(); - } -} - -static void do_exmap(exarg_T *eap, int isabbrev) -{ - int mode; - char_u *cmdp; - - cmdp = eap->cmd; - mode = get_map_mode(&cmdp, eap->forceit || isabbrev); - - switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'), - eap->arg, mode, isabbrev)) { - case 1: EMSG(_(e_invarg)); - break; - case 2: EMSG(isabbrev ? _(e_noabbr) : _(e_nomap)); - break; - } -} - -/* - * ":winsize" command (obsolete). - */ -static void ex_winsize(exarg_T *eap) -{ - int w, h; - char_u *arg = eap->arg; - char_u *p; - - w = getdigits(&arg); - arg = skipwhite(arg); - p = arg; - h = getdigits(&arg); - if (*p != NUL && *arg == NUL) - set_shellsize(w, h, TRUE); - else - EMSG(_("E465: :winsize requires two number arguments")); -} - -static void ex_wincmd(exarg_T *eap) -{ - int xchar = NUL; - char_u *p; - - if (*eap->arg == 'g' || *eap->arg == Ctrl_G) { - /* CTRL-W g and CTRL-W CTRL-G have an extra command character */ - if (eap->arg[1] == NUL) { - EMSG(_(e_invarg)); - return; - } - xchar = eap->arg[1]; - p = eap->arg + 2; - } else - p = eap->arg + 1; - - eap->nextcmd = check_nextcmd(p); - p = skipwhite(p); - if (*p != NUL && *p != '"' && eap->nextcmd == NULL) - EMSG(_(e_invarg)); - else if (!eap->skip) { - /* Pass flags on for ":vertical wincmd ]". */ - postponed_split_flags = cmdmod.split; - postponed_split_tab = cmdmod.tab; - do_window(*eap->arg, eap->addr_count > 0 ? eap->line2 : 0L, xchar); - postponed_split_flags = 0; - postponed_split_tab = 0; - } -} - -#if defined(FEAT_GUI) || defined(UNIX) || defined(MSWIN) -/* - * ":winpos". - */ -static void ex_winpos(eap) -exarg_T *eap; -{ - int x, y; - char_u *arg = eap->arg; - char_u *p; - - if (*arg == NUL) { - EMSG(_("E188: Obtaining window position not implemented for this platform")); - } else { - x = getdigits(&arg); - arg = skipwhite(arg); - p = arg; - y = getdigits(&arg); - if (*p == NUL || *arg != NUL) { - EMSG(_("E466: :winpos requires two number arguments")); - return; - } -# ifdef HAVE_TGETENT - if (*T_CWP) - term_set_winpos(x, y); -# endif - } -} -#endif - -/* - * Handle command that work like operators: ":delete", ":yank", ":>" and ":<". - */ -static void ex_operators(exarg_T *eap) -{ - oparg_T oa; - - clear_oparg(&oa); - oa.regname = eap->regname; - oa.start.lnum = eap->line1; - oa.end.lnum = eap->line2; - oa.line_count = eap->line2 - eap->line1 + 1; - oa.motion_type = MLINE; - virtual_op = FALSE; - if (eap->cmdidx != CMD_yank) { /* position cursor for undo */ - setpcmark(); - curwin->w_cursor.lnum = eap->line1; - beginline(BL_SOL | BL_FIX); - } - - if (VIsual_active) - end_visual_mode(); - - switch (eap->cmdidx) { - case CMD_delete: - oa.op_type = OP_DELETE; - op_delete(&oa); - break; - - case CMD_yank: - oa.op_type = OP_YANK; - (void)op_yank(&oa, FALSE, TRUE); - break; - - default: /* CMD_rshift or CMD_lshift */ - if ( - (eap->cmdidx == CMD_rshift) ^ curwin->w_p_rl - ) - oa.op_type = OP_RSHIFT; - else - oa.op_type = OP_LSHIFT; - op_shift(&oa, FALSE, eap->amount); - break; - } - virtual_op = MAYBE; - ex_may_print(eap); -} - -/* - * ":put". - */ -static void ex_put(exarg_T *eap) -{ - /* ":0put" works like ":1put!". */ - if (eap->line2 == 0) { - eap->line2 = 1; - eap->forceit = TRUE; - } - curwin->w_cursor.lnum = eap->line2; - do_put(eap->regname, eap->forceit ? BACKWARD : FORWARD, 1L, - PUT_LINE|PUT_CURSLINE); -} - -/* - * Handle ":copy" and ":move". - */ -static void ex_copymove(exarg_T *eap) -{ - long n; - - n = get_address(&eap->arg, FALSE, FALSE); - if (eap->arg == NULL) { /* error detected */ - eap->nextcmd = NULL; - return; - } - get_flags(eap); - - /* - * move or copy lines from 'eap->line1'-'eap->line2' to below line 'n' - */ - if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count) { - EMSG(_(e_invaddr)); - return; - } - - if (eap->cmdidx == CMD_move) { - if (do_move(eap->line1, eap->line2, n) == FAIL) - return; - } else - ex_copy(eap->line1, eap->line2, n); - u_clearline(); - beginline(BL_SOL | BL_FIX); - ex_may_print(eap); -} - -/* - * Print the current line if flags were given to the Ex command. - */ -void ex_may_print(exarg_T *eap) -{ - if (eap->flags != 0) { - print_line(curwin->w_cursor.lnum, (eap->flags & EXFLAG_NR), - (eap->flags & EXFLAG_LIST)); - ex_no_reprint = TRUE; - } -} - -/* - * ":smagic" and ":snomagic". - */ -static void ex_submagic(exarg_T *eap) -{ - int magic_save = p_magic; - - p_magic = (eap->cmdidx == CMD_smagic); - do_sub(eap); - p_magic = magic_save; -} - -/* - * ":join". - */ -static void ex_join(exarg_T *eap) -{ - curwin->w_cursor.lnum = eap->line1; - if (eap->line1 == eap->line2) { - if (eap->addr_count >= 2) /* :2,2join does nothing */ - return; - if (eap->line2 == curbuf->b_ml.ml_line_count) { - beep_flush(); - return; - } - ++eap->line2; - } - (void)do_join(eap->line2 - eap->line1 + 1, !eap->forceit, TRUE, TRUE); - beginline(BL_WHITE | BL_FIX); - ex_may_print(eap); -} - -/* - * ":[addr]@r" or ":[addr]*r": execute register - */ -static void ex_at(exarg_T *eap) -{ - int c; - int prev_len = typebuf.tb_len; - - curwin->w_cursor.lnum = eap->line2; - -#ifdef USE_ON_FLY_SCROLL - dont_scroll = TRUE; /* disallow scrolling here */ -#endif - - /* get the register name. No name means to use the previous one */ - c = *eap->arg; - if (c == NUL || (c == '*' && *eap->cmd == '*')) - c = '@'; - /* Put the register in the typeahead buffer with the "silent" flag. */ - if (do_execreg(c, TRUE, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, TRUE) - == FAIL) { - beep_flush(); - } else { - int save_efr = exec_from_reg; - - exec_from_reg = TRUE; - - /* - * Execute from the typeahead buffer. - * Continue until the stuff buffer is empty and all added characters - * have been consumed. - */ - while (!stuff_empty() || typebuf.tb_len > prev_len) - (void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE); - - exec_from_reg = save_efr; - } -} - -/* - * ":!". - */ -static void ex_bang(exarg_T *eap) -{ - do_bang(eap->addr_count, eap, eap->forceit, TRUE, TRUE); -} - -/* - * ":undo". - */ -static void ex_undo(exarg_T *eap) -{ - if (eap->addr_count == 1) /* :undo 123 */ - undo_time(eap->line2, FALSE, FALSE, TRUE); - else - u_undo(1); -} - -static void ex_wundo(exarg_T *eap) -{ - char_u hash[UNDO_HASH_SIZE]; - - u_compute_hash(hash); - u_write_undo(eap->arg, eap->forceit, curbuf, hash); -} - -static void ex_rundo(exarg_T *eap) -{ - char_u hash[UNDO_HASH_SIZE]; - - u_compute_hash(hash); - u_read_undo(eap->arg, hash, NULL); -} - -/* - * ":redo". - */ -static void ex_redo(exarg_T *eap) -{ - u_redo(1); -} - -/* - * ":earlier" and ":later". - */ -static void ex_later(exarg_T *eap) -{ - long count = 0; - int sec = FALSE; - int file = FALSE; - char_u *p = eap->arg; - - if (*p == NUL) - count = 1; - else if (isdigit(*p)) { - count = getdigits(&p); - switch (*p) { - case 's': ++p; sec = TRUE; break; - case 'm': ++p; sec = TRUE; count *= 60; break; - case 'h': ++p; sec = TRUE; count *= 60 * 60; break; - case 'd': ++p; sec = TRUE; count *= 24 * 60 * 60; break; - case 'f': ++p; file = TRUE; break; - } - } - - if (*p != NUL) - EMSG2(_(e_invarg2), eap->arg); - else - undo_time(eap->cmdidx == CMD_earlier ? -count : count, - sec, file, FALSE); -} - -/* - * ":redir": start/stop redirection. - */ -static void ex_redir(exarg_T *eap) -{ - char *mode; - char_u *fname; - char_u *arg = eap->arg; - - if (STRICMP(eap->arg, "END") == 0) - close_redir(); - else { - if (*arg == '>') { - ++arg; - if (*arg == '>') { - ++arg; - mode = "a"; - } else - mode = "w"; - arg = skipwhite(arg); - - close_redir(); - - /* Expand environment variables and "~/". */ - fname = expand_env_save(arg); - if (fname == NULL) - return; - - redir_fd = open_exfile(fname, eap->forceit, mode); - free(fname); - } else if (*arg == '@') { - /* redirect to a register a-z (resp. A-Z for appending) */ - close_redir(); - ++arg; - if (ASCII_ISALPHA(*arg) - || *arg == '"') { - redir_reg = *arg++; - if (*arg == '>' && arg[1] == '>') /* append */ - arg += 2; - else { - /* Can use both "@a" and "@a>". */ - if (*arg == '>') - arg++; - /* Make register empty when not using @A-@Z and the - * command is valid. */ - if (*arg == NUL && !isupper(redir_reg)) - write_reg_contents(redir_reg, (char_u *)"", -1, FALSE); - } - } - if (*arg != NUL) { - redir_reg = 0; - EMSG2(_(e_invarg2), eap->arg); - } - } else if (*arg == '=' && arg[1] == '>') { - int append; - - /* redirect to a variable */ - close_redir(); - arg += 2; - - if (*arg == '>') { - ++arg; - append = TRUE; - } else - append = FALSE; - - if (var_redir_start(skipwhite(arg), append) == OK) - redir_vname = 1; - } - /* TODO: redirect to a buffer */ - else - EMSG2(_(e_invarg2), eap->arg); - } - - /* Make sure redirection is not off. Can happen for cmdline completion - * that indirectly invokes a command to catch its output. */ - if (redir_fd != NULL - || redir_reg || redir_vname - ) - redir_off = FALSE; -} - -/* - * ":redraw": force redraw - */ -static void ex_redraw(exarg_T *eap) -{ - int r = RedrawingDisabled; - int p = p_lz; - - RedrawingDisabled = 0; - p_lz = FALSE; - update_topline(); - update_screen(eap->forceit ? CLEAR : - VIsual_active ? INVERTED : - 0); - if (need_maketitle) - maketitle(); - RedrawingDisabled = r; - p_lz = p; - - /* Reset msg_didout, so that a message that's there is overwritten. */ - msg_didout = FALSE; - msg_col = 0; - - /* No need to wait after an intentional redraw. */ - need_wait_return = FALSE; - - out_flush(); -} - -/* - * ":redrawstatus": force redraw of status line(s) - */ -static void ex_redrawstatus(exarg_T *eap) -{ - int r = RedrawingDisabled; - int p = p_lz; - - RedrawingDisabled = 0; - p_lz = FALSE; - if (eap->forceit) - status_redraw_all(); - else - status_redraw_curbuf(); - update_screen( - VIsual_active ? INVERTED : - 0); - RedrawingDisabled = r; - p_lz = p; - out_flush(); -} - -static void close_redir(void) -{ - if (redir_fd != NULL) { - fclose(redir_fd); - redir_fd = NULL; - } - redir_reg = 0; - if (redir_vname) { - var_redir_stop(); - redir_vname = 0; - } -} - -#if defined(FEAT_SESSION) && defined(USE_CRNL) -# define MKSESSION_NL -static int mksession_nl = FALSE; /* use NL only in put_eol() */ -#endif - -/* - * ":mkexrc", ":mkvimrc", ":mkview" and ":mksession". - */ -static void ex_mkrc(exarg_T *eap) -{ - FILE *fd; - int failed = FALSE; - char_u *fname; - int view_session = FALSE; - int using_vdir = FALSE; /* using 'viewdir'? */ - char_u *viewFile = NULL; - unsigned *flagp; - - if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) { - view_session = TRUE; - } - - /* Use the short file name until ":lcd" is used. We also don't use the - * short file name when 'acd' is set, that is checked later. */ - did_lcd = FALSE; - - /* ":mkview" or ":mkview 9": generate file name with 'viewdir' */ - if (eap->cmdidx == CMD_mkview - && (*eap->arg == NUL - || (vim_isdigit(*eap->arg) && eap->arg[1] == NUL))) { - eap->forceit = TRUE; - fname = get_view_file(*eap->arg); - if (fname == NULL) - return; - viewFile = fname; - using_vdir = TRUE; - } else if (*eap->arg != NUL) - fname = eap->arg; - else if (eap->cmdidx == CMD_mkvimrc) - fname = (char_u *)VIMRC_FILE; - else if (eap->cmdidx == CMD_mksession) - fname = (char_u *)SESSION_FILE; - else - fname = (char_u *)EXRC_FILE; - - /* When using 'viewdir' may have to create the directory. */ - if (using_vdir && !os_isdir(p_vdir)) { - vim_mkdir_emsg(p_vdir, 0755); - } - - fd = open_exfile(fname, eap->forceit, WRITEBIN); - if (fd != NULL) { - if (eap->cmdidx == CMD_mkview) - flagp = &vop_flags; - else - flagp = &ssop_flags; - -#ifdef MKSESSION_NL - /* "unix" in 'sessionoptions': use NL line separator */ - if (view_session && (*flagp & SSOP_UNIX)) - mksession_nl = TRUE; -#endif - - /* Write the version command for :mkvimrc */ - if (eap->cmdidx == CMD_mkvimrc) - (void)put_line(fd, "version 6.0"); - - if (eap->cmdidx == CMD_mksession) { - if (put_line(fd, "let SessionLoad = 1") == FAIL) - failed = TRUE; - } - - if (eap->cmdidx != CMD_mkview) { - /* Write setting 'compatible' first, because it has side effects. - * For that same reason only do it when needed. */ - if (p_cp) - (void)put_line(fd, "if !&cp | set cp | endif"); - else - (void)put_line(fd, "if &cp | set nocp | endif"); - } - - if (!view_session - || (eap->cmdidx == CMD_mksession - && (*flagp & SSOP_OPTIONS))) - failed |= (makemap(fd, NULL) == FAIL - || makeset(fd, OPT_GLOBAL, FALSE) == FAIL); - - if (!failed && view_session) { - if (put_line(fd, - "let s:so_save = &so | let s:siso_save = &siso | set so=0 siso=0") - == FAIL) - failed = TRUE; - if (eap->cmdidx == CMD_mksession) { - char_u *dirnow; /* current directory */ - - dirnow = xmalloc(MAXPATHL); - /* - * Change to session file's dir. - */ - if (os_dirname(dirnow, MAXPATHL) == FAIL - || os_chdir((char *)dirnow) != 0) - *dirnow = NUL; - if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) { - if (vim_chdirfile(fname) == OK) - shorten_fnames(TRUE); - } else if (*dirnow != NUL - && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) { - if (os_chdir((char *)globaldir) == 0) - shorten_fnames(TRUE); - } - - failed |= (makeopens(fd, dirnow) == FAIL); - - /* restore original dir */ - if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR) - || ((ssop_flags & SSOP_CURDIR) && globaldir != - NULL))) { - if (os_chdir((char *)dirnow) != 0) - EMSG(_(e_prev_dir)); - shorten_fnames(TRUE); - /* restore original dir */ - if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR) - || ((ssop_flags & SSOP_CURDIR) && globaldir != - NULL))) { - if (os_chdir((char *)dirnow) != 0) - EMSG(_(e_prev_dir)); - shorten_fnames(TRUE); - } - } - free(dirnow); - } else { - failed |= (put_view(fd, curwin, !using_vdir, flagp, - -1) == FAIL); - } - if (put_line(fd, "let &so = s:so_save | let &siso = s:siso_save") - == FAIL) - failed = TRUE; - if (put_line(fd, "doautoall SessionLoadPost") == FAIL) - failed = TRUE; - if (eap->cmdidx == CMD_mksession) { - if (put_line(fd, "unlet SessionLoad") == FAIL) - failed = TRUE; - } - } - if (put_line(fd, "\" vim: set ft=vim :") == FAIL) - failed = TRUE; - - failed |= fclose(fd); - - if (failed) - EMSG(_(e_write)); - else if (eap->cmdidx == CMD_mksession) { - /* successful session write - set this_session var */ - char_u *tbuf; - - tbuf = xmalloc(MAXPATHL); - if (vim_FullName(fname, tbuf, MAXPATHL, FALSE) == OK) - set_vim_var_string(VV_THIS_SESSION, tbuf, -1); - free(tbuf); - } -#ifdef MKSESSION_NL - mksession_nl = FALSE; -#endif - } - - free(viewFile); -} - -int vim_mkdir_emsg(char_u *name, int prot) -{ - if (os_mkdir((char *)name, prot) != 0) { - EMSG2(_("E739: Cannot create directory: %s"), name); - return FAIL; - } - return OK; -} - -/* - * Open a file for writing for an Ex command, with some checks. - * Return file descriptor, or NULL on failure. - */ -FILE * -open_exfile ( - char_u *fname, - int forceit, - char *mode /* "w" for create new file or "a" for append */ -) -{ - FILE *fd; - -#ifdef UNIX - /* with Unix it is possible to open a directory */ - if (os_isdir(fname)) { - EMSG2(_(e_isadir2), fname); - return NULL; - } -#endif - if (!forceit && *mode != 'a' && os_file_exists(fname)) { - EMSG2(_("E189: \"%s\" exists (add ! to override)"), fname); - return NULL; - } - - if ((fd = mch_fopen((char *)fname, mode)) == NULL) - EMSG2(_("E190: Cannot open \"%s\" for writing"), fname); - - return fd; -} - -/* - * ":mark" and ":k". - */ -static void ex_mark(exarg_T *eap) -{ - pos_T pos; - - if (*eap->arg == NUL) /* No argument? */ - EMSG(_(e_argreq)); - else if (eap->arg[1] != NUL) /* more than one character? */ - EMSG(_(e_trailing)); - else { - pos = curwin->w_cursor; /* save curwin->w_cursor */ - curwin->w_cursor.lnum = eap->line2; - beginline(BL_WHITE | BL_FIX); - if (setmark(*eap->arg) == FAIL) /* set mark */ - EMSG(_("E191: Argument must be a letter or forward/backward quote")); - curwin->w_cursor = pos; /* restore curwin->w_cursor */ - } -} - -/* - * Update w_topline, w_leftcol and the cursor position. - */ -void update_topline_cursor(void) -{ - check_cursor(); /* put cursor on valid line */ - update_topline(); - if (!curwin->w_p_wrap) - validate_cursor(); - update_curswant(); -} - -/* - * ":normal[!] {commands}": Execute normal mode commands. - */ -static void ex_normal(exarg_T *eap) -{ - int save_msg_scroll = msg_scroll; - int save_restart_edit = restart_edit; - int save_msg_didout = msg_didout; - int save_State = State; - tasave_T tabuf; - int save_insertmode = p_im; - int save_finish_op = finish_op; - int save_opcount = opcount; - char_u *arg = NULL; - int l; - char_u *p; - - if (ex_normal_lock > 0) { - EMSG(_(e_secure)); - return; - } - if (ex_normal_busy >= p_mmd) { - EMSG(_("E192: Recursive use of :normal too deep")); - return; - } - ++ex_normal_busy; - - msg_scroll = FALSE; /* no msg scrolling in Normal mode */ - restart_edit = 0; /* don't go to Insert mode */ - p_im = FALSE; /* don't use 'insertmode' */ - - /* - * vgetc() expects a CSI and K_SPECIAL to have been escaped. Don't do - * this for the K_SPECIAL leading byte, otherwise special keys will not - * work. - */ - if (has_mbyte) { - int len = 0; - - /* Count the number of characters to be escaped. */ - for (p = eap->arg; *p != NUL; ++p) { - for (l = (*mb_ptr2len)(p) - 1; l > 0; --l) - if (*++p == K_SPECIAL /* trailbyte K_SPECIAL or CSI */ - ) - len += 2; - } - if (len > 0) { - arg = xmalloc(STRLEN(eap->arg) + len + 1); - len = 0; - for (p = eap->arg; *p != NUL; ++p) { - arg[len++] = *p; - for (l = (*mb_ptr2len)(p) - 1; l > 0; --l) { - arg[len++] = *++p; - if (*p == K_SPECIAL) { - arg[len++] = KS_SPECIAL; - arg[len++] = KE_FILLER; - } - } - arg[len] = NUL; - } - } - } - - /* - * Save the current typeahead. This is required to allow using ":normal" - * from an event handler and makes sure we don't hang when the argument - * ends with half a command. - */ - save_typeahead(&tabuf); - if (tabuf.typebuf_valid) { - /* - * Repeat the :normal command for each line in the range. When no - * range given, execute it just once, without positioning the cursor - * first. - */ - do { - if (eap->addr_count != 0) { - curwin->w_cursor.lnum = eap->line1++; - curwin->w_cursor.col = 0; - } - - exec_normal_cmd( - arg != NULL ? arg : - eap->arg, eap->forceit ? REMAP_NONE : REMAP_YES, FALSE); - } while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int); - } - - /* Might not return to the main loop when in an event handler. */ - update_topline_cursor(); - - /* Restore the previous typeahead. */ - restore_typeahead(&tabuf); - - --ex_normal_busy; - msg_scroll = save_msg_scroll; - restart_edit = save_restart_edit; - p_im = save_insertmode; - finish_op = save_finish_op; - opcount = save_opcount; - msg_didout |= save_msg_didout; /* don't reset msg_didout now */ - - /* Restore the state (needed when called from a function executed for - * 'indentexpr'). */ - State = save_State; - free(arg); -} - -/* - * ":startinsert", ":startreplace" and ":startgreplace" - */ -static void ex_startinsert(exarg_T *eap) -{ - if (eap->forceit) { - coladvance((colnr_T)MAXCOL); - curwin->w_curswant = MAXCOL; - curwin->w_set_curswant = FALSE; - } - - /* Ignore the command when already in Insert mode. Inserting an - * expression register that invokes a function can do this. */ - if (State & INSERT) - return; - - if (eap->cmdidx == CMD_startinsert) - restart_edit = 'a'; - else if (eap->cmdidx == CMD_startreplace) - restart_edit = 'R'; - else - restart_edit = 'V'; - - if (!eap->forceit) { - if (eap->cmdidx == CMD_startinsert) - restart_edit = 'i'; - curwin->w_curswant = 0; /* avoid MAXCOL */ - } -} - -/* - * ":stopinsert" - */ -static void ex_stopinsert(exarg_T *eap) -{ - restart_edit = 0; - stop_insert_mode = TRUE; -} - -/* - * Execute normal mode command "cmd". - * "remap" can be REMAP_NONE or REMAP_YES. - */ -void exec_normal_cmd(char_u *cmd, int remap, int silent) -{ - oparg_T oa; - - /* - * Stuff the argument into the typeahead buffer. - * Execute normal_cmd() until there is no typeahead left. - */ - clear_oparg(&oa); - finish_op = FALSE; - ins_typebuf(cmd, remap, 0, TRUE, silent); - while ((!stuff_empty() || (!typebuf_typed() && typebuf.tb_len > 0)) - && !got_int) { - update_topline_cursor(); - normal_cmd(&oa, TRUE); /* execute a Normal mode cmd */ - } -} - -static void ex_checkpath(exarg_T *eap) -{ - find_pattern_in_path(NULL, 0, 0, FALSE, FALSE, CHECK_PATH, 1L, - eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW, - (linenr_T)1, (linenr_T)MAXLNUM); -} - -/* - * ":psearch" - */ -static void ex_psearch(exarg_T *eap) -{ - g_do_tagpreview = p_pvh; - ex_findpat(eap); - g_do_tagpreview = 0; -} - -static void ex_findpat(exarg_T *eap) -{ - int whole = TRUE; - long n; - char_u *p; - int action; - - switch (cmdnames[eap->cmdidx].cmd_name[2]) { - case 'e': /* ":psearch", ":isearch" and ":dsearch" */ - if (cmdnames[eap->cmdidx].cmd_name[0] == 'p') - action = ACTION_GOTO; - else - action = ACTION_SHOW; - break; - case 'i': /* ":ilist" and ":dlist" */ - action = ACTION_SHOW_ALL; - break; - case 'u': /* ":ijump" and ":djump" */ - action = ACTION_GOTO; - break; - default: /* ":isplit" and ":dsplit" */ - action = ACTION_SPLIT; - break; - } - - n = 1; - if (vim_isdigit(*eap->arg)) { /* get count */ - n = getdigits(&eap->arg); - eap->arg = skipwhite(eap->arg); - } - if (*eap->arg == '/') { /* Match regexp, not just whole words */ - whole = FALSE; - ++eap->arg; - p = skip_regexp(eap->arg, '/', p_magic, NULL); - if (*p) { - *p++ = NUL; - p = skipwhite(p); - - /* Check for trailing illegal characters */ - if (!ends_excmd(*p)) - eap->errmsg = e_trailing; - else - eap->nextcmd = check_nextcmd(p); - } - } - if (!eap->skip) - find_pattern_in_path(eap->arg, 0, (int)STRLEN(eap->arg), - whole, !eap->forceit, - *eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY, - n, action, eap->line1, eap->line2); -} - - -/* - * ":ptag", ":ptselect", ":ptjump", ":ptnext", etc. - */ -static void ex_ptag(exarg_T *eap) -{ - g_do_tagpreview = p_pvh; /* will be reset to 0 in ex_tag_cmd() */ - ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1); -} - -/* - * ":pedit" - */ -static void ex_pedit(exarg_T *eap) -{ - win_T *curwin_save = curwin; - - g_do_tagpreview = p_pvh; - prepare_tagpreview(TRUE); - keep_help_flag = curwin_save->w_buffer->b_help; - do_exedit(eap, NULL); - keep_help_flag = FALSE; - if (curwin != curwin_save && win_valid(curwin_save)) { - /* Return cursor to where we were */ - validate_cursor(); - redraw_later(VALID); - win_enter(curwin_save, TRUE); - } - g_do_tagpreview = 0; -} - -/* - * ":stag", ":stselect" and ":stjump". - */ -static void ex_stag(exarg_T *eap) -{ - postponed_split = -1; - postponed_split_flags = cmdmod.split; - postponed_split_tab = cmdmod.tab; - ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1); - postponed_split_flags = 0; - postponed_split_tab = 0; -} - -/* - * ":tag", ":tselect", ":tjump", ":tnext", etc. - */ -static void ex_tag(exarg_T *eap) -{ - ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name); -} - -static void ex_tag_cmd(exarg_T *eap, char_u *name) -{ - int cmd; - - switch (name[1]) { - case 'j': cmd = DT_JUMP; /* ":tjump" */ - break; - case 's': cmd = DT_SELECT; /* ":tselect" */ - break; - case 'p': cmd = DT_PREV; /* ":tprevious" */ - break; - case 'N': cmd = DT_PREV; /* ":tNext" */ - break; - case 'n': cmd = DT_NEXT; /* ":tnext" */ - break; - case 'o': cmd = DT_POP; /* ":pop" */ - break; - case 'f': /* ":tfirst" */ - case 'r': cmd = DT_FIRST; /* ":trewind" */ - break; - case 'l': cmd = DT_LAST; /* ":tlast" */ - break; - default: /* ":tag" */ - if (p_cst && *eap->arg != NUL) { - do_cstag(eap); - return; - } - cmd = DT_TAG; - break; - } - - if (name[0] == 'l') { - cmd = DT_LTAG; - } - - do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1, - eap->forceit, TRUE); -} - -/* - * Check "str" for starting with a special cmdline variable. - * If found return one of the SPEC_ values and set "*usedlen" to the length of - * the variable. Otherwise return -1 and "*usedlen" is unchanged. - */ -int find_cmdline_var(char_u *src, int *usedlen) -{ - int len; - int i; - static char *(spec_str[]) = { - "%", -#define SPEC_PERC 0 - "#", -#define SPEC_HASH 1 - "", /* cursor word */ -#define SPEC_CWORD 2 - "", /* cursor WORD */ -#define SPEC_CCWORD 3 - "", /* cursor path name */ -#define SPEC_CFILE 4 - "", /* ":so" file name */ -#define SPEC_SFILE 5 - "", /* ":so" file line number */ -#define SPEC_SLNUM 6 - "", /* autocommand file name */ -# define SPEC_AFILE 7 - "", /* autocommand buffer number */ -# define SPEC_ABUF 8 - "", /* autocommand match name */ -# define SPEC_AMATCH 9 - }; - - for (i = 0; i < (int)(sizeof(spec_str) / sizeof(char *)); ++i) { - len = (int)STRLEN(spec_str[i]); - if (STRNCMP(src, spec_str[i], len) == 0) { - *usedlen = len; - return i; - } - } - return -1; -} - -/* - * Evaluate cmdline variables. - * - * change '%' to curbuf->b_ffname - * '#' to curwin->w_altfile - * '' to word under the cursor - * '' to WORD under the cursor - * '' to path name under the cursor - * '' to sourced file name - * '' to sourced file line number - * '' to file name for autocommand - * '' to buffer number for autocommand - * '' to matching name for autocommand - * - * When an error is detected, "errormsg" is set to a non-NULL pointer (may be - * "" for error without a message) and NULL is returned. - * Returns an allocated string if a valid match was found. - * Returns NULL if no match was found. "usedlen" then still contains the - * number of characters to skip. - */ -char_u * -eval_vars ( - char_u *src, /* pointer into commandline */ - char_u *srcstart, /* beginning of valid memory for src */ - int *usedlen, /* characters after src that are used */ - linenr_T *lnump, /* line number for :e command, or NULL */ - char_u **errormsg, /* pointer to error message */ - int *escaped /* return value has escaped white space (can - * be NULL) */ -) -{ - int i; - char_u *s; - char_u *result; - char_u *resultbuf = NULL; - int resultlen; - buf_T *buf; - int valid = VALID_HEAD + VALID_PATH; /* assume valid result */ - int spec_idx; - int skip_mod = FALSE; - char_u strbuf[30]; - - *errormsg = NULL; - if (escaped != NULL) - *escaped = FALSE; - - /* - * Check if there is something to do. - */ - spec_idx = find_cmdline_var(src, usedlen); - if (spec_idx < 0) { /* no match */ - *usedlen = 1; - return NULL; - } - - /* - * Skip when preceded with a backslash "\%" and "\#". - * Note: In "\\%" the % is also not recognized! - */ - if (src > srcstart && src[-1] == '\\') { - *usedlen = 0; - STRMOVE(src - 1, src); /* remove backslash */ - return NULL; - } - - /* - * word or WORD under cursor - */ - if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD) { - resultlen = find_ident_under_cursor(&result, spec_idx == SPEC_CWORD ? - (FIND_IDENT|FIND_STRING) : FIND_STRING); - if (resultlen == 0) { - *errormsg = (char_u *)""; - return NULL; - } - } - /* - * '#': Alternate file name - * '%': Current file name - * File name under the cursor - * File name for autocommand - * and following modifiers - */ - else { - switch (spec_idx) { - case SPEC_PERC: /* '%': current file */ - if (curbuf->b_fname == NULL) { - result = (char_u *)""; - valid = 0; /* Must have ":p:h" to be valid */ - } else - result = curbuf->b_fname; - break; - - case SPEC_HASH: /* '#' or "#99": alternate file */ - if (src[1] == '#') { /* "##": the argument list */ - result = arg_all(); - resultbuf = result; - *usedlen = 2; - if (escaped != NULL) - *escaped = TRUE; - skip_mod = TRUE; - break; - } - s = src + 1; - if (*s == '<') /* "#<99" uses v:oldfiles */ - ++s; - i = (int)getdigits(&s); - *usedlen = (int)(s - src); /* length of what we expand */ - - if (src[1] == '<') { - if (*usedlen < 2) { - /* Should we give an error message for #b_fname == NULL) { - result = (char_u *)""; - valid = 0; /* Must have ":p:h" to be valid */ - } else - result = buf->b_fname; - } - break; - - case SPEC_CFILE: /* file name under cursor */ - result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL); - if (result == NULL) { - *errormsg = (char_u *)""; - return NULL; - } - resultbuf = result; /* remember allocated string */ - break; - - case SPEC_AFILE: /* file name for autocommand */ - result = autocmd_fname; - if (result != NULL && !autocmd_fname_full) { - /* Still need to turn the fname into a full path. It is - * postponed to avoid a delay when is not used. */ - autocmd_fname_full = TRUE; - result = FullName_save(autocmd_fname, FALSE); - free(autocmd_fname); - autocmd_fname = result; - } - if (result == NULL) { - *errormsg = (char_u *)_( - "E495: no autocommand file name to substitute for \"\""); - return NULL; - } - result = path_shorten_fname_if_possible(result); - break; - - case SPEC_ABUF: /* buffer number for autocommand */ - if (autocmd_bufnr <= 0) { - *errormsg = (char_u *)_( - "E496: no autocommand buffer number to substitute for \"\""); - return NULL; - } - sprintf((char *)strbuf, "%d", autocmd_bufnr); - result = strbuf; - break; - - case SPEC_AMATCH: /* match name for autocommand */ - result = autocmd_match; - if (result == NULL) { - *errormsg = (char_u *)_( - "E497: no autocommand match name to substitute for \"\""); - return NULL; - } - break; - - case SPEC_SFILE: /* file name for ":so" command */ - result = sourcing_name; - if (result == NULL) { - *errormsg = (char_u *)_( - "E498: no :source file name to substitute for \"\""); - return NULL; - } - break; - case SPEC_SLNUM: /* line in file for ":so" command */ - if (sourcing_name == NULL || sourcing_lnum == 0) { - *errormsg = (char_u *)_("E842: no line number to use for \"\""); - return NULL; - } - sprintf((char *)strbuf, "%" PRId64, (int64_t)sourcing_lnum); - result = strbuf; - break; - } - - resultlen = (int)STRLEN(result); /* length of new string */ - if (src[*usedlen] == '<') { /* remove the file name extension */ - ++*usedlen; - if ((s = vim_strrchr(result, '.')) != NULL && s >= path_tail(result)) - resultlen = (int)(s - result); - } else if (!skip_mod) { - valid |= modify_fname(src, usedlen, &result, &resultbuf, - &resultlen); - if (result == NULL) { - *errormsg = (char_u *)""; - return NULL; - } - } - } - - if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH) { - if (valid != VALID_HEAD + VALID_PATH) - /* xgettext:no-c-format */ - *errormsg = (char_u *)_( - "E499: Empty file name for '%' or '#', only works with \":p:h\""); - else - *errormsg = (char_u *)_("E500: Evaluates to an empty string"); - result = NULL; - } else - result = vim_strnsave(result, resultlen); - free(resultbuf); - return result; -} - -/* - * Concatenate all files in the argument list, separated by spaces, and return - * it in one allocated string. - * Spaces and backslashes in the file names are escaped with a backslash. - */ -static char_u *arg_all(void) -{ - int len; - int idx; - char_u *retval = NULL; - char_u *p; - - /* - * Do this loop two times: - * first time: compute the total length - * second time: concatenate the names - */ - for (;; ) { - len = 0; - for (idx = 0; idx < ARGCOUNT; ++idx) { - p = alist_name(&ARGLIST[idx]); - if (p != NULL) { - if (len > 0) { - /* insert a space in between names */ - if (retval != NULL) - retval[len] = ' '; - ++len; - } - for (; *p != NUL; ++p) { - if (*p == ' ' || *p == '\\') { - /* insert a backslash */ - if (retval != NULL) - retval[len] = '\\'; - ++len; - } - if (retval != NULL) - retval[len] = *p; - ++len; - } - } - } - - /* second time: break here */ - if (retval != NULL) { - retval[len] = NUL; - break; - } - - /* allocate memory */ - retval = xmalloc(len + 1); - } - - return retval; -} - -/* - * Expand the string in "arg". - * - * Returns an allocated string, or NULL for any error. - */ -char_u *expand_sfile(char_u *arg) -{ - char_u *errormsg; - int len; - char_u *result; - char_u *newres; - char_u *repl; - int srclen; - char_u *p; - - result = vim_strsave(arg); - if (result == NULL) - return NULL; - - for (p = result; *p; ) { - if (STRNCMP(p, "", 7) != 0) - ++p; - else { - /* replace "" with the sourced file name, and do ":" stuff */ - repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL); - if (errormsg != NULL) { - if (*errormsg) - emsg(errormsg); - free(result); - return NULL; - } - if (repl == NULL) { /* no match (cannot happen) */ - p += srclen; - continue; - } - len = (int)STRLEN(result) - srclen + (int)STRLEN(repl) + 1; - newres = xmalloc(len); - memmove(newres, result, (size_t)(p - result)); - STRCPY(newres + (p - result), repl); - len = (int)STRLEN(newres); - STRCAT(newres, p + srclen); - free(repl); - free(result); - result = newres; - p = newres + len; /* continue after the match */ - } - } - - return result; -} - -static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin); -static int ses_win_rec(FILE *fd, frame_T *fr); -static frame_T *ses_skipframe(frame_T *fr); -static int ses_do_frame(frame_T *fr); -static int ses_do_win(win_T *wp); -static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, - unsigned *flagp); -static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp); -static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp); - -/* - * Write openfile commands for the current buffers to an .exrc file. - * Return FAIL on error, OK otherwise. - */ -static int -makeopens ( - FILE *fd, - char_u *dirnow /* Current directory name */ -) -{ - buf_T *buf; - int only_save_windows = TRUE; - int nr; - int cnr = 1; - int restore_size = TRUE; - win_T *wp; - char_u *sname; - win_T *edited_win = NULL; - int tabnr; - win_T *tab_firstwin; - frame_T *tab_topframe; - int cur_arg_idx = 0; - int next_arg_idx = 0; - - if (ssop_flags & SSOP_BUFFERS) - only_save_windows = FALSE; /* Save ALL buffers */ - - /* - * Begin by setting the this_session variable, and then other - * sessionable variables. - */ - if (put_line(fd, "let v:this_session=expand(\":p\")") == FAIL) - return FAIL; - if (ssop_flags & SSOP_GLOBALS) - if (store_session_globals(fd) == FAIL) - return FAIL; - - /* - * Close all windows but one. - */ - if (put_line(fd, "silent only") == FAIL) - return FAIL; - - /* - * Now a :cd command to the session directory or the current directory - */ - if (ssop_flags & SSOP_SESDIR) { - if (put_line(fd, "exe \"cd \" . escape(expand(\":p:h\"), ' ')") - == FAIL) - return FAIL; - } else if (ssop_flags & SSOP_CURDIR) { - sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow); - if (sname == NULL - || fputs("cd ", fd) < 0 - || ses_put_fname(fd, sname, &ssop_flags) == FAIL - || put_eol(fd) == FAIL) { - free(sname); - return FAIL; - } - free(sname); - } - - /* - * If there is an empty, unnamed buffer we will wipe it out later. - * Remember the buffer number. - */ - if (put_line(fd, - "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") - == - FAIL) - return FAIL; - if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL) - return FAIL; - if (put_line(fd, "endif") == FAIL) - return FAIL; - - /* - * Now save the current files, current buffer first. - */ - if (put_line(fd, "set shortmess=aoO") == FAIL) - return FAIL; - - /* Now put the other buffers into the buffer list */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (!(only_save_windows && buf->b_nwindows == 0) - && !(buf->b_help && !(ssop_flags & SSOP_HELP)) - && buf->b_fname != NULL - && buf->b_p_bl) { - if (fprintf(fd, "badd +%" PRId64 " ", - buf->b_wininfo == NULL ? - (int64_t)1L : - (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 - || ses_fname(fd, buf, &ssop_flags) == FAIL) - return FAIL; - } - } - - /* the global argument list */ - if (ses_arglist(fd, "argglobal", &global_alist.al_ga, - !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) { - return FAIL; - } - - if (ssop_flags & SSOP_RESIZE) { - /* Note: after the restore we still check it worked!*/ - if (fprintf(fd, "set lines=%" PRId64 " columns=%" PRId64, - (int64_t)Rows, (int64_t)Columns) < 0 - || put_eol(fd) == FAIL) - return FAIL; - } - - - /* - * May repeat putting Windows for each tab, when "tabpages" is in - * 'sessionoptions'. - * Don't use goto_tabpage(), it may change directory and trigger - * autocommands. - */ - tab_firstwin = firstwin; /* first window in tab page "tabnr" */ - tab_topframe = topframe; - for (tabnr = 1;; ++tabnr) { - int need_tabnew = FALSE; - - if ((ssop_flags & SSOP_TABPAGES)) { - tabpage_T *tp = find_tabpage(tabnr); - - if (tp == NULL) - break; /* done all tab pages */ - if (tp == curtab) { - tab_firstwin = firstwin; - tab_topframe = topframe; - } else { - tab_firstwin = tp->tp_firstwin; - tab_topframe = tp->tp_topframe; - } - if (tabnr > 1) - need_tabnew = TRUE; - } - - /* - * Before creating the window layout, try loading one file. If this - * is aborted we don't end up with a number of useless windows. - * This may have side effects! (e.g., compressed or network file). - */ - for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) { - if (ses_do_win(wp) - && wp->w_buffer->b_ffname != NULL - && !wp->w_buffer->b_help - && !bt_nofile(wp->w_buffer) - ) { - if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0 - || ses_fname(fd, wp->w_buffer, &ssop_flags) == FAIL) - return FAIL; - need_tabnew = FALSE; - if (!wp->w_arg_idx_invalid) - edited_win = wp; - break; - } - } - - /* If no file got edited create an empty tab page. */ - if (need_tabnew && put_line(fd, "tabnew") == FAIL) - return FAIL; - - /* - * Save current window layout. - */ - if (put_line(fd, "set splitbelow splitright") == FAIL) - return FAIL; - if (ses_win_rec(fd, tab_topframe) == FAIL) - return FAIL; - if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL) - return FAIL; - if (!p_spr && put_line(fd, "set nosplitright") == FAIL) - return FAIL; - - /* - * Check if window sizes can be restored (no windows omitted). - * Remember the window number of the current window after restoring. - */ - nr = 0; - for (wp = tab_firstwin; wp != NULL; wp = W_NEXT(wp)) { - if (ses_do_win(wp)) - ++nr; - else - restore_size = FALSE; - if (curwin == wp) - cnr = nr; - } - - /* Go to the first window. */ - if (put_line(fd, "wincmd t") == FAIL) - return FAIL; - - /* - * If more than one window, see if sizes can be restored. - * First set 'winheight' and 'winwidth' to 1 to avoid the windows being - * resized when moving between windows. - * Do this before restoring the view, so that the topline and the - * cursor can be set. This is done again below. - */ - if (put_line(fd, "set winheight=1 winwidth=1") == FAIL) - return FAIL; - if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) - return FAIL; - - /* - * Restore the view of the window (options, file, cursor, etc.). - */ - for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) { - if (!ses_do_win(wp)) - continue; - if (put_view(fd, wp, wp != edited_win, &ssop_flags, - cur_arg_idx) == FAIL) - return FAIL; - if (nr > 1 && put_line(fd, "wincmd w") == FAIL) - return FAIL; - next_arg_idx = wp->w_arg_idx; - } - - /* The argument index in the first tab page is zero, need to set it in - * each window. For further tab pages it's the window where we do - * "tabedit". */ - cur_arg_idx = next_arg_idx; - - /* - * Restore cursor to the current window if it's not the first one. - */ - if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0 - || put_eol(fd) == FAIL)) - return FAIL; - - /* - * Restore window sizes again after jumping around in windows, because - * the current window has a minimum size while others may not. - */ - if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) - return FAIL; - - /* Don't continue in another tab page when doing only the current one - * or when at the last tab page. */ - if (!(ssop_flags & SSOP_TABPAGES)) - break; - } - - if (ssop_flags & SSOP_TABPAGES) { - if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0 - || put_eol(fd) == FAIL) - return FAIL; - } - - /* - * Wipe out an empty unnamed buffer we started in. - */ - if (put_line(fd, "if exists('s:wipebuf')") == FAIL) - return FAIL; - if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL) - return FAIL; - if (put_line(fd, "endif") == FAIL) - return FAIL; - if (put_line(fd, "unlet! s:wipebuf") == FAIL) - return FAIL; - - /* Re-apply 'winheight', 'winwidth' and 'shortmess'. */ - if (fprintf(fd, "set winheight=%" PRId64 " winwidth=%" PRId64 " shortmess=%s", - (int64_t)p_wh, (int64_t)p_wiw, p_shm) < 0 - || put_eol(fd) == FAIL) - return FAIL; - - /* - * Lastly, execute the x.vim file if it exists. - */ - if (put_line(fd, "let s:sx = expand(\":p:r\").\"x.vim\"") == FAIL - || put_line(fd, "if file_readable(s:sx)") == FAIL - || put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL - || put_line(fd, "endif") == FAIL) - return FAIL; - - return OK; -} - -static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin) -{ - int n = 0; - win_T *wp; - - if (restore_size && (ssop_flags & SSOP_WINSIZE)) { - for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) { - if (!ses_do_win(wp)) - continue; - ++n; - - /* restore height when not full height */ - if (wp->w_height + wp->w_status_height < topframe->fr_height - && (fprintf(fd, - "exe '%dresize ' . ((&lines * %" PRId64 - " + %" PRId64 ") / %" PRId64 ")", - n, (int64_t)wp->w_height, - (int64_t)(Rows / 2), (int64_t)Rows) < 0 - || put_eol(fd) == FAIL)) - return FAIL; - - /* restore width when not full width */ - if (wp->w_width < Columns - && (fprintf(fd, "exe 'vert %dresize ' . ((&columns * %" PRId64 - " + %" PRId64 ") / %" PRId64 ")", - n, (int64_t)wp->w_width, (int64_t)(Columns / 2), - (int64_t)Columns) < 0 - || put_eol(fd) == FAIL)) - return FAIL; - } - } else { - /* Just equalise window sizes */ - if (put_line(fd, "wincmd =") == FAIL) - return FAIL; - } - return OK; -} - -/* - * Write commands to "fd" to recursively create windows for frame "fr", - * horizontally and vertically split. - * After the commands the last window in the frame is the current window. - * Returns FAIL when writing the commands to "fd" fails. - */ -static int ses_win_rec(FILE *fd, frame_T *fr) -{ - frame_T *frc; - int count = 0; - - if (fr->fr_layout != FR_LEAF) { - /* Find first frame that's not skipped and then create a window for - * each following one (first frame is already there). */ - frc = ses_skipframe(fr->fr_child); - if (frc != NULL) - while ((frc = ses_skipframe(frc->fr_next)) != NULL) { - /* Make window as big as possible so that we have lots of room - * to split. */ - if (put_line(fd, "wincmd _ | wincmd |") == FAIL - || put_line(fd, fr->fr_layout == FR_COL - ? "split" : "vsplit") == FAIL) - return FAIL; - ++count; - } - - /* Go back to the first window. */ - if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL - ? "%dwincmd k" : "%dwincmd h", count) < 0 - || put_eol(fd) == FAIL)) - return FAIL; - - /* Recursively create frames/windows in each window of this column or - * row. */ - frc = ses_skipframe(fr->fr_child); - while (frc != NULL) { - ses_win_rec(fd, frc); - frc = ses_skipframe(frc->fr_next); - /* Go to next window. */ - if (frc != NULL && put_line(fd, "wincmd w") == FAIL) - return FAIL; - } - } - return OK; -} - -/* - * Skip frames that don't contain windows we want to save in the Session. - * Returns NULL when there none. - */ -static frame_T *ses_skipframe(frame_T *fr) -{ - frame_T *frc; - - for (frc = fr; frc != NULL; frc = frc->fr_next) - if (ses_do_frame(frc)) - break; - return frc; -} - -/* - * Return TRUE if frame "fr" has a window somewhere that we want to save in - * the Session. - */ -static int ses_do_frame(frame_T *fr) -{ - frame_T *frc; - - if (fr->fr_layout == FR_LEAF) - return ses_do_win(fr->fr_win); - for (frc = fr->fr_child; frc != NULL; frc = frc->fr_next) - if (ses_do_frame(frc)) - return TRUE; - return FALSE; -} - -/* - * Return non-zero if window "wp" is to be stored in the Session. - */ -static int ses_do_win(win_T *wp) -{ - if (wp->w_buffer->b_fname == NULL - /* When 'buftype' is "nofile" can't restore the window contents. */ - || bt_nofile(wp->w_buffer) - ) - return ssop_flags & SSOP_BLANK; - if (wp->w_buffer->b_help) - return ssop_flags & SSOP_HELP; - return TRUE; -} - -/* - * Write commands to "fd" to restore the view of a window. - * Caller must make sure 'scrolloff' is zero. - */ -static int -put_view ( - FILE *fd, - win_T *wp, - int add_edit, /* add ":edit" command to view */ - unsigned *flagp, /* vop_flags or ssop_flags */ - int current_arg_idx /* current argument index of the window, use - * -1 if unknown */ -) -{ - win_T *save_curwin; - int f; - int do_cursor; - int did_next = FALSE; - - /* Always restore cursor position for ":mksession". For ":mkview" only - * when 'viewoptions' contains "cursor". */ - do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR); - - /* - * Local argument list. - */ - if (wp->w_alist == &global_alist) { - if (put_line(fd, "argglobal") == FAIL) - return FAIL; - } else { - if (ses_arglist(fd, "arglocal", &wp->w_alist->al_ga, - flagp == &vop_flags - || !(*flagp & SSOP_CURDIR) - || wp->w_localdir != NULL, flagp) == FAIL) - return FAIL; - } - - /* Only when part of a session: restore the argument index. Some - * arguments may have been deleted, check if the index is valid. */ - if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp) - && flagp == &ssop_flags) { - if (fprintf(fd, "%" PRId64 "argu", (int64_t)(wp->w_arg_idx + 1)) < 0 - || put_eol(fd) == FAIL) - return FAIL; - did_next = TRUE; - } - - /* Edit the file. Skip this when ":next" already did it. */ - if (add_edit && (!did_next || wp->w_arg_idx_invalid)) { - /* - * Load the file. - */ - if (wp->w_buffer->b_ffname != NULL - && !bt_nofile(wp->w_buffer) - ) { - /* - * Editing a file in this buffer: use ":edit file". - * This may have side effects! (e.g., compressed or network file). - */ - if (fputs("edit ", fd) < 0 - || ses_fname(fd, wp->w_buffer, flagp) == FAIL) - return FAIL; - } else { - /* No file in this buffer, just make it empty. */ - if (put_line(fd, "enew") == FAIL) - return FAIL; - if (wp->w_buffer->b_ffname != NULL) { - /* The buffer does have a name, but it's not a file name. */ - if (fputs("file ", fd) < 0 - || ses_fname(fd, wp->w_buffer, flagp) == FAIL) - return FAIL; - } - do_cursor = FALSE; - } - } - - /* - * Local mappings and abbreviations. - */ - if ((*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) - && makemap(fd, wp->w_buffer) == FAIL) - return FAIL; - - /* - * Local options. Need to go to the window temporarily. - * Store only local values when using ":mkview" and when ":mksession" is - * used and 'sessionoptions' doesn't include "options". - * Some folding options are always stored when "folds" is included, - * otherwise the folds would not be restored correctly. - */ - save_curwin = curwin; - curwin = wp; - curbuf = curwin->w_buffer; - if (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) - f = makeset(fd, OPT_LOCAL, - flagp == &vop_flags || !(*flagp & SSOP_OPTIONS)); - else if (*flagp & SSOP_FOLDS) - f = makefoldset(fd); - else - f = OK; - curwin = save_curwin; - curbuf = curwin->w_buffer; - if (f == FAIL) - return FAIL; - - /* - * Save Folds when 'buftype' is empty and for help files. - */ - if ((*flagp & SSOP_FOLDS) - && wp->w_buffer->b_ffname != NULL - && (*wp->w_buffer->b_p_bt == NUL || wp->w_buffer->b_help) - ) { - if (put_folds(fd, wp) == FAIL) - return FAIL; - } - - /* - * Set the cursor after creating folds, since that moves the cursor. - */ - if (do_cursor) { - - /* Restore the cursor line in the file and relatively in the - * window. Don't use "G", it changes the jumplist. */ - if (fprintf(fd, - "let s:l = %" PRId64 " - ((%" PRId64 - " * winheight(0) + %" PRId64 ") / %" PRId64 ")", - (int64_t)wp->w_cursor.lnum, - (int64_t)(wp->w_cursor.lnum - wp->w_topline), - (int64_t)(wp->w_height / 2), - (int64_t)wp->w_height) < 0 - || put_eol(fd) == FAIL - || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL - || put_line(fd, "exe s:l") == FAIL - || put_line(fd, "normal! zt") == FAIL - || fprintf(fd, "%" PRId64, (int64_t)wp->w_cursor.lnum) < 0 - || put_eol(fd) == FAIL) - return FAIL; - /* Restore the cursor column and left offset when not wrapping. */ - if (wp->w_cursor.col == 0) { - if (put_line(fd, "normal! 0") == FAIL) - return FAIL; - } else { - if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0) { - if (fprintf(fd, "let s:c = %" PRId64 " - ((%" PRId64 - " * winwidth(0) + %" PRId64 ") / %" PRId64 ")", - (int64_t)(wp->w_virtcol + 1), - (int64_t)(wp->w_virtcol - wp->w_leftcol), - (int64_t)(wp->w_width / 2), - (int64_t)wp->w_width) < 0 - || put_eol(fd) == FAIL - || put_line(fd, "if s:c > 0") == FAIL - || fprintf(fd, " exe 'normal! ' . s:c . '|zs' . %" PRId64 " . '|'", - (int64_t)(wp->w_virtcol + 1)) < 0 - || put_eol(fd) == FAIL - || put_line(fd, "else") == FAIL - || fprintf(fd, " normal! 0%d|", wp->w_virtcol + 1) < 0 - || put_eol(fd) == FAIL - || put_line(fd, "endif") == FAIL) - return FAIL; - } else { - if (fprintf(fd, "normal! 0%d|", wp->w_virtcol + 1) < 0 - || put_eol(fd) == FAIL) - return FAIL; - } - } - } - - /* - * Local directory. - */ - if (wp->w_localdir != NULL) { - if (fputs("lcd ", fd) < 0 - || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL - || put_eol(fd) == FAIL) - return FAIL; - did_lcd = TRUE; - } - - return OK; -} - -/* - * Write an argument list to the session file. - * Returns FAIL if writing fails. - */ -static int -ses_arglist ( - FILE *fd, - char *cmd, - garray_T *gap, - int fullname, /* TRUE: use full path name */ - unsigned *flagp -) -{ - int i; - char_u *buf = NULL; - char_u *s; - - if (fputs(cmd, fd) < 0 || put_eol(fd) == FAIL) { - return FAIL; - } - if (put_line(fd, "silent! argdel *") == FAIL) { - return FAIL; - } - for (i = 0; i < gap->ga_len; ++i) { - /* NULL file names are skipped (only happens when out of memory). */ - s = alist_name(&((aentry_T *)gap->ga_data)[i]); - if (s != NULL) { - if (fullname) { - buf = xmalloc(MAXPATHL); - (void)vim_FullName(s, buf, MAXPATHL, FALSE); - s = buf; - } - if (fputs("argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL - || put_eol(fd) == FAIL) { - free(buf); - return FAIL; - } - free(buf); - } - } - return OK; -} - -/* - * Write a buffer name to the session file. - * Also ends the line. - * Returns FAIL if writing fails. - */ -static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp) -{ - char_u *name; - - /* Use the short file name if the current directory is known at the time - * the session file will be sourced. - * Don't do this for ":mkview", we don't know the current directory. - * Don't do this after ":lcd", we don't keep track of what the current - * directory is. */ - if (buf->b_sfname != NULL - && flagp == &ssop_flags - && (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR)) - && !p_acd - && !did_lcd) - name = buf->b_sfname; - else - name = buf->b_ffname; - if (ses_put_fname(fd, name, flagp) == FAIL || put_eol(fd) == FAIL) - return FAIL; - return OK; -} - -/* - * Write a file name to the session file. - * Takes care of the "slash" option in 'sessionoptions' and escapes special - * characters. - * Returns FAIL if writing fails or out of memory. - */ -static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) -{ - char_u *sname; - char_u *p; - int retval = OK; - - sname = home_replace_save(NULL, name); - if (sname == NULL) - return FAIL; - - if (*flagp & SSOP_SLASH) { - /* change all backslashes to forward slashes */ - for (p = sname; *p != NUL; mb_ptr_adv(p)) - if (*p == '\\') - *p = '/'; - } - - /* escape special characters */ - p = vim_strsave_fnameescape(sname, FALSE); - free(sname); - if (p == NULL) - return FAIL; - - /* write the result */ - if (fputs((char *)p, fd) < 0) - retval = FAIL; - - free(p); - return retval; -} - -/* - * ":loadview [nr]" - */ -static void ex_loadview(exarg_T *eap) -{ - char_u *fname; - - fname = get_view_file(*eap->arg); - if (fname != NULL) { - do_source(fname, FALSE, DOSO_NONE); - free(fname); - } -} - -/* - * Get the name of the view file for the current buffer. - */ -static char_u *get_view_file(int c) -{ - int len = 0; - char_u *p, *s; - char_u *retval; - char_u *sname; - - if (curbuf->b_ffname == NULL) { - EMSG(_(e_noname)); - return NULL; - } - sname = home_replace_save(NULL, curbuf->b_ffname); - if (sname == NULL) - return NULL; - - /* - * We want a file name without separators, because we're not going to make - * a directory. - * "normal" path separator -> "=+" - * "=" -> "==" - * ":" path separator -> "=-" - */ - for (p = sname; *p; ++p) - if (*p == '=' || vim_ispathsep(*p)) - ++len; - retval = xmalloc(STRLEN(sname) + len + STRLEN(p_vdir) + 9); - STRCPY(retval, p_vdir); - add_pathsep(retval); - s = retval + STRLEN(retval); - for (p = sname; *p; ++p) { - if (*p == '=') { - *s++ = '='; - *s++ = '='; - } else if (vim_ispathsep(*p)) { - *s++ = '='; -#if defined(BACKSLASH_IN_FILENAME) - if (*p == ':') - *s++ = '-'; - else -#endif - *s++ = '+'; - } else - *s++ = *p; - } - *s++ = '='; - *s++ = c; - STRCPY(s, ".vim"); - - free(sname); - return retval; -} - - -/* - * Write end-of-line character(s) for ":mkexrc", ":mkvimrc" and ":mksession". - * Return FAIL for a write error. - */ -int put_eol(FILE *fd) -{ - if ( -#ifdef USE_CRNL - ( -# ifdef MKSESSION_NL - !mksession_nl && -# endif - (putc('\r', fd) < 0)) || -#endif - (putc('\n', fd) < 0)) - return FAIL; - return OK; -} - -/* - * Write a line to "fd". - * Return FAIL for a write error. - */ -int put_line(FILE *fd, char *s) -{ - if (fputs(s, fd) < 0 || put_eol(fd) == FAIL) - return FAIL; - return OK; -} - -/* - * ":rviminfo" and ":wviminfo". - */ -static void ex_viminfo(exarg_T *eap) -{ - char_u *save_viminfo; - - save_viminfo = p_viminfo; - if (*p_viminfo == NUL) - p_viminfo = (char_u *)"'100"; - if (eap->cmdidx == CMD_rviminfo) { - if (read_viminfo(eap->arg, VIF_WANT_INFO | VIF_WANT_MARKS - | (eap->forceit ? VIF_FORCEIT : 0)) == FAIL) - EMSG(_("E195: Cannot open viminfo file for reading")); - } else - write_viminfo(eap->arg, eap->forceit); - p_viminfo = save_viminfo; -} - -/* - * Make a dialog message in "buff[DIALOG_MSG_SIZE]". - * "format" must contain "%s". - */ -void dialog_msg(char_u *buff, char *format, char_u *fname) -{ - if (fname == NULL) - fname = (char_u *)_("Untitled"); - vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname); -} - -/* - * ":behave {mswin,xterm}" - */ -static void ex_behave(exarg_T *eap) -{ - if (STRCMP(eap->arg, "mswin") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"exclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"mouse,key", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"popup", 0); - set_option_value((char_u *)"keymodel", 0L, - (char_u *)"startsel,stopsel", 0); - } else if (STRCMP(eap->arg, "xterm") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"inclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"extend", 0); - set_option_value((char_u *)"keymodel", 0L, (char_u *)"", 0); - } else - EMSG2(_(e_invarg2), eap->arg); -} - -/* - * Function given to ExpandGeneric() to obtain the possible arguments of the - * ":behave {mswin,xterm}" command. - */ -char_u *get_behave_arg(expand_T *xp, int idx) -{ - if (idx == 0) - return (char_u *)"mswin"; - if (idx == 1) - return (char_u *)"xterm"; - return NULL; -} - -static int filetype_detect = FALSE; -static int filetype_plugin = FALSE; -static int filetype_indent = FALSE; - -/* - * ":filetype [plugin] [indent] {on,off,detect}" - * on: Load the filetype.vim file to install autocommands for file types. - * off: Load the ftoff.vim file to remove all autocommands for file types. - * plugin on: load filetype.vim and ftplugin.vim - * plugin off: load ftplugof.vim - * indent on: load filetype.vim and indent.vim - * indent off: load indoff.vim - */ -static void ex_filetype(exarg_T *eap) -{ - char_u *arg = eap->arg; - int plugin = FALSE; - int indent = FALSE; - - if (*eap->arg == NUL) { - /* Print current status. */ - smsg((char_u *)"filetype detection:%s plugin:%s indent:%s", - filetype_detect ? "ON" : "OFF", - filetype_plugin ? (filetype_detect ? "ON" : "(on)") : "OFF", - filetype_indent ? (filetype_detect ? "ON" : "(on)") : "OFF"); - return; - } - - /* Accept "plugin" and "indent" in any order. */ - for (;; ) { - if (STRNCMP(arg, "plugin", 6) == 0) { - plugin = TRUE; - arg = skipwhite(arg + 6); - continue; - } - if (STRNCMP(arg, "indent", 6) == 0) { - indent = TRUE; - arg = skipwhite(arg + 6); - continue; - } - break; - } - if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0) { - if (*arg == 'o' || !filetype_detect) { - source_runtime((char_u *)FILETYPE_FILE, TRUE); - filetype_detect = TRUE; - if (plugin) { - source_runtime((char_u *)FTPLUGIN_FILE, TRUE); - filetype_plugin = TRUE; - } - if (indent) { - source_runtime((char_u *)INDENT_FILE, TRUE); - filetype_indent = TRUE; - } - } - if (*arg == 'd') { - (void)do_doautocmd((char_u *)"filetypedetect BufRead", TRUE); - do_modelines(0); - } - } else if (STRCMP(arg, "off") == 0) { - if (plugin || indent) { - if (plugin) { - source_runtime((char_u *)FTPLUGOF_FILE, TRUE); - filetype_plugin = FALSE; - } - if (indent) { - source_runtime((char_u *)INDOFF_FILE, TRUE); - filetype_indent = FALSE; - } - } else { - source_runtime((char_u *)FTOFF_FILE, TRUE); - filetype_detect = FALSE; - } - } else - EMSG2(_(e_invarg2), arg); -} - -/* - * ":setfiletype {name}" - */ -static void ex_setfiletype(exarg_T *eap) -{ - if (!did_filetype) - set_option_value((char_u *)"filetype", 0L, eap->arg, OPT_LOCAL); -} - -static void ex_digraphs(exarg_T *eap) -{ - if (*eap->arg != NUL) - putdigraph(eap->arg); - else - listdigraphs(); -} - -static void ex_set(exarg_T *eap) -{ - int flags = 0; - - if (eap->cmdidx == CMD_setlocal) - flags = OPT_LOCAL; - else if (eap->cmdidx == CMD_setglobal) - flags = OPT_GLOBAL; - (void)do_set(eap->arg, flags); -} - -/* - * ":nohlsearch" - */ -static void ex_nohlsearch(exarg_T *eap) -{ - SET_NO_HLSEARCH(TRUE); - redraw_all_later(SOME_VALID); -} - -/* - * ":[N]match {group} {pattern}" - * Sets nextcmd to the start of the next command, if any. Also called when - * skipping commands to find the next command. - */ -static void ex_match(exarg_T *eap) -{ - char_u *p; - char_u *g = NULL; - char_u *end; - int c; - int id; - - if (eap->line2 <= 3) - id = eap->line2; - else { - EMSG(e_invcmd); - return; - } - - /* First clear any old pattern. */ - if (!eap->skip) - match_delete(curwin, id, FALSE); - - if (ends_excmd(*eap->arg)) - end = eap->arg; - else if ((STRNICMP(eap->arg, "none", 4) == 0 - && (vim_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) - end = eap->arg + 4; - else { - p = skiptowhite(eap->arg); - if (!eap->skip) - g = vim_strnsave(eap->arg, (int)(p - eap->arg)); - p = skipwhite(p); - if (*p == NUL) { - /* There must be two arguments. */ - EMSG2(_(e_invarg2), eap->arg); - return; - } - end = skip_regexp(p + 1, *p, TRUE, NULL); - if (!eap->skip) { - if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { - eap->errmsg = e_trailing; - return; - } - if (*end != *p) { - EMSG2(_(e_invarg2), p); - return; - } - - c = *end; - *end = NUL; - match_add(curwin, g, p + 1, 10, id); - free(g); - *end = c; - } - } - eap->nextcmd = find_nextcmd(end); -} - -/* - * ":X": Get crypt key - */ -static void ex_X(exarg_T *eap) -{ - if (get_crypt_method(curbuf) == 0 || blowfish_self_test() == OK) - (void)get_crypt_key(TRUE, TRUE); -} - -static void ex_fold(exarg_T *eap) -{ - if (foldManualAllowed(TRUE)) - foldCreate(eap->line1, eap->line2); -} - -static void ex_foldopen(exarg_T *eap) -{ - opFoldRange(eap->line1, eap->line2, eap->cmdidx == CMD_foldopen, - eap->forceit, FALSE); -} - -static void ex_folddo(exarg_T *eap) -{ - linenr_T lnum; - - /* First set the marks for all lines closed/open. */ - for (lnum = eap->line1; lnum <= eap->line2; ++lnum) - if (hasFolding(lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed)) - ml_setmarked(lnum); - - /* Execute the command on the marked lines. */ - global_exe(eap->arg); - ml_clearmarked(); /* clear rest of the marks */ -} diff --git a/src/ex_docmd.h b/src/ex_docmd.h deleted file mode 100644 index 3cc134aab2..0000000000 --- a/src/ex_docmd.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef NEOVIM_EX_DOCMD_H -#define NEOVIM_EX_DOCMD_H -/* ex_docmd.c */ -void do_exmode(int improved); -int do_cmdline_cmd(char_u *cmd); -int do_cmdline(char_u *cmdline, char_u * - (*fgetline)(int, void *, int), void *cookie, - int flags); -int getline_equal(char_u * - (*fgetline)(int, void *, int), void *cookie, char_u * - (*func)(int, void *, int)); -void *getline_cookie(char_u *(*fgetline)(int, void *, int), void *cookie); -int checkforcmd(char_u **pp, char *cmd, int len); -int modifier_len(char_u *cmd); -int cmd_exists(char_u *name); -char_u *set_one_cmd_context(expand_T *xp, char_u *buff); -char_u *skip_range(char_u *cmd, int *ctx); -void ex_ni(exarg_T *eap); -int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp); -void separate_nextcmd(exarg_T *eap); -int ends_excmd(int c); -char_u *find_nextcmd(char_u *p); -char_u *check_nextcmd(char_u *p); -char_u *get_command_name(expand_T *xp, int idx); -void ex_comclear(exarg_T *eap); -void ex_may_print(exarg_T *eap); -void uc_clear(garray_T *gap); -char_u *get_user_commands(expand_T *xp, int idx); -char_u *get_user_cmd_flags(expand_T *xp, int idx); -char_u *get_user_cmd_nargs(expand_T *xp, int idx); -char_u *get_user_cmd_complete(expand_T *xp, int idx); -int parse_compl_arg(char_u *value, int vallen, int *complp, long *argt, - char_u **compl_arg); -void not_exiting(void); -void tabpage_close(int forceit); -void tabpage_close_other(tabpage_T *tp, int forceit); -void ex_all(exarg_T *eap); -void alist_clear(alist_T *al); -void alist_init(alist_T *al); -void alist_unlink(alist_T *al); -void alist_new(void); -void alist_expand(int *fnum_list, int fnum_len); -void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, - int *fnum_list, - int fnum_len); -void alist_add(alist_T *al, char_u *fname, int set_fnum); -void alist_slash_adjust(void); -void ex_splitview(exarg_T *eap); -void tabpage_new(void); -void do_exedit(exarg_T *eap, win_T *old_curwin); -void free_cd_dir(void); -void post_chdir(int local); -void ex_cd(exarg_T *eap); -void do_sleep(long msec); -int vim_mkdir_emsg(char_u *name, int prot); -FILE *open_exfile(char_u *fname, int forceit, char *mode); -void update_topline_cursor(void); -void exec_normal_cmd(char_u *cmd, int remap, int silent); -int find_cmdline_var(char_u *src, int *usedlen); -char_u *eval_vars(char_u *src, char_u *srcstart, int *usedlen, - linenr_T *lnump, char_u **errormsg, - int *escaped); -char_u *expand_sfile(char_u *arg); -int put_eol(FILE *fd); -int put_line(FILE *fd, char *s); -void dialog_msg(char_u *buff, char *format, char_u *fname); -char_u *get_behave_arg(expand_T *xp, int idx); - -#endif /* NEOVIM_EX_DOCMD_H */ diff --git a/src/ex_eval.c b/src/ex_eval.c deleted file mode 100644 index fc0887ca03..0000000000 --- a/src/ex_eval.c +++ /dev/null @@ -1,2073 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * ex_eval.c: functions for Ex command line for the +eval feature. - */ - -#include "vim.h" -#include "ex_eval.h" -#include "charset.h" -#include "eval.h" -#include "ex_cmds2.h" -#include "ex_docmd.h" -#include "message.h" -#include "misc2.h" -#include "memory.h" -#include "regexp.h" - - -static void free_msglist(struct msglist *l); -static int throw_exception(void *, int, char_u *); -static char_u *get_end_emsg(struct condstack *cstack); - -/* - * Exception handling terms: - * - * :try ":try" command \ - * ... try block | - * :catch RE ":catch" command | - * ... catch clause |- try conditional - * :finally ":finally" command | - * ... finally clause | - * :endtry ":endtry" command / - * - * The try conditional may have any number of catch clauses and at most one - * finally clause. A ":throw" command can be inside the try block, a catch - * clause, the finally clause, or in a function called or script sourced from - * there or even outside the try conditional. Try conditionals may be nested. - */ - -/* - * Configuration whether an exception is thrown on error or interrupt. When - * the preprocessor macros below evaluate to FALSE, an error (did_emsg) or - * interrupt (got_int) under an active try conditional terminates the script - * after the non-active finally clauses of all active try conditionals have been - * executed. Otherwise, errors and/or interrupts are converted into catchable - * exceptions (did_throw additionally set), which terminate the script only if - * not caught. For user exceptions, only did_throw is set. (Note: got_int can - * be set asynchronously afterwards by a SIGINT, so did_throw && got_int is not - * a reliant test that the exception currently being thrown is an interrupt - * exception. Similarly, did_emsg can be set afterwards on an error in an - * (unskipped) conditional command inside an inactive conditional, so did_throw - * && did_emsg is not a reliant test that the exception currently being thrown - * is an error exception.) - The macros can be defined as expressions checking - * for a variable that is allowed to be changed during execution of a script. - */ -/* Values used for the Vim release. */ -# define THROW_ON_ERROR TRUE -# define THROW_ON_ERROR_TRUE -# define THROW_ON_INTERRUPT TRUE -# define THROW_ON_INTERRUPT_TRUE - -static void catch_exception(except_T *excp); -static void finish_exception(except_T *excp); -static void discard_exception(except_T *excp, int was_finished); -static void report_pending(int action, int pending, void *value); - -/* - * When several errors appear in a row, setting "force_abort" is delayed until - * the failing command returned. "cause_abort" is set to TRUE meanwhile, in - * order to indicate that situation. This is useful when "force_abort" was set - * during execution of a function call from an expression: the aborting of the - * expression evaluation is done without producing any error messages, but all - * error messages on parsing errors during the expression evaluation are given - * (even if a try conditional is active). - */ -static int cause_abort = FALSE; - -/* - * Return TRUE when immediately aborting on error, or when an interrupt - * occurred or an exception was thrown but not caught. Use for ":{range}call" - * to check whether an aborted function that does not handle a range itself - * should be called again for the next line in the range. Also used for - * cancelling expression evaluation after a function call caused an immediate - * abort. Note that the first emsg() call temporarily resets "force_abort" - * until the throw point for error messages has been reached. That is, during - * cancellation of an expression evaluation after an aborting function call or - * due to a parsing error, aborting() always returns the same value. - */ -int aborting(void) -{ - return (did_emsg && force_abort) || got_int || did_throw; -} - -/* - * The value of "force_abort" is temporarily reset by the first emsg() call - * during an expression evaluation, and "cause_abort" is used instead. It might - * be necessary to restore "force_abort" even before the throw point for the - * error message has been reached. update_force_abort() should be called then. - */ -void update_force_abort(void) -{ - if (cause_abort) - force_abort = TRUE; -} - -/* - * Return TRUE if a command with a subcommand resulting in "retcode" should - * abort the script processing. Can be used to suppress an autocommand after - * execution of a failing subcommand as long as the error message has not been - * displayed and actually caused the abortion. - */ -int should_abort(int retcode) -{ - return (retcode == FAIL && trylevel != 0 && !emsg_silent) || aborting(); -} - -/* - * Return TRUE if a function with the "abort" flag should not be considered - * ended on an error. This means that parsing commands is continued in order - * to find finally clauses to be executed, and that some errors in skipped - * commands are still reported. - */ -int aborted_in_try(void) -{ - /* This function is only called after an error. In this case, "force_abort" - * determines whether searching for finally clauses is necessary. */ - return force_abort; -} - -/* - * cause_errthrow(): Cause a throw of an error exception if appropriate. - * Return TRUE if the error message should not be displayed by emsg(). - * Sets "ignore", if the emsg() call should be ignored completely. - * - * When several messages appear in the same command, the first is usually the - * most specific one and used as the exception value. The "severe" flag can be - * set to TRUE, if a later but severer message should be used instead. - */ -int cause_errthrow(char_u *mesg, int severe, int *ignore) -{ - struct msglist *elem; - struct msglist **plist; - - /* - * Do nothing when displaying the interrupt message or reporting an - * uncaught exception (which has already been discarded then) at the top - * level. Also when no exception can be thrown. The message will be - * displayed by emsg(). - */ - if (suppress_errthrow) - return FALSE; - - /* - * If emsg() has not been called previously, temporarily reset - * "force_abort" until the throw point for error messages has been - * reached. This ensures that aborting() returns the same value for all - * errors that appear in the same command. This means particularly that - * for parsing errors during expression evaluation emsg() will be called - * multiply, even when the expression is evaluated from a finally clause - * that was activated due to an aborting error, interrupt, or exception. - */ - if (!did_emsg) { - cause_abort = force_abort; - force_abort = FALSE; - } - - /* - * If no try conditional is active and no exception is being thrown and - * there has not been an error in a try conditional or a throw so far, do - * nothing (for compatibility of non-EH scripts). The message will then - * be displayed by emsg(). When ":silent!" was used and we are not - * currently throwing an exception, do nothing. The message text will - * then be stored to v:errmsg by emsg() without displaying it. - */ - if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw) - return FALSE; - - /* - * Ignore an interrupt message when inside a try conditional or when an - * exception is being thrown or when an error in a try conditional or - * throw has been detected previously. This is important in order that an - * interrupt exception is catchable by the innermost try conditional and - * not replaced by an interrupt message error exception. - */ - if (mesg == (char_u *)_(e_interr)) { - *ignore = TRUE; - return TRUE; - } - - /* - * Ensure that all commands in nested function calls and sourced files - * are aborted immediately. - */ - cause_abort = TRUE; - - /* - * When an exception is being thrown, some commands (like conditionals) are - * not skipped. Errors in those commands may affect what of the subsequent - * commands are regarded part of catch and finally clauses. Catching the - * exception would then cause execution of commands not intended by the - * user, who wouldn't even get aware of the problem. Therefor, discard the - * exception currently being thrown to prevent it from being caught. Just - * execute finally clauses and terminate. - */ - if (did_throw) { - /* When discarding an interrupt exception, reset got_int to prevent the - * same interrupt being converted to an exception again and discarding - * the error exception we are about to throw here. */ - if (current_exception->type == ET_INTERRUPT) - got_int = FALSE; - discard_current_exception(); - } - -#ifdef THROW_TEST - if (!THROW_ON_ERROR) { - /* - * Print error message immediately without searching for a matching - * catch clause; just finally clauses are executed before the script - * is terminated. - */ - return FALSE; - } else -#endif - { - /* - * Prepare the throw of an error exception, so that everything will - * be aborted (except for executing finally clauses), until the error - * exception is caught; if still uncaught at the top level, the error - * message will be displayed and the script processing terminated - * then. - This function has no access to the conditional stack. - * Thus, the actual throw is made after the failing command has - * returned. - Throw only the first of several errors in a row, except - * a severe error is following. - */ - if (msg_list != NULL) { - plist = msg_list; - while (*plist != NULL) - plist = &(*plist)->next; - - elem = (struct msglist *)alloc((unsigned)sizeof(struct msglist)); - if (elem == NULL) { - suppress_errthrow = TRUE; - EMSG(_(e_outofmem)); - } else { - elem->msg = vim_strsave(mesg); - if (elem->msg == NULL) { - free(elem); - suppress_errthrow = TRUE; - EMSG(_(e_outofmem)); - } else { - elem->next = NULL; - elem->throw_msg = NULL; - *plist = elem; - if (plist == msg_list || severe) { - char_u *tmsg; - - /* Skip the extra "Vim " prefix for message "E458". */ - tmsg = elem->msg; - if (STRNCMP(tmsg, "Vim E", 5) == 0 - && VIM_ISDIGIT(tmsg[5]) - && VIM_ISDIGIT(tmsg[6]) - && VIM_ISDIGIT(tmsg[7]) - && tmsg[8] == ':' - && tmsg[9] == ' ') - (*msg_list)->throw_msg = &tmsg[4]; - else - (*msg_list)->throw_msg = tmsg; - } - } - } - } - return TRUE; - } -} - -/* - * Free a "msg_list" and the messages it contains. - */ -static void free_msglist(struct msglist *l) -{ - struct msglist *messages, *next; - - messages = l; - while (messages != NULL) { - next = messages->next; - free(messages->msg); - free(messages); - messages = next; - } -} - -/* - * Free global "*msg_list" and the messages it contains, then set "*msg_list" - * to NULL. - */ -void free_global_msglist(void) -{ - free_msglist(*msg_list); - *msg_list = NULL; -} - -/* - * Throw the message specified in the call to cause_errthrow() above as an - * error exception. If cstack is NULL, postpone the throw until do_cmdline() - * has returned (see do_one_cmd()). - */ -void do_errthrow(struct condstack *cstack, char_u *cmdname) -{ - /* - * Ensure that all commands in nested function calls and sourced files - * are aborted immediately. - */ - if (cause_abort) { - cause_abort = FALSE; - force_abort = TRUE; - } - - /* If no exception is to be thrown or the conversion should be done after - * returning to a previous invocation of do_one_cmd(), do nothing. */ - if (msg_list == NULL || *msg_list == NULL) - return; - - if (throw_exception(*msg_list, ET_ERROR, cmdname) == FAIL) - free_msglist(*msg_list); - else { - if (cstack != NULL) - do_throw(cstack); - else - need_rethrow = TRUE; - } - *msg_list = NULL; -} - -/* - * do_intthrow(): Replace the current exception by an interrupt or interrupt - * exception if appropriate. Return TRUE if the current exception is discarded, - * FALSE otherwise. - */ -int do_intthrow(struct condstack *cstack) -{ - /* - * If no interrupt occurred or no try conditional is active and no exception - * is being thrown, do nothing (for compatibility of non-EH scripts). - */ - if (!got_int || (trylevel == 0 && !did_throw)) - return FALSE; - -#ifdef THROW_TEST /* avoid warning for condition always true */ - if (!THROW_ON_INTERRUPT) { - /* - * The interrupt aborts everything except for executing finally clauses. - * Discard any user or error or interrupt exception currently being - * thrown. - */ - if (did_throw) - discard_current_exception(); - } else -#endif - { - /* - * Throw an interrupt exception, so that everything will be aborted - * (except for executing finally clauses), until the interrupt exception - * is caught; if still uncaught at the top level, the script processing - * will be terminated then. - If an interrupt exception is already - * being thrown, do nothing. - * - */ - if (did_throw) { - if (current_exception->type == ET_INTERRUPT) - return FALSE; - - /* An interrupt exception replaces any user or error exception. */ - discard_current_exception(); - } - if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL) - do_throw(cstack); - } - - return TRUE; -} - -/* - * Get an exception message that is to be stored in current_exception->value. - */ -char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should_free) -{ - char_u *ret, *mesg; - int cmdlen; - char_u *p, *val; - - if (type == ET_ERROR) { - *should_free = FALSE; - mesg = ((struct msglist *)value)->throw_msg; - if (cmdname != NULL && *cmdname != NUL) { - cmdlen = (int)STRLEN(cmdname); - ret = vim_strnsave((char_u *)"Vim(", - 4 + cmdlen + 2 + (int)STRLEN(mesg)); - if (ret == NULL) - return ret; - STRCPY(&ret[4], cmdname); - STRCPY(&ret[4 + cmdlen], "):"); - val = ret + 4 + cmdlen + 2; - } else { - ret = vim_strnsave((char_u *)"Vim:", 4 + (int)STRLEN(mesg)); - if (ret == NULL) - return ret; - val = ret + 4; - } - - /* msg_add_fname may have been used to prefix the message with a file - * name in quotes. In the exception value, put the file name in - * parentheses and move it to the end. */ - for (p = mesg;; p++) { - if (*p == NUL - || (*p == 'E' - && VIM_ISDIGIT(p[1]) - && (p[2] == ':' - || (VIM_ISDIGIT(p[2]) - && (p[3] == ':' - || (VIM_ISDIGIT(p[3]) - && p[4] == ':')))))) { - if (*p == NUL || p == mesg) - STRCAT(val, mesg); /* 'E123' missing or at beginning */ - else { - /* '"filename" E123: message text' */ - if (mesg[0] != '"' || p-2 < &mesg[1] || - p[-2] != '"' || p[-1] != ' ') - /* "E123:" is part of the file name. */ - continue; - - STRCAT(val, p); - p[-2] = NUL; - sprintf((char *)(val + STRLEN(p)), " (%s)", &mesg[1]); - p[-2] = '"'; - } - break; - } - } - } else { - *should_free = FALSE; - ret = (char_u *) value; - } - - return ret; -} - - -/* - * Throw a new exception. Return FAIL when out of memory or it was tried to - * throw an illegal user exception. "value" is the exception string for a - * user or interrupt exception, or points to a message list in case of an - * error exception. - */ -static int throw_exception(void *value, int type, char_u *cmdname) -{ - except_T *excp; - int should_free; - - /* - * Disallow faking Interrupt or error exceptions as user exceptions. They - * would be treated differently from real interrupt or error exceptions - * when no active try block is found, see do_cmdline(). - */ - if (type == ET_USER) { - if (STRNCMP((char_u *)value, "Vim", 3) == 0 - && (((char_u *)value)[3] == NUL || ((char_u *)value)[3] == ':' - || ((char_u *)value)[3] == '(')) { - EMSG(_("E608: Cannot :throw exceptions with 'Vim' prefix")); - goto fail; - } - } - - excp = (except_T *)alloc((unsigned)sizeof(except_T)); - if (excp == NULL) - goto nomem; - - if (type == ET_ERROR) - /* Store the original message and prefix the exception value with - * "Vim:" or, if a command name is given, "Vim(cmdname):". */ - excp->messages = (struct msglist *)value; - - excp->value = get_exception_string(value, type, cmdname, &should_free); - if (excp->value == NULL && should_free) - goto nomem; - - excp->type = type; - excp->throw_name = vim_strsave(sourcing_name == NULL - ? (char_u *)"" : sourcing_name); - if (excp->throw_name == NULL) { - if (should_free) - free(excp->value); - goto nomem; - } - excp->throw_lnum = sourcing_lnum; - - if (p_verbose >= 13 || debug_break_level > 0) { - int save_msg_silent = msg_silent; - - if (debug_break_level > 0) - msg_silent = FALSE; /* display messages */ - else - verbose_enter(); - ++no_wait_return; - if (debug_break_level > 0 || *p_vfile == NUL) - msg_scroll = TRUE; /* always scroll up, don't overwrite */ - - smsg((char_u *)_("Exception thrown: %s"), excp->value); - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - if (debug_break_level > 0 || *p_vfile == NUL) - cmdline_row = msg_row; - --no_wait_return; - if (debug_break_level > 0) - msg_silent = save_msg_silent; - else - verbose_leave(); - } - - current_exception = excp; - return OK; - -nomem: - free(excp); - suppress_errthrow = TRUE; - EMSG(_(e_outofmem)); -fail: - current_exception = NULL; - return FAIL; -} - -/* - * Discard an exception. "was_finished" is set when the exception has been - * caught and the catch clause has been ended normally. - */ -static void discard_exception(except_T *excp, int was_finished) -{ - char_u *saved_IObuff; - - if (excp == NULL) { - EMSG(_(e_internal)); - return; - } - - if (p_verbose >= 13 || debug_break_level > 0) { - int save_msg_silent = msg_silent; - - saved_IObuff = vim_strsave(IObuff); - if (debug_break_level > 0) - msg_silent = FALSE; /* display messages */ - else - verbose_enter(); - ++no_wait_return; - if (debug_break_level > 0 || *p_vfile == NUL) - msg_scroll = TRUE; /* always scroll up, don't overwrite */ - smsg(was_finished - ? (char_u *)_("Exception finished: %s") - : (char_u *)_("Exception discarded: %s"), - excp->value); - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - if (debug_break_level > 0 || *p_vfile == NUL) - cmdline_row = msg_row; - --no_wait_return; - if (debug_break_level > 0) - msg_silent = save_msg_silent; - else - verbose_leave(); - STRCPY(IObuff, saved_IObuff); - free(saved_IObuff); - } - if (excp->type != ET_INTERRUPT) - free(excp->value); - if (excp->type == ET_ERROR) - free_msglist(excp->messages); - free(excp->throw_name); - free(excp); -} - -/* - * Discard the exception currently being thrown. - */ -void discard_current_exception(void) -{ - discard_exception(current_exception, FALSE); - current_exception = NULL; - did_throw = FALSE; - need_rethrow = FALSE; -} - -/* - * Put an exception on the caught stack. - */ -static void catch_exception(except_T *excp) -{ - excp->caught = caught_stack; - caught_stack = excp; - set_vim_var_string(VV_EXCEPTION, excp->value, -1); - if (*excp->throw_name != NUL) { - if (excp->throw_lnum != 0) - vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %" PRId64), - excp->throw_name, (int64_t)excp->throw_lnum); - else - vim_snprintf((char *)IObuff, IOSIZE, "%s", excp->throw_name); - set_vim_var_string(VV_THROWPOINT, IObuff, -1); - } else - /* throw_name not set on an exception from a command that was typed. */ - set_vim_var_string(VV_THROWPOINT, NULL, -1); - - if (p_verbose >= 13 || debug_break_level > 0) { - int save_msg_silent = msg_silent; - - if (debug_break_level > 0) - msg_silent = FALSE; /* display messages */ - else - verbose_enter(); - ++no_wait_return; - if (debug_break_level > 0 || *p_vfile == NUL) - msg_scroll = TRUE; /* always scroll up, don't overwrite */ - - smsg((char_u *)_("Exception caught: %s"), excp->value); - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - - if (debug_break_level > 0 || *p_vfile == NUL) - cmdline_row = msg_row; - --no_wait_return; - if (debug_break_level > 0) - msg_silent = save_msg_silent; - else - verbose_leave(); - } -} - -/* - * Remove an exception from the caught stack. - */ -static void finish_exception(except_T *excp) -{ - if (excp != caught_stack) - EMSG(_(e_internal)); - caught_stack = caught_stack->caught; - if (caught_stack != NULL) { - set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1); - if (*caught_stack->throw_name != NUL) { - if (caught_stack->throw_lnum != 0) - vim_snprintf((char *)IObuff, IOSIZE, - _("%s, line %" PRId64), caught_stack->throw_name, - (int64_t)caught_stack->throw_lnum); - else - vim_snprintf((char *)IObuff, IOSIZE, "%s", - caught_stack->throw_name); - set_vim_var_string(VV_THROWPOINT, IObuff, -1); - } else - /* throw_name not set on an exception from a command that was - * typed. */ - set_vim_var_string(VV_THROWPOINT, NULL, -1); - } else { - set_vim_var_string(VV_EXCEPTION, NULL, -1); - set_vim_var_string(VV_THROWPOINT, NULL, -1); - } - - /* Discard the exception, but use the finish message for 'verbose'. */ - discard_exception(excp, TRUE); -} - -/* - * Flags specifying the message displayed by report_pending. - */ -#define RP_MAKE 0 -#define RP_RESUME 1 -#define RP_DISCARD 2 - -/* - * Report information about something pending in a finally clause if required by - * the 'verbose' option or when debugging. "action" tells whether something is - * made pending or something pending is resumed or discarded. "pending" tells - * what is pending. "value" specifies the return value for a pending ":return" - * or the exception value for a pending exception. - */ -static void report_pending(int action, int pending, void *value) -{ - char_u *mesg; - char *s; - int save_msg_silent; - - - switch (action) { - case RP_MAKE: - mesg = (char_u *)_("%s made pending"); - break; - case RP_RESUME: - mesg = (char_u *)_("%s resumed"); - break; - /* case RP_DISCARD: */ - default: - mesg = (char_u *)_("%s discarded"); - break; - } - - switch (pending) { - case CSTP_NONE: - return; - - case CSTP_CONTINUE: - s = ":continue"; - break; - case CSTP_BREAK: - s = ":break"; - break; - case CSTP_FINISH: - s = ":finish"; - break; - case CSTP_RETURN: - /* ":return" command producing value, allocated */ - s = (char *)get_return_cmd(value); - break; - - default: - if (pending & CSTP_THROW) { - vim_snprintf((char *)IObuff, IOSIZE, - (char *)mesg, _("Exception")); - mesg = vim_strnsave(IObuff, (int)STRLEN(IObuff) + 4); - STRCAT(mesg, ": %s"); - s = (char *)((except_T *)value)->value; - } else if ((pending & CSTP_ERROR) && (pending & CSTP_INTERRUPT)) - s = _("Error and interrupt"); - else if (pending & CSTP_ERROR) - s = _("Error"); - else /* if (pending & CSTP_INTERRUPT) */ - s = _("Interrupt"); - } - - save_msg_silent = msg_silent; - if (debug_break_level > 0) - msg_silent = FALSE; /* display messages */ - ++no_wait_return; - msg_scroll = TRUE; /* always scroll up, don't overwrite */ - smsg(mesg, (char_u *)s); - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - cmdline_row = msg_row; - --no_wait_return; - if (debug_break_level > 0) - msg_silent = save_msg_silent; - - if (pending == CSTP_RETURN) - free(s); - else if (pending & CSTP_THROW) - free(mesg); -} - -/* - * If something is made pending in a finally clause, report it if required by - * the 'verbose' option or when debugging. - */ -void report_make_pending(int pending, void *value) -{ - if (p_verbose >= 14 || debug_break_level > 0) { - if (debug_break_level <= 0) - verbose_enter(); - report_pending(RP_MAKE, pending, value); - if (debug_break_level <= 0) - verbose_leave(); - } -} - -/* - * If something pending in a finally clause is resumed at the ":endtry", report - * it if required by the 'verbose' option or when debugging. - */ -void report_resume_pending(int pending, void *value) -{ - if (p_verbose >= 14 || debug_break_level > 0) { - if (debug_break_level <= 0) - verbose_enter(); - report_pending(RP_RESUME, pending, value); - if (debug_break_level <= 0) - verbose_leave(); - } -} - -/* - * If something pending in a finally clause is discarded, report it if required - * by the 'verbose' option or when debugging. - */ -void report_discard_pending(int pending, void *value) -{ - if (p_verbose >= 14 || debug_break_level > 0) { - if (debug_break_level <= 0) - verbose_enter(); - report_pending(RP_DISCARD, pending, value); - if (debug_break_level <= 0) - verbose_leave(); - } -} - - -/* - * ":if". - */ -void ex_if(exarg_T *eap) -{ - int error; - int skip; - int result; - struct condstack *cstack = eap->cstack; - - if (cstack->cs_idx == CSTACK_LEN - 1) - eap->errmsg = (char_u *)N_("E579: :if nesting too deep"); - else { - ++cstack->cs_idx; - cstack->cs_flags[cstack->cs_idx] = 0; - - /* - * Don't do something after an error, interrupt, or throw, or when there - * is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); - - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); - - if (!skip && !error) { - if (result) - cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE | CSF_TRUE; - } else - /* set TRUE, so this conditional will never get active */ - cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; - } -} - -/* - * ":endif". - */ -void ex_endif(exarg_T *eap) -{ - did_endif = TRUE; - if (eap->cstack->cs_idx < 0 - || (eap->cstack->cs_flags[eap->cstack->cs_idx] - & (CSF_WHILE | CSF_FOR | CSF_TRY))) - eap->errmsg = (char_u *)N_("E580: :endif without :if"); - else { - /* - * When debugging or a breakpoint was encountered, display the debug - * prompt (if not already done). This shows the user that an ":endif" - * is executed when the ":if" or a previous ":elseif" was not TRUE. - * Handle a ">quit" debug command as if an interrupt had occurred before - * the ":endif". That is, throw an interrupt exception if appropriate. - * Doing this here prevents an exception for a parsing error being - * discarded by throwing the interrupt exception later on. - */ - if (!(eap->cstack->cs_flags[eap->cstack->cs_idx] & CSF_TRUE) - && dbg_check_skipped(eap)) - (void)do_intthrow(eap->cstack); - - --eap->cstack->cs_idx; - } -} - -/* - * ":else" and ":elseif". - */ -void ex_else(exarg_T *eap) -{ - int error; - int skip; - int result; - struct condstack *cstack = eap->cstack; - - /* - * Don't do something after an error, interrupt, or throw, or when there is - * a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); - - if (cstack->cs_idx < 0 - || (cstack->cs_flags[cstack->cs_idx] - & (CSF_WHILE | CSF_FOR | CSF_TRY))) { - if (eap->cmdidx == CMD_else) { - eap->errmsg = (char_u *)N_("E581: :else without :if"); - return; - } - eap->errmsg = (char_u *)N_("E582: :elseif without :if"); - skip = TRUE; - } else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) { - if (eap->cmdidx == CMD_else) { - eap->errmsg = (char_u *)N_("E583: multiple :else"); - return; - } - eap->errmsg = (char_u *)N_("E584: :elseif after :else"); - skip = TRUE; - } - - /* if skipping or the ":if" was TRUE, reset ACTIVE, otherwise set it */ - if (skip || cstack->cs_flags[cstack->cs_idx] & CSF_TRUE) { - if (eap->errmsg == NULL) - cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; - skip = TRUE; /* don't evaluate an ":elseif" */ - } else - cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE; - - /* - * When debugging or a breakpoint was encountered, display the debug prompt - * (if not already done). This shows the user that an ":else" or ":elseif" - * is executed when the ":if" or previous ":elseif" was not TRUE. Handle - * a ">quit" debug command as if an interrupt had occurred before the - * ":else" or ":elseif". That is, set "skip" and throw an interrupt - * exception if appropriate. Doing this here prevents that an exception - * for a parsing errors is discarded when throwing the interrupt exception - * later on. - */ - if (!skip && dbg_check_skipped(eap) && got_int) { - (void)do_intthrow(cstack); - skip = TRUE; - } - - if (eap->cmdidx == CMD_elseif) { - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); - /* When throwing error exceptions, we want to throw always the first - * of several errors in a row. This is what actually happens when - * a conditional error was detected above and there is another failure - * when parsing the expression. Since the skip flag is set in this - * case, the parsing error will be ignored by emsg(). */ - - if (!skip && !error) { - if (result) - cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE | CSF_TRUE; - else - cstack->cs_flags[cstack->cs_idx] = 0; - } else if (eap->errmsg == NULL) - /* set TRUE, so this conditional will never get active */ - cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; - } else - cstack->cs_flags[cstack->cs_idx] |= CSF_ELSE; -} - -/* - * Handle ":while" and ":for". - */ -void ex_while(exarg_T *eap) -{ - int error; - int skip; - int result; - struct condstack *cstack = eap->cstack; - - if (cstack->cs_idx == CSTACK_LEN - 1) - eap->errmsg = (char_u *)N_("E585: :while/:for nesting too deep"); - else { - /* - * The loop flag is set when we have jumped back from the matching - * ":endwhile" or ":endfor". When not set, need to initialise this - * cstack entry. - */ - if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0) { - ++cstack->cs_idx; - ++cstack->cs_looplevel; - cstack->cs_line[cstack->cs_idx] = -1; - } - cstack->cs_flags[cstack->cs_idx] = - eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; - - /* - * Don't do something after an error, interrupt, or throw, or when - * there is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); - if (eap->cmdidx == CMD_while) { - /* - * ":while bool-expr" - */ - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); - } else { - void *fi; - - /* - * ":for var in list-expr" - */ - if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) { - /* Jumping here from a ":continue" or ":endfor": use the - * previously evaluated list. */ - fi = cstack->cs_forinfo[cstack->cs_idx]; - error = FALSE; - } else { - /* Evaluate the argument and get the info in a structure. */ - fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip); - cstack->cs_forinfo[cstack->cs_idx] = fi; - } - - /* use the element at the start of the list and advance */ - if (!error && fi != NULL && !skip) - result = next_for_item(fi, eap->arg); - else - result = FALSE; - - if (!result) { - free_for_info(fi); - cstack->cs_forinfo[cstack->cs_idx] = NULL; - } - } - - /* - * If this cstack entry was just initialised and is active, set the - * loop flag, so do_cmdline() will set the line number in cs_line[]. - * If executing the command a second time, clear the loop flag. - */ - if (!skip && !error && result) { - cstack->cs_flags[cstack->cs_idx] |= (CSF_ACTIVE | CSF_TRUE); - cstack->cs_lflags ^= CSL_HAD_LOOP; - } else { - cstack->cs_lflags &= ~CSL_HAD_LOOP; - /* If the ":while" evaluates to FALSE or ":for" is past the end of - * the list, show the debug prompt at the ":endwhile"/":endfor" as - * if there was a ":break" in a ":while"/":for" evaluating to - * TRUE. */ - if (!skip && !error) - cstack->cs_flags[cstack->cs_idx] |= CSF_TRUE; - } - } -} - -/* - * ":continue" - */ -void ex_continue(exarg_T *eap) -{ - int idx; - struct condstack *cstack = eap->cstack; - - if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) - eap->errmsg = (char_u *)N_("E586: :continue without :while or :for"); - else { - /* Try to find the matching ":while". This might stop at a try - * conditional not in its finally clause (which is then to be executed - * next). Therefor, inactivate all conditionals except the ":while" - * itself (if reached). */ - idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE); - if (idx >= 0 && (cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR))) { - rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel); - - /* - * Set CSL_HAD_CONT, so do_cmdline() will jump back to the - * matching ":while". - */ - cstack->cs_lflags |= CSL_HAD_CONT; /* let do_cmdline() handle it */ - } else { - /* If a try conditional not in its finally clause is reached first, - * make the ":continue" pending for execution at the ":endtry". */ - cstack->cs_pending[idx] = CSTP_CONTINUE; - report_make_pending(CSTP_CONTINUE, NULL); - } - } -} - -/* - * ":break" - */ -void ex_break(exarg_T *eap) -{ - int idx; - struct condstack *cstack = eap->cstack; - - if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) - eap->errmsg = (char_u *)N_("E587: :break without :while or :for"); - else { - /* Inactivate conditionals until the matching ":while" or a try - * conditional not in its finally clause (which is then to be - * executed next) is found. In the latter case, make the ":break" - * pending for execution at the ":endtry". */ - idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, TRUE); - if (idx >= 0 && !(cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR))) { - cstack->cs_pending[idx] = CSTP_BREAK; - report_make_pending(CSTP_BREAK, NULL); - } - } -} - -/* - * ":endwhile" and ":endfor" - */ -void ex_endwhile(exarg_T *eap) -{ - struct condstack *cstack = eap->cstack; - int idx; - char_u *err; - int csf; - int fl; - - if (eap->cmdidx == CMD_endwhile) { - err = e_while; - csf = CSF_WHILE; - } else { - err = e_for; - csf = CSF_FOR; - } - - if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) - eap->errmsg = err; - else { - fl = cstack->cs_flags[cstack->cs_idx]; - if (!(fl & csf)) { - /* If we are in a ":while" or ":for" but used the wrong endloop - * command, do not rewind to the next enclosing ":for"/":while". */ - if (fl & CSF_WHILE) - eap->errmsg = (char_u *)_("E732: Using :endfor with :while"); - else if (fl & CSF_FOR) - eap->errmsg = (char_u *)_("E733: Using :endwhile with :for"); - } - if (!(fl & (CSF_WHILE | CSF_FOR))) { - if (!(fl & CSF_TRY)) - eap->errmsg = e_endif; - else if (fl & CSF_FINALLY) - eap->errmsg = e_endtry; - /* Try to find the matching ":while" and report what's missing. */ - for (idx = cstack->cs_idx; idx > 0; --idx) { - fl = cstack->cs_flags[idx]; - if ((fl & CSF_TRY) && !(fl & CSF_FINALLY)) { - /* Give up at a try conditional not in its finally clause. - * Ignore the ":endwhile"/":endfor". */ - eap->errmsg = err; - return; - } - if (fl & csf) - break; - } - /* Cleanup and rewind all contained (and unclosed) conditionals. */ - (void)cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE); - rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel); - } - /* - * When debugging or a breakpoint was encountered, display the debug - * prompt (if not already done). This shows the user that an - * ":endwhile"/":endfor" is executed when the ":while" was not TRUE or - * after a ":break". Handle a ">quit" debug command as if an - * interrupt had occurred before the ":endwhile"/":endfor". That is, - * throw an interrupt exception if appropriate. Doing this here - * prevents that an exception for a parsing error is discarded when - * throwing the interrupt exception later on. - */ - else if (cstack->cs_flags[cstack->cs_idx] & CSF_TRUE - && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE) - && dbg_check_skipped(eap)) - (void)do_intthrow(cstack); - - /* - * Set loop flag, so do_cmdline() will jump back to the matching - * ":while" or ":for". - */ - cstack->cs_lflags |= CSL_HAD_ENDLOOP; - } -} - - -/* - * ":throw expr" - */ -void ex_throw(exarg_T *eap) -{ - char_u *arg = eap->arg; - char_u *value; - - if (*arg != NUL && *arg != '|' && *arg != '\n') - value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip); - else { - EMSG(_(e_argreq)); - value = NULL; - } - - /* On error or when an exception is thrown during argument evaluation, do - * not throw. */ - if (!eap->skip && value != NULL) { - if (throw_exception(value, ET_USER, NULL) == FAIL) - free(value); - else - do_throw(eap->cstack); - } -} - -/* - * Throw the current exception through the specified cstack. Common routine - * for ":throw" (user exception) and error and interrupt exceptions. Also - * used for rethrowing an uncaught exception. - */ -void do_throw(struct condstack *cstack) -{ - int idx; - int inactivate_try = FALSE; - - /* - * Cleanup and inactivate up to the next surrounding try conditional that - * is not in its finally clause. Normally, do not inactivate the try - * conditional itself, so that its ACTIVE flag can be tested below. But - * if a previous error or interrupt has not been converted to an exception, - * inactivate the try conditional, too, as if the conversion had been done, - * and reset the did_emsg or got_int flag, so this won't happen again at - * the next surrounding try conditional. - */ -#ifndef THROW_ON_ERROR_TRUE - if (did_emsg && !THROW_ON_ERROR) { - inactivate_try = TRUE; - did_emsg = FALSE; - } -#endif -#ifndef THROW_ON_INTERRUPT_TRUE - if (got_int && !THROW_ON_INTERRUPT) { - inactivate_try = TRUE; - got_int = FALSE; - } -#endif - idx = cleanup_conditionals(cstack, 0, inactivate_try); - if (idx >= 0) { - /* - * If this try conditional is active and we are before its first - * ":catch", set THROWN so that the ":catch" commands will check - * whether the exception matches. When the exception came from any of - * the catch clauses, it will be made pending at the ":finally" (if - * present) and rethrown at the ":endtry". This will also happen if - * the try conditional is inactive. This is the case when we are - * throwing an exception due to an error or interrupt on the way from - * a preceding ":continue", ":break", ":return", ":finish", error or - * interrupt (not converted to an exception) to the finally clause or - * from a preceding throw of a user or error or interrupt exception to - * the matching catch clause or the finally clause. - */ - if (!(cstack->cs_flags[idx] & CSF_CAUGHT)) { - if (cstack->cs_flags[idx] & CSF_ACTIVE) - cstack->cs_flags[idx] |= CSF_THROWN; - else - /* THROWN may have already been set for a catchable exception - * that has been discarded. Ensure it is reset for the new - * exception. */ - cstack->cs_flags[idx] &= ~CSF_THROWN; - } - cstack->cs_flags[idx] &= ~CSF_ACTIVE; - cstack->cs_exception[idx] = current_exception; - } - - did_throw = TRUE; -} - -/* - * ":try" - */ -void ex_try(exarg_T *eap) -{ - int skip; - struct condstack *cstack = eap->cstack; - - if (cstack->cs_idx == CSTACK_LEN - 1) - eap->errmsg = (char_u *)N_("E601: :try nesting too deep"); - else { - ++cstack->cs_idx; - ++cstack->cs_trylevel; - cstack->cs_flags[cstack->cs_idx] = CSF_TRY; - cstack->cs_pending[cstack->cs_idx] = CSTP_NONE; - - /* - * Don't do something after an error, interrupt, or throw, or when there - * is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); - - if (!skip) { - /* Set ACTIVE and TRUE. TRUE means that the corresponding ":catch" - * commands should check for a match if an exception is thrown and - * that the finally clause needs to be executed. */ - cstack->cs_flags[cstack->cs_idx] |= CSF_ACTIVE | CSF_TRUE; - - /* - * ":silent!", even when used in a try conditional, disables - * displaying of error messages and conversion of errors to - * exceptions. When the silent commands again open a try - * conditional, save "emsg_silent" and reset it so that errors are - * again converted to exceptions. The value is restored when that - * try conditional is left. If it is left normally, the commands - * following the ":endtry" are again silent. If it is left by - * a ":continue", ":break", ":return", or ":finish", the commands - * executed next are again silent. If it is left due to an - * aborting error, an interrupt, or an exception, restoring - * "emsg_silent" does not matter since we are already in the - * aborting state and/or the exception has already been thrown. - * The effect is then just freeing the memory that was allocated - * to save the value. - */ - if (emsg_silent) { - eslist_T *elem; - - elem = (eslist_T *)alloc((unsigned)sizeof(struct eslist_elem)); - if (elem == NULL) - EMSG(_(e_outofmem)); - else { - elem->saved_emsg_silent = emsg_silent; - elem->next = cstack->cs_emsg_silent_list; - cstack->cs_emsg_silent_list = elem; - cstack->cs_flags[cstack->cs_idx] |= CSF_SILENT; - emsg_silent = 0; - } - } - } - - } -} - -/* - * ":catch /{pattern}/" and ":catch" - */ -void ex_catch(exarg_T *eap) -{ - int idx = 0; - int give_up = FALSE; - int skip = FALSE; - int caught = FALSE; - char_u *end; - int save_char = 0; - char_u *save_cpo; - regmatch_T regmatch; - int prev_got_int; - struct condstack *cstack = eap->cstack; - char_u *pat; - - if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { - eap->errmsg = (char_u *)N_("E603: :catch without :try"); - give_up = TRUE; - } else { - if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { - /* Report what's missing if the matching ":try" is not in its - * finally clause. */ - eap->errmsg = get_end_emsg(cstack); - skip = TRUE; - } - for (idx = cstack->cs_idx; idx > 0; --idx) - if (cstack->cs_flags[idx] & CSF_TRY) - break; - if (cstack->cs_flags[idx] & CSF_FINALLY) { - /* Give up for a ":catch" after ":finally" and ignore it. - * Just parse. */ - eap->errmsg = (char_u *)N_("E604: :catch after :finally"); - give_up = TRUE; - } else - rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, - &cstack->cs_looplevel); - } - - if (ends_excmd(*eap->arg)) { /* no argument, catch all errors */ - pat = (char_u *)".*"; - end = NULL; - eap->nextcmd = find_nextcmd(eap->arg); - } else { - pat = eap->arg + 1; - end = skip_regexp(pat, *eap->arg, TRUE, NULL); - } - - if (!give_up) { - /* - * Don't do something when no exception has been thrown or when the - * corresponding try block never got active (because of an inactive - * surrounding conditional or after an error or interrupt or throw). - */ - if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE)) - skip = TRUE; - - /* - * Check for a match only if an exception is thrown but not caught by - * a previous ":catch". An exception that has replaced a discarded - * exception is not checked (THROWN is not set then). - */ - if (!skip && (cstack->cs_flags[idx] & CSF_THROWN) - && !(cstack->cs_flags[idx] & CSF_CAUGHT)) { - if (end != NULL && *end != NUL && !ends_excmd(*skipwhite(end + 1))) { - EMSG(_(e_trailing)); - return; - } - - /* When debugging or a breakpoint was encountered, display the - * debug prompt (if not already done) before checking for a match. - * This is a helpful hint for the user when the regular expression - * matching fails. Handle a ">quit" debug command as if an - * interrupt had occurred before the ":catch". That is, discard - * the original exception, replace it by an interrupt exception, - * and don't catch it in this try block. */ - if (!dbg_check_skipped(eap) || !do_intthrow(cstack)) { - /* Terminate the pattern and avoid the 'l' flag in 'cpoptions' - * while compiling it. */ - if (end != NULL) { - save_char = *end; - *end = NUL; - } - save_cpo = p_cpo; - p_cpo = (char_u *)""; - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); - regmatch.rm_ic = FALSE; - if (end != NULL) - *end = save_char; - p_cpo = save_cpo; - if (regmatch.regprog == NULL) - EMSG2(_(e_invarg2), pat); - else { - /* - * Save the value of got_int and reset it. We don't want - * a previous interruption cancel matching, only hitting - * CTRL-C while matching should abort it. - */ - prev_got_int = got_int; - got_int = FALSE; - caught = vim_regexec_nl(®match, current_exception->value, - (colnr_T)0); - got_int |= prev_got_int; - vim_regfree(regmatch.regprog); - } - } - } - - if (caught) { - /* Make this ":catch" clause active and reset did_emsg, got_int, - * and did_throw. Put the exception on the caught stack. */ - cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT; - did_emsg = got_int = did_throw = FALSE; - catch_exception((except_T *)cstack->cs_exception[idx]); - /* It's mandatory that the current exception is stored in the cstack - * so that it can be discarded at the next ":catch", ":finally", or - * ":endtry" or when the catch clause is left by a ":continue", - * ":break", ":return", ":finish", error, interrupt, or another - * exception. */ - if (cstack->cs_exception[cstack->cs_idx] != current_exception) - EMSG(_(e_internal)); - } else { - /* - * If there is a preceding catch clause and it caught the exception, - * finish the exception now. This happens also after errors except - * when this ":catch" was after the ":finally" or not within - * a ":try". Make the try conditional inactive so that the - * following catch clauses are skipped. On an error or interrupt - * after the preceding try block or catch clause was left by - * a ":continue", ":break", ":return", or ":finish", discard the - * pending action. - */ - cleanup_conditionals(cstack, CSF_TRY, TRUE); - } - } - - if (end != NULL) - eap->nextcmd = find_nextcmd(end); -} - -/* - * ":finally" - */ -void ex_finally(exarg_T *eap) -{ - int idx; - int skip = FALSE; - int pending = CSTP_NONE; - struct condstack *cstack = eap->cstack; - - if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) - eap->errmsg = (char_u *)N_("E606: :finally without :try"); - else { - if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { - eap->errmsg = get_end_emsg(cstack); - for (idx = cstack->cs_idx - 1; idx > 0; --idx) - if (cstack->cs_flags[idx] & CSF_TRY) - break; - /* Make this error pending, so that the commands in the following - * finally clause can be executed. This overrules also a pending - * ":continue", ":break", ":return", or ":finish". */ - pending = CSTP_ERROR; - } else - idx = cstack->cs_idx; - - if (cstack->cs_flags[idx] & CSF_FINALLY) { - /* Give up for a multiple ":finally" and ignore it. */ - eap->errmsg = (char_u *)N_("E607: multiple :finally"); - return; - } - rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, - &cstack->cs_looplevel); - - /* - * Don't do something when the corresponding try block never got active - * (because of an inactive surrounding conditional or after an error or - * interrupt or throw) or for a ":finally" without ":try" or a multiple - * ":finally". After every other error (did_emsg or the conditional - * errors detected above) or after an interrupt (got_int) or an - * exception (did_throw), the finally clause must be executed. - */ - skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); - - if (!skip) { - /* When debugging or a breakpoint was encountered, display the - * debug prompt (if not already done). The user then knows that the - * finally clause is executed. */ - if (dbg_check_skipped(eap)) { - /* Handle a ">quit" debug command as if an interrupt had - * occurred before the ":finally". That is, discard the - * original exception and replace it by an interrupt - * exception. */ - (void)do_intthrow(cstack); - } - - /* - * If there is a preceding catch clause and it caught the exception, - * finish the exception now. This happens also after errors except - * when this is a multiple ":finally" or one not within a ":try". - * After an error or interrupt, this also discards a pending - * ":continue", ":break", ":finish", or ":return" from the preceding - * try block or catch clause. - */ - cleanup_conditionals(cstack, CSF_TRY, FALSE); - - /* - * Make did_emsg, got_int, did_throw pending. If set, they overrule - * a pending ":continue", ":break", ":return", or ":finish". Then - * we have particularly to discard a pending return value (as done - * by the call to cleanup_conditionals() above when did_emsg or - * got_int is set). The pending values are restored by the - * ":endtry", except if there is a new error, interrupt, exception, - * ":continue", ":break", ":return", or ":finish" in the following - * finally clause. A missing ":endwhile", ":endfor" or ":endif" - * detected here is treated as if did_emsg and did_throw had - * already been set, respectively in case that the error is not - * converted to an exception, did_throw had already been unset. - * We must not set did_emsg here since that would suppress the - * error message. - */ - if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) { - if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) { - report_discard_pending(CSTP_RETURN, - cstack->cs_rettv[cstack->cs_idx]); - discard_pending_return(cstack->cs_rettv[cstack->cs_idx]); - } - if (pending == CSTP_ERROR && !did_emsg) - pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0; - else - pending |= did_throw ? CSTP_THROW : 0; - pending |= did_emsg ? CSTP_ERROR : 0; - pending |= got_int ? CSTP_INTERRUPT : 0; - cstack->cs_pending[cstack->cs_idx] = pending; - - /* It's mandatory that the current exception is stored in the - * cstack so that it can be rethrown at the ":endtry" or be - * discarded if the finally clause is left by a ":continue", - * ":break", ":return", ":finish", error, interrupt, or another - * exception. When emsg() is called for a missing ":endif" or - * a missing ":endwhile"/":endfor" detected here, the - * exception will be discarded. */ - if (did_throw && cstack->cs_exception[cstack->cs_idx] - != current_exception) - EMSG(_(e_internal)); - } - - /* - * Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg, - * got_int, and did_throw and make the finally clause active. - * This will happen after emsg() has been called for a missing - * ":endif" or a missing ":endwhile"/":endfor" detected here, so - * that the following finally clause will be executed even then. - */ - cstack->cs_lflags |= CSL_HAD_FINA; - } - } -} - -/* - * ":endtry" - */ -void ex_endtry(exarg_T *eap) -{ - int idx; - int skip; - int rethrow = FALSE; - int pending = CSTP_NONE; - void *rettv = NULL; - struct condstack *cstack = eap->cstack; - - if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) - eap->errmsg = (char_u *)N_("E602: :endtry without :try"); - else { - /* - * Don't do something after an error, interrupt or throw in the try - * block, catch clause, or finally clause preceding this ":endtry" or - * when an error or interrupt occurred after a ":continue", ":break", - * ":return", or ":finish" in a try block or catch clause preceding this - * ":endtry" or when the try block never got active (because of an - * inactive surrounding conditional or after an error or interrupt or - * throw) or when there is a surrounding conditional and it has been - * made inactive by a ":continue", ":break", ":return", or ":finish" in - * the finally clause. The latter case need not be tested since then - * anything pending has already been discarded. */ - skip = did_emsg || got_int || did_throw || - !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); - - if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { - eap->errmsg = get_end_emsg(cstack); - /* Find the matching ":try" and report what's missing. */ - idx = cstack->cs_idx; - do - --idx; - while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); - rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, - &cstack->cs_looplevel); - skip = TRUE; - - /* - * If an exception is being thrown, discard it to prevent it from - * being rethrown at the end of this function. It would be - * discarded by the error message, anyway. Resets did_throw. - * This does not affect the script termination due to the error - * since "trylevel" is decremented after emsg() has been called. - */ - if (did_throw) - discard_current_exception(); - } else { - idx = cstack->cs_idx; - - /* - * If we stopped with the exception currently being thrown at this - * try conditional since we didn't know that it doesn't have - * a finally clause, we need to rethrow it after closing the try - * conditional. - */ - if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE) - && !(cstack->cs_flags[idx] & CSF_FINALLY)) - rethrow = TRUE; - } - - /* If there was no finally clause, show the user when debugging or - * a breakpoint was encountered that the end of the try conditional has - * been reached: display the debug prompt (if not already done). Do - * this on normal control flow or when an exception was thrown, but not - * on an interrupt or error not converted to an exception or when - * a ":break", ":continue", ":return", or ":finish" is pending. These - * actions are carried out immediately. - */ - if ((rethrow || (!skip - && !(cstack->cs_flags[idx] & CSF_FINALLY) - && !cstack->cs_pending[idx])) - && dbg_check_skipped(eap)) { - /* Handle a ">quit" debug command as if an interrupt had occurred - * before the ":endtry". That is, throw an interrupt exception and - * set "skip" and "rethrow". */ - if (got_int) { - skip = TRUE; - (void)do_intthrow(cstack); - /* The do_intthrow() call may have reset did_throw or - * cstack->cs_pending[idx].*/ - rethrow = FALSE; - if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) - rethrow = TRUE; - } - } - - /* - * If a ":return" is pending, we need to resume it after closing the - * try conditional; remember the return value. If there was a finally - * clause making an exception pending, we need to rethrow it. Make it - * the exception currently being thrown. - */ - if (!skip) { - pending = cstack->cs_pending[idx]; - cstack->cs_pending[idx] = CSTP_NONE; - if (pending == CSTP_RETURN) - rettv = cstack->cs_rettv[idx]; - else if (pending & CSTP_THROW) - current_exception = cstack->cs_exception[idx]; - } - - /* - * Discard anything pending on an error, interrupt, or throw in the - * finally clause. If there was no ":finally", discard a pending - * ":continue", ":break", ":return", or ":finish" if an error or - * interrupt occurred afterwards, but before the ":endtry" was reached. - * If an exception was caught by the last of the catch clauses and there - * was no finally clause, finish the exception now. This happens also - * after errors except when this ":endtry" is not within a ":try". - * Restore "emsg_silent" if it has been reset by this try conditional. - */ - (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE); - - --cstack->cs_idx; - --cstack->cs_trylevel; - - if (!skip) { - report_resume_pending(pending, - (pending == CSTP_RETURN) ? rettv : - (pending & CSTP_THROW) ? (void *)current_exception : NULL); - switch (pending) { - case CSTP_NONE: - break; - - /* Reactivate a pending ":continue", ":break", ":return", - * ":finish" from the try block or a catch clause of this try - * conditional. This is skipped, if there was an error in an - * (unskipped) conditional command or an interrupt afterwards - * or if the finally clause is present and executed a new error, - * interrupt, throw, ":continue", ":break", ":return", or - * ":finish". */ - case CSTP_CONTINUE: - ex_continue(eap); - break; - case CSTP_BREAK: - ex_break(eap); - break; - case CSTP_RETURN: - do_return(eap, FALSE, FALSE, rettv); - break; - case CSTP_FINISH: - do_finish(eap, FALSE); - break; - - /* When the finally clause was entered due to an error, - * interrupt or throw (as opposed to a ":continue", ":break", - * ":return", or ":finish"), restore the pending values of - * did_emsg, got_int, and did_throw. This is skipped, if there - * was a new error, interrupt, throw, ":continue", ":break", - * ":return", or ":finish". in the finally clause. */ - default: - if (pending & CSTP_ERROR) - did_emsg = TRUE; - if (pending & CSTP_INTERRUPT) - got_int = TRUE; - if (pending & CSTP_THROW) - rethrow = TRUE; - break; - } - } - - if (rethrow) - /* Rethrow the current exception (within this cstack). */ - do_throw(cstack); - } -} - -/* - * enter_cleanup() and leave_cleanup() - * - * Functions to be called before/after invoking a sequence of autocommands for - * cleanup for a failed command. (Failure means here that a call to emsg() - * has been made, an interrupt occurred, or there is an uncaught exception - * from a previous autocommand execution of the same command.) - * - * Call enter_cleanup() with a pointer to a cleanup_T and pass the same - * pointer to leave_cleanup(). The cleanup_T structure stores the pending - * error/interrupt/exception state. - */ - -/* - * This function works a bit like ex_finally() except that there was not - * actually an extra try block around the part that failed and an error or - * interrupt has not (yet) been converted to an exception. This function - * saves the error/interrupt/ exception state and prepares for the call to - * do_cmdline() that is going to be made for the cleanup autocommand - * execution. - */ -void enter_cleanup(cleanup_T *csp) -{ - int pending = CSTP_NONE; - - /* - * Postpone did_emsg, got_int, did_throw. The pending values will be - * restored by leave_cleanup() except if there was an aborting error, - * interrupt, or uncaught exception after this function ends. - */ - if (did_emsg || got_int || did_throw || need_rethrow) { - csp->pending = (did_emsg ? CSTP_ERROR : 0) - | (got_int ? CSTP_INTERRUPT : 0) - | (did_throw ? CSTP_THROW : 0) - | (need_rethrow ? CSTP_THROW : 0); - - /* If we are currently throwing an exception (did_throw), save it as - * well. On an error not yet converted to an exception, update - * "force_abort" and reset "cause_abort" (as do_errthrow() would do). - * This is needed for the do_cmdline() call that is going to be made - * for autocommand execution. We need not save *msg_list because - * there is an extra instance for every call of do_cmdline(), anyway. - */ - if (did_throw || need_rethrow) - csp->exception = current_exception; - else { - csp->exception = NULL; - if (did_emsg) { - force_abort |= cause_abort; - cause_abort = FALSE; - } - } - did_emsg = got_int = did_throw = need_rethrow = FALSE; - - /* Report if required by the 'verbose' option or when debugging. */ - report_make_pending(pending, csp->exception); - } else { - csp->pending = CSTP_NONE; - csp->exception = NULL; - } -} - -/* - * See comment above enter_cleanup() for how this function is used. - * - * This function is a bit like ex_endtry() except that there was not actually - * an extra try block around the part that failed and an error or interrupt - * had not (yet) been converted to an exception when the cleanup autocommand - * sequence was invoked. - * - * This function has to be called with the address of the cleanup_T structure - * filled by enter_cleanup() as an argument; it restores the error/interrupt/ - * exception state saved by that function - except there was an aborting - * error, an interrupt or an uncaught exception during execution of the - * cleanup autocommands. In the latter case, the saved error/interrupt/ - * exception state is discarded. - */ -void leave_cleanup(cleanup_T *csp) -{ - int pending = csp->pending; - - if (pending == CSTP_NONE) /* nothing to do */ - return; - - /* If there was an aborting error, an interrupt, or an uncaught exception - * after the corresponding call to enter_cleanup(), discard what has been - * made pending by it. Report this to the user if required by the - * 'verbose' option or when debugging. */ - if (aborting() || need_rethrow) { - if (pending & CSTP_THROW) - /* Cancel the pending exception (includes report). */ - discard_exception((except_T *)csp->exception, FALSE); - else - report_discard_pending(pending, NULL); - - /* If an error was about to be converted to an exception when - * enter_cleanup() was called, free the message list. */ - if (msg_list != NULL) - free_global_msglist(); - } - /* - * If there was no new error, interrupt, or throw between the calls - * to enter_cleanup() and leave_cleanup(), restore the pending - * error/interrupt/exception state. - */ - else { - /* - * If there was an exception being thrown when enter_cleanup() was - * called, we need to rethrow it. Make it the exception currently - * being thrown. - */ - if (pending & CSTP_THROW) - current_exception = csp->exception; - - /* - * If an error was about to be converted to an exception when - * enter_cleanup() was called, let "cause_abort" take the part of - * "force_abort" (as done by cause_errthrow()). - */ - else if (pending & CSTP_ERROR) { - cause_abort = force_abort; - force_abort = FALSE; - } - - /* - * Restore the pending values of did_emsg, got_int, and did_throw. - */ - if (pending & CSTP_ERROR) - did_emsg = TRUE; - if (pending & CSTP_INTERRUPT) - got_int = TRUE; - if (pending & CSTP_THROW) - need_rethrow = TRUE; /* did_throw will be set by do_one_cmd() */ - - /* Report if required by the 'verbose' option or when debugging. */ - report_resume_pending(pending, - (pending & CSTP_THROW) ? (void *)current_exception : NULL); - } -} - - -/* - * Make conditionals inactive and discard what's pending in finally clauses - * until the conditional type searched for or a try conditional not in its - * finally clause is reached. If this is in an active catch clause, finish - * the caught exception. - * Return the cstack index where the search stopped. - * Values used for "searched_cond" are (CSF_WHILE | CSF_FOR) or CSF_TRY or 0, - * the latter meaning the innermost try conditional not in its finally clause. - * "inclusive" tells whether the conditional searched for should be made - * inactive itself (a try conditional not in its finally clause possibly find - * before is always made inactive). If "inclusive" is TRUE and - * "searched_cond" is CSF_TRY|CSF_SILENT, the saved former value of - * "emsg_silent", if reset when the try conditional finally reached was - * entered, is restored (used by ex_endtry()). This is normally done only - * when such a try conditional is left. - */ -int cleanup_conditionals(struct condstack *cstack, int searched_cond, int inclusive) -{ - int idx; - int stop = FALSE; - - for (idx = cstack->cs_idx; idx >= 0; --idx) { - if (cstack->cs_flags[idx] & CSF_TRY) { - /* - * Discard anything pending in a finally clause and continue the - * search. There may also be a pending ":continue", ":break", - * ":return", or ":finish" before the finally clause. We must not - * discard it, unless an error or interrupt occurred afterwards. - */ - if (did_emsg || got_int || (cstack->cs_flags[idx] & CSF_FINALLY)) { - switch (cstack->cs_pending[idx]) { - case CSTP_NONE: - break; - - case CSTP_CONTINUE: - case CSTP_BREAK: - case CSTP_FINISH: - report_discard_pending(cstack->cs_pending[idx], NULL); - cstack->cs_pending[idx] = CSTP_NONE; - break; - - case CSTP_RETURN: - report_discard_pending(CSTP_RETURN, - cstack->cs_rettv[idx]); - discard_pending_return(cstack->cs_rettv[idx]); - cstack->cs_pending[idx] = CSTP_NONE; - break; - - default: - if (cstack->cs_flags[idx] & CSF_FINALLY) { - if (cstack->cs_pending[idx] & CSTP_THROW) { - /* Cancel the pending exception. This is in the - * finally clause, so that the stack of the - * caught exceptions is not involved. */ - discard_exception((except_T *) - cstack->cs_exception[idx], - FALSE); - } else - report_discard_pending(cstack->cs_pending[idx], - NULL); - cstack->cs_pending[idx] = CSTP_NONE; - } - break; - } - } - - /* - * Stop at a try conditional not in its finally clause. If this try - * conditional is in an active catch clause, finish the caught - * exception. - */ - if (!(cstack->cs_flags[idx] & CSF_FINALLY)) { - if ((cstack->cs_flags[idx] & CSF_ACTIVE) - && (cstack->cs_flags[idx] & CSF_CAUGHT)) - finish_exception((except_T *)cstack->cs_exception[idx]); - /* Stop at this try conditional - except the try block never - * got active (because of an inactive surrounding conditional - * or when the ":try" appeared after an error or interrupt or - * throw). */ - if (cstack->cs_flags[idx] & CSF_TRUE) { - if (searched_cond == 0 && !inclusive) - break; - stop = TRUE; - } - } - } - - /* Stop on the searched conditional type (even when the surrounding - * conditional is not active or something has been made pending). - * If "inclusive" is TRUE and "searched_cond" is CSF_TRY|CSF_SILENT, - * check first whether "emsg_silent" needs to be restored. */ - if (cstack->cs_flags[idx] & searched_cond) { - if (!inclusive) - break; - stop = TRUE; - } - cstack->cs_flags[idx] &= ~CSF_ACTIVE; - if (stop && searched_cond != (CSF_TRY | CSF_SILENT)) - break; - - /* - * When leaving a try conditional that reset "emsg_silent" on its - * entry after saving the original value, restore that value here and - * free the memory used to store it. - */ - if ((cstack->cs_flags[idx] & CSF_TRY) - && (cstack->cs_flags[idx] & CSF_SILENT)) { - eslist_T *elem; - - elem = cstack->cs_emsg_silent_list; - cstack->cs_emsg_silent_list = elem->next; - emsg_silent = elem->saved_emsg_silent; - free(elem); - cstack->cs_flags[idx] &= ~CSF_SILENT; - } - if (stop) - break; - } - return idx; -} - -/* - * Return an appropriate error message for a missing endwhile/endfor/endif. - */ -static char_u *get_end_emsg(struct condstack *cstack) -{ - if (cstack->cs_flags[cstack->cs_idx] & CSF_WHILE) - return e_endwhile; - if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) - return e_endfor; - return e_endif; -} - - -/* - * Rewind conditionals until index "idx" is reached. "cond_type" and - * "cond_level" specify a conditional type and the address of a level variable - * which is to be decremented with each skipped conditional of the specified - * type. - * Also free "for info" structures where needed. - */ -void rewind_conditionals(struct condstack *cstack, int idx, int cond_type, int *cond_level) -{ - while (cstack->cs_idx > idx) { - if (cstack->cs_flags[cstack->cs_idx] & cond_type) - --*cond_level; - if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) - free_for_info(cstack->cs_forinfo[cstack->cs_idx]); - --cstack->cs_idx; - } -} - -/* - * ":endfunction" when not after a ":function" - */ -void ex_endfunction(exarg_T *eap) -{ - EMSG(_("E193: :endfunction not inside a function")); -} - -/* - * Return TRUE if the string "p" looks like a ":while" or ":for" command. - */ -int has_loop_cmd(char_u *p) -{ - int len; - - /* skip modifiers, white space and ':' */ - for (;; ) { - while (*p == ' ' || *p == '\t' || *p == ':') - ++p; - len = modifier_len(p); - if (len == 0) - break; - p += len; - } - if ((p[0] == 'w' && p[1] == 'h') - || (p[0] == 'f' && p[1] == 'o' && p[2] == 'r')) - return TRUE; - return FALSE; -} - diff --git a/src/ex_eval.h b/src/ex_eval.h deleted file mode 100644 index 4fcf4760ff..0000000000 --- a/src/ex_eval.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef NEOVIM_EX_EVAL_H -#define NEOVIM_EX_EVAL_H - -/* - * A list used for saving values of "emsg_silent". Used by ex_try() to save the - * value of "emsg_silent" if it was non-zero. When this is done, the CSF_SILENT - * flag below is set. - */ - -typedef struct eslist_elem eslist_T; -struct eslist_elem { - int saved_emsg_silent; /* saved value of "emsg_silent" */ - eslist_T *next; /* next element on the list */ -}; - -/* - * For conditional commands a stack is kept of nested conditionals. - * When cs_idx < 0, there is no conditional command. - */ -#define CSTACK_LEN 50 - -struct condstack { - short cs_flags[CSTACK_LEN]; /* CSF_ flags */ - char cs_pending[CSTACK_LEN]; /* CSTP_: what's pending in ":finally"*/ - union { - void *csp_rv[CSTACK_LEN]; /* return typeval for pending return */ - void *csp_ex[CSTACK_LEN]; /* exception for pending throw */ - } cs_pend; - void *cs_forinfo[CSTACK_LEN]; /* info used by ":for" */ - int cs_line[CSTACK_LEN]; /* line nr of ":while"/":for" line */ - int cs_idx; /* current entry, or -1 if none */ - int cs_looplevel; /* nr of nested ":while"s and ":for"s */ - int cs_trylevel; /* nr of nested ":try"s */ - eslist_T *cs_emsg_silent_list; /* saved values of "emsg_silent" */ - char cs_lflags; /* loop flags: CSL_ flags */ -}; -# define cs_rettv cs_pend.csp_rv -# define cs_exception cs_pend.csp_ex - -/* There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if" - * was used. */ -# define CSF_TRUE 0x0001 /* condition was TRUE */ -# define CSF_ACTIVE 0x0002 /* current state is active */ -# define CSF_ELSE 0x0004 /* ":else" has been passed */ -# define CSF_WHILE 0x0008 /* is a ":while" */ -# define CSF_FOR 0x0010 /* is a ":for" */ - -# define CSF_TRY 0x0100 /* is a ":try" */ -# define CSF_FINALLY 0x0200 /* ":finally" has been passed */ -# define CSF_THROWN 0x0400 /* exception thrown to this try conditional */ -# define CSF_CAUGHT 0x0800 /* exception caught by this try conditional */ -# define CSF_SILENT 0x1000 /* "emsg_silent" reset by ":try" */ -/* Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset - * (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. */ - -/* - * What's pending for being reactivated at the ":endtry" of this try - * conditional: - */ -# define CSTP_NONE 0 /* nothing pending in ":finally" clause */ -# define CSTP_ERROR 1 /* an error is pending */ -# define CSTP_INTERRUPT 2 /* an interrupt is pending */ -# define CSTP_THROW 4 /* a throw is pending */ -# define CSTP_BREAK 8 /* ":break" is pending */ -# define CSTP_CONTINUE 16 /* ":continue" is pending */ -# define CSTP_RETURN 24 /* ":return" is pending */ -# define CSTP_FINISH 32 /* ":finish" is pending */ - -/* - * Flags for the cs_lflags item in struct condstack. - */ -# define CSL_HAD_LOOP 1 /* just found ":while" or ":for" */ -# define CSL_HAD_ENDLOOP 2 /* just found ":endwhile" or ":endfor" */ -# define CSL_HAD_CONT 4 /* just found ":continue" */ -# define CSL_HAD_FINA 8 /* just found ":finally" */ - -/* - * A list of error messages that can be converted to an exception. "throw_msg" - * is only set in the first element of the list. Usually, it points to the - * original message stored in that element, but sometimes it points to a later - * message in the list. See cause_errthrow() below. - */ -struct msglist { - char_u *msg; /* original message */ - char_u *throw_msg; /* msg to throw: usually original one */ - struct msglist *next; /* next of several messages in a row */ -}; - -/* - * Structure describing an exception. - * (don't use "struct exception", it's used by the math library). - */ -typedef struct vim_exception except_T; -struct vim_exception { - int type; /* exception type */ - char_u *value; /* exception value */ - struct msglist *messages; /* message(s) causing error exception */ - char_u *throw_name; /* name of the throw point */ - linenr_T throw_lnum; /* line number of the throw point */ - except_T *caught; /* next exception on the caught stack */ -}; - -/* - * The exception types. - */ -#define ET_USER 0 /* exception caused by ":throw" command */ -#define ET_ERROR 1 /* error exception */ -#define ET_INTERRUPT 2 /* interrupt exception triggered by Ctrl-C */ - -/* - * Structure to save the error/interrupt/exception state between calls to - * enter_cleanup() and leave_cleanup(). Must be allocated as an automatic - * variable by the (common) caller of these functions. - */ -typedef struct cleanup_stuff cleanup_T; -struct cleanup_stuff { - int pending; /* error/interrupt/exception state */ - except_T *exception; /* exception value */ -}; - -/* ex_eval.c */ -int aborting(void); -void update_force_abort(void); -int should_abort(int retcode); -int aborted_in_try(void); -int cause_errthrow(char_u *mesg, int severe, int *ignore); -void free_global_msglist(void); -void do_errthrow(struct condstack *cstack, char_u *cmdname); -int do_intthrow(struct condstack *cstack); -char_u *get_exception_string(void *value, int type, char_u *cmdname, - int *should_free); -void discard_current_exception(void); -void report_make_pending(int pending, void *value); -void report_resume_pending(int pending, void *value); -void report_discard_pending(int pending, void *value); -void ex_if(exarg_T *eap); -void ex_endif(exarg_T *eap); -void ex_else(exarg_T *eap); -void ex_while(exarg_T *eap); -void ex_continue(exarg_T *eap); -void ex_break(exarg_T *eap); -void ex_endwhile(exarg_T *eap); -void ex_throw(exarg_T *eap); -void do_throw(struct condstack *cstack); -void ex_try(exarg_T *eap); -void ex_catch(exarg_T *eap); -void ex_finally(exarg_T *eap); -void ex_endtry(exarg_T *eap); -void enter_cleanup(cleanup_T *csp); -void leave_cleanup(cleanup_T *csp); -int cleanup_conditionals(struct condstack *cstack, int searched_cond, - int inclusive); -void rewind_conditionals(struct condstack *cstack, int idx, - int cond_type, - int *cond_level); -void ex_endfunction(exarg_T *eap); -int has_loop_cmd(char_u *p); - -#endif /* NEOVIM_EX_EVAL_H */ diff --git a/src/ex_getln.c b/src/ex_getln.c deleted file mode 100644 index 8e2d7cc9ae..0000000000 --- a/src/ex_getln.c +++ /dev/null @@ -1,5443 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * ex_getln.c: Functions for entering and editing an Ex command line. - */ - -#include -#include - -#include "vim.h" -#include "arabic.h" -#include "ex_getln.h" -#include "buffer.h" -#include "charset.h" -#include "digraph.h" -#include "edit.h" -#include "eval.h" -#include "ex_cmds.h" -#include "ex_cmds2.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "farsi.h" -#include "fileio.h" -#include "getchar.h" -#include "if_cscope.h" -#include "indent.h" -#include "main.h" -#include "mbyte.h" -#include "memline.h" -#include "menu.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "memory.h" -#include "cursor_shape.h" -#include "keymap.h" -#include "garray.h" -#include "move.h" -#include "ops.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "regexp.h" -#include "screen.h" -#include "search.h" -#include "syntax.h" -#include "tag.h" -#include "term.h" -#include "window.h" -#include "ui.h" -#include "os/os.h" -#include "os/event.h" - -/* - * Variables shared between getcmdline(), redrawcmdline() and others. - * These need to be saved when using CTRL-R |, that's why they are in a - * structure. - */ -struct cmdline_info { - char_u *cmdbuff; /* pointer to command line buffer */ - int cmdbufflen; /* length of cmdbuff */ - int cmdlen; /* number of chars in command line */ - int cmdpos; /* current cursor position */ - int cmdspos; /* cursor column on screen */ - int cmdfirstc; /* ':', '/', '?', '=', '>' or NUL */ - int cmdindent; /* number of spaces before cmdline */ - char_u *cmdprompt; /* message in front of cmdline */ - int cmdattr; /* attributes for prompt */ - int overstrike; /* Typing mode on the command line. Shared by - getcmdline() and put_on_cmdline(). */ - expand_T *xpc; /* struct being used for expansion, xp_pattern - may point into cmdbuff */ - int xp_context; /* type of expansion */ - char_u *xp_arg; /* user-defined expansion arg */ - int input_fn; /* when TRUE Invoked for input() function */ -}; - -/* The current cmdline_info. It is initialized in getcmdline() and after that - * used by other functions. When invoking getcmdline() recursively it needs - * to be saved with save_cmdline() and restored with restore_cmdline(). - * TODO: make it local to getcmdline() and pass it around. */ -static struct cmdline_info ccline; - -static int cmd_showtail; /* Only show path tail in lists ? */ - -static int new_cmdpos; /* position set by set_cmdline_pos() */ - -typedef struct hist_entry { - int hisnum; /* identifying number */ - int viminfo; /* when TRUE hisstr comes from viminfo */ - char_u *hisstr; /* actual entry, separator char after the NUL */ -} histentry_T; - -static histentry_T *(history[HIST_COUNT]) = {NULL, NULL, NULL, NULL, NULL}; -static int hisidx[HIST_COUNT] = {-1, -1, -1, -1, -1}; /* lastused entry */ -static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0}; -/* identifying (unique) number of newest history entry */ -static int hislen = 0; /* actual length of history tables */ - -static int hist_char2type(int c); - -static int in_history(int, char_u *, int, int, int); -static int calc_hist_idx(int histype, int num); - -static int cmd_hkmap = 0; /* Hebrew mapping during command line */ - -static int cmd_fkmap = 0; /* Farsi mapping during command line */ - -static int cmdline_charsize(int idx); -static void set_cmdspos(void); -static void set_cmdspos_cursor(void); -static void correct_cmdspos(int idx, int cells); -static void alloc_cmdbuff(int len); -static int realloc_cmdbuff(int len); -static void draw_cmdline(int start, int len); -static void save_cmdline(struct cmdline_info *ccp); -static void restore_cmdline(struct cmdline_info *ccp); -static int cmdline_paste(int regname, int literally, int remcr); -static void cmdline_del(int from); -static void redrawcmdprompt(void); -static void cursorcmd(void); -static int ccheck_abbr(int); -static int nextwild(expand_T *xp, int type, int options, int escape); -static void escape_fname(char_u **pp); -static int showmatches(expand_T *xp, int wildmenu); -static void set_expand_context(expand_T *xp); -static int ExpandFromContext(expand_T *xp, char_u *, int *, char_u ***, int); -static int expand_showtail(expand_T *xp); -static int expand_shellcmd(char_u *filepat, int *num_file, - char_u ***file, - int flagsarg); -static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, - char *dirname[]); -static char_u *get_history_arg(expand_T *xp, int idx); -static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, - int *num_file, - char_u ***file); -static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file); -static void clear_hist_entry(histentry_T *hisptr); - -static int ex_window(void); - -static int sort_func_compare(const void *s1, const void *s2); - -/* - * getcmdline() - accept a command line starting with firstc. - * - * firstc == ':' get ":" command line. - * firstc == '/' or '?' get search pattern - * firstc == '=' get expression - * firstc == '@' get text for input() function - * firstc == '>' get text for debug mode - * firstc == NUL get text for :insert command - * firstc == -1 like NUL, and break on CTRL-C - * - * The line is collected in ccline.cmdbuff, which is reallocated to fit the - * command line. - * - * Careful: getcmdline() can be called recursively! - * - * Return pointer to allocated string if there is a commandline, NULL - * otherwise. - */ -char_u * -getcmdline ( - int firstc, - long count, /* only used for incremental search */ - int indent /* indent for inside conditionals */ -) -{ - int c; - int i; - int j; - int gotesc = FALSE; /* TRUE when just typed */ - int do_abbr; /* when TRUE check for abbr. */ - char_u *lookfor = NULL; /* string to match */ - int hiscnt; /* current history line in use */ - int histype; /* history type to be used */ - pos_T old_cursor; - colnr_T old_curswant; - colnr_T old_leftcol; - linenr_T old_topline; - int old_topfill; - linenr_T old_botline; - int did_incsearch = FALSE; - int incsearch_postponed = FALSE; - int did_wild_list = FALSE; /* did wild_list() recently */ - int wim_index = 0; /* index in wim_flags[] */ - int res; - int save_msg_scroll = msg_scroll; - int save_State = State; /* remember State when called */ - int some_key_typed = FALSE; /* one of the keys was typed */ - /* mouse drag and release events are ignored, unless they are - * preceded with a mouse down event */ - int ignore_drag_release = TRUE; - int break_ctrl_c = FALSE; - expand_T xpc; - long *b_im_ptr = NULL; - /* Everything that may work recursively should save and restore the - * current command line in save_ccline. That includes update_screen(), a - * custom status line may invoke ":normal". */ - struct cmdline_info save_ccline; - - if (firstc == -1) { - firstc = NUL; - break_ctrl_c = TRUE; - } - /* start without Hebrew mapping for a command line */ - if (firstc == ':' || firstc == '=' || firstc == '>') - cmd_hkmap = 0; - - ccline.overstrike = FALSE; /* always start in insert mode */ - old_cursor = curwin->w_cursor; /* needs to be restored later */ - old_curswant = curwin->w_curswant; - old_leftcol = curwin->w_leftcol; - old_topline = curwin->w_topline; - old_topfill = curwin->w_topfill; - old_botline = curwin->w_botline; - - /* - * set some variables for redrawcmd() - */ - ccline.cmdfirstc = (firstc == '@' ? 0 : firstc); - ccline.cmdindent = (firstc > 0 ? indent : 0); - - /* alloc initial ccline.cmdbuff */ - alloc_cmdbuff(exmode_active ? 250 : indent + 1); - if (ccline.cmdbuff == NULL) - return NULL; /* out of memory */ - ccline.cmdlen = ccline.cmdpos = 0; - ccline.cmdbuff[0] = NUL; - - /* autoindent for :insert and :append */ - if (firstc <= 0) { - copy_spaces(ccline.cmdbuff, indent); - ccline.cmdbuff[indent] = NUL; - ccline.cmdpos = indent; - ccline.cmdspos = indent; - ccline.cmdlen = indent; - } - - ExpandInit(&xpc); - ccline.xpc = &xpc; - - if (curwin->w_p_rl && *curwin->w_p_rlc == 's' - && (firstc == '/' || firstc == '?')) - cmdmsg_rl = TRUE; - else - cmdmsg_rl = FALSE; - - redir_off = TRUE; /* don't redirect the typed command */ - if (!cmd_silent) { - i = msg_scrolled; - msg_scrolled = 0; /* avoid wait_return message */ - gotocmdline(TRUE); - msg_scrolled += i; - redrawcmdprompt(); /* draw prompt or indent */ - set_cmdspos(); - } - xpc.xp_context = EXPAND_NOTHING; - xpc.xp_backslash = XP_BS_NONE; -#ifndef BACKSLASH_IN_FILENAME - xpc.xp_shell = FALSE; -#endif - - if (ccline.input_fn) { - xpc.xp_context = ccline.xp_context; - xpc.xp_pattern = ccline.cmdbuff; - xpc.xp_arg = ccline.xp_arg; - } - - /* - * Avoid scrolling when called by a recursive do_cmdline(), e.g. when - * doing ":@0" when register 0 doesn't contain a CR. - */ - msg_scroll = FALSE; - - State = CMDLINE; - - if (firstc == '/' || firstc == '?' || firstc == '@') { - /* Use ":lmap" mappings for search pattern and input(). */ - if (curbuf->b_p_imsearch == B_IMODE_USE_INSERT) - b_im_ptr = &curbuf->b_p_iminsert; - else - b_im_ptr = &curbuf->b_p_imsearch; - if (*b_im_ptr == B_IMODE_LMAP) - State |= LANGMAP; -#ifdef USE_IM_CONTROL - im_set_active(*b_im_ptr == B_IMODE_IM); -#endif - } -#ifdef USE_IM_CONTROL - else if (p_imcmdline) - im_set_active(TRUE); -#endif - - setmouse(); - ui_cursor_shape(); /* may show different cursor shape */ - - /* When inside an autocommand for writing "exiting" may be set and - * terminal mode set to cooked. Need to set raw mode here then. */ - settmode(TMODE_RAW); - - init_history(); - hiscnt = hislen; /* set hiscnt to impossible history value */ - histype = hist_char2type(firstc); - - do_digraph(-1); /* init digraph typeahead */ - - /* - * Collect the command string, handling editing keys. - */ - for (;; ) { - redir_off = TRUE; /* Don't redirect the typed command. - Repeated, because a ":redir" inside - completion may switch it on. */ -#ifdef USE_ON_FLY_SCROLL - dont_scroll = FALSE; /* allow scrolling here */ -#endif - quit_more = FALSE; /* reset after CTRL-D which had a more-prompt */ - - cursorcmd(); /* set the cursor on the right spot */ - - /* Get a character. Ignore K_IGNORE, it should not do anything, such - * as stop completion. */ - do { - c = safe_vgetc(); - } while (c == K_IGNORE); - - if (KeyTyped) { - some_key_typed = TRUE; - if (cmd_hkmap) - c = hkmap(c); - if (cmd_fkmap) - c = cmdl_fkmap(c); - if (cmdmsg_rl && !KeyStuffed) { - /* Invert horizontal movements and operations. Only when - * typed by the user directly, not when the result of a - * mapping. */ - switch (c) { - case K_RIGHT: c = K_LEFT; break; - case K_S_RIGHT: c = K_S_LEFT; break; - case K_C_RIGHT: c = K_C_LEFT; break; - case K_LEFT: c = K_RIGHT; break; - case K_S_LEFT: c = K_S_RIGHT; break; - case K_C_LEFT: c = K_C_RIGHT; break; - } - } - } - - /* - * Ignore got_int when CTRL-C was typed here. - * Don't ignore it in :global, we really need to break then, e.g., for - * ":g/pat/normal /pat" (without the ). - * Don't ignore it for the input() function. - */ - if ((c == Ctrl_C -#ifdef UNIX - || c == intr_char -#endif - ) - && firstc != '@' - && !break_ctrl_c - && !global_busy) - got_int = FALSE; - - /* free old command line when finished moving around in the history - * list */ - if (lookfor != NULL - && c != K_S_DOWN && c != K_S_UP - && c != K_DOWN && c != K_UP - && c != K_PAGEDOWN && c != K_PAGEUP - && c != K_KPAGEDOWN && c != K_KPAGEUP - && c != K_LEFT && c != K_RIGHT - && (xpc.xp_numfiles > 0 || (c != Ctrl_P && c != Ctrl_N))) { - free(lookfor); - lookfor = NULL; - } - - /* - * When there are matching completions to select works like - * CTRL-P (unless 'wc' is ). - */ - if (c != p_wc && c == K_S_TAB && xpc.xp_numfiles > 0) - c = Ctrl_P; - - /* Special translations for 'wildmenu' */ - if (did_wild_list && p_wmnu) { - if (c == K_LEFT) - c = Ctrl_P; - else if (c == K_RIGHT) - c = Ctrl_N; - } - /* Hitting CR after "emenu Name.": complete submenu */ - if (xpc.xp_context == EXPAND_MENUNAMES && p_wmnu - && ccline.cmdpos > 1 - && ccline.cmdbuff[ccline.cmdpos - 1] == '.' - && ccline.cmdbuff[ccline.cmdpos - 2] != '\\' - && (c == '\n' || c == '\r' || c == K_KENTER)) - c = K_DOWN; - - /* free expanded names when finished walking through matches */ - if (xpc.xp_numfiles != -1 - && !(c == p_wc && KeyTyped) && c != p_wcm - && c != Ctrl_N && c != Ctrl_P && c != Ctrl_A - && c != Ctrl_L) { - (void)ExpandOne(&xpc, NULL, NULL, 0, WILD_FREE); - did_wild_list = FALSE; - if (!p_wmnu || (c != K_UP && c != K_DOWN)) - xpc.xp_context = EXPAND_NOTHING; - wim_index = 0; - if (p_wmnu && wild_menu_showing != 0) { - int skt = KeyTyped; - int old_RedrawingDisabled = RedrawingDisabled; - - if (ccline.input_fn) - RedrawingDisabled = 0; - - if (wild_menu_showing == WM_SCROLLED) { - /* Entered command line, move it up */ - cmdline_row--; - redrawcmd(); - } else if (save_p_ls != -1) { - /* restore 'laststatus' and 'winminheight' */ - p_ls = save_p_ls; - p_wmh = save_p_wmh; - last_status(FALSE); - save_cmdline(&save_ccline); - update_screen(VALID); /* redraw the screen NOW */ - restore_cmdline(&save_ccline); - redrawcmd(); - save_p_ls = -1; - } else { - win_redraw_last_status(topframe); - redraw_statuslines(); - } - KeyTyped = skt; - wild_menu_showing = 0; - if (ccline.input_fn) - RedrawingDisabled = old_RedrawingDisabled; - } - } - - /* Special translations for 'wildmenu' */ - if (xpc.xp_context == EXPAND_MENUNAMES && p_wmnu) { - /* Hitting after "emenu Name.": complete submenu */ - if (c == K_DOWN && ccline.cmdpos > 0 - && ccline.cmdbuff[ccline.cmdpos - 1] == '.') - c = p_wc; - else if (c == K_UP) { - /* Hitting : Remove one submenu name in front of the - * cursor */ - int found = FALSE; - - j = (int)(xpc.xp_pattern - ccline.cmdbuff); - i = 0; - while (--j > 0) { - /* check for start of menu name */ - if (ccline.cmdbuff[j] == ' ' - && ccline.cmdbuff[j - 1] != '\\') { - i = j + 1; - break; - } - /* check for start of submenu name */ - if (ccline.cmdbuff[j] == '.' - && ccline.cmdbuff[j - 1] != '\\') { - if (found) { - i = j + 1; - break; - } else - found = TRUE; - } - } - if (i > 0) - cmdline_del(i); - c = p_wc; - xpc.xp_context = EXPAND_NOTHING; - } - } - if ((xpc.xp_context == EXPAND_FILES - || xpc.xp_context == EXPAND_DIRECTORIES - || xpc.xp_context == EXPAND_SHELLCMD) && p_wmnu) { - char_u upseg[5]; - - upseg[0] = PATHSEP; - upseg[1] = '.'; - upseg[2] = '.'; - upseg[3] = PATHSEP; - upseg[4] = NUL; - - if (c == K_DOWN - && ccline.cmdpos > 0 - && ccline.cmdbuff[ccline.cmdpos - 1] == PATHSEP - && (ccline.cmdpos < 3 - || ccline.cmdbuff[ccline.cmdpos - 2] != '.' - || ccline.cmdbuff[ccline.cmdpos - 3] != '.')) { - /* go down a directory */ - c = p_wc; - } else if (STRNCMP(xpc.xp_pattern, upseg + 1, 3) == 0 && c == K_DOWN) { - /* If in a direct ancestor, strip off one ../ to go down */ - int found = FALSE; - - j = ccline.cmdpos; - i = (int)(xpc.xp_pattern - ccline.cmdbuff); - while (--j > i) { - if (has_mbyte) - j -= (*mb_head_off)(ccline.cmdbuff, ccline.cmdbuff + j); - if (vim_ispathsep(ccline.cmdbuff[j])) { - found = TRUE; - break; - } - } - if (found - && ccline.cmdbuff[j - 1] == '.' - && ccline.cmdbuff[j - 2] == '.' - && (vim_ispathsep(ccline.cmdbuff[j - 3]) || j == i + 2)) { - cmdline_del(j - 2); - c = p_wc; - } - } else if (c == K_UP) { - /* go up a directory */ - int found = FALSE; - - j = ccline.cmdpos - 1; - i = (int)(xpc.xp_pattern - ccline.cmdbuff); - while (--j > i) { - if (has_mbyte) - j -= (*mb_head_off)(ccline.cmdbuff, ccline.cmdbuff + j); - if (vim_ispathsep(ccline.cmdbuff[j]) -#ifdef BACKSLASH_IN_FILENAME - && vim_strchr(" *?[{`$%#", ccline.cmdbuff[j + 1]) - == NULL -#endif - ) { - if (found) { - i = j + 1; - break; - } else - found = TRUE; - } - } - - if (!found) - j = i; - else if (STRNCMP(ccline.cmdbuff + j, upseg, 4) == 0) - j += 4; - else if (STRNCMP(ccline.cmdbuff + j, upseg + 1, 3) == 0 - && j == i) - j += 3; - else - j = 0; - if (j > 0) { - /* TODO this is only for DOS/UNIX systems - need to put in - * machine-specific stuff here and in upseg init */ - cmdline_del(j); - put_on_cmdline(upseg + 1, 3, FALSE); - } else if (ccline.cmdpos > i) - cmdline_del(i); - - /* Now complete in the new directory. Set KeyTyped in case the - * Up key came from a mapping. */ - c = p_wc; - KeyTyped = TRUE; - } - } - - - /* CTRL-\ CTRL-N goes to Normal mode, CTRL-\ CTRL-G goes to Insert - * mode when 'insertmode' is set, CTRL-\ e prompts for an expression. */ - if (c == Ctrl_BSL) { - ++no_mapping; - ++allow_keys; - c = plain_vgetc(); - --no_mapping; - --allow_keys; - /* CTRL-\ e doesn't work when obtaining an expression, unless it - * is in a mapping. */ - if (c != Ctrl_N && c != Ctrl_G && (c != 'e' - || (ccline.cmdfirstc == '=' && - KeyTyped))) { - vungetc(c); - c = Ctrl_BSL; - } else if (c == 'e') { - char_u *p = NULL; - int len; - - /* - * Replace the command line with the result of an expression. - * Need to save and restore the current command line, to be - * able to enter a new one... - */ - if (ccline.cmdpos == ccline.cmdlen) - new_cmdpos = 99999; /* keep it at the end */ - else - new_cmdpos = ccline.cmdpos; - - save_cmdline(&save_ccline); - c = get_expr_register(); - restore_cmdline(&save_ccline); - if (c == '=') { - /* Need to save and restore ccline. And set "textlock" - * to avoid nasty things like going to another buffer when - * evaluating an expression. */ - save_cmdline(&save_ccline); - ++textlock; - p = get_expr_line(); - --textlock; - restore_cmdline(&save_ccline); - - if (p != NULL) { - len = (int)STRLEN(p); - if (realloc_cmdbuff(len + 1) == OK) { - ccline.cmdlen = len; - STRCPY(ccline.cmdbuff, p); - free(p); - - /* Restore the cursor or use the position set with - * set_cmdline_pos(). */ - if (new_cmdpos > ccline.cmdlen) - ccline.cmdpos = ccline.cmdlen; - else - ccline.cmdpos = new_cmdpos; - - KeyTyped = FALSE; /* Don't do p_wc completion. */ - redrawcmd(); - goto cmdline_changed; - } - } - } - beep_flush(); - got_int = FALSE; /* don't abandon the command line */ - did_emsg = FALSE; - emsg_on_display = FALSE; - redrawcmd(); - goto cmdline_not_changed; - } else { - if (c == Ctrl_G && p_im && restart_edit == 0) - restart_edit = 'a'; - gotesc = TRUE; /* will free ccline.cmdbuff after putting it - in history */ - goto returncmd; /* back to Normal mode */ - } - } - - if (c == cedit_key || c == K_CMDWIN) { - /* - * Open a window to edit the command line (and history). - */ - c = ex_window(); - some_key_typed = TRUE; - } else - c = do_digraph(c); - - if (c == '\n' || c == '\r' || c == K_KENTER || (c == ESC - && (!KeyTyped || - vim_strchr(p_cpo, - CPO_ESC) != - NULL))) { - /* In Ex mode a backslash escapes a newline. */ - if (exmode_active - && c != ESC - && ccline.cmdpos == ccline.cmdlen - && ccline.cmdpos > 0 - && ccline.cmdbuff[ccline.cmdpos - 1] == '\\') { - if (c == K_KENTER) - c = '\n'; - } else { - gotesc = FALSE; /* Might have typed ESC previously, don't - truncate the cmdline now. */ - if (ccheck_abbr(c + ABBR_OFF)) - goto cmdline_changed; - if (!cmd_silent) { - windgoto(msg_row, 0); - out_flush(); - } - break; - } - } - - /* - * Completion for 'wildchar' or 'wildcharm' key. - * - hitting twice means: abandon command line. - * - wildcard expansion is only done when the 'wildchar' key is really - * typed, not when it comes from a macro - */ - if ((c == p_wc && !gotesc && KeyTyped) || c == p_wcm) { - if (xpc.xp_numfiles > 0) { /* typed p_wc at least twice */ - /* if 'wildmode' contains "list" may still need to list */ - if (xpc.xp_numfiles > 1 - && !did_wild_list - && (wim_flags[wim_index] & WIM_LIST)) { - (void)showmatches(&xpc, FALSE); - redrawcmd(); - did_wild_list = TRUE; - } - if (wim_flags[wim_index] & WIM_LONGEST) - res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, - firstc != '@'); - else if (wim_flags[wim_index] & WIM_FULL) - res = nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP, - firstc != '@'); - else - res = OK; /* don't insert 'wildchar' now */ - } else { /* typed p_wc first time */ - wim_index = 0; - j = ccline.cmdpos; - /* if 'wildmode' first contains "longest", get longest - * common part */ - if (wim_flags[0] & WIM_LONGEST) - res = nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, - firstc != '@'); - else - res = nextwild(&xpc, WILD_EXPAND_KEEP, WILD_NO_BEEP, - firstc != '@'); - - /* if interrupted while completing, behave like it failed */ - if (got_int) { - (void)vpeekc(); /* remove from input stream */ - got_int = FALSE; /* don't abandon the command line */ - (void)ExpandOne(&xpc, NULL, NULL, 0, WILD_FREE); - xpc.xp_context = EXPAND_NOTHING; - goto cmdline_changed; - } - - /* when more than one match, and 'wildmode' first contains - * "list", or no change and 'wildmode' contains "longest,list", - * list all matches */ - if (res == OK && xpc.xp_numfiles > 1) { - /* a "longest" that didn't do anything is skipped (but not - * "list:longest") */ - if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) - wim_index = 1; - if ((wim_flags[wim_index] & WIM_LIST) - || (p_wmnu && (wim_flags[wim_index] & WIM_FULL) != 0) - ) { - if (!(wim_flags[0] & WIM_LONGEST)) { - int p_wmnu_save = p_wmnu; - p_wmnu = 0; - /* remove match */ - nextwild(&xpc, WILD_PREV, 0, firstc != '@'); - p_wmnu = p_wmnu_save; - } - (void)showmatches(&xpc, p_wmnu - && ((wim_flags[wim_index] & WIM_LIST) == 0)); - redrawcmd(); - did_wild_list = TRUE; - if (wim_flags[wim_index] & WIM_LONGEST) - nextwild(&xpc, WILD_LONGEST, WILD_NO_BEEP, - firstc != '@'); - else if (wim_flags[wim_index] & WIM_FULL) - nextwild(&xpc, WILD_NEXT, WILD_NO_BEEP, - firstc != '@'); - } else - vim_beep(); - } else if (xpc.xp_numfiles == -1) - xpc.xp_context = EXPAND_NOTHING; - } - if (wim_index < 3) - ++wim_index; - if (c == ESC) - gotesc = TRUE; - if (res == OK) - goto cmdline_changed; - } - - gotesc = FALSE; - - /* goes to last match, in a clumsy way */ - if (c == K_S_TAB && KeyTyped) { - if (nextwild(&xpc, WILD_EXPAND_KEEP, 0, firstc != '@') == OK - && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK - && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK) - goto cmdline_changed; - } - - if (c == NUL || c == K_ZERO) /* NUL is stored as NL */ - c = NL; - - do_abbr = TRUE; /* default: check for abbreviation */ - - /* - * Big switch for a typed command line character. - */ - switch (c) { - case K_EVENT: - event_process(); - // Force a redraw even though the command line didn't change - shell_resized(); - goto cmdline_not_changed; - case K_BS: - case Ctrl_H: - case K_DEL: - case K_KDEL: - case Ctrl_W: - if (cmd_fkmap && c == K_BS) - c = K_DEL; - if (c == K_KDEL) - c = K_DEL; - - /* - * delete current character is the same as backspace on next - * character, except at end of line - */ - if (c == K_DEL && ccline.cmdpos != ccline.cmdlen) - ++ccline.cmdpos; - if (has_mbyte && c == K_DEL) - ccline.cmdpos += mb_off_next(ccline.cmdbuff, - ccline.cmdbuff + ccline.cmdpos); - if (ccline.cmdpos > 0) { - char_u *p; - - j = ccline.cmdpos; - p = ccline.cmdbuff + j; - if (has_mbyte) { - p = mb_prevptr(ccline.cmdbuff, p); - if (c == Ctrl_W) { - while (p > ccline.cmdbuff && vim_isspace(*p)) - p = mb_prevptr(ccline.cmdbuff, p); - i = mb_get_class(p); - while (p > ccline.cmdbuff && mb_get_class(p) == i) - p = mb_prevptr(ccline.cmdbuff, p); - if (mb_get_class(p) != i) - p += (*mb_ptr2len)(p); - } - } else if (c == Ctrl_W) { - while (p > ccline.cmdbuff && vim_isspace(p[-1])) - --p; - i = vim_iswordc(p[-1]); - while (p > ccline.cmdbuff && !vim_isspace(p[-1]) - && vim_iswordc(p[-1]) == i) - --p; - } else - --p; - ccline.cmdpos = (int)(p - ccline.cmdbuff); - ccline.cmdlen -= j - ccline.cmdpos; - i = ccline.cmdpos; - while (i < ccline.cmdlen) - ccline.cmdbuff[i++] = ccline.cmdbuff[j++]; - - /* Truncate at the end, required for multi-byte chars. */ - ccline.cmdbuff[ccline.cmdlen] = NUL; - redrawcmd(); - } else if (ccline.cmdlen == 0 && c != Ctrl_W - && ccline.cmdprompt == NULL && indent == 0) { - /* In ex and debug mode it doesn't make sense to return. */ - if (exmode_active - || ccline.cmdfirstc == '>' - ) - goto cmdline_not_changed; - - free(ccline.cmdbuff); /* no commandline to return */ - ccline.cmdbuff = NULL; - if (!cmd_silent) { - if (cmdmsg_rl) - msg_col = Columns; - else - msg_col = 0; - msg_putchar(' '); /* delete ':' */ - } - redraw_cmdline = TRUE; - goto returncmd; /* back to cmd mode */ - } - goto cmdline_changed; - - case K_INS: - case K_KINS: - /* if Farsi mode set, we are in reverse insert mode - - Do not change the mode */ - if (cmd_fkmap) - beep_flush(); - else - ccline.overstrike = !ccline.overstrike; - - ui_cursor_shape(); /* may show different cursor shape */ - goto cmdline_not_changed; - - case Ctrl_HAT: - if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE)) { - /* ":lmap" mappings exists, toggle use of mappings. */ - State ^= LANGMAP; -#ifdef USE_IM_CONTROL - im_set_active(FALSE); /* Disable input method */ -#endif - if (b_im_ptr != NULL) { - if (State & LANGMAP) - *b_im_ptr = B_IMODE_LMAP; - else - *b_im_ptr = B_IMODE_NONE; - } - } -#ifdef USE_IM_CONTROL - else { - /* There are no ":lmap" mappings, toggle IM. When - * 'imdisable' is set don't try getting the status, it's - * always off. */ - if ((p_imdisable && b_im_ptr != NULL) - ? *b_im_ptr == B_IMODE_IM : im_get_status()) { - im_set_active(FALSE); /* Disable input method */ - if (b_im_ptr != NULL) - *b_im_ptr = B_IMODE_NONE; - } else { - im_set_active(TRUE); /* Enable input method */ - if (b_im_ptr != NULL) - *b_im_ptr = B_IMODE_IM; - } - } -#endif - if (b_im_ptr != NULL) { - if (b_im_ptr == &curbuf->b_p_iminsert) - set_iminsert_global(); - else - set_imsearch_global(); - } - ui_cursor_shape(); /* may show different cursor shape */ - /* Show/unshow value of 'keymap' in status lines later. */ - status_redraw_curbuf(); - goto cmdline_not_changed; - - /* case '@': only in very old vi */ - case Ctrl_U: - /* delete all characters left of the cursor */ - j = ccline.cmdpos; - ccline.cmdlen -= j; - i = ccline.cmdpos = 0; - while (i < ccline.cmdlen) - ccline.cmdbuff[i++] = ccline.cmdbuff[j++]; - /* Truncate at the end, required for multi-byte chars. */ - ccline.cmdbuff[ccline.cmdlen] = NUL; - redrawcmd(); - goto cmdline_changed; - - - case ESC: /* get here if p_wc != ESC or when ESC typed twice */ - case Ctrl_C: - /* In exmode it doesn't make sense to return. Except when - * ":normal" runs out of characters. */ - if (exmode_active - && (ex_normal_busy == 0 || typebuf.tb_len > 0) - ) - goto cmdline_not_changed; - - gotesc = TRUE; /* will free ccline.cmdbuff after - putting it in history */ - goto returncmd; /* back to cmd mode */ - - case Ctrl_R: /* insert register */ -#ifdef USE_ON_FLY_SCROLL - dont_scroll = TRUE; /* disallow scrolling here */ -#endif - putcmdline('"', TRUE); - ++no_mapping; - i = c = plain_vgetc(); /* CTRL-R */ - if (i == Ctrl_O) - i = Ctrl_R; /* CTRL-R CTRL-O == CTRL-R CTRL-R */ - if (i == Ctrl_R) - c = plain_vgetc(); /* CTRL-R CTRL-R */ - --no_mapping; - /* - * Insert the result of an expression. - * Need to save the current command line, to be able to enter - * a new one... - */ - new_cmdpos = -1; - if (c == '=') { - if (ccline.cmdfirstc == '=') { /* can't do this recursively */ - beep_flush(); - c = ESC; - } else { - save_cmdline(&save_ccline); - c = get_expr_register(); - restore_cmdline(&save_ccline); - } - } - if (c != ESC) { /* use ESC to cancel inserting register */ - cmdline_paste(c, i == Ctrl_R, FALSE); - - /* When there was a serious error abort getting the - * command line. */ - if (aborting()) { - gotesc = TRUE; /* will free ccline.cmdbuff after - putting it in history */ - goto returncmd; /* back to cmd mode */ - } - KeyTyped = FALSE; /* Don't do p_wc completion. */ - if (new_cmdpos >= 0) { - /* set_cmdline_pos() was used */ - if (new_cmdpos > ccline.cmdlen) - ccline.cmdpos = ccline.cmdlen; - else - ccline.cmdpos = new_cmdpos; - } - } - redrawcmd(); - goto cmdline_changed; - - case Ctrl_D: - if (showmatches(&xpc, FALSE) == EXPAND_NOTHING) - break; /* Use ^D as normal char instead */ - - redrawcmd(); - continue; /* don't do incremental search now */ - - case K_RIGHT: - case K_S_RIGHT: - case K_C_RIGHT: - do { - if (ccline.cmdpos >= ccline.cmdlen) - break; - i = cmdline_charsize(ccline.cmdpos); - if (KeyTyped && ccline.cmdspos + i >= Columns * Rows) - break; - ccline.cmdspos += i; - if (has_mbyte) - ccline.cmdpos += (*mb_ptr2len)(ccline.cmdbuff - + ccline.cmdpos); - else - ++ccline.cmdpos; - } while ((c == K_S_RIGHT || c == K_C_RIGHT - || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) - && ccline.cmdbuff[ccline.cmdpos] != ' '); - if (has_mbyte) - set_cmdspos_cursor(); - goto cmdline_not_changed; - - case K_LEFT: - case K_S_LEFT: - case K_C_LEFT: - if (ccline.cmdpos == 0) - goto cmdline_not_changed; - do { - --ccline.cmdpos; - if (has_mbyte) /* move to first byte of char */ - ccline.cmdpos -= (*mb_head_off)(ccline.cmdbuff, - ccline.cmdbuff + ccline.cmdpos); - ccline.cmdspos -= cmdline_charsize(ccline.cmdpos); - } while (ccline.cmdpos > 0 - && (c == K_S_LEFT || c == K_C_LEFT - || (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) - && ccline.cmdbuff[ccline.cmdpos - 1] != ' '); - if (has_mbyte) - set_cmdspos_cursor(); - goto cmdline_not_changed; - - case K_IGNORE: - /* Ignore mouse event or ex_window() result. */ - goto cmdline_not_changed; - - - case K_MIDDLEDRAG: - case K_MIDDLERELEASE: - goto cmdline_not_changed; /* Ignore mouse */ - - case K_MIDDLEMOUSE: - if (!mouse_has(MOUSE_COMMAND)) - goto cmdline_not_changed; /* Ignore mouse */ - cmdline_paste(0, TRUE, TRUE); - redrawcmd(); - goto cmdline_changed; - - - case K_LEFTDRAG: - case K_LEFTRELEASE: - case K_RIGHTDRAG: - case K_RIGHTRELEASE: - /* Ignore drag and release events when the button-down wasn't - * seen before. */ - if (ignore_drag_release) - goto cmdline_not_changed; - /* FALLTHROUGH */ - case K_LEFTMOUSE: - case K_RIGHTMOUSE: - if (c == K_LEFTRELEASE || c == K_RIGHTRELEASE) - ignore_drag_release = TRUE; - else - ignore_drag_release = FALSE; - if (!mouse_has(MOUSE_COMMAND)) - goto cmdline_not_changed; /* Ignore mouse */ - - set_cmdspos(); - for (ccline.cmdpos = 0; ccline.cmdpos < ccline.cmdlen; - ++ccline.cmdpos) { - i = cmdline_charsize(ccline.cmdpos); - if (mouse_row <= cmdline_row + ccline.cmdspos / Columns - && mouse_col < ccline.cmdspos % Columns + i) - break; - if (has_mbyte) { - /* Count ">" for double-wide char that doesn't fit. */ - correct_cmdspos(ccline.cmdpos, i); - ccline.cmdpos += (*mb_ptr2len)(ccline.cmdbuff - + ccline.cmdpos) - 1; - } - ccline.cmdspos += i; - } - goto cmdline_not_changed; - - /* Mouse scroll wheel: ignored here */ - case K_MOUSEDOWN: - case K_MOUSEUP: - case K_MOUSELEFT: - case K_MOUSERIGHT: - /* Alternate buttons ignored here */ - case K_X1MOUSE: - case K_X1DRAG: - case K_X1RELEASE: - case K_X2MOUSE: - case K_X2DRAG: - case K_X2RELEASE: - goto cmdline_not_changed; - - - - case K_SELECT: /* end of Select mode mapping - ignore */ - goto cmdline_not_changed; - - case Ctrl_B: /* begin of command line */ - case K_HOME: - case K_KHOME: - case K_S_HOME: - case K_C_HOME: - ccline.cmdpos = 0; - set_cmdspos(); - goto cmdline_not_changed; - - case Ctrl_E: /* end of command line */ - case K_END: - case K_KEND: - case K_S_END: - case K_C_END: - ccline.cmdpos = ccline.cmdlen; - set_cmdspos_cursor(); - goto cmdline_not_changed; - - case Ctrl_A: /* all matches */ - if (nextwild(&xpc, WILD_ALL, 0, firstc != '@') == FAIL) - break; - goto cmdline_changed; - - case Ctrl_L: - if (p_is && !cmd_silent && (firstc == '/' || firstc == '?')) { - /* Add a character from under the cursor for 'incsearch' */ - if (did_incsearch - && !equalpos(curwin->w_cursor, old_cursor)) { - c = gchar_cursor(); - /* If 'ignorecase' and 'smartcase' are set and the - * command line has no uppercase characters, convert - * the character to lowercase */ - if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff)) - c = vim_tolower(c); - if (c != NUL) { - if (c == firstc || vim_strchr((char_u *)( - p_magic ? "\\^$.*[" : "\\^$"), c) - != NULL) { - /* put a backslash before special characters */ - stuffcharReadbuff(c); - c = '\\'; - } - break; - } - } - goto cmdline_not_changed; - } - - /* completion: longest common part */ - if (nextwild(&xpc, WILD_LONGEST, 0, firstc != '@') == FAIL) - break; - goto cmdline_changed; - - case Ctrl_N: /* next match */ - case Ctrl_P: /* previous match */ - if (xpc.xp_numfiles > 0) { - if (nextwild(&xpc, (c == Ctrl_P) ? WILD_PREV : WILD_NEXT, - 0, firstc != '@') == FAIL) - break; - goto cmdline_changed; - } - - case K_UP: - case K_DOWN: - case K_S_UP: - case K_S_DOWN: - case K_PAGEUP: - case K_KPAGEUP: - case K_PAGEDOWN: - case K_KPAGEDOWN: - if (hislen == 0 || firstc == NUL) /* no history */ - goto cmdline_not_changed; - - i = hiscnt; - - /* save current command string so it can be restored later */ - if (lookfor == NULL) { - if ((lookfor = vim_strsave(ccline.cmdbuff)) == NULL) - goto cmdline_not_changed; - lookfor[ccline.cmdpos] = NUL; - } - - j = (int)STRLEN(lookfor); - for (;; ) { - /* one step backwards */ - if (c == K_UP|| c == K_S_UP || c == Ctrl_P - || c == K_PAGEUP || c == K_KPAGEUP) { - if (hiscnt == hislen) /* first time */ - hiscnt = hisidx[histype]; - else if (hiscnt == 0 && hisidx[histype] != hislen - 1) - hiscnt = hislen - 1; - else if (hiscnt != hisidx[histype] + 1) - --hiscnt; - else { /* at top of list */ - hiscnt = i; - break; - } - } else { /* one step forwards */ - /* on last entry, clear the line */ - if (hiscnt == hisidx[histype]) { - hiscnt = hislen; - break; - } - - /* not on a history line, nothing to do */ - if (hiscnt == hislen) - break; - if (hiscnt == hislen - 1) /* wrap around */ - hiscnt = 0; - else - ++hiscnt; - } - if (hiscnt < 0 || history[histype][hiscnt].hisstr == NULL) { - hiscnt = i; - break; - } - if ((c != K_UP && c != K_DOWN) - || hiscnt == i - || STRNCMP(history[histype][hiscnt].hisstr, - lookfor, (size_t)j) == 0) - break; - } - - if (hiscnt != i) { /* jumped to other entry */ - char_u *p; - int len; - int old_firstc; - - free(ccline.cmdbuff); - xpc.xp_context = EXPAND_NOTHING; - if (hiscnt == hislen) - p = lookfor; /* back to the old one */ - else - p = history[histype][hiscnt].hisstr; - - if (histype == HIST_SEARCH - && p != lookfor - && (old_firstc = p[STRLEN(p) + 1]) != firstc) { - /* Correct for the separator character used when - * adding the history entry vs the one used now. - * First loop: count length. - * Second loop: copy the characters. */ - for (i = 0; i <= 1; ++i) { - len = 0; - for (j = 0; p[j] != NUL; ++j) { - /* Replace old sep with new sep, unless it is - * escaped. */ - if (p[j] == old_firstc - && (j == 0 || p[j - 1] != '\\')) { - if (i > 0) - ccline.cmdbuff[len] = firstc; - } else { - /* Escape new sep, unless it is already - * escaped. */ - if (p[j] == firstc - && (j == 0 || p[j - 1] != '\\')) { - if (i > 0) - ccline.cmdbuff[len] = '\\'; - ++len; - } - if (i > 0) - ccline.cmdbuff[len] = p[j]; - } - ++len; - } - if (i == 0) { - alloc_cmdbuff(len); - if (ccline.cmdbuff == NULL) - goto returncmd; - } - } - ccline.cmdbuff[len] = NUL; - } else { - alloc_cmdbuff((int)STRLEN(p)); - if (ccline.cmdbuff == NULL) - goto returncmd; - STRCPY(ccline.cmdbuff, p); - } - - ccline.cmdpos = ccline.cmdlen = (int)STRLEN(ccline.cmdbuff); - redrawcmd(); - goto cmdline_changed; - } - beep_flush(); - goto cmdline_not_changed; - - case Ctrl_V: - case Ctrl_Q: - ignore_drag_release = TRUE; - putcmdline('^', TRUE); - c = get_literal(); /* get next (two) character(s) */ - do_abbr = FALSE; /* don't do abbreviation now */ - /* may need to remove ^ when composing char was typed */ - if (enc_utf8 && utf_iscomposing(c) && !cmd_silent) { - draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); - msg_putchar(' '); - cursorcmd(); - } - break; - - case Ctrl_K: - ignore_drag_release = TRUE; - putcmdline('?', TRUE); -#ifdef USE_ON_FLY_SCROLL - dont_scroll = TRUE; /* disallow scrolling here */ -#endif - c = get_digraph(TRUE); - if (c != NUL) - break; - - redrawcmd(); - goto cmdline_not_changed; - - case Ctrl__: /* CTRL-_: switch language mode */ - if (!p_ari) - break; - if (p_altkeymap) { - cmd_fkmap = !cmd_fkmap; - if (cmd_fkmap) /* in Farsi always in Insert mode */ - ccline.overstrike = FALSE; - } else /* Hebrew is default */ - cmd_hkmap = !cmd_hkmap; - goto cmdline_not_changed; - - default: -#ifdef UNIX - if (c == intr_char) { - gotesc = TRUE; /* will free ccline.cmdbuff after - putting it in history */ - goto returncmd; /* back to Normal mode */ - } -#endif - /* - * Normal character with no special meaning. Just set mod_mask - * to 0x0 so that typing Shift-Space in the GUI doesn't enter - * the string . This should only happen after ^V. - */ - if (!IS_SPECIAL(c)) - mod_mask = 0x0; - break; - } - /* - * End of switch on command line character. - * We come here if we have a normal character. - */ - - if (do_abbr && (IS_SPECIAL(c) || !vim_iswordc(c)) && (ccheck_abbr( - /* Add ABBR_OFF for characters above 0x100, this is - * what check_abbr() expects. */ - (has_mbyte && - c >= - 0x100) ? (c + - ABBR_OFF) : - c) || c == - Ctrl_RSB)) - goto cmdline_changed; - - /* - * put the character in the command line - */ - if (IS_SPECIAL(c) || mod_mask != 0) - put_on_cmdline(get_special_key_name(c, mod_mask), -1, TRUE); - else { - if (has_mbyte) { - j = (*mb_char2bytes)(c, IObuff); - IObuff[j] = NUL; /* exclude composing chars */ - put_on_cmdline(IObuff, j, TRUE); - } else { - IObuff[0] = c; - put_on_cmdline(IObuff, 1, TRUE); - } - } - goto cmdline_changed; - - /* - * This part implements incremental searches for "/" and "?" - * Jump to cmdline_not_changed when a character has been read but the command - * line did not change. Then we only search and redraw if something changed in - * the past. - * Jump to cmdline_changed when the command line did change. - * (Sorry for the goto's, I know it is ugly). - */ -cmdline_not_changed: - if (!incsearch_postponed) - continue; - -cmdline_changed: - /* - * 'incsearch' highlighting. - */ - if (p_is && !cmd_silent && (firstc == '/' || firstc == '?')) { - pos_T end_pos; - proftime_T tm; - - /* if there is a character waiting, search and redraw later */ - if (char_avail()) { - incsearch_postponed = TRUE; - continue; - } - incsearch_postponed = FALSE; - curwin->w_cursor = old_cursor; /* start at old position */ - - /* If there is no command line, don't do anything */ - if (ccline.cmdlen == 0) - i = 0; - else { - cursor_off(); /* so the user knows we're busy */ - out_flush(); - ++emsg_off; /* So it doesn't beep if bad expr */ - /* Set the time limit to half a second. */ - profile_setlimit(500L, &tm); - i = do_search(NULL, firstc, ccline.cmdbuff, count, - SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK, - &tm - ); - --emsg_off; - /* if interrupted while searching, behave like it failed */ - if (got_int) { - (void)vpeekc(); /* remove from input stream */ - got_int = FALSE; /* don't abandon the command line */ - i = 0; - } else if (char_avail()) - /* cancelled searching because a char was typed */ - incsearch_postponed = TRUE; - } - if (i != 0) - highlight_match = TRUE; /* highlight position */ - else - highlight_match = FALSE; /* remove highlight */ - - /* first restore the old curwin values, so the screen is - * positioned in the same way as the actual search command */ - curwin->w_leftcol = old_leftcol; - curwin->w_topline = old_topline; - curwin->w_topfill = old_topfill; - curwin->w_botline = old_botline; - changed_cline_bef_curs(); - update_topline(); - - if (i != 0) { - pos_T save_pos = curwin->w_cursor; - - /* - * First move cursor to end of match, then to the start. This - * moves the whole match onto the screen when 'nowrap' is set. - */ - curwin->w_cursor.lnum += search_match_lines; - curwin->w_cursor.col = search_match_endcol; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance((colnr_T)MAXCOL); - } - validate_cursor(); - end_pos = curwin->w_cursor; - curwin->w_cursor = save_pos; - } else - end_pos = curwin->w_cursor; /* shutup gcc 4 */ - - validate_cursor(); - /* May redraw the status line to show the cursor position. */ - if (p_ru && curwin->w_status_height > 0) - curwin->w_redr_status = TRUE; - - save_cmdline(&save_ccline); - update_screen(SOME_VALID); - restore_cmdline(&save_ccline); - - /* Leave it at the end to make CTRL-R CTRL-W work. */ - if (i != 0) - curwin->w_cursor = end_pos; - - msg_starthere(); - redrawcmdline(); - did_incsearch = TRUE; - } - - if (cmdmsg_rl - || (p_arshape && !p_tbidi && enc_utf8) - ) - /* Always redraw the whole command line to fix shaping and - * right-left typing. Not efficient, but it works. - * Do it only when there are no characters left to read - * to avoid useless intermediate redraws. */ - if (vpeekc() == NUL) - redrawcmd(); - } - -returncmd: - - cmdmsg_rl = FALSE; - - cmd_fkmap = 0; - - ExpandCleanup(&xpc); - ccline.xpc = NULL; - - if (did_incsearch) { - curwin->w_cursor = old_cursor; - curwin->w_curswant = old_curswant; - curwin->w_leftcol = old_leftcol; - curwin->w_topline = old_topline; - curwin->w_topfill = old_topfill; - curwin->w_botline = old_botline; - highlight_match = FALSE; - validate_cursor(); /* needed for TAB */ - redraw_later(SOME_VALID); - } - - if (ccline.cmdbuff != NULL) { - /* - * Put line in history buffer (":" and "=" only when it was typed). - */ - if (ccline.cmdlen && firstc != NUL - && (some_key_typed || histype == HIST_SEARCH)) { - add_to_history(histype, ccline.cmdbuff, TRUE, - histype == HIST_SEARCH ? firstc : NUL); - if (firstc == ':') { - free(new_last_cmdline); - new_last_cmdline = vim_strsave(ccline.cmdbuff); - } - } - - if (gotesc) { /* abandon command line */ - free(ccline.cmdbuff); - ccline.cmdbuff = NULL; - if (msg_scrolled == 0) - compute_cmdrow(); - MSG(""); - redraw_cmdline = TRUE; - } - } - - /* - * If the screen was shifted up, redraw the whole screen (later). - * If the line is too long, clear it, so ruler and shown command do - * not get printed in the middle of it. - */ - msg_check(); - msg_scroll = save_msg_scroll; - redir_off = FALSE; - - /* When the command line was typed, no need for a wait-return prompt. */ - if (some_key_typed) - need_wait_return = FALSE; - - State = save_State; -#ifdef USE_IM_CONTROL - if (b_im_ptr != NULL && *b_im_ptr != B_IMODE_LMAP) - im_save_status(b_im_ptr); - im_set_active(FALSE); -#endif - setmouse(); - ui_cursor_shape(); /* may show different cursor shape */ - - { - char_u *p = ccline.cmdbuff; - - /* Make ccline empty, getcmdline() may try to use it. */ - ccline.cmdbuff = NULL; - return p; - } -} - -/* - * Get a command line with a prompt. - * This is prepared to be called recursively from getcmdline() (e.g. by - * f_input() when evaluating an expression from CTRL-R =). - * Returns the command line in allocated memory, or NULL. - */ -char_u * -getcmdline_prompt ( - int firstc, - char_u *prompt, /* command line prompt */ - int attr, /* attributes for prompt */ - int xp_context, /* type of expansion */ - char_u *xp_arg /* user-defined expansion argument */ -) -{ - char_u *s; - struct cmdline_info save_ccline; - int msg_col_save = msg_col; - - save_cmdline(&save_ccline); - ccline.cmdprompt = prompt; - ccline.cmdattr = attr; - ccline.xp_context = xp_context; - ccline.xp_arg = xp_arg; - ccline.input_fn = (firstc == '@'); - s = getcmdline(firstc, 1L, 0); - restore_cmdline(&save_ccline); - /* Restore msg_col, the prompt from input() may have changed it. - * But only if called recursively and the commandline is therefore being - * restored to an old one; if not, the input() prompt stays on the screen, - * so we need its modified msg_col left intact. */ - if (ccline.cmdbuff != NULL) - msg_col = msg_col_save; - - return s; -} - -/* - * Return TRUE when the text must not be changed and we can't switch to - * another window or buffer. Used when editing the command line etc. - */ -int text_locked(void) { - if (cmdwin_type != 0) - return TRUE; - return textlock != 0; -} - -/* - * Give an error message for a command that isn't allowed while the cmdline - * window is open or editing the cmdline in another way. - */ -void text_locked_msg(void) -{ - if (cmdwin_type != 0) - EMSG(_(e_cmdwin)); - else - EMSG(_(e_secure)); -} - -/* - * Check if "curbuf_lock" or "allbuf_lock" is set and return TRUE when it is - * and give an error message. - */ -int curbuf_locked(void) -{ - if (curbuf_lock > 0) { - EMSG(_("E788: Not allowed to edit another buffer now")); - return TRUE; - } - return allbuf_locked(); -} - -/* - * Check if "allbuf_lock" is set and return TRUE when it is and give an error - * message. - */ -int allbuf_locked(void) -{ - if (allbuf_lock > 0) { - EMSG(_("E811: Not allowed to change buffer information now")); - return TRUE; - } - return FALSE; -} - -static int cmdline_charsize(int idx) -{ - if (cmdline_star > 0) /* showing '*', always 1 position */ - return 1; - return ptr2cells(ccline.cmdbuff + idx); -} - -/* - * Compute the offset of the cursor on the command line for the prompt and - * indent. - */ -static void set_cmdspos(void) -{ - if (ccline.cmdfirstc != NUL) - ccline.cmdspos = 1 + ccline.cmdindent; - else - ccline.cmdspos = 0 + ccline.cmdindent; -} - -/* - * Compute the screen position for the cursor on the command line. - */ -static void set_cmdspos_cursor(void) -{ - int i, m, c; - - set_cmdspos(); - if (KeyTyped) { - m = Columns * Rows; - if (m < 0) /* overflow, Columns or Rows at weird value */ - m = MAXCOL; - } else - m = MAXCOL; - for (i = 0; i < ccline.cmdlen && i < ccline.cmdpos; ++i) { - c = cmdline_charsize(i); - /* Count ">" for double-wide multi-byte char that doesn't fit. */ - if (has_mbyte) - correct_cmdspos(i, c); - /* If the cmdline doesn't fit, show cursor on last visible char. - * Don't move the cursor itself, so we can still append. */ - if ((ccline.cmdspos += c) >= m) { - ccline.cmdspos -= c; - break; - } - if (has_mbyte) - i += (*mb_ptr2len)(ccline.cmdbuff + i) - 1; - } -} - -/* - * Check if the character at "idx", which is "cells" wide, is a multi-byte - * character that doesn't fit, so that a ">" must be displayed. - */ -static void correct_cmdspos(int idx, int cells) -{ - if ((*mb_ptr2len)(ccline.cmdbuff + idx) > 1 - && (*mb_ptr2cells)(ccline.cmdbuff + idx) > 1 - && ccline.cmdspos % Columns + cells > Columns) - ccline.cmdspos++; -} - -/* - * Get an Ex command line for the ":" command. - */ -char_u * -getexline ( - int c, /* normally ':', NUL for ":append" */ - void *cookie, - int indent /* indent for inside conditionals */ -) -{ - /* When executing a register, remove ':' that's in front of each line. */ - if (exec_from_reg && vpeekc() == ':') - (void)vgetc(); - return getcmdline(c, 1L, indent); -} - -/* - * Get an Ex command line for Ex mode. - * In Ex mode we only use the OS supplied line editing features and no - * mappings or abbreviations. - * Returns a string in allocated memory or NULL. - */ -char_u * -getexmodeline ( - int promptc, /* normally ':', NUL for ":append" and '?' for - :s prompt */ - void *cookie, - int indent /* indent for inside conditionals */ -) -{ - garray_T line_ga; - char_u *pend; - int startcol = 0; - int c1 = 0; - int escaped = FALSE; /* CTRL-V typed */ - int vcol = 0; - char_u *p; - int prev_char; - - /* Switch cursor on now. This avoids that it happens after the "\n", which - * confuses the system function that computes tabstops. */ - cursor_on(); - - /* always start in column 0; write a newline if necessary */ - compute_cmdrow(); - if ((msg_col || msg_didout) && promptc != '?') - msg_putchar('\n'); - if (promptc == ':') { - /* indent that is only displayed, not in the line itself */ - if (p_prompt) - msg_putchar(':'); - while (indent-- > 0) - msg_putchar(' '); - startcol = msg_col; - } - - ga_init(&line_ga, 1, 30); - - /* autoindent for :insert and :append is in the line itself */ - if (promptc <= 0) { - vcol = indent; - while (indent >= 8) { - ga_append(&line_ga, TAB); - msg_puts((char_u *)" "); - indent -= 8; - } - while (indent-- > 0) { - ga_append(&line_ga, ' '); - msg_putchar(' '); - } - } - ++no_mapping; - ++allow_keys; - - /* - * Get the line, one character at a time. - */ - got_int = FALSE; - while (!got_int) { - ga_grow(&line_ga, 40); - - /* Get one character at a time. Don't use inchar(), it can't handle - * special characters. */ - prev_char = c1; - c1 = vgetc(); - - /* - * Handle line editing. - * Previously this was left to the system, putting the terminal in - * cooked mode, but then CTRL-D and CTRL-T can't be used properly. - */ - if (got_int) { - msg_putchar('\n'); - break; - } - - if (!escaped) { - /* CR typed means "enter", which is NL */ - if (c1 == '\r') - c1 = '\n'; - - if (c1 == BS || c1 == K_BS - || c1 == DEL || c1 == K_DEL || c1 == K_KDEL) { - if (line_ga.ga_len > 0) { - --line_ga.ga_len; - goto redraw; - } - continue; - } - - if (c1 == Ctrl_U) { - msg_col = startcol; - msg_clr_eos(); - line_ga.ga_len = 0; - continue; - } - - if (c1 == Ctrl_T) { - long sw = get_sw_value(curbuf); - - p = (char_u *)line_ga.ga_data; - p[line_ga.ga_len] = NUL; - indent = get_indent_str(p, 8); - indent += sw - indent % sw; -add_indent: - while (get_indent_str(p, 8) < indent) { - char_u *s = skipwhite(p); - - ga_grow(&line_ga, 1); - memmove(s + 1, s, line_ga.ga_len - (s - p) + 1); - *s = ' '; - ++line_ga.ga_len; - } -redraw: - /* redraw the line */ - msg_col = startcol; - vcol = 0; - for (p = (char_u *)line_ga.ga_data; - p < (char_u *)line_ga.ga_data + line_ga.ga_len; ++p) { - if (*p == TAB) { - do { - msg_putchar(' '); - } while (++vcol % 8); - } else { - msg_outtrans_len(p, 1); - vcol += char2cells(*p); - } - } - msg_clr_eos(); - windgoto(msg_row, msg_col); - continue; - } - - if (c1 == Ctrl_D) { - /* Delete one shiftwidth. */ - p = (char_u *)line_ga.ga_data; - if (prev_char == '0' || prev_char == '^') { - if (prev_char == '^') - ex_keep_indent = TRUE; - indent = 0; - p[--line_ga.ga_len] = NUL; - } else { - p[line_ga.ga_len] = NUL; - indent = get_indent_str(p, 8); - --indent; - indent -= indent % get_sw_value(curbuf); - } - while (get_indent_str(p, 8) > indent) { - char_u *s = skipwhite(p); - - memmove(s - 1, s, line_ga.ga_len - (s - p) + 1); - --line_ga.ga_len; - } - goto add_indent; - } - - if (c1 == Ctrl_V || c1 == Ctrl_Q) { - escaped = TRUE; - continue; - } - - if (IS_SPECIAL(c1)) { - // Process pending events - event_process(); - // Ignore other special key codes - continue; - } - } - - if (IS_SPECIAL(c1)) - c1 = '?'; - ((char_u *)line_ga.ga_data)[line_ga.ga_len] = c1; - if (c1 == '\n') - msg_putchar('\n'); - else if (c1 == TAB) { - /* Don't use chartabsize(), 'ts' can be different */ - do { - msg_putchar(' '); - } while (++vcol % 8); - } else { - msg_outtrans_len( - ((char_u *)line_ga.ga_data) + line_ga.ga_len, 1); - vcol += char2cells(c1); - } - ++line_ga.ga_len; - escaped = FALSE; - - windgoto(msg_row, msg_col); - pend = (char_u *)(line_ga.ga_data) + line_ga.ga_len; - - /* We are done when a NL is entered, but not when it comes after an - * odd number of backslashes, that results in a NUL. */ - if (line_ga.ga_len > 0 && pend[-1] == '\n') { - int bcount = 0; - - while (line_ga.ga_len - 2 >= bcount && pend[-2 - bcount] == '\\') - ++bcount; - - if (bcount > 0) { - /* Halve the number of backslashes: "\NL" -> "NUL", "\\NL" -> - * "\NL", etc. */ - line_ga.ga_len -= (bcount + 1) / 2; - pend -= (bcount + 1) / 2; - pend[-1] = '\n'; - } - - if ((bcount & 1) == 0) { - --line_ga.ga_len; - --pend; - *pend = NUL; - break; - } - } - } - - --no_mapping; - --allow_keys; - - /* make following messages go to the next line */ - msg_didout = FALSE; - msg_col = 0; - if (msg_row < Rows - 1) - ++msg_row; - emsg_on_display = FALSE; /* don't want ui_delay() */ - - if (got_int) - ga_clear(&line_ga); - - return (char_u *)line_ga.ga_data; -} - -/* - * Allocate a new command line buffer. - * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen. - * Returns the new value of ccline.cmdbuff and ccline.cmdbufflen. - */ -static void alloc_cmdbuff(int len) -{ - /* - * give some extra space to avoid having to allocate all the time - */ - if (len < 80) - len = 100; - else - len += 20; - - ccline.cmdbuff = alloc(len); /* caller should check for out-of-memory */ - ccline.cmdbufflen = len; -} - -/* - * Re-allocate the command line to length len + something extra. - * return FAIL for failure, OK otherwise - */ -static int realloc_cmdbuff(int len) -{ - char_u *p; - - if (len < ccline.cmdbufflen) - return OK; /* no need to resize */ - - p = ccline.cmdbuff; - alloc_cmdbuff(len); /* will get some more */ - if (ccline.cmdbuff == NULL) { /* out of memory */ - ccline.cmdbuff = p; /* keep the old one */ - return FAIL; - } - /* There isn't always a NUL after the command, but it may need to be - * there, thus copy up to the NUL and add a NUL. */ - memmove(ccline.cmdbuff, p, (size_t)ccline.cmdlen); - ccline.cmdbuff[ccline.cmdlen] = NUL; - free(p); - - if (ccline.xpc != NULL - && ccline.xpc->xp_pattern != NULL - && ccline.xpc->xp_context != EXPAND_NOTHING - && ccline.xpc->xp_context != EXPAND_UNSUCCESSFUL) { - int i = (int)(ccline.xpc->xp_pattern - p); - - /* If xp_pattern points inside the old cmdbuff it needs to be adjusted - * to point into the newly allocated memory. */ - if (i >= 0 && i <= ccline.cmdlen) - ccline.xpc->xp_pattern = ccline.cmdbuff + i; - } - - return OK; -} - -static char_u *arshape_buf = NULL; - -# if defined(EXITFREE) || defined(PROTO) -void free_cmdline_buf(void) -{ - free(arshape_buf); -} - -# endif - -/* - * Draw part of the cmdline at the current cursor position. But draw stars - * when cmdline_star is TRUE. - */ -static void draw_cmdline(int start, int len) -{ - int i; - - if (cmdline_star > 0) - for (i = 0; i < len; ++i) { - msg_putchar('*'); - if (has_mbyte) - i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1; - } - else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) { - static int buflen = 0; - char_u *p; - int j; - int newlen = 0; - int mb_l; - int pc, pc1 = 0; - int prev_c = 0; - int prev_c1 = 0; - int u8c; - int u8cc[MAX_MCO]; - int nc = 0; - - /* - * Do arabic shaping into a temporary buffer. This is very - * inefficient! - */ - if (len * 2 + 2 > buflen) { - /* Re-allocate the buffer. We keep it around to avoid a lot of - * alloc()/free() calls. */ - free(arshape_buf); - buflen = len * 2 + 2; - arshape_buf = alloc(buflen); - if (arshape_buf == NULL) - return; /* out of memory */ - } - - if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) { - /* Prepend a space to draw the leading composing char on. */ - arshape_buf[0] = ' '; - newlen = 1; - } - - for (j = start; j < start + len; j += mb_l) { - p = ccline.cmdbuff + j; - u8c = utfc_ptr2char_len(p, u8cc, start + len - j); - mb_l = utfc_ptr2len_len(p, start + len - j); - if (arabic_char(u8c)) { - /* Do Arabic shaping. */ - if (cmdmsg_rl) { - /* displaying from right to left */ - pc = prev_c; - pc1 = prev_c1; - prev_c1 = u8cc[0]; - if (j + mb_l >= start + len) - nc = NUL; - else - nc = utf_ptr2char(p + mb_l); - } else { - /* displaying from left to right */ - if (j + mb_l >= start + len) - pc = NUL; - else { - int pcc[MAX_MCO]; - - pc = utfc_ptr2char_len(p + mb_l, pcc, - start + len - j - mb_l); - pc1 = pcc[0]; - } - nc = prev_c; - } - prev_c = u8c; - - u8c = arabic_shape(u8c, NULL, &u8cc[0], pc, pc1, nc); - - newlen += (*mb_char2bytes)(u8c, arshape_buf + newlen); - if (u8cc[0] != 0) { - newlen += (*mb_char2bytes)(u8cc[0], arshape_buf + newlen); - if (u8cc[1] != 0) - newlen += (*mb_char2bytes)(u8cc[1], - arshape_buf + newlen); - } - } else { - prev_c = u8c; - memmove(arshape_buf + newlen, p, mb_l); - newlen += mb_l; - } - } - - msg_outtrans_len(arshape_buf, newlen); - } else - msg_outtrans_len(ccline.cmdbuff + start, len); -} - -/* - * Put a character on the command line. Shifts the following text to the - * right when "shift" is TRUE. Used for CTRL-V, CTRL-K, etc. - * "c" must be printable (fit in one display cell)! - */ -void putcmdline(int c, int shift) -{ - if (cmd_silent) - return; - msg_no_more = TRUE; - msg_putchar(c); - if (shift) - draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); - msg_no_more = FALSE; - cursorcmd(); -} - -/* - * Undo a putcmdline(c, FALSE). - */ -void unputcmdline(void) -{ - if (cmd_silent) - return; - msg_no_more = TRUE; - if (ccline.cmdlen == ccline.cmdpos) - msg_putchar(' '); - else if (has_mbyte) - draw_cmdline(ccline.cmdpos, - (*mb_ptr2len)(ccline.cmdbuff + ccline.cmdpos)); - else - draw_cmdline(ccline.cmdpos, 1); - msg_no_more = FALSE; - cursorcmd(); -} - -/* - * Put the given string, of the given length, onto the command line. - * If len is -1, then STRLEN() is used to calculate the length. - * If 'redraw' is TRUE then the new part of the command line, and the remaining - * part will be redrawn, otherwise it will not. If this function is called - * twice in a row, then 'redraw' should be FALSE and redrawcmd() should be - * called afterwards. - */ -int put_on_cmdline(char_u *str, int len, int redraw) -{ - int retval; - int i; - int m; - int c; - - if (len < 0) - len = (int)STRLEN(str); - - /* Check if ccline.cmdbuff needs to be longer */ - if (ccline.cmdlen + len + 1 >= ccline.cmdbufflen) - retval = realloc_cmdbuff(ccline.cmdlen + len + 1); - else - retval = OK; - if (retval == OK) { - if (!ccline.overstrike) { - memmove(ccline.cmdbuff + ccline.cmdpos + len, - ccline.cmdbuff + ccline.cmdpos, - (size_t)(ccline.cmdlen - ccline.cmdpos)); - ccline.cmdlen += len; - } else { - if (has_mbyte) { - /* Count nr of characters in the new string. */ - m = 0; - for (i = 0; i < len; i += (*mb_ptr2len)(str + i)) - ++m; - /* Count nr of bytes in cmdline that are overwritten by these - * characters. */ - for (i = ccline.cmdpos; i < ccline.cmdlen && m > 0; - i += (*mb_ptr2len)(ccline.cmdbuff + i)) - --m; - if (i < ccline.cmdlen) { - memmove(ccline.cmdbuff + ccline.cmdpos + len, - ccline.cmdbuff + i, (size_t)(ccline.cmdlen - i)); - ccline.cmdlen += ccline.cmdpos + len - i; - } else - ccline.cmdlen = ccline.cmdpos + len; - } else if (ccline.cmdpos + len > ccline.cmdlen) - ccline.cmdlen = ccline.cmdpos + len; - } - memmove(ccline.cmdbuff + ccline.cmdpos, str, (size_t)len); - ccline.cmdbuff[ccline.cmdlen] = NUL; - - if (enc_utf8) { - /* When the inserted text starts with a composing character, - * backup to the character before it. There could be two of them. - */ - i = 0; - c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos); - while (ccline.cmdpos > 0 && utf_iscomposing(c)) { - i = (*mb_head_off)(ccline.cmdbuff, - ccline.cmdbuff + ccline.cmdpos - 1) + 1; - ccline.cmdpos -= i; - len += i; - c = utf_ptr2char(ccline.cmdbuff + ccline.cmdpos); - } - if (i == 0 && ccline.cmdpos > 0 && arabic_maycombine(c)) { - /* Check the previous character for Arabic combining pair. */ - i = (*mb_head_off)(ccline.cmdbuff, - ccline.cmdbuff + ccline.cmdpos - 1) + 1; - if (arabic_combine(utf_ptr2char(ccline.cmdbuff - + ccline.cmdpos - i), c)) { - ccline.cmdpos -= i; - len += i; - } else - i = 0; - } - if (i != 0) { - /* Also backup the cursor position. */ - i = ptr2cells(ccline.cmdbuff + ccline.cmdpos); - ccline.cmdspos -= i; - msg_col -= i; - if (msg_col < 0) { - msg_col += Columns; - --msg_row; - } - } - } - - if (redraw && !cmd_silent) { - msg_no_more = TRUE; - i = cmdline_row; - cursorcmd(); - draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); - /* Avoid clearing the rest of the line too often. */ - if (cmdline_row != i || ccline.overstrike) - msg_clr_eos(); - msg_no_more = FALSE; - } - /* - * If we are in Farsi command mode, the character input must be in - * Insert mode. So do not advance the cmdpos. - */ - if (!cmd_fkmap) { - if (KeyTyped) { - m = Columns * Rows; - if (m < 0) /* overflow, Columns or Rows at weird value */ - m = MAXCOL; - } else - m = MAXCOL; - for (i = 0; i < len; ++i) { - c = cmdline_charsize(ccline.cmdpos); - /* count ">" for a double-wide char that doesn't fit. */ - if (has_mbyte) - correct_cmdspos(ccline.cmdpos, c); - /* Stop cursor at the end of the screen, but do increment the - * insert position, so that entering a very long command - * works, even though you can't see it. */ - if (ccline.cmdspos + c < m) - ccline.cmdspos += c; - if (has_mbyte) { - c = (*mb_ptr2len)(ccline.cmdbuff + ccline.cmdpos) - 1; - if (c > len - i - 1) - c = len - i - 1; - ccline.cmdpos += c; - i += c; - } - ++ccline.cmdpos; - } - } - } - if (redraw) - msg_check(); - return retval; -} - -static struct cmdline_info prev_ccline; -static int prev_ccline_used = FALSE; - -/* - * Save ccline, because obtaining the "=" register may execute "normal :cmd" - * and overwrite it. But get_cmdline_str() may need it, thus make it - * available globally in prev_ccline. - */ -static void save_cmdline(struct cmdline_info *ccp) -{ - if (!prev_ccline_used) { - memset(&prev_ccline, 0, sizeof(struct cmdline_info)); - prev_ccline_used = TRUE; - } - *ccp = prev_ccline; - prev_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; - ccline.xpc = NULL; -} - -/* - * Restore ccline after it has been saved with save_cmdline(). - */ -static void restore_cmdline(struct cmdline_info *ccp) -{ - ccline = prev_ccline; - prev_ccline = *ccp; -} - -/* - * Save the command line into allocated memory. Returns a pointer to be - * passed to restore_cmdline_alloc() later. - * Returns NULL when failed. - */ -char_u *save_cmdline_alloc(void) -{ - struct cmdline_info *p; - - p = (struct cmdline_info *)alloc((unsigned)sizeof(struct cmdline_info)); - if (p != NULL) - save_cmdline(p); - return (char_u *)p; -} - -/* - * Restore the command line from the return value of save_cmdline_alloc(). - */ -void restore_cmdline_alloc(char_u *p) -{ - if (p != NULL) { - restore_cmdline((struct cmdline_info *)p); - free(p); - } -} - -/* - * paste a yank register into the command line. - * used by CTRL-R command in command-line mode - * insert_reg() can't be used here, because special characters from the - * register contents will be interpreted as commands. - * - * return FAIL for failure, OK otherwise - */ -static int -cmdline_paste ( - int regname, - int literally, /* Insert text literally instead of "as typed" */ - int remcr /* remove trailing CR */ -) -{ - long i; - char_u *arg; - char_u *p; - int allocated; - struct cmdline_info save_ccline; - - /* check for valid regname; also accept special characters for CTRL-R in - * the command line */ - if (regname != Ctrl_F && regname != Ctrl_P && regname != Ctrl_W - && regname != Ctrl_A && !valid_yank_reg(regname, FALSE)) - return FAIL; - - /* A register containing CTRL-R can cause an endless loop. Allow using - * CTRL-C to break the loop. */ - line_breakcheck(); - if (got_int) - return FAIL; - - - /* Need to save and restore ccline. And set "textlock" to avoid nasty - * things like going to another buffer when evaluating an expression. */ - save_cmdline(&save_ccline); - ++textlock; - i = get_spec_reg(regname, &arg, &allocated, TRUE); - --textlock; - restore_cmdline(&save_ccline); - - if (i) { - /* Got the value of a special register in "arg". */ - if (arg == NULL) - return FAIL; - - /* When 'incsearch' is set and CTRL-R CTRL-W used: skip the duplicate - * part of the word. */ - p = arg; - if (p_is && regname == Ctrl_W) { - char_u *w; - int len; - - /* Locate start of last word in the cmd buffer. */ - for (w = ccline.cmdbuff + ccline.cmdpos; w > ccline.cmdbuff; ) { - if (has_mbyte) { - len = (*mb_head_off)(ccline.cmdbuff, w - 1) + 1; - if (!vim_iswordc(mb_ptr2char(w - len))) - break; - w -= len; - } else { - if (!vim_iswordc(w[-1])) - break; - --w; - } - } - len = (int)((ccline.cmdbuff + ccline.cmdpos) - w); - if (p_ic ? STRNICMP(w, arg, len) == 0 : STRNCMP(w, arg, len) == 0) - p += len; - } - - cmdline_paste_str(p, literally); - if (allocated) - free(arg); - return OK; - } - - return cmdline_paste_reg(regname, literally, remcr); -} - -/* - * Put a string on the command line. - * When "literally" is TRUE, insert literally. - * When "literally" is FALSE, insert as typed, but don't leave the command - * line. - */ -void cmdline_paste_str(char_u *s, int literally) -{ - int c, cv; - - if (literally) - put_on_cmdline(s, -1, TRUE); - else - while (*s != NUL) { - cv = *s; - if (cv == Ctrl_V && s[1]) - ++s; - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else - c = *s++; - if (cv == Ctrl_V || c == ESC || c == Ctrl_C - || c == CAR || c == NL || c == Ctrl_L -#ifdef UNIX - || c == intr_char -#endif - || (c == Ctrl_BSL && *s == Ctrl_N)) - stuffcharReadbuff(Ctrl_V); - stuffcharReadbuff(c); - } -} - -/* - * Delete characters on the command line, from "from" to the current - * position. - */ -static void cmdline_del(int from) -{ - memmove(ccline.cmdbuff + from, ccline.cmdbuff + ccline.cmdpos, - (size_t)(ccline.cmdlen - ccline.cmdpos + 1)); - ccline.cmdlen -= ccline.cmdpos - from; - ccline.cmdpos = from; -} - -/* - * this function is called when the screen size changes and with incremental - * search - */ -void redrawcmdline(void) -{ - if (cmd_silent) - return; - need_wait_return = FALSE; - compute_cmdrow(); - redrawcmd(); - cursorcmd(); -} - -static void redrawcmdprompt(void) -{ - int i; - - if (cmd_silent) - return; - if (ccline.cmdfirstc != NUL) - msg_putchar(ccline.cmdfirstc); - if (ccline.cmdprompt != NULL) { - msg_puts_attr(ccline.cmdprompt, ccline.cmdattr); - ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns; - /* do the reverse of set_cmdspos() */ - if (ccline.cmdfirstc != NUL) - --ccline.cmdindent; - } else - for (i = ccline.cmdindent; i > 0; --i) - msg_putchar(' '); -} - -/* - * Redraw what is currently on the command line. - */ -void redrawcmd(void) -{ - if (cmd_silent) - return; - - /* when 'incsearch' is set there may be no command line while redrawing */ - if (ccline.cmdbuff == NULL) { - windgoto(cmdline_row, 0); - msg_clr_eos(); - return; - } - - msg_start(); - redrawcmdprompt(); - - /* Don't use more prompt, truncate the cmdline if it doesn't fit. */ - msg_no_more = TRUE; - draw_cmdline(0, ccline.cmdlen); - msg_clr_eos(); - msg_no_more = FALSE; - - set_cmdspos_cursor(); - - /* - * An emsg() before may have set msg_scroll. This is used in normal mode, - * in cmdline mode we can reset them now. - */ - msg_scroll = FALSE; /* next message overwrites cmdline */ - - /* Typing ':' at the more prompt may set skip_redraw. We don't want this - * in cmdline mode */ - skip_redraw = FALSE; -} - -void compute_cmdrow(void) -{ - if (exmode_active || msg_scrolled != 0) - cmdline_row = Rows - 1; - else - cmdline_row = W_WINROW(lastwin) + lastwin->w_height - + W_STATUS_HEIGHT(lastwin); -} - -static void cursorcmd(void) -{ - if (cmd_silent) - return; - - if (cmdmsg_rl) { - msg_row = cmdline_row + (ccline.cmdspos / (int)(Columns - 1)); - msg_col = (int)Columns - (ccline.cmdspos % (int)(Columns - 1)) - 1; - if (msg_row <= 0) - msg_row = Rows - 1; - } else { - msg_row = cmdline_row + (ccline.cmdspos / (int)Columns); - msg_col = ccline.cmdspos % (int)Columns; - if (msg_row >= Rows) - msg_row = Rows - 1; - } - - windgoto(msg_row, msg_col); -} - -void gotocmdline(int clr) -{ - msg_start(); - if (cmdmsg_rl) - msg_col = Columns - 1; - else - msg_col = 0; /* always start in column 0 */ - if (clr) /* clear the bottom line(s) */ - msg_clr_eos(); /* will reset clear_cmdline */ - windgoto(cmdline_row, 0); -} - -/* - * Check the word in front of the cursor for an abbreviation. - * Called when the non-id character "c" has been entered. - * When an abbreviation is recognized it is removed from the text with - * backspaces and the replacement string is inserted, followed by "c". - */ -static int ccheck_abbr(int c) -{ - if (p_paste || no_abbr) /* no abbreviations or in paste mode */ - return FALSE; - - return check_abbr(c, ccline.cmdbuff, ccline.cmdpos, 0); -} - -static int sort_func_compare(const void *s1, const void *s2) -{ - char_u *p1 = *(char_u **)s1; - char_u *p2 = *(char_u **)s2; - - if (*p1 != '<' && *p2 == '<') return -1; - if (*p1 == '<' && *p2 != '<') return 1; - return STRCMP(p1, p2); -} - -/* - * Return FAIL if this is not an appropriate context in which to do - * completion of anything, return OK if it is (even if there are no matches). - * For the caller, this means that the character is just passed through like a - * normal character (instead of being expanded). This allows :s/^I^D etc. - */ -static int -nextwild ( - expand_T *xp, - int type, - int options, /* extra options for ExpandOne() */ - int escape /* if TRUE, escape the returned matches */ -) -{ - int i, j; - char_u *p1; - char_u *p2; - int difflen; - int v; - - if (xp->xp_numfiles == -1) { - set_expand_context(xp); - cmd_showtail = expand_showtail(xp); - } - - if (xp->xp_context == EXPAND_UNSUCCESSFUL) { - beep_flush(); - return OK; /* Something illegal on command line */ - } - if (xp->xp_context == EXPAND_NOTHING) { - /* Caller can use the character as a normal char instead */ - return FAIL; - } - - MSG_PUTS("..."); /* show that we are busy */ - out_flush(); - - i = (int)(xp->xp_pattern - ccline.cmdbuff); - xp->xp_pattern_len = ccline.cmdpos - i; - - if (type == WILD_NEXT || type == WILD_PREV) { - /* - * Get next/previous match for a previous expanded pattern. - */ - p2 = ExpandOne(xp, NULL, NULL, 0, type); - } else { - /* - * Translate string into pattern and expand it. - */ - if ((p1 = addstar(xp->xp_pattern, xp->xp_pattern_len, - xp->xp_context)) == NULL) - p2 = NULL; - else { - int use_options = options | - WILD_HOME_REPLACE|WILD_ADD_SLASH|WILD_SILENT; - if (escape) - use_options |= WILD_ESCAPE; - - if (p_wic) - use_options += WILD_ICASE; - p2 = ExpandOne(xp, p1, - vim_strnsave(&ccline.cmdbuff[i], xp->xp_pattern_len), - use_options, type); - free(p1); - /* longest match: make sure it is not shorter, happens with :help */ - if (p2 != NULL && type == WILD_LONGEST) { - for (j = 0; j < xp->xp_pattern_len; ++j) - if (ccline.cmdbuff[i + j] == '*' - || ccline.cmdbuff[i + j] == '?') - break; - if ((int)STRLEN(p2) < j) { - free(p2); - p2 = NULL; - } - } - } - } - - if (p2 != NULL && !got_int) { - difflen = (int)STRLEN(p2) - xp->xp_pattern_len; - if (ccline.cmdlen + difflen + 4 > ccline.cmdbufflen) { - v = realloc_cmdbuff(ccline.cmdlen + difflen + 4); - xp->xp_pattern = ccline.cmdbuff + i; - } else - v = OK; - if (v == OK) { - memmove(&ccline.cmdbuff[ccline.cmdpos + difflen], - &ccline.cmdbuff[ccline.cmdpos], - (size_t)(ccline.cmdlen - ccline.cmdpos + 1)); - memmove(&ccline.cmdbuff[i], p2, STRLEN(p2)); - ccline.cmdlen += difflen; - ccline.cmdpos += difflen; - } - } - free(p2); - - redrawcmd(); - cursorcmd(); - - /* When expanding a ":map" command and no matches are found, assume that - * the key is supposed to be inserted literally */ - if (xp->xp_context == EXPAND_MAPPINGS && p2 == NULL) - return FAIL; - - if (xp->xp_numfiles <= 0 && p2 == NULL) - beep_flush(); - else if (xp->xp_numfiles == 1) - /* free expanded pattern */ - (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE); - - return OK; -} - -/* - * Do wildcard expansion on the string 'str'. - * Chars that should not be expanded must be preceded with a backslash. - * Return a pointer to allocated memory containing the new string. - * Return NULL for failure. - * - * "orig" is the originally expanded string, copied to allocated memory. It - * should either be kept in orig_save or freed. When "mode" is WILD_NEXT or - * WILD_PREV "orig" should be NULL. - * - * Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode" - * is WILD_EXPAND_FREE or WILD_ALL. - * - * mode = WILD_FREE: just free previously expanded matches - * mode = WILD_EXPAND_FREE: normal expansion, do not keep matches - * mode = WILD_EXPAND_KEEP: normal expansion, keep matches - * mode = WILD_NEXT: use next match in multiple match, wrap to first - * mode = WILD_PREV: use previous match in multiple match, wrap to first - * mode = WILD_ALL: return all matches concatenated - * mode = WILD_LONGEST: return longest matched part - * mode = WILD_ALL_KEEP: get all matches, keep matches - * - * options = WILD_LIST_NOTFOUND: list entries without a match - * options = WILD_HOME_REPLACE: do home_replace() for buffer names - * options = WILD_USE_NL: Use '\n' for WILD_ALL - * options = WILD_NO_BEEP: Don't beep for multiple matches - * options = WILD_ADD_SLASH: add a slash after directory names - * options = WILD_KEEP_ALL: don't remove 'wildignore' entries - * options = WILD_SILENT: don't print warning messages - * options = WILD_ESCAPE: put backslash before special chars - * options = WILD_ICASE: ignore case for files - * - * The variables xp->xp_context and xp->xp_backslash must have been set! - */ -char_u * -ExpandOne ( - expand_T *xp, - char_u *str, - char_u *orig, /* allocated copy of original of expanded string */ - int options, - int mode -) -{ - char_u *ss = NULL; - static int findex; - static char_u *orig_save = NULL; /* kept value of orig */ - int orig_saved = FALSE; - int i; - int non_suf_match; /* number without matching suffix */ - - /* - * first handle the case of using an old match - */ - if (mode == WILD_NEXT || mode == WILD_PREV) { - if (xp->xp_numfiles > 0) { - if (mode == WILD_PREV) { - if (findex == -1) - findex = xp->xp_numfiles; - --findex; - } else /* mode == WILD_NEXT */ - ++findex; - - /* - * When wrapping around, return the original string, set findex to - * -1. - */ - if (findex < 0) { - if (orig_save == NULL) - findex = xp->xp_numfiles - 1; - else - findex = -1; - } - if (findex >= xp->xp_numfiles) { - if (orig_save == NULL) - findex = 0; - else - findex = -1; - } - if (p_wmnu) - win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, - findex, cmd_showtail); - if (findex == -1) - return vim_strsave(orig_save); - return vim_strsave(xp->xp_files[findex]); - } else - return NULL; - } - - /* free old names */ - if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) { - FreeWild(xp->xp_numfiles, xp->xp_files); - xp->xp_numfiles = -1; - free(orig_save); - orig_save = NULL; - } - findex = 0; - - if (mode == WILD_FREE) /* only release file name */ - return NULL; - - if (xp->xp_numfiles == -1) { - free(orig_save); - orig_save = orig; - orig_saved = TRUE; - - /* - * Do the expansion. - */ - if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, - options) == FAIL) { -#ifdef FNAME_ILLEGAL - /* Illegal file name has been silently skipped. But when there - * are wildcards, the real problem is that there was no match, - * causing the pattern to be added, which has illegal characters. - */ - if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND)) - EMSG2(_(e_nomatch2), str); -#endif - } else if (xp->xp_numfiles == 0) { - if (!(options & WILD_SILENT)) - EMSG2(_(e_nomatch2), str); - } else { - /* Escape the matches for use on the command line. */ - ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options); - - /* - * Check for matching suffixes in file names. - */ - if (mode != WILD_ALL && mode != WILD_ALL_KEEP - && mode != WILD_LONGEST) { - if (xp->xp_numfiles) - non_suf_match = xp->xp_numfiles; - else - non_suf_match = 1; - if ((xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_DIRECTORIES) - && xp->xp_numfiles > 1) { - /* - * More than one match; check suffix. - * The files will have been sorted on matching suffix in - * expand_wildcards, only need to check the first two. - */ - non_suf_match = 0; - for (i = 0; i < 2; ++i) - if (match_suffix(xp->xp_files[i])) - ++non_suf_match; - } - if (non_suf_match != 1) { - /* Can we ever get here unless it's while expanding - * interactively? If not, we can get rid of this all - * together. Don't really want to wait for this message - * (and possibly have to hit return to continue!). - */ - if (!(options & WILD_SILENT)) - EMSG(_(e_toomany)); - else if (!(options & WILD_NO_BEEP)) - beep_flush(); - } - if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) - ss = vim_strsave(xp->xp_files[0]); - } - } - } - - /* Find longest common part */ - if (mode == WILD_LONGEST && xp->xp_numfiles > 0) { - size_t len; - for (len = 0; xp->xp_files[0][len]; ++len) { - for (i = 0; i < xp->xp_numfiles; ++i) { - if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES - || xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_SHELLCMD - || xp->xp_context == EXPAND_BUFFERS)) { - if (TOLOWER_LOC(xp->xp_files[i][len]) != - TOLOWER_LOC(xp->xp_files[0][len])) - break; - } else if (xp->xp_files[i][len] != xp->xp_files[0][len]) - break; - } - if (i < xp->xp_numfiles) { - if (!(options & WILD_NO_BEEP)) - vim_beep(); - break; - } - } - ss = (char_u *)xstrndup((char *)xp->xp_files[0], len); - findex = -1; /* next p_wc gets first one */ - } - - // Concatenate all matching names - // TODO(philix): use xstpcpy instead of strcat in a loop (ExpandOne) - if (mode == WILD_ALL && xp->xp_numfiles > 0) { - size_t len = 0; - for (i = 0; i < xp->xp_numfiles; ++i) - len += STRLEN(xp->xp_files[i]) + 1; - ss = xmalloc(len); - *ss = NUL; - for (i = 0; i < xp->xp_numfiles; ++i) { - STRCAT(ss, xp->xp_files[i]); - if (i != xp->xp_numfiles - 1) - STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " "); - } - } - - if (mode == WILD_EXPAND_FREE || mode == WILD_ALL) - ExpandCleanup(xp); - - /* Free "orig" if it wasn't stored in "orig_save". */ - if (!orig_saved) - free(orig); - - return ss; -} - -/* - * Prepare an expand structure for use. - */ -void ExpandInit(expand_T *xp) -{ - xp->xp_pattern = NULL; - xp->xp_pattern_len = 0; - xp->xp_backslash = XP_BS_NONE; -#ifndef BACKSLASH_IN_FILENAME - xp->xp_shell = FALSE; -#endif - xp->xp_numfiles = -1; - xp->xp_files = NULL; - xp->xp_arg = NULL; - xp->xp_line = NULL; -} - -/* - * Cleanup an expand structure after use. - */ -void ExpandCleanup(expand_T *xp) -{ - if (xp->xp_numfiles >= 0) { - FreeWild(xp->xp_numfiles, xp->xp_files); - xp->xp_numfiles = -1; - } -} - -void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int options) -{ - int i; - char_u *p; - - /* - * May change home directory back to "~" - */ - if (options & WILD_HOME_REPLACE) - tilde_replace(str, numfiles, files); - - if (options & WILD_ESCAPE) { - if (xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_FILES_IN_PATH - || xp->xp_context == EXPAND_SHELLCMD - || xp->xp_context == EXPAND_BUFFERS - || xp->xp_context == EXPAND_DIRECTORIES) { - /* - * Insert a backslash into a file name before a space, \, %, # - * and wildmatch characters, except '~'. - */ - for (i = 0; i < numfiles; ++i) { - /* for ":set path=" we need to escape spaces twice */ - if (xp->xp_backslash == XP_BS_THREE) { - p = vim_strsave_escaped(files[i], (char_u *)" "); - if (p != NULL) { - free(files[i]); - files[i] = p; -#if defined(BACKSLASH_IN_FILENAME) - p = vim_strsave_escaped(files[i], (char_u *)" "); - if (p != NULL) { - free(files[i]); - files[i] = p; - } -#endif - } - } -#ifdef BACKSLASH_IN_FILENAME - p = vim_strsave_fnameescape(files[i], FALSE); -#else - p = vim_strsave_fnameescape(files[i], xp->xp_shell); -#endif - if (p != NULL) { - free(files[i]); - files[i] = p; - } - - /* If 'str' starts with "\~", replace "~" at start of - * files[i] with "\~". */ - if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~') - escape_fname(&files[i]); - } - xp->xp_backslash = XP_BS_NONE; - - /* If the first file starts with a '+' escape it. Otherwise it - * could be seen as "+cmd". */ - if (*files[0] == '+') - escape_fname(&files[0]); - } else if (xp->xp_context == EXPAND_TAGS) { - /* - * Insert a backslash before characters in a tag name that - * would terminate the ":tag" command. - */ - for (i = 0; i < numfiles; ++i) { - p = vim_strsave_escaped(files[i], (char_u *)"\\|\""); - if (p != NULL) { - free(files[i]); - files[i] = p; - } - } - } - } -} - -/* - * Escape special characters in "fname" for when used as a file name argument - * after a Vim command, or, when "shell" is non-zero, a shell command. - * Returns the result in allocated memory. - */ -char_u *vim_strsave_fnameescape(char_u *fname, int shell) -{ - char_u *p; -#ifdef BACKSLASH_IN_FILENAME - char_u buf[20]; - int j = 0; - - /* Don't escape '[', '{' and '!' if they are in 'isfname'. */ - for (p = PATH_ESC_CHARS; *p != NUL; ++p) - if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) - buf[j++] = *p; - buf[j] = NUL; - p = vim_strsave_escaped(fname, buf); -#else - p = vim_strsave_escaped(fname, shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS); - if (shell && csh_like_shell() && p != NULL) { - char_u *s; - - /* For csh and similar shells need to put two backslashes before '!'. - * One is taken by Vim, one by the shell. */ - s = vim_strsave_escaped(p, (char_u *)"!"); - free(p); - p = s; - } -#endif - - /* '>' and '+' are special at the start of some commands, e.g. ":edit" and - * ":write". "cd -" has a special meaning. */ - if (p != NULL && (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL))) - escape_fname(&p); - - return p; -} - -/* - * Put a backslash before the file name in "pp", which is in allocated memory. - */ -static void escape_fname(char_u **pp) -{ - char_u *p; - - p = alloc((unsigned)(STRLEN(*pp) + 2)); - if (p != NULL) { - p[0] = '\\'; - STRCPY(p + 1, *pp); - free(*pp); - *pp = p; - } -} - -/* - * For each file name in files[num_files]: - * If 'orig_pat' starts with "~/", replace the home directory with "~". - */ -void tilde_replace(char_u *orig_pat, int num_files, char_u **files) -{ - int i; - char_u *p; - - if (orig_pat[0] == '~' && vim_ispathsep(orig_pat[1])) { - for (i = 0; i < num_files; ++i) { - p = home_replace_save(NULL, files[i]); - if (p != NULL) { - free(files[i]); - files[i] = p; - } - } - } -} - -/* - * Show all matches for completion on the command line. - * Returns EXPAND_NOTHING when the character that triggered expansion should - * be inserted like a normal character. - */ -static int showmatches(expand_T *xp, int wildmenu) -{ -#define L_SHOWFILE(m) (showtail ? sm_gettail(files_found[m]) : files_found[m]) - int num_files; - char_u **files_found; - int i, j, k; - int maxlen; - int lines; - int columns; - char_u *p; - int lastlen; - int attr; - int showtail; - - if (xp->xp_numfiles == -1) { - set_expand_context(xp); - i = expand_cmdline(xp, ccline.cmdbuff, ccline.cmdpos, - &num_files, &files_found); - showtail = expand_showtail(xp); - if (i != EXPAND_OK) - return i; - - } else { - num_files = xp->xp_numfiles; - files_found = xp->xp_files; - showtail = cmd_showtail; - } - - if (!wildmenu) { - msg_didany = FALSE; /* lines_left will be set */ - msg_start(); /* prepare for paging */ - msg_putchar('\n'); - out_flush(); - cmdline_row = msg_row; - msg_didany = FALSE; /* lines_left will be set again */ - msg_start(); /* prepare for paging */ - } - - if (got_int) - got_int = FALSE; /* only int. the completion, not the cmd line */ - else if (wildmenu) - win_redr_status_matches(xp, num_files, files_found, 0, showtail); - else { - /* find the length of the longest file name */ - maxlen = 0; - for (i = 0; i < num_files; ++i) { - if (!showtail && (xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_SHELLCMD - || xp->xp_context == EXPAND_BUFFERS)) { - home_replace(NULL, files_found[i], NameBuff, MAXPATHL, TRUE); - j = vim_strsize(NameBuff); - } else - j = vim_strsize(L_SHOWFILE(i)); - if (j > maxlen) - maxlen = j; - } - - if (xp->xp_context == EXPAND_TAGS_LISTFILES) - lines = num_files; - else { - /* compute the number of columns and lines for the listing */ - maxlen += 2; /* two spaces between file names */ - columns = ((int)Columns + 2) / maxlen; - if (columns < 1) - columns = 1; - lines = (num_files + columns - 1) / columns; - } - - attr = hl_attr(HLF_D); /* find out highlighting for directories */ - - if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - MSG_PUTS_ATTR(_("tagname"), hl_attr(HLF_T)); - msg_clr_eos(); - msg_advance(maxlen - 3); - MSG_PUTS_ATTR(_(" kind file\n"), hl_attr(HLF_T)); - } - - /* list the files line by line */ - for (i = 0; i < lines; ++i) { - lastlen = 999; - for (k = i; k < num_files; k += lines) { - if (xp->xp_context == EXPAND_TAGS_LISTFILES) { - msg_outtrans_attr(files_found[k], hl_attr(HLF_D)); - p = files_found[k] + STRLEN(files_found[k]) + 1; - msg_advance(maxlen + 1); - msg_puts(p); - msg_advance(maxlen + 3); - msg_puts_long_attr(p + 2, hl_attr(HLF_D)); - break; - } - for (j = maxlen - lastlen; --j >= 0; ) - msg_putchar(' '); - if (xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_SHELLCMD - || xp->xp_context == EXPAND_BUFFERS) { - /* highlight directories */ - if (xp->xp_numfiles != -1) { - char_u *halved_slash; - char_u *exp_path; - - /* Expansion was done before and special characters - * were escaped, need to halve backslashes. Also - * $HOME has been replaced with ~/. */ - exp_path = expand_env_save_opt(files_found[k], TRUE); - halved_slash = backslash_halve_save( - exp_path != NULL ? exp_path : files_found[k]); - j = os_isdir(halved_slash != NULL ? halved_slash - : files_found[k]); - free(exp_path); - free(halved_slash); - } else - /* Expansion was done here, file names are literal. */ - j = os_isdir(files_found[k]); - if (showtail) - p = L_SHOWFILE(k); - else { - home_replace(NULL, files_found[k], NameBuff, MAXPATHL, - TRUE); - p = NameBuff; - } - } else { - j = FALSE; - p = L_SHOWFILE(k); - } - lastlen = msg_outtrans_attr(p, j ? attr : 0); - } - if (msg_col > 0) { /* when not wrapped around */ - msg_clr_eos(); - msg_putchar('\n'); - } - out_flush(); /* show one line at a time */ - if (got_int) { - got_int = FALSE; - break; - } - } - - /* - * we redraw the command below the lines that we have just listed - * This is a bit tricky, but it saves a lot of screen updating. - */ - cmdline_row = msg_row; /* will put it back later */ - } - - if (xp->xp_numfiles == -1) - FreeWild(num_files, files_found); - - return EXPAND_OK; -} - -/* - * Private path_tail for showmatches() (and win_redr_status_matches()): - * Find tail of file name path, but ignore trailing "/". - */ -char_u *sm_gettail(char_u *s) -{ - char_u *p; - char_u *t = s; - int had_sep = FALSE; - - for (p = s; *p != NUL; ) { - if (vim_ispathsep(*p) -#ifdef BACKSLASH_IN_FILENAME - && !rem_backslash(p) -#endif - ) - had_sep = TRUE; - else if (had_sep) { - t = p; - had_sep = FALSE; - } - mb_ptr_adv(p); - } - return t; -} - -/* - * Return TRUE if we only need to show the tail of completion matches. - * When not completing file names or there is a wildcard in the path FALSE is - * returned. - */ -static int expand_showtail(expand_T *xp) -{ - char_u *s; - char_u *end; - - /* When not completing file names a "/" may mean something different. */ - if (xp->xp_context != EXPAND_FILES - && xp->xp_context != EXPAND_SHELLCMD - && xp->xp_context != EXPAND_DIRECTORIES) - return FALSE; - - end = path_tail(xp->xp_pattern); - if (end == xp->xp_pattern) /* there is no path separator */ - return FALSE; - - for (s = xp->xp_pattern; s < end; s++) { - /* Skip escaped wildcards. Only when the backslash is not a path - * separator, on DOS the '*' "path\*\file" must not be skipped. */ - if (rem_backslash(s)) - ++s; - else if (vim_strchr((char_u *)"*?[", *s) != NULL) - return FALSE; - } - return TRUE; -} - -/* - * Prepare a string for expansion. - * When expanding file names: The string will be used with expand_wildcards(). - * Copy "fname[len]" into allocated memory and add a '*' at the end. - * When expanding other names: The string will be used with regcomp(). Copy - * the name into allocated memory and prepend "^". - */ -char_u * -addstar ( - char_u *fname, - int len, - int context /* EXPAND_FILES etc. */ -) -{ - char_u *retval; - int i, j; - int new_len; - char_u *tail; - int ends_in_star; - - if (context != EXPAND_FILES - && context != EXPAND_FILES_IN_PATH - && context != EXPAND_SHELLCMD - && context != EXPAND_DIRECTORIES) { - /* - * Matching will be done internally (on something other than files). - * So we convert the file-matching-type wildcards into our kind for - * use with vim_regcomp(). First work out how long it will be: - */ - - /* For help tags the translation is done in find_help_tags(). - * For a tag pattern starting with "/" no translation is needed. */ - if (context == EXPAND_HELP - || context == EXPAND_COLORS - || context == EXPAND_COMPILER - || context == EXPAND_OWNSYNTAX - || context == EXPAND_FILETYPE - || (context == EXPAND_TAGS && fname[0] == '/')) - retval = vim_strnsave(fname, len); - else { - new_len = len + 2; /* +2 for '^' at start, NUL at end */ - for (i = 0; i < len; i++) { - if (fname[i] == '*' || fname[i] == '~') - new_len++; /* '*' needs to be replaced by ".*" - '~' needs to be replaced by "\~" */ - - /* Buffer names are like file names. "." should be literal */ - if (context == EXPAND_BUFFERS && fname[i] == '.') - new_len++; /* "." becomes "\." */ - - /* Custom expansion takes care of special things, match - * backslashes literally (perhaps also for other types?) */ - if ((context == EXPAND_USER_DEFINED - || context == EXPAND_USER_LIST) && fname[i] == '\\') - new_len++; /* '\' becomes "\\" */ - } - retval = alloc(new_len); - if (retval != NULL) { - retval[0] = '^'; - j = 1; - for (i = 0; i < len; i++, j++) { - /* Skip backslash. But why? At least keep it for custom - * expansion. */ - if (context != EXPAND_USER_DEFINED - && context != EXPAND_USER_LIST - && fname[i] == '\\' - && ++i == len) - break; - - switch (fname[i]) { - case '*': retval[j++] = '.'; - break; - case '~': retval[j++] = '\\'; - break; - case '?': retval[j] = '.'; - continue; - case '.': if (context == EXPAND_BUFFERS) - retval[j++] = '\\'; - break; - case '\\': if (context == EXPAND_USER_DEFINED - || context == EXPAND_USER_LIST) - retval[j++] = '\\'; - break; - } - retval[j] = fname[i]; - } - retval[j] = NUL; - } - } - } else { - retval = alloc(len + 4); - if (retval != NULL) { - vim_strncpy(retval, fname, len); - - /* - * Don't add a star to *, ~, ~user, $var or `cmd`. - * * would become **, which walks the whole tree. - * ~ would be at the start of the file name, but not the tail. - * $ could be anywhere in the tail. - * ` could be anywhere in the file name. - * When the name ends in '$' don't add a star, remove the '$'. - */ - tail = path_tail(retval); - ends_in_star = (len > 0 && retval[len - 1] == '*'); -#ifndef BACKSLASH_IN_FILENAME - for (i = len - 2; i >= 0; --i) { - if (retval[i] != '\\') - break; - ends_in_star = !ends_in_star; - } -#endif - if ((*retval != '~' || tail != retval) - && !ends_in_star - && vim_strchr(tail, '$') == NULL - && vim_strchr(retval, '`') == NULL) - retval[len++] = '*'; - else if (len > 0 && retval[len - 1] == '$') - --len; - retval[len] = NUL; - } - } - return retval; -} - -/* - * Must parse the command line so far to work out what context we are in. - * Completion can then be done based on that context. - * This routine sets the variables: - * xp->xp_pattern The start of the pattern to be expanded within - * the command line (ends at the cursor). - * xp->xp_context The type of thing to expand. Will be one of: - * - * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on - * the command line, like an unknown command. Caller - * should beep. - * EXPAND_NOTHING Unrecognised context for completion, use char like - * a normal char, rather than for completion. eg - * :s/^I/ - * EXPAND_COMMANDS Cursor is still touching the command, so complete - * it. - * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands. - * EXPAND_FILES After command with XFILE set, or after setting - * with P_EXPAND set. eg :e ^I, :w>>^I - * EXPAND_DIRECTORIES In some cases this is used instead of the latter - * when we know only directories are of interest. eg - * :set dir=^I - * EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd". - * EXPAND_SETTINGS Complete variable names. eg :set d^I - * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I - * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I - * EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect - * EXPAND_HELP Complete tags from the file 'helpfile'/tags - * EXPAND_EVENTS Complete event names - * EXPAND_SYNTAX Complete :syntax command arguments - * EXPAND_HIGHLIGHT Complete highlight (syntax) group names - * EXPAND_AUGROUP Complete autocommand group names - * EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I - * EXPAND_MAPPINGS Complete mapping and abbreviation names, - * eg :unmap a^I , :cunab x^I - * EXPAND_FUNCTIONS Complete internal or user defined function names, - * eg :call sub^I - * EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I - * EXPAND_EXPRESSION Complete internal or user defined function/variable - * names in expressions, eg :while s^I - * EXPAND_ENV_VARS Complete environment variable names - * EXPAND_USER Complete user names - */ -static void set_expand_context(expand_T *xp) -{ - /* only expansion for ':', '>' and '=' command-lines */ - if (ccline.cmdfirstc != ':' - && ccline.cmdfirstc != '>' && ccline.cmdfirstc != '=' - && !ccline.input_fn - ) { - xp->xp_context = EXPAND_NOTHING; - return; - } - set_cmd_context(xp, ccline.cmdbuff, ccline.cmdlen, ccline.cmdpos); -} - -void -set_cmd_context ( - expand_T *xp, - char_u *str, /* start of command line */ - int len, /* length of command line (excl. NUL) */ - int col /* position of cursor */ -) -{ - int old_char = NUL; - char_u *nextcomm; - - /* - * Avoid a UMR warning from Purify, only save the character if it has been - * written before. - */ - if (col < len) - old_char = str[col]; - str[col] = NUL; - nextcomm = str; - - if (ccline.cmdfirstc == '=') { - /* pass CMD_SIZE because there is no real command */ - set_context_for_expression(xp, str, CMD_SIZE); - } else if (ccline.input_fn) { - xp->xp_context = ccline.xp_context; - xp->xp_pattern = ccline.cmdbuff; - xp->xp_arg = ccline.xp_arg; - } else - while (nextcomm != NULL) - nextcomm = set_one_cmd_context(xp, nextcomm); - - /* Store the string here so that call_user_expand_func() can get to them - * easily. */ - xp->xp_line = str; - xp->xp_col = col; - - str[col] = old_char; -} - -/* - * Expand the command line "str" from context "xp". - * "xp" must have been set by set_cmd_context(). - * xp->xp_pattern points into "str", to where the text that is to be expanded - * starts. - * Returns EXPAND_UNSUCCESSFUL when there is something illegal before the - * cursor. - * Returns EXPAND_NOTHING when there is nothing to expand, might insert the - * key that triggered expansion literally. - * Returns EXPAND_OK otherwise. - */ -int -expand_cmdline ( - expand_T *xp, - char_u *str, /* start of command line */ - int col, /* position of cursor */ - int *matchcount, /* return: nr of matches */ - char_u ***matches /* return: array of pointers to matches */ -) -{ - char_u *file_str = NULL; - int options = WILD_ADD_SLASH|WILD_SILENT; - - if (xp->xp_context == EXPAND_UNSUCCESSFUL) { - beep_flush(); - return EXPAND_UNSUCCESSFUL; /* Something illegal on command line */ - } - if (xp->xp_context == EXPAND_NOTHING) { - /* Caller can use the character as a normal char instead */ - return EXPAND_NOTHING; - } - - /* add star to file name, or convert to regexp if not exp. files. */ - xp->xp_pattern_len = (int)(str + col - xp->xp_pattern); - file_str = addstar(xp->xp_pattern, xp->xp_pattern_len, xp->xp_context); - if (file_str == NULL) - return EXPAND_UNSUCCESSFUL; - - if (p_wic) - options += WILD_ICASE; - - /* find all files that match the description */ - if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) { - *matchcount = 0; - *matches = NULL; - } - free(file_str); - - return EXPAND_OK; -} - -/* - * Cleanup matches for help tags: remove "@en" if "en" is the only language. - */ -static void cleanup_help_tags(int num_file, char_u **file); - -static void cleanup_help_tags(int num_file, char_u **file) -{ - int i, j; - int len; - - for (i = 0; i < num_file; ++i) { - len = (int)STRLEN(file[i]) - 3; - if (len > 0 && STRCMP(file[i] + len, "@en") == 0) { - /* Sorting on priority means the same item in another language may - * be anywhere. Search all items for a match up to the "@en". */ - for (j = 0; j < num_file; ++j) - if (j != i - && (int)STRLEN(file[j]) == len + 3 - && STRNCMP(file[i], file[j], len + 1) == 0) - break; - if (j == num_file) - file[i][len] = NUL; - } - } -} - -/* - * Do the expansion based on xp->xp_context and "pat". - */ -static int -ExpandFromContext ( - expand_T *xp, - char_u *pat, - int *num_file, - char_u ***file, - int options /* EW_ flags */ -) -{ - regmatch_T regmatch; - int ret; - int flags; - - flags = EW_DIR; /* include directories */ - if (options & WILD_LIST_NOTFOUND) - flags |= EW_NOTFOUND; - if (options & WILD_ADD_SLASH) - flags |= EW_ADDSLASH; - if (options & WILD_KEEP_ALL) - flags |= EW_KEEPALL; - if (options & WILD_SILENT) - flags |= EW_SILENT; - - if (xp->xp_context == EXPAND_FILES - || xp->xp_context == EXPAND_DIRECTORIES - || xp->xp_context == EXPAND_FILES_IN_PATH) { - /* - * Expand file or directory names. - */ - int free_pat = FALSE; - int i; - - /* for ":set path=" and ":set tags=" halve backslashes for escaped - * space */ - if (xp->xp_backslash != XP_BS_NONE) { - free_pat = TRUE; - pat = vim_strsave(pat); - for (i = 0; pat[i]; ++i) - if (pat[i] == '\\') { - if (xp->xp_backslash == XP_BS_THREE - && pat[i + 1] == '\\' - && pat[i + 2] == '\\' - && pat[i + 3] == ' ') - STRMOVE(pat + i, pat + i + 3); - if (xp->xp_backslash == XP_BS_ONE - && pat[i + 1] == ' ') - STRMOVE(pat + i, pat + i + 1); - } - } - - if (xp->xp_context == EXPAND_FILES) - flags |= EW_FILE; - else if (xp->xp_context == EXPAND_FILES_IN_PATH) - flags |= (EW_FILE | EW_PATH); - else - flags = (flags | EW_DIR) & ~EW_FILE; - if (options & WILD_ICASE) - flags |= EW_ICASE; - - /* Expand wildcards, supporting %:h and the like. */ - ret = expand_wildcards_eval(&pat, num_file, file, flags); - if (free_pat) - free(pat); - return ret; - } - - *file = (char_u **)""; - *num_file = 0; - if (xp->xp_context == EXPAND_HELP) { - /* With an empty argument we would get all the help tags, which is - * very slow. Get matches for "help" instead. */ - if (find_help_tags(*pat == NUL ? (char_u *)"help" : pat, - num_file, file, FALSE) == OK) { - cleanup_help_tags(*num_file, *file); - return OK; - } - return FAIL; - } - - if (xp->xp_context == EXPAND_SHELLCMD) - return expand_shellcmd(pat, num_file, file, flags); - if (xp->xp_context == EXPAND_OLD_SETTING) - return ExpandOldSetting(num_file, file); - if (xp->xp_context == EXPAND_BUFFERS) - return ExpandBufnames(pat, num_file, file, options); - if (xp->xp_context == EXPAND_TAGS - || xp->xp_context == EXPAND_TAGS_LISTFILES) - return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file); - if (xp->xp_context == EXPAND_COLORS) { - char *directories[] = {"colors", NULL}; - return ExpandRTDir(pat, num_file, file, directories); - } - if (xp->xp_context == EXPAND_COMPILER) { - char *directories[] = {"compiler", NULL}; - return ExpandRTDir(pat, num_file, file, directories); - } - if (xp->xp_context == EXPAND_OWNSYNTAX) { - char *directories[] = {"syntax", NULL}; - return ExpandRTDir(pat, num_file, file, directories); - } - if (xp->xp_context == EXPAND_FILETYPE) { - char *directories[] = {"syntax", "indent", "ftplugin", NULL}; - return ExpandRTDir(pat, num_file, file, directories); - } - if (xp->xp_context == EXPAND_USER_LIST) - return ExpandUserList(xp, num_file, file); - - regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) - return FAIL; - - /* set ignore-case according to p_ic, p_scs and pat */ - regmatch.rm_ic = ignorecase(pat); - - if (xp->xp_context == EXPAND_SETTINGS - || xp->xp_context == EXPAND_BOOL_SETTINGS) - ret = ExpandSettings(xp, ®match, num_file, file); - else if (xp->xp_context == EXPAND_MAPPINGS) - ret = ExpandMappings(®match, num_file, file); - else if (xp->xp_context == EXPAND_USER_DEFINED) - ret = ExpandUserDefined(xp, ®match, num_file, file); - else { - static struct expgen { - int context; - char_u *((*func)(expand_T *, int)); - int ic; - int escaped; - } tab[] = - { - {EXPAND_COMMANDS, get_command_name, FALSE, TRUE}, - {EXPAND_BEHAVE, get_behave_arg, TRUE, TRUE}, - {EXPAND_HISTORY, get_history_arg, TRUE, TRUE}, - {EXPAND_USER_COMMANDS, get_user_commands, FALSE, TRUE}, - {EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, FALSE, TRUE}, - {EXPAND_USER_NARGS, get_user_cmd_nargs, FALSE, TRUE}, - {EXPAND_USER_COMPLETE, get_user_cmd_complete, FALSE, TRUE}, - {EXPAND_USER_VARS, get_user_var_name, FALSE, TRUE}, - {EXPAND_FUNCTIONS, get_function_name, FALSE, TRUE}, - {EXPAND_USER_FUNC, get_user_func_name, FALSE, TRUE}, - {EXPAND_EXPRESSION, get_expr_name, FALSE, TRUE}, - {EXPAND_MENUS, get_menu_name, FALSE, TRUE}, - {EXPAND_MENUNAMES, get_menu_names, FALSE, TRUE}, - {EXPAND_SYNTAX, get_syntax_name, TRUE, TRUE}, - {EXPAND_SYNTIME, get_syntime_arg, TRUE, TRUE}, - {EXPAND_HIGHLIGHT, get_highlight_name, TRUE, TRUE}, - {EXPAND_EVENTS, get_event_name, TRUE, TRUE}, - {EXPAND_AUGROUP, get_augroup_name, TRUE, TRUE}, - {EXPAND_CSCOPE, get_cscope_name, TRUE, TRUE}, - {EXPAND_PROFILE, get_profile_name, TRUE, TRUE}, -#ifdef HAVE_WORKING_LIBINTL - {EXPAND_LANGUAGE, get_lang_arg, TRUE, FALSE}, - {EXPAND_LOCALES, get_locales, TRUE, FALSE}, -#endif - {EXPAND_ENV_VARS, get_env_name, TRUE, TRUE}, - {EXPAND_USER, get_users, TRUE, FALSE}, - }; - int i; - - /* - * Find a context in the table and call the ExpandGeneric() with the - * right function to do the expansion. - */ - ret = FAIL; - for (i = 0; i < (int)(sizeof(tab) / sizeof(struct expgen)); ++i) - if (xp->xp_context == tab[i].context) { - if (tab[i].ic) - regmatch.rm_ic = TRUE; - ret = ExpandGeneric(xp, ®match, num_file, file, - tab[i].func, tab[i].escaped); - break; - } - } - - vim_regfree(regmatch.regprog); - - return ret; -} - -/* - * Expand a list of names. - * - * Generic function for command line completion. It calls a function to - * obtain strings, one by one. The strings are matched against a regexp - * program. Matching strings are copied into an array, which is returned. - * - * Returns OK when no problems encountered, FAIL for error (out of memory). - */ -int ExpandGeneric(xp, regmatch, num_file, file, func, escaped) -expand_T *xp; -regmatch_T *regmatch; -int *num_file; -char_u ***file; -char_u *((*func)(expand_T *, int)); -/* returns a string from the list */ -int escaped; -{ - int i; - int count = 0; - char_u *str; - - // count the number of matching names - for (i = 0;; ++i) { - str = (*func)(xp, i); - if (str == NULL) // end of list - break; - if (*str == NUL) // skip empty strings - continue; - if (vim_regexec(regmatch, str, (colnr_T)0)) { - ++count; - } - } - if (count == 0) - return OK; - *num_file = count; - *file = (char_u **)alloc((unsigned)(count * sizeof(char_u *))); - if (*file == NULL) { - *file = (char_u **)""; - return FAIL; - } - - // copy the matching names into allocated memory - count = 0; - for (i = 0;; ++i) { - str = (*func)(xp, i); - if (str == NULL) // end of list - break; - if (*str == NUL) // skip empty strings - continue; - if (vim_regexec(regmatch, str, (colnr_T)0)) { - if (escaped) - str = vim_strsave_escaped(str, (char_u *)" \t\\."); - else - str = vim_strsave(str); - (*file)[count++] = str; - if (func == get_menu_names && str != NULL) { - /* test for separator added by get_menu_names() */ - str += STRLEN(str) - 1; - if (*str == '\001') - *str = '.'; - } - } - } - - /* Sort the results. Keep menu's in the specified order. */ - if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) { - if (xp->xp_context == EXPAND_EXPRESSION - || xp->xp_context == EXPAND_FUNCTIONS - || xp->xp_context == EXPAND_USER_FUNC) - /* functions should be sorted to the end. */ - qsort((void *)*file, (size_t)*num_file, sizeof(char_u *), - sort_func_compare); - else - sort_strings(*file, *num_file); - } - - /* Reset the variables used for special highlight names expansion, so that - * they don't show up when getting normal highlight names by ID. */ - reset_expand_highlight(); - - return OK; -} - -/* - * Complete a shell command. - * Returns FAIL or OK; - */ -static int -expand_shellcmd ( - char_u *filepat, /* pattern to match with command names */ - int *num_file, /* return: number of matches */ - char_u ***file, /* return: array with matches */ - int flagsarg /* EW_ flags */ -) -{ - char_u *pat; - int i; - char_u *path; - int mustfree = FALSE; - garray_T ga; - char_u *buf = alloc(MAXPATHL); - size_t l; - char_u *s, *e; - int flags = flagsarg; - int ret; - - if (buf == NULL) - return FAIL; - - /* for ":set path=" and ":set tags=" halve backslashes for escaped - * space */ - pat = vim_strsave(filepat); - for (i = 0; pat[i]; ++i) - if (pat[i] == '\\' && pat[i + 1] == ' ') - STRMOVE(pat + i, pat + i + 1); - - flags |= EW_FILE | EW_EXEC; - - /* For an absolute name we don't use $PATH. */ - if (path_is_absolute_path(pat)) - path = (char_u *)" "; - else if ((pat[0] == '.' && (vim_ispathsep(pat[1]) - || (pat[1] == '.' && vim_ispathsep(pat[2]))))) - path = (char_u *)"."; - else { - path = vim_getenv((char_u *)"PATH", &mustfree); - if (path == NULL) - path = (char_u *)""; - } - - /* - * Go over all directories in $PATH. Expand matches in that directory and - * collect them in "ga". - */ - ga_init(&ga, (int)sizeof(char *), 10); - for (s = path; *s != NUL; s = e) { - if (*s == ' ') - ++s; /* Skip space used for absolute path name. */ - - e = vim_strchr(s, ':'); - if (e == NULL) - e = s + STRLEN(s); - - l = e - s; - if (l > MAXPATHL - 5) - break; - vim_strncpy(buf, s, l); - add_pathsep(buf); - l = STRLEN(buf); - vim_strncpy(buf + l, pat, MAXPATHL - 1 - l); - - /* Expand matches in one directory of $PATH. */ - ret = expand_wildcards(1, &buf, num_file, file, flags); - if (ret == OK) { - ga_grow(&ga, *num_file); - { - for (i = 0; i < *num_file; ++i) { - s = (*file)[i]; - if (STRLEN(s) > l) { - /* Remove the path again. */ - STRMOVE(s, s + l); - ((char_u **)ga.ga_data)[ga.ga_len++] = s; - } else - free(s); - } - free(*file); - } - } - if (*e != NUL) - ++e; - } - *file = ga.ga_data; - *num_file = ga.ga_len; - - free(buf); - free(pat); - if (mustfree) - free(path); - return OK; -} - -typedef void *(*user_expand_func_T)(char_u *, int, char_u **, int); -static void * call_user_expand_func(user_expand_func_T user_expand_func, - expand_T *xp, int *num_file, - char_u ***file); - -/* - * Call "user_expand_func()" to invoke a user defined VimL function and return - * the result (either a string or a List). - */ -static void * call_user_expand_func(user_expand_func, xp, num_file, file) -user_expand_func_T user_expand_func; -expand_T *xp; -int *num_file; -char_u ***file; -{ - int keep = 0; - char_u num[50]; - char_u *args[3]; - int save_current_SID = current_SID; - void *ret; - struct cmdline_info save_ccline; - - if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) - return NULL; - *num_file = 0; - *file = NULL; - - if (ccline.cmdbuff != NULL) { - keep = ccline.cmdbuff[ccline.cmdlen]; - ccline.cmdbuff[ccline.cmdlen] = 0; - } - - args[0] = vim_strnsave(xp->xp_pattern, xp->xp_pattern_len); - args[1] = xp->xp_line; - sprintf((char *)num, "%d", xp->xp_col); - args[2] = num; - - /* Save the cmdline, we don't know what the function may do. */ - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; - current_SID = xp->xp_scriptID; - - ret = user_expand_func(xp->xp_arg, 3, args, FALSE); - - ccline = save_ccline; - current_SID = save_current_SID; - if (ccline.cmdbuff != NULL) - ccline.cmdbuff[ccline.cmdlen] = keep; - - free(args[0]); - return ret; -} - -/* - * Expand names with a function defined by the user. - */ -static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file) -{ - char_u *retstr; - char_u *s; - char_u *e; - char_u keep; - garray_T ga; - - retstr = call_user_expand_func(call_func_retstr, xp, num_file, file); - if (retstr == NULL) - return FAIL; - - ga_init(&ga, (int)sizeof(char *), 3); - for (s = retstr; *s != NUL; s = e) { - e = vim_strchr(s, '\n'); - if (e == NULL) - e = s + STRLEN(s); - keep = *e; - *e = 0; - - if (xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0) { - *e = keep; - if (*e != NUL) - ++e; - continue; - } - - ga_grow(&ga, 1); - - ((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, (int)(e - s)); - ++ga.ga_len; - - *e = keep; - if (*e != NUL) - ++e; - } - free(retstr); - *file = ga.ga_data; - *num_file = ga.ga_len; - return OK; -} - -/* - * Expand names with a list returned by a function defined by the user. - */ -static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) -{ - list_T *retlist; - listitem_T *li; - garray_T ga; - - retlist = call_user_expand_func(call_func_retlist, xp, num_file, file); - if (retlist == NULL) - return FAIL; - - ga_init(&ga, (int)sizeof(char *), 3); - /* Loop over the items in the list. */ - for (li = retlist->lv_first; li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL) - continue; /* Skip non-string items and empty strings */ - - ga_grow(&ga, 1); - - ((char_u **)ga.ga_data)[ga.ga_len] = - vim_strsave(li->li_tv.vval.v_string); - ++ga.ga_len; - } - list_unref(retlist); - - *file = ga.ga_data; - *num_file = ga.ga_len; - return OK; -} - -/* - * Expand color scheme, compiler or filetype names: - * 'runtimepath'/{dirnames}/{pat}.vim - * "dirnames" is an array with one or more directory names. - */ -static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, char *dirnames[]) -{ - char_u *matches; - char_u *s; - char_u *e; - garray_T ga; - int i; - int pat_len; - - *num_file = 0; - *file = NULL; - pat_len = (int)STRLEN(pat); - ga_init(&ga, (int)sizeof(char *), 10); - - for (i = 0; dirnames[i] != NULL; ++i) { - s = alloc((unsigned)(STRLEN(dirnames[i]) + pat_len + 7)); - if (s == NULL) { - ga_clear_strings(&ga); - return FAIL; - } - sprintf((char *)s, "%s/%s*.vim", dirnames[i], pat); - matches = globpath(p_rtp, s, 0); - free(s); - if (matches == NULL) - continue; - - for (s = matches; *s != NUL; s = e) { - e = vim_strchr(s, '\n'); - if (e == NULL) - e = s + STRLEN(s); - ga_grow(&ga, 1); - if (e - 4 > s && STRNICMP(e - 4, ".vim", 4) == 0) { - for (s = e - 4; s > matches; mb_ptr_back(matches, s)) - if (*s == '\n' || vim_ispathsep(*s)) - break; - ++s; - ((char_u **)ga.ga_data)[ga.ga_len] = - vim_strnsave(s, (int)(e - s - 4)); - ++ga.ga_len; - } - if (*e != NUL) - ++e; - } - free(matches); - } - if (ga.ga_len == 0) - return FAIL; - - /* Sort and remove duplicates which can happen when specifying multiple - * directories in dirnames. */ - ga_remove_duplicate_strings(&ga); - - *file = ga.ga_data; - *num_file = ga.ga_len; - return OK; -} - - -/* - * Expand "file" for all comma-separated directories in "path". - * Returns an allocated string with all matches concatenated, separated by - * newlines. Returns NULL for an error or no matches. - */ -char_u *globpath(char_u *path, char_u *file, int expand_options) -{ - expand_T xpc; - char_u *buf; - garray_T ga; - int i; - int len; - int num_p; - char_u **p; - char_u *cur = NULL; - - buf = alloc(MAXPATHL); - if (buf == NULL) - return NULL; - - ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; - - ga_init(&ga, 1, 100); - - /* Loop over all entries in {path}. */ - while (*path != NUL) { - /* Copy one item of the path to buf[] and concatenate the file name. */ - copy_option_part(&path, buf, MAXPATHL, ","); - if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) { - add_pathsep(buf); - STRCAT(buf, file); - if (ExpandFromContext(&xpc, buf, &num_p, &p, - WILD_SILENT|expand_options) != FAIL && num_p > 0) { - ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT|expand_options); - for (len = 0, i = 0; i < num_p; ++i) - len += (int)STRLEN(p[i]) + 1; - - /* Concatenate new results to previous ones. */ - ga_grow(&ga, len); - cur = (char_u *)ga.ga_data + ga.ga_len; - for (i = 0; i < num_p; ++i) { - STRCPY(cur, p[i]); - cur += STRLEN(p[i]); - *cur++ = '\n'; - } - ga.ga_len += len; - - FreeWild(num_p, p); - } - } - } - if (cur != NULL) - *--cur = 0; /* Replace trailing newline with NUL */ - - free(buf); - return (char_u *)ga.ga_data; -} - - - -/********************************* -* Command line history stuff * -*********************************/ - -/* - * Translate a history character to the associated type number. - */ -static int hist_char2type(int c) -{ - if (c == ':') - return HIST_CMD; - if (c == '=') - return HIST_EXPR; - if (c == '@') - return HIST_INPUT; - if (c == '>') - return HIST_DEBUG; - return HIST_SEARCH; /* must be '?' or '/' */ -} - -/* - * Table of history names. - * These names are used in :history and various hist...() functions. - * It is sufficient to give the significant prefix of a history name. - */ - -static char *(history_names[]) = -{ - "cmd", - "search", - "expr", - "input", - "debug", - NULL -}; - -/* - * Function given to ExpandGeneric() to obtain the possible first - * arguments of the ":history command. - */ -static char_u *get_history_arg(expand_T *xp, int idx) -{ - static char_u compl[2] = { NUL, NUL }; - char *short_names = ":=@>?/"; - int short_names_count = (int)STRLEN(short_names); - int history_name_count = sizeof(history_names) / sizeof(char *) - 1; - - if (idx < short_names_count) { - compl[0] = (char_u)short_names[idx]; - return compl; - } - if (idx < short_names_count + history_name_count) - return (char_u *)history_names[idx - short_names_count]; - if (idx == short_names_count + history_name_count) - return (char_u *)"all"; - return NULL; -} - -/* - * init_history() - Initialize the command line history. - * Also used to re-allocate the history when the size changes. - */ -void init_history(void) -{ - /* - * If size of history table changed, reallocate it - */ - ssize_t newlen = p_hi; - if (newlen != hislen) { - histentry_T *temp; - ssize_t i; - ssize_t j; - - // adjust the tables - for (int type = 0; type < HIST_COUNT; ++type) { - if (newlen) { - temp = xmalloc(newlen * sizeof(*temp)); - } else - temp = NULL; - if (newlen == 0 || temp != NULL) { - if (hisidx[type] < 0) { /* there are no entries yet */ - for (i = 0; i < newlen; ++i) - clear_hist_entry(&temp[i]); - } else if (newlen > hislen) { /* array becomes bigger */ - for (i = 0; i <= hisidx[type]; ++i) - temp[i] = history[type][i]; - j = i; - for (; i <= newlen - (hislen - hisidx[type]); ++i) - clear_hist_entry(&temp[i]); - for (; j < hislen; ++i, ++j) - temp[i] = history[type][j]; - } else { /* array becomes smaller or 0 */ - j = hisidx[type]; - for (i = newlen - 1;; --i) { - if (i >= 0) /* copy newest entries */ - temp[i] = history[type][j]; - else /* remove older entries */ - free(history[type][j].hisstr); - if (--j < 0) - j = hislen - 1; - if (j == hisidx[type]) - break; - } - hisidx[type] = newlen - 1; - } - free(history[type]); - history[type] = temp; - } - } - hislen = newlen; - } -} - -static void clear_hist_entry(histentry_T *hisptr) -{ - hisptr->hisnum = 0; - hisptr->viminfo = FALSE; - hisptr->hisstr = NULL; -} - -/* - * Check if command line 'str' is already in history. - * If 'move_to_front' is TRUE, matching entry is moved to end of history. - */ -static int -in_history ( - int type, - char_u *str, - int move_to_front, /* Move the entry to the front if it exists */ - int sep, - int writing /* ignore entries read from viminfo */ -) -{ - int i; - int last_i = -1; - char_u *p; - - if (hisidx[type] < 0) - return FALSE; - i = hisidx[type]; - do { - if (history[type][i].hisstr == NULL) - return FALSE; - - /* For search history, check that the separator character matches as - * well. */ - p = history[type][i].hisstr; - if (STRCMP(str, p) == 0 - && !(writing && history[type][i].viminfo) - && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) { - if (!move_to_front) - return TRUE; - last_i = i; - break; - } - if (--i < 0) - i = hislen - 1; - } while (i != hisidx[type]); - - if (last_i >= 0) { - str = history[type][i].hisstr; - while (i != hisidx[type]) { - if (++i >= hislen) - i = 0; - history[type][last_i] = history[type][i]; - last_i = i; - } - history[type][i].hisnum = ++hisnum[type]; - history[type][i].viminfo = FALSE; - history[type][i].hisstr = str; - return TRUE; - } - return FALSE; -} - -/* - * Convert history name (from table above) to its HIST_ equivalent. - * When "name" is empty, return "cmd" history. - * Returns -1 for unknown history name. - */ -int get_histtype(char_u *name) -{ - int i; - int len = (int)STRLEN(name); - - /* No argument: use current history. */ - if (len == 0) - return hist_char2type(ccline.cmdfirstc); - - for (i = 0; history_names[i] != NULL; ++i) - if (STRNICMP(name, history_names[i], len) == 0) - return i; - - if (vim_strchr((char_u *)":=@>?/", name[0]) != NULL && name[1] == NUL) - return hist_char2type(name[0]); - - return -1; -} - -static int last_maptick = -1; /* last seen maptick */ - -/* - * Add the given string to the given history. If the string is already in the - * history then it is moved to the front. "histype" may be one of he HIST_ - * values. - */ -void -add_to_history ( - int histype, - char_u *new_entry, - int in_map, /* consider maptick when inside a mapping */ - int sep /* separator character used (search hist) */ -) -{ - histentry_T *hisptr; - int len; - - if (hislen == 0) /* no history */ - return; - - if (cmdmod.keeppatterns && histype == HIST_SEARCH) - return; - - /* - * Searches inside the same mapping overwrite each other, so that only - * the last line is kept. Be careful not to remove a line that was moved - * down, only lines that were added. - */ - if (histype == HIST_SEARCH && in_map) { - if (maptick == last_maptick) { - /* Current line is from the same mapping, remove it */ - hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]]; - free(hisptr->hisstr); - clear_hist_entry(hisptr); - --hisnum[histype]; - if (--hisidx[HIST_SEARCH] < 0) - hisidx[HIST_SEARCH] = hislen - 1; - } - last_maptick = -1; - } - if (!in_history(histype, new_entry, TRUE, sep, FALSE)) { - if (++hisidx[histype] == hislen) - hisidx[histype] = 0; - hisptr = &history[histype][hisidx[histype]]; - free(hisptr->hisstr); - - /* Store the separator after the NUL of the string. */ - len = (int)STRLEN(new_entry); - hisptr->hisstr = vim_strnsave(new_entry, len + 2); - if (hisptr->hisstr != NULL) - hisptr->hisstr[len + 1] = sep; - - hisptr->hisnum = ++hisnum[histype]; - hisptr->viminfo = FALSE; - if (histype == HIST_SEARCH && in_map) - last_maptick = maptick; - } -} - - -/* - * Get identifier of newest history entry. - * "histype" may be one of the HIST_ values. - */ -int get_history_idx(int histype) -{ - if (hislen == 0 || histype < 0 || histype >= HIST_COUNT - || hisidx[histype] < 0) - return -1; - - return history[histype][hisidx[histype]].hisnum; -} - -static struct cmdline_info *get_ccline_ptr(void); - -/* - * Get pointer to the command line info to use. cmdline_paste() may clear - * ccline and put the previous value in prev_ccline. - */ -static struct cmdline_info *get_ccline_ptr(void) -{ - if ((State & CMDLINE) == 0) - return NULL; - if (ccline.cmdbuff != NULL) - return &ccline; - if (prev_ccline_used && prev_ccline.cmdbuff != NULL) - return &prev_ccline; - return NULL; -} - -/* - * Get the current command line in allocated memory. - * Only works when the command line is being edited. - * Returns NULL when something is wrong. - */ -char_u *get_cmdline_str(void) -{ - struct cmdline_info *p = get_ccline_ptr(); - - if (p == NULL) - return NULL; - return vim_strnsave(p->cmdbuff, p->cmdlen); -} - -/* - * Get the current command line position, counted in bytes. - * Zero is the first position. - * Only works when the command line is being edited. - * Returns -1 when something is wrong. - */ -int get_cmdline_pos(void) -{ - struct cmdline_info *p = get_ccline_ptr(); - - if (p == NULL) - return -1; - return p->cmdpos; -} - -/* - * Set the command line byte position to "pos". Zero is the first position. - * Only works when the command line is being edited. - * Returns 1 when failed, 0 when OK. - */ -int set_cmdline_pos(int pos) -{ - struct cmdline_info *p = get_ccline_ptr(); - - if (p == NULL) - return 1; - - /* The position is not set directly but after CTRL-\ e or CTRL-R = has - * changed the command line. */ - if (pos < 0) - new_cmdpos = 0; - else - new_cmdpos = pos; - return 0; -} - -/* - * Get the current command-line type. - * Returns ':' or '/' or '?' or '@' or '>' or '-' - * Only works when the command line is being edited. - * Returns NUL when something is wrong. - */ -int get_cmdline_type(void) -{ - struct cmdline_info *p = get_ccline_ptr(); - - if (p == NULL) - return NUL; - if (p->cmdfirstc == NUL) - return (p->input_fn) ? '@' : '-'; - return p->cmdfirstc; -} - -/* - * Calculate history index from a number: - * num > 0: seen as identifying number of a history entry - * num < 0: relative position in history wrt newest entry - * "histype" may be one of the HIST_ values. - */ -static int calc_hist_idx(int histype, int num) -{ - int i; - histentry_T *hist; - int wrapped = FALSE; - - if (hislen == 0 || histype < 0 || histype >= HIST_COUNT - || (i = hisidx[histype]) < 0 || num == 0) - return -1; - - hist = history[histype]; - if (num > 0) { - while (hist[i].hisnum > num) - if (--i < 0) { - if (wrapped) - break; - i += hislen; - wrapped = TRUE; - } - if (hist[i].hisnum == num && hist[i].hisstr != NULL) - return i; - } else if (-num <= hislen) { - i += num + 1; - if (i < 0) - i += hislen; - if (hist[i].hisstr != NULL) - return i; - } - return -1; -} - -/* - * Get a history entry by its index. - * "histype" may be one of the HIST_ values. - */ -char_u *get_history_entry(int histype, int idx) -{ - idx = calc_hist_idx(histype, idx); - if (idx >= 0) - return history[histype][idx].hisstr; - else - return (char_u *)""; -} - -/* - * Clear all entries of a history. - * "histype" may be one of the HIST_ values. - */ -int clr_history(int histype) -{ - int i; - histentry_T *hisptr; - - if (hislen != 0 && histype >= 0 && histype < HIST_COUNT) { - hisptr = history[histype]; - for (i = hislen; i--; ) { - free(hisptr->hisstr); - clear_hist_entry(hisptr); - } - hisidx[histype] = -1; /* mark history as cleared */ - hisnum[histype] = 0; /* reset identifier counter */ - return OK; - } - return FAIL; -} - -/* - * Remove all entries matching {str} from a history. - * "histype" may be one of the HIST_ values. - */ -int del_history_entry(int histype, char_u *str) -{ - regmatch_T regmatch; - histentry_T *hisptr; - int idx; - int i; - int last; - int found = FALSE; - - regmatch.regprog = NULL; - regmatch.rm_ic = FALSE; /* always match case */ - if (hislen != 0 - && histype >= 0 - && histype < HIST_COUNT - && *str != NUL - && (idx = hisidx[histype]) >= 0 - && (regmatch.regprog = vim_regcomp(str, RE_MAGIC + RE_STRING)) - != NULL) { - i = last = idx; - do { - hisptr = &history[histype][i]; - if (hisptr->hisstr == NULL) - break; - if (vim_regexec(®match, hisptr->hisstr, (colnr_T)0)) { - found = TRUE; - free(hisptr->hisstr); - clear_hist_entry(hisptr); - } else { - if (i != last) { - history[histype][last] = *hisptr; - clear_hist_entry(hisptr); - } - if (--last < 0) - last += hislen; - } - if (--i < 0) - i += hislen; - } while (i != idx); - if (history[histype][idx].hisstr == NULL) - hisidx[histype] = -1; - } - vim_regfree(regmatch.regprog); - return found; -} - -/* - * Remove an indexed entry from a history. - * "histype" may be one of the HIST_ values. - */ -int del_history_idx(int histype, int idx) -{ - int i, j; - - i = calc_hist_idx(histype, idx); - if (i < 0) - return FALSE; - idx = hisidx[histype]; - free(history[histype][i].hisstr); - - /* When deleting the last added search string in a mapping, reset - * last_maptick, so that the last added search string isn't deleted again. - */ - if (histype == HIST_SEARCH && maptick == last_maptick && i == idx) - last_maptick = -1; - - while (i != idx) { - j = (i + 1) % hislen; - history[histype][i] = history[histype][j]; - i = j; - } - clear_hist_entry(&history[histype][i]); - if (--i < 0) - i += hislen; - hisidx[histype] = i; - return TRUE; -} - - -/* - * Very specific function to remove the value in ":set key=val" from the - * history. - */ -void remove_key_from_history(void) -{ - char_u *p; - int i; - - i = hisidx[HIST_CMD]; - if (i < 0) - return; - p = history[HIST_CMD][i].hisstr; - if (p != NULL) - for (; *p; ++p) - if (STRNCMP(p, "key", 3) == 0 && !isalpha(p[3])) { - p = vim_strchr(p + 3, '='); - if (p == NULL) - break; - ++p; - for (i = 0; p[i] && !vim_iswhite(p[i]); ++i) - if (p[i] == '\\' && p[i + 1]) - ++i; - STRMOVE(p, p + i); - --p; - } -} - -/* - * Get indices "num1,num2" that specify a range within a list (not a range of - * text lines in a buffer!) from a string. Used for ":history" and ":clist". - * Returns OK if parsed successfully, otherwise FAIL. - */ -int get_list_range(char_u **str, int *num1, int *num2) -{ - int len; - int first = FALSE; - long num; - - *str = skipwhite(*str); - if (**str == '-' || vim_isdigit(**str)) { /* parse "from" part of range */ - vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); - *str += len; - *num1 = (int)num; - first = TRUE; - } - *str = skipwhite(*str); - if (**str == ',') { /* parse "to" part of range */ - *str = skipwhite(*str + 1); - vim_str2nr(*str, NULL, &len, FALSE, FALSE, &num, NULL); - if (len > 0) { - *num2 = (int)num; - *str = skipwhite(*str + len); - } else if (!first) /* no number given at all */ - return FAIL; - } else if (first) /* only one number given */ - *num2 = *num1; - return OK; -} - -/* - * :history command - print a history - */ -void ex_history(exarg_T *eap) -{ - histentry_T *hist; - int histype1 = HIST_CMD; - int histype2 = HIST_CMD; - int hisidx1 = 1; - int hisidx2 = -1; - int idx; - int i, j, k; - char_u *end; - char_u *arg = eap->arg; - - if (hislen == 0) { - MSG(_("'history' option is zero")); - return; - } - - if (!(VIM_ISDIGIT(*arg) || *arg == '-' || *arg == ',')) { - end = arg; - while (ASCII_ISALPHA(*end) - || vim_strchr((char_u *)":=@>/?", *end) != NULL) - end++; - i = *end; - *end = NUL; - histype1 = get_histtype(arg); - if (histype1 == -1) { - if (STRNICMP(arg, "all", STRLEN(arg)) == 0) { - histype1 = 0; - histype2 = HIST_COUNT-1; - } else { - *end = i; - EMSG(_(e_trailing)); - return; - } - } else - histype2 = histype1; - *end = i; - } else - end = arg; - if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) { - EMSG(_(e_trailing)); - return; - } - - for (; !got_int && histype1 <= histype2; ++histype1) { - STRCPY(IObuff, "\n # "); - STRCAT(STRCAT(IObuff, history_names[histype1]), " history"); - MSG_PUTS_TITLE(IObuff); - idx = hisidx[histype1]; - hist = history[histype1]; - j = hisidx1; - k = hisidx2; - if (j < 0) - j = (-j > hislen) ? 0 : hist[(hislen+j+idx+1) % hislen].hisnum; - if (k < 0) - k = (-k > hislen) ? 0 : hist[(hislen+k+idx+1) % hislen].hisnum; - if (idx >= 0 && j <= k) - for (i = idx + 1; !got_int; ++i) { - if (i == hislen) - i = 0; - if (hist[i].hisstr != NULL - && hist[i].hisnum >= j && hist[i].hisnum <= k) { - msg_putchar('\n'); - sprintf((char *)IObuff, "%c%6d ", i == idx ? '>' : ' ', - hist[i].hisnum); - if (vim_strsize(hist[i].hisstr) > (int)Columns - 10) - trunc_string(hist[i].hisstr, IObuff + STRLEN(IObuff), - (int)Columns - 10, IOSIZE - (int)STRLEN(IObuff)); - else - STRCAT(IObuff, hist[i].hisstr); - msg_outtrans(IObuff); - out_flush(); - } - if (i == idx) - break; - } - } -} - -/* - * Buffers for history read from a viminfo file. Only valid while reading. - */ -static char_u **viminfo_history[HIST_COUNT] = {NULL, NULL, NULL, NULL}; -static int viminfo_hisidx[HIST_COUNT] = {0, 0, 0, 0}; -static int viminfo_hislen[HIST_COUNT] = {0, 0, 0, 0}; -static int viminfo_add_at_front = FALSE; - -static int hist_type2char(int type, int use_question); - -/* - * Translate a history type number to the associated character. - */ -static int -hist_type2char ( - int type, - int use_question /* use '?' instead of '/' */ -) -{ - if (type == HIST_CMD) - return ':'; - if (type == HIST_SEARCH) { - if (use_question) - return '?'; - else - return '/'; - } - if (type == HIST_EXPR) - return '='; - return '@'; -} - -/* - * Prepare for reading the history from the viminfo file. - * This allocates history arrays to store the read history lines. - */ -void prepare_viminfo_history(int asklen, int writing) -{ - int i; - int num; - - init_history(); - viminfo_add_at_front = (asklen != 0 && !writing); - if (asklen > hislen) - asklen = hislen; - - for (int type = 0; type < HIST_COUNT; ++type) { - /* Count the number of empty spaces in the history list. Entries read - * from viminfo previously are also considered empty. If there are - * more spaces available than we request, then fill them up. */ - for (i = 0, num = 0; i < hislen; i++) - if (history[type][i].hisstr == NULL || history[type][i].viminfo) - num++; - int len = asklen; - if (num > len) - len = num; - if (len <= 0) - viminfo_history[type] = NULL; - else - viminfo_history[type] = xmalloc(len * sizeof(char_u *)); - if (viminfo_history[type] == NULL) - len = 0; - viminfo_hislen[type] = len; - viminfo_hisidx[type] = 0; - } -} - -/* - * Accept a line from the viminfo, store it in the history array when it's - * new. - */ -int read_viminfo_history(vir_T *virp, int writing) -{ - int type; - char_u *val; - - type = hist_char2type(virp->vir_line[0]); - if (viminfo_hisidx[type] < viminfo_hislen[type]) { - val = viminfo_readstring(virp, 1, TRUE); - if (val != NULL && *val != NUL) { - int sep = (*val == ' ' ? NUL : *val); - - if (!in_history(type, val + (type == HIST_SEARCH), - viminfo_add_at_front, sep, writing)) { - /* Need to re-allocate to append the separator byte. */ - size_t len = STRLEN(val); - char_u *p = xmalloc(len + 2); - if (type == HIST_SEARCH) { - /* Search entry: Move the separator from the first - * column to after the NUL. */ - memmove(p, val + 1, len); - p[len] = sep; - } else { - /* Not a search entry: No separator in the viminfo - * file, add a NUL separator. */ - memmove(p, val, len + 1); - p[len + 1] = NUL; - } - viminfo_history[type][viminfo_hisidx[type]++] = p; - } - } - free(val); - } - return viminfo_readline(virp); -} - -/* - * Finish reading history lines from viminfo. Not used when writing viminfo. - */ -void finish_viminfo_history(void) -{ - int idx; - int i; - int type; - - for (type = 0; type < HIST_COUNT; ++type) { - if (history[type] == NULL) - continue; - idx = hisidx[type] + viminfo_hisidx[type]; - if (idx >= hislen) - idx -= hislen; - else if (idx < 0) - idx = hislen - 1; - if (viminfo_add_at_front) - hisidx[type] = idx; - else { - if (hisidx[type] == -1) - hisidx[type] = hislen - 1; - do { - if (history[type][idx].hisstr != NULL - || history[type][idx].viminfo) - break; - if (++idx == hislen) - idx = 0; - } while (idx != hisidx[type]); - if (idx != hisidx[type] && --idx < 0) - idx = hislen - 1; - } - for (i = 0; i < viminfo_hisidx[type]; i++) { - free(history[type][idx].hisstr); - history[type][idx].hisstr = viminfo_history[type][i]; - history[type][idx].viminfo = TRUE; - if (--idx < 0) - idx = hislen - 1; - } - idx += 1; - idx %= hislen; - for (i = 0; i < viminfo_hisidx[type]; i++) { - history[type][idx++].hisnum = ++hisnum[type]; - idx %= hislen; - } - free(viminfo_history[type]); - viminfo_history[type] = NULL; - viminfo_hisidx[type] = 0; - } -} - -/* - * Write history to viminfo file in "fp". - * When "merge" is TRUE merge history lines with a previously read viminfo - * file, data is in viminfo_history[]. - * When "merge" is FALSE just write all history lines. Used for ":wviminfo!". - */ -void write_viminfo_history(FILE *fp, int merge) -{ - int i; - int type; - int num_saved; - char_u *p; - int c; - int round; - - init_history(); - if (hislen == 0) - return; - for (type = 0; type < HIST_COUNT; ++type) { - num_saved = get_viminfo_parameter(hist_type2char(type, FALSE)); - if (num_saved == 0) - continue; - if (num_saved < 0) /* Use default */ - num_saved = hislen; - fprintf(fp, _("\n# %s History (newest to oldest):\n"), - type == HIST_CMD ? _("Command Line") : - type == HIST_SEARCH ? _("Search String") : - type == HIST_EXPR ? _("Expression") : - _("Input Line")); - if (num_saved > hislen) - num_saved = hislen; - - /* - * Merge typed and viminfo history: - * round 1: history of typed commands. - * round 2: history from recently read viminfo. - */ - for (round = 1; round <= 2; ++round) { - if (round == 1) - /* start at newest entry, somewhere in the list */ - i = hisidx[type]; - else if (viminfo_hisidx[type] > 0) - /* start at newest entry, first in the list */ - i = 0; - else - /* empty list */ - i = -1; - if (i >= 0) - while (num_saved > 0 - && !(round == 2 && i >= viminfo_hisidx[type])) { - p = round == 1 ? history[type][i].hisstr - : viminfo_history[type] == NULL ? NULL - : viminfo_history[type][i]; - if (p != NULL && (round == 2 - || !merge - || !history[type][i].viminfo)) { - --num_saved; - fputc(hist_type2char(type, TRUE), fp); - /* For the search history: put the separator in the - * second column; use a space if there isn't one. */ - if (type == HIST_SEARCH) { - c = p[STRLEN(p) + 1]; - putc(c == NUL ? ' ' : c, fp); - } - viminfo_writestring(fp, p); - } - if (round == 1) { - /* Decrement index, loop around and stop when back at - * the start. */ - if (--i < 0) - i = hislen - 1; - if (i == hisidx[type]) - break; - } else { - /* Increment index. Stop at the end in the while. */ - ++i; - } - } - } - for (i = 0; i < viminfo_hisidx[type]; ++i) - if (viminfo_history[type] != NULL) - free(viminfo_history[type][i]); - free(viminfo_history[type]); - viminfo_history[type] = NULL; - viminfo_hisidx[type] = 0; - } -} - -/* - * Write a character at the current cursor+offset position. - * It is directly written into the command buffer block. - */ -void cmd_pchar(int c, int offset) -{ - if (ccline.cmdpos + offset >= ccline.cmdlen || ccline.cmdpos + offset < 0) { - EMSG(_("E198: cmd_pchar beyond the command length")); - return; - } - ccline.cmdbuff[ccline.cmdpos + offset] = (char_u)c; - ccline.cmdbuff[ccline.cmdlen] = NUL; -} - -int cmd_gchar(int offset) -{ - if (ccline.cmdpos + offset >= ccline.cmdlen || ccline.cmdpos + offset < 0) { - /* EMSG(_("cmd_gchar beyond the command length")); */ - return NUL; - } - return (int)ccline.cmdbuff[ccline.cmdpos + offset]; -} - -/* - * Open a window on the current command line and history. Allow editing in - * the window. Returns when the window is closed. - * Returns: - * CR if the command is to be executed - * Ctrl_C if it is to be abandoned - * K_IGNORE if editing continues - */ -static int ex_window(void) -{ - struct cmdline_info save_ccline; - buf_T *old_curbuf = curbuf; - win_T *old_curwin = curwin; - buf_T *bp; - win_T *wp; - int i; - linenr_T lnum; - int histtype; - garray_T winsizes; - char_u typestr[2]; - int save_restart_edit = restart_edit; - int save_State = State; - int save_exmode = exmode_active; - int save_cmdmsg_rl = cmdmsg_rl; - - /* Can't do this recursively. Can't do it when typing a password. */ - if (cmdwin_type != 0 - || cmdline_star > 0 - ) { - beep_flush(); - return K_IGNORE; - } - - /* Save current window sizes. */ - win_size_save(&winsizes); - - /* Don't execute autocommands while creating the window. */ - block_autocmds(); - /* don't use a new tab page */ - cmdmod.tab = 0; - - /* Create a window for the command-line buffer. */ - if (win_split((int)p_cwh, WSP_BOT) == FAIL) { - beep_flush(); - unblock_autocmds(); - return K_IGNORE; - } - cmdwin_type = get_cmdline_type(); - - /* Create the command-line buffer empty. */ - (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL); - (void)setfname(curbuf, (char_u *)"[Command Line]", NULL, TRUE); - set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - curbuf->b_p_ma = TRUE; - curwin->w_p_fen = FALSE; - curwin->w_p_rl = cmdmsg_rl; - cmdmsg_rl = FALSE; - RESET_BINDING(curwin); - - /* Do execute autocommands for setting the filetype (load syntax). */ - unblock_autocmds(); - - /* Showing the prompt may have set need_wait_return, reset it. */ - need_wait_return = FALSE; - - histtype = hist_char2type(cmdwin_type); - if (histtype == HIST_CMD || histtype == HIST_DEBUG) { - if (p_wc == TAB) { - add_map((char_u *)" ", INSERT); - add_map((char_u *)" a", NORMAL); - } - set_option_value((char_u *)"ft", 0L, (char_u *)"vim", OPT_LOCAL); - } - - /* Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin - * sets 'textwidth' to 78). */ - curbuf->b_p_tw = 0; - - /* Fill the buffer with the history. */ - init_history(); - if (hislen > 0) { - i = hisidx[histtype]; - if (i >= 0) { - lnum = 0; - do { - if (++i == hislen) - i = 0; - if (history[histtype][i].hisstr != NULL) - ml_append(lnum++, history[histtype][i].hisstr, - (colnr_T)0, FALSE); - } while (i != hisidx[histtype]); - } - } - - /* Replace the empty last line with the current command-line and put the - * cursor there. */ - ml_replace(curbuf->b_ml.ml_line_count, ccline.cmdbuff, TRUE); - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - curwin->w_cursor.col = ccline.cmdpos; - changed_line_abv_curs(); - invalidate_botline(); - redraw_later(SOME_VALID); - - /* Save the command line info, can be used recursively. */ - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; - - /* No Ex mode here! */ - exmode_active = 0; - - State = NORMAL; - setmouse(); - - /* Trigger CmdwinEnter autocommands. */ - typestr[0] = cmdwin_type; - typestr[1] = NUL; - apply_autocmds(EVENT_CMDWINENTER, typestr, typestr, FALSE, curbuf); - if (restart_edit != 0) /* autocmd with ":startinsert" */ - stuffcharReadbuff(K_NOP); - - i = RedrawingDisabled; - RedrawingDisabled = 0; - - /* - * Call the main loop until or CTRL-C is typed. - */ - cmdwin_result = 0; - main_loop(TRUE, FALSE); - - RedrawingDisabled = i; - - /* Trigger CmdwinLeave autocommands. */ - apply_autocmds(EVENT_CMDWINLEAVE, typestr, typestr, FALSE, curbuf); - - /* Restore the command line info. */ - ccline = save_ccline; - cmdwin_type = 0; - - exmode_active = save_exmode; - - /* Safety check: The old window or buffer was deleted: It's a bug when - * this happens! */ - if (!win_valid(old_curwin) || !buf_valid(old_curbuf)) { - cmdwin_result = Ctrl_C; - EMSG(_("E199: Active window or buffer deleted")); - } else { - /* autocmds may abort script processing */ - if (aborting() && cmdwin_result != K_IGNORE) - cmdwin_result = Ctrl_C; - /* Set the new command line from the cmdline buffer. */ - free(ccline.cmdbuff); - if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { /* :qa[!] typed */ - char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; - - if (histtype == HIST_CMD) { - /* Execute the command directly. */ - ccline.cmdbuff = vim_strsave((char_u *)p); - cmdwin_result = CAR; - } else { - /* First need to cancel what we were doing. */ - ccline.cmdbuff = NULL; - stuffcharReadbuff(':'); - stuffReadbuff((char_u *)p); - stuffcharReadbuff(CAR); - } - } else if (cmdwin_result == K_XF2) { /* :qa typed */ - ccline.cmdbuff = vim_strsave((char_u *)"qa"); - cmdwin_result = CAR; - } else if (cmdwin_result == Ctrl_C) { - /* :q or :close, don't execute any command - * and don't modify the cmd window. */ - ccline.cmdbuff = NULL; - } else - ccline.cmdbuff = vim_strsave(ml_get_curline()); - if (ccline.cmdbuff == NULL) - cmdwin_result = Ctrl_C; - else { - ccline.cmdlen = (int)STRLEN(ccline.cmdbuff); - ccline.cmdbufflen = ccline.cmdlen + 1; - ccline.cmdpos = curwin->w_cursor.col; - if (ccline.cmdpos > ccline.cmdlen) - ccline.cmdpos = ccline.cmdlen; - if (cmdwin_result == K_IGNORE) { - set_cmdspos_cursor(); - redrawcmd(); - } - } - - /* Don't execute autocommands while deleting the window. */ - block_autocmds(); - wp = curwin; - bp = curbuf; - win_goto(old_curwin); - win_close(wp, TRUE); - - /* win_close() may have already wiped the buffer when 'bh' is - * set to 'wipe' */ - if (buf_valid(bp)) - close_buffer(NULL, bp, DOBUF_WIPE, FALSE); - - /* Restore window sizes. */ - win_size_restore(&winsizes); - - unblock_autocmds(); - } - - ga_clear(&winsizes); - restart_edit = save_restart_edit; - cmdmsg_rl = save_cmdmsg_rl; - - State = save_State; - setmouse(); - - return cmdwin_result; -} - -/* - * Used for commands that either take a simple command string argument, or: - * cmd << endmarker - * {script} - * endmarker - * Returns a pointer to allocated memory with {script} or NULL. - */ -char_u *script_get(exarg_T *eap, char_u *cmd) -{ - char_u *theline; - char *end_pattern = NULL; - char dot[] = "."; - garray_T ga; - - if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) - return NULL; - - ga_init(&ga, 1, 0x400); - - if (cmd[2] != NUL) - end_pattern = (char *)skipwhite(cmd + 2); - else - end_pattern = dot; - - for (;; ) { - theline = eap->getline( - eap->cstack->cs_looplevel > 0 ? -1 : - NUL, eap->cookie, 0); - - if (theline == NULL || STRCMP(end_pattern, theline) == 0) { - free(theline); - break; - } - - ga_concat(&ga, theline); - ga_append(&ga, '\n'); - free(theline); - } - ga_append(&ga, NUL); - - return (char_u *)ga.ga_data; -} diff --git a/src/ex_getln.h b/src/ex_getln.h deleted file mode 100644 index 32980783b2..0000000000 --- a/src/ex_getln.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef NEOVIM_EX_GETLN_H -#define NEOVIM_EX_GETLN_H -/* ex_getln.c */ -char_u *getcmdline(int firstc, long count, int indent); -char_u *getcmdline_prompt(int firstc, char_u *prompt, int attr, - int xp_context, - char_u *xp_arg); -int text_locked(void); -void text_locked_msg(void); -int curbuf_locked(void); -int allbuf_locked(void); -char_u *getexline(int c, void *cookie, int indent); -char_u *getexmodeline(int promptc, void *cookie, int indent); -void free_cmdline_buf(void); -void putcmdline(int c, int shift); -void unputcmdline(void); -int put_on_cmdline(char_u *str, int len, int redraw); -char_u *save_cmdline_alloc(void); -void restore_cmdline_alloc(char_u *p); -void cmdline_paste_str(char_u *s, int literally); -void redrawcmdline(void); -void redrawcmd(void); -void compute_cmdrow(void); -void gotocmdline(int clr); -char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, - int mode); -void ExpandInit(expand_T *xp); -void ExpandCleanup(expand_T *xp); -void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u * - *files, - int options); -char_u *vim_strsave_fnameescape(char_u *fname, int shell); -void tilde_replace(char_u *orig_pat, int num_files, char_u **files); -char_u *sm_gettail(char_u *s); -char_u *addstar(char_u *fname, int len, int context); -void set_cmd_context(expand_T *xp, char_u *str, int len, int col); -int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, - char_u ***matches); -int ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, - char_u ***file, char_u *((*func)(expand_T *, int)), - int escaped); -char_u *globpath(char_u *path, char_u *file, int expand_options); -void init_history(void); -int get_histtype(char_u *name); -void add_to_history(int histype, char_u *new_entry, int in_map, int sep); -int get_history_idx(int histype); -char_u *get_cmdline_str(void); -int get_cmdline_pos(void); -int set_cmdline_pos(int pos); -int get_cmdline_type(void); -char_u *get_history_entry(int histype, int idx); -int clr_history(int histype); -int del_history_entry(int histype, char_u *str); -int del_history_idx(int histype, int idx); -void remove_key_from_history(void); -int get_list_range(char_u **str, int *num1, int *num2); -void ex_history(exarg_T *eap); -void prepare_viminfo_history(int asklen, int writing); -int read_viminfo_history(vir_T *virp, int writing); -void finish_viminfo_history(void); -void write_viminfo_history(FILE *fp, int merge); -void cmd_pchar(int c, int offset); -int cmd_gchar(int offset); -char_u *script_get(exarg_T *eap, char_u *cmd); - -#endif /* NEOVIM_EX_GETLN_H */ diff --git a/src/farsi.c b/src/farsi.c deleted file mode 100644 index ea9432ac8b..0000000000 --- a/src/farsi.c +++ /dev/null @@ -1,2994 +0,0 @@ -/// @file farsi.c -/// -/// Functions for Farsi language -/// - - -#include "edit.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "ex_getln.h" -#include "farsi.h" -#include "getchar.h" -#include "memline.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "screen.h" -#include "vim.h" - - -#define SRC_EDT 0 -#define SRC_CMD 1 - -#define AT_CURSOR 0 - -// special Farsi text messages - -const char_u farsi_text_1[] = { - YE_, _SIN, RE, ALEF_, _FE, ' ', 'V', 'I', 'M', - ' ', F_HE, _BE, ' ', SHIN, RE, _GAF, DAL, ' ', NOON, - ALEF_, _YE, ALEF_, _PE, '\0' -}; - -const char_u farsi_text_2[] = { - YE_, _SIN, RE, ALEF_, _FE, ' ', FARSI_3, FARSI_3, - FARSI_4, FARSI_2, ' ', DAL, RE, ALEF, DAL, _NOON, - ALEF_, _TE, _SIN, ALEF, ' ', F_HE, _BE, ' ', SHIN, - RE, _GAF, DAL, ' ', NOON, ALEF_, _YE, ALEF_, _PE, '\0' -}; - -const char_u farsi_text_3[] = { - DAL, WAW, _SHIN, _YE, _MIM, _NOON, ' ', YE_, _NOON, - ALEF_, _BE, _YE, _TE, _SHIN, _PE, ' ', 'R', 'E', 'P', 'L', - 'A', 'C', 'E', ' ', NOON, ALEF_, _MIM, RE, _FE, ZE, ALEF, - ' ', 'R', 'E', 'V', 'E', 'R', 'S', 'E', ' ', 'I', 'N', - 'S', 'E', 'R', 'T', ' ', SHIN, WAW, RE, ' ', ALEF_, _BE, - ' ', YE_, _SIN, RE, ALEF_, _FE, ' ', RE, DAL, ' ', RE, - ALEF_, _KAF, ' ', MIM, ALEF_, _GAF, _NOON, _HE, '\0' -}; - -const char_u farsi_text_5[] = { - ' ', YE_, _SIN, RE, ALEF_, _FE, '\0' -}; - -static int toF_Xor_X_(int c); -static int F_is_TyE(int c); -static int F_is_TyC_TyD(int c); -static int F_is_TyB_TyC_TyD(int src, int offset); -static int toF_TyB(int c); -static void put_curr_and_l_to_X(int c); -static void put_and_redo(int c); -static void chg_c_toX_orX(void); -static void chg_c_to_X_orX_(void); -static void chg_c_to_X_or_X(void); -static void chg_l_to_X_orX_(void); -static void chg_l_toXor_X(void); -static void chg_r_to_Xor_X_(void); -static int toF_leading(int c); -static int toF_Rjoin(int c); -static int canF_Ljoin(int c); -static int canF_Rjoin(int c); -static int F_isterm(int c); -static int toF_ending(int c); -static void lrswapbuf(char_u *buf, int len); - -/// Convert the given Farsi character into a _X or _X_ type -/// -/// @param c The character to convert. -/// -/// @return Farsi character converted to a _X or _X_ type. -static int toF_Xor_X_(int c) -{ - int tempc; - - switch (c) { - case BE: - return _BE; - - case PE: - return _PE; - - case TE: - return _TE; - - case SE: - return _SE; - - case JIM: - return _JIM; - - case CHE: - return _CHE; - - case HE_J: - return _HE_J; - - case XE: - return _XE; - - case SIN: - return _SIN; - - case SHIN: - return _SHIN; - - case SAD: - return _SAD; - - case ZAD: - return _ZAD; - - case AYN: - return _AYN; - - case AYN_: - return _AYN_; - - case GHAYN: - return _GHAYN; - - case GHAYN_: - return _GHAYN_; - - case FE: - return _FE; - - case GHAF: - return _GHAF; - - case KAF: - return _KAF; - - case GAF: - return _GAF; - - case LAM: - return _LAM; - - case MIM: - return _MIM; - - case NOON: - return _NOON; - - case YE: - case YE_: - return _YE; - - case YEE: - case YEE_: - return _YEE; - - case IE: - case IE_: - return _IE; - - case F_HE: - tempc = _HE; - - if (p_ri && - (curwin->w_cursor.col + 1 < (colnr_T)STRLEN(ml_get_curline()))) { - inc_cursor(); - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = _HE_; - } - dec_cursor(); - } - - if (!p_ri && STRLEN(ml_get_curline())) { - dec_cursor(); - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = _HE_; - } - inc_cursor(); - } - - return tempc; - } - return 0; -} - -/// Convert the given Farsi character into Farsi capital character. -/// -/// @param c The character to convert. -/// -/// @return Character converted to the Farsi capital leter. -int toF_TyA(int c) -{ - switch (c) { - case ALEF_: - return ALEF; - - case ALEF_U_H_: - return ALEF_U_H; - - case _BE: - return BE; - - case _PE: - return PE; - - case _TE: - return TE; - - case _SE: - return SE; - - case _JIM: - return JIM; - - case _CHE: - return CHE; - - case _HE_J: - return HE_J; - - case _XE: - return XE; - - case _SIN: - return SIN; - - case _SHIN: - return SHIN; - - case _SAD: - return SAD; - - case _ZAD: - return ZAD; - - case _AYN: - case AYN_: - case _AYN_: - return AYN; - - case _GHAYN: - case GHAYN_: - case _GHAYN_: - return GHAYN; - - case _FE: - return FE; - - case _GHAF: - return GHAF; - - // I am not sure what it is !!! - // case _KAF_H: - case _KAF: - return KAF; - - case _GAF: - return GAF; - - case _LAM: - return LAM; - - case _MIM: - return MIM; - - case _NOON: - return NOON; - - case _YE: - case YE_: - return YE; - - case _YEE: - case YEE_: - return YEE; - - case TEE_: - return TEE; - - case _IE: - case IE_: - return IE; - - case _HE: - case _HE_: - return F_HE; - } - return c; -} - -/// Is the character under the cursor+offset in the given buffer a join type. -/// That is a character that is combined with the others. -/// Note: the offset is used only for command line buffer. -/// -/// @param src -/// @param offset -/// -/// @return TRUE if the character under the cursor+offset is a join type. -static int F_is_TyB_TyC_TyD(int src, int offset) -{ - int c; - - if (src == SRC_EDT) { - c = gchar_cursor(); - } else { - c = cmd_gchar(AT_CURSOR + offset); - } - - switch (c) { - case _LAM: - case _BE: - case _PE: - case _TE: - case _SE: - case _JIM: - case _CHE: - case _HE_J: - case _XE: - case _SIN: - case _SHIN: - case _SAD: - case _ZAD: - case _TA: - case _ZA: - case _AYN: - case _AYN_: - case _GHAYN: - case _GHAYN_: - case _FE: - case _GHAF: - case _KAF: - case _KAF_H: - case _GAF: - case _MIM: - case _NOON: - case _YE: - case _YEE: - case _IE: - case _HE_: - case _HE: - return TRUE; - } - return FALSE; -} - -/// Is the Farsi character one of the terminating only type. -/// -/// @param c The character to check. -/// -/// @return TRUE if the Farsi character is one of the terminating only types. -static int F_is_TyE(int c) -{ - switch (c) { - case ALEF_A: - case ALEF_D_H: - case DAL: - case ZAL: - case RE: - case ZE: - case JE: - case WAW: - case WAW_H: - case HAMZE: - return TRUE; - } - return FALSE; -} - -/// Is the Farsi character one of the none leading type. -/// -/// @param c The character to check. -/// -/// @return TRUE if the Farsi character is one of the none-leading types. -static int F_is_TyC_TyD(int c) -{ - switch (c) { - case ALEF_: - case ALEF_U_H_: - case _AYN_: - case AYN_: - case _GHAYN_: - case GHAYN_: - case _HE_: - case YE_: - case IE_: - case TEE_: - case YEE_: - return TRUE; - } - return FALSE; -} - -/// Convert a none leading Farsi char into a leading type. -/// -/// @param c The character to convert. -/// -/// @return The character converted into a leading type. -static int toF_TyB(int c) -{ - switch (c) { - case ALEF_: - return ALEF; - - case ALEF_U_H_: - return ALEF_U_H; - - case _AYN_: - return _AYN; - - case AYN_: - // exception - there are many of them - return AYN; - - case _GHAYN_: - return _GHAYN; - - case GHAYN_: - // exception - there are many of them - return GHAYN; - - case _HE_: - return _HE; - - case YE_: - return YE; - - case IE_: - return IE; - - case TEE_: - return TEE; - - case YEE_: - return YEE; - } - return c; -} - -/// Overwrite the current redo and cursor characters + left adjust -/// -/// @param c -static void put_curr_and_l_to_X(int c) -{ - int tempc; - - if (curwin->w_p_rl && p_ri) { - return; - } - - if ((curwin->w_cursor.col < (colnr_T)STRLEN(ml_get_curline()))) { - if ((p_ri && curwin->w_cursor.col) || !p_ri) { - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } - - if (F_is_TyC_TyD((tempc = gchar_cursor()))) { - pchar_cursor(toF_TyB(tempc)); - AppendCharToRedobuff(K_BS); - AppendCharToRedobuff(tempc); - } - - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } - } - } - - put_and_redo(c); -} - -static void put_and_redo(int c) -{ - pchar_cursor(c); - AppendCharToRedobuff(K_BS); - AppendCharToRedobuff(c); -} - -/// Change the char. under the cursor to a X_ or X type -static void chg_c_toX_orX(void) -{ - int tempc, curc; - - switch ((curc = gchar_cursor())) { - case _BE: - tempc = BE; - break; - - case _PE: - tempc = PE; - break; - - case _TE: - tempc = TE; - break; - - case _SE: - tempc = SE; - break; - - case _JIM: - tempc = JIM; - break; - - case _CHE: - tempc = CHE; - break; - - case _HE_J: - tempc = HE_J; - break; - - case _XE: - tempc = XE; - break; - - case _SIN: - tempc = SIN; - break; - - case _SHIN: - tempc = SHIN; - break; - - case _SAD: - tempc = SAD; - break; - - case _ZAD: - tempc = ZAD; - break; - - case _FE: - tempc = FE; - break; - - case _GHAF: - tempc = GHAF; - break; - - case _KAF_H: - case _KAF: - tempc = KAF; - break; - - case _GAF: - tempc = GAF; - break; - - case _AYN: - tempc = AYN; - break; - - case _AYN_: - tempc = AYN_; - break; - - case _GHAYN: - tempc = GHAYN; - break; - - case _GHAYN_: - tempc = GHAYN_; - break; - - case _LAM: - tempc = LAM; - break; - - case _MIM: - tempc = MIM; - break; - - case _NOON: - tempc = NOON; - break; - - case _HE: - case _HE_: - tempc = F_HE; - break; - - case _YE: - case _IE: - case _YEE: - if (p_ri) { - inc_cursor(); - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = (curc == _YE ? YE_ : - (curc == _IE ? IE_ : YEE_)); - } else { - tempc = (curc == _YE ? YE : - (curc == _IE ? IE : YEE)); - } - dec_cursor(); - } else { - if (curwin->w_cursor.col) { - dec_cursor(); - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = (curc == _YE ? YE_ : - (curc == _IE ? IE_ : YEE_)); - } else { - tempc = (curc == _YE ? YE : - (curc == _IE ? IE : YEE)); - } - inc_cursor(); - } else { - tempc = (curc == _YE ? YE : - (curc == _IE ? IE : YEE)); - } - } - break; - - default: - tempc = 0; - } - - if (tempc) { - put_and_redo(tempc); - } -} - -/// Change the char. under the cursor to a _X_ or X_ type -static void chg_c_to_X_orX_(void) -{ - int tempc; - - switch (gchar_cursor()) { - case ALEF: - tempc = ALEF_; - break; - - case ALEF_U_H: - tempc = ALEF_U_H_; - break; - - case _AYN: - tempc = _AYN_; - break; - - case AYN: - tempc = AYN_; - break; - - case _GHAYN: - tempc = _GHAYN_; - break; - - case GHAYN: - tempc = GHAYN_; - break; - - case _HE: - tempc = _HE_; - break; - - case YE: - tempc = YE_; - break; - - case IE: - tempc = IE_; - break; - - case TEE: - tempc = TEE_; - break; - - case YEE: - tempc = YEE_; - break; - - default: - tempc = 0; - } - - if (tempc) { - put_and_redo(tempc); - } -} - -/// Change the char. under the cursor to a _X_ or _X type -static void chg_c_to_X_or_X(void) -{ - int tempc; - - tempc = gchar_cursor(); - - if (curwin->w_cursor.col + 1 < (colnr_T)STRLEN(ml_get_curline())) { - inc_cursor(); - if ((tempc == F_HE) && (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR))) { - tempc = _HE_; - dec_cursor(); - put_and_redo(tempc); - return; - } - - dec_cursor(); - } - - if ((tempc = toF_Xor_X_(tempc)) != 0) { - put_and_redo(tempc); - } -} - -/// Change the character left to the cursor to a _X_ or X_ type -static void chg_l_to_X_orX_(void) -{ - int tempc; - - if ((curwin->w_cursor.col != 0) - && (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(ml_get_curline()))) { - return; - } - - if (!curwin->w_cursor.col && p_ri) { - return; - } - - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } - - switch (gchar_cursor()) { - case ALEF: - tempc = ALEF_; - break; - - case ALEF_U_H: - tempc = ALEF_U_H_; - break; - - case _AYN: - tempc = _AYN_; - break; - - case AYN: - tempc = AYN_; - break; - - case _GHAYN: - tempc = _GHAYN_; - break; - - case GHAYN: - tempc = GHAYN_; - break; - - case _HE: - tempc = _HE_; - break; - - case YE: - tempc = YE_; - break; - - case IE: - tempc = IE_; - break; - - case TEE: - tempc = TEE_; - break; - - case YEE: - tempc = YEE_; - break; - - default: - tempc = 0; - } - - if (tempc) { - put_and_redo(tempc); - } - - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } -} - -/// Change the character left to the cursor to a X or _X type -static void chg_l_toXor_X(void) -{ - int tempc; - - if ((curwin->w_cursor.col != 0) && - (curwin->w_cursor.col + 1 == (colnr_T)STRLEN(ml_get_curline()))) { - return; - } - - if (!curwin->w_cursor.col && p_ri) { - return; - } - - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } - - switch (gchar_cursor()) { - case ALEF_: - tempc = ALEF; - break; - - case ALEF_U_H_: - tempc = ALEF_U_H; - break; - - case _AYN_: - tempc = _AYN; - break; - - case AYN_: - tempc = AYN; - break; - - case _GHAYN_: - tempc = _GHAYN; - break; - - case GHAYN_: - tempc = GHAYN; - break; - - case _HE_: - tempc = _HE; - break; - - case YE_: - tempc = YE; - break; - - case IE_: - tempc = IE; - break; - - case TEE_: - tempc = TEE; - break; - - case YEE_: - tempc = YEE; - break; - - default: - tempc = 0; - } - - if (tempc) { - put_and_redo(tempc); - } - - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } -} - -/// Change the character right to the cursor to a _X or _X_ type -static void chg_r_to_Xor_X_(void) -{ - int tempc, c; - - if (curwin->w_cursor.col) { - if (!p_ri) { - dec_cursor(); - } - - tempc = gchar_cursor(); - if ((c = toF_Xor_X_(tempc)) != 0) { - put_and_redo(c); - } - - if (!p_ri) { - inc_cursor(); - } - } -} - -/// Map Farsi keyboard when in fkmap mode. -int fkmap(int c) -{ - int tempc; - static int revins; - - if (IS_SPECIAL(c)) { - return c; - } - - if (VIM_ISDIGIT(c) - || (((c == '.') - || (c == '+') - || (c == '-') - || (c == '^') - || (c == '%') - || (c == '#') - || (c == '=')) - && revins)) { - if (!revins) { - if (curwin->w_cursor.col) { - if (!p_ri) { - dec_cursor(); - } - - chg_c_toX_orX(); - chg_l_toXor_X(); - if (!p_ri) { - inc_cursor(); - } - } - } - - arrow_used = TRUE; - (void)stop_arrow(); - - if (!curwin->w_p_rl && revins) { - inc_cursor(); - } - - revins++; - p_ri = 1; - } else { - if (revins) { - arrow_used = TRUE; - (void)stop_arrow(); - - revins = 0; - if (curwin->w_p_rl) { - while ((F_isdigit(gchar_cursor()) - || (gchar_cursor() == F_PERIOD - || gchar_cursor() == F_PLUS - || gchar_cursor() == F_MINUS - || gchar_cursor() == F_MUL - || gchar_cursor() == F_DIVIDE - || gchar_cursor() == F_PERCENT - || gchar_cursor() == F_EQUALS)) - && gchar_cursor() != NUL) { - curwin->w_cursor.col++; - } - } else { - if (curwin->w_cursor.col) { - while ((F_isdigit(gchar_cursor()) - || (gchar_cursor() == F_PERIOD - || gchar_cursor() == F_PLUS - || gchar_cursor() == F_MINUS - || gchar_cursor() == F_MUL - || gchar_cursor() == F_DIVIDE - || gchar_cursor() == F_PERCENT - || gchar_cursor() == F_EQUALS)) - && --curwin->w_cursor.col) { - } - } - - if (!F_isdigit(gchar_cursor())) { - ++curwin->w_cursor.col; - } - } - } - } - - if (!revins) { - if (curwin->w_p_rl) { - p_ri = 0; - } - - if (!curwin->w_p_rl) { - p_ri = 1; - } - } - - if ((c < 0x100) && - (isalpha(c) || - (c == '&') || - (c == '^') || - (c == ';') || - (c == '\'') || - (c == ',') || - (c == '[') || - (c == ']') || - (c == '{') || - (c == '}'))) { - chg_r_to_Xor_X_(); - } - - tempc = 0; - switch (c) { - case '`': - case ' ': - case '.': - case '!': - case '"': - case '$': - case '%': - case '^': - case '&': - case '/': - case '(': - case ')': - case '=': - case '\\': - case '?': - case '+': - case '-': - case '_': - case '*': - case ':': - case '#': - case '~': - case '@': - case '<': - case '>': - case '{': - case '}': - case '|': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'B': - case 'E': - case 'F': - case 'H': - case 'I': - case 'K': - case 'L': - case 'M': - case 'O': - case 'P': - case 'Q': - case 'R': - case 'T': - case 'U': - case 'W': - case 'Y': - case NL: - case TAB: - if (p_ri && (c == NL) && curwin->w_cursor.col) { - // If the char before the cursor is _X_ or X_ do not change - // the one under the cursor with X type. - - dec_cursor(); - if (F_isalpha(gchar_cursor())) { - inc_cursor(); - return NL; - } - inc_cursor(); - } - - if (!p_ri) { - if (!curwin->w_cursor.col) { - switch (c) { - case '0': - return FARSI_0; - - case '1': - return FARSI_1; - - case '2': - return FARSI_2; - - case '3': - return FARSI_3; - - case '4': - return FARSI_4; - - case '5': - return FARSI_5; - - case '6': - return FARSI_6; - - case '7': - return FARSI_7; - - case '8': - return FARSI_8; - - case '9': - return FARSI_9; - - case 'B': - return F_PSP; - - case 'E': - return JAZR_N; - - case 'F': - return ALEF_D_H; - - case 'H': - return ALEF_A; - - case 'I': - return TASH; - - case 'K': - return F_LQUOT; - - case 'L': - return F_RQUOT; - - case 'M': - return HAMZE; - - case 'O': - return '['; - - case 'P': - return ']'; - - case 'Q': - return OO; - - case 'R': - return MAD_N; - - case 'T': - return OW; - - case 'U': - return MAD; - - case 'W': - return OW_OW; - - case 'Y': - return JAZR; - - case '`': - return F_PCN; - - case '!': - return F_EXCL; - - case '@': - return F_COMMA; - - case '#': - return F_DIVIDE; - - case '$': - return F_CURRENCY; - - case '%': - return F_PERCENT; - - case '^': - return F_MUL; - - case '&': - return F_BCOMMA; - - case '*': - return F_STAR; - - case '(': - return F_LPARENT; - - case ')': - return F_RPARENT; - - case '-': - return F_MINUS; - - case '_': - return F_UNDERLINE; - - case '=': - return F_EQUALS; - - case '+': - return F_PLUS; - - case '\\': - return F_BSLASH; - - case '|': - return F_PIPE; - - case ':': - return F_DCOLON; - - case '"': - return F_SEMICOLON; - - case '.': - return F_PERIOD; - - case '/': - return F_SLASH; - - case '<': - return F_LESS; - - case '>': - return F_GREATER; - - case '?': - return F_QUESTION; - - case ' ': - return F_BLANK; - } - break; - } - } - - if (!p_ri) { - dec_cursor(); - } - - switch ((tempc = gchar_cursor())) { - case _BE: - case _PE: - case _TE: - case _SE: - case _JIM: - case _CHE: - case _HE_J: - case _XE: - case _SIN: - case _SHIN: - case _SAD: - case _ZAD: - case _FE: - case _GHAF: - case _KAF: - case _KAF_H: - case _GAF: - case _LAM: - case _MIM: - case _NOON: - case _HE: - case _HE_: - case _TA: - case _ZA: - put_curr_and_l_to_X(toF_TyA(tempc)); - break; - - case _AYN: - case _AYN_: - if (!p_ri) { - if (!curwin->w_cursor.col) { - put_curr_and_l_to_X(AYN); - break; - } - } - - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = AYN_; - } else { - tempc = AYN; - } - - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } - - put_curr_and_l_to_X(tempc); - break; - - case _GHAYN: - case _GHAYN_: - - if (!p_ri) { - if (!curwin->w_cursor.col) { - put_curr_and_l_to_X(GHAYN); - break; - } - } - - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = GHAYN_; - } else { - tempc = GHAYN; - } - - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } - - put_curr_and_l_to_X(tempc); - break; - - case _YE: - case _IE: - case _YEE: - - if (!p_ri) { - if (!curwin->w_cursor.col) { - put_curr_and_l_to_X((tempc == _YE ? YE : - (tempc == _IE ? IE : YEE))); - break; - } - } - - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = (tempc == _YE ? YE_ : - (tempc == _IE ? IE_ : YEE_)); - } else { - tempc = (tempc == _YE ? YE : - (tempc == _IE ? IE : YEE)); - } - - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } - - put_curr_and_l_to_X(tempc); - break; - } - - if (!p_ri) { - inc_cursor(); - } - - tempc = 0; - - switch (c) { - case '0': - return FARSI_0; - - case '1': - return FARSI_1; - - case '2': - return FARSI_2; - - case '3': - return FARSI_3; - - case '4': - return FARSI_4; - - case '5': - return FARSI_5; - - case '6': - return FARSI_6; - - case '7': - return FARSI_7; - - case '8': - return FARSI_8; - - case '9': - return FARSI_9; - - case 'B': - return F_PSP; - - case 'E': - return JAZR_N; - - case 'F': - return ALEF_D_H; - - case 'H': - return ALEF_A; - - case 'I': - return TASH; - - case 'K': - return F_LQUOT; - - case 'L': - return F_RQUOT; - - case 'M': - return HAMZE; - - case 'O': - return '['; - - case 'P': - return ']'; - - case 'Q': - return OO; - - case 'R': - return MAD_N; - - case 'T': - return OW; - - case 'U': - return MAD; - - case 'W': - return OW_OW; - - case 'Y': - return JAZR; - - case '`': - return F_PCN; - - case '!': - return F_EXCL; - - case '@': - return F_COMMA; - - case '#': - return F_DIVIDE; - - case '$': - return F_CURRENCY; - - case '%': - return F_PERCENT; - - case '^': - return F_MUL; - - case '&': - return F_BCOMMA; - - case '*': - return F_STAR; - - case '(': - return F_LPARENT; - - case ')': - return F_RPARENT; - - case '-': - return F_MINUS; - - case '_': - return F_UNDERLINE; - - case '=': - return F_EQUALS; - - case '+': - return F_PLUS; - - case '\\': - return F_BSLASH; - - case '|': - return F_PIPE; - - case ':': - return F_DCOLON; - - case '"': - return F_SEMICOLON; - - case '.': - return F_PERIOD; - - case '/': - return F_SLASH; - - case '<': - return F_LESS; - - case '>': - return F_GREATER; - - case '?': - return F_QUESTION; - - case ' ': - return F_BLANK; - } - break; - - case 'a': - tempc = _SHIN; - break; - - case 'A': - tempc = WAW_H; - break; - - case 'b': - tempc = ZAL; - break; - - case 'c': - tempc = ZE; - break; - - case 'C': - tempc = JE; - break; - - case 'd': - tempc = _YE; - break; - - case 'D': - tempc = _YEE; - break; - - case 'e': - tempc = _SE; - break; - - case 'f': - tempc = _BE; - break; - - case 'g': - tempc = _LAM; - break; - - case 'G': - if (!curwin->w_cursor.col && STRLEN(ml_get_curline())) { - if (gchar_cursor() == _LAM) { - chg_c_toX_orX(); - } else if (p_ri) { - chg_c_to_X_or_X(); - } - } - - if (!p_ri) { - if (!curwin->w_cursor.col) { - return ALEF_U_H; - } - } - - if (!p_ri) { - dec_cursor(); - } - - if (gchar_cursor() == _LAM) { - chg_c_toX_orX(); - chg_l_toXor_X(); - tempc = ALEF_U_H; - } else if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = ALEF_U_H_; - chg_l_toXor_X(); - } else { - tempc = ALEF_U_H; - } - - if (!p_ri) { - inc_cursor(); - } - - return tempc; - - case 'h': - if (!curwin->w_cursor.col && STRLEN(ml_get_curline())) { - if (p_ri) { - chg_c_to_X_or_X(); - } - } - - if (!p_ri) { - if (!curwin->w_cursor.col) { - return ALEF; - } - } - - if (!p_ri) { - dec_cursor(); - } - - if (gchar_cursor() == _LAM) { - chg_l_toXor_X(); - del_char(FALSE); - AppendCharToRedobuff(K_BS); - - if (!p_ri) { - dec_cursor(); - } - - tempc = LA; - } else { - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = ALEF_; - chg_l_toXor_X(); - } else { - tempc = ALEF; - } - } - - if (!p_ri) { - inc_cursor(); - } - - return tempc; - - case 'i': - - if (!curwin->w_cursor.col && STRLEN(ml_get_curline())) { - if (!p_ri && !F_is_TyE(tempc)) { - chg_c_to_X_orX_(); - } - - if (p_ri) { - chg_c_to_X_or_X(); - } - } - - if (!p_ri && !curwin->w_cursor.col) { - return _HE; - } - - if (!p_ri) { - dec_cursor(); - } - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = _HE_; - } else { - tempc = _HE; - } - - if (!p_ri) { - inc_cursor(); - } - break; - - case 'j': - tempc = _TE; - break; - - case 'J': - - if (!curwin->w_cursor.col && STRLEN(ml_get_curline())) { - if (p_ri) { - chg_c_to_X_or_X(); - } - } - - if (!p_ri) { - if (!curwin->w_cursor.col) { - return TEE; - } - } - - if (!p_ri) { - dec_cursor(); - } - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = TEE_; - chg_l_toXor_X(); - } else { - tempc = TEE; - } - - if (!p_ri) { - inc_cursor(); - } - - return tempc; - - case 'k': - tempc = _NOON; - break; - - case 'l': - tempc = _MIM; - break; - - case 'm': - tempc = _PE; - break; - - case 'n': - case 'N': - tempc = DAL; - break; - - case 'o': - tempc = _XE; - break; - - case 'p': - tempc = _HE_J; - break; - - case 'q': - tempc = _ZAD; - break; - - case 'r': - tempc = _GHAF; - break; - - case 's': - tempc = _SIN; - break; - - case 'S': - tempc = _IE; - break; - - case 't': - tempc = _FE; - break; - - case 'u': - if (!curwin->w_cursor.col && STRLEN(ml_get_curline())) { - if (!p_ri && !F_is_TyE(tempc)) { - chg_c_to_X_orX_(); - } - - if (p_ri) { - chg_c_to_X_or_X(); - } - } - - if (!p_ri && !curwin->w_cursor.col) { - return _AYN; - } - - if (!p_ri) { - dec_cursor(); - } - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = _AYN_; - } else { - tempc = _AYN; - } - - if (!p_ri) { - inc_cursor(); - } - break; - - case 'v': - case 'V': - tempc = RE; - break; - - case 'w': - tempc = _SAD; - break; - - case 'x': - case 'X': - tempc = _TA; - break; - - case 'y': - if (!curwin->w_cursor.col && STRLEN(ml_get_curline())) { - if (!p_ri && !F_is_TyE(tempc)) { - chg_c_to_X_orX_(); - } - - if (p_ri) { - chg_c_to_X_or_X(); - } - } - - if (!p_ri && !curwin->w_cursor.col) { - return _GHAYN; - } - - if (!p_ri) { - dec_cursor(); - } - - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = _GHAYN_; - } else { - tempc = _GHAYN; - } - - if (!p_ri) { - inc_cursor(); - } - - break; - - case 'z': - tempc = _ZA; - break; - - case 'Z': - tempc = _KAF_H; - break; - - case ';': - tempc = _KAF; - break; - - case '\'': - tempc = _GAF; - break; - - case ',': - tempc = WAW; - break; - - case '[': - tempc = _JIM; - break; - - case ']': - tempc = _CHE; - break; - } - - if ((F_isalpha(tempc) || F_isdigit(tempc))) { - if (!curwin->w_cursor.col && STRLEN(ml_get_curline())) { - if (!p_ri && !F_is_TyE(tempc)) { - chg_c_to_X_orX_(); - } - - if (p_ri) { - chg_c_to_X_or_X(); - } - } - - if (curwin->w_cursor.col) { - if (!p_ri) { - dec_cursor(); - } - - if (F_is_TyE(tempc)) { - chg_l_toXor_X(); - } else { - chg_l_to_X_orX_(); - } - - if (!p_ri) { - inc_cursor(); - } - } - } - - if (tempc) { - return tempc; - } - return c; -} - -/// Convert a none leading Farsi char into a leading type. -/// -/// @param c The character to convert. -/// -/// @return The non-leading Farsi character converted to a leading type. -static int toF_leading(int c) -{ - switch (c) { - case ALEF_: - return ALEF; - - case ALEF_U_H_: - return ALEF_U_H; - - case BE: - return _BE; - - case PE: - return _PE; - - case TE: - return _TE; - - case SE: - return _SE; - - case JIM: - return _JIM; - - case CHE: - return _CHE; - - case HE_J: - return _HE_J; - - case XE: - return _XE; - - case SIN: - return _SIN; - - case SHIN: - return _SHIN; - - case SAD: - return _SAD; - - case ZAD: - return _ZAD; - - case AYN: - case AYN_: - case _AYN_: - return _AYN; - - case GHAYN: - case GHAYN_: - case _GHAYN_: - return _GHAYN; - - case FE: - return _FE; - - case GHAF: - return _GHAF; - - case KAF: - return _KAF; - - case GAF: - return _GAF; - - case LAM: - return _LAM; - - case MIM: - return _MIM; - - case NOON: - return _NOON; - - case _HE_: - case F_HE: - return _HE; - - case YE: - case YE_: - return _YE; - - case IE_: - case IE: - return _IE; - - case YEE: - case YEE_: - return _YEE; - } - return c; -} - -/// Convert a given Farsi char into right joining type. -/// -/// @param c The character to convert. -/// -/// @return The Farsi character converted into a right joining type -static int toF_Rjoin(int c) -{ - switch (c) { - case ALEF: - return ALEF_; - - case ALEF_U_H: - return ALEF_U_H_; - - case BE: - return _BE; - - case PE: - return _PE; - - case TE: - return _TE; - - case SE: - return _SE; - - case JIM: - return _JIM; - - case CHE: - return _CHE; - - case HE_J: - return _HE_J; - - case XE: - return _XE; - - case SIN: - return _SIN; - - case SHIN: - return _SHIN; - - case SAD: - return _SAD; - - case ZAD: - return _ZAD; - - case AYN: - case AYN_: - case _AYN: - return _AYN_; - - case GHAYN: - case GHAYN_: - case _GHAYN_: - return _GHAYN_; - - case FE: - return _FE; - - case GHAF: - return _GHAF; - - case KAF: - return _KAF; - - case GAF: - return _GAF; - - case LAM: - return _LAM; - - case MIM: - return _MIM; - - case NOON: - return _NOON; - - case _HE: - case F_HE: - return _HE_; - - case YE: - case YE_: - return _YE; - - case IE_: - case IE: - return _IE; - - case TEE: - return TEE_; - - case YEE: - case YEE_: - return _YEE; - } - return c; -} - -/// Can a given Farsi character join via its left edj. -/// -/// @param c The character to check. -/// -/// @return TRUE if the character can join via its left edj. -static int canF_Ljoin(int c) -{ - switch (c) { - case _BE: - case BE: - case PE: - case _PE: - case TE: - case _TE: - case SE: - case _SE: - case JIM: - case _JIM: - case CHE: - case _CHE: - case HE_J: - case _HE_J: - case XE: - case _XE: - case SIN: - case _SIN: - case SHIN: - case _SHIN: - case SAD: - case _SAD: - case ZAD: - case _ZAD: - case _TA: - case _ZA: - case AYN: - case _AYN: - case _AYN_: - case AYN_: - case GHAYN: - case GHAYN_: - case _GHAYN_: - case _GHAYN: - case FE: - case _FE: - case GHAF: - case _GHAF: - case _KAF_H: - case KAF: - case _KAF: - case GAF: - case _GAF: - case LAM: - case _LAM: - case MIM: - case _MIM: - case NOON: - case _NOON: - case IE: - case _IE: - case IE_: - case YE: - case _YE: - case YE_: - case YEE: - case _YEE: - case YEE_: - case F_HE: - case _HE: - case _HE_: - return TRUE; - } - return FALSE; -} - -/// Can a given Farsi character join via its right edj. -/// -/// @param c -/// -/// @return TRUE if the character can join via its right edj. -static int canF_Rjoin(int c) -{ - switch (c) { - case ALEF: - case ALEF_: - case ALEF_U_H: - case ALEF_U_H_: - case DAL: - case ZAL: - case RE: - case JE: - case ZE: - case TEE: - case TEE_: - case WAW: - case WAW_H: - return TRUE; - } - - return canF_Ljoin(c); -} - -/// Is a given Farsi character a terminating type. -/// -/// @param c -/// -/// @return TRUE if the character is a terminating type. -static int F_isterm(int c) -{ - switch (c) { - case ALEF: - case ALEF_: - case ALEF_U_H: - case ALEF_U_H_: - case DAL: - case ZAL: - case RE: - case JE: - case ZE: - case WAW: - case WAW_H: - case TEE: - case TEE_: - return TRUE; - } - - return FALSE; -} - -/// Convert the given Farsi character into a ending type. -/// -/// @param c The character to convert. -/// -/// @return The character converted into an ending type. -static int toF_ending(int c) -{ - switch (c) { - case _BE: - return BE; - - case _PE: - return PE; - - case _TE: - return TE; - - case _SE: - return SE; - - case _JIM: - return JIM; - - case _CHE: - return CHE; - - case _HE_J: - return HE_J; - - case _XE: - return XE; - - case _SIN: - return SIN; - - case _SHIN: - return SHIN; - - case _SAD: - return SAD; - - case _ZAD: - return ZAD; - - case _AYN: - return AYN; - - case _AYN_: - return AYN_; - - case _GHAYN: - return GHAYN; - - case _GHAYN_: - return GHAYN_; - - case _FE: - return FE; - - case _GHAF: - return GHAF; - - case _KAF_H: - case _KAF: - return KAF; - - case _GAF: - return GAF; - - case _LAM: - return LAM; - - case _MIM: - return MIM; - - case _NOON: - return NOON; - - case _YE: - return YE_; - - case YE_: - return YE; - - case _YEE: - return YEE_; - - case YEE_: - return YEE; - - case TEE: - return TEE_; - - case _IE: - return IE_; - - case IE_: - return IE; - - case _HE: - case _HE_: - return F_HE; - } - return c; -} - -/// Convert the Farsi 3342 standard into Farsi VIM. -void conv_to_pvim(void) -{ - char_u *ptr; - int lnum, llen, i; - - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { - ptr = ml_get((linenr_T)lnum); - llen = (int)STRLEN(ptr); - for (i = 0; i < llen - 1; i++) { - if (canF_Ljoin(ptr[i]) && canF_Rjoin(ptr[i + 1])) { - ptr[i] = toF_leading(ptr[i]); - i++; - - while (canF_Rjoin(ptr[i]) && i < llen) { - ptr[i] = toF_Rjoin(ptr[i]); - if (F_isterm(ptr[i]) || !F_isalpha(ptr[i])) { - break; - } - i++; - } - - if (!F_isalpha(ptr[i]) || !canF_Rjoin(ptr[i])) { - ptr[i - 1] = toF_ending(ptr[i - 1]); - } - } else { - ptr[i] = toF_TyA(ptr[i]); - } - } - } - - // Following lines contains Farsi encoded character. - do_cmdline_cmd((char_u *)"%s/\202\231/\232/g"); - do_cmdline_cmd((char_u *)"%s/\201\231/\370\334/g"); - - // Assume the screen has been messed up: clear it and redraw. - redraw_later(CLEAR); - MSG_ATTR(farsi_text_1, hl_attr(HLF_S)); -} - -/// Convert the Farsi VIM into Farsi 3342 standard. -void conv_to_pstd(void) -{ - char_u *ptr; - int lnum, llen, i; - - // Following line contains Farsi encoded character. - do_cmdline_cmd((char_u *)"%s/\232/\202\231/g"); - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { - ptr = ml_get((linenr_T)lnum); - llen = (int)STRLEN(ptr); - for (i = 0; i < llen; i++) { - ptr[i] = toF_TyA(ptr[i]); - } - } - - // Assume the screen has been messed up: clear it and redraw. - redraw_later(CLEAR); - MSG_ATTR(farsi_text_2, hl_attr(HLF_S)); -} - -/// left-right swap the characters in buf[len]. -/// -/// @param buf -/// @param len -static void lrswapbuf(char_u *buf, int len) -{ - char_u *s, *e; - int c; - - s = buf; - e = buf + len - 1; - while (e > s) { - c = *s; - *s = *e; - *e = c; - ++s; - --e; - } -} - -/// swap all the characters in reverse direction -/// -/// @param ibuf -/// -/// @return The buffer with the characters swapped. -char_u* lrswap(char_u *ibuf) -{ - if ((ibuf != NULL) && (*ibuf != NUL)) { - lrswapbuf(ibuf, (int)STRLEN(ibuf)); - } - return ibuf; -} - -/// swap all the Farsi characters in reverse direction -/// -/// @param cmdbuf -/// @param . -/// -/// @return The buffer with all Farsi characters swapped. -char_u* lrFswap(char_u *cmdbuf, int len) -{ - int i, cnt; - if (cmdbuf == NULL) { - return cmdbuf; - } - - if ((len == 0) && ((len = (int)STRLEN(cmdbuf)) == 0)) { - return cmdbuf; - } - - for (i = 0; i < len; i++) { - for (cnt = 0; i + cnt < len - && (F_isalpha(cmdbuf[i + cnt]) - || F_isdigit(cmdbuf[i + cnt]) - || cmdbuf[i + cnt] == ' '); ++cnt) { - } - - lrswapbuf(cmdbuf + i, cnt); - i += cnt; - } - return cmdbuf; -} - -/// Reverse the characters in the search path and substitute section -/// accordingly. -/// TODO: handle different separator characters. Use skip_regexp(). -/// -/// @param ibuf -/// -/// @return The buffer with the characters in the search path and substitute -/// section reversed. -char_u* lrF_sub(char_u *ibuf) -{ - char_u *p, *ep; - int i, cnt; - - p = ibuf; - - // Find the boundary of the search path - while (((p = vim_strchr(p + 1, '/')) != NULL) && p[-1] == '\\') { - } - - if (p == NULL) { - return ibuf; - } - - // Reverse the Farsi characters in the search path. - lrFswap(ibuf, (int)(p - ibuf)); - - // Now find the boundary of the substitute section - if ((ep = (char_u *)strrchr((char *)++p, '/')) != NULL) { - cnt = (int)(ep - p); - } else { - cnt = (int)STRLEN(p); - } - - // Reverse the characters in the substitute section and take care of '\' - for (i = 0; i < cnt - 1; i++) { - if (p[i] == '\\') { - p[i] = p[i + 1]; - p[++i] = '\\'; - } - } - - lrswapbuf(p, cnt); - return ibuf; -} - -/// Map Farsi keyboard when in cmd_fkmap mode. -/// -/// @param c -/// -/// @return The mapped character. -int cmdl_fkmap(int c) -{ - int tempc; - - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '`': - case ' ': - case '.': - case '!': - case '"': - case '$': - case '%': - case '^': - case '&': - case '/': - case '(': - case ')': - case '=': - case '\\': - case '?': - case '+': - case '-': - case '_': - case '*': - case ':': - case '#': - case '~': - case '@': - case '<': - case '>': - case '{': - case '}': - case '|': - case 'B': - case 'E': - case 'F': - case 'H': - case 'I': - case 'K': - case 'L': - case 'M': - case 'O': - case 'P': - case 'Q': - case 'R': - case 'T': - case 'U': - case 'W': - case 'Y': - case NL: - case TAB: - switch ((tempc = cmd_gchar(AT_CURSOR))) { - case _BE: - case _PE: - case _TE: - case _SE: - case _JIM: - case _CHE: - case _HE_J: - case _XE: - case _SIN: - case _SHIN: - case _SAD: - case _ZAD: - case _AYN: - case _GHAYN: - case _FE: - case _GHAF: - case _KAF: - case _GAF: - case _LAM: - case _MIM: - case _NOON: - case _HE: - case _HE_: - cmd_pchar(toF_TyA(tempc), AT_CURSOR); - break; - - case _AYN_: - cmd_pchar(AYN_, AT_CURSOR); - break; - - case _GHAYN_: - cmd_pchar(GHAYN_, AT_CURSOR); - break; - - case _IE: - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR + 1)) { - cmd_pchar(IE_, AT_CURSOR); - } else { - cmd_pchar(IE, AT_CURSOR); - } - break; - - case _YEE: - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR + 1)) { - cmd_pchar(YEE_, AT_CURSOR); - } else { - cmd_pchar(YEE, AT_CURSOR); - } - break; - - case _YE: - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR + 1)) { - cmd_pchar(YE_, AT_CURSOR); - } else { - cmd_pchar(YE, AT_CURSOR); - } - } - - switch (c) { - case '0': - return FARSI_0; - - case '1': - return FARSI_1; - - case '2': - return FARSI_2; - - case '3': - return FARSI_3; - - case '4': - return FARSI_4; - - case '5': - return FARSI_5; - - case '6': - return FARSI_6; - - case '7': - return FARSI_7; - - case '8': - return FARSI_8; - - case '9': - return FARSI_9; - - case 'B': - return F_PSP; - - case 'E': - return JAZR_N; - - case 'F': - return ALEF_D_H; - - case 'H': - return ALEF_A; - - case 'I': - return TASH; - - case 'K': - return F_LQUOT; - - case 'L': - return F_RQUOT; - - case 'M': - return HAMZE; - - case 'O': - return '['; - - case 'P': - return ']'; - - case 'Q': - return OO; - - case 'R': - return MAD_N; - - case 'T': - return OW; - - case 'U': - return MAD; - - case 'W': - return OW_OW; - - case 'Y': - return JAZR; - - case '`': - return F_PCN; - - case '!': - return F_EXCL; - - case '@': - return F_COMMA; - - case '#': - return F_DIVIDE; - - case '$': - return F_CURRENCY; - - case '%': - return F_PERCENT; - - case '^': - return F_MUL; - - case '&': - return F_BCOMMA; - - case '*': - return F_STAR; - - case '(': - return F_LPARENT; - - case ')': - return F_RPARENT; - - case '-': - return F_MINUS; - - case '_': - return F_UNDERLINE; - - case '=': - return F_EQUALS; - - case '+': - return F_PLUS; - - case '\\': - return F_BSLASH; - - case '|': - return F_PIPE; - - case ':': - return F_DCOLON; - - case '"': - return F_SEMICOLON; - - case '.': - return F_PERIOD; - - case '/': - return F_SLASH; - - case '<': - return F_LESS; - - case '>': - return F_GREATER; - - case '?': - return F_QUESTION; - - case ' ': - return F_BLANK; - } - break; - - case 'a': - return _SHIN; - - case 'A': - return WAW_H; - - case 'b': - return ZAL; - - case 'c': - return ZE; - - case 'C': - return JE; - - case 'd': - return _YE; - - case 'D': - return _YEE; - - case 'e': - return _SE; - - case 'f': - return _BE; - - case 'g': - return _LAM; - - case 'G': - if (cmd_gchar(AT_CURSOR) == _LAM) { - cmd_pchar(LAM, AT_CURSOR); - return ALEF_U_H; - } - - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR)) { - return ALEF_U_H_; - } else { - return ALEF_U_H; - } - - case 'h': - if (cmd_gchar(AT_CURSOR) == _LAM) { - cmd_pchar(LA, AT_CURSOR); - redrawcmdline(); - return K_IGNORE; - } - - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR)) { - return ALEF_; - } else { - return ALEF; - } - - case 'i': - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR)) { - return _HE_; - } else { - return _HE; - } - - case 'j': - return _TE; - - case 'J': - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR)) { - return TEE_; - } else { - return TEE; - } - - case 'k': - return _NOON; - - case 'l': - return _MIM; - - case 'm': - return _PE; - - case 'n': - case 'N': - return DAL; - - case 'o': - return _XE; - - case 'p': - return _HE_J; - - case 'q': - return _ZAD; - - case 'r': - return _GHAF; - - case 's': - return _SIN; - - case 'S': - return _IE; - - case 't': - return _FE; - - case 'u': - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR)) { - return _AYN_; - } else { - return _AYN; - } - - case 'v': - case 'V': - return RE; - - case 'w': - return _SAD; - - case 'x': - case 'X': - return _TA; - - case 'y': - if (F_is_TyB_TyC_TyD(SRC_CMD, AT_CURSOR)) { - return _GHAYN_; - } else { - return _GHAYN; - } - - case 'z': - case 'Z': - return _ZA; - - case ';': - return _KAF; - - case '\'': - return _GAF; - - case ',': - return WAW; - - case '[': - return _JIM; - - case ']': - return _CHE; - } - - return c; -} - -/// F_isalpha returns TRUE if 'c' is a Farsi alphabet -/// -/// @param c The character to check. -/// -/// @return TRUE if 'c' is a Farsi alphabet character. -int F_isalpha(int c) -{ - return (c >= TEE_ && c <= _YE) - || (c >= ALEF_A && c <= YE) - || (c >= _IE && c <= YE_); -} - -/// F_isdigit returns TRUE if 'c' is a Farsi digit -/// -/// @param c The character to check. -/// -/// @return TRUE if 'c' is a Farsi digit. -int F_isdigit(int c) -{ - return c >= FARSI_0 && c <= FARSI_9; -} - -/// F_ischar returns TRUE if 'c' is a Farsi character. -/// -/// @param c The character to check. -/// -/// @return TRUE if 'c' is a Farsi character. -int F_ischar(int c) -{ - return c >= TEE_ && c <= YE_; -} - -void farsi_fkey(cmdarg_T *cap) -{ - int c = cap->cmdchar; - - if (c == K_F8) { - if (p_altkeymap) { - if (curwin->w_farsi & W_R_L) { - p_fkmap = 0; - do_cmdline_cmd((char_u *)"set norl"); - MSG(""); - } else { - p_fkmap = 1; - do_cmdline_cmd((char_u *)"set rl"); - MSG(""); - } - - curwin->w_farsi = curwin->w_farsi ^ W_R_L; - } - } - - if (c == K_F9) { - if (p_altkeymap && curwin->w_p_rl) { - curwin->w_farsi = curwin->w_farsi ^ W_CONV; - if (curwin->w_farsi & W_CONV) { - conv_to_pvim(); - } else { - conv_to_pstd(); - } - } - } -} diff --git a/src/farsi.h b/src/farsi.h deleted file mode 100644 index c5ee336a0f..0000000000 --- a/src/farsi.h +++ /dev/null @@ -1,183 +0,0 @@ -/// @file farsi.h -/// -/// Farsi characters are categorized into following types: -/// -/// TyA (for capital letter representation) -/// TyB (for types that look like _X e.g. AYN) -/// TyC (for types that look like X_ e.g. YE_) -/// TyD (for types that look like _X_ e.g. _AYN_) -/// TyE (for types that look like X e.g. RE) - -#ifndef NEOVIM_FARSI_H -#define NEOVIM_FARSI_H - -#include "normal.h" -#include "types.h" - -// Farsi character set definition - -// Begin of the non-standard part - -#define TEE_ 0x80 -#define ALEF_U_H_ 0x81 -#define ALEF_ 0x82 -#define _BE 0x83 -#define _PE 0x84 -#define _TE 0x85 -#define _SE 0x86 -#define _JIM 0x87 -#define _CHE 0x88 -#define _HE_J 0x89 -#define _XE 0x8a -#define _SIN 0x8b -#define _SHIN 0x8c -#define _SAD 0x8d -#define _ZAD 0x8e -#define _AYN 0x8f -#define _AYN_ 0x90 -#define AYN_ 0x91 -#define _GHAYN 0x92 -#define _GHAYN_ 0x93 -#define GHAYN_ 0x94 -#define _FE 0x95 -#define _GHAF 0x96 -#define _KAF 0x97 -#define _GAF 0x98 -#define _LAM 0x99 -#define LA 0x9a -#define _MIM 0x9b -#define _NOON 0x9c -#define _HE 0x9d -#define _HE_ 0x9e -#define _YE 0x9f -#define _IE 0xec -#define IE_ 0xed -#define IE 0xfb -#define _YEE 0xee -#define YEE_ 0xef -#define YE_ 0xff - -// End of the non-standard part - -// Standard part - -#define F_BLANK 0xa0 // Farsi ' ' (SP) character -#define F_PSP 0xa1 // PSP for capitalizing of a character -#define F_PCN 0xa2 // PCN for redefining of the hamye meaning -#define F_EXCL 0xa3 // Farsi ! character -#define F_CURRENCY 0xa4 // Farsi Rial character -#define F_PERCENT 0xa5 // Farsi % character -#define F_PERIOD 0xa6 // Farsi '.' character -#define F_COMMA 0xa7 // Farsi ',' character -#define F_LPARENT 0xa8 // Farsi '(' character -#define F_RPARENT 0xa9 // Farsi ')' character -#define F_MUL 0xaa // Farsi 'x' character -#define F_PLUS 0xab // Farsi '+' character -#define F_BCOMMA 0xac // Farsi comma character -#define F_MINUS 0xad // Farsi '-' character -#define F_DIVIDE 0xae // Farsi divide (/) character -#define F_SLASH 0xaf // Farsi '/' character - -#define FARSI_0 0xb0 -#define FARSI_1 0xb1 -#define FARSI_2 0xb2 -#define FARSI_3 0xb3 -#define FARSI_4 0xb4 -#define FARSI_5 0xb5 -#define FARSI_6 0xb6 -#define FARSI_7 0xb7 -#define FARSI_8 0xb8 -#define FARSI_9 0xb9 - -#define F_DCOLON 0xba // Farsi ':' character -#define F_SEMICOLON 0xbb // Farsi ';' character -#define F_GREATER 0xbc // Farsi '>' character -#define F_EQUALS 0xbd // Farsi '=' character -#define F_LESS 0xbe // Farsi '<' character -#define F_QUESTION 0xbf // Farsi ? character - -#define ALEF_A 0xc0 -#define ALEF 0xc1 -#define HAMZE 0xc2 -#define BE 0xc3 -#define PE 0xc4 -#define TE 0xc5 -#define SE 0xc6 -#define JIM 0xc7 -#define CHE 0xc8 -#define HE_J 0xc9 -#define XE 0xca -#define DAL 0xcb -#define ZAL 0xcc -#define RE 0xcd -#define ZE 0xce -#define JE 0xcf -#define SIN 0xd0 -#define SHIN 0xd1 -#define SAD 0xd2 -#define ZAD 0xd3 -#define _TA 0xd4 -#define _ZA 0xd5 -#define AYN 0xd6 -#define GHAYN 0xd7 -#define FE 0xd8 -#define GHAF 0xd9 -#define KAF 0xda -#define GAF 0xdb -#define LAM 0xdc -#define MIM 0xdd -#define NOON 0xde -#define WAW 0xdf -#define F_HE 0xe0 // F_ added for name clash with Perl -#define YE 0xe1 -#define TEE 0xfc -#define _KAF_H 0xfd -#define YEE 0xfe - -#define F_LBRACK 0xe2 // Farsi '[' character -#define F_RBRACK 0xe3 // Farsi ']' character -#define F_LBRACE 0xe4 // Farsi '{' character -#define F_RBRACE 0xe5 // Farsi '}' character -#define F_LQUOT 0xe6 // Farsi left quotation character -#define F_RQUOT 0xe7 // Farsi right quotation character -#define F_STAR 0xe8 // Farsi '*' character -#define F_UNDERLINE 0xe9 // Farsi '_' character -#define F_PIPE 0xea // Farsi '|' character -#define F_BSLASH 0xeb // Farsi '\' character - -#define MAD 0xf0 -#define JAZR 0xf1 -#define OW 0xf2 -#define MAD_N 0xf3 -#define JAZR_N 0xf4 -#define OW_OW 0xf5 -#define TASH 0xf6 -#define OO 0xf7 -#define ALEF_U_H 0xf8 -#define WAW_H 0xf9 -#define ALEF_D_H 0xfa - -// definitions for the window dependent functions (w_farsi). -#define W_CONV 0x1 -#define W_R_L 0x2 - -// special Farsi text messages -extern const char_u farsi_text_1[]; -extern const char_u farsi_text_2[]; -extern const char_u farsi_text_3[]; -extern const char_u farsi_text_5[]; - -int toF_TyA(int c); -int fkmap(int c); -void conv_to_pvim(void); -void conv_to_pstd(void); -char_u *lrswap(char_u *ibuf); -char_u *lrFswap(char_u *cmdbuf, int len); -char_u *lrF_sub(char_u *ibuf); -int cmdl_fkmap(int c); -int F_isalpha(int c); -int F_isdigit(int c); -int F_ischar(int c); -void farsi_fkey(cmdarg_T *cap); - -#endif // NEOVIM_FARSI_H diff --git a/src/file_search.c b/src/file_search.c deleted file mode 100644 index 96f0b71b73..0000000000 --- a/src/file_search.c +++ /dev/null @@ -1,1565 +0,0 @@ -/* TODO: make some #ifdef for this */ -/*--------[ file searching ]-------------------------------------------------*/ -/* - * File searching functions for 'path', 'tags' and 'cdpath' options. - * External visible functions: - * vim_findfile_init() creates/initialises the search context - * vim_findfile_free_visited() free list of visited files/dirs of search - * context - * vim_findfile() find a file in the search context - * vim_findfile_cleanup() cleanup/free search context created by - * vim_findfile_init() - * - * All static functions and variables start with 'ff_' - * - * In general it works like this: - * First you create yourself a search context by calling vim_findfile_init(). - * It is possible to give a search context from a previous call to - * vim_findfile_init(), so it can be reused. After this you call vim_findfile() - * until you are satisfied with the result or it returns NULL. On every call it - * returns the next file which matches the conditions given to - * vim_findfile_init(). If it doesn't find a next file it returns NULL. - * - * It is possible to call vim_findfile_init() again to reinitialise your search - * with some new parameters. Don't forget to pass your old search context to - * it, so it can reuse it and especially reuse the list of already visited - * directories. If you want to delete the list of already visited directories - * simply call vim_findfile_free_visited(). - * - * When you are done call vim_findfile_cleanup() to free the search context. - * - * The function vim_findfile_init() has a long comment, which describes the - * needed parameters. - * - * - * - * ATTENTION: - * ========== - * Also we use an allocated search context here, this functions are NOT - * thread-safe!!!!! - * - * To minimize parameter passing (or because I'm to lazy), only the - * external visible functions get a search context as a parameter. This is - * then assigned to a static global, which is used throughout the local - * functions. - */ - -#include - -#include "vim.h" -#include "file_search.h" -#include "charset.h" -#include "fileio.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "os_unix.h" -#include "path.h" -#include "tag.h" -#include "ui.h" -#include "window.h" -#include "os/os.h" - -static char_u *ff_expand_buffer = NULL; /* used for expanding filenames */ - -/* - * type for the directory search stack - */ -typedef struct ff_stack { - struct ff_stack *ffs_prev; - - /* the fix part (no wildcards) and the part containing the wildcards - * of the search path - */ - char_u *ffs_fix_path; - char_u *ffs_wc_path; - - /* files/dirs found in the above directory, matched by the first wildcard - * of wc_part - */ - char_u **ffs_filearray; - int ffs_filearray_size; - char_u ffs_filearray_cur; /* needed for partly handled dirs */ - - /* to store status of partly handled directories - * 0: we work on this directory for the first time - * 1: this directory was partly searched in an earlier step - */ - int ffs_stage; - - /* How deep are we in the directory tree? - * Counts backward from value of level parameter to vim_findfile_init - */ - int ffs_level; - - /* Did we already expand '**' to an empty string? */ - int ffs_star_star_empty; -} ff_stack_T; - -/* - * type for already visited directories or files. - */ -typedef struct ff_visited { - struct ff_visited *ffv_next; - - /* Visited directories are different if the wildcard string are - * different. So we have to save it. - */ - char_u *ffv_wc_path; - /* for unix use inode etc for comparison (needed because of links), else - * use filename. - */ - int ffv_dev_valid; /* ffv_dev and ffv_ino were set */ - uint64_t ffv_dev; /* device number */ - uint64_t ffv_ino; /* inode number */ - /* The memory for this struct is allocated according to the length of - * ffv_fname. - */ - char_u ffv_fname[1]; /* actually longer */ -} ff_visited_T; - -/* - * We might have to manage several visited lists during a search. - * This is especially needed for the tags option. If tags is set to: - * "./++/tags,./++/TAGS,++/tags" (replace + with *) - * So we have to do 3 searches: - * 1) search from the current files directory downward for the file "tags" - * 2) search from the current files directory downward for the file "TAGS" - * 3) search from Vims current directory downwards for the file "tags" - * As you can see, the first and the third search are for the same file, so for - * the third search we can use the visited list of the first search. For the - * second search we must start from a empty visited list. - * The struct ff_visited_list_hdr is used to manage a linked list of already - * visited lists. - */ -typedef struct ff_visited_list_hdr { - struct ff_visited_list_hdr *ffvl_next; - - /* the filename the attached visited list is for */ - char_u *ffvl_filename; - - ff_visited_T *ffvl_visited_list; - -} ff_visited_list_hdr_T; - - -/* - * '**' can be expanded to several directory levels. - * Set the default maximum depth. - */ -#define FF_MAX_STAR_STAR_EXPAND ((char_u)30) - -/* - * The search context: - * ffsc_stack_ptr: the stack for the dirs to search - * ffsc_visited_list: the currently active visited list - * ffsc_dir_visited_list: the currently active visited list for search dirs - * ffsc_visited_lists_list: the list of all visited lists - * ffsc_dir_visited_lists_list: the list of all visited lists for search dirs - * ffsc_file_to_search: the file to search for - * ffsc_start_dir: the starting directory, if search path was relative - * ffsc_fix_path: the fix part of the given path (without wildcards) - * Needed for upward search. - * ffsc_wc_path: the part of the given path containing wildcards - * ffsc_level: how many levels of dirs to search downwards - * ffsc_stopdirs_v: array of stop directories for upward search - * ffsc_find_what: FINDFILE_BOTH, FINDFILE_DIR or FINDFILE_FILE - * ffsc_tagfile: searching for tags file, don't use 'suffixesadd' - */ -typedef struct ff_search_ctx_T { - ff_stack_T *ffsc_stack_ptr; - ff_visited_list_hdr_T *ffsc_visited_list; - ff_visited_list_hdr_T *ffsc_dir_visited_list; - ff_visited_list_hdr_T *ffsc_visited_lists_list; - ff_visited_list_hdr_T *ffsc_dir_visited_lists_list; - char_u *ffsc_file_to_search; - char_u *ffsc_start_dir; - char_u *ffsc_fix_path; - char_u *ffsc_wc_path; - int ffsc_level; - char_u **ffsc_stopdirs_v; - int ffsc_find_what; - int ffsc_tagfile; -} ff_search_ctx_T; - -/* locally needed functions */ -static int ff_check_visited(ff_visited_T **, char_u *, char_u *); -static void vim_findfile_free_visited_list - (ff_visited_list_hdr_T **list_headp); -static void ff_free_visited_list(ff_visited_T *vl); -static ff_visited_list_hdr_T* ff_get_visited_list - (char_u *, ff_visited_list_hdr_T **list_headp); -static int ff_wc_equal(char_u *s1, char_u *s2); - -static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr); -static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx); -static void ff_clear(ff_search_ctx_T *search_ctx); -static void ff_free_stack_element(ff_stack_T *stack_ptr); -static ff_stack_T *ff_create_stack_element(char_u *, char_u *, int, int); -static int ff_path_in_stoplist(char_u *, int, char_u **); - -static char_u e_pathtoolong[] = N_("E854: path too long for completion"); - - -/* - * Initialization routine for vim_findfile(). - * - * Returns the newly allocated search context or NULL if an error occurred. - * - * Don't forget to clean up by calling vim_findfile_cleanup() if you are done - * with the search context. - * - * Find the file 'filename' in the directory 'path'. - * The parameter 'path' may contain wildcards. If so only search 'level' - * directories deep. The parameter 'level' is the absolute maximum and is - * not related to restricts given to the '**' wildcard. If 'level' is 100 - * and you use '**200' vim_findfile() will stop after 100 levels. - * - * 'filename' cannot contain wildcards! It is used as-is, no backslashes to - * escape special characters. - * - * If 'stopdirs' is not NULL and nothing is found downward, the search is - * restarted on the next higher directory level. This is repeated until the - * start-directory of a search is contained in 'stopdirs'. 'stopdirs' has the - * format ";**\(;\)*;\=$". - * - * If the 'path' is relative, the starting dir for the search is either VIM's - * current dir or if the path starts with "./" the current files dir. - * If the 'path' is absolute, the starting dir is that part of the path before - * the first wildcard. - * - * Upward search is only done on the starting dir. - * - * If 'free_visited' is TRUE the list of already visited files/directories is - * cleared. Set this to FALSE if you just want to search from another - * directory, but want to be sure that no directory from a previous search is - * searched again. This is useful if you search for a file at different places. - * The list of visited files/dirs can also be cleared with the function - * vim_findfile_free_visited(). - * - * Set the parameter 'find_what' to FINDFILE_DIR if you want to search for - * directories only, FINDFILE_FILE for files only, FINDFILE_BOTH for both. - * - * A search context returned by a previous call to vim_findfile_init() can be - * passed in the parameter "search_ctx_arg". This context is reused and - * reinitialized with the new parameters. The list of already visited - * directories from this context is only deleted if the parameter - * "free_visited" is true. Be aware that the passed "search_ctx_arg" is freed - * if the reinitialization fails. - * - * If you don't have a search context from a previous call "search_ctx_arg" - * must be NULL. - * - * This function silently ignores a few errors, vim_findfile() will have - * limited functionality then. - */ -void * -vim_findfile_init ( - char_u *path, - char_u *filename, - char_u *stopdirs, - int level, - int free_visited, - int find_what, - void *search_ctx_arg, - int tagfile, /* expanding names of tags files */ - char_u *rel_fname /* file name to use for "." */ -) -{ - char_u *wc_part; - ff_stack_T *sptr; - ff_search_ctx_T *search_ctx; - - /* If a search context is given by the caller, reuse it, else allocate a - * new one. - */ - if (search_ctx_arg != NULL) - search_ctx = search_ctx_arg; - else { - search_ctx = (ff_search_ctx_T*)alloc((unsigned)sizeof(ff_search_ctx_T)); - memset(search_ctx, 0, sizeof(ff_search_ctx_T)); - } - search_ctx->ffsc_find_what = find_what; - search_ctx->ffsc_tagfile = tagfile; - - /* clear the search context, but NOT the visited lists */ - ff_clear(search_ctx); - - /* clear visited list if wanted */ - if (free_visited == TRUE) - vim_findfile_free_visited(search_ctx); - else { - /* Reuse old visited lists. Get the visited list for the given - * filename. If no list for the current filename exists, creates a new - * one. */ - search_ctx->ffsc_visited_list = ff_get_visited_list(filename, - &search_ctx->ffsc_visited_lists_list); - if (search_ctx->ffsc_visited_list == NULL) - goto error_return; - search_ctx->ffsc_dir_visited_list = ff_get_visited_list(filename, - &search_ctx->ffsc_dir_visited_lists_list); - if (search_ctx->ffsc_dir_visited_list == NULL) - goto error_return; - } - - if (ff_expand_buffer == NULL) { - ff_expand_buffer = (char_u*)alloc(MAXPATHL); - } - - /* Store information on starting dir now if path is relative. - * If path is absolute, we do that later. */ - if (path[0] == '.' - && (vim_ispathsep(path[1]) || path[1] == NUL) - && (!tagfile || vim_strchr(p_cpo, CPO_DOTTAG) == NULL) - && rel_fname != NULL) { - int len = (int)(path_tail(rel_fname) - rel_fname); - - if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) { - /* Make the start dir an absolute path name. */ - vim_strncpy(ff_expand_buffer, rel_fname, len); - search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, FALSE); - } else - search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len); - if (search_ctx->ffsc_start_dir == NULL) - goto error_return; - if (*++path != NUL) - ++path; - } else if (*path == NUL || !vim_isAbsName(path)) { -#ifdef BACKSLASH_IN_FILENAME - /* "c:dir" needs "c:" to be expanded, otherwise use current dir */ - if (*path != NUL && path[1] == ':') { - char_u drive[3]; - - drive[0] = path[0]; - drive[1] = ':'; - drive[2] = NUL; - if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) - goto error_return; - path += 2; - } else -#endif - if (os_dirname(ff_expand_buffer, MAXPATHL) == FAIL) - goto error_return; - - search_ctx->ffsc_start_dir = vim_strsave(ff_expand_buffer); - if (search_ctx->ffsc_start_dir == NULL) - goto error_return; - -#ifdef BACKSLASH_IN_FILENAME - /* A path that starts with "/dir" is relative to the drive, not to the - * directory (but not for "//machine/dir"). Only use the drive name. */ - if ((*path == '/' || *path == '\\') - && path[1] != path[0] - && search_ctx->ffsc_start_dir[1] == ':') - search_ctx->ffsc_start_dir[2] = NUL; -#endif - } - - /* - * If stopdirs are given, split them into an array of pointers. - * If this fails (mem allocation), there is no upward search at all or a - * stop directory is not recognized -> continue silently. - * If stopdirs just contains a ";" or is empty, - * search_ctx->ffsc_stopdirs_v will only contain a NULL pointer. This - * is handled as unlimited upward search. See function - * ff_path_in_stoplist() for details. - */ - if (stopdirs != NULL) { - char_u *walker = stopdirs; - int dircount; - - while (*walker == ';') - walker++; - - dircount = 1; - search_ctx->ffsc_stopdirs_v = - (char_u **)alloc((unsigned)sizeof(char_u *)); - - do { - char_u *helper; - void *ptr; - - helper = walker; - ptr = xrealloc(search_ctx->ffsc_stopdirs_v, - (dircount + 1) * sizeof(char_u *)); - search_ctx->ffsc_stopdirs_v = ptr; - walker = vim_strchr(walker, ';'); - if (walker) { - search_ctx->ffsc_stopdirs_v[dircount-1] = - vim_strnsave(helper, (int)(walker - helper)); - walker++; - } else - /* this might be "", which means ascent till top - * of directory tree. - */ - search_ctx->ffsc_stopdirs_v[dircount-1] = - vim_strsave(helper); - - dircount++; - - } while (walker != NULL); - search_ctx->ffsc_stopdirs_v[dircount-1] = NULL; - } - - search_ctx->ffsc_level = level; - - /* split into: - * -fix path - * -wildcard_stuff (might be NULL) - */ - wc_part = vim_strchr(path, '*'); - if (wc_part != NULL) { - int llevel; - int len; - char *errpt; - - /* save the fix part of the path */ - search_ctx->ffsc_fix_path = vim_strnsave(path, (int)(wc_part - path)); - - /* - * copy wc_path and add restricts to the '**' wildcard. - * The octet after a '**' is used as a (binary) counter. - * So '**3' is transposed to '**^C' ('^C' is ASCII value 3) - * or '**76' is transposed to '**N'( 'N' is ASCII value 76). - * For EBCDIC you get different character values. - * If no restrict is given after '**' the default is used. - * Due to this technique the path looks awful if you print it as a - * string. - */ - len = 0; - while (*wc_part != NUL) { - if (len + 5 >= MAXPATHL) { - EMSG(_(e_pathtoolong)); - break; - } - if (STRNCMP(wc_part, "**", 2) == 0) { - ff_expand_buffer[len++] = *wc_part++; - ff_expand_buffer[len++] = *wc_part++; - - llevel = strtol((char *)wc_part, &errpt, 10); - if ((char_u *)errpt != wc_part && llevel > 0 && llevel < 255) - ff_expand_buffer[len++] = llevel; - else if ((char_u *)errpt != wc_part && llevel == 0) - /* restrict is 0 -> remove already added '**' */ - len -= 2; - else - ff_expand_buffer[len++] = FF_MAX_STAR_STAR_EXPAND; - wc_part = (char_u *)errpt; - if (*wc_part != NUL && !vim_ispathsep(*wc_part)) { - EMSG2(_( - "E343: Invalid path: '**[number]' must be at the end of the path or be followed by '%s'."), - PATHSEPSTR); - goto error_return; - } - } else - ff_expand_buffer[len++] = *wc_part++; - } - ff_expand_buffer[len] = NUL; - search_ctx->ffsc_wc_path = vim_strsave(ff_expand_buffer); - - if (search_ctx->ffsc_wc_path == NULL) - goto error_return; - } else - search_ctx->ffsc_fix_path = vim_strsave(path); - - if (search_ctx->ffsc_start_dir == NULL) { - /* store the fix part as startdir. - * This is needed if the parameter path is fully qualified. - */ - search_ctx->ffsc_start_dir = vim_strsave(search_ctx->ffsc_fix_path); - if (search_ctx->ffsc_start_dir == NULL) - goto error_return; - search_ctx->ffsc_fix_path[0] = NUL; - } - - /* create an absolute path */ - if (STRLEN(search_ctx->ffsc_start_dir) - + STRLEN(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL) { - EMSG(_(e_pathtoolong)); - goto error_return; - } - STRCPY(ff_expand_buffer, search_ctx->ffsc_start_dir); - add_pathsep(ff_expand_buffer); - { - int eb_len = (int)STRLEN(ff_expand_buffer); - char_u *buf = alloc(eb_len - + (int)STRLEN(search_ctx->ffsc_fix_path) + 1); - - STRCPY(buf, ff_expand_buffer); - STRCPY(buf + eb_len, search_ctx->ffsc_fix_path); - if (os_isdir(buf)) { - STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path); - add_pathsep(ff_expand_buffer); - } else { - char_u *p = path_tail(search_ctx->ffsc_fix_path); - char_u *wc_path = NULL; - char_u *temp = NULL; - int len = 0; - - if (p > search_ctx->ffsc_fix_path) { - len = (int)(p - search_ctx->ffsc_fix_path) - 1; - STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len); - add_pathsep(ff_expand_buffer); - } else - len = (int)STRLEN(search_ctx->ffsc_fix_path); - - if (search_ctx->ffsc_wc_path != NULL) { - wc_path = vim_strsave(search_ctx->ffsc_wc_path); - temp = alloc((int)(STRLEN(search_ctx->ffsc_wc_path) - + STRLEN(search_ctx->ffsc_fix_path + len) - + 1)); - } - - if (temp == NULL || wc_path == NULL) { - free(buf); - free(temp); - free(wc_path); - goto error_return; - } - - STRCPY(temp, search_ctx->ffsc_fix_path + len); - STRCAT(temp, search_ctx->ffsc_wc_path); - free(search_ctx->ffsc_wc_path); - free(wc_path); - search_ctx->ffsc_wc_path = temp; - } - free(buf); - } - - sptr = ff_create_stack_element(ff_expand_buffer, - search_ctx->ffsc_wc_path, - level, 0); - - if (sptr == NULL) - goto error_return; - - ff_push(search_ctx, sptr); - - search_ctx->ffsc_file_to_search = vim_strsave(filename); - if (search_ctx->ffsc_file_to_search == NULL) - goto error_return; - - return search_ctx; - -error_return: - /* - * We clear the search context now! - * Even when the caller gave us a (perhaps valid) context we free it here, - * as we might have already destroyed it. - */ - vim_findfile_cleanup(search_ctx); - return NULL; -} - -/* - * Get the stopdir string. Check that ';' is not escaped. - */ -char_u *vim_findfile_stopdir(char_u *buf) -{ - char_u *r_ptr = buf; - - while (*r_ptr != NUL && *r_ptr != ';') { - if (r_ptr[0] == '\\' && r_ptr[1] == ';') { - /* Overwrite the escape char, - * use STRLEN(r_ptr) to move the trailing '\0'. */ - STRMOVE(r_ptr, r_ptr + 1); - r_ptr++; - } - r_ptr++; - } - if (*r_ptr == ';') { - *r_ptr = 0; - r_ptr++; - } else if (*r_ptr == NUL) - r_ptr = NULL; - return r_ptr; -} - -/* - * Clean up the given search context. Can handle a NULL pointer. - */ -void vim_findfile_cleanup(void *ctx) -{ - if (ctx == NULL) - return; - - vim_findfile_free_visited(ctx); - ff_clear(ctx); - free(ctx); -} - -/* - * Find a file in a search context. - * The search context was created with vim_findfile_init() above. - * Return a pointer to an allocated file name or NULL if nothing found. - * To get all matching files call this function until you get NULL. - * - * If the passed search_context is NULL, NULL is returned. - * - * The search algorithm is depth first. To change this replace the - * stack with a list (don't forget to leave partly searched directories on the - * top of the list). - */ -char_u *vim_findfile(void *search_ctx_arg) -{ - char_u *file_path; - char_u *rest_of_wildcards; - char_u *path_end = NULL; - ff_stack_T *stackp; - int len; - int i; - char_u *p; - char_u *suf; - ff_search_ctx_T *search_ctx; - - if (search_ctx_arg == NULL) - return NULL; - - search_ctx = (ff_search_ctx_T *)search_ctx_arg; - - /* - * filepath is used as buffer for various actions and as the storage to - * return a found filename. - */ - if ((file_path = alloc((int)MAXPATHL)) == NULL) - return NULL; - - /* store the end of the start dir -- needed for upward search */ - if (search_ctx->ffsc_start_dir != NULL) - path_end = &search_ctx->ffsc_start_dir[ - STRLEN(search_ctx->ffsc_start_dir)]; - - /* upward search loop */ - for (;; ) { - /* downward search loop */ - for (;; ) { - /* check if user user wants to stop the search*/ - ui_breakcheck(); - if (got_int) - break; - - /* get directory to work on from stack */ - stackp = ff_pop(search_ctx); - if (stackp == NULL) - break; - - /* - * TODO: decide if we leave this test in - * - * GOOD: don't search a directory(-tree) twice. - * BAD: - check linked list for every new directory entered. - * - check for double files also done below - * - * Here we check if we already searched this directory. - * We already searched a directory if: - * 1) The directory is the same. - * 2) We would use the same wildcard string. - * - * Good if you have links on same directory via several ways - * or you have selfreferences in directories (e.g. SuSE Linux 6.3: - * /etc/rc.d/init.d is linked to /etc/rc.d -> endless loop) - * - * This check is only needed for directories we work on for the - * first time (hence stackp->ff_filearray == NULL) - */ - if (stackp->ffs_filearray == NULL - && ff_check_visited(&search_ctx->ffsc_dir_visited_list - ->ffvl_visited_list, - stackp->ffs_fix_path - , stackp->ffs_wc_path - ) == FAIL) { -#ifdef FF_VERBOSE - if (p_verbose >= 5) { - verbose_enter_scroll(); - smsg((char_u *)"Already Searched: %s (%s)", - stackp->ffs_fix_path, stackp->ffs_wc_path); - /* don't overwrite this either */ - msg_puts((char_u *)"\n"); - verbose_leave_scroll(); - } -#endif - ff_free_stack_element(stackp); - continue; - } -#ifdef FF_VERBOSE - else if (p_verbose >= 5) { - verbose_enter_scroll(); - smsg((char_u *)"Searching: %s (%s)", - stackp->ffs_fix_path, stackp->ffs_wc_path); - /* don't overwrite this either */ - msg_puts((char_u *)"\n"); - verbose_leave_scroll(); - } -#endif - - /* check depth */ - if (stackp->ffs_level <= 0) { - ff_free_stack_element(stackp); - continue; - } - - file_path[0] = NUL; - - /* - * If no filearray till now expand wildcards - * The function expand_wildcards() can handle an array of paths - * and all possible expands are returned in one array. We use this - * to handle the expansion of '**' into an empty string. - */ - if (stackp->ffs_filearray == NULL) { - char_u *dirptrs[2]; - - /* we use filepath to build the path expand_wildcards() should - * expand. - */ - dirptrs[0] = file_path; - dirptrs[1] = NULL; - - /* if we have a start dir copy it in */ - if (!vim_isAbsName(stackp->ffs_fix_path) - && search_ctx->ffsc_start_dir) { - STRCPY(file_path, search_ctx->ffsc_start_dir); - add_pathsep(file_path); - } - - /* append the fix part of the search path */ - STRCAT(file_path, stackp->ffs_fix_path); - add_pathsep(file_path); - - rest_of_wildcards = stackp->ffs_wc_path; - if (*rest_of_wildcards != NUL) { - len = (int)STRLEN(file_path); - if (STRNCMP(rest_of_wildcards, "**", 2) == 0) { - /* pointer to the restrict byte - * The restrict byte is not a character! - */ - p = rest_of_wildcards + 2; - - if (*p > 0) { - (*p)--; - file_path[len++] = '*'; - } - - if (*p == 0) { - /* remove '** from wildcards */ - STRMOVE(rest_of_wildcards, rest_of_wildcards + 3); - } else - rest_of_wildcards += 3; - - if (stackp->ffs_star_star_empty == 0) { - /* if not done before, expand '**' to empty */ - stackp->ffs_star_star_empty = 1; - dirptrs[1] = stackp->ffs_fix_path; - } - } - - /* - * Here we copy until the next path separator or the end of - * the path. If we stop at a path separator, there is - * still something else left. This is handled below by - * pushing every directory returned from expand_wildcards() - * on the stack again for further search. - */ - while (*rest_of_wildcards - && !vim_ispathsep(*rest_of_wildcards)) - file_path[len++] = *rest_of_wildcards++; - - file_path[len] = NUL; - if (vim_ispathsep(*rest_of_wildcards)) - rest_of_wildcards++; - } - - /* - * Expand wildcards like "*" and "$VAR". - * If the path is a URL don't try this. - */ - if (path_with_url(dirptrs[0])) { - stackp->ffs_filearray = (char_u **) - alloc((unsigned)sizeof(char *)); - if ((stackp->ffs_filearray[0] = vim_strsave(dirptrs[0])) != NULL) - stackp->ffs_filearray_size = 1; - else - stackp->ffs_filearray_size = 0; - } else - /* Add EW_NOTWILD because the expanded path may contain - * wildcard characters that are to be taken literally. - * This is a bit of a hack. */ - expand_wildcards((dirptrs[1] == NULL) ? 1 : 2, dirptrs, - &stackp->ffs_filearray_size, - &stackp->ffs_filearray, - EW_DIR|EW_ADDSLASH|EW_SILENT|EW_NOTWILD); - - stackp->ffs_filearray_cur = 0; - stackp->ffs_stage = 0; - } else - rest_of_wildcards = &stackp->ffs_wc_path[ - STRLEN(stackp->ffs_wc_path)]; - - if (stackp->ffs_stage == 0) { - /* this is the first time we work on this directory */ - if (*rest_of_wildcards == NUL) { - /* - * We don't have further wildcards to expand, so we have to - * check for the final file now. - */ - for (i = stackp->ffs_filearray_cur; - i < stackp->ffs_filearray_size; ++i) { - if (!path_with_url(stackp->ffs_filearray[i]) - && !os_isdir(stackp->ffs_filearray[i])) - continue; /* not a directory */ - - /* prepare the filename to be checked for existence - * below */ - STRCPY(file_path, stackp->ffs_filearray[i]); - add_pathsep(file_path); - STRCAT(file_path, search_ctx->ffsc_file_to_search); - - /* - * Try without extra suffix and then with suffixes - * from 'suffixesadd'. - */ - len = (int)STRLEN(file_path); - if (search_ctx->ffsc_tagfile) - suf = (char_u *)""; - else - suf = curbuf->b_p_sua; - for (;; ) { - /* if file exists and we didn't already find it */ - if ((path_with_url(file_path) - || (os_file_exists(file_path) - && (search_ctx->ffsc_find_what - == FINDFILE_BOTH - || ((search_ctx->ffsc_find_what - == FINDFILE_DIR) - == os_isdir(file_path))))) -#ifndef FF_VERBOSE - && (ff_check_visited( - &search_ctx->ffsc_visited_list->ffvl_visited_list, - file_path - , (char_u *)"" - ) == OK) -#endif - ) { -#ifdef FF_VERBOSE - if (ff_check_visited( - &search_ctx->ffsc_visited_list->ffvl_visited_list, - file_path - , (char_u *)"" - ) == FAIL) { - if (p_verbose >= 5) { - verbose_enter_scroll(); - smsg((char_u *)"Already: %s", - file_path); - /* don't overwrite this either */ - msg_puts((char_u *)"\n"); - verbose_leave_scroll(); - } - continue; - } -#endif - - /* push dir to examine rest of subdirs later */ - stackp->ffs_filearray_cur = i + 1; - ff_push(search_ctx, stackp); - - if (!path_with_url(file_path)) - simplify_filename(file_path); - if (os_dirname(ff_expand_buffer, MAXPATHL) - == OK) { - p = path_shorten_fname(file_path, - ff_expand_buffer); - if (p != NULL) - STRMOVE(file_path, p); - } -#ifdef FF_VERBOSE - if (p_verbose >= 5) { - verbose_enter_scroll(); - smsg((char_u *)"HIT: %s", file_path); - /* don't overwrite this either */ - msg_puts((char_u *)"\n"); - verbose_leave_scroll(); - } -#endif - return file_path; - } - - /* Not found or found already, try next suffix. */ - if (*suf == NUL) - break; - copy_option_part(&suf, file_path + len, - MAXPATHL - len, ","); - } - } - } else { - /* - * still wildcards left, push the directories for further - * search - */ - for (i = stackp->ffs_filearray_cur; - i < stackp->ffs_filearray_size; ++i) { - if (!os_isdir(stackp->ffs_filearray[i])) - continue; /* not a directory */ - - ff_push(search_ctx, - ff_create_stack_element( - stackp->ffs_filearray[i], - rest_of_wildcards, - stackp->ffs_level - 1, 0)); - } - } - stackp->ffs_filearray_cur = 0; - stackp->ffs_stage = 1; - } - - /* - * if wildcards contains '**' we have to descent till we reach the - * leaves of the directory tree. - */ - if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0) { - for (i = stackp->ffs_filearray_cur; - i < stackp->ffs_filearray_size; ++i) { - if (fnamecmp(stackp->ffs_filearray[i], - stackp->ffs_fix_path) == 0) - continue; /* don't repush same directory */ - if (!os_isdir(stackp->ffs_filearray[i])) - continue; /* not a directory */ - ff_push(search_ctx, - ff_create_stack_element(stackp->ffs_filearray[i], - stackp->ffs_wc_path, stackp->ffs_level - 1, 1)); - } - } - - /* we are done with the current directory */ - ff_free_stack_element(stackp); - - } - - /* If we reached this, we didn't find anything downwards. - * Let's check if we should do an upward search. - */ - if (search_ctx->ffsc_start_dir - && search_ctx->ffsc_stopdirs_v != NULL && !got_int) { - ff_stack_T *sptr; - - /* is the last starting directory in the stop list? */ - if (ff_path_in_stoplist(search_ctx->ffsc_start_dir, - (int)(path_end - search_ctx->ffsc_start_dir), - search_ctx->ffsc_stopdirs_v) == TRUE) - break; - - /* cut of last dir */ - while (path_end > search_ctx->ffsc_start_dir - && vim_ispathsep(*path_end)) - path_end--; - while (path_end > search_ctx->ffsc_start_dir - && !vim_ispathsep(path_end[-1])) - path_end--; - *path_end = 0; - path_end--; - - if (*search_ctx->ffsc_start_dir == 0) - break; - - STRCPY(file_path, search_ctx->ffsc_start_dir); - add_pathsep(file_path); - STRCAT(file_path, search_ctx->ffsc_fix_path); - - /* create a new stack entry */ - sptr = ff_create_stack_element(file_path, - search_ctx->ffsc_wc_path, search_ctx->ffsc_level, 0); - if (sptr == NULL) - break; - ff_push(search_ctx, sptr); - } else - break; - } - - free(file_path); - return NULL; -} - -/* - * Free the list of lists of visited files and directories - * Can handle it if the passed search_context is NULL; - */ -void vim_findfile_free_visited(void *search_ctx_arg) -{ - ff_search_ctx_T *search_ctx; - - if (search_ctx_arg == NULL) - return; - - search_ctx = (ff_search_ctx_T *)search_ctx_arg; - vim_findfile_free_visited_list(&search_ctx->ffsc_visited_lists_list); - vim_findfile_free_visited_list(&search_ctx->ffsc_dir_visited_lists_list); -} - -static void vim_findfile_free_visited_list(ff_visited_list_hdr_T **list_headp) -{ - ff_visited_list_hdr_T *vp; - - while (*list_headp != NULL) { - vp = (*list_headp)->ffvl_next; - ff_free_visited_list((*list_headp)->ffvl_visited_list); - - free((*list_headp)->ffvl_filename); - free(*list_headp); - *list_headp = vp; - } - *list_headp = NULL; -} - -static void ff_free_visited_list(ff_visited_T *vl) -{ - ff_visited_T *vp; - - while (vl != NULL) { - vp = vl->ffv_next; - free(vl->ffv_wc_path); - free(vl); - vl = vp; - } - vl = NULL; -} - -/* - * Returns the already visited list for the given filename. If none is found it - * allocates a new one. - */ -static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename, ff_visited_list_hdr_T **list_headp) -{ - ff_visited_list_hdr_T *retptr = NULL; - - /* check if a visited list for the given filename exists */ - if (*list_headp != NULL) { - retptr = *list_headp; - while (retptr != NULL) { - if (fnamecmp(filename, retptr->ffvl_filename) == 0) { -#ifdef FF_VERBOSE - if (p_verbose >= 5) { - verbose_enter_scroll(); - smsg((char_u *)"ff_get_visited_list: FOUND list for %s", - filename); - /* don't overwrite this either */ - msg_puts((char_u *)"\n"); - verbose_leave_scroll(); - } -#endif - return retptr; - } - retptr = retptr->ffvl_next; - } - } - -#ifdef FF_VERBOSE - if (p_verbose >= 5) { - verbose_enter_scroll(); - smsg((char_u *)"ff_get_visited_list: new list for %s", filename); - /* don't overwrite this either */ - msg_puts((char_u *)"\n"); - verbose_leave_scroll(); - } -#endif - - /* - * if we reach this we didn't find a list and we have to allocate new list - */ - retptr = (ff_visited_list_hdr_T*)alloc((unsigned)sizeof(*retptr)); - - retptr->ffvl_visited_list = NULL; - retptr->ffvl_filename = vim_strsave(filename); - if (retptr->ffvl_filename == NULL) { - free(retptr); - return NULL; - } - retptr->ffvl_next = *list_headp; - *list_headp = retptr; - - return retptr; -} - -/* - * check if two wildcard paths are equal. Returns TRUE or FALSE. - * They are equal if: - * - both paths are NULL - * - they have the same length - * - char by char comparison is OK - * - the only differences are in the counters behind a '**', so - * '**\20' is equal to '**\24' - */ -static int ff_wc_equal(char_u *s1, char_u *s2) -{ - int i; - int prev1 = NUL; - int prev2 = NUL; - - if (s1 == s2) - return TRUE; - - if (s1 == NULL || s2 == NULL) - return FALSE; - - if (STRLEN(s1) != STRLEN(s2)) - return FAIL; - - for (i = 0; s1[i] != NUL && s2[i] != NUL; i += MB_PTR2LEN(s1 + i)) { - int c1 = PTR2CHAR(s1 + i); - int c2 = PTR2CHAR(s2 + i); - - if ((p_fic ? vim_tolower(c1) != vim_tolower(c2) : c1 != c2) - && (prev1 != '*' || prev2 != '*')) - return FAIL; - prev2 = prev1; - prev1 = c1; - } - return TRUE; -} - -/* - * maintains the list of already visited files and dirs - * returns FAIL if the given file/dir is already in the list - * returns OK if it is newly added - */ -static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *wc_path) -{ - ff_visited_T *vp; - bool url = false; - - FileInfo file_info; - // For an URL we only compare the name, otherwise we compare the - // device/inode. - if (path_with_url(fname)) { - vim_strncpy(ff_expand_buffer, fname, MAXPATHL - 1); - url = true; - } else { - ff_expand_buffer[0] = NUL; - if (!os_get_file_info((char *)fname, &file_info)) { - return FAIL; - } - } - - /* check against list of already visited files */ - for (vp = *visited_list; vp != NULL; vp = vp->ffv_next) { - if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0) - || (!url && vp->ffv_dev_valid - && vp->ffv_dev == file_info.stat.st_dev - && vp->ffv_ino == file_info.stat.st_ino)) { - /* are the wildcard parts equal */ - if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE) - /* already visited */ - return FAIL; - } - } - - /* - * New file/dir. Add it to the list of visited files/dirs. - */ - vp = (ff_visited_T *)alloc((unsigned)(sizeof(ff_visited_T) - + STRLEN(ff_expand_buffer))); - - if (!url) { - vp->ffv_dev_valid = TRUE; - vp->ffv_ino = file_info.stat.st_ino; - vp->ffv_dev = file_info.stat.st_dev; - vp->ffv_fname[0] = NUL; - } else { - vp->ffv_dev_valid = FALSE; - STRCPY(vp->ffv_fname, ff_expand_buffer); - } - - if (wc_path != NULL) - vp->ffv_wc_path = vim_strsave(wc_path); - else - vp->ffv_wc_path = NULL; - - vp->ffv_next = *visited_list; - *visited_list = vp; - - return OK; -} - -/* - * create stack element from given path pieces - */ -static ff_stack_T *ff_create_stack_element(char_u *fix_part, char_u *wc_part, int level, int star_star_empty) -{ - ff_stack_T *new; - - new = (ff_stack_T *)alloc((unsigned)sizeof(ff_stack_T)); - - new->ffs_prev = NULL; - new->ffs_filearray = NULL; - new->ffs_filearray_size = 0; - new->ffs_filearray_cur = 0; - new->ffs_stage = 0; - new->ffs_level = level; - new->ffs_star_star_empty = star_star_empty;; - - /* the following saves NULL pointer checks in vim_findfile */ - if (fix_part == NULL) - fix_part = (char_u *)""; - new->ffs_fix_path = vim_strsave(fix_part); - - if (wc_part == NULL) - wc_part = (char_u *)""; - new->ffs_wc_path = vim_strsave(wc_part); - - if (new->ffs_fix_path == NULL - || new->ffs_wc_path == NULL - ) { - ff_free_stack_element(new); - new = NULL; - } - - return new; -} - -/* - * Push a dir on the directory stack. - */ -static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr) -{ - /* check for NULL pointer, not to return an error to the user, but - * to prevent a crash */ - if (stack_ptr != NULL) { - stack_ptr->ffs_prev = search_ctx->ffsc_stack_ptr; - search_ctx->ffsc_stack_ptr = stack_ptr; - } -} - -/* - * Pop a dir from the directory stack. - * Returns NULL if stack is empty. - */ -static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx) -{ - ff_stack_T *sptr; - - sptr = search_ctx->ffsc_stack_ptr; - if (search_ctx->ffsc_stack_ptr != NULL) - search_ctx->ffsc_stack_ptr = search_ctx->ffsc_stack_ptr->ffs_prev; - - return sptr; -} - -/* - * free the given stack element - */ -static void ff_free_stack_element(ff_stack_T *stack_ptr) -{ - /* free handles possible NULL pointers */ - free(stack_ptr->ffs_fix_path); - free(stack_ptr->ffs_wc_path); - - if (stack_ptr->ffs_filearray != NULL) - FreeWild(stack_ptr->ffs_filearray_size, stack_ptr->ffs_filearray); - - free(stack_ptr); -} - -/* - * Clear the search context, but NOT the visited list. - */ -static void ff_clear(ff_search_ctx_T *search_ctx) -{ - ff_stack_T *sptr; - - /* clear up stack */ - while ((sptr = ff_pop(search_ctx)) != NULL) - ff_free_stack_element(sptr); - - free(search_ctx->ffsc_file_to_search); - free(search_ctx->ffsc_start_dir); - free(search_ctx->ffsc_fix_path); - free(search_ctx->ffsc_wc_path); - - if (search_ctx->ffsc_stopdirs_v != NULL) { - int i = 0; - - while (search_ctx->ffsc_stopdirs_v[i] != NULL) { - free(search_ctx->ffsc_stopdirs_v[i]); - i++; - } - free(search_ctx->ffsc_stopdirs_v); - } - search_ctx->ffsc_stopdirs_v = NULL; - - /* reset everything */ - search_ctx->ffsc_file_to_search = NULL; - search_ctx->ffsc_start_dir = NULL; - search_ctx->ffsc_fix_path = NULL; - search_ctx->ffsc_wc_path = NULL; - search_ctx->ffsc_level = 0; -} - -/* - * check if the given path is in the stopdirs - * returns TRUE if yes else FALSE - */ -static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v) -{ - int i = 0; - - /* eat up trailing path separators, except the first */ - while (path_len > 1 && vim_ispathsep(path[path_len - 1])) - path_len--; - - /* if no path consider it as match */ - if (path_len == 0) - return TRUE; - - for (i = 0; stopdirs_v[i] != NULL; i++) { - if ((int)STRLEN(stopdirs_v[i]) > path_len) { - /* match for parent directory. So '/home' also matches - * '/home/rks'. Check for PATHSEP in stopdirs_v[i], else - * '/home/r' would also match '/home/rks' - */ - if (fnamencmp(stopdirs_v[i], path, path_len) == 0 - && vim_ispathsep(stopdirs_v[i][path_len])) - return TRUE; - } else { - if (fnamecmp(stopdirs_v[i], path) == 0) - return TRUE; - } - } - return FALSE; -} - -/* - * Find the file name "ptr[len]" in the path. Also finds directory names. - * - * On the first call set the parameter 'first' to TRUE to initialize - * the search. For repeating calls to FALSE. - * - * Repeating calls will return other files called 'ptr[len]' from the path. - * - * Only on the first call 'ptr' and 'len' are used. For repeating calls they - * don't need valid values. - * - * If nothing found on the first call the option FNAME_MESS will issue the - * message: - * 'Can't find file "" in path' - * On repeating calls: - * 'No more file "" found in path' - * - * options: - * FNAME_MESS give error message when not found - * - * Uses NameBuff[]! - * - * Returns an allocated string for the file name. NULL for error. - * - */ -char_u * -find_file_in_path ( - char_u *ptr, /* file name */ - int len, /* length of file name */ - int options, - int first, /* use count'th matching file name */ - char_u *rel_fname /* file name searching relative to */ -) -{ - return find_file_in_path_option(ptr, len, options, first, - *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path, - FINDFILE_BOTH, rel_fname, curbuf->b_p_sua); -} - -static char_u *ff_file_to_find = NULL; -static void *fdip_search_ctx = NULL; - -#if defined(EXITFREE) -void free_findfile(void) -{ - free(ff_file_to_find); - vim_findfile_cleanup(fdip_search_ctx); - free(ff_expand_buffer); -} - -#endif - -/* - * Find the directory name "ptr[len]" in the path. - * - * options: - * FNAME_MESS give error message when not found - * - * Uses NameBuff[]! - * - * Returns an allocated string for the file name. NULL for error. - */ -char_u * -find_directory_in_path ( - char_u *ptr, /* file name */ - int len, /* length of file name */ - int options, - char_u *rel_fname /* file name searching relative to */ -) -{ - return find_file_in_path_option(ptr, len, options, TRUE, p_cdpath, - FINDFILE_DIR, rel_fname, (char_u *)""); -} - -char_u * -find_file_in_path_option ( - char_u *ptr, /* file name */ - int len, /* length of file name */ - int options, - int first, /* use count'th matching file name */ - char_u *path_option, /* p_path or p_cdpath */ - int find_what, /* FINDFILE_FILE, _DIR or _BOTH */ - char_u *rel_fname, /* file name we are looking relative to. */ - char_u *suffixes /* list of suffixes, 'suffixesadd' option */ -) -{ - static char_u *dir; - static int did_findfile_init = FALSE; - char_u save_char; - char_u *file_name = NULL; - char_u *buf = NULL; - int rel_to_curdir; - - if (first == TRUE) { - /* copy file name into NameBuff, expanding environment variables */ - save_char = ptr[len]; - ptr[len] = NUL; - expand_env(ptr, NameBuff, MAXPATHL); - ptr[len] = save_char; - - free(ff_file_to_find); - ff_file_to_find = vim_strsave(NameBuff); - if (ff_file_to_find == NULL) { /* out of memory */ - file_name = NULL; - goto theend; - } - } - - rel_to_curdir = (ff_file_to_find[0] == '.' - && (ff_file_to_find[1] == NUL - || vim_ispathsep(ff_file_to_find[1]) - || (ff_file_to_find[1] == '.' - && (ff_file_to_find[2] == NUL - || vim_ispathsep(ff_file_to_find[2]))))); - if (vim_isAbsName(ff_file_to_find) - /* "..", "../path", "." and "./path": don't use the path_option */ - || rel_to_curdir - ) { - /* - * Absolute path, no need to use "path_option". - * If this is not a first call, return NULL. We already returned a - * filename on the first call. - */ - if (first == TRUE) { - int l; - int run; - - if (path_with_url(ff_file_to_find)) { - file_name = vim_strsave(ff_file_to_find); - goto theend; - } - - /* When FNAME_REL flag given first use the directory of the file. - * Otherwise or when this fails use the current directory. */ - for (run = 1; run <= 2; ++run) { - l = (int)STRLEN(ff_file_to_find); - if (run == 1 - && rel_to_curdir - && (options & FNAME_REL) - && rel_fname != NULL - && STRLEN(rel_fname) + l < MAXPATHL) { - STRCPY(NameBuff, rel_fname); - STRCPY(path_tail(NameBuff), ff_file_to_find); - l = (int)STRLEN(NameBuff); - } else { - STRCPY(NameBuff, ff_file_to_find); - run = 2; - } - - /* When the file doesn't exist, try adding parts of - * 'suffixesadd'. */ - buf = suffixes; - for (;; ) { - if ( - (os_file_exists(NameBuff) - && (find_what == FINDFILE_BOTH - || ((find_what == FINDFILE_DIR) - == os_isdir(NameBuff))))) { - file_name = vim_strsave(NameBuff); - goto theend; - } - if (*buf == NUL) - break; - copy_option_part(&buf, NameBuff + l, MAXPATHL - l, ","); - } - } - } - } else { - /* - * Loop over all paths in the 'path' or 'cdpath' option. - * When "first" is set, first setup to the start of the option. - * Otherwise continue to find the next match. - */ - if (first == TRUE) { - /* vim_findfile_free_visited can handle a possible NULL pointer */ - vim_findfile_free_visited(fdip_search_ctx); - dir = path_option; - did_findfile_init = FALSE; - } - - for (;; ) { - if (did_findfile_init) { - file_name = vim_findfile(fdip_search_ctx); - if (file_name != NULL) - break; - - did_findfile_init = FALSE; - } else { - char_u *r_ptr; - - if (dir == NULL || *dir == NUL) { - /* We searched all paths of the option, now we can - * free the search context. */ - vim_findfile_cleanup(fdip_search_ctx); - fdip_search_ctx = NULL; - break; - } - - buf = alloc((int)(MAXPATHL)); - - /* copy next path */ - buf[0] = 0; - copy_option_part(&dir, buf, MAXPATHL, " ,"); - - /* get the stopdir string */ - r_ptr = vim_findfile_stopdir(buf); - fdip_search_ctx = vim_findfile_init(buf, ff_file_to_find, - r_ptr, 100, FALSE, find_what, - fdip_search_ctx, FALSE, rel_fname); - if (fdip_search_ctx != NULL) - did_findfile_init = TRUE; - free(buf); - } - } - } - if (file_name == NULL && (options & FNAME_MESS)) { - if (first == TRUE) { - if (find_what == FINDFILE_DIR) - EMSG2(_("E344: Can't find directory \"%s\" in cdpath"), - ff_file_to_find); - else - EMSG2(_("E345: Can't find file \"%s\" in path"), - ff_file_to_find); - } else { - if (find_what == FINDFILE_DIR) - EMSG2(_("E346: No more directory \"%s\" found in cdpath"), - ff_file_to_find); - else - EMSG2(_("E347: No more file \"%s\" found in path"), - ff_file_to_find); - } - } - -theend: - return file_name; -} - diff --git a/src/file_search.h b/src/file_search.h deleted file mode 100644 index 13c09a122d..0000000000 --- a/src/file_search.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef NEOVIM_FILE_SEARCH_H -#define NEOVIM_FILE_SEARCH_H - -void *vim_findfile_init(char_u *path, char_u *filename, char_u * - stopdirs, int level, int free_visited, - int find_what, void *search_ctx_arg, - int tagfile, - char_u *rel_fname); -char_u *vim_findfile_stopdir(char_u *buf); -void vim_findfile_cleanup(void *ctx); -char_u *vim_findfile(void *search_ctx_arg); -void vim_findfile_free_visited(void *search_ctx_arg); -char_u *find_file_in_path(char_u *ptr, int len, int options, int first, - char_u *rel_fname); -void free_findfile(void); -char_u *find_directory_in_path(char_u *ptr, int len, int options, - char_u *rel_fname); -char_u *find_file_in_path_option(char_u *ptr, int len, int options, - int first, char_u *path_option, - int find_what, char_u *rel_fname, - char_u *suffixes); - -#endif /* NEOVIM_FILE_SEARCH_H */ diff --git a/src/fileio.c b/src/fileio.c deleted file mode 100644 index 4fc73d8ebf..0000000000 --- a/src/fileio.c +++ /dev/null @@ -1,8148 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * fileio.c: read from and write to a file - */ - -#include - -#include "vim.h" -#include "fileio.h" -#include "blowfish.h" -#include "buffer.h" -#include "charset.h" -#include "diff.h" -#include "edit.h" -#include "eval.h" -#include "ex_cmds.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "fold.h" -#include "getchar.h" -#include "hashtab.h" -#include "mbyte.h" -#include "memfile.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "crypt.h" -#include "garray.h" -#include "move.h" -#include "normal.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "search.h" -#include "sha256.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "window.h" -#include "os/os.h" - - -#if defined(HAVE_UTIME) && defined(HAVE_UTIME_H) -# include /* for struct utimbuf */ -#endif - -#define BUFSIZE 8192 /* size of normal write buffer */ -#define SMBUFSIZE 256 /* size of emergency write buffer */ - -/* crypt_magic[0] is pkzip crypt, crypt_magic[1] is sha2+blowfish */ -static char *crypt_magic[] = {"VimCrypt~01!", "VimCrypt~02!"}; -static char crypt_magic_head[] = "VimCrypt~"; -# define CRYPT_MAGIC_LEN 12 /* must be multiple of 4! */ - -/* For blowfish, after the magic header, we store 8 bytes of salt and then 8 - * bytes of seed (initialisation vector). */ -static int crypt_salt_len[] = {0, 8}; -static int crypt_seed_len[] = {0, 8}; -#define CRYPT_SALT_LEN_MAX 8 -#define CRYPT_SEED_LEN_MAX 8 - -static char_u *next_fenc(char_u **pp); -static char_u *readfile_charconvert(char_u *fname, char_u *fenc, - int *fdp); -static void check_marks_read(void); -static int crypt_method_from_magic(char *ptr, int len); -static char_u *check_for_cryptkey(char_u *cryptkey, char_u *ptr, - long *sizep, off_t *filesizep, - int newfile, char_u *fname, - int *did_ask); -#ifdef UNIX -static void set_file_time(char_u *fname, time_t atime, time_t mtime); -#endif -static int set_rw_fname(char_u *fname, char_u *sfname); -static int msg_add_fileformat(int eol_type); -static void msg_add_eol(void); -static int check_mtime(buf_T *buf, FileInfo *file_info); -static int time_differs(long t1, long t2); -static int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, - int force, buf_T *buf, - exarg_T *eap); -static int au_find_group(char_u *name); - -# define AUGROUP_DEFAULT -1 /* default autocmd group */ -# define AUGROUP_ERROR -2 /* erroneous autocmd group */ -# define AUGROUP_ALL -3 /* all autocmd groups */ - -# define HAS_BW_FLAGS -# define FIO_LATIN1 0x01 /* convert Latin1 */ -# define FIO_UTF8 0x02 /* convert UTF-8 */ -# define FIO_UCS2 0x04 /* convert UCS-2 */ -# define FIO_UCS4 0x08 /* convert UCS-4 */ -# define FIO_UTF16 0x10 /* convert UTF-16 */ -# define FIO_ENDIAN_L 0x80 /* little endian */ -# define FIO_ENCRYPTED 0x1000 /* encrypt written bytes */ -# define FIO_NOCONVERT 0x2000 /* skip encoding conversion */ -# define FIO_UCSBOM 0x4000 /* check for BOM at start of file */ -# define FIO_ALL -1 /* allow all formats */ - -/* When converting, a read() or write() may leave some bytes to be converted - * for the next call. The value is guessed... */ -#define CONV_RESTLEN 30 - -/* We have to guess how much a sequence of bytes may expand when converting - * with iconv() to be able to allocate a buffer. */ -#define ICONV_MULT 8 - -/* - * Structure to pass arguments from buf_write() to buf_write_bytes(). - */ -struct bw_info { - int bw_fd; /* file descriptor */ - char_u *bw_buf; /* buffer with data to be written */ - int bw_len; /* length of data */ -#ifdef HAS_BW_FLAGS - int bw_flags; /* FIO_ flags */ -#endif - char_u bw_rest[CONV_RESTLEN]; /* not converted bytes */ - int bw_restlen; /* nr of bytes in bw_rest[] */ - int bw_first; /* first write call */ - char_u *bw_conv_buf; /* buffer for writing converted chars */ - int bw_conv_buflen; /* size of bw_conv_buf */ - int bw_conv_error; /* set for conversion error */ - linenr_T bw_conv_error_lnum; /* first line with error or zero */ - linenr_T bw_start_lnum; /* line number at start of buffer */ -# ifdef USE_ICONV - iconv_t bw_iconv_fd; /* descriptor for iconv() or -1 */ -# endif -}; - -static int buf_write_bytes(struct bw_info *ip); - -static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, - char_u *endp); -static int ucs2bytes(unsigned c, char_u **pp, int flags); -static int need_conversion(char_u *fenc); -static int get_fio_flags(char_u *ptr); -static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags); -static int make_bom(char_u *buf, char_u *name); -static int move_lines(buf_T *frombuf, buf_T *tobuf); -#ifdef TEMPDIRNAMES -static void vim_settempdir(char_u *tempdir); -#endif -static char *e_auchangedbuf = N_( - "E812: Autocommands changed buffer or buffer name"); - -void filemess(buf_T *buf, char_u *name, char_u *s, int attr) -{ - int msg_scroll_save; - - if (msg_silent != 0) - return; - msg_add_fname(buf, name); /* put file name in IObuff with quotes */ - /* If it's extremely long, truncate it. */ - if (STRLEN(IObuff) > IOSIZE - 80) - IObuff[IOSIZE - 80] = NUL; - STRCAT(IObuff, s); - /* - * For the first message may have to start a new line. - * For further ones overwrite the previous one, reset msg_scroll before - * calling filemess(). - */ - msg_scroll_save = msg_scroll; - if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) - msg_scroll = FALSE; - if (!msg_scroll) /* wait a bit when overwriting an error msg */ - check_for_delay(FALSE); - msg_start(); - msg_scroll = msg_scroll_save; - msg_scrolled_ign = TRUE; - /* may truncate the message to avoid a hit-return prompt */ - msg_outtrans_attr(msg_may_trunc(FALSE, IObuff), attr); - msg_clr_eos(); - out_flush(); - msg_scrolled_ign = FALSE; -} - -/* - * Read lines from file "fname" into the buffer after line "from". - * - * 1. We allocate blocks with try_malloc, as big as possible. - * 2. Each block is filled with characters from the file with a single read(). - * 3. The lines are inserted in the buffer with ml_append(). - * - * (caller must check that fname != NULL, unless READ_STDIN is used) - * - * "lines_to_skip" is the number of lines that must be skipped - * "lines_to_read" is the number of lines that are appended - * When not recovering lines_to_skip is 0 and lines_to_read MAXLNUM. - * - * flags: - * READ_NEW starting to edit a new buffer - * READ_FILTER reading filter output - * READ_STDIN read from stdin instead of a file - * READ_BUFFER read from curbuf instead of a file (converting after reading - * stdin) - * READ_DUMMY read into a dummy buffer (to check if file contents changed) - * READ_KEEP_UNDO don't clear undo info or read it from a file - * - * return FAIL for failure, OK otherwise - */ -int -readfile ( - char_u *fname, - char_u *sfname, - linenr_T from, - linenr_T lines_to_skip, - linenr_T lines_to_read, - exarg_T *eap, /* can be NULL! */ - int flags -) -{ - int fd = 0; - int newfile = (flags & READ_NEW); - int check_readonly; - int filtering = (flags & READ_FILTER); - int read_stdin = (flags & READ_STDIN); - int read_buffer = (flags & READ_BUFFER); - int set_options = newfile || read_buffer - || (eap != NULL && eap->read_edit); - linenr_T read_buf_lnum = 1; /* next line to read from curbuf */ - colnr_T read_buf_col = 0; /* next char to read from this line */ - char_u c; - linenr_T lnum = from; - char_u *ptr = NULL; /* pointer into read buffer */ - char_u *buffer = NULL; /* read buffer */ - char_u *new_buffer = NULL; /* init to shut up gcc */ - char_u *line_start = NULL; /* init to shut up gcc */ - int wasempty; /* buffer was empty before reading */ - colnr_T len; - long size = 0; - char_u *p; - off_t filesize = 0; - int skip_read = FALSE; - char_u *cryptkey = NULL; - int did_ask_for_key = FALSE; - int crypt_method_used; - context_sha256_T sha_ctx; - int read_undo_file = FALSE; - int split = 0; /* number of split lines */ - linenr_T linecnt; - int error = FALSE; /* errors encountered */ - int ff_error = EOL_UNKNOWN; /* file format with errors */ - long linerest = 0; /* remaining chars in line */ -#ifdef UNIX - int perm = 0; - int swap_mode = -1; /* protection bits for swap file */ -#else - int perm; -#endif - int fileformat = 0; /* end-of-line format */ - int keep_fileformat = FALSE; - int file_readonly; - linenr_T skip_count = 0; - linenr_T read_count = 0; - int msg_save = msg_scroll; - linenr_T read_no_eol_lnum = 0; /* non-zero lnum when last line of - * last read was missing the eol */ - int try_mac = (vim_strchr(p_ffs, 'm') != NULL); - int try_dos = (vim_strchr(p_ffs, 'd') != NULL); - int try_unix = (vim_strchr(p_ffs, 'x') != NULL); - int file_rewind = FALSE; - int can_retry; - linenr_T conv_error = 0; /* line nr with conversion error */ - linenr_T illegal_byte = 0; /* line nr with illegal byte */ - int keep_dest_enc = FALSE; /* don't retry when char doesn't fit - in destination encoding */ - int bad_char_behavior = BAD_REPLACE; - /* BAD_KEEP, BAD_DROP or character to - * replace with */ - char_u *tmpname = NULL; /* name of 'charconvert' output file */ - int fio_flags = 0; - char_u *fenc; /* fileencoding to use */ - int fenc_alloced; /* fenc_next is in allocated memory */ - char_u *fenc_next = NULL; /* next item in 'fencs' or NULL */ - int advance_fenc = FALSE; - long real_size = 0; -# ifdef USE_ICONV - iconv_t iconv_fd = (iconv_t)-1; /* descriptor for iconv() or -1 */ - int did_iconv = FALSE; /* TRUE when iconv() failed and trying - 'charconvert' next */ -# endif - int converted = FALSE; /* TRUE if conversion done */ - int notconverted = FALSE; /* TRUE if conversion wanted but it - wasn't possible */ - char_u conv_rest[CONV_RESTLEN]; - int conv_restlen = 0; /* nr of bytes in conv_rest[] */ - buf_T *old_curbuf; - char_u *old_b_ffname; - char_u *old_b_fname; - int using_b_ffname; - int using_b_fname; - - curbuf->b_no_eol_lnum = 0; /* in case it was set by the previous read */ - - /* - * If there is no file name yet, use the one for the read file. - * BF_NOTEDITED is set to reflect this. - * Don't do this for a read from a filter. - * Only do this when 'cpoptions' contains the 'f' flag. - */ - if (curbuf->b_ffname == NULL - && !filtering - && fname != NULL - && vim_strchr(p_cpo, CPO_FNAMER) != NULL - && !(flags & READ_DUMMY)) { - if (set_rw_fname(fname, sfname) == FAIL) - return FAIL; - } - - /* Remember the initial values of curbuf, curbuf->b_ffname and - * curbuf->b_fname to detect whether they are altered as a result of - * executing nasty autocommands. Also check if "fname" and "sfname" - * point to one of these values. */ - old_curbuf = curbuf; - old_b_ffname = curbuf->b_ffname; - old_b_fname = curbuf->b_fname; - using_b_ffname = (fname == curbuf->b_ffname) - || (sfname == curbuf->b_ffname); - using_b_fname = (fname == curbuf->b_fname) || (sfname == curbuf->b_fname); - - /* After reading a file the cursor line changes but we don't want to - * display the line. */ - ex_no_reprint = TRUE; - - /* don't display the file info for another buffer now */ - need_fileinfo = FALSE; - - /* - * For Unix: Use the short file name whenever possible. - * Avoids problems with networks and when directory names are changed. - * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to - * another directory, which we don't detect. - */ - if (sfname == NULL) - sfname = fname; -#if defined(UNIX) - fname = sfname; -#endif - - /* - * The BufReadCmd and FileReadCmd events intercept the reading process by - * executing the associated commands instead. - */ - if (!filtering && !read_stdin && !read_buffer) { - pos_T pos; - - pos = curbuf->b_op_start; - - /* Set '[ mark to the line above where the lines go (line 1 if zero). */ - curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); - curbuf->b_op_start.col = 0; - - if (newfile) { - if (apply_autocmds_exarg(EVENT_BUFREADCMD, NULL, sfname, - FALSE, curbuf, eap)) - return aborting() ? FAIL : OK; - } else if (apply_autocmds_exarg(EVENT_FILEREADCMD, sfname, sfname, - FALSE, NULL, eap)) - return aborting() ? FAIL : OK; - - curbuf->b_op_start = pos; - } - - if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) - msg_scroll = FALSE; /* overwrite previous file message */ - else - msg_scroll = TRUE; /* don't overwrite previous file message */ - - /* - * If the name ends in a path separator, we can't open it. Check here, - * because reading the file may actually work, but then creating the swap - * file may destroy it! Reported on MS-DOS and Win 95. - * If the name is too long we might crash further on, quit here. - */ - if (fname != NULL && *fname != NUL) { - p = fname + STRLEN(fname); - if (after_pathsep(fname, p) || STRLEN(fname) >= MAXPATHL) { - filemess(curbuf, fname, (char_u *)_("Illegal file name"), 0); - msg_end(); - msg_scroll = msg_save; - return FAIL; - } - } - - if (!read_stdin && !read_buffer) { -#ifdef UNIX - /* - * On Unix it is possible to read a directory, so we have to - * check for it before the mch_open(). - */ - perm = os_getperm(fname); - if (perm >= 0 && !S_ISREG(perm) /* not a regular file ... */ -# ifdef S_ISFIFO - && !S_ISFIFO(perm) /* ... or fifo */ -# endif -# ifdef S_ISSOCK - && !S_ISSOCK(perm) /* ... or socket */ -# endif -# ifdef OPEN_CHR_FILES - && !(S_ISCHR(perm) && is_dev_fd_file(fname)) - /* ... or a character special file named /dev/fd/ */ -# endif - ) { - if (S_ISDIR(perm)) - filemess(curbuf, fname, (char_u *)_("is a directory"), 0); - else - filemess(curbuf, fname, (char_u *)_("is not a file"), 0); - msg_end(); - msg_scroll = msg_save; - return FAIL; - } -#endif - } - - /* Set default or forced 'fileformat' and 'binary'. */ - set_file_options(set_options, eap); - - /* - * When opening a new file we take the readonly flag from the file. - * Default is r/w, can be set to r/o below. - * Don't reset it when in readonly mode - * Only set/reset b_p_ro when BF_CHECK_RO is set. - */ - check_readonly = (newfile && (curbuf->b_flags & BF_CHECK_RO)); - if (check_readonly && !readonlymode) - curbuf->b_p_ro = FALSE; - - if (newfile && !read_stdin && !read_buffer) { - /* Remember time of file. */ - FileInfo file_info; - if (os_get_file_info((char *)fname, &file_info)) { - buf_store_file_info(curbuf, &file_info); - curbuf->b_mtime_read = curbuf->b_mtime; -#ifdef UNIX - /* - * Use the protection bits of the original file for the swap file. - * This makes it possible for others to read the name of the - * edited file from the swapfile, but only if they can read the - * edited file. - * Remove the "write" and "execute" bits for group and others - * (they must not write the swapfile). - * Add the "read" and "write" bits for the user, otherwise we may - * not be able to write to the file ourselves. - * Setting the bits is done below, after creating the swap file. - */ - swap_mode = (file_info.stat.st_mode & 0644) | 0600; -#endif - } else { - curbuf->b_mtime = 0; - curbuf->b_mtime_read = 0; - curbuf->b_orig_size = 0; - curbuf->b_orig_mode = 0; - } - - /* Reset the "new file" flag. It will be set again below when the - * file doesn't exist. */ - curbuf->b_flags &= ~(BF_NEW | BF_NEW_W); - } - - /* - * Check readonly by trying to open the file for writing. - * If this fails, we know that the file is readonly. - */ - file_readonly = FALSE; - if (!read_buffer && !read_stdin) { - if (!newfile || readonlymode) { - file_readonly = TRUE; - } else if ((fd = mch_open((char *)fname, O_RDWR, 0)) < 0) { - // opening in readwrite mode failed => file is readonly - file_readonly = TRUE; - } - if (file_readonly == TRUE) { - // try to open readonly - fd = mch_open((char *)fname, O_RDONLY, 0); - } - } - - if (fd < 0) { /* cannot open at all */ - msg_scroll = msg_save; -#ifndef UNIX - /* - * On non-unix systems we can't open a directory, check here. - */ - perm = os_getperm(fname); /* check if the file exists */ - if (os_isdir(fname)) { - filemess(curbuf, sfname, (char_u *)_("is a directory"), 0); - curbuf->b_p_ro = TRUE; /* must use "w!" now */ - } else -#endif - if (newfile) { - if (perm < 0 -#ifdef ENOENT - && errno == ENOENT -#endif - ) { - /* - * Set the 'new-file' flag, so that when the file has - * been created by someone else, a ":w" will complain. - */ - curbuf->b_flags |= BF_NEW; - - /* Create a swap file now, so that other Vims are warned - * that we are editing this file. Don't do this for a - * "nofile" or "nowrite" buffer type. */ - if (!bt_dontwrite(curbuf)) { - check_need_swap(newfile); - /* SwapExists autocommand may mess things up */ - if (curbuf != old_curbuf - || (using_b_ffname - && (old_b_ffname != curbuf->b_ffname)) - || (using_b_fname - && (old_b_fname != curbuf->b_fname))) { - EMSG(_(e_auchangedbuf)); - return FAIL; - } - } - if (dir_of_file_exists(fname)) - filemess(curbuf, sfname, (char_u *)_("[New File]"), 0); - else - filemess(curbuf, sfname, - (char_u *)_("[New DIRECTORY]"), 0); - /* Even though this is a new file, it might have been - * edited before and deleted. Get the old marks. */ - check_marks_read(); - /* Set forced 'fileencoding'. */ - if (eap != NULL) - set_forced_fenc(eap); - apply_autocmds_exarg(EVENT_BUFNEWFILE, sfname, sfname, - FALSE, curbuf, eap); - /* remember the current fileformat */ - save_file_ff(curbuf); - - if (aborting()) /* autocmds may abort script processing */ - return FAIL; - return OK; /* a new file is not an error */ - } else { - filemess(curbuf, sfname, (char_u *)( -# ifdef EFBIG - (errno == EFBIG) ? _("[File too big]") : -# endif -# ifdef EOVERFLOW - (errno == EOVERFLOW) ? _("[File too big]") : -# endif - _("[Permission Denied]")), 0); - curbuf->b_p_ro = TRUE; /* must use "w!" now */ - } - } - - return FAIL; - } - - /* - * Only set the 'ro' flag for readonly files the first time they are - * loaded. Help files always get readonly mode - */ - if ((check_readonly && file_readonly) || curbuf->b_help) - curbuf->b_p_ro = TRUE; - - if (set_options) { - /* Don't change 'eol' if reading from buffer as it will already be - * correctly set when reading stdin. */ - if (!read_buffer) { - curbuf->b_p_eol = TRUE; - curbuf->b_start_eol = TRUE; - } - curbuf->b_p_bomb = FALSE; - curbuf->b_start_bomb = FALSE; - } - - /* Create a swap file now, so that other Vims are warned that we are - * editing this file. - * Don't do this for a "nofile" or "nowrite" buffer type. */ - if (!bt_dontwrite(curbuf)) { - check_need_swap(newfile); - if (!read_stdin && (curbuf != old_curbuf - || (using_b_ffname && (old_b_ffname != curbuf->b_ffname)) - || (using_b_fname && - (old_b_fname != curbuf->b_fname)))) { - EMSG(_(e_auchangedbuf)); - if (!read_buffer) - close(fd); - return FAIL; - } -#ifdef UNIX - /* Set swap file protection bits after creating it. */ - if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL - && curbuf->b_ml.ml_mfp->mf_fname != NULL) - (void)os_setperm(curbuf->b_ml.ml_mfp->mf_fname, (long)swap_mode); -#endif - } - -#if defined(HAS_SWAP_EXISTS_ACTION) - /* If "Quit" selected at ATTENTION dialog, don't load the file */ - if (swap_exists_action == SEA_QUIT) { - if (!read_buffer && !read_stdin) - close(fd); - return FAIL; - } -#endif - - ++no_wait_return; /* don't wait for return yet */ - - /* - * Set '[ mark to the line above where the lines go (line 1 if zero). - */ - curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); - curbuf->b_op_start.col = 0; - - if (!read_buffer) { - int m = msg_scroll; - int n = msg_scrolled; - - /* - * The file must be closed again, the autocommands may want to change - * the file before reading it. - */ - if (!read_stdin) - close(fd); /* ignore errors */ - - /* - * The output from the autocommands should not overwrite anything and - * should not be overwritten: Set msg_scroll, restore its value if no - * output was done. - */ - msg_scroll = TRUE; - if (filtering) - apply_autocmds_exarg(EVENT_FILTERREADPRE, NULL, sfname, - FALSE, curbuf, eap); - else if (read_stdin) - apply_autocmds_exarg(EVENT_STDINREADPRE, NULL, sfname, - FALSE, curbuf, eap); - else if (newfile) - apply_autocmds_exarg(EVENT_BUFREADPRE, NULL, sfname, - FALSE, curbuf, eap); - else - apply_autocmds_exarg(EVENT_FILEREADPRE, sfname, sfname, - FALSE, NULL, eap); - if (msg_scrolled == n) - msg_scroll = m; - - if (aborting()) { /* autocmds may abort script processing */ - --no_wait_return; - msg_scroll = msg_save; - curbuf->b_p_ro = TRUE; /* must use "w!" now */ - return FAIL; - } - /* - * Don't allow the autocommands to change the current buffer. - * Try to re-open the file. - * - * Don't allow the autocommands to change the buffer name either - * (cd for example) if it invalidates fname or sfname. - */ - if (!read_stdin && (curbuf != old_curbuf - || (using_b_ffname && (old_b_ffname != curbuf->b_ffname)) - || (using_b_fname && (old_b_fname != curbuf->b_fname)) - || (fd = - mch_open((char *)fname, O_RDONLY, - 0)) < 0)) { - --no_wait_return; - msg_scroll = msg_save; - if (fd < 0) - EMSG(_("E200: *ReadPre autocommands made the file unreadable")); - else - EMSG(_("E201: *ReadPre autocommands must not change current buffer")); - curbuf->b_p_ro = TRUE; /* must use "w!" now */ - return FAIL; - } - } - - /* Autocommands may add lines to the file, need to check if it is empty */ - wasempty = (curbuf->b_ml.ml_flags & ML_EMPTY); - - if (!recoverymode && !filtering && !(flags & READ_DUMMY)) { - /* - * Show the user that we are busy reading the input. Sometimes this - * may take a while. When reading from stdin another program may - * still be running, don't move the cursor to the last line, unless - * always using the GUI. - */ - if (read_stdin) { - mch_msg(_("Vim: Reading from stdin...\n")); - } else if (!read_buffer) - filemess(curbuf, sfname, (char_u *)"", 0); - } - - msg_scroll = FALSE; /* overwrite the file message */ - - /* - * Set linecnt now, before the "retry" caused by a wrong guess for - * fileformat, and after the autocommands, which may change them. - */ - linecnt = curbuf->b_ml.ml_line_count; - - /* "++bad=" argument. */ - if (eap != NULL && eap->bad_char != 0) { - bad_char_behavior = eap->bad_char; - if (set_options) - curbuf->b_bad_char = eap->bad_char; - } else - curbuf->b_bad_char = 0; - - /* - * Decide which 'encoding' to use or use first. - */ - if (eap != NULL && eap->force_enc != 0) { - fenc = enc_canonize(eap->cmd + eap->force_enc); - fenc_alloced = TRUE; - keep_dest_enc = TRUE; - } else if (curbuf->b_p_bin) { - fenc = (char_u *)""; /* binary: don't convert */ - fenc_alloced = FALSE; - } else if (curbuf->b_help) { - char_u firstline[80]; - int fc; - - /* Help files are either utf-8 or latin1. Try utf-8 first, if this - * fails it must be latin1. - * Always do this when 'encoding' is "utf-8". Otherwise only do - * this when needed to avoid [converted] remarks all the time. - * It is needed when the first line contains non-ASCII characters. - * That is only in *.??x files. */ - fenc = (char_u *)"latin1"; - c = enc_utf8; - if (!c && !read_stdin) { - fc = fname[STRLEN(fname) - 1]; - if (TOLOWER_ASC(fc) == 'x') { - /* Read the first line (and a bit more). Immediately rewind to - * the start of the file. If the read() fails "len" is -1. */ - len = read_eintr(fd, firstline, 80); - lseek(fd, (off_t)0L, SEEK_SET); - for (p = firstline; p < firstline + len; ++p) - if (*p >= 0x80) { - c = TRUE; - break; - } - } - } - - if (c) { - fenc_next = fenc; - fenc = (char_u *)"utf-8"; - - /* When the file is utf-8 but a character doesn't fit in - * 'encoding' don't retry. In help text editing utf-8 bytes - * doesn't make sense. */ - if (!enc_utf8) - keep_dest_enc = TRUE; - } - fenc_alloced = FALSE; - } else if (*p_fencs == NUL) { - fenc = curbuf->b_p_fenc; /* use format from buffer */ - fenc_alloced = FALSE; - } else { - fenc_next = p_fencs; /* try items in 'fileencodings' */ - fenc = next_fenc(&fenc_next); - fenc_alloced = TRUE; - } - - /* - * Jump back here to retry reading the file in different ways. - * Reasons to retry: - * - encoding conversion failed: try another one from "fenc_next" - * - BOM detected and fenc was set, need to setup conversion - * - "fileformat" check failed: try another - * - * Variables set for special retry actions: - * "file_rewind" Rewind the file to start reading it again. - * "advance_fenc" Advance "fenc" using "fenc_next". - * "skip_read" Re-use already read bytes (BOM detected). - * "did_iconv" iconv() conversion failed, try 'charconvert'. - * "keep_fileformat" Don't reset "fileformat". - * - * Other status indicators: - * "tmpname" When != NULL did conversion with 'charconvert'. - * Output file has to be deleted afterwards. - * "iconv_fd" When != -1 did conversion with iconv(). - */ -retry: - - if (file_rewind) { - if (read_buffer) { - read_buf_lnum = 1; - read_buf_col = 0; - } else if (read_stdin || lseek(fd, (off_t)0L, SEEK_SET) != 0) { - /* Can't rewind the file, give up. */ - error = TRUE; - goto failed; - } - /* Delete the previously read lines. */ - while (lnum > from) - ml_delete(lnum--, FALSE); - file_rewind = FALSE; - if (set_options) { - curbuf->b_p_bomb = FALSE; - curbuf->b_start_bomb = FALSE; - } - conv_error = 0; - } - - if (cryptkey != NULL) - /* Need to reset the state, but keep the key, don't want to ask for it - * again. */ - crypt_pop_state(); - - /* - * When retrying with another "fenc" and the first time "fileformat" - * will be reset. - */ - if (keep_fileformat) - keep_fileformat = FALSE; - else { - if (eap != NULL && eap->force_ff != 0) { - fileformat = get_fileformat_force(curbuf, eap); - try_unix = try_dos = try_mac = FALSE; - } else if (curbuf->b_p_bin) - fileformat = EOL_UNIX; /* binary: use Unix format */ - else if (*p_ffs == NUL) - fileformat = get_fileformat(curbuf); /* use format from buffer */ - else - fileformat = EOL_UNKNOWN; /* detect from file */ - } - -# ifdef USE_ICONV - if (iconv_fd != (iconv_t)-1) { - /* aborted conversion with iconv(), close the descriptor */ - iconv_close(iconv_fd); - iconv_fd = (iconv_t)-1; - } -# endif - - if (advance_fenc) { - /* - * Try the next entry in 'fileencodings'. - */ - advance_fenc = FALSE; - - if (eap != NULL && eap->force_enc != 0) { - /* Conversion given with "++cc=" wasn't possible, read - * without conversion. */ - notconverted = TRUE; - conv_error = 0; - if (fenc_alloced) - free(fenc); - fenc = (char_u *)""; - fenc_alloced = FALSE; - } else { - if (fenc_alloced) - free(fenc); - if (fenc_next != NULL) { - fenc = next_fenc(&fenc_next); - fenc_alloced = (fenc_next != NULL); - } else { - fenc = (char_u *)""; - fenc_alloced = FALSE; - } - } - if (tmpname != NULL) { - os_remove((char *)tmpname); // delete converted file - free(tmpname); - tmpname = NULL; - } - } - - /* - * Conversion may be required when the encoding of the file is different - * from 'encoding' or 'encoding' is UTF-16, UCS-2 or UCS-4. - */ - fio_flags = 0; - converted = need_conversion(fenc); - if (converted) { - - /* "ucs-bom" means we need to check the first bytes of the file - * for a BOM. */ - if (STRCMP(fenc, ENC_UCSBOM) == 0) - fio_flags = FIO_UCSBOM; - - /* - * Check if UCS-2/4 or Latin1 to UTF-8 conversion needs to be - * done. This is handled below after read(). Prepare the - * fio_flags to avoid having to parse the string each time. - * Also check for Unicode to Latin1 conversion, because iconv() - * appears not to handle this correctly. This works just like - * conversion to UTF-8 except how the resulting character is put in - * the buffer. - */ - else if (enc_utf8 || STRCMP(p_enc, "latin1") == 0) - fio_flags = get_fio_flags(fenc); - - - -# ifdef USE_ICONV - /* - * Try using iconv() if we can't convert internally. - */ - if (fio_flags == 0 - && !did_iconv - ) - iconv_fd = (iconv_t)my_iconv_open( - enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc); -# endif - - /* - * Use the 'charconvert' expression when conversion is required - * and we can't do it internally or with iconv(). - */ - if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL -# ifdef USE_ICONV - && iconv_fd == (iconv_t)-1 -# endif - ) { -# ifdef USE_ICONV - did_iconv = FALSE; -# endif - /* Skip conversion when it's already done (retry for wrong - * "fileformat"). */ - if (tmpname == NULL) { - tmpname = readfile_charconvert(fname, fenc, &fd); - if (tmpname == NULL) { - /* Conversion failed. Try another one. */ - advance_fenc = TRUE; - if (fd < 0) { - /* Re-opening the original file failed! */ - EMSG(_("E202: Conversion made file unreadable!")); - error = TRUE; - goto failed; - } - goto retry; - } - } - } else { - if (fio_flags == 0 -# ifdef USE_ICONV - && iconv_fd == (iconv_t)-1 -# endif - ) { - /* Conversion wanted but we can't. - * Try the next conversion in 'fileencodings' */ - advance_fenc = TRUE; - goto retry; - } - } - } - - /* Set "can_retry" when it's possible to rewind the file and try with - * another "fenc" value. It's FALSE when no other "fenc" to try, reading - * stdin or fixed at a specific encoding. */ - can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc); - - if (!skip_read) { - linerest = 0; - filesize = 0; - skip_count = lines_to_skip; - read_count = lines_to_read; - conv_restlen = 0; - read_undo_file = (newfile && (flags & READ_KEEP_UNDO) == 0 - && curbuf->b_ffname != NULL - && curbuf->b_p_udf - && !filtering - && !read_stdin - && !read_buffer); - if (read_undo_file) - sha256_start(&sha_ctx); - } - - while (!error && !got_int) { - /* - * We allocate as much space for the file as we can get, plus - * space for the old line plus room for one terminating NUL. - * The amount is limited by the fact that read() only can read - * upto max_unsigned characters (and other things). - */ - { - if (!skip_read) { - size = 0x10000L; /* use buffer >= 64K */ - - for (; size >= 10; size /= 2) { - new_buffer = verbose_try_malloc((size_t)size + (size_t)linerest + 1); - if (new_buffer) { - break; - } - } - if (new_buffer == NULL) { - error = TRUE; - break; - } - if (linerest) /* copy characters from the previous buffer */ - memmove(new_buffer, ptr - linerest, (size_t)linerest); - free(buffer); - buffer = new_buffer; - ptr = buffer + linerest; - line_start = buffer; - - /* May need room to translate into. - * For iconv() we don't really know the required space, use a - * factor ICONV_MULT. - * latin1 to utf-8: 1 byte becomes up to 2 bytes - * utf-16 to utf-8: 2 bytes become up to 3 bytes, 4 bytes - * become up to 4 bytes, size must be multiple of 2 - * ucs-2 to utf-8: 2 bytes become up to 3 bytes, size must be - * multiple of 2 - * ucs-4 to utf-8: 4 bytes become up to 6 bytes, size must be - * multiple of 4 */ - real_size = (int)size; -# ifdef USE_ICONV - if (iconv_fd != (iconv_t)-1) - size = size / ICONV_MULT; - else -# endif - if (fio_flags & FIO_LATIN1) - size = size / 2; - else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) - size = (size * 2 / 3) & ~1; - else if (fio_flags & FIO_UCS4) - size = (size * 2 / 3) & ~3; - else if (fio_flags == FIO_UCSBOM) - size = size / ICONV_MULT; /* worst case */ - - if (conv_restlen > 0) { - /* Insert unconverted bytes from previous line. */ - memmove(ptr, conv_rest, conv_restlen); - ptr += conv_restlen; - size -= conv_restlen; - } - - if (read_buffer) { - /* - * Read bytes from curbuf. Used for converting text read - * from stdin. - */ - if (read_buf_lnum > from) - size = 0; - else { - int n, ni; - long tlen; - - tlen = 0; - for (;; ) { - p = ml_get(read_buf_lnum) + read_buf_col; - n = (int)STRLEN(p); - if ((int)tlen + n + 1 > size) { - /* Filled up to "size", append partial line. - * Change NL to NUL to reverse the effect done - * below. */ - n = (int)(size - tlen); - for (ni = 0; ni < n; ++ni) { - if (p[ni] == NL) - ptr[tlen++] = NUL; - else - ptr[tlen++] = p[ni]; - } - read_buf_col += n; - break; - } else { - /* Append whole line and new-line. Change NL - * to NUL to reverse the effect done below. */ - for (ni = 0; ni < n; ++ni) { - if (p[ni] == NL) - ptr[tlen++] = NUL; - else - ptr[tlen++] = p[ni]; - } - ptr[tlen++] = NL; - read_buf_col = 0; - if (++read_buf_lnum > from) { - /* When the last line didn't have an - * end-of-line don't add it now either. */ - if (!curbuf->b_p_eol) - --tlen; - size = tlen; - break; - } - } - } - } - } else { - /* - * Read bytes from the file. - */ - size = read_eintr(fd, ptr, size); - } - - if (size <= 0) { - if (size < 0) /* read error */ - error = TRUE; - else if (conv_restlen > 0) { - /* - * Reached end-of-file but some trailing bytes could - * not be converted. Truncated file? - */ - - /* When we did a conversion report an error. */ - if (fio_flags != 0 -# ifdef USE_ICONV - || iconv_fd != (iconv_t)-1 -# endif - ) { - if (can_retry) - goto rewind_retry; - if (conv_error == 0) - conv_error = curbuf->b_ml.ml_line_count - - linecnt + 1; - } - /* Remember the first linenr with an illegal byte */ - else if (illegal_byte == 0) - illegal_byte = curbuf->b_ml.ml_line_count - - linecnt + 1; - if (bad_char_behavior == BAD_DROP) { - *(ptr - conv_restlen) = NUL; - conv_restlen = 0; - } else { - /* Replace the trailing bytes with the replacement - * character if we were converting; if we weren't, - * leave the UTF8 checking code to do it, as it - * works slightly differently. */ - if (bad_char_behavior != BAD_KEEP && (fio_flags != 0 -# ifdef USE_ICONV - || iconv_fd != (iconv_t)-1 -# endif - )) { - while (conv_restlen > 0) { - *(--ptr) = bad_char_behavior; - --conv_restlen; - } - } - fio_flags = 0; /* don't convert this */ -# ifdef USE_ICONV - if (iconv_fd != (iconv_t)-1) { - iconv_close(iconv_fd); - iconv_fd = (iconv_t)-1; - } -# endif - } - } - } - - /* - * At start of file: Check for magic number of encryption. - */ - if (filesize == 0) - cryptkey = check_for_cryptkey(cryptkey, ptr, &size, - &filesize, newfile, sfname, - &did_ask_for_key); - /* - * Decrypt the read bytes. - */ - if (cryptkey != NULL && size > 0) - crypt_decode(ptr, size); - } - skip_read = FALSE; - - /* - * At start of file (or after crypt magic number): Check for BOM. - * Also check for a BOM for other Unicode encodings, but not after - * converting with 'charconvert' or when a BOM has already been - * found. - */ - if ((filesize == 0 - || (filesize == (CRYPT_MAGIC_LEN - + crypt_salt_len[use_crypt_method] - + crypt_seed_len[use_crypt_method]) - && cryptkey != NULL) - ) - && (fio_flags == FIO_UCSBOM - || (!curbuf->b_p_bomb - && tmpname == NULL - && (*fenc == 'u' || (*fenc == NUL && enc_utf8))))) { - char_u *ccname; - int blen; - - /* no BOM detection in a short file or in binary mode */ - if (size < 2 || curbuf->b_p_bin) - ccname = NULL; - else - ccname = check_for_bom(ptr, size, &blen, - fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags(fenc)); - if (ccname != NULL) { - /* Remove BOM from the text */ - filesize += blen; - size -= blen; - memmove(ptr, ptr + blen, (size_t)size); - if (set_options) { - curbuf->b_p_bomb = TRUE; - curbuf->b_start_bomb = TRUE; - } - } - - if (fio_flags == FIO_UCSBOM) { - if (ccname == NULL) { - /* No BOM detected: retry with next encoding. */ - advance_fenc = TRUE; - } else { - /* BOM detected: set "fenc" and jump back */ - if (fenc_alloced) - free(fenc); - fenc = ccname; - fenc_alloced = FALSE; - } - /* retry reading without getting new bytes or rewinding */ - skip_read = TRUE; - goto retry; - } - } - - /* Include not converted bytes. */ - ptr -= conv_restlen; - size += conv_restlen; - conv_restlen = 0; - /* - * Break here for a read error or end-of-file. - */ - if (size <= 0) - break; - - -# ifdef USE_ICONV - if (iconv_fd != (iconv_t)-1) { - /* - * Attempt conversion of the read bytes to 'encoding' using - * iconv(). - */ - const char *fromp; - char *top; - size_t from_size; - size_t to_size; - - fromp = (char *)ptr; - from_size = size; - ptr += size; - top = (char *)ptr; - to_size = real_size - size; - - /* - * If there is conversion error or not enough room try using - * another conversion. Except for when there is no - * alternative (help files). - */ - while ((iconv(iconv_fd, (void *)&fromp, &from_size, - &top, &to_size) - == (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL) - || from_size > CONV_RESTLEN) { - if (can_retry) - goto rewind_retry; - if (conv_error == 0) - conv_error = readfile_linenr(linecnt, - ptr, (char_u *)top); - - /* Deal with a bad byte and continue with the next. */ - ++fromp; - --from_size; - if (bad_char_behavior == BAD_KEEP) { - *top++ = *(fromp - 1); - --to_size; - } else if (bad_char_behavior != BAD_DROP) { - *top++ = bad_char_behavior; - --to_size; - } - } - - if (from_size > 0) { - /* Some remaining characters, keep them for the next - * round. */ - memmove(conv_rest, (char_u *)fromp, from_size); - conv_restlen = (int)from_size; - } - - /* move the linerest to before the converted characters */ - line_start = ptr - linerest; - memmove(line_start, buffer, (size_t)linerest); - size = (long)((char_u *)top - ptr); - } -# endif - -# ifdef MACOS_CONVERT - if (fio_flags & FIO_MACROMAN) { - /* - * Conversion from Apple MacRoman char encoding to UTF-8 or - * latin1. This is in os_mac_conv.c. - */ - if (macroman2enc(ptr, &size, real_size) == FAIL) - goto rewind_retry; - } else -# endif - if (fio_flags != 0) { - int u8c; - char_u *dest; - char_u *tail = NULL; - - /* - * "enc_utf8" set: Convert Unicode or Latin1 to UTF-8. - * "enc_utf8" not set: Convert Unicode to Latin1. - * Go from end to start through the buffer, because the number - * of bytes may increase. - * "dest" points to after where the UTF-8 bytes go, "p" points - * to after the next character to convert. - */ - dest = ptr + real_size; - if (fio_flags == FIO_LATIN1 || fio_flags == FIO_UTF8) { - p = ptr + size; - if (fio_flags == FIO_UTF8) { - /* Check for a trailing incomplete UTF-8 sequence */ - tail = ptr + size - 1; - while (tail > ptr && (*tail & 0xc0) == 0x80) - --tail; - if (tail + utf_byte2len(*tail) <= ptr + size) - tail = NULL; - else - p = tail; - } - } else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) { - /* Check for a trailing byte */ - p = ptr + (size & ~1); - if (size & 1) - tail = p; - if ((fio_flags & FIO_UTF16) && p > ptr) { - /* Check for a trailing leading word */ - if (fio_flags & FIO_ENDIAN_L) { - u8c = (*--p << 8); - u8c += *--p; - } else { - u8c = *--p; - u8c += (*--p << 8); - } - if (u8c >= 0xd800 && u8c <= 0xdbff) - tail = p; - else - p += 2; - } - } else { /* FIO_UCS4 */ - /* Check for trailing 1, 2 or 3 bytes */ - p = ptr + (size & ~3); - if (size & 3) - tail = p; - } - - /* If there is a trailing incomplete sequence move it to - * conv_rest[]. */ - if (tail != NULL) { - conv_restlen = (int)((ptr + size) - tail); - memmove(conv_rest, (char_u *)tail, conv_restlen); - size -= conv_restlen; - } - - - while (p > ptr) { - if (fio_flags & FIO_LATIN1) - u8c = *--p; - else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) { - if (fio_flags & FIO_ENDIAN_L) { - u8c = (*--p << 8); - u8c += *--p; - } else { - u8c = *--p; - u8c += (*--p << 8); - } - if ((fio_flags & FIO_UTF16) - && u8c >= 0xdc00 && u8c <= 0xdfff) { - int u16c; - - if (p == ptr) { - /* Missing leading word. */ - if (can_retry) - goto rewind_retry; - if (conv_error == 0) - conv_error = readfile_linenr(linecnt, - ptr, p); - if (bad_char_behavior == BAD_DROP) - continue; - if (bad_char_behavior != BAD_KEEP) - u8c = bad_char_behavior; - } - - /* found second word of double-word, get the first - * word and compute the resulting character */ - if (fio_flags & FIO_ENDIAN_L) { - u16c = (*--p << 8); - u16c += *--p; - } else { - u16c = *--p; - u16c += (*--p << 8); - } - u8c = 0x10000 + ((u16c & 0x3ff) << 10) - + (u8c & 0x3ff); - - /* Check if the word is indeed a leading word. */ - if (u16c < 0xd800 || u16c > 0xdbff) { - if (can_retry) - goto rewind_retry; - if (conv_error == 0) - conv_error = readfile_linenr(linecnt, - ptr, p); - if (bad_char_behavior == BAD_DROP) - continue; - if (bad_char_behavior != BAD_KEEP) - u8c = bad_char_behavior; - } - } - } else if (fio_flags & FIO_UCS4) { - if (fio_flags & FIO_ENDIAN_L) { - u8c = (*--p << 24); - u8c += (*--p << 16); - u8c += (*--p << 8); - u8c += *--p; - } else { /* big endian */ - u8c = *--p; - u8c += (*--p << 8); - u8c += (*--p << 16); - u8c += (*--p << 24); - } - } else { /* UTF-8 */ - if (*--p < 0x80) - u8c = *p; - else { - len = utf_head_off(ptr, p); - p -= len; - u8c = utf_ptr2char(p); - if (len == 0) { - /* Not a valid UTF-8 character, retry with - * another fenc when possible, otherwise just - * report the error. */ - if (can_retry) - goto rewind_retry; - if (conv_error == 0) - conv_error = readfile_linenr(linecnt, - ptr, p); - if (bad_char_behavior == BAD_DROP) - continue; - if (bad_char_behavior != BAD_KEEP) - u8c = bad_char_behavior; - } - } - } - if (enc_utf8) { /* produce UTF-8 */ - dest -= utf_char2len(u8c); - (void)utf_char2bytes(u8c, dest); - } else { /* produce Latin1 */ - --dest; - if (u8c >= 0x100) { - /* character doesn't fit in latin1, retry with - * another fenc when possible, otherwise just - * report the error. */ - if (can_retry) - goto rewind_retry; - if (conv_error == 0) - conv_error = readfile_linenr(linecnt, ptr, p); - if (bad_char_behavior == BAD_DROP) - ++dest; - else if (bad_char_behavior == BAD_KEEP) - *dest = u8c; - else if (eap != NULL && eap->bad_char != 0) - *dest = bad_char_behavior; - else - *dest = 0xBF; - } else - *dest = u8c; - } - } - - /* move the linerest to before the converted characters */ - line_start = dest - linerest; - memmove(line_start, buffer, (size_t)linerest); - size = (long)((ptr + real_size) - dest); - ptr = dest; - } else if (enc_utf8 && !curbuf->b_p_bin) { - int incomplete_tail = FALSE; - - /* Reading UTF-8: Check if the bytes are valid UTF-8. */ - for (p = ptr;; ++p) { - int todo = (int)((ptr + size) - p); - int l; - - if (todo <= 0) - break; - if (*p >= 0x80) { - /* A length of 1 means it's an illegal byte. Accept - * an incomplete character at the end though, the next - * read() will get the next bytes, we'll check it - * then. */ - l = utf_ptr2len_len(p, todo); - if (l > todo && !incomplete_tail) { - /* Avoid retrying with a different encoding when - * a truncated file is more likely, or attempting - * to read the rest of an incomplete sequence when - * we have already done so. */ - if (p > ptr || filesize > 0) - incomplete_tail = TRUE; - /* Incomplete byte sequence, move it to conv_rest[] - * and try to read the rest of it, unless we've - * already done so. */ - if (p > ptr) { - conv_restlen = todo; - memmove(conv_rest, p, conv_restlen); - size -= conv_restlen; - break; - } - } - if (l == 1 || l > todo) { - /* Illegal byte. If we can try another encoding - * do that, unless at EOF where a truncated - * file is more likely than a conversion error. */ - if (can_retry && !incomplete_tail) - break; -# ifdef USE_ICONV - /* When we did a conversion report an error. */ - if (iconv_fd != (iconv_t)-1 && conv_error == 0) - conv_error = readfile_linenr(linecnt, ptr, p); -# endif - /* Remember the first linenr with an illegal byte */ - if (conv_error == 0 && illegal_byte == 0) - illegal_byte = readfile_linenr(linecnt, ptr, p); - - /* Drop, keep or replace the bad byte. */ - if (bad_char_behavior == BAD_DROP) { - memmove(p, p + 1, todo - 1); - --p; - --size; - } else if (bad_char_behavior != BAD_KEEP) - *p = bad_char_behavior; - } else - p += l - 1; - } - } - if (p < ptr + size && !incomplete_tail) { - /* Detected a UTF-8 error. */ -rewind_retry: - /* Retry reading with another conversion. */ -# if defined(FEAT_EVAL) && defined(USE_ICONV) - if (*p_ccv != NUL && iconv_fd != (iconv_t)-1) - /* iconv() failed, try 'charconvert' */ - did_iconv = TRUE; - else -# endif - /* use next item from 'fileencodings' */ - advance_fenc = TRUE; - file_rewind = TRUE; - goto retry; - } - } - - /* count the number of characters (after conversion!) */ - filesize += size; - - /* - * when reading the first part of a file: guess EOL type - */ - if (fileformat == EOL_UNKNOWN) { - /* First try finding a NL, for Dos and Unix */ - if (try_dos || try_unix) { - for (p = ptr; p < ptr + size; ++p) { - if (*p == NL) { - if (!try_unix - || (try_dos && p > ptr && p[-1] == CAR)) - fileformat = EOL_DOS; - else - fileformat = EOL_UNIX; - break; - } - } - - /* Don't give in to EOL_UNIX if EOL_MAC is more likely */ - if (fileformat == EOL_UNIX && try_mac) { - /* Need to reset the counters when retrying fenc. */ - try_mac = 1; - try_unix = 1; - for (; p >= ptr && *p != CAR; p--) - ; - if (p >= ptr) { - for (p = ptr; p < ptr + size; ++p) { - if (*p == NL) - try_unix++; - else if (*p == CAR) - try_mac++; - } - if (try_mac > try_unix) - fileformat = EOL_MAC; - } - } - } - - /* No NL found: may use Mac format */ - if (fileformat == EOL_UNKNOWN && try_mac) - fileformat = EOL_MAC; - - /* Still nothing found? Use first format in 'ffs' */ - if (fileformat == EOL_UNKNOWN) - fileformat = default_fileformat(); - - // May set 'p_ff' if editing a new file. - if (set_options) { - set_fileformat(fileformat, OPT_LOCAL); - } - } - } - - /* - * This loop is executed once for every character read. - * Keep it fast! - */ - if (fileformat == EOL_MAC) { - --ptr; - while (++ptr, --size >= 0) { - /* catch most common case first */ - if ((c = *ptr) != NUL && c != CAR && c != NL) - continue; - if (c == NUL) - *ptr = NL; /* NULs are replaced by newlines! */ - else if (c == NL) - *ptr = CAR; /* NLs are replaced by CRs! */ - else { - if (skip_count == 0) { - *ptr = NUL; /* end of line */ - len = (colnr_T) (ptr - line_start + 1); - if (ml_append(lnum, line_start, len, newfile) == FAIL) { - error = TRUE; - break; - } - if (read_undo_file) - sha256_update(&sha_ctx, line_start, len); - ++lnum; - if (--read_count == 0) { - error = TRUE; /* break loop */ - line_start = ptr; /* nothing left to write */ - break; - } - } else - --skip_count; - line_start = ptr + 1; - } - } - } else { - --ptr; - while (++ptr, --size >= 0) { - if ((c = *ptr) != NUL && c != NL) /* catch most common case */ - continue; - if (c == NUL) - *ptr = NL; /* NULs are replaced by newlines! */ - else { - if (skip_count == 0) { - *ptr = NUL; /* end of line */ - len = (colnr_T)(ptr - line_start + 1); - if (fileformat == EOL_DOS) { - if (ptr[-1] == CAR) { /* remove CR */ - ptr[-1] = NUL; - --len; - } - /* - * Reading in Dos format, but no CR-LF found! - * When 'fileformats' includes "unix", delete all - * the lines read so far and start all over again. - * Otherwise give an error message later. - */ - else if (ff_error != EOL_DOS) { - if ( try_unix - && !read_stdin - && (read_buffer - || lseek(fd, (off_t)0L, SEEK_SET) == 0)) { - fileformat = EOL_UNIX; - if (set_options) - set_fileformat(EOL_UNIX, OPT_LOCAL); - file_rewind = TRUE; - keep_fileformat = TRUE; - goto retry; - } - ff_error = EOL_DOS; - } - } - if (ml_append(lnum, line_start, len, newfile) == FAIL) { - error = TRUE; - break; - } - if (read_undo_file) - sha256_update(&sha_ctx, line_start, len); - ++lnum; - if (--read_count == 0) { - error = TRUE; /* break loop */ - line_start = ptr; /* nothing left to write */ - break; - } - } else - --skip_count; - line_start = ptr + 1; - } - } - } - linerest = (long)(ptr - line_start); - ui_breakcheck(); - } - -failed: - /* not an error, max. number of lines reached */ - if (error && read_count == 0) - error = FALSE; - - /* - * If we get EOF in the middle of a line, note the fact and - * complete the line ourselves. - * In Dos format ignore a trailing CTRL-Z, unless 'binary' set. - */ - if (!error - && !got_int - && linerest != 0 - && !(!curbuf->b_p_bin - && fileformat == EOL_DOS - && *line_start == Ctrl_Z - && ptr == line_start + 1)) { - /* remember for when writing */ - if (set_options) - curbuf->b_p_eol = FALSE; - *ptr = NUL; - len = (colnr_T)(ptr - line_start + 1); - if (ml_append(lnum, line_start, len, newfile) == FAIL) - error = TRUE; - else { - if (read_undo_file) - sha256_update(&sha_ctx, line_start, len); - read_no_eol_lnum = ++lnum; - } - } - - if (set_options) - save_file_ff(curbuf); /* remember the current file format */ - - crypt_method_used = use_crypt_method; - if (cryptkey != NULL) { - crypt_pop_state(); - if (cryptkey != curbuf->b_p_key) - free_crypt_key(cryptkey); - /* don't set cryptkey to NULL, it's used below as a flag that - * encryption was used */ - } - - /* If editing a new file: set 'fenc' for the current buffer. - * Also for ":read ++edit file". */ - if (set_options) - set_string_option_direct((char_u *)"fenc", -1, fenc, - OPT_FREE|OPT_LOCAL, 0); - if (fenc_alloced) - free(fenc); -# ifdef USE_ICONV - if (iconv_fd != (iconv_t)-1) { - iconv_close(iconv_fd); - iconv_fd = (iconv_t)-1; - } -# endif - - if (!read_buffer && !read_stdin) - close(fd); /* errors are ignored */ -#ifdef HAVE_FD_CLOEXEC - else { - int fdflags = fcntl(fd, F_GETFD); - if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) - fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC); - } -#endif - free(buffer); - -#ifdef HAVE_DUP - if (read_stdin) { - /* Use stderr for stdin, makes shell commands work. */ - close(0); - ignored = dup(2); - } -#endif - - if (tmpname != NULL) { - os_remove((char *)tmpname); // delete converted file - free(tmpname); - } - --no_wait_return; /* may wait for return now */ - - /* - * In recovery mode everything but autocommands is skipped. - */ - if (!recoverymode) { - /* need to delete the last line, which comes from the empty buffer */ - if (newfile && wasempty && !(curbuf->b_ml.ml_flags & ML_EMPTY)) { - ml_delete(curbuf->b_ml.ml_line_count, FALSE); - --linecnt; - } - linecnt = curbuf->b_ml.ml_line_count - linecnt; - if (filesize == 0) - linecnt = 0; - if (newfile || read_buffer) { - redraw_curbuf_later(NOT_VALID); - /* After reading the text into the buffer the diff info needs to - * be updated. */ - diff_invalidate(curbuf); - /* All folds in the window are invalid now. Mark them for update - * before triggering autocommands. */ - foldUpdateAll(curwin); - } else if (linecnt) /* appended at least one line */ - appended_lines_mark(from, linecnt); - - /* - * If we were reading from the same terminal as where messages go, - * the screen will have been messed up. - * Switch on raw mode now and clear the screen. - */ - if (read_stdin) { - settmode(TMODE_RAW); /* set to raw mode */ - starttermcap(); - screenclear(); - } - - if (got_int) { - if (!(flags & READ_DUMMY)) { - filemess(curbuf, sfname, (char_u *)_(e_interr), 0); - if (newfile) - curbuf->b_p_ro = TRUE; /* must use "w!" now */ - } - msg_scroll = msg_save; - check_marks_read(); - return OK; /* an interrupt isn't really an error */ - } - - if (!filtering && !(flags & READ_DUMMY)) { - msg_add_fname(curbuf, sfname); /* fname in IObuff with quotes */ - c = FALSE; - -#ifdef UNIX -# ifdef S_ISFIFO - if (S_ISFIFO(perm)) { /* fifo or socket */ - STRCAT(IObuff, _("[fifo/socket]")); - c = TRUE; - } -# else -# ifdef S_IFIFO - if ((perm & S_IFMT) == S_IFIFO) { /* fifo */ - STRCAT(IObuff, _("[fifo]")); - c = TRUE; - } -# endif -# ifdef S_IFSOCK - if ((perm & S_IFMT) == S_IFSOCK) { /* or socket */ - STRCAT(IObuff, _("[socket]")); - c = TRUE; - } -# endif -# endif -# ifdef OPEN_CHR_FILES - if (S_ISCHR(perm)) { /* or character special */ - STRCAT(IObuff, _("[character special]")); - c = TRUE; - } -# endif -#endif - if (curbuf->b_p_ro) { - STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]")); - c = TRUE; - } - if (read_no_eol_lnum) { - msg_add_eol(); - c = TRUE; - } - if (ff_error == EOL_DOS) { - STRCAT(IObuff, _("[CR missing]")); - c = TRUE; - } - if (split) { - STRCAT(IObuff, _("[long lines split]")); - c = TRUE; - } - if (notconverted) { - STRCAT(IObuff, _("[NOT converted]")); - c = TRUE; - } else if (converted) { - STRCAT(IObuff, _("[converted]")); - c = TRUE; - } - if (cryptkey != NULL) { - if (crypt_method_used == 1) - STRCAT(IObuff, _("[blowfish]")); - else - STRCAT(IObuff, _("[crypted]")); - c = TRUE; - } - if (conv_error != 0) { - sprintf((char *)IObuff + STRLEN(IObuff), - _("[CONVERSION ERROR in line %" PRId64 "]"), (int64_t)conv_error); - c = TRUE; - } else if (illegal_byte > 0) { - sprintf((char *)IObuff + STRLEN(IObuff), - _("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte); - c = TRUE; - } else if (error) { - STRCAT(IObuff, _("[READ ERRORS]")); - c = TRUE; - } - if (msg_add_fileformat(fileformat)) - c = TRUE; - if (cryptkey != NULL) - msg_add_lines(c, (long)linecnt, filesize - - CRYPT_MAGIC_LEN - - crypt_salt_len[use_crypt_method] - - crypt_seed_len[use_crypt_method]); - else - msg_add_lines(c, (long)linecnt, filesize); - - free(keep_msg); - keep_msg = NULL; - msg_scrolled_ign = TRUE; - p = msg_trunc_attr(IObuff, FALSE, 0); - if (read_stdin || read_buffer || restart_edit != 0 - || (msg_scrolled != 0 && !need_wait_return)) - /* Need to repeat the message after redrawing when: - * - When reading from stdin (the screen will be cleared next). - * - When restart_edit is set (otherwise there will be a delay - * before redrawing). - * - When the screen was scrolled but there is no wait-return - * prompt. */ - set_keep_msg(p, 0); - msg_scrolled_ign = FALSE; - } - - /* with errors writing the file requires ":w!" */ - if (newfile && (error - || conv_error != 0 - || (illegal_byte > 0 && bad_char_behavior != BAD_KEEP) - )) - curbuf->b_p_ro = TRUE; - - u_clearline(); /* cannot use "U" command after adding lines */ - - /* - * In Ex mode: cursor at last new line. - * Otherwise: cursor at first new line. - */ - if (exmode_active) - curwin->w_cursor.lnum = from + linecnt; - else - curwin->w_cursor.lnum = from + 1; - check_cursor_lnum(); - beginline(BL_WHITE | BL_FIX); /* on first non-blank */ - - /* - * Set '[ and '] marks to the newly read lines. - */ - curbuf->b_op_start.lnum = from + 1; - curbuf->b_op_start.col = 0; - curbuf->b_op_end.lnum = from + linecnt; - curbuf->b_op_end.col = 0; - - } - msg_scroll = msg_save; - - /* - * Get the marks before executing autocommands, so they can be used there. - */ - check_marks_read(); - - /* - * Trick: We remember if the last line of the read didn't have - * an eol even when 'binary' is off, for when writing it again with - * 'binary' on. This is required for - * ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work. - */ - curbuf->b_no_eol_lnum = read_no_eol_lnum; - - /* When reloading a buffer put the cursor at the first line that is - * different. */ - if (flags & READ_KEEP_UNDO) - u_find_first_changed(); - - /* - * When opening a new file locate undo info and read it. - */ - if (read_undo_file) { - char_u hash[UNDO_HASH_SIZE]; - - sha256_finish(&sha_ctx, hash); - u_read_undo(NULL, hash, fname); - } - - if (!read_stdin && !read_buffer) { - int m = msg_scroll; - int n = msg_scrolled; - - /* Save the fileformat now, otherwise the buffer will be considered - * modified if the format/encoding was automatically detected. */ - if (set_options) - save_file_ff(curbuf); - - /* - * The output from the autocommands should not overwrite anything and - * should not be overwritten: Set msg_scroll, restore its value if no - * output was done. - */ - msg_scroll = TRUE; - if (filtering) - apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname, - FALSE, curbuf, eap); - else if (newfile) - apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname, - FALSE, curbuf, eap); - else - apply_autocmds_exarg(EVENT_FILEREADPOST, sfname, sfname, - FALSE, NULL, eap); - if (msg_scrolled == n) - msg_scroll = m; - if (aborting()) /* autocmds may abort script processing */ - return FAIL; - } - - if (recoverymode && error) - return FAIL; - return OK; -} - -#ifdef OPEN_CHR_FILES -/* - * Returns TRUE if the file name argument is of the form "/dev/fd/\d\+", - * which is the name of files used for process substitution output by - * some shells on some operating systems, e.g., bash on SunOS. - * Do not accept "/dev/fd/[012]", opening these may hang Vim. - */ -static int is_dev_fd_file(char_u *fname) -{ - return STRNCMP(fname, "/dev/fd/", 8) == 0 - && VIM_ISDIGIT(fname[8]) - && *skipdigits(fname + 9) == NUL - && (fname[9] != NUL - || (fname[8] != '0' && fname[8] != '1' && fname[8] != '2')); -} -#endif - - -/* - * From the current line count and characters read after that, estimate the - * line number where we are now. - * Used for error messages that include a line number. - */ -static linenr_T -readfile_linenr ( - linenr_T linecnt, /* line count before reading more bytes */ - char_u *p, /* start of more bytes read */ - char_u *endp /* end of more bytes read */ -) -{ - char_u *s; - linenr_T lnum; - - lnum = curbuf->b_ml.ml_line_count - linecnt + 1; - for (s = p; s < endp; ++s) - if (*s == '\n') - ++lnum; - return lnum; -} - -/* - * Fill "*eap" to force the 'fileencoding', 'fileformat' and 'binary to be - * equal to the buffer "buf". Used for calling readfile(). - */ -void prep_exarg(exarg_T *eap, buf_T *buf) -{ - eap->cmd = xmalloc(STRLEN(buf->b_p_ff) + STRLEN(buf->b_p_fenc) + 15); - - sprintf((char *)eap->cmd, "e ++ff=%s ++enc=%s", buf->b_p_ff, buf->b_p_fenc); - eap->force_enc = 14 + (int)STRLEN(buf->b_p_ff); - eap->bad_char = buf->b_bad_char; - eap->force_ff = 7; - - eap->force_bin = buf->b_p_bin ? FORCE_BIN : FORCE_NOBIN; - eap->read_edit = FALSE; - eap->forceit = FALSE; -} - -/* - * Set default or forced 'fileformat' and 'binary'. - */ -void set_file_options(int set_options, exarg_T *eap) -{ - /* set default 'fileformat' */ - if (set_options) { - if (eap != NULL && eap->force_ff != 0) - set_fileformat(get_fileformat_force(curbuf, eap), OPT_LOCAL); - else if (*p_ffs != NUL) - set_fileformat(default_fileformat(), OPT_LOCAL); - } - - /* set or reset 'binary' */ - if (eap != NULL && eap->force_bin != 0) { - int oldval = curbuf->b_p_bin; - - curbuf->b_p_bin = (eap->force_bin == FORCE_BIN); - set_options_bin(oldval, curbuf->b_p_bin, OPT_LOCAL); - } -} - -/* - * Set forced 'fileencoding'. - */ -void set_forced_fenc(exarg_T *eap) -{ - if (eap->force_enc != 0) { - char_u *fenc = enc_canonize(eap->cmd + eap->force_enc); - - if (fenc != NULL) - set_string_option_direct((char_u *)"fenc", -1, - fenc, OPT_FREE|OPT_LOCAL, 0); - free(fenc); - } -} - -/* - * Find next fileencoding to use from 'fileencodings'. - * "pp" points to fenc_next. It's advanced to the next item. - * When there are no more items, an empty string is returned and *pp is set to - * NULL. - * When *pp is not set to NULL, the result is in allocated memory. - */ -static char_u *next_fenc(char_u **pp) -{ - char_u *p; - char_u *r; - - if (**pp == NUL) { - *pp = NULL; - return (char_u *)""; - } - p = vim_strchr(*pp, ','); - if (p == NULL) { - r = enc_canonize(*pp); - *pp += STRLEN(*pp); - } else { - r = vim_strnsave(*pp, (int)(p - *pp)); - *pp = p + 1; - if (r != NULL) { - p = enc_canonize(r); - free(r); - r = p; - } - } - if (r == NULL) { /* out of memory */ - r = (char_u *)""; - *pp = NULL; - } - return r; -} - -/* - * Convert a file with the 'charconvert' expression. - * This closes the file which is to be read, converts it and opens the - * resulting file for reading. - * Returns name of the resulting converted file (the caller should delete it - * after reading it). - * Returns NULL if the conversion failed ("*fdp" is not set) . - */ -static char_u * -readfile_charconvert ( - char_u *fname, /* name of input file */ - char_u *fenc, /* converted from */ - int *fdp /* in/out: file descriptor of file */ -) -{ - char_u *tmpname; - char_u *errmsg = NULL; - - tmpname = vim_tempname('r'); - if (tmpname == NULL) - errmsg = (char_u *)_("Can't find temp file for conversion"); - else { - close(*fdp); /* close the input file, ignore errors */ - *fdp = -1; - if (eval_charconvert(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc, - fname, tmpname) == FAIL) - errmsg = (char_u *)_("Conversion with 'charconvert' failed"); - if (errmsg == NULL && (*fdp = mch_open((char *)tmpname, - O_RDONLY, 0)) < 0) - errmsg = (char_u *)_("can't read output of 'charconvert'"); - } - - if (errmsg != NULL) { - /* Don't use emsg(), it breaks mappings, the retry with - * another type of conversion might still work. */ - MSG(errmsg); - if (tmpname != NULL) { - os_remove((char *)tmpname); // delete converted file - free(tmpname); - tmpname = NULL; - } - } - - /* If the input file is closed, open it (caller should check for error). */ - if (*fdp < 0) - *fdp = mch_open((char *)fname, O_RDONLY, 0); - - return tmpname; -} - - -/* - * Read marks for the current buffer from the viminfo file, when we support - * buffer marks and the buffer has a name. - */ -static void check_marks_read(void) -{ - if (!curbuf->b_marks_read && get_viminfo_parameter('\'') > 0 - && curbuf->b_ffname != NULL) - read_viminfo(NULL, VIF_WANT_MARKS); - - /* Always set b_marks_read; needed when 'viminfo' is changed to include - * the ' parameter after opening a buffer. */ - curbuf->b_marks_read = TRUE; -} - -/* - * Get the crypt method used for a file from "ptr[len]", the magic text at the - * start of the file. - * Returns -1 when no encryption used. - */ -static int crypt_method_from_magic(char *ptr, int len) -{ - int i; - - for (i = 0; i < (int)(sizeof(crypt_magic) / sizeof(crypt_magic[0])); i++) { - if (len < (CRYPT_MAGIC_LEN + crypt_salt_len[i] + crypt_seed_len[i])) - continue; - if (memcmp(ptr, crypt_magic[i], CRYPT_MAGIC_LEN) == 0) - return i; - } - - i = (int)STRLEN(crypt_magic_head); - if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0) - EMSG(_("E821: File is encrypted with unknown method")); - - return -1; -} - -/* - * Check for magic number used for encryption. Applies to the current buffer. - * If found, the magic number is removed from ptr[*sizep] and *sizep and - * *filesizep are updated. - * Return the (new) encryption key, NULL for no encryption. - */ -static char_u * -check_for_cryptkey ( - char_u *cryptkey, /* previous encryption key or NULL */ - char_u *ptr, /* pointer to read bytes */ - long *sizep, /* length of read bytes */ - off_t *filesizep, /* nr of bytes used from file */ - int newfile, /* editing a new buffer */ - char_u *fname, /* file name to display */ - int *did_ask /* flag: whether already asked for key */ -) -{ - int method = crypt_method_from_magic((char *)ptr, *sizep); - int b_p_ro = curbuf->b_p_ro; - - if (method >= 0) { - /* Mark the buffer as read-only until the decryption has taken place. - * Avoids accidentally overwriting the file with garbage. */ - curbuf->b_p_ro = TRUE; - - set_crypt_method(curbuf, method); - if (method > 0) - (void)blowfish_self_test(); - if (cryptkey == NULL && !*did_ask) { - if (*curbuf->b_p_key) - cryptkey = curbuf->b_p_key; - else { - /* When newfile is TRUE, store the typed key in the 'key' - * option and don't free it. bf needs hash of the key saved. - * Don't ask for the key again when first time Enter was hit. - * Happens when retrying to detect encoding. */ - smsg((char_u *)_(need_key_msg), fname); - msg_scroll = TRUE; - cryptkey = get_crypt_key(newfile, FALSE); - *did_ask = TRUE; - - /* check if empty key entered */ - if (cryptkey != NULL && *cryptkey == NUL) { - if (cryptkey != curbuf->b_p_key) - free(cryptkey); - cryptkey = NULL; - } - } - } - - if (cryptkey != NULL) { - int seed_len = crypt_seed_len[method]; - int salt_len = crypt_salt_len[method]; - - crypt_push_state(); - use_crypt_method = method; - if (method == 0) - crypt_init_keys(cryptkey); - else { - bf_key_init(cryptkey, ptr + CRYPT_MAGIC_LEN, salt_len); - bf_cfb_init(ptr + CRYPT_MAGIC_LEN + salt_len, seed_len); - } - - /* Remove magic number from the text */ - *filesizep += CRYPT_MAGIC_LEN + salt_len + seed_len; - *sizep -= CRYPT_MAGIC_LEN + salt_len + seed_len; - memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len, - (size_t)*sizep); - /* Restore the read-only flag. */ - curbuf->b_p_ro = b_p_ro; - } - } - /* When starting to edit a new file which does not have encryption, clear - * the 'key' option, except when starting up (called with -x argument) */ - else if (newfile && *curbuf->b_p_key != NUL && !starting) - set_option_value((char_u *)"key", 0L, (char_u *)"", OPT_LOCAL); - - return cryptkey; -} - -/* - * Check for magic number used for encryption. Applies to the current buffer. - * If found and decryption is possible returns OK; - */ -int prepare_crypt_read(FILE *fp) -{ - int method; - char_u buffer[CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX - + CRYPT_SEED_LEN_MAX + 2]; - - if (fread(buffer, CRYPT_MAGIC_LEN, 1, fp) != 1) - return FAIL; - method = crypt_method_from_magic((char *)buffer, - CRYPT_MAGIC_LEN + - CRYPT_SEED_LEN_MAX + - CRYPT_SALT_LEN_MAX); - if (method < 0 || method != get_crypt_method(curbuf)) - return FAIL; - - crypt_push_state(); - if (method == 0) - crypt_init_keys(curbuf->b_p_key); - else { - int salt_len = crypt_salt_len[method]; - int seed_len = crypt_seed_len[method]; - - if (fread(buffer, salt_len + seed_len, 1, fp) != 1) - return FAIL; - bf_key_init(curbuf->b_p_key, buffer, salt_len); - bf_cfb_init(buffer + salt_len, seed_len); - } - return OK; -} - -/* - * Prepare for writing encrypted bytes for buffer "buf". - * Returns a pointer to an allocated header of length "*lenp". - * When out of memory returns NULL. - * Otherwise calls crypt_push_state(), call crypt_pop_state() later. - */ -char_u *prepare_crypt_write(buf_T *buf, int *lenp) -{ - char_u *header = xcalloc(1, CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX - + CRYPT_SEED_LEN_MAX + 2); - int seed_len = crypt_seed_len[get_crypt_method(buf)]; - int salt_len = crypt_salt_len[get_crypt_method(buf)]; - char_u *salt; - char_u *seed; - - crypt_push_state(); - use_crypt_method = get_crypt_method(buf); /* select zip or blowfish */ - vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method], - CRYPT_MAGIC_LEN); - if (use_crypt_method == 0) - crypt_init_keys(buf->b_p_key); - else { - /* Using blowfish, add salt and seed. */ - salt = header + CRYPT_MAGIC_LEN; - seed = salt + salt_len; - sha2_seed(salt, salt_len, seed, seed_len); - bf_key_init(buf->b_p_key, salt, salt_len); - bf_cfb_init(seed, seed_len); - } - - *lenp = CRYPT_MAGIC_LEN + salt_len + seed_len; - return header; -} - - -#ifdef UNIX -static void -set_file_time ( - char_u *fname, - time_t atime, /* access time */ - time_t mtime /* modification time */ -) -{ -# if defined(HAVE_UTIME) && defined(HAVE_UTIME_H) - struct utimbuf buf; - - buf.actime = atime; - buf.modtime = mtime; - (void)utime((char *)fname, &buf); -# else -# if defined(HAVE_UTIMES) - struct timeval tvp[2]; - - tvp[0].tv_sec = atime; - tvp[0].tv_usec = 0; - tvp[1].tv_sec = mtime; - tvp[1].tv_usec = 0; - (void)utimes((char *)fname, (const struct timeval *)&tvp); -# endif -# endif -} -#endif /* UNIX */ - -/* - * buf_write() - write to file "fname" lines "start" through "end" - * - * We do our own buffering here because fwrite() is so slow. - * - * If "forceit" is true, we don't care for errors when attempting backups. - * In case of an error everything possible is done to restore the original - * file. But when "forceit" is TRUE, we risk losing it. - * - * When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and - * "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed. - * - * This function must NOT use NameBuff (because it's called by autowrite()). - * - * return FAIL for failure, OK otherwise - */ -int -buf_write ( - buf_T *buf, - char_u *fname, - char_u *sfname, - linenr_T start, - linenr_T end, - exarg_T *eap, /* for forced 'ff' and 'fenc', can be - NULL! */ - int append, /* append to the file */ - int forceit, - int reset_changed, - int filtering -) -{ - int fd; - char_u *backup = NULL; - int backup_copy = FALSE; /* copy the original file? */ - int dobackup; - char_u *ffname; - char_u *wfname = NULL; /* name of file to write to */ - char_u *s; - char_u *ptr; - char_u c; - int len; - linenr_T lnum; - long nchars; - char_u *errmsg = NULL; - int errmsg_allocated = FALSE; - char_u *errnum = NULL; - char_u *buffer; - char_u smallbuf[SMBUFSIZE]; - char_u *backup_ext; - int bufsize; - long perm; /* file permissions */ - int retval = OK; - int newfile = FALSE; /* TRUE if file doesn't exist yet */ - int msg_save = msg_scroll; - int overwriting; /* TRUE if writing over original */ - int no_eol = FALSE; /* no end-of-line written */ - int device = FALSE; /* writing to a device */ - int prev_got_int = got_int; - bool file_readonly = false; /* overwritten file is read-only */ - static char *err_readonly = - "is read-only (cannot override: \"W\" in 'cpoptions')"; -#if defined(UNIX) - int made_writable = FALSE; /* 'w' bit has been set */ -#endif - /* writing everything */ - int whole = (start == 1 && end == buf->b_ml.ml_line_count); - linenr_T old_line_count = buf->b_ml.ml_line_count; - int attr; - int fileformat; - int write_bin; - struct bw_info write_info; /* info for buf_write_bytes() */ - int converted = FALSE; - int notconverted = FALSE; - char_u *fenc; /* effective 'fileencoding' */ - char_u *fenc_tofree = NULL; /* allocated "fenc" */ -#ifdef HAS_BW_FLAGS - int wb_flags = 0; -#endif -#ifdef HAVE_ACL - vim_acl_T acl = NULL; /* ACL copied from original file to - backup or new file */ -#endif - int write_undo_file = FALSE; - context_sha256_T sha_ctx; - int crypt_method_used; - - if (fname == NULL || *fname == NUL) /* safety check */ - return FAIL; - if (buf->b_ml.ml_mfp == NULL) { - /* This can happen during startup when there is a stray "w" in the - * vimrc file. */ - EMSG(_(e_emptybuf)); - return FAIL; - } - - /* - * Disallow writing from .exrc and .vimrc in current directory for - * security reasons. - */ - if (check_secure()) - return FAIL; - - /* Avoid a crash for a long name. */ - if (STRLEN(fname) >= MAXPATHL) { - EMSG(_(e_longname)); - return FAIL; - } - - /* must init bw_conv_buf and bw_iconv_fd before jumping to "fail" */ - write_info.bw_conv_buf = NULL; - write_info.bw_conv_error = FALSE; - write_info.bw_conv_error_lnum = 0; - write_info.bw_restlen = 0; -# ifdef USE_ICONV - write_info.bw_iconv_fd = (iconv_t)-1; -# endif - - /* After writing a file changedtick changes but we don't want to display - * the line. */ - ex_no_reprint = TRUE; - - /* - * If there is no file name yet, use the one for the written file. - * BF_NOTEDITED is set to reflect this (in case the write fails). - * Don't do this when the write is for a filter command. - * Don't do this when appending. - * Only do this when 'cpoptions' contains the 'F' flag. - */ - if (buf->b_ffname == NULL - && reset_changed - && whole - && buf == curbuf - && !bt_nofile(buf) - && !filtering - && (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL) - && vim_strchr(p_cpo, CPO_FNAMEW) != NULL) { - if (set_rw_fname(fname, sfname) == FAIL) - return FAIL; - buf = curbuf; /* just in case autocmds made "buf" invalid */ - } - - if (sfname == NULL) - sfname = fname; - /* - * For Unix: Use the short file name whenever possible. - * Avoids problems with networks and when directory names are changed. - * Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to - * another directory, which we don't detect - */ - ffname = fname; /* remember full fname */ -#ifdef UNIX - fname = sfname; -#endif - - if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0) - overwriting = TRUE; - else - overwriting = FALSE; - - if (exiting) - settmode(TMODE_COOK); /* when exiting allow typeahead now */ - - ++no_wait_return; /* don't wait for return yet */ - - /* - * Set '[ and '] marks to the lines to be written. - */ - buf->b_op_start.lnum = start; - buf->b_op_start.col = 0; - buf->b_op_end.lnum = end; - buf->b_op_end.col = 0; - - { - aco_save_T aco; - int buf_ffname = FALSE; - int buf_sfname = FALSE; - int buf_fname_f = FALSE; - int buf_fname_s = FALSE; - int did_cmd = FALSE; - int nofile_err = FALSE; - int empty_memline = (buf->b_ml.ml_mfp == NULL); - - /* - * Apply PRE autocommands. - * Set curbuf to the buffer to be written. - * Careful: The autocommands may call buf_write() recursively! - */ - if (ffname == buf->b_ffname) - buf_ffname = TRUE; - if (sfname == buf->b_sfname) - buf_sfname = TRUE; - if (fname == buf->b_ffname) - buf_fname_f = TRUE; - if (fname == buf->b_sfname) - buf_fname_s = TRUE; - - /* set curwin/curbuf to buf and save a few things */ - aucmd_prepbuf(&aco, buf); - - if (append) { - if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD, - sfname, sfname, FALSE, curbuf, eap))) { - if (overwriting && bt_nofile(curbuf)) - nofile_err = TRUE; - else - apply_autocmds_exarg(EVENT_FILEAPPENDPRE, - sfname, sfname, FALSE, curbuf, eap); - } - } else if (filtering) { - apply_autocmds_exarg(EVENT_FILTERWRITEPRE, - NULL, sfname, FALSE, curbuf, eap); - } else if (reset_changed && whole) { - int was_changed = curbufIsChanged(); - - did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD, - sfname, sfname, FALSE, curbuf, eap); - if (did_cmd) { - if (was_changed && !curbufIsChanged()) { - /* Written everything correctly and BufWriteCmd has reset - * 'modified': Correct the undo information so that an - * undo now sets 'modified'. */ - u_unchanged(curbuf); - u_update_save_nr(curbuf); - } - } else { - if (overwriting && bt_nofile(curbuf)) - nofile_err = TRUE; - else - apply_autocmds_exarg(EVENT_BUFWRITEPRE, - sfname, sfname, FALSE, curbuf, eap); - } - } else { - if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD, - sfname, sfname, FALSE, curbuf, eap))) { - if (overwriting && bt_nofile(curbuf)) - nofile_err = TRUE; - else - apply_autocmds_exarg(EVENT_FILEWRITEPRE, - sfname, sfname, FALSE, curbuf, eap); - } - } - - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - - /* - * In three situations we return here and don't write the file: - * 1. the autocommands deleted or unloaded the buffer. - * 2. The autocommands abort script processing. - * 3. If one of the "Cmd" autocommands was executed. - */ - if (!buf_valid(buf)) - buf = NULL; - if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline) - || did_cmd || nofile_err - || aborting() - ) { - --no_wait_return; - msg_scroll = msg_save; - if (nofile_err) - EMSG(_("E676: No matching autocommands for acwrite buffer")); - - if (nofile_err - || aborting() - ) - /* An aborting error, interrupt or exception in the - * autocommands. */ - return FAIL; - if (did_cmd) { - if (buf == NULL) - /* The buffer was deleted. We assume it was written - * (can't retry anyway). */ - return OK; - if (overwriting) { - /* Assume the buffer was written, update the timestamp. */ - ml_timestamp(buf); - if (append) - buf->b_flags &= ~BF_NEW; - else - buf->b_flags &= ~BF_WRITE_MASK; - } - if (reset_changed && buf->b_changed && !append - && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)) - /* Buffer still changed, the autocommands didn't work - * properly. */ - return FAIL; - return OK; - } - if (!aborting()) - EMSG(_("E203: Autocommands deleted or unloaded buffer to be written")); - return FAIL; - } - - /* - * The autocommands may have changed the number of lines in the file. - * When writing the whole file, adjust the end. - * When writing part of the file, assume that the autocommands only - * changed the number of lines that are to be written (tricky!). - */ - if (buf->b_ml.ml_line_count != old_line_count) { - if (whole) /* write all */ - end = buf->b_ml.ml_line_count; - else if (buf->b_ml.ml_line_count > old_line_count) /* more lines */ - end += buf->b_ml.ml_line_count - old_line_count; - else { /* less lines */ - end -= old_line_count - buf->b_ml.ml_line_count; - if (end < start) { - --no_wait_return; - msg_scroll = msg_save; - EMSG(_("E204: Autocommand changed number of lines in unexpected way")); - return FAIL; - } - } - } - - /* - * The autocommands may have changed the name of the buffer, which may - * be kept in fname, ffname and sfname. - */ - if (buf_ffname) - ffname = buf->b_ffname; - if (buf_sfname) - sfname = buf->b_sfname; - if (buf_fname_f) - fname = buf->b_ffname; - if (buf_fname_s) - fname = buf->b_sfname; - } - - - if (shortmess(SHM_OVER) && !exiting) - msg_scroll = FALSE; /* overwrite previous file message */ - else - msg_scroll = TRUE; /* don't overwrite previous file message */ - if (!filtering) - filemess(buf, -#ifndef UNIX - sfname, -#else - fname, -#endif - (char_u *)"", 0); /* show that we are busy */ - msg_scroll = FALSE; /* always overwrite the file message now */ - - buffer = verbose_try_malloc(BUFSIZE); - // can't allocate big buffer, use small one (to be able to write when out of - // memory) - if (buffer == NULL) { - buffer = smallbuf; - bufsize = SMBUFSIZE; - } else - bufsize = BUFSIZE; - - /* - * Get information about original file (if there is one). - */ -#if defined(UNIX) - perm = -1; - FileInfo file_info_old; - if (!os_get_file_info((char *)fname, &file_info_old)) { - newfile = TRUE; - } else { - perm = file_info_old.stat.st_mode; - if (!S_ISREG(file_info_old.stat.st_mode)) { /* not a file */ - if (S_ISDIR(file_info_old.stat.st_mode)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); - goto fail; - } - if (mch_nodetype(fname) != NODE_WRITABLE) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); - goto fail; - } - /* It's a device of some kind (or a fifo) which we can write to - * but for which we can't make a backup. */ - device = TRUE; - newfile = TRUE; - perm = -1; - } - } -#else /* !UNIX */ - /* - * Check for a writable device name. - */ - c = mch_nodetype(fname); - if (c == NODE_OTHER) { - errnum = (char_u *)"E503: "; - errmsg = (char_u *)_("is not a file or writable device"); - goto fail; - } - if (c == NODE_WRITABLE) { - device = TRUE; - newfile = TRUE; - perm = -1; - } else { - perm = os_getperm(fname); - if (perm < 0) - newfile = TRUE; - else if (os_isdir(fname)) { - errnum = (char_u *)"E502: "; - errmsg = (char_u *)_("is a directory"); - goto fail; - } - if (overwriting) { - os_get_file_info((char *)fname, &file_info_old); - } - - } -#endif /* !UNIX */ - - if (!device && !newfile) { - /* - * Check if the file is really writable (when renaming the file to - * make a backup we won't discover it later). - */ - file_readonly = os_file_is_readonly((char *)fname); - - if (!forceit && file_readonly) { - if (vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); - } else { - errnum = (char_u *)"E505: "; - errmsg = (char_u *)_("is read-only (add ! to override)"); - } - goto fail; - } - - /* - * Check if the timestamp hasn't changed since reading the file. - */ - if (overwriting) { - retval = check_mtime(buf, &file_info_old); - if (retval == FAIL) - goto fail; - } - } - -#ifdef HAVE_ACL - /* - * For systems that support ACL: get the ACL from the original file. - */ - if (!newfile) - acl = mch_get_acl(fname); -#endif - - /* - * If 'backupskip' is not empty, don't make a backup for some files. - */ - dobackup = (p_wb || p_bk || *p_pm != NUL); - if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname)) - dobackup = FALSE; - - /* - * Save the value of got_int and reset it. We don't want a previous - * interruption cancel writing, only hitting CTRL-C while writing should - * abort it. - */ - prev_got_int = got_int; - got_int = FALSE; - - /* Mark the buffer as 'being saved' to prevent changed buffer warnings */ - buf->b_saving = TRUE; - - /* - * If we are not appending or filtering, the file exists, and the - * 'writebackup', 'backup' or 'patchmode' option is set, need a backup. - * When 'patchmode' is set also make a backup when appending. - * - * Do not make any backup, if 'writebackup' and 'backup' are both switched - * off. This helps when editing large files on almost-full disks. - */ - if (!(append && *p_pm == NUL) && !filtering && perm >= 0 && dobackup) { - FileInfo file_info; - - if ((bkc_flags & BKC_YES) || append) { /* "yes" */ - backup_copy = TRUE; - } else if ((bkc_flags & BKC_AUTO)) { /* "auto" */ - int i; - -# ifdef UNIX - /* - * Don't rename the file when: - * - it's a hard link - * - it's a symbolic link - * - we don't have write permission in the directory - * - we can't set the owner/group of the new file - */ - if (file_info_old.stat.st_nlink > 1 - || !os_get_file_info_link((char *)fname, &file_info) - || !os_file_info_id_equal(&file_info, &file_info_old) -# ifndef HAVE_FCHOWN - || file_info.stat.st_uid != file_info_old.stat.st_uid - || file_info.stat.st_gid != file_info_old.stat.st_gid -# endif - ) { - backup_copy = TRUE; - } else -# endif - { - /* - * Check if we can create a file and set the owner/group to - * the ones from the original file. - * First find a file name that doesn't exist yet (use some - * arbitrary numbers). - */ - STRCPY(IObuff, fname); - for (i = 4913;; i += 123) { - sprintf((char *)path_tail(IObuff), "%d", i); - if (!os_get_file_info_link((char *)IObuff, &file_info)) { - break; - } - } - fd = mch_open((char *)IObuff, - O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm); - if (fd < 0) /* can't write in directory */ - backup_copy = TRUE; - else { -# ifdef UNIX -# ifdef HAVE_FCHOWN - fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); -# endif - if (!os_get_file_info((char *)IObuff, &file_info) - || file_info.stat.st_uid != file_info_old.stat.st_uid - || file_info.stat.st_gid != file_info_old.stat.st_gid - || (long)file_info.stat.st_mode != perm) { - backup_copy = TRUE; - } -# endif - /* Close the file before removing it, on MS-Windows we - * can't delete an open file. */ - close(fd); - os_remove((char *)IObuff); - } - } - } - - /* - * Break symlinks and/or hardlinks if we've been asked to. - */ - if ((bkc_flags & BKC_BREAKSYMLINK) || (bkc_flags & BKC_BREAKHARDLINK)) { -# ifdef UNIX - bool file_info_link_ok = os_get_file_info_link((char *)fname, &file_info); - - /* Symlinks. */ - if ((bkc_flags & BKC_BREAKSYMLINK) - && file_info_link_ok - && !os_file_info_id_equal(&file_info, &file_info_old)) { - backup_copy = FALSE; - } - - /* Hardlinks. */ - if ((bkc_flags & BKC_BREAKHARDLINK) - && file_info_old.stat.st_nlink > 1 - && (!file_info_link_ok - || os_file_info_id_equal(&file_info, &file_info_old))) { - backup_copy = FALSE; - } -# endif - } - - /* make sure we have a valid backup extension to use */ - if (*p_bex == NUL) - backup_ext = (char_u *)".bak"; - else - backup_ext = p_bex; - - if (backup_copy - && (fd = mch_open((char *)fname, O_RDONLY, 0)) >= 0) { - int bfd; - char_u *copybuf, *wp; - int some_error = FALSE; - char_u *dirp; - char_u *rootname; - - copybuf = verbose_try_malloc(BUFSIZE + 1); - if (copybuf == NULL) { - // out of memory - some_error = TRUE; - goto nobackup; - } - - /* - * Try to make the backup in each directory in the 'bdir' option. - * - * Unix semantics has it, that we may have a writable file, - * that cannot be recreated with a simple open(..., O_CREAT, ) e.g: - * - the directory is not writable, - * - the file may be a symbolic link, - * - the file may belong to another user/group, etc. - * - * For these reasons, the existing writable file must be truncated - * and reused. Creation of a backup COPY will be attempted. - */ - dirp = p_bdir; - while (*dirp) { - /* - * Isolate one directory name, using an entry in 'bdir'. - */ - (void)copy_option_part(&dirp, copybuf, BUFSIZE, ","); - rootname = get_file_in_dir(fname, copybuf); - if (rootname == NULL) { - some_error = TRUE; /* out of memory */ - goto nobackup; - } - - FileInfo file_info_new; - { - /* - * Make backup file name. - */ - backup = modname(rootname, backup_ext, FALSE); - if (backup == NULL) { - free(rootname); - some_error = TRUE; /* out of memory */ - goto nobackup; - } - - /* - * Check if backup file already exists. - */ - if (os_get_file_info((char *)backup, &file_info_new)) { - /* - * Check if backup file is same as original file. - * May happen when modname() gave the same file back (e.g. silly - * link). If we don't check here, we either ruin the file when - * copying or erase it after writing. - */ - if (os_file_info_id_equal(&file_info_new, &file_info_old)) { - free(backup); - backup = NULL; /* no backup file to delete */ - } - - /* - * If we are not going to keep the backup file, don't - * delete an existing one, try to use another name. - * Change one character, just before the extension. - */ - if (!p_bk) { - wp = backup + STRLEN(backup) - 1 - - STRLEN(backup_ext); - if (wp < backup) /* empty file name ??? */ - wp = backup; - *wp = 'z'; - while (*wp > 'a' - && os_get_file_info((char *)backup, &file_info_new)) { - --*wp; - } - /* They all exist??? Must be something wrong. */ - if (*wp == 'a') { - free(backup); - backup = NULL; - } - } - } - } - free(rootname); - - /* - * Try to create the backup file - */ - if (backup != NULL) { - /* remove old backup, if present */ - os_remove((char *)backup); - /* Open with O_EXCL to avoid the file being created while - * we were sleeping (symlink hacker attack?) */ - bfd = mch_open((char *)backup, - O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, - perm & 0777); - if (bfd < 0) { - free(backup); - backup = NULL; - } else { - /* set file protection same as original file, but - * strip s-bit */ - (void)os_setperm(backup, perm & 0777); - -#ifdef UNIX - /* - * Try to set the group of the backup same as the - * original file. If this fails, set the protection - * bits for the group same as the protection bits for - * others. - */ - if (file_info_new.stat.st_gid != file_info_old.stat.st_gid -# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */ - && fchown(bfd, (uid_t)-1, file_info_old.stat.st_gid) != 0 -# endif - ) { - os_setperm(backup, (perm & 0707) | ((perm & 07) << 3)); - } -# ifdef HAVE_SELINUX - mch_copy_sec(fname, backup); -# endif -#endif - - /* - * copy the file. - */ - write_info.bw_fd = bfd; - write_info.bw_buf = copybuf; -#ifdef HAS_BW_FLAGS - write_info.bw_flags = FIO_NOCONVERT; -#endif - while ((write_info.bw_len = read_eintr(fd, copybuf, - BUFSIZE)) > 0) { - if (buf_write_bytes(&write_info) == FAIL) { - errmsg = (char_u *)_( - "E506: Can't write to backup file (add ! to override)"); - break; - } - ui_breakcheck(); - if (got_int) { - errmsg = (char_u *)_(e_interr); - break; - } - } - - if (close(bfd) < 0 && errmsg == NULL) - errmsg = (char_u *)_( - "E507: Close error for backup file (add ! to override)"); - if (write_info.bw_len < 0) - errmsg = (char_u *)_( - "E508: Can't read file for backup (add ! to override)"); -#ifdef UNIX - set_file_time(backup, - file_info_old.stat.st_atim.tv_sec, - file_info_old.stat.st_mtim.tv_sec); -#endif -#ifdef HAVE_ACL - mch_set_acl(backup, acl); -#endif -#ifdef HAVE_SELINUX - mch_copy_sec(fname, backup); -#endif - break; - } - } - } -nobackup: - close(fd); /* ignore errors for closing read file */ - free(copybuf); - - if (backup == NULL && errmsg == NULL) - errmsg = (char_u *)_( - "E509: Cannot create backup file (add ! to override)"); - /* ignore errors when forceit is TRUE */ - if ((some_error || errmsg != NULL) && !forceit) { - retval = FAIL; - goto fail; - } - errmsg = NULL; - } else { - char_u *dirp; - char_u *p; - char_u *rootname; - - /* - * Make a backup by renaming the original file. - */ - /* - * If 'cpoptions' includes the "W" flag, we don't want to - * overwrite a read-only file. But rename may be possible - * anyway, thus we need an extra check here. - */ - if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL) { - errnum = (char_u *)"E504: "; - errmsg = (char_u *)_(err_readonly); - goto fail; - } - - /* - * - * Form the backup file name - change path/fo.o.h to - * path/fo.o.h.bak Try all directories in 'backupdir', first one - * that works is used. - */ - dirp = p_bdir; - while (*dirp) { - /* - * Isolate one directory name and make the backup file name. - */ - (void)copy_option_part(&dirp, IObuff, IOSIZE, ","); - rootname = get_file_in_dir(fname, IObuff); - if (rootname == NULL) - backup = NULL; - else { - backup = modname(rootname, backup_ext, FALSE); - free(rootname); - } - - if (backup != NULL) { - /* - * If we are not going to keep the backup file, don't - * delete an existing one, try to use another name. - * Change one character, just before the extension. - */ - if (!p_bk && os_file_exists(backup)) { - p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext); - if (p < backup) /* empty file name ??? */ - p = backup; - *p = 'z'; - while (*p > 'a' && os_file_exists(backup)) - --*p; - /* They all exist??? Must be something wrong! */ - if (*p == 'a') { - free(backup); - backup = NULL; - } - } - } - if (backup != NULL) { - /* - * Delete any existing backup and move the current version - * to the backup. For safety, we don't remove the backup - * until the write has finished successfully. And if the - * 'backup' option is set, leave it around. - */ - /* - * If the renaming of the original file to the backup file - * works, quit here. - */ - if (vim_rename(fname, backup) == 0) - break; - - free(backup); /* don't do the rename below */ - backup = NULL; - } - } - if (backup == NULL && !forceit) { - errmsg = (char_u *)_("E510: Can't make backup file (add ! to override)"); - goto fail; - } - } - } - -#if defined(UNIX) - /* When using ":w!" and the file was read-only: make it writable */ - if (forceit && perm >= 0 && !(perm & 0200) - && file_info_old.stat.st_uid == getuid() - && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { - perm |= 0200; - (void)os_setperm(fname, perm); - made_writable = TRUE; - } -#endif - - /* When using ":w!" and writing to the current file, 'readonly' makes no - * sense, reset it, unless 'Z' appears in 'cpoptions'. */ - if (forceit && overwriting && vim_strchr(p_cpo, CPO_KEEPRO) == NULL) { - buf->b_p_ro = FALSE; - need_maketitle = TRUE; /* set window title later */ - status_redraw_all(); /* redraw status lines later */ - } - - if (end > buf->b_ml.ml_line_count) - end = buf->b_ml.ml_line_count; - if (buf->b_ml.ml_flags & ML_EMPTY) - start = end + 1; - - /* - * If the original file is being overwritten, there is a small chance that - * we crash in the middle of writing. Therefore the file is preserved now. - * This makes all block numbers positive so that recovery does not need - * the original file. - * Don't do this if there is a backup file and we are exiting. - */ - if (reset_changed && !newfile && overwriting - && !(exiting && backup != NULL)) { - ml_preserve(buf, FALSE); - if (got_int) { - errmsg = (char_u *)_(e_interr); - goto restore_backup; - } - } - - - /* Default: write the file directly. May write to a temp file for - * multi-byte conversion. */ - wfname = fname; - - /* Check for forced 'fileencoding' from "++opt=val" argument. */ - if (eap != NULL && eap->force_enc != 0) { - fenc = eap->cmd + eap->force_enc; - fenc = enc_canonize(fenc); - fenc_tofree = fenc; - } else - fenc = buf->b_p_fenc; - - /* - * Check if the file needs to be converted. - */ - converted = need_conversion(fenc); - - /* - * Check if UTF-8 to UCS-2/4 or Latin1 conversion needs to be done. Or - * Latin1 to Unicode conversion. This is handled in buf_write_bytes(). - * Prepare the flags for it and allocate bw_conv_buf when needed. - */ - if (converted && (enc_utf8 || STRCMP(p_enc, "latin1") == 0)) { - wb_flags = get_fio_flags(fenc); - if (wb_flags & (FIO_UCS2 | FIO_UCS4 | FIO_UTF16 | FIO_UTF8)) { - /* Need to allocate a buffer to translate into. */ - if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8)) - write_info.bw_conv_buflen = bufsize * 2; - else /* FIO_UCS4 */ - write_info.bw_conv_buflen = bufsize * 4; - write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen); - if (!write_info.bw_conv_buf) { - end = 0; - } - } - } - - - - if (converted && wb_flags == 0) { -# ifdef USE_ICONV - /* - * Use iconv() conversion when conversion is needed and it's not done - * internally. - */ - write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc, - enc_utf8 ? (char_u *)"utf-8" : p_enc); - if (write_info.bw_iconv_fd != (iconv_t)-1) { - /* We're going to use iconv(), allocate a buffer to convert in. */ - write_info.bw_conv_buflen = bufsize * ICONV_MULT; - write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen); - if (!write_info.bw_conv_buf) { - end = 0; - } - write_info.bw_first = TRUE; - } else -# endif - - /* - * When the file needs to be converted with 'charconvert' after - * writing, write to a temp file instead and let the conversion - * overwrite the original file. - */ - if (*p_ccv != NUL) { - wfname = vim_tempname('w'); - if (wfname == NULL) { /* Can't write without a tempfile! */ - errmsg = (char_u *)_("E214: Can't find temp file for writing"); - goto restore_backup; - } - } - } - if (converted && wb_flags == 0 -# ifdef USE_ICONV - && write_info.bw_iconv_fd == (iconv_t)-1 -# endif - && wfname == fname - ) { - if (!forceit) { - errmsg = (char_u *)_( - "E213: Cannot convert (add ! to write without conversion)"); - goto restore_backup; - } - notconverted = TRUE; - } - - /* - * Open the file "wfname" for writing. - * We may try to open the file twice: If we can't write to the - * file and forceit is TRUE we delete the existing file and try to create - * a new one. If this still fails we may have lost the original file! - * (this may happen when the user reached his quotum for number of files). - * Appending will fail if the file does not exist and forceit is FALSE. - */ - while ((fd = mch_open((char *)wfname, O_WRONLY | (append - ? (forceit ? ( - O_APPEND | - O_CREAT) : - O_APPEND) - : (O_CREAT | - O_TRUNC)) - , perm < 0 ? 0666 : (perm & 0777))) < 0) { - /* - * A forced write will try to create a new file if the old one is - * still readonly. This may also happen when the directory is - * read-only. In that case the os_remove() will fail. - */ - if (errmsg == NULL) { -#ifdef UNIX - FileInfo file_info; - - /* Don't delete the file when it's a hard or symbolic link. */ - if ((!newfile && file_info_old.stat.st_nlink > 1) - || (os_get_file_info_link((char *)fname, &file_info) - && !os_file_info_id_equal(&file_info, &file_info_old))) { - errmsg = (char_u *)_("E166: Can't open linked file for writing"); - } else -#endif - { - errmsg = (char_u *)_("E212: Can't open file for writing"); - if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL - && perm >= 0) { -#ifdef UNIX - /* we write to the file, thus it should be marked - writable after all */ - if (!(perm & 0200)) - made_writable = TRUE; - perm |= 0200; - if (file_info_old.stat.st_uid != getuid() - || file_info_old.stat.st_gid != getgid()) { - perm &= 0777; - } -#endif - if (!append) /* don't remove when appending */ - os_remove((char *)wfname); - continue; - } - } - } - -restore_backup: - { - /* - * If we failed to open the file, we don't need a backup. Throw it - * away. If we moved or removed the original file try to put the - * backup in its place. - */ - if (backup != NULL && wfname == fname) { - if (backup_copy) { - /* - * There is a small chance that we removed the original, - * try to move the copy in its place. - * This may not work if the vim_rename() fails. - * In that case we leave the copy around. - */ - /* If file does not exist, put the copy in its place */ - if (!os_file_exists(fname)) { - vim_rename(backup, fname); - } - /* if original file does exist throw away the copy */ - if (os_file_exists(fname)) { - os_remove((char *)backup); - } - } else { - /* try to put the original file back */ - vim_rename(backup, fname); - } - } - - /* if original file no longer exists give an extra warning */ - if (!newfile && !os_file_exists(fname)) { - end = 0; - } - } - - if (wfname != fname) - free(wfname); - goto fail; - } - errmsg = NULL; - - - write_info.bw_fd = fd; - - if (*buf->b_p_key != NUL && !filtering) { - char_u *header; - int header_len; - - header = prepare_crypt_write(buf, &header_len); - if (header == NULL) - end = 0; - else { - /* Write magic number, so that Vim knows that this file is - * encrypted when reading it again. This also undergoes utf-8 to - * ucs-2/4 conversion when needed. */ - write_info.bw_buf = header; - write_info.bw_len = header_len; - write_info.bw_flags = FIO_NOCONVERT; - if (buf_write_bytes(&write_info) == FAIL) - end = 0; - wb_flags |= FIO_ENCRYPTED; - free(header); - } - } - - write_info.bw_buf = buffer; - nchars = 0; - - /* use "++bin", "++nobin" or 'binary' */ - if (eap != NULL && eap->force_bin != 0) - write_bin = (eap->force_bin == FORCE_BIN); - else - write_bin = buf->b_p_bin; - - /* - * The BOM is written just after the encryption magic number. - * Skip it when appending and the file already existed, the BOM only makes - * sense at the start of the file. - */ - if (buf->b_p_bomb && !write_bin && (!append || perm < 0)) { - write_info.bw_len = make_bom(buffer, fenc); - if (write_info.bw_len > 0) { - /* don't convert, do encryption */ - write_info.bw_flags = FIO_NOCONVERT | wb_flags; - if (buf_write_bytes(&write_info) == FAIL) - end = 0; - else - nchars += write_info.bw_len; - } - } - write_info.bw_start_lnum = start; - - write_undo_file = (buf->b_p_udf && overwriting && !append - && !filtering && reset_changed); - if (write_undo_file) - /* Prepare for computing the hash value of the text. */ - sha256_start(&sha_ctx); - - write_info.bw_len = bufsize; -#ifdef HAS_BW_FLAGS - write_info.bw_flags = wb_flags; -#endif - fileformat = get_fileformat_force(buf, eap); - s = buffer; - len = 0; - for (lnum = start; lnum <= end; ++lnum) { - /* - * The next while loop is done once for each character written. - * Keep it fast! - */ - ptr = ml_get_buf(buf, lnum, FALSE) - 1; - if (write_undo_file) - sha256_update(&sha_ctx, ptr + 1, (uint32_t)(STRLEN(ptr + 1) + 1)); - while ((c = *++ptr) != NUL) { - if (c == NL) - *s = NUL; /* replace newlines with NULs */ - else if (c == CAR && fileformat == EOL_MAC) - *s = NL; /* Mac: replace CRs with NLs */ - else - *s = c; - ++s; - if (++len != bufsize) - continue; - if (buf_write_bytes(&write_info) == FAIL) { - end = 0; /* write error: break loop */ - break; - } - nchars += bufsize; - s = buffer; - len = 0; - write_info.bw_start_lnum = lnum; - } - /* write failed or last line has no EOL: stop here */ - if (end == 0 - || (lnum == end - && write_bin - && (lnum == buf->b_no_eol_lnum - || (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol)))) { - ++lnum; /* written the line, count it */ - no_eol = TRUE; - break; - } - if (fileformat == EOL_UNIX) - *s++ = NL; - else { - *s++ = CAR; /* EOL_MAC or EOL_DOS: write CR */ - if (fileformat == EOL_DOS) { /* write CR-NL */ - if (++len == bufsize) { - if (buf_write_bytes(&write_info) == FAIL) { - end = 0; /* write error: break loop */ - break; - } - nchars += bufsize; - s = buffer; - len = 0; - } - *s++ = NL; - } - } - if (++len == bufsize && end) { - if (buf_write_bytes(&write_info) == FAIL) { - end = 0; /* write error: break loop */ - break; - } - nchars += bufsize; - s = buffer; - len = 0; - - ui_breakcheck(); - if (got_int) { - end = 0; /* Interrupted, break loop */ - break; - } - } - } - if (len > 0 && end > 0) { - write_info.bw_len = len; - if (buf_write_bytes(&write_info) == FAIL) - end = 0; /* write error */ - nchars += len; - } - -#if defined(UNIX) && defined(HAVE_FSYNC) - /* On many journalling file systems there is a bug that causes both the - * original and the backup file to be lost when halting the system right - * after writing the file. That's because only the meta-data is - * journalled. Syncing the file slows down the system, but assures it has - * been written to disk and we don't lose it. - * For a device do try the fsync() but don't complain if it does not work - * (could be a pipe). - * If the 'fsync' option is FALSE, don't fsync(). Useful for laptops. */ - if (p_fs && fsync(fd) != 0 && !device) { - errmsg = (char_u *)_("E667: Fsync failed"); - end = 0; - } -#endif - -#ifdef HAVE_SELINUX - /* Probably need to set the security context. */ - if (!backup_copy) - mch_copy_sec(backup, wfname); -#endif - -#ifdef UNIX - /* When creating a new file, set its owner/group to that of the original - * file. Get the new device and inode number. */ - if (backup != NULL && !backup_copy) { -# ifdef HAVE_FCHOWN - - /* don't change the owner when it's already OK, some systems remove - * permission or ACL stuff */ - FileInfo file_info; - if (!os_get_file_info((char *)wfname, &file_info) - || file_info.stat.st_uid != file_info_old.stat.st_uid - || file_info.stat.st_gid != file_info_old.stat.st_gid) { - fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); - if (perm >= 0) /* set permission again, may have changed */ - (void)os_setperm(wfname, perm); - } -# endif - buf_setino(buf); - } else if (!buf->b_dev_valid) - /* Set the inode when creating a new file. */ - buf_setino(buf); -#endif - - if (close(fd) != 0) { - errmsg = (char_u *)_("E512: Close failed"); - end = 0; - } - -#ifdef UNIX - if (made_writable) - perm &= ~0200; /* reset 'w' bit for security reasons */ -#endif - if (perm >= 0) /* set perm. of new file same as old file */ - (void)os_setperm(wfname, perm); -#ifdef HAVE_ACL - /* Probably need to set the ACL before changing the user (can't set the - * ACL on a file the user doesn't own). */ - if (!backup_copy) - mch_set_acl(wfname, acl); -#endif - crypt_method_used = use_crypt_method; - if (wb_flags & FIO_ENCRYPTED) - crypt_pop_state(); - - - if (wfname != fname) { - /* - * The file was written to a temp file, now it needs to be converted - * with 'charconvert' to (overwrite) the output file. - */ - if (end != 0) { - if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc, - wfname, fname) == FAIL) { - write_info.bw_conv_error = TRUE; - end = 0; - } - } - os_remove((char *)wfname); - free(wfname); - } - - if (end == 0) { - if (errmsg == NULL) { - if (write_info.bw_conv_error) { - if (write_info.bw_conv_error_lnum == 0) - errmsg = (char_u *)_( - "E513: write error, conversion failed (make 'fenc' empty to override)"); - else { - errmsg_allocated = TRUE; - errmsg = alloc(300); - vim_snprintf((char *)errmsg, 300, - _("E513: write error, conversion failed in line %" PRId64 - " (make 'fenc' empty to override)"), - (int64_t)write_info.bw_conv_error_lnum); - } - } else if (got_int) - errmsg = (char_u *)_(e_interr); - else - errmsg = (char_u *)_("E514: write error (file system full?)"); - } - - /* - * If we have a backup file, try to put it in place of the new file, - * because the new file is probably corrupt. This avoids losing the - * original file when trying to make a backup when writing the file a - * second time. - * When "backup_copy" is set we need to copy the backup over the new - * file. Otherwise rename the backup file. - * If this is OK, don't give the extra warning message. - */ - if (backup != NULL) { - if (backup_copy) { - /* This may take a while, if we were interrupted let the user - * know we got the message. */ - if (got_int) { - MSG(_(e_interr)); - out_flush(); - } - if ((fd = mch_open((char *)backup, O_RDONLY, 0)) >= 0) { - if ((write_info.bw_fd = mch_open((char *)fname, - O_WRONLY | O_CREAT | O_TRUNC, - perm & 0777)) >= 0) { - /* copy the file. */ - write_info.bw_buf = smallbuf; -#ifdef HAS_BW_FLAGS - write_info.bw_flags = FIO_NOCONVERT; -#endif - while ((write_info.bw_len = read_eintr(fd, smallbuf, - SMBUFSIZE)) > 0) - if (buf_write_bytes(&write_info) == FAIL) - break; - - if (close(write_info.bw_fd) >= 0 - && write_info.bw_len == 0) - end = 1; /* success */ - } - close(fd); /* ignore errors for closing read file */ - } - } else { - if (vim_rename(backup, fname) == 0) - end = 1; - } - } - goto fail; - } - - lnum -= start; /* compute number of written lines */ - --no_wait_return; /* may wait for return now */ - -#if !defined(UNIX) - fname = sfname; /* use shortname now, for the messages */ -#endif - if (!filtering) { - msg_add_fname(buf, fname); /* put fname in IObuff with quotes */ - c = FALSE; - if (write_info.bw_conv_error) { - STRCAT(IObuff, _(" CONVERSION ERROR")); - c = TRUE; - if (write_info.bw_conv_error_lnum != 0) - vim_snprintf_add((char *)IObuff, IOSIZE, _(" in line %" PRId64 ";"), - (int64_t)write_info.bw_conv_error_lnum); - } else if (notconverted) { - STRCAT(IObuff, _("[NOT converted]")); - c = TRUE; - } else if (converted) { - STRCAT(IObuff, _("[converted]")); - c = TRUE; - } - if (device) { - STRCAT(IObuff, _("[Device]")); - c = TRUE; - } else if (newfile) { - STRCAT(IObuff, shortmess(SHM_NEW) ? _("[New]") : _("[New File]")); - c = TRUE; - } - if (no_eol) { - msg_add_eol(); - c = TRUE; - } - /* may add [unix/dos/mac] */ - if (msg_add_fileformat(fileformat)) - c = TRUE; - if (wb_flags & FIO_ENCRYPTED) { - if (crypt_method_used == 1) - STRCAT(IObuff, _("[blowfish]")); - else - STRCAT(IObuff, _("[crypted]")); - c = TRUE; - } - msg_add_lines(c, (long)lnum, nchars); /* add line/char count */ - if (!shortmess(SHM_WRITE)) { - if (append) - STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended")); - else - STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written")); - } - - set_keep_msg(msg_trunc_attr(IObuff, FALSE, 0), 0); - } - - /* When written everything correctly: reset 'modified'. Unless not - * writing to the original file and '+' is not in 'cpoptions'. */ - if (reset_changed && whole && !append - && !write_info.bw_conv_error - && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL) - ) { - unchanged(buf, TRUE); - u_unchanged(buf); - u_update_save_nr(buf); - } - - /* - * If written to the current file, update the timestamp of the swap file - * and reset the BF_WRITE_MASK flags. Also sets buf->b_mtime. - */ - if (overwriting) { - ml_timestamp(buf); - if (append) - buf->b_flags &= ~BF_NEW; - else - buf->b_flags &= ~BF_WRITE_MASK; - } - - /* - * If we kept a backup until now, and we are in patch mode, then we make - * the backup file our 'original' file. - */ - if (*p_pm && dobackup) { - char *org = (char *)modname(fname, p_pm, FALSE); - - if (backup != NULL) { - /* - * If the original file does not exist yet - * the current backup file becomes the original file - */ - if (org == NULL) - EMSG(_("E205: Patchmode: can't save original file")); - else if (!os_file_exists((char_u *)org)) { - vim_rename(backup, (char_u *)org); - free(backup); /* don't delete the file */ - backup = NULL; -#ifdef UNIX - set_file_time((char_u *)org, - file_info_old.stat.st_atim.tv_sec, - file_info_old.stat.st_mtim.tv_sec); -#endif - } - } - /* - * If there is no backup file, remember that a (new) file was - * created. - */ - else { - int empty_fd; - - if (org == NULL - || (empty_fd = mch_open(org, - O_CREAT | O_EXCL | O_NOFOLLOW, - perm < 0 ? 0666 : (perm & 0777))) < 0) - EMSG(_("E206: patchmode: can't touch empty original file")); - else - close(empty_fd); - } - if (org != NULL) { - os_setperm((char_u *)org, os_getperm(fname) & 0777); - free(org); - } - } - - /* - * Remove the backup unless 'backup' option is set - */ - if (!p_bk && backup != NULL && os_remove((char *)backup) != 0) - EMSG(_("E207: Can't delete backup file")); - - - goto nofail; - - /* - * Finish up. We get here either after failure or success. - */ -fail: - --no_wait_return; /* may wait for return now */ -nofail: - - /* Done saving, we accept changed buffer warnings again */ - buf->b_saving = FALSE; - - free(backup); - if (buffer != smallbuf) - free(buffer); - free(fenc_tofree); - free(write_info.bw_conv_buf); -# ifdef USE_ICONV - if (write_info.bw_iconv_fd != (iconv_t)-1) { - iconv_close(write_info.bw_iconv_fd); - write_info.bw_iconv_fd = (iconv_t)-1; - } -# endif -#ifdef HAVE_ACL - mch_free_acl(acl); -#endif - - if (errmsg != NULL) { - int numlen = errnum != NULL ? (int)STRLEN(errnum) : 0; - - attr = hl_attr(HLF_E); /* set highlight for error messages */ - msg_add_fname(buf, -#ifndef UNIX - sfname -#else - fname -#endif - ); /* put file name in IObuff with quotes */ - if (STRLEN(IObuff) + STRLEN(errmsg) + numlen >= IOSIZE) - IObuff[IOSIZE - STRLEN(errmsg) - numlen - 1] = NUL; - /* If the error message has the form "is ...", put the error number in - * front of the file name. */ - if (errnum != NULL) { - STRMOVE(IObuff + numlen, IObuff); - memmove(IObuff, errnum, (size_t)numlen); - } - STRCAT(IObuff, errmsg); - emsg(IObuff); - if (errmsg_allocated) - free(errmsg); - - retval = FAIL; - if (end == 0) { - MSG_PUTS_ATTR(_("\nWARNING: Original file may be lost or damaged\n"), - attr | MSG_HIST); - MSG_PUTS_ATTR(_( - "don't quit the editor until the file is successfully written!"), - attr | MSG_HIST); - - /* Update the timestamp to avoid an "overwrite changed file" - * prompt when writing again. */ - if (os_get_file_info((char *)fname, &file_info_old)) { - buf_store_file_info(buf, &file_info_old); - buf->b_mtime_read = buf->b_mtime; - } - } - } - msg_scroll = msg_save; - - /* - * When writing the whole file and 'undofile' is set, also write the undo - * file. - */ - if (retval == OK && write_undo_file) { - char_u hash[UNDO_HASH_SIZE]; - - sha256_finish(&sha_ctx, hash); - u_write_undo(NULL, FALSE, buf, hash); - } - - if (!should_abort(retval)) { - aco_save_T aco; - - curbuf->b_no_eol_lnum = 0; /* in case it was set by the previous read */ - - /* - * Apply POST autocommands. - * Careful: The autocommands may call buf_write() recursively! - */ - aucmd_prepbuf(&aco, buf); - - if (append) - apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname, - FALSE, curbuf, eap); - else if (filtering) - apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname, - FALSE, curbuf, eap); - else if (reset_changed && whole) - apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname, - FALSE, curbuf, eap); - else - apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname, - FALSE, curbuf, eap); - - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - - if (aborting()) /* autocmds may abort script processing */ - retval = FALSE; - } - - got_int |= prev_got_int; - - return retval; -} - -/* - * Set the name of the current buffer. Use when the buffer doesn't have a - * name and a ":r" or ":w" command with a file name is used. - */ -static int set_rw_fname(char_u *fname, char_u *sfname) -{ - buf_T *buf = curbuf; - - /* It's like the unnamed buffer is deleted.... */ - if (curbuf->b_p_bl) - apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf); - if (aborting()) /* autocmds may abort script processing */ - return FAIL; - if (curbuf != buf) { - /* We are in another buffer now, don't do the renaming. */ - EMSG(_(e_auchangedbuf)); - return FAIL; - } - - if (setfname(curbuf, fname, sfname, FALSE) == OK) - curbuf->b_flags |= BF_NOTEDITED; - - /* ....and a new named one is created */ - apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, curbuf); - if (curbuf->b_p_bl) - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf); - if (aborting()) /* autocmds may abort script processing */ - return FAIL; - - /* Do filetype detection now if 'filetype' is empty. */ - if (*curbuf->b_p_ft == NUL) { - if (au_has_group((char_u *)"filetypedetect")) - (void)do_doautocmd((char_u *)"filetypedetect BufRead", FALSE); - do_modelines(0); - } - - return OK; -} - -/* - * Put file name into IObuff with quotes. - */ -void msg_add_fname(buf_T *buf, char_u *fname) -{ - if (fname == NULL) - fname = (char_u *)"-stdin-"; - home_replace(buf, fname, IObuff + 1, IOSIZE - 4, TRUE); - IObuff[0] = '"'; - STRCAT(IObuff, "\" "); -} - -/* - * Append message for text mode to IObuff. - * Return TRUE if something appended. - */ -static int msg_add_fileformat(int eol_type) -{ -#ifndef USE_CRNL - if (eol_type == EOL_DOS) { - STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]")); - return TRUE; - } -#endif -#ifndef USE_CR - if (eol_type == EOL_MAC) { - STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]")); - return TRUE; - } -#endif -#if defined(USE_CRNL) || defined(USE_CR) - if (eol_type == EOL_UNIX) { - STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]")); - return TRUE; - } -#endif - return FALSE; -} - -/* - * Append line and character count to IObuff. - */ -void msg_add_lines(int insert_space, long lnum, off_t nchars) -{ - char_u *p; - - p = IObuff + STRLEN(IObuff); - - if (insert_space) - *p++ = ' '; - if (shortmess(SHM_LINES)) { - sprintf((char *)p, "%" PRId64 "L, %" PRId64 "C", - (int64_t)lnum, (int64_t)nchars); - } - else { - if (lnum == 1) - STRCPY(p, _("1 line, ")); - else - sprintf((char *)p, _("%" PRId64 " lines, "), (int64_t)lnum); - p += STRLEN(p); - if (nchars == 1) - STRCPY(p, _("1 character")); - else { - sprintf((char *)p, _("%" PRId64 " characters"), (int64_t)nchars); - } - } -} - -/* - * Append message for missing line separator to IObuff. - */ -static void msg_add_eol(void) -{ - STRCAT(IObuff, - shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]")); -} - -/* - * Check modification time of file, before writing to it. - * The size isn't checked, because using a tool like "gzip" takes care of - * using the same timestamp but can't set the size. - */ -static int check_mtime(buf_T *buf, FileInfo *file_info) -{ - if (buf->b_mtime_read != 0 - && time_differs((long)file_info->stat.st_mtim.tv_sec, - buf->b_mtime_read)) { - msg_scroll = TRUE; /* don't overwrite messages here */ - msg_silent = 0; /* must give this prompt */ - /* don't use emsg() here, don't want to flush the buffers */ - MSG_ATTR(_("WARNING: The file has been changed since reading it!!!"), - hl_attr(HLF_E)); - if (ask_yesno((char_u *)_("Do you really want to write to it"), - TRUE) == 'n') - return FAIL; - msg_scroll = FALSE; /* always overwrite the file message now */ - } - return OK; -} - -static int time_differs(long t1, long t2) -{ -#if defined(__linux__) || defined(MSWIN) - /* On a FAT filesystem, esp. under Linux, there are only 5 bits to store - * the seconds. Since the roundoff is done when flushing the inode, the - * time may change unexpectedly by one second!!! */ - return t1 - t2 > 1 || t2 - t1 > 1; -#else - return t1 != t2; -#endif -} - -/* - * Call write() to write a number of bytes to the file. - * Handles encryption and 'encoding' conversion. - * - * Return FAIL for failure, OK otherwise. - */ -static int buf_write_bytes(struct bw_info *ip) -{ - int wlen; - char_u *buf = ip->bw_buf; /* data to write */ - int len = ip->bw_len; /* length of data */ -#ifdef HAS_BW_FLAGS - int flags = ip->bw_flags; /* extra flags */ -#endif - - /* - * Skip conversion when writing the crypt magic number or the BOM. - */ - if (!(flags & FIO_NOCONVERT)) { - char_u *p; - unsigned c; - int n; - - if (flags & FIO_UTF8) { - /* - * Convert latin1 in the buffer to UTF-8 in the file. - */ - p = ip->bw_conv_buf; /* translate to buffer */ - for (wlen = 0; wlen < len; ++wlen) - p += utf_char2bytes(buf[wlen], p); - buf = ip->bw_conv_buf; - len = (int)(p - ip->bw_conv_buf); - } else if (flags & (FIO_UCS4 | FIO_UTF16 | FIO_UCS2 | FIO_LATIN1)) { - /* - * Convert UTF-8 bytes in the buffer to UCS-2, UCS-4, UTF-16 or - * Latin1 chars in the file. - */ - if (flags & FIO_LATIN1) - p = buf; /* translate in-place (can only get shorter) */ - else - p = ip->bw_conv_buf; /* translate to buffer */ - for (wlen = 0; wlen < len; wlen += n) { - if (wlen == 0 && ip->bw_restlen != 0) { - int l; - - /* Use remainder of previous call. Append the start of - * buf[] to get a full sequence. Might still be too - * short! */ - l = CONV_RESTLEN - ip->bw_restlen; - if (l > len) - l = len; - memmove(ip->bw_rest + ip->bw_restlen, buf, (size_t)l); - n = utf_ptr2len_len(ip->bw_rest, ip->bw_restlen + l); - if (n > ip->bw_restlen + len) { - /* We have an incomplete byte sequence at the end to - * be written. We can't convert it without the - * remaining bytes. Keep them for the next call. */ - if (ip->bw_restlen + len > CONV_RESTLEN) - return FAIL; - ip->bw_restlen += len; - break; - } - if (n > 1) - c = utf_ptr2char(ip->bw_rest); - else - c = ip->bw_rest[0]; - if (n >= ip->bw_restlen) { - n -= ip->bw_restlen; - ip->bw_restlen = 0; - } else { - ip->bw_restlen -= n; - memmove(ip->bw_rest, ip->bw_rest + n, - (size_t)ip->bw_restlen); - n = 0; - } - } else { - n = utf_ptr2len_len(buf + wlen, len - wlen); - if (n > len - wlen) { - /* We have an incomplete byte sequence at the end to - * be written. We can't convert it without the - * remaining bytes. Keep them for the next call. */ - if (len - wlen > CONV_RESTLEN) - return FAIL; - ip->bw_restlen = len - wlen; - memmove(ip->bw_rest, buf + wlen, - (size_t)ip->bw_restlen); - break; - } - if (n > 1) - c = utf_ptr2char(buf + wlen); - else - c = buf[wlen]; - } - - if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error) { - ip->bw_conv_error = TRUE; - ip->bw_conv_error_lnum = ip->bw_start_lnum; - } - if (c == NL) - ++ip->bw_start_lnum; - } - if (flags & FIO_LATIN1) - len = (int)(p - buf); - else { - buf = ip->bw_conv_buf; - len = (int)(p - ip->bw_conv_buf); - } - } - - -# ifdef MACOS_CONVERT - else if (flags & FIO_MACROMAN) { - /* - * Convert UTF-8 or latin1 to Apple MacRoman. - */ - char_u *from; - size_t fromlen; - - if (ip->bw_restlen > 0) { - /* Need to concatenate the remainder of the previous call and - * the bytes of the current call. Use the end of the - * conversion buffer for this. */ - fromlen = len + ip->bw_restlen; - from = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; - memmove(from, ip->bw_rest, (size_t)ip->bw_restlen); - memmove(from + ip->bw_restlen, buf, (size_t)len); - } else { - from = buf; - fromlen = len; - } - - if (enc2macroman(from, fromlen, - ip->bw_conv_buf, &len, ip->bw_conv_buflen, - ip->bw_rest, &ip->bw_restlen) == FAIL) { - ip->bw_conv_error = TRUE; - return FAIL; - } - buf = ip->bw_conv_buf; - } -# endif - -# ifdef USE_ICONV - if (ip->bw_iconv_fd != (iconv_t)-1) { - const char *from; - size_t fromlen; - char *to; - size_t tolen; - - /* Convert with iconv(). */ - if (ip->bw_restlen > 0) { - char *fp; - - /* Need to concatenate the remainder of the previous call and - * the bytes of the current call. Use the end of the - * conversion buffer for this. */ - fromlen = len + ip->bw_restlen; - fp = (char *)ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; - memmove(fp, ip->bw_rest, (size_t)ip->bw_restlen); - memmove(fp + ip->bw_restlen, buf, (size_t)len); - from = fp; - tolen = ip->bw_conv_buflen - fromlen; - } else { - from = (const char *)buf; - fromlen = len; - tolen = ip->bw_conv_buflen; - } - to = (char *)ip->bw_conv_buf; - - if (ip->bw_first) { - size_t save_len = tolen; - - /* output the initial shift state sequence */ - (void)iconv(ip->bw_iconv_fd, NULL, NULL, &to, &tolen); - - /* There is a bug in iconv() on Linux (which appears to be - * wide-spread) which sets "to" to NULL and messes up "tolen". - */ - if (to == NULL) { - to = (char *)ip->bw_conv_buf; - tolen = save_len; - } - ip->bw_first = FALSE; - } - - /* - * If iconv() has an error or there is not enough room, fail. - */ - if ((iconv(ip->bw_iconv_fd, (void *)&from, &fromlen, &to, &tolen) - == (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL) - || fromlen > CONV_RESTLEN) { - ip->bw_conv_error = TRUE; - return FAIL; - } - - /* copy remainder to ip->bw_rest[] to be used for the next call. */ - if (fromlen > 0) - memmove(ip->bw_rest, (void *)from, fromlen); - ip->bw_restlen = (int)fromlen; - - buf = ip->bw_conv_buf; - len = (int)((char_u *)to - ip->bw_conv_buf); - } -# endif - } - - if (flags & FIO_ENCRYPTED) /* encrypt the data */ - crypt_encode(buf, len, buf); - - wlen = write_eintr(ip->bw_fd, buf, len); - return (wlen < len) ? FAIL : OK; -} - -/* - * Convert a Unicode character to bytes. - * Return TRUE for an error, FALSE when it's OK. - */ -static int -ucs2bytes ( - unsigned c, /* in: character */ - char_u **pp, /* in/out: pointer to result */ - int flags /* FIO_ flags */ -) -{ - char_u *p = *pp; - int error = FALSE; - int cc; - - - if (flags & FIO_UCS4) { - if (flags & FIO_ENDIAN_L) { - *p++ = c; - *p++ = (c >> 8); - *p++ = (c >> 16); - *p++ = (c >> 24); - } else { - *p++ = (c >> 24); - *p++ = (c >> 16); - *p++ = (c >> 8); - *p++ = c; - } - } else if (flags & (FIO_UCS2 | FIO_UTF16)) { - if (c >= 0x10000) { - if (flags & FIO_UTF16) { - /* Make two words, ten bits of the character in each. First - * word is 0xd800 - 0xdbff, second one 0xdc00 - 0xdfff */ - c -= 0x10000; - if (c >= 0x100000) - error = TRUE; - cc = ((c >> 10) & 0x3ff) + 0xd800; - if (flags & FIO_ENDIAN_L) { - *p++ = cc; - *p++ = ((unsigned)cc >> 8); - } else { - *p++ = ((unsigned)cc >> 8); - *p++ = cc; - } - c = (c & 0x3ff) + 0xdc00; - } else - error = TRUE; - } - if (flags & FIO_ENDIAN_L) { - *p++ = c; - *p++ = (c >> 8); - } else { - *p++ = (c >> 8); - *p++ = c; - } - } else { /* Latin1 */ - if (c >= 0x100) { - error = TRUE; - *p++ = 0xBF; - } else - *p++ = c; - } - - *pp = p; - return error; -} - -/* - * Return TRUE if file encoding "fenc" requires conversion from or to - * 'encoding'. - */ -static int need_conversion(char_u *fenc) -{ - int same_encoding; - int enc_flags; - int fenc_flags; - - if (*fenc == NUL || STRCMP(p_enc, fenc) == 0) { - same_encoding = TRUE; - fenc_flags = 0; - } else { - /* Ignore difference between "ansi" and "latin1", "ucs-4" and - * "ucs-4be", etc. */ - enc_flags = get_fio_flags(p_enc); - fenc_flags = get_fio_flags(fenc); - same_encoding = (enc_flags != 0 && fenc_flags == enc_flags); - } - if (same_encoding) { - /* Specified encoding matches with 'encoding'. This requires - * conversion when 'encoding' is Unicode but not UTF-8. */ - return enc_unicode != 0; - } - - /* Encodings differ. However, conversion is not needed when 'enc' is any - * Unicode encoding and the file is UTF-8. */ - return !(enc_utf8 && fenc_flags == FIO_UTF8); -} - -/* - * Check "ptr" for a unicode encoding and return the FIO_ flags needed for the - * internal conversion. - * if "ptr" is an empty string, use 'encoding'. - */ -static int get_fio_flags(char_u *ptr) -{ - int prop; - - if (*ptr == NUL) - ptr = p_enc; - - prop = enc_canon_props(ptr); - if (prop & ENC_UNICODE) { - if (prop & ENC_2BYTE) { - if (prop & ENC_ENDIAN_L) - return FIO_UCS2 | FIO_ENDIAN_L; - return FIO_UCS2; - } - if (prop & ENC_4BYTE) { - if (prop & ENC_ENDIAN_L) - return FIO_UCS4 | FIO_ENDIAN_L; - return FIO_UCS4; - } - if (prop & ENC_2WORD) { - if (prop & ENC_ENDIAN_L) - return FIO_UTF16 | FIO_ENDIAN_L; - return FIO_UTF16; - } - return FIO_UTF8; - } - if (prop & ENC_LATIN1) - return FIO_LATIN1; - /* must be ENC_DBCS, requires iconv() */ - return 0; -} - - - -/* - * Check for a Unicode BOM (Byte Order Mark) at the start of p[size]. - * "size" must be at least 2. - * Return the name of the encoding and set "*lenp" to the length. - * Returns NULL when no BOM found. - */ -static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags) -{ - char *name = NULL; - int len = 2; - - if (p[0] == 0xef && p[1] == 0xbb && size >= 3 && p[2] == 0xbf - && (flags == FIO_ALL || flags == FIO_UTF8 || flags == 0)) { - name = "utf-8"; /* EF BB BF */ - len = 3; - } else if (p[0] == 0xff && p[1] == 0xfe) { - if (size >= 4 && p[2] == 0 && p[3] == 0 - && (flags == FIO_ALL || flags == (FIO_UCS4 | FIO_ENDIAN_L))) { - name = "ucs-4le"; /* FF FE 00 00 */ - len = 4; - } else if (flags == (FIO_UCS2 | FIO_ENDIAN_L)) - name = "ucs-2le"; /* FF FE */ - else if (flags == FIO_ALL || flags == (FIO_UTF16 | FIO_ENDIAN_L)) - /* utf-16le is preferred, it also works for ucs-2le text */ - name = "utf-16le"; /* FF FE */ - } else if (p[0] == 0xfe && p[1] == 0xff - && (flags == FIO_ALL || flags == FIO_UCS2 || flags == - FIO_UTF16)) { - /* Default to utf-16, it works also for ucs-2 text. */ - if (flags == FIO_UCS2) - name = "ucs-2"; /* FE FF */ - else - name = "utf-16"; /* FE FF */ - } else if (size >= 4 && p[0] == 0 && p[1] == 0 && p[2] == 0xfe - && p[3] == 0xff && (flags == FIO_ALL || flags == FIO_UCS4)) { - name = "ucs-4"; /* 00 00 FE FF */ - len = 4; - } - - *lenp = len; - return (char_u *)name; -} - -/* - * Generate a BOM in "buf[4]" for encoding "name". - * Return the length of the BOM (zero when no BOM). - */ -static int make_bom(char_u *buf, char_u *name) -{ - int flags; - char_u *p; - - flags = get_fio_flags(name); - - /* Can't put a BOM in a non-Unicode file. */ - if (flags == FIO_LATIN1 || flags == 0) - return 0; - - if (flags == FIO_UTF8) { /* UTF-8 */ - buf[0] = 0xef; - buf[1] = 0xbb; - buf[2] = 0xbf; - return 3; - } - p = buf; - (void)ucs2bytes(0xfeff, &p, flags); - return (int)(p - buf); -} - -/* - * Shorten filenames for all buffers. - * When "force" is TRUE: Use full path from now on for files currently being - * edited, both for file name and swap file name. Try to shorten the file - * names a bit, if safe to do so. - * When "force" is FALSE: Only try to shorten absolute file names. - * For buffers that have buftype "nofile" or "scratch": never change the file - * name. - */ -void shorten_fnames(int force) -{ - char_u dirname[MAXPATHL]; - buf_T *buf; - char_u *p; - - os_dirname(dirname, MAXPATHL); - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (buf->b_fname != NULL - && !bt_nofile(buf) - && !path_with_url(buf->b_fname) - && (force - || buf->b_sfname == NULL - || path_is_absolute_path(buf->b_sfname))) { - free(buf->b_sfname); - buf->b_sfname = NULL; - p = path_shorten_fname(buf->b_ffname, dirname); - if (p != NULL) { - buf->b_sfname = vim_strsave(p); - buf->b_fname = buf->b_sfname; - } - if (p == NULL || buf->b_fname == NULL) - buf->b_fname = buf->b_ffname; - } - - /* Always make the swap file name a full path, a "nofile" buffer may - * also have a swap file. */ - mf_fullname(buf->b_ml.ml_mfp); - } - status_redraw_all(); - redraw_tabline = TRUE; -} - -/* - * add extension to file name - change path/fo.o.h to path/fo.o.h.ext - * - * Assumed that fname is a valid name found in the filesystem we assure that - * the return value is a different name and ends in 'ext'. - * "ext" MUST be at most 4 characters long if it starts with a dot, 3 - * characters otherwise. - * Space for the returned name is allocated, must be freed later. - * Returns NULL when out of memory. - */ -char_u * -modname ( - char_u *fname, - char_u *ext, - int prepend_dot /* may prepend a '.' to file name */ -) -{ - char_u *retval; - char_u *s; - char_u *e; - char_u *ptr; - int fnamelen, extlen; - - extlen = (int)STRLEN(ext); - - /* - * If there is no file name we must get the name of the current directory - * (we need the full path in case :cd is used). - */ - if (fname == NULL || *fname == NUL) { - retval = alloc((unsigned)(MAXPATHL + extlen + 3)); - if (os_dirname(retval, MAXPATHL) == FAIL || - (fnamelen = (int)STRLEN(retval)) == 0) { - free(retval); - return NULL; - } - if (!after_pathsep(retval, retval + fnamelen)) { - retval[fnamelen++] = PATHSEP; - retval[fnamelen] = NUL; - } - prepend_dot = FALSE; /* nothing to prepend a dot to */ - } else { - fnamelen = (int)STRLEN(fname); - retval = alloc((unsigned)(fnamelen + extlen + 3)); - STRCPY(retval, fname); - } - - /* - * search backwards until we hit a '/', '\' or ':'. - * Then truncate what is after the '/', '\' or ':' to BASENAMELEN characters. - */ - for (ptr = retval + fnamelen; ptr > retval; mb_ptr_back(retval, ptr)) { - if (vim_ispathsep(*ptr)) { - ++ptr; - break; - } - } - - /* the file name has at most BASENAMELEN characters. */ - if (STRLEN(ptr) > BASENAMELEN) - ptr[BASENAMELEN] = '\0'; - - s = ptr + STRLEN(ptr); - -#if defined(WIN3264) - /* - * If there is no file name, and the extension starts with '.', put a - * '_' before the dot, because just ".ext" may be invalid if it's on a - * FAT partition, and on HPFS it doesn't matter. - */ - else if ((fname == NULL || *fname == NUL) && *ext == '.') - *s++ = '_'; -#endif - - /* - * Append the extension. - * ext can start with '.' and cannot exceed 3 more characters. - */ - STRCPY(s, ext); - - /* - * Prepend the dot. - */ - if (prepend_dot && *(e = path_tail(retval)) != '.') { - STRMOVE(e + 1, e); - *e = '.'; - } - - /* - * Check that, after appending the extension, the file name is really - * different. - */ - if (fname != NULL && STRCMP(fname, retval) == 0) { - /* we search for a character that can be replaced by '_' */ - while (--s >= ptr) { - if (*s != '_') { - *s = '_'; - break; - } - } - if (s < ptr) /* fname was "________.", how tricky! */ - *ptr = 'v'; - } - return retval; -} - -/* - * Like fgets(), but if the file line is too long, it is truncated and the - * rest of the line is thrown away. Returns TRUE for end-of-file. - */ -int vim_fgets(char_u *buf, int size, FILE *fp) -{ - char *eof; -#define FGETS_SIZE 200 - char tbuf[FGETS_SIZE]; - - buf[size - 2] = NUL; -#ifdef USE_CR - eof = fgets_cr((char *)buf, size, fp); -#else - eof = fgets((char *)buf, size, fp); -#endif - if (buf[size - 2] != NUL && buf[size - 2] != '\n') { - buf[size - 1] = NUL; /* Truncate the line */ - - /* Now throw away the rest of the line: */ - do { - tbuf[FGETS_SIZE - 2] = NUL; -#ifdef USE_CR - ignoredp = fgets_cr((char *)tbuf, FGETS_SIZE, fp); -#else - ignoredp = fgets((char *)tbuf, FGETS_SIZE, fp); -#endif - } while (tbuf[FGETS_SIZE - 2] != NUL && tbuf[FGETS_SIZE - 2] != '\n'); - } - return eof == NULL; -} - -#if defined(USE_CR) || defined(PROTO) -/* - * Like vim_fgets(), but accept any line terminator: CR, CR-LF or LF. - * Returns TRUE for end-of-file. - * Only used for the Mac, because it's much slower than vim_fgets(). - */ -int tag_fgets(char_u *buf, int size, FILE *fp) -{ - int i = 0; - int c; - int eof = FALSE; - - for (;; ) { - c = fgetc(fp); - if (c == EOF) { - eof = TRUE; - break; - } - if (c == '\r') { - /* Always store a NL for end-of-line. */ - if (i < size - 1) - buf[i++] = '\n'; - c = fgetc(fp); - if (c != '\n') /* Macintosh format: single CR. */ - ungetc(c, fp); - break; - } - if (i < size - 1) - buf[i++] = c; - if (c == '\n') - break; - } - buf[i] = NUL; - return eof; -} -#endif - -/* - * os_rename() only works if both files are on the same file system, this - * function will (attempts to?) copy the file across if rename fails -- webb - * Return -1 for failure, 0 for success. - */ -int vim_rename(char_u *from, char_u *to) -{ - int fd_in; - int fd_out; - int n; - char *errmsg = NULL; - char *buffer; - long perm; -#ifdef HAVE_ACL - vim_acl_T acl; /* ACL from original file */ -#endif - bool use_tmp_file = false; - - /* - * When the names are identical, there is nothing to do. When they refer - * to the same file (ignoring case and slash/backslash differences) but - * the file name differs we need to go through a temp file. - */ - if (fnamecmp(from, to) == 0) { - if (p_fic && STRCMP(path_tail(from), path_tail(to)) != 0) - use_tmp_file = true; - else - return 0; - } - - // Fail if the "from" file doesn't exist. Avoids that "to" is deleted. - FileInfo from_info; - if (!os_get_file_info((char *)from, &from_info)) { - return -1; - } - - // It's possible for the source and destination to be the same file. - // This happens when "from" and "to" differ in case and are on a FAT32 - // filesystem. In that case go through a temp file name. - FileInfo to_info; - if (os_get_file_info((char *)to, &to_info) - && os_file_info_id_equal(&from_info, &to_info)) { - use_tmp_file = true; - } - - if (use_tmp_file) { - char_u tempname[MAXPATHL + 1]; - - /* - * Find a name that doesn't exist and is in the same directory. - * Rename "from" to "tempname" and then rename "tempname" to "to". - */ - if (STRLEN(from) >= MAXPATHL - 5) - return -1; - STRCPY(tempname, from); - for (n = 123; n < 99999; ++n) { - sprintf((char *)path_tail(tempname), "%d", n); - if (!os_file_exists(tempname)) { - if (os_rename(from, tempname) == OK) { - if (os_rename(tempname, to) == OK) - return 0; - /* Strange, the second step failed. Try moving the - * file back and return failure. */ - os_rename(tempname, from); - return -1; - } - /* If it fails for one temp name it will most likely fail - * for any temp name, give up. */ - return -1; - } - } - return -1; - } - - /* - * Delete the "to" file, this is required on some systems to make the - * os_rename() work, on other systems it makes sure that we don't have - * two files when the os_rename() fails. - */ - - os_remove((char *)to); - - /* - * First try a normal rename, return if it works. - */ - if (os_rename(from, to) == OK) - return 0; - - /* - * Rename() failed, try copying the file. - */ - perm = os_getperm(from); -#ifdef HAVE_ACL - /* For systems that support ACL: get the ACL from the original file. */ - acl = mch_get_acl(from); -#endif - fd_in = mch_open((char *)from, O_RDONLY, 0); - if (fd_in == -1) { -#ifdef HAVE_ACL - mch_free_acl(acl); -#endif - return -1; - } - - /* Create the new file with same permissions as the original. */ - fd_out = mch_open((char *)to, - O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, (int)perm); - if (fd_out == -1) { - close(fd_in); -#ifdef HAVE_ACL - mch_free_acl(acl); -#endif - return -1; - } - - // Avoid xmalloc() here as vim_rename() is called by buf_write() when neovim - // is `preserve_exit()`ing. - buffer = try_malloc(BUFSIZE); - if (buffer == NULL) { - close(fd_out); - close(fd_in); -#ifdef HAVE_ACL - mch_free_acl(acl); -#endif - return -1; - } - - while ((n = read_eintr(fd_in, buffer, BUFSIZE)) > 0) - if (write_eintr(fd_out, buffer, n) != n) { - errmsg = _("E208: Error writing to \"%s\""); - break; - } - - free(buffer); - close(fd_in); - if (close(fd_out) < 0) - errmsg = _("E209: Error closing \"%s\""); - if (n < 0) { - errmsg = _("E210: Error reading \"%s\""); - to = from; - } -#ifndef UNIX /* for Unix mch_open() already set the permission */ - os_setperm(to, perm); -#endif -#ifdef HAVE_ACL - mch_set_acl(to, acl); - mch_free_acl(acl); -#endif -#ifdef HAVE_SELINUX - mch_copy_sec(from, to); -#endif - if (errmsg != NULL) { - EMSG2(errmsg, to); - return -1; - } - os_remove((char *)from); - return 0; -} - -static int already_warned = FALSE; - -/* - * Check if any not hidden buffer has been changed. - * Postpone the check if there are characters in the stuff buffer, a global - * command is being executed, a mapping is being executed or an autocommand is - * busy. - * Returns TRUE if some message was written (screen should be redrawn and - * cursor positioned). - */ -int -check_timestamps ( - int focus /* called for GUI focus event */ -) -{ - buf_T *buf; - int didit = 0; - int n; - - /* Don't check timestamps while system() or another low-level function may - * cause us to lose and gain focus. */ - if (no_check_timestamps > 0) - return FALSE; - - /* Avoid doing a check twice. The OK/Reload dialog can cause a focus - * event and we would keep on checking if the file is steadily growing. - * Do check again after typing something. */ - if (focus && did_check_timestamps) { - need_check_timestamps = TRUE; - return FALSE; - } - - if (!stuff_empty() || global_busy || !typebuf_typed() - || autocmd_busy || curbuf_lock > 0 || allbuf_lock > 0 - ) - need_check_timestamps = TRUE; /* check later */ - else { - ++no_wait_return; - did_check_timestamps = TRUE; - already_warned = FALSE; - for (buf = firstbuf; buf != NULL; ) { - /* Only check buffers in a window. */ - if (buf->b_nwindows > 0) { - n = buf_check_timestamp(buf, focus); - if (didit < n) - didit = n; - if (n > 0 && !buf_valid(buf)) { - /* Autocommands have removed the buffer, start at the - * first one again. */ - buf = firstbuf; - continue; - } - } - buf = buf->b_next; - } - --no_wait_return; - need_check_timestamps = FALSE; - if (need_wait_return && didit == 2) { - /* make sure msg isn't overwritten */ - msg_puts((char_u *)"\n"); - out_flush(); - } - } - return didit; -} - -/* - * Move all the lines from buffer "frombuf" to buffer "tobuf". - * Return OK or FAIL. When FAIL "tobuf" is incomplete and/or "frombuf" is not - * empty. - */ -static int move_lines(buf_T *frombuf, buf_T *tobuf) -{ - buf_T *tbuf = curbuf; - int retval = OK; - linenr_T lnum; - char_u *p; - - /* Copy the lines in "frombuf" to "tobuf". */ - curbuf = tobuf; - for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; ++lnum) { - p = vim_strsave(ml_get_buf(frombuf, lnum, FALSE)); - if (p == NULL || ml_append(lnum - 1, p, 0, FALSE) == FAIL) { - free(p); - retval = FAIL; - break; - } - free(p); - } - - /* Delete all the lines in "frombuf". */ - if (retval != FAIL) { - curbuf = frombuf; - for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; --lnum) - if (ml_delete(lnum, FALSE) == FAIL) { - /* Oops! We could try putting back the saved lines, but that - * might fail again... */ - retval = FAIL; - break; - } - } - - curbuf = tbuf; - return retval; -} - -/* - * Check if buffer "buf" has been changed. - * Also check if the file for a new buffer unexpectedly appeared. - * return 1 if a changed buffer was found. - * return 2 if a message has been displayed. - * return 0 otherwise. - */ -int -buf_check_timestamp ( - buf_T *buf, - int focus /* called for GUI focus event */ -) -{ - int retval = 0; - char_u *path; - char_u *tbuf; - char *mesg = NULL; - char *mesg2 = ""; - int helpmesg = FALSE; - int reload = FALSE; - int can_reload = FALSE; - off_t orig_size = buf->b_orig_size; - int orig_mode = buf->b_orig_mode; - static int busy = FALSE; - int n; - char_u *s; - char *reason; - - /* If there is no file name, the buffer is not loaded, 'buftype' is - * set, we are in the middle of a save or being called recursively: ignore - * this buffer. */ - if (buf->b_ffname == NULL - || buf->b_ml.ml_mfp == NULL - || *buf->b_p_bt != NUL - || buf->b_saving - || busy - ) - return 0; - - FileInfo file_info; - bool file_info_ok; - if (!(buf->b_flags & BF_NOTEDITED) - && buf->b_mtime != 0 - && (!(file_info_ok = os_get_file_info((char *)buf->b_ffname, &file_info)) - || time_differs((long)file_info.stat.st_mtim.tv_sec, buf->b_mtime) - || (int)file_info.stat.st_mode != buf->b_orig_mode - )) { - retval = 1; - - /* set b_mtime to stop further warnings (e.g., when executing - * FileChangedShell autocmd) */ - if (!file_info_ok) { - buf->b_mtime = 0; - buf->b_orig_size = 0; - buf->b_orig_mode = 0; - } else { - buf_store_file_info(buf, &file_info); - } - - /* Don't do anything for a directory. Might contain the file - * explorer. */ - if (os_isdir(buf->b_fname)) - ; - - /* - * If 'autoread' is set, the buffer has no changes and the file still - * exists, reload the buffer. Use the buffer-local option value if it - * was set, the global option value otherwise. - */ - else if ((buf->b_p_ar >= 0 ? buf->b_p_ar : p_ar) - && !bufIsChanged(buf) && file_info_ok) - reload = TRUE; - else { - if (!file_info_ok) - reason = "deleted"; - else if (bufIsChanged(buf)) - reason = "conflict"; - else if (orig_size != buf->b_orig_size || buf_contents_changed(buf)) - reason = "changed"; - else if (orig_mode != buf->b_orig_mode) - reason = "mode"; - else - reason = "time"; - - /* - * Only give the warning if there are no FileChangedShell - * autocommands. - * Avoid being called recursively by setting "busy". - */ - busy = TRUE; - set_vim_var_string(VV_FCS_REASON, (char_u *)reason, -1); - set_vim_var_string(VV_FCS_CHOICE, (char_u *)"", -1); - ++allbuf_lock; - n = apply_autocmds(EVENT_FILECHANGEDSHELL, - buf->b_fname, buf->b_fname, FALSE, buf); - --allbuf_lock; - busy = FALSE; - if (n) { - if (!buf_valid(buf)) - EMSG(_("E246: FileChangedShell autocommand deleted buffer")); - s = get_vim_var_str(VV_FCS_CHOICE); - if (STRCMP(s, "reload") == 0 && *reason != 'd') - reload = TRUE; - else if (STRCMP(s, "ask") == 0) - n = FALSE; - else - return 2; - } - if (!n) { - if (*reason == 'd') - mesg = _("E211: File \"%s\" no longer available"); - else { - helpmesg = TRUE; - can_reload = TRUE; - /* - * Check if the file contents really changed to avoid - * giving a warning when only the timestamp was set (e.g., - * checked out of CVS). Always warn when the buffer was - * changed. - */ - if (reason[2] == 'n') { - mesg = _( - "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as well"); - mesg2 = _("See \":help W12\" for more info."); - } else if (reason[1] == 'h') { - mesg = _( - "W11: Warning: File \"%s\" has changed since editing started"); - mesg2 = _("See \":help W11\" for more info."); - } else if (*reason == 'm') { - mesg = _( - "W16: Warning: Mode of file \"%s\" has changed since editing started"); - mesg2 = _("See \":help W16\" for more info."); - } else - /* Only timestamp changed, store it to avoid a warning - * in check_mtime() later. */ - buf->b_mtime_read = buf->b_mtime; - } - } - } - - } else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W) - && os_file_exists(buf->b_ffname)) { - retval = 1; - mesg = _("W13: Warning: File \"%s\" has been created after editing started"); - buf->b_flags |= BF_NEW_W; - can_reload = TRUE; - } - - if (mesg != NULL) { - path = home_replace_save(buf, buf->b_fname); - if (path != NULL) { - if (!helpmesg) - mesg2 = ""; - tbuf = alloc((unsigned)(STRLEN(path) + STRLEN(mesg) - + STRLEN(mesg2) + 2)); - sprintf((char *)tbuf, mesg, path); - /* Set warningmsg here, before the unimportant and output-specific - * mesg2 has been appended. */ - set_vim_var_string(VV_WARNINGMSG, tbuf, -1); - if (can_reload) { - if (*mesg2 != NUL) { - STRCAT(tbuf, "\n"); - STRCAT(tbuf, mesg2); - } - if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), tbuf, - (char_u *)_("&OK\n&Load File"), 1, NULL, TRUE) == 2) - reload = TRUE; - } else if (State > NORMAL_BUSY || (State & CMDLINE) || - already_warned) { - if (*mesg2 != NUL) { - STRCAT(tbuf, "; "); - STRCAT(tbuf, mesg2); - } - EMSG(tbuf); - retval = 2; - } else { - if (!autocmd_busy) { - msg_start(); - msg_puts_attr(tbuf, hl_attr(HLF_E) + MSG_HIST); - if (*mesg2 != NUL) - msg_puts_attr((char_u *)mesg2, - hl_attr(HLF_W) + MSG_HIST); - msg_clr_eos(); - (void)msg_end(); - if (emsg_silent == 0) { - out_flush(); - /* give the user some time to think about it */ - ui_delay(1000L, TRUE); - - /* don't redraw and erase the message */ - redraw_cmdline = FALSE; - } - } - already_warned = TRUE; - } - - free(path); - free(tbuf); - } - } - - if (reload) { - /* Reload the buffer. */ - buf_reload(buf, orig_mode); - if (buf->b_p_udf && buf->b_ffname != NULL) { - char_u hash[UNDO_HASH_SIZE]; - buf_T *save_curbuf = curbuf; - - /* Any existing undo file is unusable, write it now. */ - curbuf = buf; - u_compute_hash(hash); - u_write_undo(NULL, FALSE, buf, hash); - curbuf = save_curbuf; - } - } - - /* Trigger FileChangedShell when the file was changed in any way. */ - if (buf_valid(buf) && retval != 0) - (void)apply_autocmds(EVENT_FILECHANGEDSHELLPOST, - buf->b_fname, buf->b_fname, FALSE, buf); - - return retval; -} - -/* - * Reload a buffer that is already loaded. - * Used when the file was changed outside of Vim. - * "orig_mode" is buf->b_orig_mode before the need for reloading was detected. - * buf->b_orig_mode may have been reset already. - */ -void buf_reload(buf_T *buf, int orig_mode) -{ - exarg_T ea; - pos_T old_cursor; - linenr_T old_topline; - int old_ro = buf->b_p_ro; - buf_T *savebuf; - int saved = OK; - aco_save_T aco; - int flags = READ_NEW; - - /* set curwin/curbuf for "buf" and save some things */ - aucmd_prepbuf(&aco, buf); - - // We only want to read the text from the file, not reset the syntax - // highlighting, clear marks, diff status, etc. Force the fileformat and - // encoding to be the same. - - prep_exarg(&ea, buf); - old_cursor = curwin->w_cursor; - old_topline = curwin->w_topline; - - if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) { - /* Save all the text, so that the reload can be undone. - * Sync first so that this is a separate undo-able action. */ - u_sync(FALSE); - saved = u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, TRUE); - flags |= READ_KEEP_UNDO; - } - - /* - * To behave like when a new file is edited (matters for - * BufReadPost autocommands) we first need to delete the current - * buffer contents. But if reading the file fails we should keep - * the old contents. Can't use memory only, the file might be - * too big. Use a hidden buffer to move the buffer contents to. - */ - if (bufempty() || saved == FAIL) - savebuf = NULL; - else { - /* Allocate a buffer without putting it in the buffer list. */ - savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); - if (savebuf != NULL && buf == curbuf) { - /* Open the memline. */ - curbuf = savebuf; - curwin->w_buffer = savebuf; - saved = ml_open(curbuf); - curbuf = buf; - curwin->w_buffer = buf; - } - if (savebuf == NULL || saved == FAIL || buf != curbuf - || move_lines(buf, savebuf) == FAIL) { - EMSG2(_("E462: Could not prepare for reloading \"%s\""), - buf->b_fname); - saved = FAIL; - } - } - - if (saved == OK) { - curbuf->b_flags |= BF_CHECK_RO; /* check for RO again */ - keep_filetype = TRUE; /* don't detect 'filetype' */ - if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, - (linenr_T)0, - (linenr_T)MAXLNUM, &ea, flags) == FAIL) { - if (!aborting()) - EMSG2(_("E321: Could not reload \"%s\""), buf->b_fname); - if (savebuf != NULL && buf_valid(savebuf) && buf == curbuf) { - /* Put the text back from the save buffer. First - * delete any lines that readfile() added. */ - while (!bufempty()) - if (ml_delete(buf->b_ml.ml_line_count, FALSE) == FAIL) - break; - (void)move_lines(savebuf, buf); - } - } else if (buf == curbuf) { /* "buf" still valid */ - /* Mark the buffer as unmodified and free undo info. */ - unchanged(buf, TRUE); - if ((flags & READ_KEEP_UNDO) == 0) { - u_blockfree(buf); - u_clearall(buf); - } else { - /* Mark all undo states as changed. */ - u_unchanged(curbuf); - } - } - } - free(ea.cmd); - - if (savebuf != NULL && buf_valid(savebuf)) - wipe_buffer(savebuf, FALSE); - - /* Invalidate diff info if necessary. */ - diff_invalidate(curbuf); - - /* Restore the topline and cursor position and check it (lines may - * have been removed). */ - if (old_topline > curbuf->b_ml.ml_line_count) - curwin->w_topline = curbuf->b_ml.ml_line_count; - else - curwin->w_topline = old_topline; - curwin->w_cursor = old_cursor; - check_cursor(); - update_topline(); - keep_filetype = FALSE; - { - win_T *wp; - tabpage_T *tp; - - /* Update folds unless they are defined manually. */ - FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_buffer == curwin->w_buffer - && !foldmethodIsManual(wp)) - foldUpdateAll(wp); - } - /* If the mode didn't change and 'readonly' was set, keep the old - * value; the user probably used the ":view" command. But don't - * reset it, might have had a read error. */ - if (orig_mode == curbuf->b_orig_mode) - curbuf->b_p_ro |= old_ro; - - /* Modelines must override settings done by autocommands. */ - do_modelines(0); - - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - /* Careful: autocommands may have made "buf" invalid! */ -} - -void buf_store_file_info(buf_T *buf, FileInfo *file_info) -{ - buf->b_mtime = (long)file_info->stat.st_mtim.tv_sec; - buf->b_orig_size = file_info->stat.st_size; - buf->b_orig_mode = (int)file_info->stat.st_mode; -} - -/* - * Adjust the line with missing eol, used for the next write. - * Used for do_filter(), when the input lines for the filter are deleted. - */ -void write_lnum_adjust(linenr_T offset) -{ - if (curbuf->b_no_eol_lnum != 0) /* only if there is a missing eol */ - curbuf->b_no_eol_lnum += offset; -} - -#if defined(TEMPDIRNAMES) || defined(PROTO) -static long temp_count = 0; /* Temp filename counter. */ - -/* - * Delete the temp directory and all files it contains. - */ -void vim_deltempdir(void) -{ - char_u **files; - int file_count; - int i; - - if (vim_tempdir != NULL) { - sprintf((char *)NameBuff, "%s*", vim_tempdir); - if (gen_expand_wildcards(1, &NameBuff, &file_count, &files, - EW_DIR|EW_FILE|EW_SILENT) == OK) { - for (i = 0; i < file_count; ++i) - os_remove((char *)files[i]); - FreeWild(file_count, files); - } - path_tail(NameBuff)[-1] = NUL; - os_rmdir((char *)NameBuff); - - free(vim_tempdir); - vim_tempdir = NULL; - } -} - -#endif - -#ifdef TEMPDIRNAMES -/* - * Directory "tempdir" was created. Expand this name to a full path and put - * it in "vim_tempdir". This avoids that using ":cd" would confuse us. - * "tempdir" must be no longer than MAXPATHL. - */ -static void vim_settempdir(char_u *tempdir) -{ - char_u *buf = verbose_try_malloc((size_t)MAXPATHL + 2); - if (buf) { - if (vim_FullName(tempdir, buf, MAXPATHL, FALSE) == FAIL) - STRCPY(buf, tempdir); - add_pathsep(buf); - vim_tempdir = vim_strsave(buf); - free(buf); - } -} -#endif - -/* - * vim_tempname(): Return a unique name that can be used for a temp file. - * - * The temp file is NOT created. - * - * The returned pointer is to allocated memory. - * The returned pointer is NULL if no valid name was found. - */ -char_u * -vim_tempname ( - int extra_char /* char to use in the name instead of '?' */ -) -{ -#ifdef USE_TMPNAM - char_u itmp[L_tmpnam]; /* use tmpnam() */ -#else - char_u itmp[TEMPNAMELEN]; -#endif - -#ifdef TEMPDIRNAMES - static char *(tempdirs[]) = {TEMPDIRNAMES}; - int i; - - /* - * This will create a directory for private use by this instance of Vim. - * This is done once, and the same directory is used for all temp files. - * This method avoids security problems because of symlink attacks et al. - * It's also a bit faster, because we only need to check for an existing - * file when creating the directory and not for each temp file. - */ - if (vim_tempdir == NULL) { - /* - * Try the entries in TEMPDIRNAMES to create the temp directory. - */ - for (i = 0; i < (int)(sizeof(tempdirs) / sizeof(char *)); ++i) { -# ifndef HAVE_MKDTEMP - size_t itmplen; - long nr; - long off; -# endif - - /* expand $TMP, leave room for "/v1100000/999999999" */ - expand_env((char_u *)tempdirs[i], itmp, TEMPNAMELEN - 20); - if (os_isdir(itmp)) { /* directory exists */ - add_pathsep(itmp); - -# ifdef HAVE_MKDTEMP - /* Leave room for filename */ - STRCAT(itmp, "vXXXXXX"); - if (mkdtemp((char *)itmp) != NULL) - vim_settempdir(itmp); -# else - /* Get an arbitrary number of up to 6 digits. When it's - * unlikely that it already exists it will be faster, - * otherwise it doesn't matter. The use of mkdir() avoids any - * security problems because of the predictable number. */ - nr = (os_get_pid() + (long)time(NULL)) % 1000000L; - itmplen = STRLEN(itmp); - - /* Try up to 10000 different values until we find a name that - * doesn't exist. */ - for (off = 0; off < 10000L; ++off) { - int r; -# if defined(UNIX) - mode_t umask_save; -# endif - - sprintf((char *)itmp + itmplen, "v%" PRId64, (int64_t)(nr + off)); -# ifndef EEXIST - /* If mkdir() does not set errno to EEXIST, check for - * existing file here. There is a race condition then, - * although it's fail-safe. */ - if (os_file_exists(itmp)) - continue; -# endif -# if defined(UNIX) - /* Make sure the umask doesn't remove the executable bit. - * "repl" has been reported to use "177". */ - umask_save = umask(077); -# endif - r = os_mkdir((char *)itmp, 0700); -# if defined(UNIX) - (void)umask(umask_save); -# endif - if (r == 0) { - vim_settempdir(itmp); - break; - } -# ifdef EEXIST - /* If the mkdir() didn't fail because the file/dir exists, - * we probably can't create any dir here, try another - * place. */ - if (errno != EEXIST) -# endif - break; - } -# endif /* HAVE_MKDTEMP */ - if (vim_tempdir != NULL) - break; - } - } - } - - if (vim_tempdir != NULL) { - /* There is no need to check if the file exists, because we own the - * directory and nobody else creates a file in it. */ - sprintf((char *)itmp, "%s%" PRId64, vim_tempdir, (int64_t)temp_count++); - return vim_strsave(itmp); - } - - return NULL; - -#else /* TEMPDIRNAMES */ - - -# ifdef USE_TMPNAM - char_u *p; - - /* tmpnam() will make its own name */ - p = tmpnam((char *)itmp); - if (p == NULL || *p == NUL) - return NULL; -# else - char_u *p; - - STRCPY(itmp, TEMPNAME); - if ((p = vim_strchr(itmp, '?')) != NULL) - *p = extra_char; - if (mktemp((char *)itmp) == NULL) - return NULL; -# endif - - return vim_strsave(itmp); -#endif /* TEMPDIRNAMES */ -} - -#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) -/* - * Convert all backslashes in fname to forward slashes in-place. - */ -void forward_slash(char_u *fname) -{ - char_u *p; - - for (p = fname; *p != NUL; ++p) - /* The Big5 encoding can have '\' in the trail byte. */ - if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) - ++p; - else if (*p == '\\') - *p = '/'; -} -#endif - - -/* - * Code for automatic commands. - * - * Only included when "FEAT_AUTOCMD" has been defined. - */ - - -/* - * The autocommands are stored in a list for each event. - * Autocommands for the same pattern, that are consecutive, are joined - * together, to avoid having to match the pattern too often. - * The result is an array of Autopat lists, which point to AutoCmd lists: - * - * first_autopat[0] --> Autopat.next --> Autopat.next --> NULL - * Autopat.cmds Autopat.cmds - * | | - * V V - * AutoCmd.next AutoCmd.next - * | | - * V V - * AutoCmd.next NULL - * | - * V - * NULL - * - * first_autopat[1] --> Autopat.next --> NULL - * Autopat.cmds - * | - * V - * AutoCmd.next - * | - * V - * NULL - * etc. - * - * The order of AutoCmds is important, this is the order in which they were - * defined and will have to be executed. - */ -typedef struct AutoCmd { - char_u *cmd; /* The command to be executed (NULL - when command has been removed) */ - char nested; /* If autocommands nest here */ - char last; /* last command in list */ - scid_T scriptID; /* script ID where defined */ - struct AutoCmd *next; /* Next AutoCmd in list */ -} AutoCmd; - -typedef struct AutoPat { - char_u *pat; /* pattern as typed (NULL when pattern - has been removed) */ - regprog_T *reg_prog; /* compiled regprog for pattern */ - AutoCmd *cmds; /* list of commands to do */ - struct AutoPat *next; /* next AutoPat in AutoPat list */ - int group; /* group ID */ - int patlen; /* strlen() of pat */ - int buflocal_nr; /* !=0 for buffer-local AutoPat */ - char allow_dirs; /* Pattern may match whole path */ - char last; /* last pattern for apply_autocmds() */ -} AutoPat; - -static struct event_name { - char *name; /* event name */ - event_T event; /* event number */ -} event_names[] = -{ - {"BufAdd", EVENT_BUFADD}, - {"BufCreate", EVENT_BUFADD}, - {"BufDelete", EVENT_BUFDELETE}, - {"BufEnter", EVENT_BUFENTER}, - {"BufFilePost", EVENT_BUFFILEPOST}, - {"BufFilePre", EVENT_BUFFILEPRE}, - {"BufHidden", EVENT_BUFHIDDEN}, - {"BufLeave", EVENT_BUFLEAVE}, - {"BufNew", EVENT_BUFNEW}, - {"BufNewFile", EVENT_BUFNEWFILE}, - {"BufRead", EVENT_BUFREADPOST}, - {"BufReadCmd", EVENT_BUFREADCMD}, - {"BufReadPost", EVENT_BUFREADPOST}, - {"BufReadPre", EVENT_BUFREADPRE}, - {"BufUnload", EVENT_BUFUNLOAD}, - {"BufWinEnter", EVENT_BUFWINENTER}, - {"BufWinLeave", EVENT_BUFWINLEAVE}, - {"BufWipeout", EVENT_BUFWIPEOUT}, - {"BufWrite", EVENT_BUFWRITEPRE}, - {"BufWritePost", EVENT_BUFWRITEPOST}, - {"BufWritePre", EVENT_BUFWRITEPRE}, - {"BufWriteCmd", EVENT_BUFWRITECMD}, - {"CmdwinEnter", EVENT_CMDWINENTER}, - {"CmdwinLeave", EVENT_CMDWINLEAVE}, - {"ColorScheme", EVENT_COLORSCHEME}, - {"CompleteDone", EVENT_COMPLETEDONE}, - {"CursorHold", EVENT_CURSORHOLD}, - {"CursorHoldI", EVENT_CURSORHOLDI}, - {"CursorMoved", EVENT_CURSORMOVED}, - {"CursorMovedI", EVENT_CURSORMOVEDI}, - {"EncodingChanged", EVENT_ENCODINGCHANGED}, - {"FileEncoding", EVENT_ENCODINGCHANGED}, - {"FileAppendPost", EVENT_FILEAPPENDPOST}, - {"FileAppendPre", EVENT_FILEAPPENDPRE}, - {"FileAppendCmd", EVENT_FILEAPPENDCMD}, - {"FileChangedShell",EVENT_FILECHANGEDSHELL}, - {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST}, - {"FileChangedRO", EVENT_FILECHANGEDRO}, - {"FileReadPost", EVENT_FILEREADPOST}, - {"FileReadPre", EVENT_FILEREADPRE}, - {"FileReadCmd", EVENT_FILEREADCMD}, - {"FileType", EVENT_FILETYPE}, - {"FileWritePost", EVENT_FILEWRITEPOST}, - {"FileWritePre", EVENT_FILEWRITEPRE}, - {"FileWriteCmd", EVENT_FILEWRITECMD}, - {"FilterReadPost", EVENT_FILTERREADPOST}, - {"FilterReadPre", EVENT_FILTERREADPRE}, - {"FilterWritePost", EVENT_FILTERWRITEPOST}, - {"FilterWritePre", EVENT_FILTERWRITEPRE}, - {"FocusGained", EVENT_FOCUSGAINED}, - {"FocusLost", EVENT_FOCUSLOST}, - {"FuncUndefined", EVENT_FUNCUNDEFINED}, - {"GUIEnter", EVENT_GUIENTER}, - {"GUIFailed", EVENT_GUIFAILED}, - {"InsertChange", EVENT_INSERTCHANGE}, - {"InsertEnter", EVENT_INSERTENTER}, - {"InsertLeave", EVENT_INSERTLEAVE}, - {"InsertCharPre", EVENT_INSERTCHARPRE}, - {"JobActivity", EVENT_JOBACTIVITY}, - {"MenuPopup", EVENT_MENUPOPUP}, - {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST}, - {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE}, - {"QuitPre", EVENT_QUITPRE}, - {"RemoteReply", EVENT_REMOTEREPLY}, - {"SessionLoadPost", EVENT_SESSIONLOADPOST}, - {"ShellCmdPost", EVENT_SHELLCMDPOST}, - {"ShellFilterPost", EVENT_SHELLFILTERPOST}, - {"SourcePre", EVENT_SOURCEPRE}, - {"SourceCmd", EVENT_SOURCECMD}, - {"SpellFileMissing",EVENT_SPELLFILEMISSING}, - {"StdinReadPost", EVENT_STDINREADPOST}, - {"StdinReadPre", EVENT_STDINREADPRE}, - {"SwapExists", EVENT_SWAPEXISTS}, - {"Syntax", EVENT_SYNTAX}, - {"TabEnter", EVENT_TABENTER}, - {"TabLeave", EVENT_TABLEAVE}, - {"TermChanged", EVENT_TERMCHANGED}, - {"TermResponse", EVENT_TERMRESPONSE}, - {"TextChanged", EVENT_TEXTCHANGED}, - {"TextChangedI", EVENT_TEXTCHANGEDI}, - {"User", EVENT_USER}, - {"VimEnter", EVENT_VIMENTER}, - {"VimLeave", EVENT_VIMLEAVE}, - {"VimLeavePre", EVENT_VIMLEAVEPRE}, - {"WinEnter", EVENT_WINENTER}, - {"WinLeave", EVENT_WINLEAVE}, - {"VimResized", EVENT_VIMRESIZED}, - {NULL, (event_T)0} -}; - -static AutoPat *first_autopat[NUM_EVENTS] = -{ - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL -}; - -/* - * struct used to keep status while executing autocommands for an event. - */ -typedef struct AutoPatCmd { - AutoPat *curpat; /* next AutoPat to examine */ - AutoCmd *nextcmd; /* next AutoCmd to execute */ - int group; /* group being used */ - char_u *fname; /* fname to match with */ - char_u *sfname; /* sfname to match with */ - char_u *tail; /* tail of fname */ - event_T event; /* current event */ - int arg_bufnr; /* initially equal to , set to zero when - buf is deleted */ - struct AutoPatCmd *next; /* chain of active apc-s for auto-invalidation*/ -} AutoPatCmd; - -static AutoPatCmd *active_apc_list = NULL; /* stack of active autocommands */ - -/* - * augroups stores a list of autocmd group names. - */ -static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL}; -#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i]) - -/* - * The ID of the current group. Group 0 is the default one. - */ -static int current_augroup = AUGROUP_DEFAULT; - -static int au_need_clean = FALSE; /* need to delete marked patterns */ - -static void show_autocmd(AutoPat *ap, event_T event); -static void au_remove_pat(AutoPat *ap); -static void au_remove_cmds(AutoPat *ap); -static void au_cleanup(void); -static int au_new_group(char_u *name); -static void au_del_group(char_u *name); -static event_T event_name2nr(char_u *start, char_u **end); -static char_u *event_nr2name(event_T event); -static char_u *find_end_event(char_u *arg, int have_group); -static int event_ignored(event_T event); -static int au_get_grouparg(char_u **argp); -static int do_autocmd_event(event_T event, char_u *pat, int nested, - char_u *cmd, int forceit, - int group); -static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, - int force, int group, buf_T *buf, - exarg_T *eap); -static void auto_next_pat(AutoPatCmd *apc, int stop_at_last); - - -static event_T last_event; -static int last_group; -static int autocmd_blocked = 0; /* block all autocmds */ - -/* - * Show the autocommands for one AutoPat. - */ -static void show_autocmd(AutoPat *ap, event_T event) -{ - AutoCmd *ac; - - /* Check for "got_int" (here and at various places below), which is set - * when "q" has been hit for the "--more--" prompt */ - if (got_int) - return; - if (ap->pat == NULL) /* pattern has been removed */ - return; - - msg_putchar('\n'); - if (got_int) - return; - if (event != last_event || ap->group != last_group) { - if (ap->group != AUGROUP_DEFAULT) { - if (AUGROUP_NAME(ap->group) == NULL) - msg_puts_attr((char_u *)_("--Deleted--"), hl_attr(HLF_E)); - else - msg_puts_attr(AUGROUP_NAME(ap->group), hl_attr(HLF_T)); - msg_puts((char_u *)" "); - } - msg_puts_attr(event_nr2name(event), hl_attr(HLF_T)); - last_event = event; - last_group = ap->group; - msg_putchar('\n'); - if (got_int) - return; - } - msg_col = 4; - msg_outtrans(ap->pat); - - for (ac = ap->cmds; ac != NULL; ac = ac->next) { - if (ac->cmd != NULL) { /* skip removed commands */ - if (msg_col >= 14) - msg_putchar('\n'); - msg_col = 14; - if (got_int) - return; - msg_outtrans(ac->cmd); - if (p_verbose > 0) - last_set_msg(ac->scriptID); - if (got_int) - return; - if (ac->next != NULL) { - msg_putchar('\n'); - if (got_int) - return; - } - } - } -} - -/* - * Mark an autocommand pattern for deletion. - */ -static void au_remove_pat(AutoPat *ap) -{ - free(ap->pat); - ap->pat = NULL; - ap->buflocal_nr = -1; - au_need_clean = TRUE; -} - -/* - * Mark all commands for a pattern for deletion. - */ -static void au_remove_cmds(AutoPat *ap) -{ - AutoCmd *ac; - - for (ac = ap->cmds; ac != NULL; ac = ac->next) { - free(ac->cmd); - ac->cmd = NULL; - } - au_need_clean = TRUE; -} - -/* - * Cleanup autocommands and patterns that have been deleted. - * This is only done when not executing autocommands. - */ -static void au_cleanup(void) -{ - AutoPat *ap, **prev_ap; - AutoCmd *ac, **prev_ac; - event_T event; - - if (autocmd_busy || !au_need_clean) - return; - - /* loop over all events */ - for (event = (event_T)0; (int)event < (int)NUM_EVENTS; - event = (event_T)((int)event + 1)) { - /* loop over all autocommand patterns */ - prev_ap = &(first_autopat[(int)event]); - for (ap = *prev_ap; ap != NULL; ap = *prev_ap) { - /* loop over all commands for this pattern */ - prev_ac = &(ap->cmds); - for (ac = *prev_ac; ac != NULL; ac = *prev_ac) { - /* remove the command if the pattern is to be deleted or when - * the command has been marked for deletion */ - if (ap->pat == NULL || ac->cmd == NULL) { - *prev_ac = ac->next; - free(ac->cmd); - free(ac); - } else - prev_ac = &(ac->next); - } - - /* remove the pattern if it has been marked for deletion */ - if (ap->pat == NULL) { - *prev_ap = ap->next; - vim_regfree(ap->reg_prog); - free(ap); - } else - prev_ap = &(ap->next); - } - } - - au_need_clean = FALSE; -} - -/* - * Called when buffer is freed, to remove/invalidate related buffer-local - * autocmds. - */ -void aubuflocal_remove(buf_T *buf) -{ - AutoPat *ap; - event_T event; - AutoPatCmd *apc; - - /* invalidate currently executing autocommands */ - for (apc = active_apc_list; apc; apc = apc->next) - if (buf->b_fnum == apc->arg_bufnr) - apc->arg_bufnr = 0; - - /* invalidate buflocals looping through events */ - for (event = (event_T)0; (int)event < (int)NUM_EVENTS; - event = (event_T)((int)event + 1)) - /* loop over all autocommand patterns */ - for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) - if (ap->buflocal_nr == buf->b_fnum) { - au_remove_pat(ap); - if (p_verbose >= 6) { - verbose_enter(); - smsg((char_u *) - _("auto-removing autocommand: %s "), - event_nr2name(event), buf->b_fnum); - verbose_leave(); - } - } - au_cleanup(); -} - -/* - * Add an autocmd group name. - * Return it's ID. Returns AUGROUP_ERROR (< 0) for error. - */ -static int au_new_group(char_u *name) -{ - int i; - - i = au_find_group(name); - if (i == AUGROUP_ERROR) { /* the group doesn't exist yet, add it */ - /* First try using a free entry. */ - for (i = 0; i < augroups.ga_len; ++i) - if (AUGROUP_NAME(i) == NULL) - break; - if (i == augroups.ga_len) { - ga_grow(&augroups, 1); - } - - AUGROUP_NAME(i) = vim_strsave(name); - if (AUGROUP_NAME(i) == NULL) - return AUGROUP_ERROR; - if (i == augroups.ga_len) - ++augroups.ga_len; - } - - return i; -} - -static void au_del_group(char_u *name) -{ - int i; - - i = au_find_group(name); - if (i == AUGROUP_ERROR) /* the group doesn't exist */ - EMSG2(_("E367: No such group: \"%s\""), name); - else { - free(AUGROUP_NAME(i)); - AUGROUP_NAME(i) = NULL; - } -} - -/* - * Find the ID of an autocmd group name. - * Return it's ID. Returns AUGROUP_ERROR (< 0) for error. - */ -static int au_find_group(char_u *name) -{ - int i; - - for (i = 0; i < augroups.ga_len; ++i) - if (AUGROUP_NAME(i) != NULL && STRCMP(AUGROUP_NAME(i), name) == 0) - return i; - return AUGROUP_ERROR; -} - -/* - * Return TRUE if augroup "name" exists. - */ -int au_has_group(char_u *name) -{ - return au_find_group(name) != AUGROUP_ERROR; -} - -/* - * ":augroup {name}". - */ -void do_augroup(char_u *arg, int del_group) -{ - int i; - - if (del_group) { - if (*arg == NUL) - EMSG(_(e_argreq)); - else - au_del_group(arg); - } else if (STRICMP(arg, "end") == 0) /* ":aug end": back to group 0 */ - current_augroup = AUGROUP_DEFAULT; - else if (*arg) { /* ":aug xxx": switch to group xxx */ - i = au_new_group(arg); - if (i != AUGROUP_ERROR) - current_augroup = i; - } else { /* ":aug": list the group names */ - msg_start(); - for (i = 0; i < augroups.ga_len; ++i) { - if (AUGROUP_NAME(i) != NULL) { - msg_puts(AUGROUP_NAME(i)); - msg_puts((char_u *)" "); - } - } - msg_clr_eos(); - msg_end(); - } -} - -#if defined(EXITFREE) || defined(PROTO) -void free_all_autocmds(void) -{ - for (current_augroup = -1; current_augroup < augroups.ga_len; - ++current_augroup) - do_autocmd((char_u *)"", TRUE); - ga_clear_strings(&augroups); -} - -#endif - -/* - * Return the event number for event name "start". - * Return NUM_EVENTS if the event name was not found. - * Return a pointer to the next event name in "end". - */ -static event_T event_name2nr(char_u *start, char_u **end) -{ - char_u *p; - int i; - int len; - - /* the event name ends with end of line, a blank or a comma */ - for (p = start; *p && !vim_iswhite(*p) && *p != ','; ++p) - ; - for (i = 0; event_names[i].name != NULL; ++i) { - len = (int)STRLEN(event_names[i].name); - if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) - break; - } - if (*p == ',') - ++p; - *end = p; - if (event_names[i].name == NULL) - return NUM_EVENTS; - return event_names[i].event; -} - -/* - * Return the name for event "event". - */ -static char_u *event_nr2name(event_T event) -{ - int i; - - for (i = 0; event_names[i].name != NULL; ++i) - if (event_names[i].event == event) - return (char_u *)event_names[i].name; - return (char_u *)"Unknown"; -} - -/* - * Scan over the events. "*" stands for all events. - */ -static char_u * -find_end_event ( - char_u *arg, - int have_group /* TRUE when group name was found */ -) -{ - char_u *pat; - char_u *p; - - if (*arg == '*') { - if (arg[1] && !vim_iswhite(arg[1])) { - EMSG2(_("E215: Illegal character after *: %s"), arg); - return NULL; - } - pat = arg + 1; - } else { - for (pat = arg; *pat && !vim_iswhite(*pat); pat = p) { - if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS) { - if (have_group) - EMSG2(_("E216: No such event: %s"), pat); - else - EMSG2(_("E216: No such group or event: %s"), pat); - return NULL; - } - } - } - return pat; -} - -/* - * Return TRUE if "event" is included in 'eventignore'. - */ -static int event_ignored(event_T event) -{ - char_u *p = p_ei; - - while (*p != NUL) { - if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) - return TRUE; - if (event_name2nr(p, &p) == event) - return TRUE; - } - - return FALSE; -} - -/* - * Return OK when the contents of p_ei is valid, FAIL otherwise. - */ -int check_ei(void) -{ - char_u *p = p_ei; - - while (*p) { - if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) { - p += 3; - if (*p == ',') - ++p; - } else if (event_name2nr(p, &p) == NUM_EVENTS) - return FAIL; - } - - return OK; -} - -/* - * Add "what" to 'eventignore' to skip loading syntax highlighting for every - * buffer loaded into the window. "what" must start with a comma. - * Returns the old value of 'eventignore' in allocated memory. - */ -char_u *au_event_disable(char *what) -{ - char_u *new_ei; - char_u *save_ei; - - save_ei = vim_strsave(p_ei); - if (save_ei != NULL) { - new_ei = vim_strnsave(p_ei, (int)(STRLEN(p_ei) + STRLEN(what))); - if (new_ei != NULL) { - if (*what == ',' && *p_ei == NUL) - STRCPY(new_ei, what + 1); - else - STRCAT(new_ei, what); - set_string_option_direct((char_u *)"ei", -1, new_ei, - OPT_FREE, SID_NONE); - free(new_ei); - } - } - return save_ei; -} - -void au_event_restore(char_u *old_ei) -{ - if (old_ei != NULL) { - set_string_option_direct((char_u *)"ei", -1, old_ei, - OPT_FREE, SID_NONE); - free(old_ei); - } -} - -/* - * do_autocmd() -- implements the :autocmd command. Can be used in the - * following ways: - * - * :autocmd Add to the list of commands that - * will be automatically executed for - * when editing a file matching , in - * the current group. - * :autocmd Show the auto-commands associated with - * and . - * :autocmd Show the auto-commands associated with - * . - * :autocmd Show all auto-commands. - * :autocmd! Remove all auto-commands associated with - * and , and add the command - * , for the current group. - * :autocmd! Remove all auto-commands associated with - * and for the current group. - * :autocmd! Remove all auto-commands associated with - * for the current group. - * :autocmd! Remove ALL auto-commands for the current - * group. - * - * Multiple events and patterns may be given separated by commas. Here are - * some examples: - * :autocmd bufread,bufenter *.c,*.h set tw=0 smartindent noic - * :autocmd bufleave * set tw=79 nosmartindent ic infercase - * - * :autocmd * *.c show all autocommands for *.c files. - * - * Mostly a {group} argument can optionally appear before . - */ -void do_autocmd(char_u *arg, int forceit) -{ - char_u *pat; - char_u *envpat = NULL; - char_u *cmd; - event_T event; - int need_free = FALSE; - int nested = FALSE; - int group; - - /* - * Check for a legal group name. If not, use AUGROUP_ALL. - */ - group = au_get_grouparg(&arg); - if (arg == NULL) /* out of memory */ - return; - - /* - * Scan over the events. - * If we find an illegal name, return here, don't do anything. - */ - pat = find_end_event(arg, group != AUGROUP_ALL); - if (pat == NULL) - return; - - /* - * Scan over the pattern. Put a NUL at the end. - */ - pat = skipwhite(pat); - cmd = pat; - while (*cmd && (!vim_iswhite(*cmd) || cmd[-1] == '\\')) - cmd++; - if (*cmd) - *cmd++ = NUL; - - /* Expand environment variables in the pattern. Set 'shellslash', we want - * forward slashes here. */ - if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) { -#ifdef BACKSLASH_IN_FILENAME - int p_ssl_save = p_ssl; - - p_ssl = TRUE; -#endif - envpat = expand_env_save(pat); -#ifdef BACKSLASH_IN_FILENAME - p_ssl = p_ssl_save; -#endif - if (envpat != NULL) - pat = envpat; - } - - /* - * Check for "nested" flag. - */ - cmd = skipwhite(cmd); - if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 && vim_iswhite(cmd[6])) { - nested = TRUE; - cmd = skipwhite(cmd + 6); - } - - /* - * Find the start of the commands. - * Expand in it. - */ - if (*cmd != NUL) { - cmd = expand_sfile(cmd); - if (cmd == NULL) /* some error */ - return; - need_free = TRUE; - } - - /* - * Print header when showing autocommands. - */ - if (!forceit && *cmd == NUL) { - /* Highlight title */ - MSG_PUTS_TITLE(_("\n--- Auto-Commands ---")); - } - - /* - * Loop over the events. - */ - last_event = (event_T)-1; /* for listing the event name */ - last_group = AUGROUP_ERROR; /* for listing the group name */ - if (*arg == '*' || *arg == NUL) { - for (event = (event_T)0; (int)event < (int)NUM_EVENTS; - event = (event_T)((int)event + 1)) - if (do_autocmd_event(event, pat, - nested, cmd, forceit, group) == FAIL) - break; - } else { - while (*arg && !vim_iswhite(*arg)) - if (do_autocmd_event(event_name2nr(arg, &arg), pat, - nested, cmd, forceit, group) == FAIL) - break; - } - - if (need_free) - free(cmd); - free(envpat); -} - -/* - * Find the group ID in a ":autocmd" or ":doautocmd" argument. - * The "argp" argument is advanced to the following argument. - * - * Returns the group ID, AUGROUP_ERROR for error (out of memory). - */ -static int au_get_grouparg(char_u **argp) -{ - char_u *group_name; - char_u *p; - char_u *arg = *argp; - int group = AUGROUP_ALL; - - p = skiptowhite(arg); - if (p > arg) { - group_name = vim_strnsave(arg, (int)(p - arg)); - if (group_name == NULL) /* out of memory */ - return AUGROUP_ERROR; - group = au_find_group(group_name); - if (group == AUGROUP_ERROR) - group = AUGROUP_ALL; /* no match, use all groups */ - else - *argp = skipwhite(p); /* match, skip over group name */ - free(group_name); - } - return group; -} - -/* - * do_autocmd() for one event. - * If *pat == NUL do for all patterns. - * If *cmd == NUL show entries. - * If forceit == TRUE delete entries. - * If group is not AUGROUP_ALL, only use this group. - */ -static int do_autocmd_event(event_T event, char_u *pat, int nested, char_u *cmd, int forceit, int group) -{ - AutoPat *ap; - AutoPat **prev_ap; - AutoCmd *ac; - AutoCmd **prev_ac; - int brace_level; - char_u *endpat; - int findgroup; - int allgroups; - int patlen; - int is_buflocal; - int buflocal_nr; - char_u buflocal_pat[25]; /* for "" */ - - if (group == AUGROUP_ALL) - findgroup = current_augroup; - else - findgroup = group; - allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL); - - /* - * Show or delete all patterns for an event. - */ - if (*pat == NUL) { - for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { - if (forceit) { /* delete the AutoPat, if it's in the current group */ - if (ap->group == findgroup) - au_remove_pat(ap); - } else if (group == AUGROUP_ALL || ap->group == group) - show_autocmd(ap, event); - } - } - - /* - * Loop through all the specified patterns. - */ - for (; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat)) { - /* - * Find end of the pattern. - * Watch out for a comma in braces, like "*.\{obj,o\}". - */ - brace_level = 0; - for (endpat = pat; *endpat && (*endpat != ',' || brace_level - || endpat[-1] == '\\'); ++endpat) { - if (*endpat == '{') - brace_level++; - else if (*endpat == '}') - brace_level--; - } - if (pat == endpat) /* ignore single comma */ - continue; - patlen = (int)(endpat - pat); - - /* - * detect special buffer-local patterns - */ - is_buflocal = FALSE; - buflocal_nr = 0; - - if (patlen >= 7 && STRNCMP(pat, "') { - /* Error will be printed only for addition. printing and removing - * will proceed silently. */ - is_buflocal = TRUE; - if (patlen == 8) - buflocal_nr = curbuf->b_fnum; - else if (patlen > 9 && pat[7] == '=') { - /* */ - if (patlen == 13 && STRNICMP(pat, "", 13)) - buflocal_nr = autocmd_bufnr; - /* */ - else if (skipdigits(pat + 8) == pat + patlen - 1) - buflocal_nr = atoi((char *)pat + 8); - } - } - - if (is_buflocal) { - /* normalize pat into standard "#N" form */ - sprintf((char *)buflocal_pat, "", buflocal_nr); - pat = buflocal_pat; /* can modify pat and patlen */ - patlen = (int)STRLEN(buflocal_pat); /* but not endpat */ - } - - /* - * Find AutoPat entries with this pattern. - */ - prev_ap = &first_autopat[(int)event]; - while ((ap = *prev_ap) != NULL) { - if (ap->pat != NULL) { - /* Accept a pattern when: - * - a group was specified and it's that group, or a group was - * not specified and it's the current group, or a group was - * not specified and we are listing - * - the length of the pattern matches - * - the pattern matches. - * For , this condition works because we normalize - * all buffer-local patterns. - */ - if ((allgroups || ap->group == findgroup) - && ap->patlen == patlen - && STRNCMP(pat, ap->pat, patlen) == 0) { - /* - * Remove existing autocommands. - * If adding any new autocmd's for this AutoPat, don't - * delete the pattern from the autopat list, append to - * this list. - */ - if (forceit) { - if (*cmd != NUL && ap->next == NULL) { - au_remove_cmds(ap); - break; - } - au_remove_pat(ap); - } - /* - * Show autocmd's for this autopat, or buflocals - */ - else if (*cmd == NUL) - show_autocmd(ap, event); - - /* - * Add autocmd to this autopat, if it's the last one. - */ - else if (ap->next == NULL) - break; - } - } - prev_ap = &ap->next; - } - - /* - * Add a new command. - */ - if (*cmd != NUL) { - /* - * If the pattern we want to add a command to does appear at the - * end of the list (or not is not in the list at all), add the - * pattern at the end of the list. - */ - if (ap == NULL) { - /* refuse to add buffer-local ap if buffer number is invalid */ - if (is_buflocal && (buflocal_nr == 0 - || buflist_findnr(buflocal_nr) == NULL)) { - EMSGN(_("E680: : invalid buffer number "), - buflocal_nr); - return FAIL; - } - - ap = (AutoPat *)alloc((unsigned)sizeof(AutoPat)); - ap->pat = vim_strnsave(pat, patlen); - ap->patlen = patlen; - if (ap->pat == NULL) { - free(ap); - return FAIL; - } - - if (is_buflocal) { - ap->buflocal_nr = buflocal_nr; - ap->reg_prog = NULL; - } else { - char_u *reg_pat; - - ap->buflocal_nr = 0; - reg_pat = file_pat_to_reg_pat(pat, endpat, - &ap->allow_dirs, TRUE); - if (reg_pat != NULL) - ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC); - free(reg_pat); - if (reg_pat == NULL || ap->reg_prog == NULL) { - free(ap->pat); - free(ap); - return FAIL; - } - } - ap->cmds = NULL; - *prev_ap = ap; - ap->next = NULL; - if (group == AUGROUP_ALL) - ap->group = current_augroup; - else - ap->group = group; - } - - /* - * Add the autocmd at the end of the AutoCmd list. - */ - prev_ac = &(ap->cmds); - while ((ac = *prev_ac) != NULL) - prev_ac = &ac->next; - ac = (AutoCmd *)alloc((unsigned)sizeof(AutoCmd)); - ac->cmd = vim_strsave(cmd); - ac->scriptID = current_SID; - if (ac->cmd == NULL) { - free(ac); - return FAIL; - } - ac->next = NULL; - *prev_ac = ac; - ac->nested = nested; - } - } - - au_cleanup(); /* may really delete removed patterns/commands now */ - return OK; -} - -/* - * Implementation of ":doautocmd [group] event [fname]". - * Return OK for success, FAIL for failure; - */ -int -do_doautocmd ( - char_u *arg, - int do_msg /* give message for no matching autocmds? */ -) -{ - char_u *fname; - int nothing_done = TRUE; - int group; - - /* - * Check for a legal group name. If not, use AUGROUP_ALL. - */ - group = au_get_grouparg(&arg); - if (arg == NULL) /* out of memory */ - return FAIL; - - if (*arg == '*') { - EMSG(_("E217: Can't execute autocommands for ALL events")); - return FAIL; - } - - /* - * Scan over the events. - * If we find an illegal name, return here, don't do anything. - */ - fname = find_end_event(arg, group != AUGROUP_ALL); - if (fname == NULL) - return FAIL; - - fname = skipwhite(fname); - - /* - * Loop over the events. - */ - while (*arg && !vim_iswhite(*arg)) - if (apply_autocmds_group(event_name2nr(arg, &arg), - fname, NULL, TRUE, group, curbuf, NULL)) - nothing_done = FALSE; - - if (nothing_done && do_msg) - MSG(_("No matching autocommands")); - - return aborting() ? FAIL : OK; -} - -/* - * ":doautoall": execute autocommands for each loaded buffer. - */ -void ex_doautoall(exarg_T *eap) -{ - int retval; - aco_save_T aco; - buf_T *buf; - char_u *arg = eap->arg; - int call_do_modelines = check_nomodeline(&arg); - - /* - * This is a bit tricky: For some commands curwin->w_buffer needs to be - * equal to curbuf, but for some buffers there may not be a window. - * So we change the buffer for the current window for a moment. This - * gives problems when the autocommands make changes to the list of - * buffers or windows... - */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (buf->b_ml.ml_mfp != NULL) { - /* find a window for this buffer and save some values */ - aucmd_prepbuf(&aco, buf); - - /* execute the autocommands for this buffer */ - retval = do_doautocmd(arg, FALSE); - - if (call_do_modelines) { - /* Execute the modeline settings, but don't set window-local - * options if we are using the current window for another - * buffer. */ - do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0); - } - - /* restore the current window */ - aucmd_restbuf(&aco); - - /* stop if there is some error or buffer was deleted */ - if (retval == FAIL || !buf_valid(buf)) - break; - } - } - - check_cursor(); /* just in case lines got deleted */ -} - -/* - * Check *argp for . When it is present return FALSE, otherwise - * return TRUE and advance *argp to after it. - * Thus return TRUE when do_modelines() should be called. - */ -int check_nomodeline(char_u **argp) -{ - if (STRNCMP(*argp, "", 12) == 0) { - *argp = skipwhite(*argp + 12); - return FALSE; - } - return TRUE; -} - -/* - * Prepare for executing autocommands for (hidden) buffer "buf". - * Search for a visible window containing the current buffer. If there isn't - * one then use "aucmd_win". - * Set "curbuf" and "curwin" to match "buf". - * When FEAT_AUTOCMD is not defined another version is used, see below. - */ -void -aucmd_prepbuf ( - aco_save_T *aco, /* structure to save values in */ - buf_T *buf /* new curbuf */ -) -{ - win_T *win; - int save_ea; - int save_acd; - - /* Find a window that is for the new buffer */ - if (buf == curbuf) /* be quick when buf is curbuf */ - win = curwin; - else - for (win = firstwin; win != NULL; win = win->w_next) - if (win->w_buffer == buf) - break; - - /* Allocate "aucmd_win" when needed. If this fails (out of memory) fall - * back to using the current window. */ - if (win == NULL && aucmd_win == NULL) { - win_alloc_aucmd_win(); - if (aucmd_win == NULL) - win = curwin; - } - if (win == NULL && aucmd_win_used) - /* Strange recursive autocommand, fall back to using the current - * window. Expect a few side effects... */ - win = curwin; - - aco->save_curwin = curwin; - aco->save_curbuf = curbuf; - if (win != NULL) { - /* There is a window for "buf" in the current tab page, make it the - * curwin. This is preferred, it has the least side effects (esp. if - * "buf" is curbuf). */ - aco->use_aucmd_win = FALSE; - curwin = win; - } else { - /* There is no window for "buf", use "aucmd_win". To minimize the side - * effects, insert it in the current tab page. - * Anything related to a window (e.g., setting folds) may have - * unexpected results. */ - aco->use_aucmd_win = TRUE; - aucmd_win_used = TRUE; - aucmd_win->w_buffer = buf; - aucmd_win->w_s = &buf->b_s; - ++buf->b_nwindows; - win_init_empty(aucmd_win); /* set cursor and topline to safe values */ - - /* Make sure w_localdir and globaldir are NULL to avoid a chdir() in - * win_enter_ext(). */ - free(aucmd_win->w_localdir); - aucmd_win->w_localdir = NULL; - aco->globaldir = globaldir; - globaldir = NULL; - - - /* Split the current window, put the aucmd_win in the upper half. - * We don't want the BufEnter or WinEnter autocommands. */ - block_autocmds(); - make_snapshot(SNAP_AUCMD_IDX); - save_ea = p_ea; - p_ea = FALSE; - - /* Prevent chdir() call in win_enter_ext(), through do_autochdir(). */ - save_acd = p_acd; - p_acd = FALSE; - - (void)win_split_ins(0, WSP_TOP, aucmd_win, 0); - (void)win_comp_pos(); /* recompute window positions */ - p_ea = save_ea; - p_acd = save_acd; - unblock_autocmds(); - curwin = aucmd_win; - } - curbuf = buf; - aco->new_curwin = curwin; - aco->new_curbuf = curbuf; -} - -/* - * Cleanup after executing autocommands for a (hidden) buffer. - * Restore the window as it was (if possible). - * When FEAT_AUTOCMD is not defined another version is used, see below. - */ -void -aucmd_restbuf ( - aco_save_T *aco /* structure holding saved values */ -) -{ - int dummy; - - if (aco->use_aucmd_win) { - --curbuf->b_nwindows; - /* Find "aucmd_win", it can't be closed, but it may be in another tab - * page. Do not trigger autocommands here. */ - block_autocmds(); - if (curwin != aucmd_win) { - tabpage_T *tp; - win_T *wp; - - FOR_ALL_TAB_WINDOWS(tp, wp) - { - if (wp == aucmd_win) { - if (tp != curtab) - goto_tabpage_tp(tp, TRUE, TRUE); - win_goto(aucmd_win); - goto win_found; - } - } - } -win_found: - - /* Remove the window and frame from the tree of frames. */ - (void)winframe_remove(curwin, &dummy, NULL); - win_remove(curwin, NULL); - aucmd_win_used = FALSE; - last_status(FALSE); /* may need to remove last status line */ - restore_snapshot(SNAP_AUCMD_IDX, FALSE); - (void)win_comp_pos(); /* recompute window positions */ - unblock_autocmds(); - - if (win_valid(aco->save_curwin)) - curwin = aco->save_curwin; - else - /* Hmm, original window disappeared. Just use the first one. */ - curwin = firstwin; - vars_clear(&aucmd_win->w_vars->dv_hashtab); /* free all w: variables */ - hash_init(&aucmd_win->w_vars->dv_hashtab); /* re-use the hashtab */ - curbuf = curwin->w_buffer; - - free(globaldir); - globaldir = aco->globaldir; - - /* the buffer contents may have changed */ - check_cursor(); - if (curwin->w_topline > curbuf->b_ml.ml_line_count) { - curwin->w_topline = curbuf->b_ml.ml_line_count; - curwin->w_topfill = 0; - } - } else { - /* restore curwin */ - if (win_valid(aco->save_curwin)) { - /* Restore the buffer which was previously edited by curwin, if - * it was changed, we are still the same window and the buffer is - * valid. */ - if (curwin == aco->new_curwin - && curbuf != aco->new_curbuf - && buf_valid(aco->new_curbuf) - && aco->new_curbuf->b_ml.ml_mfp != NULL) { - if (curwin->w_s == &curbuf->b_s) - curwin->w_s = &aco->new_curbuf->b_s; - --curbuf->b_nwindows; - curbuf = aco->new_curbuf; - curwin->w_buffer = curbuf; - ++curbuf->b_nwindows; - } - - curwin = aco->save_curwin; - curbuf = curwin->w_buffer; - } - } -} - -static int autocmd_nested = FALSE; - -/* - * Execute autocommands for "event" and file name "fname". - * Return TRUE if some commands were executed. - */ -int -apply_autocmds ( - event_T event, - char_u *fname, /* NULL or empty means use actual file name */ - char_u *fname_io, /* fname to use for on cmdline */ - int force, /* when TRUE, ignore autocmd_busy */ - buf_T *buf /* buffer for */ -) -{ - return apply_autocmds_group(event, fname, fname_io, force, - AUGROUP_ALL, buf, NULL); -} - -/* - * Like apply_autocmds(), but with extra "eap" argument. This takes care of - * setting v:filearg. - */ -static int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap) -{ - return apply_autocmds_group(event, fname, fname_io, force, - AUGROUP_ALL, buf, eap); -} - -/* - * Like apply_autocmds(), but handles the caller's retval. If the script - * processing is being aborted or if retval is FAIL when inside a try - * conditional, no autocommands are executed. If otherwise the autocommands - * cause the script to be aborted, retval is set to FAIL. - */ -int -apply_autocmds_retval ( - event_T event, - char_u *fname, /* NULL or empty means use actual file name */ - char_u *fname_io, /* fname to use for on cmdline */ - int force, /* when TRUE, ignore autocmd_busy */ - buf_T *buf, /* buffer for */ - int *retval /* pointer to caller's retval */ -) -{ - int did_cmd; - - if (should_abort(*retval)) - return FALSE; - - did_cmd = apply_autocmds_group(event, fname, fname_io, force, - AUGROUP_ALL, buf, NULL); - if (did_cmd - && aborting() - ) - *retval = FAIL; - return did_cmd; -} - -/* - * Return TRUE when there is a CursorHold autocommand defined. - */ -int has_cursorhold(void) -{ - return first_autopat[(int)(get_real_state() == NORMAL_BUSY - ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI)] != NULL; -} - -/* - * Return TRUE if the CursorHold event can be triggered. - */ -int trigger_cursorhold(void) -{ - int state; - - if (!did_cursorhold - && has_cursorhold() - && !Recording - && typebuf.tb_len == 0 - && !ins_compl_active() - ) { - state = get_real_state(); - if (state == NORMAL_BUSY || (state & INSERT) != 0) - return TRUE; - } - return FALSE; -} - -/* - * Return TRUE when there is a CursorMoved autocommand defined. - */ -int has_cursormoved(void) -{ - return first_autopat[(int)EVENT_CURSORMOVED] != NULL; -} - -/* - * Return TRUE when there is a CursorMovedI autocommand defined. - */ -int has_cursormovedI(void) -{ - return first_autopat[(int)EVENT_CURSORMOVEDI] != NULL; -} - -/* - * Return TRUE when there is a TextChanged autocommand defined. - */ -int has_textchanged(void) -{ - return first_autopat[(int)EVENT_TEXTCHANGED] != NULL; -} - -/* - * Return TRUE when there is a TextChangedI autocommand defined. - */ -int has_textchangedI(void) -{ - return first_autopat[(int)EVENT_TEXTCHANGEDI] != NULL; -} - -/* - * Return TRUE when there is an InsertCharPre autocommand defined. - */ -int has_insertcharpre(void) -{ - return first_autopat[(int)EVENT_INSERTCHARPRE] != NULL; -} - -static int -apply_autocmds_group ( - event_T event, - char_u *fname, /* NULL or empty means use actual file name */ - char_u *fname_io, /* fname to use for on cmdline, NULL means - use fname */ - int force, /* when TRUE, ignore autocmd_busy */ - int group, /* group ID, or AUGROUP_ALL */ - buf_T *buf, /* buffer for */ - exarg_T *eap /* command arguments */ -) -{ - char_u *sfname = NULL; /* short file name */ - char_u *tail; - int save_changed; - buf_T *old_curbuf; - int retval = FALSE; - char_u *save_sourcing_name; - linenr_T save_sourcing_lnum; - char_u *save_autocmd_fname; - int save_autocmd_fname_full; - int save_autocmd_bufnr; - char_u *save_autocmd_match; - int save_autocmd_busy; - int save_autocmd_nested; - static int nesting = 0; - AutoPatCmd patcmd; - AutoPat *ap; - scid_T save_current_SID; - void *save_funccalp; - char_u *save_cmdarg; - long save_cmdbang; - static int filechangeshell_busy = FALSE; - proftime_T wait_time; - - /* - * Quickly return if there are no autocommands for this event or - * autocommands are blocked. - */ - if (first_autopat[(int)event] == NULL || autocmd_blocked > 0) - goto BYPASS_AU; - - /* - * When autocommands are busy, new autocommands are only executed when - * explicitly enabled with the "nested" flag. - */ - if (autocmd_busy && !(force || autocmd_nested)) - goto BYPASS_AU; - - /* - * Quickly return when immediately aborting on error, or when an interrupt - * occurred or an exception was thrown but not caught. - */ - if (aborting()) - goto BYPASS_AU; - - /* - * FileChangedShell never nests, because it can create an endless loop. - */ - if (filechangeshell_busy && (event == EVENT_FILECHANGEDSHELL - || event == EVENT_FILECHANGEDSHELLPOST)) - goto BYPASS_AU; - - /* - * Ignore events in 'eventignore'. - */ - if (event_ignored(event)) - goto BYPASS_AU; - - /* - * Allow nesting of autocommands, but restrict the depth, because it's - * possible to create an endless loop. - */ - if (nesting == 10) { - EMSG(_("E218: autocommand nesting too deep")); - goto BYPASS_AU; - } - - /* - * Check if these autocommands are disabled. Used when doing ":all" or - * ":ball". - */ - if ( (autocmd_no_enter - && (event == EVENT_WINENTER || event == EVENT_BUFENTER)) - || (autocmd_no_leave - && (event == EVENT_WINLEAVE || event == EVENT_BUFLEAVE))) - goto BYPASS_AU; - - /* - * Save the autocmd_* variables and info about the current buffer. - */ - save_autocmd_fname = autocmd_fname; - save_autocmd_fname_full = autocmd_fname_full; - save_autocmd_bufnr = autocmd_bufnr; - save_autocmd_match = autocmd_match; - save_autocmd_busy = autocmd_busy; - save_autocmd_nested = autocmd_nested; - save_changed = curbuf->b_changed; - old_curbuf = curbuf; - - /* - * Set the file name to be used for . - * Make a copy to avoid that changing a buffer name or directory makes it - * invalid. - */ - if (fname_io == NULL) { - if (event == EVENT_COLORSCHEME) - autocmd_fname = NULL; - else if (fname != NULL && *fname != NUL) - autocmd_fname = fname; - else if (buf != NULL) - autocmd_fname = buf->b_ffname; - else - autocmd_fname = NULL; - } else - autocmd_fname = fname_io; - if (autocmd_fname != NULL) - autocmd_fname = vim_strsave(autocmd_fname); - autocmd_fname_full = FALSE; /* call FullName_save() later */ - - /* - * Set the buffer number to be used for . - */ - if (buf == NULL) - autocmd_bufnr = 0; - else - autocmd_bufnr = buf->b_fnum; - - /* - * When the file name is NULL or empty, use the file name of buffer "buf". - * Always use the full path of the file name to match with, in case - * "allow_dirs" is set. - */ - if (fname == NULL || *fname == NUL) { - if (buf == NULL) - fname = NULL; - else { - if (event == EVENT_SYNTAX) - fname = buf->b_p_syn; - else if (event == EVENT_FILETYPE) - fname = buf->b_p_ft; - else { - if (buf->b_sfname != NULL) - sfname = vim_strsave(buf->b_sfname); - fname = buf->b_ffname; - } - } - if (fname == NULL) - fname = (char_u *)""; - fname = vim_strsave(fname); /* make a copy, so we can change it */ - } else { - sfname = vim_strsave(fname); - /* Don't try expanding FileType, Syntax, FuncUndefined, WindowID, - * ColorScheme, QuickFixCmd or JobActivity */ - if (event == EVENT_FILETYPE - || event == EVENT_SYNTAX - || event == EVENT_FUNCUNDEFINED - || event == EVENT_REMOTEREPLY - || event == EVENT_SPELLFILEMISSING - || event == EVENT_QUICKFIXCMDPRE - || event == EVENT_COLORSCHEME - || event == EVENT_QUICKFIXCMDPOST - || event == EVENT_JOBACTIVITY) - fname = vim_strsave(fname); - else - fname = FullName_save(fname, FALSE); - } - if (fname == NULL) { /* out of memory */ - free(sfname); - retval = FALSE; - goto BYPASS_AU; - } - -#ifdef BACKSLASH_IN_FILENAME - /* - * Replace all backslashes with forward slashes. This makes the - * autocommand patterns portable between Unix and MS-DOS. - */ - if (sfname != NULL) - forward_slash(sfname); - forward_slash(fname); -#endif - - - /* - * Set the name to be used for . - */ - autocmd_match = fname; - - - /* Don't redraw while doing auto commands. */ - ++RedrawingDisabled; - save_sourcing_name = sourcing_name; - sourcing_name = NULL; /* don't free this one */ - save_sourcing_lnum = sourcing_lnum; - sourcing_lnum = 0; /* no line number here */ - - save_current_SID = current_SID; - - if (do_profiling == PROF_YES) - prof_child_enter(&wait_time); /* doesn't count for the caller itself */ - - /* Don't use local function variables, if called from a function */ - save_funccalp = save_funccal(); - - /* - * When starting to execute autocommands, save the search patterns. - */ - if (!autocmd_busy) { - save_search_patterns(); - saveRedobuff(); - did_filetype = keep_filetype; - } - - /* - * Note that we are applying autocmds. Some commands need to know. - */ - autocmd_busy = TRUE; - filechangeshell_busy = (event == EVENT_FILECHANGEDSHELL); - ++nesting; /* see matching decrement below */ - - /* Remember that FileType was triggered. Used for did_filetype(). */ - if (event == EVENT_FILETYPE) - did_filetype = TRUE; - - tail = path_tail(fname); - - /* Find first autocommand that matches */ - patcmd.curpat = first_autopat[(int)event]; - patcmd.nextcmd = NULL; - patcmd.group = group; - patcmd.fname = fname; - patcmd.sfname = sfname; - patcmd.tail = tail; - patcmd.event = event; - patcmd.arg_bufnr = autocmd_bufnr; - patcmd.next = NULL; - auto_next_pat(&patcmd, FALSE); - - /* found one, start executing the autocommands */ - if (patcmd.curpat != NULL) { - /* add to active_apc_list */ - patcmd.next = active_apc_list; - active_apc_list = &patcmd; - - /* set v:cmdarg (only when there is a matching pattern) */ - save_cmdbang = get_vim_var_nr(VV_CMDBANG); - if (eap != NULL) { - save_cmdarg = set_cmdarg(eap, NULL); - set_vim_var_nr(VV_CMDBANG, (long)eap->forceit); - } else - save_cmdarg = NULL; /* avoid gcc warning */ - retval = TRUE; - /* mark the last pattern, to avoid an endless loop when more patterns - * are added when executing autocommands */ - for (ap = patcmd.curpat; ap->next != NULL; ap = ap->next) - ap->last = FALSE; - ap->last = TRUE; - check_lnums(TRUE); /* make sure cursor and topline are valid */ - do_cmdline(NULL, getnextac, (void *)&patcmd, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); - if (eap != NULL) { - (void)set_cmdarg(NULL, save_cmdarg); - set_vim_var_nr(VV_CMDBANG, save_cmdbang); - } - /* delete from active_apc_list */ - if (active_apc_list == &patcmd) /* just in case */ - active_apc_list = patcmd.next; - } - - --RedrawingDisabled; - autocmd_busy = save_autocmd_busy; - filechangeshell_busy = FALSE; - autocmd_nested = save_autocmd_nested; - free(sourcing_name); - sourcing_name = save_sourcing_name; - sourcing_lnum = save_sourcing_lnum; - free(autocmd_fname); - autocmd_fname = save_autocmd_fname; - autocmd_fname_full = save_autocmd_fname_full; - autocmd_bufnr = save_autocmd_bufnr; - autocmd_match = save_autocmd_match; - current_SID = save_current_SID; - restore_funccal(save_funccalp); - if (do_profiling == PROF_YES) - prof_child_exit(&wait_time); - free(fname); - free(sfname); - --nesting; /* see matching increment above */ - - // When stopping to execute autocommands, restore the search patterns and - // the redo buffer. Free buffers in the au_pending_free_buf list. - if (!autocmd_busy) { - restore_search_patterns(); - restoreRedobuff(); - did_filetype = FALSE; - while (au_pending_free_buf != NULL) { - buf_T *b = au_pending_free_buf->b_next; - free(au_pending_free_buf); - au_pending_free_buf = b; - } - } - - /* - * Some events don't set or reset the Changed flag. - * Check if still in the same buffer! - */ - if (curbuf == old_curbuf - && (event == EVENT_BUFREADPOST - || event == EVENT_BUFWRITEPOST - || event == EVENT_FILEAPPENDPOST - || event == EVENT_VIMLEAVE - || event == EVENT_VIMLEAVEPRE)) { - if (curbuf->b_changed != save_changed) - need_maketitle = TRUE; - curbuf->b_changed = save_changed; - } - - au_cleanup(); /* may really delete removed patterns/commands now */ - -BYPASS_AU: - /* When wiping out a buffer make sure all its buffer-local autocommands - * are deleted. */ - if (event == EVENT_BUFWIPEOUT && buf != NULL) - aubuflocal_remove(buf); - - return retval; -} - -static char_u *old_termresponse = NULL; - -/* - * Block triggering autocommands until unblock_autocmd() is called. - * Can be used recursively, so long as it's symmetric. - */ -void block_autocmds(void) -{ - /* Remember the value of v:termresponse. */ - if (autocmd_blocked == 0) - old_termresponse = get_vim_var_str(VV_TERMRESPONSE); - ++autocmd_blocked; -} - -void unblock_autocmds(void) -{ - --autocmd_blocked; - - /* When v:termresponse was set while autocommands were blocked, trigger - * the autocommands now. Esp. useful when executing a shell command - * during startup (vimdiff). */ - if (autocmd_blocked == 0 - && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse) - apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, FALSE, curbuf); -} - -/* - * Find next autocommand pattern that matches. - */ -static void -auto_next_pat ( - AutoPatCmd *apc, - int stop_at_last /* stop when 'last' flag is set */ -) -{ - AutoPat *ap; - AutoCmd *cp; - char_u *name; - char *s; - - free(sourcing_name); - sourcing_name = NULL; - - for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next) { - apc->curpat = NULL; - - /* Only use a pattern when it has not been removed, has commands and - * the group matches. For buffer-local autocommands only check the - * buffer number. */ - if (ap->pat != NULL && ap->cmds != NULL - && (apc->group == AUGROUP_ALL || apc->group == ap->group)) { - /* execution-condition */ - if (ap->buflocal_nr == 0 - ? (match_file_pat(NULL, ap->reg_prog, apc->fname, - apc->sfname, apc->tail, ap->allow_dirs)) - : ap->buflocal_nr == apc->arg_bufnr) { - name = event_nr2name(apc->event); - s = _("%s Auto commands for \"%s\""); - sourcing_name = alloc((unsigned)(STRLEN(s) - + STRLEN(name) + ap->patlen + 1)); - sprintf((char *)sourcing_name, s, - (char *)name, (char *)ap->pat); - if (p_verbose >= 8) { - verbose_enter(); - smsg((char_u *)_("Executing %s"), sourcing_name); - verbose_leave(); - } - - apc->curpat = ap; - apc->nextcmd = ap->cmds; - /* mark last command */ - for (cp = ap->cmds; cp->next != NULL; cp = cp->next) - cp->last = FALSE; - cp->last = TRUE; - } - line_breakcheck(); - if (apc->curpat != NULL) /* found a match */ - break; - } - if (stop_at_last && ap->last) - break; - } -} - -/* - * Get next autocommand command. - * Called by do_cmdline() to get the next line for ":if". - * Returns allocated string, or NULL for end of autocommands. - */ -char_u *getnextac(int c, void *cookie, int indent) -{ - AutoPatCmd *acp = (AutoPatCmd *)cookie; - char_u *retval; - AutoCmd *ac; - - /* Can be called again after returning the last line. */ - if (acp->curpat == NULL) - return NULL; - - /* repeat until we find an autocommand to execute */ - for (;; ) { - /* skip removed commands */ - while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL) - if (acp->nextcmd->last) - acp->nextcmd = NULL; - else - acp->nextcmd = acp->nextcmd->next; - - if (acp->nextcmd != NULL) - break; - - /* at end of commands, find next pattern that matches */ - if (acp->curpat->last) - acp->curpat = NULL; - else - acp->curpat = acp->curpat->next; - if (acp->curpat != NULL) - auto_next_pat(acp, TRUE); - if (acp->curpat == NULL) - return NULL; - } - - ac = acp->nextcmd; - - if (p_verbose >= 9) { - verbose_enter_scroll(); - smsg((char_u *)_("autocommand %s"), ac->cmd); - msg_puts((char_u *)"\n"); /* don't overwrite this either */ - verbose_leave_scroll(); - } - retval = vim_strsave(ac->cmd); - autocmd_nested = ac->nested; - current_SID = ac->scriptID; - if (ac->last) - acp->nextcmd = NULL; - else - acp->nextcmd = ac->next; - return retval; -} - -/* - * Return TRUE if there is a matching autocommand for "fname". - * To account for buffer-local autocommands, function needs to know - * in which buffer the file will be opened. - */ -int has_autocmd(event_T event, char_u *sfname, buf_T *buf) -{ - AutoPat *ap; - char_u *fname; - char_u *tail = path_tail(sfname); - int retval = FALSE; - - fname = FullName_save(sfname, FALSE); - if (fname == NULL) - return FALSE; - -#ifdef BACKSLASH_IN_FILENAME - /* - * Replace all backslashes with forward slashes. This makes the - * autocommand patterns portable between Unix and MS-DOS. - */ - sfname = vim_strsave(sfname); - if (sfname != NULL) - forward_slash(sfname); - forward_slash(fname); -#endif - - for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) - if (ap->pat != NULL && ap->cmds != NULL - && (ap->buflocal_nr == 0 - ? match_file_pat(NULL, ap->reg_prog, - fname, sfname, tail, ap->allow_dirs) - : buf != NULL && ap->buflocal_nr == buf->b_fnum - )) { - retval = TRUE; - break; - } - - free(fname); -#ifdef BACKSLASH_IN_FILENAME - free(sfname); -#endif - - return retval; -} - -/* - * Function given to ExpandGeneric() to obtain the list of autocommand group - * names. - */ -char_u *get_augroup_name(expand_T *xp, int idx) -{ - if (idx == augroups.ga_len) /* add "END" add the end */ - return (char_u *)"END"; - if (idx >= augroups.ga_len) /* end of list */ - return NULL; - if (AUGROUP_NAME(idx) == NULL) /* skip deleted entries */ - return (char_u *)""; - return AUGROUP_NAME(idx); /* return a name */ -} - -static int include_groups = FALSE; - -char_u * -set_context_in_autocmd ( - expand_T *xp, - char_u *arg, - int doautocmd /* TRUE for :doauto*, FALSE for :autocmd */ -) -{ - char_u *p; - int group; - - /* check for a group name, skip it if present */ - include_groups = FALSE; - p = arg; - group = au_get_grouparg(&arg); - if (group == AUGROUP_ERROR) - return NULL; - /* If there only is a group name that's what we expand. */ - if (*arg == NUL && group != AUGROUP_ALL && !vim_iswhite(arg[-1])) { - arg = p; - group = AUGROUP_ALL; - } - - /* skip over event name */ - for (p = arg; *p != NUL && !vim_iswhite(*p); ++p) - if (*p == ',') - arg = p + 1; - if (*p == NUL) { - if (group == AUGROUP_ALL) - include_groups = TRUE; - xp->xp_context = EXPAND_EVENTS; /* expand event name */ - xp->xp_pattern = arg; - return NULL; - } - - /* skip over pattern */ - arg = skipwhite(p); - while (*arg && (!vim_iswhite(*arg) || arg[-1] == '\\')) - arg++; - if (*arg) - return arg; /* expand (next) command */ - - if (doautocmd) - xp->xp_context = EXPAND_FILES; /* expand file names */ - else - xp->xp_context = EXPAND_NOTHING; /* pattern is not expanded */ - return NULL; -} - -/* - * Function given to ExpandGeneric() to obtain the list of event names. - */ -char_u *get_event_name(expand_T *xp, int idx) -{ - if (idx < augroups.ga_len) { /* First list group names, if wanted */ - if (!include_groups || AUGROUP_NAME(idx) == NULL) - return (char_u *)""; /* skip deleted entries */ - return AUGROUP_NAME(idx); /* return a name */ - } - return (char_u *)event_names[idx - augroups.ga_len].name; -} - - -/* - * Return TRUE if autocmd is supported. - */ -int autocmd_supported(char_u *name) -{ - char_u *p; - - return event_name2nr(name, &p) != NUM_EVENTS; -} - -/* - * Return TRUE if an autocommand is defined for a group, event and - * pattern: The group can be omitted to accept any group. "event" and "pattern" - * can be NULL to accept any event and pattern. "pattern" can be NULL to accept - * any pattern. Buffer-local patterns or are accepted. - * Used for: - * exists("#Group") or - * exists("#Group#Event") or - * exists("#Group#Event#pat") or - * exists("#Event") or - * exists("#Event#pat") - */ -int au_exists(char_u *arg) -{ - char_u *arg_save; - char_u *pattern = NULL; - char_u *event_name; - char_u *p; - event_T event; - AutoPat *ap; - buf_T *buflocal_buf = NULL; - int group; - int retval = FALSE; - - /* Make a copy so that we can change the '#' chars to a NUL. */ - arg_save = vim_strsave(arg); - if (arg_save == NULL) - return FALSE; - p = vim_strchr(arg_save, '#'); - if (p != NULL) - *p++ = NUL; - - /* First, look for an autocmd group name */ - group = au_find_group(arg_save); - if (group == AUGROUP_ERROR) { - /* Didn't match a group name, assume the first argument is an event. */ - group = AUGROUP_ALL; - event_name = arg_save; - } else { - if (p == NULL) { - /* "Group": group name is present and it's recognized */ - retval = TRUE; - goto theend; - } - - /* Must be "Group#Event" or "Group#Event#pat". */ - event_name = p; - p = vim_strchr(event_name, '#'); - if (p != NULL) - *p++ = NUL; /* "Group#Event#pat" */ - } - - pattern = p; /* "pattern" is NULL when there is no pattern */ - - /* find the index (enum) for the event name */ - event = event_name2nr(event_name, &p); - - /* return FALSE if the event name is not recognized */ - if (event == NUM_EVENTS) - goto theend; - - /* Find the first autocommand for this event. - * If there isn't any, return FALSE; - * If there is one and no pattern given, return TRUE; */ - ap = first_autopat[(int)event]; - if (ap == NULL) - goto theend; - - /* if pattern is "", special handling is needed which uses curbuf */ - /* for pattern ", fnamecmp() will work fine */ - if (pattern != NULL && STRICMP(pattern, "") == 0) - buflocal_buf = curbuf; - - /* Check if there is an autocommand with the given pattern. */ - for (; ap != NULL; ap = ap->next) - /* only use a pattern when it has not been removed and has commands. */ - /* For buffer-local autocommands, fnamecmp() works fine. */ - if (ap->pat != NULL && ap->cmds != NULL - && (group == AUGROUP_ALL || ap->group == group) - && (pattern == NULL - || (buflocal_buf == NULL - ? fnamecmp(ap->pat, pattern) == 0 - : ap->buflocal_nr == buflocal_buf->b_fnum))) { - retval = TRUE; - break; - } - -theend: - free(arg_save); - return retval; -} - - - -/* - * Try matching a filename with a "pattern" ("prog" is NULL), or use the - * precompiled regprog "prog" ("pattern" is NULL). That avoids calling - * vim_regcomp() often. - * Used for autocommands and 'wildignore'. - * Returns TRUE if there is a match, FALSE otherwise. - */ -int -match_file_pat ( - char_u *pattern, /* pattern to match with */ - regprog_T *prog, /* pre-compiled regprog or NULL */ - char_u *fname, /* full path of file name */ - char_u *sfname, /* short file name or NULL */ - char_u *tail, /* tail of path */ - int allow_dirs /* allow matching with dir */ -) -{ - regmatch_T regmatch; - int result = FALSE; -#ifdef FEAT_OSFILETYPE - int no_pattern = FALSE; /* TRUE if check is filetype only */ - char_u *type_start; - char_u c; - int match = FALSE; -#endif - - regmatch.rm_ic = p_fic; /* ignore case if 'fileignorecase' is set */ -#ifdef FEAT_OSFILETYPE - if (*pattern == '<') { - /* There is a filetype condition specified with this pattern. - * Check the filetype matches first. If not, don't bother with the - * pattern (set regprog to NULL). - * Always use magic for the regexp. - */ - - for (type_start = pattern + 1; (c = *pattern); pattern++) { - if ((c == ';' || c == '>') && match == FALSE) { - *pattern = NUL; /* Terminate the string */ - /* TODO: match with 'filetype' of buffer that "fname" comes - * from. */ - match = mch_check_filetype(fname, type_start); - *pattern = c; /* Restore the terminator */ - type_start = pattern + 1; - } - if (c == '>') - break; - } - - /* (c should never be NUL, but check anyway) */ - if (match == FALSE || c == NUL) - regmatch.regprog = NULL; /* Doesn't match - don't check pat. */ - else if (*pattern == NUL) { - regmatch.regprog = NULL; /* Vim will try to free regprog later */ - no_pattern = TRUE; /* Always matches - don't check pat. */ - } else - regmatch.regprog = vim_regcomp(pattern + 1, RE_MAGIC); - } else -#endif - { - if (prog != NULL) - regmatch.regprog = prog; - else - regmatch.regprog = vim_regcomp(pattern, RE_MAGIC); - } - - /* - * Try for a match with the pattern with: - * 1. the full file name, when the pattern has a '/'. - * 2. the short file name, when the pattern has a '/'. - * 3. the tail of the file name, when the pattern has no '/'. - */ - if ( -#ifdef FEAT_OSFILETYPE - /* If the check is for a filetype only and we don't care - * about the path then skip all the regexp stuff. - */ - no_pattern || -#endif - (regmatch.regprog != NULL - && ((allow_dirs - && (vim_regexec(®match, fname, (colnr_T)0) - || (sfname != NULL - && vim_regexec(®match, sfname, (colnr_T)0)))) - || (!allow_dirs && vim_regexec(®match, tail, (colnr_T)0))))) - result = TRUE; - - if (prog == NULL) - vim_regfree(regmatch.regprog); - return result; -} - -/* - * Return TRUE if a file matches with a pattern in "list". - * "list" is a comma-separated list of patterns, like 'wildignore'. - * "sfname" is the short file name or NULL, "ffname" the long file name. - */ -int match_file_list(char_u *list, char_u *sfname, char_u *ffname) -{ - char_u buf[100]; - char_u *tail; - char_u *regpat; - char allow_dirs; - int match; - char_u *p; - - tail = path_tail(sfname); - - /* try all patterns in 'wildignore' */ - p = list; - while (*p) { - copy_option_part(&p, buf, 100, ","); - regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, FALSE); - if (regpat == NULL) - break; - match = match_file_pat(regpat, NULL, ffname, sfname, - tail, (int)allow_dirs); - free(regpat); - if (match) - return TRUE; - } - return FALSE; -} - -/* - * Convert the given pattern "pat" which has shell style wildcards in it, into - * a regular expression, and return the result in allocated memory. If there - * is a directory path separator to be matched, then TRUE is put in - * allow_dirs, otherwise FALSE is put there -- webb. - * Handle backslashes before special characters, like "\*" and "\ ". - * - * If FEAT_OSFILETYPE defined then pass initial through unchanged. Eg: - * 'myfile' becomes '^myfile$' -- leonard. - * - * Returns NULL on failure. - */ -char_u * -file_pat_to_reg_pat ( - char_u *pat, - char_u *pat_end, /* first char after pattern or NULL */ - char *allow_dirs, /* Result passed back out in here */ - int no_bslash /* Don't use a backward slash as pathsep */ -) -{ - size_t size; - char_u *endp; - char_u *reg_pat; - char_u *p; - int i; - int nested = 0; - int add_dollar = TRUE; -#ifdef FEAT_OSFILETYPE - int check_length = 0; -#endif - - if (allow_dirs != NULL) - *allow_dirs = FALSE; - if (pat_end == NULL) - pat_end = pat + STRLEN(pat); - -#ifdef FEAT_OSFILETYPE - /* Find out how much of the string is the filetype check */ - if (*pat == '<') { - /* Count chars until the next '>' */ - for (p = pat + 1; p < pat_end && *p != '>'; p++) - ; - if (p < pat_end) { - /* Pattern is of the form <.*>.* */ - check_length = p - pat + 1; - if (p + 1 >= pat_end) { - /* The 'pattern' is a filetype check ONLY */ - reg_pat = xmemdupz(pat, (size_t)check_length); - return reg_pat; - } - } - /* else: there was no closing '>' - assume it was a normal pattern */ - - } - pat += check_length; - size = 2 + (size_t)check_length; -#else - size = 2; /* '^' at start, '$' at end */ -#endif - - for (p = pat; p < pat_end; p++) { - switch (*p) { - case '*': - case '.': - case ',': - case '{': - case '}': - case '~': - size += 2; /* extra backslash */ - break; -#ifdef BACKSLASH_IN_FILENAME - case '\\': - case '/': - size += 4; /* could become "[\/]" */ - break; -#endif - default: - size++; - if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) { - ++p; - ++size; - } - break; - } - } - reg_pat = xmalloc(size + 1); - -#ifdef FEAT_OSFILETYPE - /* Copy the type check in to the start. */ - if (check_length) - memmove(reg_pat, pat - check_length, (size_t)check_length); - i = check_length; -#else - i = 0; -#endif - - if (pat[0] == '*') - while (pat[0] == '*' && pat < pat_end - 1) - pat++; - else - reg_pat[i++] = '^'; - endp = pat_end - 1; - if (*endp == '*') { - while (endp - pat > 0 && *endp == '*') - endp--; - add_dollar = FALSE; - } - for (p = pat; *p && nested >= 0 && p <= endp; p++) { - switch (*p) { - case '*': - reg_pat[i++] = '.'; - reg_pat[i++] = '*'; - while (p[1] == '*') /* "**" matches like "*" */ - ++p; - break; - case '.': - case '~': - reg_pat[i++] = '\\'; - reg_pat[i++] = *p; - break; - case '?': - reg_pat[i++] = '.'; - break; - case '\\': - if (p[1] == NUL) - break; -#ifdef BACKSLASH_IN_FILENAME - if (!no_bslash) { - /* translate: - * "\x" to "\\x" e.g., "dir\file" - * "\*" to "\\.*" e.g., "dir\*.c" - * "\?" to "\\." e.g., "dir\??.c" - * "\+" to "\+" e.g., "fileX\+.c" - */ - if ((vim_isfilec(p[1]) || p[1] == '*' || p[1] == '?') - && p[1] != '+') { - reg_pat[i++] = '['; - reg_pat[i++] = '\\'; - reg_pat[i++] = '/'; - reg_pat[i++] = ']'; - if (allow_dirs != NULL) - *allow_dirs = TRUE; - break; - } - } -#endif - /* Undo escaping from ExpandEscape(): - * foo\?bar -> foo?bar - * foo\%bar -> foo%bar - * foo\,bar -> foo,bar - * foo\ bar -> foo bar - * Don't unescape \, * and others that are also special in a - * regexp. - * An escaped { must be unescaped since we use magic not - * verymagic. Use "\\\{n,m\}"" to get "\{n,m}". - */ - if (*++p == '?' -#ifdef BACKSLASH_IN_FILENAME - && no_bslash -#endif - ) - reg_pat[i++] = '?'; - else if (*p == ',' || *p == '%' || *p == '#' - || *p == ' ' || *p == '{' || *p == '}') - reg_pat[i++] = *p; - else if (*p == '\\' && p[1] == '\\' && p[2] == '{') { - reg_pat[i++] = '\\'; - reg_pat[i++] = '{'; - p += 2; - } else { - if (allow_dirs != NULL && vim_ispathsep(*p) -#ifdef BACKSLASH_IN_FILENAME - && (!no_bslash || *p != '\\') -#endif - ) - *allow_dirs = TRUE; - reg_pat[i++] = '\\'; - reg_pat[i++] = *p; - } - break; -#ifdef BACKSLASH_IN_FILENAME - case '/': - reg_pat[i++] = '['; - reg_pat[i++] = '\\'; - reg_pat[i++] = '/'; - reg_pat[i++] = ']'; - if (allow_dirs != NULL) - *allow_dirs = TRUE; - break; -#endif - case '{': - reg_pat[i++] = '\\'; - reg_pat[i++] = '('; - nested++; - break; - case '}': - reg_pat[i++] = '\\'; - reg_pat[i++] = ')'; - --nested; - break; - case ',': - if (nested) { - reg_pat[i++] = '\\'; - reg_pat[i++] = '|'; - } else - reg_pat[i++] = ','; - break; - default: - if (enc_dbcs != 0 && (*mb_ptr2len)(p) > 1) - reg_pat[i++] = *p++; - else if (allow_dirs != NULL && vim_ispathsep(*p)) - *allow_dirs = TRUE; - reg_pat[i++] = *p; - break; - } - } - if (add_dollar) - reg_pat[i++] = '$'; - reg_pat[i] = NUL; - if (nested != 0) { - if (nested < 0) - EMSG(_("E219: Missing {.")); - else - EMSG(_("E220: Missing }.")); - free(reg_pat); - reg_pat = NULL; - } - return reg_pat; -} - -#if defined(EINTR) || defined(PROTO) -/* - * Version of read() that retries when interrupted by EINTR (possibly - * by a SIGWINCH). - */ -long read_eintr(fd, buf, bufsize) -int fd; -void *buf; -size_t bufsize; -{ - long ret; - - for (;; ) { - ret = vim_read(fd, buf, bufsize); - if (ret >= 0 || errno != EINTR) - break; - } - return ret; -} - -/* - * Version of write() that retries when interrupted by EINTR (possibly - * by a SIGWINCH). - */ -long write_eintr(fd, buf, bufsize) -int fd; -void *buf; -size_t bufsize; -{ - long ret = 0; - long wlen; - - /* Repeat the write() so long it didn't fail, other than being interrupted - * by a signal. */ - while (ret < (long)bufsize) { - wlen = vim_write(fd, (char *)buf + ret, bufsize - ret); - if (wlen < 0) { - if (errno != EINTR) - break; - } else - ret += wlen; - } - return ret; -} -#endif diff --git a/src/fileio.h b/src/fileio.h deleted file mode 100644 index 960ba26fa3..0000000000 --- a/src/fileio.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef NEOVIM_FILEIO_H -#define NEOVIM_FILEIO_H - -#include "buffer_defs.h" -#include "os/os.h" - -/* - * Struct to save values in before executing autocommands for a buffer that is - * not the current buffer. Without FEAT_AUTOCMD only "curbuf" is remembered. - */ -typedef struct { - buf_T *save_curbuf; /* saved curbuf */ - int use_aucmd_win; /* using aucmd_win */ - win_T *save_curwin; /* saved curwin */ - win_T *new_curwin; /* new curwin */ - buf_T *new_curbuf; /* new curbuf */ - char_u *globaldir; /* saved value of globaldir */ -} aco_save_T; - -/* fileio.c */ -void filemess(buf_T *buf, char_u *name, char_u *s, int attr); -int readfile(char_u *fname, char_u *sfname, linenr_T from, - linenr_T lines_to_skip, linenr_T lines_to_read, exarg_T *eap, - int flags); -void prep_exarg(exarg_T *eap, buf_T *buf); -void set_file_options(int set_options, exarg_T *eap); -void set_forced_fenc(exarg_T *eap); -int prepare_crypt_read(FILE *fp); -char_u *prepare_crypt_write(buf_T *buf, int *lenp); -int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, - linenr_T end, exarg_T *eap, int append, int forceit, - int reset_changed, - int filtering); -void msg_add_fname(buf_T *buf, char_u *fname); -void msg_add_lines(int insert_space, long lnum, off_t nchars); -void shorten_fnames(int force); -char_u *modname(char_u *fname, char_u *ext, int prepend_dot); -int vim_fgets(char_u *buf, int size, FILE *fp); -int tag_fgets(char_u *buf, int size, FILE *fp); -int vim_rename(char_u *from, char_u *to); -int check_timestamps(int focus); -int buf_check_timestamp(buf_T *buf, int focus); -void buf_reload(buf_T *buf, int orig_mode); -void buf_store_file_info(buf_T *buf, FileInfo *file_info); -void write_lnum_adjust(linenr_T offset); -void vim_deltempdir(void); -char_u *vim_tempname(int extra_char); -void forward_slash(char_u *fname); -void aubuflocal_remove(buf_T *buf); -int au_has_group(char_u *name); -void do_augroup(char_u *arg, int del_group); -void free_all_autocmds(void); -int check_ei(void); -char_u *au_event_disable(char *what); -void au_event_restore(char_u *old_ei); -void do_autocmd(char_u *arg, int forceit); -int do_doautocmd(char_u *arg, int do_msg); -void ex_doautoall(exarg_T *eap); -int check_nomodeline(char_u **argp); -void aucmd_prepbuf(aco_save_T *aco, buf_T *buf); -void aucmd_restbuf(aco_save_T *aco); -int apply_autocmds(event_T event, char_u *fname, char_u *fname_io, - int force, - buf_T *buf); -int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, - int force, buf_T *buf, - int *retval); -int has_cursorhold(void); -int trigger_cursorhold(void); -int has_cursormoved(void); -int has_cursormovedI(void); -int has_textchanged(void); -int has_textchangedI(void); -int has_insertcharpre(void); -void block_autocmds(void); -void unblock_autocmds(void); -char_u *getnextac(int c, void *cookie, int indent); -int has_autocmd(event_T event, char_u *sfname, buf_T *buf); -char_u *get_augroup_name(expand_T *xp, int idx); -char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd); -char_u *get_event_name(expand_T *xp, int idx); -int autocmd_supported(char_u *name); -int au_exists(char_u *arg); -int match_file_pat(char_u *pattern, regprog_T *prog, char_u *fname, - char_u *sfname, char_u *tail, - int allow_dirs); -int match_file_list(char_u *list, char_u *sfname, char_u *ffname); -char_u *file_pat_to_reg_pat(char_u *pat, char_u *pat_end, - char *allow_dirs, - int no_bslash); -long read_eintr(int fd, void *buf, size_t bufsize); -long write_eintr(int fd, void *buf, size_t bufsize); - -#endif /* NEOVIM_FILEIO_H */ diff --git a/src/fold.c b/src/fold.c deleted file mode 100644 index 143b35ea10..0000000000 --- a/src/fold.c +++ /dev/null @@ -1,3033 +0,0 @@ -/* vim: set fdm=marker fdl=1 fdc=3 - * - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * fold.c: code for folding - */ - -#include - -#include "vim.h" -#include "fold.h" -#include "charset.h" -#include "diff.h" -#include "eval.h" -#include "ex_docmd.h" -#include "indent.h" -#include "mark.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "garray.h" -#include "move.h" -#include "option.h" -#include "screen.h" -#include "syntax.h" -#include "undo.h" - -/* local declarations. {{{1 */ -/* typedef fold_T {{{2 */ -/* - * The toplevel folds for each window are stored in the w_folds growarray. - * Each toplevel fold can contain an array of second level folds in the - * fd_nested growarray. - * The info stored in both growarrays is the same: An array of fold_T. - */ -typedef struct { - linenr_T fd_top; /* first line of fold; for nested fold - * relative to parent */ - linenr_T fd_len; /* number of lines in the fold */ - garray_T fd_nested; /* array of nested folds */ - char fd_flags; /* see below */ - char fd_small; /* TRUE, FALSE or MAYBE: fold smaller than - 'foldminlines'; MAYBE applies to nested - folds too */ -} fold_T; - -#define FD_OPEN 0 /* fold is open (nested ones can be closed) */ -#define FD_CLOSED 1 /* fold is closed */ -#define FD_LEVEL 2 /* depends on 'foldlevel' (nested folds too) */ - -#define MAX_LEVEL 20 /* maximum fold depth */ - -/* static functions {{{2 */ -static void newFoldLevelWin(win_T *wp); -static int checkCloseRec(garray_T *gap, linenr_T lnum, int level); -static int foldFind(garray_T *gap, linenr_T lnum, fold_T **fpp); -static int foldLevelWin(win_T *wp, linenr_T lnum); -static void checkupdate(win_T *wp); -static void setFoldRepeat(linenr_T lnum, long count, int do_open); -static linenr_T setManualFold(linenr_T lnum, int opening, int recurse, - int *donep); -static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, - int recurse, - int *donep); -static void foldOpenNested(fold_T *fpr); -static void deleteFoldEntry(garray_T *gap, int idx, int recursive); -static void foldMarkAdjustRecurse(garray_T *gap, linenr_T line1, - linenr_T line2, long amount, - long amount_after); -static int getDeepestNestingRecurse(garray_T *gap); -static int check_closed(win_T *win, fold_T *fp, int *use_levelp, - int level, int *maybe_smallp, - linenr_T lnum_off); -static void checkSmall(win_T *wp, fold_T *fp, linenr_T lnum_off); -static void setSmallMaybe(garray_T *gap); -static void foldCreateMarkers(linenr_T start, linenr_T end); -static void foldAddMarker(linenr_T lnum, char_u *marker, int markerlen); -static void deleteFoldMarkers(fold_T *fp, int recursive, - linenr_T lnum_off); -static void foldDelMarker(linenr_T lnum, char_u *marker, int markerlen); -static void foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot); -static void parseMarker(win_T *wp); - -static char *e_nofold = N_("E490: No fold found"); - -/* - * While updating the folds lines between invalid_top and invalid_bot have an - * undefined fold level. Only used for the window currently being updated. - */ -static linenr_T invalid_top = (linenr_T)0; -static linenr_T invalid_bot = (linenr_T)0; - -/* - * When using 'foldexpr' we sometimes get the level of the next line, which - * calls foldlevel() to get the level of the current line, which hasn't been - * stored yet. To get around this chicken-egg problem the level of the - * previous line is stored here when available. prev_lnum is zero when the - * level is not available. - */ -static linenr_T prev_lnum = 0; -static int prev_lnum_lvl = -1; - -/* Flags used for "done" argument of setManualFold. */ -#define DONE_NOTHING 0 -#define DONE_ACTION 1 /* did close or open a fold */ -#define DONE_FOLD 2 /* did find a fold */ - -static int foldstartmarkerlen; -static char_u *foldendmarker; -static int foldendmarkerlen; - -/* Exported folding functions. {{{1 */ -/* copyFoldingState() {{{2 */ -/* - * Copy that folding state from window "wp_from" to window "wp_to". - */ -void copyFoldingState(win_T *wp_from, win_T *wp_to) -{ - wp_to->w_fold_manual = wp_from->w_fold_manual; - wp_to->w_foldinvalid = wp_from->w_foldinvalid; - cloneFoldGrowArray(&wp_from->w_folds, &wp_to->w_folds); -} - -/* hasAnyFolding() {{{2 */ -/* - * Return TRUE if there may be folded lines in the current window. - */ -int hasAnyFolding(win_T *win) -{ - /* very simple now, but can become more complex later */ - return win->w_p_fen - && (!foldmethodIsManual(win) || win->w_folds.ga_len > 0); -} - -/* hasFolding() {{{2 */ -/* - * Return TRUE if line "lnum" in the current window is part of a closed - * fold. - * When returning TRUE, *firstp and *lastp are set to the first and last - * lnum of the sequence of folded lines (skipped when NULL). - */ -int hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp) -{ - return hasFoldingWin(curwin, lnum, firstp, lastp, TRUE, NULL); -} - -/* hasFoldingWin() {{{2 */ -int -hasFoldingWin ( - win_T *win, - linenr_T lnum, - linenr_T *firstp, - linenr_T *lastp, - int cache, /* when TRUE: use cached values of window */ - foldinfo_T *infop /* where to store fold info */ -) -{ - int had_folded = FALSE; - linenr_T first = 0; - linenr_T last = 0; - linenr_T lnum_rel = lnum; - int x; - fold_T *fp; - int level = 0; - int use_level = FALSE; - int maybe_small = FALSE; - garray_T *gap; - int low_level = 0;; - - checkupdate(win); - /* - * Return quickly when there is no folding at all in this window. - */ - if (!hasAnyFolding(win)) { - if (infop != NULL) - infop->fi_level = 0; - return FALSE; - } - - if (cache) { - /* - * First look in cached info for displayed lines. This is probably - * the fastest, but it can only be used if the entry is still valid. - */ - x = find_wl_entry(win, lnum); - if (x >= 0) { - first = win->w_lines[x].wl_lnum; - last = win->w_lines[x].wl_lastlnum; - had_folded = win->w_lines[x].wl_folded; - } - } - - if (first == 0) { - /* - * Recursively search for a fold that contains "lnum". - */ - gap = &win->w_folds; - for (;; ) { - if (!foldFind(gap, lnum_rel, &fp)) - break; - - /* Remember lowest level of fold that starts in "lnum". */ - if (lnum_rel == fp->fd_top && low_level == 0) - low_level = level + 1; - - first += fp->fd_top; - last += fp->fd_top; - - /* is this fold closed? */ - had_folded = check_closed(win, fp, &use_level, level, - &maybe_small, lnum - lnum_rel); - if (had_folded) { - /* Fold closed: Set last and quit loop. */ - last += fp->fd_len - 1; - break; - } - - /* Fold found, but it's open: Check nested folds. Line number is - * relative to containing fold. */ - gap = &fp->fd_nested; - lnum_rel -= fp->fd_top; - ++level; - } - } - - if (!had_folded) { - if (infop != NULL) { - infop->fi_level = level; - infop->fi_lnum = lnum - lnum_rel; - infop->fi_low_level = low_level == 0 ? level : low_level; - } - return FALSE; - } - - if (lastp != NULL) - *lastp = last; - if (firstp != NULL) - *firstp = first; - if (infop != NULL) { - infop->fi_level = level + 1; - infop->fi_lnum = first; - infop->fi_low_level = low_level == 0 ? level + 1 : low_level; - } - return TRUE; -} - -/* foldLevel() {{{2 */ -/* - * Return fold level at line number "lnum" in the current window. - */ -int foldLevel(linenr_T lnum) -{ - /* While updating the folds lines between invalid_top and invalid_bot have - * an undefined fold level. Otherwise update the folds first. */ - if (invalid_top == (linenr_T)0) - checkupdate(curwin); - else if (lnum == prev_lnum && prev_lnum_lvl >= 0) - return prev_lnum_lvl; - else if (lnum >= invalid_top && lnum <= invalid_bot) - return -1; - - /* Return quickly when there is no folding at all in this window. */ - if (!hasAnyFolding(curwin)) - return 0; - - return foldLevelWin(curwin, lnum); -} - -/* lineFolded() {{{2 */ -/* - * Low level function to check if a line is folded. Doesn't use any caching. - * Return TRUE if line is folded. - * Return FALSE if line is not folded. - * Return MAYBE if the line is folded when next to a folded line. - */ -int lineFolded(win_T *win, linenr_T lnum) -{ - return foldedCount(win, lnum, NULL) != 0; -} - -/* foldedCount() {{{2 */ -/* - * Count the number of lines that are folded at line number "lnum". - * Normally "lnum" is the first line of a possible fold, and the returned - * number is the number of lines in the fold. - * Doesn't use caching from the displayed window. - * Returns number of folded lines from "lnum", or 0 if line is not folded. - * When "infop" is not NULL, fills *infop with the fold level info. - */ -long foldedCount(win_T *win, linenr_T lnum, foldinfo_T *infop) -{ - linenr_T last; - - if (hasFoldingWin(win, lnum, NULL, &last, FALSE, infop)) - return (long)(last - lnum + 1); - return 0; -} - -/* foldmethodIsManual() {{{2 */ -/* - * Return TRUE if 'foldmethod' is "manual" - */ -int foldmethodIsManual(win_T *wp) -{ - return wp->w_p_fdm[3] == 'u'; -} - -/* foldmethodIsIndent() {{{2 */ -/* - * Return TRUE if 'foldmethod' is "indent" - */ -int foldmethodIsIndent(win_T *wp) -{ - return wp->w_p_fdm[0] == 'i'; -} - -/* foldmethodIsExpr() {{{2 */ -/* - * Return TRUE if 'foldmethod' is "expr" - */ -int foldmethodIsExpr(win_T *wp) -{ - return wp->w_p_fdm[1] == 'x'; -} - -/* foldmethodIsMarker() {{{2 */ -/* - * Return TRUE if 'foldmethod' is "marker" - */ -int foldmethodIsMarker(win_T *wp) -{ - return wp->w_p_fdm[2] == 'r'; -} - -/* foldmethodIsSyntax() {{{2 */ -/* - * Return TRUE if 'foldmethod' is "syntax" - */ -int foldmethodIsSyntax(win_T *wp) -{ - return wp->w_p_fdm[0] == 's'; -} - -/* foldmethodIsDiff() {{{2 */ -/* - * Return TRUE if 'foldmethod' is "diff" - */ -int foldmethodIsDiff(win_T *wp) -{ - return wp->w_p_fdm[0] == 'd'; -} - -/* closeFold() {{{2 */ -/* - * Close fold for current window at line "lnum". - * Repeat "count" times. - */ -void closeFold(linenr_T lnum, long count) -{ - setFoldRepeat(lnum, count, FALSE); -} - -/* closeFoldRecurse() {{{2 */ -/* - * Close fold for current window at line "lnum" recursively. - */ -void closeFoldRecurse(linenr_T lnum) -{ - (void)setManualFold(lnum, FALSE, TRUE, NULL); -} - -/* opFoldRange() {{{2 */ -/* - * Open or Close folds for current window in lines "first" to "last". - * Used for "zo", "zO", "zc" and "zC" in Visual mode. - */ -void -opFoldRange ( - linenr_T first, - linenr_T last, - int opening, /* TRUE to open, FALSE to close */ - int recurse, /* TRUE to do it recursively */ - int had_visual /* TRUE when Visual selection used */ -) -{ - int done = DONE_NOTHING; /* avoid error messages */ - linenr_T lnum; - linenr_T lnum_next; - - for (lnum = first; lnum <= last; lnum = lnum_next + 1) { - lnum_next = lnum; - /* Opening one level only: next fold to open is after the one going to - * be opened. */ - if (opening && !recurse) - (void)hasFolding(lnum, NULL, &lnum_next); - (void)setManualFold(lnum, opening, recurse, &done); - /* Closing one level only: next line to close a fold is after just - * closed fold. */ - if (!opening && !recurse) - (void)hasFolding(lnum, NULL, &lnum_next); - } - if (done == DONE_NOTHING) - EMSG(_(e_nofold)); - /* Force a redraw to remove the Visual highlighting. */ - if (had_visual) - redraw_curbuf_later(INVERTED); -} - -/* openFold() {{{2 */ -/* - * Open fold for current window at line "lnum". - * Repeat "count" times. - */ -void openFold(linenr_T lnum, long count) -{ - setFoldRepeat(lnum, count, TRUE); -} - -/* openFoldRecurse() {{{2 */ -/* - * Open fold for current window at line "lnum" recursively. - */ -void openFoldRecurse(linenr_T lnum) -{ - (void)setManualFold(lnum, TRUE, TRUE, NULL); -} - -/* foldOpenCursor() {{{2 */ -/* - * Open folds until the cursor line is not in a closed fold. - */ -void foldOpenCursor(void) -{ - int done; - - checkupdate(curwin); - if (hasAnyFolding(curwin)) - for (;; ) { - done = DONE_NOTHING; - (void)setManualFold(curwin->w_cursor.lnum, TRUE, FALSE, &done); - if (!(done & DONE_ACTION)) - break; - } -} - -/* newFoldLevel() {{{2 */ -/* - * Set new foldlevel for current window. - */ -void newFoldLevel(void) -{ - newFoldLevelWin(curwin); - - if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { - win_T *wp; - - /* - * Set the same foldlevel in other windows in diff mode. - */ - FOR_ALL_WINDOWS(wp) - { - if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { - wp->w_p_fdl = curwin->w_p_fdl; - newFoldLevelWin(wp); - } - } - } -} - -static void newFoldLevelWin(win_T *wp) -{ - fold_T *fp; - int i; - - checkupdate(wp); - if (wp->w_fold_manual) { - /* Set all flags for the first level of folds to FD_LEVEL. Following - * manual open/close will then change the flags to FD_OPEN or - * FD_CLOSED for those folds that don't use 'foldlevel'. */ - fp = (fold_T *)wp->w_folds.ga_data; - for (i = 0; i < wp->w_folds.ga_len; ++i) - fp[i].fd_flags = FD_LEVEL; - wp->w_fold_manual = FALSE; - } - changed_window_setting_win(wp); -} - -/* foldCheckClose() {{{2 */ -/* - * Apply 'foldlevel' to all folds that don't contain the cursor. - */ -void foldCheckClose(void) -{ - if (*p_fcl != NUL) { /* can only be "all" right now */ - checkupdate(curwin); - if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum, - (int)curwin->w_p_fdl)) - changed_window_setting(); - } -} - -/* checkCloseRec() {{{2 */ -static int checkCloseRec(garray_T *gap, linenr_T lnum, int level) -{ - fold_T *fp; - int retval = FALSE; - int i; - - fp = (fold_T *)gap->ga_data; - for (i = 0; i < gap->ga_len; ++i) { - /* Only manually opened folds may need to be closed. */ - if (fp[i].fd_flags == FD_OPEN) { - if (level <= 0 && (lnum < fp[i].fd_top - || lnum >= fp[i].fd_top + fp[i].fd_len)) { - fp[i].fd_flags = FD_LEVEL; - retval = TRUE; - } else - retval |= checkCloseRec(&fp[i].fd_nested, lnum - fp[i].fd_top, - level - 1); - } - } - return retval; -} - -/* foldCreateAllowed() {{{2 */ -/* - * Return TRUE if it's allowed to manually create or delete a fold. - * Give an error message and return FALSE if not. - */ -int foldManualAllowed(int create) -{ - if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) - return TRUE; - if (create) - EMSG(_("E350: Cannot create fold with current 'foldmethod'")); - else - EMSG(_("E351: Cannot delete fold with current 'foldmethod'")); - return FALSE; -} - -/* foldCreate() {{{2 */ -/* - * Create a fold from line "start" to line "end" (inclusive) in the current - * window. - */ -void foldCreate(linenr_T start, linenr_T end) -{ - fold_T *fp; - garray_T *gap; - garray_T fold_ga; - int i, j; - int cont; - int use_level = FALSE; - int closed = FALSE; - int level = 0; - linenr_T start_rel = start; - linenr_T end_rel = end; - - if (start > end) { - /* reverse the range */ - end = start_rel; - start = end_rel; - start_rel = start; - end_rel = end; - } - - /* When 'foldmethod' is "marker" add markers, which creates the folds. */ - if (foldmethodIsMarker(curwin)) { - foldCreateMarkers(start, end); - return; - } - - checkupdate(curwin); - - /* Find the place to insert the new fold. */ - gap = &curwin->w_folds; - for (;; ) { - if (!foldFind(gap, start_rel, &fp)) - break; - if (fp->fd_top + fp->fd_len > end_rel) { - /* New fold is completely inside this fold: Go one level deeper. */ - gap = &fp->fd_nested; - start_rel -= fp->fd_top; - end_rel -= fp->fd_top; - if (use_level || fp->fd_flags == FD_LEVEL) { - use_level = TRUE; - if (level >= curwin->w_p_fdl) - closed = TRUE; - } else if (fp->fd_flags == FD_CLOSED) - closed = TRUE; - ++level; - } else { - /* This fold and new fold overlap: Insert here and move some folds - * inside the new fold. */ - break; - } - } - - i = (int)(fp - (fold_T *)gap->ga_data); - ga_grow(gap, 1); - { - fp = (fold_T *)gap->ga_data + i; - ga_init(&fold_ga, (int)sizeof(fold_T), 10); - - /* Count number of folds that will be contained in the new fold. */ - for (cont = 0; i + cont < gap->ga_len; ++cont) - if (fp[cont].fd_top > end_rel) - break; - if (cont > 0) { - ga_grow(&fold_ga, cont); - /* If the first fold starts before the new fold, let the new fold - * start there. Otherwise the existing fold would change. */ - if (start_rel > fp->fd_top) - start_rel = fp->fd_top; - - /* When last contained fold isn't completely contained, adjust end - * of new fold. */ - if (end_rel < fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1) - end_rel = fp[cont - 1].fd_top + fp[cont - 1].fd_len - 1; - /* Move contained folds to inside new fold. */ - memmove(fold_ga.ga_data, fp, sizeof(fold_T) * cont); - fold_ga.ga_len += cont; - i += cont; - - /* Adjust line numbers in contained folds to be relative to the - * new fold. */ - for (j = 0; j < cont; ++j) - ((fold_T *)fold_ga.ga_data)[j].fd_top -= start_rel; - } - /* Move remaining entries to after the new fold. */ - if (i < gap->ga_len) - memmove(fp + 1, (fold_T *)gap->ga_data + i, - sizeof(fold_T) * (gap->ga_len - i)); - gap->ga_len = gap->ga_len + 1 - cont; - - /* insert new fold */ - fp->fd_nested = fold_ga; - fp->fd_top = start_rel; - fp->fd_len = end_rel - start_rel + 1; - - /* We want the new fold to be closed. If it would remain open because - * of using 'foldlevel', need to adjust fd_flags of containing folds. - */ - if (use_level && !closed && level < curwin->w_p_fdl) - closeFold(start, 1L); - if (!use_level) - curwin->w_fold_manual = TRUE; - fp->fd_flags = FD_CLOSED; - fp->fd_small = MAYBE; - - /* redraw */ - changed_window_setting(); - } -} - - -/* deleteFold() {{{2 */ -/* - * Delete a fold at line "start" in the current window. - * When "end" is not 0, delete all folds from "start" to "end". - * When "recursive" is TRUE delete recursively. - */ -void -deleteFold ( - linenr_T start, - linenr_T end, - int recursive, - int had_visual /* TRUE when Visual selection used */ -) -{ - garray_T *gap; - fold_T *fp; - garray_T *found_ga; - fold_T *found_fp = NULL; - linenr_T found_off = 0; - int use_level; - int maybe_small = FALSE; - int level = 0; - linenr_T lnum = start; - linenr_T lnum_off; - int did_one = FALSE; - linenr_T first_lnum = MAXLNUM; - linenr_T last_lnum = 0; - - checkupdate(curwin); - - while (lnum <= end) { - /* Find the deepest fold for "start". */ - gap = &curwin->w_folds; - found_ga = NULL; - lnum_off = 0; - use_level = FALSE; - for (;; ) { - if (!foldFind(gap, lnum - lnum_off, &fp)) - break; - /* lnum is inside this fold, remember info */ - found_ga = gap; - found_fp = fp; - found_off = lnum_off; - - /* if "lnum" is folded, don't check nesting */ - if (check_closed(curwin, fp, &use_level, level, - &maybe_small, lnum_off)) - break; - - /* check nested folds */ - gap = &fp->fd_nested; - lnum_off += fp->fd_top; - ++level; - } - if (found_ga == NULL) { - ++lnum; - } else { - lnum = found_fp->fd_top + found_fp->fd_len + found_off; - - if (foldmethodIsManual(curwin)) - deleteFoldEntry(found_ga, - (int)(found_fp - (fold_T *)found_ga->ga_data), recursive); - else { - if (first_lnum > found_fp->fd_top + found_off) - first_lnum = found_fp->fd_top + found_off; - if (last_lnum < lnum) - last_lnum = lnum; - if (!did_one) - parseMarker(curwin); - deleteFoldMarkers(found_fp, recursive, found_off); - } - did_one = TRUE; - - /* redraw window */ - changed_window_setting(); - } - } - if (!did_one) { - EMSG(_(e_nofold)); - /* Force a redraw to remove the Visual highlighting. */ - if (had_visual) - redraw_curbuf_later(INVERTED); - } else - /* Deleting markers may make cursor column invalid. */ - check_cursor_col(); - - if (last_lnum > 0) - changed_lines(first_lnum, (colnr_T)0, last_lnum, 0L); -} - -/* clearFolding() {{{2 */ -/* - * Remove all folding for window "win". - */ -void clearFolding(win_T *win) -{ - deleteFoldRecurse(&win->w_folds); - win->w_foldinvalid = FALSE; -} - -/* foldUpdate() {{{2 */ -/* - * Update folds for changes in the buffer of a window. - * Note that inserted/deleted lines must have already been taken care of by - * calling foldMarkAdjust(). - * The changes in lines from top to bot (inclusive). - */ -void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) -{ - fold_T *fp; - - /* Mark all folds from top to bot as maybe-small. */ - (void)foldFind(&curwin->w_folds, top, &fp); - while (fp < (fold_T *)curwin->w_folds.ga_data + curwin->w_folds.ga_len - && fp->fd_top < bot) { - fp->fd_small = MAYBE; - ++fp; - } - - if (foldmethodIsIndent(wp) - || foldmethodIsExpr(wp) - || foldmethodIsMarker(wp) - || foldmethodIsDiff(wp) - || foldmethodIsSyntax(wp)) { - int save_got_int = got_int; - - /* reset got_int here, otherwise it won't work */ - got_int = FALSE; - foldUpdateIEMS(wp, top, bot); - got_int |= save_got_int; - } -} - -/* foldUpdateAll() {{{2 */ -/* - * Update all lines in a window for folding. - * Used when a fold setting changes or after reloading the buffer. - * The actual updating is postponed until fold info is used, to avoid doing - * every time a setting is changed or a syntax item is added. - */ -void foldUpdateAll(win_T *win) -{ - win->w_foldinvalid = TRUE; - redraw_win_later(win, NOT_VALID); -} - -/* foldMoveTo() {{{2 */ -/* - * If "updown" is FALSE: Move to the start or end of the fold. - * If "updown" is TRUE: move to fold at the same level. - * If not moved return FAIL. - */ -int -foldMoveTo ( - int updown, - int dir, /* FORWARD or BACKWARD */ - long count -) -{ - long n; - int retval = FAIL; - linenr_T lnum_off; - linenr_T lnum_found; - linenr_T lnum; - int use_level; - int maybe_small; - garray_T *gap; - fold_T *fp; - int level; - int last; - - checkupdate(curwin); - - /* Repeat "count" times. */ - for (n = 0; n < count; ++n) { - /* Find nested folds. Stop when a fold is closed. The deepest fold - * that moves the cursor is used. */ - lnum_off = 0; - gap = &curwin->w_folds; - use_level = FALSE; - maybe_small = FALSE; - lnum_found = curwin->w_cursor.lnum; - level = 0; - last = FALSE; - for (;; ) { - if (!foldFind(gap, curwin->w_cursor.lnum - lnum_off, &fp)) { - if (!updown) - break; - - /* When moving up, consider a fold above the cursor; when - * moving down consider a fold below the cursor. */ - if (dir == FORWARD) { - if (fp - (fold_T *)gap->ga_data >= gap->ga_len) - break; - --fp; - } else { - if (fp == (fold_T *)gap->ga_data) - break; - } - /* don't look for contained folds, they will always move - * the cursor too far. */ - last = TRUE; - } - - if (!last) { - /* Check if this fold is closed. */ - if (check_closed(curwin, fp, &use_level, level, - &maybe_small, lnum_off)) - last = TRUE; - - /* "[z" and "]z" stop at closed fold */ - if (last && !updown) - break; - } - - if (updown) { - if (dir == FORWARD) { - /* to start of next fold if there is one */ - if (fp + 1 - (fold_T *)gap->ga_data < gap->ga_len) { - lnum = fp[1].fd_top + lnum_off; - if (lnum > curwin->w_cursor.lnum) - lnum_found = lnum; - } - } else { - /* to end of previous fold if there is one */ - if (fp > (fold_T *)gap->ga_data) { - lnum = fp[-1].fd_top + lnum_off + fp[-1].fd_len - 1; - if (lnum < curwin->w_cursor.lnum) - lnum_found = lnum; - } - } - } else { - /* Open fold found, set cursor to its start/end and then check - * nested folds. */ - if (dir == FORWARD) { - lnum = fp->fd_top + lnum_off + fp->fd_len - 1; - if (lnum > curwin->w_cursor.lnum) - lnum_found = lnum; - } else { - lnum = fp->fd_top + lnum_off; - if (lnum < curwin->w_cursor.lnum) - lnum_found = lnum; - } - } - - if (last) - break; - - /* Check nested folds (if any). */ - gap = &fp->fd_nested; - lnum_off += fp->fd_top; - ++level; - } - if (lnum_found != curwin->w_cursor.lnum) { - if (retval == FAIL) - setpcmark(); - curwin->w_cursor.lnum = lnum_found; - curwin->w_cursor.col = 0; - retval = OK; - } else - break; - } - - return retval; -} - -/* foldInitWin() {{{2 */ -/* - * Init the fold info in a new window. - */ -void foldInitWin(win_T *new_win) -{ - ga_init(&new_win->w_folds, (int)sizeof(fold_T), 10); -} - -/* find_wl_entry() {{{2 */ -/* - * Find an entry in the win->w_lines[] array for buffer line "lnum". - * Only valid entries are considered (for entries where wl_valid is FALSE the - * line number can be wrong). - * Returns index of entry or -1 if not found. - */ -int find_wl_entry(win_T *win, linenr_T lnum) -{ - int i; - - for (i = 0; i < win->w_lines_valid; ++i) - if (win->w_lines[i].wl_valid) { - if (lnum < win->w_lines[i].wl_lnum) - return -1; - if (lnum <= win->w_lines[i].wl_lastlnum) - return i; - } - return -1; -} - -/* foldAdjustVisual() {{{2 */ -/* - * Adjust the Visual area to include any fold at the start or end completely. - */ -void foldAdjustVisual(void) -{ - pos_T *start, *end; - char_u *ptr; - - if (!VIsual_active || !hasAnyFolding(curwin)) - return; - - if (ltoreq(VIsual, curwin->w_cursor)) { - start = &VIsual; - end = &curwin->w_cursor; - } else { - start = &curwin->w_cursor; - end = &VIsual; - } - if (hasFolding(start->lnum, &start->lnum, NULL)) - start->col = 0; - if (hasFolding(end->lnum, NULL, &end->lnum)) { - ptr = ml_get(end->lnum); - end->col = (colnr_T)STRLEN(ptr); - if (end->col > 0 && *p_sel == 'o') - --end->col; - /* prevent cursor from moving on the trail byte */ - if (has_mbyte) - mb_adjust_cursor(); - } -} - -/* cursor_foldstart() {{{2 */ -/* - * Move the cursor to the first line of a closed fold. - */ -void foldAdjustCursor(void) -{ - (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); -} - -/* Internal functions for "fold_T" {{{1 */ -/* cloneFoldGrowArray() {{{2 */ -/* - * Will "clone" (i.e deep copy) a garray_T of folds. - */ -void cloneFoldGrowArray(garray_T *from, garray_T *to) -{ - int i; - fold_T *from_p; - fold_T *to_p; - - ga_init(to, from->ga_itemsize, from->ga_growsize); - - if (from->ga_len == 0) - return; - - ga_grow(to, from->ga_len); - - from_p = (fold_T *)from->ga_data; - to_p = (fold_T *)to->ga_data; - - for (i = 0; i < from->ga_len; i++) { - to_p->fd_top = from_p->fd_top; - to_p->fd_len = from_p->fd_len; - to_p->fd_flags = from_p->fd_flags; - to_p->fd_small = from_p->fd_small; - cloneFoldGrowArray(&from_p->fd_nested, &to_p->fd_nested); - ++to->ga_len; - ++from_p; - ++to_p; - } -} - -/* foldFind() {{{2 */ -/* - * Search for line "lnum" in folds of growarray "gap". - * Set *fpp to the fold struct for the fold that contains "lnum" or - * the first fold below it (careful: it can be beyond the end of the array!). - * Returns FALSE when there is no fold that contains "lnum". - */ -static int foldFind(garray_T *gap, linenr_T lnum, fold_T **fpp) -{ - linenr_T low, high; - fold_T *fp; - int i; - - /* - * Perform a binary search. - * "low" is lowest index of possible match. - * "high" is highest index of possible match. - */ - fp = (fold_T *)gap->ga_data; - low = 0; - high = gap->ga_len - 1; - while (low <= high) { - i = (low + high) / 2; - if (fp[i].fd_top > lnum) - /* fold below lnum, adjust high */ - high = i - 1; - else if (fp[i].fd_top + fp[i].fd_len <= lnum) - /* fold above lnum, adjust low */ - low = i + 1; - else { - /* lnum is inside this fold */ - *fpp = fp + i; - return TRUE; - } - } - *fpp = fp + low; - return FALSE; -} - -/* foldLevelWin() {{{2 */ -/* - * Return fold level at line number "lnum" in window "wp". - */ -static int foldLevelWin(win_T *wp, linenr_T lnum) -{ - fold_T *fp; - linenr_T lnum_rel = lnum; - int level = 0; - garray_T *gap; - - /* Recursively search for a fold that contains "lnum". */ - gap = &wp->w_folds; - for (;; ) { - if (!foldFind(gap, lnum_rel, &fp)) - break; - /* Check nested folds. Line number is relative to containing fold. */ - gap = &fp->fd_nested; - lnum_rel -= fp->fd_top; - ++level; - } - - return level; -} - -/* checkupdate() {{{2 */ -/* - * Check if the folds in window "wp" are invalid and update them if needed. - */ -static void checkupdate(win_T *wp) -{ - if (wp->w_foldinvalid) { - foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); /* will update all */ - wp->w_foldinvalid = FALSE; - } -} - -/* setFoldRepeat() {{{2 */ -/* - * Open or close fold for current window at line "lnum". - * Repeat "count" times. - */ -static void setFoldRepeat(linenr_T lnum, long count, int do_open) -{ - int done; - long n; - - for (n = 0; n < count; ++n) { - done = DONE_NOTHING; - (void)setManualFold(lnum, do_open, FALSE, &done); - if (!(done & DONE_ACTION)) { - /* Only give an error message when no fold could be opened. */ - if (n == 0 && !(done & DONE_FOLD)) - EMSG(_(e_nofold)); - break; - } - } -} - -/* setManualFold() {{{2 */ -/* - * Open or close the fold in the current window which contains "lnum". - * Also does this for other windows in diff mode when needed. - */ -static linenr_T -setManualFold ( - linenr_T lnum, - int opening, /* TRUE when opening, FALSE when closing */ - int recurse, /* TRUE when closing/opening recursive */ - int *donep -) -{ - if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { - win_T *wp; - linenr_T dlnum; - - /* - * Do the same operation in other windows in diff mode. Calculate the - * line number from the diffs. - */ - FOR_ALL_WINDOWS(wp) - { - if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { - dlnum = diff_lnum_win(curwin->w_cursor.lnum, wp); - if (dlnum != 0) - (void)setManualFoldWin(wp, dlnum, opening, recurse, NULL); - } - } - } - - return setManualFoldWin(curwin, lnum, opening, recurse, donep); -} - -/* setManualFoldWin() {{{2 */ -/* - * Open or close the fold in window "wp" which contains "lnum". - * "donep", when not NULL, points to flag that is set to DONE_FOLD when some - * fold was found and to DONE_ACTION when some fold was opened or closed. - * When "donep" is NULL give an error message when no fold was found for - * "lnum", but only if "wp" is "curwin". - * Return the line number of the next line that could be closed. - * It's only valid when "opening" is TRUE! - */ -static linenr_T -setManualFoldWin ( - win_T *wp, - linenr_T lnum, - int opening, /* TRUE when opening, FALSE when closing */ - int recurse, /* TRUE when closing/opening recursive */ - int *donep -) -{ - fold_T *fp; - fold_T *fp2; - fold_T *found = NULL; - int j; - int level = 0; - int use_level = FALSE; - int found_fold = FALSE; - garray_T *gap; - linenr_T next = MAXLNUM; - linenr_T off = 0; - int done = 0; - - checkupdate(wp); - - /* - * Find the fold, open or close it. - */ - gap = &wp->w_folds; - for (;; ) { - if (!foldFind(gap, lnum, &fp)) { - /* If there is a following fold, continue there next time. */ - if (fp < (fold_T *)gap->ga_data + gap->ga_len) - next = fp->fd_top + off; - break; - } - - /* lnum is inside this fold */ - found_fold = TRUE; - - /* If there is a following fold, continue there next time. */ - if (fp + 1 < (fold_T *)gap->ga_data + gap->ga_len) - next = fp[1].fd_top + off; - - /* Change from level-dependent folding to manual. */ - if (use_level || fp->fd_flags == FD_LEVEL) { - use_level = TRUE; - if (level >= wp->w_p_fdl) - fp->fd_flags = FD_CLOSED; - else - fp->fd_flags = FD_OPEN; - fp2 = (fold_T *)fp->fd_nested.ga_data; - for (j = 0; j < fp->fd_nested.ga_len; ++j) - fp2[j].fd_flags = FD_LEVEL; - } - - /* Simple case: Close recursively means closing the fold. */ - if (!opening && recurse) { - if (fp->fd_flags != FD_CLOSED) { - done |= DONE_ACTION; - fp->fd_flags = FD_CLOSED; - } - } else if (fp->fd_flags == FD_CLOSED) { - /* When opening, open topmost closed fold. */ - if (opening) { - fp->fd_flags = FD_OPEN; - done |= DONE_ACTION; - if (recurse) - foldOpenNested(fp); - } - break; - } - - /* fold is open, check nested folds */ - found = fp; - gap = &fp->fd_nested; - lnum -= fp->fd_top; - off += fp->fd_top; - ++level; - } - if (found_fold) { - /* When closing and not recurse, close deepest open fold. */ - if (!opening && found != NULL) { - found->fd_flags = FD_CLOSED; - done |= DONE_ACTION; - } - wp->w_fold_manual = TRUE; - if (done & DONE_ACTION) - changed_window_setting_win(wp); - done |= DONE_FOLD; - } else if (donep == NULL && wp == curwin) - EMSG(_(e_nofold)); - - if (donep != NULL) - *donep |= done; - - return next; -} - -/* foldOpenNested() {{{2 */ -/* - * Open all nested folds in fold "fpr" recursively. - */ -static void foldOpenNested(fold_T *fpr) -{ - int i; - fold_T *fp; - - fp = (fold_T *)fpr->fd_nested.ga_data; - for (i = 0; i < fpr->fd_nested.ga_len; ++i) { - foldOpenNested(&fp[i]); - fp[i].fd_flags = FD_OPEN; - } -} - -/* deleteFoldEntry() {{{2 */ -/* - * Delete fold "idx" from growarray "gap". - * When "recursive" is TRUE also delete all the folds contained in it. - * When "recursive" is FALSE contained folds are moved one level up. - */ -static void deleteFoldEntry(garray_T *gap, int idx, int recursive) -{ - fold_T *fp; - int i; - long moved; - fold_T *nfp; - - fp = (fold_T *)gap->ga_data + idx; - if (recursive || fp->fd_nested.ga_len == 0) { - /* recursively delete the contained folds */ - deleteFoldRecurse(&fp->fd_nested); - --gap->ga_len; - if (idx < gap->ga_len) - memmove(fp, fp + 1, sizeof(fold_T) * (gap->ga_len - idx)); - } else { - /* Move nested folds one level up, to overwrite the fold that is - * deleted. */ - moved = fp->fd_nested.ga_len; - ga_grow(gap, (int)(moved - 1)); - { - /* Get "fp" again, the array may have been reallocated. */ - fp = (fold_T *)gap->ga_data + idx; - - /* adjust fd_top and fd_flags for the moved folds */ - nfp = (fold_T *)fp->fd_nested.ga_data; - for (i = 0; i < moved; ++i) { - nfp[i].fd_top += fp->fd_top; - if (fp->fd_flags == FD_LEVEL) - nfp[i].fd_flags = FD_LEVEL; - if (fp->fd_small == MAYBE) - nfp[i].fd_small = MAYBE; - } - - /* move the existing folds down to make room */ - if (idx + 1 < gap->ga_len) - memmove(fp + moved, fp + 1, - sizeof(fold_T) * (gap->ga_len - (idx + 1))); - /* move the contained folds one level up */ - memmove(fp, nfp, (size_t)(sizeof(fold_T) * moved)); - free(nfp); - gap->ga_len += moved - 1; - } - } -} - -/* deleteFoldRecurse() {{{2 */ -/* - * Delete nested folds in a fold. - */ -void deleteFoldRecurse(garray_T *gap) -{ - int i; - - for (i = 0; i < gap->ga_len; ++i) - deleteFoldRecurse(&(((fold_T *)(gap->ga_data))[i].fd_nested)); - ga_clear(gap); -} - -/* foldMarkAdjust() {{{2 */ -/* - * Update line numbers of folds for inserted/deleted lines. - */ -void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after) -{ - /* If deleting marks from line1 to line2, but not deleting all those - * lines, set line2 so that only deleted lines have their folds removed. */ - if (amount == MAXLNUM && line2 >= line1 && line2 - line1 >= -amount_after) - line2 = line1 - amount_after - 1; - /* If appending a line in Insert mode, it should be included in the fold - * just above the line. */ - if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) - --line1; - foldMarkAdjustRecurse(&wp->w_folds, line1, line2, amount, amount_after); -} - -/* foldMarkAdjustRecurse() {{{2 */ -static void foldMarkAdjustRecurse(garray_T *gap, linenr_T line1, linenr_T line2, long amount, long amount_after) -{ - fold_T *fp; - int i; - linenr_T last; - linenr_T top; - - /* In Insert mode an inserted line at the top of a fold is considered part - * of the fold, otherwise it isn't. */ - if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) - top = line1 + 1; - else - top = line1; - - /* Find the fold containing or just below "line1". */ - (void)foldFind(gap, line1, &fp); - - /* - * Adjust all folds below "line1" that are affected. - */ - for (i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; ++i, ++fp) { - /* - * Check for these situations: - * 1 2 3 - * 1 2 3 - * line1 2 3 4 5 - * 2 3 4 5 - * 2 3 4 5 - * line2 2 3 4 5 - * 3 5 6 - * 3 5 6 - */ - - last = fp->fd_top + fp->fd_len - 1; /* last line of fold */ - - /* 1. fold completely above line1: nothing to do */ - if (last < line1) - continue; - - /* 6. fold below line2: only adjust for amount_after */ - if (fp->fd_top > line2) { - if (amount_after == 0) - break; - fp->fd_top += amount_after; - } else { - if (fp->fd_top >= top && last <= line2) { - /* 4. fold completely contained in range */ - if (amount == MAXLNUM) { - /* Deleting lines: delete the fold completely */ - deleteFoldEntry(gap, i, TRUE); - --i; /* adjust index for deletion */ - --fp; - } else - fp->fd_top += amount; - } else { - if (fp->fd_top < top) { - /* 2 or 3: need to correct nested folds too */ - foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, - line2 - fp->fd_top, amount, amount_after); - if (last <= line2) { - /* 2. fold contains line1, line2 is below fold */ - if (amount == MAXLNUM) - fp->fd_len = line1 - fp->fd_top; - else - fp->fd_len += amount; - } else { - /* 3. fold contains line1 and line2 */ - fp->fd_len += amount_after; - } - } else { - /* 5. fold is below line1 and contains line2; need to - * correct nested folds too */ - foldMarkAdjustRecurse(&fp->fd_nested, line1 - fp->fd_top, - line2 - fp->fd_top, amount, - amount_after + (fp->fd_top - top)); - if (amount == MAXLNUM) { - fp->fd_len -= line2 - fp->fd_top + 1; - fp->fd_top = line1; - } else { - fp->fd_len += amount_after - amount; - fp->fd_top += amount; - } - } - } - } - } -} - -/* getDeepestNesting() {{{2 */ -/* - * Get the lowest 'foldlevel' value that makes the deepest nested fold in the - * current window open. - */ -int getDeepestNesting(void) -{ - checkupdate(curwin); - return getDeepestNestingRecurse(&curwin->w_folds); -} - -static int getDeepestNestingRecurse(garray_T *gap) -{ - int i; - int level; - int maxlevel = 0; - fold_T *fp; - - fp = (fold_T *)gap->ga_data; - for (i = 0; i < gap->ga_len; ++i) { - level = getDeepestNestingRecurse(&fp[i].fd_nested) + 1; - if (level > maxlevel) - maxlevel = level; - } - - return maxlevel; -} - -/* check_closed() {{{2 */ -/* - * Check if a fold is closed and update the info needed to check nested folds. - */ -static int -check_closed ( - win_T *win, - fold_T *fp, - int *use_levelp, /* TRUE: outer fold had FD_LEVEL */ - int level, /* folding depth */ - int *maybe_smallp, /* TRUE: outer this had fd_small == MAYBE */ - linenr_T lnum_off /* line number offset for fp->fd_top */ -) -{ - int closed = FALSE; - - /* Check if this fold is closed. If the flag is FD_LEVEL this - * fold and all folds it contains depend on 'foldlevel'. */ - if (*use_levelp || fp->fd_flags == FD_LEVEL) { - *use_levelp = TRUE; - if (level >= win->w_p_fdl) - closed = TRUE; - } else if (fp->fd_flags == FD_CLOSED) - closed = TRUE; - - /* Small fold isn't closed anyway. */ - if (fp->fd_small == MAYBE) - *maybe_smallp = TRUE; - if (closed) { - if (*maybe_smallp) - fp->fd_small = MAYBE; - checkSmall(win, fp, lnum_off); - if (fp->fd_small == TRUE) - closed = FALSE; - } - return closed; -} - -/* checkSmall() {{{2 */ -/* - * Update fd_small field of fold "fp". - */ -static void -checkSmall ( - win_T *wp, - fold_T *fp, - linenr_T lnum_off /* offset for fp->fd_top */ -) -{ - int count; - int n; - - if (fp->fd_small == MAYBE) { - /* Mark any nested folds to maybe-small */ - setSmallMaybe(&fp->fd_nested); - - if (fp->fd_len > curwin->w_p_fml) - fp->fd_small = FALSE; - else { - count = 0; - for (n = 0; n < fp->fd_len; ++n) { - count += plines_win_nofold(wp, fp->fd_top + lnum_off + n); - if (count > curwin->w_p_fml) { - fp->fd_small = FALSE; - return; - } - } - fp->fd_small = TRUE; - } - } -} - -/* setSmallMaybe() {{{2 */ -/* - * Set small flags in "gap" to MAYBE. - */ -static void setSmallMaybe(garray_T *gap) -{ - int i; - fold_T *fp; - - fp = (fold_T *)gap->ga_data; - for (i = 0; i < gap->ga_len; ++i) - fp[i].fd_small = MAYBE; -} - -/* foldCreateMarkers() {{{2 */ -/* - * Create a fold from line "start" to line "end" (inclusive) in the current - * window by adding markers. - */ -static void foldCreateMarkers(linenr_T start, linenr_T end) -{ - if (!curbuf->b_p_ma) { - EMSG(_(e_modifiable)); - return; - } - parseMarker(curwin); - - foldAddMarker(start, curwin->w_p_fmr, foldstartmarkerlen); - foldAddMarker(end, foldendmarker, foldendmarkerlen); - - /* Update both changes here, to avoid all folds after the start are - * changed when the start marker is inserted and the end isn't. */ - changed_lines(start, (colnr_T)0, end, 0L); -} - -/* foldAddMarker() {{{2 */ -/* - * Add "marker[markerlen]" in 'commentstring' to line "lnum". - */ -static void foldAddMarker(linenr_T lnum, char_u *marker, int markerlen) -{ - char_u *cms = curbuf->b_p_cms; - char_u *line; - int line_len; - char_u *newline; - char_u *p = (char_u *)strstr((char *)curbuf->b_p_cms, "%s"); - - /* Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end */ - line = ml_get(lnum); - line_len = (int)STRLEN(line); - - if (u_save(lnum - 1, lnum + 1) == OK) { - newline = alloc((unsigned)(line_len + markerlen + STRLEN(cms) + 1)); - STRCPY(newline, line); - if (p == NULL) - vim_strncpy(newline + line_len, marker, markerlen); - else { - STRCPY(newline + line_len, cms); - STRNCPY(newline + line_len + (p - cms), marker, markerlen); - STRCPY(newline + line_len + (p - cms) + markerlen, p + 2); - } - - ml_replace(lnum, newline, FALSE); - } -} - -/* deleteFoldMarkers() {{{2 */ -/* - * Delete the markers for a fold, causing it to be deleted. - */ -static void -deleteFoldMarkers ( - fold_T *fp, - int recursive, - linenr_T lnum_off /* offset for fp->fd_top */ -) -{ - int i; - - if (recursive) - for (i = 0; i < fp->fd_nested.ga_len; ++i) - deleteFoldMarkers((fold_T *)fp->fd_nested.ga_data + i, TRUE, - lnum_off + fp->fd_top); - foldDelMarker(fp->fd_top + lnum_off, curwin->w_p_fmr, foldstartmarkerlen); - foldDelMarker(fp->fd_top + lnum_off + fp->fd_len - 1, - foldendmarker, foldendmarkerlen); -} - -/* foldDelMarker() {{{2 */ -/* - * Delete marker "marker[markerlen]" at the end of line "lnum". - * Delete 'commentstring' if it matches. - * If the marker is not found, there is no error message. Could a missing - * close-marker. - */ -static void foldDelMarker(linenr_T lnum, char_u *marker, int markerlen) -{ - char_u *line; - char_u *newline; - char_u *p; - int len; - char_u *cms = curbuf->b_p_cms; - char_u *cms2; - - line = ml_get(lnum); - for (p = line; *p != NUL; ++p) - if (STRNCMP(p, marker, markerlen) == 0) { - /* Found the marker, include a digit if it's there. */ - len = markerlen; - if (VIM_ISDIGIT(p[len])) - ++len; - if (*cms != NUL) { - /* Also delete 'commentstring' if it matches. */ - cms2 = (char_u *)strstr((char *)cms, "%s"); - if (p - line >= cms2 - cms - && STRNCMP(p - (cms2 - cms), cms, cms2 - cms) == 0 - && STRNCMP(p + len, cms2 + 2, STRLEN(cms2 + 2)) == 0) { - p -= cms2 - cms; - len += (int)STRLEN(cms) - 2; - } - } - if (u_save(lnum - 1, lnum + 1) == OK) { - /* Make new line: text-before-marker + text-after-marker */ - newline = alloc((unsigned)(STRLEN(line) - len + 1)); - STRNCPY(newline, line, p - line); - STRCPY(newline + (p - line), p + len); - ml_replace(lnum, newline, FALSE); - } - break; - } -} - -/* get_foldtext() {{{2 */ -/* - * Return the text for a closed fold at line "lnum", with last line "lnume". - * When 'foldtext' isn't set puts the result in "buf[51]". Otherwise the - * result is in allocated memory. - */ -char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T *foldinfo, char_u *buf) -{ - char_u *text = NULL; - /* an error occurred when evaluating 'fdt' setting */ - static int got_fdt_error = FALSE; - int save_did_emsg = did_emsg; - static win_T *last_wp = NULL; - static linenr_T last_lnum = 0; - - if (last_wp != wp || last_wp == NULL - || last_lnum > lnum || last_lnum == 0) - /* window changed, try evaluating foldtext setting once again */ - got_fdt_error = FALSE; - - if (!got_fdt_error) - /* a previous error should not abort evaluating 'foldexpr' */ - did_emsg = FALSE; - - if (*wp->w_p_fdt != NUL) { - char_u dashes[MAX_LEVEL + 2]; - win_T *save_curwin; - int level; - char_u *p; - - /* Set "v:foldstart" and "v:foldend". */ - set_vim_var_nr(VV_FOLDSTART, lnum); - set_vim_var_nr(VV_FOLDEND, lnume); - - /* Set "v:folddashes" to a string of "level" dashes. */ - /* Set "v:foldlevel" to "level". */ - level = foldinfo->fi_level; - if (level > (int)sizeof(dashes) - 1) - level = (int)sizeof(dashes) - 1; - memset(dashes, '-', (size_t)level); - dashes[level] = NUL; - set_vim_var_string(VV_FOLDDASHES, dashes, -1); - set_vim_var_nr(VV_FOLDLEVEL, (long)level); - - /* skip evaluating foldtext on errors */ - if (!got_fdt_error) { - save_curwin = curwin; - curwin = wp; - curbuf = wp->w_buffer; - - ++emsg_silent; /* handle exceptions, but don't display errors */ - text = eval_to_string_safe(wp->w_p_fdt, NULL, - was_set_insecurely((char_u *)"foldtext", OPT_LOCAL)); - --emsg_silent; - - if (text == NULL || did_emsg) - got_fdt_error = TRUE; - - curwin = save_curwin; - curbuf = curwin->w_buffer; - } - last_lnum = lnum; - last_wp = wp; - set_vim_var_string(VV_FOLDDASHES, NULL, -1); - - if (!did_emsg && save_did_emsg) - did_emsg = save_did_emsg; - - if (text != NULL) { - /* Replace unprintable characters, if there are any. But - * replace a TAB with a space. */ - for (p = text; *p != NUL; ++p) { - int len; - - if (has_mbyte && (len = (*mb_ptr2len)(p)) > 1) { - if (!vim_isprintc((*mb_ptr2char)(p))) - break; - p += len - 1; - } else if (*p == TAB) - *p = ' '; - else if (ptr2cells(p) > 1) - break; - } - if (*p != NUL) { - p = transstr(text); - free(text); - text = p; - } - } - } - if (text == NULL) { - sprintf((char *)buf, _("+--%3ld lines folded "), - (long)(lnume - lnum + 1)); - text = buf; - } - return text; -} - -/* foldtext_cleanup() {{{2 */ -/* - * Remove 'foldmarker' and 'commentstring' from "str" (in-place). - */ -void foldtext_cleanup(char_u *str) -{ - char_u *cms_start; /* first part or the whole comment */ - int cms_slen = 0; /* length of cms_start */ - char_u *cms_end; /* last part of the comment or NULL */ - int cms_elen = 0; /* length of cms_end */ - char_u *s; - char_u *p; - int len; - int did1 = FALSE; - int did2 = FALSE; - - /* Ignore leading and trailing white space in 'commentstring'. */ - cms_start = skipwhite(curbuf->b_p_cms); - cms_slen = (int)STRLEN(cms_start); - while (cms_slen > 0 && vim_iswhite(cms_start[cms_slen - 1])) - --cms_slen; - - /* locate "%s" in 'commentstring', use the part before and after it. */ - cms_end = (char_u *)strstr((char *)cms_start, "%s"); - if (cms_end != NULL) { - cms_elen = cms_slen - (int)(cms_end - cms_start); - cms_slen = (int)(cms_end - cms_start); - - /* exclude white space before "%s" */ - while (cms_slen > 0 && vim_iswhite(cms_start[cms_slen - 1])) - --cms_slen; - - /* skip "%s" and white space after it */ - s = skipwhite(cms_end + 2); - cms_elen -= (int)(s - cms_end); - cms_end = s; - } - parseMarker(curwin); - - for (s = str; *s != NUL; ) { - len = 0; - if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) - len = foldstartmarkerlen; - else if (STRNCMP(s, foldendmarker, foldendmarkerlen) == 0) - len = foldendmarkerlen; - if (len > 0) { - if (VIM_ISDIGIT(s[len])) - ++len; - - /* May remove 'commentstring' start. Useful when it's a double - * quote and we already removed a double quote. */ - for (p = s; p > str && vim_iswhite(p[-1]); --p) - ; - if (p >= str + cms_slen - && STRNCMP(p - cms_slen, cms_start, cms_slen) == 0) { - len += (int)(s - p) + cms_slen; - s = p - cms_slen; - } - } else if (cms_end != NULL) { - if (!did1 && cms_slen > 0 && STRNCMP(s, cms_start, cms_slen) == 0) { - len = cms_slen; - did1 = TRUE; - } else if (!did2 && cms_elen > 0 - && STRNCMP(s, cms_end, cms_elen) == 0) { - len = cms_elen; - did2 = TRUE; - } - } - if (len != 0) { - while (vim_iswhite(s[len])) - ++len; - STRMOVE(s, s + len); - } else { - mb_ptr_adv(s); - } - } -} - -/* Folding by indent, expr, marker and syntax. {{{1 */ -/* Define "fline_T", passed to get fold level for a line. {{{2 */ -typedef struct { - win_T *wp; /* window */ - linenr_T lnum; /* current line number */ - linenr_T off; /* offset between lnum and real line number */ - linenr_T lnum_save; /* line nr used by foldUpdateIEMSRecurse() */ - int lvl; /* current level (-1 for undefined) */ - int lvl_next; /* level used for next line */ - int start; /* number of folds that are forced to start at - this line. */ - int end; /* level of fold that is forced to end below - this line */ - int had_end; /* level of fold that is forced to end above - this line (copy of "end" of prev. line) */ -} fline_T; - -/* Flag is set when redrawing is needed. */ -static int fold_changed; - -/* Function declarations. {{{2 */ -static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, - linenr_T startlnum, fline_T *flp, - void (*getlevel)(fline_T *), linenr_T bot, - int topflags); -static void foldInsert(garray_T *gap, int i); -static void foldSplit(garray_T *gap, int i, linenr_T top, linenr_T bot); -static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot); -static void foldMerge(fold_T *fp1, garray_T *gap, fold_T *fp2); -static void foldlevelIndent(fline_T *flp); -static void foldlevelDiff(fline_T *flp); -static void foldlevelExpr(fline_T *flp); -static void foldlevelMarker(fline_T *flp); -static void foldlevelSyntax(fline_T *flp); - -/* foldUpdateIEMS() {{{2 */ -/* - * Update the folding for window "wp", at least from lines "top" to "bot". - * Return TRUE if any folds did change. - */ -static void foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot) -{ - linenr_T start; - linenr_T end; - fline_T fline; - void (*getlevel)(fline_T *); - int level; - fold_T *fp; - - /* Avoid problems when being called recursively. */ - if (invalid_top != (linenr_T)0) - return; - - if (wp->w_foldinvalid) { - /* Need to update all folds. */ - top = 1; - bot = wp->w_buffer->b_ml.ml_line_count; - wp->w_foldinvalid = FALSE; - - /* Mark all folds a maybe-small. */ - setSmallMaybe(&wp->w_folds); - } - - /* add the context for "diff" folding */ - if (foldmethodIsDiff(wp)) { - if (top > diff_context) - top -= diff_context; - else - top = 1; - bot += diff_context; - } - - /* When deleting lines at the end of the buffer "top" can be past the end - * of the buffer. */ - if (top > wp->w_buffer->b_ml.ml_line_count) - top = wp->w_buffer->b_ml.ml_line_count; - - fold_changed = FALSE; - fline.wp = wp; - fline.off = 0; - fline.lvl = 0; - fline.lvl_next = -1; - fline.start = 0; - fline.end = MAX_LEVEL + 1; - fline.had_end = MAX_LEVEL + 1; - - invalid_top = top; - invalid_bot = bot; - - if (foldmethodIsMarker(wp)) { - getlevel = foldlevelMarker; - - /* Init marker variables to speed up foldlevelMarker(). */ - parseMarker(wp); - - /* Need to get the level of the line above top, it is used if there is - * no marker at the top. */ - if (top > 1) { - /* Get the fold level at top - 1. */ - level = foldLevelWin(wp, top - 1); - - /* The fold may end just above the top, check for that. */ - fline.lnum = top - 1; - fline.lvl = level; - getlevel(&fline); - - /* If a fold started here, we already had the level, if it stops - * here, we need to use lvl_next. Could also start and end a fold - * in the same line. */ - if (fline.lvl > level) - fline.lvl = level - (fline.lvl - fline.lvl_next); - else - fline.lvl = fline.lvl_next; - } - fline.lnum = top; - getlevel(&fline); - } else { - fline.lnum = top; - if (foldmethodIsExpr(wp)) { - getlevel = foldlevelExpr; - /* start one line back, because a "<1" may indicate the end of a - * fold in the topline */ - if (top > 1) - --fline.lnum; - } else if (foldmethodIsSyntax(wp)) - getlevel = foldlevelSyntax; - else if (foldmethodIsDiff(wp)) - getlevel = foldlevelDiff; - else - getlevel = foldlevelIndent; - - /* Backup to a line for which the fold level is defined. Since it's - * always defined for line one, we will stop there. */ - fline.lvl = -1; - for (; !got_int; --fline.lnum) { - /* Reset lvl_next each time, because it will be set to a value for - * the next line, but we search backwards here. */ - fline.lvl_next = -1; - getlevel(&fline); - if (fline.lvl >= 0) - break; - } - } - - /* - * If folding is defined by the syntax, it is possible that a change in - * one line will cause all sub-folds of the current fold to change (e.g., - * closing a C-style comment can cause folds in the subsequent lines to - * appear). To take that into account we should adjust the value of "bot" - * to point to the end of the current fold: - */ - if (foldlevelSyntax == getlevel) { - garray_T *gap = &wp->w_folds; - fold_T *fpn = NULL; - int current_fdl = 0; - linenr_T fold_start_lnum = 0; - linenr_T lnum_rel = fline.lnum; - - while (current_fdl < fline.lvl) { - if (!foldFind(gap, lnum_rel, &fpn)) - break; - ++current_fdl; - - fold_start_lnum += fpn->fd_top; - gap = &fpn->fd_nested; - lnum_rel -= fpn->fd_top; - } - if (fpn != NULL && current_fdl == fline.lvl) { - linenr_T fold_end_lnum = fold_start_lnum + fpn->fd_len; - - if (fold_end_lnum > bot) - bot = fold_end_lnum; - } - } - - start = fline.lnum; - end = bot; - /* Do at least one line. */ - if (start > end && end < wp->w_buffer->b_ml.ml_line_count) - end = start; - while (!got_int) { - /* Always stop at the end of the file ("end" can be past the end of - * the file). */ - if (fline.lnum > wp->w_buffer->b_ml.ml_line_count) - break; - if (fline.lnum > end) { - /* For "marker", "expr" and "syntax" methods: If a change caused - * a fold to be removed, we need to continue at least until where - * it ended. */ - if (getlevel != foldlevelMarker - && getlevel != foldlevelSyntax - && getlevel != foldlevelExpr) - break; - if ((start <= end - && foldFind(&wp->w_folds, end, &fp) - && fp->fd_top + fp->fd_len - 1 > end) - || (fline.lvl == 0 - && foldFind(&wp->w_folds, fline.lnum, &fp) - && fp->fd_top < fline.lnum)) - end = fp->fd_top + fp->fd_len - 1; - else if (getlevel == foldlevelSyntax - && foldLevelWin(wp, fline.lnum) != fline.lvl) - /* For "syntax" method: Compare the foldlevel that the syntax - * tells us to the foldlevel from the existing folds. If they - * don't match continue updating folds. */ - end = fline.lnum; - else - break; - } - - /* A level 1 fold starts at a line with foldlevel > 0. */ - if (fline.lvl > 0) { - invalid_top = fline.lnum; - invalid_bot = end; - end = foldUpdateIEMSRecurse(&wp->w_folds, - 1, start, &fline, getlevel, end, FD_LEVEL); - start = fline.lnum; - } else { - if (fline.lnum == wp->w_buffer->b_ml.ml_line_count) - break; - ++fline.lnum; - fline.lvl = fline.lvl_next; - getlevel(&fline); - } - } - - /* There can't be any folds from start until end now. */ - foldRemove(&wp->w_folds, start, end); - - /* If some fold changed, need to redraw and position cursor. */ - if (fold_changed && wp->w_p_fen) - changed_window_setting_win(wp); - - /* If we updated folds past "bot", need to redraw more lines. Don't do - * this in other situations, the changed lines will be redrawn anyway and - * this method can cause the whole window to be updated. */ - if (end != bot) { - if (wp->w_redraw_top == 0 || wp->w_redraw_top > top) - wp->w_redraw_top = top; - if (wp->w_redraw_bot < end) - wp->w_redraw_bot = end; - } - - invalid_top = (linenr_T)0; -} - -/* foldUpdateIEMSRecurse() {{{2 */ -/* - * Update a fold that starts at "flp->lnum". At this line there is always a - * valid foldlevel, and its level >= "level". - * "flp" is valid for "flp->lnum" when called and it's valid when returning. - * "flp->lnum" is set to the lnum just below the fold, if it ends before - * "bot", it's "bot" plus one if the fold continues and it's bigger when using - * the marker method and a text change made following folds to change. - * When returning, "flp->lnum_save" is the line number that was used to get - * the level when the level at "flp->lnum" is invalid. - * Remove any folds from "startlnum" up to here at this level. - * Recursively update nested folds. - * Below line "bot" there are no changes in the text. - * "flp->lnum", "flp->lnum_save" and "bot" are relative to the start of the - * outer fold. - * "flp->off" is the offset to the real line number in the buffer. - * - * All this would be a lot simpler if all folds in the range would be deleted - * and then created again. But we would lose all information about the - * folds, even when making changes that don't affect the folding (e.g. "vj~"). - * - * Returns bot, which may have been increased for lines that also need to be - * updated as a result of a detected change in the fold. - */ -static linenr_T foldUpdateIEMSRecurse(gap, level, startlnum, flp, getlevel, bot, - topflags) -garray_T *gap; -int level; -linenr_T startlnum; -fline_T *flp; -void (*getlevel)(fline_T *); -linenr_T bot; -int topflags; /* flags used by containing fold */ -{ - linenr_T ll; - fold_T *fp = NULL; - fold_T *fp2; - int lvl = level; - linenr_T startlnum2 = startlnum; - linenr_T firstlnum = flp->lnum; /* first lnum we got */ - int i; - int finish = FALSE; - linenr_T linecount = flp->wp->w_buffer->b_ml.ml_line_count - flp->off; - int concat; - - /* - * If using the marker method, the start line is not the start of a fold - * at the level we're dealing with and the level is non-zero, we must use - * the previous fold. But ignore a fold that starts at or below - * startlnum, it must be deleted. - */ - if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level - && flp->lvl > 0) { - foldFind(gap, startlnum - 1, &fp); - if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len - || fp->fd_top >= startlnum) - fp = NULL; - } - - /* - * Loop over all lines in this fold, or until "bot" is hit. - * Handle nested folds inside of this fold. - * "flp->lnum" is the current line. When finding the end of the fold, it - * is just below the end of the fold. - * "*flp" contains the level of the line "flp->lnum" or a following one if - * there are lines with an invalid fold level. "flp->lnum_save" is the - * line number that was used to get the fold level (below "flp->lnum" when - * it has an invalid fold level). When called the fold level is always - * valid, thus "flp->lnum_save" is equal to "flp->lnum". - */ - flp->lnum_save = flp->lnum; - while (!got_int) { - /* Updating folds can be slow, check for CTRL-C. */ - line_breakcheck(); - - /* Set "lvl" to the level of line "flp->lnum". When flp->start is set - * and after the first line of the fold, set the level to zero to - * force the fold to end. Do the same when had_end is set: Previous - * line was marked as end of a fold. */ - lvl = flp->lvl; - if (lvl > MAX_LEVEL) - lvl = MAX_LEVEL; - if (flp->lnum > firstlnum - && (level > lvl - flp->start || level >= flp->had_end)) - lvl = 0; - - if (flp->lnum > bot && !finish && fp != NULL) { - /* For "marker" and "syntax" methods: - * - If a change caused a nested fold to be removed, we need to - * delete it and continue at least until where it ended. - * - If a change caused a nested fold to be created, or this fold - * to continue below its original end, need to finish this fold. - */ - if (getlevel != foldlevelMarker - && getlevel != foldlevelExpr - && getlevel != foldlevelSyntax) - break; - i = 0; - fp2 = fp; - if (lvl >= level) { - /* Compute how deep the folds currently are, if it's deeper - * than "lvl" then some must be deleted, need to update - * at least one nested fold. */ - ll = flp->lnum - fp->fd_top; - while (foldFind(&fp2->fd_nested, ll, &fp2)) { - ++i; - ll -= fp2->fd_top; - } - } - if (lvl < level + i) { - foldFind(&fp->fd_nested, flp->lnum - fp->fd_top, &fp2); - if (fp2 != NULL) - bot = fp2->fd_top + fp2->fd_len - 1 + fp->fd_top; - } else if (fp->fd_top + fp->fd_len <= flp->lnum && lvl >= level) - finish = TRUE; - else - break; - } - - /* At the start of the first nested fold and at the end of the current - * fold: check if existing folds at this level, before the current - * one, need to be deleted or truncated. */ - if (fp == NULL - && (lvl != level - || flp->lnum_save >= bot - || flp->start != 0 - || flp->had_end <= MAX_LEVEL - || flp->lnum == linecount)) { - /* - * Remove or update folds that have lines between startlnum and - * firstlnum. - */ - while (!got_int) { - /* set concat to 1 if it's allowed to concatenated this fold - * with a previous one that touches it. */ - if (flp->start != 0 || flp->had_end <= MAX_LEVEL) - concat = 0; - else - concat = 1; - - /* Find an existing fold to re-use. Preferably one that - * includes startlnum, otherwise one that ends just before - * startlnum or starts after it. */ - if (foldFind(gap, startlnum, &fp) - || (fp < ((fold_T *)gap->ga_data) + gap->ga_len - && fp->fd_top <= firstlnum) - || foldFind(gap, firstlnum - concat, &fp) - || (fp < ((fold_T *)gap->ga_data) + gap->ga_len - && ((lvl < level && fp->fd_top < flp->lnum) - || (lvl >= level - && fp->fd_top <= flp->lnum_save)))) { - if (fp->fd_top + fp->fd_len + concat > firstlnum) { - /* Use existing fold for the new fold. If it starts - * before where we started looking, extend it. If it - * starts at another line, update nested folds to keep - * their position, compensating for the new fd_top. */ - if (fp->fd_top >= startlnum && fp->fd_top != firstlnum) { - if (fp->fd_top > firstlnum) - /* like lines are inserted */ - foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum), 0L); - else - /* like lines are deleted */ - foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, - (long)(firstlnum - fp->fd_top - 1), - (linenr_T)MAXLNUM, - (long)(fp->fd_top - firstlnum)); - fp->fd_len += fp->fd_top - firstlnum; - fp->fd_top = firstlnum; - fold_changed = TRUE; - } else if (flp->start != 0 && lvl == level - && fp->fd_top != firstlnum) { - /* Existing fold that includes startlnum must stop - * if we find the start of a new fold at the same - * level. Split it. Delete contained folds at - * this point to split them too. */ - foldRemove(&fp->fd_nested, flp->lnum - fp->fd_top, - flp->lnum - fp->fd_top); - i = (int)(fp - (fold_T *)gap->ga_data); - foldSplit(gap, i, flp->lnum, flp->lnum - 1); - fp = (fold_T *)gap->ga_data + i + 1; - /* If using the "marker" or "syntax" method, we - * need to continue until the end of the fold is - * found. */ - if (getlevel == foldlevelMarker - || getlevel == foldlevelExpr - || getlevel == foldlevelSyntax) - finish = TRUE; - } - break; - } - if (fp->fd_top >= startlnum) { - /* A fold that starts at or after startlnum and stops - * before the new fold must be deleted. Continue - * looking for the next one. */ - deleteFoldEntry(gap, - (int)(fp - (fold_T *)gap->ga_data), TRUE); - } else { - /* A fold has some lines above startlnum, truncate it - * to stop just above startlnum. */ - fp->fd_len = startlnum - fp->fd_top; - foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)fp->fd_len, (linenr_T)MAXLNUM, - (linenr_T)MAXLNUM, 0L); - fold_changed = TRUE; - } - } else { - /* Insert new fold. Careful: ga_data may be NULL and it - * may change! */ - i = (int)(fp - (fold_T *)gap->ga_data); - foldInsert(gap, i); - fp = (fold_T *)gap->ga_data + i; - /* The new fold continues until bot, unless we find the - * end earlier. */ - fp->fd_top = firstlnum; - fp->fd_len = bot - firstlnum + 1; - /* When the containing fold is open, the new fold is open. - * The new fold is closed if the fold above it is closed. - * The first fold depends on the containing fold. */ - if (topflags == FD_OPEN) { - flp->wp->w_fold_manual = TRUE; - fp->fd_flags = FD_OPEN; - } else if (i <= 0) { - fp->fd_flags = topflags; - if (topflags != FD_LEVEL) - flp->wp->w_fold_manual = TRUE; - } else - fp->fd_flags = (fp - 1)->fd_flags; - fp->fd_small = MAYBE; - /* If using the "marker", "expr" or "syntax" method, we - * need to continue until the end of the fold is found. */ - if (getlevel == foldlevelMarker - || getlevel == foldlevelExpr - || getlevel == foldlevelSyntax) - finish = TRUE; - fold_changed = TRUE; - break; - } - } - } - - if (lvl < level || flp->lnum > linecount) { - /* - * Found a line with a lower foldlevel, this fold ends just above - * "flp->lnum". - */ - break; - } - - /* - * The fold includes the line "flp->lnum" and "flp->lnum_save". - * Check "fp" for safety. - */ - if (lvl > level && fp != NULL) { - /* - * There is a nested fold, handle it recursively. - */ - /* At least do one line (can happen when finish is TRUE). */ - if (bot < flp->lnum) - bot = flp->lnum; - - /* Line numbers in the nested fold are relative to the start of - * this fold. */ - flp->lnum = flp->lnum_save - fp->fd_top; - flp->off += fp->fd_top; - i = (int)(fp - (fold_T *)gap->ga_data); - bot = foldUpdateIEMSRecurse(&fp->fd_nested, level + 1, - startlnum2 - fp->fd_top, flp, getlevel, - bot - fp->fd_top, fp->fd_flags); - fp = (fold_T *)gap->ga_data + i; - flp->lnum += fp->fd_top; - flp->lnum_save += fp->fd_top; - flp->off -= fp->fd_top; - bot += fp->fd_top; - startlnum2 = flp->lnum; - - /* This fold may end at the same line, don't incr. flp->lnum. */ - } else { - /* - * Get the level of the next line, then continue the loop to check - * if it ends there. - * Skip over undefined lines, to find the foldlevel after it. - * For the last line in the file the foldlevel is always valid. - */ - flp->lnum = flp->lnum_save; - ll = flp->lnum + 1; - while (!got_int) { - /* Make the previous level available to foldlevel(). */ - prev_lnum = flp->lnum; - prev_lnum_lvl = flp->lvl; - - if (++flp->lnum > linecount) - break; - flp->lvl = flp->lvl_next; - getlevel(flp); - if (flp->lvl >= 0 || flp->had_end <= MAX_LEVEL) - break; - } - prev_lnum = 0; - if (flp->lnum > linecount) - break; - - /* leave flp->lnum_save to lnum of the line that was used to get - * the level, flp->lnum to the lnum of the next line. */ - flp->lnum_save = flp->lnum; - flp->lnum = ll; - } - } - - if (fp == NULL) /* only happens when got_int is set */ - return bot; - - /* - * Get here when: - * lvl < level: the folds ends just above "flp->lnum" - * lvl >= level: fold continues below "bot" - */ - - /* Current fold at least extends until lnum. */ - if (fp->fd_len < flp->lnum - fp->fd_top) { - fp->fd_len = flp->lnum - fp->fd_top; - fp->fd_small = MAYBE; - fold_changed = TRUE; - } - - /* Delete contained folds from the end of the last one found until where - * we stopped looking. */ - foldRemove(&fp->fd_nested, startlnum2 - fp->fd_top, - flp->lnum - 1 - fp->fd_top); - - if (lvl < level) { - /* End of fold found, update the length when it got shorter. */ - if (fp->fd_len != flp->lnum - fp->fd_top) { - if (fp->fd_top + fp->fd_len > bot + 1) { - /* fold continued below bot */ - if (getlevel == foldlevelMarker - || getlevel == foldlevelExpr - || getlevel == foldlevelSyntax) { - /* marker method: truncate the fold and make sure the - * previously included lines are processed again */ - bot = fp->fd_top + fp->fd_len - 1; - fp->fd_len = flp->lnum - fp->fd_top; - } else { - /* indent or expr method: split fold to create a new one - * below bot */ - i = (int)(fp - (fold_T *)gap->ga_data); - foldSplit(gap, i, flp->lnum, bot); - fp = (fold_T *)gap->ga_data + i; - } - } else - fp->fd_len = flp->lnum - fp->fd_top; - fold_changed = TRUE; - } - } - - /* delete following folds that end before the current line */ - for (;; ) { - fp2 = fp + 1; - if (fp2 >= (fold_T *)gap->ga_data + gap->ga_len - || fp2->fd_top > flp->lnum) - break; - if (fp2->fd_top + fp2->fd_len > flp->lnum) { - if (fp2->fd_top < flp->lnum) { - /* Make fold that includes lnum start at lnum. */ - foldMarkAdjustRecurse(&fp2->fd_nested, - (linenr_T)0, (long)(flp->lnum - fp2->fd_top - 1), - (linenr_T)MAXLNUM, (long)(fp2->fd_top - flp->lnum)); - fp2->fd_len -= flp->lnum - fp2->fd_top; - fp2->fd_top = flp->lnum; - fold_changed = TRUE; - } - - if (lvl >= level) { - /* merge new fold with existing fold that follows */ - foldMerge(fp, gap, fp2); - } - break; - } - fold_changed = TRUE; - deleteFoldEntry(gap, (int)(fp2 - (fold_T *)gap->ga_data), TRUE); - } - - /* Need to redraw the lines we inspected, which might be further down than - * was asked for. */ - if (bot < flp->lnum - 1) - bot = flp->lnum - 1; - - return bot; -} - -/* foldInsert() {{{2 */ -/* - * Insert a new fold in "gap" at position "i". - */ -static void foldInsert(garray_T *gap, int i) -{ - fold_T *fp; - - ga_grow(gap, 1); - - fp = (fold_T *)gap->ga_data + i; - if (i < gap->ga_len) - memmove(fp + 1, fp, sizeof(fold_T) * (gap->ga_len - i)); - ++gap->ga_len; - ga_init(&fp->fd_nested, (int)sizeof(fold_T), 10); -} - -/* foldSplit() {{{2 */ -/* - * Split the "i"th fold in "gap", which starts before "top" and ends below - * "bot" in two pieces, one ending above "top" and the other starting below - * "bot". - * The caller must first have taken care of any nested folds from "top" to - * "bot"! - */ -static void foldSplit(garray_T *gap, int i, linenr_T top, linenr_T bot) -{ - fold_T *fp; - fold_T *fp2; - garray_T *gap1; - garray_T *gap2; - int idx; - int len; - - /* The fold continues below bot, need to split it. */ - foldInsert(gap, i + 1); - - fp = (fold_T *)gap->ga_data + i; - fp[1].fd_top = bot + 1; - fp[1].fd_len = fp->fd_len - (fp[1].fd_top - fp->fd_top); - fp[1].fd_flags = fp->fd_flags; - fp[1].fd_small = MAYBE; - fp->fd_small = MAYBE; - - /* Move nested folds below bot to new fold. There can't be - * any between top and bot, they have been removed by the caller. */ - gap1 = &fp->fd_nested; - gap2 = &fp[1].fd_nested; - (void)(foldFind(gap1, bot + 1 - fp->fd_top, &fp2)); - len = (int)((fold_T *)gap1->ga_data + gap1->ga_len - fp2); - if (len > 0) { - ga_grow(gap2, len); - for (idx = 0; idx < len; ++idx) { - ((fold_T *)gap2->ga_data)[idx] = fp2[idx]; - ((fold_T *)gap2->ga_data)[idx].fd_top - -= fp[1].fd_top - fp->fd_top; - } - gap2->ga_len = len; - gap1->ga_len -= len; - } - fp->fd_len = top - fp->fd_top; - fold_changed = TRUE; -} - -/* foldRemove() {{{2 */ -/* - * Remove folds within the range "top" to and including "bot". - * Check for these situations: - * 1 2 3 - * 1 2 3 - * top 2 3 4 5 - * 2 3 4 5 - * bot 2 3 4 5 - * 3 5 6 - * 3 5 6 - * - * 1: not changed - * 2: truncate to stop above "top" - * 3: split in two parts, one stops above "top", other starts below "bot". - * 4: deleted - * 5: made to start below "bot". - * 6: not changed - */ -static void foldRemove(garray_T *gap, linenr_T top, linenr_T bot) -{ - fold_T *fp = NULL; - - if (bot < top) - return; /* nothing to do */ - - for (;; ) { - /* Find fold that includes top or a following one. */ - if (foldFind(gap, top, &fp) && fp->fd_top < top) { - /* 2: or 3: need to delete nested folds */ - foldRemove(&fp->fd_nested, top - fp->fd_top, bot - fp->fd_top); - if (fp->fd_top + fp->fd_len > bot + 1) { - /* 3: need to split it. */ - foldSplit(gap, (int)(fp - (fold_T *)gap->ga_data), top, bot); - } else { - /* 2: truncate fold at "top". */ - fp->fd_len = top - fp->fd_top; - } - fold_changed = TRUE; - continue; - } - if (fp >= (fold_T *)(gap->ga_data) + gap->ga_len - || fp->fd_top > bot) { - /* 6: Found a fold below bot, can stop looking. */ - break; - } - if (fp->fd_top >= top) { - /* Found an entry below top. */ - fold_changed = TRUE; - if (fp->fd_top + fp->fd_len - 1 > bot) { - /* 5: Make fold that includes bot start below bot. */ - foldMarkAdjustRecurse(&fp->fd_nested, - (linenr_T)0, (long)(bot - fp->fd_top), - (linenr_T)MAXLNUM, (long)(fp->fd_top - bot - 1)); - fp->fd_len -= bot - fp->fd_top + 1; - fp->fd_top = bot + 1; - break; - } - - /* 4: Delete completely contained fold. */ - deleteFoldEntry(gap, (int)(fp - (fold_T *)gap->ga_data), TRUE); - } - } -} - -/* foldMerge() {{{2 */ -/* - * Merge two adjacent folds (and the nested ones in them). - * This only works correctly when the folds are really adjacent! Thus "fp1" - * must end just above "fp2". - * The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1". - * Fold entry "fp2" in "gap" is deleted. - */ -static void foldMerge(fold_T *fp1, garray_T *gap, fold_T *fp2) -{ - fold_T *fp3; - fold_T *fp4; - int idx; - garray_T *gap1 = &fp1->fd_nested; - garray_T *gap2 = &fp2->fd_nested; - - /* If the last nested fold in fp1 touches the first nested fold in fp2, - * merge them recursively. */ - if (foldFind(gap1, fp1->fd_len - 1L, &fp3) && foldFind(gap2, 0L, &fp4)) - foldMerge(fp3, gap2, fp4); - - /* Move nested folds in fp2 to the end of fp1. */ - if (gap2->ga_len > 0) { - ga_grow(gap1, gap2->ga_len); - for (idx = 0; idx < gap2->ga_len; ++idx) { - ((fold_T *)gap1->ga_data)[gap1->ga_len] - = ((fold_T *)gap2->ga_data)[idx]; - ((fold_T *)gap1->ga_data)[gap1->ga_len].fd_top += fp1->fd_len; - ++gap1->ga_len; - } - gap2->ga_len = 0; - } - - fp1->fd_len += fp2->fd_len; - deleteFoldEntry(gap, (int)(fp2 - (fold_T *)gap->ga_data), TRUE); - fold_changed = TRUE; -} - -/* foldlevelIndent() {{{2 */ -/* - * Low level function to get the foldlevel for the "indent" method. - * Doesn't use any caching. - * Returns a level of -1 if the foldlevel depends on surrounding lines. - */ -static void foldlevelIndent(fline_T *flp) -{ - char_u *s; - buf_T *buf; - linenr_T lnum = flp->lnum + flp->off; - - buf = flp->wp->w_buffer; - s = skipwhite(ml_get_buf(buf, lnum, FALSE)); - - /* empty line or lines starting with a character in 'foldignore': level - * depends on surrounding lines */ - if (*s == NUL || vim_strchr(flp->wp->w_p_fdi, *s) != NULL) { - /* first and last line can't be undefined, use level 0 */ - if (lnum == 1 || lnum == buf->b_ml.ml_line_count) - flp->lvl = 0; - else - flp->lvl = -1; - } else - flp->lvl = get_indent_buf(buf, lnum) / get_sw_value(curbuf); - if (flp->lvl > flp->wp->w_p_fdn) { - flp->lvl = flp->wp->w_p_fdn; - if (flp->lvl < 0) - flp->lvl = 0; - } -} - -/* foldlevelDiff() {{{2 */ -/* - * Low level function to get the foldlevel for the "diff" method. - * Doesn't use any caching. - */ -static void foldlevelDiff(fline_T *flp) -{ - if (diff_infold(flp->wp, flp->lnum + flp->off)) - flp->lvl = 1; - else - flp->lvl = 0; -} - -/* foldlevelExpr() {{{2 */ -/* - * Low level function to get the foldlevel for the "expr" method. - * Doesn't use any caching. - * Returns a level of -1 if the foldlevel depends on surrounding lines. - */ -static void foldlevelExpr(fline_T *flp) -{ - win_T *win; - int n; - int c; - linenr_T lnum = flp->lnum + flp->off; - int save_keytyped; - - win = curwin; - curwin = flp->wp; - curbuf = flp->wp->w_buffer; - set_vim_var_nr(VV_LNUM, lnum); - - flp->start = 0; - flp->had_end = flp->end; - flp->end = MAX_LEVEL + 1; - if (lnum <= 1) - flp->lvl = 0; - - /* KeyTyped may be reset to 0 when calling a function which invokes - * do_cmdline(). To make 'foldopen' work correctly restore KeyTyped. */ - save_keytyped = KeyTyped; - n = eval_foldexpr(flp->wp->w_p_fde, &c); - KeyTyped = save_keytyped; - - switch (c) { - /* "a1", "a2", .. : add to the fold level */ - case 'a': if (flp->lvl >= 0) { - flp->lvl += n; - flp->lvl_next = flp->lvl; - } - flp->start = n; - break; - - /* "s1", "s2", .. : subtract from the fold level */ - case 's': if (flp->lvl >= 0) { - if (n > flp->lvl) - flp->lvl_next = 0; - else - flp->lvl_next = flp->lvl - n; - flp->end = flp->lvl_next + 1; - } - break; - - /* ">1", ">2", .. : start a fold with a certain level */ - case '>': flp->lvl = n; - flp->lvl_next = n; - flp->start = 1; - break; - - /* "<1", "<2", .. : end a fold with a certain level */ - case '<': flp->lvl_next = n - 1; - flp->end = n; - break; - - /* "=": No change in level */ - case '=': flp->lvl_next = flp->lvl; - break; - - /* "-1", "0", "1", ..: set fold level */ - default: if (n < 0) - /* Use the current level for the next line, so that "a1" - * will work there. */ - flp->lvl_next = flp->lvl; - else - flp->lvl_next = n; - flp->lvl = n; - break; - } - - /* If the level is unknown for the first or the last line in the file, use - * level 0. */ - if (flp->lvl < 0) { - if (lnum <= 1) { - flp->lvl = 0; - flp->lvl_next = 0; - } - if (lnum == curbuf->b_ml.ml_line_count) - flp->lvl_next = 0; - } - - curwin = win; - curbuf = curwin->w_buffer; -} - -/* parseMarker() {{{2 */ -/* - * Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and - * "foldendmarkerlen". - * Relies on the option value to have been checked for correctness already. - */ -static void parseMarker(win_T *wp) -{ - foldendmarker = vim_strchr(wp->w_p_fmr, ','); - foldstartmarkerlen = (int)(foldendmarker++ - wp->w_p_fmr); - foldendmarkerlen = (int)STRLEN(foldendmarker); -} - -/* foldlevelMarker() {{{2 */ -/* - * Low level function to get the foldlevel for the "marker" method. - * "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been - * set before calling this. - * Requires that flp->lvl is set to the fold level of the previous line! - * Careful: This means you can't call this function twice on the same line. - * Doesn't use any caching. - * Sets flp->start when a start marker was found. - */ -static void foldlevelMarker(fline_T *flp) -{ - char_u *startmarker; - int cstart; - int cend; - int start_lvl = flp->lvl; - char_u *s; - int n; - - /* cache a few values for speed */ - startmarker = flp->wp->w_p_fmr; - cstart = *startmarker; - ++startmarker; - cend = *foldendmarker; - - /* Default: no start found, next level is same as current level */ - flp->start = 0; - flp->lvl_next = flp->lvl; - - s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, FALSE); - while (*s) { - if (*s == cstart - && STRNCMP(s + 1, startmarker, foldstartmarkerlen - 1) == 0) { - /* found startmarker: set flp->lvl */ - s += foldstartmarkerlen; - if (VIM_ISDIGIT(*s)) { - n = atoi((char *)s); - if (n > 0) { - flp->lvl = n; - flp->lvl_next = n; - if (n <= start_lvl) - flp->start = 1; - else - flp->start = n - start_lvl; - } - } else { - ++flp->lvl; - ++flp->lvl_next; - ++flp->start; - } - } else if (*s == cend - && STRNCMP(s + 1, foldendmarker + 1, - foldendmarkerlen - 1) == 0) { - /* found endmarker: set flp->lvl_next */ - s += foldendmarkerlen; - if (VIM_ISDIGIT(*s)) { - n = atoi((char *)s); - if (n > 0) { - flp->lvl = n; - flp->lvl_next = n - 1; - /* never start a fold with an end marker */ - if (flp->lvl_next > start_lvl) - flp->lvl_next = start_lvl; - } - } else - --flp->lvl_next; - } else - mb_ptr_adv(s); - } - - /* The level can't go negative, must be missing a start marker. */ - if (flp->lvl_next < 0) - flp->lvl_next = 0; -} - -/* foldlevelSyntax() {{{2 */ -/* - * Low level function to get the foldlevel for the "syntax" method. - * Doesn't use any caching. - */ -static void foldlevelSyntax(fline_T *flp) -{ - linenr_T lnum = flp->lnum + flp->off; - int n; - - /* Use the maximum fold level at the start of this line and the next. */ - flp->lvl = syn_get_foldlevel(flp->wp, lnum); - flp->start = 0; - if (lnum < flp->wp->w_buffer->b_ml.ml_line_count) { - n = syn_get_foldlevel(flp->wp, lnum + 1); - if (n > flp->lvl) { - flp->start = n - flp->lvl; /* fold(s) start here */ - flp->lvl = n; - } - } -} - -/* functions for storing the fold state in a View {{{1 */ -/* put_folds() {{{2 */ -static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off); -static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, - linenr_T off); -static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off); - -/* - * Write commands to "fd" to restore the manual folds in window "wp". - * Return FAIL if writing fails. - */ -int put_folds(FILE *fd, win_T *wp) -{ - if (foldmethodIsManual(wp)) { - if (put_line(fd, "silent! normal! zE") == FAIL - || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL) - return FAIL; - } - - /* If some folds are manually opened/closed, need to restore that. */ - if (wp->w_fold_manual) - return put_foldopen_recurse(fd, wp, &wp->w_folds, (linenr_T)0); - - return OK; -} - -/* put_folds_recurse() {{{2 */ -/* - * Write commands to "fd" to recreate manually created folds. - * Returns FAIL when writing failed. - */ -static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off) -{ - int i; - fold_T *fp; - - fp = (fold_T *)gap->ga_data; - for (i = 0; i < gap->ga_len; i++) { - /* Do nested folds first, they will be created closed. */ - if (put_folds_recurse(fd, &fp->fd_nested, off + fp->fd_top) == FAIL) - return FAIL; - if (fprintf(fd, "%" PRId64 ",%" PRId64 "fold", - (int64_t)(fp->fd_top + off), - (int64_t)(fp->fd_top + off + fp->fd_len - 1)) < 0 - || put_eol(fd) == FAIL) - return FAIL; - ++fp; - } - return OK; -} - -/* put_foldopen_recurse() {{{2 */ -/* - * Write commands to "fd" to open and close manually opened/closed folds. - * Returns FAIL when writing failed. - */ -static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off) -{ - int i; - int level; - fold_T *fp; - - fp = (fold_T *)gap->ga_data; - for (i = 0; i < gap->ga_len; i++) { - if (fp->fd_flags != FD_LEVEL) { - if (fp->fd_nested.ga_len > 0) { - /* open nested folds while this fold is open */ - if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0 - || put_eol(fd) == FAIL - || put_line(fd, "normal! zo") == FAIL) - return FAIL; - if (put_foldopen_recurse(fd, wp, &fp->fd_nested, - off + fp->fd_top) - == FAIL) - return FAIL; - /* close the parent when needed */ - if (fp->fd_flags == FD_CLOSED) { - if (put_fold_open_close(fd, fp, off) == FAIL) - return FAIL; - } - } else { - /* Open or close the leaf according to the window foldlevel. - * Do not close a leaf that is already closed, as it will close - * the parent. */ - level = foldLevelWin(wp, off + fp->fd_top); - if ((fp->fd_flags == FD_CLOSED && wp->w_p_fdl >= level) - || (fp->fd_flags != FD_CLOSED && wp->w_p_fdl < level)) - if (put_fold_open_close(fd, fp, off) == FAIL) - return FAIL; - } - } - ++fp; - } - - return OK; -} - -/* put_fold_open_close() {{{2 */ -/* - * Write the open or close command to "fd". - * Returns FAIL when writing failed. - */ -static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off) -{ - if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0 - || put_eol(fd) == FAIL - || fprintf(fd, "normal! z%c", - fp->fd_flags == FD_CLOSED ? 'c' : 'o') < 0 - || put_eol(fd) == FAIL) - return FAIL; - - return OK; -} - -/* }}}1 */ diff --git a/src/fold.h b/src/fold.h deleted file mode 100644 index 5023511e0d..0000000000 --- a/src/fold.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef NEOVIM_FOLD_H -#define NEOVIM_FOLD_H - -/* - * Info used to pass info about a fold from the fold-detection code to the - * code that displays the foldcolumn. - */ -typedef struct foldinfo { - int fi_level; /* level of the fold; when this is zero the - other fields are invalid */ - int fi_lnum; /* line number where fold starts */ - int fi_low_level; /* lowest fold level that starts in the same - line */ -} foldinfo_T; - -void copyFoldingState(win_T *wp_from, win_T *wp_to); -int hasAnyFolding(win_T *win); -int hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp); -int hasFoldingWin(win_T *win, linenr_T lnum, linenr_T *firstp, - linenr_T *lastp, int cache, - foldinfo_T *infop); -int foldLevel(linenr_T lnum); -int lineFolded(win_T *win, linenr_T lnum); -long foldedCount(win_T *win, linenr_T lnum, foldinfo_T *infop); -int foldmethodIsManual(win_T *wp); -int foldmethodIsIndent(win_T *wp); -int foldmethodIsExpr(win_T *wp); -int foldmethodIsMarker(win_T *wp); -int foldmethodIsSyntax(win_T *wp); -int foldmethodIsDiff(win_T *wp); -void closeFold(linenr_T lnum, long count); -void closeFoldRecurse(linenr_T lnum); -void opFoldRange(linenr_T first, linenr_T last, int opening, - int recurse, - int had_visual); -void openFold(linenr_T lnum, long count); -void openFoldRecurse(linenr_T lnum); -void foldOpenCursor(void); -void newFoldLevel(void); -void foldCheckClose(void); -int foldManualAllowed(int create); -void foldCreate(linenr_T start, linenr_T end); -void deleteFold(linenr_T start, linenr_T end, int recursive, - int had_visual); -void clearFolding(win_T *win); -void foldUpdate(win_T *wp, linenr_T top, linenr_T bot); -void foldUpdateAll(win_T *win); -int foldMoveTo(int updown, int dir, long count); -void foldInitWin(win_T *new_win); -int find_wl_entry(win_T *win, linenr_T lnum); -void foldAdjustVisual(void); -void foldAdjustCursor(void); -void cloneFoldGrowArray(garray_T *from, garray_T *to); -void deleteFoldRecurse(garray_T *gap); -void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, - long amount, - long amount_after); -int getDeepestNesting(void); -char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, - foldinfo_T *foldinfo, - char_u *buf); -void foldtext_cleanup(char_u *str); -int put_folds(FILE *fd, win_T *wp); - -#endif /* NEOVIM_FOLD_H */ diff --git a/src/func_attr.h b/src/func_attr.h deleted file mode 100644 index 4275d23ff1..0000000000 --- a/src/func_attr.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef NEOVIM_FUNC_ATTR_H -#define NEOVIM_FUNC_ATTR_H - -// gcc and clang expose their version as follows: -// -// gcc 4.7.2: -// __GNUC__ = 4 -// __GNUC_MINOR__ = 7 -// __GNUC_PATCHLEVEL = 2 -// -// clang 3.4 (claims compat with gcc 4.2.1): -// __GNUC__ = 4 -// __GNUC_MINOR__ = 2 -// __GNUC_PATCHLEVEL = 1 -// __clang__ = 1 -// __clang_major__ = 3 -// __clang_minor__ = 4 -// -// To view the default defines of these compilers, you can perform: -// -// $ gcc -E -dM - = 40900 - #define FUNC_ATTR_NONNULL_RET __attribute__((returns_nonnull)) - #endif - #endif -#endif - -// define function attributes that haven't been defined for this specific -// compiler. - -#ifndef FUNC_ATTR_MALLOC - #define FUNC_ATTR_MALLOC -#endif - -#ifndef FUNC_ATTR_ALLOC_SIZE - #define FUNC_ATTR_ALLOC_SIZE(x) -#endif - -#ifndef FUNC_ATTR_ALLOC_SIZE_PROD - #define FUNC_ATTR_ALLOC_SIZE_PROD(x,y) -#endif - -#ifndef FUNC_ATTR_ALLOC_ALIGN - #define FUNC_ATTR_ALLOC_ALIGN(x) -#endif - -#ifndef FUNC_ATTR_PURE - #define FUNC_ATTR_PURE -#endif - -#ifndef FUNC_ATTR_CONST - #define FUNC_ATTR_CONST -#endif - -#ifndef FUNC_ATTR_WARN_UNUSED_RESULT - #define FUNC_ATTR_WARN_UNUSED_RESULT -#endif - -#ifndef FUNC_ATTR_ALWAYS_INLINE - #define FUNC_ATTR_ALWAYS_INLINE -#endif - -#ifndef FUNC_ATTR_UNUSED - #define FUNC_ATTR_UNUSED -#endif - -#ifndef FUNC_ATTR_NONNULL_ALL - #define FUNC_ATTR_NONNULL_ALL -#endif - -#ifndef FUNC_ATTR_NONNULL_ARG - #define FUNC_ATTR_NONNULL_ARG(...) -#endif - -#ifndef FUNC_ATTR_NONNULL_RET - #define FUNC_ATTR_NONNULL_RET -#endif - -#endif // NEOVIM_FUNC_ATTR_H diff --git a/src/garray.c b/src/garray.c deleted file mode 100644 index e1046ceb5e..0000000000 --- a/src/garray.c +++ /dev/null @@ -1,197 +0,0 @@ -/// @file garray.c -/// -/// Functions for handling growing arrays. - -#include - -#include "vim.h" -#include "ascii.h" -#include "misc2.h" -#include "memory.h" -#include "path.h" -#include "garray.h" - -// #include "globals.h" -#include "memline.h" - -/// Clear an allocated growing array. -void ga_clear(garray_T *gap) -{ - free(gap->ga_data); - - // Initialize growing array without resetting itemsize or growsize - gap->ga_data = NULL; - gap->ga_maxlen = 0; - gap->ga_len = 0; -} - -/// Clear a growing array that contains a list of strings. -/// -/// @param gap -void ga_clear_strings(garray_T *gap) -{ - int i; - for (i = 0; i < gap->ga_len; ++i) { - free(((char_u **)(gap->ga_data))[i]); - } - ga_clear(gap); -} - -/// Initialize a growing array. -/// -/// @param gap -/// @param itemsize -/// @param growsize -void ga_init(garray_T *gap, int itemsize, int growsize) -{ - gap->ga_data = NULL; - gap->ga_maxlen = 0; - gap->ga_len = 0; - gap->ga_itemsize = itemsize; - gap->ga_growsize = growsize; -} - -/// Make room in growing array "gap" for at least "n" items. -/// -/// @param gap -/// @param n -void ga_grow(garray_T *gap, int n) -{ - if (gap->ga_maxlen - gap->ga_len >= n) { - // the garray still has enough space, do nothing - return; - } - - // the garray grows by at least growsize (do we have a MIN macro somewhere?) - n = (n < gap->ga_growsize) ? gap->ga_growsize : n; - - size_t new_size = (size_t)(gap->ga_itemsize * (gap->ga_len + n)); - size_t old_size = (size_t)(gap->ga_itemsize * gap->ga_maxlen); - - // reallocate and clear the new memory - char_u *pp = xrealloc(gap->ga_data, new_size); - memset(pp + old_size, 0, new_size - old_size); - - gap->ga_maxlen = gap->ga_len + n; - gap->ga_data = pp; -} - -/// Sort "gap" and remove duplicate entries. "gap" is expected to contain a -/// list of file names in allocated memory. -/// -/// @param gap -void ga_remove_duplicate_strings(garray_T *gap) -{ - char_u **fnames = gap->ga_data; - - // sort the growing array, which puts duplicates next to each other - sort_strings(fnames, gap->ga_len); - - // loop over the growing array in reverse - for (int i = gap->ga_len - 1; i > 0; i--) { - if (fnamecmp(fnames[i - 1], fnames[i]) == 0) { - free(fnames[i]); - - // close the gap (move all strings one slot lower) - for (int j = i + 1; j < gap->ga_len; j++) { - fnames[j - 1] = fnames[j]; - } - - --gap->ga_len; - } - } -} - -/// For a growing array that contains a list of strings: concatenate all the -/// strings with sep as separator. -/// -/// @param gap -/// -/// @returns the concatenated strings -char_u *ga_concat_strings_sep(const garray_T *gap, const char *sep) -{ - const size_t nelem = (size_t) gap->ga_len; - const char **strings = gap->ga_data; - - if (nelem == 0) { - return (char_u *) xstrdup(""); - } - - size_t len = 0; - for (size_t i = 0; i < nelem; i++) { - len += strlen(strings[i]); - } - - // add some space for the (num - 1) separators - len += (nelem - 1) * strlen(sep); - char *const ret = xmallocz(len); - - char *s = ret; - for (size_t i = 0; i < nelem - 1; i++) { - s = xstpcpy(s, strings[i]); - s = xstpcpy(s, sep); - } - s = xstpcpy(s, strings[nelem - 1]); - - return (char_u *) ret; -} - -/// For a growing array that contains a list of strings: concatenate all the -/// strings with a separating comma. -/// -/// @param gap -/// -/// @returns the concatenated strings -char_u* ga_concat_strings(const garray_T *gap) -{ - return ga_concat_strings_sep(gap, ","); -} - -/// Concatenate a string to a growarray which contains characters. -/// -/// WARNING: -/// - Does NOT copy the NUL at the end! -/// - The parameter may not overlap with the growing array -/// -/// @param gap -/// @param s -void ga_concat(garray_T *gap, const char_u *restrict s) -{ - int len = (int)strlen((char *) s); - ga_grow(gap, len); - char *data = gap->ga_data; - memcpy(data + gap->ga_len, s, (size_t) len); - gap->ga_len += len; -} - -/// Append one byte to a growarray which contains bytes. -/// -/// @param gap -/// @param c -void ga_append(garray_T *gap, char c) -{ - ga_grow(gap, 1); - char *str = gap->ga_data; - str[gap->ga_len] = c; - gap->ga_len++; -} - -#if defined(UNIX) || defined(WIN3264) - -/// Append the text in "gap" below the cursor line and clear "gap". -/// -/// @param gap -void append_ga_line(garray_T *gap) -{ - // Remove trailing CR. - if ((gap->ga_len > 0) - && !curbuf->b_p_bin - && (((char_u *)gap->ga_data)[gap->ga_len - 1] == CAR)) { - gap->ga_len--; - } - ga_append(gap, NUL); - ml_append(curwin->w_cursor.lnum++, gap->ga_data, 0, FALSE); - gap->ga_len = 0; -} - -#endif // if defined(UNIX) || defined(WIN3264) diff --git a/src/garray.h b/src/garray.h deleted file mode 100644 index 2a0deacef7..0000000000 --- a/src/garray.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef NEOVIM_GARRAY_H -#define NEOVIM_GARRAY_H - -#include "func_attr.h" - -/// Structure used for growing arrays. -/// This is used to store information that only grows, is deleted all at -/// once, and needs to be accessed by index. See ga_clear() and ga_grow(). -typedef struct growarray { - int ga_len; // current number of items used - int ga_maxlen; // maximum number of items possible - int ga_itemsize; // sizeof(item) - int ga_growsize; // number of items to grow each time - void *ga_data; // pointer to the first item -} garray_T; - -#define GA_EMPTY { 0, 0, 0, 0, NULL } - -void ga_clear(garray_T *gap); -void ga_clear_strings(garray_T *gap); -void ga_init(garray_T *gap, int itemsize, int growsize); -void ga_grow(garray_T *gap, int n); -char_u *ga_concat_strings_sep(const garray_T *gap, const char *sep) - FUNC_ATTR_NONNULL_RET; -char_u *ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET; -void ga_remove_duplicate_strings(garray_T *gap); -void ga_concat(garray_T *gap, const char_u *restrict s); -void ga_append(garray_T *gap, char c); -void append_ga_line(garray_T *gap); - -#endif // NEOVIM_GARRAY_H diff --git a/src/getchar.c b/src/getchar.c deleted file mode 100644 index 99dbc11e4d..0000000000 --- a/src/getchar.c +++ /dev/null @@ -1,4333 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * getchar.c - * - * functions related with getting a character from the user/mapping/redo/... - * - * manipulations with redo buffer and stuff buffer - * mappings and abbreviations - */ - -#include - -#include "vim.h" -#include "getchar.h" -#include "charset.h" -#include "edit.h" -#include "eval.h" -#include "ex_docmd.h" -#include "ex_getln.h" -#include "farsi.h" -#include "main.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "keymap.h" -#include "garray.h" -#include "move.h" -#include "normal.h" -#include "ops.h" -#include "option.h" -#include "regexp.h" -#include "screen.h" -#include "term.h" -#include "ui.h" -#include "undo.h" - -/* - * These buffers are used for storing: - * - stuffed characters: A command that is translated into another command. - * - redo characters: will redo the last change. - * - recorded characters: for the "q" command. - * - * The bytes are stored like in the typeahead buffer: - * - K_SPECIAL introduces a special key (two more bytes follow). A literal - * K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER. - * - CSI introduces a GUI termcap code (also when gui.in_use is FALSE, - * otherwise switching the GUI on would make mappings invalid). - * A literal CSI is stored as CSI KS_EXTRA KE_CSI. - * These translations are also done on multi-byte characters! - * - * Escaping CSI bytes is done by the system-specific input functions, called - * by ui_inchar(). - * Escaping K_SPECIAL is done by inchar(). - * Un-escaping is done by vgetc(). - */ - -#define MINIMAL_SIZE 20 /* minimal size for b_str */ - -static buffheader_T redobuff = {{NULL, {NUL}}, NULL, 0, 0}; -static buffheader_T old_redobuff = {{NULL, {NUL}}, NULL, 0, 0}; -static buffheader_T save_redobuff = {{NULL, {NUL}}, NULL, 0, 0}; -static buffheader_T save_old_redobuff = {{NULL, {NUL}}, NULL, 0, 0}; -static buffheader_T recordbuff = {{NULL, {NUL}}, NULL, 0, 0}; - -// First read ahead buffer. Used for translated commands. -static buffheader_T readbuf1 = {{NULL, {NUL}}, NULL, 0, 0}; - -// Second read ahead buffer. Used for redo. -static buffheader_T readbuf2 = {{NULL, {NUL}}, NULL, 0, 0}; - -static int typeahead_char = 0; /* typeahead char that's not flushed */ - -/* - * when block_redo is TRUE redo buffer will not be changed - * used by edit() to repeat insertions and 'V' command for redoing - */ -static int block_redo = FALSE; - -/* - * Make a hash value for a mapping. - * "mode" is the lower 4 bits of the State for the mapping. - * "c1" is the first character of the "lhs". - * Returns a value between 0 and 255, index in maphash. - * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode. - */ -#define MAP_HASH(mode, \ - c1) (((mode) & \ - (NORMAL + VISUAL + SELECTMODE + \ - OP_PENDING)) ? (c1) : ((c1) ^ 0x80)) - -/* - * Each mapping is put in one of the 256 hash lists, to speed up finding it. - */ -static mapblock_T *(maphash[256]); -static int maphash_valid = FALSE; - -/* - * List used for abbreviations. - */ -static mapblock_T *first_abbr = NULL; /* first entry in abbrlist */ - -static int KeyNoremap = 0; /* remapping flags */ - -/* - * variables used by vgetorpeek() and flush_buffers() - * - * typebuf.tb_buf[] contains all characters that are not consumed yet. - * typebuf.tb_buf[typebuf.tb_off] is the first valid character. - * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1] is the last valid char. - * typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] must be NUL. - * The head of the buffer may contain the result of mappings, abbreviations - * and @a commands. The length of this part is typebuf.tb_maplen. - * typebuf.tb_silent is the part where applies. - * After the head are characters that come from the terminal. - * typebuf.tb_no_abbr_cnt is the number of characters in typebuf.tb_buf that - * should not be considered for abbreviations. - * Some parts of typebuf.tb_buf may not be mapped. These parts are remembered - * in typebuf.tb_noremap[], which is the same length as typebuf.tb_buf and - * contains RM_NONE for the characters that are not to be remapped. - * typebuf.tb_noremap[typebuf.tb_off] is the first valid flag. - * (typebuf has been put in globals.h, because check_termcode() needs it). - */ -#define RM_YES 0 /* tb_noremap: remap */ -#define RM_NONE 1 /* tb_noremap: don't remap */ -#define RM_SCRIPT 2 /* tb_noremap: remap local script mappings */ -#define RM_ABBR 4 /* tb_noremap: don't remap, do abbrev. */ - -/* typebuf.tb_buf has three parts: room in front (for result of mappings), the - * middle for typeahead and room for new characters (which needs to be 3 * - * MAXMAPLEN) for the Amiga). - */ -#define TYPELEN_INIT (5 * (MAXMAPLEN + 3)) -static char_u typebuf_init[TYPELEN_INIT]; /* initial typebuf.tb_buf */ -static char_u noremapbuf_init[TYPELEN_INIT]; /* initial typebuf.tb_noremap */ - -static int last_recorded_len = 0; /* number of last recorded chars */ - -static char_u *get_buffcont(buffheader_T *, int); -static void add_buff(buffheader_T *, char_u *, long n); -static void add_num_buff(buffheader_T *, long); -static void add_char_buff(buffheader_T *, int); -static int read_readbuffers(int advance); -static int read_readbuf(buffheader_T *buf, int advance); -static void start_stuff(void); -static int read_redo(int, int); -static void copy_redo(int); -static void init_typebuf(void); -static void gotchars(char_u *, int); -static void may_sync_undo(void); -static void closescript(void); -static int vgetorpeek(int); -static void map_free(mapblock_T **); -static void validate_maphash(void); -static void showmap(mapblock_T *mp, int local); -static char_u *eval_map_expr(char_u *str, int c); -static bool is_user_input(int k); - -/* - * Free and clear a buffer. - */ -void free_buff(buffheader_T *buf) -{ - buffblock_T *p, *np; - - for (p = buf->bh_first.b_next; p != NULL; p = np) { - np = p->b_next; - free(p); - } - buf->bh_first.b_next = NULL; -} - -/* - * Return the contents of a buffer as a single string. - * K_SPECIAL and CSI in the returned string are escaped. - */ -static char_u *get_buffcont(buffheader_T *buffer, - int dozero // count == zero is not an error - ) -{ - size_t count = 0; - char_u *p = NULL; - char_u *p2; - char_u *str; - - /* compute the total length of the string */ - for (buffblock_T *bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next) - count += STRLEN(bp->b_str); - - if (count || dozero) { - p = xmalloc(count + 1); - p2 = p; - for (buffblock_T *bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next) - for (str = bp->b_str; *str; ) - *p2++ = *str++; - *p2 = NUL; - } - return p; -} - -/* - * Return the contents of the record buffer as a single string - * and clear the record buffer. - * K_SPECIAL and CSI in the returned string are escaped. - */ -char_u *get_recorded(void) -{ - char_u *p; - size_t len; - - p = get_buffcont(&recordbuff, TRUE); - free_buff(&recordbuff); - - /* - * Remove the characters that were added the last time, these must be the - * (possibly mapped) characters that stopped the recording. - */ - len = STRLEN(p); - if ((int)len >= last_recorded_len) { - len -= last_recorded_len; - p[len] = NUL; - } - - /* - * When stopping recording from Insert mode with CTRL-O q, also remove the - * CTRL-O. - */ - if (len > 0 && restart_edit != 0 && p[len - 1] == Ctrl_O) - p[len - 1] = NUL; - - return p; -} - -/* - * Return the contents of the redo buffer as a single string. - * K_SPECIAL and CSI in the returned string are escaped. - */ -char_u *get_inserted(void) -{ - return get_buffcont(&redobuff, FALSE); -} - -/* - * Add string "s" after the current block of buffer "buf". - * K_SPECIAL and CSI should have been escaped already. - */ -static void -add_buff ( - buffheader_T *buf, - char_u *s, - long slen /* length of "s" or -1 */ -) -{ - if (slen < 0) - slen = (long)STRLEN(s); - if (slen == 0) /* don't add empty strings */ - return; - - if (buf->bh_first.b_next == NULL) { /* first add to list */ - buf->bh_space = 0; - buf->bh_curr = &(buf->bh_first); - } else if (buf->bh_curr == NULL) { /* buffer has already been read */ - EMSG(_("E222: Add to read buffer")); - return; - } else if (buf->bh_index != 0) - memmove(buf->bh_first.b_next->b_str, - buf->bh_first.b_next->b_str + buf->bh_index, - STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); - buf->bh_index = 0; - - ssize_t len; - if (buf->bh_space >= (int)slen) { - len = STRLEN(buf->bh_curr->b_str); - vim_strncpy(buf->bh_curr->b_str + len, s, (size_t)slen); - buf->bh_space -= slen; - } else { - if (slen < MINIMAL_SIZE) - len = MINIMAL_SIZE; - else - len = slen; - buffblock_T *p = xmalloc(sizeof(buffblock_T) + len); - buf->bh_space = (int)(len - slen); - vim_strncpy(p->b_str, s, (size_t)slen); - - p->b_next = buf->bh_curr->b_next; - buf->bh_curr->b_next = p; - buf->bh_curr = p; - } - return; -} - -/* - * Add number "n" to buffer "buf". - */ -static void add_num_buff(buffheader_T *buf, long n) -{ - char_u number[32]; - - sprintf((char *)number, "%" PRId64, (int64_t)n); - add_buff(buf, number, -1L); -} - -/* - * Add character 'c' to buffer "buf". - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ -static void add_char_buff(buffheader_T *buf, int c) -{ - char_u bytes[MB_MAXBYTES + 1]; - int len; - int i; - char_u temp[4]; - - if (IS_SPECIAL(c)) - len = 1; - else - len = (*mb_char2bytes)(c, bytes); - for (i = 0; i < len; ++i) { - if (!IS_SPECIAL(c)) - c = bytes[i]; - - if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { - /* translate special key code into three byte sequence */ - temp[0] = K_SPECIAL; - temp[1] = K_SECOND(c); - temp[2] = K_THIRD(c); - temp[3] = NUL; - } else { - temp[0] = c; - temp[1] = NUL; - } - add_buff(buf, temp, -1L); - } -} - -/* - * Get one byte from the read buffers. Use readbuf1 one first, use readbuf2 - * if that one is empty. - * If advance == TRUE go to the next char. - * No translation is done K_SPECIAL and CSI are escaped. - */ -static int read_readbuffers(int advance) -{ - int c; - - c = read_readbuf(&readbuf1, advance); - if (c == NUL) - c = read_readbuf(&readbuf2, advance); - return c; -} - -static int read_readbuf(buffheader_T *buf, int advance) -{ - char_u c; - buffblock_T *curr; - - if (buf->bh_first.b_next == NULL) /* buffer is empty */ - return NUL; - - curr = buf->bh_first.b_next; - c = curr->b_str[buf->bh_index]; - - if (advance) { - if (curr->b_str[++buf->bh_index] == NUL) { - buf->bh_first.b_next = curr->b_next; - free(curr); - buf->bh_index = 0; - } - } - return c; -} - -/* - * Prepare the read buffers for reading (if they contain something). - */ -static void start_stuff(void) -{ - if (readbuf1.bh_first.b_next != NULL) { - readbuf1.bh_curr = &(readbuf1.bh_first); - readbuf1.bh_space = 0; - } - if (readbuf2.bh_first.b_next != NULL) { - readbuf2.bh_curr = &(readbuf2.bh_first); - readbuf2.bh_space = 0; - } -} - -/* - * Return TRUE if the stuff buffer is empty. - */ -int stuff_empty(void) -{ - return (readbuf1.bh_first.b_next == NULL && readbuf2.bh_first.b_next == NULL); -} - -/* - * Return TRUE if readbuf1 is empty. There may still be redo characters in - * redbuf2. - */ -int readbuf1_empty() -{ - return (readbuf1.bh_first.b_next == NULL); -} - -/* - * Set a typeahead character that won't be flushed. - */ -void typeahead_noflush(int c) -{ - typeahead_char = c; -} - -/* - * Remove the contents of the stuff buffer and the mapped characters in the - * typeahead buffer (used in case of an error). If "flush_typeahead" is true, - * flush all typeahead characters (used when interrupted by a CTRL-C). - */ -void flush_buffers(int flush_typeahead) -{ - init_typebuf(); - - start_stuff(); - while (read_readbuffers(TRUE) != NUL) { - } - - if (flush_typeahead) { /* remove all typeahead */ - /* - * We have to get all characters, because we may delete the first part - * of an escape sequence. - * In an xterm we get one char at a time and we have to get them all. - */ - while (inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 10L, - typebuf.tb_change_cnt) != 0) - ; - typebuf.tb_off = MAXMAPLEN; - typebuf.tb_len = 0; - } else { /* remove mapped characters at the start only */ - typebuf.tb_off += typebuf.tb_maplen; - typebuf.tb_len -= typebuf.tb_maplen; - } - typebuf.tb_maplen = 0; - typebuf.tb_silent = 0; - cmd_silent = FALSE; - typebuf.tb_no_abbr_cnt = 0; -} - -/* - * The previous contents of the redo buffer is kept in old_redobuffer. - * This is used for the CTRL-O <.> command in insert mode. - */ -void ResetRedobuff(void) -{ - if (!block_redo) { - free_buff(&old_redobuff); - old_redobuff = redobuff; - redobuff.bh_first.b_next = NULL; - } -} - -/* - * Discard the contents of the redo buffer and restore the previous redo - * buffer. - */ -void CancelRedo(void) -{ - if (!block_redo) { - free_buff(&redobuff); - redobuff = old_redobuff; - old_redobuff.bh_first.b_next = NULL; - start_stuff(); - while (read_readbuffers(TRUE) != NUL) { - } - } -} - -/* - * Save redobuff and old_redobuff to save_redobuff and save_old_redobuff. - * Used before executing autocommands and user functions. - */ -static int save_level = 0; - -void saveRedobuff(void) -{ - char_u *s; - - if (save_level++ == 0) { - save_redobuff = redobuff; - redobuff.bh_first.b_next = NULL; - save_old_redobuff = old_redobuff; - old_redobuff.bh_first.b_next = NULL; - - /* Make a copy, so that ":normal ." in a function works. */ - s = get_buffcont(&save_redobuff, FALSE); - if (s != NULL) { - add_buff(&redobuff, s, -1L); - free(s); - } - } -} - -/* - * Restore redobuff and old_redobuff from save_redobuff and save_old_redobuff. - * Used after executing autocommands and user functions. - */ -void restoreRedobuff(void) -{ - if (--save_level == 0) { - free_buff(&redobuff); - redobuff = save_redobuff; - free_buff(&old_redobuff); - old_redobuff = save_old_redobuff; - } -} - -/* - * Append "s" to the redo buffer. - * K_SPECIAL and CSI should already have been escaped. - */ -void AppendToRedobuff(char_u *s) -{ - if (!block_redo) - add_buff(&redobuff, s, -1L); -} - -/* - * Append to Redo buffer literally, escaping special characters with CTRL-V. - * K_SPECIAL and CSI are escaped as well. - */ -void -AppendToRedobuffLit ( - char_u *str, - int len /* length of "str" or -1 for up to the NUL */ -) -{ - char_u *s = str; - int c; - char_u *start; - - if (block_redo) - return; - - while (len < 0 ? *s != NUL : s - str < len) { - /* Put a string of normal characters in the redo buffer (that's - * faster). */ - start = s; - while (*s >= ' ' - && *s < DEL /* EBCDIC: all chars above space are normal */ - && (len < 0 || s - str < len)) - ++s; - - /* Don't put '0' or '^' as last character, just in case a CTRL-D is - * typed next. */ - if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) - --s; - if (s > start) - add_buff(&redobuff, start, (long)(s - start)); - - if (*s == NUL || (len >= 0 && s - str >= len)) - break; - - /* Handle a special or multibyte character. */ - if (has_mbyte) - /* Handle composing chars separately. */ - c = mb_cptr2char_adv(&s); - else - c = *s++; - if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) - add_char_buff(&redobuff, Ctrl_V); - - /* CTRL-V '0' must be inserted as CTRL-V 048 (EBCDIC: xf0) */ - if (*s == NUL && c == '0') - add_buff(&redobuff, (char_u *)"048", 3L); - else - add_char_buff(&redobuff, c); - } -} - -/* - * Append a character to the redo buffer. - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ -void AppendCharToRedobuff(int c) -{ - if (!block_redo) - add_char_buff(&redobuff, c); -} - -/* - * Append a number to the redo buffer. - */ -void AppendNumberToRedobuff(long n) -{ - if (!block_redo) - add_num_buff(&redobuff, n); -} - -/* - * Append string "s" to the stuff buffer. - * CSI and K_SPECIAL must already have been escaped. - */ -void stuffReadbuff(char_u *s) -{ - add_buff(&readbuf1, s, -1L); -} - -void stuffReadbuffLen(char_u *s, long len) -{ - add_buff(&readbuf1, s, len); -} - -/* - * Stuff "s" into the stuff buffer, leaving special key codes unmodified and - * escaping other K_SPECIAL and CSI bytes. - * Change CR, LF and ESC into a space. - */ -void stuffReadbuffSpec(char_u *s) -{ - int c; - - while (*s != NUL) { - if (*s == K_SPECIAL && s[1] != NUL && s[2] != NUL) { - /* Insert special key literally. */ - stuffReadbuffLen(s, 3L); - s += 3; - } else { - c = mb_ptr2char_adv(&s); - if (c == CAR || c == NL || c == ESC) - c = ' '; - stuffcharReadbuff(c); - } - } -} - -/* - * Append a character to the stuff buffer. - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ -void stuffcharReadbuff(int c) -{ - add_char_buff(&readbuf1, c); -} - -/* - * Append a number to the stuff buffer. - */ -void stuffnumReadbuff(long n) -{ - add_num_buff(&readbuf1, n); -} - -/* - * Read a character from the redo buffer. Translates K_SPECIAL, CSI and - * multibyte characters. - * The redo buffer is left as it is. - * If init is TRUE, prepare for redo, return FAIL if nothing to redo, OK - * otherwise. - * If old is TRUE, use old_redobuff instead of redobuff. - */ -static int read_redo(int init, int old_redo) -{ - static buffblock_T *bp; - static char_u *p; - int c; - int n; - char_u buf[MB_MAXBYTES + 1]; - int i; - - if (init) { - if (old_redo) - bp = old_redobuff.bh_first.b_next; - else - bp = redobuff.bh_first.b_next; - if (bp == NULL) - return FAIL; - p = bp->b_str; - return OK; - } - if ((c = *p) != NUL) { - /* Reverse the conversion done by add_char_buff() */ - /* For a multi-byte character get all the bytes and return the - * converted character. */ - if (has_mbyte && (c != K_SPECIAL || p[1] == KS_SPECIAL)) - n = MB_BYTE2LEN_CHECK(c); - else - n = 1; - for (i = 0;; ++i) { - if (c == K_SPECIAL) { /* special key or escaped K_SPECIAL */ - c = TO_SPECIAL(p[1], p[2]); - p += 2; - } - if (*++p == NUL && bp->b_next != NULL) { - bp = bp->b_next; - p = bp->b_str; - } - buf[i] = c; - if (i == n - 1) { /* last byte of a character */ - if (n != 1) - c = (*mb_ptr2char)(buf); - break; - } - c = *p; - if (c == NUL) /* cannot happen? */ - break; - } - } - - return c; -} - -/* - * Copy the rest of the redo buffer into the stuff buffer (in a slow way). - * If old_redo is TRUE, use old_redobuff instead of redobuff. - * The escaped K_SPECIAL and CSI are copied without translation. - */ -static void copy_redo(int old_redo) -{ - int c; - - while ((c = read_redo(FALSE, old_redo)) != NUL) { - add_char_buff(&readbuf2, c); - } -} - -/* - * Stuff the redo buffer into readbuf2. - * Insert the redo count into the command. - * If "old_redo" is TRUE, the last but one command is repeated - * instead of the last command (inserting text). This is used for - * CTRL-O <.> in insert mode - * - * return FAIL for failure, OK otherwise - */ -int start_redo(long count, int old_redo) -{ - int c; - - /* init the pointers; return if nothing to redo */ - if (read_redo(TRUE, old_redo) == FAIL) - return FAIL; - - c = read_redo(FALSE, old_redo); - - /* copy the buffer name, if present */ - if (c == '"') { - add_buff(&readbuf2, (char_u *)"\"", 1L); - c = read_redo(FALSE, old_redo); - - /* if a numbered buffer is used, increment the number */ - if (c >= '1' && c < '9') - ++c; - add_char_buff(&readbuf2, c); - c = read_redo(FALSE, old_redo); - } - - if (c == 'v') { /* redo Visual */ - VIsual = curwin->w_cursor; - VIsual_active = TRUE; - VIsual_select = FALSE; - VIsual_reselect = TRUE; - redo_VIsual_busy = TRUE; - c = read_redo(FALSE, old_redo); - } - - /* try to enter the count (in place of a previous count) */ - if (count) { - while (VIM_ISDIGIT(c)) /* skip "old" count */ - c = read_redo(FALSE, old_redo); - add_num_buff(&readbuf2, count); - } - - /* copy from the redo buffer into the stuff buffer */ - add_char_buff(&readbuf2, c); - copy_redo(old_redo); - return OK; -} - -/* - * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing - * the redo buffer into readbuf2. - * return FAIL for failure, OK otherwise - */ -int start_redo_ins(void) -{ - int c; - - if (read_redo(TRUE, FALSE) == FAIL) - return FAIL; - start_stuff(); - - /* skip the count and the command character */ - while ((c = read_redo(FALSE, FALSE)) != NUL) { - if (vim_strchr((char_u *)"AaIiRrOo", c) != NULL) { - if (c == 'O' || c == 'o') { - add_buff(&readbuf2, NL_STR, -1L); - } - break; - } - } - - /* copy the typed text from the redo buffer into the stuff buffer */ - copy_redo(FALSE); - block_redo = TRUE; - return OK; -} - -void stop_redo_ins(void) -{ - block_redo = FALSE; -} - -/* - * Initialize typebuf.tb_buf to point to typebuf_init. - * alloc() cannot be used here: In out-of-memory situations it would - * be impossible to type anything. - */ -static void init_typebuf(void) -{ - if (typebuf.tb_buf == NULL) { - typebuf.tb_buf = typebuf_init; - typebuf.tb_noremap = noremapbuf_init; - typebuf.tb_buflen = TYPELEN_INIT; - typebuf.tb_len = 0; - typebuf.tb_off = 0; - typebuf.tb_change_cnt = 1; - } -} - -/* - * insert a string in position 'offset' in the typeahead buffer (for "@r" - * and ":normal" command, vgetorpeek() and check_termcode()) - * - * If noremap is REMAP_YES, new string can be mapped again. - * If noremap is REMAP_NONE, new string cannot be mapped again. - * If noremap is REMAP_SKIP, fist char of new string cannot be mapped again, - * but abbreviations are allowed. - * If noremap is REMAP_SCRIPT, new string cannot be mapped again, except for - * script-local mappings. - * If noremap is > 0, that many characters of the new string cannot be mapped. - * - * If nottyped is TRUE, the string does not return KeyTyped (don't use when - * offset is non-zero!). - * - * If silent is TRUE, cmd_silent is set when the characters are obtained. - * - * return FAIL for failure, OK otherwise - */ -int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, int silent) -{ - char_u *s1, *s2; - int newlen; - int addlen; - int i; - int newoff; - int val; - int nrm; - - init_typebuf(); - if (++typebuf.tb_change_cnt == 0) - typebuf.tb_change_cnt = 1; - - addlen = (int)STRLEN(str); - - /* - * Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off] - */ - if (offset == 0 && addlen <= typebuf.tb_off) { - typebuf.tb_off -= addlen; - memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); - } - /* - * Need to allocate a new buffer. - * In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4 - * characters. We add some extra room to avoid having to allocate too - * often. - */ - else { - newoff = MAXMAPLEN + 4; - newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4); - if (newlen < 0) { /* string is getting too long */ - EMSG(_(e_toocompl)); /* also calls flush_buffers */ - setcursor(); - return FAIL; - } - s1 = alloc(newlen); - if (s1 == NULL) /* out of memory */ - return FAIL; - s2 = alloc(newlen); - if (s2 == NULL) { /* out of memory */ - free(s1); - return FAIL; - } - typebuf.tb_buflen = newlen; - - /* copy the old chars, before the insertion point */ - memmove(s1 + newoff, typebuf.tb_buf + typebuf.tb_off, - (size_t)offset); - /* copy the new chars */ - memmove(s1 + newoff + offset, str, (size_t)addlen); - /* copy the old chars, after the insertion point, including the NUL at - * the end */ - memmove(s1 + newoff + offset + addlen, - typebuf.tb_buf + typebuf.tb_off + offset, - (size_t)(typebuf.tb_len - offset + 1)); - if (typebuf.tb_buf != typebuf_init) - free(typebuf.tb_buf); - typebuf.tb_buf = s1; - - memmove(s2 + newoff, typebuf.tb_noremap + typebuf.tb_off, - (size_t)offset); - memmove(s2 + newoff + offset + addlen, - typebuf.tb_noremap + typebuf.tb_off + offset, - (size_t)(typebuf.tb_len - offset)); - if (typebuf.tb_noremap != noremapbuf_init) - free(typebuf.tb_noremap); - typebuf.tb_noremap = s2; - - typebuf.tb_off = newoff; - } - typebuf.tb_len += addlen; - - /* If noremap == REMAP_SCRIPT: do remap script-local mappings. */ - if (noremap == REMAP_SCRIPT) - val = RM_SCRIPT; - else if (noremap == REMAP_SKIP) - val = RM_ABBR; - else - val = RM_NONE; - - /* - * Adjust typebuf.tb_noremap[] for the new characters: - * If noremap == REMAP_NONE or REMAP_SCRIPT: new characters are - * (sometimes) not remappable - * If noremap == REMAP_YES: all the new characters are mappable - * If noremap > 0: "noremap" characters are not remappable, the rest - * mappable - */ - if (noremap == REMAP_SKIP) - nrm = 1; - else if (noremap < 0) - nrm = addlen; - else - nrm = noremap; - for (i = 0; i < addlen; ++i) - typebuf.tb_noremap[typebuf.tb_off + i + offset] = - (--nrm >= 0) ? val : RM_YES; - - /* tb_maplen and tb_silent only remember the length of mapped and/or - * silent mappings at the start of the buffer, assuming that a mapped - * sequence doesn't result in typed characters. */ - if (nottyped || typebuf.tb_maplen > offset) - typebuf.tb_maplen += addlen; - if (silent || typebuf.tb_silent > offset) { - typebuf.tb_silent += addlen; - cmd_silent = TRUE; - } - if (typebuf.tb_no_abbr_cnt && offset == 0) /* and not used for abbrev.s */ - typebuf.tb_no_abbr_cnt += addlen; - - return OK; -} - -/* - * Put character "c" back into the typeahead buffer. - * Can be used for a character obtained by vgetc() that needs to be put back. - * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to - * the char. - */ -void ins_char_typebuf(int c) -{ - char_u buf[MB_MAXBYTES + 1]; - if (IS_SPECIAL(c)) { - buf[0] = K_SPECIAL; - buf[1] = K_SECOND(c); - buf[2] = K_THIRD(c); - buf[3] = NUL; - } else { - buf[(*mb_char2bytes)(c, buf)] = NUL; - } - (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); -} - -/* - * Return TRUE if the typeahead buffer was changed (while waiting for a - * character to arrive). Happens when a message was received from a client or - * from feedkeys(). - * But check in a more generic way to avoid trouble: When "typebuf.tb_buf" - * changed it was reallocated and the old pointer can no longer be used. - * Or "typebuf.tb_off" may have been changed and we would overwrite characters - * that was just added. - */ -int -typebuf_changed ( - int tb_change_cnt /* old value of typebuf.tb_change_cnt */ -) -{ - return tb_change_cnt != 0 && (typebuf.tb_change_cnt != tb_change_cnt - || typebuf_was_filled - ); -} - -/* - * Return TRUE if there are no characters in the typeahead buffer that have - * not been typed (result from a mapping or come from ":normal"). - */ -int typebuf_typed(void) -{ - return typebuf.tb_maplen == 0; -} - -/* - * Return the number of characters that are mapped (or not typed). - */ -int typebuf_maplen(void) -{ - return typebuf.tb_maplen; -} - -/* - * remove "len" characters from typebuf.tb_buf[typebuf.tb_off + offset] - */ -void del_typebuf(int len, int offset) -{ - int i; - - if (len == 0) - return; /* nothing to do */ - - typebuf.tb_len -= len; - - /* - * Easy case: Just increase typebuf.tb_off. - */ - if (offset == 0 && typebuf.tb_buflen - (typebuf.tb_off + len) - >= 3 * MAXMAPLEN + 3) - typebuf.tb_off += len; - /* - * Have to move the characters in typebuf.tb_buf[] and typebuf.tb_noremap[] - */ - else { - i = typebuf.tb_off + offset; - /* - * Leave some extra room at the end to avoid reallocation. - */ - if (typebuf.tb_off > MAXMAPLEN) { - memmove(typebuf.tb_buf + MAXMAPLEN, - typebuf.tb_buf + typebuf.tb_off, (size_t)offset); - memmove(typebuf.tb_noremap + MAXMAPLEN, - typebuf.tb_noremap + typebuf.tb_off, (size_t)offset); - typebuf.tb_off = MAXMAPLEN; - } - /* adjust typebuf.tb_buf (include the NUL at the end) */ - memmove(typebuf.tb_buf + typebuf.tb_off + offset, - typebuf.tb_buf + i + len, - (size_t)(typebuf.tb_len - offset + 1)); - /* adjust typebuf.tb_noremap[] */ - memmove(typebuf.tb_noremap + typebuf.tb_off + offset, - typebuf.tb_noremap + i + len, - (size_t)(typebuf.tb_len - offset)); - } - - if (typebuf.tb_maplen > offset) { /* adjust tb_maplen */ - if (typebuf.tb_maplen < offset + len) - typebuf.tb_maplen = offset; - else - typebuf.tb_maplen -= len; - } - if (typebuf.tb_silent > offset) { /* adjust tb_silent */ - if (typebuf.tb_silent < offset + len) - typebuf.tb_silent = offset; - else - typebuf.tb_silent -= len; - } - if (typebuf.tb_no_abbr_cnt > offset) { /* adjust tb_no_abbr_cnt */ - if (typebuf.tb_no_abbr_cnt < offset + len) - typebuf.tb_no_abbr_cnt = offset; - else - typebuf.tb_no_abbr_cnt -= len; - } - - /* Reset the flag that text received from a client or from feedkeys() - * was inserted in the typeahead buffer. */ - typebuf_was_filled = FALSE; - if (++typebuf.tb_change_cnt == 0) - typebuf.tb_change_cnt = 1; -} - -/* - * Write typed characters to script file. - * If recording is on put the character in the recordbuffer. - */ -static void gotchars(char_u *chars, int len) -{ - char_u *s = chars; - int c; - char_u buf[2]; - int todo = len; - - /* remember how many chars were last recorded */ - if (Recording) - last_recorded_len += len; - - buf[1] = NUL; - while (todo--) { - /* Handle one byte at a time; no translation to be done. */ - c = *s++; - updatescript(c); - - if (Recording) { - buf[0] = c; - add_buff(&recordbuff, buf, 1L); - } - } - may_sync_undo(); - - /* output "debug mode" message next time in debug mode */ - debug_did_msg = FALSE; - - /* Since characters have been typed, consider the following to be in - * another mapping. Search string will be kept in history. */ - ++maptick; -} - -/* - * Sync undo. Called when typed characters are obtained from the typeahead - * buffer, or when a menu is used. - * Do not sync: - * - In Insert mode, unless cursor key has been used. - * - While reading a script file. - * - When no_u_sync is non-zero. - */ -static void may_sync_undo(void) -{ - if ((!(State & (INSERT + CMDLINE)) || arrow_used) - && scriptin[curscript] == NULL) - u_sync(FALSE); -} - -/* - * Make "typebuf" empty and allocate new buffers. - * Returns FAIL when out of memory. - */ -int alloc_typebuf(void) -{ - typebuf.tb_buf = alloc(TYPELEN_INIT); - typebuf.tb_noremap = alloc(TYPELEN_INIT); - if (typebuf.tb_buf == NULL || typebuf.tb_noremap == NULL) { - free_typebuf(); - return FAIL; - } - typebuf.tb_buflen = TYPELEN_INIT; - typebuf.tb_off = 0; - typebuf.tb_len = 0; - typebuf.tb_maplen = 0; - typebuf.tb_silent = 0; - typebuf.tb_no_abbr_cnt = 0; - if (++typebuf.tb_change_cnt == 0) - typebuf.tb_change_cnt = 1; - return OK; -} - -/* - * Free the buffers of "typebuf". - */ -void free_typebuf(void) -{ - if (typebuf.tb_buf == typebuf_init) - EMSG2(_(e_intern2), "Free typebuf 1"); - else - free(typebuf.tb_buf); - if (typebuf.tb_noremap == noremapbuf_init) - EMSG2(_(e_intern2), "Free typebuf 2"); - else - free(typebuf.tb_noremap); -} - -/* - * When doing ":so! file", the current typeahead needs to be saved, and - * restored when "file" has been read completely. - */ -static typebuf_T saved_typebuf[NSCRIPT]; - -int save_typebuf(void) -{ - init_typebuf(); - saved_typebuf[curscript] = typebuf; - /* If out of memory: restore typebuf and close file. */ - if (alloc_typebuf() == FAIL) { - closescript(); - return FAIL; - } - return OK; -} - -static int old_char = -1; /* character put back by vungetc() */ -static int old_mod_mask; /* mod_mask for ungotten character */ -static int old_mouse_row; /* mouse_row related to old_char */ -static int old_mouse_col; /* mouse_col related to old_char */ - - -/* - * Save all three kinds of typeahead, so that the user must type at a prompt. - */ -void save_typeahead(tasave_T *tp) -{ - tp->save_typebuf = typebuf; - tp->typebuf_valid = (alloc_typebuf() == OK); - if (!tp->typebuf_valid) - typebuf = tp->save_typebuf; - - tp->old_char = old_char; - tp->old_mod_mask = old_mod_mask; - old_char = -1; - - tp->save_readbuf1 = readbuf1; - readbuf1.bh_first.b_next = NULL; - tp->save_readbuf2 = readbuf2; - readbuf2.bh_first.b_next = NULL; -# ifdef USE_INPUT_BUF - tp->save_inputbuf = get_input_buf(); -# endif -} - -/* - * Restore the typeahead to what it was before calling save_typeahead(). - * The allocated memory is freed, can only be called once! - */ -void restore_typeahead(tasave_T *tp) -{ - if (tp->typebuf_valid) { - free_typebuf(); - typebuf = tp->save_typebuf; - } - - old_char = tp->old_char; - old_mod_mask = tp->old_mod_mask; - - free_buff(&readbuf1); - readbuf1 = tp->save_readbuf1; - free_buff(&readbuf2); - readbuf2 = tp->save_readbuf2; -# ifdef USE_INPUT_BUF - set_input_buf(tp->save_inputbuf); -# endif -} - -/* - * Open a new script file for the ":source!" command. - */ -void -openscript ( - char_u *name, - int directly /* when TRUE execute directly */ -) -{ - if (curscript + 1 == NSCRIPT) { - EMSG(_(e_nesting)); - return; - } - if (ignore_script) - /* Not reading from script, also don't open one. Warning message? */ - return; - - if (scriptin[curscript] != NULL) /* already reading script */ - ++curscript; - /* use NameBuff for expanded name */ - expand_env(name, NameBuff, MAXPATHL); - if ((scriptin[curscript] = mch_fopen((char *)NameBuff, READBIN)) == NULL) { - EMSG2(_(e_notopen), name); - if (curscript) - --curscript; - return; - } - if (save_typebuf() == FAIL) - return; - - /* - * Execute the commands from the file right now when using ":source!" - * after ":global" or ":argdo" or in a loop. Also when another command - * follows. This means the display won't be updated. Don't do this - * always, "make test" would fail. - */ - if (directly) { - oparg_T oa; - int oldcurscript; - int save_State = State; - int save_restart_edit = restart_edit; - int save_insertmode = p_im; - int save_finish_op = finish_op; - int save_msg_scroll = msg_scroll; - - State = NORMAL; - msg_scroll = FALSE; /* no msg scrolling in Normal mode */ - restart_edit = 0; /* don't go to Insert mode */ - p_im = FALSE; /* don't use 'insertmode' */ - clear_oparg(&oa); - finish_op = FALSE; - - oldcurscript = curscript; - do { - update_topline_cursor(); /* update cursor position and topline */ - normal_cmd(&oa, FALSE); /* execute one command */ - vpeekc(); /* check for end of file */ - } while (scriptin[oldcurscript] != NULL); - - State = save_State; - msg_scroll = save_msg_scroll; - restart_edit = save_restart_edit; - p_im = save_insertmode; - finish_op = save_finish_op; - } -} - -/* - * Close the currently active input script. - */ -static void closescript(void) -{ - free_typebuf(); - typebuf = saved_typebuf[curscript]; - - fclose(scriptin[curscript]); - scriptin[curscript] = NULL; - if (curscript > 0) - --curscript; -} - -#if defined(EXITFREE) || defined(PROTO) -void close_all_scripts(void) -{ - while (scriptin[0] != NULL) - closescript(); -} - -#endif - -/* - * Return TRUE when reading keys from a script file. - */ -int using_script(void) -{ - return scriptin[curscript] != NULL; -} - -/* - * This function is called just before doing a blocking wait. Thus after - * waiting 'updatetime' for a character to arrive. - */ -void before_blocking(void) -{ - updatescript(0); - if (may_garbage_collect) - garbage_collect(); -} - -/* - * updatescipt() is called when a character can be written into the script file - * or when we have waited some time for a character (c == 0) - * - * All the changed memfiles are synced if c == 0 or when the number of typed - * characters reaches 'updatecount' and 'updatecount' is non-zero. - */ -void updatescript(int c) -{ - static int count = 0; - - if (c && scriptout) - putc(c, scriptout); - if (c == 0 || (p_uc > 0 && ++count >= p_uc)) { - ml_sync_all(c == 0, TRUE); - count = 0; - } -} - -/* - * Get the next input character. - * Can return a special key or a multi-byte character. - * Can return NUL when called recursively, use safe_vgetc() if that's not - * wanted. - * This translates escaped K_SPECIAL and CSI bytes to a K_SPECIAL or CSI byte. - * Collects the bytes of a multibyte character into the whole character. - * Returns the modifiers in the global "mod_mask". - */ -int vgetc(void) -{ - int c, c2; - int n; - char_u buf[MB_MAXBYTES + 1]; - int i; - - /* Do garbage collection when garbagecollect() was called previously and - * we are now at the toplevel. */ - if (may_garbage_collect && want_garbage_collect) - garbage_collect(); - - /* - * If a character was put back with vungetc, it was already processed. - * Return it directly. - */ - if (old_char != -1) { - c = old_char; - old_char = -1; - mod_mask = old_mod_mask; - mouse_row = old_mouse_row; - mouse_col = old_mouse_col; - } else { - mod_mask = 0x0; - last_recorded_len = 0; - for (;; ) { /* this is done twice if there are modifiers */ - if (mod_mask) { /* no mapping after modifier has been read */ - ++no_mapping; - ++allow_keys; - } - c = vgetorpeek(TRUE); - if (mod_mask) { - --no_mapping; - --allow_keys; - } - - /* Get two extra bytes for special keys */ - if (c == K_SPECIAL - ) { - int save_allow_keys = allow_keys; - - ++no_mapping; - allow_keys = 0; /* make sure BS is not found */ - c2 = vgetorpeek(TRUE); /* no mapping for these chars */ - c = vgetorpeek(TRUE); - --no_mapping; - allow_keys = save_allow_keys; - if (c2 == KS_MODIFIER) { - mod_mask = c; - continue; - } - c = TO_SPECIAL(c2, c); - - } - - /* a keypad or special function key was not mapped, use it like - * its ASCII equivalent */ - switch (c) { - case K_KPLUS: c = '+'; break; - case K_KMINUS: c = '-'; break; - case K_KDIVIDE: c = '/'; break; - case K_KMULTIPLY: c = '*'; break; - case K_KENTER: c = CAR; break; - case K_KPOINT: - c = '.'; break; - case K_K0: c = '0'; break; - case K_K1: c = '1'; break; - case K_K2: c = '2'; break; - case K_K3: c = '3'; break; - case K_K4: c = '4'; break; - case K_K5: c = '5'; break; - case K_K6: c = '6'; break; - case K_K7: c = '7'; break; - case K_K8: c = '8'; break; - case K_K9: c = '9'; break; - - case K_XHOME: - case K_ZHOME: if (mod_mask == MOD_MASK_SHIFT) { - c = K_S_HOME; - mod_mask = 0; - } else if (mod_mask == MOD_MASK_CTRL) { - c = K_C_HOME; - mod_mask = 0; - } else - c = K_HOME; - break; - case K_XEND: - case K_ZEND: if (mod_mask == MOD_MASK_SHIFT) { - c = K_S_END; - mod_mask = 0; - } else if (mod_mask == MOD_MASK_CTRL) { - c = K_C_END; - mod_mask = 0; - } else - c = K_END; - break; - - case K_XUP: c = K_UP; break; - case K_XDOWN: c = K_DOWN; break; - case K_XLEFT: c = K_LEFT; break; - case K_XRIGHT: c = K_RIGHT; break; - } - - /* For a multi-byte character get all the bytes and return the - * converted character. - * Note: This will loop until enough bytes are received! - */ - if (has_mbyte && (n = MB_BYTE2LEN_CHECK(c)) > 1) { - ++no_mapping; - buf[0] = c; - for (i = 1; i < n; ++i) { - buf[i] = vgetorpeek(TRUE); - if (buf[i] == K_SPECIAL - ) { - /* Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, - * which represents a K_SPECIAL (0x80), - * or a CSI - KS_EXTRA - KE_CSI sequence, which represents - * a CSI (0x9B), - * of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. */ - c = vgetorpeek(TRUE); - if (vgetorpeek(TRUE) == (int)KE_CSI && c == KS_EXTRA) - buf[i] = CSI; - } - } - --no_mapping; - c = (*mb_ptr2char)(buf); - } - - break; - } - } - - /* - * In the main loop "may_garbage_collect" can be set to do garbage - * collection in the first next vgetc(). It's disabled after that to - * avoid internally used Lists and Dicts to be freed. - */ - may_garbage_collect = FALSE; - - return c; -} - -/* - * Like vgetc(), but never return a NUL when called recursively, get a key - * directly from the user (ignoring typeahead). - */ -int safe_vgetc(void) -{ - int c; - - c = vgetc(); - if (c == NUL) - c = get_keystroke(); - return c; -} - -/* - * Like safe_vgetc(), but loop to handle K_IGNORE. - * Also ignore scrollbar events. - */ -int plain_vgetc(void) -{ - int c; - - do { - c = safe_vgetc(); - } while (c == K_IGNORE || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR); - return c; -} - -/* - * Check if a character is available, such that vgetc() will not block. - * If the next character is a special character or multi-byte, the returned - * character is not valid!. - */ -int vpeekc(void) -{ - if (old_char != -1) - return old_char; - return vgetorpeek(FALSE); -} - -/* - * Like vpeekc(), but don't allow mapping. Do allow checking for terminal - * codes. - */ -int vpeekc_nomap(void) -{ - int c; - - ++no_mapping; - ++allow_keys; - c = vpeekc(); - --no_mapping; - --allow_keys; - return c; -} - -/* - * Check if any character is available, also half an escape sequence. - * Trick: when no typeahead found, but there is something in the typeahead - * buffer, it must be an ESC that is recognized as the start of a key code. - */ -int vpeekc_any(void) -{ - int c; - - c = vpeekc(); - if (c == NUL && typebuf.tb_len > 0) - c = ESC; - return c; -} - -/* - * Call vpeekc() without causing anything to be mapped. - * Return TRUE if a character is available, FALSE otherwise. - */ -int char_avail(void) -{ - int retval; - - ++no_mapping; - retval = vpeekc(); - --no_mapping; - return retval != NUL; -} - -void -vungetc ( /* unget one character (can only be done once!) */ - int c -) -{ - old_char = c; - old_mod_mask = mod_mask; - old_mouse_row = mouse_row; - old_mouse_col = mouse_col; -} - -/* - * get a character: - * 1. from the stuffbuffer - * This is used for abbreviated commands like "D" -> "d$". - * Also used to redo a command for ".". - * 2. from the typeahead buffer - * Stores text obtained previously but not used yet. - * Also stores the result of mappings. - * Also used for the ":normal" command. - * 3. from the user - * This may do a blocking wait if "advance" is TRUE. - * - * if "advance" is TRUE (vgetc()): - * really get the character. - * KeyTyped is set to TRUE in the case the user typed the key. - * KeyStuffed is TRUE if the character comes from the stuff buffer. - * if "advance" is FALSE (vpeekc()): - * just look whether there is a character available. - * - * When "no_mapping" is zero, checks for mappings in the current mode. - * Only returns one byte (of a multi-byte character). - * K_SPECIAL and CSI may be escaped, need to get two more bytes then. - */ -static int vgetorpeek(int advance) -{ - int c, c1; - int keylen; - char_u *s; - mapblock_T *mp; - mapblock_T *mp2; - mapblock_T *mp_match; - int mp_match_len = 0; - int timedout = FALSE; /* waited for more than 1 second - for mapping to complete */ - int mapdepth = 0; /* check for recursive mapping */ - int mode_deleted = FALSE; /* set when mode has been deleted */ - int local_State; - int mlen; - int max_mlen; - int i; - int new_wcol, new_wrow; - int n; - int nolmaplen; - int old_wcol, old_wrow; - int wait_tb_len; - - /* - * This function doesn't work very well when called recursively. This may - * happen though, because of: - * 1. The call to add_to_showcmd(). char_avail() is then used to check if - * there is a character available, which calls this function. In that - * case we must return NUL, to indicate no character is available. - * 2. A GUI callback function writes to the screen, causing a - * wait_return(). - * Using ":normal" can also do this, but it saves the typeahead buffer, - * thus it should be OK. But don't get a key from the user then. - */ - if (vgetc_busy > 0 - && ex_normal_busy == 0 - ) - return NUL; - - local_State = get_real_state(); - - ++vgetc_busy; - - if (advance) - KeyStuffed = FALSE; - - init_typebuf(); - start_stuff(); - if (advance && typebuf.tb_maplen == 0) - Exec_reg = FALSE; - do { - /* - * get a character: 1. from the stuffbuffer - */ - if (typeahead_char != 0) { - c = typeahead_char; - if (advance) - typeahead_char = 0; - } else { - c = read_readbuffers(advance); - } - if (c != NUL && !got_int) { - if (advance) { - /* KeyTyped = FALSE; When the command that stuffed something - * was typed, behave like the stuffed command was typed. - * needed for CTRL-W CTRl-] to open a fold, for example. */ - KeyStuffed = TRUE; - } - if (typebuf.tb_no_abbr_cnt == 0) - typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */ - } else { - /* - * Loop until we either find a matching mapped key, or we - * are sure that it is not a mapped key. - * If a mapped key sequence is found we go back to the start to - * try re-mapping. - */ - for (;; ) { - /* - * ui_breakcheck() is slow, don't use it too often when - * inside a mapping. But call it each time for typed - * characters. - */ - if (typebuf.tb_maplen) - line_breakcheck(); - else - ui_breakcheck(); /* check for CTRL-C */ - keylen = 0; - if (got_int) { - /* flush all input */ - c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L, - typebuf.tb_change_cnt); - /* - * If inchar() returns TRUE (script file was active) or we - * are inside a mapping, get out of insert mode. - * Otherwise we behave like having gotten a CTRL-C. - * As a result typing CTRL-C in insert mode will - * really insert a CTRL-C. - */ - if ((c || typebuf.tb_maplen) - && (State & (INSERT + CMDLINE))) - c = ESC; - else - c = Ctrl_C; - flush_buffers(TRUE); /* flush all typeahead */ - - if (advance) { - /* Also record this character, it might be needed to - * get out of Insert mode. */ - *typebuf.tb_buf = c; - gotchars(typebuf.tb_buf, 1); - } - cmd_silent = FALSE; - - break; - } else if (typebuf.tb_len > 0) { - /* - * Check for a mappable key sequence. - * Walk through one maphash[] list until we find an - * entry that matches. - * - * Don't look for mappings if: - * - no_mapping set: mapping disabled (e.g. for CTRL-V) - * - maphash_valid not set: no mappings present. - * - typebuf.tb_buf[typebuf.tb_off] should not be remapped - * - in insert or cmdline mode and 'paste' option set - * - waiting for "hit return to continue" and CR or SPACE - * typed - * - waiting for a char with --more-- - * - in Ctrl-X mode, and we get a valid char for that mode - */ - mp = NULL; - max_mlen = 0; - c1 = typebuf.tb_buf[typebuf.tb_off]; - if (no_mapping == 0 && maphash_valid - && (no_zero_mapping == 0 || c1 != '0') - && (typebuf.tb_maplen == 0 - || (p_remap - && (typebuf.tb_noremap[typebuf.tb_off] - & (RM_NONE|RM_ABBR)) == 0)) - && !(p_paste && (State & (INSERT + CMDLINE))) - && !(State == HITRETURN && (c1 == CAR || c1 == ' ')) - && State != ASKMORE - && State != CONFIRM - && !((ctrl_x_mode != 0 && vim_is_ctrl_x_key(c1)) - || ((compl_cont_status & CONT_LOCAL) - && (c1 == Ctrl_N || c1 == Ctrl_P))) - ) { - if (c1 == K_SPECIAL) - nolmaplen = 2; - else { - LANGMAP_ADJUST(c1, TRUE); - nolmaplen = 0; - } - /* First try buffer-local mappings. */ - mp = curbuf->b_maphash[MAP_HASH(local_State, c1)]; - mp2 = maphash[MAP_HASH(local_State, c1)]; - if (mp == NULL) { - /* There are no buffer-local mappings. */ - mp = mp2; - mp2 = NULL; - } - /* - * Loop until a partly matching mapping is found or - * all (local) mappings have been checked. - * The longest full match is remembered in "mp_match". - * A full match is only accepted if there is no partly - * match, so "aa" and "aaa" can both be mapped. - */ - mp_match = NULL; - mp_match_len = 0; - for (; mp != NULL; - mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : - (mp = mp->m_next)) { - /* - * Only consider an entry if the first character - * matches and it is for the current state. - * Skip ":lmap" mappings if keys were mapped. - */ - if (mp->m_keys[0] == c1 - && (mp->m_mode & local_State) - && ((mp->m_mode & LANGMAP) == 0 - || typebuf.tb_maplen == 0)) { - int nomap = nolmaplen; - int c2; - /* find the match length of this mapping */ - for (mlen = 1; mlen < typebuf.tb_len; ++mlen) { - c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; - if (nomap > 0) - --nomap; - else if (c2 == K_SPECIAL) - nomap = 2; - else - LANGMAP_ADJUST(c2, TRUE); - if (mp->m_keys[mlen] != c2) - break; - } - - /* Don't allow mapping the first byte(s) of a - * multi-byte char. Happens when mapping - * and then changing 'encoding'. Beware - * that 0x80 is escaped. */ - char_u *p1 = mp->m_keys; - char_u *p2 = mb_unescape(&p1); - - if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2)) - mlen = 0; - /* - * Check an entry whether it matches. - * - Full match: mlen == keylen - * - Partly match: mlen == typebuf.tb_len - */ - keylen = mp->m_keylen; - if (mlen == keylen - || (mlen == typebuf.tb_len - && typebuf.tb_len < keylen)) { - /* - * If only script-local mappings are - * allowed, check if the mapping starts - * with K_SNR. - */ - s = typebuf.tb_noremap + typebuf.tb_off; - if (*s == RM_SCRIPT - && (mp->m_keys[0] != K_SPECIAL - || mp->m_keys[1] != KS_EXTRA - || mp->m_keys[2] - != (int)KE_SNR)) - continue; - /* - * If one of the typed keys cannot be - * remapped, skip the entry. - */ - for (n = mlen; --n >= 0; ) - if (*s++ & (RM_NONE|RM_ABBR)) - break; - if (n >= 0) - continue; - - if (keylen > typebuf.tb_len) { - if (!timedout && !(mp_match != NULL - && mp_match->m_nowait)) { - /* break at a partly match */ - keylen = KEYLEN_PART_MAP; - break; - } - } else if (keylen > mp_match_len) { - /* found a longer match */ - mp_match = mp; - mp_match_len = keylen; - } - } else - /* No match; may have to check for - * termcode at next character. */ - if (max_mlen < mlen) - max_mlen = mlen; - } - } - - /* If no partly match found, use the longest full - * match. */ - if (keylen != KEYLEN_PART_MAP) { - mp = mp_match; - keylen = mp_match_len; - } - } - - /* Check for match with 'pastetoggle' */ - if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { - for (mlen = 0; mlen < typebuf.tb_len && p_pt[mlen]; - ++mlen) - if (p_pt[mlen] != typebuf.tb_buf[typebuf.tb_off - + mlen]) - break; - if (p_pt[mlen] == NUL) { /* match */ - /* write chars to script file(s) */ - if (mlen > typebuf.tb_maplen) - gotchars(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_maplen, - mlen - typebuf.tb_maplen); - - del_typebuf(mlen, 0); /* remove the chars */ - set_option_value((char_u *)"paste", - (long)!p_paste, NULL, 0); - if (!(State & INSERT)) { - msg_col = 0; - msg_row = Rows - 1; - msg_clr_eos(); /* clear ruler */ - } - status_redraw_all(); - redraw_statuslines(); - showmode(); - setcursor(); - continue; - } - /* Need more chars for partly match. */ - if (mlen == typebuf.tb_len) - keylen = KEYLEN_PART_KEY; - else if (max_mlen < mlen) - /* no match, may have to check for termcode at - * next character */ - max_mlen = mlen + 1; - } - - if ((mp == NULL || max_mlen >= mp_match_len) - && keylen != KEYLEN_PART_MAP) { - int save_keylen = keylen; - - /* - * When no matching mapping found or found a - * non-matching mapping that matches at least what the - * matching mapping matched: - * Check if we have a terminal code, when: - * mapping is allowed, - * keys have not been mapped, - * and not an ESC sequence, not in insert mode or - * p_ek is on, - * and when not timed out, - */ - if ((no_mapping == 0 || allow_keys != 0) - && (typebuf.tb_maplen == 0 - || (p_remap && typebuf.tb_noremap[ - typebuf.tb_off] == RM_YES)) - && !timedout) { - keylen = check_termcode(max_mlen + 1, - NULL, 0, NULL); - - /* If no termcode matched but 'pastetoggle' - * matched partially it's like an incomplete key - * sequence. */ - if (keylen == 0 && save_keylen == KEYLEN_PART_KEY) - keylen = KEYLEN_PART_KEY; - - /* - * When getting a partial match, but the last - * characters were not typed, don't wait for a - * typed character to complete the termcode. - * This helps a lot when a ":normal" command ends - * in an ESC. - */ - if (keylen < 0 - && typebuf.tb_len == typebuf.tb_maplen) - keylen = 0; - } else - keylen = 0; - if (keylen == 0) { /* no matching terminal code */ - /* When there was a matching mapping and no - * termcode could be replaced after another one, - * use that mapping (loop around). If there was - * no mapping use the character from the - * typeahead buffer right here. */ - if (mp == NULL) { - /* - * get a character: 2. from the typeahead buffer - */ - c = typebuf.tb_buf[typebuf.tb_off] & 255; - if (advance) { /* remove chars from tb_buf */ - cmd_silent = (typebuf.tb_silent > 0); - if (typebuf.tb_maplen > 0) - KeyTyped = FALSE; - else { - KeyTyped = TRUE; - /* write char to script file(s) */ - gotchars(typebuf.tb_buf - + typebuf.tb_off, 1); - } - KeyNoremap = typebuf.tb_noremap[ - typebuf.tb_off]; - del_typebuf(1, 0); - } - break; /* got character, break for loop */ - } - } - if (keylen > 0) { /* full matching terminal code */ - continue; /* try mapping again */ - } - - /* Partial match: get some more characters. When a - * matching mapping was found use that one. */ - if (mp == NULL || keylen < 0) - keylen = KEYLEN_PART_KEY; - else - keylen = mp_match_len; - } - - /* complete match */ - if (keylen >= 0 && keylen <= typebuf.tb_len) { - int save_m_expr; - int save_m_noremap; - int save_m_silent; - char_u *save_m_keys; - char_u *save_m_str; - - /* write chars to script file(s) */ - if (keylen > typebuf.tb_maplen) - gotchars(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_maplen, - keylen - typebuf.tb_maplen); - - cmd_silent = (typebuf.tb_silent > 0); - del_typebuf(keylen, 0); /* remove the mapped keys */ - - /* - * Put the replacement string in front of mapstr. - * The depth check catches ":map x y" and ":map y x". - */ - if (++mapdepth >= p_mmd) { - EMSG(_("E223: recursive mapping")); - if (State & CMDLINE) - redrawcmdline(); - else - setcursor(); - flush_buffers(FALSE); - mapdepth = 0; /* for next one */ - c = -1; - break; - } - - /* - * In Select mode and a Visual mode mapping is used: - * Switch to Visual mode temporarily. Append K_SELECT - * to switch back to Select mode. - */ - if (VIsual_active && VIsual_select - && (mp->m_mode & VISUAL)) { - VIsual_select = FALSE; - (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, - 0, TRUE, FALSE); - } - - /* Copy the values from *mp that are used, because - * evaluating the expression may invoke a function - * that redefines the mapping, thereby making *mp - * invalid. */ - save_m_expr = mp->m_expr; - save_m_noremap = mp->m_noremap; - save_m_silent = mp->m_silent; - save_m_keys = NULL; /* only saved when needed */ - save_m_str = NULL; /* only saved when needed */ - - /* - * Handle ":map ": evaluate the {rhs} as an - * expression. Also save and restore the command line - * for "normal :". - */ - if (mp->m_expr) { - int save_vgetc_busy = vgetc_busy; - - vgetc_busy = 0; - save_m_keys = vim_strsave(mp->m_keys); - save_m_str = vim_strsave(mp->m_str); - s = eval_map_expr(save_m_str, NUL); - vgetc_busy = save_vgetc_busy; - } else - s = mp->m_str; - - /* - * Insert the 'to' part in the typebuf.tb_buf. - * If 'from' field is the same as the start of the - * 'to' field, don't remap the first character (but do - * allow abbreviations). - * If m_noremap is set, don't remap the whole 'to' - * part. - */ - if (s == NULL) - i = FAIL; - else { - int noremap; - - if (save_m_noremap != REMAP_YES) - noremap = save_m_noremap; - else if ( - STRNCMP(s, save_m_keys != NULL - ? save_m_keys : mp->m_keys, - (size_t)keylen) - != 0) - noremap = REMAP_YES; - else - noremap = REMAP_SKIP; - i = ins_typebuf(s, noremap, - 0, TRUE, cmd_silent || save_m_silent); - if (save_m_expr) - free(s); - } - free(save_m_keys); - free(save_m_str); - if (i == FAIL) { - c = -1; - break; - } - continue; - } - } - - /* - * get a character: 3. from the user - handle in Insert mode - */ - /* - * special case: if we get an in insert mode and there - * are no more characters at once, we pretend to go out of - * insert mode. This prevents the one second delay after - * typing an . If we get something after all, we may - * have to redisplay the mode. That the cursor is in the wrong - * place does not matter. - */ - c = 0; - new_wcol = curwin->w_wcol; - new_wrow = curwin->w_wrow; - if ( advance - && typebuf.tb_len == 1 - && typebuf.tb_buf[typebuf.tb_off] == ESC - && !no_mapping - && ex_normal_busy == 0 - && typebuf.tb_maplen == 0 - && (State & INSERT) - && (p_timeout - || (keylen == KEYLEN_PART_KEY && p_ttimeout)) - && (c = inchar(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_len, 3, 25L, - typebuf.tb_change_cnt)) == 0) { - colnr_T col = 0, vcol; - char_u *ptr; - - if (mode_displayed) { - unshowmode(TRUE); - mode_deleted = TRUE; - } - validate_cursor(); - old_wcol = curwin->w_wcol; - old_wrow = curwin->w_wrow; - - /* move cursor left, if possible */ - if (curwin->w_cursor.col != 0) { - if (curwin->w_wcol > 0) { - if (did_ai) { - /* - * We are expecting to truncate the trailing - * white-space, so find the last non-white - * character -- webb - */ - col = vcol = curwin->w_wcol = 0; - ptr = ml_get_curline(); - while (col < curwin->w_cursor.col) { - if (!vim_iswhite(ptr[col])) - curwin->w_wcol = vcol; - vcol += lbr_chartabsize(ptr + col, - (colnr_T)vcol); - if (has_mbyte) - col += (*mb_ptr2len)(ptr + col); - else - ++col; - } - curwin->w_wrow = curwin->w_cline_row - + curwin->w_wcol / W_WIDTH(curwin); - curwin->w_wcol %= W_WIDTH(curwin); - curwin->w_wcol += curwin_col_off(); - col = 0; /* no correction needed */ - } else { - --curwin->w_wcol; - col = curwin->w_cursor.col - 1; - } - } else if (curwin->w_p_wrap && curwin->w_wrow) { - --curwin->w_wrow; - curwin->w_wcol = W_WIDTH(curwin) - 1; - col = curwin->w_cursor.col - 1; - } - if (has_mbyte && col > 0 && curwin->w_wcol > 0) { - /* Correct when the cursor is on the right halve - * of a double-wide character. */ - ptr = ml_get_curline(); - col -= (*mb_head_off)(ptr, ptr + col); - if ((*mb_ptr2cells)(ptr + col) > 1) - --curwin->w_wcol; - } - } - setcursor(); - out_flush(); - new_wcol = curwin->w_wcol; - new_wrow = curwin->w_wrow; - curwin->w_wcol = old_wcol; - curwin->w_wrow = old_wrow; - } - if (c < 0) - continue; /* end of input script reached */ - typebuf.tb_len += c; - - /* buffer full, don't map */ - if (typebuf.tb_len >= typebuf.tb_maplen + MAXMAPLEN) { - timedout = TRUE; - continue; - } - - if (ex_normal_busy > 0) { - static int tc = 0; - - /* No typeahead left and inside ":normal". Must return - * something to avoid getting stuck. When an incomplete - * mapping is present, behave like it timed out. */ - if (typebuf.tb_len > 0) { - timedout = TRUE; - continue; - } - /* When 'insertmode' is set, ESC just beeps in Insert - * mode. Use CTRL-L to make edit() return. - * For the command line only CTRL-C always breaks it. - * For the cmdline window: Alternate between ESC and - * CTRL-C: ESC for most situations and CTRL-C to close the - * cmdline window. */ - if (p_im && (State & INSERT)) - c = Ctrl_L; - else if ((State & CMDLINE) - || (cmdwin_type > 0 && tc == ESC) - ) - c = Ctrl_C; - else - c = ESC; - tc = c; - break; - } - - /* - * get a character: 3. from the user - update display - */ - /* In insert mode a screen update is skipped when characters - * are still available. But when those available characters - * are part of a mapping, and we are going to do a blocking - * wait here. Need to update the screen to display the - * changed text so far. Also for when 'lazyredraw' is set and - * redrawing was postponed because there was something in the - * input buffer (e.g., termresponse). */ - if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 - && advance && must_redraw != 0 && !need_wait_return) { - update_screen(0); - setcursor(); /* put cursor back where it belongs */ - } - - /* - * If we have a partial match (and are going to wait for more - * input from the user), show the partially matched characters - * to the user with showcmd. - */ - i = 0; - c1 = 0; - if (typebuf.tb_len > 0 && advance && !exmode_active) { - if (((State & (NORMAL | INSERT)) || State == LANGMAP) - && State != HITRETURN) { - /* this looks nice when typing a dead character map */ - if (State & INSERT - && ptr2cells(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_len - 1) == 1) { - edit_putchar(typebuf.tb_buf[typebuf.tb_off - + typebuf.tb_len - 1], FALSE); - setcursor(); /* put cursor back where it belongs */ - c1 = 1; - } - /* need to use the col and row from above here */ - old_wcol = curwin->w_wcol; - old_wrow = curwin->w_wrow; - curwin->w_wcol = new_wcol; - curwin->w_wrow = new_wrow; - push_showcmd(); - if (typebuf.tb_len > SHOWCMD_COLS) - i = typebuf.tb_len - SHOWCMD_COLS; - while (i < typebuf.tb_len) - (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off - + i++]); - curwin->w_wcol = old_wcol; - curwin->w_wrow = old_wrow; - } - - /* this looks nice when typing a dead character map */ - if ((State & CMDLINE) - && cmdline_star == 0 - && ptr2cells(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_len - 1) == 1) { - putcmdline(typebuf.tb_buf[typebuf.tb_off - + typebuf.tb_len - 1], FALSE); - c1 = 1; - } - } - - /* - * get a character: 3. from the user - get it - */ - wait_tb_len = typebuf.tb_len; - c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, - typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1, - !advance - ? 0 - : ((typebuf.tb_len == 0 - || !(p_timeout || (p_ttimeout - && keylen == KEYLEN_PART_KEY))) - ? -1L - : ((keylen == KEYLEN_PART_KEY && p_ttm >= 0) - ? p_ttm - : p_tm)), typebuf.tb_change_cnt); - - if (i != 0) - pop_showcmd(); - if (c1 == 1) { - if (State & INSERT) - edit_unputchar(); - if (State & CMDLINE) - unputcmdline(); - else - setcursor(); /* put cursor back where it belongs */ - } - - if (c < 0) - continue; /* end of input script reached */ - if (c == NUL) { /* no character available */ - if (!advance) - break; - if (wait_tb_len > 0) { /* timed out */ - timedout = TRUE; - continue; - } - } else { /* allow mapping for just typed characters */ - while (typebuf.tb_buf[typebuf.tb_off - + typebuf.tb_len] != NUL) - typebuf.tb_noremap[typebuf.tb_off - + typebuf.tb_len++] = RM_YES; -#ifdef USE_IM_CONTROL - /* Get IM status right after getting keys, not after the - * timeout for a mapping (focus may be lost by then). */ - vgetc_im_active = im_get_status(); -#endif - } - } /* for (;;) */ - } /* if (!character from stuffbuf) */ - - /* if advance is FALSE don't loop on NULs */ - } while (c < 0 || (advance && c == NUL)); - - /* - * The "INSERT" message is taken care of here: - * if we return an ESC to exit insert mode, the message is deleted - * if we don't return an ESC but deleted the message before, redisplay it - */ - if (advance && p_smd && msg_silent == 0 && (State & INSERT)) { - if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) { - if (typebuf.tb_len && !KeyTyped) - redraw_cmdline = TRUE; /* delete mode later */ - else - unshowmode(FALSE); - } else if (c != ESC && mode_deleted) { - if (typebuf.tb_len && !KeyTyped) - redraw_cmdline = TRUE; /* show mode later */ - else - showmode(); - } - } - - --vgetc_busy; - - return c; -} - -/* - * inchar() - get one character from - * 1. a scriptfile - * 2. the keyboard - * - * As much characters as we can get (upto 'maxlen') are put in "buf" and - * NUL terminated (buffer length must be 'maxlen' + 1). - * Minimum for "maxlen" is 3!!!! - * - * "tb_change_cnt" is the value of typebuf.tb_change_cnt if "buf" points into - * it. When typebuf.tb_change_cnt changes (e.g., when a message is received - * from a remote client) "buf" can no longer be used. "tb_change_cnt" is 0 - * otherwise. - * - * If we got an interrupt all input is read until none is available. - * - * If wait_time == 0 there is no waiting for the char. - * If wait_time == n we wait for n msec for a character to arrive. - * If wait_time == -1 we wait forever for a character to arrive. - * - * Return the number of obtained characters. - * Return -1 when end of input script reached. - */ -int -inchar ( - char_u *buf, - int maxlen, - long wait_time, /* milli seconds */ - int tb_change_cnt -) -{ - int len = 0; /* init for GCC */ - int retesc = FALSE; /* return ESC with gotint */ - int script_char; - - if (wait_time == -1L || wait_time > 100L) { /* flush output before waiting */ - cursor_on(); - out_flush(); - } - - /* - * Don't reset these when at the hit-return prompt, otherwise a endless - * recursive loop may result (write error in swapfile, hit-return, timeout - * on char wait, flush swapfile, write error....). - */ - if (State != HITRETURN) { - did_outofmem_msg = FALSE; /* display out of memory message (again) */ - did_swapwrite_msg = FALSE; /* display swap file write error again */ - } - undo_off = FALSE; /* restart undo now */ - - /* - * Get a character from a script file if there is one. - * If interrupted: Stop reading script files, close them all. - */ - script_char = -1; - while (scriptin[curscript] != NULL && script_char < 0 - && !ignore_script - ) { - - - if (got_int || (script_char = getc(scriptin[curscript])) < 0) { - /* Reached EOF. - * Careful: closescript() frees typebuf.tb_buf[] and buf[] may - * point inside typebuf.tb_buf[]. Don't use buf[] after this! */ - closescript(); - /* - * When reading script file is interrupted, return an ESC to get - * back to normal mode. - * Otherwise return -1, because typebuf.tb_buf[] has changed. - */ - if (got_int) - retesc = TRUE; - else - return -1; - } else { - buf[0] = script_char; - len = 1; - } - } - - if (script_char < 0) { /* did not get a character from script */ - /* - * If we got an interrupt, skip all previously typed characters and - * return TRUE if quit reading script file. - * Stop reading typeahead when a single CTRL-C was read, - * fill_input_buf() returns this when not able to read from stdin. - * Don't use buf[] here, closescript() may have freed typebuf.tb_buf[] - * and buf may be pointing inside typebuf.tb_buf[]. - */ - if (got_int) { -#define DUM_LEN MAXMAPLEN * 3 + 3 - char_u dum[DUM_LEN + 1]; - - for (;; ) { - len = ui_inchar(dum, DUM_LEN, 0L, 0); - if (len == 0 || (len == 1 && dum[0] == 3)) - break; - } - return retesc; - } - - /* - * Always flush the output characters when getting input characters - * from the user. - */ - out_flush(); - - /* - * Fill up to a third of the buffer, because each character may be - * tripled below. - */ - len = ui_inchar(buf, maxlen / 3, wait_time, tb_change_cnt); - } - - if (typebuf_changed(tb_change_cnt)) - return 0; - - return fix_input_buffer(buf, len, script_char >= 0); -} - -/* - * Fix typed characters for use by vgetc() and check_termcode(). - * buf[] must have room to triple the number of bytes! - * Returns the new length. - */ -int -fix_input_buffer ( - char_u *buf, - int len, - int script /* TRUE when reading from a script */ -) -{ - int i; - char_u *p = buf; - - /* - * Two characters are special: NUL and K_SPECIAL. - * When compiled With the GUI CSI is also special. - * Replace NUL by K_SPECIAL KS_ZERO KE_FILLER - * Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER - * Replace CSI by K_SPECIAL KS_EXTRA KE_CSI - * Don't replace K_SPECIAL when reading a script file. - */ - for (i = len; --i >= 0; ++p) { - if (p[0] == NUL - || (p[0] == K_SPECIAL - && !script - && (i < 2 || p[1] != KS_EXTRA || is_user_input(p[2])))) { - memmove(p + 3, p + 1, (size_t)i); - p[2] = K_THIRD(p[0]); - p[1] = K_SECOND(p[0]); - p[0] = K_SPECIAL; - p += 2; - len += 2; - } - } - *p = NUL; /* add trailing NUL */ - return len; -} - -#if defined(USE_INPUT_BUF) || defined(PROTO) -/* - * Return TRUE when bytes are in the input buffer or in the typeahead buffer. - * Normally the input buffer would be sufficient, but feedkeys() may insert - * characters in the typeahead buffer while we are waiting for input to arrive. - */ -int input_available(void) -{ - return !vim_is_input_buf_empty() - || typebuf_was_filled - ; -} - -#endif - -/* - * map[!] : show all key mappings - * map[!] {lhs} : show key mapping for {lhs} - * map[!] {lhs} {rhs} : set key mapping for {lhs} to {rhs} - * noremap[!] {lhs} {rhs} : same, but no remapping for {rhs} - * unmap[!] {lhs} : remove key mapping for {lhs} - * abbr : show all abbreviations - * abbr {lhs} : show abbreviations for {lhs} - * abbr {lhs} {rhs} : set abbreviation for {lhs} to {rhs} - * noreabbr {lhs} {rhs} : same, but no remapping for {rhs} - * unabbr {lhs} : remove abbreviation for {lhs} - * - * maptype: 0 for :map, 1 for :unmap, 2 for noremap. - * - * arg is pointer to any arguments. Note: arg cannot be a read-only string, - * it will be modified. - * - * for :map mode is NORMAL + VISUAL + SELECTMODE + OP_PENDING - * for :map! mode is INSERT + CMDLINE - * for :cmap mode is CMDLINE - * for :imap mode is INSERT - * for :lmap mode is LANGMAP - * for :nmap mode is NORMAL - * for :vmap mode is VISUAL + SELECTMODE - * for :xmap mode is VISUAL - * for :smap mode is SELECTMODE - * for :omap mode is OP_PENDING - * - * for :abbr mode is INSERT + CMDLINE - * for :iabbr mode is INSERT - * for :cabbr mode is CMDLINE - * - * Return 0 for success - * 1 for invalid arguments - * 2 for no match - * 4 for out of mem - * 5 for entry not unique - */ -int -do_map ( - int maptype, - char_u *arg, - int mode, - int abbrev /* not a mapping but an abbreviation */ -) -{ - char_u *keys; - mapblock_T *mp, **mpp; - char_u *rhs; - char_u *p; - int n; - int len = 0; /* init for GCC */ - char_u *newstr; - int hasarg; - int haskey; - int did_it = FALSE; - int did_local = FALSE; - int round; - char_u *keys_buf = NULL; - char_u *arg_buf = NULL; - int retval = 0; - int do_backslash; - int hash; - int new_hash; - mapblock_T **abbr_table; - mapblock_T **map_table; - int unique = FALSE; - int nowait = FALSE; - int silent = FALSE; - int special = FALSE; - int expr = FALSE; - int noremap; - char_u *orig_rhs; - - keys = arg; - map_table = maphash; - abbr_table = &first_abbr; - - /* For ":noremap" don't remap, otherwise do remap. */ - if (maptype == 2) - noremap = REMAP_NONE; - else - noremap = REMAP_YES; - - /* Accept , , ,